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