1 #import "JubGtkRosterUI.h"
2 #import "JubGObjectMap.h"
3 #import "JubGtkChatUI.h"
4 #import "JubGtkHelper.h"
8 static gboolean 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
23 if (!jid_s) return TRUE;
25 pool = [OFAutoreleasePool new];
26 jid = [XMPPJID JIDWithString: [OFString stringWithUTF8String: jid_s]];
28 [roster performSelectorOnMainThread: @selector(chatForJID:)
36 static void presence_changed(GtkComboBox *combo_box, gpointer data)
39 XMPPConnection *connection = data;
40 OFAutoreleasePool *pool = [OFAutoreleasePool new];
42 const char *status = gtk_combo_box_get_active_id(combo_box);
44 if (!strcmp(status, "unavailable"))
45 pres = [XMPPPresence presenceWithType: @"unavailable"];
47 pres = [XMPPPresence presence];
48 if (strcmp(status, "available"))
49 [pres addShow: @(status)];
52 [connection sendStanza: pres];
57 static gboolean filter_roster_by_presence(GtkTreeModel *model,
58 GtkTreeIter *iter, gpointer data)
62 OFCountedSet *presences = data;
64 gtk_tree_model_get(model, iter, 1, &jid_s, -1);
70 jid = [[OFString alloc] initWithUTF8String: jid_s];
74 int num = [presences countForObject: jid];
84 @implementation JubGtkRosterUI
85 - initWithBuilder: (GtkBuilder*)builder_
86 connection: (XMPPConnection*)connection_
91 GtkTreeView *roster_view;
92 GtkComboBox *presence_combo;
94 groupMap = [[OFMapTable alloc]
95 initWithKeyFunctions: keyFunctions
96 valueFunctions: rowRefFunctions];
97 contactMap = [[OFMutableDictionary alloc] init];
98 chatMap = [[OFMutableDictionary alloc] init];
99 presences = [[OFCountedSet alloc] init];
100 connection = [connection_ retain];
102 builder = g_object_ref(builder_);
104 roster_model = GTK_TREE_STORE(gtk_builder_get_object(builder,
107 roster_filter = GTK_TREE_MODEL_FILTER(
108 gtk_builder_get_object(builder, "RosterTreeModelFilter"));
110 gtk_tree_model_filter_set_visible_func(roster_filter,
111 filter_roster_by_presence, presences, NULL);
113 roster_view = GTK_TREE_VIEW(gtk_builder_get_object(builder,
116 g_signal_connect(roster_view, "row_activated",
117 G_CALLBACK(roster_row_activated), self);
119 presence_combo = GTK_COMBO_BOX(gtk_builder_get_object(builder,
120 "PresenceComboBox"));
122 g_signal_connect(presence_combo, "changed",
123 G_CALLBACK(presence_changed), connection);
135 [contactMap release];
137 [connection release];
140 g_object_unref(roster_model);
143 g_object_unref(roster_filter);
145 g_object_unref(builder);
150 /* Presence handling */
151 - (void)connection: (XMPPConnection*)connection
152 didReceivePresence: (XMPPPresence*)presence
154 if ([presence.type isEqual: @"available"])
155 [presences addObject: [presence.from bareJID]];
156 else if ([presence.type isEqual: @"unavailable"])
157 [presences removeObject: [presence.from bareJID]];
160 gtk_tree_model_filter_refilter(roster_filter);
164 // FIXME: This needs to move somewhere else
165 - (JubGtkChatUI*)chatForJID: (XMPPJID*)jid
167 OFAutoreleasePool *pool = [OFAutoreleasePool new];
169 [chatMap objectForKey: [jid bareJID]];
171 OFString * title = [@"Chat with " stringByAppendingString:
174 chat = [[[JubGtkChatUI alloc]
177 [chatMap removeObjectForKey: [jid bareJID]];
179 sendBlock: ^(OFString *text) {
181 [XMPPMessage messageWithType: @"chat"];
184 [connection sendStanza: msg];
188 [chatMap setObject: chat
189 forKey: [jid bareJID]];
197 - (void)connection: (XMPPConnection*)connection
198 didReceiveMessage: (XMPPMessage*)message
200 JubGtkChatUI *chat = [self chatForJID: message.from];
201 [chat addMessage: message.body
202 sender: [message.from bareJID]];
205 /* Roster Delegate methods */
206 - (void)Jub_addRosterItem: (XMPPRosterItem*)item
207 group: (OFString*)group
210 GtkTreeIter group_iter, contact_iter;
211 GtkTreeRowReference *group_ref, *contact_ref;
212 GtkTreePath *group_path, *contact_path;
213 OFMapTable *contactRows =
214 [contactMap objectForKey: [item.JID bareJID]];
216 group_ref = [groupMap valueForKey: group];
219 // Create new group row
220 gtk_tree_store_append(roster_model, &group_iter, NULL);
221 gtk_tree_store_set(roster_model, &group_iter,
222 0, [group UTF8String], -1);
224 group_path = gtk_tree_model_get_path(GTK_TREE_MODEL(
225 roster_model), &group_iter);
227 group_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(
228 roster_model), group_path);
230 [groupMap setValue: group_ref
233 // Get iter for existing group row
234 group_path = gtk_tree_row_reference_get_path(group_ref);
236 gtk_tree_model_get_iter(GTK_TREE_MODEL(roster_model),
237 &group_iter, group_path);
239 gtk_tree_path_free(group_path);
241 // Create new contact row
242 gtk_tree_store_append(roster_model, &contact_iter, &group_iter);
244 gtk_tree_store_set(roster_model, &contact_iter,
245 0, [item.name UTF8String],
246 1, [[item.JID bareJID] UTF8String], -1);
248 gtk_tree_store_set(roster_model, &contact_iter,
249 0, [item.JID.node UTF8String],
250 1, [[item.JID bareJID] UTF8String], -1);
252 contact_path = gtk_tree_model_get_path(GTK_TREE_MODEL(
253 roster_model), &contact_iter);
255 contact_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(
256 roster_model), contact_path);
258 gtk_tree_path_free(contact_path);
260 [contactRows setValue: contact_ref
265 - (void)Jub_removeRosterItem: (XMPPRosterItem*)item
266 group: (OFString*)group
269 GtkTreeIter contact_iter, group_iter;
270 GtkTreePath *contact_path, *group_path;
271 GtkTreeRowReference *contact_ref, *group_ref;
272 OFMapTable *contactRows =
273 [contactMap objectForKey: [item.JID bareJID]];
275 contact_ref = [contactRows valueForKey: group];
276 contact_path = gtk_tree_row_reference_get_path(contact_ref);
277 gtk_tree_model_get_iter(GTK_TREE_MODEL(roster_model),
278 &contact_iter, contact_path);
280 gtk_tree_store_remove(roster_model, &contact_iter);
282 group_ref = [groupMap valueForKey: group];
283 group_path = gtk_tree_row_reference_get_path(group_ref);
284 gtk_tree_model_get_iter(GTK_TREE_MODEL(roster_model),
285 &group_iter, group_path);
287 if (!gtk_tree_model_iter_has_child(GTK_TREE_MODEL(roster_model),
289 gtk_tree_store_remove(roster_model, &group_iter);
290 [groupMap removeValueForKey: group];
293 gtk_tree_path_free(group_path);
297 - (void)rosterWasReceived: (XMPPRoster*)roster_
299 of_log(@"Handling roster");
300 [[roster_ rosterItems] enumerateKeysAndObjectsUsingBlock:
301 ^(OFString *bareJID, XMPPRosterItem *item, BOOL *stop) {
303 OFMapTable *contactRows = [OFMapTable
304 mapTableWithKeyFunctions: keyFunctions
305 valueFunctions: rowRefFunctions];
307 [contactMap setObject: contactRows
310 if (item.groups != nil)
311 groups = item.groups;
313 groups = @[@"General"];
315 for (OFString *group in groups)
316 [self Jub_addRosterItem: item
321 - (void)roster: (XMPPRoster*)roster_
322 didReceiveRosterItem: (XMPPRosterItem*)item
325 XMPPRosterItem *oldItem =
326 [roster_.rosterItems objectForKey: [item.JID bareJID]];
329 if (oldItem.groups != nil)
330 groups = oldItem.groups;
332 groups = @[@"General"];
334 for (OFString *group in groups)
335 [self Jub_removeRosterItem: oldItem
338 [contactMap removeObjectForKey: [item.JID bareJID]];
341 if (![item.subscription isEqual: @"remove"]) {
342 OFMapTable *contactRows = [OFMapTable
343 mapTableWithKeyFunctions: keyFunctions
344 valueFunctions: rowRefFunctions];
346 [contactMap setObject: contactRows
347 forKey: [item.JID bareJID]];
349 if (item.groups != nil)
350 groups = item.groups;
352 groups = @[@"General"];
354 for (OFString *group in groups)
355 [self Jub_addRosterItem: item