/* cc-keyboard-shortcut-editor.h * * Copyright (C) 2016 Endless, Inc * * 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 2 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 . * * Authors: Georges Basile Stavracas Neto */ #include #include #include "cc-keyboard-shortcut-editor.h" #include "keyboard-shortcuts.h" struct _CcKeyboardShortcutEditor { GtkDialog parent; GtkButton *add_button; GtkButton *cancel_button; GtkButton *change_custom_shortcut_button; GtkEntry *command_entry; GtkGrid *custom_grid; GtkShortcutLabel *custom_shortcut_accel_label; GtkStack *custom_shortcut_stack; GtkBox *edit_box; GtkHeaderBar *headerbar; GtkEntry *name_entry; GtkLabel *new_shortcut_conflict_label; GtkButton *remove_button; GtkButton *replace_button; GtkButton *reset_button; GtkButton *reset_custom_button; GtkButton *set_button; GtkShortcutLabel *shortcut_accel_label; GtkLabel *shortcut_conflict_label; GtkBox *standard_box; GtkStack *stack; GtkLabel *top_info_label; CcShortcutEditorMode mode; CcKeyboardManager *manager; CcKeyboardItem *item; GBinding *reset_item_binding; CcKeyboardItem *collision_item; /* Custom shortcuts */ gboolean system_shortcuts_inhibited; guint grab_idle_id; CcKeyCombo *custom_combo; gboolean custom_is_modifier; gboolean edited : 1; }; static void command_entry_changed_cb (CcKeyboardShortcutEditor *self); static void name_entry_changed_cb (CcKeyboardShortcutEditor *self); static void set_button_clicked_cb (CcKeyboardShortcutEditor *self); G_DEFINE_TYPE (CcKeyboardShortcutEditor, cc_keyboard_shortcut_editor, GTK_TYPE_DIALOG) enum { PROP_0, PROP_KEYBOARD_ITEM, PROP_MANAGER, N_PROPS }; typedef enum { HEADER_MODE_NONE, HEADER_MODE_ADD, HEADER_MODE_SET, HEADER_MODE_REPLACE, HEADER_MODE_CUSTOM_CANCEL, HEADER_MODE_CUSTOM_EDIT } HeaderMode; typedef enum { PAGE_CUSTOM, PAGE_EDIT, PAGE_STANDARD, } ShortcutEditorPage; static GParamSpec *properties [N_PROPS] = { NULL, }; /* Getter and setter for ShortcutEditorPage */ static ShortcutEditorPage get_shortcut_editor_page (CcKeyboardShortcutEditor *self) { if (gtk_stack_get_visible_child (self->stack) == GTK_WIDGET (self->edit_box)) return PAGE_EDIT; if (gtk_stack_get_visible_child (self->stack) == GTK_WIDGET (self->custom_grid)) return PAGE_CUSTOM; return PAGE_STANDARD; } static void set_shortcut_editor_page (CcKeyboardShortcutEditor *self, ShortcutEditorPage page) { switch (page) { case PAGE_CUSTOM: gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->custom_grid)); break; case PAGE_EDIT: gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->edit_box)); break; case PAGE_STANDARD: gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->standard_box)); break; default: g_assert_not_reached (); } gtk_widget_set_visible (GTK_WIDGET (self->top_info_label), page != PAGE_CUSTOM); } static void apply_custom_item_fields (CcKeyboardShortcutEditor *self, CcKeyboardItem *item) { /* Only setup the binding when it was actually edited */ if (self->edited) { CcKeyCombo *combo = self->custom_combo; cc_keyboard_item_disable (item); if (combo->keycode != 0 || combo->keyval != 0 || combo->mask != 0) cc_keyboard_item_add_key_combo (item, combo); } /* Set the keyboard shortcut name and command for custom entries */ if (cc_keyboard_item_get_item_type (item) == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH) { g_settings_set_string (cc_keyboard_item_get_settings (item), "name", gtk_editable_get_text (GTK_EDITABLE (self->name_entry))); g_settings_set_string (cc_keyboard_item_get_settings (item), "command", gtk_editable_get_text (GTK_EDITABLE (self->command_entry))); } } static void clear_custom_entries (CcKeyboardShortcutEditor *self) { g_signal_handlers_block_by_func (self->command_entry, command_entry_changed_cb, self); g_signal_handlers_block_by_func (self->name_entry, name_entry_changed_cb, self); gtk_editable_set_text (GTK_EDITABLE (self->name_entry), ""); gtk_editable_set_text (GTK_EDITABLE (self->command_entry), ""); gtk_shortcut_label_set_accelerator (GTK_SHORTCUT_LABEL (self->custom_shortcut_accel_label), ""); gtk_label_set_label (self->new_shortcut_conflict_label, ""); gtk_label_set_label (self->shortcut_conflict_label, ""); memset (self->custom_combo, 0, sizeof (CcKeyCombo)); self->custom_is_modifier = TRUE; self->edited = FALSE; self->collision_item = NULL; g_signal_handlers_unblock_by_func (self->command_entry, command_entry_changed_cb, self); g_signal_handlers_unblock_by_func (self->name_entry, name_entry_changed_cb, self); } static void cancel_editing (CcKeyboardShortcutEditor *self) { cc_keyboard_shortcut_editor_set_item (self, NULL); clear_custom_entries (self); gtk_widget_hide (GTK_WIDGET (self)); } static gboolean is_custom_shortcut (CcKeyboardShortcutEditor *self) { return self->item == NULL || cc_keyboard_item_get_item_type (self->item) == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH; } static void inhibit_system_shortcuts (CcKeyboardShortcutEditor *self) { GtkNative *native; GdkSurface *surface; if (self->system_shortcuts_inhibited) return; native = gtk_widget_get_native (GTK_WIDGET (self)); surface = gtk_native_get_surface (native); if (GDK_IS_TOPLEVEL (surface)) { gdk_toplevel_inhibit_system_shortcuts (GDK_TOPLEVEL (surface), NULL); self->system_shortcuts_inhibited = TRUE; } } static void uninhibit_system_shortcuts (CcKeyboardShortcutEditor *self) { GtkNative *native; GdkSurface *surface; if (!self->system_shortcuts_inhibited) return; native = gtk_widget_get_native (GTK_WIDGET (self)); surface = gtk_native_get_surface (native); if (GDK_IS_TOPLEVEL (surface)) { gdk_toplevel_restore_system_shortcuts (GDK_TOPLEVEL (surface)); self->system_shortcuts_inhibited = FALSE; } } static void update_shortcut (CcKeyboardShortcutEditor *self) { if (!self->item) return; /* Setup the binding */ apply_custom_item_fields (self, self->item); /* Eventually disable the conflict shortcut */ if (self->collision_item) cc_keyboard_item_disable (self->collision_item); /* Cleanup whatever was set before */ clear_custom_entries (self); cc_keyboard_shortcut_editor_set_item (self, NULL); } static GtkShortcutLabel* get_current_shortcut_label (CcKeyboardShortcutEditor *self) { if (is_custom_shortcut (self)) return GTK_SHORTCUT_LABEL (self->custom_shortcut_accel_label); return GTK_SHORTCUT_LABEL (self->shortcut_accel_label); } static void set_header_mode (CcKeyboardShortcutEditor *self, HeaderMode mode) { gtk_header_bar_set_show_title_buttons (self->headerbar, mode == HEADER_MODE_CUSTOM_EDIT); gtk_widget_set_visible (GTK_WIDGET (self->add_button), mode == HEADER_MODE_ADD); gtk_widget_set_visible (GTK_WIDGET (self->cancel_button), mode != HEADER_MODE_NONE && mode != HEADER_MODE_CUSTOM_EDIT); gtk_widget_set_visible (GTK_WIDGET (self->replace_button), mode == HEADER_MODE_REPLACE); gtk_widget_set_visible (GTK_WIDGET (self->set_button), mode == HEADER_MODE_SET); gtk_widget_set_visible (GTK_WIDGET (self->remove_button), mode == HEADER_MODE_CUSTOM_EDIT); /* By setting the default response, the action button gets the 'suggested-action' applied */ switch (mode) { case HEADER_MODE_SET: gtk_dialog_set_default_response (GTK_DIALOG (self), GTK_RESPONSE_APPLY); break; case HEADER_MODE_REPLACE: gtk_dialog_set_default_response (GTK_DIALOG (self), GTK_RESPONSE_ACCEPT); break; case HEADER_MODE_ADD: gtk_dialog_set_default_response (GTK_DIALOG (self), GTK_RESPONSE_OK); break; default: gtk_dialog_set_default_response (GTK_DIALOG (self), GTK_RESPONSE_NONE); } } static void setup_custom_shortcut (CcKeyboardShortcutEditor *self) { GtkShortcutLabel *shortcut_label; CcKeyboardItem *collision_item; HeaderMode mode; gboolean is_custom, is_accel_empty; gboolean valid, accel_valid; g_autofree char *accel = NULL; is_custom = is_custom_shortcut (self); accel_valid = is_valid_binding (self->custom_combo) && is_valid_accel (self->custom_combo) && !self->custom_is_modifier; is_accel_empty = is_empty_binding (self->custom_combo); if (is_accel_empty) accel_valid = TRUE; valid = accel_valid; /* Additional checks for custom shortcuts */ if (is_custom) { if (accel_valid) { set_shortcut_editor_page (self, PAGE_CUSTOM); /* We have to check if the current accelerator is empty in order to * decide if we show the "Set Shortcut" button or the accelerator label */ gtk_stack_set_visible_child (self->custom_shortcut_stack, is_accel_empty ? GTK_WIDGET (self->change_custom_shortcut_button) : GTK_WIDGET (self->custom_shortcut_accel_label)); gtk_widget_set_visible (GTK_WIDGET (self->reset_custom_button), !is_accel_empty); } valid = accel_valid && gtk_entry_get_text_length (self->name_entry) > 0 && gtk_entry_get_text_length (self->command_entry) > 0; } gtk_widget_set_sensitive (GTK_WIDGET (self->replace_button), valid); gtk_widget_set_sensitive (GTK_WIDGET (self->add_button), valid); if (valid) set_header_mode (self, HEADER_MODE_ADD); else set_header_mode (self, is_custom ? HEADER_MODE_CUSTOM_CANCEL : HEADER_MODE_NONE); /* Nothing else to do if the shortcut is invalid */ if (!accel_valid) return; /* Valid shortcut, show it in the standard page */ if (!is_custom) set_shortcut_editor_page (self, PAGE_STANDARD); shortcut_label = get_current_shortcut_label (self); collision_item = cc_keyboard_manager_get_collision (self->manager, self->item, self->custom_combo); accel = gtk_accelerator_name (self->custom_combo->keyval, self->custom_combo->mask); /* Setup the accelerator label */ gtk_shortcut_label_set_accelerator (shortcut_label, accel); self->edited = TRUE; uninhibit_system_shortcuts (self); /* * Oops! Looks like the accelerator is already being used, so we * must warn the user and let it be very clear that adding this * shortcut will disable the other. */ gtk_widget_set_visible (GTK_WIDGET (self->new_shortcut_conflict_label), collision_item != NULL); if (collision_item) { GtkLabel *label; g_autofree gchar *friendly_accelerator = NULL; g_autofree gchar *accelerator_text = NULL; g_autofree gchar *collision_text = NULL; friendly_accelerator = convert_keysym_state_to_string (self->custom_combo); accelerator_text = g_strdup_printf ("%s", friendly_accelerator); collision_text = g_strdup_printf (_("%s is already being used for %s. If you " "replace it, %s will be disabled"), accelerator_text, cc_keyboard_item_get_description (collision_item), cc_keyboard_item_get_description (collision_item)); label = is_custom_shortcut (self) ? self->new_shortcut_conflict_label : self->shortcut_conflict_label; gtk_label_set_markup (label, collision_text); } /* * When there is a collision between the current shortcut and another shortcut, * and we're editing an existing shortcut (rather than creating a new one), setup * the headerbar to display "Cancel" and "Replace". Otherwise, make sure to set * only the close button again. */ if (collision_item) { mode = HEADER_MODE_REPLACE; } else { if (self->mode == CC_SHORTCUT_EDITOR_EDIT) mode = is_custom ? HEADER_MODE_CUSTOM_EDIT : HEADER_MODE_SET; else mode = is_custom ? HEADER_MODE_ADD : HEADER_MODE_SET; } set_header_mode (self, mode); self->collision_item = collision_item; } static void add_button_clicked_cb (CcKeyboardShortcutEditor *self) { CcKeyboardItem *item; item = cc_keyboard_manager_create_custom_shortcut (self->manager); /* Apply the custom shortcut setup at the new item */ apply_custom_item_fields (self, item); /* Eventually disable the conflict shortcut */ if (self->collision_item) cc_keyboard_item_disable (self->collision_item); /* Cleanup everything once we're done */ clear_custom_entries (self); cc_keyboard_manager_add_custom_shortcut (self->manager, item); gtk_widget_hide (GTK_WIDGET (self)); } static void cancel_button_clicked_cb (GtkWidget *button, CcKeyboardShortcutEditor *self) { cancel_editing (self); } static void change_custom_shortcut_button_clicked_cb (CcKeyboardShortcutEditor *self) { inhibit_system_shortcuts (self); set_shortcut_editor_page (self, PAGE_EDIT); set_header_mode (self, HEADER_MODE_NONE); } static void command_entry_changed_cb (CcKeyboardShortcutEditor *self) { setup_custom_shortcut (self); } static void name_entry_changed_cb (CcKeyboardShortcutEditor *self) { setup_custom_shortcut (self); } static void remove_button_clicked_cb (CcKeyboardShortcutEditor *self) { gtk_widget_hide (GTK_WIDGET (self)); cc_keyboard_manager_remove_custom_shortcut (self->manager, self->item); } static void replace_button_clicked_cb (CcKeyboardShortcutEditor *self) { if (self->mode == CC_SHORTCUT_EDITOR_CREATE) add_button_clicked_cb (self); else set_button_clicked_cb (self); } static void reset_custom_clicked_cb (CcKeyboardShortcutEditor *self) { if (self->item) cc_keyboard_manager_reset_shortcut (self->manager, self->item); gtk_stack_set_visible_child (self->custom_shortcut_stack, GTK_WIDGET (self->change_custom_shortcut_button)); gtk_widget_hide (GTK_WIDGET (self->reset_custom_button)); } static void reset_item_clicked_cb (CcKeyboardShortcutEditor *self) { CcKeyCombo combo; gchar *accel; /* Reset first, then update the shortcut */ cc_keyboard_manager_reset_shortcut (self->manager, self->item); combo = cc_keyboard_item_get_primary_combo (self->item); accel = gtk_accelerator_name (combo.keyval, combo.mask); gtk_shortcut_label_set_accelerator (GTK_SHORTCUT_LABEL (self->shortcut_accel_label), accel); g_free (accel); } static void set_button_clicked_cb (CcKeyboardShortcutEditor *self) { update_shortcut (self); gtk_widget_hide (GTK_WIDGET (self)); } static void setup_keyboard_item (CcKeyboardShortcutEditor *self, CcKeyboardItem *item) { CcKeyCombo combo; gboolean is_custom; g_autofree gchar *accel = NULL; g_autofree gchar *description_text = NULL; g_autofree gchar *text = NULL; if (!item) { gtk_label_set_text (self->top_info_label, _("Enter the new shortcut")); return; } combo = cc_keyboard_item_get_primary_combo (item); is_custom = cc_keyboard_item_get_item_type (item) == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH; accel = gtk_accelerator_name (combo.keyval, combo.mask); /* To avoid accidentally thinking we unset the current keybinding, set the values * of the keyboard item that is being edited */ self->custom_is_modifier = FALSE; *self->custom_combo = combo; /* Headerbar */ gtk_window_set_title (GTK_WINDOW (self), is_custom ? _("Set Custom Shortcut") : _("Set Shortcut")); set_header_mode (self, is_custom ? HEADER_MODE_CUSTOM_EDIT : HEADER_MODE_NONE); gtk_widget_hide (GTK_WIDGET (self->add_button)); gtk_widget_hide (GTK_WIDGET (self->cancel_button)); gtk_widget_hide (GTK_WIDGET (self->replace_button)); /* Setup the top label */ description_text = g_strdup_printf ("%s", cc_keyboard_item_get_description (item)); /* TRANSLATORS: %s is replaced with a description of the keyboard shortcut */ text = g_strdup_printf (_("Enter new shortcut to change %s."), description_text); gtk_label_set_markup (self->top_info_label, text); /* Accelerator labels */ gtk_shortcut_label_set_accelerator (self->shortcut_accel_label, accel); gtk_shortcut_label_set_accelerator (self->custom_shortcut_accel_label, accel); g_clear_pointer (&self->reset_item_binding, g_binding_unbind); self->reset_item_binding = g_object_bind_property (item, "is-value-default", self->reset_button, "visible", G_BINDING_DEFAULT | G_BINDING_INVERT_BOOLEAN | G_BINDING_SYNC_CREATE); /* Setup the custom entries */ if (is_custom) { gboolean is_accel_empty; g_signal_handlers_block_by_func (self->command_entry, command_entry_changed_cb, self); g_signal_handlers_block_by_func (self->name_entry, name_entry_changed_cb, self); /* Name entry */ gtk_editable_set_text (GTK_EDITABLE (self->name_entry), cc_keyboard_item_get_description (item)); gtk_widget_set_sensitive (GTK_WIDGET (self->name_entry), cc_keyboard_item_get_desc_editable (item)); /* Command entry */ gtk_editable_set_text (GTK_EDITABLE (self->command_entry), cc_keyboard_item_get_command (item)); gtk_widget_set_sensitive (GTK_WIDGET (self->command_entry), cc_keyboard_item_get_cmd_editable (item)); /* If there is no accelerator set for this custom shortcut, show the "Set Shortcut" button. */ is_accel_empty = !accel || accel[0] == '\0'; gtk_stack_set_visible_child (self->custom_shortcut_stack, is_accel_empty ? GTK_WIDGET (self->change_custom_shortcut_button) : GTK_WIDGET (self->custom_shortcut_accel_label)); gtk_widget_set_visible (GTK_WIDGET (self->reset_custom_button), !is_accel_empty); g_signal_handlers_unblock_by_func (self->command_entry, command_entry_changed_cb, self); g_signal_handlers_unblock_by_func (self->name_entry, name_entry_changed_cb, self); uninhibit_system_shortcuts (self); } /* Show the appropriate view */ set_shortcut_editor_page (self, is_custom ? PAGE_CUSTOM : PAGE_EDIT); } static void cc_keyboard_shortcut_editor_finalize (GObject *object) { CcKeyboardShortcutEditor *self = (CcKeyboardShortcutEditor *)object; g_clear_object (&self->item); g_clear_object (&self->manager); g_clear_pointer (&self->custom_combo, g_free); G_OBJECT_CLASS (cc_keyboard_shortcut_editor_parent_class)->finalize (object); } static void cc_keyboard_shortcut_editor_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { CcKeyboardShortcutEditor *self = CC_KEYBOARD_SHORTCUT_EDITOR (object); switch (prop_id) { case PROP_KEYBOARD_ITEM: g_value_set_object (value, self->item); break; case PROP_MANAGER: g_value_set_object (value, self->manager); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void cc_keyboard_shortcut_editor_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { CcKeyboardShortcutEditor *self = CC_KEYBOARD_SHORTCUT_EDITOR (object); switch (prop_id) { case PROP_KEYBOARD_ITEM: cc_keyboard_shortcut_editor_set_item (self, g_value_get_object (value)); break; case PROP_MANAGER: g_set_object (&self->manager, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static gboolean on_key_pressed_cb (GtkEventControllerKey *key_controller, guint keyval, guint keycode, GdkModifierType state, CcKeyboardShortcutEditor *self) { GdkModifierType real_mask; GdkEvent *event; gboolean editing; gboolean is_modifier; guint keyval_lower; /* Being in the "change-shortcut" page is the only check we must * perform to decide if we're editing a shortcut. */ editing = get_shortcut_editor_page (self) == PAGE_EDIT; if (!editing) return GDK_EVENT_PROPAGATE; normalize_keyval_and_mask (keycode, state, gtk_event_controller_key_get_group (key_controller), &keyval_lower, &real_mask); event = gtk_event_controller_get_current_event (GTK_EVENT_CONTROLLER (key_controller)); is_modifier = gdk_key_event_is_modifier (event); /* A single Escape press cancels the editing */ if (!is_modifier && real_mask == 0 && keyval_lower == GDK_KEY_Escape) { self->edited = FALSE; uninhibit_system_shortcuts (self); cancel_editing (self); return GDK_EVENT_STOP; } /* Backspace disables the current shortcut */ if (!is_modifier && real_mask == 0 && keyval_lower == GDK_KEY_BackSpace) { self->edited = TRUE; self->custom_is_modifier = FALSE; memset (self->custom_combo, 0, sizeof (CcKeyCombo)); gtk_shortcut_label_set_accelerator (GTK_SHORTCUT_LABEL (self->custom_shortcut_accel_label), ""); gtk_shortcut_label_set_accelerator (GTK_SHORTCUT_LABEL (self->shortcut_accel_label), ""); uninhibit_system_shortcuts (self); self->edited = FALSE; setup_custom_shortcut (self); return GDK_EVENT_STOP; } self->custom_is_modifier = is_modifier; self->custom_combo->keycode = keycode; self->custom_combo->keyval = keyval_lower; self->custom_combo->mask = real_mask; /* CapsLock isn't supported as a keybinding modifier, so keep it from confusing us */ self->custom_combo->mask &= ~GDK_LOCK_MASK; setup_custom_shortcut (self); return GDK_EVENT_STOP; } static void cc_keyboard_shortcut_editor_close (GtkDialog *dialog) { CcKeyboardShortcutEditor *self = CC_KEYBOARD_SHORTCUT_EDITOR (dialog); if (self->mode == CC_SHORTCUT_EDITOR_EDIT) update_shortcut (self); GTK_DIALOG_CLASS (cc_keyboard_shortcut_editor_parent_class)->close (dialog); } static void cc_keyboard_shortcut_editor_response (GtkDialog *dialog, gint response_id) { CcKeyboardShortcutEditor *self = CC_KEYBOARD_SHORTCUT_EDITOR (dialog); if (response_id == GTK_RESPONSE_DELETE_EVENT && self->mode == CC_SHORTCUT_EDITOR_EDIT) { update_shortcut (self); } } static gboolean grab_idle (gpointer data) { CcKeyboardShortcutEditor *self = data; if (self->item && cc_keyboard_item_get_item_type (self->item) != CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH) inhibit_system_shortcuts (self); self->grab_idle_id = 0; return G_SOURCE_REMOVE; } static void cc_keyboard_shortcut_editor_show (GtkWidget *widget) { CcKeyboardShortcutEditor *self = CC_KEYBOARD_SHORTCUT_EDITOR (widget); /* Map before grabbing, so that the window is visible */ GTK_WIDGET_CLASS (cc_keyboard_shortcut_editor_parent_class)->show (widget); self->grab_idle_id = g_timeout_add (100, grab_idle, self); } static void cc_keyboard_shortcut_editor_unrealize (GtkWidget *widget) { CcKeyboardShortcutEditor *self = CC_KEYBOARD_SHORTCUT_EDITOR (widget); if (self->grab_idle_id) { g_source_remove (self->grab_idle_id); self->grab_idle_id = 0; } uninhibit_system_shortcuts (self); GTK_WIDGET_CLASS (cc_keyboard_shortcut_editor_parent_class)->unrealize (widget); } static void cc_keyboard_shortcut_editor_class_init (CcKeyboardShortcutEditorClass *klass) { GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GtkDialogClass *dialog_class = GTK_DIALOG_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = cc_keyboard_shortcut_editor_finalize; object_class->get_property = cc_keyboard_shortcut_editor_get_property; object_class->set_property = cc_keyboard_shortcut_editor_set_property; widget_class->show = cc_keyboard_shortcut_editor_show; widget_class->unrealize = cc_keyboard_shortcut_editor_unrealize; dialog_class->close = cc_keyboard_shortcut_editor_close; dialog_class->response = cc_keyboard_shortcut_editor_response; /** * CcKeyboardShortcutEditor:keyboard-item: * * The current keyboard shortcut being edited. */ properties[PROP_KEYBOARD_ITEM] = g_param_spec_object ("keyboard-item", "Keyboard item", "The keyboard item being edited", CC_TYPE_KEYBOARD_ITEM, G_PARAM_READWRITE); /** * CcKeyboardShortcutEditor:panel: * * The current keyboard panel. */ properties[PROP_MANAGER] = g_param_spec_object ("manager", "Keyboard manager", "The keyboard manager", CC_TYPE_KEYBOARD_MANAGER, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties (object_class, N_PROPS, properties); gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/keyboard/cc-keyboard-shortcut-editor.ui"); gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, add_button); gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, cancel_button); gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, change_custom_shortcut_button); gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, command_entry); gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, custom_grid); gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, custom_shortcut_accel_label); gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, custom_shortcut_stack); gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, edit_box); gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, headerbar); gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, name_entry); gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, new_shortcut_conflict_label); gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, remove_button); gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, replace_button); gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, reset_button); gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, reset_custom_button); gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, set_button); gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, shortcut_accel_label); gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, shortcut_conflict_label); gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, standard_box); gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, stack); gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, top_info_label); gtk_widget_class_bind_template_callback (widget_class, add_button_clicked_cb); gtk_widget_class_bind_template_callback (widget_class, cancel_button_clicked_cb); gtk_widget_class_bind_template_callback (widget_class, change_custom_shortcut_button_clicked_cb); gtk_widget_class_bind_template_callback (widget_class, command_entry_changed_cb); gtk_widget_class_bind_template_callback (widget_class, name_entry_changed_cb); gtk_widget_class_bind_template_callback (widget_class, on_key_pressed_cb); gtk_widget_class_bind_template_callback (widget_class, remove_button_clicked_cb); gtk_widget_class_bind_template_callback (widget_class, replace_button_clicked_cb); gtk_widget_class_bind_template_callback (widget_class, reset_custom_clicked_cb); gtk_widget_class_bind_template_callback (widget_class, reset_item_clicked_cb); gtk_widget_class_bind_template_callback (widget_class, set_button_clicked_cb); } static void cc_keyboard_shortcut_editor_init (CcKeyboardShortcutEditor *self) { gtk_widget_init_template (GTK_WIDGET (self)); self->mode = CC_SHORTCUT_EDITOR_EDIT; self->custom_is_modifier = TRUE; self->custom_combo = g_new0 (CcKeyCombo, 1); gtk_widget_set_direction (GTK_WIDGET (self->custom_shortcut_accel_label), GTK_TEXT_DIR_LTR); gtk_widget_set_direction (GTK_WIDGET (self->shortcut_accel_label), GTK_TEXT_DIR_LTR); } /** * cc_keyboard_shortcut_editor_new: * * Creates a new #CcKeyboardShortcutEditor. * * Returns: (transfer full): a newly created #CcKeyboardShortcutEditor. */ GtkWidget* cc_keyboard_shortcut_editor_new (CcKeyboardManager *manager) { return g_object_new (CC_TYPE_KEYBOARD_SHORTCUT_EDITOR, "manager", manager, "use-header-bar", 1, NULL); } /** * cc_keyboard_shortcut_editor_get_item: * @self: a #CcKeyboardShortcutEditor * * Retrieves the current keyboard shortcut being edited. * * Returns: (transfer none)(nullable): a #CcKeyboardItem */ CcKeyboardItem* cc_keyboard_shortcut_editor_get_item (CcKeyboardShortcutEditor *self) { g_return_val_if_fail (CC_IS_KEYBOARD_SHORTCUT_EDITOR (self), NULL); return self->item; } /** * cc_keyboard_shortcut_editor_set_item: * @self: a #CcKeyboardShortcutEditor * @item: a #CcKeyboardItem * * Sets the current keyboard shortcut to be edited. */ void cc_keyboard_shortcut_editor_set_item (CcKeyboardShortcutEditor *self, CcKeyboardItem *item) { g_return_if_fail (CC_IS_KEYBOARD_SHORTCUT_EDITOR (self)); setup_keyboard_item (self, item); if (!g_set_object (&self->item, item)) return; g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_KEYBOARD_ITEM]); } CcShortcutEditorMode cc_keyboard_shortcut_editor_get_mode (CcKeyboardShortcutEditor *self) { g_return_val_if_fail (CC_IS_KEYBOARD_SHORTCUT_EDITOR (self), 0); return self->mode; } void cc_keyboard_shortcut_editor_set_mode (CcKeyboardShortcutEditor *self, CcShortcutEditorMode mode) { gboolean is_create_mode; g_return_if_fail (CC_IS_KEYBOARD_SHORTCUT_EDITOR (self)); self->mode = mode; is_create_mode = mode == CC_SHORTCUT_EDITOR_CREATE; gtk_widget_set_visible (GTK_WIDGET (self->new_shortcut_conflict_label), is_create_mode); gtk_stack_set_visible_child (self->custom_shortcut_stack, is_create_mode ? GTK_WIDGET (self->change_custom_shortcut_button) : GTK_WIDGET (self->custom_shortcut_accel_label)); if (mode == CC_SHORTCUT_EDITOR_CREATE) { /* Cleanup whatever was set before */ clear_custom_entries (self); set_header_mode (self, HEADER_MODE_ADD); set_shortcut_editor_page (self, PAGE_CUSTOM); gtk_window_set_title (GTK_WINDOW (self), _("Add Custom Shortcut")); gtk_widget_set_sensitive (GTK_WIDGET (self->command_entry), TRUE); gtk_widget_set_sensitive (GTK_WIDGET (self->name_entry), TRUE); gtk_widget_set_sensitive (GTK_WIDGET (self->add_button), FALSE); gtk_widget_hide (GTK_WIDGET (self->reset_custom_button)); } }