forked from AuroraMiddleware/gtk
d732c869c2
Add the names of the main widgets as keywords to our demos, but also things like "game". This helps finding relevant demos in our growing list. You can now for example type "label", and find the "error states" and "links" demos showing GtkLabel features.
435 lines
13 KiB
C
435 lines
13 KiB
C
/* Lists/Settings
|
||
* #Keywords: GtkListItemFactory, GListModel
|
||
*
|
||
* 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_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_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;
|
||
}
|