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