Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-3.0-or-later
2 : : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
3 : :
4 : : #define G_LOG_DOMAIN "valent-notification-preferences"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <glib/gi18n.h>
9 : : #include <adwaita.h>
10 : : #include <gtk/gtk.h>
11 : : #include <valent.h>
12 : :
13 : : #include "valent-device-preferences-notification.h"
14 : :
15 : :
16 : : struct _ValentNotificationPreferences
17 : : {
18 : : ValentDevicePreferencesGroup parent_instance;
19 : :
20 : : /* template */
21 : : AdwExpanderRow *forward_notifications;
22 : : GtkSwitch *forward_when_active;
23 : :
24 : : AdwNavigationPage *application_page;
25 : : GtkStack *application_title_stack;
26 : : GtkToggleButton *application_filter_button;
27 : : GtkSearchEntry *application_filter_entry;
28 : : AdwActionRow *application_row;
29 : : GtkListBox *application_list;
30 : :
31 : : char *application_filter;
32 : : GHashTable *application_rows;
33 : : };
34 : :
35 [ + + + - ]: 43 : G_DEFINE_FINAL_TYPE (ValentNotificationPreferences, valent_notification_preferences, VALENT_TYPE_DEVICE_PREFERENCES_GROUP)
36 : :
37 : :
38 : : static gboolean
39 : 0 : application_list_filter (GtkListBoxRow *row,
40 : : gpointer user_data)
41 : : {
42 : 0 : ValentNotificationPreferences *self = VALENT_NOTIFICATION_PREFERENCES (user_data);
43 : 0 : g_autofree char *haystack = NULL;
44 : 0 : const char *title = NULL;
45 : :
46 [ # # ]: 0 : g_assert (VALENT_IS_NOTIFICATION_PREFERENCES (self));
47 : :
48 [ # # # # ]: 0 : if (self->application_filter == NULL || *self->application_filter == '\0')
49 : : return TRUE;
50 : :
51 : 0 : title = adw_preferences_row_get_title (ADW_PREFERENCES_ROW (row));
52 : 0 : haystack = g_utf8_casefold (title, -1);
53 : :
54 : 0 : return g_strrstr (haystack, self->application_filter) != NULL;
55 : : }
56 : :
57 : : static int
58 : 0 : application_list_sort (GtkListBoxRow *row1,
59 : : GtkListBoxRow *row2,
60 : : gpointer user_data)
61 : : {
62 [ # # # # ]: 0 : if G_UNLIKELY (!ADW_IS_PREFERENCES_ROW (row1) ||
63 : : !ADW_IS_PREFERENCES_ROW (row2))
64 : 0 : return 0;
65 : :
66 : 0 : return g_utf8_collate (adw_preferences_row_get_title ((AdwPreferencesRow *)row1),
67 : 0 : adw_preferences_row_get_title ((AdwPreferencesRow *)row2));
68 : : }
69 : :
70 : : /*
71 : : * Template Callbacks
72 : : */
73 : : static void
74 : 0 : on_search_changed (GtkSearchEntry *entry,
75 : : ValentNotificationPreferences *self)
76 : : {
77 : 0 : g_autofree char *query = NULL;
78 : :
79 [ # # ]: 0 : g_assert (VALENT_IS_NOTIFICATION_PREFERENCES (self));
80 : :
81 : 0 : query = g_utf8_casefold (gtk_editable_get_text (GTK_EDITABLE (entry)), -1);
82 : :
83 [ # # ]: 0 : if (g_set_str (&self->application_filter, query))
84 : 0 : gtk_list_box_invalidate_filter (self->application_list);
85 : 0 : }
86 : :
87 : : static void
88 : 0 : on_search_toggled (GtkToggleButton *button,
89 : : GParamSpec *pspec,
90 : : ValentNotificationPreferences *self)
91 : : {
92 [ # # ]: 0 : g_assert (VALENT_IS_NOTIFICATION_PREFERENCES (self));
93 : :
94 [ # # ]: 0 : if (gtk_toggle_button_get_active (button))
95 : : {
96 : 0 : gtk_stack_set_visible_child_name (self->application_title_stack, "search");
97 : 0 : gtk_widget_grab_focus (GTK_WIDGET (self->application_filter_entry));
98 : 0 : gtk_editable_set_position (GTK_EDITABLE (self->application_filter_entry), -1);
99 : : }
100 : : else
101 : : {
102 : 0 : gtk_editable_set_text (GTK_EDITABLE (self->application_filter_entry), "");
103 : 0 : gtk_stack_set_visible_child_name (self->application_title_stack, "title");
104 : : }
105 : 0 : }
106 : :
107 : : static void
108 : 0 : on_search_started (GtkSearchEntry *entry,
109 : : ValentNotificationPreferences *self)
110 : : {
111 [ # # ]: 0 : g_assert (VALENT_IS_NOTIFICATION_PREFERENCES (self));
112 : :
113 : 0 : gtk_toggle_button_set_active (self->application_filter_button, TRUE);
114 : 0 : }
115 : :
116 : : static void
117 : 0 : on_stop_search (GtkSearchEntry *entry,
118 : : ValentNotificationPreferences *self)
119 : : {
120 [ # # ]: 0 : g_assert (VALENT_IS_NOTIFICATION_PREFERENCES (self));
121 : :
122 : 0 : gtk_toggle_button_set_active (self->application_filter_button, FALSE);
123 : 0 : }
124 : :
125 : : static void
126 : 0 : on_switch_toggled (GObject *object,
127 : : GParamSpec *pspec,
128 : : ValentNotificationPreferences *self)
129 : : {
130 : 0 : ValentDevicePreferencesGroup *group = VALENT_DEVICE_PREFERENCES_GROUP (self);
131 : 0 : GSettings *settings;
132 : 0 : GHashTableIter iter;
133 : 0 : gpointer row;
134 : 0 : GVariantBuilder builder;
135 : 0 : GVariant *forward_deny;
136 : :
137 : 0 : g_hash_table_iter_init (&iter, self->application_rows);
138 : 0 : g_variant_builder_init (&builder, G_VARIANT_TYPE_STRING_ARRAY);
139 : :
140 [ # # ]: 0 : while (g_hash_table_iter_next (&iter, &row, NULL))
141 : : {
142 : 0 : const char *name;
143 : :
144 [ # # ]: 0 : if (adw_switch_row_get_active (ADW_SWITCH_ROW (row)))
145 : 0 : continue;
146 : :
147 : 0 : name = adw_preferences_row_get_title (ADW_PREFERENCES_ROW (row));
148 : 0 : g_variant_builder_add (&builder, "s", name);
149 : : }
150 : :
151 : 0 : forward_deny = g_variant_builder_end (&builder);
152 : 0 : settings = valent_device_preferences_group_get_settings (group);
153 : 0 : g_settings_set_value (settings, "forward-deny", forward_deny);
154 : 0 : }
155 : :
156 : : static void
157 : 0 : add_application (ValentNotificationPreferences *self,
158 : : GVariant *app,
159 : : gboolean enabled)
160 : : {
161 : 0 : GtkWidget *row;
162 : 0 : GtkWidget *image;
163 : 0 : const char *title;
164 : 0 : g_autoptr (GVariant) icon_v = NULL;
165 [ # # ]: 0 : g_autoptr (GIcon) icon = NULL;
166 : :
167 [ # # ]: 0 : if (!g_variant_lookup (app, "name", "&s", &title))
168 : 0 : return;
169 : :
170 : 0 : row = g_object_new (ADW_TYPE_SWITCH_ROW,
171 : : "activatable", TRUE,
172 : : "selectable", TRUE,
173 : : "title", title,
174 : : "active", enabled,
175 : : NULL);
176 : 0 : g_signal_connect_object (row,
177 : : "notify::active",
178 : : G_CALLBACK (on_switch_toggled),
179 : : self, 0);
180 : :
181 : : /* App icon */
182 [ # # ]: 0 : if ((icon_v = g_variant_lookup_value (app, "icon", NULL)) != NULL)
183 : 0 : icon = g_icon_deserialize (icon_v);
184 : :
185 [ # # ]: 0 : if (icon == NULL)
186 : 0 : icon = g_icon_new_for_string ("application-x-executable", NULL);
187 : :
188 : 0 : image = g_object_new (GTK_TYPE_IMAGE,
189 : : "gicon", icon,
190 : : "icon-size", GTK_ICON_SIZE_LARGE,
191 : : NULL);
192 : 0 : adw_action_row_add_prefix (ADW_ACTION_ROW (row), image);
193 : :
194 : 0 : g_hash_table_add (self->application_rows, row);
195 [ # # ]: 0 : gtk_list_box_insert (self->application_list, row, -1);
196 : : }
197 : :
198 : : static void
199 : 1 : populate_applications (ValentNotificationPreferences *self)
200 : : {
201 : 1 : ValentDevicePreferencesGroup *group = VALENT_DEVICE_PREFERENCES_GROUP (self);
202 : 1 : GSettings *settings;
203 : 1 : GVariant *known;
204 : 2 : g_auto (GStrv) deny = NULL;
205 : 1 : GVariantIter iter;
206 : 1 : const char *key;
207 : 1 : GVariant *value;
208 : :
209 : 1 : settings = valent_device_preferences_group_get_settings (group);
210 : 1 : deny = g_settings_get_strv (settings, "forward-deny");
211 : :
212 : : /* Query the known applications */
213 : 1 : known = valent_notifications_get_applications (NULL);
214 : 1 : g_variant_iter_init (&iter, known);
215 : :
216 [ - + ]: 1 : while (g_variant_iter_loop (&iter, "{sv}", &key, &value))
217 : : {
218 : 0 : gboolean enabled;
219 : :
220 : 0 : enabled = !g_strv_contains ((const char * const *)deny, key);
221 : 0 : add_application (self, value, enabled);
222 : : }
223 : 1 : }
224 : :
225 : : /*
226 : : * GAction
227 : : */
228 : : static void
229 : 0 : applications_action (GtkWidget *widget,
230 : : const char *action,
231 : : GVariant *parameter)
232 : : {
233 : 0 : ValentNotificationPreferences *self = VALENT_NOTIFICATION_PREFERENCES (widget);
234 : 0 : GtkWidget *dialog = NULL;
235 : :
236 [ # # ]: 0 : g_assert (VALENT_IS_NOTIFICATION_PREFERENCES (self));
237 : :
238 : 0 : dialog = gtk_widget_get_ancestor (widget, ADW_TYPE_PREFERENCES_DIALOG);
239 [ # # ]: 0 : if (dialog != NULL)
240 : : {
241 : 0 : adw_preferences_dialog_push_subpage (ADW_PREFERENCES_DIALOG (dialog),
242 : : self->application_page);
243 : : }
244 : 0 : }
245 : :
246 : : static void
247 : 0 : reset_action (GtkWidget *widget,
248 : : const char *action,
249 : : GVariant *parameter)
250 : : {
251 : 0 : ValentDevicePreferencesGroup *group = VALENT_DEVICE_PREFERENCES_GROUP (widget);
252 : 0 : ValentNotificationPreferences *self = VALENT_NOTIFICATION_PREFERENCES (group);
253 : 0 : GSettings *settings;
254 : 0 : GHashTableIter iter;
255 : 0 : gpointer row_switch;
256 : :
257 [ # # ]: 0 : g_assert (VALENT_IS_NOTIFICATION_PREFERENCES (self));
258 [ # # ]: 0 : g_assert (VALENT_IS_DEVICE_PREFERENCES_GROUP (self));
259 : :
260 : 0 : g_hash_table_iter_init (&iter, self->application_rows);
261 : :
262 [ # # ]: 0 : while (g_hash_table_iter_next (&iter, NULL, &row_switch))
263 : 0 : gtk_switch_set_active (GTK_SWITCH (row_switch), TRUE);
264 : :
265 : 0 : settings = valent_device_preferences_group_get_settings (group);
266 : 0 : g_settings_reset (settings, "applications");
267 : 0 : }
268 : :
269 : : /*
270 : : * GObject
271 : : */
272 : : static void
273 : 1 : valent_notification_preferences_constructed (GObject *object)
274 : : {
275 : 1 : ValentNotificationPreferences *self = VALENT_NOTIFICATION_PREFERENCES (object);
276 : 1 : ValentDevicePreferencesGroup *group = VALENT_DEVICE_PREFERENCES_GROUP (self);
277 : 1 : GSettings *settings;
278 : :
279 : 1 : settings = valent_device_preferences_group_get_settings (group);
280 : 1 : g_settings_bind (settings, "forward-notifications",
281 : 1 : self->forward_notifications, "enable-expansion",
282 : : G_SETTINGS_BIND_DEFAULT);
283 : 1 : g_settings_bind (settings, "forward-when-active",
284 : 1 : self->forward_when_active, "active",
285 : : G_SETTINGS_BIND_INVERT_BOOLEAN);
286 : :
287 : 1 : gtk_list_box_set_filter_func (self->application_list,
288 : : application_list_filter,
289 : : self, NULL);
290 : 1 : gtk_list_box_set_sort_func (self->application_list,
291 : : application_list_sort,
292 : : self, NULL);
293 : 1 : populate_applications (self);
294 : :
295 : 1 : G_OBJECT_CLASS (valent_notification_preferences_parent_class)->constructed (object);
296 : 1 : }
297 : :
298 : : static void
299 : 1 : valent_notification_preferences_dispose (GObject *object)
300 : : {
301 : 1 : GtkWidget *widget = GTK_WIDGET (object);
302 : :
303 : 1 : gtk_widget_dispose_template (widget, VALENT_TYPE_NOTIFICATION_PREFERENCES);
304 : :
305 : 1 : G_OBJECT_CLASS (valent_notification_preferences_parent_class)->dispose (object);
306 : 1 : }
307 : :
308 : : static void
309 : 1 : valent_notification_preferences_finalize (GObject *object)
310 : : {
311 : 1 : ValentNotificationPreferences *self = VALENT_NOTIFICATION_PREFERENCES (object);
312 : :
313 [ - + ]: 1 : g_clear_pointer (&self->application_filter, g_free);
314 [ + - ]: 1 : g_clear_pointer (&self->application_rows, g_hash_table_unref);
315 : :
316 : 1 : G_OBJECT_CLASS (valent_notification_preferences_parent_class)->finalize (object);
317 : 1 : }
318 : :
319 : : static void
320 : 1 : valent_notification_preferences_class_init (ValentNotificationPreferencesClass *klass)
321 : : {
322 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
323 : 1 : GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
324 : :
325 : 1 : object_class->constructed = valent_notification_preferences_constructed;
326 : 1 : object_class->dispose = valent_notification_preferences_dispose;
327 : 1 : object_class->finalize = valent_notification_preferences_finalize;
328 : :
329 : 1 : gtk_widget_class_set_template_from_resource (widget_class, "/plugins/gnome/valent-device-preferences-notification.ui");
330 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentNotificationPreferences, forward_notifications);
331 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentNotificationPreferences, forward_when_active);
332 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentNotificationPreferences, application_page);
333 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentNotificationPreferences, application_title_stack);
334 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentNotificationPreferences, application_filter_entry);
335 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentNotificationPreferences, application_filter_button);
336 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentNotificationPreferences, application_list);
337 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentNotificationPreferences, application_row);
338 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_search_changed);
339 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_search_started);
340 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_search_toggled);
341 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_stop_search);
342 : :
343 : 1 : gtk_widget_class_install_action (widget_class, "preferences.applications", NULL, applications_action);
344 : 1 : gtk_widget_class_install_action (widget_class, "preferences.reset", NULL, reset_action);
345 : 1 : }
346 : :
347 : : static void
348 : 1 : valent_notification_preferences_init (ValentNotificationPreferences *self)
349 : : {
350 : 1 : gtk_widget_init_template (GTK_WIDGET (self));
351 : 1 : gtk_search_entry_set_key_capture_widget (self->application_filter_entry,
352 : 1 : GTK_WIDGET (self->application_page));
353 : :
354 : 1 : self->application_rows = g_hash_table_new (NULL, NULL);
355 : 1 : }
356 : :
|