[rhythmbox] add a composite widget for editing simple GObject properties
- From: Jonathan Matthew <jmatthew src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [rhythmbox] add a composite widget for editing simple GObject properties
- Date: Sun, 22 Apr 2012 23:01:51 +0000 (UTC)
commit e6ce4f93ba67fb0ccbab4758e1fc58bbe1962414
Author: Jonathan Matthew <jonathan d14n org>
Date: Mon Apr 23 08:45:46 2012 +1000
add a composite widget for editing simple GObject properties
This shouldn't be used if it can be avoided, but sometimes
this is all you can do.
widgets/Makefile.am | 6 +-
widgets/rb-object-property-editor.c | 415 +++++++++++++++++++++++++++++++++++
widgets/rb-object-property-editor.h | 67 ++++++
3 files changed, 486 insertions(+), 2 deletions(-)
---
diff --git a/widgets/Makefile.am b/widgets/Makefile.am
index 9f8510d..e46096f 100644
--- a/widgets/Makefile.am
+++ b/widgets/Makefile.am
@@ -14,7 +14,8 @@ widgetinclude_HEADERS = \
rb-song-info.h \
rb-source-toolbar.h \
rb-uri-dialog.h \
- rb-fading-image.h
+ rb-fading-image.h \
+ rb-object-property-editor.h
librbwidgets_la_SOURCES = \
$(widgetinclude_HEADERS) \
@@ -46,7 +47,8 @@ librbwidgets_la_SOURCES = \
eggwrapbox-enums.c \
eggwrapbox-enums.h \
rb-source-toolbar.c \
- rb-fading-image.c
+ rb-fading-image.c \
+ rb-object-property-editor.c
INCLUDES = \
-DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
diff --git a/widgets/rb-object-property-editor.c b/widgets/rb-object-property-editor.c
new file mode 100644
index 0000000..f1844b3
--- /dev/null
+++ b/widgets/rb-object-property-editor.c
@@ -0,0 +1,415 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2012 Jonathan Matthew <jonathan d14n org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * The Rhythmbox authors hereby grant permission for non-GPL compatible
+ * GStreamer plugins to be used and distributed together with GStreamer
+ * and Rhythmbox. This permission is above and beyond the permissions granted
+ * by the GPL license by which Rhythmbox is covered. If you modify this code
+ * you may extend this exception to your version of the code, but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <config.h>
+
+#include <widgets/rb-object-property-editor.h>
+#include <lib/rb-util.h>
+#include <lib/rb-debug.h>
+
+static void rb_object_property_editor_class_init (RBObjectPropertyEditorClass *klass);
+static void rb_object_property_editor_init (RBObjectPropertyEditor *editor);
+
+struct _RBObjectPropertyEditorPrivate
+{
+ GObject *object;
+ char **properties;
+
+ gboolean changed;
+ gulong notify_id;
+};
+
+G_DEFINE_TYPE (RBObjectPropertyEditor, rb_object_property_editor, GTK_TYPE_GRID);
+
+/**
+ * SECTION:rb-object-property-editor
+ * @short_description: builds widgetry for editing simple GObject properties
+ *
+ * RBObjectPropertyEditor can be used to provide an interface to edit
+ * simple (boolean, integer, enum, float) properties of a GObject.
+ */
+
+enum
+{
+ PROP_0,
+ PROP_OBJECT,
+ PROP_PROPERTIES
+};
+
+enum
+{
+ CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+impl_finalize (GObject *object)
+{
+ RBObjectPropertyEditor *editor = RB_OBJECT_PROPERTY_EDITOR (object);
+
+ g_strfreev (editor->priv->properties);
+
+ G_OBJECT_CLASS (rb_object_property_editor_parent_class)->finalize (object);
+}
+
+static void
+impl_dispose (GObject *object)
+{
+ RBObjectPropertyEditor *editor = RB_OBJECT_PROPERTY_EDITOR (object);
+
+ if (editor->priv->object != NULL) {
+ if (editor->priv->notify_id) {
+ g_signal_handler_disconnect (editor->priv->object,
+ editor->priv->notify_id);
+ editor->priv->notify_id = 0;
+ }
+ g_object_unref (editor->priv->object);
+ editor->priv->object = NULL;
+ }
+
+ G_OBJECT_CLASS (rb_object_property_editor_parent_class)->dispose (object);
+}
+
+static void
+notify_cb (GObject *object, GParamSpec *pspec, RBObjectPropertyEditor *editor)
+{
+ editor->priv->changed = TRUE;
+}
+
+static void
+focus_out_cb (GtkWidget *widget, GdkEvent *event, RBObjectPropertyEditor *editor)
+{
+ if (editor->priv->changed) {
+ rb_debug ("emitting changed");
+ g_signal_emit (editor, signals[CHANGED], 0);
+ editor->priv->changed = FALSE;
+ } else {
+ rb_debug ("not emitting changed");
+ }
+}
+
+static GtkWidget *
+create_boolean_editor (RBObjectPropertyEditor *editor, const char *property, GParamSpec *pspec)
+{
+ GtkWidget *control;
+
+ control = gtk_check_button_new ();
+
+ g_object_bind_property (editor->priv->object, property,
+ control, "active",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+ return control;
+}
+
+static GtkWidget *
+create_enum_editor (RBObjectPropertyEditor *editor, const char *property, GParamSpec *pspec)
+{
+ GParamSpecEnum *penum;
+ GtkListStore *model;
+ GtkCellRenderer *renderer;
+ GtkWidget *control;
+ int p;
+
+ control = gtk_combo_box_new ();
+ penum = G_PARAM_SPEC_ENUM (pspec);
+ renderer = gtk_cell_renderer_text_new ();
+
+ model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
+ gtk_combo_box_set_model (GTK_COMBO_BOX (control), GTK_TREE_MODEL (model));
+ for (p = 0; p < penum->enum_class->n_values; p++) {
+ gtk_list_store_insert_with_values (model, NULL, p,
+ 0, penum->enum_class->values[p].value_name,
+ 1, p,
+ -1);
+ }
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (control), renderer, TRUE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (control), renderer, "text", 0, NULL);
+
+ g_object_bind_property (editor->priv->object, property,
+ control, "active",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+ return control;
+}
+
+static GtkWidget *
+create_int_editor (RBObjectPropertyEditor *editor, const char *property, GParamSpec *pspec)
+{
+ GParamSpecInt *pint;
+ GtkWidget *control;
+ GtkAdjustment *adjustment;
+
+ pint = G_PARAM_SPEC_INT (pspec);
+
+ adjustment = gtk_adjustment_new (pint->default_value,
+ pint->minimum,
+ pint->maximum + 1,
+ 1.0,
+ 1.0, 1.0);
+
+ control = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, adjustment);
+ gtk_scale_set_digits (GTK_SCALE (control), 0);
+
+ g_object_bind_property (editor->priv->object, property,
+ adjustment, "value",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+ return control;
+}
+
+static GtkWidget *
+create_float_editor (RBObjectPropertyEditor *editor, const char *property, GParamSpec *pspec)
+{
+ GParamSpecFloat *pfloat;
+ GtkWidget *control;
+ GtkAdjustment *adjustment;
+
+ pfloat = G_PARAM_SPEC_FLOAT (pspec);
+
+ adjustment = gtk_adjustment_new (pfloat->default_value,
+ pfloat->minimum,
+ pfloat->maximum + pfloat->epsilon*2,
+ pfloat->epsilon*10,
+ 0.1, 0.1);
+
+ control = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, adjustment);
+
+ g_object_bind_property (editor->priv->object, property,
+ adjustment, "value",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+ return control;
+}
+
+static GtkWidget *
+create_double_editor (RBObjectPropertyEditor *editor, const char *property, GParamSpec *pspec)
+{
+ GParamSpecDouble *pdouble;
+ GtkWidget *control;
+ GtkAdjustment *adjustment;
+
+ pdouble = G_PARAM_SPEC_DOUBLE (pspec);
+
+ adjustment = gtk_adjustment_new (pdouble->default_value,
+ pdouble->minimum,
+ pdouble->maximum + pdouble->epsilon*2,
+ pdouble->epsilon*10,
+ 0.1, 0.1);
+
+ control = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, adjustment);
+
+ g_object_bind_property (editor->priv->object, property,
+ adjustment, "value",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+ return control;
+}
+
+static void
+impl_constructed (GObject *object)
+{
+ RBObjectPropertyEditor *editor;
+ GObjectClass *klass;
+ int i;
+ int row;
+
+ RB_CHAIN_GOBJECT_METHOD (rb_object_property_editor_parent_class, constructed, object);
+
+ editor = RB_OBJECT_PROPERTY_EDITOR (object);
+ klass = G_OBJECT_GET_CLASS (editor->priv->object);
+
+ editor->priv->notify_id = g_signal_connect (editor->priv->object, "notify", G_CALLBACK (notify_cb), editor);
+
+ row = 0;
+ for (i = 0; editor->priv->properties[i] != NULL; i++) {
+ GParamSpec *pspec;
+ GtkWidget *label;
+ GtkWidget *control;
+ GType prop_type;
+
+ pspec = g_object_class_find_property (klass, editor->priv->properties[i]);
+ if (pspec == NULL) {
+ g_warning ("couldn't find property %s on object %s",
+ editor->priv->properties[i],
+ G_OBJECT_CLASS_NAME (klass));
+ continue;
+ }
+
+ prop_type = G_PARAM_SPEC_TYPE (pspec);
+ if (prop_type == G_TYPE_PARAM_BOOLEAN) {
+ control = create_boolean_editor (editor, editor->priv->properties[i], pspec);
+ } else if (prop_type == G_TYPE_PARAM_ENUM) {
+ control = create_enum_editor (editor, editor->priv->properties[i], pspec);
+ } else if (prop_type == G_TYPE_PARAM_INT) {
+ control = create_int_editor (editor, editor->priv->properties[i], pspec);
+ } else if (prop_type == G_TYPE_PARAM_FLOAT) {
+ control = create_float_editor (editor, editor->priv->properties[i], pspec);
+ } else if (prop_type == G_TYPE_PARAM_DOUBLE) {
+ control = create_double_editor (editor, editor->priv->properties[i], pspec);
+ } else {
+ /* can't do this */
+ g_warning ("don't know how to edit %s", g_type_name (prop_type));
+ continue;
+ }
+ g_signal_connect (control, "focus-out-event", G_CALLBACK (focus_out_cb), editor);
+ gtk_widget_set_hexpand (control, TRUE);
+
+ label = gtk_label_new (g_param_spec_get_nick (pspec));
+ gtk_widget_set_tooltip_text (label, g_param_spec_get_blurb (pspec));
+
+ gtk_grid_attach (GTK_GRID (editor),
+ label,
+ 0, row, 1, 1);
+ gtk_grid_attach (GTK_GRID (editor),
+ control,
+ 1, row, 1, 1);
+
+ row++;
+ }
+}
+
+static void
+impl_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+ RBObjectPropertyEditor *editor = RB_OBJECT_PROPERTY_EDITOR (object);
+ switch (prop_id) {
+ case PROP_OBJECT:
+ editor->priv->object = g_value_dup_object (value);
+ break;
+ case PROP_PROPERTIES:
+ editor->priv->properties = g_value_dup_boxed (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ }
+}
+
+static void
+impl_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+ RBObjectPropertyEditor *editor = RB_OBJECT_PROPERTY_EDITOR (object);
+ switch (prop_id) {
+ case PROP_OBJECT:
+ g_value_set_object (value, editor->priv->object);
+ break;
+ case PROP_PROPERTIES:
+ g_value_set_boxed (value, editor->priv->properties);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ }
+}
+
+static void
+rb_object_property_editor_init (RBObjectPropertyEditor *editor)
+{
+ editor->priv = G_TYPE_INSTANCE_GET_PRIVATE (editor,
+ RB_TYPE_OBJECT_PROPERTY_EDITOR,
+ RBObjectPropertyEditorPrivate);
+}
+
+
+static void
+rb_object_property_editor_class_init (RBObjectPropertyEditorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = impl_constructed;
+ object_class->dispose = impl_dispose;
+ object_class->finalize = impl_finalize;
+ object_class->set_property = impl_set_property;
+ object_class->get_property = impl_get_property;
+
+ /**
+ * RBObjectPropertyEditor::changed:
+ * @editor: the #RBObjectPropertyEditor
+ *
+ * Emitted when a property has been changed.
+ * This won't be emitted on every single change (use 'notify' on the
+ * object being edited for that), but rather when the edit widget
+ * for a property loses focus and the value was changed.
+ */
+ signals[CHANGED] =
+ g_signal_new ("changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (RBObjectPropertyEditorClass, changed),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE,
+ 0);
+
+ /**
+ * RBObjectPropertyEditor:object
+ *
+ * The object to edit.
+ */
+ g_object_class_install_property (object_class,
+ PROP_OBJECT,
+ g_param_spec_object ("object",
+ "object",
+ "object to edit",
+ G_TYPE_OBJECT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * RBObjectPropertyEditor:properties
+ *
+ * Array of names of properties to edit.
+ */
+ g_object_class_install_property (object_class,
+ PROP_PROPERTIES,
+ g_param_spec_boxed ("properties",
+ "properties",
+ "properties to edit",
+ G_TYPE_STRV,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_type_class_add_private (klass, sizeof (RBObjectPropertyEditorPrivate));
+}
+
+/**
+ * rb_object_property_editor_new:
+ * @object: the object to edit
+ * @properties: array of names of properties to edit
+ *
+ * Creates a property editor for the specified properties of an object.
+ *
+ * Return value: property editor widget.
+ */
+GtkWidget *
+rb_object_property_editor_new (GObject *object, char **properties)
+{
+ return GTK_WIDGET (g_object_new (RB_TYPE_OBJECT_PROPERTY_EDITOR,
+ "object", object,
+ "properties", properties,
+ NULL));
+}
diff --git a/widgets/rb-object-property-editor.h b/widgets/rb-object-property-editor.h
new file mode 100644
index 0000000..8f019b5
--- /dev/null
+++ b/widgets/rb-object-property-editor.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2012 Jonathan Matthew <jonathan d14n org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * The Rhythmbox authors hereby grant permission for non-GPL compatible
+ * GStreamer plugins to be used and distributed together with GStreamer
+ * and Rhythmbox. This permission is above and beyond the permissions granted
+ * by the GPL license by which Rhythmbox is covered. If you modify this code
+ * you may extend this exception to your version of the code, but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <gtk/gtk.h>
+
+#ifndef RB_OBJECT_PROPERTY_EDITOR_H
+#define RB_OBJECT_PROPERTY_EDITOR_H
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_OBJECT_PROPERTY_EDITOR (rb_object_property_editor_get_type ())
+#define RB_OBJECT_PROPERTY_EDITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_OBJECT_PROPERTY_EDITOR, RBObjectPropertyEditor))
+#define RB_OBJECT_PROPERTY_EDITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_OBJECT_PROPERTY_EDITOR, RBObjectPropertyEditorClass))
+#define RB_IS_OBJECT_PROPERTY_EDITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_OBJECT_PROPERTY_EDITOR))
+#define RB_IS_OBJECT_PROPERTY_EDITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_OBJECT_PROPERTY_EDITOR))
+#define RB_OBJECT_PROPERTY_EDITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_OBJECT_PROPERTY_EDITOR, RBObjectPropertyEditorClass))
+
+typedef struct _RBObjectPropertyEditor RBObjectPropertyEditor;
+typedef struct _RBObjectPropertyEditorClass RBObjectPropertyEditorClass;
+typedef struct _RBObjectPropertyEditorPrivate RBObjectPropertyEditorPrivate;
+
+struct _RBObjectPropertyEditor
+{
+ GtkGrid parent;
+
+ RBObjectPropertyEditorPrivate *priv;
+};
+
+struct _RBObjectPropertyEditorClass
+{
+ GtkGridClass parent_class;
+
+ void (*changed) (RBObjectPropertyEditor *editor);
+};
+
+GType rb_object_property_editor_get_type (void);
+
+GtkWidget * rb_object_property_editor_new (GObject *object, char **properties);
+
+
+G_END_DECLS
+
+#endif /* RB_OBJECT_PROPERTY_EDITOR_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]