Fix bug#5020 - Unable to save a preset called "ontheroead (vibration)".
[tweakr] / modules / tweakr-profile.c
1 /*
2  * vim:ts=4:sw=4:et:cindent:cino=(0
3  */ 
4
5 #include <config.h>
6 #include <glib.h>
7 #include <glib/gi18n-lib.h>
8
9 #include <gtk/gtk.h>
10 #include <hildon/hildon-picker-button.h>
11 #include <hildon/hildon-touch-selector.h>
12 #include <hildon/hildon-entry.h>
13 #include <hildon/hildon-defines.h>
14 #include <hildon/hildon-banner.h>
15 #include <hildon/hildon-pannable-area.h>
16
17 #include <gconf/gconf-client.h>
18
19 #include <libprofile.h>
20
21 #include "libtweakr-section/tweakr-section.h"
22 #include "libtweakr-section/tweakr-module.h"
23
24
25 #define TWEAKR_TYPE_PROFILE_SECTION \
26         (tweakr_profile_section_type)
27 #define TWEAKR_PROFILE_SECTION(obj) \
28         (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
29         TWEAKR_TYPE_PROFILE_SECTION, \
30         TweakrProfileSection))
31 #define TWEAKR_PROFILE_SECTION_CLASS(k) \
32         (G_TYPE_CHECK_CLASS_CAST((k), \
33         TWEAKR_TYPE_PROFILE_SECTION, \
34         TweakrProfileSectionClass))
35 #define TWEAKR_IS_PROFILE_SECTION(obj) \
36         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
37         TWEAKR_TYPE_PROFILE_SECTION))
38
39 #define GCONF_PATH "/system/tweakr"
40
41 typedef struct _TweakrProfileSection TweakrProfileSection;
42 typedef struct _TweakrProfileSectionClass
43                TweakrProfileSectionClass;
44
45 struct _TweakrProfileSection
46 {
47     TweakrSection parent_instance;
48
49     GtkWidget *preset_button;
50     GtkWidget *delete_button;
51     GtkWidget *delete_dialog;
52     GtkWidget *delete_box;
53     GConfClient *gconf;
54 };
55
56 struct _TweakrProfileSectionClass
57 {
58     TweakrSectionClass parent_class;
59 };
60
61
62 static GType tweakr_profile_section_get_type (GTypeModule *module);
63 static void tweakr_profile_section_class_init
64     (TweakrProfileSectionClass *class);
65 static void tweakr_profile_section_init
66     (TweakrProfileSection *section);
67 static void tweakr_profile_section_dispose (GObject *obj);
68
69 static gboolean _save (TweakrSection *section,
70                        gboolean *requires_restart);
71
72 static GType tweakr_profile_section_type = 0;
73 static TweakrSectionClass *
74     tweakr_profile_section_parent_class = NULL;
75
76
77 G_MODULE_EXPORT void
78 tweakr_module_load (TweakrModule *module)
79 {
80     tweakr_profile_section_get_type (G_TYPE_MODULE (module));
81 }
82
83 G_MODULE_EXPORT void
84 tweakr_module_unload (TweakrModule *module)
85 {
86 }
87
88 static GType
89 tweakr_profile_section_get_type (GTypeModule *module)
90 {
91     if (!tweakr_profile_section_type)
92     {
93         static const GTypeInfo section_info =
94         {
95             sizeof (TweakrProfileSectionClass),
96             (GBaseInitFunc) NULL,
97             (GBaseFinalizeFunc) NULL,
98             (GClassInitFunc) tweakr_profile_section_class_init,
99             NULL,           /* class_finalize */
100             NULL,           /* class_data     */
101             sizeof (TweakrProfileSection),
102             0,              /* n_preallocs    */
103             (GInstanceInitFunc) tweakr_profile_section_init
104         };
105
106         tweakr_profile_section_type =
107             g_type_module_register_type (module, TWEAKR_TYPE_SECTION,
108                                          "TweakrProfileSection",
109                                          &section_info, 0);
110     }
111
112     return tweakr_profile_section_type;
113 }
114
115 static void
116 tweakr_profile_section_class_init
117     (TweakrProfileSectionClass *klass)
118 {
119     GObjectClass *object_class = G_OBJECT_CLASS (klass);
120     TweakrSectionClass *section_class =
121         TWEAKR_SECTION_CLASS (klass);
122
123     tweakr_profile_section_parent_class =
124         g_type_class_peek_parent (klass);
125
126     section_class->name   = "_Profile";
127     section_class->save = _save;
128
129     object_class->dispose = tweakr_profile_section_dispose;
130 }
131
132 static void
133 _save_preset_clicked (HildonPickerButton *button,
134                       TweakrProfileSection *section)
135 {
136     GtkWindow *parent;
137     GtkWidget *dialog, *entry;
138     const gchar* text = NULL;
139
140     parent = GTK_WINDOW (gtk_widget_get_ancestor (tweakr_section_get_widget
141                                                   (TWEAKR_SECTION (section)),
142                                                   GTK_TYPE_WINDOW));
143     dialog = gtk_dialog_new_with_buttons
144         (_("Preset name"),
145          parent,
146          GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR,
147          GTK_STOCK_OK, GTK_RESPONSE_OK,
148          NULL);
149
150     entry = hildon_entry_new (HILDON_SIZE_FINGER_HEIGHT);
151     gtk_widget_show (entry);
152
153     gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), entry,
154                         TRUE, TRUE, 0);
155     while (TRUE)
156     {
157         gint ret;
158         gint iter;
159
160         ret = gtk_dialog_run (GTK_DIALOG (dialog));
161         if (ret == GTK_RESPONSE_OK)
162         {
163             GtkWidget *banner;
164             profileval_t *values, *values_iter;
165             GConfClient *gconf;
166             gboolean cont = FALSE;
167
168             text = hildon_entry_get_text (HILDON_ENTRY (entry));
169             if (text == NULL || text[0] == '\0')
170             {
171                 GtkWidget *banner;
172
173                 banner = hildon_banner_show_information
174                     (dialog, NULL, _("Enter the name first."));
175
176                 continue;
177             }
178
179             for (iter = 0; iter < strlen (text); iter++)
180             {
181                 if (!g_ascii_isalnum (text[iter]) &&
182                     text[iter] != '-' &&
183                     text[iter] != '_')
184                 {
185                     GtkWidget *banner;
186
187                     banner = hildon_banner_show_information
188                         (dialog, NULL,
189                          _("Preset name can only contains letters, numbers, - and _."));
190
191                     cont = TRUE;
192                 }
193             }
194
195             if (cont)
196                 continue;
197
198             /* Save the settings to our own gconf directory. */
199
200             gconf = gconf_client_get_default ();
201             gconf_client_set_string (gconf, GCONF_PATH "/current-preset",
202                                      _("None"), NULL);
203             values = profile_get_values ("general");
204             for (values_iter = values;
205                  values_iter->pv_key != NULL;
206                  values_iter++)
207             {
208                 gchar key[128];
209
210                 g_snprintf (key, 128, "%s/%s/%s", GCONF_PATH, text,
211                             values_iter->pv_key);
212                 gconf_client_set_string (gconf, key, values_iter->pv_val,
213                                          NULL);
214             }
215             profile_free_values (values);
216             g_object_unref (gconf);
217
218             banner = hildon_banner_show_information
219                 (GTK_WIDGET (parent), NULL,
220                  _("Preset saved. Use the status menu to select it."));
221         }
222         else
223         {
224             gtk_widget_destroy (dialog);
225             break;
226         }
227         gtk_widget_destroy (dialog);
228     }
229 }
230
231 static GtkWidget *
232 _build_save_preset_button (TweakrProfileSection *section)
233 {
234     GtkWidget *button;
235
236     button = hildon_button_new (HILDON_SIZE_AUTO |
237                                 HILDON_SIZE_FINGER_HEIGHT,
238                                 HILDON_BUTTON_ARRANGEMENT_VERTICAL);
239     hildon_button_set_title (HILDON_BUTTON (button),
240                              _("Save current General profile to new preset"));
241     gtk_button_set_alignment (GTK_BUTTON (button), 0.0f, 0.5f);
242
243     g_signal_connect (G_OBJECT (button), "clicked",
244                       G_CALLBACK (_save_preset_clicked), section);
245
246     gtk_widget_show (button);
247     return button;
248 }
249
250 static void
251 _preset_clicked (HildonButton *button, TweakrProfileSection *section)
252 {
253     const gchar *path = g_object_get_data (G_OBJECT (button), "path");
254     gchar *basename;
255     gchar *current;
256
257     current = gconf_client_get_string (section->gconf,
258                                        GCONF_PATH "/current-preset",
259                                        NULL);
260     basename = g_path_get_basename (path);
261     if (g_strcmp0 (current, basename) == 0)
262     {
263         gconf_client_set_string (section->gconf,
264                                  GCONF_PATH "/current-preset", _("None"),
265                                  NULL);
266     }
267
268     gconf_client_recursive_unset (section->gconf, path, 0, NULL);
269     g_free (basename);
270     g_free (current);
271
272     gtk_dialog_response (GTK_DIALOG (section->delete_dialog),
273                          GTK_RESPONSE_OK);
274     hildon_banner_show_information (NULL, NULL,
275                                     _("Preset deleted."));
276 }
277
278 static void
279 _add_preset (gchar *preset, TweakrProfileSection *section)
280 {
281     GtkWidget *button;
282     gchar *basename;
283
284     basename = g_path_get_basename (preset);
285     button = hildon_button_new_with_text
286         (HILDON_SIZE_FINGER_HEIGHT, HILDON_BUTTON_ARRANGEMENT_VERTICAL,
287          basename, NULL);
288     gtk_button_set_alignment (GTK_BUTTON (button), 0.0f, 0.5f);
289     gtk_box_pack_start (GTK_BOX (section->delete_box), button, FALSE, FALSE,
290                         0);
291     g_object_set_data_full (G_OBJECT (button), "path", preset,
292                             (GDestroyNotify) g_free);
293     g_signal_connect (button, "clicked", G_CALLBACK (_preset_clicked),
294                       section);
295 }
296
297 static void
298 _delete_preset_clicked (HildonButton *b, TweakrProfileSection *section)
299 {
300     GtkWidget *panarea;
301     gint ret;
302     GSList *presets;
303     GtkWidget *parent;
304
305     parent = gtk_widget_get_ancestor (GTK_WIDGET (b), GTK_TYPE_WINDOW);
306
307     section->delete_dialog = gtk_dialog_new ();
308     gtk_window_set_modal (GTK_WINDOW (section->delete_dialog), TRUE);
309     gtk_window_set_title (GTK_WINDOW (section->delete_dialog),
310                           _("Select preset"));
311
312     panarea = hildon_pannable_area_new ();
313     section->delete_box = gtk_vbox_new (FALSE, 0);
314
315     hildon_pannable_area_add_with_viewport (HILDON_PANNABLE_AREA (panarea),
316                                             section->delete_box);
317     gtk_box_pack_start (GTK_BOX (GTK_DIALOG (section->delete_dialog)->vbox),
318                         panarea, TRUE, TRUE, 0);
319
320     if (section->gconf == NULL)
321     {
322         section->gconf = gconf_client_get_default ();
323     }
324     presets = gconf_client_all_dirs (section->gconf, GCONF_PATH, NULL);
325
326     g_object_set (G_OBJECT (panarea), "height-request",
327                   MIN (350, g_slist_length (presets) * 70), NULL);
328
329     g_slist_foreach (presets, (GFunc) _add_preset, section);
330
331     gtk_widget_show_all (GTK_DIALOG (section->delete_dialog)->vbox);
332     ret = gtk_dialog_run (GTK_DIALOG (section->delete_dialog));
333     gtk_widget_destroy (section->delete_dialog);
334
335     g_slist_free (presets);
336 }
337
338 static GtkWidget *
339 _build_delete_preset_button (TweakrProfileSection *section)
340 {
341     GtkWidget *button;
342
343     button = hildon_button_new (HILDON_SIZE_AUTO |
344                                 HILDON_SIZE_FINGER_HEIGHT,
345                                 HILDON_BUTTON_ARRANGEMENT_VERTICAL);
346     hildon_button_set_title (HILDON_BUTTON (button),
347                              _("Delete a profile preset"));
348     gtk_button_set_alignment (GTK_BUTTON (button), 0.0f, 0.5f);
349
350     g_signal_connect (G_OBJECT (button), "clicked",
351                       G_CALLBACK (_delete_preset_clicked), section);
352
353     gtk_widget_show (button);
354     return button;
355 }
356
357 static void
358 tweakr_profile_section_init (TweakrProfileSection *section)
359 {
360     TweakrSection *iface;
361
362     iface = TWEAKR_SECTION (section);
363     iface->name = _("Profile presets");
364     iface->widget = gtk_vbox_new (FALSE, 0);
365
366     section->preset_button = _build_save_preset_button (section);
367     section->delete_button = _build_delete_preset_button (section);
368
369     gtk_box_pack_start (GTK_BOX (iface->widget), section->preset_button,
370                         FALSE, FALSE, 0);
371     gtk_box_pack_start (GTK_BOX (iface->widget), section->delete_button,
372                         FALSE, FALSE, 0);
373 }
374
375 static void
376 tweakr_profile_section_dispose (GObject *obj)
377 {
378     TweakrProfileSection *section = TWEAKR_PROFILE_SECTION (obj);
379
380     if (section->gconf != NULL)
381     {
382         g_object_unref (section->gconf);
383         section->gconf = NULL;
384     }
385
386     G_OBJECT_CLASS (tweakr_profile_section_parent_class)->dispose (obj);
387 }
388
389 static gboolean _save (TweakrSection *section, gboolean *requires_restart)
390 {
391     return TRUE;
392 }
393