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