/* GIMP - The GNU Image Manipulation Program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * gimpmodifierseditor.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 . */ #include "config.h" #include #include #include "libgimpbase/gimpbase.h" #include "libgimpwidgets/gimpwidgets.h" #include "widgets-types.h" #include "core/gimp.h" #include "display/display-types.h" #include "display/gimpmodifiersmanager.h" #include "gimpaction.h" #include "gimpactionview.h" #include "gimpactioneditor.h" #include "gimphelp-ids.h" #include "gimpmodifierseditor.h" #include "gimpshortcutbutton.h" #include "gimpuimanager.h" #include "gimpwidgets-utils.h" #include "gimp-intl.h" enum { PROP_0, PROP_MANAGER, PROP_GIMP, }; struct _GimpModifiersEditorPrivate { GdkDevice *device; guint button; GtkWidget *header; GtkWidget *warning; GtkWidget *select_button; GtkWidget *stack; GtkWidget *current_settings; GtkWidget *plus_button; GtkSizeGroup *mod_size_group; GtkSizeGroup *action_size_group; GtkSizeGroup *action_action_size_group; GtkSizeGroup *minus_size_group; GimpModifiersManager *manager; Gimp *gimp; GHashTable *rows; GtkTreeSelection *action_selection; }; static void gimp_modifiers_editor_constructed (GObject *object); static void gimp_modifiers_editor_finalize (GObject *object); static void gimp_modifiers_editor_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void gimp_modifiers_editor_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static gboolean gimp_modifiers_editor_button_press_event (GtkWidget *editor, GdkEventButton *event, gpointer user_data); static void gimp_modifiers_editor_plus_button_clicked (GtkButton *button, GimpModifiersEditor *editor); static void gimp_modifiers_editor_minus_button_clicked (GtkButton *minus_button, GimpModifiersEditor *editor); static void gimp_modifiers_editor_notify_accelerator (GtkWidget *widget, const GParamSpec *pspec, GimpModifiersEditor *editor); static void gimp_modifiers_editor_search_clicked (GtkWidget *button, GimpModifiersEditor *editor); static void gimp_modifiers_editor_show_settings (GimpModifiersEditor *editor, GdkDevice *device, guint button); static void gimp_modifiers_editor_add_mapping (GimpModifiersEditor *editor, GdkModifierType modifiers, GimpModifierAction mod_action, const gchar *action_desc); static void gimp_controller_modifiers_action_activated (GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *column, GtkWidget *edit_dialog); static void gimp_modifiers_editor_search_response (GtkWidget *dialog, gint response_id, GimpModifiersEditor *editor); static gchar * gimp_modifiers_editor_make_hash_key (GdkDevice *device, guint button, GdkModifierType modifiers); static void gimp_modifiers_editor_update_rows (GimpModifiersEditor *editor, GdkModifierType modifiers, GtkWidget *box_child); static gboolean gimp_modifiers_editor_search_row (gpointer key, gpointer value, gpointer user_data); G_DEFINE_TYPE_WITH_PRIVATE (GimpModifiersEditor, gimp_modifiers_editor, GIMP_TYPE_FRAME) #define parent_class gimp_modifiers_editor_parent_class static void gimp_modifiers_editor_class_init (GimpModifiersEditorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = gimp_modifiers_editor_constructed; object_class->finalize = gimp_modifiers_editor_finalize; object_class->get_property = gimp_modifiers_editor_get_property; object_class->set_property = gimp_modifiers_editor_set_property; g_object_class_install_property (object_class, PROP_MANAGER, g_param_spec_object ("manager", NULL, NULL, GIMP_TYPE_MODIFIERS_MANAGER, GIMP_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_GIMP, g_param_spec_object ("gimp", NULL, NULL, GIMP_TYPE_GIMP, GIMP_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); } static void gimp_modifiers_editor_init (GimpModifiersEditor *editor) { GtkWidget *grid; GtkWidget *hbox; GtkWidget *hint; GtkWidget *image; gchar *text; editor->priv = gimp_modifiers_editor_get_instance_private (editor); editor->priv->device = NULL; editor->priv->plus_button = NULL; editor->priv->current_settings = NULL; editor->priv->mod_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); editor->priv->action_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); editor->priv->action_action_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); editor->priv->minus_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); editor->priv->rows = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); /* Setup the title. */ gtk_frame_set_label_align (GTK_FRAME (editor), 0.5, 0.5); grid = gtk_grid_new (); gtk_grid_set_column_spacing (GTK_GRID (grid), 2); gtk_grid_set_row_spacing (GTK_GRID (grid), 4); editor->priv->header = gtk_label_new (NULL); gtk_grid_attach (GTK_GRID (grid), editor->priv->header, 0, 0, 2, 1); gtk_widget_show (editor->priv->header); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2); gtk_widget_show (hbox); hint = gtk_label_new (NULL); text = g_strdup_printf ("%s", _("Click here to set a button's modifiers")); gtk_label_set_markup (GTK_LABEL (hint), text); g_free (text); gtk_box_pack_start (GTK_BOX (hbox), hint, TRUE, TRUE, 2); gtk_widget_show (hint); image = gtk_image_new_from_icon_name ("gimp-cursor", GTK_ICON_SIZE_LARGE_TOOLBAR); gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 2); gtk_widget_show (image); editor->priv->warning = gtk_label_new (NULL); text = g_strdup_printf ("%s", _("Modifiers cannot be customized on the primary button.")); gtk_label_set_markup (GTK_LABEL (editor->priv->warning), text); g_free (text); gtk_grid_attach (GTK_GRID (grid), editor->priv->warning, 0, 2, 2, 1); gtk_widget_hide (editor->priv->warning); editor->priv->select_button = gtk_button_new (); gtk_container_add (GTK_CONTAINER (editor->priv->select_button), hbox); gtk_grid_attach (GTK_GRID (grid), editor->priv->select_button, 0, 1, 1, 1); gtk_widget_show (editor->priv->select_button); gtk_frame_set_label_widget (GTK_FRAME (editor), grid); gtk_widget_show (grid); /* Setup the stack. */ editor->priv->stack = gtk_stack_new (); gtk_container_add (GTK_CONTAINER (editor), editor->priv->stack); gtk_widget_show (editor->priv->stack); } static void gimp_modifiers_editor_constructed (GObject *object) { GimpModifiersEditor *editor = GIMP_MODIFIERS_EDITOR (object); G_OBJECT_CLASS (parent_class)->constructed (object); g_signal_connect (editor->priv->select_button, "button-press-event", G_CALLBACK (gimp_modifiers_editor_button_press_event), object); } static void gimp_modifiers_editor_finalize (GObject *object) { GimpModifiersEditor *editor = GIMP_MODIFIERS_EDITOR (object); G_OBJECT_CLASS (parent_class)->finalize (object); g_clear_object (&editor->priv->device); g_object_unref (editor->priv->mod_size_group); g_object_unref (editor->priv->action_size_group); g_object_unref (editor->priv->action_action_size_group); g_object_unref (editor->priv->minus_size_group); g_hash_table_unref (editor->priv->rows); } static void gimp_modifiers_editor_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GimpModifiersEditor *editor = GIMP_MODIFIERS_EDITOR (object); switch (property_id) { case PROP_MANAGER: editor->priv->manager = g_value_get_object (value); break; case PROP_GIMP: editor->priv->gimp = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gimp_modifiers_editor_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GimpModifiersEditor *editor = GIMP_MODIFIERS_EDITOR (object); switch (property_id) { case PROP_MANAGER: g_value_set_object (value, editor->priv->manager); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } /* public functions */ GtkWidget * gimp_modifiers_editor_new (GimpModifiersManager *manager, Gimp *gimp) { GimpModifiersEditor *editor; g_return_val_if_fail (GIMP_IS_MODIFIERS_MANAGER (manager), NULL); editor = g_object_new (GIMP_TYPE_MODIFIERS_EDITOR, "manager", manager, "gimp", gimp, NULL); return GTK_WIDGET (editor); } void gimp_modifiers_editor_clear (GimpModifiersEditor *editor) { gimp_modifiers_manager_clear (editor->priv->manager); g_hash_table_remove_all (editor->priv->rows); gtk_container_foreach (GTK_CONTAINER (editor->priv->stack), (GtkCallback) gtk_widget_destroy, NULL); if (editor->priv->device && GDK_IS_DEVICE (editor->priv->device)) gimp_modifiers_editor_show_settings (editor, editor->priv->device, editor->priv->button); } /* private functions */ static void gimp_modifiers_editor_show_settings (GimpModifiersEditor *editor, GdkDevice *device, guint button) { const gchar *vendor_id; const gchar *product_id; gchar *title; gchar *text; vendor_id = gdk_device_get_vendor_id (device); product_id = gdk_device_get_product_id (device); if (device != editor->priv->device) { g_clear_object (&editor->priv->device); editor->priv->device = g_object_ref (device); } editor->priv->button = button; /* Update header. */ if (gdk_device_get_name (device) != NULL) text = g_strdup_printf (_("Editing modifiers for button %d of %s"), editor->priv->button, gdk_device_get_name (device)); else text = g_strdup_printf (_("Editing modifiers for button %d"), editor->priv->button); title = g_strdup_printf ("%s", text); gtk_label_set_markup (GTK_LABEL (editor->priv->header), title); g_free (title); g_free (text); /* Update modifier settings. */ text = g_strdup_printf ("%s:%s-%d", vendor_id ? vendor_id : "*", product_id ? product_id : "*", button); editor->priv->current_settings = gtk_stack_get_child_by_name (GTK_STACK (editor->priv->stack), text); if (! editor->priv->current_settings) { GtkWidget *plus_button; GList *modifiers; GList *iter; editor->priv->current_settings = gtk_list_box_new (); gtk_stack_add_named (GTK_STACK (editor->priv->stack), editor->priv->current_settings, text); modifiers = gimp_modifiers_manager_get_modifiers (editor->priv->manager, device, editor->priv->button); for (iter = modifiers; iter; iter = iter->next) { GdkModifierType mods = GPOINTER_TO_INT (iter->data); GimpModifierAction action; const gchar *action_desc = NULL; action = gimp_modifiers_manager_get_action (editor->priv->manager, device, editor->priv->button, mods, &action_desc); gimp_modifiers_editor_add_mapping (editor, mods, action, action_desc); } plus_button = gtk_button_new_from_icon_name ("list-add", GTK_ICON_SIZE_LARGE_TOOLBAR); gtk_list_box_insert (GTK_LIST_BOX (editor->priv->current_settings), plus_button, -1); gtk_widget_show (plus_button); g_signal_connect (plus_button, "clicked", G_CALLBACK (gimp_modifiers_editor_plus_button_clicked), editor); g_object_set_data (G_OBJECT (editor->priv->current_settings), "plus-button", plus_button); if (g_list_length (modifiers) == 0) gimp_modifiers_editor_plus_button_clicked (GTK_BUTTON (plus_button), editor); gtk_widget_show (editor->priv->current_settings); g_list_free (modifiers); } gtk_stack_set_visible_child (GTK_STACK (editor->priv->stack), editor->priv->current_settings); g_free (text); } static gboolean gimp_modifiers_editor_button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data) { GimpModifiersEditor *editor = GIMP_MODIFIERS_EDITOR (user_data); GdkDevice *device = gdk_event_get_source_device ((GdkEvent *) event); /* Update warning. */ if (event->button == GDK_BUTTON_PRIMARY) { gtk_widget_show (editor->priv->warning); gimp_widget_blink (editor->priv->warning); } else { gtk_widget_hide (editor->priv->warning); } if (event->button != GDK_BUTTON_PRIMARY && (event->button != editor->priv->button || editor->priv->device == NULL || g_strcmp0 (gdk_device_get_vendor_id (editor->priv->device), gdk_device_get_vendor_id (device)) != 0 || g_strcmp0 (gdk_device_get_product_id (editor->priv->device), gdk_device_get_product_id (device)) != 0)) { gimp_modifiers_editor_show_settings (editor, device, event->button); } return FALSE; } static void gimp_modifiers_editor_add_mapping (GimpModifiersEditor *editor, GdkModifierType modifiers, GimpModifierAction mod_action, const gchar *action_desc) { GtkWidget *box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2); GtkWidget *box_row; GtkWidget *combo_action_box; GtkWidget *combo; GtkWidget *shortcut; GtkWidget *minus_button; GtkWidget *action_button = NULL; GtkWidget *plus_button; plus_button = g_object_get_data (G_OBJECT (editor->priv->current_settings), "plus-button"); shortcut = gimp_shortcut_button_new (NULL); gimp_shortcut_button_accepts_modifier (GIMP_SHORTCUT_BUTTON (shortcut), TRUE, FALSE); gimp_shortcut_button_set_accelerator (GIMP_SHORTCUT_BUTTON (shortcut), NULL, 0, modifiers); gtk_box_pack_start (GTK_BOX (box), shortcut, FALSE, FALSE, 0); gtk_size_group_add_widget (editor->priv->mod_size_group, shortcut); gtk_widget_show (shortcut); combo_action_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 1); gtk_box_pack_start (GTK_BOX (box), combo_action_box, FALSE, FALSE, 0); gtk_size_group_add_widget (editor->priv->action_action_size_group, combo_action_box); gtk_widget_show (combo_action_box); combo = gimp_enum_combo_box_new (GIMP_TYPE_MODIFIER_ACTION); gtk_box_pack_start (GTK_BOX (combo_action_box), combo, FALSE, FALSE, 0); gtk_combo_box_set_active (GTK_COMBO_BOX (combo), mod_action); gtk_size_group_add_widget (editor->priv->action_size_group, combo); gtk_widget_show (combo); if (action_desc) { gchar *action_name = strchr (action_desc, '/'); if (action_name) action_name++; if (action_name && strlen (action_name) > 0) action_button = gtk_button_new_with_label (action_name); } if (action_button == NULL) action_button = gtk_button_new_from_icon_name ("system-search", GTK_ICON_SIZE_SMALL_TOOLBAR); gtk_box_pack_start (GTK_BOX (combo_action_box), action_button, FALSE, FALSE, 0); gtk_widget_set_visible (action_button, mod_action == GIMP_MODIFIER_ACTION_ACTION); minus_button = gtk_button_new_from_icon_name ("list-remove", GTK_ICON_SIZE_SMALL_TOOLBAR); gtk_size_group_add_widget (editor->priv->minus_size_group, minus_button); gtk_box_pack_start (GTK_BOX (box), minus_button, FALSE, FALSE, 0); g_object_set_data (G_OBJECT (minus_button), "shortcut-button", shortcut); g_signal_connect (minus_button, "clicked", G_CALLBACK (gimp_modifiers_editor_minus_button_clicked), editor); gtk_widget_show (minus_button); g_object_set_data (G_OBJECT (shortcut), "shortcut-modifiers", GINT_TO_POINTER (modifiers)); g_object_set_data (G_OBJECT (shortcut), "shortcut-button", shortcut); g_object_set_data (G_OBJECT (shortcut), "shortcut-action", combo); g_object_set_data (G_OBJECT (shortcut), "shortcut-action-action", action_button); g_object_set_data (G_OBJECT (combo), "shortcut-button", shortcut); g_object_set_data (G_OBJECT (combo), "shortcut-action", combo); g_object_set_data (G_OBJECT (combo), "shortcut-action-action", action_button); g_signal_connect (shortcut, "notify::accelerator", G_CALLBACK (gimp_modifiers_editor_notify_accelerator), editor); g_signal_connect (combo, "notify::active", G_CALLBACK (gimp_modifiers_editor_notify_accelerator), editor); g_object_set_data (G_OBJECT (action_button), "shortcut-button", shortcut); g_signal_connect (action_button, "clicked", G_CALLBACK (gimp_modifiers_editor_search_clicked), editor); gtk_list_box_insert (GTK_LIST_BOX (editor->priv->current_settings), box, -1); if (mod_action != GIMP_MODIFIER_ACTION_NONE) gimp_modifiers_editor_update_rows (editor, modifiers, shortcut); if (plus_button) { g_object_ref (plus_button); box_row = gtk_widget_get_parent (GTK_WIDGET (plus_button)); gtk_container_remove (GTK_CONTAINER (box_row), GTK_WIDGET (plus_button)); gtk_container_remove (GTK_CONTAINER (editor->priv->current_settings), box_row); gtk_list_box_insert (GTK_LIST_BOX (editor->priv->current_settings), GTK_WIDGET (plus_button), -1); } gtk_widget_show (box); } static void gimp_modifiers_editor_plus_button_clicked (GtkButton *plus_button, GimpModifiersEditor *editor) { gimp_modifiers_editor_add_mapping (editor, 0, GIMP_MODIFIER_ACTION_NONE, NULL); } static void gimp_modifiers_editor_minus_button_clicked (GtkButton *minus_button, GimpModifiersEditor *editor) { GtkWidget *shortcut = g_object_get_data (G_OBJECT (minus_button), "shortcut-button"); GdkModifierType modifiers; gimp_shortcut_button_get_keys (GIMP_SHORTCUT_BUTTON (shortcut), NULL, &modifiers); gimp_modifiers_manager_remove (editor->priv->manager, editor->priv->device, editor->priv->button, modifiers); /* Always leave at least 1 row. Simply reset it instead. Since * GtkListBox doesn't have an API for length, I get the row at * position 2. If there is none, then there is just 1 row (+ 1 row for * the plus button). */ if (gtk_list_box_get_row_at_index (GTK_LIST_BOX (editor->priv->current_settings), 2) == NULL) { GtkWidget *combo = g_object_get_data (G_OBJECT (minus_button), "shortcut-action"); gtk_combo_box_set_active (GTK_COMBO_BOX (combo), GIMP_MODIFIER_ACTION_NONE); gimp_shortcut_button_set_accelerator (GIMP_SHORTCUT_BUTTON (shortcut), NULL, 0, 0); } else { GtkWidget *box_row; box_row = gtk_widget_get_parent (GTK_WIDGET (minus_button)); box_row = gtk_widget_get_parent (box_row); gimp_modifiers_editor_update_rows (editor, modifiers, NULL); } } static void gimp_modifiers_editor_notify_accelerator (GtkWidget *widget, const GParamSpec *pspec, GimpModifiersEditor *editor) { GtkWidget *shortcut; GtkWidget *combo; GtkWidget *action_button; GimpModifierAction action = GIMP_MODIFIER_ACTION_NONE; GdkModifierType old_modifiers; GdkModifierType modifiers; old_modifiers = (GdkModifierType) g_object_get_data (G_OBJECT (widget), "shortcut-modifiers"); shortcut = g_object_get_data (G_OBJECT (widget), "shortcut-button"); combo = g_object_get_data (G_OBJECT (widget), "shortcut-action"); action_button = g_object_get_data (G_OBJECT (widget), "shortcut-action-action"); gimp_shortcut_button_get_keys (GIMP_SHORTCUT_BUTTON (shortcut), NULL, &modifiers); /* Delete the previous mapping. */ if (old_modifiers != modifiers) gimp_modifiers_manager_remove (editor->priv->manager, editor->priv->device, editor->priv->button, old_modifiers); g_object_set_data (G_OBJECT (shortcut), "shortcut-modifiers", GINT_TO_POINTER (modifiers)); if (gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), (gint *) &action)) { const gchar *action_desc; /* Check if the new mapping was on another row. */ if (GIMP_IS_SHORTCUT_BUTTON (widget) && old_modifiers != modifiers && action != GIMP_MODIFIER_ACTION_NONE) gimp_modifiers_editor_update_rows (editor, modifiers, shortcut); /* Finally set the new mapping. */ action_desc = g_object_get_data (G_OBJECT (action_button), "shortcut-action-desc"); gimp_modifiers_manager_set (editor->priv->manager, editor->priv->device, editor->priv->button, modifiers, action, action_desc); gtk_widget_set_visible (action_button, action == GIMP_MODIFIER_ACTION_ACTION); } } static void gimp_modifiers_editor_search_clicked (GtkWidget *button, GimpModifiersEditor *editor) { gchar *accel_name = NULL; GtkWidget *shortcut; GdkModifierType modifiers; shortcut = g_object_get_data (G_OBJECT (button), "shortcut-button"); gimp_shortcut_button_get_keys (GIMP_SHORTCUT_BUTTON (shortcut), NULL, &modifiers); accel_name = gtk_accelerator_name (0, modifiers); if (accel_name) { GtkWidget *view; GtkWidget *edit_dialog; gchar *title; if (strlen (accel_name) > 0) { if (gdk_device_get_name (editor->priv->device) != NULL) /* TRANSLATORS: first %s is modifier keys, %d is button * number, last %s is an input device (e.g. a mouse) name. */ title = g_strdup_printf (_("Select Action for %s button %d of %s"), accel_name, editor->priv->button, gdk_device_get_name (editor->priv->device)); else /* TRANSLATORS: %s is modifiers key, %d is a button number. */ title = g_strdup_printf (_("Editing modifiers for %s button %d"), accel_name, editor->priv->button); } else { if (gdk_device_get_name (editor->priv->device) != NULL) /* TRANSLATORS: %d is a button number, %s is the device (e.g. a mouse) name. */ title = g_strdup_printf (_("Select Action for button %d of %s"), editor->priv->button, gdk_device_get_name (editor->priv->device)); else /* TRANSLATORS: %d is an input device button number. */ title = g_strdup_printf (_("Editing modifiers for button %d"), editor->priv->button); } edit_dialog = gimp_dialog_new (title, "gimp-modifiers-action-dialog", gtk_widget_get_toplevel (GTK_WIDGET (editor)), GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, gimp_standard_help_func, GIMP_HELP_PREFS_CANVAS_MODIFIERS, _("_Cancel"), GTK_RESPONSE_CANCEL, _("_OK"), GTK_RESPONSE_OK, NULL); g_free (title); /* Default height is very crappy because of the scrollbar so we * end up to resize manually each time. Let's have a minimum * height. */ gtk_window_set_default_size (GTK_WINDOW (edit_dialog), -1, 400); gimp_dialog_set_alternative_button_order (GTK_DIALOG (edit_dialog), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1); g_object_set_data (G_OBJECT (edit_dialog), "shortcut-button", shortcut); g_object_set_data (G_OBJECT (edit_dialog), "shortcut-action-action", button); g_signal_connect (edit_dialog, "response", G_CALLBACK (gimp_modifiers_editor_search_response), editor); view = gimp_action_editor_new (editor->priv->gimp, NULL, FALSE); gtk_container_set_border_width (GTK_CONTAINER (view), 12); gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (edit_dialog))), view, TRUE, TRUE, 0); gtk_widget_show (view); g_signal_connect_object (GIMP_ACTION_EDITOR (view)->view, "row-activated", G_CALLBACK (gimp_controller_modifiers_action_activated), edit_dialog, 0); g_set_weak_pointer (&editor->priv->action_selection, gtk_tree_view_get_selection (GTK_TREE_VIEW (GIMP_ACTION_EDITOR (view)->view))); gtk_widget_show (edit_dialog); g_free (accel_name); } } static void gimp_controller_modifiers_action_activated (GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *column, GtkWidget *edit_dialog) { gtk_dialog_response (GTK_DIALOG (edit_dialog), GTK_RESPONSE_OK); } static void gimp_modifiers_editor_search_response (GtkWidget *dialog, gint response_id, GimpModifiersEditor *editor) { if (response_id == GTK_RESPONSE_OK) { GtkTreeModel *model; GtkTreeIter iter; gchar *icon_name = NULL; GimpAction *action = NULL; if (gtk_tree_selection_get_selected (editor->priv->action_selection, &model, &iter)) gtk_tree_model_get (model, &iter, GIMP_ACTION_VIEW_COLUMN_ACTION, &action, GIMP_ACTION_VIEW_COLUMN_ICON_NAME, &icon_name, -1); if (action) { GimpActionGroup *group; group = gimp_action_get_group (action); if (group) { GtkWidget *action_button; GtkWidget *shortcut; GtkWidget *label; gchar *action_desc; GdkModifierType modifiers; shortcut = g_object_get_data (G_OBJECT (dialog), "shortcut-button"); gimp_shortcut_button_get_keys (GIMP_SHORTCUT_BUTTON (shortcut), NULL, &modifiers); action_button = g_object_get_data (G_OBJECT (dialog), "shortcut-action-action"); action_desc = g_strdup (gimp_action_get_name (action)); g_object_set_data_full (G_OBJECT (action_button), "shortcut-action-desc", action_desc, g_free); gimp_modifiers_manager_set (editor->priv->manager, editor->priv->device, editor->priv->button, modifiers, GIMP_MODIFIER_ACTION_ACTION, action_desc); /* Change the button label. */ gtk_container_foreach (GTK_CONTAINER (action_button), (GtkCallback) gtk_widget_destroy, NULL); label = gtk_label_new (gimp_action_get_name (action)); gtk_container_add (GTK_CONTAINER (action_button), label); gtk_widget_show (label); } } g_free (icon_name); g_clear_object (&action); } gtk_widget_destroy (dialog); } static gchar * gimp_modifiers_editor_make_hash_key (GdkDevice *device, guint button, GdkModifierType modifiers) { const gchar *vendor_id; const gchar *product_id; g_return_val_if_fail (GDK_IS_DEVICE (device) || device == NULL, 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 (); return g_strdup_printf ("%s:%s-%d-%d", vendor_id ? vendor_id : "(no-vendor-id)", product_id ? product_id : "(no-product-id)", button, modifiers); } static void gimp_modifiers_editor_update_rows (GimpModifiersEditor *editor, GdkModifierType modifiers, GtkWidget *box_child) { GtkWidget *box_row; gchar *hash_key; hash_key = gimp_modifiers_editor_make_hash_key (editor->priv->device, editor->priv->button, modifiers); if ((box_row = g_hash_table_lookup (editor->priv->rows, hash_key)) != NULL) { gimp_modifiers_manager_remove (editor->priv->manager, editor->priv->device, editor->priv->button, modifiers); gtk_container_remove (GTK_CONTAINER (editor->priv->current_settings), box_row); } if (box_child) { box_row = box_child; while (box_row && ! GTK_IS_LIST_BOX_ROW (box_row)) box_row = gtk_widget_get_parent (box_row); g_return_if_fail (box_row != NULL && GTK_IS_LIST_BOX_ROW (box_row)); g_hash_table_foreach_remove (editor->priv->rows, gimp_modifiers_editor_search_row, box_row); g_hash_table_insert (editor->priv->rows, hash_key, box_row); } else { g_free (hash_key); } } static gboolean gimp_modifiers_editor_search_row (gpointer key, gpointer value, gpointer user_data) { return (value == user_data); }