3 * Pidgin is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
25 #include "pidginaccountchooser.h"
26 #include "pidginstock.h"
27 #include "pidgintooltip.h"
31 #include "connection.h"
34 #include "gtk3compat.h"
35 #include "gtkroomlist.h"
39 GtkWidget
*account_widget
;
43 GtkWidget
*stop_button
;
44 GtkWidget
*list_button
;
45 GtkWidget
*add_button
;
46 GtkWidget
*join_button
;
47 GtkWidget
*close_button
;
49 PurpleAccount
*account
;
50 PurpleRoomlist
*roomlist
;
52 gboolean pg_needs_pulse
;
54 } PidginRoomlistDialog
;
57 PidginRoomlistDialog
*dialog
;
60 GHashTable
*cats
; /* Meow. */
61 gint num_rooms
, total_rooms
;
63 GdkRectangle tip_rect
;
64 PangoLayout
*tip_layout
;
65 PangoLayout
*tip_name_layout
;
78 static GList
*roomlists
= NULL
;
80 static gint
delete_win_cb(GtkWidget
*w
, GdkEventAny
*e
, gpointer d
)
82 PidginRoomlistDialog
*dialog
= d
;
84 if (dialog
->roomlist
&& purple_roomlist_get_in_progress(dialog
->roomlist
))
85 purple_roomlist_cancel_get_list(dialog
->roomlist
);
87 if (dialog
->pg_update_to
> 0)
88 g_source_remove(dialog
->pg_update_to
);
90 if (dialog
->roomlist
) {
91 PidginRoomlist
*rl
= purple_roomlist_get_ui_data(dialog
->roomlist
);
93 if (dialog
->pg_update_to
> 0)
94 /* yes, that's right, unref it twice. */
95 g_object_unref(dialog
->roomlist
);
99 g_object_unref(dialog
->roomlist
);
102 dialog
->progress
= NULL
;
109 dialog_select_account_cb(GtkWidget
*chooser
, PidginRoomlistDialog
*dialog
)
111 PurpleAccount
*account
= pidgin_account_chooser_get_selected(chooser
);
112 gboolean change
= (account
!= dialog
->account
);
113 dialog
->account
= account
;
115 if (change
&& dialog
->roomlist
) {
116 PidginRoomlist
*rl
= purple_roomlist_get_ui_data(dialog
->roomlist
);
118 gtk_widget_destroy(rl
->tree
);
121 g_object_unref(dialog
->roomlist
);
122 dialog
->roomlist
= NULL
;
126 static void list_button_cb(GtkButton
*button
, PidginRoomlistDialog
*dialog
)
128 PurpleConnection
*gc
;
131 gc
= purple_account_get_connection(dialog
->account
);
135 if (dialog
->roomlist
!= NULL
) {
136 rl
= purple_roomlist_get_ui_data(dialog
->roomlist
);
137 gtk_widget_destroy(rl
->tree
);
138 g_object_unref(dialog
->roomlist
);
141 dialog
->roomlist
= purple_roomlist_get_list(gc
);
142 if (!dialog
->roomlist
)
144 g_object_ref(dialog
->roomlist
);
145 rl
= purple_roomlist_get_ui_data(dialog
->roomlist
);
148 if (dialog
->account_widget
)
149 gtk_widget_set_sensitive(dialog
->account_widget
, FALSE
);
151 gtk_container_add(GTK_CONTAINER(dialog
->sw
), rl
->tree
);
153 /* some protocols (not bundled with libpurple) finish getting their
154 * room list immediately */
155 if(purple_roomlist_get_in_progress(dialog
->roomlist
)) {
156 gtk_widget_set_sensitive(dialog
->stop_button
, TRUE
);
157 gtk_widget_set_sensitive(dialog
->list_button
, FALSE
);
159 gtk_widget_set_sensitive(dialog
->stop_button
, FALSE
);
160 gtk_widget_set_sensitive(dialog
->list_button
, TRUE
);
162 gtk_widget_set_sensitive(dialog
->add_button
, FALSE
);
163 gtk_widget_set_sensitive(dialog
->join_button
, FALSE
);
166 static void stop_button_cb(GtkButton
*button
, PidginRoomlistDialog
*dialog
)
168 purple_roomlist_cancel_get_list(dialog
->roomlist
);
170 if (dialog
->account_widget
)
171 gtk_widget_set_sensitive(dialog
->account_widget
, TRUE
);
173 gtk_widget_set_sensitive(dialog
->stop_button
, FALSE
);
174 gtk_widget_set_sensitive(dialog
->list_button
, TRUE
);
175 gtk_widget_set_sensitive(dialog
->add_button
, FALSE
);
176 gtk_widget_set_sensitive(dialog
->join_button
, FALSE
);
179 static void close_button_cb(GtkButton
*button
, PidginRoomlistDialog
*dialog
)
181 GtkWidget
*window
= dialog
->window
;
183 delete_win_cb(NULL
, NULL
, dialog
);
184 gtk_widget_destroy(window
);
187 struct _menu_cb_info
{
188 PurpleRoomlist
*list
;
189 PurpleRoomlistRoom
*room
;
193 selection_changed_cb(GtkTreeSelection
*selection
, PidginRoomlist
*grl
) {
196 PurpleRoomlistRoom
*room
;
197 static struct _menu_cb_info
*info
;
198 PidginRoomlistDialog
*dialog
= grl
->dialog
;
200 if (gtk_tree_selection_get_selected(selection
, NULL
, &iter
)) {
202 gtk_tree_model_get_value(GTK_TREE_MODEL(grl
->model
), &iter
, ROOM_COLUMN
, &val
);
203 room
= g_value_get_pointer(&val
);
204 if (!room
|| !(purple_roomlist_room_get_room_type(room
) & PURPLE_ROOMLIST_ROOMTYPE_ROOM
)) {
205 gtk_widget_set_sensitive(dialog
->join_button
, FALSE
);
206 gtk_widget_set_sensitive(dialog
->add_button
, FALSE
);
210 info
= g_new0(struct _menu_cb_info
, 1);
211 info
->list
= dialog
->roomlist
;
214 g_object_set_data_full(G_OBJECT(dialog
->join_button
), "room-info",
216 g_object_set_data(G_OBJECT(dialog
->add_button
), "room-info", info
);
218 gtk_widget_set_sensitive(dialog
->add_button
, TRUE
);
219 gtk_widget_set_sensitive(dialog
->join_button
, TRUE
);
221 gtk_widget_set_sensitive(dialog
->add_button
, FALSE
);
222 gtk_widget_set_sensitive(dialog
->join_button
, FALSE
);
226 static void do_add_room_cb(GtkWidget
*w
, struct _menu_cb_info
*info
)
229 PurpleAccount
*account
= purple_roomlist_get_account(info
->list
);
230 PurpleConnection
*gc
= purple_account_get_connection(account
);
231 PurpleProtocol
*protocol
= NULL
;
234 protocol
= purple_connection_get_protocol(gc
);
236 if(protocol
!= NULL
&& PURPLE_PROTOCOL_IMPLEMENTS(protocol
, ROOMLIST
, room_serialize
))
237 name
= purple_protocol_roomlist_iface_room_serialize(protocol
, info
->room
);
239 name
= g_strdup(purple_roomlist_room_get_name(info
->room
));
241 purple_blist_request_add_chat(account
, NULL
, NULL
, name
);
246 static void add_room_to_blist_cb(GtkButton
*button
, PidginRoomlistDialog
*dialog
)
248 PurpleRoomlist
*rl
= dialog
->roomlist
;
249 PidginRoomlist
*grl
= purple_roomlist_get_ui_data(rl
);
250 struct _menu_cb_info
*info
= g_object_get_data(G_OBJECT(button
), "room-info");
253 do_add_room_cb(grl
->tree
, info
);
256 static void do_join_cb(GtkWidget
*w
, struct _menu_cb_info
*info
)
258 purple_roomlist_room_join(info
->list
, info
->room
);
261 static void join_button_cb(GtkButton
*button
, PidginRoomlistDialog
*dialog
)
263 PurpleRoomlist
*rl
= dialog
->roomlist
;
264 PidginRoomlist
*grl
= purple_roomlist_get_ui_data(rl
);
265 struct _menu_cb_info
*info
= g_object_get_data(G_OBJECT(button
), "room-info");
268 do_join_cb(grl
->tree
, info
);
271 static void row_activated_cb(GtkTreeView
*tv
, GtkTreePath
*path
, GtkTreeViewColumn
*arg2
,
272 PurpleRoomlist
*list
)
274 PidginRoomlist
*grl
= purple_roomlist_get_ui_data(list
);
276 PurpleRoomlistRoom
*room
;
278 struct _menu_cb_info info
;
280 gtk_tree_model_get_iter(GTK_TREE_MODEL(grl
->model
), &iter
, path
);
282 gtk_tree_model_get_value(GTK_TREE_MODEL(grl
->model
), &iter
, ROOM_COLUMN
, &val
);
283 room
= g_value_get_pointer(&val
);
284 if (!room
|| !(purple_roomlist_room_get_room_type(room
) & PURPLE_ROOMLIST_ROOMTYPE_ROOM
))
290 do_join_cb(GTK_WIDGET(tv
), &info
);
293 static gboolean
room_click_cb(GtkWidget
*tv
, GdkEventButton
*event
, PurpleRoomlist
*list
)
296 PidginRoomlist
*grl
= purple_roomlist_get_ui_data(list
);
298 PurpleRoomlistRoom
*room
;
301 static struct _menu_cb_info info
; /* XXX? */
303 if (!gdk_event_triggers_context_menu((GdkEvent
*)event
))
306 /* Here we figure out which room was clicked */
307 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv
), event
->x
, event
->y
, &path
, NULL
, NULL
, NULL
))
309 gtk_tree_model_get_iter(GTK_TREE_MODEL(grl
->model
), &iter
, path
);
310 gtk_tree_path_free(path
);
312 gtk_tree_model_get_value (GTK_TREE_MODEL(grl
->model
), &iter
, ROOM_COLUMN
, &val
);
313 room
= g_value_get_pointer(&val
);
315 if (!room
|| !(purple_roomlist_room_get_room_type(room
) & PURPLE_ROOMLIST_ROOMTYPE_ROOM
))
321 menu
= gtk_menu_new();
322 pidgin_new_menu_item(menu
, _("_Join"), PIDGIN_STOCK_CHAT
,
323 G_CALLBACK(do_join_cb
), &info
);
324 pidgin_new_menu_item(menu
, _("_Add"), GTK_STOCK_ADD
,
325 G_CALLBACK(do_add_room_cb
), &info
);
327 gtk_widget_show_all(menu
);
328 gtk_menu_popup_at_pointer(GTK_MENU(menu
), (GdkEvent
*)event
);
333 static void row_expanded_cb(GtkTreeView
*treeview
, GtkTreeIter
*arg1
, GtkTreePath
*arg2
, gpointer user_data
)
335 PurpleRoomlist
*list
= user_data
;
336 PurpleRoomlistRoom
*category
;
340 gtk_tree_model_get_value(gtk_tree_view_get_model(treeview
), arg1
, ROOM_COLUMN
, &val
);
341 category
= g_value_get_pointer(&val
);
343 if (!purple_roomlist_room_get_expanded_once(category
)) {
344 purple_roomlist_expand_category(list
, category
);
345 purple_roomlist_room_set_expanded_once(category
, TRUE
);
349 #define SMALL_SPACE 6
350 #define TOOLTIP_BORDER 12
353 pidgin_roomlist_paint_tooltip(GtkWidget
*widget
, cairo_t
*cr
, gpointer user_data
)
355 PurpleRoomlist
*list
= user_data
;
356 PidginRoomlist
*grl
= purple_roomlist_get_ui_data(list
);
357 int current_height
, max_width
;
359 GtkTextDirection dir
= gtk_widget_get_direction(GTK_WIDGET(grl
->tree
));
360 GtkStyleContext
*context
;
362 context
= gtk_widget_get_style_context(grl
->tipwindow
);
363 gtk_style_context_add_class(context
, GTK_STYLE_CLASS_TOOLTIP
);
365 max_text_width
= MAX(grl
->tip_width
, grl
->tip_name_width
);
366 max_width
= TOOLTIP_BORDER
+ SMALL_SPACE
+ max_text_width
+ TOOLTIP_BORDER
;
370 if (dir
== GTK_TEXT_DIR_RTL
) {
371 gtk_render_layout(context
, cr
,
372 max_width
- (TOOLTIP_BORDER
+ SMALL_SPACE
) - PANGO_PIXELS(600000),
374 grl
->tip_name_layout
);
376 gtk_render_layout(context
, cr
,
377 TOOLTIP_BORDER
+ SMALL_SPACE
,
379 grl
->tip_name_layout
);
381 if (dir
!= GTK_TEXT_DIR_RTL
) {
382 gtk_render_layout(context
, cr
,
383 TOOLTIP_BORDER
+ SMALL_SPACE
,
384 current_height
+ grl
->tip_name_height
,
387 gtk_render_layout(context
, cr
,
388 max_width
- (TOOLTIP_BORDER
+ SMALL_SPACE
) - PANGO_PIXELS(600000),
389 current_height
+ grl
->tip_name_height
,
396 static gboolean
pidgin_roomlist_create_tip(PurpleRoomlist
*list
, GtkTreePath
*path
)
398 PidginRoomlist
*grl
= purple_roomlist_get_ui_data(list
);
399 PurpleRoomlistRoom
*room
;
402 gchar
*name
, *tmp
, *node_name
;
403 GString
*tooltip_text
= NULL
;
406 gboolean first
= TRUE
;
409 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv
), grl
->tip_rect
.x
, grl
->tip_rect
.y
+ (grl
->tip_rect
.height
/2),
410 &path
, NULL
, NULL
, NULL
))
413 gtk_tree_model_get_iter(GTK_TREE_MODEL(grl
->model
), &iter
, path
);
416 gtk_tree_model_get_value(GTK_TREE_MODEL(grl
->model
), &iter
, ROOM_COLUMN
, &val
);
417 room
= g_value_get_pointer(&val
);
419 if (!room
|| !(purple_roomlist_room_get_room_type(room
) & PURPLE_ROOMLIST_ROOMTYPE_ROOM
))
422 tooltip_text
= g_string_new("");
423 gtk_tree_model_get(GTK_TREE_MODEL(grl
->model
), &iter
, NAME_COLUMN
, &name
, -1);
425 for (j
= NUM_OF_COLUMNS
,
426 l
= purple_roomlist_room_get_fields(room
),
427 k
= purple_roomlist_get_fields(list
);
428 l
&& k
; j
++, l
= l
->next
, k
= k
->next
)
430 PurpleRoomlistField
*f
= k
->data
;
432 if (purple_roomlist_field_get_hidden(f
))
434 label
= g_markup_escape_text(purple_roomlist_field_get_label(f
), -1);
435 switch (purple_roomlist_field_get_field_type(f
)) {
436 case PURPLE_ROOMLIST_FIELD_BOOL
:
437 g_string_append_printf(tooltip_text
, "%s<b>%s:</b> %s", first
? "" : "\n", label
, l
->data
? "True" : "False");
439 case PURPLE_ROOMLIST_FIELD_INT
:
440 g_string_append_printf(tooltip_text
, "%s<b>%s:</b> %d", first
? "" : "\n", label
, GPOINTER_TO_INT(l
->data
));
442 case PURPLE_ROOMLIST_FIELD_STRING
:
443 tmp
= g_markup_escape_text((char *)l
->data
, -1);
444 g_string_append_printf(tooltip_text
, "%s<b>%s:</b> %s", first
? "" : "\n", label
, tmp
);
452 grl
->tip_layout
= gtk_widget_create_pango_layout(grl
->tipwindow
, NULL
);
453 grl
->tip_name_layout
= gtk_widget_create_pango_layout(grl
->tipwindow
, NULL
);
455 tmp
= g_markup_escape_text(name
, -1);
457 node_name
= g_strdup_printf("<span size='x-large' weight='bold'>%s</span>", tmp
);
460 pango_layout_set_markup(grl
->tip_layout
, tooltip_text
->str
, -1);
461 pango_layout_set_wrap(grl
->tip_layout
, PANGO_WRAP_WORD
);
462 pango_layout_set_width(grl
->tip_layout
, 600000);
464 pango_layout_get_size (grl
->tip_layout
, &grl
->tip_width
, &grl
->tip_height
);
465 grl
->tip_width
= PANGO_PIXELS(grl
->tip_width
);
466 grl
->tip_height
= PANGO_PIXELS(grl
->tip_height
);
468 pango_layout_set_markup(grl
->tip_name_layout
, node_name
, -1);
469 pango_layout_set_wrap(grl
->tip_name_layout
, PANGO_WRAP_WORD
);
470 pango_layout_set_width(grl
->tip_name_layout
, 600000);
472 pango_layout_get_size (grl
->tip_name_layout
, &grl
->tip_name_width
, &grl
->tip_name_height
);
473 grl
->tip_name_width
= PANGO_PIXELS(grl
->tip_name_width
) + SMALL_SPACE
;
474 grl
->tip_name_height
= MAX(PANGO_PIXELS(grl
->tip_name_height
), SMALL_SPACE
);
477 g_string_free(tooltip_text
, TRUE
);
483 pidgin_roomlist_create_tooltip(GtkWidget
*widget
, GtkTreePath
*path
,
484 gpointer data
, int *w
, int *h
)
486 PurpleRoomlist
*list
= data
;
487 PidginRoomlist
*grl
= purple_roomlist_get_ui_data(list
);
488 grl
->tipwindow
= widget
;
489 if (!pidgin_roomlist_create_tip(data
, path
))
492 *w
= TOOLTIP_BORDER
+ SMALL_SPACE
+
493 MAX(grl
->tip_width
, grl
->tip_name_width
) + TOOLTIP_BORDER
;
495 *h
= TOOLTIP_BORDER
+ grl
->tip_height
+ grl
->tip_name_height
500 static gboolean
account_filter_func(PurpleAccount
*account
)
502 PurpleConnection
*conn
= purple_account_get_connection(account
);
503 PurpleProtocol
*protocol
= NULL
;
505 if (conn
&& PURPLE_CONNECTION_IS_CONNECTED(conn
))
506 protocol
= purple_connection_get_protocol(conn
);
508 return (protocol
&& PURPLE_PROTOCOL_IMPLEMENTS(protocol
, ROOMLIST
, get_list
));
512 pidgin_roomlist_is_showable()
515 PurpleConnection
*gc
;
517 for (c
= purple_connections_get_all(); c
!= NULL
; c
= c
->next
) {
520 if (account_filter_func(purple_connection_get_account(gc
)))
527 static PidginRoomlistDialog
*
528 pidgin_roomlist_dialog_new_with_account(PurpleAccount
*account
)
530 PidginRoomlistDialog
*dialog
;
531 GtkWidget
*window
, *vbox
, *vbox2
, *bbox
;
533 dialog
= g_new0(PidginRoomlistDialog
, 1);
534 dialog
->account
= account
;
536 /* Create the window. */
537 dialog
->window
= window
= pidgin_create_dialog(_("Room List"), 0, "room list", TRUE
);
539 g_signal_connect(G_OBJECT(window
), "delete_event",
540 G_CALLBACK(delete_win_cb
), dialog
);
542 /* Create the parent vbox for everything. */
543 vbox
= pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(window
), FALSE
, PIDGIN_HIG_BORDER
);
545 vbox2
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, PIDGIN_HIG_BORDER
);
546 gtk_container_add(GTK_CONTAINER(vbox
), vbox2
);
547 gtk_widget_show(vbox2
);
549 /* accounts dropdown list */
550 dialog
->account_widget
=
551 pidgin_account_chooser_new(dialog
->account
, FALSE
);
552 pidgin_account_chooser_set_filter_func(
553 PIDGIN_ACCOUNT_CHOOSER(dialog
->account_widget
),
554 account_filter_func
);
555 g_signal_connect(dialog
->account_widget
, "changed",
556 G_CALLBACK(dialog_select_account_cb
), dialog
);
557 if (!dialog
->account
) /* this is normally null, and we normally don't care what the first selected item is */
558 dialog
->account
= pidgin_account_chooser_get_selected(
559 dialog
->account_widget
);
560 pidgin_add_widget_to_vbox(GTK_BOX(vbox2
), _("_Account:"), NULL
, dialog
->account_widget
, TRUE
, NULL
);
562 /* scrolled window */
563 dialog
->sw
= pidgin_make_scrollable(NULL
, GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
, GTK_SHADOW_IN
, -1, 250);
564 gtk_box_pack_start(GTK_BOX(vbox2
), dialog
->sw
, TRUE
, TRUE
, 0);
567 dialog
->progress
= gtk_progress_bar_new();
568 gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(dialog
->progress
), 0.1);
569 gtk_box_pack_start(GTK_BOX(vbox2
), dialog
->progress
, FALSE
, FALSE
, 0);
570 gtk_widget_show(dialog
->progress
);
573 bbox
= pidgin_dialog_get_action_area(GTK_DIALOG(window
));
574 gtk_box_set_spacing(GTK_BOX(bbox
), PIDGIN_HIG_BOX_SPACE
);
575 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox
), GTK_BUTTONBOX_END
);
578 dialog
->stop_button
= pidgin_dialog_add_button(GTK_DIALOG(window
), GTK_STOCK_STOP
,
579 G_CALLBACK(stop_button_cb
), dialog
);
580 gtk_widget_set_sensitive(dialog
->stop_button
, FALSE
);
583 dialog
->list_button
= pidgin_pixbuf_button_from_stock(_("_Get List"), GTK_STOCK_REFRESH
,
584 PIDGIN_BUTTON_HORIZONTAL
);
585 gtk_box_pack_start(GTK_BOX(bbox
), dialog
->list_button
, FALSE
, FALSE
, 0);
586 g_signal_connect(G_OBJECT(dialog
->list_button
), "clicked",
587 G_CALLBACK(list_button_cb
), dialog
);
588 gtk_widget_show(dialog
->list_button
);
591 dialog
->add_button
= pidgin_pixbuf_button_from_stock(_("_Add Chat"), GTK_STOCK_ADD
,
592 PIDGIN_BUTTON_HORIZONTAL
);
593 gtk_box_pack_start(GTK_BOX(bbox
), dialog
->add_button
, FALSE
, FALSE
, 0);
594 g_signal_connect(G_OBJECT(dialog
->add_button
), "clicked",
595 G_CALLBACK(add_room_to_blist_cb
), dialog
);
596 gtk_widget_set_sensitive(dialog
->add_button
, FALSE
);
597 gtk_widget_show(dialog
->add_button
);
600 dialog
->join_button
= pidgin_pixbuf_button_from_stock(_("_Join"), PIDGIN_STOCK_CHAT
,
601 PIDGIN_BUTTON_HORIZONTAL
);
602 gtk_box_pack_start(GTK_BOX(bbox
), dialog
->join_button
, FALSE
, FALSE
, 0);
603 g_signal_connect(G_OBJECT(dialog
->join_button
), "clicked",
604 G_CALLBACK(join_button_cb
), dialog
);
605 gtk_widget_set_sensitive(dialog
->join_button
, FALSE
);
606 gtk_widget_show(dialog
->join_button
);
609 dialog
->close_button
= pidgin_dialog_add_button(GTK_DIALOG(window
), GTK_STOCK_CLOSE
,
610 G_CALLBACK(close_button_cb
), dialog
);
612 /* show the dialog window and return the dialog */
613 gtk_widget_show(dialog
->window
);
618 void pidgin_roomlist_dialog_show_with_account(PurpleAccount
*account
)
620 PidginRoomlistDialog
*dialog
= pidgin_roomlist_dialog_new_with_account(account
);
625 list_button_cb(GTK_BUTTON(dialog
->list_button
), dialog
);
628 void pidgin_roomlist_dialog_show(void)
630 pidgin_roomlist_dialog_new_with_account(NULL
);
633 static void pidgin_roomlist_new(PurpleRoomlist
*list
)
635 PidginRoomlist
*rl
= g_new0(PidginRoomlist
, 1);
637 purple_roomlist_set_ui_data(list
, rl
);
639 rl
->cats
= g_hash_table_new_full(NULL
, NULL
, NULL
, (GDestroyNotify
)gtk_tree_row_reference_free
);
641 roomlists
= g_list_append(roomlists
, list
);
644 static void int_cell_data_func(GtkTreeViewColumn
*col
, GtkCellRenderer
*renderer
,
645 GtkTreeModel
*model
, GtkTreeIter
*iter
, gpointer user_data
)
650 gtk_tree_model_get(model
, iter
, GPOINTER_TO_INT(user_data
), &myint
, -1);
653 g_snprintf(buf
, sizeof(buf
), "%d", myint
);
657 g_object_set(renderer
, "text", buf
, NULL
);
660 /* this sorts backwards on purpose, so that clicking name sorts a-z, while clicking users sorts
661 infinity-0. you can still click again to reverse it on any of them. */
662 static gint
int_sort_func(GtkTreeModel
*model
, GtkTreeIter
*a
, GtkTreeIter
*b
, gpointer user_data
)
668 gtk_tree_model_get(model
, a
, GPOINTER_TO_INT(user_data
), &c
, -1);
669 gtk_tree_model_get(model
, b
, GPOINTER_TO_INT(user_data
), &d
, -1);
680 _search_func(GtkTreeModel
*model
, gint column
, const gchar
*key
, GtkTreeIter
*iter
, gpointer search_data
)
683 gchar
*name
, *fold
, *fkey
;
685 gtk_tree_model_get(model
, iter
, column
, &name
, -1);
686 fold
= g_utf8_casefold(name
, -1);
687 fkey
= g_utf8_casefold(key
, -1);
689 result
= (g_strstr_len(fold
, strlen(fold
), fkey
) == NULL
);
698 static void pidgin_roomlist_set_fields(PurpleRoomlist
*list
, GList
*fields
)
700 PidginRoomlist
*grl
= purple_roomlist_get_ui_data(list
);
701 gint columns
= NUM_OF_COLUMNS
;
705 GtkCellRenderer
*renderer
;
706 GtkTreeViewColumn
*column
;
707 GtkTreeSelection
*selection
;
711 g_return_if_fail(grl
!= NULL
);
713 columns
+= g_list_length(fields
);
714 types
= g_new(GType
, columns
);
716 types
[NAME_COLUMN
] = G_TYPE_STRING
;
717 types
[ROOM_COLUMN
] = G_TYPE_POINTER
;
719 for (j
= NUM_OF_COLUMNS
, l
= fields
; l
; l
= l
->next
, j
++) {
720 PurpleRoomlistField
*f
= l
->data
;
722 switch (purple_roomlist_field_get_field_type(f
)) {
723 case PURPLE_ROOMLIST_FIELD_BOOL
:
724 types
[j
] = G_TYPE_BOOLEAN
;
726 case PURPLE_ROOMLIST_FIELD_INT
:
727 types
[j
] = G_TYPE_INT
;
729 case PURPLE_ROOMLIST_FIELD_STRING
:
730 types
[j
] = G_TYPE_STRING
;
735 model
= gtk_tree_store_newv(columns
, types
);
738 tree
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(model
));
740 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(tree
));
741 g_signal_connect(G_OBJECT(selection
), "changed",
742 G_CALLBACK(selection_changed_cb
), grl
);
744 g_object_unref(model
);
748 gtk_widget_show(grl
->tree
);
750 renderer
= gtk_cell_renderer_text_new();
751 column
= gtk_tree_view_column_new_with_attributes(_("Name"), renderer
,
752 "text", NAME_COLUMN
, NULL
);
753 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column
),
754 GTK_TREE_VIEW_COLUMN_GROW_ONLY
);
755 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column
), TRUE
);
756 gtk_tree_view_column_set_sort_column_id(GTK_TREE_VIEW_COLUMN(column
), NAME_COLUMN
);
757 gtk_tree_view_column_set_reorderable(GTK_TREE_VIEW_COLUMN(column
), TRUE
);
758 gtk_tree_view_append_column(GTK_TREE_VIEW(tree
), column
);
760 for (j
= NUM_OF_COLUMNS
, l
= fields
; l
; l
= l
->next
, j
++) {
761 PurpleRoomlistField
*f
= l
->data
;
763 if (purple_roomlist_field_get_hidden(f
))
766 renderer
= gtk_cell_renderer_text_new();
767 column
= gtk_tree_view_column_new_with_attributes(
768 purple_roomlist_field_get_label(f
), renderer
,
770 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column
),
771 GTK_TREE_VIEW_COLUMN_GROW_ONLY
);
772 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column
), TRUE
);
773 gtk_tree_view_column_set_sort_column_id(GTK_TREE_VIEW_COLUMN(column
), j
);
774 gtk_tree_view_column_set_reorderable(GTK_TREE_VIEW_COLUMN(column
), TRUE
);
775 if (purple_roomlist_field_get_field_type(f
) == PURPLE_ROOMLIST_FIELD_INT
) {
776 gtk_tree_view_column_set_cell_data_func(column
, renderer
, int_cell_data_func
,
777 GINT_TO_POINTER(j
), NULL
);
778 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(model
), j
, int_sort_func
,
779 GINT_TO_POINTER(j
), NULL
);
781 gtk_tree_view_append_column(GTK_TREE_VIEW(tree
), column
);
784 g_signal_connect(G_OBJECT(tree
), "button-press-event", G_CALLBACK(room_click_cb
), list
);
785 g_signal_connect(G_OBJECT(tree
), "row-expanded", G_CALLBACK(row_expanded_cb
), list
);
786 g_signal_connect(G_OBJECT(tree
), "row-activated", G_CALLBACK(row_activated_cb
), list
);
787 #if 0 /* uncomment this when the tooltips are slightly less annoying and more well behaved */
788 g_signal_connect(G_OBJECT(tree
), "motion-notify-event", G_CALLBACK(row_motion_cb
), list
);
789 g_signal_connect(G_OBJECT(tree
), "leave-notify-event", G_CALLBACK(row_leave_cb
), list
);
791 pidgin_tooltip_setup_for_treeview(tree
, list
,
792 pidgin_roomlist_create_tooltip
,
793 pidgin_roomlist_paint_tooltip
);
795 /* Enable CTRL+F searching */
796 gtk_tree_view_set_search_column(GTK_TREE_VIEW(tree
), NAME_COLUMN
);
797 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(tree
), _search_func
, NULL
, NULL
);
801 static gboolean
pidgin_progress_bar_pulse(gpointer data
)
803 PurpleRoomlist
*list
= data
;
804 PidginRoomlist
*rl
= purple_roomlist_get_ui_data(list
);
806 if (!rl
|| !rl
->dialog
|| !rl
->dialog
->pg_needs_pulse
) {
807 if (rl
&& rl
->dialog
)
808 rl
->dialog
->pg_update_to
= 0;
809 g_object_unref(list
);
813 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(rl
->dialog
->progress
));
814 rl
->dialog
->pg_needs_pulse
= FALSE
;
818 static void pidgin_roomlist_add_room(PurpleRoomlist
*list
, PurpleRoomlistRoom
*room
)
820 PidginRoomlist
*rl
= purple_roomlist_get_ui_data(list
);
821 GtkTreeRowReference
*rr
, *parentrr
= NULL
;
823 GtkTreeIter iter
, parent
, child
;
826 gboolean append
= TRUE
;
829 if (purple_roomlist_room_get_room_type(room
) == PURPLE_ROOMLIST_ROOMTYPE_ROOM
)
833 if (rl
->dialog
->pg_update_to
== 0) {
835 rl
->dialog
->pg_update_to
= g_timeout_add(100, pidgin_progress_bar_pulse
, list
);
836 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(rl
->dialog
->progress
));
838 rl
->dialog
->pg_needs_pulse
= TRUE
;
841 if (purple_roomlist_room_get_parent(room
)) {
842 parentrr
= g_hash_table_lookup(rl
->cats
, purple_roomlist_room_get_parent(room
));
843 path
= gtk_tree_row_reference_get_path(parentrr
);
845 PurpleRoomlistRoom
*tmproom
= NULL
;
847 gtk_tree_model_get_iter(GTK_TREE_MODEL(rl
->model
), &parent
, path
);
848 gtk_tree_path_free(path
);
850 if (gtk_tree_model_iter_children(GTK_TREE_MODEL(rl
->model
), &child
, &parent
)) {
851 gtk_tree_model_get(GTK_TREE_MODEL(rl
->model
), &child
, ROOM_COLUMN
, &tmproom
, -1);
859 gtk_tree_store_append(rl
->model
, &iter
, (parentrr
? &parent
: NULL
));
863 if (purple_roomlist_room_get_room_type(room
) & PURPLE_ROOMLIST_ROOMTYPE_CATEGORY
)
864 gtk_tree_store_append(rl
->model
, &child
, &iter
);
866 path
= gtk_tree_model_get_path(GTK_TREE_MODEL(rl
->model
), &iter
);
868 if (purple_roomlist_room_get_room_type(room
) & PURPLE_ROOMLIST_ROOMTYPE_CATEGORY
) {
869 rr
= gtk_tree_row_reference_new(GTK_TREE_MODEL(rl
->model
), path
);
870 g_hash_table_insert(rl
->cats
, room
, rr
);
873 gtk_tree_path_free(path
);
875 gtk_tree_store_set(rl
->model
, &iter
, NAME_COLUMN
, purple_roomlist_room_get_name(room
), -1);
876 gtk_tree_store_set(rl
->model
, &iter
, ROOM_COLUMN
, room
, -1);
878 for (j
= NUM_OF_COLUMNS
,
879 l
= purple_roomlist_room_get_fields(room
),
880 k
= purple_roomlist_get_fields(list
);
881 l
&& k
; j
++, l
= l
->next
, k
= k
->next
)
883 PurpleRoomlistField
*f
= k
->data
;
884 if (purple_roomlist_field_get_hidden(f
))
886 gtk_tree_store_set(rl
->model
, &iter
, j
, l
->data
, -1);
890 static void pidgin_roomlist_in_progress(PurpleRoomlist
*list
, gboolean in_progress
)
892 PidginRoomlist
*rl
= purple_roomlist_get_ui_data(list
);
894 if (!rl
|| !rl
->dialog
)
898 if (rl
->dialog
->account_widget
)
899 gtk_widget_set_sensitive(rl
->dialog
->account_widget
, FALSE
);
900 gtk_widget_set_sensitive(rl
->dialog
->stop_button
, TRUE
);
901 gtk_widget_set_sensitive(rl
->dialog
->list_button
, FALSE
);
903 rl
->dialog
->pg_needs_pulse
= FALSE
;
904 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(rl
->dialog
->progress
), 0.0);
905 if (rl
->dialog
->account_widget
)
906 gtk_widget_set_sensitive(rl
->dialog
->account_widget
, TRUE
);
907 gtk_widget_set_sensitive(rl
->dialog
->stop_button
, FALSE
);
908 gtk_widget_set_sensitive(rl
->dialog
->list_button
, TRUE
);
912 static void pidgin_roomlist_destroy(PurpleRoomlist
*list
)
914 PidginRoomlist
*rl
= purple_roomlist_get_ui_data(list
);
916 roomlists
= g_list_remove(roomlists
, list
);
918 g_return_if_fail(rl
!= NULL
);
920 g_hash_table_destroy(rl
->cats
);
922 purple_roomlist_set_ui_data(list
, NULL
);
925 static PurpleRoomlistUiOps ops
= {
926 pidgin_roomlist_dialog_show_with_account
,
928 pidgin_roomlist_set_fields
,
929 pidgin_roomlist_add_room
,
930 pidgin_roomlist_in_progress
,
931 pidgin_roomlist_destroy
,
939 void pidgin_roomlist_init(void)
941 purple_roomlist_set_ui_ops(&ops
);