Line |
Branch |
Exec |
Source |
1 |
|
|
/* cc-keyboard-shortcut-dialog.c |
2 |
|
|
* |
3 |
|
|
* Copyright (C) 2010 Intel, Inc |
4 |
|
|
* Copyright (C) 2016 Endless, Inc |
5 |
|
|
* Copyright (C) 2020 System76, Inc. |
6 |
|
|
* Copyright (C) 2022 Purism SPC |
7 |
|
|
* |
8 |
|
|
* This program is free software; you can redistribute it and/or modify |
9 |
|
|
* it under the terms of the GNU General Public License as published by |
10 |
|
|
* the Free Software Foundation; either version 2 of the License, or |
11 |
|
|
* (at your option) any later version. |
12 |
|
|
* |
13 |
|
|
* This program is distributed in the hope that it will be useful, |
14 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 |
|
|
* GNU General Public License for more details. |
17 |
|
|
* |
18 |
|
|
* You should have received a copy of the GNU General Public License |
19 |
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>. |
20 |
|
|
* |
21 |
|
|
* Author: Thomas Wood <thomas.wood@intel.com> |
22 |
|
|
* Georges Basile Stavracas Neto <gbsneto@gnome.org> |
23 |
|
|
* Ian Douglas Scott <idscott@system76.com> |
24 |
|
|
* Mohammed Sadiq <sadiq@sadiqpk.org> |
25 |
|
|
* |
26 |
|
|
* SPDX-License-Identifier: GPL-2.0-or-later |
27 |
|
|
*/ |
28 |
|
|
|
29 |
|
|
#include <config.h> |
30 |
|
|
#include <glib/gi18n.h> |
31 |
|
|
#include <adwaita.h> |
32 |
|
|
|
33 |
|
|
#include "cc-keyboard-shortcut-dialog.h" |
34 |
|
|
#include "cc-keyboard-item.h" |
35 |
|
|
#include "cc-keyboard-manager.h" |
36 |
|
|
#include "cc-keyboard-shortcut-editor.h" |
37 |
|
|
#include "cc-keyboard-shortcut-group.h" |
38 |
|
|
#include "cc-keyboard-shortcut-row.h" |
39 |
|
|
#include "cc-list-row.h" |
40 |
|
|
#include "cc-util.h" |
41 |
|
|
#include "keyboard-shortcuts.h" |
42 |
|
|
|
43 |
|
|
struct _CcKeyboardShortcutDialog |
44 |
|
|
{ |
45 |
|
|
AdwWindow parent_instance; |
46 |
|
|
|
47 |
|
|
AdwNavigationView *navigation_view; |
48 |
|
|
AdwNavigationPage *main_page; |
49 |
|
|
GtkButton *reset_all_button; |
50 |
|
|
GtkSearchEntry *search_entry; |
51 |
|
|
GtkStack *section_stack; |
52 |
|
|
AdwPreferencesPage *section_list_page; |
53 |
|
|
GtkListBox *section_list_box; |
54 |
|
|
AdwPreferencesPage *search_result_page; |
55 |
|
|
AdwStatusPage *empty_results_page; |
56 |
|
|
|
57 |
|
|
AdwNavigationPage *subview_page; |
58 |
|
|
GtkStack *subview_stack; |
59 |
|
|
GtkStack *shortcut_list_stack; |
60 |
|
|
AdwStatusPage *empty_custom_shortcut_page; |
61 |
|
|
GtkSizeGroup *accelerator_size_group; |
62 |
|
|
|
63 |
|
|
/* A GListStore of sections containing a GListStore of CcKeyboardItem */ |
64 |
|
|
GListStore *sections; |
65 |
|
|
GListStore *visible_section; |
66 |
|
|
GtkFlattenListModel *filtered_shortcuts; |
67 |
|
|
|
68 |
|
|
CcKeyboardManager *manager; |
69 |
|
|
GtkWidget *shortcut_editor; |
70 |
|
|
GStrv search_terms; |
71 |
|
|
}; |
72 |
|
|
|
73 |
|
✗ |
G_DEFINE_TYPE (CcKeyboardShortcutDialog, cc_keyboard_shortcut_dialog, ADW_TYPE_WINDOW) |
74 |
|
|
|
75 |
|
|
|
76 |
|
|
static GListStore * |
77 |
|
✗ |
keyboard_shortcut_get_section_store (CcKeyboardShortcutDialog *self, |
78 |
|
|
const char *section_id, |
79 |
|
|
const char *section_title) |
80 |
|
|
{ |
81 |
|
✗ |
g_autoptr(GListStore) section = NULL; |
82 |
|
|
GtkWidget *group; |
83 |
|
|
guint n_items; |
84 |
|
|
|
85 |
|
✗ |
g_assert (CC_IS_KEYBOARD_SHORTCUT_DIALOG (self)); |
86 |
|
✗ |
g_assert (section_id && *section_id); |
87 |
|
|
|
88 |
|
✗ |
n_items = g_list_model_get_n_items (G_LIST_MODEL (self->sections)); |
89 |
|
|
|
90 |
|
✗ |
for (guint i = 0; i < n_items; i++) |
91 |
|
|
{ |
92 |
|
✗ |
g_autoptr(GObject) item = NULL; |
93 |
|
|
const char *item_section_id; |
94 |
|
|
|
95 |
|
✗ |
item = g_list_model_get_item (G_LIST_MODEL (self->sections), i); |
96 |
|
✗ |
item_section_id = g_object_get_data (item, "id"); |
97 |
|
|
|
98 |
|
✗ |
if (g_str_equal (item_section_id, section_id)) |
99 |
|
✗ |
return G_LIST_STORE (item); |
100 |
|
|
} |
101 |
|
|
|
102 |
|
|
/* Found no matching section, so create one */ |
103 |
|
✗ |
section = g_list_store_new (CC_TYPE_KEYBOARD_ITEM); |
104 |
|
✗ |
g_object_set_data_full (G_OBJECT (section), "id", g_strdup (section_id), g_free); |
105 |
|
✗ |
g_object_set_data_full (G_OBJECT (section), "title", g_strdup (section_title), g_free); |
106 |
|
|
|
107 |
|
|
/* This group shall be shown in the search results page */ |
108 |
|
✗ |
group = cc_keyboard_shortcut_group_new (G_LIST_MODEL (section), |
109 |
|
|
section_id, section_title, |
110 |
|
|
self->manager, |
111 |
|
✗ |
CC_KEYBOARD_SHORTCUT_EDITOR (self->shortcut_editor), |
112 |
|
|
self->accelerator_size_group); |
113 |
|
✗ |
g_object_set_data (G_OBJECT (section), "search-group", group); |
114 |
|
|
|
115 |
|
|
/* This group shall be shown when a section title row is activated */ |
116 |
|
✗ |
group = cc_keyboard_shortcut_group_new (G_LIST_MODEL (section), |
117 |
|
|
section_id, NULL, |
118 |
|
|
self->manager, |
119 |
|
✗ |
CC_KEYBOARD_SHORTCUT_EDITOR (self->shortcut_editor), |
120 |
|
|
self->accelerator_size_group); |
121 |
|
✗ |
g_object_set_data (G_OBJECT (section), "group", group); |
122 |
|
|
|
123 |
|
✗ |
g_list_store_append (self->sections, section); |
124 |
|
|
|
125 |
|
✗ |
return section; |
126 |
|
|
} |
127 |
|
|
|
128 |
|
|
static void |
129 |
|
✗ |
shortcut_added_cb (CcKeyboardShortcutDialog *self, |
130 |
|
|
CcKeyboardItem *item, |
131 |
|
|
const char *section_id, |
132 |
|
|
const char *section_title) |
133 |
|
|
{ |
134 |
|
|
GListStore *section; |
135 |
|
|
|
136 |
|
✗ |
section = keyboard_shortcut_get_section_store (self, section_id, section_title); |
137 |
|
✗ |
g_object_set_data (G_OBJECT (item), "section", section); |
138 |
|
✗ |
g_list_store_append (section, item); |
139 |
|
✗ |
} |
140 |
|
|
|
141 |
|
|
static void |
142 |
|
✗ |
shortcut_removed_cb (CcKeyboardShortcutDialog *self, |
143 |
|
|
CcKeyboardItem *item) |
144 |
|
|
{ |
145 |
|
|
GListStore *section; |
146 |
|
|
guint position; |
147 |
|
|
|
148 |
|
✗ |
section = g_object_get_data (G_OBJECT (item), "section"); |
149 |
|
✗ |
g_return_if_fail (section); |
150 |
|
|
|
151 |
|
✗ |
if (g_list_store_find (section, item, &position)) |
152 |
|
✗ |
g_list_store_remove (section, position); |
153 |
|
|
} |
154 |
|
|
|
155 |
|
|
static void |
156 |
|
✗ |
shortcut_custom_items_changed (CcKeyboardShortcutDialog *self) |
157 |
|
|
{ |
158 |
|
|
GListStore *section; |
159 |
|
|
GtkWidget *page; |
160 |
|
|
|
161 |
|
✗ |
g_assert (CC_IS_KEYBOARD_SHORTCUT_DIALOG (self)); |
162 |
|
|
|
163 |
|
✗ |
section = keyboard_shortcut_get_section_store (self, "custom", "Custom Shortcuts"); |
164 |
|
|
|
165 |
|
✗ |
if (self->visible_section == section) |
166 |
|
|
{ |
167 |
|
|
guint n_items; |
168 |
|
|
|
169 |
|
✗ |
n_items = g_list_model_get_n_items (G_LIST_MODEL (section)); |
170 |
|
|
|
171 |
|
✗ |
if (n_items) |
172 |
|
✗ |
page = GTK_WIDGET (self->shortcut_list_stack); |
173 |
|
|
else |
174 |
|
✗ |
page = GTK_WIDGET (self->empty_custom_shortcut_page); |
175 |
|
|
} |
176 |
|
|
else |
177 |
|
✗ |
page = GTK_WIDGET (self->shortcut_list_stack); |
178 |
|
|
|
179 |
|
✗ |
gtk_stack_set_visible_child (self->subview_stack, page); |
180 |
|
✗ |
} |
181 |
|
|
|
182 |
|
|
static int |
183 |
|
✗ |
compare_sections_title (gconstpointer a, |
184 |
|
|
gconstpointer b, |
185 |
|
|
gpointer user_data) |
186 |
|
|
{ |
187 |
|
|
GObject *obj_a, *obj_b; |
188 |
|
|
|
189 |
|
|
const char *title_a, *title_b, *id_a, *id_b; |
190 |
|
|
|
191 |
|
✗ |
obj_a = G_OBJECT (a); |
192 |
|
✗ |
obj_b = G_OBJECT (b); |
193 |
|
|
|
194 |
|
✗ |
id_a = g_object_get_data (obj_a, "id"); |
195 |
|
✗ |
id_b = g_object_get_data (obj_b, "id"); |
196 |
|
|
|
197 |
|
|
/* Always place custom row as the last item */ |
198 |
|
✗ |
if (g_str_equal (id_a, "custom")) |
199 |
|
✗ |
return 1; |
200 |
|
|
|
201 |
|
✗ |
if (g_str_equal (id_b, "custom")) |
202 |
|
✗ |
return -1; |
203 |
|
|
|
204 |
|
✗ |
title_a = _(g_object_get_data (obj_a, "title")); |
205 |
|
✗ |
title_b = _(g_object_get_data (obj_b, "title")); |
206 |
|
|
|
207 |
|
✗ |
return g_strcmp0 (title_a, title_b); |
208 |
|
|
} |
209 |
|
|
|
210 |
|
|
static void |
211 |
|
✗ |
shortcut_search_result_changed_cb (CcKeyboardShortcutDialog *self) |
212 |
|
|
{ |
213 |
|
|
GListModel *model; |
214 |
|
|
GtkWidget *page; |
215 |
|
|
guint n_items; |
216 |
|
|
|
217 |
|
✗ |
g_assert (CC_IS_KEYBOARD_SHORTCUT_DIALOG (self)); |
218 |
|
|
|
219 |
|
|
/* If a section is already shown, it is handled in search change callback */ |
220 |
|
✗ |
if (self->visible_section) |
221 |
|
✗ |
return; |
222 |
|
|
|
223 |
|
✗ |
model = G_LIST_MODEL (self->filtered_shortcuts); |
224 |
|
✗ |
n_items = g_list_model_get_n_items (model); |
225 |
|
|
|
226 |
|
✗ |
if (n_items == 0) |
227 |
|
✗ |
page = GTK_WIDGET (self->empty_results_page); |
228 |
|
✗ |
else if (self->search_terms) |
229 |
|
✗ |
page = GTK_WIDGET (self->search_result_page); |
230 |
|
|
else |
231 |
|
✗ |
page = GTK_WIDGET (self->section_list_page); |
232 |
|
|
|
233 |
|
✗ |
gtk_stack_set_visible_child (self->section_stack, page); |
234 |
|
|
} |
235 |
|
|
|
236 |
|
|
/* All items have loaded, now sort the groups and add them to the page */ |
237 |
|
|
static void |
238 |
|
✗ |
shortcuts_loaded_cb (CcKeyboardShortcutDialog *self) |
239 |
|
|
{ |
240 |
|
✗ |
g_autoptr(GPtrArray) filtered_items = NULL; |
241 |
|
✗ |
g_autoptr(GPtrArray) widgets = NULL; |
242 |
|
|
GListStore *filtered_lists; |
243 |
|
|
GListStore *custom_store; |
244 |
|
|
guint n_items; |
245 |
|
|
|
246 |
|
|
/* Ensure that custom shorcuts section exists */ |
247 |
|
✗ |
custom_store = keyboard_shortcut_get_section_store (self, "custom", "Custom Shortcuts"); |
248 |
|
✗ |
n_items = g_list_model_get_n_items (G_LIST_MODEL (self->sections)); |
249 |
|
✗ |
widgets = g_ptr_array_new (); |
250 |
|
✗ |
filtered_items = g_ptr_array_new (); |
251 |
|
✗ |
filtered_lists = g_list_store_new (G_TYPE_LIST_MODEL); |
252 |
|
|
|
253 |
|
✗ |
g_signal_connect_object (custom_store, "items-changed", |
254 |
|
|
G_CALLBACK (shortcut_custom_items_changed), |
255 |
|
|
self, G_CONNECT_SWAPPED); |
256 |
|
|
|
257 |
|
✗ |
g_list_store_sort (self->sections, compare_sections_title, NULL); |
258 |
|
|
|
259 |
|
✗ |
for (guint i = 0; i < n_items; i++) |
260 |
|
|
{ |
261 |
|
✗ |
g_autoptr(GObject) item = NULL; |
262 |
|
|
CcKeyboardShortcutGroup *group; |
263 |
|
|
GListModel *model; |
264 |
|
|
GtkWidget *page; |
265 |
|
|
|
266 |
|
✗ |
item = g_list_model_get_item (G_LIST_MODEL (self->sections), i); |
267 |
|
✗ |
group = g_object_get_data (item, "search-group"); |
268 |
|
✗ |
g_ptr_array_add (widgets, group); |
269 |
|
|
|
270 |
|
✗ |
model = cc_keyboard_shortcut_group_get_model (group); |
271 |
|
✗ |
g_ptr_array_add (filtered_items, model); |
272 |
|
|
|
273 |
|
|
/* Populate shortcut section page */ |
274 |
|
✗ |
group = g_object_get_data (item, "group"); |
275 |
|
✗ |
page = adw_preferences_page_new (); |
276 |
|
✗ |
g_object_set_data (item, "page", page); |
277 |
|
✗ |
adw_preferences_page_add (ADW_PREFERENCES_PAGE (page), ADW_PREFERENCES_GROUP (group)); |
278 |
|
✗ |
gtk_stack_add_child (self->shortcut_list_stack, page); |
279 |
|
|
} |
280 |
|
|
|
281 |
|
|
/* Populate search results page */ |
282 |
|
✗ |
for (guint i = 0; i < widgets->len; i++) |
283 |
|
✗ |
adw_preferences_page_add (self->search_result_page, widgets->pdata[i]); |
284 |
|
|
|
285 |
|
|
/* Keep track of search results so as to update empty state */ |
286 |
|
✗ |
g_list_store_splice (filtered_lists, 0, 0, filtered_items->pdata, filtered_items->len); |
287 |
|
✗ |
self->filtered_shortcuts = gtk_flatten_list_model_new (G_LIST_MODEL (filtered_lists)); |
288 |
|
|
|
289 |
|
✗ |
g_signal_connect_object (self->filtered_shortcuts, "items-changed", |
290 |
|
|
G_CALLBACK (shortcut_search_result_changed_cb), |
291 |
|
|
self, G_CONNECT_SWAPPED); |
292 |
|
✗ |
} |
293 |
|
|
|
294 |
|
|
static GtkWidget * |
295 |
|
✗ |
shortcut_dialog_row_new (gpointer item, |
296 |
|
|
gpointer user_data) |
297 |
|
|
{ |
298 |
|
|
GtkWidget *row, *group; |
299 |
|
|
const char *title; |
300 |
|
|
|
301 |
|
✗ |
group = g_object_get_data (item, "search-group"); |
302 |
|
✗ |
title = g_object_get_data (item, "title"); |
303 |
|
✗ |
row = g_object_new (CC_TYPE_LIST_ROW, |
304 |
|
|
"title", _(title), |
305 |
|
|
"show-arrow", TRUE, |
306 |
|
|
NULL); |
307 |
|
|
|
308 |
|
✗ |
g_object_set_data (G_OBJECT (row), "section", item); |
309 |
|
|
|
310 |
|
✗ |
g_object_bind_property (group, "modified-text", |
311 |
|
|
row, "secondary-label", |
312 |
|
|
G_BINDING_SYNC_CREATE); |
313 |
|
|
|
314 |
|
✗ |
return row; |
315 |
|
|
} |
316 |
|
|
|
317 |
|
|
static void |
318 |
|
✗ |
add_custom_shortcut_clicked_cb (CcKeyboardShortcutDialog *self) |
319 |
|
|
{ |
320 |
|
|
CcKeyboardShortcutEditor *editor; |
321 |
|
|
|
322 |
|
✗ |
editor = CC_KEYBOARD_SHORTCUT_EDITOR (self->shortcut_editor); |
323 |
|
|
|
324 |
|
✗ |
cc_keyboard_shortcut_editor_set_mode (editor, CC_SHORTCUT_EDITOR_CREATE); |
325 |
|
✗ |
cc_keyboard_shortcut_editor_set_item (editor, NULL); |
326 |
|
|
|
327 |
|
✗ |
gtk_widget_set_visible (self->shortcut_editor, TRUE); |
328 |
|
✗ |
} |
329 |
|
|
|
330 |
|
|
static void |
331 |
|
✗ |
on_reset_all_dialog_response_cb (CcKeyboardShortcutDialog *self, |
332 |
|
|
gchar *response, |
333 |
|
|
AdwMessageDialog *dialog) |
334 |
|
|
{ |
335 |
|
|
guint n_items, j_items; |
336 |
|
|
|
337 |
|
✗ |
gtk_window_destroy (GTK_WINDOW (dialog)); |
338 |
|
|
|
339 |
|
✗ |
if (g_strcmp0 (response, "cancel") == 0) |
340 |
|
✗ |
return; |
341 |
|
|
|
342 |
|
✗ |
n_items = g_list_model_get_n_items (G_LIST_MODEL (self->sections)); |
343 |
|
|
|
344 |
|
✗ |
for (guint i = 0; i < n_items; i++) |
345 |
|
|
{ |
346 |
|
✗ |
g_autoptr(GListModel) section = NULL; |
347 |
|
|
|
348 |
|
✗ |
section = g_list_model_get_item (G_LIST_MODEL (self->sections), i); |
349 |
|
✗ |
j_items = g_list_model_get_n_items (section); |
350 |
|
|
|
351 |
|
✗ |
for (guint j = 0; j < j_items; j++) |
352 |
|
|
{ |
353 |
|
✗ |
g_autoptr(CcKeyboardItem) item = NULL; |
354 |
|
|
|
355 |
|
✗ |
item = g_list_model_get_item (section, j); |
356 |
|
|
|
357 |
|
|
/* Don't reset custom shortcuts */ |
358 |
|
✗ |
if (cc_keyboard_item_get_item_type (item) == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH) |
359 |
|
✗ |
return; |
360 |
|
|
|
361 |
|
|
/* cc_keyboard_manager_reset_shortcut() already resets conflicting shortcuts, |
362 |
|
|
* so no other check is needed here. */ |
363 |
|
✗ |
cc_keyboard_manager_reset_shortcut (self->manager, item); |
364 |
|
|
} |
365 |
|
|
} |
366 |
|
|
} |
367 |
|
|
|
368 |
|
|
static void |
369 |
|
✗ |
reset_all_clicked_cb (CcKeyboardShortcutDialog *self) |
370 |
|
|
{ |
371 |
|
|
GtkWidget *dialog; |
372 |
|
|
|
373 |
|
✗ |
dialog = adw_message_dialog_new (GTK_WINDOW (self), |
374 |
|
✗ |
_("Reset All Shortcuts?"), |
375 |
|
|
NULL); |
376 |
|
|
|
377 |
|
✗ |
adw_message_dialog_format_body (ADW_MESSAGE_DIALOG (dialog), |
378 |
|
✗ |
_("All changes to keyboard shortcuts will be lost.")); |
379 |
|
|
|
380 |
|
✗ |
adw_message_dialog_add_responses (ADW_MESSAGE_DIALOG (dialog), |
381 |
|
|
"cancel", _("_Cancel"), |
382 |
|
|
"reset_all", _("_Reset All"), |
383 |
|
|
NULL); |
384 |
|
|
|
385 |
|
✗ |
adw_message_dialog_set_response_appearance (ADW_MESSAGE_DIALOG (dialog), |
386 |
|
|
"reset_all", |
387 |
|
|
ADW_RESPONSE_DESTRUCTIVE); |
388 |
|
|
|
389 |
|
✗ |
adw_message_dialog_set_default_response (ADW_MESSAGE_DIALOG (dialog), |
390 |
|
|
"cancel"); |
391 |
|
|
|
392 |
|
✗ |
adw_message_dialog_set_close_response (ADW_MESSAGE_DIALOG (dialog), |
393 |
|
|
"cancel"); |
394 |
|
|
|
395 |
|
✗ |
g_signal_connect_swapped (dialog, |
396 |
|
|
"response", |
397 |
|
|
G_CALLBACK (on_reset_all_dialog_response_cb), |
398 |
|
|
self); |
399 |
|
|
|
400 |
|
✗ |
gtk_window_present (GTK_WINDOW (dialog)); |
401 |
|
✗ |
} |
402 |
|
|
|
403 |
|
|
static void |
404 |
|
✗ |
shortcut_dialog_visible_page_changed_cb (CcKeyboardShortcutDialog *self) |
405 |
|
|
{ |
406 |
|
|
gpointer visible_page; |
407 |
|
|
gboolean is_main_view; |
408 |
|
|
|
409 |
|
✗ |
visible_page = adw_navigation_view_get_visible_page (self->navigation_view); |
410 |
|
✗ |
is_main_view = visible_page == self->main_page; |
411 |
|
|
|
412 |
|
✗ |
if (is_main_view) |
413 |
|
|
{ |
414 |
|
✗ |
gtk_editable_set_text (GTK_EDITABLE (self->search_entry), ""); |
415 |
|
✗ |
gtk_widget_grab_focus (GTK_WIDGET (self->search_entry)); |
416 |
|
|
|
417 |
|
✗ |
self->visible_section = NULL; |
418 |
|
|
} |
419 |
|
✗ |
else if (self->visible_section) |
420 |
|
|
{ |
421 |
|
|
const char *title; |
422 |
|
|
|
423 |
|
✗ |
title = g_object_get_data (G_OBJECT (self->visible_section), "title"); |
424 |
|
✗ |
adw_navigation_page_set_title (self->subview_page, _(title) ?: ""); |
425 |
|
|
} |
426 |
|
✗ |
} |
427 |
|
|
|
428 |
|
|
static void |
429 |
|
✗ |
shortcut_search_entry_changed_cb (CcKeyboardShortcutDialog *self) |
430 |
|
|
{ |
431 |
|
✗ |
g_autofree char *search = NULL; |
432 |
|
|
const char *search_text; |
433 |
|
|
guint n_items; |
434 |
|
|
|
435 |
|
✗ |
g_assert (CC_IS_KEYBOARD_SHORTCUT_DIALOG (self)); |
436 |
|
|
|
437 |
|
|
/* Don't update search if we are in a subview */ |
438 |
|
✗ |
if (self->visible_section) |
439 |
|
✗ |
return; |
440 |
|
|
|
441 |
|
✗ |
n_items = g_list_model_get_n_items (G_LIST_MODEL (self->sections)); |
442 |
|
✗ |
search_text = gtk_editable_get_text (GTK_EDITABLE (self->search_entry)); |
443 |
|
✗ |
search = cc_util_normalize_casefold_and_unaccent (search_text); |
444 |
|
|
|
445 |
|
✗ |
g_clear_pointer (&self->search_terms, g_strfreev); |
446 |
|
✗ |
if (search && *search && *search != ' ') |
447 |
|
✗ |
self->search_terms = g_strsplit (search, " ", -1); |
448 |
|
|
|
449 |
|
|
/* "Reset all..." button should be sensitive only if the search is not active */ |
450 |
|
✗ |
gtk_widget_set_sensitive (GTK_WIDGET (self->reset_all_button), !self->search_terms); |
451 |
|
|
|
452 |
|
✗ |
for (guint i = 0; i < n_items; i++) |
453 |
|
|
{ |
454 |
|
✗ |
g_autoptr(GObject) item = NULL; |
455 |
|
|
CcKeyboardShortcutGroup *group; |
456 |
|
|
|
457 |
|
✗ |
item = g_list_model_get_item (G_LIST_MODEL (self->sections), i); |
458 |
|
✗ |
group = g_object_get_data (item, "search-group"); |
459 |
|
|
|
460 |
|
✗ |
cc_keyboard_shortcut_group_set_filter (group, self->search_terms); |
461 |
|
|
} |
462 |
|
|
|
463 |
|
✗ |
shortcut_search_result_changed_cb (self); |
464 |
|
|
} |
465 |
|
|
|
466 |
|
|
static void |
467 |
|
✗ |
shortcut_search_entry_stopped_cb (CcKeyboardShortcutDialog *self) |
468 |
|
|
{ |
469 |
|
|
const char *search_text; |
470 |
|
✗ |
search_text = gtk_editable_get_text (GTK_EDITABLE (self->search_entry)); |
471 |
|
|
|
472 |
|
✗ |
if (search_text && g_strcmp0 (search_text, "") != 0) |
473 |
|
✗ |
gtk_editable_set_text (GTK_EDITABLE (self->search_entry), ""); |
474 |
|
|
else |
475 |
|
✗ |
gtk_window_close (GTK_WINDOW (self)); |
476 |
|
✗ |
} |
477 |
|
|
|
478 |
|
|
static void |
479 |
|
✗ |
shortcut_section_row_activated_cb (CcKeyboardShortcutDialog *self, |
480 |
|
|
GtkListBoxRow *row) |
481 |
|
|
{ |
482 |
|
|
GListStore *section; |
483 |
|
|
GtkWidget *page; |
484 |
|
|
|
485 |
|
✗ |
g_assert (CC_IS_KEYBOARD_SHORTCUT_DIALOG (self)); |
486 |
|
✗ |
g_assert (GTK_IS_LIST_BOX_ROW (row)); |
487 |
|
|
|
488 |
|
✗ |
section = g_object_get_data (G_OBJECT (row), "section"); |
489 |
|
✗ |
self->visible_section = section; |
490 |
|
|
|
491 |
|
✗ |
page = g_object_get_data (G_OBJECT (section), "page"); |
492 |
|
✗ |
gtk_stack_set_visible_child (self->shortcut_list_stack, page); |
493 |
|
✗ |
adw_navigation_view_push (self->navigation_view, self->subview_page); |
494 |
|
✗ |
shortcut_custom_items_changed (self); |
495 |
|
✗ |
} |
496 |
|
|
|
497 |
|
|
static void |
498 |
|
✗ |
cc_keyboard_shortcut_dialog_constructed (GObject *object) |
499 |
|
|
{ |
500 |
|
✗ |
CcKeyboardShortcutDialog *self = CC_KEYBOARD_SHORTCUT_DIALOG (object); |
501 |
|
|
|
502 |
|
✗ |
G_OBJECT_CLASS (cc_keyboard_shortcut_dialog_parent_class)->constructed (object); |
503 |
|
|
|
504 |
|
|
/* Setup the dialog's transient parent */ |
505 |
|
✗ |
gtk_window_set_transient_for (GTK_WINDOW (self->shortcut_editor), GTK_WINDOW (self)); |
506 |
|
✗ |
} |
507 |
|
|
|
508 |
|
|
static void |
509 |
|
✗ |
cc_keyboard_shortcut_dialog_finalize (GObject *object) |
510 |
|
|
{ |
511 |
|
✗ |
CcKeyboardShortcutDialog *self = CC_KEYBOARD_SHORTCUT_DIALOG (object); |
512 |
|
|
|
513 |
|
✗ |
g_clear_object (&self->manager); |
514 |
|
✗ |
g_clear_object (&self->sections); |
515 |
|
✗ |
g_clear_pointer (&self->search_terms, g_strfreev); |
516 |
|
✗ |
g_clear_object (&self->sections); |
517 |
|
✗ |
g_clear_object (&self->filtered_shortcuts); |
518 |
|
✗ |
g_clear_pointer ((GtkWindow**)&self->shortcut_editor, gtk_window_destroy); |
519 |
|
|
|
520 |
|
✗ |
G_OBJECT_CLASS (cc_keyboard_shortcut_dialog_parent_class)->finalize (object); |
521 |
|
✗ |
} |
522 |
|
|
|
523 |
|
|
static void |
524 |
|
✗ |
cc_keyboard_shortcut_dialog_class_init (CcKeyboardShortcutDialogClass *klass) |
525 |
|
|
{ |
526 |
|
✗ |
GObjectClass *object_class = G_OBJECT_CLASS (klass); |
527 |
|
✗ |
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); |
528 |
|
|
|
529 |
|
✗ |
object_class->constructed = cc_keyboard_shortcut_dialog_constructed; |
530 |
|
✗ |
object_class->finalize = cc_keyboard_shortcut_dialog_finalize; |
531 |
|
|
|
532 |
|
✗ |
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_Escape, 0, "window.close", NULL); |
533 |
|
|
|
534 |
|
✗ |
gtk_widget_class_set_template_from_resource (widget_class, |
535 |
|
|
"/org/gnome/control-center/" |
536 |
|
|
"keyboard/cc-keyboard-shortcut-dialog.ui"); |
537 |
|
|
|
538 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, navigation_view); |
539 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, main_page); |
540 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, reset_all_button); |
541 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, search_entry); |
542 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, section_stack); |
543 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, section_list_page); |
544 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, section_list_box); |
545 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, search_result_page); |
546 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, empty_results_page); |
547 |
|
|
|
548 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, subview_page); |
549 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, subview_stack); |
550 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, shortcut_list_stack); |
551 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, empty_custom_shortcut_page); |
552 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, accelerator_size_group); |
553 |
|
|
|
554 |
|
✗ |
gtk_widget_class_bind_template_callback (widget_class, add_custom_shortcut_clicked_cb); |
555 |
|
✗ |
gtk_widget_class_bind_template_callback (widget_class, reset_all_clicked_cb); |
556 |
|
✗ |
gtk_widget_class_bind_template_callback (widget_class, shortcut_dialog_visible_page_changed_cb); |
557 |
|
✗ |
gtk_widget_class_bind_template_callback (widget_class, shortcut_search_entry_changed_cb); |
558 |
|
✗ |
gtk_widget_class_bind_template_callback (widget_class, shortcut_search_entry_stopped_cb); |
559 |
|
✗ |
gtk_widget_class_bind_template_callback (widget_class, shortcut_section_row_activated_cb); |
560 |
|
✗ |
} |
561 |
|
|
|
562 |
|
|
static void |
563 |
|
✗ |
cc_keyboard_shortcut_dialog_init (CcKeyboardShortcutDialog *self) |
564 |
|
|
{ |
565 |
|
|
GtkWindow *toplevel; |
566 |
|
|
|
567 |
|
✗ |
gtk_widget_init_template (GTK_WIDGET (self)); |
568 |
|
✗ |
shortcut_dialog_visible_page_changed_cb (self); |
569 |
|
|
|
570 |
|
✗ |
self->manager = cc_keyboard_manager_new (); |
571 |
|
|
|
572 |
|
✗ |
toplevel = GTK_WINDOW (gtk_widget_get_native (GTK_WIDGET (self))); |
573 |
|
✗ |
self->shortcut_editor = cc_keyboard_shortcut_editor_new (toplevel, self->manager); |
574 |
|
✗ |
shortcut_dialog_visible_page_changed_cb (self); |
575 |
|
|
|
576 |
|
✗ |
self->sections = g_list_store_new (G_TYPE_LIST_STORE); |
577 |
|
|
|
578 |
|
✗ |
g_signal_connect_object (self->manager, |
579 |
|
|
"shortcut-added", |
580 |
|
|
G_CALLBACK (shortcut_added_cb), |
581 |
|
|
self, G_CONNECT_SWAPPED); |
582 |
|
✗ |
g_signal_connect_object (self->manager, |
583 |
|
|
"shortcut-removed", |
584 |
|
|
G_CALLBACK (shortcut_removed_cb), |
585 |
|
|
self, G_CONNECT_SWAPPED); |
586 |
|
✗ |
g_signal_connect_object (self->manager, |
587 |
|
|
"shortcuts-loaded", |
588 |
|
|
G_CALLBACK (shortcuts_loaded_cb), |
589 |
|
|
self, G_CONNECT_SWAPPED); |
590 |
|
|
|
591 |
|
✗ |
cc_keyboard_manager_load_shortcuts (self->manager); |
592 |
|
|
|
593 |
|
✗ |
gtk_list_box_bind_model (self->section_list_box, |
594 |
|
✗ |
G_LIST_MODEL (self->sections), |
595 |
|
|
shortcut_dialog_row_new, |
596 |
|
|
self, NULL); |
597 |
|
✗ |
} |
598 |
|
|
|
599 |
|
|
GtkWidget* |
600 |
|
✗ |
cc_keyboard_shortcut_dialog_new (void) |
601 |
|
|
{ |
602 |
|
✗ |
return g_object_new (CC_TYPE_KEYBOARD_SHORTCUT_DIALOG, NULL); |
603 |
|
|
} |
604 |
|
|
|