diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml index b56038d12e..9d66c91078 100644 --- a/demos/gtk-demo/demo.gresource.xml +++ b/demos/gtk-demo/demo.gresource.xml @@ -118,6 +118,9 @@ gnome-fs-directory.png gnome-fs-regular.png + + listview_settings.ui + listview_weather.txt @@ -197,6 +200,7 @@ infobar.c links.c listbox.c + listview_settings.c listview_weather.c list_store.c markup.c diff --git a/demos/gtk-demo/listview_settings.c b/demos/gtk-demo/listview_settings.c new file mode 100644 index 0000000000..8f91853e5a --- /dev/null +++ b/demos/gtk-demo/listview_settings.c @@ -0,0 +1,269 @@ +/* Lists/Settings + * + * This demo shows a settings viewer for GSettings. + * + * It demonstrates how to implement support for trees with listview. + */ + +#include + +/* Create an object that wraps GSettingsSchemaKey because that's a boxed type */ +typedef struct _SettingsKey SettingsKey; +struct _SettingsKey +{ + GObject parent_instance; + + GSettings *settings; + GSettingsSchemaKey *key; +}; + +enum { + PROP_0, + PROP_NAME, + PROP_SUMMARY, + PROP_DESCRIPTION, + PROP_VALUE, + + N_PROPS +}; + +#define SETTINGS_TYPE_KEY (settings_key_get_type ()) +G_DECLARE_FINAL_TYPE (SettingsKey, settings_key, SETTINGS, KEY, GObject); + +G_DEFINE_TYPE (SettingsKey, settings_key, G_TYPE_OBJECT); +static GParamSpec *properties[N_PROPS] = { NULL, }; + +static void +settings_key_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + SettingsKey *self = SETTINGS_KEY (object); + + switch (property_id) + { + case PROP_DESCRIPTION: + g_value_set_string (value, g_settings_schema_key_get_description (self->key)); + break; + + case PROP_NAME: + g_value_set_string (value, g_settings_schema_key_get_name (self->key)); + break; + + case PROP_SUMMARY: + g_value_set_string (value, g_settings_schema_key_get_summary (self->key)); + break; + + case PROP_VALUE: + { + GVariant *variant = g_settings_get_value (self->settings, g_settings_schema_key_get_name (self->key)); + g_value_take_string (value, g_variant_print (variant, FALSE)); + g_variant_unref (variant); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +settings_key_finalize (GObject *object) +{ + SettingsKey *self = SETTINGS_KEY (object); + + g_object_unref (self->settings); + g_settings_schema_key_unref (self->key); + + G_OBJECT_CLASS (settings_key_parent_class)->finalize (object); +} + +static void +settings_key_class_init (SettingsKeyClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = settings_key_finalize; + gobject_class->get_property = settings_key_get_property; + + properties[PROP_DESCRIPTION] = + g_param_spec_string ("description", NULL, NULL, NULL, G_PARAM_READABLE); + properties[PROP_NAME] = + g_param_spec_string ("name", NULL, NULL, NULL, G_PARAM_READABLE); + properties[PROP_SUMMARY] = + g_param_spec_string ("summary", NULL, NULL, NULL, G_PARAM_READABLE); + properties[PROP_VALUE] = + g_param_spec_string ("value", NULL, NULL, NULL, G_PARAM_READABLE); + + g_object_class_install_properties (gobject_class, N_PROPS, properties); +} + +static void +settings_key_init (SettingsKey *self) +{ +} + +static SettingsKey * +settings_key_new (GSettings *settings, + GSettingsSchemaKey *key) +{ + SettingsKey *result = g_object_new (SETTINGS_TYPE_KEY, NULL); + + result->settings = g_object_ref (settings); + result->key = g_settings_schema_key_ref (key); + + return result; +} + +static int +strvcmp (gconstpointer p1, + gconstpointer p2) +{ + const char * const *s1 = p1; + const char * const *s2 = p2; + + return strcmp (*s1, *s2); +} + +static gboolean +transform_settings_to_keys (GBinding *binding, + const GValue *from_value, + GValue *to_value, + gpointer unused) +{ + GtkTreeListRow *treelistrow; + GSettings *settings; + GSettingsSchema *schema; + GListStore *store; + char **keys; + guint i; + + treelistrow = g_value_get_object (from_value); + if (treelistrow == NULL) + return TRUE; + settings = gtk_tree_list_row_get_item (treelistrow); + g_object_get (settings, "settings-schema", &schema, NULL); + + store = g_list_store_new (SETTINGS_TYPE_KEY); + + keys = g_settings_schema_list_keys (schema); + qsort (keys, g_strv_length (keys), sizeof (char *), strvcmp); + + for (i = 0; keys[i] != NULL; i++) + { + GSettingsSchemaKey *almost_there = g_settings_schema_get_key (schema, keys[i]); + SettingsKey *finally = settings_key_new (settings, almost_there); + g_list_store_append (store, finally); + g_object_unref (finally); + g_settings_schema_key_unref (almost_there); + } + + g_strfreev (keys); + g_settings_schema_unref (schema); + g_object_unref (settings); + + g_value_take_object (to_value, store); + + return TRUE; +} + +static GListModel * +create_settings_model (gpointer item, + gpointer unused) +{ + GSettings *settings = item; + char **schemas; + GListStore *result; + guint i; + + if (settings == NULL) + { + g_settings_schema_source_list_schemas (g_settings_schema_source_get_default (), + TRUE, + &schemas, + NULL); + } + else + { + schemas = g_settings_list_children (settings); + } + + if (schemas == NULL || schemas[0] == NULL) + { + g_free (schemas); + return NULL; + } + + qsort (schemas, g_strv_length (schemas), sizeof (char *), strvcmp); + + result = g_list_store_new (G_TYPE_SETTINGS); + for (i = 0; schemas[i] != NULL; i++) + { + GSettings *child; + + if (settings == NULL) + child = g_settings_new (schemas[i]); + else + child = g_settings_get_child (settings, schemas[i]); + + g_list_store_append (result, child); + g_object_unref (child); + } + + g_strfreev (schemas); + + return G_LIST_MODEL (result); +} + +static GtkWidget *window = NULL; + +GtkWidget * +do_listview_settings (GtkWidget *do_widget) +{ + if (window == NULL) + { + GtkWidget *listview, *columnview; + GListModel *model; + GtkTreeListModel *treemodel; + GtkSingleSelection *selection; + GtkBuilder *builder; + + g_type_ensure (SETTINGS_TYPE_KEY); + + builder = gtk_builder_new_from_resource ("/listview_settings/listview_settings.ui"); + window = GTK_WIDGET (gtk_builder_get_object (builder, "window")); + gtk_window_set_display (GTK_WINDOW (window), + gtk_widget_get_display (do_widget)); + g_object_add_weak_pointer (G_OBJECT (window), (gpointer *) &window); + + listview = GTK_WIDGET (gtk_builder_get_object (builder, "listview")); + columnview = GTK_WIDGET (gtk_builder_get_object (builder, "columnview")); + model = create_settings_model (NULL, NULL); + treemodel = gtk_tree_list_model_new (FALSE, + model, + TRUE, + create_settings_model, + NULL, + NULL); + selection = gtk_single_selection_new (G_LIST_MODEL (treemodel)); + g_object_bind_property_full (selection, "selected-item", + columnview, "model", + G_BINDING_SYNC_CREATE, + transform_settings_to_keys, + NULL, + NULL, NULL); + gtk_list_view_set_model (GTK_LIST_VIEW (listview), G_LIST_MODEL (selection)); + g_object_unref (selection); + g_object_unref (treemodel); + g_object_unref (model); + } + + if (!gtk_widget_get_visible (window)) + gtk_widget_show (window); + else + gtk_window_destroy (GTK_WINDOW (window)); + + return window; +} diff --git a/demos/gtk-demo/listview_settings.ui b/demos/gtk-demo/listview_settings.ui new file mode 100644 index 0000000000..529adb4756 --- /dev/null +++ b/demos/gtk-demo/listview_settings.ui @@ -0,0 +1,108 @@ + + + + Settings + 600 + 400 + + + + + + + + + + + + + ]]> + + + + + + + + + + + + + Name + + + + + + + ]]> + + + + + + + Value + + + + + + + ]]> + + + + + + + + + + + + diff --git a/demos/gtk-demo/meson.build b/demos/gtk-demo/meson.build index 9261e89d1b..f4d73ff327 100644 --- a/demos/gtk-demo/meson.build +++ b/demos/gtk-demo/meson.build @@ -41,6 +41,7 @@ demos = files([ 'listbox.c', 'flowbox.c', 'list_store.c', + 'listview_settings.c', 'listview_weather.c', 'markup.c', 'modelbutton.c',