2591e84456415923798157a6311259976dc618dd
[browser-switch] / config-ui / browser-switchboard-cp.c
1 /*
2  * browser-switchboard-cp.c -- a hildon-control-panel applet for
3  * selecting the default browser for Browser Switchboard
4  * 
5  * Copyright (C) 2009 Steven Luo
6  * 
7  * Derived from services-cp.c from maemo-control-services
8  * Copyright (c) 2008 Janne Kataja <janne.kataja@iki.fi>
9  * Copyright (c) 2008 Nokia Corporation
10  * 
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2, or (at your option)
14  * any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
24  * USA.
25  */
26
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <glib.h>
34 #include <glib/gstdio.h>
35 #include <gtk/gtk.h>
36 #ifdef HILDON
37 #include <hildon/hildon-banner.h>
38 #include <hildon/hildon-program.h>
39 #ifdef HILDON_CP_APPLET
40 #include <hildon-cp-plugin/hildon-cp-plugin-interface.h>
41 #endif
42 #endif
43
44 #include "configfile.h"
45
46 #define CONTINUOUS_MODE_DEFAULT 0
47
48 struct browser_entry {
49         char *config;
50         char *displayname;
51 };
52 struct browser_entry browsers[] = {
53         { "microb", "MicroB" }, /* First entry is the default! */
54         { "tear", "Tear" },
55         { "fennec", "Mobile Firefox (Fennec)" },
56         { "midori", "Midori" },
57         { "other", "Other" },
58         { NULL, NULL },
59 };
60
61 struct config_widgets {
62         GtkWidget *continuous_mode_off_radio;
63         GtkWidget *continuous_mode_on_radio;
64         GtkWidget *default_browser_combo;
65         GtkWidget *other_browser_cmd_entry;
66         GtkWidget *other_browser_cmd_entry_label;
67 };
68 struct config_widgets cw;
69 GtkWidget *dialog;
70
71
72 /**********************************************************************
73  * Configuration routines
74  **********************************************************************/
75
76 static inline int get_continuous_mode(void) {
77         return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.continuous_mode_on_radio));
78 }
79 static inline void set_continuous_mode(int state) {
80         if (state)
81                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.continuous_mode_on_radio), TRUE);
82         else
83                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.continuous_mode_off_radio), TRUE);
84 }
85
86 static inline char *get_default_browser(void) {
87         return browsers[gtk_combo_box_get_active(GTK_COMBO_BOX(cw.default_browser_combo))].config;
88 }
89 static void set_default_browser(char *browser) {
90         gint i;
91
92         /* Loop through browsers looking for a match */
93         for (i = 0; browsers[i].config && strcmp(browsers[i].config, browser);
94                         ++i);
95
96         if (!browsers[i].config)
97                 /* No match found, set to the default browser */
98                 i = 0;
99
100         gtk_combo_box_set_active(GTK_COMBO_BOX(cw.default_browser_combo), i);
101 }
102
103 static inline char *get_other_browser_cmd(void) {
104         return (char *)gtk_entry_get_text(GTK_ENTRY(cw.other_browser_cmd_entry));
105 }
106 static inline void set_other_browser_cmd(char *cmd) {
107         gtk_entry_set_text(GTK_ENTRY(cw.other_browser_cmd_entry), cmd);
108 }
109
110 static void load_config(void) {
111         FILE *fp;
112         int continuous_mode_seen = 0;
113         int default_browser_seen = 0;
114         int other_browser_cmd_seen = 0;
115         struct swb_config_line line;
116
117         if (!(fp = open_config_file()))
118                 return;
119
120         /* Parse the config file
121            TODO: should we handle errors differently than EOF? */
122         if (!parse_config_file_begin())
123                 goto out;
124         while (!parse_config_file_line(fp, &line)) {
125                 if (line.parsed) {
126                         if (!strcmp(line.key, "continuous_mode")) {
127                                 if (!continuous_mode_seen) {
128                                         set_continuous_mode(atoi(line.value));
129                                         continuous_mode_seen = 1;
130                                 }
131                         } else if (!strcmp(line.key, "default_browser")) {
132                                 if (!default_browser_seen) {
133                                         set_default_browser(line.value);
134                                         default_browser_seen = 1;
135                                 }
136                         } else if (!strcmp(line.key, "other_browser_cmd")) {
137                                 if (!other_browser_cmd_seen) {
138                                         set_other_browser_cmd(line.value);
139                                         other_browser_cmd_seen = 1;
140                                 }
141                         }
142                 }
143                 free(line.key);
144                 free(line.value);
145         }
146         parse_config_file_end();
147
148 out:
149         fclose(fp);
150         return;
151 }
152
153 static void save_config(void) {
154         FILE *fp = NULL, *tmpfp = NULL;
155         char *homedir, *tempfile, *newfile;
156         size_t len;
157         int continuous_mode_seen = 0;
158         int default_browser_seen = 0;
159         int other_browser_cmd_seen = 0;
160         struct swb_config_line line;
161
162         /* If CONFIGFILE_DIR doesn't exist already, try to create it */
163         if (!(homedir = getenv("HOME")))
164                 homedir = DEFAULT_HOMEDIR;
165         len = strlen(homedir) + strlen(CONFIGFILE_DIR) + 1;
166         if (!(newfile = calloc(len, sizeof(char))))
167                 return;
168         snprintf(newfile, len, "%s%s", homedir, CONFIGFILE_DIR);
169         if (access(newfile, F_OK) == -1 && errno == ENOENT) {
170                 mkdir(newfile, 0750);
171         }
172         free(newfile);
173
174         /* Put together the path to the new config file and the tempfile */
175         len = strlen(homedir) + strlen(CONFIGFILE_LOC) + 1;
176         if (!(newfile = calloc(len, sizeof(char))))
177                 return;
178         /* 4 = strlen(".tmp") */
179         if (!(tempfile = calloc(len+4, sizeof(char)))) {
180                 free(newfile);
181                 return;
182         }
183         snprintf(newfile, len, "%s%s", homedir, CONFIGFILE_LOC);
184         snprintf(tempfile, len+4, "%s%s", newfile, ".tmp");
185
186         /* Open the temporary file for writing */
187         if (!(tmpfp = fopen(tempfile, "w")))
188                 /* TODO: report the error somehow? */
189                 goto out;
190
191         /* Open the old config file, if it exists */
192         if ((fp = open_config_file()) && parse_config_file_begin()) {
193                 /* Copy the old config file over to the new one line by line,
194                    replacing old config values with new ones
195                    TODO: should we handle errors differently than EOF? */
196                 while (!parse_config_file_line(fp, &line)) {
197                         if (line.parsed) {
198                                 /* Is a config line, print the new value here */
199                                 if (!strcmp(line.key, "continuous_mode")) {
200                                         if (!continuous_mode_seen) {
201                                                 fprintf(tmpfp, "%s = %d\n",
202                                                         line.key,
203                                                         get_continuous_mode());
204                                                 continuous_mode_seen = 1;
205                                         }
206                                 } else if (!strcmp(line.key,
207                                                         "default_browser")) {
208                                         if (!default_browser_seen) {
209                                                 fprintf(tmpfp, "%s = \"%s\"\n",
210                                                         line.key,
211                                                         get_default_browser());
212                                                 default_browser_seen = 1;
213                                         }
214                                 } else if (!strcmp(line.key,
215                                                         "other_browser_cmd")) {
216                                         if (!other_browser_cmd_seen &&
217                                             strlen(get_other_browser_cmd())>0) {
218                                                 fprintf(tmpfp, "%s = \"%s\"\n",
219                                                         line.key,
220                                                         get_other_browser_cmd());
221                                                 other_browser_cmd_seen = 1;
222                                         }
223                                 }
224                         } else {
225                                 /* Just copy the old line over */
226                                 fprintf(tmpfp, "%s\n", line.key);
227                         }
228                         free(line.key);
229                         free(line.value);
230                 }
231                 parse_config_file_end();
232         }
233
234         /* If we haven't written them yet, write out the new config values */
235         if (!continuous_mode_seen)
236                 fprintf(tmpfp, "%s = %d\n",
237                         "continuous_mode", get_continuous_mode());
238         if (!default_browser_seen)
239                 fprintf(tmpfp, "%s = \"%s\"\n",
240                         "default_browser", get_default_browser());
241         if (!other_browser_cmd_seen && strlen(get_other_browser_cmd()) > 0)
242                 fprintf(tmpfp, "%s = \"%s\"\n",
243                         "other_browser_cmd", get_other_browser_cmd());
244
245         /* Replace the old config file with the new one */
246         fclose(tmpfp);
247         tmpfp = NULL;
248         rename(tempfile, newfile);
249
250 out:
251         free(newfile);
252         free(tempfile);
253         if (tmpfp)
254                 fclose(tmpfp);
255         if (fp)
256                 fclose(fp);
257         return;
258 }
259
260 /**********************************************************************
261  * Callbacks
262  **********************************************************************/
263
264 static void default_browser_combo_callback(GtkWidget *widget, gpointer data) {
265         if (!strcmp(get_default_browser(), "other")) {
266                 gtk_widget_set_sensitive(cw.other_browser_cmd_entry, TRUE);
267                 gtk_widget_set_sensitive(cw.other_browser_cmd_entry_label, TRUE);
268         } else {
269                 gtk_widget_set_sensitive(cw.other_browser_cmd_entry, FALSE);
270                 gtk_widget_set_sensitive(cw.other_browser_cmd_entry_label, FALSE);
271         }
272 }
273
274 static inline void close_dialog(void) {
275         gtk_dialog_response(GTK_DIALOG(dialog), GTK_RESPONSE_NONE);
276 }
277
278 static void ok_callback(GtkWidget *widget, gpointer data) {
279         save_config();
280         /* TODO: is there any cleanup necessary? */
281         close_dialog();
282 }
283
284 static void cancel_callback(GtkWidget *widget, gpointer data) {
285         /* TODO: is there any cleanup necessary? */
286         close_dialog();
287 }
288
289
290 /**********************************************************************
291  * Interface
292  **********************************************************************/
293
294 static GtkDialog *swb_config_dialog(void) {
295         GtkWidget *dialog_vbox;
296
297         GtkWidget *options_table;
298         GtkWidget *default_browser_combo_label;
299         GtkWidget *continuous_mode_label;
300         int i;
301
302         GtkWidget *action_area;
303         GtkWidget *okbutton, *cancelbutton;
304
305         dialog = gtk_dialog_new();
306         /* Doesn't seem to be necessary?
307            gtk_widget_set_size_request(GTK_WIDGET(dialog), 580, 180); */
308         gtk_window_set_title (GTK_WINDOW(dialog), "Browser Switchboard");
309         gtk_window_set_type_hint (GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
310
311         dialog_vbox = GTK_DIALOG(dialog)->vbox;
312
313         /* Config options */
314         options_table = gtk_table_new(3, 2, FALSE);
315         gtk_table_set_row_spacings(GTK_TABLE(options_table), 5);
316         gtk_box_pack_start(GTK_BOX(dialog_vbox), options_table, FALSE, FALSE, 0);
317
318         cw.default_browser_combo = gtk_combo_box_new_text();
319         for (i = 0; browsers[i].config; ++i)
320                 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.default_browser_combo),
321                                           browsers[i].displayname);
322         gtk_combo_box_set_active(GTK_COMBO_BOX(cw.default_browser_combo), 0);
323         default_browser_combo_label = gtk_label_new("Default browser:");
324         gtk_misc_set_alignment(GTK_MISC(default_browser_combo_label), 1, 0.5);
325         g_signal_connect(G_OBJECT(cw.default_browser_combo), "changed",
326                          G_CALLBACK(default_browser_combo_callback), NULL);
327         gtk_table_attach(GTK_TABLE(options_table),
328                         default_browser_combo_label,
329                         0, 1,
330                         0, 1,
331                         GTK_FILL, GTK_FILL|GTK_EXPAND,
332                         5, 0);
333         gtk_table_attach(GTK_TABLE(options_table),
334                         cw.default_browser_combo,
335                         1, 2,
336                         0, 1,
337                         GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND,
338                         5, 0);
339
340         cw.other_browser_cmd_entry = gtk_entry_new();
341         cw.other_browser_cmd_entry_label = gtk_label_new("Command (%s for URI):");
342         gtk_misc_set_alignment(GTK_MISC(cw.other_browser_cmd_entry_label), 1, 0.5);
343         gtk_widget_set_sensitive(cw.other_browser_cmd_entry, FALSE);
344         gtk_widget_set_sensitive(cw.other_browser_cmd_entry_label, FALSE);
345         gtk_table_attach(GTK_TABLE(options_table),
346                         cw.other_browser_cmd_entry_label,
347                         0, 1,
348                         1, 2,
349                         GTK_FILL, GTK_FILL|GTK_EXPAND,
350                         5, 0);
351         gtk_table_attach(GTK_TABLE(options_table),
352                         cw.other_browser_cmd_entry,
353                         1, 2,
354                         1, 2,
355                         GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND,
356                         5, 0);
357         gtk_table_set_row_spacing(GTK_TABLE(options_table), 1, 15);
358
359         continuous_mode_label = gtk_label_new("Optimize Browser Switchboard for:");
360         gtk_misc_set_alignment(GTK_MISC(continuous_mode_label), 0, 0.5);
361         cw.continuous_mode_off_radio = gtk_radio_button_new_with_label(NULL,
362                         "Lower memory usage");
363         cw.continuous_mode_on_radio = gtk_radio_button_new_with_label_from_widget(
364                         GTK_RADIO_BUTTON(cw.continuous_mode_off_radio),
365                         "Faster browser startup time");
366         set_continuous_mode(CONTINUOUS_MODE_DEFAULT);
367         gtk_table_attach(GTK_TABLE(options_table),
368                         continuous_mode_label,
369                         0, 2,
370                         2, 3,
371                         GTK_FILL, GTK_FILL|GTK_EXPAND,
372                         5, 0);
373         gtk_table_attach(GTK_TABLE(options_table),
374                         cw.continuous_mode_off_radio,
375                         0, 2,
376                         3, 4,
377                         GTK_FILL, GTK_FILL|GTK_EXPAND,
378                         20, 0);
379         gtk_table_attach(GTK_TABLE(options_table),
380                         cw.continuous_mode_on_radio,
381                         0, 2,
382                         4, 5,
383                         GTK_FILL, GTK_FILL|GTK_EXPAND,
384                         20, 5);
385         gtk_table_set_row_spacing(GTK_TABLE(options_table), 3, 0);
386
387
388         /* Dialog buttons */
389         action_area = GTK_DIALOG(dialog)->action_area;
390
391         okbutton = gtk_button_new_from_stock(GTK_STOCK_OK);
392         g_signal_connect(G_OBJECT(okbutton), "clicked",
393                          G_CALLBACK(ok_callback), NULL);
394         gtk_box_pack_start(GTK_BOX(action_area), okbutton, FALSE, FALSE, 0);
395
396         cancelbutton = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
397         g_signal_connect(G_OBJECT(cancelbutton), "clicked",
398                          G_CALLBACK(cancel_callback), NULL);
399         gtk_box_pack_start(GTK_BOX(action_area), cancelbutton, FALSE, FALSE, 0);
400
401         gtk_widget_show_all(dialog);
402         return GTK_DIALOG(dialog);
403 }
404
405
406 /**********************************************************************
407  * Entry
408  **********************************************************************/
409
410 #ifdef HILDON_CP_APPLET
411 /*
412  * Application was started from control panel.
413  */
414 osso_return_t execute(osso_context_t *osso,
415                       gpointer userdata, gboolean user_activated) {
416         HildonProgram *program;
417         GtkDialog *dialog;
418
419         program = HILDON_PROGRAM(hildon_program_get_instance());
420         if (osso == NULL)
421                 return OSSO_ERROR;
422
423         dialog = GTK_DIALOG(swb_config_dialog());
424         load_config();
425         gtk_dialog_run(dialog);
426         gtk_widget_destroy(GTK_WIDGET(dialog));
427
428         return OSSO_OK;
429 }
430 #else
431 /*
432  * Application was started from command line.
433  */
434 int main(int argc, char *argv[]) {
435         GtkDialog *dialog;
436 #ifdef HILDON
437         HildonProgram *program = NULL;
438 #endif
439
440         gtk_init(&argc, &argv);
441 #ifdef HILDON
442         program = HILDON_PROGRAM(hildon_program_get_instance());
443 #endif
444
445         g_set_application_name("Browser Switchboard");
446
447         dialog = GTK_DIALOG(swb_config_dialog());
448         load_config();
449         gtk_dialog_run(dialog);
450         gtk_widget_destroy(GTK_WIDGET(dialog));
451
452         exit(0);
453 }
454 #endif