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