Mercurial > pidgin
annotate src/gtkroomlist.c @ 11849:dc51cdcb648e
[gaim-migrate @ 14140]
sf patch #1339135, from Sadrul Habib Chowdhury
Hi. Bleeter found this bug:
(1) open up some conversation window
(2) open the smiley dialog
(3) close the conversation window. the smiley dialog
remains open
(4) select some smiley
(5) Gaim crashes
This patch fixes this crash. It shouldn't cause any
probs with regards to gtk-version-checking. The other
code handling the smiley-dialog doesn't do the
version-checking, so this shouldn't be necessary either.
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Thu, 27 Oct 2005 12:00:17 +0000 |
parents | 1ae1454204cd |
children | 255e6912607b |
rev | line source |
---|---|
8113 | 1 /** |
8939 | 2 * @file gtkroomlist.c GTK+ Room List UI |
8113 | 3 * @ingroup gtkui |
4 * | |
5 * gaim | |
6 * | |
8146 | 7 * Gaim is the legal property of its developers, whose names are too numerous |
8 * to list here. Please refer to the COPYRIGHT file distributed with this | |
9 * source distribution. | |
8113 | 10 * |
11 * This program is free software; you can redistribute it and/or modify | |
12 * it under the terms of the GNU General Public License as published by | |
13 * the Free Software Foundation; either version 2 of the License, or | |
14 * (at your option) any later version. | |
15 * | |
16 * This program is distributed in the hope that it will be useful, | |
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 * GNU General Public License for more details. | |
20 * | |
21 * You should have received a copy of the GNU General Public License | |
22 * along with this program; if not, write to the Free Software | |
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
24 */ | |
25 | |
9791 | 26 #include "internal.h" |
27 #include "gtkgaim.h" | |
8113 | 28 #include "gtkutils.h" |
10299 | 29 #include "gtkstock.h" |
8113 | 30 #include "debug.h" |
31 #include "account.h" | |
32 #include "connection.h" | |
33 #include "notify.h" | |
34 | |
35 #include "gtkroomlist.h" | |
36 | |
37 typedef struct _GaimGtkRoomlist { | |
38 GaimGtkRoomlistDialog *dialog; | |
39 GtkTreeStore *model; | |
40 GtkWidget *tree; | |
41 GHashTable *cats; /**< Meow. */ | |
42 gint num_rooms, total_rooms; | |
43 } GaimGtkRoomlist; | |
44 | |
45 struct _GaimGtkRoomlistDialog { | |
46 GtkWidget *window; | |
47 GtkWidget *account_widget; | |
48 GtkWidget *progress; | |
49 GtkWidget *sw; | |
50 | |
8199 | 51 GtkWidget *stop_button; |
8113 | 52 GtkWidget *list_button; |
8199 | 53 GtkWidget *join_button; |
8113 | 54 GtkWidget *close_button; |
55 | |
56 GaimAccount *account; | |
57 GaimRoomlist *roomlist; | |
8230 | 58 |
59 gboolean pg_needs_pulse; | |
60 gboolean pg_to_active; | |
61 guint pg_update_to; | |
8113 | 62 }; |
63 | |
64 enum { | |
65 NAME_COLUMN = 0, | |
66 ROOM_COLUMN, | |
67 NUM_OF_COLUMNS, | |
68 }; | |
69 | |
70 static GList *roomlists = NULL; | |
71 | |
72 static gint delete_win_cb(GtkWidget *w, GdkEventAny *e, gpointer d) | |
73 { | |
74 GaimGtkRoomlistDialog *dialog; | |
75 | |
76 dialog = (GaimGtkRoomlistDialog *) d; | |
77 | |
8199 | 78 if (dialog->roomlist && gaim_roomlist_get_in_progress(dialog->roomlist)) |
79 gaim_roomlist_cancel_get_list(dialog->roomlist); | |
80 | |
8230 | 81 if (dialog->roomlist) { |
82 if (dialog->pg_to_active) { | |
8287
ef881489396e
[gaim-migrate @ 9011]
Christian Hammond <chipx86@chipx86.com>
parents:
8230
diff
changeset
|
83 gaim_timeout_remove(dialog->pg_update_to); |
8230 | 84 dialog->pg_to_active = FALSE; |
85 /* yes, that's right, unref it twice. */ | |
86 gaim_roomlist_unref(dialog->roomlist); | |
87 } | |
88 } | |
89 | |
8113 | 90 /* free stuff here */ |
91 if (dialog->roomlist) | |
92 gaim_roomlist_unref(dialog->roomlist); | |
93 g_free(dialog); | |
94 | |
95 return FALSE; | |
96 } | |
97 | |
98 static void dialog_select_account_cb(GObject *w, GaimAccount *account, | |
99 GaimGtkRoomlistDialog *dialog) | |
100 { | |
101 dialog->account = account; | |
102 } | |
103 | |
104 static void list_button_cb(GtkButton *button, GaimGtkRoomlistDialog *dialog) | |
105 { | |
106 GaimConnection *gc; | |
107 GaimGtkRoomlist *rl; | |
108 | |
109 gc = gaim_account_get_connection(dialog->account); | |
110 if (!gc) | |
111 return; | |
112 | |
8199 | 113 if (dialog->roomlist != NULL) { |
114 rl = dialog->roomlist->ui_data; | |
115 gtk_widget_destroy(rl->tree); | |
116 gaim_roomlist_unref(dialog->roomlist); | |
117 } | |
118 | |
8113 | 119 dialog->roomlist = gaim_roomlist_get_list(gc); |
9159 | 120 if (!dialog->roomlist) |
121 return; | |
8113 | 122 gaim_roomlist_ref(dialog->roomlist); |
123 rl = dialog->roomlist->ui_data; | |
124 rl->dialog = dialog; | |
8199 | 125 |
8113 | 126 if (dialog->account_widget) |
127 gtk_widget_set_sensitive(dialog->account_widget, FALSE); | |
8199 | 128 |
129 gtk_container_add(GTK_CONTAINER(dialog->sw), rl->tree); | |
130 | |
131 gtk_widget_set_sensitive(dialog->stop_button, TRUE); | |
8113 | 132 gtk_widget_set_sensitive(dialog->list_button, FALSE); |
8199 | 133 gtk_widget_set_sensitive(dialog->join_button, FALSE); |
8113 | 134 } |
135 | |
136 static void stop_button_cb(GtkButton *button, GaimGtkRoomlistDialog *dialog) | |
137 { | |
138 gaim_roomlist_cancel_get_list(dialog->roomlist); | |
8199 | 139 |
140 if (dialog->account_widget) | |
141 gtk_widget_set_sensitive(dialog->account_widget, TRUE); | |
142 | |
143 gtk_widget_set_sensitive(dialog->stop_button, FALSE); | |
144 gtk_widget_set_sensitive(dialog->list_button, TRUE); | |
145 gtk_widget_set_sensitive(dialog->join_button, FALSE); | |
8113 | 146 } |
147 | |
148 static void close_button_cb(GtkButton *button, GaimGtkRoomlistDialog *dialog) | |
149 { | |
150 GtkWidget *window = dialog->window; | |
151 | |
152 delete_win_cb(NULL, NULL, dialog); | |
153 gtk_widget_destroy(window); | |
154 } | |
155 | |
156 struct _menu_cb_info { | |
157 GaimRoomlist *list; | |
158 GaimRoomlistRoom *room; | |
159 }; | |
160 | |
8199 | 161 static void |
8377 | 162 join_button_data_change_cb(gpointer data) { |
163 g_free(data); | |
164 } | |
165 | |
166 static void | |
8199 | 167 selection_changed_cb(GtkTreeSelection *selection, GaimGtkRoomlist *grl) { |
168 GtkTreeIter iter; | |
169 GValue val = { 0, }; | |
170 GaimRoomlistRoom *room; | |
171 static struct _menu_cb_info *info; | |
172 GaimGtkRoomlistDialog *dialog; | |
173 | |
174 dialog = grl->dialog; | |
175 | |
176 if (gtk_tree_selection_get_selected(selection, NULL, &iter)) { | |
177 gtk_tree_model_get_value(GTK_TREE_MODEL(grl->model), &iter, ROOM_COLUMN, &val); | |
178 room = g_value_get_pointer(&val); | |
179 if (!room || !(room->type & GAIM_ROOMLIST_ROOMTYPE_ROOM)) { | |
180 gtk_widget_set_sensitive(dialog->join_button, FALSE); | |
181 return; | |
182 } | |
183 | |
184 info = g_new0(struct _menu_cb_info, 1); | |
185 info->list = dialog->roomlist; | |
186 info->room = room; | |
187 | |
8377 | 188 g_object_set_data_full(G_OBJECT(dialog->join_button), "room-info", |
189 info, join_button_data_change_cb); | |
8199 | 190 |
191 gtk_widget_set_sensitive(dialog->join_button, TRUE); | |
192 } else { | |
193 gtk_widget_set_sensitive(dialog->join_button, FALSE); | |
194 } | |
195 } | |
196 | |
8113 | 197 static void do_join_cb(GtkWidget *w, struct _menu_cb_info *info) |
198 { | |
8199 | 199 gaim_roomlist_room_join(info->list, info->room); |
200 } | |
8113 | 201 |
8199 | 202 static void join_button_cb(GtkButton *button, GaimGtkRoomlistDialog *dialog) |
203 { | |
204 GaimRoomlist *rl = dialog->roomlist; | |
205 GaimGtkRoomlist *grl = rl->ui_data; | |
206 struct _menu_cb_info *info; | |
207 | |
208 info = (struct _menu_cb_info*)g_object_get_data(G_OBJECT(button), "room-info"); | |
209 | |
8377 | 210 if(info != NULL) |
211 do_join_cb(grl->tree, info); | |
8113 | 212 } |
213 | |
214 static void row_activated_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *arg2, | |
215 GaimRoomlist *list) | |
216 { | |
217 GaimGtkRoomlist *grl = list->ui_data; | |
218 GtkTreeIter iter; | |
219 GaimRoomlistRoom *room; | |
220 GValue val = { 0, }; | |
221 struct _menu_cb_info info; | |
222 | |
223 gtk_tree_model_get_iter(GTK_TREE_MODEL(grl->model), &iter, path); | |
224 gtk_tree_model_get_value(GTK_TREE_MODEL(grl->model), &iter, ROOM_COLUMN, &val); | |
225 room = g_value_get_pointer(&val); | |
226 if (!room || !(room->type & GAIM_ROOMLIST_ROOMTYPE_ROOM)) | |
227 return; | |
228 | |
229 info.list = list; | |
230 info.room = room; | |
231 | |
232 do_join_cb(GTK_WIDGET(tv), &info); | |
233 } | |
234 | |
235 static gboolean room_click_cb(GtkWidget *tv, GdkEventButton *event, GaimRoomlist *list) | |
236 { | |
237 GtkTreePath *path; | |
238 GaimGtkRoomlist *grl = list->ui_data; | |
239 GValue val = { 0, }; | |
240 GaimRoomlistRoom *room; | |
241 GtkTreeIter iter; | |
242 GtkWidget *menu; | |
243 static struct _menu_cb_info info; /* XXX? */ | |
244 | |
245 if (event->button != 3 || event->type != GDK_BUTTON_PRESS) | |
246 return FALSE; | |
247 | |
248 /* Here we figure out which room was clicked */ | |
249 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL)) | |
250 return FALSE; | |
251 gtk_tree_model_get_iter(GTK_TREE_MODEL(grl->model), &iter, path); | |
252 gtk_tree_path_free(path); | |
253 gtk_tree_model_get_value (GTK_TREE_MODEL(grl->model), &iter, ROOM_COLUMN, &val); | |
254 room = g_value_get_pointer(&val); | |
255 | |
256 if (!room || !(room->type & GAIM_ROOMLIST_ROOMTYPE_ROOM)) | |
257 return FALSE; | |
258 | |
259 info.list = list; | |
260 info.room = room; | |
261 | |
262 | |
263 menu = gtk_menu_new(); | |
264 gaim_new_item_from_stock(menu, _("_Join"), GAIM_STOCK_CHAT, | |
265 G_CALLBACK(do_join_cb), &info, 0, 0, NULL); | |
266 | |
267 | |
268 gtk_widget_show_all(menu); | |
269 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time); | |
270 | |
271 return FALSE; | |
272 } | |
273 | |
274 static void row_expanded_cb(GtkTreeView *treeview, GtkTreeIter *arg1, GtkTreePath *arg2, gpointer user_data) | |
275 { | |
276 GaimRoomlist *list = user_data; | |
8584 | 277 GaimRoomlistRoom *category; |
8113 | 278 GValue val = { 0, }; |
279 | |
280 gtk_tree_model_get_value(gtk_tree_view_get_model(treeview), arg1, ROOM_COLUMN, &val); | |
8584 | 281 category = g_value_get_pointer(&val); |
8113 | 282 |
8584 | 283 if (!category->expanded_once) { |
284 gaim_roomlist_expand_category(list, category); | |
285 category->expanded_once = TRUE; | |
8113 | 286 } |
287 } | |
288 | |
8939 | 289 static gboolean account_filter_func(GaimAccount *account) |
8113 | 290 { |
8939 | 291 GaimConnection *gc = gaim_account_get_connection(account); |
292 GaimPluginProtocolInfo *prpl_info = NULL; | |
293 | |
294 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); | |
295 | |
296 return (prpl_info->roomlist_get_list != NULL); | |
297 } | |
298 | |
299 gboolean | |
300 gaim_gtk_roomlist_is_showable() | |
301 { | |
302 GList *c; | |
8113 | 303 GaimConnection *gc; |
304 | |
8939 | 305 for (c = gaim_connections_get_all(); c != NULL; c = c->next) { |
306 gc = c->data; | |
307 | |
308 if (account_filter_func(gaim_connection_get_account(gc))) | |
309 return TRUE; | |
310 } | |
311 | |
312 return FALSE; | |
8113 | 313 } |
314 | |
315 GaimGtkRoomlistDialog *gaim_gtk_roomlist_dialog_new_with_account(GaimAccount *account) | |
316 { | |
317 GaimGtkRoomlistDialog *dialog; | |
318 GtkWidget *window; | |
8199 | 319 GtkWidget *vbox; |
320 GtkWidget *vbox2; | |
8113 | 321 GtkWidget *account_hbox; |
322 GtkWidget *bbox; | |
323 GtkWidget *label; | |
324 | |
325 dialog = g_new0(GaimGtkRoomlistDialog, 1); | |
8937 | 326 dialog->account = account; |
8113 | 327 |
328 /* Create the window. */ | |
329 dialog->window = window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
330 gtk_window_set_role(GTK_WINDOW(window), "room list"); | |
331 gtk_window_set_title(GTK_WINDOW(window), _("Room List")); | |
332 | |
11243 | 333 gtk_container_set_border_width(GTK_CONTAINER(window), GAIM_HIG_BORDER); |
8113 | 334 |
335 g_signal_connect(G_OBJECT(window), "delete_event", | |
336 G_CALLBACK(delete_win_cb), dialog); | |
337 | |
338 /* Create the parent vbox for everything. */ | |
11243 | 339 vbox = gtk_vbox_new(FALSE, GAIM_HIG_BORDER); |
8199 | 340 gtk_container_add(GTK_CONTAINER(window), vbox); |
341 gtk_widget_show(vbox); | |
8113 | 342 |
11243 | 343 vbox2 = gtk_vbox_new(FALSE, GAIM_HIG_BORDER); |
8199 | 344 gtk_container_add(GTK_CONTAINER(vbox), vbox2); |
8113 | 345 gtk_widget_show(vbox2); |
346 | |
8352 | 347 /* accounts dropdown list */ |
11243 | 348 account_hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE); |
8352 | 349 gtk_box_pack_start(GTK_BOX(vbox2), account_hbox, FALSE, FALSE, 0); |
350 gtk_widget_show(account_hbox); | |
8113 | 351 |
8352 | 352 label = gtk_label_new(NULL); |
8425
6d8ec773a485
[gaim-migrate @ 9155]
Christian Hammond <chipx86@chipx86.com>
parents:
8377
diff
changeset
|
353 gtk_box_pack_start(GTK_BOX(account_hbox), label, FALSE, FALSE, 0); |
8352 | 354 gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("_Account:")); |
8425
6d8ec773a485
[gaim-migrate @ 9155]
Christian Hammond <chipx86@chipx86.com>
parents:
8377
diff
changeset
|
355 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); |
8352 | 356 gtk_widget_show(label); |
8113 | 357 |
8352 | 358 dialog->account_widget = gaim_gtk_account_option_menu_new(dialog->account, FALSE, |
8939 | 359 G_CALLBACK(dialog_select_account_cb), account_filter_func, dialog); |
8199 | 360 |
9067 | 361 if (!dialog->account) /* this is normally null, and we normally don't care what the first selected item is */ |
362 dialog->account = gaim_gtk_account_option_menu_get_selected(dialog->account_widget); | |
363 | |
8352 | 364 gtk_box_pack_start(GTK_BOX(account_hbox), dialog->account_widget, TRUE, TRUE, 0); |
365 gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_WIDGET(dialog->account_widget)); | |
366 gtk_widget_show(dialog->account_widget); | |
8113 | 367 |
8199 | 368 /* scrolled window */ |
8113 | 369 dialog->sw = gtk_scrolled_window_new(NULL, NULL); |
370 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(dialog->sw), | |
371 GTK_SHADOW_IN); | |
372 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dialog->sw), | |
373 GTK_POLICY_AUTOMATIC, | |
374 GTK_POLICY_AUTOMATIC); | |
8199 | 375 gtk_box_pack_start(GTK_BOX(vbox2), dialog->sw, TRUE, TRUE, 0); |
376 gtk_widget_set_size_request(dialog->sw, -1, 250); | |
8113 | 377 gtk_widget_show(dialog->sw); |
378 | |
8199 | 379 /* progress bar */ |
380 dialog->progress = gtk_progress_bar_new(); | |
381 gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(dialog->progress), 0.1); | |
382 gtk_box_pack_start(GTK_BOX(vbox2), dialog->progress, FALSE, FALSE, 0); | |
383 gtk_widget_show(dialog->progress); | |
384 | |
385 | |
386 /* button box */ | |
387 bbox = gtk_hbutton_box_new(); | |
11243 | 388 gtk_box_set_spacing(GTK_BOX(bbox), GAIM_HIG_BOX_SPACE); |
8199 | 389 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); |
390 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, TRUE, 0); | |
391 gtk_widget_show(bbox); | |
392 | |
393 /* stop button */ | |
394 dialog->stop_button = gtk_button_new_from_stock(GTK_STOCK_STOP); | |
395 gtk_box_pack_start(GTK_BOX(bbox), dialog->stop_button, FALSE, FALSE, 0); | |
396 g_signal_connect(G_OBJECT(dialog->stop_button), "clicked", | |
397 G_CALLBACK(stop_button_cb), dialog); | |
398 gtk_widget_set_sensitive(dialog->stop_button, FALSE); | |
399 gtk_widget_show(dialog->stop_button); | |
400 | |
401 /* list button */ | |
402 dialog->list_button = gtk_button_new_with_mnemonic(_("_Get List")); | |
403 gtk_box_pack_start(GTK_BOX(bbox), dialog->list_button, FALSE, FALSE, 0); | |
404 g_signal_connect(G_OBJECT(dialog->list_button), "clicked", | |
405 G_CALLBACK(list_button_cb), dialog); | |
406 gtk_widget_show(dialog->list_button); | |
407 | |
408 /* join button */ | |
409 dialog->join_button = gaim_pixbuf_button_from_stock(_("_Join"), GAIM_STOCK_CHAT, | |
410 GAIM_BUTTON_HORIZONTAL); | |
411 gtk_box_pack_start(GTK_BOX(bbox), dialog->join_button, FALSE, FALSE, 0); | |
412 g_signal_connect(G_OBJECT(dialog->join_button), "clicked", | |
413 G_CALLBACK(join_button_cb), dialog); | |
414 gtk_widget_set_sensitive(dialog->join_button, FALSE); | |
415 gtk_widget_show(dialog->join_button); | |
416 | |
417 /* close button */ | |
418 dialog->close_button = gtk_button_new_from_stock(GTK_STOCK_CLOSE); | |
419 gtk_box_pack_start(GTK_BOX(bbox), dialog->close_button, FALSE, FALSE, 0); | |
420 g_signal_connect(G_OBJECT(dialog->close_button), "clicked", | |
421 G_CALLBACK(close_button_cb), dialog); | |
422 gtk_widget_show(dialog->close_button); | |
423 | |
424 /* show the dialog window and return the dialog */ | |
425 gtk_widget_show(dialog->window); | |
426 | |
8113 | 427 return dialog; |
428 } | |
429 | |
430 GaimGtkRoomlistDialog *gaim_gtk_roomlist_dialog_new(void) | |
431 { | |
432 return gaim_gtk_roomlist_dialog_new_with_account(NULL); | |
433 } | |
434 | |
8352 | 435 static void gaim_gtk_roomlist_dialog_show_with_account(GaimAccount *account) |
436 { | |
437 GaimGtkRoomlistDialog *dialog; | |
438 | |
439 dialog = gaim_gtk_roomlist_dialog_new_with_account(account); | |
440 if (!dialog) | |
441 return; | |
442 | |
443 list_button_cb(GTK_BUTTON(dialog->list_button), dialog); | |
444 } | |
445 | |
8113 | 446 void gaim_gtk_roomlist_dialog_show(void) |
447 { | |
448 gaim_gtk_roomlist_dialog_new(); | |
449 } | |
450 | |
451 static void gaim_gtk_roomlist_new(GaimRoomlist *list) | |
452 { | |
453 GaimGtkRoomlist *rl; | |
454 | |
455 rl = g_new0(GaimGtkRoomlist, 1); | |
456 | |
457 list->ui_data = rl; | |
458 | |
459 rl->cats = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)gtk_tree_row_reference_free); | |
460 | |
461 roomlists = g_list_append(roomlists, list); | |
462 } | |
463 | |
464 static void int_cell_data_func(GtkTreeViewColumn *col, GtkCellRenderer *renderer, | |
465 GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data) | |
466 { | |
467 gchar buf[16]; | |
468 int myint; | |
469 | |
470 gtk_tree_model_get(model, iter, GPOINTER_TO_INT(user_data), &myint, -1); | |
471 | |
472 if (myint) | |
473 g_snprintf(buf, sizeof(buf), "%d", myint); | |
474 else | |
475 buf[0] = '\0'; | |
476 | |
477 g_object_set(renderer, "text", buf, NULL); | |
478 } | |
479 | |
480 /* this sorts backwards on purpose, so that clicking name sorts a-z, while clicking users sorts | |
481 infinity-0. you can still click again to reverse it on any of them. */ | |
482 static gint int_sort_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data) | |
483 { | |
484 int c, d; | |
485 | |
486 c = d = 0; | |
487 | |
488 gtk_tree_model_get(model, a, GPOINTER_TO_INT(user_data), &c, -1); | |
489 gtk_tree_model_get(model, b, GPOINTER_TO_INT(user_data), &d, -1); | |
490 | |
491 if (c == d) | |
492 return 0; | |
493 else if (c > d) | |
494 return -1; | |
495 else | |
496 return 1; | |
497 } | |
498 | |
11490 | 499 static gboolean |
500 _search_func(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer search_data) | |
501 { | |
502 gboolean result; | |
503 gchar *name, *fold, *fkey; | |
504 | |
505 gtk_tree_model_get(model, iter, column, &name, -1); | |
506 fold = g_utf8_casefold(name, -1); | |
507 fkey = g_utf8_casefold(key, -1); | |
508 | |
509 result = (g_strstr_len(fold, strlen(fold), fkey) == NULL); | |
510 | |
511 g_free(fold); | |
512 g_free(fkey); | |
513 g_free(name); | |
514 | |
515 return result; | |
516 } | |
517 | |
8113 | 518 static void gaim_gtk_roomlist_set_fields(GaimRoomlist *list, GList *fields) |
519 { | |
520 GaimGtkRoomlist *grl = list->ui_data; | |
521 gint columns = NUM_OF_COLUMNS; | |
522 int j; | |
523 GtkTreeStore *model; | |
524 GtkWidget *tree; | |
525 GtkCellRenderer *renderer; | |
526 GtkTreeViewColumn *column; | |
8199 | 527 GtkTreeSelection *selection; |
8113 | 528 GList *l; |
529 GType *types; | |
530 | |
531 g_return_if_fail(grl != NULL); | |
532 | |
533 columns += g_list_length(fields); | |
534 types = g_new(GType, columns); | |
535 | |
536 types[NAME_COLUMN] = G_TYPE_STRING; | |
537 types[ROOM_COLUMN] = G_TYPE_POINTER; | |
538 | |
539 for (j = NUM_OF_COLUMNS, l = fields; l; l = l->next, j++) { | |
540 GaimRoomlistField *f = l->data; | |
541 | |
542 switch (f->type) { | |
543 case GAIM_ROOMLIST_FIELD_BOOL: | |
544 types[j] = G_TYPE_BOOLEAN; | |
545 break; | |
546 case GAIM_ROOMLIST_FIELD_INT: | |
547 types[j] = G_TYPE_INT; | |
548 break; | |
549 case GAIM_ROOMLIST_FIELD_STRING: | |
550 types[j] = G_TYPE_STRING; | |
551 break; | |
552 } | |
553 } | |
554 | |
555 model = gtk_tree_store_newv(columns, types); | |
556 g_free(types); | |
557 | |
558 tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); | |
559 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree), TRUE); | |
560 | |
8199 | 561 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree)); |
562 g_signal_connect(G_OBJECT(selection), "changed", | |
563 G_CALLBACK(selection_changed_cb), grl); | |
564 | |
8113 | 565 g_object_unref(model); |
566 | |
567 grl->model = model; | |
568 grl->tree = tree; | |
569 gtk_widget_show(grl->tree); | |
570 | |
571 renderer = gtk_cell_renderer_text_new(); | |
572 column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer, | |
573 "text", NAME_COLUMN, NULL); | |
574 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column), | |
575 GTK_TREE_VIEW_COLUMN_GROW_ONLY); | |
576 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE); | |
577 gtk_tree_view_column_set_sort_column_id(GTK_TREE_VIEW_COLUMN(column), NAME_COLUMN); | |
578 gtk_tree_view_column_set_reorderable(GTK_TREE_VIEW_COLUMN(column), TRUE); | |
579 gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); | |
580 | |
581 for (j = NUM_OF_COLUMNS, l = fields; l; l = l->next, j++) { | |
582 GaimRoomlistField *f = l->data; | |
583 | |
584 if (f->hidden) | |
585 continue; | |
586 | |
587 renderer = gtk_cell_renderer_text_new(); | |
588 column = gtk_tree_view_column_new_with_attributes(f->label, renderer, | |
589 "text", j, NULL); | |
590 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column), | |
591 GTK_TREE_VIEW_COLUMN_GROW_ONLY); | |
592 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE); | |
593 gtk_tree_view_column_set_sort_column_id(GTK_TREE_VIEW_COLUMN(column), j); | |
594 gtk_tree_view_column_set_reorderable(GTK_TREE_VIEW_COLUMN(column), TRUE); | |
595 if (f->type == GAIM_ROOMLIST_FIELD_INT) { | |
596 gtk_tree_view_column_set_cell_data_func(column, renderer, int_cell_data_func, | |
597 GINT_TO_POINTER(j), NULL); | |
598 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(model), j, int_sort_func, | |
599 GINT_TO_POINTER(j), NULL); | |
600 } | |
601 gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); | |
602 } | |
603 | |
604 g_signal_connect(G_OBJECT(tree), "button-press-event", G_CALLBACK(room_click_cb), list); | |
605 g_signal_connect(G_OBJECT(tree), "row-expanded", G_CALLBACK(row_expanded_cb), list); | |
606 g_signal_connect(G_OBJECT(tree), "row-activated", G_CALLBACK(row_activated_cb), list); | |
11490 | 607 |
608 /* Enable CTRL+F searching */ | |
609 gtk_tree_view_set_search_column(GTK_TREE_VIEW(tree), NAME_COLUMN); | |
610 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(tree), _search_func, NULL, NULL); | |
611 | |
8113 | 612 } |
613 | |
8230 | 614 static gboolean gaim_gtk_progress_bar_pulse(gpointer data) |
615 { | |
616 GaimRoomlist *list = data; | |
617 GaimGtkRoomlist *rl = list->ui_data; | |
618 | |
619 if (!rl || !rl->dialog || !rl->dialog->pg_needs_pulse) { | |
620 rl->dialog->pg_to_active = FALSE; | |
621 gaim_roomlist_unref(list); | |
622 return FALSE; | |
623 } | |
624 | |
625 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(rl->dialog->progress)); | |
626 rl->dialog->pg_needs_pulse = FALSE; | |
627 return TRUE; | |
628 } | |
629 | |
8113 | 630 static void gaim_gtk_roomlist_add_room(GaimRoomlist *list, GaimRoomlistRoom *room) |
631 { | |
632 GaimGtkRoomlist *rl= list->ui_data; | |
633 GtkTreeRowReference *rr, *parentrr = NULL; | |
634 GtkTreePath *path; | |
635 GtkTreeIter iter, parent, child; | |
636 GList *l, *k; | |
637 int j; | |
638 gboolean append = TRUE; | |
639 | |
640 rl->total_rooms++; | |
641 if (room->type == GAIM_ROOMLIST_ROOMTYPE_ROOM) | |
642 rl->num_rooms++; | |
643 | |
644 if (rl->dialog) { | |
8230 | 645 if (!rl->dialog->pg_to_active) { |
646 rl->dialog->pg_to_active = TRUE; | |
647 gaim_roomlist_ref(list); | |
648 rl->dialog->pg_update_to = g_timeout_add(100, gaim_gtk_progress_bar_pulse, list); | |
649 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(rl->dialog->progress)); | |
650 } else { | |
651 rl->dialog->pg_needs_pulse = TRUE; | |
652 } | |
8113 | 653 } |
654 if (room->parent) { | |
655 parentrr = g_hash_table_lookup(rl->cats, room->parent); | |
656 path = gtk_tree_row_reference_get_path(parentrr); | |
657 if (path) { | |
658 GaimRoomlistRoom *tmproom = NULL; | |
659 | |
660 gtk_tree_model_get_iter(GTK_TREE_MODEL(rl->model), &parent, path); | |
661 gtk_tree_path_free(path); | |
662 | |
663 if (gtk_tree_model_iter_children(GTK_TREE_MODEL(rl->model), &child, &parent)) { | |
664 gtk_tree_model_get(GTK_TREE_MODEL(rl->model), &child, ROOM_COLUMN, &tmproom, -1); | |
665 if (!tmproom) | |
666 append = FALSE; | |
667 } | |
668 } | |
669 } | |
670 | |
671 if (append) | |
672 gtk_tree_store_append(rl->model, &iter, (parentrr ? &parent : NULL)); | |
673 else | |
674 iter = child; | |
675 | |
8584 | 676 if (room->type & GAIM_ROOMLIST_ROOMTYPE_CATEGORY) |
8113 | 677 gtk_tree_store_append(rl->model, &child, &iter); |
678 | |
679 path = gtk_tree_model_get_path(GTK_TREE_MODEL(rl->model), &iter); | |
680 | |
8584 | 681 if (room->type & GAIM_ROOMLIST_ROOMTYPE_CATEGORY) { |
8113 | 682 rr = gtk_tree_row_reference_new(GTK_TREE_MODEL(rl->model), path); |
683 g_hash_table_insert(rl->cats, room, rr); | |
684 } | |
685 | |
686 gtk_tree_path_free(path); | |
687 | |
688 gtk_tree_store_set(rl->model, &iter, NAME_COLUMN, room->name, -1); | |
689 gtk_tree_store_set(rl->model, &iter, ROOM_COLUMN, room, -1); | |
690 | |
691 for (j = NUM_OF_COLUMNS, l = room->fields, k = list->fields; l && k; j++, l = l->next, k = k->next) { | |
692 GaimRoomlistField *f = k->data; | |
693 if (f->hidden) | |
694 continue; | |
695 gtk_tree_store_set(rl->model, &iter, j, l->data, -1); | |
696 } | |
697 } | |
698 | |
699 static void gaim_gtk_roomlist_in_progress(GaimRoomlist *list, gboolean flag) | |
700 { | |
701 GaimGtkRoomlist *rl = list->ui_data; | |
702 | |
703 if (!rl || !rl->dialog) | |
704 return; | |
705 | |
706 if (flag) { | |
8199 | 707 if (rl->dialog->account_widget) |
708 gtk_widget_set_sensitive(rl->dialog->account_widget, FALSE); | |
709 gtk_widget_set_sensitive(rl->dialog->stop_button, TRUE); | |
710 gtk_widget_set_sensitive(rl->dialog->list_button, FALSE); | |
8113 | 711 } else { |
8230 | 712 rl->dialog->pg_needs_pulse = FALSE; |
8113 | 713 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(rl->dialog->progress), 0.0); |
8199 | 714 if (rl->dialog->account_widget) |
715 gtk_widget_set_sensitive(rl->dialog->account_widget, TRUE); | |
716 gtk_widget_set_sensitive(rl->dialog->stop_button, FALSE); | |
717 gtk_widget_set_sensitive(rl->dialog->list_button, TRUE); | |
8113 | 718 } |
719 } | |
720 | |
721 static void gaim_gtk_roomlist_destroy(GaimRoomlist *list) | |
722 { | |
723 GaimGtkRoomlist *rl; | |
724 | |
725 roomlists = g_list_remove(roomlists, list); | |
726 | |
727 rl = list->ui_data; | |
728 | |
729 g_return_if_fail(rl != NULL); | |
730 | |
731 g_hash_table_destroy(rl->cats); | |
732 g_free(rl); | |
733 list->ui_data = NULL; | |
734 } | |
735 | |
736 static GaimRoomlistUiOps ops = { | |
8352 | 737 gaim_gtk_roomlist_dialog_show_with_account, |
8113 | 738 gaim_gtk_roomlist_new, |
739 gaim_gtk_roomlist_set_fields, | |
740 gaim_gtk_roomlist_add_room, | |
741 gaim_gtk_roomlist_in_progress, | |
742 gaim_gtk_roomlist_destroy | |
743 }; | |
744 | |
745 | |
746 void gaim_gtk_roomlist_init(void) | |
747 { | |
748 gaim_roomlist_set_ui_ops(&ops); | |
749 } |