24480bb2d847c73f172d96b0619da205d50f2548
[claws.git] / src / prefs_gtk.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2002 Hiroyuki Yamamoto
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 2 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, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include <gtk/gtk.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <errno.h>
30
31 #include "intl.h"
32 #include "main.h"
33 #include "prefs.h"
34 #include "prefs_gtk.h"
35 #include "utils.h"
36 #include "gtkutils.h"
37
38 typedef enum
39 {
40         DUMMY_PARAM
41 } DummyEnum;
42
43 void prefs_read_config(PrefParam *param, const gchar *label,
44                        const gchar *rcfile)
45 {
46         FILE *fp;
47         gchar buf[PREFSBUFSIZE];
48         gchar *rcpath;
49         gchar *block_label;
50
51         g_return_if_fail(param != NULL);
52         g_return_if_fail(label != NULL);
53         g_return_if_fail(rcfile != NULL);
54
55         debug_print("Reading configuration...\n");
56
57         prefs_set_default(param);
58
59         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, rcfile, NULL);
60         if ((fp = fopen(rcpath, "rb")) == NULL) {
61                 if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen");
62                 g_free(rcpath);
63                 return;
64         }
65         g_free(rcpath);
66
67         block_label = g_strdup_printf("[%s]", label);
68
69         /* search aiming block */
70         while (fgets(buf, sizeof(buf), fp) != NULL) {
71                 gint val;
72
73                 val = strncmp(buf, block_label, strlen(block_label));
74                 if (val == 0) {
75                         debug_print("Found %s\n", block_label);
76                         break;
77                 }
78         }
79         g_free(block_label);
80
81         while (fgets(buf, sizeof(buf), fp) != NULL) {
82                 strretchomp(buf);
83                 /* reached next block */
84                 if (buf[0] == '[') break;
85
86                 prefs_config_parse_one_line(param, buf);
87         }
88
89         debug_print("Finished reading configuration.\n");
90         fclose(fp);
91 }
92
93 void prefs_config_parse_one_line(PrefParam *param, const gchar *buf)
94 {
95         gint i;
96         gint name_len;
97         const gchar *value;
98
99         for (i = 0; param[i].name != NULL; i++) {
100                 name_len = strlen(param[i].name);
101                 if (strncasecmp(buf, param[i].name, name_len))
102                         continue;
103                 if (buf[name_len] != '=')
104                         continue;
105                 value = buf + name_len + 1;
106                 /* debug_print("%s = %s\n", param[i].name, value); */
107
108                 switch (param[i].type) {
109                 case P_STRING:
110                         g_free(*((gchar **)param[i].data));
111                         *((gchar **)param[i].data) =
112                                 *value ? g_strdup(value) : NULL;
113                         break;
114                 case P_INT:
115                         *((gint *)param[i].data) =
116                                 (gint)atoi(value);
117                         break;
118                 case P_BOOL:
119                         *((gboolean *)param[i].data) =
120                                 (*value == '0' || *value == '\0')
121                                         ? FALSE : TRUE;
122                         break;
123                 case P_ENUM:
124                         *((DummyEnum *)param[i].data) =
125                                 (DummyEnum)atoi(value);
126                         break;
127                 case P_USHORT:
128                         *((gushort *)param[i].data) =
129                                 (gushort)atoi(value);
130                         break;
131                 default:
132                         break;
133                 }
134         }
135 }
136
137 #define TRY(func) \
138 if (!(func)) \
139 { \
140         g_warning("failed to write configuration to file\n"); \
141         if (orig_fp) fclose(orig_fp); \
142         prefs_file_close_revert(pfile); \
143         g_free(rcpath); \
144         g_free(block_label); \
145         return; \
146 } \
147
148 void prefs_save_config(PrefParam *param, const gchar *label,
149                        const gchar *rcfile)
150 {
151         FILE *orig_fp;
152         PrefFile *pfile;
153         gchar *rcpath;
154         gchar buf[PREFSBUFSIZE];
155         gchar *block_label = NULL;
156         gboolean block_matched = FALSE;
157
158         g_return_if_fail(param != NULL);
159         g_return_if_fail(label != NULL);
160         g_return_if_fail(rcfile != NULL);
161
162         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, rcfile, NULL);
163         if ((orig_fp = fopen(rcpath, "rb")) == NULL) {
164                 if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen");
165         }
166
167         if ((pfile = prefs_write_open(rcpath)) == NULL) {
168                 g_warning("failed to write configuration to file\n");
169                 if (orig_fp) fclose(orig_fp);
170                 g_free(rcpath);
171                 return;
172         }
173
174         block_label = g_strdup_printf("[%s]", label);
175
176         /* search aiming block */
177         if (orig_fp) {
178                 while (fgets(buf, sizeof(buf), orig_fp) != NULL) {
179                         gint val;
180
181                         val = strncmp(buf, block_label, strlen(block_label));
182                         if (val == 0) {
183                                 debug_print("Found %s\n", block_label);
184                                 block_matched = TRUE;
185                                 break;
186                         } else
187                                 TRY(fputs(buf, pfile->fp) != EOF);
188                 }
189         }
190
191         TRY(fprintf(pfile->fp, "%s\n", block_label) > 0);
192         g_free(block_label);
193         block_label = NULL;
194
195         /* write all param data to file */
196         TRY(prefs_write_param(param, pfile->fp) == 0);
197
198         if (block_matched) {
199                 while (fgets(buf, sizeof(buf), orig_fp) != NULL) {
200                         /* next block */
201                         if (buf[0] == '[') {
202                                 TRY(fputc('\n', pfile->fp) != EOF &&
203                                     fputs(buf, pfile->fp)  != EOF);
204                                 break;
205                         }
206                 }
207                 while (fgets(buf, sizeof(buf), orig_fp) != NULL)
208                         TRY(fputs(buf, pfile->fp) != EOF);
209         }
210
211         if (orig_fp) fclose(orig_fp);
212         if (prefs_file_close(pfile) < 0)
213                 g_warning("failed to write configuration to file\n");
214         g_free(rcpath);
215
216         debug_print("Configuration is saved.\n");
217 }
218
219 gint prefs_write_param(PrefParam *param, FILE *fp)
220 {
221         gint i;
222         gchar buf[PREFSBUFSIZE];
223
224         for (i = 0; param[i].name != NULL; i++) {
225                 switch (param[i].type) {
226                 case P_STRING:
227                         g_snprintf(buf, sizeof(buf), "%s=%s\n", param[i].name,
228                                    *((gchar **)param[i].data) ?
229                                    *((gchar **)param[i].data) : "");
230                         break;
231                 case P_INT:
232                         g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name,
233                                    *((gint *)param[i].data));
234                         break;
235                 case P_BOOL:
236                         g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name,
237                                    *((gboolean *)param[i].data));
238                         break;
239                 case P_ENUM:
240                         g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name,
241                                    *((DummyEnum *)param[i].data));
242                         break;
243                 case P_USHORT:
244                         g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name,
245                                    *((gushort *)param[i].data));
246                         break;
247                 default:
248                         buf[0] = '\0';
249                 }
250
251                 if (buf[0] != '\0') {
252                         if (fputs(buf, fp) == EOF) {
253                                 perror("fputs");
254                                 return -1;
255                         }
256                 }
257         }
258
259         return 0;
260 }
261
262 void prefs_set_default(PrefParam *param)
263 {
264         gint i;
265
266         g_return_if_fail(param != NULL);
267
268         for (i = 0; param[i].name != NULL; i++) {
269                 if (!param[i].data) continue;
270
271                 switch (param[i].type) {
272                 case P_STRING:
273                         if (param[i].defval != NULL) {
274                                 if (!strncasecmp(param[i].defval, "ENV_", 4))
275                                         *((gchar **)param[i].data) =
276                                                 g_strdup(g_getenv(param[i].defval + 4));
277                                 else if (param[i].defval[0] == '~')
278                                         *((gchar **)param[i].data) =
279                                                 g_strconcat(get_home_dir(),
280                                                             param[i].defval + 1,
281                                                             NULL);
282                                 else if (param[i].defval[0] != '\0')
283                                         *((gchar **)param[i].data) =
284                                                 g_strdup(param[i].defval);
285                                 else
286                                         *((gchar **)param[i].data) = NULL;
287                         } else
288                                 *((gchar **)param[i].data) = NULL;
289                         break;
290                 case P_INT:
291                         if (param[i].defval != NULL)
292                                 *((gint *)param[i].data) =
293                                         (gint)atoi(param[i].defval);
294                         else
295                                 *((gint *)param[i].data) = 0;
296                         break;
297                 case P_BOOL:
298                         if (param[i].defval != NULL) {
299                                 if (!strcasecmp(param[i].defval, "TRUE"))
300                                         *((gboolean *)param[i].data) = TRUE;
301                                 else
302                                         *((gboolean *)param[i].data) =
303                                                 atoi(param[i].defval) ? TRUE : FALSE;
304                         } else
305                                 *((gboolean *)param[i].data) = FALSE;
306                         break;
307                 case P_ENUM:
308                         if (param[i].defval != NULL)
309                                 *((DummyEnum*)param[i].data) =
310                                         (DummyEnum)atoi(param[i].defval);
311                         else
312                                 *((DummyEnum *)param[i].data) = 0;
313                         break;
314                 case P_USHORT:
315                         if (param[i].defval != NULL)
316                                 *((gushort *)param[i].data) =
317                                         (gushort)atoi(param[i].defval);
318                         else
319                                 *((gushort *)param[i].data) = 0;
320                         break;
321                 default:
322                         break;
323                 }
324         }
325 }
326
327 void prefs_free(PrefParam *param)
328 {
329         gint i;
330
331         g_return_if_fail(param != NULL);
332
333         for (i = 0; param[i].name != NULL; i++) {
334                 if (!param[i].data) continue;
335
336                 switch (param[i].type) {
337                 case P_STRING:
338                         g_free(*((gchar **)param[i].data));
339                         break;
340                 default:
341                         break;
342                 }
343         }
344 }
345
346 void prefs_dialog_create(PrefsDialog *dialog)
347 {
348         GtkWidget *window;
349         GtkWidget *vbox;
350         GtkWidget *notebook;
351
352         GtkWidget *confirm_area;
353         GtkWidget *ok_btn;
354         GtkWidget *cancel_btn;
355         GtkWidget *apply_btn;
356
357         g_return_if_fail(dialog != NULL);
358
359         window = gtk_window_new (GTK_WINDOW_DIALOG);
360         gtk_container_set_border_width (GTK_CONTAINER (window), 8);
361         gtk_window_position (GTK_WINDOW(window), GTK_WIN_POS_CENTER);
362         gtk_window_set_modal (GTK_WINDOW (window), TRUE);
363         gtk_window_set_policy (GTK_WINDOW(window), FALSE, TRUE, FALSE);
364
365         vbox = gtk_vbox_new (FALSE, 6);
366         gtk_widget_show(vbox);
367         gtk_container_add (GTK_CONTAINER (window), vbox);
368
369         notebook = gtk_notebook_new ();
370         gtk_widget_show(notebook);
371         gtk_box_pack_start (GTK_BOX (vbox), notebook, TRUE, TRUE, 0);
372         gtk_container_set_border_width (GTK_CONTAINER (notebook), 2);
373         /* GTK_WIDGET_UNSET_FLAGS (notebook, GTK_CAN_FOCUS); */
374         gtk_notebook_set_scrollable (GTK_NOTEBOOK (notebook), TRUE);
375         
376         gtk_notebook_popup_enable (GTK_NOTEBOOK (notebook));
377         
378         gtkut_button_set_create(&confirm_area,
379                                 &ok_btn,        _("OK"),
380                                 &cancel_btn,    _("Cancel"),
381                                 &apply_btn,     _("Apply"));
382         gtk_widget_show(confirm_area);
383         gtk_box_pack_end (GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
384         gtk_widget_grab_default(ok_btn);
385
386         dialog->window     = window;
387         dialog->notebook   = notebook;
388         dialog->ok_btn     = ok_btn;
389         dialog->cancel_btn = cancel_btn;
390         dialog->apply_btn  = apply_btn;
391 }
392
393 void prefs_dialog_destroy(PrefsDialog *dialog)
394 {
395         gtk_widget_destroy(dialog->window);
396         dialog->window     = NULL;
397         dialog->notebook   = NULL;
398         dialog->ok_btn     = NULL;
399         dialog->cancel_btn = NULL;
400         dialog->apply_btn  = NULL;
401 }
402
403 void prefs_button_toggled(GtkToggleButton *toggle_btn, GtkWidget *widget)
404 {
405         gboolean is_active;
406
407         is_active = gtk_toggle_button_get_active(toggle_btn);
408         gtk_widget_set_sensitive(widget, is_active);
409 }
410
411 void prefs_set_dialog(PrefParam *param)
412 {
413         gint i;
414
415         for (i = 0; param[i].name != NULL; i++) {
416                 if (param[i].widget_set_func)
417                         param[i].widget_set_func(&param[i]);
418         }
419 }
420
421 void prefs_set_data_from_dialog(PrefParam *param)
422 {
423         gint i;
424
425         for (i = 0; param[i].name != NULL; i++) {
426                 if (param[i].data_set_func)
427                         param[i].data_set_func(&param[i]);
428         }
429 }
430
431 void prefs_set_dialog_to_default(PrefParam *param)
432 {
433         gint       i;
434         PrefParam  tmpparam;
435         gchar     *str_data = NULL;
436         gint       int_data;
437         gushort    ushort_data;
438         gboolean   bool_data;
439         DummyEnum  enum_data;
440
441         for (i = 0; param[i].name != NULL; i++) {
442                 if (!param[i].widget_set_func) continue;
443
444                 tmpparam = param[i];
445
446                 switch (tmpparam.type) {
447                 case P_STRING:
448                         if (tmpparam.defval) {
449                                 if (!strncasecmp(tmpparam.defval, "ENV_", 4)) {
450                                         str_data = g_strdup(g_getenv(param[i].defval + 4));
451                                         tmpparam.data = &str_data;
452                                         break;
453                                 } else if (tmpparam.defval[0] == '~') {
454                                         str_data =
455                                                 g_strconcat(get_home_dir(),
456                                                             param[i].defval + 1,
457                                                             NULL);
458                                         tmpparam.data = &str_data;
459                                         break;
460                                 }
461                         }
462                         tmpparam.data = &tmpparam.defval;
463                         break;
464                 case P_INT:
465                         if (tmpparam.defval)
466                                 int_data = atoi(tmpparam.defval);
467                         else
468                                 int_data = 0;
469                         tmpparam.data = &int_data;
470                         break;
471                 case P_USHORT:
472                         if (tmpparam.defval)
473                                 ushort_data = atoi(tmpparam.defval);
474                         else
475                                 ushort_data = 0;
476                         tmpparam.data = &ushort_data;
477                         break;
478                 case P_BOOL:
479                         if (tmpparam.defval) {
480                                 if (!strcasecmp(tmpparam.defval, "TRUE"))
481                                         bool_data = TRUE;
482                                 else
483                                         bool_data = atoi(tmpparam.defval)
484                                                 ? TRUE : FALSE;
485                         } else
486                                 bool_data = FALSE;
487                         tmpparam.data = &bool_data;
488                         break;
489                 case P_ENUM:
490                         if (tmpparam.defval)
491                                 enum_data = (DummyEnum)atoi(tmpparam.defval);
492                         else
493                                 enum_data = 0;
494                         tmpparam.data = &enum_data;
495                         break;
496                 case P_OTHER:
497                         break;
498                 }
499                 tmpparam.widget_set_func(&tmpparam);
500                 g_free(str_data);
501                 str_data = NULL;
502         }
503 }
504
505 void prefs_set_data_from_entry(PrefParam *pparam)
506 {
507         gchar **str, *entry_str;
508
509         g_return_if_fail(*pparam->widget != NULL);
510
511         entry_str = gtk_entry_get_text(GTK_ENTRY(*pparam->widget));
512
513         switch (pparam->type) {
514         case P_STRING:
515                 str = (gchar **)pparam->data;
516                 g_free(*str);
517                 *str = entry_str[0] ? g_strdup(entry_str) : NULL;
518                 break;
519         case P_USHORT:
520                 *((gushort *)pparam->data) = atoi(entry_str);
521                 break;
522         case P_INT:
523                 *((gint *)pparam->data) = atoi(entry_str);
524                 break;
525         default:
526                 g_warning("Invalid PrefType for GtkEntry widget: %d\n",
527                           pparam->type);
528         }
529 }
530
531 void prefs_set_entry(PrefParam *pparam)
532 {
533         gchar **str;
534
535         g_return_if_fail(*pparam->widget != NULL);
536
537         switch (pparam->type) {
538         case P_STRING:
539                 str = (gchar **)pparam->data;
540                 gtk_entry_set_text(GTK_ENTRY(*pparam->widget),
541                                    *str ? *str : "");
542                 break;
543         case P_INT:
544                 gtk_entry_set_text(GTK_ENTRY(*pparam->widget),
545                                    itos(*((gint *)pparam->data)));
546                 break;
547         case P_USHORT:
548                 gtk_entry_set_text(GTK_ENTRY(*pparam->widget),
549                                    itos(*((gushort *)pparam->data)));
550                 break;
551         default:
552                 g_warning("Invalid PrefType for GtkEntry widget: %d\n",
553                           pparam->type);
554         }
555 }
556
557 void prefs_set_data_from_text(PrefParam *pparam)
558 {
559         gchar **str;
560         gchar *text, *tp;
561         gchar *tmp, *tmpp;
562
563         g_return_if_fail(*pparam->widget != NULL);
564
565         switch (pparam->type) {
566         case P_STRING:
567                 str = (gchar **)pparam->data;
568                 g_free(*str);
569                 tp = text = gtk_editable_get_chars
570                         (GTK_EDITABLE(*pparam->widget), 0, -1);
571                 if (text[0] == '\0') {
572                         *str = NULL;
573                         g_free(text);
574                         break;
575                 }
576
577                 Xalloca(tmpp = tmp, strlen(text) * 2 + 1,
578                         { *str = NULL; break; });
579                 while (*tp) {
580                         if (*tp == '\n') {
581                                 *tmpp++ = '\\';
582                                 *tmpp++ = 'n';
583                                 tp++;
584                         } else
585                                 *tmpp++ = *tp++;
586                 }
587                 *tmpp = '\0';
588                 *str = g_strdup(tmp);
589                 g_free(text);
590                 break;
591         default:
592                 g_warning("Invalid PrefType for GtkText widget: %d\n",
593                           pparam->type);
594         }
595 }
596
597 void prefs_set_text(PrefParam *pparam)
598 {
599         gchar *buf, *sp, *bufp;
600         gchar **str;
601         GtkText *text;
602
603         g_return_if_fail(*pparam->widget != NULL);
604
605         switch (pparam->type) {
606         case P_STRING:
607                 str = (gchar **)pparam->data;
608                 if (*str) {
609                         bufp = buf = alloca(strlen(*str) + 1);
610                         if (!buf) buf = "";
611                         else {
612                                 sp = *str;
613                                 while (*sp) {
614                                         if (*sp == '\\' && *(sp + 1) == 'n') {
615                                                 *bufp++ = '\n';
616                                                 sp += 2;
617                                         } else
618                                                 *bufp++ = *sp++;
619                                 }
620                                 *bufp = '\0';
621                         }
622                 } else
623                         buf = "";
624
625                 text = GTK_TEXT(*pparam->widget);
626                 gtk_text_set_point(text, 0);
627                 gtk_text_forward_delete(text, gtk_text_get_length(text));
628                 gtk_text_set_point(text, 0);
629                 gtk_text_insert(text, NULL, NULL, NULL, buf, -1);
630                 break;
631         default:
632                 g_warning("Invalid PrefType for GtkText widget: %d\n",
633                           pparam->type);
634         }
635 }
636
637 void prefs_set_data_from_toggle(PrefParam *pparam)
638 {
639         g_return_if_fail(pparam->type == P_BOOL);
640         g_return_if_fail(*pparam->widget != NULL);
641         
642         *((gboolean *)pparam->data) =
643                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(*pparam->widget));
644 }
645
646 void prefs_set_toggle(PrefParam *pparam)
647 {
648         g_return_if_fail(pparam->type == P_BOOL);
649         g_return_if_fail(*pparam->widget != NULL);
650
651         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(*pparam->widget),
652                                      *((gboolean *)pparam->data));
653 }
654
655 void prefs_set_data_from_spinbtn(PrefParam *pparam)
656 {
657         g_return_if_fail(*pparam->widget != NULL);
658
659         switch (pparam->type) {
660         case P_INT:
661                 *((gint *)pparam->data) =
662                         gtk_spin_button_get_value_as_int
663                         (GTK_SPIN_BUTTON(*pparam->widget));
664                 break;
665         case P_USHORT:
666                 *((gushort *)pparam->data) =
667                         (gushort)gtk_spin_button_get_value_as_int
668                         (GTK_SPIN_BUTTON(*pparam->widget));
669                 break;
670         default:
671                 g_warning("Invalid PrefType for GtkSpinButton widget: %d\n",
672                           pparam->type);
673         }
674 }
675
676 void prefs_set_spinbtn(PrefParam *pparam)
677 {
678         g_return_if_fail(*pparam->widget != NULL);
679
680         switch (pparam->type) {
681         case P_INT:
682                 gtk_spin_button_set_value(GTK_SPIN_BUTTON(*pparam->widget),
683                                           (gfloat)*((gint *)pparam->data));
684                 break;
685         case P_USHORT:
686                 gtk_spin_button_set_value(GTK_SPIN_BUTTON(*pparam->widget),
687                                           (gfloat)*((gushort *)pparam->data));
688                 break;
689         default:
690                 g_warning("Invalid PrefType for GtkSpinButton widget: %d\n",
691                           pparam->type);
692         }
693 }
694
695 static GSList *prefs_pages = NULL;
696
697 void prefs_gtk_open()
698 {
699         prefswindow_open(prefs_pages, NULL);
700 }
701
702 void prefs_gtk_register_page(PrefsPage *page)
703 {
704         prefs_pages = g_slist_append(prefs_pages, page);
705 }
706
707 void prefs_gtk_unregister_page(PrefsPage *page)
708 {
709         prefs_pages = g_slist_remove(prefs_pages, page);
710 }
711
712 void prefs_gtk_destroy_all_pages()
713 {
714         GSList *cur;
715
716         for (cur = prefs_pages; cur != NULL; cur = g_slist_next(cur)) {
717                 PrefsPage *page = (PrefsPage *) cur->data;
718
719                 page->destroy_page(page);
720         }
721 }