Add the humble beginnings for a preferences dialog.
[dhcpcd-ui] / prefs.c
1 /*
2  * dhcpcd-gtk
3  * Copyright 2009 Roy Marples <roy@marples.name>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <dbus/dbus.h>
28
29 #include "dhcpcd-gtk.h"
30 #include "prefs.h"
31
32 static GPtrArray *config;
33 static GtkWidget *blocks, *names, *controls;
34 static GtkWidget *hostname, *fqdn, *clientid, *duid, *arp, *ipv4ll;
35
36 static void
37 free_config(GPtrArray **array)
38 {
39         GPtrArray *a;
40         guint i;
41         GValueArray *c;
42
43         a = *array;
44         if (a == NULL)
45                 return;
46         for (i = 0; i < a->len; i++) {
47                 c = g_ptr_array_index(a, i);
48                 g_value_array_free(c);
49         }
50         g_ptr_array_free(a, TRUE);
51         *array = NULL;
52 }       
53
54 static GPtrArray *
55 read_config(const char *block, const char *name)
56 {
57         GType otype;
58         GError *error;
59         GPtrArray *array;
60
61         error = NULL;
62         otype = dbus_g_type_get_struct("GValueArray",
63             G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
64         otype = dbus_g_type_get_collection("GPtrArray", otype);
65         if (!dbus_g_proxy_call(dbus, "GetConfig", &error,
66                 G_TYPE_STRING, block, G_TYPE_STRING, name, G_TYPE_INVALID,
67                 otype, &array, G_TYPE_INVALID))
68         {
69                 g_critical("GetConfig: %s", error->message);
70                 g_clear_error(&error);
71                 return NULL;
72         }
73         return array;
74 }
75
76 static gboolean
77 get_config(GPtrArray *array, const char *option, const char **value)
78 {
79         guint i;
80         GValueArray *c;
81         GValue *val;
82         const char *str;
83
84         if (array == NULL)
85                 return FALSE;
86         for (i = 0; i < array->len; i++) {
87                 c = g_ptr_array_index(array, i);
88                 val = g_value_array_get_nth(c, 0);
89                 str = g_value_get_string(val);
90                 if (strcmp(str, option) != 0)
91                         continue;
92                 if (value != NULL) {
93                         val = g_value_array_get_nth(c, 1);
94                         str = g_value_get_string(val);
95                         if (*str == '\0')
96                                 *value = NULL;
97                         else
98                                 *value = str;
99                 }
100                 return TRUE;
101         }
102         if (value != NULL)
103                 *value = NULL;
104         return FALSE;
105 }
106
107 static gboolean
108 toggle_generic(gboolean has, const char *val)
109 {
110         return (has && (val == NULL || g_strcmp0(val, "\"\"") == 0));
111 }
112
113 static gboolean
114 toggle_generic_neg(gboolean has, _unused const char *val)
115 {
116         return !has;
117 }
118
119 static gboolean
120 toggle_fqdn(gboolean has, const char *val)
121 {
122         return (has &&
123             (val == NULL ||
124                 g_strcmp0(val, "both") == 0 ||
125                 g_strcmp0(val, "ptr") == 0));
126 }
127
128 static void
129 set_check(GtkToggleButton *button,
130     GPtrArray *global, GPtrArray *conf, const char *name,
131     gboolean (*test)(gboolean, const char *))
132 {
133         const char *val;
134         gboolean has, incons;
135         
136         if (get_config(conf, name, &val)) {
137                 has = TRUE;
138                 incons = FALSE;
139         } else {
140                 has = get_config(global, name, &val);
141                 incons = TRUE;
142         }
143         gtk_toggle_button_set_active(button, test(has, val));
144         gtk_toggle_button_set_inconsistent(button, incons);
145 }
146
147 static void
148 show_config(const char *block, const char *name)
149 {
150         GPtrArray *global;
151
152         if (block || name)
153                 global = read_config(NULL, NULL);
154         else
155                 global = NULL;
156         
157         free_config(&config);
158         config = read_config(block, name);
159         set_check(GTK_TOGGLE_BUTTON(hostname), global, config,
160             "hostname", toggle_generic);
161         set_check(GTK_TOGGLE_BUTTON(fqdn), global, config,
162             "fqdn", toggle_fqdn);
163         set_check(GTK_TOGGLE_BUTTON(clientid), global, config,
164             "clientid", toggle_generic);
165         set_check(GTK_TOGGLE_BUTTON(duid), global, config,
166             "duid", toggle_generic);
167         gtk_widget_set_sensitive(duid,
168             gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(clientid)));
169         set_check(GTK_TOGGLE_BUTTON(arp), global, config,
170             "noarp", toggle_generic_neg);
171         set_check(GTK_TOGGLE_BUTTON(ipv4ll), global, config,
172             "noipv4ll", toggle_generic_neg);
173         gtk_widget_set_sensitive(ipv4ll,
174             gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(arp)));
175         free_config(&global);
176 }
177
178 static char *
179 combo_active_text(GtkWidget *widget)
180 {
181         GtkListStore *store;
182         GtkTreeIter iter;
183         GValue val;
184         char *text;
185
186         store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(widget)));
187         if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter))
188                 return NULL;
189         memset(&val, 0, sizeof(val));
190         gtk_tree_model_get_value(GTK_TREE_MODEL(store), &iter, 1, &val);
191         text = g_strdup(g_value_get_string(&val));
192         g_value_unset(&val);
193         return text;
194 }
195
196 static GSList *
197 list_interfaces(void)
198 {
199         GSList *list, *l;
200         const struct if_msg *ifm;
201
202         list = NULL;
203         for (l = interfaces; l; l = l->next) {
204                 ifm = (const struct if_msg *)l->data;
205                 list = g_slist_append(list, ifm->ifname);
206         }
207         return list;
208 }
209
210 static GSList *
211 list_ssids(void)
212 {
213         GSList *list, *l, *a, *la;
214         const struct if_msg *ifm;
215         const struct if_ap *ifa;
216
217         list = NULL;
218         for (l = interfaces; l; l = l->next) {
219                 ifm = (const struct if_msg *)l->data;
220                 if (!ifm->wireless)
221                         continue;
222                 for (a = ifm->scan_results; a; a = a->next) {
223                         ifa = (const struct if_ap *)a->data;
224                         for (la = list; la; la = la->next)
225                                 if (g_strcmp0((const char *)la->data,
226                                         ifa->ssid) == 0)
227                                         break;
228                         if (la == NULL)
229                                 list = g_slist_append(list, ifa->ssid);
230                 }
231         }
232         return list;
233 }
234
235 static void
236 blocks_on_change(GtkWidget *widget, _unused gpointer data)
237 {
238         GtkListStore *store;
239         GtkTreeIter iter;
240         GError *error;
241         char **list, **lp, *block;
242         const char *iname, *nn;
243         GSList *l, *new_names;
244         GtkIconTheme *it;
245         GdkPixbuf *pb;
246         gint n;
247
248         block = combo_active_text(widget);
249         store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(names)));
250         gtk_list_store_clear(store);
251         if (strcmp(block, "global") == 0) {
252                 g_free(block);
253                 gtk_widget_set_sensitive(names, FALSE);
254                 gtk_widget_set_sensitive(controls, TRUE);
255                 show_config(NULL, NULL);
256                 return;
257         }
258         error = NULL;
259         if (!dbus_g_proxy_call(dbus, "GetConfigBlocks", &error,
260                 G_TYPE_STRING, block, G_TYPE_INVALID,
261                 G_TYPE_STRV, &list, G_TYPE_INVALID))
262         {
263                 g_free(block);
264                 g_warning("GetConfigBlocks: %s", error->message);
265                 g_clear_error(&error);
266                 return;
267         }
268
269         it = gtk_icon_theme_get_default();
270         if (g_strcmp0(block, "interface") == 0)
271                 new_names = list_interfaces();
272         else
273                 new_names = list_ssids();
274
275         n = 0;
276         for (l = new_names; l; l = l->next) {
277                 nn = (const char *)l->data;
278                 for (lp = list; *lp; lp++)
279                         if (g_strcmp0(nn, *lp) == 0)
280                                 break;
281                 if (*lp)
282                         iname = "document-save";
283                 else
284                         iname = "document-new";
285                 pb = gtk_icon_theme_load_icon(it, iname,
286                     GTK_ICON_SIZE_MENU, 0, &error);
287                 gtk_list_store_append(store, &iter);
288                 gtk_list_store_set(store, &iter, 0, pb, 1, nn, -1);
289                 g_object_unref(pb);
290                 n++;
291         }
292
293         for (lp = list; *lp; lp++) {
294                 for (l = new_names; l; l = l->next)
295                         if (g_strcmp0((const char *)l->data, *lp) == 0)
296                                 break;
297                 if (l != NULL)
298                         continue;
299                 pb = gtk_icon_theme_load_icon(it, "document-save",
300                     GTK_ICON_SIZE_MENU, 0, &error);
301                 gtk_list_store_append(store, &iter);
302                 gtk_list_store_set(store, &iter, 0, pb, 1, *lp, -1);
303                 g_object_unref(pb);
304                 n++;
305         }
306         gtk_widget_set_sensitive(names, n);
307         gtk_widget_set_sensitive(controls, FALSE);
308         g_slist_free(new_names);
309         g_strfreev(list);
310         g_free(block);
311 }
312
313 static void
314 names_on_change(GtkWidget *widget, _unused gpointer data)
315 {
316         char *block, *name;
317
318         block = combo_active_text(blocks);
319         name = combo_active_text(widget);
320         gtk_widget_set_sensitive(controls, TRUE);
321         show_config(block, name);
322         g_free(block);
323         g_free(name);
324 }
325
326 static void
327 on_toggle(GtkWidget *widget, gpointer data)
328 {
329         gboolean active;
330
331         gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(widget), FALSE);
332         if (data == NULL)
333                 return;
334         active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
335         gtk_widget_set_sensitive(GTK_WIDGET((GtkWidget *)data), active);
336 }
337
338 gboolean
339 dhcpcd_prefs(void)
340 {
341         GtkWidget *dialog, *hbox, *vbox, *label;
342         gint result;
343         GtkListStore *store;
344         GtkTreeIter iter;
345         GtkCellRenderer *rend;
346         GError *error;
347         GtkIconTheme *it;
348         GdkPixbuf *pb;
349         
350         dialog = gtk_dialog_new_with_buttons("dhcpcd preferences",
351             NULL,
352             GTK_DIALOG_MODAL,
353             GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
354             GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
355             NULL);
356         gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
357         gtk_window_set_icon_name(GTK_WINDOW(dialog), "config-users");
358         gtk_dialog_set_default_response(GTK_DIALOG(dialog),
359             GTK_RESPONSE_ACCEPT);
360
361         hbox = gtk_hbox_new(FALSE, 10);
362         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
363         label = gtk_label_new("Configuration block:");
364         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 3);
365         store = gtk_list_store_new(2, GDK_TYPE_PIXBUF, G_TYPE_STRING);
366         it = gtk_icon_theme_get_default();
367         error = NULL;
368         pb = gtk_icon_theme_load_icon(it, "config-users",
369             GTK_ICON_SIZE_MENU, 0, &error);     
370         gtk_list_store_append(store, &iter);
371         gtk_list_store_set(store, &iter, 0, pb, 1, "global", -1);
372         g_object_unref(pb);
373         pb = gtk_icon_theme_load_icon(it, "network-wired",
374             GTK_ICON_SIZE_MENU, 0, &error);     
375         gtk_list_store_append(store, &iter);
376         gtk_list_store_set(store, &iter, 0, pb, 1, "interface", -1);
377         g_object_unref(pb);
378         pb = gtk_icon_theme_load_icon(it, "network-wireless",
379             GTK_ICON_SIZE_MENU, 0, &error);
380         gtk_list_store_append(store, &iter);
381         gtk_list_store_set(store, &iter, 0, pb, 1, "ssid", -1);
382         g_object_unref(pb);
383         blocks = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
384         rend = gtk_cell_renderer_pixbuf_new();
385         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(blocks), rend, FALSE);
386         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(blocks),
387             rend, "pixbuf", 0);
388         rend = gtk_cell_renderer_text_new();
389         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(blocks), rend, TRUE);
390         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(blocks),
391             rend, "text", 1);
392         gtk_combo_box_set_active(GTK_COMBO_BOX(blocks), 0);
393         gtk_box_pack_start(GTK_BOX(hbox), blocks, FALSE, FALSE, 3);
394         label = gtk_label_new("Block name:");
395         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 3);
396         store = gtk_list_store_new(2, GDK_TYPE_PIXBUF, G_TYPE_STRING);
397         names = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
398         rend = gtk_cell_renderer_pixbuf_new();
399         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(names), rend, FALSE);
400         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(names),
401             rend, "pixbuf", 0);
402         rend = gtk_cell_renderer_text_new();
403         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(names), rend, TRUE);
404         gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(names), rend, "text", 1);
405         gtk_widget_set_sensitive(names, FALSE);
406         gtk_box_pack_start(GTK_BOX(hbox), names, FALSE, FALSE, 3);
407         gtk_signal_connect(GTK_OBJECT(blocks), "changed",
408             G_CALLBACK(blocks_on_change), NULL);
409         gtk_signal_connect(GTK_OBJECT(names), "changed",
410             G_CALLBACK(names_on_change), NULL);
411         
412         vbox = GTK_DIALOG(dialog)->vbox;
413         label = gtk_hseparator_new();
414         gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, FALSE, 3);
415         controls = gtk_hbox_new(FALSE, 10);
416         gtk_box_pack_start(GTK_BOX(vbox), controls, TRUE, TRUE, 0);
417         vbox = gtk_vbox_new(FALSE, 3);
418         gtk_box_pack_start(GTK_BOX(controls), vbox, FALSE, FALSE, 0);
419         hostname = gtk_check_button_new_with_label(_("Send Hostname"));
420         gtk_signal_connect(GTK_OBJECT(hostname), "toggled",
421             G_CALLBACK(on_toggle), NULL);
422         gtk_box_pack_start(GTK_BOX(vbox), hostname, FALSE, FALSE, 3);
423         fqdn = gtk_check_button_new_with_label(_("Send FQDN"));
424         gtk_signal_connect(GTK_OBJECT(fqdn), "toggled",
425             G_CALLBACK(on_toggle), NULL);
426         gtk_box_pack_start(GTK_BOX(vbox), fqdn, FALSE, FALSE, 3);
427         clientid = gtk_check_button_new_with_label(_("Send ClientID"));
428         gtk_box_pack_start(GTK_BOX(vbox), clientid, FALSE, FALSE, 3);
429         duid = gtk_check_button_new_with_label(_("Send DUID"));
430         gtk_signal_connect(GTK_OBJECT(clientid), "toggled",
431             G_CALLBACK(on_toggle), duid);
432         gtk_signal_connect(GTK_OBJECT(duid), "toggled",
433             G_CALLBACK(on_toggle), NULL);
434         gtk_box_pack_start(GTK_BOX(vbox), duid, FALSE, FALSE, 3);
435         arp = gtk_check_button_new_with_label(_("Enable ARP checking"));
436         gtk_box_pack_start(GTK_BOX(vbox), arp, FALSE, FALSE, 3);
437         ipv4ll = gtk_check_button_new_with_label(_("Enable Zeroconf"));
438         gtk_box_pack_start(GTK_BOX(vbox), ipv4ll, FALSE, FALSE, 3);
439         gtk_signal_connect(GTK_OBJECT(arp), "toggled",
440             G_CALLBACK(on_toggle), ipv4ll);
441         gtk_signal_connect(GTK_OBJECT(ipv4ll), "toggled",
442             G_CALLBACK(on_toggle), NULL);
443
444         show_config(NULL, NULL);
445         gtk_widget_show_all(dialog);
446         result = gtk_dialog_run(GTK_DIALOG(dialog));
447         
448         gtk_widget_destroy(dialog);
449         free_config(&config);
450         return TRUE;
451 }