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-preferences-dialog"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <glib/gi18n-lib.h>
9 : : #include <adwaita.h>
10 : : #include <gtk/gtk.h>
11 : : #include <pango/pango.h>
12 : : #include <libvalent-core.h>
13 : : #include <libvalent-clipboard.h>
14 : : #include <libvalent-contacts.h>
15 : : #include <libvalent-device.h>
16 : : #include <libvalent-input.h>
17 : : #include <libvalent-media.h>
18 : : #include <libvalent-mixer.h>
19 : : #include <libvalent-notifications.h>
20 : : #include <libvalent-session.h>
21 : :
22 : : #include "valent-preferences-page.h"
23 : : #include "valent-preferences-dialog.h"
24 : :
25 : :
26 : : struct _ValentPreferencesDialog
27 : : {
28 : : AdwPreferencesDialog parent_instance;
29 : :
30 : : GSettings *settings;
31 : : GHashTable *pages;
32 : : GHashTable *rows;
33 : :
34 : : /* template */
35 : : AdwPreferencesPage *main_page;
36 : :
37 : : AdwPreferencesGroup *general_group;
38 : : AdwEntryRow *name_entry;
39 : :
40 : : AdwPreferencesGroup *plugin_group;
41 : : GtkListBox *plugin_list;
42 : : };
43 : :
44 [ + + + - ]: 94 : G_DEFINE_FINAL_TYPE (ValentPreferencesDialog, valent_preferences_dialog, ADW_TYPE_PREFERENCES_DIALOG)
45 : :
46 : :
47 : : typedef struct
48 : : {
49 : : GType gtype;
50 : : char *title;
51 : : char *domain;
52 : : } ExtensionDescription;
53 : :
54 : : enum {
55 : : EXTEN_APPLICATION_PLUGIN,
56 : : EXTEN_CHANNEL_SERVICE,
57 : : EXTEN_CLIPBOARD_ADAPTER,
58 : : EXTEN_CONTACTS_ADAPTER,
59 : : EXTEN_INPUT_ADAPTER,
60 : : EXTEN_MEDIA_ADAPTER,
61 : : EXTEN_MIXER_ADAPTER,
62 : : EXTEN_NOTIFICATION_ADAPTER,
63 : : EXTEN_SESSION_ADAPTER,
64 : : N_EXTENSIONS,
65 : : };
66 : :
67 : : static ExtensionDescription extensions[N_EXTENSIONS] = { 0, };
68 : :
69 : :
70 : : static int
71 : 0 : plugin_list_sort (GtkListBoxRow *row1,
72 : : GtkListBoxRow *row2,
73 : : gpointer user_data)
74 : : {
75 [ # # # # ]: 0 : if G_UNLIKELY (!ADW_IS_PREFERENCES_ROW (row1) ||
76 : : !ADW_IS_PREFERENCES_ROW (row2))
77 : 0 : return 0;
78 : :
79 : 0 : return g_utf8_collate (adw_preferences_row_get_title ((AdwPreferencesRow *)row1),
80 : 0 : adw_preferences_row_get_title ((AdwPreferencesRow *)row2));
81 : : }
82 : :
83 : : /*
84 : : * Device Name Callbacks
85 : : */
86 : : static void
87 : 0 : on_name_apply (GtkEditable *editable,
88 : : ValentPreferencesDialog *self)
89 : : {
90 : 0 : const char *name = NULL;
91 : :
92 [ # # # # : 0 : g_assert (GTK_IS_EDITABLE (editable));
# # # # ]
93 [ # # ]: 0 : g_assert (VALENT_IS_PREFERENCES_DIALOG (self));
94 : :
95 : 0 : name = gtk_editable_get_text (editable);
96 : :
97 [ # # # # ]: 0 : if (name == NULL || *name == '\0')
98 : : return;
99 : :
100 : 0 : g_settings_set_string (self->settings, "name", name);
101 : : }
102 : :
103 : : static void
104 : 0 : on_settings_changed (GSettings *settings,
105 : : const char *key,
106 : : ValentPreferencesDialog *self)
107 : : {
108 : 0 : const char *text = NULL;
109 : 0 : g_autofree char *name = NULL;
110 : :
111 [ # # # # : 0 : g_assert (G_IS_SETTINGS (settings));
# # # # ]
112 [ # # # # ]: 0 : g_assert (key != NULL && *key != '\0');
113 [ # # ]: 0 : g_assert (VALENT_IS_PREFERENCES_DIALOG (self));
114 : :
115 : 0 : name = g_settings_get_string (self->settings, "name");
116 : 0 : text = gtk_editable_get_text (GTK_EDITABLE (self->name_entry));
117 : :
118 [ # # ]: 0 : if (g_strcmp0 (text, name) != 0)
119 : 0 : gtk_editable_set_text (GTK_EDITABLE (self->name_entry), name);
120 : 0 : }
121 : :
122 : : /*
123 : : * PeasEngine Callbacks
124 : : */
125 : : static void
126 : 5 : plugin_row_add_extensions (AdwExpanderRow *plugin_row,
127 : : PeasPluginInfo *info)
128 : : {
129 : 5 : PeasEngine *engine = valent_get_plugin_engine ();
130 : 5 : GtkWidget *row;
131 : :
132 [ + + ]: 50 : for (unsigned int i = 0; i < N_EXTENSIONS; i++)
133 : : {
134 : 45 : ExtensionDescription extension = extensions[i];
135 : 45 : g_autoptr (ValentContext) domain = NULL;
136 [ + - ]: 45 : g_autoptr (ValentContext) context = NULL;
137 [ + - ]: 45 : g_autoptr (GSettings) settings = NULL;
138 : :
139 [ - + ]: 45 : if (!peas_engine_provides_extension (engine, info, extension.gtype))
140 : 0 : continue;
141 : :
142 : 45 : row = g_object_new (ADW_TYPE_SWITCH_ROW,
143 : 45 : "title", _(extension.title),
144 : : "selectable", FALSE,
145 : : NULL);
146 : 45 : adw_expander_row_add_row (ADW_EXPANDER_ROW (plugin_row), row);
147 : :
148 : 45 : domain = valent_context_new (NULL, extension.domain, NULL);
149 : 45 : context = valent_context_get_plugin_context (domain, info);
150 : 45 : settings = valent_context_create_settings (context,
151 : : "ca.andyholmes.Valent.Plugin");
152 : 45 : g_settings_bind (settings, "enabled",
153 : : row, "active",
154 : : G_SETTINGS_BIND_DEFAULT);
155 : 45 : adw_switch_row_set_active (ADW_SWITCH_ROW (row),
156 : : g_settings_get_boolean (settings, "enabled"));
157 [ + - ]: 45 : g_object_set_data_full (G_OBJECT (row),
158 : : "plugin-settings",
159 : : g_steal_pointer (&settings),
160 : : g_object_unref);
161 : : }
162 : 5 : }
163 : :
164 : : static void
165 : 17 : on_load_plugin (PeasEngine *engine,
166 : : PeasPluginInfo *info,
167 : : ValentPreferencesDialog *self)
168 : : {
169 : 17 : GtkWidget *row = NULL;
170 : 17 : const char *module;
171 : 17 : const char *title;
172 : 17 : const char *subtitle;
173 : 17 : const char *icon_name;
174 : :
175 [ + - ]: 17 : g_assert (PEAS_IS_ENGINE (engine));
176 [ - + ]: 17 : g_assert (info != NULL);
177 [ - + ]: 17 : g_assert (VALENT_IS_PREFERENCES_DIALOG (self));
178 : :
179 [ + + ]: 17 : if (peas_plugin_info_is_hidden (info))
180 : : return;
181 : :
182 : 9 : engine = valent_get_plugin_engine ();
183 : 9 : module = peas_plugin_info_get_module_name (info);
184 : 9 : title = peas_plugin_info_get_name (info);
185 : 9 : subtitle = peas_plugin_info_get_description (info);
186 : 9 : icon_name = peas_plugin_info_get_icon_name (info);
187 : :
188 [ + + + - ]: 13 : if (peas_engine_provides_extension (engine, info, VALENT_TYPE_APPLICATION_PLUGIN) ||
189 [ + - ]: 8 : peas_engine_provides_extension (engine, info, VALENT_TYPE_CLIPBOARD_ADAPTER) ||
190 [ + - ]: 8 : peas_engine_provides_extension (engine, info, VALENT_TYPE_CHANNEL_SERVICE) ||
191 [ + - ]: 8 : peas_engine_provides_extension (engine, info, VALENT_TYPE_CONTACTS_ADAPTER) ||
192 [ + - ]: 8 : peas_engine_provides_extension (engine, info, VALENT_TYPE_INPUT_ADAPTER) ||
193 [ + - ]: 8 : peas_engine_provides_extension (engine, info, VALENT_TYPE_MEDIA_ADAPTER) ||
194 [ + - ]: 8 : peas_engine_provides_extension (engine, info, VALENT_TYPE_MIXER_ADAPTER) ||
195 [ + - ]: 8 : peas_engine_provides_extension (engine, info, VALENT_TYPE_NOTIFICATIONS_ADAPTER) ||
196 [ - + ]: 8 : peas_engine_provides_extension (engine, info, VALENT_TYPE_SESSION_ADAPTER) ||
197 : 4 : peas_engine_provides_extension (engine, info, VALENT_TYPE_PREFERENCES_PAGE))
198 : : {
199 : 5 : GtkWidget *icon;
200 : :
201 : 5 : row = g_object_new (ADW_TYPE_EXPANDER_ROW,
202 : : "title", title,
203 : : "subtitle", subtitle,
204 : : "selectable", FALSE,
205 : : NULL);
206 : 5 : icon = g_object_new (GTK_TYPE_IMAGE,
207 : : "accessible-role", GTK_ACCESSIBLE_ROLE_PRESENTATION,
208 : : "icon-name", icon_name,
209 : : NULL);
210 : 5 : adw_expander_row_add_prefix (ADW_EXPANDER_ROW (row), icon);
211 : :
212 : 5 : plugin_row_add_extensions (ADW_EXPANDER_ROW (row), info);
213 : :
214 : 5 : gtk_list_box_insert (self->plugin_list, row, -1);
215 : 5 : g_hash_table_insert (self->rows, info, g_object_ref (row));
216 : : }
217 : :
218 : : /* Preferences Page */
219 [ + + ]: 9 : if (peas_engine_provides_extension (engine,
220 : : info,
221 : : VALENT_TYPE_PREFERENCES_PAGE))
222 : : {
223 : 5 : GObject *page;
224 : 5 : GtkWidget *button;
225 : :
226 : 5 : button = g_object_new (GTK_TYPE_BUTTON,
227 : : "action-target", g_variant_new_string (module),
228 : : "action-name", "win.page",
229 : : "icon-name", "emblem-system-symbolic",
230 : : "valign", GTK_ALIGN_CENTER,
231 : : NULL);
232 : : #if ADW_CHECK_VERSION (1, 4, 0)
233 : 5 : adw_expander_row_add_suffix (ADW_EXPANDER_ROW (row), button);
234 : : #else
235 : : adw_expander_row_add_action (ADW_EXPANDER_ROW (row), button);
236 : : #endif
237 : :
238 : 5 : page = peas_engine_create_extension (engine,
239 : : info,
240 : : VALENT_TYPE_PREFERENCES_PAGE,
241 : : "name", module,
242 : : "icon-name", icon_name,
243 : : "title", title,
244 : : NULL);
245 : 5 : adw_preferences_dialog_add (ADW_PREFERENCES_DIALOG (self),
246 : : ADW_PREFERENCES_PAGE (page));
247 : 5 : g_hash_table_insert (self->pages, info, g_object_ref (page));
248 : : }
249 : : }
250 : :
251 : : static void
252 : 1 : on_unload_plugin (PeasEngine *engine,
253 : : PeasPluginInfo *info,
254 : : ValentPreferencesDialog *self)
255 : : {
256 : 2 : g_autoptr (AdwPreferencesPage) page = NULL;
257 [ + - ]: 1 : g_autoptr (GtkWidget) row = NULL;
258 : :
259 [ + - ]: 1 : if (g_hash_table_steal_extended (self->pages, info, NULL, (void **)&page))
260 : 1 : adw_preferences_dialog_remove (ADW_PREFERENCES_DIALOG (self), page);
261 : :
262 [ + - ]: 1 : if (g_hash_table_steal_extended (self->rows, info, NULL, (void **)&row))
263 : 1 : gtk_list_box_remove (self->plugin_list, row);
264 : 1 : }
265 : :
266 : : /*
267 : : * GActions
268 : : */
269 : : static void
270 : 1 : page_action (GtkWidget *widget,
271 : : const char *action_name,
272 : : GVariant *parameter)
273 : : {
274 : 1 : AdwPreferencesDialog *window = ADW_PREFERENCES_DIALOG (widget);
275 : 1 : const char *module;
276 : :
277 : 1 : module = g_variant_get_string (parameter, NULL);
278 : 1 : adw_preferences_dialog_set_visible_page_name (window, module);
279 : 1 : }
280 : :
281 : : /*
282 : : * GObject
283 : : */
284 : : static void
285 : 4 : valent_preferences_dialog_constructed (GObject *object)
286 : : {
287 : 4 : ValentPreferencesDialog *self = VALENT_PREFERENCES_DIALOG (object);
288 : 8 : g_autofree char *name = NULL;
289 : 4 : PeasEngine *engine = valent_get_plugin_engine ();
290 : 4 : unsigned int n_plugins = 0;
291 : :
292 : : /* Application Settings */
293 : 4 : self->settings = g_settings_new ("ca.andyholmes.Valent");
294 : 4 : g_signal_connect_object (self->settings,
295 : : "changed::name",
296 : : G_CALLBACK (on_settings_changed),
297 : : self, 0);
298 : 4 : name = g_settings_get_string (self->settings, "name");
299 : 4 : gtk_editable_set_text (GTK_EDITABLE (self->name_entry), name);
300 : :
301 : : /* Application Plugins */
302 : 4 : n_plugins = g_list_model_get_n_items (G_LIST_MODEL (engine));
303 : :
304 [ + + ]: 20 : for (unsigned int i = 0; i < n_plugins; i++)
305 : : {
306 : 16 : g_autoptr (PeasPluginInfo) info = NULL;
307 : :
308 : 16 : info = g_list_model_get_item (G_LIST_MODEL (engine), i);
309 : :
310 [ + - ]: 16 : if (peas_plugin_info_is_loaded (info))
311 : 16 : on_load_plugin (engine, info, self);
312 : : }
313 : :
314 : 4 : g_signal_connect_object (engine,
315 : : "load-plugin",
316 : : G_CALLBACK (on_load_plugin),
317 : : self,
318 : : G_CONNECT_AFTER);
319 : :
320 : 4 : g_signal_connect_object (engine,
321 : : "unload-plugin",
322 : : G_CALLBACK (on_unload_plugin),
323 : : self,
324 : : 0);
325 : :
326 : 4 : G_OBJECT_CLASS (valent_preferences_dialog_parent_class)->constructed (object);
327 : 4 : }
328 : :
329 : : static void
330 : 4 : valent_preferences_dialog_dispose (GObject *object)
331 : : {
332 : 4 : ValentPreferencesDialog *self = VALENT_PREFERENCES_DIALOG (object);
333 : :
334 : 4 : g_signal_handlers_disconnect_by_data (valent_get_plugin_engine (), self);
335 [ + - ]: 4 : g_clear_object (&self->settings);
336 : :
337 : 4 : gtk_widget_dispose_template (GTK_WIDGET (object),
338 : : VALENT_TYPE_PREFERENCES_DIALOG);
339 : :
340 : 4 : G_OBJECT_CLASS (valent_preferences_dialog_parent_class)->dispose (object);
341 : 4 : }
342 : :
343 : : static void
344 : 4 : valent_preferences_dialog_finalize (GObject *object)
345 : : {
346 : 4 : ValentPreferencesDialog *self = VALENT_PREFERENCES_DIALOG (object);
347 : :
348 [ + - ]: 4 : g_clear_pointer (&self->pages, g_hash_table_unref);
349 [ + - ]: 4 : g_clear_pointer (&self->rows, g_hash_table_unref);
350 : :
351 : 4 : G_OBJECT_CLASS (valent_preferences_dialog_parent_class)->finalize (object);
352 : 4 : }
353 : :
354 : : static void
355 : 2 : valent_preferences_dialog_class_init (ValentPreferencesDialogClass *klass)
356 : : {
357 : 2 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
358 : 2 : GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
359 : :
360 : 2 : object_class->constructed = valent_preferences_dialog_constructed;
361 : 2 : object_class->dispose = valent_preferences_dialog_dispose;
362 : 2 : object_class->finalize = valent_preferences_dialog_finalize;
363 : :
364 : 2 : gtk_widget_class_set_template_from_resource (widget_class, "/ca/andyholmes/Valent/ui/valent-preferences-dialog.ui");
365 : :
366 : 2 : gtk_widget_class_bind_template_child (widget_class, ValentPreferencesDialog, general_group);
367 : 2 : gtk_widget_class_bind_template_child (widget_class, ValentPreferencesDialog, main_page);
368 : 2 : gtk_widget_class_bind_template_child (widget_class, ValentPreferencesDialog, plugin_group);
369 : 2 : gtk_widget_class_bind_template_child (widget_class, ValentPreferencesDialog, plugin_list);
370 : 2 : gtk_widget_class_bind_template_child (widget_class, ValentPreferencesDialog, name_entry);
371 : :
372 : 2 : gtk_widget_class_bind_template_callback (widget_class, on_name_apply);
373 : :
374 : 2 : gtk_widget_class_install_action (widget_class, "win.page", "s", page_action);
375 : :
376 : : /* ... */
377 : 4 : extensions[EXTEN_APPLICATION_PLUGIN] =
378 : : (ExtensionDescription){
379 : 2 : VALENT_TYPE_APPLICATION_PLUGIN,
380 : : N_("Application"),
381 : : "application",
382 : : };
383 : :
384 : 4 : extensions[EXTEN_CHANNEL_SERVICE] =
385 : : (ExtensionDescription){
386 : 2 : VALENT_TYPE_CHANNEL_SERVICE,
387 : : N_("Device Connections"),
388 : : "network",
389 : : };
390 : :
391 : 4 : extensions[EXTEN_CLIPBOARD_ADAPTER] =
392 : : (ExtensionDescription){
393 : 2 : VALENT_TYPE_CLIPBOARD_ADAPTER,
394 : : N_("Clipboard"),
395 : : "clipboard",
396 : : };
397 : :
398 : 4 : extensions[EXTEN_CONTACTS_ADAPTER] =
399 : : (ExtensionDescription){
400 : 2 : VALENT_TYPE_CONTACTS_ADAPTER,
401 : : N_("Contacts"),
402 : : "contacts",
403 : : };
404 : :
405 : 4 : extensions[EXTEN_INPUT_ADAPTER] =
406 : : (ExtensionDescription){
407 : 2 : VALENT_TYPE_INPUT_ADAPTER,
408 : : N_("Mouse and Keyboard"),
409 : : "input",
410 : : };
411 : :
412 : 4 : extensions[EXTEN_MEDIA_ADAPTER] =
413 : : (ExtensionDescription){
414 : 2 : VALENT_TYPE_MEDIA_ADAPTER,
415 : : N_("Media Players"),
416 : : "media",
417 : : };
418 : :
419 : 4 : extensions[EXTEN_MIXER_ADAPTER] =
420 : : (ExtensionDescription){
421 : 2 : VALENT_TYPE_MIXER_ADAPTER,
422 : : N_("Volume Control"),
423 : : "mixer",
424 : : };
425 : :
426 : 4 : extensions[EXTEN_NOTIFICATION_ADAPTER] =
427 : : (ExtensionDescription){
428 : 2 : VALENT_TYPE_NOTIFICATIONS_ADAPTER,
429 : : N_("Notifications"),
430 : : "notifications",
431 : : };
432 : :
433 : 4 : extensions[EXTEN_SESSION_ADAPTER] =
434 : : (ExtensionDescription){
435 : 2 : VALENT_TYPE_SESSION_ADAPTER,
436 : : N_("Session Manager"),
437 : : "session",
438 : : };
439 : 2 : }
440 : :
441 : : static void
442 : 4 : valent_preferences_dialog_init (ValentPreferencesDialog *self)
443 : : {
444 : 4 : gtk_widget_init_template (GTK_WIDGET (self));
445 : :
446 : 4 : gtk_list_box_set_sort_func (self->plugin_list, plugin_list_sort, NULL, NULL);
447 : :
448 : 4 : self->pages = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref);
449 : 4 : self->rows = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref);
450 : 4 : }
451 : :
|