/* PIKA - Photo and Image Kooker Application * a rebranding of The GNU Image Manipulation Program (created with heckimp) * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio * * Original copyright, applying to most contents (license remains unchanged): * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * pikamodifierseditor.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 "libpikabase/pikabase.h" #include "libpikawidgets/pikawidgets.h" #include "widgets-types.h" #include "core/pika.h" #include "display/display-types.h" #include "display/pikamodifiersmanager.h" #include "pikaaction.h" #include "pikaactionview.h" #include "pikaactioneditor.h" #include "pikahelp-ids.h" #include "pikamodifierseditor.h" #include "pikashortcutbutton.h" #include "pikauimanager.h" #include "pikawidgets-utils.h" #include "pika-intl.h" enum { PROP_0, PROP_MANAGER, PROP_PIKA, }; struct _PikaModifiersEditorPrivate { 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; PikaModifiersManager *manager; Pika *pika; GHashTable *rows; GtkTreeSelection *action_selection; }; static void pika_modifiers_editor_constructed (GObject *object); static void pika_modifiers_editor_finalize (GObject *object); static void pika_modifiers_editor_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void pika_modifiers_editor_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static gboolean pika_modifiers_editor_button_press_event (GtkWidget *editor, GdkEventButton *event, gpointer user_data); static void pika_modifiers_editor_plus_button_clicked (GtkButton *button, PikaModifiersEditor *editor); static void pika_modifiers_editor_minus_button_clicked (GtkButton *minus_button, PikaModifiersEditor *editor); static void pika_modifiers_editor_notify_accelerator (GtkWidget *widget, const GParamSpec *pspec, PikaModifiersEditor *editor); static void pika_modifiers_editor_search_clicked (GtkWidget *button, PikaModifiersEditor *editor); static void pika_modifiers_editor_show_settings (PikaModifiersEditor *editor, GdkDevice *device, guint button); static void pika_modifiers_editor_add_mapping (PikaModifiersEditor *editor, GdkModifierType modifiers, PikaModifierAction mod_action, const gchar *action_desc); static void pika_controller_modifiers_action_activated (GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *column, GtkWidget *edit_dialog); static void pika_modifiers_editor_search_response (GtkWidget *dialog, gint response_id, PikaModifiersEditor *editor); static gchar * pika_modifiers_editor_make_hash_key (GdkDevice *device, guint button, GdkModifierType modifiers); static void pika_modifiers_editor_update_rows (PikaModifiersEditor *editor, GdkModifierType modifiers, GtkWidget *box_child); static gboolean pika_modifiers_editor_search_row (gpointer key, gpointer value, gpointer user_data); G_DEFINE_TYPE_WITH_PRIVATE (PikaModifiersEditor, pika_modifiers_editor, PIKA_TYPE_FRAME) #define parent_class pika_modifiers_editor_parent_class static void pika_modifiers_editor_class_init (PikaModifiersEditorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = pika_modifiers_editor_constructed; object_class->finalize = pika_modifiers_editor_finalize; object_class->get_property = pika_modifiers_editor_get_property; object_class->set_property = pika_modifiers_editor_set_property; g_object_class_install_property (object_class, PROP_MANAGER, g_param_spec_object ("manager", NULL, NULL, PIKA_TYPE_MODIFIERS_MANAGER, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_PIKA, g_param_spec_object ("pika", NULL, NULL, PIKA_TYPE_PIKA, PIKA_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); } static void pika_modifiers_editor_init (PikaModifiersEditor *editor) { GtkWidget *grid; GtkWidget *hbox; GtkWidget *hint; GtkWidget *image; gchar *text; editor->priv = pika_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 ("pika-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 pika_modifiers_editor_constructed (GObject *object) { PikaModifiersEditor *editor = PIKA_MODIFIERS_EDITOR (object); G_OBJECT_CLASS (parent_class)->constructed (object); g_signal_connect (editor->priv->select_button, "button-press-event", G_CALLBACK (pika_modifiers_editor_button_press_event), object); } static void pika_modifiers_editor_finalize (GObject *object) { PikaModifiersEditor *editor = PIKA_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 pika_modifiers_editor_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { PikaModifiersEditor *editor = PIKA_MODIFIERS_EDITOR (object); switch (property_id) { case PROP_MANAGER: editor->priv->manager = g_value_get_object (value); break; case PROP_PIKA: editor->priv->pika = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pika_modifiers_editor_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { PikaModifiersEditor *editor = PIKA_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 * pika_modifiers_editor_new (PikaModifiersManager *manager, Pika *pika) { PikaModifiersEditor *editor; g_return_val_if_fail (PIKA_IS_MODIFIERS_MANAGER (manager), NULL); editor = g_object_new (PIKA_TYPE_MODIFIERS_EDITOR, "manager", manager, "pika", pika, NULL); return GTK_WIDGET (editor); } void pika_modifiers_editor_clear (PikaModifiersEditor *editor) { pika_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); pika_modifiers_editor_show_settings (editor, editor->priv->device, editor->priv->button); } /* private functions */ static void pika_modifiers_editor_show_settings (PikaModifiersEditor *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 = pika_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); PikaModifierAction action; const gchar *action_desc = NULL; action = pika_modifiers_manager_get_action (editor->priv->manager, device, editor->priv->button, mods, &action_desc); pika_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 (pika_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) pika_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 pika_modifiers_editor_button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data) { PikaModifiersEditor *editor = PIKA_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); pika_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)) { pika_modifiers_editor_show_settings (editor, device, event->button); } return FALSE; } static void pika_modifiers_editor_add_mapping (PikaModifiersEditor *editor, GdkModifierType modifiers, PikaModifierAction 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 = pika_shortcut_button_new (NULL); pika_shortcut_button_accepts_modifier (PIKA_SHORTCUT_BUTTON (shortcut), TRUE, FALSE); pika_shortcut_button_set_accelerator (PIKA_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 = pika_enum_combo_box_new (PIKA_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 == PIKA_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 (pika_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 (pika_modifiers_editor_notify_accelerator), editor); g_signal_connect (combo, "notify::active", G_CALLBACK (pika_modifiers_editor_notify_accelerator), editor); g_object_set_data (G_OBJECT (action_button), "shortcut-button", shortcut); g_signal_connect (action_button, "clicked", G_CALLBACK (pika_modifiers_editor_search_clicked), editor); gtk_list_box_insert (GTK_LIST_BOX (editor->priv->current_settings), box, -1); if (mod_action != PIKA_MODIFIER_ACTION_NONE) pika_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 pika_modifiers_editor_plus_button_clicked (GtkButton *plus_button, PikaModifiersEditor *editor) { pika_modifiers_editor_add_mapping (editor, 0, PIKA_MODIFIER_ACTION_NONE, NULL); } static void pika_modifiers_editor_minus_button_clicked (GtkButton *minus_button, PikaModifiersEditor *editor) { GtkWidget *shortcut = g_object_get_data (G_OBJECT (minus_button), "shortcut-button"); GdkModifierType modifiers; pika_shortcut_button_get_keys (PIKA_SHORTCUT_BUTTON (shortcut), NULL, &modifiers); pika_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), PIKA_MODIFIER_ACTION_NONE); pika_shortcut_button_set_accelerator (PIKA_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); pika_modifiers_editor_update_rows (editor, modifiers, NULL); } } static void pika_modifiers_editor_notify_accelerator (GtkWidget *widget, const GParamSpec *pspec, PikaModifiersEditor *editor) { GtkWidget *shortcut; GtkWidget *combo; GtkWidget *action_button; PikaModifierAction action = PIKA_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"); pika_shortcut_button_get_keys (PIKA_SHORTCUT_BUTTON (shortcut), NULL, &modifiers); /* Delete the previous mapping. */ if (old_modifiers != modifiers) pika_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 (pika_int_combo_box_get_active (PIKA_INT_COMBO_BOX (combo), (gint *) &action)) { const gchar *action_desc; /* Check if the new mapping was on another row. */ if (PIKA_IS_SHORTCUT_BUTTON (widget) && old_modifiers != modifiers && action != PIKA_MODIFIER_ACTION_NONE) pika_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"); pika_modifiers_manager_set (editor->priv->manager, editor->priv->device, editor->priv->button, modifiers, action, action_desc); gtk_widget_set_visible (action_button, action == PIKA_MODIFIER_ACTION_ACTION); } } static void pika_modifiers_editor_search_clicked (GtkWidget *button, PikaModifiersEditor *editor) { gchar *accel_name = NULL; GtkWidget *shortcut; GdkModifierType modifiers; shortcut = g_object_get_data (G_OBJECT (button), "shortcut-button"); pika_shortcut_button_get_keys (PIKA_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 = pika_dialog_new (title, "pika-modifiers-action-dialog", gtk_widget_get_toplevel (GTK_WIDGET (editor)), GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, pika_standard_help_func, PIKA_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); pika_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 (pika_modifiers_editor_search_response), editor); view = pika_action_editor_new (editor->priv->pika, 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 (PIKA_ACTION_EDITOR (view)->view, "row-activated", G_CALLBACK (pika_controller_modifiers_action_activated), edit_dialog, 0); g_set_weak_pointer (&editor->priv->action_selection, gtk_tree_view_get_selection (GTK_TREE_VIEW (PIKA_ACTION_EDITOR (view)->view))); gtk_widget_show (edit_dialog); g_free (accel_name); } } static void pika_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 pika_modifiers_editor_search_response (GtkWidget *dialog, gint response_id, PikaModifiersEditor *editor) { if (response_id == GTK_RESPONSE_OK) { GtkTreeModel *model; GtkTreeIter iter; gchar *icon_name = NULL; PikaAction *action = NULL; if (gtk_tree_selection_get_selected (editor->priv->action_selection, &model, &iter)) gtk_tree_model_get (model, &iter, PIKA_ACTION_VIEW_COLUMN_ACTION, &action, PIKA_ACTION_VIEW_COLUMN_ICON_NAME, &icon_name, -1); if (action) { PikaActionGroup *group; group = pika_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"); pika_shortcut_button_get_keys (PIKA_SHORTCUT_BUTTON (shortcut), NULL, &modifiers); action_button = g_object_get_data (G_OBJECT (dialog), "shortcut-action-action"); action_desc = g_strdup (pika_action_get_name (action)); g_object_set_data_full (G_OBJECT (action_button), "shortcut-action-desc", action_desc, g_free); pika_modifiers_manager_set (editor->priv->manager, editor->priv->device, editor->priv->button, modifiers, PIKA_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 (pika_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 * pika_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 & pika_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 pika_modifiers_editor_update_rows (PikaModifiersEditor *editor, GdkModifierType modifiers, GtkWidget *box_child) { GtkWidget *box_row; gchar *hash_key; hash_key = pika_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) { pika_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, pika_modifiers_editor_search_row, box_row); g_hash_table_insert (editor->priv->rows, hash_key, box_row); } else { g_free (hash_key); } } static gboolean pika_modifiers_editor_search_row (gpointer key, gpointer value, gpointer user_data) { return (value == user_data); }