/* * 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 */ #define G_LOG_DOMAIN "cc-window" #include #include "cc-debug.h" #include "cc-window.h" #include #include #include #include #include #include #include #include #include #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); }