]> cgit.babelmonkeys.de Git - jubjub.git/blob - src/gui/gtk/JubGtkUI.m
Initial commit
[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 @implementation JubGtkUI
13 - init
14 {
15         self = [super init];
16
17         @try {
18                 groupMap = [[OFMapTable alloc]
19                     initWithKeyFunctions: keyFunctions
20                           valueFunctions: rowRefFunctions];
21                 contactMap = [[OFMutableDictionary alloc] init];
22         } @catch (id e) {
23                 [self release];
24                 @throw e;
25         }
26
27         return self;
28 }
29
30 - (void)dealloc
31 {
32
33         [groupMap release];
34         [contactMap release];
35
36         if (roster_model)
37                 g_object_unref(roster_model);
38
39         [super dealloc];
40 }
41
42 - (void)startUIThread
43 {
44         int *argc;
45         char ***argv;
46
47         GtkBuilder *builder;
48         GtkWidget *roster_window;
49
50         [[OFApplication sharedApplication] getArgumentCount: &argc
51                                           andArgumentValues: &argv];
52
53         gtk_init(argc, argv);
54
55         builder = gtk_builder_new();
56         gtk_builder_add_from_file(builder, "data/gtk/roster.ui", NULL);
57
58         roster_window =
59             GTK_WIDGET(gtk_builder_get_object(builder, "RosterWindow"));
60
61         roster_model =
62             GTK_TREE_STORE(gtk_builder_get_object(builder, "RosterTreeStore"));
63
64         gtk_builder_connect_signals(builder, NULL);
65         g_object_unref(G_OBJECT(builder));
66
67         gtk_widget_show(roster_window);
68
69         [[OFThread threadWithBlock: ^(void){
70                 gtk_main();
71                 [OFApplication terminate];
72
73                 return nil;
74         }] start];
75 }
76
77
78 - (id<XMPPRosterDelegate>)rosterDelegate
79 {
80         return self;
81 }
82
83 /* Roster Delegate methods */
84 struct add_roster_item_param {
85         OFString *group;
86         OFString *name;
87         OFString *jid;
88         OFMapTable *groupMap;
89         OFMapTable *contactRows;
90         GtkTreeStore *roster_model;
91 };
92
93 static gboolean add_roster_item(gpointer user_data)
94 {
95         struct add_roster_item_param *params = user_data;
96         GtkTreeIter group_iter, contact_iter;
97         GtkTreeRowReference *group_ref, *contact_ref;
98         GtkTreePath *group_path, *contact_path;
99
100         group_ref = [params->groupMap valueForKey: params->group];
101
102         if (!group_ref) {
103                 // Create new group row
104                 gtk_tree_store_append(params->roster_model, &group_iter, NULL);
105                 gtk_tree_store_set(params->roster_model, &group_iter,
106                     0, [params->group UTF8String], -1);
107
108                 group_path = gtk_tree_model_get_path(GTK_TREE_MODEL(
109                         params->roster_model), &group_iter);
110
111                 group_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(
112                         params->roster_model), group_path);
113
114                 [params->groupMap setValue: group_ref
115                                     forKey: params->group];
116         } else {
117                 // Get iter for existing group row
118                 group_path = gtk_tree_row_reference_get_path(group_ref);
119
120                 gtk_tree_model_get_iter(GTK_TREE_MODEL(params->roster_model),
121                     &group_iter, group_path);
122         }
123         gtk_tree_path_free(group_path);
124
125         // Create new contact row
126         gtk_tree_store_append(params->roster_model, &contact_iter, &group_iter);
127         gtk_tree_store_set(params->roster_model, &contact_iter,
128             0, [params->name UTF8String], 1, [params->jid UTF8String], -1);
129
130         contact_path = gtk_tree_model_get_path(GTK_TREE_MODEL(
131                 params->roster_model), &contact_iter);
132
133         contact_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(
134                 params->roster_model), contact_path);
135
136         gtk_tree_path_free(contact_path);
137
138         [params->contactRows setValue: contact_ref
139                                forKey: params->group];
140
141         [params->group release];
142         [params->name release];
143         [params->jid release];
144         [params->groupMap release];
145         [params->contactRows release];
146         g_object_unref(params->roster_model);
147         free(params);
148
149         return FALSE;
150 }
151
152 - (void)rosterWasReceived: (XMPPRoster*)roster_
153 {
154         [[roster_ rosterItems] enumerateKeysAndObjectsUsingBlock:
155             ^(OFString *bareJID, XMPPRosterItem *item, BOOL *stop) {
156                 OFArray *groups;
157                 OFMapTable *contactRows = [OFMapTable
158                     mapTableWithKeyFunctions: keyFunctions
159                               valueFunctions: rowRefFunctions];
160
161                 [contactMap setObject: contactRows
162                                forKey: bareJID];
163
164                 if (item.groups != nil)
165                         groups = item.groups;
166                 else
167                         groups = @[@"General"];
168
169                 for (OFString *group in groups) {
170                         struct add_roster_item_param *params =
171                             malloc(sizeof(*params));
172                         params->group = [group retain];
173                         params->name = [item.name retain];
174                         params->jid = [bareJID retain];
175                         params->groupMap = [groupMap retain];
176                         params->contactRows = [contactRows retain];
177                         params->roster_model = g_object_ref(roster_model);
178                         g_idle_add(add_roster_item, params);
179                 }
180         }];
181 }
182
183 struct remove_roster_item_param {
184         OFString *group;
185         OFMapTable *groupMap;
186         OFMapTable *contactRows;
187         GtkTreeStore *roster_model;
188 };
189
190 static gboolean remove_roster_item(gpointer user_data)
191 {
192         struct remove_roster_item_param *params = user_data;
193         GtkTreeIter contact_iter, group_iter;
194         GtkTreePath *contact_path, *group_path;
195         GtkTreeRowReference *contact_ref, *group_ref;
196
197         contact_ref = [params->contactRows valueForKey: params->group];
198         contact_path = gtk_tree_row_reference_get_path(contact_ref);
199         gtk_tree_model_get_iter(GTK_TREE_MODEL(params->roster_model),
200             &contact_iter, contact_path);
201
202         gtk_tree_store_remove(params->roster_model, &contact_iter);
203
204         group_ref = [params->groupMap valueForKey: params->group];
205         group_path = gtk_tree_row_reference_get_path(group_ref);
206         gtk_tree_model_get_iter(GTK_TREE_MODEL(params->roster_model),
207             &group_iter, group_path);
208
209         if (!gtk_tree_model_iter_has_child(GTK_TREE_MODEL(params->roster_model),
210             &group_iter)) {
211                 gtk_tree_store_remove(params->roster_model, &group_iter);
212                 [params->groupMap removeValueForKey: params->group];
213         }
214
215         gtk_tree_path_free(group_path);
216
217         [params->group release];
218         [params->groupMap release];
219         [params->contactRows release];
220         g_object_unref(params->roster_model);
221         free(params);
222
223         return FALSE;
224 }
225
226 -         (void)roster: (XMPPRoster*)roster_
227   didReceiveRosterItem: (XMPPRosterItem*)item
228 {
229         OFArray *groups;
230         XMPPRosterItem *oldItem =
231             [roster_.rosterItems objectForKey: [item.JID bareJID]];
232
233         if (oldItem) {
234                 if (oldItem.groups != nil)
235                         groups = oldItem.groups;
236                 else
237                         groups = @[@"General"];
238
239                 for (OFString *group in groups) {
240                         struct remove_roster_item_param *params =
241                             malloc(sizeof(*params));
242                         params->group = [group retain];
243                         params->contactRows = [[contactMap objectForKey:
244                             [oldItem.JID bareJID]] retain];
245                         params->groupMap = [groupMap retain];
246                         params->roster_model = g_object_ref(roster_model);
247                         g_idle_add(remove_roster_item, params);
248                 }
249
250                 [contactMap removeObjectForKey: [item.JID bareJID]];
251         }
252
253         if (![item.subscription isEqual: @"remove"]) {
254                 OFMapTable *contactRows = [OFMapTable
255                     mapTableWithKeyFunctions: keyFunctions
256                               valueFunctions: rowRefFunctions];
257
258                 [contactMap setObject: contactRows
259                                forKey: [item.JID bareJID]];
260
261                 if (item.groups != nil)
262                         groups = item.groups;
263                 else
264                         groups = @[@"General"];
265
266                 for (OFString *group in groups) {
267                         struct add_roster_item_param *params =
268                             malloc(sizeof(*params));
269                         params->group = [group retain];
270                         params->name = [item.name retain];
271                         params->jid = [[item.JID bareJID] retain];
272                         params->groupMap = [groupMap retain];
273                         params->contactRows = [contactRows retain];
274                         params->roster_model = g_object_ref(roster_model);
275                         g_idle_add(add_roster_item, params);
276                 }
277         }
278 }
279 @end