From a9eb81a016adaddbd70e1a902f301df7c010ebb3 Mon Sep 17 00:00:00 2001 From: Christian Dywan Date: Sun, 13 Apr 2008 21:21:39 +0200 Subject: [PATCH] Implement property proxy widget creation. The functions katze_property_proxy and katze_propery_label can intelligently create widgets that represent properties of a particular object. This allows for building up a configuration interface with few to no code dealing with specific settings. --- katze/katze-utils.c | 236 +++++++++++++++++++++++++++++++++++++++++++- katze/katze-utils.h | 13 ++- 2 files changed, 246 insertions(+), 3 deletions(-) diff --git a/katze/katze-utils.c b/katze/katze-utils.c index 620a5d01..ee8d41fd 100644 --- a/katze/katze-utils.c +++ b/katze/katze-utils.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2007 Christian Dywan + Copyright (C) 2007-2008 Christian Dywan This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -10,3 +10,237 @@ */ #include "katze-utils.h" + +#include + +static void +proxy_toggle_button_toggled_cb (GtkToggleButton* button, GObject* object) +{ + gboolean toggled = gtk_toggle_button_get_active (button); + const gchar* property = g_object_get_data (G_OBJECT (button), "property"); + g_object_set (object, property, toggled, NULL); +} + +static void +proxy_file_file_set_cb (GtkFileChooser* button, + GObject* object) +{ + const gchar* file = gtk_file_chooser_get_filename (button); + const gchar* property = g_object_get_data (G_OBJECT (button), "property"); + g_object_set (object, property, file, NULL); +} + +static void +proxy_folder_file_set_cb (GtkFileChooser* button, + GObject* object) +{ + const gchar* file = gtk_file_chooser_get_current_folder (button); + const gchar* property = g_object_get_data (G_OBJECT (button), "property"); + g_object_set (object, property, file, NULL); +} + +static void +proxy_uri_file_set_cb (GtkFileChooser* button, + GObject* object) +{ + const gchar* file = gtk_file_chooser_get_uri (button); + const gchar* property = g_object_get_data (G_OBJECT (button), "property"); + g_object_set (object, property, file, NULL); +} + +static gboolean +proxy_entry_focus_out_event_cb (GtkEntry* entry, + GdkEventFocus* event, + GObject* object) +{ + const gchar* text = gtk_entry_get_text (entry); + const gchar* property = g_object_get_data (G_OBJECT (entry), "property"); + g_object_set (object, property, text, NULL); + return FALSE; +} + +static gboolean +proxy_spin_button_changed_cb (GtkSpinButton* button, GObject* object) +{ + gdouble value = gtk_spin_button_get_value (button); + const gchar* property = g_object_get_data (G_OBJECT (button), "property"); + g_object_set (object, property, value, NULL); + return FALSE; +} + +static gchar* +proxy_combo_box_changed_cb (GtkComboBox* button, GObject* object) +{ + gint value = gtk_combo_box_get_active (button); + const gchar* property = g_object_get_data (G_OBJECT (button), "property"); + g_object_set (object, property, value, NULL); + return FALSE; +} + +/** + * katze_property_proxy: + * @object: a #GObject + * @property: the name of a property + * @hint: a special hint + * + * Create a widget of an appropriate type to represent the specified + * object's property. If the property is writable changes of the value + * through the widget will be reflected in the value of the property. + * + * Supported values for @hint are as follows: + * "blurb": the blurb of the property will be used to provide a kind + * of label, instead of the name. + * "file": the widget created will be particularly suitable for + * choosing an existing filename. + * "folder": the widget created will be particularly suitable for + * choosing an existing folder. + * "uri": the widget created will be particularly suitable for + * choosing an existing filename, encoded as an URI. + * + * Any other values for @hint are silently ignored. + * + * Return value: a new widget + **/ +GtkWidget* +katze_property_proxy (gpointer object, + const gchar* property, + const gchar* hint) +{ + g_return_val_if_fail (G_IS_OBJECT (object), NULL); + GObjectClass* class = G_OBJECT_GET_CLASS (object); + GParamSpec* pspec = g_object_class_find_property (class, property); + if (!pspec) + { + g_warning ("Property '%s' is invalid for %s", + property, G_OBJECT_CLASS_NAME (class)); + return gtk_label_new (property); + } + GType type = G_PARAM_SPEC_TYPE (pspec); + const gchar* nick = g_param_spec_get_nick (pspec); + const gchar* _hint = g_intern_string (hint); + if (_hint == g_intern_string ("blurb")) + nick = g_param_spec_get_blurb (pspec); + GtkWidget* widget; + const gchar* string; + if (type == G_TYPE_PARAM_BOOLEAN) + { + widget = gtk_check_button_new_with_label (nick); + gboolean toggled; + g_object_get (object, property, &toggled, NULL); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), toggled); + g_signal_connect (widget, "toggled", + G_CALLBACK (proxy_toggle_button_toggled_cb), object); + } + else if (type == G_TYPE_PARAM_STRING && _hint == g_intern_string ("file")) + { + widget = gtk_file_chooser_button_new (_("Choose file"), + GTK_FILE_CHOOSER_ACTION_OPEN); + g_object_get (object, property, &string, NULL); + if (!string) + string = G_PARAM_SPEC_STRING (pspec)->default_value; + gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (widget), + string ? string : ""); + g_signal_connect (widget, "file-set", + G_CALLBACK (proxy_file_file_set_cb), object); + } + else if (type == G_TYPE_PARAM_STRING && _hint == g_intern_string ("folder")) + { + widget = gtk_file_chooser_button_new (_("Choose folder"), + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER); + g_object_get (object, property, &string, NULL); + if (!string) + string = G_PARAM_SPEC_STRING (pspec)->default_value; + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget), + string ? string : ""); + g_signal_connect (widget, "file-set", + G_CALLBACK (proxy_folder_file_set_cb), object); + } + else if (type == G_TYPE_PARAM_STRING && _hint == g_intern_string ("uri")) + { + widget = gtk_file_chooser_button_new (_("Choose file"), + GTK_FILE_CHOOSER_ACTION_OPEN); + g_object_get (object, property, &string, NULL); + if (!string) + string = G_PARAM_SPEC_STRING (pspec)->default_value; + gtk_file_chooser_set_uri (GTK_FILE_CHOOSER (widget), + string ? string : ""); + g_signal_connect (widget, "file-set", + G_CALLBACK (proxy_uri_file_set_cb), object); + } + else if (type == G_TYPE_PARAM_STRING) + { + widget = gtk_entry_new (); + g_object_get (object, property, &string, NULL); + if (!string) + string = G_PARAM_SPEC_STRING (pspec)->default_value; + gtk_entry_set_text (GTK_ENTRY (widget), string ? string : ""); + g_signal_connect (widget, "focus-out-event", + G_CALLBACK (proxy_entry_focus_out_event_cb), object); + } + else if (type == G_TYPE_PARAM_INT) + { + widget = gtk_spin_button_new_with_range ( + G_PARAM_SPEC_INT (pspec)->minimum, + G_PARAM_SPEC_INT (pspec)->maximum, 1); + gdouble value; + g_object_get (object, property, &value, NULL); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), value); + g_signal_connect (widget, "changed", + G_CALLBACK (proxy_spin_button_changed_cb), object); + } + else if (type == G_TYPE_PARAM_ENUM) + { + GEnumClass* enum_class = G_ENUM_CLASS ( + g_type_class_ref (pspec->value_type)); + widget = gtk_combo_box_new_text (); + gint i = 0; + while (i < enum_class->n_values) + { + const gchar* label = enum_class->values[i].value_nick; + gtk_combo_box_append_text (GTK_COMBO_BOX (widget), label); + i++; + } + gint value; + g_object_get (object, property, &value, NULL); + gtk_combo_box_set_active (GTK_COMBO_BOX (widget), value); + g_signal_connect (widget, "changed", + G_CALLBACK (proxy_combo_box_changed_cb), object); + g_type_class_unref (enum_class); + } + else + widget = gtk_label_new (nick); + + gtk_widget_set_sensitive (widget, pspec->flags & G_PARAM_WRITABLE); + + g_object_set_data (G_OBJECT (widget), "property", (gchar*)property); + + return widget; +} + +/** + * katze_property_label: + * @object: a #GObject + * @property: the name of a property + * + * Create a label widget displaying the name of the specified object's property. + * + * Return value: a new label widget + **/ +GtkWidget* +katze_property_label (gpointer object, + const gchar* property) +{ + g_return_val_if_fail (G_IS_OBJECT (object), NULL); + GObjectClass* class = G_OBJECT_GET_CLASS (object); + GParamSpec* pspec = g_object_class_find_property (class, property); + if (!pspec) + { + g_warning ("Property '%s' is invalid for %s", + property, G_OBJECT_CLASS_NAME (class)); + return gtk_label_new (property); + } + const gchar* nick = g_param_spec_get_nick (pspec); + GtkWidget* widget = gtk_label_new (nick); + + return widget; +} diff --git a/katze/katze-utils.h b/katze/katze-utils.h index 45dda897..c42267d6 100644 --- a/katze/katze-utils.h +++ b/katze/katze-utils.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2007 Christian Dywan + Copyright (C) 2007-2008 Christian Dywan This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -12,7 +12,7 @@ #ifndef __KATZE_UTILS_H__ #define __KATZE_UTILS_H__ -#include +#include G_BEGIN_DECLS @@ -45,6 +45,15 @@ G_BEGIN_DECLS lvalue = rvalue; \ } +GtkWidget* +katze_property_proxy (gpointer object, + const gchar* property, + const gchar* hint); + +GtkWidget* +katze_property_label (gpointer object, + const gchar* property); + G_END_DECLS #endif /* __KATZE_UTILS_H__ */