]> cgit.babelmonkeys.de Git - jubjub.git/blob - src/gui/gtk/JubGtkRosterUI.m
06ffd28b4edf8523c919bb01d20f6c4a6c5fbcc8
[jubjub.git] / src / gui / gtk / JubGtkRosterUI.m
1 #import "JubGtkRosterUI.h"
2 #import "JubGObjectMap.h"
3 #import "JubGtkChatUI.h"
4
5 static gboolean filter_roster_by_presence(GtkTreeModel *model,
6     GtkTreeIter *iter, gpointer data)
7 {
8         char *jid_s;
9         OFString *jid;
10         OFCountedSet *presences = data;
11
12         gtk_tree_model_get(model, iter, 1, &jid_s, -1);
13
14         // Groups have no JID
15         if (!jid_s)
16                 return TRUE;
17
18         jid = [[OFString alloc] initWithUTF8String: jid_s];
19
20         g_free(jid_s);
21
22         int num = [presences countForObject: jid];
23         if (num) {
24                 [jid release];
25                 return TRUE;
26         } else {
27                 [jid release];
28                 return FALSE;
29         }
30 }
31
32 @implementation JubGtkRosterUI
33 - initWithBuilder: (GtkBuilder*)builder_
34 {
35         self = [super init];
36
37         @try {
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];
44
45                 builder = g_object_ref(builder_);
46
47                 roster_model = GTK_TREE_STORE(gtk_builder_get_object(builder,
48                     "RosterTreeStore"));
49
50                 roster_filter = GTK_TREE_MODEL_FILTER(
51                     gtk_builder_get_object(builder, "RosterTreeModelFilter"));
52
53                 gtk_tree_model_filter_set_visible_func(roster_filter,
54                     filter_roster_by_presence, presences, NULL);
55         } @catch (id e) {
56                 [self release];
57                 @throw e;
58         }
59
60         return self;
61 }
62
63 - (void)dealloc
64 {
65         [groupMap release];
66         [contactMap release];
67         [presences release];
68
69         if (roster_model)
70                 g_object_unref(roster_model);
71
72         if (roster_filter)
73                 g_object_unref(roster_filter);
74
75         g_object_unref(builder);
76
77         [super dealloc];
78 }
79
80 /* Presence handling */
81 static gboolean refilter_roster(gpointer data)
82 {
83         gtk_tree_model_filter_refilter(data);
84
85         return FALSE;
86 }
87
88 -   (void)connection: (XMPPConnection*)connection
89   didReceivePresence: (XMPPPresence*)presence
90 {
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]];
95
96         g_idle_add(refilter_roster, roster_filter);
97 }
98
99 // FIXME: This needs to move somewhere else
100 -  (void)connection: (XMPPConnection*)connection
101   didReceiveMessage: (XMPPMessage*)message
102 {
103         JubGtkChatUI *chat =
104             [chatMap objectForKey: [message.from bareJID]];
105         if (chat == nil) {
106                 OFString * title = [@"Chat with " stringByAppendingString:
107                     [message.from bareJID]];
108
109                 chat = [[[JubGtkChatUI alloc]
110                     initWithTitle: title
111                        closeBlock: ^{
112                                 [chatMap removeObjectForKey:
113                                     [message.from bareJID]];
114                         }
115                         sendBlock: ^(OFString *text) {
116                                 XMPPMessage *msg =
117                                     [XMPPMessage messageWithType: @"chat"];
118                                 msg.to = message.from;
119                                 msg.body = text;
120                                 [connection sendStanza: msg];
121                         }
122                 ] autorelease];
123
124                 [chatMap setObject: chat
125                             forKey: [message.from bareJID]];
126         }
127
128         [chat addMessage: message.body
129                   sender: [message.from bareJID]];
130 }
131
132 /* Roster Delegate methods */
133 struct add_roster_item_param {
134         OFString *group;
135         OFString *name;
136         XMPPJID *jid;
137         OFMapTable *groupMap;
138         OFMapTable *contactRows;
139         GtkTreeStore *roster_model;
140 };
141
142 static gboolean add_roster_item(gpointer user_data)
143 {
144         struct add_roster_item_param *params = user_data;
145         GtkTreeIter group_iter, contact_iter;
146         GtkTreeRowReference *group_ref, *contact_ref;
147         GtkTreePath *group_path, *contact_path;
148
149         group_ref = [params->groupMap valueForKey: params->group];
150
151         if (!group_ref) {
152                 // Create new group row
153                 gtk_tree_store_append(params->roster_model, &group_iter, NULL);
154                 gtk_tree_store_set(params->roster_model, &group_iter,
155                     0, [params->group UTF8String], -1);
156
157                 group_path = gtk_tree_model_get_path(GTK_TREE_MODEL(
158                         params->roster_model), &group_iter);
159
160                 group_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(
161                         params->roster_model), group_path);
162
163                 [params->groupMap setValue: group_ref
164                                     forKey: params->group];
165         } else {
166                 // Get iter for existing group row
167                 group_path = gtk_tree_row_reference_get_path(group_ref);
168
169                 gtk_tree_model_get_iter(GTK_TREE_MODEL(params->roster_model),
170                     &group_iter, group_path);
171         }
172         gtk_tree_path_free(group_path);
173
174         // Create new contact row
175         gtk_tree_store_append(params->roster_model, &contact_iter, &group_iter);
176         if (params->name)
177                 gtk_tree_store_set(params->roster_model, &contact_iter,
178                     0, [params->name UTF8String],
179                     1, [[params->jid bareJID] UTF8String], -1);
180         else
181                 gtk_tree_store_set(params->roster_model, &contact_iter,
182                     0, [params->jid.node UTF8String],
183                     1, [[params->jid bareJID] UTF8String], -1);
184
185         contact_path = gtk_tree_model_get_path(GTK_TREE_MODEL(
186                 params->roster_model), &contact_iter);
187
188         contact_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(
189                 params->roster_model), contact_path);
190
191         gtk_tree_path_free(contact_path);
192
193         [params->contactRows setValue: contact_ref
194                                forKey: params->group];
195
196         [params->group release];
197         [params->name release];
198         [params->jid release];
199         [params->groupMap release];
200         [params->contactRows release];
201         g_object_unref(params->roster_model);
202         free(params);
203
204         return FALSE;
205 }
206
207 struct remove_roster_item_param {
208         OFString *group;
209         OFMapTable *groupMap;
210         OFMapTable *contactRows;
211         GtkTreeStore *roster_model;
212 };
213
214 static gboolean remove_roster_item(gpointer user_data)
215 {
216         struct remove_roster_item_param *params = user_data;
217         GtkTreeIter contact_iter, group_iter;
218         GtkTreePath *contact_path, *group_path;
219         GtkTreeRowReference *contact_ref, *group_ref;
220
221         contact_ref = [params->contactRows valueForKey: params->group];
222         contact_path = gtk_tree_row_reference_get_path(contact_ref);
223         gtk_tree_model_get_iter(GTK_TREE_MODEL(params->roster_model),
224             &contact_iter, contact_path);
225
226         gtk_tree_store_remove(params->roster_model, &contact_iter);
227
228         group_ref = [params->groupMap valueForKey: params->group];
229         group_path = gtk_tree_row_reference_get_path(group_ref);
230         gtk_tree_model_get_iter(GTK_TREE_MODEL(params->roster_model),
231             &group_iter, group_path);
232
233         if (!gtk_tree_model_iter_has_child(GTK_TREE_MODEL(params->roster_model),
234             &group_iter)) {
235                 gtk_tree_store_remove(params->roster_model, &group_iter);
236                 [params->groupMap removeValueForKey: params->group];
237         }
238
239         gtk_tree_path_free(group_path);
240
241         [params->group release];
242         [params->groupMap release];
243         [params->contactRows release];
244         g_object_unref(params->roster_model);
245         free(params);
246
247         return FALSE;
248 }
249
250 - (void)rosterWasReceived: (XMPPRoster*)roster_
251 {
252         of_log(@"Handling roster");
253         [[roster_ rosterItems] enumerateKeysAndObjectsUsingBlock:
254             ^(OFString *bareJID, XMPPRosterItem *item, BOOL *stop) {
255                 OFArray *groups;
256                 OFMapTable *contactRows = [OFMapTable
257                     mapTableWithKeyFunctions: keyFunctions
258                               valueFunctions: rowRefFunctions];
259
260                 [contactMap setObject: contactRows
261                                forKey: bareJID];
262
263                 if (item.groups != nil)
264                         groups = item.groups;
265                 else
266                         groups = @[@"General"];
267
268                 for (OFString *group in groups) {
269                         struct add_roster_item_param *params =
270                             malloc(sizeof(*params));
271                         params->group = [group retain];
272                         params->name = [item.name retain];
273                         params->jid = [item.JID retain];
274                         params->groupMap = [groupMap retain];
275                         params->contactRows = [contactRows retain];
276                         params->roster_model = g_object_ref(roster_model);
277                         g_idle_add(add_roster_item, params);
278                 }
279         }];
280 }
281
282 -         (void)roster: (XMPPRoster*)roster_
283   didReceiveRosterItem: (XMPPRosterItem*)item
284 {
285         OFArray *groups;
286         XMPPRosterItem *oldItem =
287             [roster_.rosterItems objectForKey: [item.JID bareJID]];
288
289         if (oldItem) {
290                 if (oldItem.groups != nil)
291                         groups = oldItem.groups;
292                 else
293                         groups = @[@"General"];
294
295                 for (OFString *group in groups) {
296                         struct remove_roster_item_param *params =
297                             malloc(sizeof(*params));
298                         params->group = [group retain];
299                         params->contactRows = [[contactMap objectForKey:
300                             [oldItem.JID bareJID]] retain];
301                         params->groupMap = [groupMap retain];
302                         params->roster_model = g_object_ref(roster_model);
303                         g_idle_add(remove_roster_item, params);
304                 }
305
306                 [contactMap removeObjectForKey: [item.JID bareJID]];
307         }
308
309         if (![item.subscription isEqual: @"remove"]) {
310                 OFMapTable *contactRows = [OFMapTable
311                     mapTableWithKeyFunctions: keyFunctions
312                               valueFunctions: rowRefFunctions];
313
314                 [contactMap setObject: contactRows
315                                forKey: [item.JID bareJID]];
316
317                 if (item.groups != nil)
318                         groups = item.groups;
319                 else
320                         groups = @[@"General"];
321
322                 for (OFString *group in groups) {
323                         struct add_roster_item_param *params =
324                             malloc(sizeof(*params));
325                         params->group = [group retain];
326                         params->name = [item.name retain];
327                         params->jid = [item.JID retain];
328                         params->groupMap = [groupMap retain];
329                         params->contactRows = [contactRows retain];
330                         params->roster_model = g_object_ref(roster_model);
331                         g_idle_add(add_roster_item, params);
332                 }
333         }
334 }
335
336 @end