1 #import "JubGtkRosterUI.h"
2 #import "JubGObjectMap.h"
3 #import "JubGtkChatUI.h"
4 #import "JubGtkHelper.h"
6 static gboolean roster_row_activated(GtkTreeView *tree_view, GtkTreePath *path,
7 GtkTreeViewColumn *column, gpointer data)
9 JubGtkRosterUI *roster = data;
11 GtkTreeModel *tree_model;
14 OFAutoreleasePool *pool;
16 tree_model = gtk_tree_view_get_model(tree_view);
17 gtk_tree_model_get_iter(tree_model, &row_iter, path);
18 gtk_tree_model_get(tree_model, &row_iter, 1, &jid_s, -1);
20 // This was a group row
21 if (!jid_s) return TRUE;
23 pool = [OFAutoreleasePool new];
24 jid = [XMPPJID JIDWithString: [OFString stringWithUTF8String: jid_s]];
26 [roster performSelectorOnMainThread: @selector(chatForJID:)
34 static gboolean filter_roster_by_presence(GtkTreeModel *model,
35 GtkTreeIter *iter, gpointer data)
39 OFCountedSet *presences = data;
41 gtk_tree_model_get(model, iter, 1, &jid_s, -1);
47 jid = [[OFString alloc] initWithUTF8String: jid_s];
51 int num = [presences countForObject: jid];
61 @implementation JubGtkRosterUI
62 - initWithBuilder: (GtkBuilder*)builder_
63 connection: (XMPPConnection*)connection_
68 GtkTreeView *roster_view;
70 groupMap = [[OFMapTable alloc]
71 initWithKeyFunctions: keyFunctions
72 valueFunctions: rowRefFunctions];
73 contactMap = [[OFMutableDictionary alloc] init];
74 chatMap = [[OFMutableDictionary alloc] init];
75 presences = [[OFCountedSet alloc] init];
76 connection = [connection_ retain];
78 builder = g_object_ref(builder_);
80 roster_model = GTK_TREE_STORE(gtk_builder_get_object(builder,
83 roster_filter = GTK_TREE_MODEL_FILTER(
84 gtk_builder_get_object(builder, "RosterTreeModelFilter"));
86 gtk_tree_model_filter_set_visible_func(roster_filter,
87 filter_roster_by_presence, presences, NULL);
89 roster_view = GTK_TREE_VIEW(gtk_builder_get_object(builder,
92 g_signal_connect(roster_view, "row_activated",
93 G_CALLBACK(roster_row_activated), self);
105 [contactMap release];
107 [connection release];
110 g_object_unref(roster_model);
113 g_object_unref(roster_filter);
115 g_object_unref(builder);
120 /* Presence handling */
121 - (void)connection: (XMPPConnection*)connection
122 didReceivePresence: (XMPPPresence*)presence
124 if ([presence.type isEqual: @"available"])
125 [presences addObject: [presence.from bareJID]];
126 else if ([presence.type isEqual: @"unavailable"])
127 [presences removeObject: [presence.from bareJID]];
130 gtk_tree_model_filter_refilter(roster_filter);
134 // FIXME: This needs to move somewhere else
135 - (JubGtkChatUI*)chatForJID: (XMPPJID*)jid
137 OFAutoreleasePool *pool = [OFAutoreleasePool new];
139 [chatMap objectForKey: [jid bareJID]];
141 OFString * title = [@"Chat with " stringByAppendingString:
144 chat = [[[JubGtkChatUI alloc]
147 [chatMap removeObjectForKey: [jid bareJID]];
149 sendBlock: ^(OFString *text) {
151 [XMPPMessage messageWithType: @"chat"];
154 [connection sendStanza: msg];
158 [chatMap setObject: chat
159 forKey: [jid bareJID]];
167 - (void)connection: (XMPPConnection*)connection
168 didReceiveMessage: (XMPPMessage*)message
170 JubGtkChatUI *chat = [self chatForJID: message.from];
171 [chat addMessage: message.body
172 sender: [message.from bareJID]];
175 /* Roster Delegate methods */
176 - (void)Jub_addRosterItem: (XMPPRosterItem*)item
177 group: (OFString*)group
180 GtkTreeIter group_iter, contact_iter;
181 GtkTreeRowReference *group_ref, *contact_ref;
182 GtkTreePath *group_path, *contact_path;
183 OFMapTable *contactRows =
184 [contactMap objectForKey: [item.JID bareJID]];
186 group_ref = [groupMap valueForKey: group];
189 // Create new group row
190 gtk_tree_store_append(roster_model, &group_iter, NULL);
191 gtk_tree_store_set(roster_model, &group_iter,
192 0, [group UTF8String], -1);
194 group_path = gtk_tree_model_get_path(GTK_TREE_MODEL(
195 roster_model), &group_iter);
197 group_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(
198 roster_model), group_path);
200 [groupMap setValue: group_ref
203 // Get iter for existing group row
204 group_path = gtk_tree_row_reference_get_path(group_ref);
206 gtk_tree_model_get_iter(GTK_TREE_MODEL(roster_model),
207 &group_iter, group_path);
209 gtk_tree_path_free(group_path);
211 // Create new contact row
212 gtk_tree_store_append(roster_model, &contact_iter, &group_iter);
214 gtk_tree_store_set(roster_model, &contact_iter,
215 0, [item.name UTF8String],
216 1, [[item.JID bareJID] UTF8String], -1);
218 gtk_tree_store_set(roster_model, &contact_iter,
219 0, [item.JID.node UTF8String],
220 1, [[item.JID bareJID] UTF8String], -1);
222 contact_path = gtk_tree_model_get_path(GTK_TREE_MODEL(
223 roster_model), &contact_iter);
225 contact_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(
226 roster_model), contact_path);
228 gtk_tree_path_free(contact_path);
230 [contactRows setValue: contact_ref
235 - (void)Jub_removeRosterItem: (XMPPRosterItem*)item
236 group: (OFString*)group
239 GtkTreeIter contact_iter, group_iter;
240 GtkTreePath *contact_path, *group_path;
241 GtkTreeRowReference *contact_ref, *group_ref;
242 OFMapTable *contactRows =
243 [contactMap objectForKey: [item.JID bareJID]];
245 contact_ref = [contactRows valueForKey: group];
246 contact_path = gtk_tree_row_reference_get_path(contact_ref);
247 gtk_tree_model_get_iter(GTK_TREE_MODEL(roster_model),
248 &contact_iter, contact_path);
250 gtk_tree_store_remove(roster_model, &contact_iter);
252 group_ref = [groupMap valueForKey: group];
253 group_path = gtk_tree_row_reference_get_path(group_ref);
254 gtk_tree_model_get_iter(GTK_TREE_MODEL(roster_model),
255 &group_iter, group_path);
257 if (!gtk_tree_model_iter_has_child(GTK_TREE_MODEL(roster_model),
259 gtk_tree_store_remove(roster_model, &group_iter);
260 [groupMap removeValueForKey: group];
263 gtk_tree_path_free(group_path);
267 - (void)rosterWasReceived: (XMPPRoster*)roster_
269 of_log(@"Handling roster");
270 [[roster_ rosterItems] enumerateKeysAndObjectsUsingBlock:
271 ^(OFString *bareJID, XMPPRosterItem *item, BOOL *stop) {
273 OFMapTable *contactRows = [OFMapTable
274 mapTableWithKeyFunctions: keyFunctions
275 valueFunctions: rowRefFunctions];
277 [contactMap setObject: contactRows
280 if (item.groups != nil)
281 groups = item.groups;
283 groups = @[@"General"];
285 for (OFString *group in groups)
286 [self Jub_addRosterItem: item
291 - (void)roster: (XMPPRoster*)roster_
292 didReceiveRosterItem: (XMPPRosterItem*)item
295 XMPPRosterItem *oldItem =
296 [roster_.rosterItems objectForKey: [item.JID bareJID]];
299 if (oldItem.groups != nil)
300 groups = oldItem.groups;
302 groups = @[@"General"];
304 for (OFString *group in groups)
305 [self Jub_removeRosterItem: oldItem
308 [contactMap removeObjectForKey: [item.JID bareJID]];
311 if (![item.subscription isEqual: @"remove"]) {
312 OFMapTable *contactRows = [OFMapTable
313 mapTableWithKeyFunctions: keyFunctions
314 valueFunctions: rowRefFunctions];
316 [contactMap setObject: contactRows
317 forKey: [item.JID bareJID]];
319 if (item.groups != nil)
320 groups = item.groups;
322 groups = @[@"General"];
324 for (OFString *group in groups)
325 [self Jub_addRosterItem: item