1 #import "JubGtkRosterUI.h"
2 #import "JubGObjectMap.h"
3 #import "JubGtkChatUI.h"
5 static gboolean filter_roster_by_presence(GtkTreeModel *model,
6 GtkTreeIter *iter, gpointer data)
10 OFCountedSet *presences = data;
12 gtk_tree_model_get(model, iter, 1, &jid_s, -1);
18 jid = [[OFString alloc] initWithUTF8String: jid_s];
22 int num = [presences countForObject: jid];
32 @implementation JubGtkRosterUI
33 - initWithBuilder: (GtkBuilder*)builder_
38 groupMap = [[OFMapTable alloc]
39 initWithKeyFunctions: keyFunctions
40 valueFunctions: rowRefFunctions];
41 contactMap = [[OFMutableDictionary alloc] init];
42 chatMap = [[OFMutableDictionary alloc] init];
43 presences = [[OFCountedSet alloc] init];
45 builder = g_object_ref(builder_);
47 roster_model = GTK_TREE_STORE(gtk_builder_get_object(builder,
50 roster_filter = GTK_TREE_MODEL_FILTER(
51 gtk_builder_get_object(builder, "RosterTreeModelFilter"));
53 gtk_tree_model_filter_set_visible_func(roster_filter,
54 filter_roster_by_presence, presences, NULL);
70 g_object_unref(roster_model);
73 g_object_unref(roster_filter);
75 g_object_unref(builder);
80 /* Presence handling */
81 static gboolean refilter_roster(gpointer data)
83 gtk_tree_model_filter_refilter(data);
88 - (void)connection: (XMPPConnection*)connection
89 didReceivePresence: (XMPPPresence*)presence
91 if ([presence.type isEqual: @"available"])
92 [presences addObject: [presence.from bareJID]];
93 else if ([presence.type isEqual: @"unavailable"])
94 [presences removeObject: [presence.from bareJID]];
96 g_idle_add(refilter_roster, roster_filter);
99 // FIXME: This needs to move somewhere else
100 - (void)connection: (XMPPConnection*)connection
101 didReceiveMessage: (XMPPMessage*)message
103 JubGtkChatUI *chat = [chatMap objectForKey: [message.from bareJID]];
105 OFString * title = [@"Chat with " stringByAppendingString:
106 [message.from bareJID]];
107 chat = [JubGtkChatUI alloc];
108 [[chat initWithTitle: title
109 sendBlock: ^(OFString *text) {
111 [XMPPMessage messageWithType: @"chat"];
112 msg.to = message.from;
114 [connection sendStanza: msg];
116 [chat addMessage: msg.body
117 sender: [message.to bareJID]];
120 [chatMap setObject: chat
121 forKey: [message.from bareJID]];
123 [chat addMessage: message.body
124 sender: [message.from bareJID]];
127 /* Roster Delegate methods */
128 struct add_roster_item_param {
132 OFMapTable *groupMap;
133 OFMapTable *contactRows;
134 GtkTreeStore *roster_model;
137 static gboolean add_roster_item(gpointer user_data)
139 struct add_roster_item_param *params = user_data;
140 GtkTreeIter group_iter, contact_iter;
141 GtkTreeRowReference *group_ref, *contact_ref;
142 GtkTreePath *group_path, *contact_path;
144 group_ref = [params->groupMap valueForKey: params->group];
147 // Create new group row
148 gtk_tree_store_append(params->roster_model, &group_iter, NULL);
149 gtk_tree_store_set(params->roster_model, &group_iter,
150 0, [params->group UTF8String], -1);
152 group_path = gtk_tree_model_get_path(GTK_TREE_MODEL(
153 params->roster_model), &group_iter);
155 group_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(
156 params->roster_model), group_path);
158 [params->groupMap setValue: group_ref
159 forKey: params->group];
161 // Get iter for existing group row
162 group_path = gtk_tree_row_reference_get_path(group_ref);
164 gtk_tree_model_get_iter(GTK_TREE_MODEL(params->roster_model),
165 &group_iter, group_path);
167 gtk_tree_path_free(group_path);
169 // Create new contact row
170 gtk_tree_store_append(params->roster_model, &contact_iter, &group_iter);
171 gtk_tree_store_set(params->roster_model, &contact_iter,
172 0, [params->name UTF8String], 1, [params->jid UTF8String], -1);
174 contact_path = gtk_tree_model_get_path(GTK_TREE_MODEL(
175 params->roster_model), &contact_iter);
177 contact_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(
178 params->roster_model), contact_path);
180 gtk_tree_path_free(contact_path);
182 [params->contactRows setValue: contact_ref
183 forKey: params->group];
185 [params->group release];
186 [params->name release];
187 [params->jid release];
188 [params->groupMap release];
189 [params->contactRows release];
190 g_object_unref(params->roster_model);
196 struct remove_roster_item_param {
198 OFMapTable *groupMap;
199 OFMapTable *contactRows;
200 GtkTreeStore *roster_model;
203 static gboolean remove_roster_item(gpointer user_data)
205 struct remove_roster_item_param *params = user_data;
206 GtkTreeIter contact_iter, group_iter;
207 GtkTreePath *contact_path, *group_path;
208 GtkTreeRowReference *contact_ref, *group_ref;
210 contact_ref = [params->contactRows valueForKey: params->group];
211 contact_path = gtk_tree_row_reference_get_path(contact_ref);
212 gtk_tree_model_get_iter(GTK_TREE_MODEL(params->roster_model),
213 &contact_iter, contact_path);
215 gtk_tree_store_remove(params->roster_model, &contact_iter);
217 group_ref = [params->groupMap valueForKey: params->group];
218 group_path = gtk_tree_row_reference_get_path(group_ref);
219 gtk_tree_model_get_iter(GTK_TREE_MODEL(params->roster_model),
220 &group_iter, group_path);
222 if (!gtk_tree_model_iter_has_child(GTK_TREE_MODEL(params->roster_model),
224 gtk_tree_store_remove(params->roster_model, &group_iter);
225 [params->groupMap removeValueForKey: params->group];
228 gtk_tree_path_free(group_path);
230 [params->group release];
231 [params->groupMap release];
232 [params->contactRows release];
233 g_object_unref(params->roster_model);
239 - (void)rosterWasReceived: (XMPPRoster*)roster_
241 of_log(@"Handling roster");
242 [[roster_ rosterItems] enumerateKeysAndObjectsUsingBlock:
243 ^(OFString *bareJID, XMPPRosterItem *item, BOOL *stop) {
245 OFMapTable *contactRows = [OFMapTable
246 mapTableWithKeyFunctions: keyFunctions
247 valueFunctions: rowRefFunctions];
249 [contactMap setObject: contactRows
252 if (item.groups != nil)
253 groups = item.groups;
255 groups = @[@"General"];
257 for (OFString *group in groups) {
258 struct add_roster_item_param *params =
259 malloc(sizeof(*params));
260 params->group = [group retain];
261 params->name = [item.name retain];
262 params->jid = [bareJID retain];
263 params->groupMap = [groupMap retain];
264 params->contactRows = [contactRows retain];
265 params->roster_model = g_object_ref(roster_model);
266 g_idle_add(add_roster_item, params);
271 - (void)roster: (XMPPRoster*)roster_
272 didReceiveRosterItem: (XMPPRosterItem*)item
275 XMPPRosterItem *oldItem =
276 [roster_.rosterItems objectForKey: [item.JID bareJID]];
279 if (oldItem.groups != nil)
280 groups = oldItem.groups;
282 groups = @[@"General"];
284 for (OFString *group in groups) {
285 struct remove_roster_item_param *params =
286 malloc(sizeof(*params));
287 params->group = [group retain];
288 params->contactRows = [[contactMap objectForKey:
289 [oldItem.JID bareJID]] retain];
290 params->groupMap = [groupMap retain];
291 params->roster_model = g_object_ref(roster_model);
292 g_idle_add(remove_roster_item, params);
295 [contactMap removeObjectForKey: [item.JID bareJID]];
298 if (![item.subscription isEqual: @"remove"]) {
299 OFMapTable *contactRows = [OFMapTable
300 mapTableWithKeyFunctions: keyFunctions
301 valueFunctions: rowRefFunctions];
303 [contactMap setObject: contactRows
304 forKey: [item.JID bareJID]];
306 if (item.groups != nil)
307 groups = item.groups;
309 groups = @[@"General"];
311 for (OFString *group in groups) {
312 struct add_roster_item_param *params =
313 malloc(sizeof(*params));
314 params->group = [group retain];
315 params->name = [item.name retain];
316 params->jid = [[item.JID bareJID] retain];
317 params->groupMap = [groupMap retain];
318 params->contactRows = [contactRows retain];
319 params->roster_model = g_object_ref(roster_model);
320 g_idle_add(add_roster_item, params);