summaryrefslogtreecommitdiffstats
path: root/shell/cc-window.c
diff options
context:
space:
mode:
Diffstat (limited to 'shell/cc-window.c')
-rw-r--r--shell/cc-window.c894
1 files changed, 894 insertions, 0 deletions
diff --git a/shell/cc-window.c b/shell/cc-window.c
new file mode 100644
index 0000000..15d7124
--- /dev/null
+++ b/shell/cc-window.c
@@ -0,0 +1,894 @@
+/*
+ * Copyright (c) 2009, 2010 Intel, Inc.
+ * Copyright (c) 2010 Red Hat, Inc.
+ * Copyright (c) 2016 Endless, Inc.
+ *
+ * The Control Center 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.
+ *
+ * The Control Center 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 the Control Center; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author: Thomas Wood <thos@gnome.org>
+ */
+
+#define G_LOG_DOMAIN "cc-window"
+
+#include <config.h>
+
+#include "cc-debug.h"
+#include "cc-window.h"
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
+#include <gtk/gtk.h>
+#include <string.h>
+#include <time.h>
+
+#include "cc-application.h"
+#include "cc-panel-private.h"
+#include "cc-shell.h"
+#include "cc-shell-model.h"
+#include "cc-panel-list.h"
+#include "cc-panel-loader.h"
+#include "cc-util.h"
+
+#define MOUSE_BACK_BUTTON 8
+
+#define DEFAULT_WINDOW_ICON_NAME "gnome-control-center"
+
+struct _CcWindow
+{
+ AdwApplicationWindow parent;
+
+ GtkMessageDialog *development_warning_dialog;
+ AdwHeaderBar *header;
+ AdwLeaflet *main_leaflet;
+ CcPanelList *panel_list;
+ GtkButton *previous_button;
+ GtkSearchBar *search_bar;
+ GtkToggleButton *search_button;
+ GtkSearchEntry *search_entry;
+ GtkBox *sidebar_box;
+ AdwWindowTitle *sidebar_title_widget;
+ GtkStack *stack;
+
+ GtkWidget *old_panel;
+ GtkWidget *current_panel;
+ char *current_panel_id;
+ GQueue *previous_panels;
+
+ GtkWidget *custom_titlebar;
+
+ CcShellModel *store;
+
+ CcPanel *active_panel;
+ GSettings *settings;
+
+ gboolean folded;
+
+ CcPanelListView previous_list_view;
+};
+
+static void cc_shell_iface_init (CcShellInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (CcWindow, cc_window, ADW_TYPE_APPLICATION_WINDOW,
+ G_IMPLEMENT_INTERFACE (CC_TYPE_SHELL, cc_shell_iface_init))
+
+enum
+{
+ PROP_0,
+ PROP_ACTIVE_PANEL,
+ PROP_MODEL,
+ PROP_FOLDED,
+};
+
+/* Auxiliary methods */
+static void
+load_window_state (CcWindow *self)
+{
+ gint current_width = -1;
+ gint current_height = -1;
+ gboolean maximized = FALSE;
+
+ g_settings_get (self->settings,
+ "window-state",
+ "(iib)",
+ &current_width,
+ &current_height,
+ &maximized);
+
+ if (current_width != -1 && current_height != -1)
+ gtk_window_set_default_size (GTK_WINDOW (self), current_width, current_height);
+ if (maximized)
+ gtk_window_maximize (GTK_WINDOW (self));
+}
+
+static gboolean
+in_flatpak_sandbox (void)
+{
+ return g_strcmp0 (PROFILE, "development") == 0;
+}
+
+static void
+on_sidebar_activated_cb (CcWindow *self)
+{
+ adw_leaflet_navigate (self->main_leaflet, ADW_NAVIGATION_DIRECTION_FORWARD);
+}
+
+static gboolean
+activate_panel (CcWindow *self,
+ const gchar *id,
+ GVariant *parameters,
+ const gchar *name,
+ GIcon *gicon,
+ CcPanelVisibility visibility)
+{
+ g_autoptr(GTimer) timer = NULL;
+ GtkWidget *sidebar_widget;
+ gdouble ellapsed_time;
+
+ CC_ENTRY;
+
+ if (!id)
+ CC_RETURN (FALSE);
+
+ if (visibility == CC_PANEL_HIDDEN)
+ CC_RETURN (FALSE);
+
+ timer = g_timer_new ();
+
+ g_settings_set_string (self->settings, "last-panel", id);
+
+ /* Begin the profile */
+ g_timer_start (timer);
+
+ if (self->current_panel)
+ g_signal_handlers_disconnect_by_data (self->current_panel, self);
+ self->current_panel = GTK_WIDGET (cc_panel_loader_load_by_name (CC_SHELL (self), id, name, parameters));
+ cc_panel_set_folded (CC_PANEL (self->current_panel), adw_leaflet_get_folded (self->main_leaflet));
+ cc_shell_set_active_panel (CC_SHELL (self), CC_PANEL (self->current_panel));
+
+ gtk_stack_add_named (self->stack, self->current_panel, id);
+
+ /* switch to the new panel */
+ gtk_stack_set_visible_child_name (self->stack, id);
+
+ sidebar_widget = cc_panel_get_sidebar_widget (CC_PANEL (self->current_panel));
+ cc_panel_list_add_sidebar_widget (self->panel_list, sidebar_widget);
+ /* Ensure we show the panel when the leaflet is folded and a sidebar widget's
+ * row is activated.
+ */
+ g_signal_connect_object (self->current_panel, "sidebar-activated", G_CALLBACK (on_sidebar_activated_cb), self, G_CONNECT_SWAPPED);
+
+ /* Finish profiling */
+ g_timer_stop (timer);
+
+ ellapsed_time = g_timer_elapsed (timer, NULL);
+
+ g_debug ("Time to open panel '%s': %lfs", name, ellapsed_time);
+
+ CC_RETURN (TRUE);
+}
+
+static void
+add_current_panel_to_history (CcWindow *self,
+ const char *start_id)
+{
+ g_return_if_fail (start_id != NULL);
+
+ if (!self->current_panel_id || g_strcmp0 (self->current_panel_id, start_id) == 0)
+ return;
+
+ g_queue_push_head (self->previous_panels, g_strdup (self->current_panel_id));
+ g_debug ("Added '%s' to the previous panels", self->current_panel_id);
+}
+
+static gboolean
+find_iter_for_panel_id (CcWindow *self,
+ const gchar *panel_id,
+ GtkTreeIter *out_iter)
+{
+ GtkTreeIter iter;
+ gboolean valid;
+
+ valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->store), &iter);
+
+ while (valid)
+ {
+ g_autofree gchar *id = NULL;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (self->store),
+ &iter,
+ COL_ID, &id,
+ -1);
+
+ if (g_strcmp0 (id, panel_id) == 0)
+ break;
+
+ valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (self->store), &iter);
+ }
+
+ g_assert (out_iter != NULL);
+ *out_iter = iter;
+
+ return valid;
+}
+
+static void
+update_list_title (CcWindow *self)
+{
+ CcPanelListView view;
+ GtkTreeIter iter;
+ g_autofree gchar *title = NULL;
+
+ CC_ENTRY;
+
+ view = cc_panel_list_get_view (self->panel_list);
+ title = NULL;
+
+ switch (view)
+ {
+ case CC_PANEL_LIST_PRIVACY:
+ title = g_strdup (_("Privacy"));
+ break;
+
+ case CC_PANEL_LIST_MAIN:
+ title = g_strdup (_("Settings"));
+ break;
+
+ case CC_PANEL_LIST_WIDGET:
+ find_iter_for_panel_id (self, self->current_panel_id, &iter);
+ gtk_tree_model_get (GTK_TREE_MODEL (self->store),
+ &iter,
+ COL_NAME, &title,
+ -1);
+ break;
+
+ case CC_PANEL_LIST_SEARCH:
+ title = NULL;
+ break;
+ }
+
+ if (title)
+ adw_window_title_set_title (self->sidebar_title_widget, title);
+
+ CC_EXIT;
+}
+
+static void
+on_row_changed_cb (CcWindow *self,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ GtkTreeModel *model)
+{
+ g_autofree gchar *id = NULL;
+ CcPanelVisibility visibility;
+
+ gtk_tree_model_get (model, iter,
+ COL_ID, &id,
+ COL_VISIBILITY, &visibility,
+ -1);
+
+ cc_panel_list_set_panel_visibility (self->panel_list, id, visibility);
+}
+
+static void
+setup_model (CcWindow *self)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean valid;
+
+ /* CcApplication must have a valid model at this point */
+ g_assert (self->store != NULL);
+
+ model = GTK_TREE_MODEL (self->store);
+
+ cc_panel_loader_fill_model (self->store);
+
+ /* Create a row for each panel */
+ valid = gtk_tree_model_get_iter_first (model, &iter);
+
+ while (valid)
+ {
+ CcPanelCategory category;
+ g_autoptr(GIcon) icon = NULL;
+ g_autofree gchar *name = NULL;
+ g_autofree gchar *description = NULL;
+ g_autofree gchar *id = NULL;
+ g_auto(GStrv) keywords = NULL;
+ CcPanelVisibility visibility;
+ gboolean has_sidebar;
+ const gchar *icon_name = NULL;
+
+ gtk_tree_model_get (model, &iter,
+ COL_CATEGORY, &category,
+ COL_DESCRIPTION, &description,
+ COL_GICON, &icon,
+ COL_ID, &id,
+ COL_NAME, &name,
+ COL_KEYWORDS, &keywords,
+ COL_VISIBILITY, &visibility,
+ COL_HAS_SIDEBAR, &has_sidebar,
+ -1);
+
+ if (G_IS_THEMED_ICON (icon))
+ icon_name = g_themed_icon_get_names (G_THEMED_ICON (icon))[0];
+
+ cc_panel_list_add_panel (self->panel_list,
+ category,
+ id,
+ name,
+ description,
+ keywords,
+ icon_name,
+ visibility,
+ has_sidebar);
+
+ valid = gtk_tree_model_iter_next (model, &iter);
+ }
+
+ /* React to visibility changes */
+ g_signal_connect_object (model, "row-changed", G_CALLBACK (on_row_changed_cb), self, G_CONNECT_SWAPPED);
+}
+
+static void
+update_headerbar_buttons (CcWindow *self)
+{
+ gboolean is_main_view;
+
+ CC_ENTRY;
+
+ is_main_view = cc_panel_list_get_view (self->panel_list) == CC_PANEL_LIST_MAIN;
+
+ gtk_widget_set_visible (GTK_WIDGET (self->previous_button), !is_main_view);
+ gtk_widget_set_visible (GTK_WIDGET (self->search_button), is_main_view);
+ gtk_search_bar_set_key_capture_widget (self->search_bar, is_main_view ? GTK_WIDGET (self) : NULL);
+
+ update_list_title (self);
+
+ CC_EXIT;
+}
+
+static gboolean
+set_active_panel_from_id (CcWindow *self,
+ const gchar *start_id,
+ GVariant *parameters,
+ gboolean add_to_history,
+ gboolean force_moving_to_the_panel,
+ GError **error)
+{
+ g_autoptr(GIcon) gicon = NULL;
+ g_autofree gchar *name = NULL;
+ CcPanelVisibility visibility;
+ GtkTreeIter iter;
+ CcPanelListView view;
+ gboolean activated;
+ gboolean found;
+
+ CC_ENTRY;
+
+ view = cc_panel_list_get_view (self->panel_list);
+
+ /* When loading the same panel again, just set its parameters */
+ if (g_strcmp0 (self->current_panel_id, start_id) == 0)
+ {
+ g_object_set (G_OBJECT (self->current_panel), "parameters", parameters, NULL);
+ if (force_moving_to_the_panel || self->previous_list_view == view)
+ adw_leaflet_navigate (self->main_leaflet, ADW_NAVIGATION_DIRECTION_FORWARD);
+ self->previous_list_view = view;
+ CC_RETURN (TRUE);
+ }
+
+ found = find_iter_for_panel_id (self, start_id, &iter);
+ if (!found)
+ {
+ g_warning ("Could not find settings panel \"%s\"", start_id);
+ CC_RETURN (TRUE);
+ }
+
+ if (self->old_panel)
+ gtk_stack_remove (self->stack, g_steal_pointer (&self->old_panel));
+
+ /* old_panel will be removed by the on_stack_transition_running_changed_cb
+ * callback - or, if panels changed before the transition ended, by the code
+ * just above.
+ */
+ self->old_panel = self->current_panel;
+ if (self->old_panel)
+ cc_panel_deactivate (CC_PANEL (self->old_panel));
+
+ gtk_tree_model_get (GTK_TREE_MODEL (self->store),
+ &iter,
+ COL_NAME, &name,
+ COL_GICON, &gicon,
+ COL_VISIBILITY, &visibility,
+ -1);
+
+ /* Activate the panel */
+ activated = activate_panel (self, start_id, parameters, name, gicon, visibility);
+
+ /* Failed to activate the panel for some reason, let's keep the old
+ * panel around instead */
+ if (!activated)
+ {
+ g_debug ("Failed to activate panel");
+ CC_RETURN (TRUE);
+ }
+
+ if (add_to_history)
+ add_current_panel_to_history (self, start_id);
+
+ if (force_moving_to_the_panel)
+ adw_leaflet_navigate (self->main_leaflet, ADW_NAVIGATION_DIRECTION_FORWARD);
+
+ g_free (self->current_panel_id);
+ self->current_panel_id = g_strdup (start_id);
+
+ CC_TRACE_MSG ("Current panel id: %s", start_id);
+
+ cc_panel_list_set_active_panel (self->panel_list, start_id);
+
+ update_headerbar_buttons (self);
+
+ CC_RETURN (TRUE);
+}
+
+static void
+set_active_panel (CcWindow *self,
+ CcPanel *panel)
+{
+ g_return_if_fail (CC_IS_SHELL (self));
+ g_return_if_fail (panel == NULL || CC_IS_PANEL (panel));
+
+ if (panel != self->active_panel)
+ {
+ /* remove the old panel */
+ g_clear_object (&self->active_panel);
+
+ /* set the new panel */
+ if (panel)
+ self->active_panel = g_object_ref (panel);
+
+ g_object_notify (G_OBJECT (self), "active-panel");
+ }
+}
+
+static void
+switch_to_previous_panel (CcWindow *self)
+{
+ g_autofree gchar *previous_panel_id = NULL;
+
+ CC_ENTRY;
+
+ if (g_queue_get_length (self->previous_panels) == 0)
+ CC_RETURN ();
+
+ previous_panel_id = g_queue_pop_head (self->previous_panels);
+
+ g_debug ("Going to previous panel (%s)", previous_panel_id);
+
+ set_active_panel_from_id (self, previous_panel_id, NULL, FALSE, FALSE, NULL);
+
+ CC_EXIT;
+}
+
+/* Callbacks */
+
+static void
+navigate_action_cb (GtkWidget *widget,
+ const gchar *action_name,
+ GVariant *parameter)
+{
+ CcWindow *self = CC_WINDOW (widget);
+
+ adw_leaflet_navigate (self->main_leaflet, g_variant_get_int32 (parameter));
+}
+
+static void
+on_main_leaflet_folded_changed_cb (CcWindow *self)
+{
+ GtkSelectionMode selection_mode;
+ gboolean folded;
+
+ g_assert (CC_IS_WINDOW (self));
+
+ folded = adw_leaflet_get_folded (self->main_leaflet);
+
+ selection_mode = folded ? GTK_SELECTION_NONE : GTK_SELECTION_SINGLE;
+ cc_panel_list_set_selection_mode (self->panel_list, selection_mode);
+
+ cc_panel_set_folded (CC_PANEL (self->current_panel), folded);
+}
+
+static void
+show_panel_cb (CcWindow *self,
+ const gchar *panel_id)
+{
+ if (!panel_id)
+ return;
+
+ set_active_panel_from_id (self, panel_id, NULL, TRUE, FALSE, NULL);
+}
+
+static void
+search_entry_activate_cb (CcWindow *self)
+{
+ gboolean changed;
+
+ changed = cc_panel_list_activate (self->panel_list);
+
+ gtk_search_bar_set_search_mode (self->search_bar, !changed);
+}
+
+static void
+previous_button_clicked_cb (CcWindow *self)
+{
+ g_debug ("Num previous panels? %d", g_queue_get_length (self->previous_panels));
+
+ /* When in search, simply unset the search mode */
+ if (gtk_search_bar_get_search_mode (self->search_bar))
+ gtk_search_bar_set_search_mode (self->search_bar, FALSE);
+ else
+ cc_panel_list_go_previous (self->panel_list);
+
+ update_headerbar_buttons (self);
+}
+
+static gboolean
+go_back_shortcut_cb (GtkWidget *widget,
+ GVariant *args,
+ gpointer user_data)
+{
+ g_debug ("Going to previous panel");
+ switch_to_previous_panel (CC_WINDOW (widget));
+
+ return GDK_EVENT_STOP;
+}
+
+static gboolean
+search_shortcut_cb (GtkWidget *widget,
+ GVariant *args,
+ gpointer user_data)
+{
+ CcPanelListView view;
+ CcWindow *self;
+ gboolean search;
+
+ self = CC_WINDOW (widget);
+ view = cc_panel_list_get_view (self->panel_list);
+
+ /* The search only happens when we're in the MAIN view */
+ if (view != CC_PANEL_LIST_MAIN && view != CC_PANEL_LIST_SEARCH)
+ return GDK_EVENT_PROPAGATE;
+
+ search = !gtk_search_bar_get_search_mode (self->search_bar);
+ gtk_search_bar_set_search_mode (self->search_bar, search);
+ if (search)
+ gtk_widget_grab_focus (GTK_WIDGET (self->search_entry));
+
+ return GDK_EVENT_STOP;
+}
+
+static void
+on_development_warning_dialog_responded_cb (CcWindow *self)
+{
+ g_debug ("Disabling development build warning dialog");
+ g_settings_set_boolean (self->settings, "show-development-warning", FALSE);
+
+ gtk_widget_hide (GTK_WIDGET (self->development_warning_dialog));
+}
+
+static void
+on_stack_transition_running_changed_cb (GtkStack *stack,
+ GParamSpec *pspec,
+ CcWindow *self)
+{
+ gboolean transition_running;
+
+ CC_ENTRY;
+
+ transition_running = gtk_stack_get_transition_running (stack);
+
+ if (!transition_running && self->old_panel)
+ gtk_stack_remove (self->stack, g_steal_pointer (&self->old_panel));
+
+ CC_EXIT;
+}
+
+/* CcShell implementation */
+static gboolean
+cc_window_set_active_panel_from_id (CcShell *shell,
+ const gchar *start_id,
+ GVariant *parameters,
+ GError **error)
+{
+ return set_active_panel_from_id (CC_WINDOW (shell), start_id, parameters, TRUE, TRUE, error);
+}
+
+static GtkWidget *
+cc_window_get_toplevel (CcShell *self)
+{
+ return GTK_WIDGET (self);
+}
+
+static void
+cc_shell_iface_init (CcShellInterface *iface)
+{
+ iface->set_active_panel_from_id = cc_window_set_active_panel_from_id;
+ iface->get_toplevel = cc_window_get_toplevel;
+}
+
+/* GtkWidget overrides */
+static void
+cc_window_map (GtkWidget *widget)
+{
+ CcWindow *self = (CcWindow *) widget;
+
+ GTK_WIDGET_CLASS (cc_window_parent_class)->map (widget);
+
+ /* Show a warning for Flatpak builds */
+ if (in_flatpak_sandbox () && g_settings_get_boolean (self->settings, "show-development-warning"))
+ gtk_window_present (GTK_WINDOW (self->development_warning_dialog));
+}
+
+static void
+cc_window_unmap (GtkWidget *widget)
+{
+ CcWindow *self = CC_WINDOW (widget);
+ gboolean maximized;
+ gint height;
+ gint width;
+
+ maximized = gtk_window_is_maximized (GTK_WINDOW (self));
+ gtk_window_get_default_size (GTK_WINDOW (self), &width, &height);
+
+ g_settings_set (self->settings,
+ "window-state",
+ "(iib)",
+ width,
+ height,
+ maximized);
+
+ GTK_WIDGET_CLASS (cc_window_parent_class)->unmap (widget);
+}
+
+/* GObject Implementation */
+static void
+cc_window_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CcWindow *self = CC_WINDOW (object);
+
+ switch (property_id)
+ {
+ case PROP_ACTIVE_PANEL:
+ g_value_set_object (value, self->active_panel);
+ break;
+
+ case PROP_MODEL:
+ g_value_set_object (value, self->store);
+ break;
+
+ case PROP_FOLDED:
+ g_value_set_boolean (value, self->folded);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+cc_window_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CcWindow *self = CC_WINDOW (object);
+
+ switch (property_id)
+ {
+ case PROP_ACTIVE_PANEL:
+ set_active_panel (self, g_value_get_object (value));
+ break;
+
+ case PROP_MODEL:
+ g_assert (self->store == NULL);
+ self->store = g_value_dup_object (value);
+ break;
+
+ case PROP_FOLDED:
+ self->folded = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+cc_window_constructed (GObject *object)
+{
+ CcWindow *self = CC_WINDOW (object);
+ g_autofree char *id = NULL;
+
+ load_window_state (self);
+
+ /* Add the panels */
+ setup_model (self);
+
+ /* After everything is loaded, select the last used panel, if any,
+ * or the first visible panel */
+ id = g_settings_get_string (self->settings, "last-panel");
+ if (id != NULL && cc_shell_model_has_panel (self->store, id))
+ cc_panel_list_set_active_panel (self->panel_list, id);
+ else
+ cc_panel_list_activate (self->panel_list);
+
+ g_signal_connect_swapped (self->panel_list,
+ "notify::view",
+ G_CALLBACK (update_headerbar_buttons),
+ self);
+
+ update_headerbar_buttons (self);
+ adw_leaflet_set_visible_child (self->main_leaflet,
+ GTK_WIDGET (self->sidebar_box));
+
+ G_OBJECT_CLASS (cc_window_parent_class)->constructed (object);
+}
+
+static void
+cc_window_dispose (GObject *object)
+{
+ CcWindow *self = CC_WINDOW (object);
+
+ g_clear_pointer (&self->current_panel_id, g_free);
+ g_clear_object (&self->store);
+ g_clear_object (&self->active_panel);
+
+ G_OBJECT_CLASS (cc_window_parent_class)->dispose (object);
+}
+
+static void
+cc_window_finalize (GObject *object)
+{
+ CcWindow *self = CC_WINDOW (object);
+
+ if (self->previous_panels)
+ {
+ g_queue_free_full (self->previous_panels, g_free);
+ self->previous_panels = NULL;
+ }
+
+ g_clear_object (&self->settings);
+
+ G_OBJECT_CLASS (cc_window_parent_class)->finalize (object);
+}
+
+static void
+cc_window_class_init (CcWindowClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = cc_window_get_property;
+ object_class->set_property = cc_window_set_property;
+ object_class->constructed = cc_window_constructed;
+ object_class->dispose = cc_window_dispose;
+ object_class->finalize = cc_window_finalize;
+
+ widget_class->map = cc_window_map;
+ widget_class->unmap = cc_window_unmap;
+
+ g_object_class_override_property (object_class, PROP_ACTIVE_PANEL, "active-panel");
+
+ g_object_class_install_property (object_class,
+ PROP_MODEL,
+ g_param_spec_object ("model",
+ "Model",
+ "The CcShellModel of this application",
+ CC_TYPE_SHELL_MODEL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_FOLDED,
+ g_param_spec_boolean ("folded",
+ "Folded",
+ "Whether the window is foled",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Settings/gtk/cc-window.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, CcWindow, development_warning_dialog);
+ gtk_widget_class_bind_template_child (widget_class, CcWindow, header);
+ gtk_widget_class_bind_template_child (widget_class, CcWindow, main_leaflet);
+ gtk_widget_class_bind_template_child (widget_class, CcWindow, panel_list);
+ gtk_widget_class_bind_template_child (widget_class, CcWindow, previous_button);
+ gtk_widget_class_bind_template_child (widget_class, CcWindow, search_bar);
+ gtk_widget_class_bind_template_child (widget_class, CcWindow, search_button);
+ gtk_widget_class_bind_template_child (widget_class, CcWindow, search_entry);
+ gtk_widget_class_bind_template_child (widget_class, CcWindow, sidebar_box);
+ gtk_widget_class_bind_template_child (widget_class, CcWindow, sidebar_title_widget);
+ gtk_widget_class_bind_template_child (widget_class, CcWindow, stack);
+
+ gtk_widget_class_bind_template_callback (widget_class, on_main_leaflet_folded_changed_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_development_warning_dialog_responded_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_stack_transition_running_changed_cb);
+ gtk_widget_class_bind_template_callback (widget_class, previous_button_clicked_cb);
+ gtk_widget_class_bind_template_callback (widget_class, search_entry_activate_cb);
+ gtk_widget_class_bind_template_callback (widget_class, show_panel_cb);
+ gtk_widget_class_bind_template_callback (widget_class, update_list_title);
+
+ gtk_widget_class_add_binding (widget_class, GDK_KEY_Left, GDK_ALT_MASK, go_back_shortcut_cb, NULL);
+ gtk_widget_class_add_binding (widget_class, GDK_KEY_s, GDK_CONTROL_MASK, search_shortcut_cb, NULL);
+ gtk_widget_class_add_binding (widget_class, GDK_KEY_S, GDK_CONTROL_MASK, search_shortcut_cb, NULL);
+ gtk_widget_class_add_binding (widget_class, GDK_KEY_f, GDK_CONTROL_MASK, search_shortcut_cb, NULL);
+ gtk_widget_class_add_binding (widget_class, GDK_KEY_F, GDK_CONTROL_MASK, search_shortcut_cb, NULL);
+ gtk_widget_class_add_binding_action (widget_class, GDK_KEY_q, GDK_CONTROL_MASK, "window.close", NULL);
+ gtk_widget_class_add_binding_action (widget_class, GDK_KEY_Q, GDK_CONTROL_MASK, "window.close", NULL);
+ gtk_widget_class_add_binding_action (widget_class, GDK_KEY_w, GDK_CONTROL_MASK, "window.close", NULL);
+ gtk_widget_class_add_binding_action (widget_class, GDK_KEY_W, GDK_CONTROL_MASK, "window.close", NULL);
+
+ gtk_widget_class_install_action (widget_class, "window.navigate", "i", navigate_action_cb);
+
+ g_type_ensure (CC_TYPE_PANEL_LIST);
+}
+
+static void
+cc_window_init (CcWindow *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ self->settings = g_settings_new ("org.gnome.Settings");
+ self->previous_panels = g_queue_new ();
+ self->previous_list_view = cc_panel_list_get_view (self->panel_list);
+
+ g_object_bind_property (self->main_leaflet,
+ "folded",
+ self,
+ "folded",
+ G_BINDING_SYNC_CREATE);
+
+ /* Add a custom CSS class on development builds */
+ if (in_flatpak_sandbox ())
+ gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (self)), "devel");
+}
+
+CcWindow *
+cc_window_new (GtkApplication *application,
+ CcShellModel *model)
+{
+ g_return_val_if_fail (GTK_IS_APPLICATION (application), NULL);
+
+ return g_object_new (CC_TYPE_WINDOW,
+ "application", application,
+ "resizable", TRUE,
+ "title", _("Settings"),
+ "icon-name", DEFAULT_WINDOW_ICON_NAME,
+ "show-menubar", FALSE,
+ "model", model,
+ NULL);
+}
+
+void
+cc_window_set_search_item (CcWindow *center,
+ const char *search)
+{
+ gtk_search_bar_set_search_mode (center->search_bar, TRUE);
+ gtk_editable_set_text (GTK_EDITABLE (center->search_entry), search);
+ gtk_editable_set_position (GTK_EDITABLE (center->search_entry), -1);
+}