forked from AuroraMiddleware/gtk
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;
|
||
}
|