summaryrefslogtreecommitdiffstats
path: root/shell/cc-window.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--shell/cc-window.c948
1 files changed, 948 insertions, 0 deletions
diff --git a/shell/cc-window.c b/shell/cc-window.c
new file mode 100644
index 0000000..400c391
--- /dev/null
+++ b/shell/cc-window.c
@@ -0,0 +1,948 @@
+/*
+ * 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 <gdk/gdkkeysyms.h>
+#include <gdk/gdkx.h>
+#include <handy.h>
+#include <string.h>
+#include <time.h>
+
+#include "cc-application.h"
+#include "cc-panel.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
+{
+ GtkApplicationWindow parent;
+
+ GtkRevealer *back_revealer;
+ GtkMessageDialog *development_warning_dialog;
+ GtkHeaderBar *header;
+ HdyLeaflet *header_box;
+ GtkSizeGroup *header_sizegroup;
+ HdyLeaflet *main_leaflet;
+ GtkHeaderBar *panel_headerbar;
+ CcPanelList *panel_list;
+ GtkButton *previous_button;
+ GtkSearchBar *search_bar;
+ GtkToggleButton *search_button;
+ GtkSearchEntry *search_entry;
+ GtkBox *sidebar_box;
+ GtkStack *stack;
+ GtkBox *top_left_box;
+ GtkBox *top_right_box;
+
+ GtkWidget *current_panel;
+ char *current_panel_id;
+ GQueue *previous_panels;
+
+ GPtrArray *custom_widgets;
+
+ CcShellModel *store;
+
+ CcPanel *active_panel;
+ GSettings *settings;
+
+ CcPanelListView previous_list_view;
+};
+
+static void cc_shell_iface_init (CcShellInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (CcWindow, cc_window, GTK_TYPE_APPLICATION_WINDOW,
+ G_IMPLEMENT_INTERFACE (CC_TYPE_SHELL, cc_shell_iface_init))
+
+enum
+{
+ PROP_0,
+ PROP_ACTIVE_PANEL,
+ PROP_MODEL
+};
+
+/* Auxiliary methods */
+static gboolean
+in_flatpak_sandbox (void)
+{
+ return g_strcmp0 (PROFILE, "development") == 0;
+}
+
+static void
+remove_all_custom_widgets (CcWindow *self)
+{
+ GtkWidget *parent;
+ GtkWidget *widget;
+ guint i;
+
+ CC_ENTRY;
+
+ /* remove from the header */
+ for (i = 0; i < self->custom_widgets->len; i++)
+ {
+ widget = g_ptr_array_index (self->custom_widgets, i);
+ parent = gtk_widget_get_parent (widget);
+
+ g_assert (parent == GTK_WIDGET (self->top_right_box) || parent == GTK_WIDGET (self->top_left_box));
+ gtk_container_remove (GTK_CONTAINER (parent), widget);
+ }
+ g_ptr_array_set_size (self->custom_widgets, 0);
+
+ CC_EXIT;
+}
+
+static void
+show_panel (CcWindow *self)
+{
+ hdy_leaflet_set_visible_child (self->main_leaflet, GTK_WIDGET (self->stack));
+ hdy_leaflet_set_visible_child (self->header_box, GTK_WIDGET (self->panel_headerbar));
+}
+
+static void
+show_sidebar (CcWindow *self)
+{
+ hdy_leaflet_set_visible_child (self->main_leaflet, GTK_WIDGET (self->sidebar_box));
+ hdy_leaflet_set_visible_child (self->header_box, GTK_WIDGET (self->header));
+}
+
+static void
+on_sidebar_activated_cb (CcWindow *self)
+{
+ show_panel (self);
+}
+
+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;
+ GtkWidget *title_widget;
+ gdouble ellapsed_time;
+
+ CC_ENTRY;
+
+ if (!id)
+ CC_RETURN (FALSE);
+
+ if (visibility == CC_PANEL_HIDDEN)
+ CC_RETURN (FALSE);
+
+ /* clear any custom widgets */
+ remove_all_custom_widgets (self);
+
+ 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, parameters));
+ cc_shell_set_active_panel (CC_SHELL (self), CC_PANEL (self->current_panel));
+ gtk_widget_show (self->current_panel);
+
+ gtk_stack_add_named (self->stack, self->current_panel, id);
+
+ /* switch to the new panel */
+ gtk_widget_show (self->current_panel);
+ gtk_stack_set_visible_child_name (self->stack, id);
+
+ /* set the title of the window */
+ gtk_window_set_role (GTK_WINDOW (self), id);
+ gtk_header_bar_set_title (self->panel_headerbar, name);
+
+ title_widget = cc_panel_get_title_widget (CC_PANEL (self->current_panel));
+ gtk_header_bar_set_custom_title (self->panel_headerbar, title_widget);
+
+ 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)
+ gtk_header_bar_set_title (self->header, 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);
+
+ 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;
+ GtkWidget *old_panel;
+ 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)
+ show_panel (self);
+ 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);
+ }
+
+ old_panel = self->current_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)
+ show_panel (self);
+
+ g_free (self->current_panel_id);
+ self->current_panel_id = g_strdup (start_id);
+
+ CC_TRACE_MSG ("Current panel id: %s", start_id);
+
+ if (old_panel)
+ gtk_container_remove (GTK_CONTAINER (self->stack), old_panel);
+
+ 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
+on_main_leaflet_folded_changed_cb (CcWindow *self)
+{
+ GtkSelectionMode selection_mode;
+
+ g_assert (CC_IS_WINDOW (self));
+
+ selection_mode = GTK_SELECTION_SINGLE;
+
+ if (hdy_leaflet_get_folded (self->main_leaflet))
+ selection_mode = GTK_SELECTION_NONE;
+
+ cc_panel_list_set_selection_mode (self->panel_list, selection_mode);
+}
+
+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
+back_button_clicked_cb (CcWindow *self)
+{
+ show_sidebar (self);
+}
+
+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 unsed 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 void
+gdk_window_set_cb (CcWindow *self)
+{
+ GdkWindow *window;
+ g_autofree gchar *str = NULL;
+
+ if (!GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
+ return;
+
+ window = gtk_widget_get_window (GTK_WIDGET (self));
+
+ if (!window)
+ return;
+
+ str = g_strdup_printf ("%u", (guint) GDK_WINDOW_XID (window));
+ g_setenv ("GNOME_CONTROL_CENTER_XID", str, TRUE);
+}
+
+static gboolean
+window_map_event_cb (CcWindow *self)
+{
+ /* If focus ends up in a category icon view one of the items is
+ * immediately selected which looks odd when we are starting up, so
+ * we explicitly unset the focus here. */
+ gtk_window_set_focus (GTK_WINDOW (self), NULL);
+ return GDK_EVENT_PROPAGATE;
+}
+
+static gboolean
+window_key_press_event_cb (CcWindow *self,
+ GdkEventKey *event)
+{
+ GdkModifierType state;
+ CcPanelListView view;
+ GdkKeymap *keymap;
+ gboolean retval;
+ gboolean is_rtl;
+
+ retval = GDK_EVENT_PROPAGATE;
+ state = event->state;
+ keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (self)));
+ gdk_keymap_add_virtual_modifiers (keymap, &state);
+
+ state = state & gtk_accelerator_get_default_mod_mask ();
+ is_rtl = gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL;
+ 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 &&
+ gtk_search_bar_handle_event (self->search_bar, (GdkEvent*) event) == GDK_EVENT_STOP)
+ {
+ return GDK_EVENT_STOP;
+ }
+
+ if (state == GDK_CONTROL_MASK)
+ {
+ switch (event->keyval)
+ {
+ case GDK_KEY_s:
+ case GDK_KEY_S:
+ case GDK_KEY_f:
+ case GDK_KEY_F:
+ /* The search only happens when we're in the MAIN view */
+ if (view != CC_PANEL_LIST_MAIN &&
+ view != CC_PANEL_LIST_SEARCH)
+ {
+ break;
+ }
+
+ retval = !gtk_search_bar_get_search_mode (self->search_bar);
+ gtk_search_bar_set_search_mode (self->search_bar, retval);
+ if (retval)
+ gtk_widget_grab_focus (GTK_WIDGET (self->search_entry));
+ retval = GDK_EVENT_STOP;
+ break;
+ case GDK_KEY_Q:
+ case GDK_KEY_q:
+ case GDK_KEY_W:
+ case GDK_KEY_w:
+ gtk_widget_destroy (GTK_WIDGET (self));
+ retval = GDK_EVENT_STOP;
+ break;
+ }
+ }
+ else if ((!is_rtl && state == GDK_MOD1_MASK && event->keyval == GDK_KEY_Left) ||
+ (is_rtl && state == GDK_MOD1_MASK && event->keyval == GDK_KEY_Right) ||
+ event->keyval == GDK_KEY_Back)
+ {
+ g_debug ("Going to previous panel");
+ switch_to_previous_panel (self);
+ retval = GDK_EVENT_STOP;
+ }
+
+ return retval;
+}
+
+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));
+}
+
+/* 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 void
+cc_window_embed_widget_in_header (CcShell *shell,
+ GtkWidget *widget,
+ GtkPositionType position)
+{
+ CcWindow *self = CC_WINDOW (shell);
+
+ CC_ENTRY;
+
+ /* add to header */
+ switch (position)
+ {
+ case GTK_POS_RIGHT:
+ gtk_container_add (GTK_CONTAINER (self->top_right_box), widget);
+ break;
+
+ case GTK_POS_LEFT:
+ gtk_container_add (GTK_CONTAINER (self->top_left_box), widget);
+ break;
+
+ case GTK_POS_TOP:
+ case GTK_POS_BOTTOM:
+ default:
+ g_warning ("Invalid position passed");
+ return;
+ }
+
+ g_ptr_array_add (self->custom_widgets, g_object_ref (widget));
+
+ gtk_size_group_add_widget (self->header_sizegroup, widget);
+
+ CC_EXIT;
+}
+
+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->embed_widget_in_header = cc_window_embed_widget_in_header;
+ 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));
+}
+
+/* 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;
+
+ 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;
+
+ 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;
+
+ /* 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);
+ show_sidebar (self);
+
+ 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_pointer (&self->custom_widgets, g_ptr_array_unref);
+ 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;
+
+ 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));
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/ControlCenter/gtk/cc-window.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, CcWindow, back_revealer);
+ 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, header_box);
+ gtk_widget_class_bind_template_child (widget_class, CcWindow, header_sizegroup);
+ gtk_widget_class_bind_template_child (widget_class, CcWindow, main_leaflet);
+ gtk_widget_class_bind_template_child (widget_class, CcWindow, panel_headerbar);
+ 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, stack);
+ gtk_widget_class_bind_template_child (widget_class, CcWindow, top_left_box);
+ gtk_widget_class_bind_template_child (widget_class, CcWindow, top_right_box);
+
+ gtk_widget_class_bind_template_callback (widget_class, back_button_clicked_cb);
+ gtk_widget_class_bind_template_callback (widget_class, gdk_window_set_cb);
+ 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, 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_bind_template_callback (widget_class, window_key_press_event_cb);
+ gtk_widget_class_bind_template_callback (widget_class, window_map_event_cb);
+
+ g_type_ensure (CC_TYPE_PANEL_LIST);
+}
+
+static void
+cc_window_init (CcWindow *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ gtk_widget_add_events (GTK_WIDGET (self), GDK_BUTTON_RELEASE_MASK);
+
+ self->settings = g_settings_new ("org.gnome.ControlCenter");
+ self->custom_widgets = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
+ self->previous_panels = g_queue_new ();
+ self->previous_list_view = cc_panel_list_get_view (self->panel_list);
+
+ /* 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,
+ "window-position", GTK_WIN_POS_CENTER,
+ "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_entry_set_text (GTK_ENTRY (center->search_entry), search);
+ gtk_editable_set_position (GTK_EDITABLE (center->search_entry), -1);
+}