Files
gimp/app/widgets/gimplayermodecombobox.c
Jehan 10aa988afa Issue #2828: Scrolling up with a mouse within a drop-down list.
We were doing it all the wrong way, fixing one combo box object at a
time. So this commit basically reverses commits 68a33ab5bd, 6dfca83c2a
and a9a979b2d0 and instead runs the same code in the class code. This
way, all objects based on these base classes will have the fix from
scratch.
These improved various other drop-down lists (I found some of them, and
probably not all) as I fixed all GIMP custom widgets based on
GtkComboBox.

Note that it has to be run after filling the list apparently (I had the
problem especially with GimpIntComboBox if running in the _init() code,
then the list widget showed wrong).

(cherry picked from commit 1d984542e9)
2019-01-20 13:48:33 +01:00

471 lines
15 KiB
C

/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1999 Peter Mattis and Spencer Kimball
*
* gimplayermodecombobox.c
* Copyright (C) 2017 Michael Natterer <mitch@gimp.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 3 of the License, or
* (at your option) any later 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, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gtk/gtk.h>
#include <gegl.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpwidgets/gimpwidgets.h"
#include "widgets-types.h"
#include "operations/layer-modes/gimp-layer-modes.h"
#include "gimplayermodecombobox.h"
/**
* SECTION: gimplayermodecombobox
* @title: GimpLayerModeComboBox
* @short_description: A #GimpEnumComboBox subclass for selecting a layer mode.
*
* A #GtkComboBox subclass for selecting a layer mode
**/
enum
{
PROP_0,
PROP_CONTEXT,
PROP_LAYER_MODE,
PROP_GROUP
};
struct _GimpLayerModeComboBoxPrivate
{
GimpLayerModeContext context;
GimpLayerMode layer_mode;
GimpLayerModeGroup group;
};
static void gimp_layer_mode_combo_box_constructed (GObject *object);
static void gimp_layer_mode_combo_box_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_layer_mode_combo_box_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void gimp_layer_mode_combo_box_changed (GtkComboBox *gtk_combo);
static void gimp_layer_mode_combo_box_update_model (GimpLayerModeComboBox *combo,
gboolean change_mode);
static gboolean gimp_layer_mode_combo_box_separator_func (GtkTreeModel *model,
GtkTreeIter *iter,
gpointer data);
G_DEFINE_TYPE_WITH_PRIVATE (GimpLayerModeComboBox, gimp_layer_mode_combo_box,
GIMP_TYPE_ENUM_COMBO_BOX)
#define parent_class gimp_layer_mode_combo_box_parent_class
static void
gimp_layer_mode_combo_box_class_init (GimpLayerModeComboBoxClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkComboBoxClass *combo_class = GTK_COMBO_BOX_CLASS (klass);
object_class->constructed = gimp_layer_mode_combo_box_constructed;
object_class->set_property = gimp_layer_mode_combo_box_set_property;
object_class->get_property = gimp_layer_mode_combo_box_get_property;
combo_class->changed = gimp_layer_mode_combo_box_changed;
g_object_class_install_property (object_class, PROP_CONTEXT,
g_param_spec_flags ("context",
NULL, NULL,
GIMP_TYPE_LAYER_MODE_CONTEXT,
GIMP_LAYER_MODE_CONTEXT_ALL,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_LAYER_MODE,
g_param_spec_enum ("layer-mode",
NULL, NULL,
GIMP_TYPE_LAYER_MODE,
GIMP_LAYER_MODE_NORMAL,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_GROUP,
g_param_spec_enum ("group",
NULL, NULL,
GIMP_TYPE_LAYER_MODE_GROUP,
GIMP_LAYER_MODE_GROUP_DEFAULT,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
}
static void
gimp_layer_mode_combo_box_init (GimpLayerModeComboBox *combo)
{
combo->priv = gimp_layer_mode_combo_box_get_instance_private (combo);
gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo),
gimp_layer_mode_combo_box_separator_func,
GINT_TO_POINTER (GIMP_LAYER_MODE_SEPARATOR),
NULL);
}
static void
gimp_layer_mode_combo_box_constructed (GObject *object)
{
GimpLayerModeComboBox *combo = GIMP_LAYER_MODE_COMBO_BOX (object);
G_OBJECT_CLASS (parent_class)->constructed (object);
gimp_layer_mode_combo_box_update_model (combo, FALSE);
gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
combo->priv->layer_mode);
}
static void
gimp_layer_mode_combo_box_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GimpLayerModeComboBox *combo = GIMP_LAYER_MODE_COMBO_BOX (object);
switch (prop_id)
{
case PROP_CONTEXT:
gimp_layer_mode_combo_box_set_context (combo, g_value_get_flags (value));
break;
case PROP_LAYER_MODE:
gimp_layer_mode_combo_box_set_mode (combo, g_value_get_enum (value));
break;
case PROP_GROUP:
gimp_layer_mode_combo_box_set_group (combo, g_value_get_enum (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gimp_layer_mode_combo_box_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GimpLayerModeComboBox *combo = GIMP_LAYER_MODE_COMBO_BOX (object);
switch (prop_id)
{
case PROP_CONTEXT:
g_value_set_flags (value, combo->priv->context);
break;
case PROP_LAYER_MODE:
g_value_set_enum (value, combo->priv->layer_mode);
break;
case PROP_GROUP:
g_value_set_enum (value, combo->priv->group);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gimp_layer_mode_combo_box_changed (GtkComboBox *gtk_combo)
{
GimpLayerModeComboBox *combo = GIMP_LAYER_MODE_COMBO_BOX (gtk_combo);
GimpLayerMode mode;
if (gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo),
(gint *) &mode))
{
combo->priv->layer_mode = mode;
g_object_notify (G_OBJECT (combo), "layer-mode");
}
}
/**
* gimp_layer_mode_combo_box_new:
* Foo.
*
* Return value: a new #GimpLayerModeComboBox.
**/
GtkWidget *
gimp_layer_mode_combo_box_new (GimpLayerModeContext context)
{
return g_object_new (GIMP_TYPE_LAYER_MODE_COMBO_BOX,
"context", context,
NULL);
}
void
gimp_layer_mode_combo_box_set_context (GimpLayerModeComboBox *combo,
GimpLayerModeContext context)
{
g_return_if_fail (GIMP_IS_LAYER_MODE_COMBO_BOX (combo));
if (context != combo->priv->context)
{
g_object_freeze_notify (G_OBJECT (combo));
combo->priv->context = context;
g_object_notify (G_OBJECT (combo), "context");
gimp_layer_mode_combo_box_update_model (combo, TRUE);
g_object_thaw_notify (G_OBJECT (combo));
}
}
GimpLayerModeContext
gimp_layer_mode_combo_box_get_context (GimpLayerModeComboBox *combo)
{
g_return_val_if_fail (GIMP_IS_LAYER_MODE_COMBO_BOX (combo),
GIMP_LAYER_MODE_CONTEXT_ALL);
return combo->priv->context;
}
void
gimp_layer_mode_combo_box_set_mode (GimpLayerModeComboBox *combo,
GimpLayerMode mode)
{
g_return_if_fail (GIMP_IS_LAYER_MODE_COMBO_BOX (combo));
g_return_if_fail (gimp_layer_mode_get_context (mode) & combo->priv->context);
if (mode != combo->priv->layer_mode)
{
GtkTreeModel *model;
GtkTreeIter dummy;
model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
g_object_freeze_notify (G_OBJECT (combo));
if (! gimp_int_store_lookup_by_value (model, mode, &dummy))
{
combo->priv->group = gimp_layer_mode_get_group (mode);
g_object_notify (G_OBJECT (combo), "group");
gimp_layer_mode_combo_box_update_model (combo, FALSE);
}
gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), mode);
g_object_thaw_notify (G_OBJECT (combo));
}
}
GimpLayerMode
gimp_layer_mode_combo_box_get_mode (GimpLayerModeComboBox *combo)
{
g_return_val_if_fail (GIMP_IS_LAYER_MODE_COMBO_BOX (combo),
GIMP_LAYER_MODE_NORMAL);
return combo->priv->layer_mode;
}
void
gimp_layer_mode_combo_box_set_group (GimpLayerModeComboBox *combo,
GimpLayerModeGroup group)
{
g_return_if_fail (GIMP_IS_LAYER_MODE_COMBO_BOX (combo));
if (group != combo->priv->group)
{
g_object_freeze_notify (G_OBJECT (combo));
combo->priv->group = group;
g_object_notify (G_OBJECT (combo), "group");
gimp_layer_mode_combo_box_update_model (combo, TRUE);
g_object_thaw_notify (G_OBJECT (combo));
}
}
GimpLayerModeGroup
gimp_layer_mode_combo_box_get_group (GimpLayerModeComboBox *combo)
{
g_return_val_if_fail (GIMP_IS_LAYER_MODE_COMBO_BOX (combo),
GIMP_LAYER_MODE_GROUP_DEFAULT);
return combo->priv->group;
}
/* private functions */
static void
gimp_enum_store_add_value (GtkListStore *store,
GEnumValue *value)
{
GtkTreeIter iter = { 0, };
const gchar *desc;
const gchar *abbrev;
gchar *stripped;
desc = gimp_enum_value_get_desc (GIMP_ENUM_STORE (store)->enum_class, value);
abbrev = gimp_enum_value_get_abbrev (GIMP_ENUM_STORE (store)->enum_class, value);
/* no mnemonics in combo boxes */
stripped = gimp_strip_uline (desc);
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
GIMP_INT_STORE_VALUE, value->value,
GIMP_INT_STORE_LABEL, stripped,
GIMP_INT_STORE_ABBREV, abbrev,
-1);
g_free (stripped);
}
static void
gimp_enum_store_add_separator (GtkListStore *store)
{
GtkTreeIter iter = { 0, };
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
GIMP_INT_STORE_VALUE, GIMP_LAYER_MODE_SEPARATOR,
-1);
}
static GtkListStore *
gimp_enum_store_new_from_array (GType enum_type,
gint n_values,
const gint *values,
GimpLayerModeContext context)
{
GtkListStore *store;
GEnumValue *value;
gboolean first_item = TRUE;
gboolean prepend_separator = FALSE;
gint i;
g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
g_return_val_if_fail (n_values > 1, NULL);
g_return_val_if_fail (values != NULL, NULL);
store = g_object_new (GIMP_TYPE_ENUM_STORE,
"enum-type", enum_type,
NULL);
for (i = 0; i < n_values; i++)
{
if (values[i] != GIMP_LAYER_MODE_SEPARATOR)
{
if (gimp_layer_mode_get_context (values[i]) & context)
{
value = g_enum_get_value (GIMP_ENUM_STORE (store)->enum_class,
values[i]);
if (value)
{
if (prepend_separator)
{
gimp_enum_store_add_separator (store);
prepend_separator = FALSE;
}
gimp_enum_store_add_value (store, value);
first_item = FALSE;
}
}
}
else
{
if (! first_item)
prepend_separator = TRUE;
}
}
return store;
}
static void
gimp_layer_mode_combo_box_update_model (GimpLayerModeComboBox *combo,
gboolean change_mode)
{
GtkListStore *store;
const GimpLayerMode *modes;
gint n_modes;
modes = gimp_layer_mode_get_group_array (combo->priv->group, &n_modes);
store = gimp_enum_store_new_from_array (GIMP_TYPE_LAYER_MODE,
n_modes, (gint *) modes,
combo->priv->context);
gtk_combo_box_set_model (GTK_COMBO_BOX (combo), GTK_TREE_MODEL (store));
g_object_unref (store);
if (change_mode)
{
GimpLayerMode new_mode;
if (gimp_layer_mode_get_for_group (combo->priv->layer_mode,
combo->priv->group,
&new_mode) &&
(gimp_layer_mode_get_context (new_mode) & combo->priv->context))
{
gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), new_mode);
}
else
{
GtkTreeIter iter;
/* switch to the first mode, which will be one of the "normal" */
gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
}
}
}
static gboolean
gimp_layer_mode_combo_box_separator_func (GtkTreeModel *model,
GtkTreeIter *iter,
gpointer data)
{
gint value;
gtk_tree_model_get (model, iter, GIMP_INT_STORE_VALUE, &value, -1);
return value == GPOINTER_TO_INT (data);
}