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