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 "pidginstock.h"
26 #include "pidgintooltip.h"
30 #include "connection.h"
33 #include "gtk3compat.h"
34 #include "gtkroomlist.h"
36 typedef struct _PidginRoomlistDialog
{
38 GtkWidget
*account_widget
;
42 GtkWidget
*stop_button
;
43 GtkWidget
*list_button
;
44 GtkWidget
*add_button
;
45 GtkWidget
*join_button
;
46 GtkWidget
*close_button
;
48 PurpleAccount
*account
;
49 PurpleRoomlist
*roomlist
;
51 gboolean pg_needs_pulse
;
53 } PidginRoomlistDialog
;
55 typedef struct _PidginRoomlist
{
56 PidginRoomlistDialog
*dialog
;
59 GHashTable
*cats
; /* Meow. */
60 gint num_rooms
, total_rooms
;
62 GdkRectangle tip_rect
;
63 PangoLayout
*tip_layout
;
64 PangoLayout
*tip_name_layout
;
77 static GList
*roomlists
= NULL
;
79 static gint
delete_win_cb(GtkWidget
*w
, GdkEventAny
*e
, gpointer d
)
81 PidginRoomlistDialog
*dialog
= d
;
83 if (dialog
->roomlist
&& purple_roomlist_get_in_progress(dialog
->roomlist
))
84 purple_roomlist_cancel_get_list(dialog
->roomlist
);
86 if (dialog
->pg_update_to
> 0)
87 purple_timeout_remove(dialog
->pg_update_to
);
89 if (dialog
->roomlist
) {
90 PidginRoomlist
*rl
= purple_roomlist_get_ui_data(dialog
->roomlist
);
92 if (dialog
->pg_update_to
> 0)
93 /* yes, that's right, unref it twice. */
94 g_object_unref(dialog
->roomlist
);
98 g_object_unref(dialog
->roomlist
);
101 dialog
->progress
= NULL
;
107 static void dialog_select_account_cb(GObject
*w
, PurpleAccount
*account
,
108 PidginRoomlistDialog
*dialog
)
110 gboolean change
= (account
!= dialog
->account
);
111 dialog
->account
= account
;
113 if (change
&& dialog
->roomlist
) {
114 PidginRoomlist
*rl
= purple_roomlist_get_ui_data(dialog
->roomlist
);
116 gtk_widget_destroy(rl
->tree
);
119 g_object_unref(dialog
->roomlist
);
120 dialog
->roomlist
= NULL
;
124 static void list_button_cb(GtkButton
*button
, PidginRoomlistDialog
*dialog
)
126 PurpleConnection
*gc
;
129 gc
= purple_account_get_connection(dialog
->account
);
133 if (dialog
->roomlist
!= NULL
) {
134 rl
= purple_roomlist_get_ui_data(dialog
->roomlist
);
135 gtk_widget_destroy(rl
->tree
);
136 g_object_unref(dialog
->roomlist
);
139 dialog
->roomlist
= purple_roomlist_get_list(gc
);
140 if (!dialog
->roomlist
)
142 g_object_ref(dialog
->roomlist
);
143 rl
= purple_roomlist_get_ui_data(dialog
->roomlist
);
146 if (dialog
->account_widget
)
147 gtk_widget_set_sensitive(dialog
->account_widget
, FALSE
);
149 gtk_container_add(GTK_CONTAINER(dialog
->sw
), rl
->tree
);
151 /* some protocols (not bundled with libpurple) finish getting their
152 * room list immediately */
153 if(purple_roomlist_get_in_progress(dialog
->roomlist
)) {
154 gtk_widget_set_sensitive(dialog
->stop_button
, TRUE
);
155 gtk_widget_set_sensitive(dialog
->list_button
, FALSE
);
157 gtk_widget_set_sensitive(dialog
->stop_button
, FALSE
);
158 gtk_widget_set_sensitive(dialog
->list_button
, TRUE
);
160 gtk_widget_set_sensitive(dialog
->add_button
, FALSE
);
161 gtk_widget_set_sensitive(dialog
->join_button
, FALSE
);
164 static void stop_button_cb(GtkButton
*button
, PidginRoomlistDialog
*dialog
)
166 purple_roomlist_cancel_get_list(dialog
->roomlist
);
168 if (dialog
->account_widget
)
169 gtk_widget_set_sensitive(dialog
->account_widget
, TRUE
);
171 gtk_widget_set_sensitive(dialog
->stop_button
, FALSE
);
172 gtk_widget_set_sensitive(dialog
->list_button
, TRUE
);
173 gtk_widget_set_sensitive(dialog
->add_button
, FALSE
);
174 gtk_widget_set_sensitive(dialog
->join_button
, FALSE
);
177 static void close_button_cb(GtkButton
*button
, PidginRoomlistDialog
*dialog
)
179 GtkWidget
*window
= dialog
->window
;
181 delete_win_cb(NULL
, NULL
, dialog
);
182 gtk_widget_destroy(window
);
185 struct _menu_cb_info
{
186 PurpleRoomlist
*list
;
187 PurpleRoomlistRoom
*room
;
191 selection_changed_cb(GtkTreeSelection
*selection
, PidginRoomlist
*grl
) {
194 PurpleRoomlistRoom
*room
;
195 static struct _menu_cb_info
*info
;
196 PidginRoomlistDialog
*dialog
= grl
->dialog
;
198 if (gtk_tree_selection_get_selected(selection
, NULL
, &iter
)) {
200 gtk_tree_model_get_value(GTK_TREE_MODEL(grl
->model
), &iter
, ROOM_COLUMN
, &val
);
201 room
= g_value_get_pointer(&val
);
202 if (!room
|| !(purple_roomlist_room_get_room_type(room
) & PURPLE_ROOMLIST_ROOMTYPE_ROOM
)) {
203 gtk_widget_set_sensitive(dialog
->join_button
, FALSE
);
204 gtk_widget_set_sensitive(dialog
->add_button
, FALSE
);
208 info
= g_new0(struct _menu_cb_info
, 1);
209 info
->list
= dialog
->roomlist
;
212 g_object_set_data_full(G_OBJECT(dialog
->join_button
), "room-info",
214 g_object_set_data(G_OBJECT(dialog
->add_button
), "room-info", info
);
216 gtk_widget_set_sensitive(dialog
->add_button
, TRUE
);
217 gtk_widget_set_sensitive(dialog
->join_button
, TRUE
);
219 gtk_widget_set_sensitive(dialog
->add_button
, FALSE
);
220 gtk_widget_set_sensitive(dialog
->join_button
, FALSE
);
224 static void do_add_room_cb(GtkWidget
*w
, struct _menu_cb_info
*info
)
227 PurpleAccount
*account
= purple_roomlist_get_account(info
->list
);
228 PurpleConnection
*gc
= purple_account_get_connection(account
);
229 PurpleProtocol
*protocol
= NULL
;
232 protocol
= purple_connection_get_protocol(gc
);
234 if(protocol
!= NULL
&& PURPLE_PROTOCOL_IMPLEMENTS(protocol
, ROOMLIST_IFACE
, room_serialize
))
235 name
= purple_protocol_roomlist_iface_room_serialize(protocol
, info
->room
);
237 name
= g_strdup(purple_roomlist_room_get_name(info
->room
));
239 purple_blist_request_add_chat(account
, NULL
, NULL
, name
);
244 static void add_room_to_blist_cb(GtkButton
*button
, PidginRoomlistDialog
*dialog
)
246 PurpleRoomlist
*rl
= dialog
->roomlist
;
247 PidginRoomlist
*grl
= purple_roomlist_get_ui_data(rl
);
248 struct _menu_cb_info
*info
= g_object_get_data(G_OBJECT(button
), "room-info");
251 do_add_room_cb(grl
->tree
, info
);
254 static void do_join_cb(GtkWidget
*w
, struct _menu_cb_info
*info
)
256 purple_roomlist_room_join(info
->list
, info
->room
);
259 static void join_button_cb(GtkButton
*button
, PidginRoomlistDialog
*dialog
)
261 PurpleRoomlist
*rl
= dialog
->roomlist
;
262 PidginRoomlist
*grl
= purple_roomlist_get_ui_data(rl
);
263 struct _menu_cb_info
*info
= g_object_get_data(G_OBJECT(button
), "room-info");
266 do_join_cb(grl
->tree
, info
);
269 static void row_activated_cb(GtkTreeView
*tv
, GtkTreePath
*path
, GtkTreeViewColumn
*arg2
,
270 PurpleRoomlist
*list
)
272 PidginRoomlist
*grl
= purple_roomlist_get_ui_data(list
);
274 PurpleRoomlistRoom
*room
;
276 struct _menu_cb_info info
;
278 gtk_tree_model_get_iter(GTK_TREE_MODEL(grl
->model
), &iter
, path
);
280 gtk_tree_model_get_value(GTK_TREE_MODEL(grl
->model
), &iter
, ROOM_COLUMN
, &val
);
281 room
= g_value_get_pointer(&val
);
282 if (!room
|| !(purple_roomlist_room_get_room_type(room
) & PURPLE_ROOMLIST_ROOMTYPE_ROOM
))
288 do_join_cb(GTK_WIDGET(tv
), &info
);
291 static gboolean
room_click_cb(GtkWidget
*tv
, GdkEventButton
*event
, PurpleRoomlist
*list
)
294 PidginRoomlist
*grl
= purple_roomlist_get_ui_data(list
);
296 PurpleRoomlistRoom
*room
;
299 static struct _menu_cb_info info
; /* XXX? */
301 if (event
->button
!= 3 || event
->type
!= GDK_BUTTON_PRESS
)
304 /* Here we figure out which room was clicked */
305 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv
), event
->x
, event
->y
, &path
, NULL
, NULL
, NULL
))
307 gtk_tree_model_get_iter(GTK_TREE_MODEL(grl
->model
), &iter
, path
);
308 gtk_tree_path_free(path
);
310 gtk_tree_model_get_value (GTK_TREE_MODEL(grl
->model
), &iter
, ROOM_COLUMN
, &val
);
311 room
= g_value_get_pointer(&val
);
313 if (!room
|| !(purple_roomlist_room_get_room_type(room
) & PURPLE_ROOMLIST_ROOMTYPE_ROOM
))
319 menu
= gtk_menu_new();
320 pidgin_new_menu_item(menu
, _("_Join"), PIDGIN_STOCK_CHAT
,
321 G_CALLBACK(do_join_cb
), &info
);
322 pidgin_new_menu_item(menu
, _("_Add"), GTK_STOCK_ADD
,
323 G_CALLBACK(do_add_room_cb
), &info
);
325 gtk_widget_show_all(menu
);
326 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
, 3, event
->time
);
331 static void row_expanded_cb(GtkTreeView
*treeview
, GtkTreeIter
*arg1
, GtkTreePath
*arg2
, gpointer user_data
)
333 PurpleRoomlist
*list
= user_data
;
334 PurpleRoomlistRoom
*category
;
338 gtk_tree_model_get_value(gtk_tree_view_get_model(treeview
), arg1
, ROOM_COLUMN
, &val
);
339 category
= g_value_get_pointer(&val
);
341 if (!purple_roomlist_room_get_expanded_once(category
)) {
342 purple_roomlist_expand_category(list
, category
);
343 purple_roomlist_room_set_expanded_once(category
, TRUE
);
347 #define SMALL_SPACE 6
348 #define TOOLTIP_BORDER 12
351 pidgin_roomlist_paint_tooltip(GtkWidget
*widget
, cairo_t
*cr
, gpointer user_data
)
353 PurpleRoomlist
*list
= user_data
;
354 PidginRoomlist
*grl
= purple_roomlist_get_ui_data(list
);
355 int current_height
, max_width
;
357 GtkTextDirection dir
= gtk_widget_get_direction(GTK_WIDGET(grl
->tree
));
358 GtkStyleContext
*context
;
360 context
= gtk_widget_get_style_context(grl
->tipwindow
);
361 gtk_style_context_add_class(context
, GTK_STYLE_CLASS_TOOLTIP
);
363 max_text_width
= MAX(grl
->tip_width
, grl
->tip_name_width
);
364 max_width
= TOOLTIP_BORDER
+ SMALL_SPACE
+ max_text_width
+ TOOLTIP_BORDER
;
368 if (dir
== GTK_TEXT_DIR_RTL
) {
369 gtk_render_layout(context
, cr
,
370 max_width
- (TOOLTIP_BORDER
+ SMALL_SPACE
) - PANGO_PIXELS(600000),
372 grl
->tip_name_layout
);
374 gtk_render_layout(context
, cr
,
375 TOOLTIP_BORDER
+ SMALL_SPACE
,
377 grl
->tip_name_layout
);
379 if (dir
!= GTK_TEXT_DIR_RTL
) {
380 gtk_render_layout(context
, cr
,
381 TOOLTIP_BORDER
+ SMALL_SPACE
,
382 current_height
+ grl
->tip_name_height
,
385 gtk_render_layout(context
, cr
,
386 max_width
- (TOOLTIP_BORDER
+ SMALL_SPACE
) - PANGO_PIXELS(600000),
387 current_height
+ grl
->tip_name_height
,
394 static gboolean
pidgin_roomlist_create_tip(PurpleRoomlist
*list
, GtkTreePath
*path
)
396 PidginRoomlist
*grl
= purple_roomlist_get_ui_data(list
);
397 PurpleRoomlistRoom
*room
;
400 gchar
*name
, *tmp
, *node_name
;
401 GString
*tooltip_text
= NULL
;
404 gboolean first
= TRUE
;
407 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),
408 &path
, NULL
, NULL
, NULL
))
411 gtk_tree_model_get_iter(GTK_TREE_MODEL(grl
->model
), &iter
, path
);
414 gtk_tree_model_get_value(GTK_TREE_MODEL(grl
->model
), &iter
, ROOM_COLUMN
, &val
);
415 room
= g_value_get_pointer(&val
);
417 if (!room
|| !(purple_roomlist_room_get_room_type(room
) & PURPLE_ROOMLIST_ROOMTYPE_ROOM
))
420 tooltip_text
= g_string_new("");
421 gtk_tree_model_get(GTK_TREE_MODEL(grl
->model
), &iter
, NAME_COLUMN
, &name
, -1);
423 for (j
= NUM_OF_COLUMNS
,
424 l
= purple_roomlist_room_get_fields(room
),
425 k
= purple_roomlist_get_fields(list
);
426 l
&& k
; j
++, l
= l
->next
, k
= k
->next
)
428 PurpleRoomlistField
*f
= k
->data
;
430 if (purple_roomlist_field_get_hidden(f
))
432 label
= g_markup_escape_text(purple_roomlist_field_get_label(f
), -1);
433 switch (purple_roomlist_field_get_field_type(f
)) {
434 case PURPLE_ROOMLIST_FIELD_BOOL
:
435 g_string_append_printf(tooltip_text
, "%s<b>%s:</b> %s", first
? "" : "\n", label
, l
->data
? "True" : "False");
437 case PURPLE_ROOMLIST_FIELD_INT
:
438 g_string_append_printf(tooltip_text
, "%s<b>%s:</b> %d", first
? "" : "\n", label
, GPOINTER_TO_INT(l
->data
));
440 case PURPLE_ROOMLIST_FIELD_STRING
:
441 tmp
= g_markup_escape_text((char *)l
->data
, -1);
442 g_string_append_printf(tooltip_text
, "%s<b>%s:</b> %s", first
? "" : "\n", label
, tmp
);
450 grl
->tip_layout
= gtk_widget_create_pango_layout(grl
->tipwindow
, NULL
);
451 grl
->tip_name_layout
= gtk_widget_create_pango_layout(grl
->tipwindow
, NULL
);
453 tmp
= g_markup_escape_text(name
, -1);
455 node_name
= g_strdup_printf("<span size='x-large' weight='bold'>%s</span>", tmp
);
458 pango_layout_set_markup(grl
->tip_layout
, tooltip_text
->str
, -1);
459 pango_layout_set_wrap(grl
->tip_layout
, PANGO_WRAP_WORD
);
460 pango_layout_set_width(grl
->tip_layout
, 600000);
462 pango_layout_get_size (grl
->tip_layout
, &grl
->tip_width
, &grl
->tip_height
);
463 grl
->tip_width
= PANGO_PIXELS(grl
->tip_width
);
464 grl
->tip_height
= PANGO_PIXELS(grl
->tip_height
);
466 pango_layout_set_markup(grl
->tip_name_layout
, node_name
, -1);
467 pango_layout_set_wrap(grl
->tip_name_layout
, PANGO_WRAP_WORD
);
468 pango_layout_set_width(grl
->tip_name_layout
, 600000);
470 pango_layout_get_size (grl
->tip_name_layout
, &grl
->tip_name_width
, &grl
->tip_name_height
);
471 grl
->tip_name_width
= PANGO_PIXELS(grl
->tip_name_width
) + SMALL_SPACE
;
472 grl
->tip_name_height
= MAX(PANGO_PIXELS(grl
->tip_name_height
), SMALL_SPACE
);
475 g_string_free(tooltip_text
, TRUE
);
481 pidgin_roomlist_create_tooltip(GtkWidget
*widget
, GtkTreePath
*path
,
482 gpointer data
, int *w
, int *h
)
484 PurpleRoomlist
*list
= data
;
485 PidginRoomlist
*grl
= purple_roomlist_get_ui_data(list
);
486 grl
->tipwindow
= widget
;
487 if (!pidgin_roomlist_create_tip(data
, path
))
490 *w
= TOOLTIP_BORDER
+ SMALL_SPACE
+
491 MAX(grl
->tip_width
, grl
->tip_name_width
) + TOOLTIP_BORDER
;
493 *h
= TOOLTIP_BORDER
+ grl
->tip_height
+ grl
->tip_name_height
498 static gboolean
account_filter_func(PurpleAccount
*account
)
500 PurpleConnection
*conn
= purple_account_get_connection(account
);
501 PurpleProtocol
*protocol
= NULL
;
503 if (conn
&& PURPLE_CONNECTION_IS_CONNECTED(conn
))
504 protocol
= purple_connection_get_protocol(conn
);
506 return (protocol
&& PURPLE_PROTOCOL_IMPLEMENTS(protocol
, ROOMLIST_IFACE
, get_list
));
510 pidgin_roomlist_is_showable()
513 PurpleConnection
*gc
;
515 for (c
= purple_connections_get_all(); c
!= NULL
; c
= c
->next
) {
518 if (account_filter_func(purple_connection_get_account(gc
)))
525 static PidginRoomlistDialog
*
526 pidgin_roomlist_dialog_new_with_account(PurpleAccount
*account
)
528 PidginRoomlistDialog
*dialog
;
529 GtkWidget
*window
, *vbox
, *vbox2
, *bbox
;
531 dialog
= g_new0(PidginRoomlistDialog
, 1);
532 dialog
->account
= account
;
534 /* Create the window. */
535 dialog
->window
= window
= pidgin_create_dialog(_("Room List"), 0, "room list", TRUE
);
537 g_signal_connect(G_OBJECT(window
), "delete_event",
538 G_CALLBACK(delete_win_cb
), dialog
);
540 /* Create the parent vbox for everything. */
541 vbox
= pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(window
), FALSE
, PIDGIN_HIG_BORDER
);
543 vbox2
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, PIDGIN_HIG_BORDER
);
544 gtk_container_add(GTK_CONTAINER(vbox
), vbox2
);
545 gtk_widget_show(vbox2
);
547 /* accounts dropdown list */
548 dialog
->account_widget
= pidgin_account_option_menu_new(dialog
->account
, FALSE
,
549 G_CALLBACK(dialog_select_account_cb
), account_filter_func
, dialog
);
550 if (!dialog
->account
) /* this is normally null, and we normally don't care what the first selected item is */
551 dialog
->account
= pidgin_account_option_menu_get_selected(dialog
->account_widget
);
552 pidgin_add_widget_to_vbox(GTK_BOX(vbox2
), _("_Account:"), NULL
, dialog
->account_widget
, TRUE
, NULL
);
554 /* scrolled window */
555 dialog
->sw
= pidgin_make_scrollable(NULL
, GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
, GTK_SHADOW_IN
, -1, 250);
556 gtk_box_pack_start(GTK_BOX(vbox2
), dialog
->sw
, TRUE
, TRUE
, 0);
559 dialog
->progress
= gtk_progress_bar_new();
560 gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(dialog
->progress
), 0.1);
561 gtk_box_pack_start(GTK_BOX(vbox2
), dialog
->progress
, FALSE
, FALSE
, 0);
562 gtk_widget_show(dialog
->progress
);
565 bbox
= pidgin_dialog_get_action_area(GTK_DIALOG(window
));
566 gtk_box_set_spacing(GTK_BOX(bbox
), PIDGIN_HIG_BOX_SPACE
);
567 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox
), GTK_BUTTONBOX_END
);
570 dialog
->stop_button
= pidgin_dialog_add_button(GTK_DIALOG(window
), GTK_STOCK_STOP
,
571 G_CALLBACK(stop_button_cb
), dialog
);
572 gtk_widget_set_sensitive(dialog
->stop_button
, FALSE
);
575 dialog
->list_button
= pidgin_pixbuf_button_from_stock(_("_Get List"), GTK_STOCK_REFRESH
,
576 PIDGIN_BUTTON_HORIZONTAL
);
577 gtk_box_pack_start(GTK_BOX(bbox
), dialog
->list_button
, FALSE
, FALSE
, 0);
578 g_signal_connect(G_OBJECT(dialog
->list_button
), "clicked",
579 G_CALLBACK(list_button_cb
), dialog
);
580 gtk_widget_show(dialog
->list_button
);
583 dialog
->add_button
= pidgin_pixbuf_button_from_stock(_("_Add Chat"), GTK_STOCK_ADD
,
584 PIDGIN_BUTTON_HORIZONTAL
);
585 gtk_box_pack_start(GTK_BOX(bbox
), dialog
->add_button
, FALSE
, FALSE
, 0);
586 g_signal_connect(G_OBJECT(dialog
->add_button
), "clicked",
587 G_CALLBACK(add_room_to_blist_cb
), dialog
);
588 gtk_widget_set_sensitive(dialog
->add_button
, FALSE
);
589 gtk_widget_show(dialog
->add_button
);
592 dialog
->join_button
= pidgin_pixbuf_button_from_stock(_("_Join"), PIDGIN_STOCK_CHAT
,
593 PIDGIN_BUTTON_HORIZONTAL
);
594 gtk_box_pack_start(GTK_BOX(bbox
), dialog
->join_button
, FALSE
, FALSE
, 0);
595 g_signal_connect(G_OBJECT(dialog
->join_button
), "clicked",
596 G_CALLBACK(join_button_cb
), dialog
);
597 gtk_widget_set_sensitive(dialog
->join_button
, FALSE
);
598 gtk_widget_show(dialog
->join_button
);
601 dialog
->close_button
= pidgin_dialog_add_button(GTK_DIALOG(window
), GTK_STOCK_CLOSE
,
602 G_CALLBACK(close_button_cb
), dialog
);
604 /* show the dialog window and return the dialog */
605 gtk_widget_show(dialog
->window
);
610 void pidgin_roomlist_dialog_show_with_account(PurpleAccount
*account
)
612 PidginRoomlistDialog
*dialog
= pidgin_roomlist_dialog_new_with_account(account
);
617 list_button_cb(GTK_BUTTON(dialog
->list_button
), dialog
);
620 void pidgin_roomlist_dialog_show(void)
622 pidgin_roomlist_dialog_new_with_account(NULL
);
625 static void pidgin_roomlist_new(PurpleRoomlist
*list
)
627 PidginRoomlist
*rl
= g_new0(PidginRoomlist
, 1);
629 purple_roomlist_set_ui_data(list
, rl
);
631 rl
->cats
= g_hash_table_new_full(NULL
, NULL
, NULL
, (GDestroyNotify
)gtk_tree_row_reference_free
);
633 roomlists
= g_list_append(roomlists
, list
);
636 static void int_cell_data_func(GtkTreeViewColumn
*col
, GtkCellRenderer
*renderer
,
637 GtkTreeModel
*model
, GtkTreeIter
*iter
, gpointer user_data
)
642 gtk_tree_model_get(model
, iter
, GPOINTER_TO_INT(user_data
), &myint
, -1);
645 g_snprintf(buf
, sizeof(buf
), "%d", myint
);
649 g_object_set(renderer
, "text", buf
, NULL
);
652 /* this sorts backwards on purpose, so that clicking name sorts a-z, while clicking users sorts
653 infinity-0. you can still click again to reverse it on any of them. */
654 static gint
int_sort_func(GtkTreeModel
*model
, GtkTreeIter
*a
, GtkTreeIter
*b
, gpointer user_data
)
660 gtk_tree_model_get(model
, a
, GPOINTER_TO_INT(user_data
), &c
, -1);
661 gtk_tree_model_get(model
, b
, GPOINTER_TO_INT(user_data
), &d
, -1);
672 _search_func(GtkTreeModel
*model
, gint column
, const gchar
*key
, GtkTreeIter
*iter
, gpointer search_data
)
675 gchar
*name
, *fold
, *fkey
;
677 gtk_tree_model_get(model
, iter
, column
, &name
, -1);
678 fold
= g_utf8_casefold(name
, -1);
679 fkey
= g_utf8_casefold(key
, -1);
681 result
= (g_strstr_len(fold
, strlen(fold
), fkey
) == NULL
);
690 static void pidgin_roomlist_set_fields(PurpleRoomlist
*list
, GList
*fields
)
692 PidginRoomlist
*grl
= purple_roomlist_get_ui_data(list
);
693 gint columns
= NUM_OF_COLUMNS
;
697 GtkCellRenderer
*renderer
;
698 GtkTreeViewColumn
*column
;
699 GtkTreeSelection
*selection
;
703 g_return_if_fail(grl
!= NULL
);
705 columns
+= g_list_length(fields
);
706 types
= g_new(GType
, columns
);
708 types
[NAME_COLUMN
] = G_TYPE_STRING
;
709 types
[ROOM_COLUMN
] = G_TYPE_POINTER
;
711 for (j
= NUM_OF_COLUMNS
, l
= fields
; l
; l
= l
->next
, j
++) {
712 PurpleRoomlistField
*f
= l
->data
;
714 switch (purple_roomlist_field_get_field_type(f
)) {
715 case PURPLE_ROOMLIST_FIELD_BOOL
:
716 types
[j
] = G_TYPE_BOOLEAN
;
718 case PURPLE_ROOMLIST_FIELD_INT
:
719 types
[j
] = G_TYPE_INT
;
721 case PURPLE_ROOMLIST_FIELD_STRING
:
722 types
[j
] = G_TYPE_STRING
;
727 model
= gtk_tree_store_newv(columns
, types
);
730 tree
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(model
));
731 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree
), TRUE
);
733 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(tree
));
734 g_signal_connect(G_OBJECT(selection
), "changed",
735 G_CALLBACK(selection_changed_cb
), grl
);
737 g_object_unref(model
);
741 gtk_widget_show(grl
->tree
);
743 renderer
= gtk_cell_renderer_text_new();
744 column
= gtk_tree_view_column_new_with_attributes(_("Name"), renderer
,
745 "text", NAME_COLUMN
, NULL
);
746 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column
),
747 GTK_TREE_VIEW_COLUMN_GROW_ONLY
);
748 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column
), TRUE
);
749 gtk_tree_view_column_set_sort_column_id(GTK_TREE_VIEW_COLUMN(column
), NAME_COLUMN
);
750 gtk_tree_view_column_set_reorderable(GTK_TREE_VIEW_COLUMN(column
), TRUE
);
751 gtk_tree_view_append_column(GTK_TREE_VIEW(tree
), column
);
753 for (j
= NUM_OF_COLUMNS
, l
= fields
; l
; l
= l
->next
, j
++) {
754 PurpleRoomlistField
*f
= l
->data
;
756 if (purple_roomlist_field_get_hidden(f
))
759 renderer
= gtk_cell_renderer_text_new();
760 column
= gtk_tree_view_column_new_with_attributes(
761 purple_roomlist_field_get_label(f
), renderer
,
763 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column
),
764 GTK_TREE_VIEW_COLUMN_GROW_ONLY
);
765 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column
), TRUE
);
766 gtk_tree_view_column_set_sort_column_id(GTK_TREE_VIEW_COLUMN(column
), j
);
767 gtk_tree_view_column_set_reorderable(GTK_TREE_VIEW_COLUMN(column
), TRUE
);
768 if (purple_roomlist_field_get_field_type(f
) == PURPLE_ROOMLIST_FIELD_INT
) {
769 gtk_tree_view_column_set_cell_data_func(column
, renderer
, int_cell_data_func
,
770 GINT_TO_POINTER(j
), NULL
);
771 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(model
), j
, int_sort_func
,
772 GINT_TO_POINTER(j
), NULL
);
774 gtk_tree_view_append_column(GTK_TREE_VIEW(tree
), column
);
777 g_signal_connect(G_OBJECT(tree
), "button-press-event", G_CALLBACK(room_click_cb
), list
);
778 g_signal_connect(G_OBJECT(tree
), "row-expanded", G_CALLBACK(row_expanded_cb
), list
);
779 g_signal_connect(G_OBJECT(tree
), "row-activated", G_CALLBACK(row_activated_cb
), list
);
780 #if 0 /* uncomment this when the tooltips are slightly less annoying and more well behaved */
781 g_signal_connect(G_OBJECT(tree
), "motion-notify-event", G_CALLBACK(row_motion_cb
), list
);
782 g_signal_connect(G_OBJECT(tree
), "leave-notify-event", G_CALLBACK(row_leave_cb
), list
);
784 pidgin_tooltip_setup_for_treeview(tree
, list
,
785 pidgin_roomlist_create_tooltip
,
786 pidgin_roomlist_paint_tooltip
);
788 /* Enable CTRL+F searching */
789 gtk_tree_view_set_search_column(GTK_TREE_VIEW(tree
), NAME_COLUMN
);
790 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(tree
), _search_func
, NULL
, NULL
);
794 static gboolean
pidgin_progress_bar_pulse(gpointer data
)
796 PurpleRoomlist
*list
= data
;
797 PidginRoomlist
*rl
= purple_roomlist_get_ui_data(list
);
799 if (!rl
|| !rl
->dialog
|| !rl
->dialog
->pg_needs_pulse
) {
800 if (rl
&& rl
->dialog
)
801 rl
->dialog
->pg_update_to
= 0;
802 g_object_unref(list
);
806 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(rl
->dialog
->progress
));
807 rl
->dialog
->pg_needs_pulse
= FALSE
;
811 static void pidgin_roomlist_add_room(PurpleRoomlist
*list
, PurpleRoomlistRoom
*room
)
813 PidginRoomlist
*rl
= purple_roomlist_get_ui_data(list
);
814 GtkTreeRowReference
*rr
, *parentrr
= NULL
;
816 GtkTreeIter iter
, parent
, child
;
819 gboolean append
= TRUE
;
822 if (purple_roomlist_room_get_room_type(room
) == PURPLE_ROOMLIST_ROOMTYPE_ROOM
)
826 if (rl
->dialog
->pg_update_to
== 0) {
828 rl
->dialog
->pg_update_to
= g_timeout_add(100, pidgin_progress_bar_pulse
, list
);
829 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(rl
->dialog
->progress
));
831 rl
->dialog
->pg_needs_pulse
= TRUE
;
834 if (purple_roomlist_room_get_parent(room
)) {
835 parentrr
= g_hash_table_lookup(rl
->cats
, purple_roomlist_room_get_parent(room
));
836 path
= gtk_tree_row_reference_get_path(parentrr
);
838 PurpleRoomlistRoom
*tmproom
= NULL
;
840 gtk_tree_model_get_iter(GTK_TREE_MODEL(rl
->model
), &parent
, path
);
841 gtk_tree_path_free(path
);
843 if (gtk_tree_model_iter_children(GTK_TREE_MODEL(rl
->model
), &child
, &parent
)) {
844 gtk_tree_model_get(GTK_TREE_MODEL(rl
->model
), &child
, ROOM_COLUMN
, &tmproom
, -1);
852 gtk_tree_store_append(rl
->model
, &iter
, (parentrr
? &parent
: NULL
));
856 if (purple_roomlist_room_get_room_type(room
) & PURPLE_ROOMLIST_ROOMTYPE_CATEGORY
)
857 gtk_tree_store_append(rl
->model
, &child
, &iter
);
859 path
= gtk_tree_model_get_path(GTK_TREE_MODEL(rl
->model
), &iter
);
861 if (purple_roomlist_room_get_room_type(room
) & PURPLE_ROOMLIST_ROOMTYPE_CATEGORY
) {
862 rr
= gtk_tree_row_reference_new(GTK_TREE_MODEL(rl
->model
), path
);
863 g_hash_table_insert(rl
->cats
, room
, rr
);
866 gtk_tree_path_free(path
);
868 gtk_tree_store_set(rl
->model
, &iter
, NAME_COLUMN
, purple_roomlist_room_get_name(room
), -1);
869 gtk_tree_store_set(rl
->model
, &iter
, ROOM_COLUMN
, room
, -1);
871 for (j
= NUM_OF_COLUMNS
,
872 l
= purple_roomlist_room_get_fields(room
),
873 k
= purple_roomlist_get_fields(list
);
874 l
&& k
; j
++, l
= l
->next
, k
= k
->next
)
876 PurpleRoomlistField
*f
= k
->data
;
877 if (purple_roomlist_field_get_hidden(f
))
879 gtk_tree_store_set(rl
->model
, &iter
, j
, l
->data
, -1);
883 static void pidgin_roomlist_in_progress(PurpleRoomlist
*list
, gboolean in_progress
)
885 PidginRoomlist
*rl
= purple_roomlist_get_ui_data(list
);
887 if (!rl
|| !rl
->dialog
)
891 if (rl
->dialog
->account_widget
)
892 gtk_widget_set_sensitive(rl
->dialog
->account_widget
, FALSE
);
893 gtk_widget_set_sensitive(rl
->dialog
->stop_button
, TRUE
);
894 gtk_widget_set_sensitive(rl
->dialog
->list_button
, FALSE
);
896 rl
->dialog
->pg_needs_pulse
= FALSE
;
897 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(rl
->dialog
->progress
), 0.0);
898 if (rl
->dialog
->account_widget
)
899 gtk_widget_set_sensitive(rl
->dialog
->account_widget
, TRUE
);
900 gtk_widget_set_sensitive(rl
->dialog
->stop_button
, FALSE
);
901 gtk_widget_set_sensitive(rl
->dialog
->list_button
, TRUE
);
905 static void pidgin_roomlist_destroy(PurpleRoomlist
*list
)
907 PidginRoomlist
*rl
= purple_roomlist_get_ui_data(list
);
909 roomlists
= g_list_remove(roomlists
, list
);
911 g_return_if_fail(rl
!= NULL
);
913 g_hash_table_destroy(rl
->cats
);
915 purple_roomlist_set_ui_data(list
, NULL
);
918 static PurpleRoomlistUiOps ops
= {
919 pidgin_roomlist_dialog_show_with_account
,
921 pidgin_roomlist_set_fields
,
922 pidgin_roomlist_add_room
,
923 pidgin_roomlist_in_progress
,
924 pidgin_roomlist_destroy
,
932 void pidgin_roomlist_init(void)
934 purple_roomlist_set_ui_ops(&ops
);