1 #import "JubGtkRosterUI.h"
2 #import "JubGObjectMap.h"
3 #import "JubGtkChatUI.h"
4 #import "JubGtkHelper.h"
8 static void roster_row_activated(GtkTreeView *tree_view, GtkTreePath *path,
9 GtkTreeViewColumn *column, gpointer data)
11 JubGtkRosterUI *roster = data;
13 GtkTreeModel *tree_model;
16 OFAutoreleasePool *pool;
18 tree_model = gtk_tree_view_get_model(tree_view);
19 gtk_tree_model_get_iter(tree_model, &row_iter, path);
20 gtk_tree_model_get(tree_model, &row_iter, 1, &jid_s, -1);
22 // This was a group row
25 pool = [OFAutoreleasePool new];
26 jid = [XMPPJID JIDWithString: [OFString stringWithUTF8String: jid_s]];
28 [roster performSelectorOnMainThread: @selector(chatForJID:)
34 static void presence_changed(GtkComboBox *combo_box, gpointer data)
37 XMPPConnection *connection = data;
38 OFAutoreleasePool *pool = [OFAutoreleasePool new];
40 const char *status = gtk_combo_box_get_active_id(combo_box);
42 if (!strcmp(status, "unavailable"))
43 pres = [XMPPPresence presenceWithType: @"unavailable"];
45 pres = [XMPPPresence presence];
46 if (strcmp(status, "available"))
47 [pres addShow: @(status)];
50 [connection sendStanza: pres];
55 static gboolean filter_roster_by_presence(GtkTreeModel *model,
56 GtkTreeIter *iter, gpointer data)
60 OFCountedSet *presences = data;
62 gtk_tree_model_get(model, iter, 1, &jid_s, -1);
68 jid = [[OFString alloc] initWithUTF8String: jid_s];
72 int num = [presences countForObject: jid];
82 @implementation JubGtkRosterUI
83 - initWithBuilder: (GtkBuilder*)builder_
84 connection: (XMPPConnection*)connection_
89 GtkTreeView *roster_view;
90 GtkComboBox *presence_combo;
92 groupMap = [[OFMapTable alloc]
93 initWithKeyFunctions: keyFunctions
94 valueFunctions: rowRefFunctions];
95 contactMap = [[OFMutableDictionary alloc] init];
96 chatMap = [[OFMutableDictionary alloc] init];
97 presences = [[OFCountedSet alloc] init];
98 connection = [connection_ retain];
100 builder = g_object_ref(builder_);
102 roster_model = GTK_TREE_STORE(gtk_builder_get_object(builder,
105 roster_filter = GTK_TREE_MODEL_FILTER(
106 gtk_builder_get_object(builder, "RosterTreeModelFilter"));
108 gtk_tree_model_filter_set_visible_func(roster_filter,
109 filter_roster_by_presence, presences, NULL);
111 roster_view = GTK_TREE_VIEW(gtk_builder_get_object(builder,
114 g_signal_connect(roster_view, "row_activated",
115 G_CALLBACK(roster_row_activated), self);
117 presence_combo = GTK_COMBO_BOX(gtk_builder_get_object(builder,
118 "PresenceComboBox"));
120 g_signal_connect(presence_combo, "changed",
121 G_CALLBACK(presence_changed), connection);
133 [contactMap release];
135 [connection release];
138 g_object_unref(roster_model);
141 g_object_unref(roster_filter);
143 g_object_unref(builder);
148 /* Presence handling */
149 - (void)connection: (XMPPConnection*)connection
150 didReceivePresence: (XMPPPresence*)presence
152 if ([presence.type isEqual: @"available"])
153 [presences addObject: [presence.from bareJID]];
154 else if ([presence.type isEqual: @"unavailable"])
155 [presences removeObject: [presence.from bareJID]];
158 gtk_tree_model_filter_refilter(roster_filter);
162 // FIXME: This needs to move somewhere else
163 - (JubGtkChatUI*)chatForJID: (XMPPJID*)jid
165 OFAutoreleasePool *pool = [OFAutoreleasePool new];
167 [chatMap objectForKey: [jid bareJID]];
169 OFString * title = [@"Chat with " stringByAppendingString:
172 chat = [[[JubGtkChatUI alloc]
175 [chatMap removeObjectForKey: [jid bareJID]];
177 sendBlock: ^(OFString *text) {
179 [XMPPMessage messageWithType: @"chat"];
182 [connection sendStanza: msg];
186 [chatMap setObject: chat
187 forKey: [jid bareJID]];
195 - (void)connection: (XMPPConnection*)connection
196 didReceiveMessage: (XMPPMessage*)message
198 JubGtkChatUI *chat = [self chatForJID: message.from];
199 [chat addMessage: message.body
200 sender: [message.from bareJID]];
203 /* Roster Delegate methods */
204 - (void)Jub_addRosterItem: (XMPPRosterItem*)item
205 group: (OFString*)group
208 GtkTreeIter group_iter, contact_iter;
209 GtkTreeRowReference *group_ref, *contact_ref;
210 GtkTreePath *group_path, *contact_path;
211 OFMapTable *contactRows =
212 [contactMap objectForKey: [item.JID bareJID]];
214 group_ref = [groupMap valueForKey: group];
217 // Create new group row
218 gtk_tree_store_append(roster_model, &group_iter, NULL);
219 gtk_tree_store_set(roster_model, &group_iter,
220 0, [group UTF8String], -1);
222 group_path = gtk_tree_model_get_path(GTK_TREE_MODEL(
223 roster_model), &group_iter);
225 group_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(
226 roster_model), group_path);
228 [groupMap setValue: group_ref
231 // Get iter for existing group row
232 group_path = gtk_tree_row_reference_get_path(group_ref);
234 gtk_tree_model_get_iter(GTK_TREE_MODEL(roster_model),
235 &group_iter, group_path);
237 gtk_tree_path_free(group_path);
239 // Create new contact row
240 gtk_tree_store_append(roster_model, &contact_iter, &group_iter);
242 gtk_tree_store_set(roster_model, &contact_iter,
243 0, [item.name UTF8String],
244 1, [[item.JID bareJID] UTF8String], -1);
246 gtk_tree_store_set(roster_model, &contact_iter,
247 0, [item.JID.node UTF8String],
248 1, [[item.JID bareJID] UTF8String], -1);
250 contact_path = gtk_tree_model_get_path(GTK_TREE_MODEL(
251 roster_model), &contact_iter);
253 contact_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(
254 roster_model), contact_path);
256 gtk_tree_path_free(contact_path);
258 [contactRows setValue: contact_ref
263 - (void)Jub_removeRosterItem: (XMPPRosterItem*)item
264 group: (OFString*)group
267 GtkTreeIter contact_iter, group_iter;
268 GtkTreePath *contact_path, *group_path;
269 GtkTreeRowReference *contact_ref, *group_ref;
270 OFMapTable *contactRows =
271 [contactMap objectForKey: [item.JID bareJID]];
273 contact_ref = [contactRows valueForKey: group];
274 contact_path = gtk_tree_row_reference_get_path(contact_ref);
275 gtk_tree_model_get_iter(GTK_TREE_MODEL(roster_model),
276 &contact_iter, contact_path);
278 gtk_tree_store_remove(roster_model, &contact_iter);
280 group_ref = [groupMap valueForKey: group];
281 group_path = gtk_tree_row_reference_get_path(group_ref);
282 gtk_tree_model_get_iter(GTK_TREE_MODEL(roster_model),
283 &group_iter, group_path);
285 if (!gtk_tree_model_iter_has_child(GTK_TREE_MODEL(roster_model),
287 gtk_tree_store_remove(roster_model, &group_iter);
288 [groupMap removeValueForKey: group];
291 gtk_tree_path_free(group_path);
295 - (void)rosterWasReceived: (XMPPRoster*)roster_
297 of_log(@"Handling roster");
298 [[roster_ rosterItems] enumerateKeysAndObjectsUsingBlock:
299 ^(OFString *bareJID, XMPPRosterItem *item, BOOL *stop) {
301 OFMapTable *contactRows = [OFMapTable
302 mapTableWithKeyFunctions: keyFunctions
303 valueFunctions: rowRefFunctions];
305 [contactMap setObject: contactRows
308 if (item.groups != nil)
309 groups = item.groups;
311 groups = @[@"General"];
313 for (OFString *group in groups)
314 [self Jub_addRosterItem: item
319 - (void)roster: (XMPPRoster*)roster_
320 didReceiveRosterItem: (XMPPRosterItem*)item
323 XMPPRosterItem *oldItem =
324 [roster_.rosterItems objectForKey: [item.JID bareJID]];
327 if (oldItem.groups != nil)
328 groups = oldItem.groups;
330 groups = @[@"General"];
332 for (OFString *group in groups)
333 [self Jub_removeRosterItem: oldItem
336 [contactMap removeObjectForKey: [item.JID bareJID]];
339 if (![item.subscription isEqual: @"remove"]) {
340 OFMapTable *contactRows = [OFMapTable
341 mapTableWithKeyFunctions: keyFunctions
342 valueFunctions: rowRefFunctions];
344 [contactMap setObject: contactRows
345 forKey: [item.JID bareJID]];
347 if (item.groups != nil)
348 groups = item.groups;
350 groups = @[@"General"];
352 for (OFString *group in groups)
353 [self Jub_addRosterItem: item