pidgin/pidgin

Convert XMPP Service Discovery to GtkColumnView

15 months ago, Elliott Sales de Andrade
a109a8cc8059
Parents 083eae91c9a8
Children 7e0d24e06ce2
Convert XMPP Service Discovery to GtkColumnView

As with the other ports to GtkColumnView, this drops the popover and the tooltip.

Connecting to the expanded property (in order to lazy-load children) is a bit weird, but seems to be the only way I could find to do it.

Testing Done:
Browsed `pidgin.im`, double-clicked `conference.pidgin.im`/clicked the expander, and the rooms were loaded. Double-clicked all the rooms and confirmed the Add Chat dialog was correctly filled. Also clicked the Add button after selecting one and confirmed the same.

Bugs closed: PIDGIN-17747

Reviewed at https://reviews.imfreedom.org/r/2256/
--- a/pidgin/plugins/disco/gtkdisco.c Thu Feb 23 23:44:43 2023 -0600
+++ b/pidgin/plugins/disco/gtkdisco.c Fri Feb 24 00:45:22 2023 -0600
@@ -51,7 +51,6 @@
static void
pidgin_disco_list_destroy(PidginDiscoList *list)
{
- g_hash_table_destroy(list->services);
if (list->dialog && list->dialog->discolist == list) {
list->dialog->discolist = NULL;
}
@@ -181,11 +180,9 @@
g_simple_action_set_enabled(dialog->register_action, FALSE);
g_clear_pointer(&dialog->discolist, pidgin_disco_list_unref);
- gtk_tree_store_clear(dialog->model);
+ g_list_store_remove_all(dialog->root);
pdl = dialog->discolist = pidgin_disco_list_new();
- pdl->services = g_hash_table_new_full(NULL, NULL, NULL,
- (GDestroyNotify)gtk_tree_row_reference_free);
pdl->pc = pc;
/* We keep a copy... */
pidgin_disco_list_ref(pdl);
@@ -245,64 +242,30 @@
}
}
-static gboolean
-service_click_cb(G_GNUC_UNUSED GtkGestureClick *click,
- G_GNUC_UNUSED gint n_press,
- gdouble x, gdouble y, gpointer data)
+static void
+selection_changed_cb(GtkSelectionModel *self, G_GNUC_UNUSED guint position,
+ G_GNUC_UNUSED guint n_items, gpointer data)
{
PidginDiscoDialog *dialog = data;
- XmppDiscoService *service;
-
- GtkTreePath *path;
- GtkTreeIter iter;
- GValue val;
-
- GdkRectangle rect;
-
- /* Figure out what was clicked */
- if (!gtk_tree_view_get_path_at_pos(dialog->tree, (gint)x, (gint)y, &path,
- NULL, NULL, NULL))
- {
- return FALSE;
- }
- gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path);
- gtk_tree_path_free(path);
- val.g_type = 0;
- gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter,
- SERVICE_COLUMN, &val);
- service = g_value_get_pointer(&val);
-
- if (!service) {
- return FALSE;
- }
-
- gtk_tree_view_convert_bin_window_to_widget_coords(dialog->tree,
- (gint)x, (gint)y,
- &rect.x, &rect.y);
- rect.width = rect.height = 1;
-
- gtk_popover_set_pointing_to(GTK_POPOVER(dialog->popover), &rect);
- gtk_popover_popup(GTK_POPOVER(dialog->popover));
-
- return FALSE;
-}
-
-static void
-selection_changed_cb(GtkTreeSelection *selection, PidginDiscoDialog *dialog)
-{
- GtkTreeIter iter;
- GValue val;
+ GtkTreeListRow *row = NULL;
gboolean allow_add = FALSE, allow_register = FALSE;
- if (gtk_tree_selection_get_selected(selection, NULL, &iter)) {
- val.g_type = 0;
- gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter,
- SERVICE_COLUMN, &val);
- dialog->selected = g_value_get_pointer(&val);
- if (dialog->selected != NULL) {
- XmppDiscoServiceFlags flags = xmpp_disco_service_get_flags(dialog->selected);
+ /* The passed in position and n_items gives the *range* of selections that
+ * have changed, so just re-query it since GtkSingleSelection has exactly
+ * one. */
+ row = gtk_single_selection_get_selected_item(GTK_SINGLE_SELECTION(self));
+ if(row != NULL) {
+ dialog->selected = gtk_tree_list_row_get_item(row);
+ if(XMPP_DISCO_IS_SERVICE(dialog->selected)) {
+ XmppDiscoServiceFlags flags;
+
+ flags = xmpp_disco_service_get_flags(dialog->selected);
allow_add = (flags & XMPP_DISCO_ADD) != 0;
allow_register = (flags & XMPP_DISCO_REGISTER) != 0;
+
+ /* gtk_tree_list_row_get_item returns a ref, but this struct isn't
+ * supposed to hold one, as the model holds on to it. */
+ g_object_unref(dialog->selected);
}
}
@@ -310,54 +273,73 @@
g_simple_action_set_enabled(dialog->register_action, allow_register);
}
-static void
-row_expanded_cb(G_GNUC_UNUSED GtkTreeView *tree, GtkTreeIter *arg1,
- G_GNUC_UNUSED GtkTreePath *rg2, gpointer user_data)
-{
- PidginDiscoDialog *dialog = user_data;
- XmppDiscoService *service;
- GValue val;
+static GListModel *
+service_create_child_model_cb(GObject *item, G_GNUC_UNUSED gpointer data) {
+ XmppDiscoService *service = XMPP_DISCO_SERVICE(item);
+ GListModel *model = NULL;
- val.g_type = 0;
- gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), arg1,
- SERVICE_COLUMN, &val);
- service = g_value_get_pointer(&val);
- xmpp_disco_service_expand(service);
+ model = xmpp_disco_service_get_child_model(service);
+ if(G_IS_LIST_MODEL(model)) {
+ /* Always return a new ref, as the caller takes ownership. */
+ g_object_ref(model);
+ }
+
+ return model;
}
static void
-row_activated_cb(G_GNUC_UNUSED GtkTreeView *tree_view,
- GtkTreePath *path,
- G_GNUC_UNUSED GtkTreeViewColumn *column,
- gpointer user_data)
+row_expanded_cb(GObject *obj, G_GNUC_UNUSED GParamSpec *pspec,
+ G_GNUC_UNUSED gpointer data)
{
+ GtkTreeListRow *row = GTK_TREE_LIST_ROW(obj);
+
+ if(gtk_tree_list_row_get_expanded(row)) {
+ XmppDiscoService *service = gtk_tree_list_row_get_item(row);
+ if(XMPP_DISCO_IS_SERVICE(service)) {
+ xmpp_disco_service_expand(service);
+ }
+ g_clear_object(&service);
+ }
+}
+
+static void
+list_row_notify_cb(GObject *obj, GParamSpec *pspec, gpointer data) {
+ GtkTreeListRow *row = NULL;
+
+ row = gtk_tree_expander_get_list_row(GTK_TREE_EXPANDER(obj));
+ if(GTK_IS_TREE_LIST_ROW(row)) {
+ g_signal_connect(row, "notify::expanded", G_CALLBACK(row_expanded_cb),
+ NULL);
+ }
+}
+
+static void
+row_activated_cb(GtkColumnView *self, guint position, gpointer user_data) {
PidginDiscoDialog *dialog = user_data;
- GtkTreeIter iter;
- XmppDiscoService *service;
+ GtkSelectionModel *model = NULL;
+ GtkTreeListRow *row = NULL;
+ XmppDiscoService *service = NULL;
XmppDiscoServiceFlags flags;
- GValue val;
- if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path)) {
- return;
- }
-
- val.g_type = 0;
- gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter,
- SERVICE_COLUMN, &val);
- service = g_value_get_pointer(&val);
+ model = gtk_column_view_get_model(self);
+ row = g_list_model_get_item(G_LIST_MODEL(model), position);
+ service = gtk_tree_list_row_get_item(row);
flags = xmpp_disco_service_get_flags(service);
if((flags & XMPP_DISCO_BROWSE) != 0) {
- if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(dialog->tree), path)) {
- gtk_tree_view_collapse_row(GTK_TREE_VIEW(dialog->tree), path);
+ if(gtk_tree_list_row_get_expanded(row)) {
+ gtk_tree_list_row_set_expanded(row, FALSE);
} else {
- gtk_tree_view_expand_row(GTK_TREE_VIEW(dialog->tree), path, FALSE);
+ gtk_tree_list_row_set_expanded(row, TRUE);
}
} else if((flags & XMPP_DISCO_REGISTER) != 0) {
g_action_activate(G_ACTION(dialog->register_action), NULL);
} else if((flags & XMPP_DISCO_ADD) != 0) {
g_action_activate(G_ACTION(dialog->add_action), NULL);
}
+
+ g_clear_object(&service);
+ g_clear_object(&row);
}
static void
@@ -393,104 +375,6 @@
pidgin_disco_list_set_in_progress(dialog->discolist, FALSE);
}
-static gboolean
-disco_query_tooltip(GtkWidget *widget, int x, int y, gboolean keyboard_mode,
- GtkTooltip *tooltip, gpointer data)
-{
- PidginDiscoDialog *dialog = data;
- GtkTreePath *path = NULL;
- GtkTreeIter iter;
- XmppDiscoService *service;
- GValue val;
- const char *type = NULL;
- char *markup, *jid, *name, *desc = NULL;
-
- if (keyboard_mode) {
- GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
- if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) {
- return FALSE;
- }
- path = gtk_tree_model_get_path(GTK_TREE_MODEL(dialog->model), &iter);
- } else {
- gint bx, by;
-
- gtk_tree_view_convert_widget_to_bin_window_coords(GTK_TREE_VIEW(widget),
- x, y, &bx, &by);
- gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bx, by, &path,
- NULL, NULL, NULL);
- if (path == NULL) {
- return FALSE;
- }
-
- if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path)) {
- gtk_tree_path_free(path);
- return FALSE;
- }
- }
-
- val.g_type = 0;
- gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter,
- SERVICE_COLUMN, &val);
- service = g_value_get_pointer(&val);
- if (!service) {
- gtk_tree_path_free(path);
- return FALSE;
- }
-
- switch(xmpp_disco_service_get_service_type(service)) {
- case XMPP_DISCO_SERVICE_TYPE_UNSET:
- type = _("Unknown");
- break;
-
- case XMPP_DISCO_SERVICE_TYPE_GATEWAY:
- type = _("Gateway");
- break;
-
- case XMPP_DISCO_SERVICE_TYPE_DIRECTORY:
- type = _("Directory");
- break;
-
- case XMPP_DISCO_SERVICE_TYPE_CHAT:
- type = _("Chat");
- break;
-
- case XMPP_DISCO_SERVICE_TYPE_PUBSUB_COLLECTION:
- type = _("PubSub Collection");
- break;
-
- case XMPP_DISCO_SERVICE_TYPE_PUBSUB_LEAF:
- type = _("PubSub Leaf");
- break;
-
- case XMPP_DISCO_SERVICE_TYPE_OTHER:
- type = _("Other");
- break;
- }
-
- name = g_markup_escape_text(xmpp_disco_service_get_name(service), -1);
- jid = g_markup_escape_text(xmpp_disco_service_get_jid(service), -1);
- if(xmpp_disco_service_get_description(service) != NULL) {
- desc = g_markup_escape_text(xmpp_disco_service_get_description(service),
- -1);
- }
-
- markup = g_strdup_printf("<span size='x-large' weight='bold'>%s</span>\n<b>%s:</b> %s%s%s",
- name, type, jid,
- desc != NULL ? _("\n<b>Description:</b> ") : "",
- desc != NULL ? desc : "");
-
- gtk_tooltip_set_markup(tooltip, markup);
- gtk_tree_view_set_tooltip_row(GTK_TREE_VIEW(widget), tooltip, path);
-
- g_free(markup);
- g_free(jid);
- g_free(name);
- g_free(desc);
- gtk_tree_path_free(path);
-
- return TRUE;
-}
-
void pidgin_disco_signed_off_cb(PurpleConnection *pc)
{
GList *node;
@@ -506,7 +390,7 @@
pidgin_disco_list_set_in_progress(list, FALSE);
}
- gtk_tree_store_clear(dialog->model);
+ g_list_store_remove_all(dialog->root);
pidgin_disco_list_unref(list);
dialog->discolist = NULL;
@@ -550,18 +434,13 @@
gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
progress);
gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
- tree);
- gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
- model);
- gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
- popover);
+ sorter);
gtk_widget_class_bind_template_callback(widget_class, destroy_win_cb);
gtk_widget_class_bind_template_callback(widget_class,
dialog_select_account_cb);
gtk_widget_class_bind_template_callback(widget_class, row_activated_cb);
- gtk_widget_class_bind_template_callback(widget_class, row_expanded_cb);
- gtk_widget_class_bind_template_callback(widget_class, service_click_cb);
+ gtk_widget_class_bind_template_callback(widget_class, list_row_notify_cb);
gtk_widget_class_bind_template_callback(widget_class,
selection_changed_cb);
}
@@ -574,6 +453,7 @@
static void
pidgin_disco_dialog_init(PidginDiscoDialog *dialog)
{
+ GtkTreeListModel *model = NULL;
GActionEntry entries[] = {
{ .name = "stop", .activate = activate_stop },
{ .name = "browse", .activate = activate_browse },
@@ -591,10 +471,6 @@
dialog->account = pidgin_account_chooser_get_selected(
PIDGIN_ACCOUNT_CHOOSER(dialog->account_chooser));
- gtk_widget_set_has_tooltip(GTK_WIDGET(dialog->tree), TRUE);
- g_signal_connect(G_OBJECT(dialog->tree), "query-tooltip",
- G_CALLBACK(disco_query_tooltip), dialog);
-
action_group = g_simple_action_group_new();
action_map = G_ACTION_MAP(action_group);
g_action_map_add_action_entries(action_map, entries, G_N_ELEMENTS(entries),
@@ -618,6 +494,13 @@
gtk_widget_insert_action_group(GTK_WIDGET(dialog), "disco",
G_ACTION_GROUP(action_group));
+
+ dialog->root = g_list_store_new(XMPP_DISCO_TYPE_SERVICE);
+ model = gtk_tree_list_model_new(G_LIST_MODEL(dialog->root), FALSE, FALSE,
+ (GtkTreeListModelCreateModelFunc)service_create_child_model_cb,
+ NULL, NULL);
+ gtk_sort_list_model_set_model(dialog->sorter, G_LIST_MODEL(model));
+ g_object_unref(model);
}
/******************************************************************************
@@ -638,86 +521,23 @@
return dialog;
}
-void pidgin_disco_add_service(PidginDiscoList *pdl, XmppDiscoService *service, XmppDiscoService *parent)
+void
+pidgin_disco_add_service(PidginDiscoList *pdl, XmppDiscoService *service,
+ XmppDiscoService *parent)
{
PidginDiscoDialog *dialog;
- GtkTreeIter iter, parent_iter, child;
- char *icon_name = NULL;
- gboolean append = TRUE;
dialog = pdl->dialog;
g_return_if_fail(dialog != NULL);
- if (service != NULL) {
- purple_debug_info("xmppdisco", "Adding service \"%s\"",
- xmpp_disco_service_get_name(service));
- } else {
- purple_debug_info("xmppdisco", "Service \"%s\" has no children",
- xmpp_disco_service_get_name(parent));
- }
+ purple_debug_info("xmppdisco", "Adding service \"%s\" to %p",
+ xmpp_disco_service_get_name(service), parent);
gtk_progress_bar_pulse(GTK_PROGRESS_BAR(dialog->progress));
- if (parent) {
- GtkTreeRowReference *rr;
- GtkTreePath *path;
-
- rr = g_hash_table_lookup(pdl->services, parent);
- path = gtk_tree_row_reference_get_path(rr);
- if (path) {
- gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model),
- &parent_iter, path);
- gtk_tree_path_free(path);
-
- if (gtk_tree_model_iter_children(
- GTK_TREE_MODEL(dialog->model), &child,
- &parent_iter)) {
- PidginDiscoList *tmp;
- gtk_tree_model_get(
- GTK_TREE_MODEL(dialog->model), &child,
- SERVICE_COLUMN, &tmp, -1);
- if (!tmp) {
- append = FALSE;
- }
- }
- }
- }
-
- if (service == NULL) {
- if (parent != NULL && !append) {
- gtk_tree_store_remove(dialog->model, &child);
- }
- return;
+ if(parent != NULL) {
+ xmpp_disco_service_add_child(parent, service);
+ } else {
+ g_list_store_append(dialog->root, service);
}
-
- if (append) {
- gtk_tree_store_append(dialog->model, &iter,
- (parent ? &parent_iter : NULL));
- } else {
- iter = child;
- }
-
- if((xmpp_disco_service_get_flags(service) & XMPP_DISCO_BROWSE) != 0) {
- GtkTreeRowReference *rr;
- GtkTreePath *path;
-
- gtk_tree_store_append(dialog->model, &child, &iter);
-
- path = gtk_tree_model_get_path(GTK_TREE_MODEL(dialog->model),
- &iter);
- rr = gtk_tree_row_reference_new(GTK_TREE_MODEL(dialog->model),
- path);
- g_hash_table_insert(pdl->services, service, rr);
- gtk_tree_path_free(path);
- }
-
- icon_name = xmpp_disco_service_get_icon_name(service);
-
- gtk_tree_store_set(dialog->model, &iter, ICON_NAME_COLUMN, icon_name,
- NAME_COLUMN, xmpp_disco_service_get_name(service),
- DESCRIPTION_COLUMN, xmpp_disco_service_get_description(service),
- SERVICE_COLUMN, service, -1);
-
- g_free(icon_name);
}
-
--- a/pidgin/plugins/disco/gtkdisco.h Thu Feb 23 23:44:43 2023 -0600
+++ b/pidgin/plugins/disco/gtkdisco.h Fri Feb 24 00:45:22 2023 -0600
@@ -43,13 +43,11 @@
GSimpleAction *add_action;
XmppDiscoService *selected;
- GtkTreeView *tree;
- GtkTreeStore *model;
+ GtkSortListModel *sorter;
+ GListStore *root;
PurpleAccount *account;
PidginDiscoList *discolist;
- GtkPopoverMenu *popover;
-
gpointer prompt_handle;
};
--- a/pidgin/plugins/disco/resources/disco.ui Thu Feb 23 23:44:43 2023 -0600
+++ b/pidgin/plugins/disco/resources/disco.ui Fri Feb 24 00:45:22 2023 -0600
@@ -25,30 +25,6 @@
<!-- interface-name Pidgin -->
<!-- interface-description Internet Messenger -->
<!-- interface-copyright Pidgin Developers <devel@pidgin.im> -->
- <menu id="popover_menu">
- <section>
- <item>
- <attribute name="action">disco.add</attribute>
- <attribute name="label" translatable="yes">Add to Buddy List</attribute>
- </item>
- <item>
- <attribute name="action">disco.register</attribute>
- <attribute name="label" translatable="yes">Register</attribute>
- </item>
- </section>
- </menu>
- <object class="GtkTreeStore" id="model">
- <columns>
- <!-- column-name icon-name -->
- <column type="gchararray"/>
- <!-- column-name name -->
- <column type="gchararray"/>
- <!-- column-name description -->
- <column type="gchararray"/>
- <!-- column-name service -->
- <column type="gpointer"/>
- </columns>
- </object>
<template class="PidginDiscoDialog" parent="GtkDialog">
<property name="title" translatable="1">Service Discovery</property>
<signal name="destroy" handler="destroy_win_cb" swapped="no"/>
@@ -93,59 +69,126 @@
<property name="vscrollbar_policy">always</property>
<property name="min_content_height">250</property>
<property name="child">
- <object class="GtkTreeView" id="tree">
+ <object class="GtkColumnView" id="columnview">
<property name="focusable">1</property>
- <property name="model">model</property>
- <signal name="row-activated" handler="row_activated_cb" object="PidginDiscoDialog" swapped="no"/>
- <signal name="row-expanded" handler="row_expanded_cb" object="PidginDiscoDialog" swapped="no"/>
- <child internal-child="selection">
- <object class="GtkTreeSelection">
- <signal name="changed" handler="selection_changed_cb" swapped="no"/>
+ <property name="model">
+ <object class="GtkSingleSelection">
+ <property name="model">
+ <object class="GtkSortListModel" id="sorter">
+ <property name="sorter">
+ <object class="GtkTreeListRowSorter">
+ <binding name="sorter">
+ <lookup name="sorter">columnview</lookup>
+ </binding>
+ </object>
+ </property>
+ </object>
+ </property>
+ <signal name="selection-changed" handler="selection_changed_cb"/>
</object>
- </child>
+ </property>
+ <signal name="activate" handler="row_activated_cb"/>
<child>
- <object class="GtkGestureClick">
- <property name="button">3</property>
- <signal name="pressed" handler="service_click_cb" object="PidginDiscoDialog" swapped="no"/>
- </object>
- </child>
- <child>
- <object class="GtkPopoverMenu" id="popover">
- <property name="menu-model">popover_menu</property>
+ <object class="GtkColumnViewColumn">
+ <property name="resizable">1</property>
+ <property name="title" translatable="1">Name</property>
+ <property name="factory">
+ <object class="GtkBuilderListItemFactory">
+ <property name="bytes">
+<![CDATA[
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="GtkListItem">
+ <property name="child">
+ <object class="GtkTreeExpander">
+ <property name="child">
+ <object class="GtkBox">
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkImage">
+ <binding name="icon-name">
+ <lookup name="icon-name" type="XmppDiscoService">
+ <lookup name="item" type="GtkTreeListRow">
+ <lookup name="item">GtkListItem</lookup>
+ </lookup>
+ </lookup>
+ </binding>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="ellipsize">end</property>
+ <property name="xalign">0</property>
+ <binding name="label">
+ <lookup name="name" type="XmppDiscoService">
+ <lookup name="item" type="GtkTreeListRow">
+ <lookup name="item">GtkListItem</lookup>
+ </lookup>
+ </lookup>
+ </binding>
+ </object>
+ </child>
+ </object>
+ </property>
+ <binding name="list-row">
+ <lookup name="item">GtkListItem</lookup>
+ </binding>
+ <signal name="notify::list-row" handler="list_row_notify_cb"/>
+ </object>
+ </property>
+ </template>
+</interface>
+]]>
+ </property>
+ </object>
+ </property>
+ <property name="sorter">
+ <object class="GtkStringSorter">
+ <property name="expression">
+ <lookup name="name" type="XmppDiscoService"></lookup>
+ </property>
+ </object>
+ </property>
</object>
</child>
<child>
- <object class="GtkTreeViewColumn">
- <property name="resizable">1</property>
- <property name="title" translatable="1">Name</property>
- <property name="reorderable">1</property>
- <property name="sort_column_id">1</property>
- <child>
- <object class="GtkCellRendererPixbuf"/>
- <attributes>
- <attribute name="icon-name">0</attribute>
- </attributes>
- </child>
- <child>
- <object class="GtkCellRendererText"/>
- <attributes>
- <attribute name="text">1</attribute>
- </attributes>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkTreeViewColumn">
+ <object class="GtkColumnViewColumn">
+ <property name="expand">1</property>
<property name="resizable">1</property>
<property name="title" translatable="1">Description</property>
- <property name="reorderable">1</property>
- <property name="sort_column_id">2</property>
- <child>
- <object class="GtkCellRendererText"/>
- <attributes>
- <attribute name="text">2</attribute>
- </attributes>
- </child>
+ <property name="factory">
+ <object class="GtkBuilderListItemFactory">
+ <property name="bytes">
+<![CDATA[
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="GtkListItem">
+ <property name="child">
+ <object class="GtkLabel">
+ <property name="ellipsize">end</property>
+ <property name="xalign">0</property>
+ <binding name="label">
+ <lookup name="description" type="XmppDiscoService">
+ <lookup name="item" type="GtkTreeListRow">
+ <lookup name="item">GtkListItem</lookup>
+ </lookup>
+ </lookup>
+ </binding>
+ </object>
+ </property>
+ </template>
+</interface>
+]]>
+ </property>
+ </object>
+ </property>
+ <property name="sorter">
+ <object class="GtkStringSorter">
+ <property name="expression">
+ <lookup name="description" type="XmppDiscoService"></lookup>
+ </property>
+ </object>
+ </property>
</object>
</child>
</object>
--- a/pidgin/plugins/disco/xmppdiscoservice.c Thu Feb 23 23:44:43 2023 -0600
+++ b/pidgin/plugins/disco/xmppdiscoservice.c Fri Feb 24 00:45:22 2023 -0600
@@ -29,6 +29,7 @@
GObject parent;
PidginDiscoList *list;
+ GListStore *children;
char *name;
char *description;
@@ -53,6 +54,7 @@
PROP_NODE,
PROP_EXPANDED,
PROP_ICON_NAME,
+ PROP_CHILD_MODEL,
PROP_LAST
};
@@ -68,6 +70,29 @@
g_object_notify_by_pspec(G_OBJECT(service), properties[PROP_LIST]);
}
+static void
+xmpp_disco_service_refresh_child_model(GObject *obj,
+ G_GNUC_UNUSED GParamSpec *pspec,
+ G_GNUC_UNUSED gpointer data)
+{
+ XmppDiscoService *service = XMPP_DISCO_SERVICE(obj);
+ gboolean changed = FALSE;
+
+ if((service->flags & XMPP_DISCO_BROWSE) != 0) {
+ if(service->children == NULL) {
+ service->children = g_list_store_new(XMPP_DISCO_TYPE_SERVICE);
+ changed = TRUE;
+ }
+ } else {
+ changed = service->children != NULL;
+ g_clear_object(&service->children);
+ }
+
+ if(changed) {
+ g_object_notify_by_pspec(G_OBJECT(service), properties[PROP_CHILD_MODEL]);
+ }
+}
+
/******************************************************************************
* GObject implementation
*****************************************************************************/
@@ -115,6 +140,10 @@
g_value_take_string(value,
xmpp_disco_service_get_icon_name(service));
break;
+ case PROP_CHILD_MODEL:
+ g_value_set_object(value,
+ xmpp_disco_service_get_child_model(service));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
@@ -174,6 +203,7 @@
g_clear_pointer(&service->gateway_type, g_free);
g_clear_pointer(&service->jid, g_free);
g_clear_pointer(&service->node, g_free);
+ g_clear_object(&service->children);
G_OBJECT_CLASS(xmpp_disco_service_parent_class)->finalize(obj);
}
@@ -237,6 +267,12 @@
NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ properties[PROP_CHILD_MODEL] = g_param_spec_object(
+ "child-model", "child-model",
+ "The model containing children of this service.",
+ G_TYPE_LIST_MODEL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
g_object_class_install_properties(obj_class, PROP_LAST, properties);
}
@@ -245,7 +281,9 @@
}
static void
-xmpp_disco_service_init(G_GNUC_UNUSED XmppDiscoService *service) {
+xmpp_disco_service_init(XmppDiscoService *service) {
+ g_signal_connect(service, "notify::flags",
+ G_CALLBACK(xmpp_disco_service_refresh_child_model), NULL);
}
/******************************************************************************
@@ -465,3 +503,21 @@
return icon_name;
}
+
+GListModel *
+xmpp_disco_service_get_child_model(XmppDiscoService *service) {
+ g_return_val_if_fail(XMPP_DISCO_IS_SERVICE(service), NULL);
+
+ return G_LIST_MODEL(service->children);
+}
+
+void
+xmpp_disco_service_add_child(XmppDiscoService *service,
+ XmppDiscoService *child)
+{
+ g_return_if_fail(XMPP_DISCO_IS_SERVICE(service));
+ g_return_if_fail(XMPP_DISCO_IS_SERVICE(child));
+ g_return_if_fail((service->flags & XMPP_DISCO_BROWSE) != 0);
+
+ g_list_store_append(service->children, child);
+}
--- a/pidgin/plugins/disco/xmppdiscoservice.h Thu Feb 23 23:44:43 2023 -0600
+++ b/pidgin/plugins/disco/xmppdiscoservice.h Fri Feb 24 00:45:22 2023 -0600
@@ -108,4 +108,7 @@
char *xmpp_disco_service_get_icon_name(XmppDiscoService *service);
+GListModel *xmpp_disco_service_get_child_model(XmppDiscoService *service);
+void xmpp_disco_service_add_child(XmppDiscoService *service, XmppDiscoService *child);
+
#endif /* PIDGIN_XMPP_DISCO_SERVICE_H */