2019-10-13 05:40:58 +00:00
|
|
|
/* Lists/Settings
|
|
|
|
*
|
|
|
|
* This demo shows a settings viewer for GSettings.
|
|
|
|
*
|
2019-12-14 16:53:24 +00:00
|
|
|
* It demonstrates how to implement support for trees with GtkListView.
|
2019-12-16 04:48:34 +00:00
|
|
|
* It also shows how to set up sorting and filtering for columns in a
|
|
|
|
* GtkColumnView.
|
2019-10-13 05:40:58 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <gtk/gtk.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,
|
2020-06-01 03:42:36 +00:00
|
|
|
PROP_TYPE,
|
|
|
|
PROP_DEFAULT_VALUE,
|
2019-10-13 05:40:58 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2020-06-01 03:42:36 +00:00
|
|
|
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;
|
|
|
|
|
2019-10-13 05:40:58 +00:00
|
|
|
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);
|
2020-06-01 03:42:36 +00:00
|
|
|
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);
|
2019-10-13 05:40:58 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-06-03 23:33:45 +00:00
|
|
|
static void
|
|
|
|
item_value_changed (GtkListItem *item,
|
|
|
|
GtkEntry *entry)
|
|
|
|
{
|
|
|
|
SettingsKey *self;
|
|
|
|
const char *text;
|
|
|
|
const GVariantType *type;
|
|
|
|
GVariant *variant;
|
|
|
|
GError *error = NULL;
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
text = gtk_editable_get_text (GTK_EDITABLE (entry));
|
|
|
|
|
|
|
|
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);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!g_settings_schema_key_range_check (self->key, variant))
|
|
|
|
{
|
|
|
|
g_warning ("Not a valid value for %s", name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_settings_set_value (self->settings, name, variant);
|
|
|
|
g_variant_unref (variant);
|
|
|
|
}
|
|
|
|
|
2019-10-13 05:40:58 +00:00
|
|
|
static int
|
|
|
|
strvcmp (gconstpointer p1,
|
|
|
|
gconstpointer p2)
|
|
|
|
{
|
|
|
|
const char * const *s1 = p1;
|
|
|
|
const char * const *s2 = p2;
|
|
|
|
|
|
|
|
return strcmp (*s1, *s2);
|
|
|
|
}
|
|
|
|
|
2019-12-16 04:48:34 +00:00
|
|
|
static GtkFilter *current_filter;
|
|
|
|
|
2019-10-13 05:40:58 +00:00
|
|
|
static gboolean
|
|
|
|
transform_settings_to_keys (GBinding *binding,
|
|
|
|
const GValue *from_value,
|
|
|
|
GValue *to_value,
|
2019-12-14 16:53:24 +00:00
|
|
|
gpointer data)
|
2019-10-13 05:40:58 +00:00
|
|
|
{
|
|
|
|
GtkTreeListRow *treelistrow;
|
|
|
|
GSettings *settings;
|
|
|
|
GSettingsSchema *schema;
|
|
|
|
GListStore *store;
|
2019-12-14 16:53:24 +00:00
|
|
|
GtkSortListModel *sort_model;
|
2019-12-16 04:48:34 +00:00
|
|
|
GtkFilterListModel *filter_model;
|
|
|
|
GtkFilter *filter;
|
2020-06-03 20:56:19 +00:00
|
|
|
GtkNoSelection *selection_model;
|
2019-12-16 04:48:34 +00:00
|
|
|
GtkExpression *expression;
|
2019-10-13 05:40:58 +00:00
|
|
|
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);
|
|
|
|
|
2019-12-14 16:53:24 +00:00
|
|
|
sort_model = gtk_sort_list_model_new (G_LIST_MODEL (store),
|
|
|
|
gtk_column_view_get_sorter (GTK_COLUMN_VIEW (data)));
|
|
|
|
g_object_unref (store);
|
|
|
|
|
2019-12-16 04:48:34 +00:00
|
|
|
expression = gtk_property_expression_new (SETTINGS_TYPE_KEY, NULL, "name");
|
|
|
|
filter = gtk_string_filter_new ();
|
|
|
|
gtk_string_filter_set_expression (GTK_STRING_FILTER (filter), expression);
|
|
|
|
filter_model = gtk_filter_list_model_new (G_LIST_MODEL (sort_model), filter);
|
|
|
|
gtk_expression_unref (expression);
|
|
|
|
g_object_unref (sort_model);
|
|
|
|
|
|
|
|
g_set_object (¤t_filter, filter);
|
|
|
|
|
|
|
|
g_object_unref (filter);
|
|
|
|
|
2020-06-03 20:56:19 +00:00
|
|
|
selection_model = gtk_no_selection_new (G_LIST_MODEL (filter_model));
|
|
|
|
g_object_unref (filter_model);
|
|
|
|
|
|
|
|
g_value_take_object (to_value, selection_model);
|
2019-10-13 05:40:58 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-12-16 04:48:34 +00:00
|
|
|
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), "");
|
|
|
|
}
|
|
|
|
|
2019-10-13 05:40:58 +00:00
|
|
|
static GtkWidget *window = NULL;
|
|
|
|
|
|
|
|
GtkWidget *
|
|
|
|
do_listview_settings (GtkWidget *do_widget)
|
|
|
|
{
|
|
|
|
if (window == NULL)
|
|
|
|
{
|
|
|
|
GtkWidget *listview, *columnview;
|
|
|
|
GListModel *model;
|
|
|
|
GtkTreeListModel *treemodel;
|
|
|
|
GtkSingleSelection *selection;
|
2019-12-16 04:48:34 +00:00
|
|
|
GtkBuilderScope *scope;
|
2019-10-13 05:40:58 +00:00
|
|
|
GtkBuilder *builder;
|
2019-12-14 16:53:24 +00:00
|
|
|
GtkColumnViewColumn *name_column;
|
2020-05-31 18:44:42 +00:00
|
|
|
GtkColumnViewColumn *type_column;
|
|
|
|
GtkColumnViewColumn *default_column;
|
2020-06-01 18:46:12 +00:00
|
|
|
GtkColumnViewColumn *summary_column;
|
|
|
|
GtkColumnViewColumn *description_column;
|
2019-12-14 16:53:24 +00:00
|
|
|
GtkSorter *sorter;
|
2020-05-31 18:44:42 +00:00
|
|
|
GActionGroup *actions;
|
|
|
|
GAction *action;
|
2019-10-13 05:40:58 +00:00
|
|
|
|
|
|
|
g_type_ensure (SETTINGS_TYPE_KEY);
|
|
|
|
|
2019-12-16 04:48:34 +00:00
|
|
|
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);
|
2020-06-03 23:33:45 +00:00
|
|
|
gtk_builder_cscope_add_callback_symbol (GTK_BUILDER_CSCOPE (scope), "item_value_changed", (GCallback)item_value_changed);
|
2019-12-16 04:48:34 +00:00
|
|
|
|
|
|
|
builder = gtk_builder_new ();
|
|
|
|
gtk_builder_set_scope (builder, scope);
|
|
|
|
gtk_builder_add_from_resource (builder, "/listview_settings/listview_settings.ui", NULL);
|
|
|
|
|
2019-10-13 05:40:58 +00:00
|
|
|
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"));
|
2020-05-31 18:44:42 +00:00
|
|
|
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"));
|
2020-06-01 18:46:12 +00:00
|
|
|
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"));
|
2020-05-31 18:44:42 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2020-06-01 18:46:12 +00:00
|
|
|
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);
|
|
|
|
|
2020-05-31 18:44:42 +00:00
|
|
|
gtk_widget_insert_action_group (columnview, "columnview", actions);
|
|
|
|
g_object_unref (actions);
|
|
|
|
|
2019-10-13 05:40:58 +00:00
|
|
|
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,
|
2019-12-14 16:53:24 +00:00
|
|
|
columnview, NULL);
|
2019-10-13 05:40:58 +00:00
|
|
|
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);
|
2019-12-14 16:53:24 +00:00
|
|
|
|
|
|
|
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);
|
2020-06-01 19:16:02 +00:00
|
|
|
|
|
|
|
g_object_unref (builder);
|
2019-10-13 05:40:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!gtk_widget_get_visible (window))
|
|
|
|
gtk_widget_show (window);
|
|
|
|
else
|
|
|
|
gtk_window_destroy (GTK_WINDOW (window));
|
|
|
|
|
|
|
|
return window;
|
|
|
|
}
|