[gimp/wip/Jehan/issue-498-quick-brush-edit: 519/523] app: add a GimpModifiersManager object to GimpDisplayConfig.
- From: Jehan <jehanp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/wip/Jehan/issue-498-quick-brush-edit: 519/523] app: add a GimpModifiersManager object to GimpDisplayConfig.
- Date: Thu, 11 Aug 2022 17:06:27 +0000 (UTC)
commit cbd7b6bd951cf5e233853806dc47a709b67b24e6
Author: Jehan <jehan girinstud io>
Date: Tue Jul 26 19:21:56 2022 +0200
app: add a GimpModifiersManager object to GimpDisplayConfig.
This object's goal will be to manage customized modifiers per input
device button, which is why I add it to GimpDisplayConfig. It is in its
own new config file (`modifiersrc` in config dir) because it requires
GDK types access (well I could have done without, but it would have been
less semantic, hence not as good of an API). Anyway it is only useful
when running GIMP as GUI.
The GUI widget and the usage code to make this actually useful will come
in upcoming commits.
app/config/gimpdisplayconfig.c | 19 ++
app/config/gimpdisplayconfig.h | 2 +
app/display/Makefile.am | 2 +
app/display/display-enums.c | 43 +++
app/display/display-enums.h | 19 ++
app/display/display-types.h | 2 +
app/display/gimpmodifiersmanager.c | 585 +++++++++++++++++++++++++++++++++++++
app/display/gimpmodifiersmanager.h | 80 +++++
app/display/meson.build | 1 +
app/gui/Makefile.am | 2 +
app/gui/gui.c | 6 +
app/gui/meson.build | 1 +
app/gui/modifiers.c | 218 ++++++++++++++
app/gui/modifiers.h | 36 +++
14 files changed, 1016 insertions(+)
---
diff --git a/app/config/gimpdisplayconfig.c b/app/config/gimpdisplayconfig.c
index 89ecaa3a23..1037b81924 100644
--- a/app/config/gimpdisplayconfig.c
+++ b/app/config/gimpdisplayconfig.c
@@ -65,6 +65,7 @@ enum
PROP_SHOW_PAINT_TOOL_CURSOR,
PROP_IMAGE_TITLE_FORMAT,
PROP_IMAGE_STATUS_FORMAT,
+ PROP_MODIFIERS_MANAGER,
PROP_MONITOR_XRESOLUTION,
PROP_MONITOR_YRESOLUTION,
PROP_MONITOR_RES_FROM_GDK,
@@ -388,6 +389,17 @@ gimp_display_config_class_init (GimpDisplayConfigClass *klass)
TRUE,
GIMP_PARAM_STATIC_STRINGS |
GIMP_CONFIG_PARAM_IGNORE);
+
+ /* Stored as a property because we want to copy the object when we
+ * copy the config (for Preferences, etc.). But we don't want it to be
+ * stored as a config property into the rc files.
+ * Modifiers have their own rc file.
+ */
+ g_object_class_install_property (object_class, PROP_MODIFIERS_MANAGER,
+ g_param_spec_object ("modifiers-manager",
+ NULL, NULL,
+ G_TYPE_OBJECT,
+ GIMP_PARAM_READWRITE));
}
static void
@@ -418,6 +430,7 @@ gimp_display_config_finalize (GObject *object)
g_clear_object (&display_config->default_view);
g_clear_object (&display_config->default_fullscreen_view);
+ g_clear_object (&display_config->modifiers_manager);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@@ -523,6 +536,9 @@ gimp_display_config_set_property (GObject *object,
case PROP_SPACE_BAR_ACTION:
display_config->space_bar_action = g_value_get_enum (value);
break;
+ case PROP_MODIFIERS_MANAGER:
+ display_config->modifiers_manager = g_value_dup_object (value);
+ break;
case PROP_ZOOM_QUALITY:
display_config->zoom_quality = g_value_get_enum (value);
break;
@@ -640,6 +656,9 @@ gimp_display_config_get_property (GObject *object,
case PROP_SPACE_BAR_ACTION:
g_value_set_enum (value, display_config->space_bar_action);
break;
+ case PROP_MODIFIERS_MANAGER:
+ g_value_set_object (value, display_config->modifiers_manager);
+ break;
case PROP_ZOOM_QUALITY:
g_value_set_enum (value, display_config->zoom_quality);
break;
diff --git a/app/config/gimpdisplayconfig.h b/app/config/gimpdisplayconfig.h
index 008b009a1a..5fb3d15bb5 100644
--- a/app/config/gimpdisplayconfig.h
+++ b/app/config/gimpdisplayconfig.h
@@ -71,6 +71,8 @@ struct _GimpDisplayConfig
GimpSpaceBarAction space_bar_action;
GimpZoomQuality zoom_quality;
gboolean use_event_history;
+
+ GObject *modifiers_manager;
};
struct _GimpDisplayConfigClass
diff --git a/app/display/Makefile.am b/app/display/Makefile.am
index cb30eea9b7..f64d98d9d3 100644
--- a/app/display/Makefile.am
+++ b/app/display/Makefile.am
@@ -143,6 +143,8 @@ libappdisplay_a_sources = \
gimpdisplayshell-transform.h \
gimpdisplayshell-utils.c \
gimpdisplayshell-utils.h \
+ gimpmodifiersmanager.c \
+ gimpmodifiersmanager.h \
gimpimagewindow.c \
gimpimagewindow.h \
gimpmotionbuffer.c \
diff --git a/app/display/display-enums.c b/app/display/display-enums.c
index b1cd881407..db317dddbc 100644
--- a/app/display/display-enums.c
+++ b/app/display/display-enums.c
@@ -592,6 +592,49 @@ gimp_zoom_focus_get_type (void)
return type;
}
+GType
+gimp_modifier_action_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_MODIFIER_ACTION_NONE, "GIMP_MODIFIER_ACTION_NONE", "none" },
+ { GIMP_MODIFIER_ACTION_PANNING, "GIMP_MODIFIER_ACTION_PANNING", "panning" },
+ { GIMP_MODIFIER_ACTION_ZOOMING, "GIMP_MODIFIER_ACTION_ZOOMING", "zooming" },
+ { GIMP_MODIFIER_ACTION_ROTATING, "GIMP_MODIFIER_ACTION_ROTATING", "rotating" },
+ { GIMP_MODIFIER_ACTION_STEP_ROTATING, "GIMP_MODIFIER_ACTION_STEP_ROTATING", "step-rotating" },
+ { GIMP_MODIFIER_ACTION_LAYER_PICKING, "GIMP_MODIFIER_ACTION_LAYER_PICKING", "layer-picking" },
+ { GIMP_MODIFIER_ACTION_MENU, "GIMP_MODIFIER_ACTION_MENU", "menu" },
+ { GIMP_MODIFIER_ACTION_BRUSH_PIXEL_SIZE, "GIMP_MODIFIER_ACTION_BRUSH_PIXEL_SIZE", "brush-pixel-size" },
+ { GIMP_MODIFIER_ACTION_BRUSH_SIZE, "GIMP_MODIFIER_ACTION_BRUSH_SIZE", "brush-size" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_MODIFIER_ACTION_NONE, NC_("modifier-action", "No action"), NULL },
+ { GIMP_MODIFIER_ACTION_PANNING, NC_("modifier-action", "Pan"), NULL },
+ { GIMP_MODIFIER_ACTION_ZOOMING, NC_("modifier-action", "Zoom"), NULL },
+ { GIMP_MODIFIER_ACTION_ROTATING, NC_("modifier-action", "Rotate"), NULL },
+ { GIMP_MODIFIER_ACTION_STEP_ROTATING, NC_("modifier-action", "Rotate by 15 degree steps"), NULL },
+ { GIMP_MODIFIER_ACTION_LAYER_PICKING, NC_("modifier-action", "Pick a layer"), NULL },
+ { GIMP_MODIFIER_ACTION_MENU, NC_("modifier-action", "Display the menu"), NULL },
+ { GIMP_MODIFIER_ACTION_BRUSH_PIXEL_SIZE, NC_("modifier-action", "Change brush size in canvas pixels"),
NULL },
+ { GIMP_MODIFIER_ACTION_BRUSH_SIZE, NC_("modifier-action", "Change brush size relatively"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpModifierAction", values);
+ gimp_type_set_translation_context (type, "modifier-action");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
/* Generated data ends here */
diff --git a/app/display/display-enums.h b/app/display/display-enums.h
index 71374f2856..82d2dd7dd2 100644
--- a/app/display/display-enums.h
+++ b/app/display/display-enums.h
@@ -261,6 +261,25 @@ typedef enum
} GimpZoomFocus;
+#define GIMP_TYPE_MODIFIER_ACTION (gimp_modifier_action_get_type ())
+
+GType gimp_modifier_action_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_MODIFIER_ACTION_NONE, /*< desc="No action" >*/
+ GIMP_MODIFIER_ACTION_PANNING, /*< desc="Pan" >*/
+ GIMP_MODIFIER_ACTION_ZOOMING, /*< desc="Zoom" >*/
+ GIMP_MODIFIER_ACTION_ROTATING, /*< desc="Rotate" >*/
+ GIMP_MODIFIER_ACTION_STEP_ROTATING, /*< desc="Rotate by 15 degree steps" >*/
+ GIMP_MODIFIER_ACTION_LAYER_PICKING, /*< desc="Pick a layer" >*/
+
+ GIMP_MODIFIER_ACTION_MENU, /*< desc="Display the menu" >*/
+ GIMP_MODIFIER_ACTION_BRUSH_PIXEL_SIZE, /*< desc="Change brush size in canvas pixels" >*/
+ GIMP_MODIFIER_ACTION_BRUSH_SIZE /*< desc="Change brush size relatively" >*/
+} GimpModifierAction;
+
+
/*
* non-registered enums; register them if needed
*/
diff --git a/app/display/display-types.h b/app/display/display-types.h
index 302f465d19..04f2e8d237 100644
--- a/app/display/display-types.h
+++ b/app/display/display-types.h
@@ -48,5 +48,7 @@ typedef struct _GimpToolWidgetGroup GimpToolWidgetGroup;
typedef struct _GimpDisplayXfer GimpDisplayXfer;
typedef struct _Selection Selection;
+typedef struct _GimpModifiersManager GimpModifiersManager;
+
#endif /* __DISPLAY_TYPES_H__ */
diff --git a/app/display/gimpmodifiersmanager.c b/app/display/gimpmodifiersmanager.c
new file mode 100644
index 0000000000..9b31560cb4
--- /dev/null
+++ b/app/display/gimpmodifiersmanager.c
@@ -0,0 +1,585 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpmodifiersmanager.c
+ * Copyright (C) 2022 Jehan
+ *
+ * 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 "libgimpbase/gimpbase.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "display-types.h"
+
+#include "widgets/gimpwidgets-utils.h"
+
+#include "gimpmodifiersmanager.h"
+
+#include "gimp-intl.h"
+
+enum
+{
+ MODIFIERS_MANAGER_MAPPING,
+ MODIFIERS_MANAGER_MODIFIERS,
+ MODIFIERS_MANAGER_MOD_ACTION,
+};
+
+typedef struct
+{
+ GdkModifierType modifiers;
+ GimpModifierAction mod_action;
+} GimpModifierMapping;
+
+struct _GimpModifiersManagerPrivate
+{
+ GHashTable *actions;
+ GList *buttons;
+};
+
+static void gimp_modifiers_manager_config_iface_init (GimpConfigInterface *iface);
+static void gimp_modifiers_manager_finalize (GObject *object);
+static gboolean gimp_modifiers_manager_serialize (GimpConfig *config,
+ GimpConfigWriter *writer,
+ gpointer data);
+static gboolean gimp_modifiers_manager_deserialize (GimpConfig *config,
+ GScanner *scanner,
+ gint nest_level,
+ gpointer data);
+
+static void gimp_modifiers_manager_free_mapping (GimpModifierMapping *mapping);
+
+static void gimp_modifiers_manager_get_keys (GdkDevice *device,
+ guint button,
+ GdkModifierType modifiers,
+ gchar **actions_key,
+ gchar **buttons_key);
+static void gimp_modifiers_manager_initialize (GimpModifiersManager *manager,
+ GdkDevice *device,
+ guint button);
+
+
+G_DEFINE_TYPE_WITH_CODE (GimpModifiersManager, gimp_modifiers_manager, G_TYPE_OBJECT,
+ G_ADD_PRIVATE (GimpModifiersManager)
+ G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG,
+ gimp_modifiers_manager_config_iface_init))
+
+#define parent_class gimp_modifiers_manager_parent_class
+
+
+static void
+gimp_modifiers_manager_class_init (GimpModifiersManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gimp_modifiers_manager_finalize;
+}
+
+static void
+gimp_modifiers_manager_init (GimpModifiersManager *manager)
+{
+ manager->p = gimp_modifiers_manager_get_instance_private (manager);
+
+ manager->p->actions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+ (GDestroyNotify) gimp_modifiers_manager_free_mapping);
+}
+
+static void
+gimp_modifiers_manager_config_iface_init (GimpConfigInterface *iface)
+{
+ iface->serialize = gimp_modifiers_manager_serialize;
+ iface->deserialize = gimp_modifiers_manager_deserialize;
+}
+
+static void
+gimp_modifiers_manager_finalize (GObject *object)
+{
+ GimpModifiersManager *manager = GIMP_MODIFIERS_MANAGER (object);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+
+ g_hash_table_unref (manager->p->actions);
+ g_list_free_full (manager->p->buttons, g_free);
+}
+
+static gboolean
+gimp_modifiers_manager_serialize (GimpConfig *config,
+ GimpConfigWriter *writer,
+ gpointer data)
+{
+ GimpModifiersManager *manager = GIMP_MODIFIERS_MANAGER (config);
+ GEnumClass *enum_class;
+ GList *keys;
+ GList *iter;
+
+ enum_class = g_type_class_ref (GIMP_TYPE_MODIFIER_ACTION);
+ keys = g_hash_table_get_keys (manager->p->actions);
+
+ for (iter = keys; iter; iter = iter->next)
+ {
+ const gchar *button = iter->data;
+ GimpModifierMapping *mapping;
+ GEnumValue *enum_value;
+
+ gimp_config_writer_open (writer, "mapping");
+ gimp_config_writer_string (writer, button);
+
+ mapping = g_hash_table_lookup (manager->p->actions, button);
+
+ gimp_config_writer_open (writer, "modifiers");
+ gimp_config_writer_printf (writer, "%d", mapping->modifiers);
+ gimp_config_writer_close (writer);
+
+ enum_value = g_enum_get_value (enum_class, GPOINTER_TO_INT (mapping->mod_action));
+ gimp_config_writer_open (writer, "mod-action");
+ gimp_config_writer_identifier (writer, enum_value->value_nick);
+ gimp_config_writer_close (writer);
+
+ gimp_config_writer_close (writer);
+ }
+
+ g_list_free (keys);
+ g_type_class_unref (enum_class);
+
+ return TRUE;
+}
+
+static gboolean
+gimp_modifiers_manager_deserialize (GimpConfig *config,
+ GScanner *scanner,
+ gint nest_level,
+ gpointer data)
+{
+ GimpModifiersManager *manager = GIMP_MODIFIERS_MANAGER (config);
+ GEnumClass *enum_class;
+ GTokenType token;
+ guint scope_id;
+ guint old_scope_id;
+ gchar *actions_key = NULL;
+ GdkModifierType modifiers;
+
+ scope_id = g_type_qname (G_TYPE_FROM_INSTANCE (config));
+ old_scope_id = g_scanner_set_scope (scanner, scope_id);
+ enum_class = g_type_class_ref (GIMP_TYPE_MODIFIER_ACTION);
+
+ g_scanner_scope_add_symbol (scanner, scope_id, "mapping",
+ GINT_TO_POINTER (MODIFIERS_MANAGER_MAPPING));
+ g_scanner_scope_add_symbol (scanner, scope_id, "modifiers",
+ GINT_TO_POINTER (MODIFIERS_MANAGER_MODIFIERS));
+ g_scanner_scope_add_symbol (scanner, scope_id, "mod-action",
+ GINT_TO_POINTER (MODIFIERS_MANAGER_MOD_ACTION));
+
+ token = G_TOKEN_LEFT_PAREN;
+
+ while (g_scanner_peek_next_token (scanner) == token)
+ {
+ token = g_scanner_get_next_token (scanner);
+
+ switch (token)
+ {
+ case G_TOKEN_LEFT_PAREN:
+ token = G_TOKEN_SYMBOL;
+ break;
+
+ case G_TOKEN_SYMBOL:
+ switch (GPOINTER_TO_INT (scanner->value.v_symbol))
+ {
+ case MODIFIERS_MANAGER_MAPPING:
+ token = G_TOKEN_LEFT_PAREN;
+ if (! gimp_scanner_parse_string (scanner, &actions_key))
+ goto error;
+ break;
+
+ case MODIFIERS_MANAGER_MOD_ACTION:
+ {
+ GimpModifierMapping *mapping;
+ GEnumValue *enum_value;
+
+ token = G_TOKEN_IDENTIFIER;
+ if (g_scanner_peek_next_token (scanner) != token)
+ goto error;
+
+ g_scanner_get_next_token (scanner);
+
+ enum_value = g_enum_get_value_by_nick (enum_class,
+ scanner->value.v_identifier);
+
+ if (! enum_value)
+ enum_value = g_enum_get_value_by_name (enum_class,
+ scanner->value.v_identifier);
+
+ if (! enum_value)
+ {
+ g_scanner_error (scanner,
+ _("invalid value '%s' for contextual action"),
+ scanner->value.v_identifier);
+ return G_TOKEN_NONE;
+ }
+
+ if (g_hash_table_lookup (manager->p->actions, actions_key))
+ {
+ /* This should not happen. But let's avoid breaking
+ * the whole parsing for a duplicate. Just output to
+ * stderr to track any problematic modifiersrc
+ * creation.
+ */
+ g_printerr ("%s: ignoring duplicate action %s for mapping %s\n",
+ G_STRFUNC, scanner->value.v_identifier, actions_key);
+ g_clear_pointer (&actions_key, g_free);
+ }
+ else
+ {
+ gchar *suffix;
+ suffix = g_strdup_printf ("-%d", modifiers);
+
+ if (g_str_has_suffix (actions_key, suffix))
+ {
+ gchar *buttons_key = g_strndup (actions_key,
+ strlen (actions_key) - strlen (suffix));
+
+ mapping = g_slice_new (GimpModifierMapping);
+ mapping->modifiers = modifiers;
+ mapping->mod_action = enum_value->value;
+ g_hash_table_insert (manager->p->actions, actions_key, mapping);
+
+ if (g_list_find_custom (manager->p->buttons, buttons_key, (GCompareFunc)
g_strcmp0))
+ g_free (buttons_key);
+ else
+ manager->p->buttons = g_list_prepend (manager->p->buttons, buttons_key);
+ }
+ else
+ {
+ g_printerr ("%s: ignoring mapping %s with invalid modifiers %d\n",
+ G_STRFUNC, actions_key, modifiers);
+ g_clear_pointer (&actions_key, g_free);
+ }
+
+ g_free (suffix);
+ }
+
+ /* Closing parentheses twice. */
+ token = G_TOKEN_RIGHT_PAREN;
+ if (g_scanner_peek_next_token (scanner) != token)
+ goto error;
+
+ g_scanner_get_next_token (scanner);
+ }
+ break;
+
+ case MODIFIERS_MANAGER_MODIFIERS:
+ token = G_TOKEN_RIGHT_PAREN;
+ if (! gimp_scanner_parse_int (scanner, (int *) &modifiers))
+ goto error;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case G_TOKEN_RIGHT_PAREN:
+ token = G_TOKEN_LEFT_PAREN;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ error:
+
+ g_scanner_scope_remove_symbol (scanner, scope_id, "mapping");
+ g_scanner_scope_remove_symbol (scanner, scope_id, "modifiers");
+ g_scanner_scope_remove_symbol (scanner, scope_id, "mod-action");
+
+ g_scanner_set_scope (scanner, old_scope_id);
+ g_type_class_unref (enum_class);
+
+ return G_TOKEN_LEFT_PAREN;
+}
+
+
+/* public functions */
+
+GimpModifiersManager *
+gimp_modifiers_manager_new (void)
+{
+ return g_object_new (GIMP_TYPE_MODIFIERS_MANAGER, NULL);
+}
+
+GimpModifierAction
+gimp_modifiers_manager_get_action (GimpModifiersManager *manager,
+ GdkDevice *device,
+ guint button,
+ GdkModifierType state)
+{
+ gchar *actions_key = NULL;
+ gchar *buttons_key = NULL;
+ GdkModifierType mod_state;
+ GimpModifierAction retval = GIMP_MODIFIER_ACTION_NONE;
+
+ mod_state = state & gimp_get_all_modifiers_mask ();
+
+ gimp_modifiers_manager_get_keys (device, button, mod_state,
+ &actions_key, &buttons_key);
+
+ if (g_list_find_custom (manager->p->buttons, buttons_key, (GCompareFunc) g_strcmp0))
+ {
+ GimpModifierMapping *mapping;
+
+ mapping = g_hash_table_lookup (manager->p->actions, actions_key);
+
+ if (mapping == NULL)
+ retval = GIMP_MODIFIER_ACTION_NONE;
+ else
+ retval = mapping->mod_action;
+ }
+ else if (button == 2)
+ {
+ if (mod_state == gimp_get_extend_selection_mask ())
+ retval = GIMP_MODIFIER_ACTION_ROTATING;
+ else if (mod_state == (gimp_get_extend_selection_mask () | GDK_CONTROL_MASK))
+ retval = GIMP_MODIFIER_ACTION_STEP_ROTATING;
+ else if (mod_state == gimp_get_toggle_behavior_mask ())
+ retval = GIMP_MODIFIER_ACTION_ZOOMING;
+ else if (mod_state == GDK_MOD1_MASK)
+ retval = GIMP_MODIFIER_ACTION_LAYER_PICKING;
+ else if (mod_state == 0)
+ retval = GIMP_MODIFIER_ACTION_PANNING;
+ }
+ else if (button == 3)
+ {
+ if (mod_state == GDK_MOD1_MASK)
+ retval = GIMP_MODIFIER_ACTION_BRUSH_SIZE;
+ else if (mod_state == 0)
+ retval = GIMP_MODIFIER_ACTION_MENU;
+ }
+
+ g_free (actions_key);
+ g_free (buttons_key);
+
+ return retval;
+}
+
+GList *
+gimp_modifiers_manager_get_modifiers (GimpModifiersManager *manager,
+ GdkDevice *device,
+ guint button)
+{
+ gchar *buttons_key = NULL;
+ GList *modifiers = NULL;
+ GList *action_keys;
+ GList *iter;
+ gchar *action_prefix;
+
+ gimp_modifiers_manager_initialize (manager, device, button);
+
+ gimp_modifiers_manager_get_keys (device, button, 0, NULL,
+ &buttons_key);
+ action_prefix = g_strdup_printf ("%s-", buttons_key);
+ g_free (buttons_key);
+
+ action_keys = g_hash_table_get_keys (manager->p->actions);
+ for (iter = action_keys; iter; iter = iter->next)
+ {
+ if (g_str_has_prefix (iter->data, action_prefix))
+ {
+ GimpModifierMapping *mapping;
+
+ mapping = g_hash_table_lookup (manager->p->actions, iter->data);
+
+ /* TODO: the modifiers list should be sorted to ensure
+ * consistency.
+ */
+ modifiers = g_list_prepend (modifiers, GINT_TO_POINTER (mapping->modifiers));
+ }
+ }
+
+ g_free (action_prefix);
+ g_list_free (action_keys);
+
+ return modifiers;
+}
+
+void
+gimp_modifiers_manager_set (GimpModifiersManager *manager,
+ GdkDevice *device,
+ guint button,
+ GdkModifierType modifiers,
+ GimpModifierAction action)
+{
+ gchar *actions_key = NULL;
+ gchar *buttons_key = NULL;
+
+ g_return_if_fail (GIMP_IS_MODIFIERS_MANAGER (manager));
+ g_return_if_fail (GDK_IS_DEVICE (device));
+
+ gimp_modifiers_manager_get_keys (device, button, modifiers,
+ &actions_key, &buttons_key);
+
+ gimp_modifiers_manager_initialize (manager, device, button);
+
+ if (action == GIMP_MODIFIER_ACTION_NONE)
+ {
+ g_hash_table_remove (manager->p->actions, actions_key);
+ g_free (actions_key);
+ }
+ else
+ {
+ GimpModifierMapping *mapping;
+
+ mapping = g_slice_new (GimpModifierMapping);
+ mapping->modifiers = modifiers;
+ mapping->mod_action = action;
+ g_hash_table_insert (manager->p->actions, actions_key,
+ mapping);
+ }
+}
+
+void
+gimp_modifiers_manager_remove (GimpModifiersManager *manager,
+ GdkDevice *device,
+ guint button,
+ GdkModifierType modifiers)
+{
+ gimp_modifiers_manager_set (manager, device, button, modifiers,
+ GIMP_MODIFIER_ACTION_NONE);
+}
+
+/* Private functions */
+
+static void
+gimp_modifiers_manager_free_mapping (GimpModifierMapping *mapping)
+{
+ g_slice_free (GimpModifierMapping, mapping);
+}
+
+static void
+gimp_modifiers_manager_get_keys (GdkDevice *device,
+ guint button,
+ GdkModifierType modifiers,
+ gchar **actions_key,
+ gchar **buttons_key)
+{
+ const gchar *vendor_id;
+ const gchar *product_id;
+
+ g_return_if_fail (GDK_IS_DEVICE (device) || device == NULL);
+
+ vendor_id = device ? gdk_device_get_vendor_id (device) : NULL;
+ product_id = device ? gdk_device_get_product_id (device) : NULL;
+ modifiers = modifiers & gimp_get_all_modifiers_mask ();
+
+ if (actions_key)
+ *actions_key = g_strdup_printf ("%s:%s-%d-%d",
+ vendor_id ? vendor_id : "(no-vendor-id)",
+ product_id ? product_id : "(no-product-id)",
+ button, modifiers);
+ if (buttons_key)
+ *buttons_key = g_strdup_printf ("%s:%s-%d",
+ vendor_id ? vendor_id : "(no-vendor-id)",
+ product_id ? product_id : "(no-product-id)",
+ button);
+}
+
+static void
+gimp_modifiers_manager_initialize (GimpModifiersManager *manager,
+ GdkDevice *device,
+ guint button)
+{
+ gchar *buttons_key = NULL;
+
+ g_return_if_fail (GIMP_IS_MODIFIERS_MANAGER (manager));
+ g_return_if_fail (GDK_IS_DEVICE (device));
+
+ gimp_modifiers_manager_get_keys (device, button, 0,
+ NULL, &buttons_key);
+
+ /* Add the button to buttons whether or not we insert or remove an
+ * action. It mostly mean that we "touched" the settings for a given
+ * device/button. So it's a per-button initialized flag.
+ */
+ if (g_list_find_custom (manager->p->buttons, buttons_key, (GCompareFunc) g_strcmp0))
+ {
+ g_free (buttons_key);
+ }
+ else
+ {
+ gchar *actions_key = NULL;
+ GimpModifierMapping *mapping;
+
+ manager->p->buttons = g_list_prepend (manager->p->buttons, buttons_key);
+ if (button == 2)
+ {
+ /* The default mapping for second (middle) button which had no explicit configuration. */
+
+ mapping = g_slice_new (GimpModifierMapping);
+ mapping->modifiers = GDK_MOD1_MASK;
+ mapping->mod_action = GIMP_MODIFIER_ACTION_LAYER_PICKING;
+ gimp_modifiers_manager_get_keys (device, 2, mapping->modifiers,
+ &actions_key, NULL);
+ g_hash_table_insert (manager->p->actions, actions_key, mapping);
+
+ mapping = g_slice_new (GimpModifierMapping);
+ mapping->modifiers = gimp_get_extend_selection_mask () | GDK_CONTROL_MASK;
+ mapping->mod_action = GIMP_MODIFIER_ACTION_STEP_ROTATING;
+ gimp_modifiers_manager_get_keys (device, 2, mapping->modifiers,
+ &actions_key, NULL);
+ g_hash_table_insert (manager->p->actions, actions_key, mapping);
+
+ mapping = g_slice_new (GimpModifierMapping);
+ mapping->modifiers = gimp_get_extend_selection_mask ();
+ mapping->mod_action = GIMP_MODIFIER_ACTION_ROTATING;
+ gimp_modifiers_manager_get_keys (device, 2, mapping->modifiers,
+ &actions_key, NULL);
+ g_hash_table_insert (manager->p->actions, actions_key, mapping);
+
+ mapping = g_slice_new (GimpModifierMapping);
+ mapping->modifiers = gimp_get_toggle_behavior_mask ();
+ mapping->mod_action = GIMP_MODIFIER_ACTION_ZOOMING;
+ gimp_modifiers_manager_get_keys (device, 2, mapping->modifiers,
+ &actions_key, NULL);
+ g_hash_table_insert (manager->p->actions, actions_key, mapping);
+
+ mapping = g_slice_new (GimpModifierMapping);
+ mapping->modifiers = 0;
+ mapping->mod_action = GIMP_MODIFIER_ACTION_PANNING;
+ gimp_modifiers_manager_get_keys (device, 2, mapping->modifiers,
+ &actions_key, NULL);
+ g_hash_table_insert (manager->p->actions, actions_key, mapping);
+ }
+ else if (button == 3)
+ {
+ /* The default mapping for third button which had no explicit configuration. */
+
+ mapping = g_slice_new (GimpModifierMapping);
+ mapping->modifiers = GDK_MOD1_MASK;
+ mapping->mod_action = GIMP_MODIFIER_ACTION_BRUSH_SIZE;
+ gimp_modifiers_manager_get_keys (device, 3, mapping->modifiers,
+ &actions_key, NULL);
+ g_hash_table_insert (manager->p->actions, actions_key, mapping);
+
+ mapping = g_slice_new (GimpModifierMapping);
+ mapping->modifiers = 0;
+ mapping->mod_action = GIMP_MODIFIER_ACTION_MENU;
+ gimp_modifiers_manager_get_keys (device, 3, mapping->modifiers,
+ &actions_key, NULL);
+ g_hash_table_insert (manager->p->actions, actions_key, mapping);
+ }
+ }
+}
diff --git a/app/display/gimpmodifiersmanager.h b/app/display/gimpmodifiersmanager.h
new file mode 100644
index 0000000000..420efd66f9
--- /dev/null
+++ b/app/display/gimpmodifiersmanager.h
@@ -0,0 +1,80 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpmodifiersmanager.h
+ * Copyright (C) 2022 Jehan
+ *
+ * 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/>.
+ */
+
+#ifndef __GIMP_MODIFIERS_MANAGER_H__
+#define __GIMP_MODIFIERS_MANAGER_H__
+
+
+#define GIMP_TYPE_MODIFIERS_MANAGER (gimp_modifiers_manager_get_type ())
+#define GIMP_MODIFIERS_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
GIMP_TYPE_MODIFIERS_MANAGER, GimpModifiersManager))
+#define GIMP_MODIFIERS_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
GIMP_TYPE_MODIFIERS_MANAGER, GimpModifiersManagerClass))
+#define GIMP_IS_MODIFIERS_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
GIMP_TYPE_MODIFIERS_MANAGER))
+#define GIMP_IS_MODIFIERS_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),
GIMP_TYPE_MODIFIERS_MANAGER))
+#define GIMP_MODIFIERS_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
GIMP_TYPE_MODIFIERS_MANAGER, GimpModifiersManagerClass))
+
+
+typedef struct _GimpModifiersManagerPrivate GimpModifiersManagerPrivate;
+typedef struct _GimpModifiersManagerClass GimpModifiersManagerClass;
+
+/**
+ * GimpModifiersManager:
+ *
+ * Contains modifiers configuration for canvas interaction.
+ */
+struct _GimpModifiersManager
+{
+ GObject parent_instance;
+
+ GimpModifiersManagerPrivate *p;
+};
+
+struct _GimpModifiersManagerClass
+{
+ GObjectClass parent_class;
+};
+
+
+GType gimp_modifiers_manager_get_type (void) G_GNUC_CONST;
+
+GimpModifiersManager * gimp_modifiers_manager_new (void);
+
+GimpModifierAction gimp_modifiers_manager_get_action (GimpModifiersManager *manager,
+ GdkDevice *device,
+ guint button,
+ GdkModifierType modifiers);
+
+/* Protected functions: only use them from GimpModifiersEditor */
+
+GList * gimp_modifiers_manager_get_modifiers (GimpModifiersManager *manager,
+ GdkDevice *device,
+ guint button);
+
+void gimp_modifiers_manager_set (GimpModifiersManager *manager,
+ GdkDevice *device,
+ guint button,
+ GdkModifierType modifiers,
+ GimpModifierAction action);
+void gimp_modifiers_manager_remove (GimpModifiersManager *manager,
+ GdkDevice *device,
+ guint button,
+ GdkModifierType modifiers);
+
+
+#endif /* __GIMP_MODIFIERS_MANAGER_H__ */
diff --git a/app/display/meson.build b/app/display/meson.build
index d15953289f..43db1fd13e 100644
--- a/app/display/meson.build
+++ b/app/display/meson.build
@@ -82,6 +82,7 @@ libappdisplay_sources = [
'gimpdisplayshell-transform.c',
'gimpdisplayshell-utils.c',
'gimpdisplayshell.c',
+ 'gimpmodifiersmanager.c',
'gimpimagewindow.c',
'gimpmotionbuffer.c',
'gimpmultiwindowstrategy.c',
diff --git a/app/gui/Makefile.am b/app/gui/Makefile.am
index 579cd63bc0..c43208d2aa 100644
--- a/app/gui/Makefile.am
+++ b/app/gui/Makefile.am
@@ -46,6 +46,8 @@ libappgui_a_sources = \
gui-types.h \
icon-themes.c \
icon-themes.h \
+ modifiers.c \
+ modifiers.h \
session.c \
session.h \
splash.c \
diff --git a/app/gui/gui.c b/app/gui/gui.c
index 530504b8ef..647ea73e3f 100644
--- a/app/gui/gui.c
+++ b/app/gui/gui.c
@@ -79,6 +79,7 @@
#include "gui-unique.h"
#include "gui-vtable.h"
#include "icon-themes.h"
+#include "modifiers.h"
#include "session.h"
#include "splash.h"
#include "themes.h"
@@ -530,6 +531,7 @@ gui_restore_callback (Gimp *gimp,
gimp_devices_init (gimp);
gimp_controllers_init (gimp);
+ modifiers_init (gimp);
session_init (gimp);
g_type_class_unref (g_type_class_ref (GIMP_TYPE_COLOR_SELECTOR_PALETTE));
@@ -622,6 +624,7 @@ gui_restore_after_callback (Gimp *gimp,
gimp_devices_restore (gimp);
gimp_controllers_restore (gimp, image_ui_manager);
+ modifiers_restore (gimp);
if (status_callback == splash_update)
splash_destroy ();
@@ -766,6 +769,8 @@ gui_exit_callback (Gimp *gimp,
if (TRUE /* gui_config->save_controllers */)
gimp_controllers_save (gimp);
+ modifiers_save (gimp, FALSE);
+
g_signal_handlers_disconnect_by_func (gimp_get_user_context (gimp),
gui_display_changed,
gimp);
@@ -819,6 +824,7 @@ gui_exit_after_callback (Gimp *gimp,
gimp_render_exit (gimp);
gimp_controllers_exit (gimp);
+ modifiers_exit (gimp);
gimp_devices_exit (gimp);
dialogs_exit (gimp);
themes_exit (gimp);
diff --git a/app/gui/meson.build b/app/gui/meson.build
index d128d3101f..2883a1f350 100644
--- a/app/gui/meson.build
+++ b/app/gui/meson.build
@@ -14,6 +14,7 @@ libappgui_sources = [
'gui-vtable.c',
'gui.c',
'icon-themes.c',
+ 'modifiers.c',
'session.c',
'splash.c',
'themes.c',
diff --git a/app/gui/modifiers.c b/app/gui/modifiers.c
new file mode 100644
index 0000000000..29e8a54937
--- /dev/null
+++ b/app/gui/modifiers.c
@@ -0,0 +1,218 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * modifiers.c
+ * Copyright (C) 2022 Jehan
+ *
+ * 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 <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "gui-types.h"
+
+#include "config/gimpconfig-file.h"
+#include "config/gimpguiconfig.h"
+
+#include "core/gimp.h"
+#include "core/gimperror.h"
+
+#include "display/gimpmodifiersmanager.h"
+
+#include "widgets/gimpwidgets-utils.h"
+
+#include "dialogs/dialogs.h"
+
+#include "modifiers.h"
+#include "gimp-log.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ MODIFIERS_INFO = 1,
+ HIDE_DOCKS,
+ SINGLE_WINDOW_MODE,
+ SHOW_TABS,
+ TABS_POSITION,
+ LAST_TIP_SHOWN
+};
+
+
+static GFile * modifiers_file (Gimp *gimp);
+
+
+/* private variables */
+
+static gboolean modifiersrc_deleted = FALSE;
+
+
+/* public functions */
+
+void
+modifiers_init (Gimp *gimp)
+{
+ GimpDisplayConfig *display_config;
+ GFile *file;
+ GimpModifiersManager *manager = NULL;
+ GError *error = NULL;
+
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+
+ display_config = GIMP_DISPLAY_CONFIG (gimp->config);
+ if (display_config->modifiers_manager != NULL)
+ return;
+
+ manager = gimp_modifiers_manager_new ();
+ g_object_set (display_config, "modifiers-manager", manager, NULL);
+ g_object_unref (manager);
+
+ file = modifiers_file (gimp);
+
+ if (gimp->be_verbose)
+ g_print ("Parsing '%s'\n", gimp_file_get_utf8_name (file));
+
+ gimp_config_deserialize_file (GIMP_CONFIG (manager), file, NULL, &error);
+
+ if (error)
+ {
+ /* File not existing is considered a normal event, not an error.
+ * It can happen for instance the first time you run GIMP. When
+ * this happens, we ignore the error. The GimpModifiersManager
+ * object will simply use default modifiers.
+ */
+ if (error->domain != GIMP_CONFIG_ERROR ||
+ error->code != GIMP_CONFIG_ERROR_OPEN_ENOENT)
+ {
+ gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, error->message);
+ gimp_config_file_backup_on_error (file, "modifiersrc", NULL);
+ }
+
+ g_clear_error (&error);
+ }
+
+ g_object_unref (file);
+}
+
+void
+modifiers_exit (Gimp *gimp)
+{
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+}
+
+void
+modifiers_restore (Gimp *gimp)
+{
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+}
+
+void
+modifiers_save (Gimp *gimp,
+ gboolean always_save)
+{
+ GimpDisplayConfig *display_config;
+ GFile *file;
+ GimpModifiersManager *manager = NULL;
+ GError *error = NULL;
+
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+
+ if (modifiersrc_deleted && ! always_save)
+ return;
+
+ display_config = GIMP_DISPLAY_CONFIG (gimp->config);
+ g_return_if_fail (GIMP_IS_DISPLAY_CONFIG (display_config));
+
+ manager = GIMP_MODIFIERS_MANAGER (display_config->modifiers_manager);
+ g_return_if_fail (manager != NULL);
+ g_return_if_fail (GIMP_IS_MODIFIERS_MANAGER (manager));
+ file = modifiers_file (gimp);
+
+ if (gimp->be_verbose)
+ g_print ("Writing '%s'\n", gimp_file_get_utf8_name (file));
+
+ gimp_config_serialize_to_file (GIMP_CONFIG (manager), file,
+ "GIMP modifiersrc\n\n"
+ "This file stores modifiers configuration. "
+ "You are not supposed to edit it manually, "
+ "but of course you can do. The modifiersrc "
+ "will be entirely rewritten every time you "
+ "quit GIMP. If this file isn't found, "
+ "defaults are used.",
+ NULL, NULL, &error);
+ if (error != NULL)
+ {
+ gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, error->message);
+ g_clear_error (&error);
+ }
+
+ g_object_unref (file);
+
+ modifiersrc_deleted = FALSE;
+}
+
+gboolean
+modifiers_clear (Gimp *gimp,
+ GError **error)
+{
+ GFile *file;
+ GError *my_error = NULL;
+ gboolean success = TRUE;
+
+ g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ file = modifiers_file (gimp);
+
+ if (! g_file_delete (file, NULL, &my_error) &&
+ my_error->code != G_IO_ERROR_NOT_FOUND)
+ {
+ success = FALSE;
+
+ g_set_error (error, GIMP_ERROR, GIMP_FAILED,
+ _("Deleting \"%s\" failed: %s"),
+ gimp_file_get_utf8_name (file), my_error->message);
+ }
+ else
+ {
+ modifiersrc_deleted = TRUE;
+ }
+
+ g_clear_error (&my_error);
+ g_object_unref (file);
+
+ return success;
+}
+
+static GFile *
+modifiers_file (Gimp *gimp)
+{
+ const gchar *basename;
+ GFile *file;
+
+ basename = g_getenv ("GIMP_TESTING_MODIFIERSRC_NAME");
+ if (! basename)
+ basename = "modifiersrc";
+
+ file = gimp_directory_file (basename, NULL);
+
+ return file;
+}
diff --git a/app/gui/modifiers.h b/app/gui/modifiers.h
new file mode 100644
index 0000000000..72a1d9c2e0
--- /dev/null
+++ b/app/gui/modifiers.h
@@ -0,0 +1,36 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * modifiers.h
+ * Copyright (C) 2022 Jehan
+ *
+ * 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/>.
+ */
+
+#ifndef __MODIFIERS_H__
+#define __MODIFIERS_H__
+
+
+void modifiers_init (Gimp *gimp);
+void modifiers_exit (Gimp *gimp);
+
+void modifiers_restore (Gimp *gimp);
+void modifiers_save (Gimp *gimp,
+ gboolean always_save);
+
+gboolean modifiers_clear (Gimp *gimp,
+ GError **error);
+
+
+#endif /* __MODIFIERS_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]