Branch data Line data Source code
1 : : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 : : *
3 : : * Copyright © 2020 Endless Mobile, Inc.
4 : : *
5 : : * SPDX-License-Identifier: GPL-2.0-or-later
6 : : *
7 : : * This program is free software; you can redistribute it and/or modify
8 : : * it under the terms of the GNU General Public License as published by
9 : : * the Free Software Foundation; either version 2 of the License, or
10 : : * (at your option) any later version.
11 : : *
12 : : * This program is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : : * GNU General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU General Public License
18 : : * along with this program; if not, see <http://www.gnu.org/licenses/>.
19 : : *
20 : : * Authors:
21 : : * - Philip Withnall <withnall@endlessm.com>
22 : : */
23 : :
24 : : #include "config.h"
25 : :
26 : : #include <gio/gio.h>
27 : : #include <glib.h>
28 : : #include <glib-object.h>
29 : : #include <glib/gi18n-lib.h>
30 : : #include <gtk/gtk.h>
31 : : #include <adwaita.h>
32 : :
33 : : #include "restrict-applications-dialog.h"
34 : : #include "restrict-applications-selector.h"
35 : :
36 : :
37 : : static void update_description (MctRestrictApplicationsDialog *self);
38 : :
39 : : /**
40 : : * MctRestrictApplicationsDialog:
41 : : *
42 : : * The ‘Restrict Applications’ dialog is a dialog which shows the available
43 : : * applications on the system alongside a column of toggle switches, which
44 : : * allows the given user to be prevented from running each application.
45 : : *
46 : : * The dialog contains a single #MctRestrictApplicationsSelector. It takes a
47 : : * #MctRestrictApplicationsDialog:user and
48 : : * #MctRestrictApplicationsDialog:app-filter as input to set up the UI, and
49 : : * returns its output as set of modifications to a given #MctAppFilterBuilder
50 : : * using mct_restrict_applications_dialog_build_app_filter().
51 : : *
52 : : * Since: 0.5.0
53 : : */
54 : : struct _MctRestrictApplicationsDialog
55 : : {
56 : : AdwPreferencesDialog parent_instance;
57 : :
58 : : MctRestrictApplicationsSelector *selector;
59 : : AdwPreferencesGroup *group;
60 : : GtkSearchEntry *search_entry;
61 : :
62 : : MctAppFilter *app_filter; /* (owned) (not nullable) */
63 : : gchar *user_display_name; /* (owned) (nullable) */
64 : : };
65 : :
66 : : static void search_entry_stop_search_cb (GtkSearchEntry *search_entry,
67 : : gpointer user_data);
68 : : static gboolean focus_search_cb (GtkWidget *widget,
69 : : GVariant *arguments,
70 : : gpointer user_data);
71 : :
72 [ + + + - : 3 : G_DEFINE_TYPE (MctRestrictApplicationsDialog, mct_restrict_applications_dialog, ADW_TYPE_PREFERENCES_DIALOG)
+ - ]
73 : :
74 : : typedef enum
75 : : {
76 : : PROP_APP_FILTER = 1,
77 : : PROP_USER_DISPLAY_NAME,
78 : : } MctRestrictApplicationsDialogProperty;
79 : :
80 : : static GParamSpec *properties[PROP_USER_DISPLAY_NAME + 1];
81 : :
82 : : static void
83 : 0 : mct_restrict_applications_dialog_constructed (GObject *obj)
84 : : {
85 : 0 : MctRestrictApplicationsDialog *self = MCT_RESTRICT_APPLICATIONS_DIALOG (obj);
86 : :
87 : 0 : g_assert (self->app_filter != NULL);
88 : 0 : g_assert (self->user_display_name == NULL ||
89 : : (*self->user_display_name != '\0' &&
90 : : g_utf8_validate (self->user_display_name, -1, NULL)));
91 : :
92 : 0 : G_OBJECT_CLASS (mct_restrict_applications_dialog_parent_class)->constructed (obj);
93 : 0 : }
94 : :
95 : : static void
96 : 0 : mct_restrict_applications_dialog_get_property (GObject *object,
97 : : guint prop_id,
98 : : GValue *value,
99 : : GParamSpec *pspec)
100 : : {
101 : 0 : MctRestrictApplicationsDialog *self = MCT_RESTRICT_APPLICATIONS_DIALOG (object);
102 : :
103 [ # # # ]: 0 : switch ((MctRestrictApplicationsDialogProperty) prop_id)
104 : : {
105 : 0 : case PROP_APP_FILTER:
106 : 0 : g_value_set_boxed (value, self->app_filter);
107 : 0 : break;
108 : :
109 : 0 : case PROP_USER_DISPLAY_NAME:
110 : 0 : g_value_set_string (value, self->user_display_name);
111 : 0 : break;
112 : :
113 : 0 : default:
114 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
115 : : }
116 : 0 : }
117 : :
118 : : static void
119 : 0 : mct_restrict_applications_dialog_set_property (GObject *object,
120 : : guint prop_id,
121 : : const GValue *value,
122 : : GParamSpec *pspec)
123 : : {
124 : 0 : MctRestrictApplicationsDialog *self = MCT_RESTRICT_APPLICATIONS_DIALOG (object);
125 : :
126 [ # # # ]: 0 : switch ((MctRestrictApplicationsDialogProperty) prop_id)
127 : : {
128 : 0 : case PROP_APP_FILTER:
129 : 0 : mct_restrict_applications_dialog_set_app_filter (self, g_value_get_boxed (value));
130 : 0 : break;
131 : :
132 : 0 : case PROP_USER_DISPLAY_NAME:
133 : 0 : mct_restrict_applications_dialog_set_user_display_name (self, g_value_get_string (value));
134 : 0 : break;
135 : :
136 : 0 : default:
137 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
138 : : }
139 : 0 : }
140 : :
141 : : static void
142 : 0 : mct_restrict_applications_dialog_dispose (GObject *object)
143 : : {
144 : 0 : MctRestrictApplicationsDialog *self = (MctRestrictApplicationsDialog *)object;
145 : :
146 [ # # ]: 0 : g_clear_pointer (&self->app_filter, mct_app_filter_unref);
147 [ # # ]: 0 : g_clear_pointer (&self->user_display_name, g_free);
148 : :
149 : 0 : G_OBJECT_CLASS (mct_restrict_applications_dialog_parent_class)->dispose (object);
150 : 0 : }
151 : :
152 : : static void
153 : 0 : mct_restrict_applications_dialog_map (GtkWidget *widget)
154 : : {
155 : 0 : MctRestrictApplicationsDialog *self = (MctRestrictApplicationsDialog *)widget;
156 : :
157 : 0 : GTK_WIDGET_CLASS (mct_restrict_applications_dialog_parent_class)->map (widget);
158 : :
159 : : /* Clear and focus the search entry, in case the dialogue is being shown for
160 : : * a second time. */
161 : 0 : gtk_editable_set_text (GTK_EDITABLE (self->search_entry), "");
162 : 0 : gtk_widget_grab_focus (GTK_WIDGET (self->search_entry));
163 : 0 : }
164 : :
165 : : static void
166 : 1 : mct_restrict_applications_dialog_class_init (MctRestrictApplicationsDialogClass *klass)
167 : : {
168 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
169 : 1 : GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
170 : :
171 : 1 : object_class->constructed = mct_restrict_applications_dialog_constructed;
172 : 1 : object_class->get_property = mct_restrict_applications_dialog_get_property;
173 : 1 : object_class->set_property = mct_restrict_applications_dialog_set_property;
174 : 1 : object_class->dispose = mct_restrict_applications_dialog_dispose;
175 : :
176 : 1 : widget_class->map = mct_restrict_applications_dialog_map;
177 : :
178 : : /**
179 : : * MctRestrictApplicationsDialog:app-filter: (not nullable)
180 : : *
181 : : * The user’s current app filter, used to set up the dialog. As app filters
182 : : * are immutable, it is not updated as the dialog is changed. Use
183 : : * mct_restrict_applications_dialog_build_app_filter() to build the new app
184 : : * filter.
185 : : *
186 : : * Since: 0.5.0
187 : : */
188 : 1 : properties[PROP_APP_FILTER] =
189 : 1 : g_param_spec_boxed ("app-filter",
190 : : "App Filter",
191 : : "The user’s current app filter, used to set up the dialog.",
192 : : MCT_TYPE_APP_FILTER,
193 : : G_PARAM_READWRITE |
194 : : G_PARAM_CONSTRUCT_ONLY |
195 : : G_PARAM_STATIC_STRINGS |
196 : : G_PARAM_EXPLICIT_NOTIFY);
197 : :
198 : : /**
199 : : * MctRestrictApplicationsDialog:user-display-name: (nullable)
200 : : *
201 : : * The display name for the currently selected user account, or %NULL if no
202 : : * user is selected. This will typically be the user’s full name (if known)
203 : : * or their username.
204 : : *
205 : : * If set, it must be valid UTF-8 and non-empty.
206 : : *
207 : : * Since: 0.5.0
208 : : */
209 : 1 : properties[PROP_USER_DISPLAY_NAME] =
210 : 1 : g_param_spec_string ("user-display-name",
211 : : "User Display Name",
212 : : "The display name for the currently selected user account, or %NULL if no user is selected.",
213 : : NULL,
214 : : G_PARAM_READWRITE |
215 : : G_PARAM_STATIC_STRINGS |
216 : : G_PARAM_EXPLICIT_NOTIFY);
217 : :
218 : 1 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
219 : :
220 : 1 : gtk_widget_class_set_template_from_resource (widget_class, "/org/freedesktop/MalcontentUi/ui/restrict-applications-dialog.ui");
221 : :
222 : 1 : gtk_widget_class_bind_template_child (widget_class, MctRestrictApplicationsDialog, selector);
223 : 1 : gtk_widget_class_bind_template_child (widget_class, MctRestrictApplicationsDialog, group);
224 : 1 : gtk_widget_class_bind_template_child (widget_class, MctRestrictApplicationsDialog, search_entry);
225 : :
226 : 1 : gtk_widget_class_bind_template_callback (widget_class, search_entry_stop_search_cb);
227 : :
228 : 1 : gtk_widget_class_add_binding (widget_class,
229 : : GDK_KEY_f, GDK_CONTROL_MASK,
230 : : focus_search_cb,
231 : : NULL);
232 : :
233 : 1 : }
234 : :
235 : : static void
236 : 0 : mct_restrict_applications_dialog_init (MctRestrictApplicationsDialog *self)
237 : : {
238 : : /* Ensure the types used in the UI are registered. */
239 : 0 : g_type_ensure (MCT_TYPE_RESTRICT_APPLICATIONS_SELECTOR);
240 : :
241 : 0 : gtk_widget_init_template (GTK_WIDGET (self));
242 : :
243 : 0 : gtk_search_entry_set_key_capture_widget (self->search_entry, GTK_WIDGET (self));
244 : 0 : }
245 : :
246 : : static void
247 : 0 : update_description (MctRestrictApplicationsDialog *self)
248 : : {
249 [ # # ]: 0 : g_autofree gchar *description = NULL;
250 : :
251 [ # # ]: 0 : if (self->user_display_name == NULL)
252 : : {
253 : 0 : adw_preferences_group_set_description (self->group, NULL);
254 : 0 : return;
255 : : }
256 : :
257 : : /* Translators: the placeholder is a user’s full name */
258 : 0 : description = g_strdup_printf (_("Restrict %s from using the following installed applications."),
259 : : self->user_display_name);
260 : 0 : adw_preferences_group_set_description (self->group, description);
261 : : }
262 : :
263 : : static void
264 : 0 : search_entry_stop_search_cb (GtkSearchEntry *search_entry,
265 : : gpointer user_data)
266 : : {
267 : : /* Clear the search text as the search filtering is bound to that. */
268 : 0 : gtk_editable_set_text (GTK_EDITABLE (search_entry), "");
269 : 0 : }
270 : :
271 : : static gboolean
272 : 0 : focus_search_cb (GtkWidget *widget,
273 : : GVariant *arguments,
274 : : gpointer user_data)
275 : : {
276 : 0 : MctRestrictApplicationsDialog *self = MCT_RESTRICT_APPLICATIONS_DIALOG (widget);
277 : :
278 : 0 : gtk_widget_grab_focus (GTK_WIDGET (self->search_entry));
279 : 0 : return TRUE;
280 : : }
281 : :
282 : : /**
283 : : * mct_restrict_applications_dialog_new:
284 : : * @app_filter: (transfer none): the initial app filter configuration to show
285 : : * @user_display_name: (transfer none) (nullable): the display name of the user
286 : : * to show the app filter for, or %NULL if no user is selected
287 : : *
288 : : * Create a new #MctRestrictApplicationsDialog widget.
289 : : *
290 : : * Returns: (transfer full): a new restricted applications editing dialog
291 : : * Since: 0.5.0
292 : : */
293 : : MctRestrictApplicationsDialog *
294 : 0 : mct_restrict_applications_dialog_new (MctAppFilter *app_filter,
295 : : const gchar *user_display_name)
296 : : {
297 : 0 : g_return_val_if_fail (app_filter != NULL, NULL);
298 : 0 : g_return_val_if_fail (user_display_name == NULL ||
299 : : (*user_display_name != '\0' &&
300 : : g_utf8_validate (user_display_name, -1, NULL)), NULL);
301 : :
302 : 0 : return g_object_new (MCT_TYPE_RESTRICT_APPLICATIONS_DIALOG,
303 : : "app-filter", app_filter,
304 : : "user-display-name", user_display_name,
305 : : NULL);
306 : : }
307 : :
308 : : /**
309 : : * mct_restrict_applications_dialog_get_app_filter:
310 : : * @self: an #MctRestrictApplicationsDialog
311 : : *
312 : : * Get the value of #MctRestrictApplicationsDialog:app-filter. If the property
313 : : * was originally set to %NULL, this will be the empty app filter.
314 : : *
315 : : * Returns: (transfer none) (not nullable): the initial app filter used to
316 : : * populate the dialog
317 : : * Since: 0.5.0
318 : : */
319 : : MctAppFilter *
320 : 0 : mct_restrict_applications_dialog_get_app_filter (MctRestrictApplicationsDialog *self)
321 : : {
322 : 0 : g_return_val_if_fail (MCT_IS_RESTRICT_APPLICATIONS_DIALOG (self), NULL);
323 : :
324 : 0 : return self->app_filter;
325 : : }
326 : :
327 : : /**
328 : : * mct_restrict_applications_dialog_set_app_filter:
329 : : * @self: an #MctRestrictApplicationsDialog
330 : : * @app_filter: (nullable) (transfer none): the app filter to configure the dialog
331 : : * from, or %NULL to use an empty app filter
332 : : *
333 : : * Set the value of #MctRestrictApplicationsDialog:app-filter.
334 : : *
335 : : * Since: 0.5.0
336 : : */
337 : : void
338 : 0 : mct_restrict_applications_dialog_set_app_filter (MctRestrictApplicationsDialog *self,
339 : : MctAppFilter *app_filter)
340 : : {
341 [ # # ]: 0 : g_autoptr(MctAppFilter) owned_app_filter = NULL;
342 : :
343 : 0 : g_return_if_fail (MCT_IS_RESTRICT_APPLICATIONS_DIALOG (self));
344 : :
345 : : /* Default app filter, typically for when we’re instantiated by #GtkBuilder. */
346 [ # # ]: 0 : if (app_filter == NULL)
347 : : {
348 : 0 : g_auto(MctAppFilterBuilder) builder = MCT_APP_FILTER_BUILDER_INIT ();
349 : 0 : owned_app_filter = mct_app_filter_builder_end (&builder);
350 : 0 : app_filter = owned_app_filter;
351 : : }
352 : :
353 [ # # ]: 0 : if (app_filter == self->app_filter)
354 : 0 : return;
355 : :
356 [ # # ]: 0 : g_clear_pointer (&self->app_filter, mct_app_filter_unref);
357 : 0 : self->app_filter = mct_app_filter_ref (app_filter);
358 : :
359 : 0 : mct_restrict_applications_selector_set_app_filter (self->selector, self->app_filter);
360 : :
361 : 0 : g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_APP_FILTER]);
362 : : }
363 : :
364 : : /**
365 : : * mct_restrict_applications_dialog_get_user_display_name:
366 : : * @self: an #MctRestrictApplicationsDialog
367 : : *
368 : : * Get the value of #MctRestrictApplicationsDialog:user-display-name.
369 : : *
370 : : * Returns: (transfer none) (nullable): the display name of the user the dialog
371 : : * is configured for, or %NULL if unknown
372 : : * Since: 0.5.0
373 : : */
374 : : const gchar *
375 : 0 : mct_restrict_applications_dialog_get_user_display_name (MctRestrictApplicationsDialog *self)
376 : : {
377 : 0 : g_return_val_if_fail (MCT_IS_RESTRICT_APPLICATIONS_DIALOG (self), NULL);
378 : :
379 : 0 : return self->user_display_name;
380 : : }
381 : :
382 : : /**
383 : : * mct_restrict_applications_dialog_set_user_display_name:
384 : : * @self: an #MctRestrictApplicationsDialog
385 : : * @user_display_name: (nullable) (transfer none): the display name of the user
386 : : * to configure the dialog for, or %NULL if unknown
387 : : *
388 : : * Set the value of #MctRestrictApplicationsDialog:user-display-name.
389 : : *
390 : : * Since: 0.5.0
391 : : */
392 : : void
393 : 0 : mct_restrict_applications_dialog_set_user_display_name (MctRestrictApplicationsDialog *self,
394 : : const gchar *user_display_name)
395 : : {
396 : 0 : g_return_if_fail (MCT_IS_RESTRICT_APPLICATIONS_DIALOG (self));
397 : 0 : g_return_if_fail (user_display_name == NULL ||
398 : : (*user_display_name != '\0' &&
399 : : g_utf8_validate (user_display_name, -1, NULL)));
400 : :
401 [ # # ]: 0 : if (g_strcmp0 (self->user_display_name, user_display_name) == 0)
402 : 0 : return;
403 : :
404 [ # # ]: 0 : g_clear_pointer (&self->user_display_name, g_free);
405 : 0 : self->user_display_name = g_strdup (user_display_name);
406 : :
407 : 0 : update_description (self);
408 : 0 : g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USER_DISPLAY_NAME]);
409 : : }
410 : :
411 : : /**
412 : : * mct_restrict_applications_dialog_build_app_filter:
413 : : * @self: an #MctRestrictApplicationsDialog
414 : : * @builder: an existing #MctAppFilterBuilder to modify
415 : : *
416 : : * Get the app filter settings currently configured in the dialog, by modifying
417 : : * the given @builder.
418 : : *
419 : : * Typically this will be called in the handler for #GtkDialog::response.
420 : : *
421 : : * Since: 0.5.0
422 : : */
423 : : void
424 : 0 : mct_restrict_applications_dialog_build_app_filter (MctRestrictApplicationsDialog *self,
425 : : MctAppFilterBuilder *builder)
426 : : {
427 : 0 : g_return_if_fail (MCT_IS_RESTRICT_APPLICATIONS_DIALOG (self));
428 : 0 : g_return_if_fail (builder != NULL);
429 : :
430 : 0 : mct_restrict_applications_selector_build_app_filter (self->selector, builder);
431 : : }
|