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.
I still use a GimpIntComboBox but store the property value in the
user-data column because gpointer isn't a subset of gint.
Adds in libgimpwidgets:
- gimp_int_combo_box_set_active_by_user_data()
- gimp_int_combo_box_get_active_user_data()
- gimp_int_store_lookup_by_user_data()
- gimp_prop_pointer_combo_box_new() to create a GimpIntComboBox and
  attach it to a gpointer property.
Thanks Massimo and Mitch for reviewing my code.
This commit is contained in:
Jehan 2016-01-21 21:43:15 +01:00
parent eb25d0cead
commit 1f4839288e
9 changed files with 277 additions and 51 deletions

View File

@ -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;
}

View File

@ -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);

View File

@ -274,7 +274,7 @@ gimp_symmetry_editor_image_changed (GimpContext *context,
gtk_list_store_set (store, &iter,
GIMP_INT_STORE_LABEL,
klass->label,
GIMP_INT_STORE_VALUE,
GIMP_INT_STORE_USER_DATA,
sym_iter->data,
-1);
g_type_class_unref (klass);
@ -284,11 +284,11 @@ 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_INT_STORE_USER_DATA, 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_pointer_combo_box_new (G_OBJECT (image),
"symmetry",
GIMP_INT_STORE (store));
g_object_unref (store);
gimp_int_combo_box_set_label (GIMP_INT_COMBO_BOX (editor->p->menu),

View File

@ -470,6 +470,72 @@ gimp_int_combo_box_get_active (GimpIntComboBox *combo_box,
return FALSE;
}
/**
* gimp_int_combo_box_set_active_by_user_data:
* @combo_box: a #GimpIntComboBox
* @user_data: an integer value
*
* Looks up the item that has the given @user_data and makes it the
* selected item in the @combo_box.
*
* Return value: %TRUE on success or %FALSE if there was no item for
* this user-data.
*
* Since: 2.10
**/
gboolean
gimp_int_combo_box_set_active_by_user_data (GimpIntComboBox *combo_box,
gpointer user_data)
{
GtkTreeModel *model;
GtkTreeIter iter;
g_return_val_if_fail (GIMP_IS_INT_COMBO_BOX (combo_box), FALSE);
model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
if (gimp_int_store_lookup_by_user_data (model, user_data, &iter))
{
gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
return TRUE;
}
return FALSE;
}
/**
* gimp_int_combo_box_get_active_user_data:
* @combo_box: a #GimpIntComboBox
* @user_data: return location for the gpointer value
*
* Retrieves the user-data of the selected (active) item in the @combo_box.
*
* Return value: %TRUE if @user_data has been set or %FALSE if no item was
* active.
*
* Since: 2.10
**/
gboolean
gimp_int_combo_box_get_active_user_data (GimpIntComboBox *combo_box,
gpointer *user_data)
{
GtkTreeIter iter;
g_return_val_if_fail (GIMP_IS_INT_COMBO_BOX (combo_box), FALSE);
g_return_val_if_fail (user_data != 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_INT_STORE_USER_DATA, user_data,
-1);
return TRUE;
}
return FALSE;
}
/**
* gimp_int_combo_box_connect:
* @combo_box: a #GimpIntComboBox

View File

@ -85,6 +85,13 @@ gboolean gimp_int_combo_box_set_active (GimpIntComboBox *combo_box,
gboolean gimp_int_combo_box_get_active (GimpIntComboBox *combo_box,
gint *value);
gboolean
gimp_int_combo_box_set_active_by_user_data (GimpIntComboBox *combo_box,
gpointer user_data);
gboolean
gimp_int_combo_box_get_active_user_data (GimpIntComboBox *combo_box,
gpointer *user_data);
gulong gimp_int_combo_box_connect (GimpIntComboBox *combo_box,
gint value,
GCallback callback,

View File

@ -309,3 +309,42 @@ gimp_int_store_lookup_by_value (GtkTreeModel *model,
return iter_valid;
}
/**
* gimp_int_store_lookup_by_user_data:
* @model: a #GimpIntStore
* @user_data: a gpointer "user-data" to lookup in the @model
* @iter: return location for the iter of the given @user_data
*
* Iterate over the @model looking for @user_data.
*
* Return value: %TRUE if the user-data has been located and @iter is
* valid, %FALSE otherwise.
*
* Since: 2.10
**/
gboolean
gimp_int_store_lookup_by_user_data (GtkTreeModel *model,
gpointer user_data,
GtkTreeIter *iter)
{
gboolean iter_valid = FALSE;
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))
{
gpointer this;
gtk_tree_model_get (model, iter,
GIMP_INT_STORE_USER_DATA, &this,
-1);
if (this == user_data)
break;
}
return (gboolean) iter_valid;
}

View File

@ -92,6 +92,9 @@ GtkListStore * gimp_int_store_new (void);
gboolean gimp_int_store_lookup_by_value (GtkTreeModel *model,
gint value,
GtkTreeIter *iter);
gboolean gimp_int_store_lookup_by_user_data (GtkTreeModel *model,
gpointer user_data,
GtkTreeIter *iter);
G_END_DECLS

View File

@ -337,11 +337,17 @@ gimp_prop_enum_check_button_notify (GObject *config,
/* int/enum combo box */
/*************************/
static void gimp_prop_int_combo_box_callback (GtkWidget *widget,
GObject *config);
static void gimp_prop_int_combo_box_notify (GObject *config,
GParamSpec *param_spec,
GtkWidget *widget);
static void gimp_prop_int_combo_box_callback (GtkWidget *widget,
GObject *config);
static void gimp_prop_int_combo_box_notify (GObject *config,
GParamSpec *param_spec,
GtkWidget *widget);
static void gimp_prop_pointer_combo_box_callback (GtkWidget *widget,
GObject *config);
static void gimp_prop_pointer_combo_box_notify (GObject *config,
GParamSpec *param_spec,
GtkWidget *combo_box);
/**
* gimp_prop_int_combo_box_new:
@ -397,6 +403,72 @@ gimp_prop_int_combo_box_new (GObject *config,
return combo_box;
}
/**
* gimp_prop_pointer_combo_box_new:
* @config: Object to which property is attached.
* @property_name: Name of GType/gpointer property controlled by combo box.
* @store: #GimpIntStore holding list of labels, values, etc.
*
* Creates a #GimpIntComboBox widget to display and set the specified
* property. The contents of the widget are determined by @store,
* which should be created using gimp_int_store_new().
* Values are GType/gpointer data, and therefore must be stored in the
* "user-data" column, instead of the usual "value" column.
*
* Return value: The newly created #GimpIntComboBox widget.
*
* Since GIMP 2.10
*/
GtkWidget *
gimp_prop_pointer_combo_box_new (GObject *config,
const gchar *property_name,
GimpIntStore *store)
{
GParamSpec *param_spec;
GtkWidget *combo_box;
gpointer property_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)
{
param_spec = check_param_spec_w (config, property_name,
G_TYPE_PARAM_POINTER, G_STRFUNC);
if (! param_spec)
return NULL;
}
g_object_get (config,
property_name, &property_value,
NULL);
/* We use a GimpIntComboBox but we cannot store gpointer in the
* "value" column, because gpointer is not a subset of gint. Instead
* we store the value in the "user-data" column which is a gpointer.
*/
combo_box = g_object_new (GIMP_TYPE_INT_COMBO_BOX,
"model", store,
NULL);
gimp_int_combo_box_set_active_by_user_data (GIMP_INT_COMBO_BOX (combo_box),
property_value);
g_signal_connect (combo_box, "changed",
G_CALLBACK (gimp_prop_pointer_combo_box_callback),
config);
set_param_spec (G_OBJECT (combo_box), combo_box, param_spec);
connect_notify (config, property_name,
G_CALLBACK (gimp_prop_pointer_combo_box_notify),
combo_box);
return combo_box;
}
/**
* gimp_prop_enum_combo_box_new:
* @config: Object to which property is attached.
@ -510,6 +582,48 @@ gimp_prop_int_combo_box_notify (GObject *config,
config);
}
static void
gimp_prop_pointer_combo_box_callback (GtkWidget *widget,
GObject *config)
{
GParamSpec *param_spec;
gpointer value;
param_spec = get_param_spec (G_OBJECT (widget));
if (! param_spec)
return;
if (gimp_int_combo_box_get_active_user_data (GIMP_INT_COMBO_BOX (widget),
&value))
{
g_object_set (config,
param_spec->name, value,
NULL);
}
}
static void
gimp_prop_pointer_combo_box_notify (GObject *config,
GParamSpec *param_spec,
GtkWidget *combo_box)
{
gpointer value;
g_object_get (config,
param_spec->name, &value,
NULL);
g_signal_handlers_block_by_func (combo_box,
gimp_prop_pointer_combo_box_callback,
config);
gimp_int_combo_box_set_active_by_user_data (GIMP_INT_COMBO_BOX (combo_box),
value);
g_signal_handlers_unblock_by_func (combo_box,
gimp_prop_pointer_combo_box_callback,
config);
}
/************************/
/* boolean combo box */

View File

@ -55,6 +55,11 @@ GtkWidget * gimp_prop_int_combo_box_new (GObject *config,
const gchar *property_name,
GimpIntStore *store);
/* GParamGType */
GtkWidget * gimp_prop_pointer_combo_box_new (GObject *config,
const gchar *property_name,
GimpIntStore *store);
/* GParamEnum */