GCC Code Coverage Report


Directory: ./
File: shell/cc-window.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 312 0.0%
Functions: 0 35 0.0%
Branches: 0 148 0.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2009, 2010 Intel, Inc.
3 * Copyright (c) 2010 Red Hat, Inc.
4 * Copyright (c) 2016 Endless, Inc.
5 *
6 * The Control Center is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
10 *
11 * The Control Center is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with the Control Center; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 * Author: Thomas Wood <thos@gnome.org>
21 */
22
23 #define G_LOG_DOMAIN "cc-window"
24
25 #include <config.h>
26
27 #include "cc-log.h"
28 #include "cc-window.h"
29
30 #include <glib/gi18n.h>
31 #include <gio/gio.h>
32 #include <gio/gdesktopappinfo.h>
33 #include <gtk/gtk.h>
34 #include <string.h>
35 #include <time.h>
36
37 #include "cc-application.h"
38 #include "cc-panel.h"
39 #include "cc-shell.h"
40 #include "cc-shell-model.h"
41 #include "cc-panel-list.h"
42 #include "cc-panel-loader.h"
43 #include "cc-util.h"
44
45 #define MOUSE_BACK_BUTTON 8
46
47 #define DEFAULT_WINDOW_ICON_NAME "gnome-control-center"
48
49 struct _CcWindow
50 {
51 AdwApplicationWindow parent;
52
53 AdwMessageDialog *development_warning_dialog;
54 AdwNavigationSplitView *split_view;
55 CcPanelList *panel_list;
56 GtkSearchBar *search_bar;
57 GtkSearchEntry *search_entry;
58
59 GtkWidget *old_panel;
60 GtkWidget *current_panel;
61 char *current_panel_id;
62 GQueue *previous_panels;
63
64 GtkWidget *custom_titlebar;
65
66 CcShellModel *store;
67
68 CcPanel *active_panel;
69 GSettings *settings;
70
71 CcPanelListView previous_list_view;
72 };
73
74 static void cc_shell_iface_init (CcShellInterface *iface);
75
76 G_DEFINE_TYPE_WITH_CODE (CcWindow, cc_window, ADW_TYPE_APPLICATION_WINDOW,
77 G_IMPLEMENT_INTERFACE (CC_TYPE_SHELL, cc_shell_iface_init))
78
79 enum
80 {
81 PROP_0,
82 PROP_ACTIVE_PANEL,
83 PROP_MODEL,
84 PROP_COLLAPSED,
85 };
86
87 /* Auxiliary methods */
88 static void
89 load_window_state (CcWindow *self)
90 {
91 gint current_width = -1;
92 gint current_height = -1;
93 gboolean maximized = FALSE;
94
95 g_settings_get (self->settings,
96 "window-state",
97 "(iib)",
98 &current_width,
99 &current_height,
100 &maximized);
101
102 if (current_width != -1 && current_height != -1)
103 gtk_window_set_default_size (GTK_WINDOW (self), current_width, current_height);
104 if (maximized)
105 gtk_window_maximize (GTK_WINDOW (self));
106 }
107
108 static gboolean
109 in_flatpak_sandbox (void)
110 {
111 return g_strcmp0 (PROFILE, "development") == 0;
112 }
113
114 static gboolean
115 activate_panel (CcWindow *self,
116 const gchar *id,
117 GVariant *parameters,
118 const gchar *name,
119 GIcon *gicon,
120 CcPanelVisibility visibility)
121 {
122 g_autoptr(GTimer) timer = NULL;
123 gdouble elapsed_time;
124
125 CC_ENTRY;
126
127 if (!id)
128 CC_RETURN (FALSE);
129
130 if (visibility == CC_PANEL_HIDDEN)
131 CC_RETURN (FALSE);
132
133 timer = g_timer_new ();
134
135 /* Begin the profile */
136 g_timer_start (timer);
137
138 if (self->current_panel)
139 g_signal_handlers_disconnect_by_data (self->current_panel, self);
140 self->current_panel = GTK_WIDGET (cc_panel_loader_load_by_name (CC_SHELL (self), id, name, parameters));
141 cc_shell_set_active_panel (CC_SHELL (self), CC_PANEL (self->current_panel));
142
143 adw_navigation_split_view_set_content (self->split_view, ADW_NAVIGATION_PAGE (self->current_panel));
144
145 /* Finish profiling */
146 g_timer_stop (timer);
147
148 elapsed_time = g_timer_elapsed (timer, NULL);
149
150 g_debug ("Time to open panel '%s': %lfs", name, elapsed_time);
151
152 g_settings_set_string (self->settings, "last-panel", id);
153
154 CC_RETURN (TRUE);
155 }
156
157 static void
158 add_current_panel_to_history (CcWindow *self,
159 const char *start_id)
160 {
161 g_return_if_fail (start_id != NULL);
162
163 if (!self->current_panel_id || g_strcmp0 (self->current_panel_id, start_id) == 0)
164 return;
165
166 g_queue_push_head (self->previous_panels, g_strdup (self->current_panel_id));
167 g_debug ("Added '%s' to the previous panels", self->current_panel_id);
168 }
169
170 static gboolean
171 find_iter_for_panel_id (CcWindow *self,
172 const gchar *panel_id,
173 GtkTreeIter *out_iter)
174 {
175 GtkTreeIter iter;
176 gboolean valid;
177
178 valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->store), &iter);
179
180 while (valid)
181 {
182 g_autofree gchar *id = NULL;
183
184 gtk_tree_model_get (GTK_TREE_MODEL (self->store),
185 &iter,
186 COL_ID, &id,
187 -1);
188
189 if (g_strcmp0 (id, panel_id) == 0)
190 break;
191
192 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (self->store), &iter);
193 }
194
195 g_assert (out_iter != NULL);
196 *out_iter = iter;
197
198 return valid;
199 }
200
201 static void
202 on_row_changed_cb (CcWindow *self,
203 GtkTreePath *path,
204 GtkTreeIter *iter,
205 GtkTreeModel *model)
206 {
207 g_autofree gchar *id = NULL;
208 CcPanelVisibility visibility;
209
210 gtk_tree_model_get (model, iter,
211 COL_ID, &id,
212 COL_VISIBILITY, &visibility,
213 -1);
214
215 cc_panel_list_set_panel_visibility (self->panel_list, id, visibility);
216 }
217
218 static void
219 setup_model (CcWindow *self)
220 {
221 GtkTreeModel *model;
222 GtkTreeIter iter;
223 gboolean valid;
224
225 /* CcApplication must have a valid model at this point */
226 g_assert (self->store != NULL);
227
228 model = GTK_TREE_MODEL (self->store);
229
230 cc_panel_loader_fill_model (self->store);
231
232 /* Create a row for each panel */
233 valid = gtk_tree_model_get_iter_first (model, &iter);
234
235 while (valid)
236 {
237 CcPanelCategory category;
238 g_autoptr(GIcon) icon = NULL;
239 g_autofree gchar *name = NULL;
240 g_autofree gchar *description = NULL;
241 g_autofree gchar *id = NULL;
242 g_auto(GStrv) keywords = NULL;
243 CcPanelVisibility visibility;
244 const gchar *icon_name = NULL;
245
246 gtk_tree_model_get (model, &iter,
247 COL_CATEGORY, &category,
248 COL_DESCRIPTION, &description,
249 COL_GICON, &icon,
250 COL_ID, &id,
251 COL_NAME, &name,
252 COL_KEYWORDS, &keywords,
253 COL_VISIBILITY, &visibility,
254 -1);
255
256 if (G_IS_THEMED_ICON (icon))
257 icon_name = g_themed_icon_get_names (G_THEMED_ICON (icon))[0];
258
259 cc_panel_list_add_panel (self->panel_list,
260 category,
261 id,
262 name,
263 description,
264 keywords,
265 icon_name,
266 visibility);
267
268 valid = gtk_tree_model_iter_next (model, &iter);
269 }
270
271 /* React to visibility changes */
272 g_signal_connect_object (model, "row-changed", G_CALLBACK (on_row_changed_cb), self, G_CONNECT_SWAPPED);
273 }
274
275 static gboolean
276 set_active_panel_from_id (CcWindow *self,
277 const gchar *start_id,
278 GVariant *parameters,
279 gboolean add_to_history,
280 gboolean force_moving_to_the_panel,
281 GError **error)
282 {
283 g_autoptr(GIcon) gicon = NULL;
284 g_autofree gchar *name = NULL;
285 CcPanelVisibility visibility;
286 CcPanelCategory category;
287 g_autoptr(GVariant) system_param_overwrite = NULL;
288 GtkTreeIter iter;
289 CcPanelListView view;
290 gboolean activated;
291 gboolean found;
292
293 CC_ENTRY;
294
295 view = cc_panel_list_get_view (self->panel_list);
296
297 /* When loading the same panel again, just set its parameters */
298 if (g_strcmp0 (self->current_panel_id, start_id) == 0)
299 {
300 g_object_set (G_OBJECT (self->current_panel), "parameters", parameters, NULL);
301 if (force_moving_to_the_panel || self->previous_list_view == view)
302 adw_navigation_split_view_set_show_content (self->split_view, TRUE);
303 self->previous_list_view = view;
304 CC_RETURN (TRUE);
305 }
306
307 found = find_iter_for_panel_id (self, start_id, &iter);
308 if (!found)
309 {
310 g_warning ("Could not find settings panel \"%s\"", start_id);
311 CC_RETURN (TRUE);
312 }
313
314 self->old_panel = self->current_panel;
315 if (self->old_panel)
316 cc_panel_deactivate (CC_PANEL (self->old_panel));
317
318 gtk_tree_model_get (GTK_TREE_MODEL (self->store),
319 &iter,
320 COL_NAME, &name,
321 COL_GICON, &gicon,
322 COL_VISIBILITY, &visibility,
323 COL_CATEGORY, &category,
324 -1);
325
326 /* Handle "System" subpages by overwriting the start_id and parameters arguments.
327 * This is needed because they have their own desktop files. */
328 if (category == CC_CATEGORY_SYSTEM)
329 {
330 g_autofree gchar *param_str = NULL;
331
332 param_str = g_strdup_printf ("[<'%s'>]", start_id);
333 system_param_overwrite = g_variant_new_parsed (param_str);
334 g_variant_ref_sink (system_param_overwrite);
335
336 g_warning ("The direct access to `%s` is now deprecated. Please, use `system %s` instead.", start_id, start_id);
337
338 start_id = "system";
339 }
340
341 /* Activate the panel */
342 activated = activate_panel (self,
343 start_id,
344 system_param_overwrite != NULL ? system_param_overwrite : parameters,
345 name,
346 gicon,
347 visibility);
348
349 /* Failed to activate the panel for some reason, let's keep the old
350 * panel around instead */
351 if (!activated)
352 {
353 g_debug ("Failed to activate panel");
354 CC_RETURN (TRUE);
355 }
356
357 if (add_to_history)
358 add_current_panel_to_history (self, start_id);
359
360 if (force_moving_to_the_panel)
361 adw_navigation_split_view_set_show_content (self->split_view, TRUE);
362
363 g_free (self->current_panel_id);
364 self->current_panel_id = g_strdup (start_id);
365
366 CC_TRACE_MSG ("Current panel id: %s", start_id);
367
368 cc_panel_list_set_active_panel (self->panel_list, start_id);
369
370 CC_RETURN (TRUE);
371 }
372
373 static void
374 set_active_panel (CcWindow *self,
375 CcPanel *panel)
376 {
377 g_return_if_fail (CC_IS_SHELL (self));
378 g_return_if_fail (panel == NULL || CC_IS_PANEL (panel));
379
380 if (panel != self->active_panel)
381 {
382 /* remove the old panel */
383 g_clear_object (&self->active_panel);
384
385 /* set the new panel */
386 if (panel)
387 self->active_panel = g_object_ref (panel);
388
389 g_object_notify (G_OBJECT (self), "active-panel");
390 }
391 }
392
393 static void
394 switch_to_previous_panel (CcWindow *self)
395 {
396 g_autofree gchar *previous_panel_id = NULL;
397
398 CC_ENTRY;
399
400 if (g_queue_get_length (self->previous_panels) == 0)
401 CC_RETURN ();
402
403 previous_panel_id = g_queue_pop_head (self->previous_panels);
404
405 g_debug ("Going to previous panel (%s)", previous_panel_id);
406
407 set_active_panel_from_id (self, previous_panel_id, NULL, FALSE, FALSE, NULL);
408
409 CC_EXIT;
410 }
411
412 /* Callbacks */
413
414 static void
415 on_split_view_collapsed_changed_cb (CcWindow *self)
416 {
417 GtkSelectionMode selection_mode;
418 gboolean collapsed;
419
420 g_assert (CC_IS_WINDOW (self));
421
422 collapsed = adw_navigation_split_view_get_collapsed (self->split_view);
423
424 selection_mode = collapsed ? GTK_SELECTION_NONE : GTK_SELECTION_SINGLE;
425 cc_panel_list_set_selection_mode (self->panel_list, selection_mode);
426
427 g_object_notify (G_OBJECT (self), "collapsed");
428 }
429
430 static void
431 show_panel_cb (CcWindow *self,
432 const gchar *panel_id,
433 const gchar *parent_id)
434 {
435 GVariant *parameters;
436 GVariantBuilder builder;
437
438 if (!panel_id)
439 return;
440
441 if (!parent_id)
442 {
443 set_active_panel_from_id (self, panel_id, NULL, TRUE, FALSE, NULL);
444
445 return;
446 }
447
448 g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
449 g_variant_builder_add (&builder, "v", g_variant_new_string (panel_id));
450 parameters = g_variant_builder_end (&builder);
451
452 set_active_panel_from_id (self, parent_id, parameters, TRUE, FALSE, NULL);
453 }
454
455 static void
456 search_entry_activate_cb (CcWindow *self)
457 {
458 gboolean changed;
459
460 if (cc_panel_list_get_view (self->panel_list) != CC_PANEL_LIST_SEARCH)
461 return;
462
463 changed = cc_panel_list_activate (self->panel_list);
464
465 gtk_search_bar_set_search_mode (self->search_bar, !changed);
466 }
467
468 static gboolean
469 go_back_shortcut_cb (GtkWidget *widget,
470 GVariant *args,
471 gpointer user_data)
472 {
473 g_debug ("Going to previous panel");
474 switch_to_previous_panel (CC_WINDOW (widget));
475
476 return GDK_EVENT_STOP;
477 }
478
479 static gboolean
480 search_shortcut_cb (GtkWidget *widget,
481 GVariant *args,
482 gpointer user_data)
483 {
484 CcPanelListView view;
485 CcWindow *self;
486 gboolean search;
487
488 self = CC_WINDOW (widget);
489 view = cc_panel_list_get_view (self->panel_list);
490
491 /* The search only happens when we're in the MAIN view */
492 if (view != CC_PANEL_LIST_MAIN && view != CC_PANEL_LIST_SEARCH)
493 return GDK_EVENT_PROPAGATE;
494
495 search = !gtk_search_bar_get_search_mode (self->search_bar);
496 gtk_search_bar_set_search_mode (self->search_bar, search);
497 if (search)
498 gtk_widget_grab_focus (GTK_WIDGET (self->search_entry));
499
500 return GDK_EVENT_STOP;
501 }
502
503 static void
504 on_development_warning_dialog_responded_cb (CcWindow *self)
505 {
506 g_debug ("Disabling development build warning dialog");
507 g_settings_set_boolean (self->settings, "show-development-warning", FALSE);
508
509 gtk_window_close (GTK_WINDOW (self->development_warning_dialog));
510 }
511
512 /* CcShell implementation */
513 static gboolean
514 cc_window_set_active_panel_from_id (CcShell *shell,
515 const gchar *start_id,
516 GVariant *parameters,
517 GError **error)
518 {
519 CcWindow *self = CC_WINDOW (shell);
520
521 g_return_val_if_fail (self != NULL, FALSE);
522
523 cc_panel_list_center_activated_row (self->panel_list, TRUE);
524 return set_active_panel_from_id (self, start_id, parameters, TRUE, TRUE, error);
525 }
526
527 static GtkWidget *
528 cc_window_get_toplevel (CcShell *self)
529 {
530 return GTK_WIDGET (self);
531 }
532
533 static void
534 cc_shell_iface_init (CcShellInterface *iface)
535 {
536 iface->set_active_panel_from_id = cc_window_set_active_panel_from_id;
537 iface->get_toplevel = cc_window_get_toplevel;
538 }
539
540 /* GtkWidget overrides */
541 static void
542 cc_window_map (GtkWidget *widget)
543 {
544 CcWindow *self = (CcWindow *) widget;
545
546 GTK_WIDGET_CLASS (cc_window_parent_class)->map (widget);
547
548 /* Show a warning for Flatpak builds */
549 if (in_flatpak_sandbox () && g_settings_get_boolean (self->settings, "show-development-warning"))
550 gtk_window_present (GTK_WINDOW (self->development_warning_dialog));
551 }
552
553 static void
554 cc_window_unmap (GtkWidget *widget)
555 {
556 CcWindow *self = CC_WINDOW (widget);
557 gboolean maximized;
558 gint height;
559 gint width;
560
561 maximized = gtk_window_is_maximized (GTK_WINDOW (self));
562 gtk_window_get_default_size (GTK_WINDOW (self), &width, &height);
563
564 g_settings_set (self->settings,
565 "window-state",
566 "(iib)",
567 width,
568 height,
569 maximized);
570
571 GTK_WIDGET_CLASS (cc_window_parent_class)->unmap (widget);
572 }
573
574 /* GObject Implementation */
575 static void
576 cc_window_get_property (GObject *object,
577 guint property_id,
578 GValue *value,
579 GParamSpec *pspec)
580 {
581 CcWindow *self = CC_WINDOW (object);
582
583 switch (property_id)
584 {
585 case PROP_ACTIVE_PANEL:
586 g_value_set_object (value, self->active_panel);
587 break;
588
589 case PROP_MODEL:
590 g_value_set_object (value, self->store);
591 break;
592
593 case PROP_COLLAPSED:
594 g_value_set_boolean (value, adw_navigation_split_view_get_collapsed (self->split_view));
595 break;
596
597 default:
598 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
599 }
600 }
601
602 static void
603 cc_window_set_property (GObject *object,
604 guint property_id,
605 const GValue *value,
606 GParamSpec *pspec)
607 {
608 CcWindow *self = CC_WINDOW (object);
609
610 switch (property_id)
611 {
612 case PROP_ACTIVE_PANEL:
613 set_active_panel (self, g_value_get_object (value));
614 break;
615
616 case PROP_MODEL:
617 g_assert (self->store == NULL);
618 self->store = g_value_dup_object (value);
619 break;
620
621 default:
622 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
623 }
624 }
625
626 static void
627 maybe_load_last_panel (CcWindow *self)
628 {
629 g_autofree char *id = NULL;
630
631 id = g_settings_get_string (self->settings, "last-panel");
632 if (cc_panel_list_get_current_panel (self->panel_list))
633 return;
634
635 /* select the last used panel, if any, or the first visible panel */
636 if (id != NULL && cc_shell_model_has_panel (self->store, id))
637 {
638 cc_panel_list_center_activated_row (self->panel_list, TRUE);
639 cc_panel_list_set_active_panel (self->panel_list, id);
640 }
641 else
642 cc_panel_list_activate (self->panel_list);
643 }
644
645 static void
646 cc_window_constructed (GObject *object)
647 {
648 CcWindow *self = CC_WINDOW (object);
649
650 load_window_state (self);
651
652 /* Add the panels */
653 setup_model (self);
654
655 /* After everything is loaded, select the last used panel, if any,
656 * or the first visible panel. We do that in an idle handler so we
657 * have a chance to skip it when another panel has been explicitly
658 * activated from commandline parameter or from DBus method */
659 g_idle_add_once ((GSourceOnceFunc) maybe_load_last_panel, self);
660
661 G_OBJECT_CLASS (cc_window_parent_class)->constructed (object);
662 }
663
664 static void
665 cc_window_dispose (GObject *object)
666 {
667 CcWindow *self = CC_WINDOW (object);
668
669 g_clear_pointer (&self->current_panel_id, g_free);
670 g_clear_object (&self->store);
671 g_clear_object (&self->active_panel);
672
673 G_OBJECT_CLASS (cc_window_parent_class)->dispose (object);
674 }
675
676 static void
677 cc_window_finalize (GObject *object)
678 {
679 CcWindow *self = CC_WINDOW (object);
680
681 if (self->previous_panels)
682 {
683 g_queue_free_full (self->previous_panels, g_free);
684 self->previous_panels = NULL;
685 }
686
687 g_clear_object (&self->settings);
688
689 G_OBJECT_CLASS (cc_window_parent_class)->finalize (object);
690 }
691
692 static gboolean
693 search_entry_key_pressed_cb (CcWindow *self,
694 guint keyval,
695 guint keycode,
696 GdkModifierType state,
697 GtkEventControllerKey *key_controller)
698 {
699 GtkWidget *toplevel;
700
701 /* When pressing Arrow Down on the entry we move focus to match results list */
702 if (keyval == GDK_KEY_Down || keyval == GDK_KEY_KP_Down)
703 {
704 toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (self)));
705
706 if (!toplevel)
707 return FALSE;
708
709 return gtk_widget_child_focus (toplevel, GTK_DIR_TAB_FORWARD);
710 }
711
712 return FALSE;
713 }
714
715 static void
716 cc_window_class_init (CcWindowClass *klass)
717 {
718 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
719 GObjectClass *object_class = G_OBJECT_CLASS (klass);
720
721 object_class->get_property = cc_window_get_property;
722 object_class->set_property = cc_window_set_property;
723 object_class->constructed = cc_window_constructed;
724 object_class->dispose = cc_window_dispose;
725 object_class->finalize = cc_window_finalize;
726
727 widget_class->map = cc_window_map;
728 widget_class->unmap = cc_window_unmap;
729
730 g_object_class_override_property (object_class, PROP_ACTIVE_PANEL, "active-panel");
731
732 g_object_class_install_property (object_class,
733 PROP_MODEL,
734 g_param_spec_object ("model",
735 "Model",
736 "The CcShellModel of this application",
737 CC_TYPE_SHELL_MODEL,
738 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
739
740 g_object_class_install_property (object_class,
741 PROP_COLLAPSED,
742 g_param_spec_boolean ("collapsed",
743 "Collapsed",
744 "Whether the window is collapsed",
745 FALSE,
746 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
747
748 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Settings/gtk/cc-window.ui");
749
750 gtk_widget_class_bind_template_child (widget_class, CcWindow, development_warning_dialog);
751 gtk_widget_class_bind_template_child (widget_class, CcWindow, split_view);
752 gtk_widget_class_bind_template_child (widget_class, CcWindow, panel_list);
753 gtk_widget_class_bind_template_child (widget_class, CcWindow, search_bar);
754 gtk_widget_class_bind_template_child (widget_class, CcWindow, search_entry);
755
756 gtk_widget_class_bind_template_callback (widget_class, on_split_view_collapsed_changed_cb);
757 gtk_widget_class_bind_template_callback (widget_class, on_development_warning_dialog_responded_cb);
758 gtk_widget_class_bind_template_callback (widget_class, search_entry_activate_cb);
759 gtk_widget_class_bind_template_callback (widget_class, show_panel_cb);
760 gtk_widget_class_bind_template_callback (widget_class, search_entry_key_pressed_cb);
761
762 gtk_widget_class_add_binding (widget_class, GDK_KEY_Left, GDK_ALT_MASK, go_back_shortcut_cb, NULL);
763 gtk_widget_class_add_binding (widget_class, GDK_KEY_f, GDK_CONTROL_MASK, search_shortcut_cb, NULL);
764 gtk_widget_class_add_binding (widget_class, GDK_KEY_F, GDK_CONTROL_MASK, search_shortcut_cb, NULL);
765 gtk_widget_class_add_binding_action (widget_class, GDK_KEY_q, GDK_CONTROL_MASK, "window.close", NULL);
766 gtk_widget_class_add_binding_action (widget_class, GDK_KEY_Q, GDK_CONTROL_MASK, "window.close", NULL);
767 gtk_widget_class_add_binding_action (widget_class, GDK_KEY_w, GDK_CONTROL_MASK, "window.close", NULL);
768 gtk_widget_class_add_binding_action (widget_class, GDK_KEY_W, GDK_CONTROL_MASK, "window.close", NULL);
769
770 g_type_ensure (CC_TYPE_PANEL_LIST);
771 }
772
773 static void
774 cc_window_init (CcWindow *self)
775 {
776 gtk_widget_init_template (GTK_WIDGET (self));
777
778 self->settings = g_settings_new ("org.gnome.Settings");
779 self->previous_panels = g_queue_new ();
780 self->previous_list_view = cc_panel_list_get_view (self->panel_list);
781
782 /* Add a custom CSS class on development builds */
783 if (in_flatpak_sandbox ())
784 gtk_widget_add_css_class (GTK_WIDGET (self), "devel");
785
786 gtk_search_bar_set_key_capture_widget (self->search_bar, GTK_WIDGET (self));
787 }
788
789 CcWindow *
790 cc_window_new (GtkApplication *application,
791 CcShellModel *model)
792 {
793 g_return_val_if_fail (GTK_IS_APPLICATION (application), NULL);
794
795 return g_object_new (CC_TYPE_WINDOW,
796 "application", application,
797 "resizable", TRUE,
798 "title", _("Settings"),
799 "icon-name", DEFAULT_WINDOW_ICON_NAME,
800 "show-menubar", FALSE,
801 "model", model,
802 NULL);
803 }
804
805 void
806 cc_window_set_search_item (CcWindow *center,
807 const char *search)
808 {
809 gtk_search_bar_set_search_mode (center->search_bar, TRUE);
810 gtk_editable_set_text (GTK_EDITABLE (center->search_entry), search);
811 gtk_editable_set_position (GTK_EDITABLE (center->search_entry), -1);
812 }
813