pidgin/plugins/disco/gtkdisco.c

changeset 42090
a109a8cc8059
parent 42074
58ae70ad7f21
child 42091
7e0d24e06ce2
equal deleted inserted replaced
42089:083eae91c9a8 42090:a109a8cc8059
49 } 49 }
50 50
51 static void 51 static void
52 pidgin_disco_list_destroy(PidginDiscoList *list) 52 pidgin_disco_list_destroy(PidginDiscoList *list)
53 { 53 {
54 g_hash_table_destroy(list->services);
55 if (list->dialog && list->dialog->discolist == list) { 54 if (list->dialog && list->dialog->discolist == list) {
56 list->dialog->discolist = NULL; 55 list->dialog->discolist = NULL;
57 } 56 }
58 57
59 g_free((gchar*)list->server); 58 g_free((gchar*)list->server);
179 g_simple_action_set_enabled(dialog->browse_action, FALSE); 178 g_simple_action_set_enabled(dialog->browse_action, FALSE);
180 g_simple_action_set_enabled(dialog->add_action, FALSE); 179 g_simple_action_set_enabled(dialog->add_action, FALSE);
181 g_simple_action_set_enabled(dialog->register_action, FALSE); 180 g_simple_action_set_enabled(dialog->register_action, FALSE);
182 181
183 g_clear_pointer(&dialog->discolist, pidgin_disco_list_unref); 182 g_clear_pointer(&dialog->discolist, pidgin_disco_list_unref);
184 gtk_tree_store_clear(dialog->model); 183 g_list_store_remove_all(dialog->root);
185 184
186 pdl = dialog->discolist = pidgin_disco_list_new(); 185 pdl = dialog->discolist = pidgin_disco_list_new();
187 pdl->services = g_hash_table_new_full(NULL, NULL, NULL,
188 (GDestroyNotify)gtk_tree_row_reference_free);
189 pdl->pc = pc; 186 pdl->pc = pc;
190 /* We keep a copy... */ 187 /* We keep a copy... */
191 pidgin_disco_list_ref(pdl); 188 pidgin_disco_list_ref(pdl);
192 189
193 pdl->dialog = dialog; 190 pdl->dialog = dialog;
243 } else { 240 } else {
244 purple_blist_request_add_buddy(account, jid, NULL, NULL); 241 purple_blist_request_add_buddy(account, jid, NULL, NULL);
245 } 242 }
246 } 243 }
247 244
248 static gboolean 245 static void
249 service_click_cb(G_GNUC_UNUSED GtkGestureClick *click, 246 selection_changed_cb(GtkSelectionModel *self, G_GNUC_UNUSED guint position,
250 G_GNUC_UNUSED gint n_press, 247 G_GNUC_UNUSED guint n_items, gpointer data)
251 gdouble x, gdouble y, gpointer data)
252 { 248 {
253 PidginDiscoDialog *dialog = data; 249 PidginDiscoDialog *dialog = data;
254 XmppDiscoService *service; 250 GtkTreeListRow *row = NULL;
255
256 GtkTreePath *path;
257 GtkTreeIter iter;
258 GValue val;
259
260 GdkRectangle rect;
261
262 /* Figure out what was clicked */
263 if (!gtk_tree_view_get_path_at_pos(dialog->tree, (gint)x, (gint)y, &path,
264 NULL, NULL, NULL))
265 {
266 return FALSE;
267 }
268 gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path);
269 gtk_tree_path_free(path);
270 val.g_type = 0;
271 gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter,
272 SERVICE_COLUMN, &val);
273 service = g_value_get_pointer(&val);
274
275 if (!service) {
276 return FALSE;
277 }
278
279 gtk_tree_view_convert_bin_window_to_widget_coords(dialog->tree,
280 (gint)x, (gint)y,
281 &rect.x, &rect.y);
282 rect.width = rect.height = 1;
283
284 gtk_popover_set_pointing_to(GTK_POPOVER(dialog->popover), &rect);
285 gtk_popover_popup(GTK_POPOVER(dialog->popover));
286
287 return FALSE;
288 }
289
290 static void
291 selection_changed_cb(GtkTreeSelection *selection, PidginDiscoDialog *dialog)
292 {
293 GtkTreeIter iter;
294 GValue val;
295 gboolean allow_add = FALSE, allow_register = FALSE; 251 gboolean allow_add = FALSE, allow_register = FALSE;
296 252
297 if (gtk_tree_selection_get_selected(selection, NULL, &iter)) { 253 /* The passed in position and n_items gives the *range* of selections that
298 val.g_type = 0; 254 * have changed, so just re-query it since GtkSingleSelection has exactly
299 gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter, 255 * one. */
300 SERVICE_COLUMN, &val); 256 row = gtk_single_selection_get_selected_item(GTK_SINGLE_SELECTION(self));
301 dialog->selected = g_value_get_pointer(&val); 257 if(row != NULL) {
302 if (dialog->selected != NULL) { 258 dialog->selected = gtk_tree_list_row_get_item(row);
303 XmppDiscoServiceFlags flags = xmpp_disco_service_get_flags(dialog->selected); 259 if(XMPP_DISCO_IS_SERVICE(dialog->selected)) {
260 XmppDiscoServiceFlags flags;
261
262 flags = xmpp_disco_service_get_flags(dialog->selected);
304 allow_add = (flags & XMPP_DISCO_ADD) != 0; 263 allow_add = (flags & XMPP_DISCO_ADD) != 0;
305 allow_register = (flags & XMPP_DISCO_REGISTER) != 0; 264 allow_register = (flags & XMPP_DISCO_REGISTER) != 0;
265
266 /* gtk_tree_list_row_get_item returns a ref, but this struct isn't
267 * supposed to hold one, as the model holds on to it. */
268 g_object_unref(dialog->selected);
306 } 269 }
307 } 270 }
308 271
309 g_simple_action_set_enabled(dialog->add_action, allow_add); 272 g_simple_action_set_enabled(dialog->add_action, allow_add);
310 g_simple_action_set_enabled(dialog->register_action, allow_register); 273 g_simple_action_set_enabled(dialog->register_action, allow_register);
311 } 274 }
312 275
313 static void 276 static GListModel *
314 row_expanded_cb(G_GNUC_UNUSED GtkTreeView *tree, GtkTreeIter *arg1, 277 service_create_child_model_cb(GObject *item, G_GNUC_UNUSED gpointer data) {
315 G_GNUC_UNUSED GtkTreePath *rg2, gpointer user_data) 278 XmppDiscoService *service = XMPP_DISCO_SERVICE(item);
316 { 279 GListModel *model = NULL;
280
281 model = xmpp_disco_service_get_child_model(service);
282 if(G_IS_LIST_MODEL(model)) {
283 /* Always return a new ref, as the caller takes ownership. */
284 g_object_ref(model);
285 }
286
287 return model;
288 }
289
290 static void
291 row_expanded_cb(GObject *obj, G_GNUC_UNUSED GParamSpec *pspec,
292 G_GNUC_UNUSED gpointer data)
293 {
294 GtkTreeListRow *row = GTK_TREE_LIST_ROW(obj);
295
296 if(gtk_tree_list_row_get_expanded(row)) {
297 XmppDiscoService *service = gtk_tree_list_row_get_item(row);
298 if(XMPP_DISCO_IS_SERVICE(service)) {
299 xmpp_disco_service_expand(service);
300 }
301 g_clear_object(&service);
302 }
303 }
304
305 static void
306 list_row_notify_cb(GObject *obj, GParamSpec *pspec, gpointer data) {
307 GtkTreeListRow *row = NULL;
308
309 row = gtk_tree_expander_get_list_row(GTK_TREE_EXPANDER(obj));
310 if(GTK_IS_TREE_LIST_ROW(row)) {
311 g_signal_connect(row, "notify::expanded", G_CALLBACK(row_expanded_cb),
312 NULL);
313 }
314 }
315
316 static void
317 row_activated_cb(GtkColumnView *self, guint position, gpointer user_data) {
317 PidginDiscoDialog *dialog = user_data; 318 PidginDiscoDialog *dialog = user_data;
318 XmppDiscoService *service; 319 GtkSelectionModel *model = NULL;
319 GValue val; 320 GtkTreeListRow *row = NULL;
320 321 XmppDiscoService *service = NULL;
321 val.g_type = 0;
322 gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), arg1,
323 SERVICE_COLUMN, &val);
324 service = g_value_get_pointer(&val);
325 xmpp_disco_service_expand(service);
326 }
327
328 static void
329 row_activated_cb(G_GNUC_UNUSED GtkTreeView *tree_view,
330 GtkTreePath *path,
331 G_GNUC_UNUSED GtkTreeViewColumn *column,
332 gpointer user_data)
333 {
334 PidginDiscoDialog *dialog = user_data;
335 GtkTreeIter iter;
336 XmppDiscoService *service;
337 XmppDiscoServiceFlags flags; 322 XmppDiscoServiceFlags flags;
338 GValue val; 323
339 324 model = gtk_column_view_get_model(self);
340 if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path)) { 325 row = g_list_model_get_item(G_LIST_MODEL(model), position);
341 return; 326 service = gtk_tree_list_row_get_item(row);
342 }
343
344 val.g_type = 0;
345 gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter,
346 SERVICE_COLUMN, &val);
347 service = g_value_get_pointer(&val);
348 327
349 flags = xmpp_disco_service_get_flags(service); 328 flags = xmpp_disco_service_get_flags(service);
350 if((flags & XMPP_DISCO_BROWSE) != 0) { 329 if((flags & XMPP_DISCO_BROWSE) != 0) {
351 if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(dialog->tree), path)) { 330 if(gtk_tree_list_row_get_expanded(row)) {
352 gtk_tree_view_collapse_row(GTK_TREE_VIEW(dialog->tree), path); 331 gtk_tree_list_row_set_expanded(row, FALSE);
353 } else { 332 } else {
354 gtk_tree_view_expand_row(GTK_TREE_VIEW(dialog->tree), path, FALSE); 333 gtk_tree_list_row_set_expanded(row, TRUE);
355 } 334 }
356 } else if((flags & XMPP_DISCO_REGISTER) != 0) { 335 } else if((flags & XMPP_DISCO_REGISTER) != 0) {
357 g_action_activate(G_ACTION(dialog->register_action), NULL); 336 g_action_activate(G_ACTION(dialog->register_action), NULL);
358 } else if((flags & XMPP_DISCO_ADD) != 0) { 337 } else if((flags & XMPP_DISCO_ADD) != 0) {
359 g_action_activate(G_ACTION(dialog->add_action), NULL); 338 g_action_activate(G_ACTION(dialog->add_action), NULL);
360 } 339 }
340
341 g_clear_object(&service);
342 g_clear_object(&row);
361 } 343 }
362 344
363 static void 345 static void
364 destroy_win_cb(GtkWidget *window, G_GNUC_UNUSED gpointer data) 346 destroy_win_cb(GtkWidget *window, G_GNUC_UNUSED gpointer data)
365 { 347 {
391 PidginDiscoDialog *dialog = data; 373 PidginDiscoDialog *dialog = data;
392 374
393 pidgin_disco_list_set_in_progress(dialog->discolist, FALSE); 375 pidgin_disco_list_set_in_progress(dialog->discolist, FALSE);
394 } 376 }
395 377
396 static gboolean
397 disco_query_tooltip(GtkWidget *widget, int x, int y, gboolean keyboard_mode,
398 GtkTooltip *tooltip, gpointer data)
399 {
400 PidginDiscoDialog *dialog = data;
401 GtkTreePath *path = NULL;
402 GtkTreeIter iter;
403 XmppDiscoService *service;
404 GValue val;
405 const char *type = NULL;
406 char *markup, *jid, *name, *desc = NULL;
407
408 if (keyboard_mode) {
409 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
410 if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) {
411 return FALSE;
412 }
413 path = gtk_tree_model_get_path(GTK_TREE_MODEL(dialog->model), &iter);
414 } else {
415 gint bx, by;
416
417 gtk_tree_view_convert_widget_to_bin_window_coords(GTK_TREE_VIEW(widget),
418 x, y, &bx, &by);
419 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bx, by, &path,
420 NULL, NULL, NULL);
421 if (path == NULL) {
422 return FALSE;
423 }
424
425 if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path)) {
426 gtk_tree_path_free(path);
427 return FALSE;
428 }
429 }
430
431 val.g_type = 0;
432 gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter,
433 SERVICE_COLUMN, &val);
434 service = g_value_get_pointer(&val);
435 if (!service) {
436 gtk_tree_path_free(path);
437 return FALSE;
438 }
439
440 switch(xmpp_disco_service_get_service_type(service)) {
441 case XMPP_DISCO_SERVICE_TYPE_UNSET:
442 type = _("Unknown");
443 break;
444
445 case XMPP_DISCO_SERVICE_TYPE_GATEWAY:
446 type = _("Gateway");
447 break;
448
449 case XMPP_DISCO_SERVICE_TYPE_DIRECTORY:
450 type = _("Directory");
451 break;
452
453 case XMPP_DISCO_SERVICE_TYPE_CHAT:
454 type = _("Chat");
455 break;
456
457 case XMPP_DISCO_SERVICE_TYPE_PUBSUB_COLLECTION:
458 type = _("PubSub Collection");
459 break;
460
461 case XMPP_DISCO_SERVICE_TYPE_PUBSUB_LEAF:
462 type = _("PubSub Leaf");
463 break;
464
465 case XMPP_DISCO_SERVICE_TYPE_OTHER:
466 type = _("Other");
467 break;
468 }
469
470 name = g_markup_escape_text(xmpp_disco_service_get_name(service), -1);
471 jid = g_markup_escape_text(xmpp_disco_service_get_jid(service), -1);
472 if(xmpp_disco_service_get_description(service) != NULL) {
473 desc = g_markup_escape_text(xmpp_disco_service_get_description(service),
474 -1);
475 }
476
477 markup = g_strdup_printf("<span size='x-large' weight='bold'>%s</span>\n<b>%s:</b> %s%s%s",
478 name, type, jid,
479 desc != NULL ? _("\n<b>Description:</b> ") : "",
480 desc != NULL ? desc : "");
481
482 gtk_tooltip_set_markup(tooltip, markup);
483 gtk_tree_view_set_tooltip_row(GTK_TREE_VIEW(widget), tooltip, path);
484
485 g_free(markup);
486 g_free(jid);
487 g_free(name);
488 g_free(desc);
489 gtk_tree_path_free(path);
490
491 return TRUE;
492 }
493
494 void pidgin_disco_signed_off_cb(PurpleConnection *pc) 378 void pidgin_disco_signed_off_cb(PurpleConnection *pc)
495 { 379 {
496 GList *node; 380 GList *node;
497 381
498 for (node = dialogs; node; node = node->next) { 382 for (node = dialogs; node; node = node->next) {
504 388
505 if (list->in_progress) { 389 if (list->in_progress) {
506 pidgin_disco_list_set_in_progress(list, FALSE); 390 pidgin_disco_list_set_in_progress(list, FALSE);
507 } 391 }
508 392
509 gtk_tree_store_clear(dialog->model); 393 g_list_store_remove_all(dialog->root);
510 394
511 pidgin_disco_list_unref(list); 395 pidgin_disco_list_unref(list);
512 dialog->discolist = NULL; 396 dialog->discolist = NULL;
513 397
514 account = pidgin_account_chooser_get_selected( 398 account = pidgin_account_chooser_get_selected(
548 gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog, 432 gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
549 account_chooser); 433 account_chooser);
550 gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog, 434 gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
551 progress); 435 progress);
552 gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog, 436 gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
553 tree); 437 sorter);
554 gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
555 model);
556 gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
557 popover);
558 438
559 gtk_widget_class_bind_template_callback(widget_class, destroy_win_cb); 439 gtk_widget_class_bind_template_callback(widget_class, destroy_win_cb);
560 gtk_widget_class_bind_template_callback(widget_class, 440 gtk_widget_class_bind_template_callback(widget_class,
561 dialog_select_account_cb); 441 dialog_select_account_cb);
562 gtk_widget_class_bind_template_callback(widget_class, row_activated_cb); 442 gtk_widget_class_bind_template_callback(widget_class, row_activated_cb);
563 gtk_widget_class_bind_template_callback(widget_class, row_expanded_cb); 443 gtk_widget_class_bind_template_callback(widget_class, list_row_notify_cb);
564 gtk_widget_class_bind_template_callback(widget_class, service_click_cb);
565 gtk_widget_class_bind_template_callback(widget_class, 444 gtk_widget_class_bind_template_callback(widget_class,
566 selection_changed_cb); 445 selection_changed_cb);
567 } 446 }
568 447
569 static void 448 static void
572 } 451 }
573 452
574 static void 453 static void
575 pidgin_disco_dialog_init(PidginDiscoDialog *dialog) 454 pidgin_disco_dialog_init(PidginDiscoDialog *dialog)
576 { 455 {
456 GtkTreeListModel *model = NULL;
577 GActionEntry entries[] = { 457 GActionEntry entries[] = {
578 { .name = "stop", .activate = activate_stop }, 458 { .name = "stop", .activate = activate_stop },
579 { .name = "browse", .activate = activate_browse }, 459 { .name = "browse", .activate = activate_browse },
580 { .name = "register", .activate = activate_register }, 460 { .name = "register", .activate = activate_register },
581 { .name = "add", .activate = activate_add_to_blist }, 461 { .name = "add", .activate = activate_add_to_blist },
589 469
590 /* accounts dropdown list */ 470 /* accounts dropdown list */
591 dialog->account = pidgin_account_chooser_get_selected( 471 dialog->account = pidgin_account_chooser_get_selected(
592 PIDGIN_ACCOUNT_CHOOSER(dialog->account_chooser)); 472 PIDGIN_ACCOUNT_CHOOSER(dialog->account_chooser));
593 473
594 gtk_widget_set_has_tooltip(GTK_WIDGET(dialog->tree), TRUE);
595 g_signal_connect(G_OBJECT(dialog->tree), "query-tooltip",
596 G_CALLBACK(disco_query_tooltip), dialog);
597
598 action_group = g_simple_action_group_new(); 474 action_group = g_simple_action_group_new();
599 action_map = G_ACTION_MAP(action_group); 475 action_map = G_ACTION_MAP(action_group);
600 g_action_map_add_action_entries(action_map, entries, G_N_ELEMENTS(entries), 476 g_action_map_add_action_entries(action_map, entries, G_N_ELEMENTS(entries),
601 dialog); 477 dialog);
602 478
616 g_action_map_lookup_action(action_map, "add")); 492 g_action_map_lookup_action(action_map, "add"));
617 g_simple_action_set_enabled(dialog->add_action, FALSE); 493 g_simple_action_set_enabled(dialog->add_action, FALSE);
618 494
619 gtk_widget_insert_action_group(GTK_WIDGET(dialog), "disco", 495 gtk_widget_insert_action_group(GTK_WIDGET(dialog), "disco",
620 G_ACTION_GROUP(action_group)); 496 G_ACTION_GROUP(action_group));
497
498 dialog->root = g_list_store_new(XMPP_DISCO_TYPE_SERVICE);
499 model = gtk_tree_list_model_new(G_LIST_MODEL(dialog->root), FALSE, FALSE,
500 (GtkTreeListModelCreateModelFunc)service_create_child_model_cb,
501 NULL, NULL);
502 gtk_sort_list_model_set_model(dialog->sorter, G_LIST_MODEL(model));
503 g_object_unref(model);
621 } 504 }
622 505
623 /****************************************************************************** 506 /******************************************************************************
624 * Public API 507 * Public API
625 *****************************************************************************/ 508 *****************************************************************************/
636 PidginDiscoDialog *dialog = g_object_new(PIDGIN_TYPE_DISCO_DIALOG, NULL); 519 PidginDiscoDialog *dialog = g_object_new(PIDGIN_TYPE_DISCO_DIALOG, NULL);
637 gtk_widget_show(GTK_WIDGET(dialog)); 520 gtk_widget_show(GTK_WIDGET(dialog));
638 return dialog; 521 return dialog;
639 } 522 }
640 523
641 void pidgin_disco_add_service(PidginDiscoList *pdl, XmppDiscoService *service, XmppDiscoService *parent) 524 void
525 pidgin_disco_add_service(PidginDiscoList *pdl, XmppDiscoService *service,
526 XmppDiscoService *parent)
642 { 527 {
643 PidginDiscoDialog *dialog; 528 PidginDiscoDialog *dialog;
644 GtkTreeIter iter, parent_iter, child;
645 char *icon_name = NULL;
646 gboolean append = TRUE;
647 529
648 dialog = pdl->dialog; 530 dialog = pdl->dialog;
649 g_return_if_fail(dialog != NULL); 531 g_return_if_fail(dialog != NULL);
650 532
651 if (service != NULL) { 533 purple_debug_info("xmppdisco", "Adding service \"%s\" to %p",
652 purple_debug_info("xmppdisco", "Adding service \"%s\"", 534 xmpp_disco_service_get_name(service), parent);
653 xmpp_disco_service_get_name(service)); 535
536 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(dialog->progress));
537
538 if(parent != NULL) {
539 xmpp_disco_service_add_child(parent, service);
654 } else { 540 } else {
655 purple_debug_info("xmppdisco", "Service \"%s\" has no children", 541 g_list_store_append(dialog->root, service);
656 xmpp_disco_service_get_name(parent)); 542 }
657 } 543 }
658
659 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(dialog->progress));
660
661 if (parent) {
662 GtkTreeRowReference *rr;
663 GtkTreePath *path;
664
665 rr = g_hash_table_lookup(pdl->services, parent);
666 path = gtk_tree_row_reference_get_path(rr);
667 if (path) {
668 gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model),
669 &parent_iter, path);
670 gtk_tree_path_free(path);
671
672 if (gtk_tree_model_iter_children(
673 GTK_TREE_MODEL(dialog->model), &child,
674 &parent_iter)) {
675 PidginDiscoList *tmp;
676 gtk_tree_model_get(
677 GTK_TREE_MODEL(dialog->model), &child,
678 SERVICE_COLUMN, &tmp, -1);
679 if (!tmp) {
680 append = FALSE;
681 }
682 }
683 }
684 }
685
686 if (service == NULL) {
687 if (parent != NULL && !append) {
688 gtk_tree_store_remove(dialog->model, &child);
689 }
690 return;
691 }
692
693 if (append) {
694 gtk_tree_store_append(dialog->model, &iter,
695 (parent ? &parent_iter : NULL));
696 } else {
697 iter = child;
698 }
699
700 if((xmpp_disco_service_get_flags(service) & XMPP_DISCO_BROWSE) != 0) {
701 GtkTreeRowReference *rr;
702 GtkTreePath *path;
703
704 gtk_tree_store_append(dialog->model, &child, &iter);
705
706 path = gtk_tree_model_get_path(GTK_TREE_MODEL(dialog->model),
707 &iter);
708 rr = gtk_tree_row_reference_new(GTK_TREE_MODEL(dialog->model),
709 path);
710 g_hash_table_insert(pdl->services, service, rr);
711 gtk_tree_path_free(path);
712 }
713
714 icon_name = xmpp_disco_service_get_icon_name(service);
715
716 gtk_tree_store_set(dialog->model, &iter, ICON_NAME_COLUMN, icon_name,
717 NAME_COLUMN, xmpp_disco_service_get_name(service),
718 DESCRIPTION_COLUMN, xmpp_disco_service_get_description(service),
719 SERVICE_COLUMN, service, -1);
720
721 g_free(icon_name);
722 }
723

mercurial