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