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