[gimp/symmetry: 6/6] Bug 648776 - fixes symmetry painting after Massimo and Mitch's reviews.
- From: Jehan Pagès <jehanp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/symmetry: 6/6] Bug 648776 - fixes symmetry painting after Massimo and Mitch's reviews.
- Date: Thu, 21 Jan 2016 20:59:42 +0000 (UTC)
commit 6fee94f2447cedc38fa14ef1ef34e7b70e506186
Author: Jehan <jehan girinstud io>
Date: Thu Jan 21 21:43:15 2016 +0100
Bug 648776 - fixes symmetry painting after Massimo and Mitch's reviews.
Use a GType for the PROP_SYMMETRY property of GimpImage, and create
a default "identity" symmetry for an image.
Thanks Massimo and Mitch for reviewing my code.
app/core/gimpimage-symmetry.c | 18 +-
app/core/gimpimage.c | 56 ++--
app/widgets/gimpsymmetryeditor.c | 18 +-
app/xcf/xcf-save.c | 5 +
libgimpwidgets/Makefile.am | 6 +
libgimpwidgets/gimpgtypecombobox.c | 729 ++++++++++++++++++++++++++++++++++++
libgimpwidgets/gimpgtypecombobox.h | 103 +++++
libgimpwidgets/gimpgtypestore.c | 311 +++++++++++++++
libgimpwidgets/gimpgtypestore.h | 100 +++++
libgimpwidgets/gimppropwidgets.c | 104 +++++
libgimpwidgets/gimppropwidgets.h | 6 +
libgimpwidgets/gimpwidgets.h | 2 +
libgimpwidgets/gimpwidgetstypes.h | 2 +
13 files changed, 1410 insertions(+), 50 deletions(-)
---
diff --git a/app/core/gimpimage-symmetry.c b/app/core/gimpimage-symmetry.c
index 1fc300f..b6c6dff 100644
--- a/app/core/gimpimage-symmetry.c
+++ b/app/core/gimpimage-symmetry.c
@@ -55,6 +55,9 @@ gimp_image_symmetry_list (void)
* @type: the #GType of the symmetry
*
* Creates a new #GimpSymmetry of @type attached to @image.
+ * @type must be a subtype of `GIMP_TYPE_SYMMETRY`.
+ * Note that using the base @type `GIMP_TYPE_SYMMETRY` creates an
+ * identity transformation.
*
* Returns: the new #GimpSymmetry.
**/
@@ -63,6 +66,7 @@ gimp_image_symmetry_new (GimpImage *image,
GType type)
{
GimpSymmetry *sym = NULL;
+ g_return_val_if_fail (g_type_is_a (type, GIMP_TYPE_SYMMETRY), NULL);
if (type != G_TYPE_NONE)
{
@@ -176,21 +180,11 @@ gimp_image_symmetry_select (GimpImage *image,
GimpSymmetry *
gimp_image_symmetry_selected (GimpImage *image)
{
- static GimpImage *last_image = NULL;
- static GimpSymmetry *identity = NULL;
- GimpImagePrivate *private;
+ GimpImagePrivate *private;
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
- if (last_image != image)
- {
- if (identity)
- g_object_unref (identity);
- identity = gimp_image_symmetry_new (image,
- GIMP_TYPE_SYMMETRY);
- }
-
private = GIMP_IMAGE_GET_PRIVATE (image);
- return private->selected_symmetry ? private->selected_symmetry : identity;
+ return private->selected_symmetry;
}
diff --git a/app/core/gimpimage.c b/app/core/gimpimage.c
index 5900715..6e762ec 100644
--- a/app/core/gimpimage.c
+++ b/app/core/gimpimage.c
@@ -628,10 +628,11 @@ gimp_image_class_init (GimpImageClass *klass)
g_object_class_override_property (object_class, PROP_BUFFER, "buffer");
g_object_class_install_property (object_class, PROP_SYMMETRY,
- g_param_spec_int ("symmetry",
- NULL, _("Symmetry"),
- G_TYPE_NONE, G_MAXINT, G_TYPE_NONE,
- GIMP_PARAM_READWRITE));
+ g_param_spec_gtype ("symmetry",
+ NULL, _("Symmetry"),
+ GIMP_TYPE_SYMMETRY,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
g_type_class_add_private (klass, sizeof (GimpImagePrivate));
}
@@ -873,7 +874,8 @@ gimp_image_set_property (GObject *object,
break;
case PROP_SYMMETRY:
{
- GType type = g_value_get_int (value);
+ GList *iter;
+ GType type = g_value_get_gtype (value);
if (private->selected_symmetry)
g_object_set (private->selected_symmetry,
@@ -881,29 +883,25 @@ gimp_image_set_property (GObject *object,
NULL);
private->selected_symmetry = NULL;
- if (type != G_TYPE_NONE)
+
+ for (iter = private->symmetries; iter; iter = g_list_next (iter))
{
- GList *iter;
-
- for (iter = private->symmetries; iter; iter = g_list_next (iter))
- {
- GimpSymmetry *sym = iter->data;
- if (g_type_is_a (sym->type, type))
- private->selected_symmetry = iter->data;
- }
-
- if (private->selected_symmetry == NULL)
- {
- GimpSymmetry *sym;
-
- sym = gimp_image_symmetry_new (image, type);
- gimp_image_symmetry_add (image, sym);
- private->selected_symmetry = sym;
- }
- g_object_set (private->selected_symmetry,
- "active", TRUE,
- NULL);
+ GimpSymmetry *sym = iter->data;
+ if (type == sym->type)
+ private->selected_symmetry = iter->data;
+ }
+
+ if (private->selected_symmetry == NULL)
+ {
+ GimpSymmetry *sym;
+
+ sym = gimp_image_symmetry_new (image, type);
+ gimp_image_symmetry_add (image, sym);
+ private->selected_symmetry = sym;
}
+ g_object_set (private->selected_symmetry,
+ "active", TRUE,
+ NULL);
}
break;
default:
@@ -948,9 +946,9 @@ gimp_image_get_property (GObject *object,
g_value_set_object (value, gimp_image_get_buffer (GIMP_PICKABLE (image)));
break;
case PROP_SYMMETRY:
- g_value_set_int (value,
- private->selected_symmetry ?
- private->selected_symmetry->type : G_TYPE_NONE);
+ g_value_set_gtype (value,
+ private->selected_symmetry ?
+ private->selected_symmetry->type : GIMP_TYPE_SYMMETRY);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
diff --git a/app/widgets/gimpsymmetryeditor.c b/app/widgets/gimpsymmetryeditor.c
index eb5169d..8effb69 100644
--- a/app/widgets/gimpsymmetryeditor.c
+++ b/app/widgets/gimpsymmetryeditor.c
@@ -258,7 +258,7 @@ gimp_symmetry_editor_image_changed (GimpContext *context,
GList *sym_iter;
GimpSymmetry *symmetry;
- store = gimp_int_store_new ();
+ store = gimp_gtype_store_new ();
/* The menu of available symmetries. */
syms = gimp_image_symmetry_list ();
@@ -272,9 +272,9 @@ gimp_symmetry_editor_image_changed (GimpContext *context,
gtk_list_store_prepend (store, &iter);
gtk_list_store_set (store, &iter,
- GIMP_INT_STORE_LABEL,
+ GIMP_GTYPE_STORE_LABEL,
klass->label,
- GIMP_INT_STORE_VALUE,
+ GIMP_GTYPE_STORE_VALUE,
sym_iter->data,
-1);
g_type_class_unref (klass);
@@ -283,15 +283,15 @@ gimp_symmetry_editor_image_changed (GimpContext *context,
gtk_list_store_prepend (store, &iter);
gtk_list_store_set (store, &iter,
- GIMP_INT_STORE_LABEL, _("None"),
- GIMP_INT_STORE_VALUE, G_TYPE_NONE,
+ GIMP_GTYPE_STORE_LABEL, _("None"),
+ GIMP_GTYPE_STORE_VALUE, GIMP_TYPE_SYMMETRY,
-1);
- editor->p->menu = gimp_prop_int_combo_box_new (G_OBJECT (image),
- "symmetry",
- GIMP_INT_STORE (store));
+ editor->p->menu = gimp_prop_gtype_combo_box_new (G_OBJECT (image),
+ "symmetry",
+ GIMP_GTYPE_STORE (store));
g_object_unref (store);
- gimp_int_combo_box_set_label (GIMP_INT_COMBO_BOX (editor->p->menu),
+ gimp_gtype_combo_box_set_label (GIMP_GTYPE_COMBO_BOX (editor->p->menu),
_("Symmetry"));
g_object_set (editor->p->menu, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
diff --git a/app/xcf/xcf-save.c b/app/xcf/xcf-save.c
index 0e8e0ae..5d69a8f 100644
--- a/app/xcf/xcf-save.c
+++ b/app/xcf/xcf-save.c
@@ -435,9 +435,14 @@ xcf_save_image_props (XcfInfo *info,
if (g_list_length (gimp_image_symmetry_get (image)))
{
GimpParasite *parasite = NULL;
+ GimpSymmetry *symmetry;
for (iter = gimp_image_symmetry_get (image); iter; iter = g_list_next (iter))
{
+ symmetry = GIMP_SYMMETRY (iter->data);
+ if (symmetry->type == GIMP_TYPE_SYMMETRY)
+ /* Do not save the identity symmetry. */
+ continue;
parasite = gimp_symmetry_to_parasite (GIMP_SYMMETRY (iter->data));
gimp_parasite_list_add (private->parasites, parasite);
symmetry_parasites = g_list_prepend (symmetry_parasites, parasite);
diff --git a/libgimpwidgets/Makefile.am b/libgimpwidgets/Makefile.am
index f15900f..bf57084 100644
--- a/libgimpwidgets/Makefile.am
+++ b/libgimpwidgets/Makefile.am
@@ -122,6 +122,10 @@ libgimpwidgets_sources = \
gimpfileentry.h \
gimpframe.c \
gimpframe.h \
+ gimpgtypecombobox.c \
+ gimpgtypecombobox.h \
+ gimpgtypestore.c \
+ gimpgtypestore.h \
gimphelpui.c \
gimphelpui.h \
gimphintbox.c \
@@ -228,6 +232,8 @@ libgimpwidgetsinclude_HEADERS = \
gimpenumwidgets.h \
gimpfileentry.h \
gimpframe.h \
+ gimpgtypecombobox.h \
+ gimpgtypestore.h \
gimphelpui.h \
gimphintbox.h \
gimpicons.h \
diff --git a/libgimpwidgets/gimpgtypecombobox.c b/libgimpwidgets/gimpgtypecombobox.c
new file mode 100644
index 0000000..63cdffa
--- /dev/null
+++ b/libgimpwidgets/gimpgtypecombobox.c
@@ -0,0 +1,729 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpgtypecombobox.c
+ * Copyright (C) 2004 Sven Neumann <sven gimp org>
+ * Copyright (C) 2015 Jehan <jehan girinstud io>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libintl.h>
+
+#include <gtk/gtk.h>
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpgtypecombobox.h"
+#include "gimpgtypestore.h"
+
+
+/**
+ * SECTION: gimpgtypecombobox
+ * @title: GimpGTypeComboBox
+ * @short_description: A widget providing a popup menu of GType
+ * values.
+ *
+ * A widget providing a popup menu of GType values.
+ **/
+
+
+enum
+{
+ PROP_0,
+ PROP_ELLIPSIZE,
+ PROP_LABEL
+};
+
+
+typedef struct
+{
+ GtkCellRenderer *pixbuf_renderer;
+ GtkCellRenderer *text_renderer;
+
+ PangoEllipsizeMode ellipsize;
+ gchar *label;
+ GtkCellRenderer *label_renderer;
+
+ GimpGTypeSensitivityFunc sensitivity_func;
+ gpointer sensitivity_data;
+ GDestroyNotify sensitivity_destroy;
+} GimpGTypeComboBoxPrivate;
+
+#define GIMP_GTYPE_COMBO_BOX_GET_PRIVATE(obj) \
+ ((GimpGTypeComboBoxPrivate *) ((GimpGTypeComboBox *) (obj))->priv)
+
+
+static void gimp_gtype_combo_box_finalize (GObject *object);
+static void gimp_gtype_combo_box_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_gtype_combo_box_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_gtype_combo_box_create_cells (GimpGTypeComboBox *combo_box);
+static void gimp_gtype_combo_box_data_func (GtkCellLayout *layout,
+ GtkCellRenderer *cell,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data);
+
+
+G_DEFINE_TYPE (GimpGTypeComboBox, gimp_gtype_combo_box, GTK_TYPE_COMBO_BOX)
+
+#define parent_class gimp_gtype_combo_box_parent_class
+
+static void
+gimp_gtype_combo_box_class_init (GimpGTypeComboBoxClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gimp_gtype_combo_box_finalize;
+ object_class->set_property = gimp_gtype_combo_box_set_property;
+ object_class->get_property = gimp_gtype_combo_box_get_property;
+
+ /**
+ * GimpGTypeComboBox:ellipsize:
+ *
+ * Specifies the preferred place to ellipsize text in the combo-box,
+ * if the cell renderer does not have enough room to display the
+ * entire string.
+ *
+ * Since: 2.10
+ */
+ g_object_class_install_property (object_class, PROP_ELLIPSIZE,
+ g_param_spec_enum ("ellipsize", NULL, NULL,
+ PANGO_TYPE_ELLIPSIZE_MODE,
+ PANGO_ELLIPSIZE_NONE,
+ GIMP_PARAM_READWRITE));
+
+ /**
+ * GimpGTypeComboBox:label:
+ *
+ * Sets a label on the combo-box, see gimp_gtype_combo_box_set_label().
+ *
+ * Since: 2.10
+ */
+ g_object_class_install_property (object_class, PROP_LABEL,
+ g_param_spec_string ("label", NULL, NULL,
+ NULL,
+ GIMP_PARAM_READWRITE));
+
+ g_type_class_add_private (object_class, sizeof (GimpGTypeComboBoxPrivate));
+}
+
+static void
+gimp_gtype_combo_box_init (GimpGTypeComboBox *combo_box)
+{
+ GtkListStore *store;
+
+ combo_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (combo_box,
+ GIMP_TYPE_GTYPE_COMBO_BOX,
+ GimpGTypeComboBoxPrivate);
+
+ store = gimp_gtype_store_new ();
+ gtk_combo_box_set_model (GTK_COMBO_BOX (combo_box), GTK_TREE_MODEL (store));
+ g_object_unref (store);
+
+ gimp_gtype_combo_box_create_cells (GIMP_GTYPE_COMBO_BOX (combo_box));
+}
+
+static void
+gimp_gtype_combo_box_finalize (GObject *object)
+{
+ GimpGTypeComboBoxPrivate *priv = GIMP_GTYPE_COMBO_BOX_GET_PRIVATE (object);
+
+ if (priv->label)
+ {
+ g_free (priv->label);
+ priv->label = NULL;
+ }
+
+ if (priv->sensitivity_destroy)
+ {
+ GDestroyNotify d = priv->sensitivity_destroy;
+
+ priv->sensitivity_destroy = NULL;
+ d (priv->sensitivity_data);
+ }
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_gtype_combo_box_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpGTypeComboBoxPrivate *priv = GIMP_GTYPE_COMBO_BOX_GET_PRIVATE (object);
+
+ switch (property_id)
+ {
+ case PROP_ELLIPSIZE:
+ priv->ellipsize = g_value_get_enum (value);
+ g_object_set_property (G_OBJECT (priv->text_renderer),
+ pspec->name, value);
+ break;
+ case PROP_LABEL:
+ gimp_gtype_combo_box_set_label (GIMP_GTYPE_COMBO_BOX (object),
+ g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_gtype_combo_box_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpGTypeComboBoxPrivate *priv = GIMP_GTYPE_COMBO_BOX_GET_PRIVATE (object);
+
+ switch (property_id)
+ {
+ case PROP_ELLIPSIZE:
+ g_value_set_enum (value, priv->ellipsize);
+ break;
+ case PROP_LABEL:
+ g_value_set_string (value, priv->label);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+
+/**
+ * gimp_gtype_combo_box_new:
+ * @first_label: the label of the first item
+ * @first_value: the value of the first item
+ * @...: a %NULL terminated list of more label, value pairs
+ *
+ * Creates a GtkComboBox that has GType values associated with each
+ * item. The items to fill the combo box with are specified as a %NULL
+ * terminated list of label/value pairs.
+ *
+ * If you need to construct an empty #GimpGTypeComboBox, it's best to use
+ * g_object_new (GIMP_TYPE_GTYPE_COMBO_BOX, NULL).
+ *
+ * Return value: a new #GimpGTypeComboBox.
+ *
+ * Since: 2.10
+ **/
+GtkWidget *
+gimp_gtype_combo_box_new (const gchar *first_label,
+ GType first_value,
+ ...)
+{
+ GtkWidget *combo_box;
+ va_list args;
+
+ va_start (args, first_value);
+
+ combo_box = gimp_gtype_combo_box_new_valist (first_label, first_value, args);
+
+ va_end (args);
+
+ return combo_box;
+}
+
+/**
+ * gimp_gtype_combo_box_new_valist:
+ * @first_label: the label of the first item
+ * @first_value: the value of the first item
+ * @values: a va_list with more values
+ *
+ * A variant of gimp_gtype_combo_box_new() that takes a va_list of
+ * label/value pairs. Probably only useful for language bindings.
+ *
+ * Return value: a new #GimpGTypeComboBox.
+ *
+ * Since: 2.10
+ **/
+GtkWidget *
+gimp_gtype_combo_box_new_valist (const gchar *first_label,
+ GType first_value,
+ va_list values)
+{
+ GtkWidget *combo_box;
+ GtkListStore *store;
+ const gchar *label;
+ GType value;
+
+ combo_box = g_object_new (GIMP_TYPE_GTYPE_COMBO_BOX, NULL);
+
+ store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)));
+
+ for (label = first_label, value = first_value;
+ label;
+ label = va_arg (values, const gchar *), value = va_arg (values, GType))
+ {
+ GtkTreeIter iter = { 0, };
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ GIMP_GTYPE_STORE_VALUE, value,
+ GIMP_GTYPE_STORE_LABEL, label,
+ -1);
+ }
+
+ return combo_box;
+}
+
+/**
+ * gimp_gtype_combo_box_prepend:
+ * @combo_box: a #GimpGTypeComboBox
+ * @...: pairs of column number and value, terminated with -1
+ *
+ * This function provides a convenient way to prepend items to a
+ * #GimpGTypeComboBox. It prepends a row to the @combo_box's list store
+ * and calls gtk_list_store_set() for you.
+ *
+ * The column number must be taken from the enum #GimpGTypeStoreColumns.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_gtype_combo_box_prepend (GimpGTypeComboBox *combo_box,
+ ...)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+ va_list args;
+
+ g_return_if_fail (GIMP_IS_GTYPE_COMBO_BOX (combo_box));
+
+ store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)));
+
+ va_start (args, combo_box);
+
+ gtk_list_store_prepend (store, &iter);
+ gtk_list_store_set_valist (store, &iter, args);
+
+ va_end (args);
+}
+
+/**
+ * gimp_gtype_combo_box_append:
+ * @combo_box: a #GimpGTypeComboBox
+ * @...: pairs of column number and value, terminated with -1
+ *
+ * This function provides a convenient way to append items to a
+ * #GimpGTypeComboBox. It appends a row to the @combo_box's list store
+ * and calls gtk_list_store_set() for you.
+ *
+ * The column number must be taken from the enum #GimpGTypeStoreColumns.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_gtype_combo_box_append (GimpGTypeComboBox *combo_box,
+ ...)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+ va_list args;
+
+ g_return_if_fail (GIMP_IS_GTYPE_COMBO_BOX (combo_box));
+
+ store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)));
+
+ va_start (args, combo_box);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set_valist (store, &iter, args);
+
+ va_end (args);
+}
+
+/**
+ * gimp_gtype_combo_box_set_active:
+ * @combo_box: a #GimpGTypeComboBox
+ * @value: a GType value
+ *
+ * Looks up the item that belongs to the given @value and makes it the
+ * selected item in the @combo_box.
+ *
+ * Return value: %TRUE on success or %FALSE if there was no item for
+ * this value.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_gtype_combo_box_set_active (GimpGTypeComboBox *combo_box,
+ GType value)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (GIMP_IS_GTYPE_COMBO_BOX (combo_box), FALSE);
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
+
+ if (gimp_gtype_store_lookup_by_value (model, value, &iter))
+ {
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gimp_gtype_combo_box_get_active:
+ * @combo_box: a #GimpGTypeComboBox
+ * @value: return location for the GType value
+ *
+ * Retrieves the value of the selected (active) item in the @combo_box.
+ *
+ * Return value: %TRUE if @value has been set or %FALSE if no item was
+ * active.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_gtype_combo_box_get_active (GimpGTypeComboBox *combo_box,
+ GType *value)
+{
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (GIMP_IS_GTYPE_COMBO_BOX (combo_box), FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter))
+ {
+ gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)),
+ &iter,
+ GIMP_GTYPE_STORE_VALUE, value,
+ -1);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gimp_gtype_combo_box_connect:
+ * @combo_box: a #GimpGTypeComboBox
+ * @value: the value to set
+ * @callback: a callback to connect to the @combo_box's "changed" signal
+ * @data: a pointer passed as data to g_signal_connect()
+ *
+ * A convenience function that sets the initial @value of a
+ * #GimpGTypeComboBox and connects @callback to the "changed"
+ * signal.
+ *
+ * This function also calls the @callback once after setting the
+ * initial @value. This is often convenient when working with combo
+ * boxes that select a default active item, like for example
+ * gimp_drawable_combo_box_new(). If you pass an invalid initial
+ * @value, the @callback will be called with the default item active.
+ *
+ * Return value: the signal handler ID as returned by g_signal_connect()
+ *
+ * Since: 2.10
+ **/
+gulong
+gimp_gtype_combo_box_connect (GimpGTypeComboBox *combo_box,
+ GType value,
+ GCallback callback,
+ gpointer data)
+{
+ gulong handler = 0;
+
+ g_return_val_if_fail (GIMP_IS_GTYPE_COMBO_BOX (combo_box), 0);
+
+ if (callback)
+ handler = g_signal_connect (combo_box, "changed", callback, data);
+
+ if (! gimp_gtype_combo_box_set_active (combo_box, value))
+ g_signal_emit_by_name (combo_box, "changed", NULL);
+
+ return handler;
+}
+
+/**
+ * gimp_gtype_combo_box_set_label:
+ * @combo_box: a #GimpGTypeComboBox
+ * @label: a string to be shown as label
+ *
+ * Sets a caption on the @combo_box that will be displayed
+ * left-aligned inside the box. When a label is set, the remaining
+ * contents of the box will be right-aligned. This is useful for
+ * places where screen estate is rare, like in tool options.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_gtype_combo_box_set_label (GimpGTypeComboBox *combo_box,
+ const gchar *label)
+{
+ GimpGTypeComboBoxPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_GTYPE_COMBO_BOX (combo_box));
+
+ priv = GIMP_GTYPE_COMBO_BOX_GET_PRIVATE (combo_box);
+
+ if (label == priv->label)
+ return;
+
+ if (priv->label)
+ {
+ g_free (priv->label);
+ priv->label = NULL;
+
+ g_signal_handlers_disconnect_by_func (combo_box,
+ gimp_gtype_combo_box_create_cells,
+ NULL);
+ }
+
+ if (label)
+ {
+ priv->label = g_strdup (label);
+
+ g_signal_connect (combo_box, "notify::popup-shown",
+ G_CALLBACK (gimp_gtype_combo_box_create_cells),
+ NULL);
+ }
+
+ gimp_gtype_combo_box_create_cells (combo_box);
+
+ g_object_notify (G_OBJECT (combo_box), "label");
+}
+
+/**
+ * gimp_gtype_combo_box_get_label:
+ * @combo_box: a #GimpGTypeComboBox
+ *
+ * Returns the label previously set with gimp_gtype_combo_box_set_label(),
+ * or %NULL,
+ *
+ * Return value: the @combo_box' label.
+ *
+ * Since: 2.10
+ **/
+const gchar *
+gimp_gtype_combo_box_get_label (GimpGTypeComboBox *combo_box)
+{
+ g_return_val_if_fail (GIMP_IS_GTYPE_COMBO_BOX (combo_box), NULL);
+
+ return GIMP_GTYPE_COMBO_BOX_GET_PRIVATE (combo_box)->label;
+}
+
+/**
+ * gimp_gtype_combo_box_set_sensitivity:
+ * @combo_box: a #GimpGTypeComboBox
+ * @func: a function that returns a boolean value, or %NULL to unset
+ * @data: data to pass to @func
+ * @destroy: destroy notification for @data
+ *
+ * Sets a function that is used to decide about the sensitivity of
+ * rows in the @combo_box. Use this if you want to set certain rows
+ * insensitive.
+ *
+ * Calling gtk_widget_queue_draw() on the @combo_box will cause the
+ * sensitivity to be updated.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_gtype_combo_box_set_sensitivity (GimpGTypeComboBox *combo_box,
+ GimpGTypeSensitivityFunc func,
+ gpointer data,
+ GDestroyNotify destroy)
+{
+ GimpGTypeComboBoxPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_GTYPE_COMBO_BOX (combo_box));
+
+ priv = GIMP_GTYPE_COMBO_BOX_GET_PRIVATE (combo_box);
+
+ if (priv->sensitivity_destroy)
+ {
+ GDestroyNotify d = priv->sensitivity_destroy;
+
+ priv->sensitivity_destroy = NULL;
+ d (priv->sensitivity_data);
+ }
+
+ priv->sensitivity_func = func;
+ priv->sensitivity_data = data;
+ priv->sensitivity_destroy = destroy;
+
+ gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box),
+ priv->pixbuf_renderer,
+ func ?
+ gimp_gtype_combo_box_data_func : NULL,
+ priv, NULL);
+
+ gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box),
+ priv->text_renderer,
+ func ?
+ gimp_gtype_combo_box_data_func : NULL,
+ priv, NULL);
+}
+
+
+/* private functions */
+
+static void
+queue_resize_cell_view (GtkContainer *container)
+{
+ GList *children = gtk_container_get_children (container);
+ GList *list;
+
+ for (list = children; list; list = g_list_next (list))
+ {
+ if (GTK_IS_CELL_VIEW (list->data))
+ {
+ gtk_widget_queue_resize (list->data);
+ break;
+ }
+ else if (GTK_IS_CONTAINER (list->data))
+ {
+ queue_resize_cell_view (list->data);
+ }
+ }
+
+ g_list_free (children);
+}
+
+static void
+gimp_gtype_combo_box_create_cells (GimpGTypeComboBox *combo_box)
+{
+ GimpGTypeComboBoxPrivate *priv = GIMP_GTYPE_COMBO_BOX_GET_PRIVATE (combo_box);
+ gboolean shown;
+
+ g_object_get (combo_box, "popup-shown", &shown, NULL);
+
+ gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo_box));
+
+ priv->pixbuf_renderer = gtk_cell_renderer_pixbuf_new ();
+ g_object_set (priv->pixbuf_renderer,
+ "xpad", 2,
+ NULL);
+
+ priv->text_renderer = gtk_cell_renderer_text_new ();
+
+ if (! shown)
+ g_object_set (priv->text_renderer,
+ "ellipsize", priv->ellipsize,
+ NULL);
+
+ if (priv->label && ! shown)
+ {
+ priv->label_renderer = gtk_cell_renderer_text_new ();
+ g_object_set (priv->label_renderer,
+ "text", priv->label,
+ NULL);
+
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box),
+ priv->label_renderer, FALSE);
+
+ gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (combo_box),
+ priv->pixbuf_renderer, FALSE);
+ gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (combo_box),
+ priv->text_renderer, TRUE);
+
+ g_object_set (priv->text_renderer,
+ "xalign", 1.0,
+ NULL);
+ }
+ else
+ {
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box),
+ priv->pixbuf_renderer, FALSE);
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box),
+ priv->text_renderer, TRUE);
+ }
+
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box),
+ priv->pixbuf_renderer,
+ "icon-name", GIMP_GTYPE_STORE_ICON_NAME,
+ "pixbuf", GIMP_GTYPE_STORE_PIXBUF,
+ NULL);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box),
+ priv->text_renderer,
+ "text", GIMP_GTYPE_STORE_LABEL,
+ NULL);
+
+ if (priv->sensitivity_func)
+ {
+ gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box),
+ priv->pixbuf_renderer,
+ gimp_gtype_combo_box_data_func,
+ priv, NULL);
+
+ gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box),
+ priv->text_renderer,
+ gimp_gtype_combo_box_data_func,
+ priv, NULL);
+ }
+
+ /* HACK: GtkCellView doesn't invalidate itself when stuff is
+ * added/removed, work around this bug until GTK+ 2.24.19
+ */
+ if (gtk_check_version (2, 24, 19))
+ {
+ GList *attached_menus;
+
+ queue_resize_cell_view (GTK_CONTAINER (combo_box));
+
+ /* HACK HACK HACK OMG */
+ attached_menus = g_object_get_data (G_OBJECT (combo_box),
+ "gtk-attached-menus");
+
+ for (; attached_menus; attached_menus = g_list_next (attached_menus))
+ queue_resize_cell_view (attached_menus->data);
+ }
+}
+
+static void
+gimp_gtype_combo_box_data_func (GtkCellLayout *layout,
+ GtkCellRenderer *cell,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ GimpGTypeComboBoxPrivate *priv = data;
+
+ if (priv->sensitivity_func)
+ {
+ GType value;
+ gboolean sensitive;
+
+ gtk_tree_model_get (model, iter,
+ GIMP_GTYPE_STORE_VALUE, &value,
+ -1);
+
+ sensitive = priv->sensitivity_func (value, priv->sensitivity_data);
+
+ g_object_set (cell,
+ "sensitive", sensitive,
+ NULL);
+ }
+}
diff --git a/libgimpwidgets/gimpgtypecombobox.h b/libgimpwidgets/gimpgtypecombobox.h
new file mode 100644
index 0000000..a2809ed
--- /dev/null
+++ b/libgimpwidgets/gimpgtypecombobox.h
@@ -0,0 +1,103 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpgtypecombobox.h
+ * Copyright (C) 2004 Sven Neumann <sven gimp org>
+ * Copyright (C) 2015 Jehan <jehan girinstud io>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_GTYPE_COMBO_BOX_H__
+#define __GIMP_GTYPE_COMBO_BOX_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_GTYPE_COMBO_BOX (gimp_gtype_combo_box_get_type ())
+#define GIMP_GTYPE_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_GTYPE_COMBO_BOX,
GimpGTypeComboBox))
+#define GIMP_GTYPE_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_GTYPE_COMBO_BOX,
GimpGTypeComboBoxClass))
+#define GIMP_IS_GTYPE_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_GTYPE_COMBO_BOX))
+#define GIMP_IS_GTYPE_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_GTYPE_COMBO_BOX))
+#define GIMP_GTYPE_COMBO_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_GTYPE_COMBO_BOX,
GimpGTypeComboBoxClass))
+
+
+typedef struct _GimpGTypeComboBoxClass GimpGTypeComboBoxClass;
+
+struct _GimpGTypeComboBox
+{
+ GtkComboBox parent_instance;
+
+ /*< private >*/
+ gpointer priv;
+
+ /* Padding for future expansion (should have gone to the class) */
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+struct _GimpGTypeComboBoxClass
+{
+ GtkComboBoxClass parent_class;
+};
+
+
+typedef gboolean (* GimpGTypeSensitivityFunc) (GType value,
+ gpointer data);
+
+
+
+GType gimp_gtype_combo_box_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_gtype_combo_box_new (const gchar *first_label,
+ GType first_value,
+ ...) G_GNUC_NULL_TERMINATED;
+GtkWidget * gimp_gtype_combo_box_new_valist (const gchar *first_label,
+ GType first_value,
+ va_list values);
+
+void gimp_gtype_combo_box_prepend (GimpGTypeComboBox *combo_box,
+ ...);
+void gimp_gtype_combo_box_append (GimpGTypeComboBox *combo_box,
+ ...);
+
+gboolean gimp_gtype_combo_box_set_active (GimpGTypeComboBox *combo_box,
+ GType value);
+gboolean gimp_gtype_combo_box_get_active (GimpGTypeComboBox *combo_box,
+ GType *value);
+
+gulong gimp_gtype_combo_box_connect (GimpGTypeComboBox *combo_box,
+ GType value,
+ GCallback callback,
+ gpointer data);
+
+void gimp_gtype_combo_box_set_label (GimpGTypeComboBox *combo_box,
+ const gchar *label);
+const gchar * gimp_gtype_combo_box_get_label (GimpGTypeComboBox *combo_box);
+
+void gimp_gtype_combo_box_set_sensitivity (GimpGTypeComboBox *combo_box,
+ GimpGTypeSensitivityFunc func,
+ gpointer data,
+ GDestroyNotify destroy);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_GTYPE_COMBO_BOX_H__ */
diff --git a/libgimpwidgets/gimpgtypestore.c b/libgimpwidgets/gimpgtypestore.c
new file mode 100644
index 0000000..d96b5e9
--- /dev/null
+++ b/libgimpwidgets/gimpgtypestore.c
@@ -0,0 +1,311 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpgtypestore.c
+ * Copyright (C) 2004-2007 Sven Neumann <sven gimp org>
+ * Copyright (C) 2015 Jehan <jehan girinstud io>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpgtypestore.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpgtypestore
+ * @title: GimpGTypeStore
+ * @short_description: A model for GType based name-value pairs.
+ *
+ * A model for GType based name-value pairs.
+ **/
+
+
+enum
+{
+ PROP_0,
+ PROP_USER_DATA_TYPE
+};
+
+typedef struct
+{
+ GType user_data_type;
+} GimpGTypeStorePrivate;
+
+
+static void gimp_gtype_store_tree_model_init (GtkTreeModelIface *iface);
+
+static void gimp_gtype_store_constructed (GObject *object);
+static void gimp_gtype_store_finalize (GObject *object);
+static void gimp_gtype_store_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_gtype_store_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_gtype_store_row_inserted (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter);
+static void gimp_gtype_store_row_deleted (GtkTreeModel *model,
+ GtkTreePath *path);
+static void gimp_gtype_store_add_empty (GimpGTypeStore *store);
+
+
+G_DEFINE_TYPE_WITH_CODE (GimpGTypeStore, gimp_gtype_store, GTK_TYPE_LIST_STORE,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
+ gimp_gtype_store_tree_model_init))
+
+#define GIMP_GTYPE_STORE_GET_PRIVATE(obj) \
+ G_TYPE_INSTANCE_GET_PRIVATE (obj, GIMP_TYPE_GTYPE_STORE, GimpGTypeStorePrivate)
+
+#define parent_class gimp_gtype_store_parent_class
+
+static GtkTreeModelIface *parent_iface = NULL;
+
+
+static void
+gimp_gtype_store_class_init (GimpGTypeStoreClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = gimp_gtype_store_constructed;
+ object_class->finalize = gimp_gtype_store_finalize;
+ object_class->set_property = gimp_gtype_store_set_property;
+ object_class->get_property = gimp_gtype_store_get_property;
+
+ /**
+ * GimpGTypeStore:user-data-type:
+ *
+ * Sets the #GType for the GIMP_GTYPE_STORE_USER_DATA column.
+ *
+ * You need to set this property when constructing the store if you want
+ * to use the GIMP_GTYPE_STORE_USER_DATA column and want to have the store
+ * handle ref-counting of your user data.
+ *
+ * Since: 2.10
+ */
+ g_object_class_install_property (object_class,
+ PROP_USER_DATA_TYPE,
+ g_param_spec_gtype ("user-data-type",
+ NULL, NULL,
+ G_TYPE_NONE,
+ G_PARAM_CONSTRUCT_ONLY |
+ GIMP_PARAM_READWRITE));
+
+ g_type_class_add_private (object_class, sizeof (GimpGTypeStorePrivate));
+}
+
+static void
+gimp_gtype_store_tree_model_init (GtkTreeModelIface *iface)
+{
+ parent_iface = g_type_interface_peek_parent (iface);
+
+ iface->row_inserted = gimp_gtype_store_row_inserted;
+ iface->row_deleted = gimp_gtype_store_row_deleted;
+}
+
+static void
+gimp_gtype_store_init (GimpGTypeStore *store)
+{
+ store->empty_iter = NULL;
+}
+
+static void
+gimp_gtype_store_constructed (GObject *object)
+{
+ GimpGTypeStore *store = GIMP_GTYPE_STORE (object);
+ GimpGTypeStorePrivate *priv = GIMP_GTYPE_STORE_GET_PRIVATE (store);
+ GType types[GIMP_GTYPE_STORE_NUM_COLUMNS];
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ types[GIMP_GTYPE_STORE_VALUE] = G_TYPE_GTYPE;
+ types[GIMP_GTYPE_STORE_LABEL] = G_TYPE_STRING;
+ types[GIMP_GTYPE_STORE_ICON_NAME] = G_TYPE_STRING;
+ types[GIMP_GTYPE_STORE_PIXBUF] = GDK_TYPE_PIXBUF;
+ types[GIMP_GTYPE_STORE_USER_DATA] = (priv->user_data_type != G_TYPE_NONE ?
+ priv->user_data_type : G_TYPE_POINTER);
+
+ gtk_list_store_set_column_types (GTK_LIST_STORE (store),
+ GIMP_GTYPE_STORE_NUM_COLUMNS, types);
+
+ gimp_gtype_store_add_empty (store);
+}
+
+static void
+gimp_gtype_store_finalize (GObject *object)
+{
+ GimpGTypeStore *store = GIMP_GTYPE_STORE (object);
+
+ if (store->empty_iter)
+ {
+ gtk_tree_iter_free (store->empty_iter);
+ store->empty_iter = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_gtype_store_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpGTypeStorePrivate *priv = GIMP_GTYPE_STORE_GET_PRIVATE (object);
+
+ switch (property_id)
+ {
+ case PROP_USER_DATA_TYPE:
+ priv->user_data_type = g_value_get_gtype (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_gtype_store_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpGTypeStorePrivate *priv = GIMP_GTYPE_STORE_GET_PRIVATE (object);
+
+ switch (property_id)
+ {
+ case PROP_USER_DATA_TYPE:
+ g_value_set_gtype (value, priv->user_data_type);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_gtype_store_row_inserted (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter)
+{
+ GimpGTypeStore *store = GIMP_GTYPE_STORE (model);
+
+ if (parent_iface->row_inserted)
+ parent_iface->row_inserted (model, path, iter);
+
+ if (store->empty_iter &&
+ memcmp (iter, store->empty_iter, sizeof (GtkTreeIter)))
+ {
+ gtk_list_store_remove (GTK_LIST_STORE (store), store->empty_iter);
+ gtk_tree_iter_free (store->empty_iter);
+ store->empty_iter = NULL;
+ }
+}
+
+static void
+gimp_gtype_store_row_deleted (GtkTreeModel *model,
+ GtkTreePath *path)
+{
+ if (parent_iface->row_deleted)
+ parent_iface->row_deleted (model, path);
+}
+
+static void
+gimp_gtype_store_add_empty (GimpGTypeStore *store)
+{
+ GtkTreeIter iter = { 0, };
+
+ g_return_if_fail (store->empty_iter == NULL);
+
+ gtk_list_store_prepend (GTK_LIST_STORE (store), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (store), &iter,
+ GIMP_GTYPE_STORE_VALUE, -1,
+ /* This string appears in an empty menu as in
+ * "nothing selected and nothing to select"
+ */
+ GIMP_GTYPE_STORE_LABEL, (_("(Empty)")),
+ -1);
+
+ store->empty_iter = gtk_tree_iter_copy (&iter);
+}
+
+/**
+ * gimp_gtype_store_new:
+ *
+ * Creates a #GtkListStore with a number of useful columns.
+ * #GimpGTypeStore is especially useful if the items you want to store
+ * are identified using an GType value.
+ *
+ * Return value: a new #GimpGTypeStore.
+ *
+ * Since: 2.10
+ **/
+GtkListStore *
+gimp_gtype_store_new (void)
+{
+ return g_object_new (GIMP_TYPE_GTYPE_STORE, NULL);
+}
+
+/**
+ * gimp_gtype_store_lookup_by_value:
+ * @model: a #GimpGTypeStore
+ * @value: a #GType value to lookup in the @model
+ * @iter: return location for the iter of the given @value
+ *
+ * Iterate over the @model looking for @value.
+ *
+ * Return value: %TRUE if the value has been located and @iter is
+ * valid, %FALSE otherwise.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_gtype_store_lookup_by_value (GtkTreeModel *model,
+ GType value,
+ GtkTreeIter *iter)
+{
+ gboolean iter_valid;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE);
+ g_return_val_if_fail (iter != NULL, FALSE);
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, iter))
+ {
+ GType this;
+
+ gtk_tree_model_get (model, iter,
+ GIMP_GTYPE_STORE_VALUE, &this,
+ -1);
+ if (this == value)
+ break;
+ }
+
+ return iter_valid;
+}
diff --git a/libgimpwidgets/gimpgtypestore.h b/libgimpwidgets/gimpgtypestore.h
new file mode 100644
index 0000000..1420113
--- /dev/null
+++ b/libgimpwidgets/gimpgtypestore.h
@@ -0,0 +1,100 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpgtypestore.c
+ * Copyright (C) 2004 Sven Neumann <sven gimp org>
+ * Copyright (C) 2015 Jehan <jehan girinstud io>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_GTYPE_STORE_H__
+#define __GIMP_GTYPE_STORE_H__
+
+G_BEGIN_DECLS
+
+
+/**
+ * GimpGTypeStoreColumns:
+ * @GIMP_GTYPE_STORE_VALUE: the GType value
+ * @GIMP_GTYPE_STORE_LABEL: a human-readable label
+ * @GIMP_GTYPE_STORE_ICON_NAME: an icon name
+ * @GIMP_GTYPE_STORE_PIXBUF: a #GdkPixbuf
+ * @GIMP_GTYPE_STORE_USER_DATA: arbitrary user data
+ * @GIMP_GTYPE_STORE_NUM_COLUMNS: the number of columns
+ * @GIMP_GTYPE_STORE_STOCK_ID: compat alias for @GIMP_GTYPE_STORE_ICON_NAME
+ *
+ * The column types of #GimpGTypeStore.
+ **/
+typedef enum
+{
+ GIMP_GTYPE_STORE_VALUE,
+ GIMP_GTYPE_STORE_LABEL,
+ GIMP_GTYPE_STORE_ICON_NAME,
+ GIMP_GTYPE_STORE_PIXBUF,
+ GIMP_GTYPE_STORE_USER_DATA,
+ GIMP_GTYPE_STORE_NUM_COLUMNS,
+
+ /* deprecated */
+ GIMP_GTYPE_STORE_STOCK_ID = GIMP_GTYPE_STORE_ICON_NAME
+} GimpGTypeStoreColumns;
+
+
+#define GIMP_TYPE_GTYPE_STORE (gimp_gtype_store_get_type ())
+#define GIMP_GTYPE_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_GTYPE_STORE,
GimpGTypeStore))
+#define GIMP_GTYPE_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_GTYPE_STORE,
GimpGTypeStoreClass))
+#define GIMP_IS_GTYPE_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_GTYPE_STORE))
+#define GIMP_IS_GTYPE_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_GTYPE_STORE))
+#define GIMP_GTYPE_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_GTYPE_STORE,
GimpGTypeStoreClass))
+
+
+typedef struct _GimpGTypeStoreClass GimpGTypeStoreClass;
+
+struct _GimpGTypeStore
+{
+ GtkListStore parent_instance;
+
+ /*< private >*/
+ GtkTreeIter *empty_iter;
+};
+
+struct _GimpGTypeStoreClass
+{
+ GtkListStoreClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_gtype_store_get_type (void) G_GNUC_CONST;
+
+GtkListStore * gimp_gtype_store_new (void);
+
+gboolean gimp_gtype_store_lookup_by_value (GtkTreeModel *model,
+ GType value,
+ GtkTreeIter *iter);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_GTYPE_STORE_H__ */
diff --git a/libgimpwidgets/gimppropwidgets.c b/libgimpwidgets/gimppropwidgets.c
index 37e91ce..7d08458 100644
--- a/libgimpwidgets/gimppropwidgets.c
+++ b/libgimpwidgets/gimppropwidgets.c
@@ -510,6 +510,110 @@ gimp_prop_int_combo_box_notify (GObject *config,
config);
}
+/************************/
+/* GType combo box */
+/************************/
+
+static void gimp_prop_gtype_combo_box_callback (GtkWidget *widget,
+ GObject *config);
+static void gimp_prop_gtype_combo_box_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *widget);
+
+/**
+ * gimp_prop_gtype_combo_box_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of GType property controlled by combo box.
+ * @store: #GimpGTypeStore holding list of labels, values, etc.
+ *
+ * Creates a #GimpGTypeComboBox widget to display and set the specified
+ * property. The contents of the widget are determined by @store,
+ * which should be created using gimp_gtype_store_new().
+ *
+ * Return value: The newly created #GimpGTypeComboBox widget.
+ *
+ * Since GIMP 2.10
+ */
+GtkWidget *
+gimp_prop_gtype_combo_box_new (GObject *config,
+ const gchar *property_name,
+ GimpGTypeStore *store)
+{
+ GParamSpec *param_spec;
+ GtkWidget *combo_box;
+ GType value;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+
+ param_spec = check_param_spec_w (config, property_name,
+ G_TYPE_PARAM_GTYPE, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ g_object_get (config,
+ property_name, &value,
+ NULL);
+
+ combo_box = g_object_new (GIMP_TYPE_GTYPE_COMBO_BOX,
+ "model", store,
+ NULL);
+
+ gimp_gtype_combo_box_set_active (GIMP_GTYPE_COMBO_BOX (combo_box), value);
+
+ g_signal_connect (combo_box, "changed",
+ G_CALLBACK (gimp_prop_gtype_combo_box_callback),
+ config);
+
+ set_param_spec (G_OBJECT (combo_box), combo_box, param_spec);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_gtype_combo_box_notify),
+ combo_box);
+
+ return combo_box;
+}
+
+static void
+gimp_prop_gtype_combo_box_callback (GtkWidget *widget,
+ GObject *config)
+{
+ GParamSpec *param_spec;
+ GType value;
+
+ param_spec = get_param_spec (G_OBJECT (widget));
+ if (! param_spec)
+ return;
+
+ if (gimp_gtype_combo_box_get_active (GIMP_GTYPE_COMBO_BOX (widget), &value))
+ {
+ g_object_set (config,
+ param_spec->name, value,
+ NULL);
+ }
+}
+
+static void
+gimp_prop_gtype_combo_box_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *combo_box)
+{
+ GType value;
+
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+
+ g_signal_handlers_block_by_func (combo_box,
+ gimp_prop_gtype_combo_box_callback,
+ config);
+
+ gimp_gtype_combo_box_set_active (GIMP_GTYPE_COMBO_BOX (combo_box), value);
+
+ g_signal_handlers_unblock_by_func (combo_box,
+ gimp_prop_gtype_combo_box_callback,
+ config);
+}
/************************/
/* boolean combo box */
diff --git a/libgimpwidgets/gimppropwidgets.h b/libgimpwidgets/gimppropwidgets.h
index 469e667..3cb2c92 100644
--- a/libgimpwidgets/gimppropwidgets.h
+++ b/libgimpwidgets/gimppropwidgets.h
@@ -55,6 +55,12 @@ GtkWidget * gimp_prop_int_combo_box_new (GObject *config,
const gchar *property_name,
GimpIntStore *store);
+/* GParamGType */
+
+GtkWidget * gimp_prop_gtype_combo_box_new (GObject *config,
+ const gchar *property_name,
+ GimpGTypeStore *store);
+
/* GParamEnum */
diff --git a/libgimpwidgets/gimpwidgets.h b/libgimpwidgets/gimpwidgets.h
index 58a4a02..9eef812 100644
--- a/libgimpwidgets/gimpwidgets.h
+++ b/libgimpwidgets/gimpwidgets.h
@@ -54,6 +54,8 @@
#include <libgimpwidgets/gimpenumwidgets.h>
#include <libgimpwidgets/gimpfileentry.h>
#include <libgimpwidgets/gimpframe.h>
+#include <libgimpwidgets/gimpgtypecombobox.h>
+#include <libgimpwidgets/gimpgtypestore.h>
#include <libgimpwidgets/gimphelpui.h>
#include <libgimpwidgets/gimphintbox.h>
#include <libgimpwidgets/gimpicons.h>
diff --git a/libgimpwidgets/gimpwidgetstypes.h b/libgimpwidgets/gimpwidgetstypes.h
index 18c925a..d245514 100644
--- a/libgimpwidgets/gimpwidgetstypes.h
+++ b/libgimpwidgets/gimpwidgetstypes.h
@@ -57,6 +57,8 @@ typedef struct _GimpEnumComboBox GimpEnumComboBox;
typedef struct _GimpEnumLabel GimpEnumLabel;
typedef struct _GimpFileEntry GimpFileEntry;
typedef struct _GimpFrame GimpFrame;
+typedef struct _GimpGTypeComboBox GimpGTypeComboBox;
+typedef struct _GimpGTypeStore GimpGTypeStore;
typedef struct _GimpIntComboBox GimpIntComboBox;
typedef struct _GimpIntStore GimpIntStore;
typedef struct _GimpMemsizeEntry GimpMemsizeEntry;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]