Replace functions which called once with their bodies
[pidgin-git.git] / pidgin / plugins / disco / gtkdisco.c
blobd60e2fca3cb397b484c78541bf74736c1d86170b
1 /**
2 * @file gtkdisco.c GTK+ Service Discovery UI
3 * @ingroup pidgin
4 */
6 /* pidgin
8 * Pidgin is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
27 #include "internal.h"
28 #include "debug.h"
29 #include "gtkutils.h"
30 #include "pidgin.h"
31 #include "request.h"
32 #include "pidginaccountchooser.h"
33 #include "pidgintooltip.h"
35 #include "gtk3compat.h"
36 #include "gtkdisco.h"
37 #include "xmppdisco.h"
39 GList *dialogs = NULL;
41 enum {
42 PIXBUF_COLUMN = 0,
43 NAME_COLUMN,
44 DESCRIPTION_COLUMN,
45 SERVICE_COLUMN,
46 NUM_OF_COLUMNS
49 static void
50 pidgin_disco_list_destroy(PidginDiscoList *list)
52 g_hash_table_destroy(list->services);
53 if (list->dialog && list->dialog->discolist == list)
54 list->dialog->discolist = NULL;
56 g_free((gchar*)list->server);
57 g_free(list);
60 PidginDiscoList *pidgin_disco_list_ref(PidginDiscoList *list)
62 g_return_val_if_fail(list != NULL, NULL);
64 ++list->ref;
65 purple_debug_misc("xmppdisco", "reffing list, ref count now %d\n", list->ref);
67 return list;
70 void pidgin_disco_list_unref(PidginDiscoList *list)
72 g_return_if_fail(list != NULL);
74 --list->ref;
76 purple_debug_misc("xmppdisco", "unreffing list, ref count now %d\n", list->ref);
77 if (list->ref == 0)
78 pidgin_disco_list_destroy(list);
81 void pidgin_disco_list_set_in_progress(PidginDiscoList *list, gboolean in_progress)
83 PidginDiscoDialog *dialog = list->dialog;
85 if (!dialog)
86 return;
88 list->in_progress = in_progress;
90 if (in_progress) {
91 gtk_widget_set_sensitive(dialog->account_chooser, FALSE);
92 gtk_widget_set_sensitive(dialog->stop_button, TRUE);
93 gtk_widget_set_sensitive(dialog->browse_button, FALSE);
94 } else {
95 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(dialog->progress), 0.0);
97 gtk_widget_set_sensitive(dialog->account_chooser, TRUE);
99 gtk_widget_set_sensitive(dialog->stop_button, FALSE);
100 gtk_widget_set_sensitive(dialog->browse_button, TRUE);
102 gtk_widget_set_sensitive(dialog->register_button, FALSE);
103 gtk_widget_set_sensitive(dialog->add_button, FALSE);
108 static GdkPixbuf *
109 pidgin_disco_load_icon(XmppDiscoService *service, const char *size)
111 GdkPixbuf *pixbuf = NULL;
112 char *filename = NULL;
113 gchar *tmp_size;
115 g_return_val_if_fail(service != NULL, NULL);
116 g_return_val_if_fail(size != NULL, NULL);
118 tmp_size = g_strdup_printf("%sx%s", size, size);
120 if (service->type == XMPP_DISCO_SERVICE_TYPE_GATEWAY && service->gateway_type) {
121 char *tmp = g_strconcat("im-", service->gateway_type,
122 ".png", NULL);
124 filename = g_build_filename(PURPLE_DATADIR,
125 "pidgin", "icons", "hicolor", tmp_size, "apps",
126 tmp, NULL);
127 g_free(tmp);
128 #if 0
129 } else if (service->type == XMPP_DISCO_SERVICE_TYPE_USER) {
130 filename = g_build_filename(PURPLE_DATADIR,
131 "pixmaps", "pidgin", "status", size, "person.png", NULL);
132 #endif
133 } else if (service->type == XMPP_DISCO_SERVICE_TYPE_CHAT) {
134 filename = g_build_filename(PURPLE_DATADIR,
135 "pidgin", "icons", "hicolor", tmp_size, "status",
136 "chat.png", NULL);
139 g_free(tmp_size);
141 if (filename) {
142 pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
143 g_free(filename);
146 return pixbuf;
149 static void
150 dialog_select_account_cb(GtkWidget *chooser, PidginDiscoDialog *dialog)
152 PurpleAccount *account = pidgin_account_chooser_get_selected(chooser);
153 gboolean change = (account != dialog->account);
154 dialog->account = account;
155 gtk_widget_set_sensitive(dialog->browse_button, account != NULL);
157 if (change) {
158 g_clear_pointer(&dialog->discolist, pidgin_disco_list_unref);
162 static void register_button_cb(GtkWidget *unused, PidginDiscoDialog *dialog)
164 xmpp_disco_service_register(dialog->selected);
167 static void discolist_cancel_cb(PidginDiscoList *pdl, const char *server)
169 pdl->dialog->prompt_handle = NULL;
171 pidgin_disco_list_set_in_progress(pdl, FALSE);
172 pidgin_disco_list_unref(pdl);
175 static void discolist_ok_cb(PidginDiscoList *pdl, const char *server)
177 pdl->dialog->prompt_handle = NULL;
178 gtk_widget_set_sensitive(pdl->dialog->browse_button, TRUE);
180 if (!server || !*server) {
181 purple_notify_error(my_plugin, _("Invalid Server"), _("Invalid Server"),
182 NULL, purple_request_cpar_from_connection(pdl->pc));
184 pidgin_disco_list_set_in_progress(pdl, FALSE);
185 pidgin_disco_list_unref(pdl);
186 return;
189 pdl->server = g_strdup(server);
190 pidgin_disco_list_set_in_progress(pdl, TRUE);
191 xmpp_disco_start(pdl);
194 static void browse_button_cb(GtkWidget *button, PidginDiscoDialog *dialog)
196 PurpleConnection *pc;
197 PidginDiscoList *pdl;
198 const char *username;
199 const char *at, *slash;
200 char *server = NULL;
202 pc = purple_account_get_connection(dialog->account);
203 if (!pc)
204 return;
206 gtk_widget_set_sensitive(dialog->browse_button, FALSE);
207 gtk_widget_set_sensitive(dialog->add_button, FALSE);
208 gtk_widget_set_sensitive(dialog->register_button, FALSE);
210 g_clear_pointer(&dialog->discolist, pidgin_disco_list_unref);
211 gtk_tree_store_clear(dialog->model);
213 pdl = dialog->discolist = g_new0(PidginDiscoList, 1);
214 pdl->services = g_hash_table_new_full(NULL, NULL, NULL,
215 (GDestroyNotify)gtk_tree_row_reference_free);
216 pdl->pc = pc;
217 /* We keep a copy... */
218 pidgin_disco_list_ref(pdl);
220 pdl->dialog = dialog;
222 gtk_widget_set_sensitive(dialog->account_chooser, FALSE);
224 username = purple_account_get_username(dialog->account);
225 at = strchr(username, '@');
226 slash = strchr(username, '/');
227 if (at && !slash) {
228 server = g_strdup_printf("%s", at + 1);
229 } else if (at && slash && at + 1 < slash) {
230 server = g_strdup_printf("%.*s", (int)(slash - (at + 1)), at + 1);
233 if (server == NULL)
234 /* This shouldn't ever happen since the account is connected */
235 server = g_strdup("jabber.org");
237 /* Translators: The string "Enter an XMPP Server" is asking the user to
238 type the name of an XMPP server which will then be queried */
239 dialog->prompt_handle = purple_request_input(my_plugin, _("Server name request"), _("Enter an XMPP Server"),
240 _("Select an XMPP server to query"),
241 server, FALSE, FALSE, NULL,
242 _("Find Services"), PURPLE_CALLBACK(discolist_ok_cb),
243 _("Cancel"), PURPLE_CALLBACK(discolist_cancel_cb),
244 purple_request_cpar_from_connection(pc), pdl);
246 g_free(server);
249 static void add_to_blist_cb(GtkWidget *unused, PidginDiscoDialog *dialog)
251 XmppDiscoService *service = dialog->selected;
252 PurpleAccount *account;
253 const char *jid;
255 g_return_if_fail(service != NULL);
257 account = purple_connection_get_account(service->list->pc);
258 jid = service->jid;
260 if (service->type == XMPP_DISCO_SERVICE_TYPE_CHAT)
261 purple_blist_request_add_chat(account, NULL, NULL, jid);
262 else
263 purple_blist_request_add_buddy(account, jid, NULL, NULL);
266 static gboolean
267 service_click_cb(GtkTreeView *tree, GdkEventButton *event, gpointer user_data)
269 PidginDiscoDialog *dialog = user_data;
270 XmppDiscoService *service;
271 GtkWidget *menu;
273 GtkTreePath *path;
274 GtkTreeIter iter;
275 GValue val;
277 if (!gdk_event_triggers_context_menu((GdkEvent *)event))
278 return FALSE;
280 /* Figure out what was clicked */
281 if (!gtk_tree_view_get_path_at_pos(tree, event->x, event->y, &path,
282 NULL, NULL, NULL))
283 return FALSE;
284 gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path);
285 gtk_tree_path_free(path);
286 val.g_type = 0;
287 gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter,
288 SERVICE_COLUMN, &val);
289 service = g_value_get_pointer(&val);
291 if (!service)
292 return FALSE;
294 menu = gtk_menu_new();
296 if (service->flags & XMPP_DISCO_ADD) {
297 pidgin_new_menu_item(menu, _("Add to Buddy List"), NULL,
298 G_CALLBACK(add_to_blist_cb), dialog);
301 if (service->flags & XMPP_DISCO_REGISTER) {
302 GtkWidget *item = pidgin_new_menu_item(menu, _("Register"),
303 NULL, NULL, NULL);
304 g_signal_connect(G_OBJECT(item), "activate",
305 G_CALLBACK(register_button_cb), dialog);
308 gtk_widget_show_all(menu);
309 gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event);
310 return FALSE;
313 static void
314 selection_changed_cb(GtkTreeSelection *selection, PidginDiscoDialog *dialog)
316 GtkTreeIter iter;
317 GValue val;
319 if (gtk_tree_selection_get_selected(selection, NULL, &iter)) {
320 val.g_type = 0;
321 gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter,
322 SERVICE_COLUMN, &val);
323 dialog->selected = g_value_get_pointer(&val);
324 if (!dialog->selected) {
325 gtk_widget_set_sensitive(dialog->add_button, FALSE);
326 gtk_widget_set_sensitive(dialog->register_button, FALSE);
327 return;
330 gtk_widget_set_sensitive(dialog->add_button, dialog->selected->flags & XMPP_DISCO_ADD);
331 gtk_widget_set_sensitive(dialog->register_button, dialog->selected->flags & XMPP_DISCO_REGISTER);
332 } else {
333 gtk_widget_set_sensitive(dialog->add_button, FALSE);
334 gtk_widget_set_sensitive(dialog->register_button, FALSE);
338 static void
339 row_expanded_cb(GtkTreeView *tree, GtkTreeIter *arg1, GtkTreePath *rg2,
340 gpointer user_data)
342 PidginDiscoDialog *dialog = user_data;
343 XmppDiscoService *service;
344 GValue val;
346 val.g_type = 0;
347 gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), arg1,
348 SERVICE_COLUMN, &val);
349 service = g_value_get_pointer(&val);
350 xmpp_disco_service_expand(service);
353 static void
354 row_activated_cb(GtkTreeView *tree_view,
355 GtkTreePath *path,
356 GtkTreeViewColumn *column,
357 gpointer user_data)
359 PidginDiscoDialog *dialog = user_data;
360 GtkTreeIter iter;
361 XmppDiscoService *service;
362 GValue val;
364 if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter,
365 path)) {
366 return;
369 val.g_type = 0;
370 gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter,
371 SERVICE_COLUMN, &val);
372 service = g_value_get_pointer(&val);
374 if (service->flags & XMPP_DISCO_BROWSE) {
375 if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(dialog->tree),
376 path)) {
377 gtk_tree_view_collapse_row(GTK_TREE_VIEW(dialog->tree),
378 path);
379 } else {
380 gtk_tree_view_expand_row(GTK_TREE_VIEW(dialog->tree),
381 path, FALSE);
383 } else if (service->flags & XMPP_DISCO_REGISTER) {
384 register_button_cb(NULL, dialog);
385 } else if (service->flags & XMPP_DISCO_ADD) {
386 add_to_blist_cb(NULL, dialog);
390 static void
391 destroy_win_cb(GtkWidget *window, G_GNUC_UNUSED gpointer data)
393 PidginDiscoDialog *dialog = PIDGIN_DISCO_DIALOG(window);
394 PidginDiscoList *list = dialog->discolist;
396 if (dialog->prompt_handle)
397 purple_request_close(PURPLE_REQUEST_INPUT, dialog->prompt_handle);
399 if (list) {
400 list->dialog = NULL;
402 if (list->in_progress)
403 list->in_progress = FALSE;
405 pidgin_disco_list_unref(list);
408 dialogs = g_list_remove(dialogs, dialog);
411 static void stop_button_cb(GtkButton *button, PidginDiscoDialog *dialog)
413 pidgin_disco_list_set_in_progress(dialog->discolist, FALSE);
416 static void close_button_cb(GtkButton *button, PidginDiscoDialog *dialog)
418 gtk_widget_destroy(GTK_WIDGET(dialog));
421 static gboolean account_filter_func(PurpleAccount *account)
423 return purple_strequal(purple_account_get_protocol_id(account), XMPP_PROTOCOL_ID);
426 static gboolean
427 disco_paint_tooltip(GtkWidget *tipwindow, cairo_t *cr, gpointer data)
429 PangoLayout *layout = g_object_get_data(G_OBJECT(tipwindow), "tooltip-plugin");
430 GtkStyleContext *context = gtk_widget_get_style_context(tipwindow);
431 gtk_style_context_add_class(context, GTK_STYLE_CLASS_TOOLTIP);
432 gtk_render_layout(context, cr, 6, 6, layout);
433 return TRUE;
436 static gboolean
437 disco_create_tooltip(GtkWidget *tipwindow, GtkTreePath *path,
438 gpointer data, int *w, int *h)
440 PidginDiscoDialog *dialog = data;
441 GtkTreeIter iter;
442 PangoLayout *layout;
443 int width, height;
444 XmppDiscoService *service;
445 GValue val;
446 const char *type = NULL;
447 char *markup, *jid, *name, *desc = NULL;
449 if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter,
450 path)) {
451 return FALSE;
454 val.g_type = 0;
455 gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter,
456 SERVICE_COLUMN, &val);
457 service = g_value_get_pointer(&val);
458 if (!service)
459 return FALSE;
461 switch (service->type) {
462 case XMPP_DISCO_SERVICE_TYPE_UNSET:
463 type = _("Unknown");
464 break;
466 case XMPP_DISCO_SERVICE_TYPE_GATEWAY:
467 type = _("Gateway");
468 break;
470 case XMPP_DISCO_SERVICE_TYPE_DIRECTORY:
471 type = _("Directory");
472 break;
474 case XMPP_DISCO_SERVICE_TYPE_CHAT:
475 type = _("Chat");
476 break;
478 case XMPP_DISCO_SERVICE_TYPE_PUBSUB_COLLECTION:
479 type = _("PubSub Collection");
480 break;
482 case XMPP_DISCO_SERVICE_TYPE_PUBSUB_LEAF:
483 type = _("PubSub Leaf");
484 break;
486 case XMPP_DISCO_SERVICE_TYPE_OTHER:
487 type = _("Other");
488 break;
491 markup = g_strdup_printf("<span size='x-large' weight='bold'>%s</span>\n<b>%s:</b> %s%s%s",
492 name = g_markup_escape_text(service->name, -1),
493 type,
494 jid = g_markup_escape_text(service->jid, -1),
495 service->description ? _("\n<b>Description:</b> ") : "",
496 service->description ? desc = g_markup_escape_text(service->description, -1) : "");
498 layout = gtk_widget_create_pango_layout(tipwindow, NULL);
499 pango_layout_set_markup(layout, markup, -1);
500 pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
501 pango_layout_set_width(layout, 500000);
502 pango_layout_get_size(layout, &width, &height);
503 g_object_set_data_full(G_OBJECT(tipwindow), "tooltip-plugin", layout, g_object_unref);
505 if (w)
506 *w = PANGO_PIXELS(width) + 12;
507 if (h)
508 *h = PANGO_PIXELS(height) + 12;
510 g_free(markup);
511 g_free(jid);
512 g_free(name);
513 g_free(desc);
515 return TRUE;
518 void pidgin_disco_signed_off_cb(PurpleConnection *pc)
520 GList *node;
522 for (node = dialogs; node; node = node->next) {
523 PidginDiscoDialog *dialog = node->data;
524 PidginDiscoList *list = dialog->discolist;
526 if (list && list->pc == pc) {
527 if (list->in_progress)
528 pidgin_disco_list_set_in_progress(list, FALSE);
530 gtk_tree_store_clear(dialog->model);
532 pidgin_disco_list_unref(list);
533 dialog->discolist = NULL;
535 gtk_widget_set_sensitive(
536 dialog->browse_button,
537 pidgin_account_chooser_get_selected(
538 dialog->account_chooser) != NULL);
540 gtk_widget_set_sensitive(dialog->register_button, FALSE);
541 gtk_widget_set_sensitive(dialog->add_button, FALSE);
546 void pidgin_disco_dialogs_destroy_all(void)
548 while (dialogs) {
549 GtkWidget *dialog = dialogs->data;
551 gtk_widget_destroy(dialog);
552 /* destroy_win_cb removes the dialog from the list */
556 /******************************************************************************
557 * GObject implementation
558 *****************************************************************************/
560 G_DEFINE_DYNAMIC_TYPE(PidginDiscoDialog, pidgin_disco_dialog, GTK_TYPE_DIALOG)
562 static void
563 pidgin_disco_dialog_class_init(PidginDiscoDialogClass *klass)
565 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
567 gtk_widget_class_set_template_from_resource(
568 widget_class, "/im/pidgin/Pidgin/Plugin/XMPPDisco/disco.ui");
570 gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
571 account_chooser);
572 gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
573 progress);
574 gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
575 stop_button);
576 gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
577 browse_button);
578 gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
579 register_button);
580 gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
581 add_button);
582 gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
583 tree);
584 gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
585 model);
587 gtk_widget_class_bind_template_callback(widget_class, destroy_win_cb);
588 gtk_widget_class_bind_template_callback(widget_class, stop_button_cb);
589 gtk_widget_class_bind_template_callback(widget_class, browse_button_cb);
590 gtk_widget_class_bind_template_callback(widget_class,
591 register_button_cb);
592 gtk_widget_class_bind_template_callback(widget_class, add_to_blist_cb);
593 gtk_widget_class_bind_template_callback(widget_class, close_button_cb);
594 gtk_widget_class_bind_template_callback(widget_class,
595 dialog_select_account_cb);
596 gtk_widget_class_bind_template_callback(widget_class, row_activated_cb);
597 gtk_widget_class_bind_template_callback(widget_class, row_expanded_cb);
598 gtk_widget_class_bind_template_callback(widget_class, service_click_cb);
599 gtk_widget_class_bind_template_callback(widget_class,
600 selection_changed_cb);
603 static void
604 pidgin_disco_dialog_class_finalize(PidginDiscoDialogClass *klass)
608 static void
609 pidgin_disco_dialog_init(PidginDiscoDialog *dialog)
611 dialogs = g_list_prepend(dialogs, dialog);
613 gtk_widget_init_template(GTK_WIDGET(dialog));
615 /* accounts dropdown list */
616 pidgin_account_chooser_set_filter_func(
617 PIDGIN_ACCOUNT_CHOOSER(dialog->account_chooser),
618 account_filter_func);
619 dialog->account =
620 pidgin_account_chooser_get_selected(dialog->account_chooser);
622 /* browse button */
623 gtk_widget_set_sensitive(dialog->browse_button, dialog->account != NULL);
625 pidgin_tooltip_setup_for_treeview(GTK_WIDGET(dialog->tree), dialog,
626 disco_create_tooltip,
627 disco_paint_tooltip);
630 /******************************************************************************
631 * Public API
632 *****************************************************************************/
634 void
635 pidgin_disco_dialog_register(PurplePlugin *plugin)
637 pidgin_disco_dialog_register_type(G_TYPE_MODULE(plugin));
640 PidginDiscoDialog *
641 pidgin_disco_dialog_new(void)
643 PidginDiscoDialog *dialog =
644 g_object_new(PIDGIN_TYPE_DISCO_DIALOG, NULL);
645 gtk_widget_show_all(GTK_WIDGET(dialog));
646 return dialog;
649 void pidgin_disco_add_service(PidginDiscoList *pdl, XmppDiscoService *service, XmppDiscoService *parent)
651 PidginDiscoDialog *dialog;
652 GtkTreeIter iter, parent_iter, child;
653 GdkPixbuf *pixbuf = NULL;
654 gboolean append = TRUE;
656 dialog = pdl->dialog;
657 g_return_if_fail(dialog != NULL);
659 if (service != NULL)
660 purple_debug_info("xmppdisco", "Adding service \"%s\"\n", service->name);
661 else
662 purple_debug_info("xmppdisco", "Service \"%s\" has no childrens\n", parent->name);
664 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(dialog->progress));
666 if (parent) {
667 GtkTreeRowReference *rr;
668 GtkTreePath *path;
670 rr = g_hash_table_lookup(pdl->services, parent);
671 path = gtk_tree_row_reference_get_path(rr);
672 if (path) {
673 gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model),
674 &parent_iter, path);
675 gtk_tree_path_free(path);
677 if (gtk_tree_model_iter_children(
678 GTK_TREE_MODEL(dialog->model), &child,
679 &parent_iter)) {
680 PidginDiscoList *tmp;
681 gtk_tree_model_get(
682 GTK_TREE_MODEL(dialog->model), &child,
683 SERVICE_COLUMN, &tmp, -1);
684 if (!tmp)
685 append = FALSE;
690 if (service == NULL) {
691 if (parent != NULL && !append)
692 gtk_tree_store_remove(dialog->model, &child);
693 return;
696 if (append) {
697 gtk_tree_store_append(dialog->model, &iter,
698 (parent ? &parent_iter : NULL));
699 } else {
700 iter = child;
703 if (service->flags & XMPP_DISCO_BROWSE) {
704 GtkTreeRowReference *rr;
705 GtkTreePath *path;
707 gtk_tree_store_append(dialog->model, &child, &iter);
709 path = gtk_tree_model_get_path(GTK_TREE_MODEL(dialog->model),
710 &iter);
711 rr = gtk_tree_row_reference_new(GTK_TREE_MODEL(dialog->model),
712 path);
713 g_hash_table_insert(pdl->services, service, rr);
714 gtk_tree_path_free(path);
717 pixbuf = pidgin_disco_load_icon(service, "16");
719 gtk_tree_store_set(dialog->model, &iter, PIXBUF_COLUMN, pixbuf,
720 NAME_COLUMN, service->name, DESCRIPTION_COLUMN,
721 service->description, SERVICE_COLUMN, service, -1);
723 if (pixbuf)
724 g_object_unref(pixbuf);