1 /* dzl-preferences-switch.c
2 *
3 * Copyright (C) 2015-2017 Christian Hergert <chergert@redhat.com>
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 3 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
19 #include "config.h"
20
21 #include "util/dzl-util-private.h"
22 #include "prefs/dzl-preferences-switch.h"
23
24 struct _DzlPreferencesSwitch
25 {
26 DzlPreferencesBin parent_instance;
27
28 guint is_radio : 1;
29 guint updating : 1;
30
31 gulong handler;
32
33 gchar *key;
34 GVariant *target;
35 GSettings *settings;
36
37 GtkLabel *subtitle;
38 GtkLabel *title;
39 GtkSwitch *widget;
40 GtkImage *image;
41 };
42
43 G_DEFINE_TYPE (DzlPreferencesSwitch, dzl_preferences_switch, DZL_TYPE_PREFERENCES_BIN)
44
45 enum {
46 PROP_0,
47 PROP_IS_RADIO,
48 PROP_KEY,
49 PROP_SUBTITLE,
50 PROP_TARGET,
51 PROP_TITLE,
52 LAST_PROP
53 };
54
55 enum {
56 ACTIVATED,
57 LAST_SIGNAL
58 };
59
60 static GParamSpec *properties [LAST_PROP];
61 static guint signals [LAST_SIGNAL];
62
63 static void
dzl_preferences_switch_changed(DzlPreferencesSwitch * self,const gchar * key,GSettings * settings)64 dzl_preferences_switch_changed (DzlPreferencesSwitch *self,
65 const gchar *key,
66 GSettings *settings)
67 {
68 GVariant *value;
69 gboolean active = FALSE;
70
71 g_assert (DZL_IS_PREFERENCES_SWITCH (self));
72 g_assert (key != NULL);
73 g_assert (G_IS_SETTINGS (settings));
74
75 if (self->updating == TRUE)
76 return;
77
78 value = g_settings_get_value (settings, key);
79
80 if (g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
81 active = g_variant_get_boolean (value);
82 else if ((self->target != NULL) &&
83 g_variant_is_of_type (value, g_variant_get_type (self->target)))
84 active = g_variant_equal (value, self->target);
85 else if ((self->target != NULL) &&
86 g_variant_is_of_type (self->target, G_VARIANT_TYPE_STRING) &&
87 g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY))
88 {
89 g_autofree const gchar **strv = g_variant_get_strv (value, NULL);
90 const gchar *flag = g_variant_get_string (self->target, NULL);
91 active = g_strv_contains (strv, flag);
92 }
93
94 self->updating = TRUE;
95
96 if (self->is_radio)
97 {
98 gtk_widget_set_visible (GTK_WIDGET (self->image), active);
99 }
100 else
101 {
102 gtk_switch_set_active (self->widget, active);
103 gtk_switch_set_state (self->widget, active);
104 }
105
106 self->updating = FALSE;
107
108 g_variant_unref (value);
109 }
110
111 static void
dzl_preferences_switch_connect(DzlPreferencesBin * bin,GSettings * settings)112 dzl_preferences_switch_connect (DzlPreferencesBin *bin,
113 GSettings *settings)
114 {
115 DzlPreferencesSwitch *self = (DzlPreferencesSwitch *)bin;
116 g_autofree gchar *signal_detail = NULL;
117
118 g_assert (DZL_IS_PREFERENCES_SWITCH (self));
119
120 signal_detail = g_strdup_printf ("changed::%s", self->key);
121
122 self->settings = g_object_ref (settings);
123
124 self->handler =
125 g_signal_connect_object (settings,
126 signal_detail,
127 G_CALLBACK (dzl_preferences_switch_changed),
128 self,
129 G_CONNECT_SWAPPED);
130
131 dzl_preferences_switch_changed (self, self->key, settings);
132 }
133
134 static void
dzl_preferences_switch_disconnect(DzlPreferencesBin * bin,GSettings * settings)135 dzl_preferences_switch_disconnect (DzlPreferencesBin *bin,
136 GSettings *settings)
137 {
138 DzlPreferencesSwitch *self = (DzlPreferencesSwitch *)bin;
139
140 g_assert (DZL_IS_PREFERENCES_SWITCH (self));
141
142 g_signal_handler_disconnect (settings, self->handler);
143 self->handler = 0;
144 }
145
146 static void
dzl_preferences_switch_toggle(DzlPreferencesSwitch * self,gboolean state)147 dzl_preferences_switch_toggle (DzlPreferencesSwitch *self,
148 gboolean state)
149 {
150 GVariant *value;
151
152 g_assert (DZL_IS_PREFERENCES_SWITCH (self));
153
154 if (self->updating)
155 return;
156
157 self->updating = TRUE;
158
159 value = g_settings_get_value (self->settings, self->key);
160
161 if (g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
162 {
163 g_settings_set_boolean (self->settings, self->key, state);
164 }
165 else if ((self->target != NULL) &&
166 g_variant_is_of_type (self->target, G_VARIANT_TYPE_STRING) &&
167 g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY))
168 {
169 g_autofree const gchar **strv = g_variant_get_strv (value, NULL);
170 g_autoptr(GPtrArray) ar = g_ptr_array_new ();
171 const gchar *flag = g_variant_get_string (self->target, NULL);
172 gboolean found = FALSE;
173 gint i;
174
175 for (i = 0; strv [i]; i++)
176 {
177 if (!state && dzl_str_equal0 (strv [i], flag))
178 continue;
179 if (dzl_str_equal0 (strv [i], flag))
180 found = TRUE;
181 g_ptr_array_add (ar, (gchar *)strv [i]);
182 }
183
184 if (state && !found)
185 g_ptr_array_add (ar, (gchar *)flag);
186
187 g_ptr_array_add (ar, NULL);
188
189 g_settings_set_strv (self->settings, self->key, (const gchar * const *)ar->pdata);
190 }
191 else if ((self->target != NULL) &&
192 g_variant_is_of_type (value, g_variant_get_type (self->target)))
193 {
194 g_settings_set_value (self->settings, self->key, self->target);
195 }
196 else
197 {
198 g_warning ("I don't know how to set a variant of type %s to %s",
199 (const gchar *)g_variant_get_type (value),
200 self->target ? (const gchar *)g_variant_get_type (self->target) : "(nil)");
201 }
202
203
204 g_variant_unref (value);
205
206 if (self->is_radio)
207 gtk_widget_set_visible (GTK_WIDGET (self->image), state);
208 else
209 gtk_switch_set_state (self->widget, state);
210
211 self->updating = FALSE;
212
213 /* For good measure, so that we cleanup in the boolean deselection case */
214 dzl_preferences_switch_changed (self, self->key, self->settings);
215 }
216
217 static gboolean
dzl_preferences_switch_state_set(DzlPreferencesSwitch * self,gboolean state,GtkSwitch * widget)218 dzl_preferences_switch_state_set (DzlPreferencesSwitch *self,
219 gboolean state,
220 GtkSwitch *widget)
221 {
222 g_assert (DZL_IS_PREFERENCES_SWITCH (self));
223 g_assert (GTK_IS_SWITCH (widget));
224
225 dzl_preferences_switch_toggle (self, state);
226
227 return TRUE;
228 }
229
230 static void
dzl_preferences_switch_activate(DzlPreferencesSwitch * self)231 dzl_preferences_switch_activate (DzlPreferencesSwitch *self)
232 {
233 g_assert (DZL_IS_PREFERENCES_SWITCH (self));
234
235 if (!gtk_widget_get_sensitive (GTK_WIDGET (self)) || (self->settings == NULL))
236 return;
237
238 if (self->is_radio)
239 {
240 gboolean state;
241
242 state = !gtk_widget_get_visible (GTK_WIDGET (self->image));
243 dzl_preferences_switch_toggle (self, state);
244 }
245 else
246 gtk_widget_activate (GTK_WIDGET (self->widget));
247
248 }
249
250 static gboolean
dzl_preferences_switch_matches(DzlPreferencesBin * bin,DzlPatternSpec * spec)251 dzl_preferences_switch_matches (DzlPreferencesBin *bin,
252 DzlPatternSpec *spec)
253 {
254 DzlPreferencesSwitch *self = (DzlPreferencesSwitch *)bin;
255 const gchar *tmp;
256
257 g_assert (DZL_IS_PREFERENCES_SWITCH (self));
258 g_assert (spec != NULL);
259
260 tmp = gtk_label_get_label (self->title);
261 if (tmp && dzl_pattern_spec_match (spec, tmp))
262 return TRUE;
263
264 tmp = gtk_label_get_label (self->subtitle);
265 if (tmp && dzl_pattern_spec_match (spec, tmp))
266 return TRUE;
267
268 if (self->key && dzl_pattern_spec_match (spec, self->key))
269 return TRUE;
270
271 return FALSE;
272 }
273
274 static void
dzl_preferences_switch_finalize(GObject * object)275 dzl_preferences_switch_finalize (GObject *object)
276 {
277 DzlPreferencesSwitch *self = (DzlPreferencesSwitch *)object;
278
279 g_clear_pointer (&self->key, g_free);
280 g_clear_pointer (&self->target, g_variant_unref);
281 g_clear_object (&self->settings);
282
283 G_OBJECT_CLASS (dzl_preferences_switch_parent_class)->finalize (object);
284 }
285
286 static void
dzl_preferences_switch_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)287 dzl_preferences_switch_get_property (GObject *object,
288 guint prop_id,
289 GValue *value,
290 GParamSpec *pspec)
291 {
292 DzlPreferencesSwitch *self = DZL_PREFERENCES_SWITCH (object);
293
294 switch (prop_id)
295 {
296 case PROP_IS_RADIO:
297 g_value_set_boolean (value, self->is_radio);
298 break;
299
300 case PROP_KEY:
301 g_value_set_string (value, self->key);
302 break;
303
304 case PROP_TARGET:
305 g_value_set_variant (value, self->target);
306 break;
307
308 case PROP_TITLE:
309 g_value_set_string (value, gtk_label_get_label (self->title));
310 break;
311
312 case PROP_SUBTITLE:
313 g_value_set_string (value, gtk_label_get_label (self->subtitle));
314 break;
315
316 default:
317 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
318 }
319 }
320
321 static void
dzl_preferences_switch_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)322 dzl_preferences_switch_set_property (GObject *object,
323 guint prop_id,
324 const GValue *value,
325 GParamSpec *pspec)
326 {
327 DzlPreferencesSwitch *self = DZL_PREFERENCES_SWITCH (object);
328
329 switch (prop_id)
330 {
331 case PROP_IS_RADIO:
332 self->is_radio = g_value_get_boolean (value);
333 gtk_widget_set_visible (GTK_WIDGET (self->widget), !self->is_radio);
334 gtk_widget_set_visible (GTK_WIDGET (self->image), self->is_radio);
335 break;
336
337 case PROP_KEY:
338 self->key = g_value_dup_string (value);
339 break;
340
341 case PROP_TARGET:
342 self->target = g_value_dup_variant (value);
343 break;
344
345 case PROP_TITLE:
346 gtk_label_set_label (self->title, g_value_get_string (value));
347 break;
348
349 case PROP_SUBTITLE:
350 g_object_set (self->subtitle,
351 "label", g_value_get_string (value),
352 "visible", !!g_value_get_string (value),
353 NULL);
354 break;
355
356 default:
357 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
358 }
359 }
360
361 static void
dzl_preferences_switch_class_init(DzlPreferencesSwitchClass * klass)362 dzl_preferences_switch_class_init (DzlPreferencesSwitchClass *klass)
363 {
364 GObjectClass *object_class = G_OBJECT_CLASS (klass);
365 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
366 DzlPreferencesBinClass *bin_class = DZL_PREFERENCES_BIN_CLASS (klass);
367
368 object_class->finalize = dzl_preferences_switch_finalize;
369 object_class->get_property = dzl_preferences_switch_get_property;
370 object_class->set_property = dzl_preferences_switch_set_property;
371
372 bin_class->connect = dzl_preferences_switch_connect;
373 bin_class->disconnect = dzl_preferences_switch_disconnect;
374 bin_class->matches = dzl_preferences_switch_matches;
375
376 signals [ACTIVATED] =
377 g_signal_new_class_handler ("activated",
378 G_TYPE_FROM_CLASS (klass),
379 G_SIGNAL_RUN_LAST,
380 G_CALLBACK (dzl_preferences_switch_activate),
381 NULL, NULL, NULL, G_TYPE_NONE, 0);
382
383 widget_class->activate_signal = signals [ACTIVATED];
384
385 properties [PROP_IS_RADIO] =
386 g_param_spec_boolean ("is-radio",
387 "Is Radio",
388 "If a radio style should be used instead of a switch.",
389 FALSE,
390 (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
391
392
393 properties [PROP_TARGET] =
394 g_param_spec_variant ("target",
395 "Target",
396 "Target",
397 G_VARIANT_TYPE_ANY,
398 NULL,
399 (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
400
401 properties [PROP_KEY] =
402 g_param_spec_string ("key",
403 "Key",
404 "Key",
405 NULL,
406 (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
407
408 properties [PROP_TITLE] =
409 g_param_spec_string ("title",
410 "Title",
411 "Title",
412 NULL,
413 (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
414
415 properties [PROP_SUBTITLE] =
416 g_param_spec_string ("subtitle",
417 "Subtitle",
418 "Subtitle",
419 NULL,
420 (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
421
422 g_object_class_install_properties (object_class, LAST_PROP, properties);
423
424 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/dazzle/ui/dzl-preferences-switch.ui");
425 gtk_widget_class_bind_template_child (widget_class, DzlPreferencesSwitch, image);
426 gtk_widget_class_bind_template_child (widget_class, DzlPreferencesSwitch, subtitle);
427 gtk_widget_class_bind_template_child (widget_class, DzlPreferencesSwitch, title);
428 gtk_widget_class_bind_template_child (widget_class, DzlPreferencesSwitch, widget);
429 }
430
431 static void
dzl_preferences_switch_init(DzlPreferencesSwitch * self)432 dzl_preferences_switch_init (DzlPreferencesSwitch *self)
433 {
434 gtk_widget_init_template (GTK_WIDGET (self));
435
436 g_signal_connect_object (self->widget,
437 "state-set",
438 G_CALLBACK (dzl_preferences_switch_state_set),
439 self,
440 G_CONNECT_SWAPPED);
441 }
442