Add some padding at sides and bottom of the window
[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 struct browser_entry {
47         char *config;
48         char *displayname;
49 };
50 struct browser_entry browsers[] = {
51         { "microb", "MicroB" }, /* First entry is the default! */
52         { "tear", "Tear" },
53         { "fennec", "Mobile Firefox (Fennec)" },
54         { "midori", "Midori" },
55         { "other", "Other" },
56         { NULL, NULL },
57 };
58
59 struct config_widgets {
60         GtkWidget *continuous_mode_check;
61         GtkWidget *default_browser_combo;
62         GtkWidget *other_browser_cmd_entry;
63         GtkWidget *other_browser_cmd_entry_label;
64 };
65 struct config_widgets cw;
66 GtkWidget *dialog;
67
68
69 /**********************************************************************
70  * Configuration routines
71  **********************************************************************/
72
73 static inline int get_continuous_mode(void) {
74         return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.continuous_mode_check));
75 }
76 static inline void set_continuous_mode(int state) {
77         return gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.continuous_mode_check), (gboolean)state);
78 }
79
80 static inline char *get_default_browser(void) {
81         return browsers[gtk_combo_box_get_active(GTK_COMBO_BOX(cw.default_browser_combo))].config;
82 }
83 static void set_default_browser(char *browser) {
84         gint i;
85
86         /* Loop through browsers looking for a match */
87         for (i = 0; browsers[i].config && strcmp(browsers[i].config, browser);
88                         ++i);
89
90         if (!browsers[i].config)
91                 /* No match found, set to the default browser */
92                 i = 0;
93
94         gtk_combo_box_set_active(GTK_COMBO_BOX(cw.default_browser_combo), i);
95 }
96
97 static inline char *get_other_browser_cmd(void) {
98         return (char *)gtk_entry_get_text(GTK_ENTRY(cw.other_browser_cmd_entry));
99 }
100 static inline void set_other_browser_cmd(char *cmd) {
101         gtk_entry_set_text(GTK_ENTRY(cw.other_browser_cmd_entry), cmd);
102 }
103
104 static void load_config(void) {
105         FILE *fp;
106         int continuous_mode_seen = 0;
107         int default_browser_seen = 0;
108         int other_browser_cmd_seen = 0;
109         struct swb_config_line line;
110
111         if (!(fp = open_config_file()))
112                 return;
113
114         /* Parse the config file
115            TODO: should we handle errors differently than EOF? */
116         if (!parse_config_file_begin())
117                 goto out;
118         while (!parse_config_file_line(fp, &line)) {
119                 if (line.parsed) {
120                         if (!strcmp(line.key, "continuous_mode")) {
121                                 if (!continuous_mode_seen) {
122                                         set_continuous_mode(atoi(line.value));
123                                         continuous_mode_seen = 1;
124                                 }
125                         } else if (!strcmp(line.key, "default_browser")) {
126                                 if (!default_browser_seen) {
127                                         set_default_browser(line.value);
128                                         default_browser_seen = 1;
129                                 }
130                         } else if (!strcmp(line.key, "other_browser_cmd")) {
131                                 if (!other_browser_cmd_seen) {
132                                         set_other_browser_cmd(line.value);
133                                         other_browser_cmd_seen = 1;
134                                 }
135                         }
136                 }
137                 free(line.key);
138                 free(line.value);
139         }
140         parse_config_file_end();
141
142 out:
143         fclose(fp);
144         return;
145 }
146
147 static void save_config(void) {
148         FILE *fp = NULL, *tmpfp = NULL;
149         char *homedir, *tempfile, *newfile;
150         size_t len;
151         int continuous_mode_seen = 0;
152         int default_browser_seen = 0;
153         int other_browser_cmd_seen = 0;
154         struct swb_config_line line;
155
156         /* If CONFIGFILE_DIR doesn't exist already, try to create it */
157         if (!(homedir = getenv("HOME")))
158                 homedir = DEFAULT_HOMEDIR;
159         len = strlen(homedir) + strlen(CONFIGFILE_DIR) + 1;
160         if (!(newfile = calloc(len, sizeof(char))))
161                 return;
162         snprintf(newfile, len, "%s%s", homedir, CONFIGFILE_DIR);
163         if (access(newfile, F_OK) == -1 && errno == ENOENT) {
164                 mkdir(newfile, 0750);
165         }
166         free(newfile);
167
168         /* Put together the path to the new config file and the tempfile */
169         len = strlen(homedir) + strlen(CONFIGFILE_LOC) + 1;
170         if (!(newfile = calloc(len, sizeof(char))))
171                 return;
172         /* 4 = strlen(".tmp") */
173         if (!(tempfile = calloc(len+4, sizeof(char)))) {
174                 free(newfile);
175                 return;
176         }
177         snprintf(newfile, len, "%s%s", homedir, CONFIGFILE_LOC);
178         snprintf(tempfile, len+4, "%s%s", newfile, ".tmp");
179
180         /* Open the temporary file for writing */
181         if (!(tmpfp = fopen(tempfile, "w")))
182                 /* TODO: report the error somehow? */
183                 goto out;
184
185         /* Open the old config file, if it exists */
186         if ((fp = open_config_file()) && parse_config_file_begin()) {
187                 /* Copy the old config file over to the new one line by line,
188                    replacing old config values with new ones
189                    TODO: should we handle errors differently than EOF? */
190                 while (!parse_config_file_line(fp, &line)) {
191                         if (line.parsed) {
192                                 /* Is a config line, print the new value here */
193                                 if (!strcmp(line.key, "continuous_mode")) {
194                                         if (!continuous_mode_seen) {
195                                                 fprintf(tmpfp, "%s = %d\n",
196                                                         line.key,
197                                                         get_continuous_mode());
198                                                 continuous_mode_seen = 1;
199                                         }
200                                 } else if (!strcmp(line.key,
201                                                         "default_browser")) {
202                                         if (!default_browser_seen) {
203                                                 fprintf(tmpfp, "%s = \"%s\"\n",
204                                                         line.key,
205                                                         get_default_browser());
206                                                 default_browser_seen = 1;
207                                         }
208                                 } else if (!strcmp(line.key,
209                                                         "other_browser_cmd")) {
210                                         if (!other_browser_cmd_seen &&
211                                             strlen(get_other_browser_cmd())>0) {
212                                                 fprintf(tmpfp, "%s = \"%s\"\n",
213                                                         line.key,
214                                                         get_other_browser_cmd());
215                                                 other_browser_cmd_seen = 1;
216                                         }
217                                 }
218                         } else {
219                                 /* Just copy the old line over */
220                                 fprintf(tmpfp, "%s\n", line.key);
221                         }
222                         free(line.key);
223                         free(line.value);
224                 }
225                 parse_config_file_end();
226         }
227
228         /* If we haven't written them yet, write out the new config values */
229         if (!continuous_mode_seen)
230                 fprintf(tmpfp, "%s = %d\n",
231                         "continuous_mode", get_continuous_mode());
232         if (!default_browser_seen)
233                 fprintf(tmpfp, "%s = \"%s\"\n",
234                         "default_browser", get_default_browser());
235         if (!other_browser_cmd_seen && strlen(get_other_browser_cmd()) > 0)
236                 fprintf(tmpfp, "%s = \"%s\"\n",
237                         "other_browser_cmd", get_other_browser_cmd());
238
239         /* Replace the old config file with the new one */
240         fclose(tmpfp);
241         tmpfp = NULL;
242         rename(tempfile, newfile);
243
244 out:
245         free(newfile);
246         free(tempfile);
247         if (tmpfp)
248                 fclose(tmpfp);
249         if (fp)
250                 fclose(fp);
251         return;
252 }
253
254 /**********************************************************************
255  * Callbacks
256  **********************************************************************/
257
258 static void default_browser_combo_callback(GtkWidget *widget, gpointer data) {
259         if (!strcmp(get_default_browser(), "other")) {
260                 gtk_widget_set_sensitive(cw.other_browser_cmd_entry, TRUE);
261                 gtk_widget_set_sensitive(cw.other_browser_cmd_entry_label, TRUE);
262         } else {
263                 gtk_widget_set_sensitive(cw.other_browser_cmd_entry, FALSE);
264                 gtk_widget_set_sensitive(cw.other_browser_cmd_entry_label, FALSE);
265         }
266 }
267
268 static inline void close_dialog(void) {
269         gtk_dialog_response(GTK_DIALOG(dialog), GTK_RESPONSE_NONE);
270 }
271
272 static void ok_callback(GtkWidget *widget, gpointer data) {
273         save_config();
274         /* TODO: is there any cleanup necessary? */
275         close_dialog();
276 }
277
278 static void cancel_callback(GtkWidget *widget, gpointer data) {
279         /* TODO: is there any cleanup necessary? */
280         close_dialog();
281 }
282
283
284 /**********************************************************************
285  * Interface
286  **********************************************************************/
287
288 static GtkDialog *swb_config_dialog(void) {
289         GtkWidget *dialog_vbox;
290
291         GtkWidget *options_table;
292         GtkWidget *default_browser_combo_label;
293         int i;
294
295         GtkWidget *action_area;
296         GtkWidget *okbutton, *cancelbutton;
297
298         dialog = gtk_dialog_new();
299         /* Doesn't seem to be necessary?
300            gtk_widget_set_size_request(GTK_WIDGET(dialog), 580, 180); */
301         gtk_window_set_title (GTK_WINDOW(dialog), "Browser Switchboard");
302         gtk_window_set_type_hint (GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
303
304         dialog_vbox = GTK_DIALOG(dialog)->vbox;
305
306         /* Config options */
307         options_table = gtk_table_new(3, 2, FALSE);
308         gtk_table_set_row_spacings(GTK_TABLE(options_table), 10);
309         gtk_box_pack_start(GTK_BOX(dialog_vbox), options_table, FALSE, FALSE, 0);
310
311         cw.default_browser_combo = gtk_combo_box_new_text();
312         for (i = 0; browsers[i].config; ++i)
313                 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.default_browser_combo),
314                                           browsers[i].displayname);
315         gtk_combo_box_set_active(GTK_COMBO_BOX(cw.default_browser_combo), 0);
316         default_browser_combo_label = gtk_label_new("Default browser:");
317         gtk_misc_set_alignment(GTK_MISC(default_browser_combo_label), 1, 0.5);
318         g_signal_connect(G_OBJECT(cw.default_browser_combo), "changed",
319                          G_CALLBACK(default_browser_combo_callback), NULL);
320         gtk_table_attach(GTK_TABLE(options_table),
321                         default_browser_combo_label,
322                         0, 1,
323                         0, 1,
324                         GTK_FILL, GTK_FILL|GTK_EXPAND,
325                         5, 0);
326         gtk_table_attach(GTK_TABLE(options_table),
327                         cw.default_browser_combo,
328                         1, 2,
329                         0, 1,
330                         GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND,
331                         5, 0);
332         gtk_table_set_row_spacing(GTK_TABLE(options_table), 0, 5);
333
334         cw.other_browser_cmd_entry = gtk_entry_new();
335         cw.other_browser_cmd_entry_label = gtk_label_new("Command (%s for URI):");
336         gtk_misc_set_alignment(GTK_MISC(cw.other_browser_cmd_entry_label), 1, 0.5);
337         gtk_widget_set_sensitive(cw.other_browser_cmd_entry, FALSE);
338         gtk_widget_set_sensitive(cw.other_browser_cmd_entry_label, FALSE);
339         gtk_table_attach(GTK_TABLE(options_table),
340                         cw.other_browser_cmd_entry_label,
341                         0, 1,
342                         1, 2,
343                         GTK_FILL, GTK_FILL|GTK_EXPAND,
344                         5, 0);
345         gtk_table_attach(GTK_TABLE(options_table),
346                         cw.other_browser_cmd_entry,
347                         1, 2,
348                         1, 2,
349                         GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND,
350                         5, 0);
351
352         cw.continuous_mode_check = gtk_check_button_new_with_label("Run browser launcher continuously in the background");
353         gtk_table_attach(GTK_TABLE(options_table),
354                         cw.continuous_mode_check,
355                         0, 2,
356                         2, 3,
357                         0, GTK_FILL|GTK_EXPAND,
358                         5, 5);
359
360         /* Dialog buttons */
361         action_area = GTK_DIALOG(dialog)->action_area;
362
363         okbutton = gtk_button_new_from_stock(GTK_STOCK_OK);
364         g_signal_connect(G_OBJECT(okbutton), "clicked",
365                          G_CALLBACK(ok_callback), NULL);
366         gtk_box_pack_start(GTK_BOX(action_area), okbutton, FALSE, FALSE, 0);
367
368         cancelbutton = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
369         g_signal_connect(G_OBJECT(cancelbutton), "clicked",
370                          G_CALLBACK(cancel_callback), NULL);
371         gtk_box_pack_start(GTK_BOX(action_area), cancelbutton, FALSE, FALSE, 0);
372
373         gtk_widget_show_all(dialog);
374         return GTK_DIALOG(dialog);
375 }
376
377
378 /**********************************************************************
379  * Entry
380  **********************************************************************/
381
382 #ifdef HILDON_CP_APPLET
383 /*
384  * Application was started from control panel.
385  */
386 osso_return_t execute(osso_context_t *osso,
387                       gpointer userdata, gboolean user_activated) {
388         HildonProgram *program;
389         GtkDialog *dialog;
390
391         program = HILDON_PROGRAM(hildon_program_get_instance());
392         if (osso == NULL)
393                 return OSSO_ERROR;
394
395         dialog = GTK_DIALOG(swb_config_dialog());
396         load_config();
397         gtk_dialog_run(dialog);
398         gtk_widget_destroy(GTK_WIDGET(dialog));
399
400         return OSSO_OK;
401 }
402 #else
403 /*
404  * Application was started from command line.
405  */
406 int main(int argc, char *argv[]) {
407         GtkDialog *dialog;
408 #ifdef HILDON
409         HildonProgram *program = NULL;
410         program = HILDON_PROGRAM(hildon_program_get_instance());
411 #endif
412
413         gtk_init(&argc, &argv);
414
415         g_set_application_name("Browser Switchboard");
416
417         dialog = GTK_DIALOG(swb_config_dialog());
418         load_config();
419         gtk_dialog_run(dialog);
420         gtk_widget_destroy(GTK_WIDGET(dialog));
421
422         exit(0);
423 }
424 #endif