pidgin/gtkroomlist.c

changeset 41349
15aeaa1e84ec
parent 41030
ec8b76f3bf0a
child 41554
49216e79a56b
equal deleted inserted replaced
41348:74b1a5db343b 41349:15aeaa1e84ec
39 struct _PidginRoomlistDialog { 39 struct _PidginRoomlistDialog {
40 GtkDialog parent; 40 GtkDialog parent;
41 41
42 GtkWidget *account_widget; 42 GtkWidget *account_widget;
43 GtkWidget *progress; 43 GtkWidget *progress;
44 GtkWidget *sw; 44 GtkWidget *tree;
45 45
46 GtkWidget *stop_button; 46 GtkWidget *stop_button;
47 GtkWidget *list_button; 47 GtkWidget *list_button;
48 GtkWidget *add_button; 48 GtkWidget *add_button;
49 GtkWidget *join_button; 49 GtkWidget *join_button;
59 G_DEFINE_TYPE(PidginRoomlistDialog, pidgin_roomlist_dialog, GTK_TYPE_DIALOG) 59 G_DEFINE_TYPE(PidginRoomlistDialog, pidgin_roomlist_dialog, GTK_TYPE_DIALOG)
60 60
61 typedef struct { 61 typedef struct {
62 PidginRoomlistDialog *dialog; 62 PidginRoomlistDialog *dialog;
63 GtkTreeStore *model; 63 GtkTreeStore *model;
64 GtkWidget *tree;
65 GHashTable *cats; /* Meow. */
66 gint num_rooms, total_rooms;
67 } PidginRoomlist; 64 } PidginRoomlist;
68 65
69 enum { 66 enum {
70 NAME_COLUMN = 0, 67 ROOM_COLUMN = 0,
71 ROOM_COLUMN, 68 NAME_COLUMN,
69 DESCRIPTION_COLUMN,
72 NUM_OF_COLUMNS, 70 NUM_OF_COLUMNS,
73 }; 71 };
72
73 static gboolean
74 _search_func(GtkTreeModel *model, gint column, const gchar *key,
75 GtkTreeIter *iter, gpointer search_data)
76 {
77 gboolean result;
78 gchar *name, *fold, *fkey;
79
80 gtk_tree_model_get(model, iter, column, &name, -1);
81 fold = g_utf8_casefold(name, -1);
82 fkey = g_utf8_casefold(key, -1);
83
84 result = (g_strstr_len(fold, strlen(fold), fkey) == NULL);
85
86 g_free(fold);
87 g_free(fkey);
88 g_free(name);
89
90 return result;
91 }
74 92
75 static gint delete_win_cb(GtkWidget *w, GdkEventAny *e, gpointer d) 93 static gint delete_win_cb(GtkWidget *w, GdkEventAny *e, gpointer d)
76 { 94 {
77 PidginRoomlistDialog *dialog = PIDGIN_ROOMLIST_DIALOG(w); 95 PidginRoomlistDialog *dialog = PIDGIN_ROOMLIST_DIALOG(w);
78 96
112 if (change && dialog->roomlist) { 130 if (change && dialog->roomlist) {
113 PidginRoomlist *rl = NULL; 131 PidginRoomlist *rl = NULL;
114 132
115 rl = g_object_get_data(G_OBJECT(dialog->roomlist), 133 rl = g_object_get_data(G_OBJECT(dialog->roomlist),
116 PIDGIN_ROOMLIST_UI_DATA); 134 PIDGIN_ROOMLIST_UI_DATA);
117 if (rl->tree) { 135
118 gtk_widget_destroy(rl->tree); 136 g_clear_object(&rl->model);
119 rl->tree = NULL;
120 }
121 g_object_unref(dialog->roomlist); 137 g_object_unref(dialog->roomlist);
122 dialog->roomlist = NULL; 138 dialog->roomlist = NULL;
123 } 139 }
124 } 140 }
125 141
134 150
135 if (dialog->roomlist != NULL) { 151 if (dialog->roomlist != NULL) {
136 rl = g_object_get_data(G_OBJECT(dialog->roomlist), 152 rl = g_object_get_data(G_OBJECT(dialog->roomlist),
137 PIDGIN_ROOMLIST_UI_DATA); 153 PIDGIN_ROOMLIST_UI_DATA);
138 154
139 gtk_widget_destroy(rl->tree); 155 g_clear_object(&rl->model);
140 g_object_unref(dialog->roomlist); 156 g_object_unref(dialog->roomlist);
141 } 157 }
142 158
143 dialog->roomlist = purple_roomlist_get_list(gc); 159 dialog->roomlist = purple_roomlist_get_list(gc);
144 if (!dialog->roomlist) 160 if (!dialog->roomlist)
149 PIDGIN_ROOMLIST_UI_DATA); 165 PIDGIN_ROOMLIST_UI_DATA);
150 rl->dialog = dialog; 166 rl->dialog = dialog;
151 167
152 gtk_widget_set_sensitive(dialog->account_widget, FALSE); 168 gtk_widget_set_sensitive(dialog->account_widget, FALSE);
153 169
154 gtk_container_add(GTK_CONTAINER(dialog->sw), rl->tree); 170 gtk_tree_view_set_model(GTK_TREE_VIEW(dialog->tree),
171 GTK_TREE_MODEL(rl->model));
155 172
156 /* some protocols (not bundled with libpurple) finish getting their 173 /* some protocols (not bundled with libpurple) finish getting their
157 * room list immediately */ 174 * room list immediately */
158 if(purple_roomlist_get_in_progress(dialog->roomlist)) { 175 if(purple_roomlist_get_in_progress(dialog->roomlist)) {
159 gtk_widget_set_sensitive(dialog->stop_button, TRUE); 176 gtk_widget_set_sensitive(dialog->stop_button, TRUE);
182 PurpleRoomlist *list; 199 PurpleRoomlist *list;
183 PurpleRoomlistRoom *room; 200 PurpleRoomlistRoom *room;
184 }; 201 };
185 202
186 static void 203 static void
187 selection_changed_cb(GtkTreeSelection *selection, PidginRoomlist *grl) { 204 selection_changed_cb(GtkTreeSelection *selection,
205 PidginRoomlistDialog *dialog)
206 {
188 GtkTreeIter iter; 207 GtkTreeIter iter;
189 GValue val;
190 PurpleRoomlistRoom *room; 208 PurpleRoomlistRoom *room;
191 static struct _menu_cb_info *info; 209 static struct _menu_cb_info *info;
192 PidginRoomlistDialog *dialog = grl->dialog; 210 PidginRoomlist *grl = NULL;
211
212 grl = g_object_get_data(G_OBJECT(dialog->roomlist),
213 PIDGIN_ROOMLIST_UI_DATA);
193 214
194 if (gtk_tree_selection_get_selected(selection, NULL, &iter)) { 215 if (gtk_tree_selection_get_selected(selection, NULL, &iter)) {
195 val.g_type = 0; 216 gtk_tree_model_get(GTK_TREE_MODEL(grl->model), &iter,
196 gtk_tree_model_get_value(GTK_TREE_MODEL(grl->model), &iter, ROOM_COLUMN, &val); 217 ROOM_COLUMN, &room,
197 room = g_value_get_pointer(&val); 218 -1);
198 if (!room || !(purple_roomlist_room_get_room_type(room) & PURPLE_ROOMLIST_ROOMTYPE_ROOM)) {
199 gtk_widget_set_sensitive(dialog->join_button, FALSE);
200 gtk_widget_set_sensitive(dialog->add_button, FALSE);
201 return;
202 }
203 219
204 info = g_new0(struct _menu_cb_info, 1); 220 info = g_new0(struct _menu_cb_info, 1);
205 info->list = dialog->roomlist; 221 info->list = dialog->roomlist;
206 info->room = room; 222 info->room = room;
207 223
209 info, g_free); 225 info, g_free);
210 g_object_set_data(G_OBJECT(dialog->add_button), "room-info", info); 226 g_object_set_data(G_OBJECT(dialog->add_button), "room-info", info);
211 227
212 gtk_widget_set_sensitive(dialog->add_button, TRUE); 228 gtk_widget_set_sensitive(dialog->add_button, TRUE);
213 gtk_widget_set_sensitive(dialog->join_button, TRUE); 229 gtk_widget_set_sensitive(dialog->join_button, TRUE);
230
231 g_object_unref(room);
214 } else { 232 } else {
215 gtk_widget_set_sensitive(dialog->add_button, FALSE); 233 gtk_widget_set_sensitive(dialog->add_button, FALSE);
216 gtk_widget_set_sensitive(dialog->join_button, FALSE); 234 gtk_widget_set_sensitive(dialog->join_button, FALSE);
217 } 235 }
218 } 236 }
250 do_add_room_cb(NULL, info); 268 do_add_room_cb(NULL, info);
251 } 269 }
252 } 270 }
253 271
254 static void 272 static void
255 do_join_cb(G_GNUC_UNUSED GtkWidget *w, struct _menu_cb_info *info) 273 do_join_cb(G_GNUC_UNUSED GtkWidget *w, struct _menu_cb_info *info) {
256 { 274 purple_roomlist_join_room(info->list, info->room);
257 purple_roomlist_room_join(info->list, info->room); 275 }
258 } 276
259 277 static void
260 static void 278 join_button_cb(GtkButton *button, G_GNUC_UNUSED gpointer data) {
261 join_button_cb(GtkButton *button, G_GNUC_UNUSED gpointer data)
262 {
263 struct _menu_cb_info *info = g_object_get_data(G_OBJECT(button), "room-info"); 279 struct _menu_cb_info *info = g_object_get_data(G_OBJECT(button), "room-info");
264 280
265 if(info != NULL) { 281 purple_roomlist_join_room(info->list, info->room);
266 do_join_cb(NULL, info); 282 }
267 } 283
268 } 284 static void
269 285 row_activated_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *arg2,
270 static void row_activated_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *arg2, 286 gpointer data)
271 PurpleRoomlist *list) 287 {
272 { 288 PidginRoomlistDialog *dialog = data;
273 PidginRoomlist *grl = NULL; 289 PidginRoomlist *grl = NULL;
274 GtkTreeIter iter; 290 GtkTreeIter iter;
275 PurpleRoomlistRoom *room; 291 PurpleRoomlistRoom *room;
276 GValue val;
277 struct _menu_cb_info info; 292 struct _menu_cb_info info;
278 293
279 grl = g_object_get_data(G_OBJECT(list), PIDGIN_ROOMLIST_UI_DATA); 294 grl = g_object_get_data(G_OBJECT(dialog->roomlist), PIDGIN_ROOMLIST_UI_DATA);
280 295
281 gtk_tree_model_get_iter(GTK_TREE_MODEL(grl->model), &iter, path); 296 gtk_tree_model_get_iter(GTK_TREE_MODEL(grl->model), &iter, path);
282 val.g_type = 0; 297 gtk_tree_model_get(GTK_TREE_MODEL(grl->model), &iter, ROOM_COLUMN, &room, -1);
283 gtk_tree_model_get_value(GTK_TREE_MODEL(grl->model), &iter, ROOM_COLUMN, &val); 298
284 room = g_value_get_pointer(&val); 299 info.list = dialog->roomlist;
285 if (!room || !(purple_roomlist_room_get_room_type(room) & PURPLE_ROOMLIST_ROOMTYPE_ROOM))
286 return;
287
288 info.list = list;
289 info.room = room; 300 info.room = room;
290 301
291 do_join_cb(NULL, &info); 302 do_join_cb(NULL, &info);
292 } 303
293 304 g_clear_object(&room);
294 static gboolean room_click_cb(GtkWidget *tv, GdkEventButton *event, PurpleRoomlist *list) 305 }
295 { 306
307 static gboolean
308 room_click_cb(GtkWidget *tv, GdkEventButton *event, gpointer data) {
309 PidginRoomlistDialog *dialog = data;
296 GtkTreePath *path; 310 GtkTreePath *path;
297 PidginRoomlist *grl = NULL; 311 PidginRoomlist *grl = NULL;
298 GValue val;
299 PurpleRoomlistRoom *room; 312 PurpleRoomlistRoom *room;
300 GtkTreeIter iter; 313 GtkTreeIter iter;
301 GtkWidget *menu; 314 GtkWidget *menu;
302 GtkWidget *menuitem; 315 GtkWidget *menuitem;
303 static struct _menu_cb_info info; /* XXX? */ 316 static struct _menu_cb_info info; /* XXX? */
304 317
305 if (!gdk_event_triggers_context_menu((GdkEvent *)event)) 318 if (!gdk_event_triggers_context_menu((GdkEvent *)event))
306 return FALSE; 319 return FALSE;
307 320
308 grl = g_object_get_data(G_OBJECT(list), PIDGIN_ROOMLIST_UI_DATA); 321 grl = g_object_get_data(G_OBJECT(dialog->roomlist), PIDGIN_ROOMLIST_UI_DATA);
309 322
310 /* Here we figure out which room was clicked */ 323 /* Here we figure out which room was clicked */
311 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL)) 324 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL))
312 return FALSE; 325 return FALSE;
313 gtk_tree_model_get_iter(GTK_TREE_MODEL(grl->model), &iter, path); 326 gtk_tree_model_get_iter(GTK_TREE_MODEL(grl->model), &iter, path);
314 gtk_tree_path_free(path); 327 gtk_tree_path_free(path);
315 val.g_type = 0; 328 gtk_tree_model_get(GTK_TREE_MODEL(grl->model), &iter, ROOM_COLUMN, &room, -1);
316 gtk_tree_model_get_value (GTK_TREE_MODEL(grl->model), &iter, ROOM_COLUMN, &val); 329
317 room = g_value_get_pointer(&val); 330 info.list = dialog->roomlist;
318
319 if (!room || !(purple_roomlist_room_get_room_type(room) & PURPLE_ROOMLIST_ROOMTYPE_ROOM))
320 return FALSE;
321
322 info.list = list;
323 info.room = room; 331 info.room = room;
332
333 /* The current implementation isn't expecting a ref to unref the one we got
334 * when we pulled the room out of the model.
335 */
336 g_clear_object(&room);
324 337
325 menu = gtk_menu_new(); 338 menu = gtk_menu_new();
326 339
327 menuitem = gtk_menu_item_new_with_mnemonic(_("_Join")); 340 menuitem = gtk_menu_item_new_with_mnemonic(_("_Join"));
328 g_signal_connect(G_OBJECT(menuitem), "activate", 341 g_signal_connect(G_OBJECT(menuitem), "activate",
338 gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event); 351 gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event);
339 352
340 return FALSE; 353 return FALSE;
341 } 354 }
342 355
343 static void row_expanded_cb(GtkTreeView *treeview, GtkTreeIter *arg1, GtkTreePath *arg2, gpointer user_data)
344 {
345 PurpleRoomlist *list = user_data;
346 PurpleRoomlistRoom *category;
347 GValue val;
348
349 val.g_type = 0;
350 gtk_tree_model_get_value(gtk_tree_view_get_model(treeview), arg1, ROOM_COLUMN, &val);
351 category = g_value_get_pointer(&val);
352
353 if (!purple_roomlist_room_get_expanded_once(category)) {
354 purple_roomlist_expand_category(list, category);
355 purple_roomlist_room_set_expanded_once(category, TRUE);
356 }
357 }
358
359 #define SMALL_SPACE 6 356 #define SMALL_SPACE 6
360 357
361 static gboolean 358 static gboolean
362 pidgin_roomlist_query_tooltip(GtkWidget *widget, int x, int y, 359 pidgin_roomlist_query_tooltip(GtkWidget *widget, int x, int y,
363 gboolean keyboard_mode, GtkTooltip *tooltip, 360 gboolean keyboard_mode, GtkTooltip *tooltip,
364 gpointer data) 361 gpointer data)
365 { 362 {
366 PurpleRoomlist *list = data; 363 PidginRoomlistDialog *dialog = data;
367 PidginRoomlist *grl = NULL; 364 PidginRoomlist *grl = NULL;
368 GtkTreePath *path = NULL; 365 GtkTreePath *path = NULL;
369 PurpleRoomlistRoom *room;
370 GtkTreeIter iter; 366 GtkTreeIter iter;
371 GValue val;
372 gchar *name, *tmp; 367 gchar *name, *tmp;
373 GString *tooltip_text = NULL; 368 GString *tooltip_text = NULL;
374 GList *l, *k; 369
375 gint j; 370 grl = g_object_get_data(G_OBJECT(dialog->roomlist), PIDGIN_ROOMLIST_UI_DATA);
376 gboolean first = TRUE;
377
378 grl = g_object_get_data(G_OBJECT(list), PIDGIN_ROOMLIST_UI_DATA);
379 371
380 if (keyboard_mode) { 372 if (keyboard_mode) {
381 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); 373 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
382 if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) { 374 if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) {
383 return FALSE; 375 return FALSE;
398 gtk_tree_path_free(path); 390 gtk_tree_path_free(path);
399 return FALSE; 391 return FALSE;
400 } 392 }
401 } 393 }
402 394
403 val.g_type = 0;
404 gtk_tree_model_get_value(GTK_TREE_MODEL(grl->model), &iter, ROOM_COLUMN,
405 &val);
406 room = g_value_get_pointer(&val);
407
408 if (!room ||
409 !(purple_roomlist_room_get_room_type(room) & PURPLE_ROOMLIST_ROOMTYPE_ROOM))
410 {
411 gtk_tree_path_free(path);
412 return FALSE;
413 }
414
415 tooltip_text = g_string_new(""); 395 tooltip_text = g_string_new("");
416 396
417 gtk_tree_model_get(GTK_TREE_MODEL(grl->model), &iter, NAME_COLUMN, &name, -1); 397 gtk_tree_model_get(GTK_TREE_MODEL(grl->model), &iter, NAME_COLUMN, &name, -1);
418 tmp = g_markup_escape_text(name, -1); 398 tmp = g_markup_escape_text(name, -1);
419 g_free(name); 399 g_free(name);
420 g_string_append_printf( 400 g_string_append_printf(
421 tooltip_text, "<span size='x-large' weight='bold'>%s</span>\n", tmp); 401 tooltip_text, "<span size='x-large' weight='bold'>%s</span>\n", tmp);
422 g_free(tmp); 402 g_free(tmp);
423 403
424 for (j = NUM_OF_COLUMNS,
425 l = purple_roomlist_room_get_fields(room),
426 k = purple_roomlist_get_fields(list);
427 l && k;
428 j++, l = l->next, k = k->next)
429 {
430 PurpleRoomlistField *f = k->data;
431 gchar *label;
432 if (purple_roomlist_field_get_hidden(f)) {
433 continue;
434 }
435 label = g_markup_escape_text(purple_roomlist_field_get_label(f), -1);
436 switch (purple_roomlist_field_get_field_type(f)) {
437 case PURPLE_ROOMLIST_FIELD_BOOL:
438 g_string_append_printf(
439 tooltip_text, "%s<b>%s:</b> %s", first ? "" : "\n", label,
440 l->data ? "True" : "False");
441 break;
442 case PURPLE_ROOMLIST_FIELD_INT:
443 g_string_append_printf(
444 tooltip_text, "%s<b>%s:</b> %d", first ? "" : "\n", label,
445 GPOINTER_TO_INT(l->data));
446 break;
447 case PURPLE_ROOMLIST_FIELD_STRING:
448 tmp = g_markup_escape_text((char *)l->data, -1);
449 g_string_append_printf(
450 tooltip_text, "%s<b>%s:</b> %s", first ? "" : "\n", label,
451 tmp);
452 g_free(tmp);
453 break;
454 }
455 first = FALSE;
456 g_free(label);
457 }
458
459 gtk_tooltip_set_markup(tooltip, tooltip_text->str); 404 gtk_tooltip_set_markup(tooltip, tooltip_text->str);
460 gtk_tree_view_set_tooltip_row(GTK_TREE_VIEW(widget), tooltip, path); 405 gtk_tree_view_set_tooltip_row(GTK_TREE_VIEW(widget), tooltip, path);
461 g_string_free(tooltip_text, TRUE); 406 g_string_free(tooltip_text, TRUE);
462 gtk_tree_path_free(path); 407 gtk_tree_path_free(path);
463 408
500 widget_class, "/im/pidgin/Pidgin3/Roomlist/roomlist.ui"); 445 widget_class, "/im/pidgin/Pidgin3/Roomlist/roomlist.ui");
501 446
502 gtk_widget_class_bind_template_child(widget_class, PidginRoomlistDialog, 447 gtk_widget_class_bind_template_child(widget_class, PidginRoomlistDialog,
503 account_widget); 448 account_widget);
504 gtk_widget_class_bind_template_child(widget_class, PidginRoomlistDialog, 449 gtk_widget_class_bind_template_child(widget_class, PidginRoomlistDialog,
450 tree);
451 gtk_widget_class_bind_template_child(widget_class, PidginRoomlistDialog,
505 add_button); 452 add_button);
506 gtk_widget_class_bind_template_child(widget_class, PidginRoomlistDialog, 453 gtk_widget_class_bind_template_child(widget_class, PidginRoomlistDialog,
507 close_button); 454 close_button);
508 gtk_widget_class_bind_template_child(widget_class, PidginRoomlistDialog, 455 gtk_widget_class_bind_template_child(widget_class, PidginRoomlistDialog,
509 join_button); 456 join_button);
511 list_button); 458 list_button);
512 gtk_widget_class_bind_template_child(widget_class, PidginRoomlistDialog, 459 gtk_widget_class_bind_template_child(widget_class, PidginRoomlistDialog,
513 progress); 460 progress);
514 gtk_widget_class_bind_template_child(widget_class, PidginRoomlistDialog, 461 gtk_widget_class_bind_template_child(widget_class, PidginRoomlistDialog,
515 stop_button); 462 stop_button);
516 gtk_widget_class_bind_template_child(widget_class, PidginRoomlistDialog,
517 sw);
518 463
519 gtk_widget_class_bind_template_callback(widget_class, 464 gtk_widget_class_bind_template_callback(widget_class,
520 add_room_to_blist_cb); 465 add_room_to_blist_cb);
521 gtk_widget_class_bind_template_callback(widget_class, delete_win_cb); 466 gtk_widget_class_bind_template_callback(widget_class, delete_win_cb);
467 gtk_widget_class_bind_template_callback(widget_class, row_activated_cb);
468 gtk_widget_class_bind_template_callback(widget_class, room_click_cb);
522 gtk_widget_class_bind_template_callback(widget_class, 469 gtk_widget_class_bind_template_callback(widget_class,
523 dialog_select_account_cb); 470 dialog_select_account_cb);
471 gtk_widget_class_bind_template_callback(widget_class,
472 selection_changed_cb);
473 gtk_widget_class_bind_template_callback(widget_class,
474 pidgin_roomlist_query_tooltip);
524 gtk_widget_class_bind_template_callback(widget_class, join_button_cb); 475 gtk_widget_class_bind_template_callback(widget_class, join_button_cb);
525 gtk_widget_class_bind_template_callback(widget_class, list_button_cb); 476 gtk_widget_class_bind_template_callback(widget_class, list_button_cb);
526 gtk_widget_class_bind_template_callback(widget_class, stop_button_cb); 477 gtk_widget_class_bind_template_callback(widget_class, stop_button_cb);
527 } 478 }
528 479
532 gtk_widget_init_template(GTK_WIDGET(self)); 483 gtk_widget_init_template(GTK_WIDGET(self));
533 484
534 pidgin_account_chooser_set_filter_func( 485 pidgin_account_chooser_set_filter_func(
535 PIDGIN_ACCOUNT_CHOOSER(self->account_widget), 486 PIDGIN_ACCOUNT_CHOOSER(self->account_widget),
536 account_filter_func); 487 account_filter_func);
488
489 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(self->tree),
490 _search_func, NULL, NULL);
537 } 491 }
538 492
539 static PidginRoomlistDialog * 493 static PidginRoomlistDialog *
540 pidgin_roomlist_dialog_new_with_account(PurpleAccount *account) 494 pidgin_roomlist_dialog_new_with_account(PurpleAccount *account)
541 { 495 {
574 void pidgin_roomlist_dialog_show(void) 528 void pidgin_roomlist_dialog_show(void)
575 { 529 {
576 pidgin_roomlist_dialog_new_with_account(NULL); 530 pidgin_roomlist_dialog_new_with_account(NULL);
577 } 531 }
578 532
579 static void int_cell_data_func(GtkTreeViewColumn *col, GtkCellRenderer *renderer,
580 GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
581 {
582 gchar buf[16];
583 int myint;
584
585 gtk_tree_model_get(model, iter, GPOINTER_TO_INT(user_data), &myint, -1);
586
587 if (myint)
588 g_snprintf(buf, sizeof(buf), "%d", myint);
589 else
590 buf[0] = '\0';
591
592 g_object_set(renderer, "text", buf, NULL);
593 }
594
595 /* this sorts backwards on purpose, so that clicking name sorts a-z, while clicking users sorts
596 infinity-0. you can still click again to reverse it on any of them. */
597 static gint int_sort_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data)
598 {
599 int c, d;
600
601 c = d = 0;
602
603 gtk_tree_model_get(model, a, GPOINTER_TO_INT(user_data), &c, -1);
604 gtk_tree_model_get(model, b, GPOINTER_TO_INT(user_data), &d, -1);
605
606 if (c == d)
607 return 0;
608 else if (c > d)
609 return -1;
610 else
611 return 1;
612 }
613
614 static gboolean
615 _search_func(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer search_data)
616 {
617 gboolean result;
618 gchar *name, *fold, *fkey;
619
620 gtk_tree_model_get(model, iter, column, &name, -1);
621 fold = g_utf8_casefold(name, -1);
622 fkey = g_utf8_casefold(key, -1);
623
624 result = (g_strstr_len(fold, strlen(fold), fkey) == NULL);
625
626 g_free(fold);
627 g_free(fkey);
628 g_free(name);
629
630 return result;
631 }
632
633 static void pidgin_roomlist_set_fields(PurpleRoomlist *list, GList *fields)
634 {
635 PidginRoomlist *grl = NULL;
636 gint columns = NUM_OF_COLUMNS;
637 int j;
638 GtkTreeStore *model;
639 GtkWidget *tree;
640 GtkCellRenderer *renderer;
641 GtkTreeViewColumn *column;
642 GtkTreeSelection *selection;
643 GList *l;
644 GType *types;
645
646 grl = g_object_get_data(G_OBJECT(list), PIDGIN_ROOMLIST_UI_DATA);
647 g_return_if_fail(grl != NULL);
648
649 columns += g_list_length(fields);
650 types = g_new(GType, columns);
651
652 types[NAME_COLUMN] = G_TYPE_STRING;
653 types[ROOM_COLUMN] = G_TYPE_POINTER;
654
655 for (j = NUM_OF_COLUMNS, l = fields; l; l = l->next, j++) {
656 PurpleRoomlistField *f = l->data;
657
658 switch (purple_roomlist_field_get_field_type(f)) {
659 case PURPLE_ROOMLIST_FIELD_BOOL:
660 types[j] = G_TYPE_BOOLEAN;
661 break;
662 case PURPLE_ROOMLIST_FIELD_INT:
663 types[j] = G_TYPE_INT;
664 break;
665 case PURPLE_ROOMLIST_FIELD_STRING:
666 types[j] = G_TYPE_STRING;
667 break;
668 }
669 }
670
671 model = gtk_tree_store_newv(columns, types);
672 g_free(types);
673
674 tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
675
676 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
677 g_signal_connect(G_OBJECT(selection), "changed",
678 G_CALLBACK(selection_changed_cb), grl);
679
680 g_object_unref(model);
681
682 grl->model = model;
683 grl->tree = tree;
684 gtk_widget_show(grl->tree);
685
686 renderer = gtk_cell_renderer_text_new();
687 column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer,
688 "text", NAME_COLUMN, NULL);
689 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
690 GTK_TREE_VIEW_COLUMN_GROW_ONLY);
691 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
692 gtk_tree_view_column_set_sort_column_id(GTK_TREE_VIEW_COLUMN(column), NAME_COLUMN);
693 gtk_tree_view_column_set_reorderable(GTK_TREE_VIEW_COLUMN(column), TRUE);
694 gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
695
696 for (j = NUM_OF_COLUMNS, l = fields; l; l = l->next, j++) {
697 PurpleRoomlistField *f = l->data;
698
699 if (purple_roomlist_field_get_hidden(f))
700 continue;
701
702 renderer = gtk_cell_renderer_text_new();
703 column = gtk_tree_view_column_new_with_attributes(
704 purple_roomlist_field_get_label(f), renderer,
705 "text", j, NULL);
706 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
707 GTK_TREE_VIEW_COLUMN_GROW_ONLY);
708 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
709 gtk_tree_view_column_set_sort_column_id(GTK_TREE_VIEW_COLUMN(column), j);
710 gtk_tree_view_column_set_reorderable(GTK_TREE_VIEW_COLUMN(column), TRUE);
711 if (purple_roomlist_field_get_field_type(f) == PURPLE_ROOMLIST_FIELD_INT) {
712 gtk_tree_view_column_set_cell_data_func(column, renderer, int_cell_data_func,
713 GINT_TO_POINTER(j), NULL);
714 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(model), j, int_sort_func,
715 GINT_TO_POINTER(j), NULL);
716 }
717 gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
718 }
719
720 g_signal_connect(G_OBJECT(tree), "button-press-event", G_CALLBACK(room_click_cb), list);
721 g_signal_connect(G_OBJECT(tree), "row-expanded", G_CALLBACK(row_expanded_cb), list);
722 g_signal_connect(G_OBJECT(tree), "row-activated", G_CALLBACK(row_activated_cb), list);
723
724 gtk_widget_set_has_tooltip(tree, TRUE);
725 g_signal_connect(G_OBJECT(tree), "query-tooltip",
726 G_CALLBACK(pidgin_roomlist_query_tooltip), list);
727
728 /* Enable CTRL+F searching */
729 gtk_tree_view_set_search_column(GTK_TREE_VIEW(tree), NAME_COLUMN);
730 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(tree), _search_func, NULL, NULL);
731
732 }
733
734 static gboolean pidgin_progress_bar_pulse(gpointer data) 533 static gboolean pidgin_progress_bar_pulse(gpointer data)
735 { 534 {
736 PurpleRoomlist *list = data; 535 PurpleRoomlist *list = data;
737 PidginRoomlist *rl = NULL; 536 PidginRoomlist *rl = NULL;
738 537
747 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(rl->dialog->progress)); 546 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(rl->dialog->progress));
748 rl->dialog->pg_needs_pulse = FALSE; 547 rl->dialog->pg_needs_pulse = FALSE;
749 return TRUE; 548 return TRUE;
750 } 549 }
751 550
752 static void pidgin_roomlist_add_room(PurpleRoomlist *list, PurpleRoomlistRoom *room) 551 static void
753 { 552 pidgin_roomlist_add_room(PurpleRoomlist *list, PurpleRoomlistRoom *room) {
754 PidginRoomlist *rl = NULL; 553 PidginRoomlist *rl = NULL;
755 GtkTreeRowReference *rr, *parentrr = NULL;
756 GtkTreePath *path; 554 GtkTreePath *path;
757 GtkTreeIter iter, parent, child; 555 GtkTreeIter iter;
758 GList *l, *k;
759 int j;
760 gboolean append = TRUE;
761 556
762 rl = g_object_get_data(G_OBJECT(list), PIDGIN_ROOMLIST_UI_DATA); 557 rl = g_object_get_data(G_OBJECT(list), PIDGIN_ROOMLIST_UI_DATA);
763
764 rl->total_rooms++;
765 if (purple_roomlist_room_get_room_type(room) == PURPLE_ROOMLIST_ROOMTYPE_ROOM)
766 rl->num_rooms++;
767 558
768 if (rl->dialog) { 559 if (rl->dialog) {
769 if (rl->dialog->pg_update_to == 0) { 560 if (rl->dialog->pg_update_to == 0) {
770 g_object_ref(list); 561 g_object_ref(list);
771 rl->dialog->pg_update_to = g_timeout_add(100, pidgin_progress_bar_pulse, list); 562 rl->dialog->pg_update_to = g_timeout_add(100, pidgin_progress_bar_pulse, list);
772 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(rl->dialog->progress)); 563 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(rl->dialog->progress));
773 } else 564 } else
774 rl->dialog->pg_needs_pulse = TRUE; 565 rl->dialog->pg_needs_pulse = TRUE;
775 } 566 }
776 567
777 if (purple_roomlist_room_get_parent(room)) { 568 gtk_tree_store_append(rl->model, &iter, NULL);
778 parentrr = g_hash_table_lookup(rl->cats, purple_roomlist_room_get_parent(room));
779 path = gtk_tree_row_reference_get_path(parentrr);
780 if (path) {
781 PurpleRoomlistRoom *tmproom = NULL;
782
783 gtk_tree_model_get_iter(GTK_TREE_MODEL(rl->model), &parent, path);
784 gtk_tree_path_free(path);
785
786 if (gtk_tree_model_iter_children(GTK_TREE_MODEL(rl->model), &child, &parent)) {
787 gtk_tree_model_get(GTK_TREE_MODEL(rl->model), &child, ROOM_COLUMN, &tmproom, -1);
788 if (!tmproom)
789 append = FALSE;
790 }
791 }
792 }
793
794 if (append)
795 gtk_tree_store_append(rl->model, &iter, (parentrr ? &parent : NULL));
796 else
797 iter = child;
798
799 if (purple_roomlist_room_get_room_type(room) & PURPLE_ROOMLIST_ROOMTYPE_CATEGORY)
800 gtk_tree_store_append(rl->model, &child, &iter);
801 569
802 path = gtk_tree_model_get_path(GTK_TREE_MODEL(rl->model), &iter); 570 path = gtk_tree_model_get_path(GTK_TREE_MODEL(rl->model), &iter);
803 571
804 if (purple_roomlist_room_get_room_type(room) & PURPLE_ROOMLIST_ROOMTYPE_CATEGORY) {
805 rr = gtk_tree_row_reference_new(GTK_TREE_MODEL(rl->model), path);
806 g_hash_table_insert(rl->cats, room, rr);
807 }
808
809 gtk_tree_path_free(path); 572 gtk_tree_path_free(path);
810 573
811 gtk_tree_store_set(rl->model, &iter, NAME_COLUMN, purple_roomlist_room_get_name(room), -1); 574 gtk_tree_store_set(
812 gtk_tree_store_set(rl->model, &iter, ROOM_COLUMN, room, -1); 575 rl->model, &iter,
813 576 ROOM_COLUMN, room,
814 for (j = NUM_OF_COLUMNS, 577 NAME_COLUMN, purple_roomlist_room_get_name(room),
815 l = purple_roomlist_room_get_fields(room), 578 DESCRIPTION_COLUMN, purple_roomlist_room_get_description(room),
816 k = purple_roomlist_get_fields(list); 579 -1);
817 l && k; j++, l = l->next, k = k->next)
818 {
819 PurpleRoomlistField *f = k->data;
820 if (purple_roomlist_field_get_hidden(f))
821 continue;
822 gtk_tree_store_set(rl->model, &iter, j, l->data, -1);
823 }
824 } 580 }
825 581
826 static void 582 static void
827 pidgin_roomlist_in_progress(GObject *obj, G_GNUC_UNUSED GParamSpec *pspec, 583 pidgin_roomlist_in_progress(GObject *obj, G_GNUC_UNUSED GParamSpec *pspec,
828 gpointer data) 584 gpointer data)
848 } 604 }
849 605
850 static void 606 static void
851 pidgin_roomlist_destroy(PidginRoomlist *rl) 607 pidgin_roomlist_destroy(PidginRoomlist *rl)
852 { 608 {
853 g_hash_table_destroy(rl->cats);
854 g_free(rl); 609 g_free(rl);
855 } 610 }
856 611
857 static void 612 static void
858 pidgin_roomlist_new(PurpleRoomlist *list) 613 pidgin_roomlist_new(PurpleRoomlist *list)
860 PidginRoomlist *rl = g_new0(PidginRoomlist, 1); 615 PidginRoomlist *rl = g_new0(PidginRoomlist, 1);
861 616
862 g_object_set_data_full(G_OBJECT(list), PIDGIN_ROOMLIST_UI_DATA, rl, 617 g_object_set_data_full(G_OBJECT(list), PIDGIN_ROOMLIST_UI_DATA, rl,
863 (GDestroyNotify)pidgin_roomlist_destroy); 618 (GDestroyNotify)pidgin_roomlist_destroy);
864 619
865 rl->cats = g_hash_table_new_full( 620 rl->model = gtk_tree_store_new(3, G_TYPE_OBJECT, G_TYPE_STRING,
866 NULL, NULL, NULL, (GDestroyNotify)gtk_tree_row_reference_free); 621 G_TYPE_STRING);
867 622
868 g_signal_connect(list, "notify::in-progress", 623 g_signal_connect(list, "notify::in-progress",
869 G_CALLBACK(pidgin_roomlist_in_progress), rl); 624 G_CALLBACK(pidgin_roomlist_in_progress), rl);
870 } 625 }
871 626
872 static PurpleRoomlistUiOps ops = { 627 static PurpleRoomlistUiOps ops = {
873 pidgin_roomlist_dialog_show_with_account, 628 pidgin_roomlist_dialog_show_with_account,
874 pidgin_roomlist_new, 629 pidgin_roomlist_new,
875 pidgin_roomlist_set_fields, 630 NULL,
876 pidgin_roomlist_add_room, 631 pidgin_roomlist_add_room,
877 NULL, 632 NULL,
878 NULL, 633 NULL,
879 NULL, 634 NULL,
880 NULL 635 NULL

mercurial