diff options
Diffstat (limited to 'panels/keyboard/cc-keyboard-shortcut-dialog.c')
-rw-r--r-- | panels/keyboard/cc-keyboard-shortcut-dialog.c | 877 |
1 files changed, 877 insertions, 0 deletions
diff --git a/panels/keyboard/cc-keyboard-shortcut-dialog.c b/panels/keyboard/cc-keyboard-shortcut-dialog.c new file mode 100644 index 0000000..15f3dfd --- /dev/null +++ b/panels/keyboard/cc-keyboard-shortcut-dialog.c @@ -0,0 +1,877 @@ +/* cc-keyboard-shortcut-dialog.c + * + * Copyright (C) 2010 Intel, Inc + * Copyright (C) 2016 Endless, Inc + * Copyright (C) 2020 System76, 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 <http://www.gnu.org/licenses/>. + * + * Author: Thomas Wood <thomas.wood@intel.com> + * Georges Basile Stavracas Neto <gbsneto@gnome.org> + * Ian Douglas Scott <idscott@system76.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <config.h> +#include <glib/gi18n.h> +#include <adwaita.h> + +#include "cc-keyboard-shortcut-dialog.h" +#include "cc-keyboard-item.h" +#include "cc-keyboard-manager.h" +#include "cc-keyboard-shortcut-editor.h" +#include "cc-keyboard-shortcut-row.h" +#include "cc-list-row.h" +#include "cc-util.h" +#include "keyboard-shortcuts.h" + +#define SHORTCUT_DELIMITERS "+ " + +typedef struct { + gchar *section_title; + gchar *section_id; + guint modified_count; + GtkLabel *modified_label; +} SectionRowData; + +typedef struct { + CcKeyboardItem *item; + gchar *section_title; + gchar *section_id; + SectionRowData *section_data; +} ShortcutRowData; + +struct _CcKeyboardShortcutDialog +{ + GtkDialog parent_instance; + + GtkSizeGroup *accelerator_sizegroup; + GtkWidget *back_button; + GtkListBoxRow *custom_shortcut_add_row; + guint custom_shortcut_count; + GtkWidget *empty_custom_shortcuts_placeholder; + GtkWidget *empty_search_placeholder; + GtkHeaderBar *headerbar; + GtkStack *header_stack; + GtkWidget *reset_all_button; + GtkWidget *section_box; + GtkSearchEntry *search_entry; + GtkListBox *section_listbox; + GtkListBoxRow *section_row; + GtkWidget *shortcut_box; + GtkListBox *shortcut_listbox; + GtkStack *stack; + + CcKeyboardManager *manager; + GtkWidget *shortcut_editor; + GHashTable *sections; + }; + +G_DEFINE_TYPE (CcKeyboardShortcutDialog, cc_keyboard_shortcut_dialog, GTK_TYPE_DIALOG) +static gboolean +is_matched_shortcut_present (GtkListBox *listbox, + gpointer user_data); + +static SectionRowData* +section_row_data_new (const gchar *section_id, + const gchar *section_title, + GtkLabel *modified_label) +{ + SectionRowData *data; + + data = g_new0 (SectionRowData, 1); + data->section_id = g_strdup (section_id); + data->section_title = g_strdup (section_title); + data->modified_count = 0; + data->modified_label = modified_label; + + return data; +} + +static void +section_row_data_free (SectionRowData *data) +{ + g_free (data->section_id); + g_free (data->section_title); + g_free (data); +} + +static ShortcutRowData* +shortcut_row_data_new (CcKeyboardItem *item, + const gchar *section_id, + const gchar *section_title, + SectionRowData *section_data) +{ + ShortcutRowData *data; + + data = g_new0 (ShortcutRowData, 1); + data->item = g_object_ref (item); + data->section_id = g_strdup (section_id); + data->section_title = g_strdup (section_title); + data->section_data = section_data; + + return data; +} + +static void +shortcut_row_data_free (ShortcutRowData *data) +{ + g_object_unref (data->item); + g_free (data->section_id); + g_free (data->section_title); + g_free (data); +} + +static GtkListBoxRow* +add_section (CcKeyboardShortcutDialog *self, + const gchar *section_id, + const gchar *section_title) +{ + GtkWidget *icon, *modified_label, *row; + + icon = gtk_image_new_from_icon_name ("go-next-symbolic"); + gtk_widget_add_css_class (icon, "dim-label"); + + modified_label = gtk_label_new (NULL); + gtk_widget_add_css_class (modified_label, "dim-label"); + + row = adw_action_row_new (); + gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), TRUE); + adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), _(section_title)); + //TODO gtk_container_add (GTK_CONTAINER (row), modified_label); + //TODO gtk_container_add (GTK_CONTAINER (row), icon); + + g_object_set_data_full (G_OBJECT (row), + "data", + section_row_data_new (section_id, section_title, GTK_LABEL (modified_label)), + (GDestroyNotify)section_row_data_free); + + g_hash_table_insert (self->sections, g_strdup (section_id), row); + gtk_list_box_append (self->section_listbox, row); + + return GTK_LIST_BOX_ROW (row); +} + +static void +set_custom_shortcut_placeholder_visibility (CcKeyboardShortcutDialog *self) +{ + SectionRowData *section_data; + gboolean is_custom_shortcuts = FALSE; + + if (self->section_row != NULL) + { + section_data = g_object_get_data (G_OBJECT (self->section_row), "data"); + is_custom_shortcuts = (strcmp (section_data->section_id, "custom") == 0); + + gtk_stack_set_transition_type (self->stack, GTK_STACK_TRANSITION_TYPE_CROSSFADE); + if (is_custom_shortcuts && (self->custom_shortcut_count == 0)) + gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->empty_custom_shortcuts_placeholder)); + else + gtk_stack_set_visible_child (self->stack, self->shortcut_box); + } +} + +static void +add_item (CcKeyboardShortcutDialog *self, + CcKeyboardItem *item, + const gchar *section_id, + const gchar *section_title) +{ + GtkWidget *row; + GtkListBoxRow *section_row; + SectionRowData *section_data; + + section_row = g_hash_table_lookup (self->sections, section_id); + if (section_row == NULL) + section_row = add_section (self, section_id, section_title); + + section_data = g_object_get_data (G_OBJECT (section_row), "data"); + + row = GTK_WIDGET (cc_keyboard_shortcut_row_new (item, + self->manager, + CC_KEYBOARD_SHORTCUT_EDITOR (self->shortcut_editor), + self->accelerator_sizegroup)); + + g_object_set_data_full (G_OBJECT (row), + "data", + shortcut_row_data_new (item, section_id, section_title, section_data), + (GDestroyNotify)shortcut_row_data_free); + + if (strcmp (section_id, "custom") == 0) + { + self->custom_shortcut_count++; + set_custom_shortcut_placeholder_visibility (self); + } + + gtk_list_box_append (self->shortcut_listbox, row); +} + +static void +remove_item (CcKeyboardShortcutDialog *self, + CcKeyboardItem *item) +{ + GtkWidget *child; + + for (child = gtk_widget_get_first_child (GTK_WIDGET (self->shortcut_listbox)); + child; + child = gtk_widget_get_next_sibling (child)) + { + ShortcutRowData *row_data; + + if (!GTK_IS_LIST_BOX_ROW (child)) + continue; + + row_data = g_object_get_data (G_OBJECT (child), "data"); + + if (row_data && row_data->item == item) + { + if (strcmp (row_data->section_id, "custom") == 0) + { + self->custom_shortcut_count--; + set_custom_shortcut_placeholder_visibility (self); + } + + gtk_list_box_remove (self->shortcut_listbox, child); + break; + } + } +} + +static void +update_modified_counts (CcKeyboardShortcutDialog *self) +{ + SectionRowData *section_data; + ShortcutRowData *shortcut_data; + g_autofree gchar *modified_text = NULL; + GtkWidget *child; + + for (child = gtk_widget_get_first_child (GTK_WIDGET (self->section_listbox)); + child; + child = gtk_widget_get_next_sibling (child)) + { + if (!GTK_IS_LIST_BOX_ROW (child)) + continue; + + section_data = g_object_get_data (G_OBJECT (child), "data"); + section_data->modified_count = 0; + } + + for (child = gtk_widget_get_first_child (GTK_WIDGET (self->shortcut_listbox)); + child; + child = gtk_widget_get_next_sibling (child)) + { + if (!GTK_IS_LIST_BOX_ROW (child)) + continue; + + if (GTK_LIST_BOX_ROW (child) == self->custom_shortcut_add_row) + continue; + shortcut_data = g_object_get_data (G_OBJECT (child), "data"); + if (!cc_keyboard_item_is_value_default (shortcut_data->item)) + shortcut_data->section_data->modified_count++; + } + + for (child = gtk_widget_get_first_child (GTK_WIDGET (self->section_listbox)); + child; + child = gtk_widget_get_next_sibling (child)) + { + if (!GTK_IS_LIST_BOX_ROW (child)) + continue; + + section_data = g_object_get_data (G_OBJECT (child), "data"); + if (section_data->modified_count > 0) + { + modified_text = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, + "%d modified", + "%d modified", + section_data->modified_count), + section_data->modified_count); + gtk_label_set_text (section_data->modified_label, modified_text); + } + else + { + gtk_label_set_text (section_data->modified_label, ""); + } + } +} + +static void +show_section_list (CcKeyboardShortcutDialog *self) +{ + if (self->section_row != NULL) + gtk_stack_set_transition_type (self->stack, GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT); + else + gtk_stack_set_transition_type (self->stack, GTK_STACK_TRANSITION_TYPE_NONE); + self->section_row = NULL; + + gtk_stack_set_visible_child (self->stack, self->section_box); + gtk_window_set_title (GTK_WINDOW (self), _("Keyboard Shortcuts")); + gtk_editable_set_text (GTK_EDITABLE (self->search_entry), ""); + gtk_stack_set_visible_child (self->header_stack, self->reset_all_button); + gtk_widget_set_visible (GTK_WIDGET (self->search_entry), TRUE); + + update_modified_counts (self); +} + +static void +show_shortcut_list (CcKeyboardShortcutDialog *self) +{ + SectionRowData *section_data; + gchar *title; + gboolean is_custom_shortcuts = FALSE; + + title = _("Keyboard Shortcuts"); + gtk_stack_set_transition_type (self->stack, GTK_STACK_TRANSITION_TYPE_NONE); + if (self->section_row != NULL) + { + section_data = g_object_get_data (G_OBJECT (self->section_row), "data"); + title = _(section_data->section_title); + is_custom_shortcuts = (strcmp (section_data->section_id, "custom") == 0); + gtk_stack_set_transition_type (self->stack, GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT); + } + gtk_list_box_invalidate_filter (self->shortcut_listbox); + + if (is_custom_shortcuts && (self->custom_shortcut_count == 0)) + gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->empty_custom_shortcuts_placeholder)); + else + gtk_stack_set_visible_child (self->stack, self->shortcut_box); + + gtk_window_set_title (GTK_WINDOW (self), title); + set_custom_shortcut_placeholder_visibility (self); + gtk_stack_set_visible_child (self->header_stack, self->back_button); + gtk_widget_set_visible (GTK_WIDGET (self->search_entry), self->section_row == NULL); + +} + +static void +add_custom_shortcut_clicked_cb (CcKeyboardShortcutDialog *self) +{ + CcKeyboardShortcutEditor *editor; + + editor = CC_KEYBOARD_SHORTCUT_EDITOR (self->shortcut_editor); + + cc_keyboard_shortcut_editor_set_mode (editor, CC_SHORTCUT_EDITOR_CREATE); + cc_keyboard_shortcut_editor_set_item (editor, NULL); + + gtk_widget_show (self->shortcut_editor); +} + +static void +section_row_activated (GtkWidget *button, + GtkListBoxRow *row, + CcKeyboardShortcutDialog *self) +{ + self->section_row = row; + show_shortcut_list (self); +} + +static void +shortcut_row_activated (GtkWidget *button, + GtkListBoxRow *row, + CcKeyboardShortcutDialog *self) +{ + CcKeyboardShortcutEditor *editor; + + if (row == self->custom_shortcut_add_row) + { + add_custom_shortcut_clicked_cb (self); + return; + } + + editor = CC_KEYBOARD_SHORTCUT_EDITOR (self->shortcut_editor); + + ShortcutRowData *data = g_object_get_data (G_OBJECT (row), "data"); + + cc_keyboard_shortcut_editor_set_mode (editor, CC_SHORTCUT_EDITOR_EDIT); + cc_keyboard_shortcut_editor_set_item (editor, data->item); + + gtk_widget_show (self->shortcut_editor); +} + +static void +back_button_clicked_cb (CcKeyboardShortcutDialog *self) +{ + show_section_list (self); +} + +static void +reset_shortcut (CcKeyboardShortcutDialog *self, + GtkWidget *row) +{ + ShortcutRowData *data; + + if (row == GTK_WIDGET (self->custom_shortcut_add_row)) + return; + + data = g_object_get_data (G_OBJECT (row), "data"); + + /* Don't reset custom shortcuts */ + if (cc_keyboard_item_get_item_type (data->item) == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH) + return; + + /* cc_keyboard_manager_reset_shortcut() already resets conflicting shortcuts, + * so no other check is needed here. */ + cc_keyboard_manager_reset_shortcut (self->manager, data->item); +} + +static void +on_reset_all_dialog_response_cb (GtkDialog *dialog, + gint response, + CcKeyboardShortcutDialog *self) +{ + + if (response == GTK_RESPONSE_ACCEPT) + { + GtkWidget *child; + + for (child = gtk_widget_get_first_child (GTK_WIDGET (self->shortcut_listbox)); + child; + child = gtk_widget_get_next_sibling (child)) + { + if (!GTK_IS_LIST_BOX_ROW (child)) + continue; + if (GTK_LIST_BOX_ROW (child) == self->custom_shortcut_add_row) + continue; + reset_shortcut (self, child); + } + } + + gtk_window_destroy (GTK_WINDOW (dialog)); + update_modified_counts (self); +} + +static void +reset_all_clicked_cb (CcKeyboardShortcutDialog *self) +{ + GtkWidget *dialog, *button; + + dialog = gtk_message_dialog_new (GTK_WINDOW (self), + GTK_DIALOG_MODAL | GTK_DIALOG_USE_HEADER_BAR | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_NONE, + _("Reset All Shortcuts?")); + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("Resetting the shortcuts may affect your custom shortcuts. " + "This cannot be undone.")); + + gtk_dialog_add_buttons (GTK_DIALOG (dialog), + _("Cancel"), GTK_RESPONSE_CANCEL, + _("Reset All"), GTK_RESPONSE_ACCEPT, + NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL); + + /* Make the "Reset All" button destructive */ + button = gtk_dialog_get_widget_for_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); + gtk_widget_add_css_class (button, "destructive-action"); + + g_signal_connect (dialog, "response", G_CALLBACK (on_reset_all_dialog_response_cb), self); + + gtk_window_present (GTK_WINDOW (dialog)); +} + +static void +search_entry_cb (CcKeyboardShortcutDialog *self) +{ + gboolean is_shortcut = is_matched_shortcut_present (self->shortcut_listbox, self); + const gchar *search_text = gtk_editable_get_text (GTK_EDITABLE (self->search_entry)); + + if (!is_shortcut) + gtk_stack_set_visible_child (self->stack, self->empty_search_placeholder); + else if (g_utf8_strlen (search_text, -1) == 0 && self->section_row == NULL) + show_section_list (self); + else if (gtk_stack_get_visible_child (self->stack) != self->shortcut_box) + show_shortcut_list (self); + else + gtk_list_box_invalidate_filter (self->shortcut_listbox); +} + +static gboolean +strv_contains_prefix_or_match (gchar **strv, + const gchar *prefix) +{ + const struct { + const gchar *key; + const gchar *untranslated; + const gchar *synonym; + } key_aliases[] = + { + { "ctrl", "Ctrl", "ctrl" }, + { "win", "Super", "super" }, + { "option", NULL, "alt" }, + { "command", NULL, "super" }, + { "apple", NULL, "super" }, + }; + + for (guint i = 0; strv[i]; i++) + { + if (g_str_has_prefix (strv[i], prefix)) + return TRUE; + } + + for (guint i = 0; i < G_N_ELEMENTS (key_aliases); i++) + { + g_autofree gchar *alias = NULL; + const gchar *synonym; + + if (!g_str_has_prefix (key_aliases[i].key, prefix)) + continue; + + if (key_aliases[i].untranslated) + { + const gchar *translated_label; + + /* Steal GTK+'s translation */ + translated_label = g_dpgettext2 ("gtk40", "keyboard label", key_aliases[i].untranslated); + alias = g_utf8_strdown (translated_label, -1); + } + + synonym = key_aliases[i].synonym; + + /* If a translation or synonym of the key is in the accelerator, and we typed + * the key, also consider that a prefix */ + if ((alias && g_strv_contains ((const gchar * const *) strv, alias)) || + (synonym && g_strv_contains ((const gchar * const *) strv, synonym))) + { + return TRUE; + } + } + + return FALSE; +} + +static gboolean +search_match_shortcut (CcKeyboardItem *item, + const gchar *search) +{ + g_auto(GStrv) shortcut_tokens = NULL, search_tokens = NULL; + g_autofree gchar *normalized_accel = NULL; + g_autofree gchar *accel = NULL; + gboolean match; + GList *key_combos; + CcKeyCombo *combo; + + key_combos = cc_keyboard_item_get_key_combos (item); + for (GList *l = key_combos; l != NULL; l = l->next) + { + combo = l->data; + + if (is_empty_binding (combo)) + continue; + + match = TRUE; + accel = convert_keysym_state_to_string (combo); + normalized_accel = cc_util_normalize_casefold_and_unaccent (accel); + + shortcut_tokens = g_strsplit_set (normalized_accel, SHORTCUT_DELIMITERS, -1); + search_tokens = g_strsplit_set (search, SHORTCUT_DELIMITERS, -1); + + for (guint i = 0; search_tokens[i] != NULL; i++) + { + const gchar *token; + + /* Strip leading and trailing whitespaces */ + token = g_strstrip (search_tokens[i]); + + if (g_utf8_strlen (token, -1) == 0) + continue; + + match = match && strv_contains_prefix_or_match (shortcut_tokens, token); + + if (!match) + break; + } + + if (match) + return TRUE; + } + + return FALSE; +} + +static gint +section_sort_function (GtkListBoxRow *a, + GtkListBoxRow *b, + gpointer user_data) +{ + SectionRowData *a_data, *b_data; + + a_data = g_object_get_data (G_OBJECT (a), "data"); + b_data = g_object_get_data (G_OBJECT (b), "data"); + + /* Put custom shortcuts below everything else */ + if (g_strcmp0 (a_data->section_id, "custom") == 0) + return 1; + + return g_strcmp0 (a_data->section_title, b_data->section_title); +} + +static gint +shortcut_sort_function (GtkListBoxRow *a, + GtkListBoxRow *b, + gpointer user_data) +{ + CcKeyboardShortcutDialog *self = user_data; + ShortcutRowData *a_data, *b_data; + gint retval; + + if (a == self->custom_shortcut_add_row) + return 1; + else if (b == self->custom_shortcut_add_row) + return -1; + + a_data = g_object_get_data (G_OBJECT (a), "data"); + b_data = g_object_get_data (G_OBJECT (b), "data"); + + retval = g_strcmp0 (a_data->section_title, b_data->section_title); + + if (retval != 0) + return retval; + + return g_strcmp0 (cc_keyboard_item_get_description (a_data->item), cc_keyboard_item_get_description (b_data->item)); +} + +static gboolean +shortcut_filter_function (GtkListBoxRow *row, + gpointer userdata) +{ + CcKeyboardShortcutDialog *self = userdata; + SectionRowData *section_data; + ShortcutRowData *data; + CcKeyboardItem *item; + gboolean retval; + g_autofree gchar *search = NULL; + g_autofree gchar *name = NULL; + g_auto(GStrv) terms = NULL; + gboolean is_custom_shortcuts = FALSE; + const gchar *search_text; + + if (self->section_row != NULL) + { + section_data = g_object_get_data (G_OBJECT (self->section_row), "data"); + is_custom_shortcuts = (strcmp (section_data->section_id, "custom") == 0); + + data = g_object_get_data (G_OBJECT (row), "data"); + if (data && strcmp (data->section_id, section_data->section_id) != 0) + return FALSE; + } + + if (row == self->custom_shortcut_add_row) + return is_custom_shortcuts; + + search_text = gtk_editable_get_text (GTK_EDITABLE (self->search_entry)); + if (g_utf8_strlen (search_text, -1) == 0) + return TRUE; + + data = g_object_get_data (G_OBJECT (row), "data"); + item = data->item; + name = cc_util_normalize_casefold_and_unaccent (cc_keyboard_item_get_description (item)); + search = cc_util_normalize_casefold_and_unaccent (search_text); + terms = g_strsplit (search, " ", -1); + + for (guint i = 0; terms && terms[i]; i++) + { + retval = strstr (name, terms[i]) || search_match_shortcut (item, terms[i]); + if (!retval) + break; + } + + return retval; +} + +static gboolean +is_matched_shortcut_present (GtkListBox* listbox, + gpointer user_data) +{ + for (gint i = 0; ; i++) + { + GtkListBoxRow *current = gtk_list_box_get_row_at_index (listbox, i); + if (!current) + return FALSE; + if (shortcut_filter_function (current, user_data)) + return TRUE; + } +} + +static void +shortcut_header_function (GtkListBoxRow *row, + GtkListBoxRow *before, + gpointer user_data) +{ + CcKeyboardShortcutDialog *self = user_data; + gboolean add_header; + ShortcutRowData *data, *before_data; + + data = g_object_get_data (G_OBJECT (row), "data"); + + if (row == self->custom_shortcut_add_row) + { + + add_header = FALSE; + } + else if (before && before != self->custom_shortcut_add_row) + { + before_data = g_object_get_data (G_OBJECT (before), "data"); + add_header = g_strcmp0 (before_data->section_id, data->section_id) != 0; + } + else + { + add_header = TRUE; + } + + if (self->section_row != NULL) + add_header = FALSE; + + if (add_header) + { + GtkWidget *box, *label, *separator; + g_autofree gchar *markup = NULL; + + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + if (!before) + gtk_widget_set_margin_top (box, 6); + + markup = g_strdup_printf ("<b>%s</b>", _(data->section_title)); + label = g_object_new (GTK_TYPE_LABEL, + "label", markup, + "use-markup", TRUE, + "xalign", 0.0, + "margin-start", 6, + NULL); + gtk_box_append (GTK_BOX (box), label); + + separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); + gtk_box_append (GTK_BOX (box), separator); + + gtk_list_box_row_set_header (row, box); + } + else + { + gtk_list_box_row_set_header (row, NULL); + } +} + +static void +cc_keyboard_shortcut_dialog_constructed (GObject *object) +{ + CcKeyboardShortcutDialog *self = CC_KEYBOARD_SHORTCUT_DIALOG (object); + + G_OBJECT_CLASS (cc_keyboard_shortcut_dialog_parent_class)->constructed (object); + + /* Setup the dialog's transient parent */ + gtk_window_set_transient_for (GTK_WINDOW (self->shortcut_editor), GTK_WINDOW (self)); +} + +static void +cc_keyboard_shortcut_dialog_finalize (GObject *object) +{ + CcKeyboardShortcutDialog *self = CC_KEYBOARD_SHORTCUT_DIALOG (object); + + g_clear_object (&self->manager); + g_clear_pointer (&self->sections, g_hash_table_destroy); + g_clear_pointer ((GtkWindow**)&self->shortcut_editor, gtk_window_destroy); +} + +static void +cc_keyboard_shortcut_dialog_class_init (CcKeyboardShortcutDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->constructed = cc_keyboard_shortcut_dialog_constructed; + object_class->finalize = cc_keyboard_shortcut_dialog_finalize; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/keyboard/cc-keyboard-shortcut-dialog.ui"); + + gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, accelerator_sizegroup); + gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, back_button); + gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, custom_shortcut_add_row); + gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, empty_custom_shortcuts_placeholder); + gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, empty_search_placeholder); + gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, headerbar); + gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, header_stack); + gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, reset_all_button); + gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, search_entry); + gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, section_listbox); + gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, section_box); + gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, shortcut_listbox); + gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, shortcut_box); + gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, stack); + + gtk_widget_class_bind_template_callback (widget_class, add_custom_shortcut_clicked_cb); + gtk_widget_class_bind_template_callback (widget_class, back_button_clicked_cb); + gtk_widget_class_bind_template_callback (widget_class, reset_all_clicked_cb); + gtk_widget_class_bind_template_callback (widget_class, search_entry_cb); + gtk_widget_class_bind_template_callback (widget_class, section_row_activated); + gtk_widget_class_bind_template_callback (widget_class, shortcut_row_activated); +} + +static void +cc_keyboard_shortcut_dialog_init (CcKeyboardShortcutDialog *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); + + gtk_search_entry_set_key_capture_widget (self->search_entry, GTK_WIDGET (self)); + + self->manager = cc_keyboard_manager_new (); + + self->shortcut_editor = cc_keyboard_shortcut_editor_new (self->manager); + + self->sections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + self->section_row = NULL; + + g_signal_connect_object (self->manager, + "shortcut-added", + G_CALLBACK (add_item), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (self->manager, + "shortcut-removed", + G_CALLBACK (remove_item), + self, + G_CONNECT_SWAPPED); + + add_section(self, "custom", "Custom Shortcuts"); + cc_keyboard_manager_load_shortcuts (self->manager); + + gtk_list_box_set_sort_func (GTK_LIST_BOX (self->section_listbox), + section_sort_function, + self, + NULL); + + gtk_list_box_set_filter_func (self->shortcut_listbox, + shortcut_filter_function, + self, + NULL); + gtk_list_box_set_header_func (self->shortcut_listbox, + shortcut_header_function, + self, + NULL); + gtk_list_box_set_sort_func (GTK_LIST_BOX (self->shortcut_listbox), + shortcut_sort_function, + self, + NULL); + + show_section_list (self); +} + +GtkWidget* +cc_keyboard_shortcut_dialog_new (void) +{ + return g_object_new (CC_TYPE_KEYBOARD_SHORTCUT_DIALOG, + "use-header-bar", 1, + NULL); +} |