[gnome-control-center] keyboard: Move keyboard shortcuts configuration to a dialog window
- From: Robert Ancell <rancell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-control-center] keyboard: Move keyboard shortcuts configuration to a dialog window
- Date: Sat, 19 Dec 2020 09:48:34 +0000 (UTC)
commit 238327e0ae5e875d76aff973c754c860d4fd0ae2
Author: Ian Douglas Scott <idscott system76 com>
Date: Thu Jul 9 14:54:20 2020 -0700
keyboard: Move keyboard shortcuts configuration to a dialog window
panels/keyboard/cc-keyboard-panel.c | 564 +-------------
panels/keyboard/cc-keyboard-panel.h | 2 -
panels/keyboard/cc-keyboard-panel.ui | 127 +--
panels/keyboard/cc-keyboard-shortcut-dialog.c | 847 +++++++++++++++++++++
panels/keyboard/cc-keyboard-shortcut-dialog.h | 35 +
panels/keyboard/cc-keyboard-shortcut-dialog.ui | 257 +++++++
panels/keyboard/gnome-keyboard-panel.desktop.in.in | 2 +-
panels/keyboard/keyboard.gresource.xml | 1 +
panels/keyboard/meson.build | 1 +
9 files changed, 1200 insertions(+), 636 deletions(-)
---
diff --git a/panels/keyboard/cc-keyboard-panel.c b/panels/keyboard/cc-keyboard-panel.c
index 213ac7f7e..20fd2c907 100644
--- a/panels/keyboard/cc-keyboard-panel.c
+++ b/panels/keyboard/cc-keyboard-panel.c
@@ -1,6 +1,8 @@
-/*
+/* cc-keyboard-panel.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
@@ -17,59 +19,30 @@
*
* 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 <glib/gi18n.h>
-#include "cc-keyboard-shortcut-row.h"
-#include "cc-keyboard-item.h"
-#include "cc-keyboard-manager.h"
#include "cc-keyboard-panel.h"
#include "cc-keyboard-resources.h"
-#include "cc-keyboard-shortcut-editor.h"
+#include "cc-keyboard-shortcut-dialog.h"
#include "cc-xkb-modifier-dialog.h"
#include "keyboard-shortcuts.h"
-#include "cc-util.h"
-
-#define SHORTCUT_DELIMITERS "+ "
-
-typedef struct {
- CcKeyboardItem *item;
- gchar *section_title;
- gchar *section_id;
-} RowData;
-
struct _CcKeyboardPanel
{
CcPanel parent_instance;
- /* Search */
- GtkWidget *empty_search_placeholder;
- GtkWidget *reset_button;
- GtkWidget *search_bar;
- GtkWidget *search_button;
- GtkWidget *search_entry;
- guint search_bar_handler_id;
-
- /* Shortcuts */
- GtkWidget *shortcuts_listbox;
- GtkListBoxRow *add_shortcut_row;
- GtkSizeGroup *accelerator_sizegroup;
-
/* Alternate characters key */
CcXkbModifierDialog *xkb_modifier_dialog;
GSettings *input_source_settings;
GtkWidget *value_alternate_chars;
- /* Custom shortcut dialog */
- GtkWidget *shortcut_editor;
-
- GRegex *pictures_regex;
-
- CcKeyboardManager *manager;
+ GtkListBoxRow *common_shortcuts_row;
};
CC_PANEL_REGISTER (CcKeyboardPanel, cc_keyboard_panel)
@@ -79,11 +52,6 @@ enum {
PROP_PARAMETERS
};
-static const gchar* custom_css =
-"button.reset-shortcut-button {"
-" padding: 0;"
-"}";
-
static const XkbModifier LV3_MODIFIER = {
"lv3:",
N_("Alternate Characters Key"),
@@ -100,420 +68,35 @@ static const XkbModifier LV3_MODIFIER = {
"lv3:ralt_switch",
};
-/* RowData functions */
-static RowData *
-row_data_new (CcKeyboardItem *item,
- const gchar *section_id,
- const gchar *section_title)
-{
- RowData *data;
-
- data = g_new0 (RowData, 1);
- data->item = g_object_ref (item);
- data->section_id = g_strdup (section_id);
- data->section_title = g_strdup (section_title);
-
- return data;
-}
-
-static void
-row_data_free (RowData *data)
-{
- g_object_unref (data->item);
- g_free (data->section_id);
- g_free (data->section_title);
- g_free (data);
-}
-
-static void
-reset_all_shortcuts_cb (GtkWidget *widget,
- gpointer user_data)
-{
- CcKeyboardPanel *self;
- RowData *data;
-
- self = user_data;
-
- if (widget == (GtkWidget *) self->add_shortcut_row)
- return;
-
- data = g_object_get_data (G_OBJECT (widget), "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
-reset_all_clicked_cb (CcKeyboardPanel *self)
-{
- GtkWidget *dialog, *toplevel, *button;
- guint response;
-
- toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
- dialog = gtk_message_dialog_new (GTK_WINDOW (toplevel),
- 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_style_context_add_class (gtk_widget_get_style_context (button), "destructive-action");
-
- /* Reset shortcuts if accepted */
- response = gtk_dialog_run (GTK_DIALOG (dialog));
-
- if (response == GTK_RESPONSE_ACCEPT)
- {
- gtk_container_foreach (GTK_CONTAINER (self->shortcuts_listbox),
- reset_all_shortcuts_cb,
- self);
- }
-
- gtk_widget_destroy (dialog);
-}
-
-static void
-add_item (CcKeyboardPanel *self,
- CcKeyboardItem *item,
- const gchar *section_id,
- const gchar *section_title)
-{
- GtkWidget *row;
-
- 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",
- row_data_new (item, section_id, section_title),
- (GDestroyNotify) row_data_free);
- gtk_container_add (GTK_CONTAINER (self->shortcuts_listbox), row);
-}
-
-static void
-remove_item (CcKeyboardPanel *self,
- CcKeyboardItem *item)
-{
- GList *children, *l;
-
- children = gtk_container_get_children (GTK_CONTAINER (self->shortcuts_listbox));
-
- for (l = children; l != NULL; l = l->next)
- {
- RowData *row_data;
-
- row_data = g_object_get_data (l->data, "data");
-
- if (row_data->item == item)
- {
- gtk_container_remove (GTK_CONTAINER (self->shortcuts_listbox), l->data);
- break;
- }
- }
-
- g_list_free (children);
-}
-
-static gboolean
-strv_contains_prefix_or_match (gchar **strv,
- const gchar *prefix)
-{
- guint i;
-
- 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 (i = 0; strv[i]; i++)
- {
- if (g_str_has_prefix (strv[i], prefix))
- return TRUE;
- }
-
- for (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 ("gtk30", "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)
-{
- GStrv shortcut_tokens, search_tokens;
- g_autofree gchar *normalized_accel = NULL;
- g_autofree gchar *accel = NULL;
- gboolean match;
- guint i;
- GList *key_combos, *l;
- CcKeyCombo *combo;
-
- key_combos = cc_keyboard_item_get_key_combos (item);
- for (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 (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;
- }
-
- g_strfreev (shortcut_tokens);
- g_strfreev (search_tokens);
-
- if (match)
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gint
-sort_function (GtkListBoxRow *a,
- GtkListBoxRow *b,
- gpointer user_data)
-{
- CcKeyboardPanel *self;
- RowData *a_data, *b_data;
- gint retval;
-
- self = user_data;
-
- if (a == self->add_shortcut_row)
- return 1;
-
- if (b == self->add_shortcut_row)
- return -1;
-
- 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 (cc_keyboard_item_get_item_type (a_data->item) == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH)
- return 1;
- else if (cc_keyboard_item_get_item_type (b_data->item) == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH)
- return -1;
-
- 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 void
-header_function (GtkListBoxRow *row,
- GtkListBoxRow *before,
- gpointer user_data)
+alternate_chars_activated (GtkWidget *button,
+ GtkListBoxRow *row,
+ CcKeyboardPanel *self)
{
- CcKeyboardPanel *self;
- gboolean add_header;
- RowData *data;
-
- self = user_data;
- add_header = FALSE;
-
- /* The + row always has a separator */
- if (row == self->add_shortcut_row)
- {
- GtkWidget *separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
- gtk_widget_show (separator);
-
- gtk_list_box_row_set_header (row, separator);
-
- return;
- }
-
- data = g_object_get_data (G_OBJECT (row), "data");
-
- if (before)
- {
- RowData *before_data = g_object_get_data (G_OBJECT (before), "data");
-
- if (before_data)
- add_header = g_strcmp0 (before_data->section_id, data->section_id) != 0;
- }
- else
- {
- add_header = TRUE;
- }
-
- if (add_header)
- {
- GtkWidget *box, *label, *separator;
- g_autofree gchar *markup = NULL;
-
- box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
- gtk_widget_show (box);
- gtk_widget_set_margin_top (box, before ? 18 : 6);
-
- markup = g_strdup_printf ("<b>%s</b>", _(data->section_title));
- label = gtk_label_new (NULL);
- gtk_label_set_markup (GTK_LABEL (label), markup);
- gtk_label_set_xalign (GTK_LABEL (label), 0.0);
- gtk_widget_set_margin_start (label, 6);
- gtk_widget_show (label);
- gtk_style_context_add_class (gtk_widget_get_style_context (label), "dim-label");
- gtk_container_add (GTK_CONTAINER (box), label);
-
- separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
- gtk_widget_show (separator);
- gtk_container_add (GTK_CONTAINER (box), separator);
-
- gtk_list_box_row_set_header (row, box);
- }
- else
- {
- gtk_list_box_row_set_header (row, NULL);
- }
-}
+ GtkWindow *window;
-static gboolean
-filter_function (GtkListBoxRow *row,
- gpointer user_data)
-{
- CcKeyboardPanel *self = user_data;
- CcKeyboardItem *item;
- RowData *data;
- gboolean retval;
- g_autofree gchar *search = NULL;
- g_autofree gchar *name = NULL;
- g_auto(GStrv) terms = NULL;
- guint i;
-
- if (gtk_entry_get_text_length (GTK_ENTRY (self->search_entry)) == 0)
- return TRUE;
-
- /* When searching, the '+' row is always hidden */
- if (row == self->add_shortcut_row)
- return FALSE;
-
- 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 (gtk_entry_get_text (GTK_ENTRY (self->search_entry)));
- terms = g_strsplit (search, " ", -1);
-
- for (i = 0; terms && terms[i]; i++)
- {
- retval = strstr (name, terms[i]) || search_match_shortcut (item, terms[i]);
- if (!retval)
- break;
- }
+ window = GTK_WINDOW (cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (self))));
- return retval;
+ gtk_window_set_transient_for (GTK_WINDOW (self->xkb_modifier_dialog), window);
+ gtk_widget_show (GTK_WIDGET (self->xkb_modifier_dialog));
}
static void
-shortcut_row_activated (GtkWidget *button,
- GtkListBoxRow *row,
- CcKeyboardPanel *self)
+keyboard_shortcuts_activated (GtkWidget *button,
+ GtkListBoxRow *row,
+ CcKeyboardPanel *self)
{
- CcKeyboardShortcutEditor *editor;
-
- editor = CC_KEYBOARD_SHORTCUT_EDITOR (self->shortcut_editor);
+ GtkWindow *window;
+ GtkWidget *shortcut_dialog;
- if (row != self->add_shortcut_row)
+ if (row == self->common_shortcuts_row)
{
- RowData *data = g_object_get_data (G_OBJECT (row), "data");
+ window = GTK_WINDOW (cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (self))));
- cc_keyboard_shortcut_editor_set_mode (editor, CC_SHORTCUT_EDITOR_EDIT);
- cc_keyboard_shortcut_editor_set_item (editor, data->item);
+ shortcut_dialog = cc_keyboard_shortcut_dialog_new ();
+ gtk_window_set_transient_for (GTK_WINDOW (shortcut_dialog), window);
+ gtk_widget_show (GTK_WIDGET (shortcut_dialog));
}
- else
- {
- 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
-alternate_chars_activated (GtkWidget *button,
- GtkListBoxRow *row,
- CcKeyboardPanel *self)
-{
- GtkWindow *window;
-
- window = GTK_WINDOW (cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (self))));
-
- gtk_window_set_transient_for (GTK_WINDOW (self->xkb_modifier_dialog), window);
- gtk_widget_show (GTK_WIDGET (self->xkb_modifier_dialog));
}
static void
@@ -542,45 +125,12 @@ static void
cc_keyboard_panel_finalize (GObject *object)
{
CcKeyboardPanel *self = CC_KEYBOARD_PANEL (object);
- GtkWidget *window;
- g_clear_pointer (&self->pictures_regex, g_regex_unref);
- g_clear_object (&self->accelerator_sizegroup);
g_clear_object (&self->input_source_settings);
- if (self->search_bar_handler_id != 0)
- {
- window = cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (self)));
- g_signal_handler_disconnect (window, self->search_bar_handler_id);
- }
-
G_OBJECT_CLASS (cc_keyboard_panel_parent_class)->finalize (object);
}
-static void
-cc_keyboard_panel_constructed (GObject *object)
-{
- CcKeyboardPanel *self = CC_KEYBOARD_PANEL (object);
- GtkWindow *toplevel;
- CcShell *shell;
-
- G_OBJECT_CLASS (cc_keyboard_panel_parent_class)->constructed (object);
-
- /* Setup the dialog's transient parent */
- shell = cc_panel_get_shell (CC_PANEL (self));
- toplevel = GTK_WINDOW (cc_shell_get_toplevel (shell));
- gtk_window_set_transient_for (GTK_WINDOW (self->shortcut_editor), toplevel);
-
- cc_shell_embed_widget_in_header (shell, self->reset_button, GTK_POS_LEFT);
- cc_shell_embed_widget_in_header (shell, self->search_button, GTK_POS_RIGHT);
-
- self->search_bar_handler_id =
- g_signal_connect_swapped (toplevel,
- "key-press-event",
- G_CALLBACK (gtk_search_bar_handle_event),
- self->search_bar);
-}
-
static void
cc_keyboard_panel_class_init (CcKeyboardPanelClass *klass)
{
@@ -592,45 +142,25 @@ cc_keyboard_panel_class_init (CcKeyboardPanelClass *klass)
object_class->set_property = cc_keyboard_panel_set_property;
object_class->finalize = cc_keyboard_panel_finalize;
- object_class->constructed = cc_keyboard_panel_constructed;
g_object_class_override_property (object_class, PROP_PARAMETERS, "parameters");
gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/control-center/keyboard/cc-keyboard-panel.ui");
- gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, add_shortcut_row);
- gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, empty_search_placeholder);
- gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, reset_button);
- gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, search_bar);
- gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, search_button);
- gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, search_entry);
- gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, shortcuts_listbox);
gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, value_alternate_chars);
+ gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, common_shortcuts_row);
- gtk_widget_class_bind_template_callback (widget_class, reset_all_clicked_cb);
- gtk_widget_class_bind_template_callback (widget_class, shortcut_row_activated);
gtk_widget_class_bind_template_callback (widget_class, alternate_chars_activated);
+ gtk_widget_class_bind_template_callback (widget_class, keyboard_shortcuts_activated);
}
static void
cc_keyboard_panel_init (CcKeyboardPanel *self)
{
- GtkCssProvider *provider;
-
g_resources_register (cc_keyboard_get_resource ());
gtk_widget_init_template (GTK_WIDGET (self));
- /* Custom CSS */
- provider = gtk_css_provider_new ();
- gtk_css_provider_load_from_data (provider, custom_css, -1, NULL);
-
- gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
- GTK_STYLE_PROVIDER (provider),
- GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 1);
-
- g_object_unref (provider);
-
/* Alternate characters key */
self->input_source_settings = g_settings_new ("org.gnome.desktop.input-sources");
g_settings_bind_with_mapping (self->input_source_settings,
@@ -644,46 +174,4 @@ cc_keyboard_panel_init (CcKeyboardPanel *self)
NULL);
self->xkb_modifier_dialog = cc_xkb_modifier_dialog_new (self->input_source_settings, &LV3_MODIFIER);
-
- /* Shortcut manager */
- self->manager = cc_keyboard_manager_new ();
-
- /* Shortcut editor dialog */
- self->shortcut_editor = cc_keyboard_shortcut_editor_new (self->manager);
-
- /* Use a sizegroup to make the accelerator labels the same width */
- self->accelerator_sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
-
- 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);
-
- cc_keyboard_manager_load_shortcuts (self->manager);
-
- /* Setup the shortcuts shortcuts_listbox */
- gtk_list_box_set_sort_func (GTK_LIST_BOX (self->shortcuts_listbox),
- sort_function,
- self,
- NULL);
-
- gtk_list_box_set_header_func (GTK_LIST_BOX (self->shortcuts_listbox),
- header_function,
- self,
- NULL);
-
- gtk_list_box_set_filter_func (GTK_LIST_BOX (self->shortcuts_listbox),
- filter_function,
- self,
- NULL);
-
- gtk_list_box_set_placeholder (GTK_LIST_BOX (self->shortcuts_listbox), self->empty_search_placeholder);
}
-
diff --git a/panels/keyboard/cc-keyboard-panel.h b/panels/keyboard/cc-keyboard-panel.h
index db6e35278..3d3076b2d 100644
--- a/panels/keyboard/cc-keyboard-panel.h
+++ b/panels/keyboard/cc-keyboard-panel.h
@@ -29,6 +29,4 @@ G_BEGIN_DECLS
#define CC_TYPE_KEYBOARD_PANEL (cc_keyboard_panel_get_type ())
G_DECLARE_FINAL_TYPE (CcKeyboardPanel, cc_keyboard_panel, CC, KEYBOARD_PANEL, CcPanel)
-CcKeyboardItem* cc_keyboard_panel_create_custom_item (CcKeyboardPanel *self);
-
G_END_DECLS
diff --git a/panels/keyboard/cc-keyboard-panel.ui b/panels/keyboard/cc-keyboard-panel.ui
index 7c5073e33..7c977db69 100644
--- a/panels/keyboard/cc-keyboard-panel.ui
+++ b/panels/keyboard/cc-keyboard-panel.ui
@@ -12,26 +12,11 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="expand">True</property>
- <signal name="key-press-event" handler="gtk_search_bar_handle_event" object="search_bar" swapped="yes" />
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
- <child>
- <object class="GtkSearchBar" id="search_bar">
- <property name="visible">True</property>
- <property name="hexpand">True</property>
- <property name="search_mode_enabled" bind-source="search_button" bind-property="active"
bind-flags="bidirectional" />
- <child>
- <object class="GtkSearchEntry" id="search_entry">
- <property name="visible">True</property>
- <property name="width_chars">30</property>
- <signal name="notify::text" handler="gtk_list_box_invalidate_filter"
object="shortcuts_listbox" swapped="yes" />
- </object>
- </child>
- </object>
- </child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
@@ -53,6 +38,17 @@
<property name="margin_right">18</property>
<property name="spacing">12</property>
<property name="halign">center</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Type Special Characters</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
@@ -68,7 +64,6 @@
<object class="HdyActionRow">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="use-underline">true</property>
<property name="title" translatable="yes">Alternate Characters Key</property>
<property name="subtitle" translatable="yes">Hold down and type to enter
different characters</property>
<property name="activatable">True</property>
@@ -94,34 +89,41 @@
</child>
</object>
</child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Keyboard Shortcuts</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
- <object class="GtkListBox" id="shortcuts_listbox">
+ <object class="GtkListBox" id="keyboard_shortcuts_listbox">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="selection-mode">none</property>
<property name="width-request">250</property>
- <signal name="row-activated" handler="shortcut_row_activated"
object="CcKeyboardPanel" swapped="no" />
+ <signal name="row-activated" handler="keyboard_shortcuts_activated"
object="CcKeyboardPanel" swapped="no" />
<child>
- <object class="GtkListBoxRow" id="add_shortcut_row">
+ <object class="HdyActionRow" id="common_shortcuts_row">
<property name="visible">True</property>
<property name="can_focus">True</property>
+ <property name="title" translatable="yes">Customize Shortcuts</property>
+ <property name="activatable">True</property>
<child>
- <object class="GtkBox">
+ <object class="GtkImage">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="hexpand">True</property>
- <property name="border_width">6</property>
- <child type="center">
- <object class="GtkImage">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">list-add-symbolic</property>
- </object>
- </child>
+ <property name="icon_name">go-next-symbolic</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
</object>
</child>
</object>
@@ -137,69 +139,4 @@
</object>
</child>
</template>
-
- <!-- Header widgets -->
- <object class="GtkToggleButton" id="search_button">
- <property name="visible">True</property>
- <style>
- <class name="image-button" />
- </style>
- <child>
- <object class="GtkImage">
- <property name="visible">True</property>
- <property name="icon_name">system-search-symbolic</property>
- </object>
- </child>
- </object>
- <object class="GtkButton" id="reset_button">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="label" translatable="yes">Reset All…</property>
- <property name="tooltip-text" translatable="yes">Reset all shortcuts to their default
keybindings</property>
- <signal name="clicked" handler="reset_all_clicked_cb" object="CcKeyboardPanel" swapped="yes" />
- </object>
-
- <object class="GtkBox" id="empty_search_placeholder">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">center</property>
- <property name="valign">center</property>
- <property name="hexpand">True</property>
- <property name="vexpand">True</property>
- <property name="border_width">18</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkImage">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="pixel_size">80</property>
- <property name="icon_name">edit-find-symbolic</property>
- <style>
- <class name="dim-label"/>
- </style>
- </object>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">No keyboard shortcut found</property>
- <attributes>
- <attribute name="weight" value="bold"/>
- <attribute name="scale" value="1.44"/>
- </attributes>
- </object>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Try a different search</property>
- <style>
- <class name="dim-label"/>
- </style>
- </object>
- </child>
- </object>
</interface>
diff --git a/panels/keyboard/cc-keyboard-shortcut-dialog.c b/panels/keyboard/cc-keyboard-shortcut-dialog.c
new file mode 100644
index 000000000..c41787eb3
--- /dev/null
+++ b/panels/keyboard/cc-keyboard-shortcut-dialog.c
@@ -0,0 +1,847 @@
+/* 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 <glib/gi18n.h>
+#define HANDY_USE_UNSTABLE_API
+#include <handy.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 "list-box-helper.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;
+ GtkRevealer *back_revealer;
+ GtkWidget *custom_shortcut_add_box;
+ guint custom_shortcut_count;
+ GtkWidget *empty_custom_shortcuts_placeholder;
+ GtkWidget *empty_search_placeholder;
+ GtkHeaderBar *headerbar;
+ GtkRevealer *reset_all_revealer;
+ GtkSearchEntry *search_entry;
+ GtkListBox *section_listbox;
+ GtkListBoxRow *section_row;
+ GtkScrolledWindow *section_scrolled_window;
+ GtkListBox *shortcut_listbox;
+ GtkScrolledWindow *shortcut_scrolled_window;
+ GtkStack *stack;
+
+ CcKeyboardManager *manager;
+ GtkWidget *shortcut_editor;
+ GHashTable *sections;
+ };
+
+G_DEFINE_TYPE (CcKeyboardShortcutDialog, cc_keyboard_shortcut_dialog, GTK_TYPE_DIALOG)
+
+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, *label, *box;
+ GtkListBoxRow *row;
+
+ icon = g_object_new (GTK_TYPE_IMAGE,
+ "visible", 1,
+ "icon_name", "go-next-symbolic",
+ NULL);
+ gtk_style_context_add_class (gtk_widget_get_style_context (icon), "dim-label");
+
+ modified_label = g_object_new (GTK_TYPE_LABEL,
+ "visible", 1,
+ NULL);
+ gtk_style_context_add_class (gtk_widget_get_style_context (modified_label), "dim-label");
+
+ label = g_object_new (GTK_TYPE_LABEL,
+ "visible", 1,
+ "label", _(section_title),
+ NULL);
+ gtk_style_context_add_class (gtk_widget_get_style_context (label), "row-label");
+
+ box = g_object_new (GTK_TYPE_BOX,
+ "visible", 1,
+ "spacing", 8,
+ "margin_left", 12,
+ "margin_right", 12,
+ "margin_top", 8,
+ "margin_bottom", 8,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (box), label);
+ gtk_box_pack_end (GTK_BOX (box), icon, FALSE, FALSE, 0);
+ gtk_box_pack_end (GTK_BOX (box), modified_label, FALSE, FALSE, 0);
+
+ row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
+ "visible", 1,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (row), box);
+
+ 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_container_add (GTK_CONTAINER (self->section_listbox), GTK_WIDGET (row));
+
+ return row;
+}
+
+static void
+set_custom_shortcut_add_box_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, GTK_WIDGET (self->shortcut_scrolled_window));
+ }
+
+ gtk_widget_set_visible (self->custom_shortcut_add_box, is_custom_shortcuts);
+}
+
+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_add_box_visibility (self);
+ }
+
+ gtk_container_add (GTK_CONTAINER (self->shortcut_listbox), row);
+}
+
+static void
+remove_item (CcKeyboardShortcutDialog *self,
+ CcKeyboardItem *item)
+{
+ g_autoptr(GList) children;
+
+ children = gtk_container_get_children (GTK_CONTAINER (self->shortcut_listbox));
+
+ for (GList *l = children; l != NULL; l = l->next)
+ {
+ ShortcutRowData *row_data;
+
+ row_data = g_object_get_data (l->data, "data");
+
+ if (row_data->item == item)
+ {
+ if (strcmp (row_data->section_id, "custom") == 0)
+ {
+ self->custom_shortcut_count--;
+ set_custom_shortcut_add_box_visibility (self);
+ }
+
+ gtk_container_remove (GTK_CONTAINER (self->shortcut_listbox), l->data);
+ break;
+ }
+ }
+}
+
+static void
+update_modified_counts (CcKeyboardShortcutDialog *self)
+{
+ g_autoptr(GList) sections = NULL, shortcuts = NULL;
+ SectionRowData *section_data;
+ ShortcutRowData *shortcut_data;
+ g_autofree gchar *modified_text = NULL;
+
+ sections = gtk_container_get_children (GTK_CONTAINER (self->section_listbox));
+ shortcuts = gtk_container_get_children (GTK_CONTAINER (self->shortcut_listbox));
+
+ for (GList *l = sections; l != NULL; l = l->next)
+ {
+ section_data = g_object_get_data (G_OBJECT (l->data), "data");
+ section_data->modified_count = 0;
+ }
+
+ for (GList *l = shortcuts; l != NULL; l = l->next)
+ {
+ shortcut_data = g_object_get_data (G_OBJECT (l->data), "data");
+ if (!cc_keyboard_item_is_value_default (shortcut_data->item))
+ shortcut_data->section_data->modified_count++;
+ }
+
+ for (GList *l = sections; l != NULL; l = l->next)
+ {
+ section_data = g_object_get_data (G_OBJECT (l->data), "data");
+ if (section_data->modified_count > 0)
+ {
+ modified_text = g_strdup_printf ("%d %s", section_data->modified_count, _("modified"));
+ 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, GTK_WIDGET (self->section_scrolled_window));
+ gtk_header_bar_set_title (self->headerbar, _("Keyboard Shortcuts"));
+ gtk_entry_set_text(GTK_ENTRY (self->search_entry), "");
+ gtk_revealer_set_reveal_child (self->reset_all_revealer, TRUE);
+ gtk_revealer_set_reveal_child (self->back_revealer, FALSE);
+ 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);
+ }
+
+ if (is_custom_shortcuts)
+ gtk_list_box_set_placeholder (self->shortcut_listbox, NULL);
+ else
+ gtk_list_box_set_placeholder (self->shortcut_listbox, self->empty_search_placeholder);
+
+ 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, GTK_WIDGET (self->shortcut_scrolled_window));
+
+ gtk_header_bar_set_title (self->headerbar, title);
+ set_custom_shortcut_add_box_visibility (self);
+ gtk_revealer_set_reveal_child (self->reset_all_revealer, FALSE);
+ gtk_revealer_set_reveal_child (self->back_revealer, TRUE);
+ gtk_widget_set_visible (GTK_WIDGET (self->search_entry), self->section_row == NULL);
+
+}
+
+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;
+
+ 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
+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
+back_button_clicked_cb (CcKeyboardShortcutDialog *self)
+{
+ show_section_list (self);
+}
+
+static void
+reset_all_shortcuts_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ CcKeyboardShortcutDialog *self;
+ ShortcutRowData *data;
+
+ self = user_data;
+
+ data = g_object_get_data (G_OBJECT (widget), "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
+reset_all_clicked_cb (CcKeyboardShortcutDialog *self)
+{
+ GtkWidget *dialog, *toplevel, *button;
+ guint response;
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
+ dialog = gtk_message_dialog_new (GTK_WINDOW (toplevel),
+ 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_style_context_add_class (gtk_widget_get_style_context (button), "destructive-action");
+
+ /* Reset shortcuts if accepted */
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ if (response == GTK_RESPONSE_ACCEPT)
+ {
+ gtk_container_foreach (GTK_CONTAINER (self->shortcut_listbox),
+ reset_all_shortcuts_cb,
+ self);
+ }
+
+ gtk_widget_destroy (dialog);
+
+ update_modified_counts (self);
+}
+
+static void
+search_entry_cb (CcKeyboardShortcutDialog *self)
+{
+ if (gtk_entry_get_text_length (GTK_ENTRY (self->search_entry)) == 0 && self->section_row == NULL)
+ show_section_list (self);
+ else if (gtk_stack_get_visible_child (self->stack) != GTK_WIDGET (self->shortcut_scrolled_window))
+ show_shortcut_list (self);
+ else
+ gtk_list_box_invalidate_filter (self->shortcut_listbox);
+}
+
+static void
+key_press_cb (CcKeyboardShortcutDialog *self, GdkEvent *event)
+{
+ if (gtk_widget_get_visible (GTK_WIDGET (self->search_entry)))
+ gtk_search_entry_handle_event (self->search_entry, event);
+}
+
+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 ("gtk30", "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)
+{
+ ShortcutRowData *a_data, *b_data;
+ gint retval;
+
+ 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;
+
+ 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;
+
+ if (self->section_row != NULL)
+ {
+ section_data = g_object_get_data (G_OBJECT (self->section_row), "data");
+ data = g_object_get_data (G_OBJECT (row), "data");
+ if (strcmp (data->section_id, section_data->section_id) != 0)
+ return FALSE;
+ }
+
+ if (gtk_entry_get_text_length (GTK_ENTRY (self->search_entry)) == 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 (gtk_entry_get_text (GTK_ENTRY (self->search_entry)));
+ 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 void
+shortcut_header_function (GtkListBoxRow *row,
+ GtkListBoxRow *before,
+ gpointer user_data)
+{
+ CcKeyboardShortcutDialog *self;
+ gboolean add_header;
+ ShortcutRowData *data, *before_data;
+
+ data = g_object_get_data (G_OBJECT (row), "data");
+
+ self = user_data;
+ add_header = FALSE;
+
+ if (before)
+ {
+ 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);
+ gtk_widget_show (box);
+ if (!before)
+ gtk_widget_set_margin_top (box, 6);
+
+ if (before)
+ {
+ separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_widget_show (separator);
+ gtk_container_add (GTK_CONTAINER (box), separator);
+ }
+
+ 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_widget_show (label);
+ gtk_container_add (GTK_CONTAINER (box), label);
+
+ separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_widget_show (separator);
+ gtk_container_add (GTK_CONTAINER (box), separator);
+
+ gtk_list_box_row_set_header (row, box);
+ }
+ else if (before)
+ {
+ GtkWidget *separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_widget_show (separator);
+
+ gtk_list_box_row_set_header (row, separator);
+ }
+ 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_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;
+
+ 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_revealer);
+ gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, custom_shortcut_add_box);
+ 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, reset_all_revealer);
+ 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_scrolled_window);
+ gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, shortcut_listbox);
+ gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, shortcut_scrolled_window);
+ 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, key_press_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));
+
+ 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_header_func (self->section_listbox, cc_list_box_update_header_func, NULL, NULL);
+ 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);
+}
diff --git a/panels/keyboard/cc-keyboard-shortcut-dialog.h b/panels/keyboard/cc-keyboard-shortcut-dialog.h
new file mode 100644
index 000000000..4493dc2ce
--- /dev/null
+++ b/panels/keyboard/cc-keyboard-shortcut-dialog.h
@@ -0,0 +1,35 @@
+/* cc-keyboard-shortcut-dialog.h
+ *
+ * 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: Ian Douglas Scott <idscott system76 com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_KEYBOARD_SHORTCUT_DIALOG (cc_keyboard_shortcut_dialog_get_type ())
+
+G_DECLARE_FINAL_TYPE (CcKeyboardShortcutDialog, cc_keyboard_shortcut_dialog, CC, KEYBOARD_SHORTCUT_DIALOG,
GtkDialog)
+
+GtkWidget* cc_keyboard_shortcut_dialog_new (void);
+
+G_END_DECLS
\ No newline at end of file
diff --git a/panels/keyboard/cc-keyboard-shortcut-dialog.ui b/panels/keyboard/cc-keyboard-shortcut-dialog.ui
new file mode 100644
index 000000000..b158ef532
--- /dev/null
+++ b/panels/keyboard/cc-keyboard-shortcut-dialog.ui
@@ -0,0 +1,257 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <template class="CcKeyboardShortcutDialog" parent="GtkDialog">
+ <property name="modal">True</property>
+ <signal name="key-press-event" handler="key_press_cb" object="CcKeyboardShortcutDialog" swapped="yes" />
+ <child internal-child="vbox">
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkSearchEntry" id="search_entry">
+ <property name="visible">True</property>
+ <property name="margin-top">12</property>
+ <property name="width_chars">30</property>
+ <property name="halign">center</property>
+ <signal name="notify::text" handler="search_entry_cb" object="CcKeyboardShortcutDialog"
swapped="yes" />
+ </object>
+ </child>
+ <child>
+ <object class="GtkStack" id="stack">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkScrolledWindow" id="section_scrolled_window">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="propagate_natural_width">True</property>
+ <property name="propagate_natural_height">True</property>
+ <property name="max_content_height">350</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="border_width">12</property>
+ <child>
+ <object class="GtkListBox" id="section_listbox">
+ <property name="visible">True</property>
+ <property name="selection-mode">none</property>
+ <property name="border_width">12</property>
+ <signal name="row-activated" handler="section_row_activated"
object="CcKeyboardShortcutDialog" swapped="no" />
+ <style>
+ <class name="frame" />
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="shortcut_scrolled_window">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="propagate_natural_width">True</property>
+ <property name="propagate_natural_height">True</property>
+ <property name="max_content_height">350</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="border_width">12</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkListBox" id="custom_shortcut_add_box">
+ <property name="selection-mode">none</property>
+ <style>
+ <class name="frame" />
+ </style>
+ <child>
+ <object class="HdyActionRow">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Add Custom Shortcuts</property>
+ <property name="subtitle" translatable="yes">Set up custom shortcuts for
launching apps, running scripts, and more.</property>
+ <child>
+ <object class="GtkButton">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ <property name="label" translatable="yes">Add Shortcut</property>
+ <signal name="clicked" handler="add_custom_shortcut_clicked_cb"
object="CcKeyboardShortcutDialog" swapped="yes" />
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBox" id="shortcut_listbox">
+ <property name="visible">True</property>
+ <property name="selection-mode">none</property>
+ <style>
+ <class name="frame" />
+ </style>
+ <signal name="row-activated" handler="shortcut_row_activated"
object="CcKeyboardShortcutDialog" swapped="no" />
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="empty_custom_shortcuts_placeholder">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="border_width">18</property>
+ <property name="spacing">18</property>
+ <property name="valign">center</property>
+ <style>
+ <class name="background"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon-name">input-keyboard-symbolic</property>
+ <property name="pixel-size">128</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Add Custom Shortcuts</property>
+ <attributes>
+ <attribute name="weight" value="bold" />
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Set up custom shortcuts for launching apps,
running scripts, and more.</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton">
+ <property name="visible">True</property>
+ <property name="halign">center</property>
+ <property name="label" translatable="yes">Add Shortcut</property>
+ <style>
+ <class name="suggested-action" />
+ </style>
+ <signal name="clicked" handler="add_custom_shortcut_clicked_cb"
object="CcKeyboardShortcutDialog" swapped="yes" />
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="titlebar">
+ <object class="GtkHeaderBar" id="headerbar">
+ <property name="visible">True</property>
+ <property name="show_close_button">True</property>
+ <child>
+ <object class="GtkRevealer" id="back_revealer">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="transition-type">crossfade</property>
+ <child>
+ <object class="GtkButton" id="back">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="receives_default">False</property>
+ <property name="valign">center</property>
+ <property name="use-underline">True</property>
+ <signal name="clicked" handler="back_button_clicked_cb" object="CcKeyboardShortcutDialog"
swapped="yes" />
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="a11y-back">
+ <property name="accessible-name" translatable="yes">Back</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImage" id="back_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">go-previous-symbolic</property>
+ <property name="icon_size">1</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkRevealer" id="reset_all_revealer">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="transition-type">crossfade</property>
+ <child>
+ <object class="GtkButton" id="reset_all_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Reset All…</property>
+ <property name="tooltip-text" translatable="yes">Reset all shortcuts to their default
keybindings</property>
+ <signal name="clicked" handler="reset_all_clicked_cb" object="CcKeyboardShortcutDialog"
swapped="yes" />
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="pack_type">end</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkBox" id="empty_search_placeholder">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="border_width">18</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="pixel_size">80</property>
+ <property name="icon_name">edit-find-symbolic</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">No keyboard shortcut found</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ <attribute name="scale" value="1.44"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Try a different search</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ <object class="GtkSizeGroup" id="accelerator_sizegroup" />
+</interface>
diff --git a/panels/keyboard/gnome-keyboard-panel.desktop.in.in
b/panels/keyboard/gnome-keyboard-panel.desktop.in.in
index 9200d692c..b7b562716 100644
--- a/panels/keyboard/gnome-keyboard-panel.desktop.in.in
+++ b/panels/keyboard/gnome-keyboard-panel.desktop.in.in
@@ -1,5 +1,5 @@
[Desktop Entry]
-Name=Keyboard Shortcuts
+Name=Keyboard
Comment=View and change keyboard shortcuts and set your typing preferences
Exec=gnome-control-center keyboard
# Translators: Do NOT translate or transliterate this text (this is an icon file name)!
diff --git a/panels/keyboard/keyboard.gresource.xml b/panels/keyboard/keyboard.gresource.xml
index fc210ce75..14c05d8a1 100644
--- a/panels/keyboard/keyboard.gresource.xml
+++ b/panels/keyboard/keyboard.gresource.xml
@@ -4,6 +4,7 @@
<file preprocess="xml-stripblanks">enter-keyboard-shortcut.svg</file>
<file preprocess="xml-stripblanks">cc-xkb-modifier-dialog.ui</file>
<file preprocess="xml-stripblanks">cc-keyboard-shortcut-row.ui</file>
+ <file preprocess="xml-stripblanks">cc-keyboard-shortcut-dialog.ui</file>
<file preprocess="xml-stripblanks">cc-keyboard-panel.ui</file>
<file preprocess="xml-stripblanks">cc-keyboard-shortcut-editor.ui</file>
</gresource>
diff --git a/panels/keyboard/meson.build b/panels/keyboard/meson.build
index 64c6a988c..8055becf9 100644
--- a/panels/keyboard/meson.build
+++ b/panels/keyboard/meson.build
@@ -58,6 +58,7 @@ endforeach
sources = files(
'cc-xkb-modifier-dialog.c',
'cc-keyboard-shortcut-row.c',
+ 'cc-keyboard-shortcut-dialog.c',
'cc-keyboard-panel.c',
'cc-keyboard-item.c',
'cc-keyboard-manager.c',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]