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.
This commit is contained in:
Jehan 2022-07-26 19:21:56 +02:00
parent 924dea3a35
commit 91b30145cb
14 changed files with 1016 additions and 0 deletions

View file

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

View file

@ -71,6 +71,8 @@ struct _GimpDisplayConfig
GimpSpaceBarAction space_bar_action;
GimpZoomQuality zoom_quality;
gboolean use_event_history;
GObject *modifiers_manager;
};
struct _GimpDisplayConfigClass

View file

@ -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 \

View file

@ -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 */

View file

@ -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
*/

View file

@ -48,5 +48,7 @@ typedef struct _GimpToolWidgetGroup GimpToolWidgetGroup;
typedef struct _GimpDisplayXfer GimpDisplayXfer;
typedef struct _Selection Selection;
typedef struct _GimpModifiersManager GimpModifiersManager;
#endif /* __DISPLAY_TYPES_H__ */

View file

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

View file

@ -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__ */

View file

@ -82,6 +82,7 @@ libappdisplay_sources = [
'gimpdisplayshell-transform.c',
'gimpdisplayshell-utils.c',
'gimpdisplayshell.c',
'gimpmodifiersmanager.c',
'gimpimagewindow.c',
'gimpmotionbuffer.c',
'gimpmultiwindowstrategy.c',

View file

@ -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 \

View file

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

View file

@ -14,6 +14,7 @@ libappgui_sources = [
'gui-vtable.c',
'gui.c',
'icon-themes.c',
'modifiers.c',
'session.c',
'splash.c',
'themes.c',

218
app/gui/modifiers.c Normal file
View file

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

36
app/gui/modifiers.h Normal file
View file

@ -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__ */