mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-15 21:20:09 +00:00
8d79a32c50
Change the apis in GtkListView, GtkColumnView and GtkGridView to be explicitly about GtkSelectionModel, to make it obvious that the widgets handle selection. Update all users.
434 lines
13 KiB
C
434 lines
13 KiB
C
/* Lists/Settings
|
||
*
|
||
* This demo shows a settings viewer for GSettings.
|
||
*
|
||
* It demonstrates how to implement support for trees with GtkListView.
|
||
* It also shows how to set up sorting and filtering for columns in a
|
||
* GtkColumnView.
|
||
*
|
||
* It also demonstrates different styles of list. The tree on the left
|
||
* uses the .navigation-sidebar style class, the list on the right uses
|
||
* the .data-table style class.
|
||
*/
|
||
|
||
#include <gtk/gtk.h>
|
||
|
||
#include <stdlib.h>
|
||
|
||
/* 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,
|
||
PROP_TYPE,
|
||
PROP_DEFAULT_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;
|
||
|
||
case PROP_TYPE:
|
||
{
|
||
const GVariantType *type = g_settings_schema_key_get_value_type (self->key);
|
||
g_value_set_string (value, g_variant_type_peek_string (type));
|
||
}
|
||
break;
|
||
|
||
case PROP_DEFAULT_VALUE:
|
||
{
|
||
GVariant *variant = g_settings_schema_key_get_default_value (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);
|
||
properties[PROP_TYPE] =
|
||
g_param_spec_string ("type", NULL, NULL, NULL, G_PARAM_READABLE);
|
||
properties[PROP_DEFAULT_VALUE] =
|
||
g_param_spec_string ("default-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 void
|
||
item_value_changed (GtkEditableLabel *label,
|
||
GParamSpec *pspec,
|
||
GtkListItem *item)
|
||
{
|
||
SettingsKey *self;
|
||
const char *text;
|
||
const GVariantType *type;
|
||
GVariant *variant;
|
||
GError *error = NULL;
|
||
const char *name;
|
||
char *value;
|
||
|
||
text = gtk_editable_get_text (GTK_EDITABLE (label));
|
||
|
||
g_object_get (item, "item", &self, NULL);
|
||
g_object_unref (self);
|
||
|
||
type = g_settings_schema_key_get_value_type (self->key);
|
||
name = g_settings_schema_key_get_name (self->key);
|
||
|
||
variant = g_variant_parse (type, text, NULL, NULL, &error);
|
||
if (!variant)
|
||
{
|
||
g_warning ("%s", error->message);
|
||
g_clear_error (&error);
|
||
goto revert;
|
||
}
|
||
|
||
if (!g_settings_schema_key_range_check (self->key, variant))
|
||
{
|
||
g_warning ("Not a valid value for %s", name);
|
||
goto revert;
|
||
}
|
||
|
||
g_settings_set_value (self->settings, name, variant);
|
||
g_variant_unref (variant);
|
||
return;
|
||
|
||
revert:
|
||
gtk_widget_error_bell (GTK_WIDGET (label));
|
||
|
||
g_object_get (self, "value", &value, NULL);
|
||
gtk_editable_set_text (GTK_EDITABLE (label), value);
|
||
g_free (value);
|
||
}
|
||
|
||
static int
|
||
strvcmp (gconstpointer p1,
|
||
gconstpointer p2)
|
||
{
|
||
const char * const *s1 = p1;
|
||
const char * const *s2 = p2;
|
||
|
||
return strcmp (*s1, *s2);
|
||
}
|
||
|
||
static GtkFilter *current_filter;
|
||
|
||
static gboolean
|
||
transform_settings_to_keys (GBinding *binding,
|
||
const GValue *from_value,
|
||
GValue *to_value,
|
||
gpointer data)
|
||
{
|
||
GtkTreeListRow *treelistrow;
|
||
GSettings *settings;
|
||
GSettingsSchema *schema;
|
||
GListStore *store;
|
||
GtkSortListModel *sort_model;
|
||
GtkFilterListModel *filter_model;
|
||
GtkFilter *filter;
|
||
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);
|
||
|
||
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);
|
||
|
||
sort_model = gtk_sort_list_model_new (G_LIST_MODEL (store),
|
||
g_object_ref (gtk_column_view_get_sorter (GTK_COLUMN_VIEW (data))));
|
||
|
||
filter = gtk_string_filter_new (gtk_property_expression_new (SETTINGS_TYPE_KEY, NULL, "name"));
|
||
g_set_object (¤t_filter, filter);
|
||
filter_model = gtk_filter_list_model_new (G_LIST_MODEL (sort_model), filter);
|
||
|
||
g_value_take_object (to_value, gtk_no_selection_new (G_LIST_MODEL (filter_model)));
|
||
|
||
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 void
|
||
search_enabled (GtkSearchEntry *entry)
|
||
{
|
||
gtk_editable_set_text (GTK_EDITABLE (entry), "");
|
||
}
|
||
|
||
static void
|
||
search_changed (GtkSearchEntry *entry,
|
||
gpointer data)
|
||
{
|
||
const char *text = gtk_editable_get_text (GTK_EDITABLE (entry));
|
||
|
||
if (current_filter)
|
||
gtk_string_filter_set_search (GTK_STRING_FILTER (current_filter), text);
|
||
}
|
||
|
||
static void
|
||
stop_search (GtkSearchEntry *entry,
|
||
gpointer data)
|
||
{
|
||
gtk_editable_set_text (GTK_EDITABLE (entry), "");
|
||
|
||
if (current_filter)
|
||
gtk_string_filter_set_search (GTK_STRING_FILTER (current_filter), "");
|
||
}
|
||
|
||
static GtkWidget *window = NULL;
|
||
|
||
GtkWidget *
|
||
do_listview_settings (GtkWidget *do_widget)
|
||
{
|
||
if (window == NULL)
|
||
{
|
||
GtkWidget *listview, *columnview;
|
||
GListModel *model;
|
||
GtkTreeListModel *treemodel;
|
||
GtkSingleSelection *selection;
|
||
GtkBuilderScope *scope;
|
||
GtkBuilder *builder;
|
||
GtkColumnViewColumn *name_column;
|
||
GtkColumnViewColumn *type_column;
|
||
GtkColumnViewColumn *default_column;
|
||
GtkColumnViewColumn *summary_column;
|
||
GtkColumnViewColumn *description_column;
|
||
GtkSorter *sorter;
|
||
GActionGroup *actions;
|
||
GAction *action;
|
||
|
||
g_type_ensure (SETTINGS_TYPE_KEY);
|
||
|
||
scope = gtk_builder_cscope_new ();
|
||
gtk_builder_cscope_add_callback_symbol (GTK_BUILDER_CSCOPE (scope), "search_enabled", (GCallback)search_enabled);
|
||
gtk_builder_cscope_add_callback_symbol (GTK_BUILDER_CSCOPE (scope), "search_changed", (GCallback)search_changed);
|
||
gtk_builder_cscope_add_callback_symbol (GTK_BUILDER_CSCOPE (scope), "stop_search", (GCallback)stop_search);
|
||
gtk_builder_cscope_add_callback_symbol (GTK_BUILDER_CSCOPE (scope), "item_value_changed", (GCallback)item_value_changed);
|
||
|
||
builder = gtk_builder_new ();
|
||
gtk_builder_set_scope (builder, scope);
|
||
g_object_unref (scope);
|
||
|
||
gtk_builder_add_from_resource (builder, "/listview_settings/listview_settings.ui", NULL);
|
||
|
||
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"));
|
||
type_column = GTK_COLUMN_VIEW_COLUMN (gtk_builder_get_object (builder, "type_column"));
|
||
default_column = GTK_COLUMN_VIEW_COLUMN (gtk_builder_get_object (builder, "default_column"));
|
||
summary_column = GTK_COLUMN_VIEW_COLUMN (gtk_builder_get_object (builder, "summary_column"));
|
||
description_column = GTK_COLUMN_VIEW_COLUMN (gtk_builder_get_object (builder, "description_column"));
|
||
|
||
actions = G_ACTION_GROUP (g_simple_action_group_new ());
|
||
|
||
action = G_ACTION (g_property_action_new ("show-type", type_column, "visible"));
|
||
g_action_map_add_action (G_ACTION_MAP (actions), action);
|
||
g_object_unref (action);
|
||
|
||
action = G_ACTION (g_property_action_new ("show-default", default_column, "visible"));
|
||
g_action_map_add_action (G_ACTION_MAP (actions), action);
|
||
g_object_unref (action);
|
||
|
||
action = G_ACTION (g_property_action_new ("show-summary", summary_column, "visible"));
|
||
g_action_map_add_action (G_ACTION_MAP (actions), action);
|
||
g_object_unref (action);
|
||
|
||
action = G_ACTION (g_property_action_new ("show-description", description_column, "visible"));
|
||
g_action_map_add_action (G_ACTION_MAP (actions), action);
|
||
g_object_unref (action);
|
||
|
||
gtk_widget_insert_action_group (columnview, "columnview", actions);
|
||
g_object_unref (actions);
|
||
|
||
model = create_settings_model (NULL, NULL);
|
||
treemodel = gtk_tree_list_model_new (model,
|
||
FALSE,
|
||
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,
|
||
columnview, NULL);
|
||
gtk_list_view_set_model (GTK_LIST_VIEW (listview), GTK_SELECTION_MODEL (selection));
|
||
g_object_unref (selection);
|
||
|
||
name_column = GTK_COLUMN_VIEW_COLUMN (gtk_builder_get_object (builder, "name_column"));
|
||
sorter = gtk_string_sorter_new (gtk_property_expression_new (SETTINGS_TYPE_KEY, NULL, "name"));
|
||
gtk_column_view_column_set_sorter (name_column, sorter);
|
||
g_object_unref (sorter);
|
||
|
||
g_object_unref (builder);
|
||
}
|
||
|
||
if (!gtk_widget_get_visible (window))
|
||
gtk_widget_show (window);
|
||
else
|
||
gtk_window_destroy (GTK_WINDOW (window));
|
||
|
||
return window;
|
||
}
|