1 /*
2  * Copyright (c) 2009, 2010 Intel, Inc.
3  * Copyright (c) 2010 Red Hat, Inc.
4  *
5  * The Control Center is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; either version 2 of the License, or (at your
8  * option) any later version.
9  *
10  * The Control Center is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13  * for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with the Control Center; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  * Author: Thomas Wood <thos@gnome.org>
20  */
21 
22 #include "config.h"
23 
24 #include "cinnamon-control-center.h"
25 
26 #include <glib/gi18n.h>
27 #include <gio/gio.h>
28 #include <gio/gdesktopappinfo.h>
29 #include <gtk/gtk.h>
30 #include <gdk/gdkkeysyms.h>
31 #include <string.h>
32 
33 #define GMENU_I_KNOW_THIS_IS_UNSTABLE
34 #include <gmenu-tree.h>
35 
36 #include "cc-panel.h"
37 #include "cc-shell.h"
38 #include "cc-shell-category-view.h"
39 #include "cc-shell-model.h"
40 #include "cc-shell-nav-bar.h"
41 
42 G_DEFINE_TYPE (CinnamonControlCenter, cinnamon_control_center, CC_TYPE_SHELL)
43 
44 #define CONTROL_CENTER_PRIVATE(o) \
45   (G_TYPE_INSTANCE_GET_PRIVATE ((o), CINNAMON_TYPE_CONTROL_CENTER, CinnamonControlCenterPrivate))
46 
47 #define W(b,x) GTK_WIDGET (gtk_builder_get_object (b, x))
48 
49 /* Use a fixed width for the shell, since resizing horizontally is more awkward
50  * for the user than resizing vertically
51  * Both sizes are defined in https://live.gnome.org/Design/SystemSettings/ */
52 #define FIXED_WIDTH 740
53 #define UNITY_FIXED_WIDTH 850
54 #define FIXED_HEIGHT 650
55 #define SMALL_SCREEN_FIXED_HEIGHT 400
56 
57 #define MIN_ICON_VIEW_HEIGHT 300
58 
59 typedef enum {
60 	SMALL_SCREEN_UNSET,
61 	SMALL_SCREEN_TRUE,
62 	SMALL_SCREEN_FALSE
63 } CcSmallScreen;
64 
65 struct _CinnamonControlCenterPrivate
66 {
67   GtkBuilder *builder;
68   GtkWidget  *notebook;
69   GtkWidget  *main_vbox;
70   GtkWidget  *scrolled_window;
71   GtkWidget  *search_scrolled;
72   GtkWidget  *current_panel_box;
73   GtkWidget  *current_panel;
74   char       *current_panel_id;
75   GtkWidget  *window;
76   GtkWidget  *search_entry;
77   GtkWidget  *lock_button;
78   GPtrArray  *custom_widgets;
79   GtkWidget  *nav_bar;
80 
81   GMenuTree  *menu_tree;
82   GtkListStore *store;
83   GHashTable *category_views;
84 
85   GtkTreeModel *search_filter;
86   GtkWidget *search_view;
87   gchar *filter_string;
88 
89   guint32 last_time;
90 
91   GIOExtensionPoint *extension_point;
92 
93   gchar *default_window_title;
94   gchar *default_window_icon;
95 
96   int monitor_num;
97   CcSmallScreen small_screen;
98 };
99 
100 /* Notebook helpers */
101 static GtkWidget *
notebook_get_selected_page(GtkWidget * notebook)102 notebook_get_selected_page (GtkWidget *notebook)
103 {
104   int curr;
105 
106   curr = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook));
107   if (curr == -1)
108     return NULL;
109   return gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), curr);
110 }
111 
112 static void
notebook_select_page(GtkWidget * notebook,GtkWidget * page)113 notebook_select_page (GtkWidget *notebook,
114 		      GtkWidget *page)
115 {
116   int i, num;
117 
118   num = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));
119   for (i = 0; i < num; i++)
120     {
121       if (gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), i) == page)
122         {
123           gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), i);
124           return;
125         }
126     }
127 
128   g_warning ("Couldn't select GtkNotebook page %p", page);
129 }
130 
131 static void
notebook_remove_page(GtkWidget * notebook,GtkWidget * page)132 notebook_remove_page (GtkWidget *notebook,
133 		      GtkWidget *page)
134 {
135   int i, num;
136 
137   num = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));
138   for (i = 0; i < num; i++)
139     {
140       if (gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), i) == page)
141         {
142           gtk_notebook_remove_page (GTK_NOTEBOOK (notebook), i);
143           return;
144         }
145     }
146 
147   g_warning ("Couldn't find GtkNotebook page to remove %p", page);
148 }
149 
150 static void
notebook_add_page(GtkWidget * notebook,GtkWidget * page)151 notebook_add_page (GtkWidget *notebook,
152 		   GtkWidget *page)
153 {
154   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, NULL);
155 }
156 
157 static const gchar *
get_icon_name_from_g_icon(GIcon * gicon)158 get_icon_name_from_g_icon (GIcon *gicon)
159 {
160   const gchar * const *names;
161   GtkIconTheme *icon_theme;
162   int i;
163 
164   if (!G_IS_THEMED_ICON (gicon))
165     return NULL;
166 
167   names = g_themed_icon_get_names (G_THEMED_ICON (gicon));
168   icon_theme = gtk_icon_theme_get_default ();
169 
170   for (i = 0; names[i] != NULL; i++)
171     {
172       if (gtk_icon_theme_has_icon (icon_theme, names[i]))
173         return names[i];
174     }
175 
176   return NULL;
177 }
178 
179 static gboolean
activate_panel(CinnamonControlCenter * shell,const gchar * id,GVariant * parameters,const gchar * desktop_file,const gchar * name,GIcon * gicon)180 activate_panel (CinnamonControlCenter *shell,
181                 const gchar        *id,
182                 GVariant           *parameters,
183                 const gchar        *desktop_file,
184                 const gchar        *name,
185                 GIcon              *gicon)
186 {
187   CinnamonControlCenterPrivate *priv = shell->priv;
188   GType panel_type = G_TYPE_INVALID;
189   GList *panels, *l;
190   GtkWidget *box;
191   const gchar *icon_name;
192 
193   /* check if there is an plugin that implements this panel */
194   panels = g_io_extension_point_get_extensions (priv->extension_point);
195 
196   if (!desktop_file)
197     return FALSE;
198   if (!id)
199     return FALSE;
200 
201   for (l = panels; l != NULL; l = l->next)
202     {
203       GIOExtension *extension;
204       const gchar *name;
205 
206       extension = l->data;
207 
208       name = g_io_extension_get_name (extension);
209 
210       if (!g_strcmp0 (name, id))
211         {
212           panel_type = g_io_extension_get_type (extension);
213           break;
214         }
215     }
216 
217   if (panel_type == G_TYPE_INVALID)
218     {
219 	  GKeyFile *key_file;
220 
221 	  /* It might be an external panel */
222 	  key_file = g_key_file_new ();
223 	  if (g_key_file_load_from_file (key_file, desktop_file, G_KEY_FILE_NONE, NULL))
224 	    {
225 	      gchar *command;
226 
227 	      command = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
228 	      if (command && command[0])
229 	        {
230 		  g_spawn_command_line_async (command, NULL);
231 		  g_free (command);
232 		}
233 	    }
234 
235 	  g_key_file_free (key_file);
236       return FALSE;
237     }
238 
239   /* create the panel plugin */
240   priv->current_panel = g_object_new (panel_type, "shell", shell, "parameters", parameters, NULL);
241   cc_shell_set_active_panel (CC_SHELL (shell), CC_PANEL (priv->current_panel));
242   gtk_widget_show (priv->current_panel);
243 
244   gtk_lock_button_set_permission (GTK_LOCK_BUTTON (priv->lock_button),
245                                   cc_panel_get_permission (CC_PANEL (priv->current_panel)));
246 
247   box = gtk_alignment_new (0, 0, 1, 1);
248   gtk_alignment_set_padding (GTK_ALIGNMENT (box), 6, 6, 6, 6);
249 
250   gtk_container_add (GTK_CONTAINER (box), priv->current_panel);
251 
252   gtk_widget_set_name (box, id);
253   notebook_add_page (priv->notebook, box);
254 
255   /* switch to the new panel */
256   gtk_widget_show (box);
257   notebook_select_page (priv->notebook, box);
258   cc_shell_nav_bar_show_detail_button (CC_SHELL_NAV_BAR(shell->priv->nav_bar), name);
259 
260   /* set the title of the window */
261   icon_name = get_icon_name_from_g_icon (gicon);
262   gtk_window_set_role (GTK_WINDOW (priv->window), id);
263   gtk_window_set_title (GTK_WINDOW (priv->window), name);
264   gtk_window_set_default_icon_name (icon_name);
265   gtk_window_set_icon_name (GTK_WINDOW (priv->window), icon_name);
266 
267   priv->current_panel_box = box;
268 
269   return TRUE;
270 }
271 
272 static void
_shell_remove_all_custom_widgets(CinnamonControlCenterPrivate * priv)273 _shell_remove_all_custom_widgets (CinnamonControlCenterPrivate *priv)
274 {
275   GtkBox *box;
276   GtkWidget *widget;
277   guint i;
278 
279   /* remove from the header */
280   box = GTK_BOX (W (priv->builder, "topright"));
281   for (i = 0; i < priv->custom_widgets->len; i++)
282     {
283         widget = g_ptr_array_index (priv->custom_widgets, i);
284         gtk_container_remove (GTK_CONTAINER (box), widget);
285     }
286   g_ptr_array_set_size (priv->custom_widgets, 0);
287 }
288 
289 static void
shell_show_overview_page(CinnamonControlCenter * center)290 shell_show_overview_page (CinnamonControlCenter *center)
291 {
292   CinnamonControlCenterPrivate *priv = center->priv;
293 
294   notebook_select_page (priv->notebook, priv->scrolled_window);
295 
296   if (priv->current_panel_box)
297     notebook_remove_page (priv->notebook, priv->current_panel_box);
298   priv->current_panel = NULL;
299   priv->current_panel_box = NULL;
300 
301   if (priv->current_panel_id) {
302     g_free (priv->current_panel_id);
303     priv->current_panel_id = NULL;
304   }
305 
306   /* clear the search text */
307   g_free (priv->filter_string);
308   priv->filter_string = g_strdup ("");
309 
310   gtk_lock_button_set_permission (GTK_LOCK_BUTTON (priv->lock_button), NULL);
311 
312   /* reset window title and icon */
313   gtk_window_set_role (GTK_WINDOW (priv->window), NULL);
314   gtk_window_set_title (GTK_WINDOW (priv->window), priv->default_window_title);
315   gtk_window_set_default_icon_name (priv->default_window_icon);
316   gtk_window_set_icon_name (GTK_WINDOW (priv->window),
317                             priv->default_window_icon);
318 
319   cc_shell_set_active_panel (CC_SHELL (center), NULL);
320 
321   /* clear any custom widgets */
322   _shell_remove_all_custom_widgets (priv);
323 
324   cc_shell_nav_bar_hide_detail_button (CC_SHELL_NAV_BAR (priv->nav_bar));
325 }
326 
327 void
cinnamon_control_center_set_overview_page(CinnamonControlCenter * center)328 cinnamon_control_center_set_overview_page (CinnamonControlCenter *center)
329 {
330   shell_show_overview_page (center);
331 }
332 
333 static void
item_activated_cb(CcShellCategoryView * view,gchar * name,gchar * id,gchar * desktop_file,CinnamonControlCenter * shell)334 item_activated_cb (CcShellCategoryView *view,
335                    gchar               *name,
336                    gchar               *id,
337                    gchar               *desktop_file,
338                    CinnamonControlCenter  *shell)
339 {
340   GError *err = NULL;
341 
342   if (!cc_shell_set_active_panel_from_id (CC_SHELL (shell), id, NULL, &err))
343     {
344       /* TODO: show message to user */
345       if (err)
346         {
347           g_warning ("Could not active panel \"%s\": %s", id, err->message);
348           g_error_free (err);
349         }
350     }
351 }
352 
353 static gboolean
category_focus_out(GtkWidget * view,GdkEventFocus * event,CinnamonControlCenter * shell)354 category_focus_out (GtkWidget          *view,
355                     GdkEventFocus      *event,
356                     CinnamonControlCenter *shell)
357 {
358   gtk_icon_view_unselect_all (GTK_ICON_VIEW (view));
359 
360   return FALSE;
361 }
362 
363 static gboolean
category_focus_in(GtkWidget * view,GdkEventFocus * event,CinnamonControlCenter * shell)364 category_focus_in (GtkWidget          *view,
365                    GdkEventFocus      *event,
366                    CinnamonControlCenter *shell)
367 {
368   GtkTreePath *path;
369 
370   if (!gtk_icon_view_get_cursor (GTK_ICON_VIEW (view), &path, NULL))
371     {
372       path = gtk_tree_path_new_from_indices (0, -1);
373       gtk_icon_view_set_cursor (GTK_ICON_VIEW (view), path, NULL, FALSE);
374     }
375 
376   gtk_icon_view_select_path (GTK_ICON_VIEW (view), path);
377   gtk_tree_path_free (path);
378 
379   return FALSE;
380 }
381 
382 static GList *
get_item_views(CinnamonControlCenter * shell)383 get_item_views (CinnamonControlCenter *shell)
384 {
385   GList *list, *l;
386   GList *res;
387 
388   list = gtk_container_get_children (GTK_CONTAINER (shell->priv->main_vbox));
389   res = NULL;
390   for (l = list; l; l = l->next)
391     {
392       if (!CC_IS_SHELL_CATEGORY_VIEW (l->data))
393         continue;
394       res = g_list_append (res, cc_shell_category_view_get_item_view (CC_SHELL_CATEGORY_VIEW (l->data)));
395     }
396 
397   g_list_free (list);
398 
399   return res;
400 }
401 
402 static gboolean
keynav_failed(GtkIconView * current_view,GtkDirectionType direction,CinnamonControlCenter * shell)403 keynav_failed (GtkIconView        *current_view,
404                GtkDirectionType    direction,
405                CinnamonControlCenter *shell)
406 {
407   GList *views, *v;
408   GtkIconView *new_view;
409   GtkTreePath *path;
410   GtkTreeModel *model;
411   GtkTreeIter iter;
412   gint col, c, dist, d;
413   GtkTreePath *sel;
414   gboolean res;
415 
416   res = FALSE;
417 
418   views = get_item_views (shell);
419 
420   for (v = views; v; v = v->next)
421     {
422       if (v->data == current_view)
423         break;
424     }
425 
426   if (direction == GTK_DIR_DOWN && v != NULL && v->next != NULL)
427     {
428       new_view = v->next->data;
429 
430       if (gtk_icon_view_get_cursor (current_view, &path, NULL))
431         {
432           col = gtk_icon_view_get_item_column (current_view, path);
433           gtk_tree_path_free (path);
434 
435           sel = NULL;
436           dist = 1000;
437           model = gtk_icon_view_get_model (new_view);
438           gtk_tree_model_get_iter_first (model, &iter);
439           do {
440             path = gtk_tree_model_get_path (model, &iter);
441             c = gtk_icon_view_get_item_column (new_view, path);
442             d = ABS (c - col);
443             if (d < dist)
444               {
445                 if (sel)
446                   gtk_tree_path_free (sel);
447                 sel = path;
448                 dist = d;
449               }
450             else
451               gtk_tree_path_free (path);
452           } while (gtk_tree_model_iter_next (model, &iter));
453 
454           gtk_icon_view_set_cursor (new_view, sel, NULL, FALSE);
455           gtk_tree_path_free (sel);
456         }
457 
458       gtk_widget_grab_focus (GTK_WIDGET (new_view));
459 
460       res = TRUE;
461     }
462 
463   if (direction == GTK_DIR_UP && v != NULL && v->prev != NULL)
464     {
465       new_view = v->prev->data;
466 
467       if (gtk_icon_view_get_cursor (current_view, &path, NULL))
468         {
469           col = gtk_icon_view_get_item_column (current_view, path);
470           gtk_tree_path_free (path);
471 
472           sel = NULL;
473           dist = 1000;
474           model = gtk_icon_view_get_model (new_view);
475           gtk_tree_model_get_iter_first (model, &iter);
476           do {
477             path = gtk_tree_model_get_path (model, &iter);
478             c = gtk_icon_view_get_item_column (new_view, path);
479             d = ABS (c - col);
480             if (d <= dist)
481               {
482                 if (sel)
483                   gtk_tree_path_free (sel);
484                 sel = path;
485                 dist = d;
486               }
487             else
488               gtk_tree_path_free (path);
489           } while (gtk_tree_model_iter_next (model, &iter));
490 
491           gtk_icon_view_set_cursor (new_view, sel, NULL, FALSE);
492           gtk_tree_path_free (sel);
493         }
494 
495       gtk_widget_grab_focus (GTK_WIDGET (new_view));
496 
497       res = TRUE;
498     }
499 
500   g_list_free (views);
501 
502   return res;
503 }
504 
505 static gboolean
model_filter_func(GtkTreeModel * model,GtkTreeIter * iter,CinnamonControlCenterPrivate * priv)506 model_filter_func (GtkTreeModel              *model,
507                    GtkTreeIter               *iter,
508                    CinnamonControlCenterPrivate *priv)
509 {
510   gchar *name, *description;
511   gchar *needle, *haystack;
512   gboolean result;
513   gchar **keywords;
514 
515   gtk_tree_model_get (model, iter,
516                       COL_NAME, &name,
517                       COL_DESCRIPTION, &description,
518                       COL_KEYWORDS, &keywords,
519                       -1);
520 
521   if (!priv->filter_string || !name)
522     {
523       g_free (name);
524       g_free (description);
525       g_strfreev (keywords);
526       return FALSE;
527     }
528 
529   needle = g_utf8_casefold (priv->filter_string, -1);
530   haystack = g_utf8_casefold (name, -1);
531 
532   result = (strstr (haystack, needle) != NULL);
533 
534   if (!result && description)
535     {
536       gchar *folded;
537 
538       folded = g_utf8_casefold (description, -1);
539       result = (strstr (folded, needle) != NULL);
540       g_free (folded);
541     }
542 
543   if (!result && keywords)
544     {
545       gint i;
546       gchar *keyword;
547 
548       for (i = 0; !result && keywords[i]; i++)
549         {
550           keyword = g_utf8_casefold (keywords[i], -1);
551           result = strstr (keyword, needle) == keyword;
552           g_free (keyword);
553         }
554     }
555 
556   g_free (name);
557   g_free (haystack);
558   g_free (needle);
559   g_strfreev (keywords);
560 
561   return result;
562 }
563 
564 static gboolean
category_filter_func(GtkTreeModel * model,GtkTreeIter * iter,gchar * filter)565 category_filter_func (GtkTreeModel *model,
566                       GtkTreeIter  *iter,
567                       gchar        *filter)
568 {
569   gchar *category;
570   gboolean result;
571 
572   gtk_tree_model_get (model, iter, COL_CATEGORY, &category, -1);
573 
574   result = (g_strcmp0 (category, filter) == 0);
575 
576   g_free (category);
577 
578   return result;
579 }
580 
581 static void
search_entry_changed_cb(GtkEntry * entry,CinnamonControlCenter * center)582 search_entry_changed_cb (GtkEntry           *entry,
583                          CinnamonControlCenter *center)
584 {
585   CinnamonControlCenterPrivate *priv = center->priv;
586   char *str;
587 
588   /* if the entry text was set manually (not by the user) */
589   if (!g_strcmp0 (priv->filter_string, gtk_entry_get_text (entry)))
590     return;
591 
592   /* Don't re-filter for added trailing or leading spaces */
593   str = g_strdup (gtk_entry_get_text (entry));
594   g_strstrip (str);
595   if (!g_strcmp0 (str, priv->filter_string))
596     {
597       g_free (str);
598       return;
599     }
600 
601   g_free (priv->filter_string);
602   priv->filter_string = str;
603 
604   if (!g_strcmp0 (priv->filter_string, ""))
605     {
606       shell_show_overview_page (center);
607     }
608   else
609     {
610       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->search_filter));
611       notebook_select_page (priv->notebook, priv->search_scrolled);
612     }
613 }
614 
615 static gboolean
search_entry_key_press_event_cb(GtkEntry * entry,GdkEventKey * event,CinnamonControlCenterPrivate * priv)616 search_entry_key_press_event_cb (GtkEntry    *entry,
617                                  GdkEventKey *event,
618                                  CinnamonControlCenterPrivate   *priv)
619 {
620   if (event->keyval == GDK_KEY_Return)
621     {
622       GtkTreePath *path;
623 
624       path = gtk_tree_path_new_first ();
625 
626       priv->last_time = event->time;
627 
628       gtk_icon_view_item_activated (GTK_ICON_VIEW (priv->search_view), path);
629 
630       gtk_tree_path_free (path);
631       return TRUE;
632     }
633 
634   if (event->keyval == GDK_KEY_Escape)
635     {
636       gtk_entry_set_text (entry, "");
637       return TRUE;
638     }
639 
640   return FALSE;
641 }
642 
643 static void
on_search_selection_changed(GtkTreeSelection * selection,CinnamonControlCenter * shell)644 on_search_selection_changed (GtkTreeSelection   *selection,
645                              CinnamonControlCenter *shell)
646 {
647   GtkTreeModel *model;
648   GtkTreeIter   iter;
649   char         *id = NULL;
650 
651   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
652     return;
653 
654   gtk_tree_model_get (model, &iter,
655                       COL_ID, &id,
656                       -1);
657 
658   if (id)
659     cc_shell_set_active_panel_from_id (CC_SHELL (shell), id, NULL, NULL);
660 
661   gtk_tree_selection_unselect_all (selection);
662 
663   g_free (id);
664 }
665 
666 static void
setup_search(CinnamonControlCenter * shell)667 setup_search (CinnamonControlCenter *shell)
668 {
669   GtkWidget *search_view, *widget;
670   GtkCellRenderer *renderer;
671   GtkTreeViewColumn *column;
672   CinnamonControlCenterPrivate *priv = shell->priv;
673 
674   g_return_if_fail (priv->store != NULL);
675 
676   /* create the search filter */
677   priv->search_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (priv->store),
678                                                    NULL);
679 
680   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->search_filter),
681                                           (GtkTreeModelFilterVisibleFunc)
682                                           model_filter_func,
683                                           priv, NULL);
684 
685   /* set up the search view */
686   priv->search_view = search_view = gtk_tree_view_new ();
687   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (search_view), FALSE);
688   gtk_tree_view_set_model (GTK_TREE_VIEW (search_view),
689                            GTK_TREE_MODEL (priv->search_filter));
690 
691   renderer = gtk_cell_renderer_pixbuf_new ();
692   g_object_set (renderer,
693                 "follow-state", TRUE,
694                 "xpad", 15,
695                 "ypad", 10,
696                 "stock-size", GTK_ICON_SIZE_DIALOG,
697                 NULL);
698   column = gtk_tree_view_column_new_with_attributes ("Icon", renderer,
699                                                      "gicon", COL_GICON,
700                                                      NULL);
701   gtk_tree_view_column_set_expand (column, FALSE);
702   gtk_tree_view_append_column (GTK_TREE_VIEW (priv->search_view), column);
703 
704   renderer = gtk_cell_renderer_text_new ();
705   g_object_set (renderer,
706                 "xpad", 0,
707                 NULL);
708   column = gtk_tree_view_column_new_with_attributes ("Name", renderer,
709                                                      "text", COL_NAME,
710                                                      NULL);
711   gtk_tree_view_column_set_expand (column, FALSE);
712   gtk_tree_view_append_column (GTK_TREE_VIEW (priv->search_view), column);
713 
714   renderer = gtk_cell_renderer_text_new ();
715   g_object_set (renderer,
716                 "xpad", 15,
717                 NULL);
718   column = gtk_tree_view_column_new_with_attributes ("Description", renderer,
719                                                      "text", COL_DESCRIPTION,
720                                                      NULL);
721   gtk_tree_view_column_set_expand (column, TRUE);
722   gtk_tree_view_append_column (GTK_TREE_VIEW (priv->search_view), column);
723 
724   priv->search_scrolled = W (priv->builder, "search-scrolled-window");
725   gtk_container_add (GTK_CONTAINER (priv->search_scrolled), search_view);
726 
727   g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->search_view)),
728                     "changed",
729                     G_CALLBACK (on_search_selection_changed),
730                     shell);
731 
732 }
733 
734 static void
setup_lock(CinnamonControlCenter * shell)735 setup_lock (CinnamonControlCenter *shell)
736 {
737   CinnamonControlCenterPrivate *priv = shell->priv;
738 
739   priv->lock_button = W (priv->builder, "lock-button");
740 }
741 
742 static void
maybe_add_category_view(CinnamonControlCenter * shell,const char * name)743 maybe_add_category_view (CinnamonControlCenter *shell,
744                          const char         *name)
745 {
746   GtkTreeModel *filter;
747   GtkWidget *categoryview;
748 
749   if (g_hash_table_lookup (shell->priv->category_views, name) != NULL)
750     return;
751 
752   if (g_hash_table_size (shell->priv->category_views) > 0)
753     {
754       GtkWidget *separator;
755       separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
756       gtk_widget_set_margin_top (separator, 11);
757       gtk_widget_set_margin_bottom (separator, 10);
758       gtk_box_pack_start (GTK_BOX (shell->priv->main_vbox), separator, FALSE, FALSE, 0);
759       gtk_widget_show (separator);
760     }
761 
762   /* create new category view for this category */
763   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (shell->priv->store),
764                                       NULL);
765   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
766                                           (GtkTreeModelFilterVisibleFunc) category_filter_func,
767                                           g_strdup (name), g_free);
768 
769   categoryview = cc_shell_category_view_new (name, filter);
770   gtk_box_pack_start (GTK_BOX (shell->priv->main_vbox), categoryview, FALSE, TRUE, 0);
771 
772   g_signal_connect (cc_shell_category_view_get_item_view (CC_SHELL_CATEGORY_VIEW (categoryview)),
773                     "desktop-item-activated",
774                     G_CALLBACK (item_activated_cb), shell);
775 
776   gtk_widget_show (categoryview);
777 
778   g_signal_connect (cc_shell_category_view_get_item_view (CC_SHELL_CATEGORY_VIEW (categoryview)),
779                     "focus-in-event",
780                     G_CALLBACK (category_focus_in), shell);
781   g_signal_connect (cc_shell_category_view_get_item_view (CC_SHELL_CATEGORY_VIEW (categoryview)),
782                     "focus-out-event",
783                     G_CALLBACK (category_focus_out), shell);
784   g_signal_connect (cc_shell_category_view_get_item_view (CC_SHELL_CATEGORY_VIEW (categoryview)),
785                     "keynav-failed",
786                     G_CALLBACK (keynav_failed), shell);
787 
788   g_hash_table_insert (shell->priv->category_views, g_strdup (name), categoryview);
789 }
790 
791 static void
reload_menu(CinnamonControlCenter * shell)792 reload_menu (CinnamonControlCenter *shell)
793 {
794   GError *error;
795   GMenuTreeDirectory *d;
796   GMenuTreeIter *iter;
797   GMenuTreeItemType next_type;
798 
799   error = NULL;
800   if (!gmenu_tree_load_sync (shell->priv->menu_tree, &error))
801     {
802       g_warning ("Could not load control center menu: %s", error->message);
803       g_clear_error (&error);
804       return;
805     }
806 
807 
808   d = gmenu_tree_get_root_directory (shell->priv->menu_tree);
809   iter = gmenu_tree_directory_iter (d);
810 
811   while ((next_type = gmenu_tree_iter_next (iter)) != GMENU_TREE_ITEM_INVALID)
812     {
813       if (next_type == GMENU_TREE_ITEM_DIRECTORY)
814         {
815           GMenuTreeDirectory *subdir;
816           const gchar *dir_name;
817           GMenuTreeIter *sub_iter;
818           GMenuTreeItemType sub_next_type;
819 
820           subdir = gmenu_tree_iter_get_directory (iter);
821           dir_name = gmenu_tree_directory_get_name (subdir);
822 
823           maybe_add_category_view (shell, dir_name);
824 
825           /* add the items from this category to the model */
826           sub_iter = gmenu_tree_directory_iter (subdir);
827           while ((sub_next_type = gmenu_tree_iter_next (sub_iter)) != GMENU_TREE_ITEM_INVALID)
828             {
829               if (sub_next_type == GMENU_TREE_ITEM_ENTRY)
830                 {
831                   GMenuTreeEntry *item = gmenu_tree_iter_get_entry (sub_iter);
832                   cc_shell_model_add_item (CC_SHELL_MODEL (shell->priv->store),
833                                            dir_name,
834                                            item);
835                   gmenu_tree_item_unref (item);
836                 }
837             }
838 
839           gmenu_tree_iter_unref (sub_iter);
840           gmenu_tree_item_unref (subdir);
841         }
842     }
843 
844   gmenu_tree_iter_unref (iter);
845 }
846 
847 static void
on_menu_changed(GMenuTree * monitor,CinnamonControlCenter * shell)848 on_menu_changed (GMenuTree          *monitor,
849                  CinnamonControlCenter *shell)
850 {
851   gtk_list_store_clear (shell->priv->store);
852   reload_menu (shell);
853 }
854 
855 static void
setup_model(CinnamonControlCenter * shell)856 setup_model (CinnamonControlCenter *shell)
857 {
858   CinnamonControlCenterPrivate *priv = shell->priv;
859 
860   gtk_widget_set_margin_top (shell->priv->main_vbox, 8);
861   gtk_widget_set_margin_bottom (shell->priv->main_vbox, 8);
862   gtk_widget_set_margin_left (shell->priv->main_vbox, 12);
863   gtk_widget_set_margin_right (shell->priv->main_vbox, 12);
864   gtk_container_set_focus_vadjustment (GTK_CONTAINER (shell->priv->main_vbox),
865                                        gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (shell->priv->scrolled_window)));
866 
867   priv->store = (GtkListStore *) cc_shell_model_new ();
868   priv->category_views = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
869   priv->menu_tree = gmenu_tree_new_for_path (MENUDIR "/cinnamoncc.menu", 0);
870 
871   reload_menu (shell);
872 
873   g_signal_connect (priv->menu_tree, "changed", G_CALLBACK (on_menu_changed), shell);
874 }
875 
876 static void
load_panel_plugins(CinnamonControlCenter * shell)877 load_panel_plugins (CinnamonControlCenter *shell)
878 {
879   GList *modules;
880 
881   /* only allow this function to be run once to prevent modules being loaded
882    * twice
883    */
884   if (shell->priv->extension_point)
885     return;
886 
887   /* make sure the base type is registered */
888   g_type_from_name ("CcPanel");
889 
890   shell->priv->extension_point
891     = g_io_extension_point_register (CC_SHELL_PANEL_EXTENSION_POINT);
892 
893   /* load all the plugins in the panels directory */
894   modules = g_io_modules_load_all_in_directory (PANELS_DIR);
895   g_list_free (modules);
896 
897 }
898 
899 
900 static void
home_button_clicked_cb(GtkButton * button,CinnamonControlCenter * shell)901 home_button_clicked_cb (GtkButton *button,
902                         CinnamonControlCenter *shell)
903 {
904   shell_show_overview_page (shell);
905 }
906 
907 static void
notebook_page_notify_cb(GtkNotebook * notebook,GParamSpec * spec,CinnamonControlCenterPrivate * priv)908 notebook_page_notify_cb (GtkNotebook              *notebook,
909 			 GParamSpec               *spec,
910                          CinnamonControlCenterPrivate *priv)
911 {
912   int nat_height;
913   GtkWidget *child;
914 
915   child = notebook_get_selected_page (GTK_WIDGET (notebook));
916 
917   if (child == priv->scrolled_window || child == priv->search_scrolled)
918     {
919       gtk_widget_hide (W (priv->builder, "lock-button"));
920 
921       if (!g_strcmp0(g_getenv("XDG_CURRENT_DESKTOP"), "Unity"))
922         gtk_widget_get_preferred_height_for_width (GTK_WIDGET (priv->main_vbox),
923                                                    UNITY_FIXED_WIDTH, NULL, &nat_height);
924       else
925         gtk_widget_get_preferred_height_for_width (GTK_WIDGET (priv->main_vbox),
926                                                    FIXED_WIDTH, NULL, &nat_height);
927       gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (priv->scrolled_window),
928                                                   priv->small_screen == SMALL_SCREEN_TRUE ? SMALL_SCREEN_FIXED_HEIGHT : nat_height);
929     }
930   else
931     {
932       /* set the scrolled window small so that it doesn't force
933          the window to be larger than this panel */
934       if (!g_strcmp0(g_getenv("XDG_CURRENT_DESKTOP"), "Unity")) {
935         gtk_widget_get_preferred_height_for_width (GTK_WIDGET (priv->window),
936                                                    UNITY_FIXED_WIDTH, NULL, &nat_height);
937         gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (priv->scrolled_window), MIN_ICON_VIEW_HEIGHT);
938         gtk_window_resize (GTK_WINDOW (priv->window),
939                            UNITY_FIXED_WIDTH,
940                            nat_height);
941       }
942       else {
943         gtk_widget_get_preferred_height_for_width (GTK_WIDGET (priv->window),
944                                                    FIXED_WIDTH, NULL, &nat_height);
945         gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (priv->scrolled_window), MIN_ICON_VIEW_HEIGHT);
946         gtk_window_resize (GTK_WINDOW (priv->window),
947                            FIXED_WIDTH,
948                            nat_height);
949       }
950     }
951 }
952 
953 /* CcShell implementation */
954 static void
_shell_embed_widget_in_header(CcShell * shell,GtkWidget * widget)955 _shell_embed_widget_in_header (CcShell      *shell,
956                                GtkWidget    *widget)
957 {
958   CinnamonControlCenterPrivate *priv = CINNAMON_CONTROL_CENTER (shell)->priv;
959   GtkBox *box;
960 
961   /* add to header */
962   box = GTK_BOX (W (priv->builder, "topright"));
963   gtk_box_pack_end (box, widget, FALSE, FALSE, 0);
964   g_ptr_array_add (priv->custom_widgets, g_object_ref (widget));
965 }
966 
967 /* CcShell implementation */
968 static gboolean
_shell_set_active_panel_from_id(CcShell * shell,const gchar * start_id,GVariant * parameters,GError ** err)969 _shell_set_active_panel_from_id (CcShell      *shell,
970                                  const gchar  *start_id,
971                                  GVariant     *parameters,
972                                  GError      **err)
973 {
974   GtkTreeIter iter;
975   gboolean iter_valid;
976   gchar *name = NULL;
977   gchar *desktop = NULL;
978   GIcon *gicon = NULL;
979   CinnamonControlCenterPrivate *priv = CINNAMON_CONTROL_CENTER (shell)->priv;
980   GtkWidget *old_panel;
981 
982   /* When loading the same panel again, just set its parameters */
983   if (g_strcmp0 (priv->current_panel_id, start_id) == 0)
984     {
985       g_object_set (G_OBJECT (priv->current_panel), "parameters", parameters, NULL);
986       return TRUE;
987     }
988 
989   if (priv->current_panel_id) {
990     g_free (priv->current_panel_id);
991     priv->current_panel_id = NULL;
992   }
993 
994   /* clear any custom widgets */
995   _shell_remove_all_custom_widgets (priv);
996 
997   iter_valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->store),
998                                               &iter);
999 
1000   /* find the details for this item */
1001   while (iter_valid)
1002     {
1003       gchar *id;
1004 
1005       gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter,
1006                           COL_NAME, &name,
1007                           COL_DESKTOP_FILE, &desktop,
1008                           COL_GICON, &gicon,
1009                           COL_ID, &id,
1010                           -1);
1011 
1012       if (id && !strcmp (id, start_id))
1013         {
1014           g_free (id);
1015           break;
1016         }
1017       else
1018         {
1019           g_free (id);
1020           g_free (name);
1021           g_free (desktop);
1022 	  if (gicon)
1023 	    g_object_unref (gicon);
1024 
1025           name = NULL;
1026           id = NULL;
1027           desktop = NULL;
1028           gicon = NULL;
1029         }
1030 
1031       iter_valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->store),
1032                                              &iter);
1033     }
1034 
1035   if (!name)
1036     {
1037       g_warning ("Could not find settings panel \"%s\"", start_id);
1038     }
1039   else if (activate_panel (CINNAMON_CONTROL_CENTER (shell), start_id, parameters, desktop,
1040                            name, gicon) == FALSE)
1041     {
1042       /* Failed to activate the panel for some reason */
1043       old_panel = priv->current_panel_box;
1044       priv->current_panel_box = NULL;
1045       notebook_select_page (priv->notebook, priv->scrolled_window);
1046       if (old_panel)
1047         notebook_remove_page (priv->notebook, old_panel);
1048     }
1049   else
1050     {
1051       priv->current_panel_id = g_strdup (start_id);
1052     }
1053 
1054   g_free (name);
1055   g_free (desktop);
1056   if (gicon)
1057     g_object_unref (gicon);
1058 
1059   return TRUE;
1060 }
1061 
1062 static GtkWidget *
_shell_get_toplevel(CcShell * shell)1063 _shell_get_toplevel (CcShell *shell)
1064 {
1065   return CINNAMON_CONTROL_CENTER (shell)->priv->window;
1066 }
1067 
1068 /* GObject Implementation */
1069 static void
cinnamon_control_center_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)1070 cinnamon_control_center_get_property (GObject    *object,
1071                                    guint       property_id,
1072                                    GValue     *value,
1073                                    GParamSpec *pspec)
1074 {
1075   switch (property_id)
1076     {
1077     default:
1078       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1079     }
1080 }
1081 
1082 static void
cinnamon_control_center_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)1083 cinnamon_control_center_set_property (GObject      *object,
1084                                    guint         property_id,
1085                                    const GValue *value,
1086                                    GParamSpec   *pspec)
1087 {
1088   switch (property_id)
1089     {
1090     default:
1091       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1092     }
1093 }
1094 
1095 static void
cinnamon_control_center_dispose(GObject * object)1096 cinnamon_control_center_dispose (GObject *object)
1097 {
1098   CinnamonControlCenterPrivate *priv = CINNAMON_CONTROL_CENTER (object)->priv;
1099 
1100   g_free (priv->current_panel_id);
1101 
1102   if (priv->custom_widgets)
1103     {
1104       g_ptr_array_unref (priv->custom_widgets);
1105       priv->custom_widgets = NULL;
1106     }
1107   if (priv->window)
1108     {
1109       gtk_widget_destroy (priv->window);
1110       priv->window = NULL;
1111 
1112       /* destroying the window will destroy its children */
1113       priv->notebook = NULL;
1114       priv->search_entry = NULL;
1115       priv->search_view = NULL;
1116     }
1117 
1118   if (priv->builder)
1119     {
1120       g_object_unref (priv->builder);
1121       priv->builder = NULL;
1122     }
1123 
1124   if (priv->store)
1125     {
1126       g_object_unref (priv->store);
1127       priv->store = NULL;
1128     }
1129 
1130   if (priv->search_filter)
1131     {
1132       g_object_unref (priv->search_filter);
1133       priv->search_filter = NULL;
1134     }
1135 
1136 
1137   G_OBJECT_CLASS (cinnamon_control_center_parent_class)->dispose (object);
1138 }
1139 
1140 static void
cinnamon_control_center_finalize(GObject * object)1141 cinnamon_control_center_finalize (GObject *object)
1142 {
1143   CinnamonControlCenterPrivate *priv = CINNAMON_CONTROL_CENTER (object)->priv;
1144 
1145   if (priv->filter_string)
1146     {
1147       g_free (priv->filter_string);
1148       priv->filter_string = NULL;
1149     }
1150 
1151   if (priv->default_window_title)
1152     {
1153       g_free (priv->default_window_title);
1154       priv->default_window_title = NULL;
1155     }
1156 
1157   if (priv->default_window_icon)
1158     {
1159       g_free (priv->default_window_icon);
1160       priv->default_window_icon = NULL;
1161     }
1162 
1163   if (priv->menu_tree)
1164     {
1165       g_signal_handlers_disconnect_by_func (priv->menu_tree,
1166 					    G_CALLBACK (on_menu_changed), object);
1167       g_object_unref (priv->menu_tree);
1168     }
1169 
1170   if (priv->category_views)
1171     {
1172       g_hash_table_destroy (priv->category_views);
1173     }
1174 
1175   G_OBJECT_CLASS (cinnamon_control_center_parent_class)->finalize (object);
1176 }
1177 
1178 static void
cinnamon_control_center_class_init(CinnamonControlCenterClass * klass)1179 cinnamon_control_center_class_init (CinnamonControlCenterClass *klass)
1180 {
1181   GObjectClass *object_class = G_OBJECT_CLASS (klass);
1182   CcShellClass *shell_class = CC_SHELL_CLASS (klass);
1183 
1184   g_type_class_add_private (klass, sizeof (CinnamonControlCenterPrivate));
1185 
1186   object_class->get_property = cinnamon_control_center_get_property;
1187   object_class->set_property = cinnamon_control_center_set_property;
1188   object_class->dispose = cinnamon_control_center_dispose;
1189   object_class->finalize = cinnamon_control_center_finalize;
1190 
1191   shell_class->set_active_panel_from_id = _shell_set_active_panel_from_id;
1192   shell_class->embed_widget_in_header = _shell_embed_widget_in_header;
1193   shell_class->get_toplevel = _shell_get_toplevel;
1194 }
1195 
1196 static gboolean
window_key_press_event(GtkWidget * win,GdkEventKey * event,CinnamonControlCenter * self)1197 window_key_press_event (GtkWidget          *win,
1198 			GdkEventKey        *event,
1199 			CinnamonControlCenter *self)
1200 {
1201   GdkKeymap *keymap;
1202   gboolean retval;
1203   GdkModifierType state;
1204 
1205   if (event->state == 0)
1206     return FALSE;
1207 
1208   retval = FALSE;
1209   state = event->state;
1210   keymap = gdk_keymap_get_default ();
1211   gdk_keymap_add_virtual_modifiers (keymap, &state);
1212   state = state & gtk_accelerator_get_default_mod_mask ();
1213 
1214   if (state == GDK_CONTROL_MASK)
1215     {
1216       switch (event->keyval)
1217         {
1218           case GDK_KEY_s:
1219           case GDK_KEY_S:
1220           case GDK_KEY_f:
1221           case GDK_KEY_F:
1222             retval = TRUE;
1223             break;
1224           case GDK_KEY_Q:
1225           case GDK_KEY_q:
1226             g_object_unref (self);
1227             retval = TRUE;
1228             break;
1229           case GDK_KEY_W:
1230           case GDK_KEY_w:
1231             if (notebook_get_selected_page (self->priv->notebook) != self->priv->scrolled_window)
1232               shell_show_overview_page (self);
1233             retval = TRUE;
1234             break;
1235         }
1236     }
1237   return retval;
1238 }
1239 
1240 static gint
get_monitor_height(CinnamonControlCenter * self)1241 get_monitor_height (CinnamonControlCenter *self)
1242 {
1243   GdkScreen *screen;
1244   GdkRectangle rect;
1245 
1246   /* We cannot use workarea here, as this wouldn't
1247    * be updated when we read it after a monitors-changed signal */
1248   screen = gtk_widget_get_screen (self->priv->window);
1249   gdk_screen_get_monitor_geometry (screen, self->priv->monitor_num, &rect);
1250 
1251   return rect.height;
1252 }
1253 
1254 static gboolean
update_monitor_number(CinnamonControlCenter * self)1255 update_monitor_number (CinnamonControlCenter *self)
1256 {
1257   gboolean changed = FALSE;
1258   GtkWidget *widget;
1259   GdkScreen *screen;
1260   GdkWindow *window;
1261   int monitor;
1262 
1263   widget = self->priv->window;
1264 
1265   window = gtk_widget_get_window (widget);
1266   screen = gtk_widget_get_screen (widget);
1267   monitor = gdk_screen_get_monitor_at_window (screen, window);
1268   if (self->priv->monitor_num != monitor)
1269     {
1270       self->priv->monitor_num = monitor;
1271       changed = TRUE;
1272     }
1273 
1274   return changed;
1275 }
1276 
1277 static CcSmallScreen
is_small(CinnamonControlCenter * self)1278 is_small (CinnamonControlCenter *self)
1279 {
1280   if (get_monitor_height (self) <= FIXED_HEIGHT)
1281     return SMALL_SCREEN_TRUE;
1282   return SMALL_SCREEN_FALSE;
1283 }
1284 
1285 static void
update_small_screen_settings(CinnamonControlCenter * self)1286 update_small_screen_settings (CinnamonControlCenter *self)
1287 {
1288   CcSmallScreen small;
1289 
1290   update_monitor_number (self);
1291   small = is_small (self);
1292 
1293   if (small == SMALL_SCREEN_TRUE)
1294     {
1295       gtk_window_set_resizable (GTK_WINDOW (self->priv->window), TRUE);
1296 
1297       if (self->priv->small_screen != small)
1298         gtk_window_maximize (GTK_WINDOW (self->priv->window));
1299     }
1300   else
1301     {
1302       if (self->priv->small_screen != small)
1303         gtk_window_unmaximize (GTK_WINDOW (self->priv->window));
1304 
1305       gtk_window_set_resizable (GTK_WINDOW (self->priv->window), FALSE);
1306     }
1307 
1308   self->priv->small_screen = small;
1309 
1310   /* And update the minimum sizes */
1311   notebook_page_notify_cb (GTK_NOTEBOOK (self->priv->notebook), NULL, self->priv);
1312 }
1313 
1314 static gboolean
main_window_configure_cb(GtkWidget * widget,GdkEvent * event,CinnamonControlCenter * self)1315 main_window_configure_cb (GtkWidget *widget,
1316                           GdkEvent  *event,
1317                           CinnamonControlCenter *self)
1318 {
1319   update_small_screen_settings (self);
1320   return FALSE;
1321 }
1322 
1323 static void
application_set_cb(GObject * object,GParamSpec * pspec,CinnamonControlCenter * self)1324 application_set_cb (GObject    *object,
1325                     GParamSpec *pspec,
1326                     CinnamonControlCenter *self)
1327 {
1328   /* update small screen settings now - to avoid visible resizing, we want
1329    * to do it before showing the window, and GtkApplicationWindow cannot be
1330    * realized unless its application property has been set */
1331   if (gtk_window_get_application (GTK_WINDOW (self->priv->window)))
1332     {
1333       gtk_widget_realize (self->priv->window);
1334       update_small_screen_settings (self);
1335     }
1336 }
1337 
1338 static void
monitors_changed_cb(GdkScreen * screen,CinnamonControlCenter * self)1339 monitors_changed_cb (GdkScreen *screen,
1340                      CinnamonControlCenter *self)
1341 {
1342   /* We reset small_screen_set to make sure that the
1343    * window gets maximised if need be, in update_small_screen_settings() */
1344   self->priv->small_screen = SMALL_SCREEN_UNSET;
1345   update_small_screen_settings (self);
1346 }
1347 
1348 static void
cinnamon_control_center_init(CinnamonControlCenter * self)1349 cinnamon_control_center_init (CinnamonControlCenter *self)
1350 {
1351   GError *err = NULL;
1352   CinnamonControlCenterPrivate *priv;
1353   GdkScreen *screen;
1354   GtkWidget *widget;
1355 
1356   priv = self->priv = CONTROL_CENTER_PRIVATE (self);
1357 
1358   priv->monitor_num = -1;
1359   self->priv->small_screen = SMALL_SCREEN_UNSET;
1360 
1361   /* load the user interface */
1362   priv->builder = gtk_builder_new ();
1363 
1364   if (!gtk_builder_add_from_file (priv->builder, UIDIR "/shell.ui", &err))
1365     {
1366       g_critical ("Could not build interface: %s", err->message);
1367       g_error_free (err);
1368 
1369       return;
1370     }
1371 
1372   /* connect various signals */
1373   priv->window = W (priv->builder, "main-window");
1374   gtk_window_set_hide_titlebar_when_maximized (GTK_WINDOW (priv->window), TRUE);
1375   screen = gtk_widget_get_screen (priv->window);
1376   g_signal_connect (screen, "monitors-changed", G_CALLBACK (monitors_changed_cb), self);
1377   g_signal_connect (priv->window, "configure-event", G_CALLBACK (main_window_configure_cb), self);
1378   g_signal_connect (priv->window, "notify::application", G_CALLBACK (application_set_cb), self);
1379   g_signal_connect_swapped (priv->window, "delete-event", G_CALLBACK (g_object_unref), self);
1380   g_signal_connect_after (priv->window, "key_press_event",
1381                           G_CALLBACK (window_key_press_event), self);
1382 
1383   priv->notebook = W (priv->builder, "notebook");
1384 
1385   /* Main scrolled window */
1386   priv->scrolled_window = W (priv->builder, "scrolledwindow1");
1387 
1388   if (!g_strcmp0(g_getenv("XDG_CURRENT_DESKTOP"), "Unity"))
1389     gtk_widget_set_size_request (priv->scrolled_window, UNITY_FIXED_WIDTH, -1);
1390   else
1391     gtk_widget_set_size_request (priv->scrolled_window, FIXED_WIDTH, -1);
1392   priv->main_vbox = W (priv->builder, "main-vbox");
1393   g_signal_connect (priv->notebook, "notify::page",
1394                     G_CALLBACK (notebook_page_notify_cb), priv);
1395 
1396   priv->nav_bar = cc_shell_nav_bar_new ();
1397   widget = W (priv->builder, "hbox1");
1398   gtk_box_pack_start (GTK_BOX (widget), priv->nav_bar, FALSE, FALSE, 0);
1399   gtk_box_reorder_child (GTK_BOX (widget), priv->nav_bar, 0);
1400   gtk_widget_show (priv->nav_bar);
1401 
1402   g_signal_connect (priv->nav_bar,
1403                     "home-clicked", G_CALLBACK (home_button_clicked_cb), self);
1404 
1405   /* keep a list of custom widgets to unload on panel change */
1406   priv->custom_widgets = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1407 
1408   /* load the available settings panels */
1409   setup_model (self);
1410 
1411   /* load the panels that are implemented as plugins */
1412   load_panel_plugins (self);
1413 
1414   /* setup search functionality */
1415   setup_search (self);
1416 
1417   setup_lock (self);
1418 
1419   /* store default window title and name */
1420   priv->default_window_title = g_strdup (gtk_window_get_title (GTK_WINDOW (priv->window)));
1421   priv->default_window_icon = g_strdup (gtk_window_get_icon_name (GTK_WINDOW (priv->window)));
1422 
1423   notebook_page_notify_cb (GTK_NOTEBOOK (priv->notebook), NULL, priv);
1424 }
1425 
1426 CinnamonControlCenter *
cinnamon_control_center_new(void)1427 cinnamon_control_center_new (void)
1428 {
1429   return g_object_new (CINNAMON_TYPE_CONTROL_CENTER, NULL);
1430 }
1431 
1432 void
cinnamon_control_center_present(CinnamonControlCenter * center)1433 cinnamon_control_center_present (CinnamonControlCenter *center)
1434 {
1435   gtk_window_present (GTK_WINDOW (center->priv->window));
1436 }
1437 
1438 void
cinnamon_control_center_show(CinnamonControlCenter * center,GtkApplication * app)1439 cinnamon_control_center_show (CinnamonControlCenter *center,
1440 			   GtkApplication     *app)
1441 {
1442   gtk_window_set_application (GTK_WINDOW (center->priv->window), app);
1443   gtk_widget_show (gtk_bin_get_child (GTK_BIN (center->priv->window)));
1444 }
1445