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 : GtkWidget *row = NULL;
161 : 27 : const char *title;
162 : 27 : const char *subtitle;
163 : 27 : const char *icon_name;
164 : :
165 [ + - ]: 27 : g_assert (PEAS_IS_ENGINE (engine));
166 [ - + ]: 27 : g_assert (info != NULL);
167 [ - + ]: 27 : g_assert (VALENT_IS_PREFERENCES_DIALOG (self));
168 : :
169 [ + + ]: 27 : if (peas_plugin_info_is_hidden (info))
170 : : return;
171 : :
172 : 25 : engine = valent_get_plugin_engine ();
173 : 25 : title = peas_plugin_info_get_name (info);
174 : 25 : subtitle = peas_plugin_info_get_description (info);
175 : 25 : icon_name = peas_plugin_info_get_icon_name (info);
176 : :
177 [ + + + - ]: 45 : if (peas_engine_provides_extension (engine, info, VALENT_TYPE_APPLICATION_PLUGIN) ||
178 [ + - ]: 40 : peas_engine_provides_extension (engine, info, VALENT_TYPE_CLIPBOARD_ADAPTER) ||
179 [ + - ]: 40 : peas_engine_provides_extension (engine, info, VALENT_TYPE_CHANNEL_SERVICE) ||
180 [ + - ]: 40 : peas_engine_provides_extension (engine, info, VALENT_TYPE_CONTACTS_ADAPTER) ||
181 [ + - ]: 40 : peas_engine_provides_extension (engine, info, VALENT_TYPE_INPUT_ADAPTER) ||
182 [ + - ]: 40 : peas_engine_provides_extension (engine, info, VALENT_TYPE_MEDIA_ADAPTER) ||
183 [ + - ]: 40 : peas_engine_provides_extension (engine, info, VALENT_TYPE_MIXER_ADAPTER) ||
184 [ - + ]: 40 : peas_engine_provides_extension (engine, info, VALENT_TYPE_NOTIFICATIONS_ADAPTER) ||
185 : 20 : peas_engine_provides_extension (engine, info, VALENT_TYPE_SESSION_ADAPTER))
186 : : {
187 : 5 : GtkWidget *icon;
188 : :
189 : 5 : row = g_object_new (ADW_TYPE_EXPANDER_ROW,
190 : : "title", title,
191 : : "subtitle", subtitle,
192 : : "selectable", FALSE,
193 : : NULL);
194 : 5 : icon = g_object_new (GTK_TYPE_IMAGE,
195 : : "accessible-role", GTK_ACCESSIBLE_ROLE_PRESENTATION,
196 : : "icon-name", icon_name,
197 : : NULL);
198 : 5 : adw_expander_row_add_prefix (ADW_EXPANDER_ROW (row), icon);
199 : :
200 : 5 : plugin_row_add_extensions (ADW_EXPANDER_ROW (row), info);
201 : :
202 : 5 : gtk_list_box_insert (self->plugin_list, row, -1);
203 : 5 : g_hash_table_insert (self->rows, info, g_object_ref (row));
204 : : }
205 : : }
206 : :
207 : : static void
208 : 1 : on_unload_plugin (PeasEngine *engine,
209 : : PeasPluginInfo *info,
210 : : ValentPreferencesDialog *self)
211 : : {
212 : 2 : g_autoptr (AdwPreferencesPage) page = NULL;
213 [ - + ]: 1 : g_autoptr (GtkWidget) row = NULL;
214 : :
215 [ - + ]: 1 : if (g_hash_table_steal_extended (self->pages, info, NULL, (void **)&page))
216 : 0 : adw_preferences_dialog_remove (ADW_PREFERENCES_DIALOG (self), page);
217 : :
218 [ + - ]: 1 : if (g_hash_table_steal_extended (self->rows, info, NULL, (void **)&row))
219 : 1 : gtk_list_box_remove (self->plugin_list, row);
220 : 1 : }
221 : :
222 : : /*
223 : : * GActions
224 : : */
225 : : static void
226 : 0 : page_action (GtkWidget *widget,
227 : : const char *action_name,
228 : : GVariant *parameter)
229 : : {
230 : 0 : AdwPreferencesDialog *window = ADW_PREFERENCES_DIALOG (widget);
231 : 0 : const char *module;
232 : :
233 : 0 : module = g_variant_get_string (parameter, NULL);
234 : 0 : adw_preferences_dialog_set_visible_page_name (window, module);
235 : 0 : }
236 : :
237 : : /*
238 : : * GObject
239 : : */
240 : : static void
241 : 2 : valent_preferences_dialog_constructed (GObject *object)
242 : : {
243 : 2 : ValentPreferencesDialog *self = VALENT_PREFERENCES_DIALOG (object);
244 : 4 : g_autofree char *name = NULL;
245 : 2 : PeasEngine *engine = valent_get_plugin_engine ();
246 : 2 : unsigned int n_plugins = 0;
247 : :
248 : : /* Application Settings */
249 : 2 : self->settings = g_settings_new ("ca.andyholmes.Valent");
250 : 2 : g_signal_connect_object (self->settings,
251 : : "changed::name",
252 : : G_CALLBACK (on_settings_changed),
253 : : self, 0);
254 : 2 : name = g_settings_get_string (self->settings, "name");
255 : 2 : gtk_editable_set_text (GTK_EDITABLE (self->name_entry), name);
256 : :
257 : : /* Application Plugins */
258 : 2 : n_plugins = g_list_model_get_n_items (G_LIST_MODEL (engine));
259 : :
260 [ + + ]: 28 : for (unsigned int i = 0; i < n_plugins; i++)
261 : : {
262 : 26 : g_autoptr (PeasPluginInfo) info = NULL;
263 : :
264 : 26 : info = g_list_model_get_item (G_LIST_MODEL (engine), i);
265 : :
266 [ + - ]: 26 : if (peas_plugin_info_is_loaded (info))
267 : 26 : on_load_plugin (engine, info, self);
268 : : }
269 : :
270 : 2 : g_signal_connect_object (engine,
271 : : "load-plugin",
272 : : G_CALLBACK (on_load_plugin),
273 : : self,
274 : : G_CONNECT_AFTER);
275 : :
276 : 2 : g_signal_connect_object (engine,
277 : : "unload-plugin",
278 : : G_CALLBACK (on_unload_plugin),
279 : : self,
280 : : 0);
281 : :
282 : 2 : G_OBJECT_CLASS (valent_preferences_dialog_parent_class)->constructed (object);
283 : 2 : }
284 : :
285 : : static void
286 : 2 : valent_preferences_dialog_dispose (GObject *object)
287 : : {
288 : 2 : ValentPreferencesDialog *self = VALENT_PREFERENCES_DIALOG (object);
289 : :
290 : 2 : g_signal_handlers_disconnect_by_data (valent_get_plugin_engine (), self);
291 [ + - ]: 2 : g_clear_object (&self->settings);
292 : :
293 : 2 : gtk_widget_dispose_template (GTK_WIDGET (object),
294 : : VALENT_TYPE_PREFERENCES_DIALOG);
295 : :
296 : 2 : G_OBJECT_CLASS (valent_preferences_dialog_parent_class)->dispose (object);
297 : 2 : }
298 : :
299 : : static void
300 : 2 : valent_preferences_dialog_finalize (GObject *object)
301 : : {
302 : 2 : ValentPreferencesDialog *self = VALENT_PREFERENCES_DIALOG (object);
303 : :
304 [ + - ]: 2 : g_clear_pointer (&self->pages, g_hash_table_unref);
305 [ + - ]: 2 : g_clear_pointer (&self->rows, g_hash_table_unref);
306 : :
307 : 2 : G_OBJECT_CLASS (valent_preferences_dialog_parent_class)->finalize (object);
308 : 2 : }
309 : :
310 : : static void
311 : 1 : valent_preferences_dialog_class_init (ValentPreferencesDialogClass *klass)
312 : : {
313 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
314 : 1 : GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
315 : :
316 : 1 : object_class->constructed = valent_preferences_dialog_constructed;
317 : 1 : object_class->dispose = valent_preferences_dialog_dispose;
318 : 1 : object_class->finalize = valent_preferences_dialog_finalize;
319 : :
320 : 1 : gtk_widget_class_set_template_from_resource (widget_class, "/plugins/gnome/valent-preferences-dialog.ui");
321 : :
322 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentPreferencesDialog, general_group);
323 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentPreferencesDialog, main_page);
324 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentPreferencesDialog, plugin_group);
325 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentPreferencesDialog, plugin_list);
326 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentPreferencesDialog, name_entry);
327 : :
328 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_name_apply);
329 : :
330 : 1 : gtk_widget_class_install_action (widget_class, "win.page", "s", page_action);
331 : :
332 : : /* ... */
333 : 2 : extensions[EXTEN_APPLICATION_PLUGIN] =
334 : : (ExtensionDescription){
335 : 1 : VALENT_TYPE_APPLICATION_PLUGIN,
336 : : N_("Application"),
337 : : "application",
338 : : };
339 : :
340 : 2 : extensions[EXTEN_CHANNEL_SERVICE] =
341 : : (ExtensionDescription){
342 : 1 : VALENT_TYPE_CHANNEL_SERVICE,
343 : : N_("Device Connections"),
344 : : "network",
345 : : };
346 : :
347 : 2 : extensions[EXTEN_CLIPBOARD_ADAPTER] =
348 : : (ExtensionDescription){
349 : 1 : VALENT_TYPE_CLIPBOARD_ADAPTER,
350 : : N_("Clipboard"),
351 : : "clipboard",
352 : : };
353 : :
354 : 2 : extensions[EXTEN_CONTACTS_ADAPTER] =
355 : : (ExtensionDescription){
356 : 1 : VALENT_TYPE_CONTACTS_ADAPTER,
357 : : N_("Contacts"),
358 : : "contacts",
359 : : };
360 : :
361 : 2 : extensions[EXTEN_INPUT_ADAPTER] =
362 : : (ExtensionDescription){
363 : 1 : VALENT_TYPE_INPUT_ADAPTER,
364 : : N_("Mouse and Keyboard"),
365 : : "input",
366 : : };
367 : :
368 : 2 : extensions[EXTEN_MEDIA_ADAPTER] =
369 : : (ExtensionDescription){
370 : 1 : VALENT_TYPE_MEDIA_ADAPTER,
371 : : N_("Media Players"),
372 : : "media",
373 : : };
374 : :
375 : 2 : extensions[EXTEN_MIXER_ADAPTER] =
376 : : (ExtensionDescription){
377 : 1 : VALENT_TYPE_MIXER_ADAPTER,
378 : : N_("Volume Control"),
379 : : "mixer",
380 : : };
381 : :
382 : 2 : extensions[EXTEN_NOTIFICATION_ADAPTER] =
383 : : (ExtensionDescription){
384 : 1 : VALENT_TYPE_NOTIFICATIONS_ADAPTER,
385 : : N_("Notifications"),
386 : : "notifications",
387 : : };
388 : :
389 : 2 : extensions[EXTEN_SESSION_ADAPTER] =
390 : : (ExtensionDescription){
391 : 1 : VALENT_TYPE_SESSION_ADAPTER,
392 : : N_("Session Manager"),
393 : : "session",
394 : : };
395 : 1 : }
396 : :
397 : : static void
398 : 2 : valent_preferences_dialog_init (ValentPreferencesDialog *self)
399 : : {
400 : 2 : gtk_widget_init_template (GTK_WIDGET (self));
401 : :
402 : 2 : gtk_list_box_set_sort_func (self->plugin_list, plugin_list_sort, NULL, NULL);
403 : :
404 : 2 : self->pages = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref);
405 : 2 : self->rows = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref);
406 : 2 : }
407 : :
|