Move sync_options_with_current_state() call to save_options().
[geeqie.git] / src / rcfile.c
1 /*
2  * Geeqie
3  * (C) 2006 John Ellis
4  * Copyright (C) 2008 The Geeqie Team
5  *
6  * Author: John Ellis
7  *
8  * This software is released under the GNU General Public License (GNU GPL).
9  * Please read the included file COPYING for more information.
10  * This software comes with no warranty of any kind, use at your own risk!
11  */
12
13 #include <glib/gstdio.h>
14 #include <errno.h>
15
16 #include "main.h"
17 #include "rcfile.h"
18
19 #include "bar_exif.h"
20 #include "editors.h"
21 #include "filefilter.h"
22 #include "pixbuf-renderer.h"
23 #include "secure_save.h"
24 #include "slideshow.h"
25 #include "ui_fileops.h"
26
27
28 /*
29  *-----------------------------------------------------------------------------
30  * line write/parse routines (private)
31  *-----------------------------------------------------------------------------
32  */
33
34 /*
35    returns text without quotes or NULL for empty or broken string
36    any text up to first '"' is skipped
37    tail is set to point at the char after the second '"'
38    or at the ending \0
39
40 */
41
42 gchar *quoted_value(const gchar *text, const gchar **tail)
43 {
44         const gchar *ptr;
45         gint c = 0;
46         gint l = strlen(text);
47         gchar *retval = NULL;
48
49         if (tail) *tail = text;
50
51         if (l == 0) return retval;
52
53         while (c < l && text[c] != '"') c++;
54         if (text[c] == '"')
55                 {
56                 gint e;
57                 c++;
58                 ptr = text + c;
59                 e = c;
60                 while (e < l)
61                         {
62                         if (text[e-1] != '\\' && text[e] == '"') break;
63                         e++;
64                         }
65                 if (text[e] == '"')
66                         {
67                         if (e - c > 0)
68                                 {
69                                 gchar *substring = g_strndup(ptr, e - c);
70
71                                 if (substring)
72                                         {
73                                         retval = g_strcompress(substring);
74                                         g_free(substring);
75                                         }
76                                 }
77                         }
78                 if (tail) *tail = text + e + 1;
79                 }
80         else
81                 /* for compatibility with older formats (<0.3.7)
82                  * read a line without quotes too */
83                 {
84                 c = 0;
85                 while (c < l && text[c] != '\n' && !g_ascii_isspace(text[c])) c++;
86                 if (c != 0)
87                         {
88                         retval = g_strndup(text, c);
89                         }
90                 if (tail) *tail = text + c;
91                 }
92
93         return retval;
94 }
95
96 gchar *escquote_value(const gchar *text)
97 {
98         gchar *e;
99
100         if (!text) return g_strdup("\"\"");
101
102         e = g_strescape(text, "");
103         if (e)
104                 {
105                 gchar *retval = g_strdup_printf("\"%s\"", e);
106                 g_free(e);
107                 return retval;
108                 }
109         return g_strdup("\"\"");
110 }
111
112 static void write_char_option(SecureSaveInfo *ssi, gchar *label, gchar *text)
113 {
114         gchar *escval = escquote_value(text);
115
116         secure_fprintf(ssi, "%s: %s\n", label, escval);
117         g_free(escval);
118 }
119
120 static gboolean read_char_option(FILE *f, gchar *option, gchar *label, gchar *value, gchar **text)
121 {
122         if (g_ascii_strcasecmp(option, label) != 0) return FALSE;
123         if (!text) return FALSE;
124
125         g_free(*text);
126         *text = quoted_value(value, NULL);
127         return TRUE;
128 }
129
130 /* Since gdk_color_to_string() is only available since gtk 2.12
131  * here is an equivalent stub function. */
132 static gchar *color_to_string(GdkColor *color)
133 {
134         return g_strdup_printf("#%04X%04X%04X", color->red, color->green, color->blue);
135 }
136
137 static void write_color_option(SecureSaveInfo *ssi, gchar *label, GdkColor *color)
138 {
139         if (color)
140                 {
141                 gchar *colorstring = color_to_string(color);
142
143                 write_char_option(ssi, label, colorstring);
144                 g_free(colorstring);
145                 }
146         else
147                 secure_fprintf(ssi, "%s: \n", label);
148 }
149
150 static gboolean read_color_option(FILE *f, gchar *option, gchar *label, gchar *value, GdkColor *color)
151 {
152         gchar *colorstr;
153         
154         if (g_ascii_strcasecmp(option, label) != 0) return FALSE;
155         if (!color) return FALSE;
156
157         colorstr = quoted_value(value, NULL);
158         if (!colorstr) return FALSE;
159         gdk_color_parse(colorstr, color);
160         g_free(colorstr);
161         return TRUE;
162 }
163
164 static void write_int_option(SecureSaveInfo *ssi, gchar *label, gint n)
165 {
166         secure_fprintf(ssi, "%s: %d\n", label, n);
167 }
168
169 static gboolean read_int_option(FILE *f, gchar *option, gchar *label, gchar *value, gint *n)
170 {
171         if (g_ascii_strcasecmp(option, label) != 0) return FALSE;
172         if (!n) return FALSE;
173
174         if (g_ascii_isdigit(value[0]) || (value[0] == '-' && g_ascii_isdigit(value[1])))
175                 {
176                 *n = strtol(value, NULL, 10);
177                 }
178         else
179                 {
180                 if (g_ascii_strcasecmp(value, "true") == 0)
181                         *n = 1;
182                 else
183                         *n = 0;
184                 }
185
186         return TRUE;
187 }
188
189 static void write_uint_option(SecureSaveInfo *ssi, gchar *label, guint n)
190 {
191         secure_fprintf(ssi, "%s: %u\n", label, n);
192 }
193
194 static gboolean read_uint_option(FILE *f, gchar *option, gchar *label, gchar *value, guint *n)
195 {
196         if (g_ascii_strcasecmp(option, label) != 0) return FALSE;
197         if (!n) return FALSE;
198
199         if (g_ascii_isdigit(value[0]))
200                 {
201                 *n = strtoul(value, NULL, 10);
202                 }
203         else
204                 {
205                 if (g_ascii_strcasecmp(value, "true") == 0)
206                         *n = 1;
207                 else
208                         *n = 0;
209                 }
210         
211         return TRUE;
212 }
213
214 static gboolean read_uint_option_clamp(FILE *f, gchar *option, gchar *label, gchar *value, guint *n, guint min, guint max)
215 {
216         gboolean ret;
217
218         ret = read_uint_option(f, option, label, value, n);
219         if (ret) *n = CLAMP(*n, min, max);
220
221         return ret;
222 }
223
224
225 static gboolean read_int_option_clamp(FILE *f, gchar *option, gchar *label, gchar *value, gint *n, gint min, gint max)
226 {
227         gboolean ret;
228
229         ret = read_int_option(f, option, label, value, n);
230         if (ret) *n = CLAMP(*n, min, max);
231
232         return ret;
233 }
234
235 static void write_int_unit_option(SecureSaveInfo *ssi, gchar *label, gint n, gint subunits)
236 {
237         gint l, r;
238
239         if (subunits > 0)
240                 {
241                 l = n / subunits;
242                 r = n % subunits;
243                 }
244         else
245                 {
246                 l = n;
247                 r = 0;
248                 }
249
250         secure_fprintf(ssi, "%s: %d.%d\n", label, l, r);
251 }
252
253 static gboolean read_int_unit_option(FILE *f, gchar *option, gchar *label, gchar *value, gint *n, gint subunits)
254 {
255         gint l, r;
256         gchar *ptr;
257
258         if (g_ascii_strcasecmp(option, label) != 0) return FALSE;
259         if (!n) return FALSE;
260
261         ptr = value;
262         while (*ptr != '\0' && *ptr != '.') ptr++;
263         if (*ptr == '.')
264                 {
265                 *ptr = '\0';
266                 l = strtol(value, NULL, 10);
267                 *ptr = '.';
268                 ptr++;
269                 r = strtol(ptr, NULL, 10);
270                 }
271         else
272                 {
273                 l = strtol(value, NULL, 10);
274                 r = 0;
275                 }
276
277         *n = l * subunits + r;
278
279         return TRUE;
280 }
281
282 static void write_bool_option(SecureSaveInfo *ssi, gchar *label, gint n)
283 {
284         secure_fprintf(ssi, "%s: ", label);
285         if (n) secure_fprintf(ssi, "true\n"); else secure_fprintf(ssi, "false\n");
286 }
287
288 static gboolean read_bool_option(FILE *f, gchar *option, gchar *label, gchar *value, gint *n)
289 {
290         if (g_ascii_strcasecmp(option, label) != 0) return FALSE;
291         if (!n) return FALSE;
292
293         if (g_ascii_strcasecmp(value, "true") == 0 || atoi(value) != 0)
294                 *n = TRUE;
295         else
296                 *n = FALSE;
297
298         return TRUE;
299 }
300
301
302 /*
303  *-----------------------------------------------------------------------------
304  * save configuration (public)
305  *-----------------------------------------------------------------------------
306  */
307
308 static gboolean save_options_to(const gchar *utf8_path, ConfOptions *options)
309 {
310         SecureSaveInfo *ssi;
311         gchar *rc_pathl;
312         gint i;
313
314         rc_pathl = path_from_utf8(utf8_path);
315         ssi = secure_open(rc_pathl);
316         g_free(rc_pathl);
317         if (!ssi)
318                 {
319                 log_printf(_("error saving config file: %s\n"), utf8_path);
320                 return FALSE;
321                 }
322
323 #define WRITE_BOOL(_name_) write_bool_option(ssi, #_name_, options->_name_)
324 #define WRITE_INT(_name_) write_int_option(ssi, #_name_, options->_name_)
325 #define WRITE_UINT(_name_) write_uint_option(ssi, #_name_, options->_name_)
326 #define WRITE_INT_UNIT(_name_, _unit_) write_int_unit_option(ssi, #_name_, options->_name_, _unit_)
327 #define WRITE_CHAR(_name_) write_char_option(ssi, #_name_, options->_name_)
328 #define WRITE_COLOR(_name_) write_color_option(ssi, #_name_, &options->_name_)
329
330 #define WRITE_SEPARATOR() secure_fputc(ssi, '\n')
331 #define WRITE_SUBTITLE(_title_) secure_fprintf(ssi, "\n\n##### "_title_" #####\n\n")
332
333         secure_fprintf(ssi, "######################################################################\n");
334         secure_fprintf(ssi, "# %30s config file      version %-10s #\n", GQ_APPNAME, VERSION);
335         secure_fprintf(ssi, "######################################################################\n");
336         WRITE_SEPARATOR();
337
338         secure_fprintf(ssi, "# Note: This file is autogenerated. Options can be changed here,\n");
339         secure_fprintf(ssi, "#       but user comments and formatting will be lost.\n");
340         WRITE_SEPARATOR();
341
342         WRITE_SUBTITLE("General Options");
343
344         WRITE_BOOL(show_icon_names);
345         WRITE_BOOL(show_copy_path);
346         WRITE_SEPARATOR();
347
348         WRITE_BOOL(tree_descend_subdirs);
349         WRITE_BOOL(lazy_image_sync);
350         WRITE_BOOL(update_on_time_change);
351         WRITE_SEPARATOR();
352
353         WRITE_BOOL(progressive_key_scrolling);
354         WRITE_BOOL(enable_metadata_dirs);
355         WRITE_BOOL(save_metadata_in_image_file);
356
357         WRITE_UINT(duplicates_similarity_threshold);
358         WRITE_SEPARATOR();
359
360         WRITE_BOOL(mousewheel_scrolls);
361         WRITE_INT(open_recent_list_maxsize);
362         WRITE_INT(dnd_icon_size);
363         WRITE_BOOL(place_dialogs_under_mouse);
364
365
366         WRITE_SUBTITLE("Startup Options");
367
368         WRITE_BOOL(startup.restore_path);
369         WRITE_BOOL(startup.use_last_path);
370         WRITE_CHAR(startup.path);
371
372
373         WRITE_SUBTITLE("File operations Options");
374
375         WRITE_BOOL(file_ops.enable_in_place_rename);
376         WRITE_BOOL(file_ops.confirm_delete);
377         WRITE_BOOL(file_ops.enable_delete_key);
378         WRITE_BOOL(file_ops.safe_delete_enable);
379         WRITE_CHAR(file_ops.safe_delete_path);
380         WRITE_INT(file_ops.safe_delete_folder_maxsize);
381
382
383         WRITE_SUBTITLE("Layout Options");
384
385         WRITE_INT(layout.style);
386         WRITE_CHAR(layout.order);
387         WRITE_UINT(layout.dir_view_type);
388         WRITE_UINT(layout.file_view_type);
389         WRITE_BOOL(layout.show_marks);
390         WRITE_BOOL(layout.show_thumbnails);
391         WRITE_BOOL(layout.show_directory_date);
392         WRITE_CHAR(layout.home_path);
393         WRITE_SEPARATOR();
394
395         WRITE_BOOL(layout.save_window_positions);
396         WRITE_SEPARATOR();
397
398         WRITE_INT(layout.main_window.x);
399         WRITE_INT(layout.main_window.y);
400         WRITE_INT(layout.main_window.w);
401         WRITE_INT(layout.main_window.h);
402         WRITE_BOOL(layout.main_window.maximized);
403         WRITE_INT(layout.main_window.hdivider_pos);
404         WRITE_INT(layout.main_window.vdivider_pos);
405         WRITE_SEPARATOR();
406
407         WRITE_INT(layout.float_window.x);
408         WRITE_INT(layout.float_window.y);
409         WRITE_INT(layout.float_window.w);
410         WRITE_INT(layout.float_window.h);
411         WRITE_INT(layout.float_window.vdivider_pos);
412         WRITE_SEPARATOR();
413
414         WRITE_INT(layout.properties_window.w);
415         WRITE_INT(layout.properties_window.h);
416         WRITE_SEPARATOR();
417
418         WRITE_BOOL(layout.tools_float);
419         WRITE_BOOL(layout.tools_hidden);
420         WRITE_BOOL(layout.tools_restore_state);
421         WRITE_SEPARATOR();
422
423         WRITE_BOOL(layout.toolbar_hidden);
424
425         WRITE_SUBTITLE("Panels Options");
426
427         WRITE_BOOL(panels.exif.enabled);
428         WRITE_INT(panels.exif.width);
429         WRITE_BOOL(panels.info.enabled);
430         WRITE_INT(panels.info.width);
431         WRITE_BOOL(panels.sort.enabled);
432         WRITE_INT(panels.sort.action_state);
433         WRITE_INT(panels.sort.mode_state);
434         WRITE_INT(panels.sort.selection_state);
435
436         WRITE_SUBTITLE("Properties dialog Options");
437         WRITE_CHAR(properties.tabs_order);
438
439         WRITE_SUBTITLE("Image Options");
440
441         secure_fprintf(ssi, "# image.zoom_mode possible values are:\n"
442                             "#   original\n"
443                             "#   fit\n"
444                             "#   dont_change\n");
445         secure_fprintf(ssi, "image.zoom_mode: ");
446         switch (options->image.zoom_mode)
447         {
448         case ZOOM_RESET_ORIGINAL: secure_fprintf(ssi, "original\n"); break;
449         case ZOOM_RESET_FIT_WINDOW: secure_fprintf(ssi, "fit\n"); break;
450         case ZOOM_RESET_NONE: secure_fprintf(ssi, "dont_change\n"); break;
451         }
452         WRITE_SEPARATOR();
453         WRITE_BOOL(image.zoom_2pass);
454         WRITE_BOOL(image.zoom_to_fit_allow_expand);
455         WRITE_UINT(image.zoom_quality);
456         WRITE_INT(image.zoom_increment);
457         WRITE_BOOL(image.fit_window_to_image);
458         WRITE_BOOL(image.limit_window_size);
459         WRITE_INT(image.max_window_size);
460         WRITE_BOOL(image.limit_autofit_size);
461         WRITE_INT(image.max_autofit_size);
462         WRITE_UINT(image.scroll_reset_method);
463         WRITE_INT(image.tile_cache_max);
464         WRITE_INT(image.image_cache_max);
465         WRITE_UINT(image.dither_quality);
466         WRITE_BOOL(image.enable_read_ahead);
467         WRITE_BOOL(image.exif_rotate_enable);
468         WRITE_BOOL(image.use_custom_border_color);
469         WRITE_COLOR(image.border_color);
470         WRITE_INT(image.read_buffer_size);
471         WRITE_INT(image.idle_read_loop_count);
472
473         WRITE_SUBTITLE("Thumbnails Options");
474
475         WRITE_INT(thumbnails.max_width);
476         WRITE_INT(thumbnails.max_height);
477         WRITE_BOOL(thumbnails.enable_caching);
478         WRITE_BOOL(thumbnails.cache_into_dirs);
479         WRITE_BOOL(thumbnails.fast);
480         WRITE_BOOL(thumbnails.use_xvpics);
481         WRITE_BOOL(thumbnails.spec_standard);
482         WRITE_UINT(thumbnails.quality);
483
484
485         WRITE_SUBTITLE("File sorting Options");
486
487         WRITE_INT(file_sort.method);
488         WRITE_BOOL(file_sort.ascending);
489         WRITE_BOOL(file_sort.case_sensitive);
490
491
492         WRITE_SUBTITLE("Fullscreen Options");
493
494         WRITE_INT(fullscreen.screen);
495         WRITE_BOOL(fullscreen.clean_flip);
496         WRITE_BOOL(fullscreen.disable_saver);
497         WRITE_BOOL(fullscreen.above);
498
499
500         WRITE_SUBTITLE("Histogram Options");
501         WRITE_UINT(histogram.last_channel_mode);
502         WRITE_UINT(histogram.last_log_mode);
503
504
505         WRITE_SUBTITLE("Image Overlay Options");
506         WRITE_UINT(image_overlay.common.state);
507         WRITE_BOOL(image_overlay.common.show_at_startup);
508         WRITE_CHAR(image_overlay.common.template_string);
509         WRITE_SEPARATOR();
510
511         secure_fprintf(ssi, "# these are relative positions:\n");
512         secure_fprintf(ssi, "# x >= 0: |x| pixels from left border\n");
513         secure_fprintf(ssi, "# x < 0 : |x| pixels from right border\n");
514         secure_fprintf(ssi, "# y >= 0: |y| pixels from top border\n");
515         secure_fprintf(ssi, "# y < 0 : |y| pixels from bottom border\n");
516         WRITE_INT(image_overlay.common.x);
517         WRITE_INT(image_overlay.common.y);
518
519
520         WRITE_SUBTITLE("Slideshow Options");
521
522         WRITE_INT_UNIT(slideshow.delay, SLIDESHOW_SUBSECOND_PRECISION);
523         WRITE_BOOL(slideshow.random);
524         WRITE_BOOL(slideshow.repeat);
525
526
527         WRITE_SUBTITLE("Collection Options");
528
529         WRITE_BOOL(collections.rectangular_selection);
530
531
532         WRITE_SUBTITLE("Filtering Options");
533
534         WRITE_BOOL(file_filter.show_hidden_files);
535         WRITE_BOOL(file_filter.show_dot_directory);
536         WRITE_BOOL(file_filter.disable);
537         WRITE_SEPARATOR();
538
539         filter_write_list(ssi);
540
541
542         WRITE_SUBTITLE("Sidecars Options");
543
544         sidecar_ext_write(ssi);
545
546
547         WRITE_SUBTITLE("Color Profiles");
548
549 #ifndef HAVE_LCMS
550         secure_fprintf(ssi, "# NOTICE: %s was not built with support for color profiles,\n"
551                             "#         color profile options will have no effect.\n\n", GQ_APPNAME);
552 #endif
553
554         WRITE_BOOL(color_profile.enabled);
555         WRITE_BOOL(color_profile.use_image);
556         WRITE_INT(color_profile.input_type);
557         WRITE_SEPARATOR();
558
559         for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
560                 {
561                 gchar *buf;
562
563                 buf = g_strdup_printf("color_profile.input_file_%d", i + 1);
564                 write_char_option(ssi, buf, options->color_profile.input_file[i]);
565                 g_free(buf);
566
567                 buf = g_strdup_printf("color_profile.input_name_%d", i + 1);
568                 write_char_option(ssi, buf, options->color_profile.input_name[i]);
569                 g_free(buf);
570                 }
571
572         WRITE_SEPARATOR();
573         WRITE_INT(color_profile.screen_type);
574         WRITE_CHAR(color_profile.screen_file);
575
576
577         WRITE_SUBTITLE("Shell command");
578         WRITE_CHAR(shell.path);
579         WRITE_CHAR(shell.options);
580
581
582         WRITE_SUBTITLE("Helpers");
583         secure_fprintf(ssi, "# Html browser\n");
584         secure_fprintf(ssi, "# command_name is: the binary's name to look for in the path\n");
585         secure_fprintf(ssi, "# If command_name is empty, the program will try various common html browsers\n");
586         secure_fprintf(ssi, "# command_line is:\n");
587         secure_fprintf(ssi, "# \"\" (empty string)  = execute binary with html file path as command line\n");
588         secure_fprintf(ssi, "# \"string\"           = execute string and use results for command line\n");
589         secure_fprintf(ssi, "# \"!string\"          = use text following ! as command line, replacing optional %%s with html file path\n");
590         WRITE_CHAR(helpers.html_browser.command_name);
591         WRITE_CHAR(helpers.html_browser.command_line);
592
593
594         WRITE_SUBTITLE("External Programs");
595         secure_fprintf(ssi, "# Maximum of %d programs (external_1 through external_%d)\n", GQ_EDITOR_GENERIC_SLOTS, GQ_EDITOR_GENERIC_SLOTS);
596         secure_fprintf(ssi, "# external_%d through external_%d are used for file ops\n", GQ_EDITOR_GENERIC_SLOTS + 1, GQ_EDITOR_SLOTS);
597         secure_fprintf(ssi, "# format: external_n: \"menu name\" \"command line\"\n\n");
598
599         for (i = 0; i < GQ_EDITOR_SLOTS; i++)
600                 {
601                 if (i == GQ_EDITOR_GENERIC_SLOTS) secure_fputc(ssi, '\n');
602                 gchar *qname = escquote_value(options->editor[i].name);
603                 gchar *qcommand = escquote_value(options->editor[i].command);
604                 secure_fprintf(ssi, "external_%d: %s %s\n", i+1, qname, qcommand);
605                 g_free(qname);
606                 g_free(qcommand);
607                 }
608
609
610         WRITE_SUBTITLE("Exif Options");
611         secure_fprintf(ssi, "# Display: 0: never\n"
612                             "#          1: if set\n"
613                             "#          2: always\n\n");
614         for (i = 0; ExifUIList[i].key; i++)
615                 {
616                 secure_fprintf(ssi, "exif.display.");
617                 write_int_option(ssi, (gchar *)ExifUIList[i].key, ExifUIList[i].current);
618                 }
619
620         WRITE_SUBTITLE("Documentation Options");
621         WRITE_CHAR(documentation.helpdir);
622         WRITE_CHAR(documentation.htmldir);
623
624         WRITE_SEPARATOR();
625         WRITE_SEPARATOR();
626
627         secure_fprintf(ssi, "######################################################################\n");
628         secure_fprintf(ssi, "#                         end of config file                         #\n");
629         secure_fprintf(ssi, "######################################################################\n");
630
631
632         if (secure_close(ssi))
633                 {
634                 log_printf(_("error saving config file: %s\nerror: %s\n"), utf8_path,
635                            secsave_strerror(secsave_errno));
636                 return FALSE;
637                 }
638
639         return TRUE;
640 }
641
642 void save_options(ConfOptions *options)
643 {
644         gchar *rc_path;
645
646         sync_options_with_current_state(options);
647
648         rc_path = g_build_filename(homedir(), GQ_RC_DIR, RC_FILE_NAME, NULL);
649         save_options_to(rc_path, options);
650         g_free(rc_path);
651 }
652
653
654
655 /*
656  *-----------------------------------------------------------------------------
657  * load configuration (public)
658  *-----------------------------------------------------------------------------
659  */
660
661 static gboolean is_numbered_option(const gchar *option, const gchar *prefix, gint *number)
662 {
663         gsize n;
664         gsize option_len = strlen(option);
665         gsize prefix_len = strlen(prefix);
666         
667         if (option_len <= prefix_len) return FALSE;
668         if (g_ascii_strncasecmp(option, prefix, prefix_len) != 0) return FALSE;
669
670         n = prefix_len;
671         while (g_ascii_isdigit(option[n])) n++;
672         if (n < option_len) return FALSE;
673         
674         if (number) *number = atoi(option + prefix_len);
675         return TRUE;
676 }
677
678 #define OPTION_READ_BUFFER_SIZE 1024
679
680 static gboolean load_options_from(const gchar *utf8_path, ConfOptions *options)
681 {
682         FILE *f;
683         gchar *rc_pathl;
684         gchar s_buf[OPTION_READ_BUFFER_SIZE];
685         gchar value_all[OPTION_READ_BUFFER_SIZE];
686         gchar *option;
687         gchar *value;
688         gint i;
689
690         rc_pathl = path_from_utf8(utf8_path);
691         f = fopen(rc_pathl,"r");
692         g_free(rc_pathl);
693         if (!f) return FALSE;
694
695         while (fgets(s_buf, sizeof(s_buf), f))
696                 {
697                 gchar *value_end;
698                 gchar *p = s_buf;
699
700                 /* skip empty lines and comments */
701                 while (g_ascii_isspace(*p)) p++;
702                 if (!*p || *p == '\n' || *p == '#') continue;
703
704                 /* parse option name */
705                 option = p;
706                 while (g_ascii_isalnum(*p) || *p == '_' || *p == '.') p++;
707                 if (!*p) continue;
708                 *p = '\0';
709                 p++;
710
711                 /* search for value start, name and value are normally separated by ': '
712                  * but we allow relaxed syntax here, so '=', ':=' or just a tab will work too */
713                 while (*p == ':' || g_ascii_isspace(*p) || *p == '=') p++;
714                 value = p;
715
716                 while (*p && !g_ascii_isspace(*p) && *p != '\n') p++;
717                 value_end = p; /* value part up to the first whitespace or end of line */
718                 while (*p != '\0') p++;
719                 memcpy(value_all, value, 1 + p - value);
720
721                 *value_end = '\0';
722
723 #define READ_BOOL(_name_) if (read_bool_option(f, option, #_name_, value, &options->_name_)) continue;
724 #define READ_INT(_name_) if (read_int_option(f, option, #_name_, value, &options->_name_)) continue;
725 #define READ_UINT(_name_) if (read_uint_option(f, option, #_name_, value, &options->_name_)) continue;
726 #define READ_INT_CLAMP(_name_, _min_, _max_) if (read_int_option_clamp(f, option, #_name_, value, &options->_name_, _min_, _max_)) continue;
727 #define READ_UINT_CLAMP(_name_, _min_, _max_) if (read_uint_option_clamp(f, option, #_name_, value, &options->_name_, _min_, _max_)) continue;
728 #define READ_INT_UNIT(_name_, _unit_) if (read_int_unit_option(f, option, #_name_, value, &options->_name_, _unit_)) continue;
729 #define READ_CHAR(_name_) if (read_char_option(f, option, #_name_, value_all, &options->_name_)) continue;
730 #define READ_COLOR(_name_) if (read_color_option(f, option, #_name_, value, &options->_name_)) continue;
731
732 #define COMPAT_READ_BOOL(_oldname_, _name_) if (read_bool_option(f, option, #_oldname_, value, &options->_name_)) continue;
733 #define COMPAT_READ_INT(_oldname_, _name_) if (read_int_option(f, option, #_oldname_, value, &options->_name_)) continue;
734 #define COMPAT_READ_UINT(_oldname_, _name_) if (read_uint_option(f, option, #_oldname_, value, &options->_name_)) continue;
735 #define COMPAT_READ_INT_CLAMP(_oldname_, _name_, _min_, _max_) if (read_int_option_clamp(f, option, #_oldname_, value, &options->_name_, _min_, _max_)) continue;
736 #define COMPAT_READ_INT_UNIT(_oldname_, _name_, _unit_) if (read_int_unit_option(f, option, #_oldname_, value, &options->_name_, _unit_)) continue;
737 #define COMPAT_READ_CHAR(_oldname_, _name_) if (read_char_option(f, option, #_oldname_, value_all, &options->_name_)) continue;
738 #define COMPAT_READ_COLOR(_oldname_, _name_) if (read_color_option(f, option, #_oldname_, value, &options->_name_)) continue;
739
740                 /* general options */
741                 READ_BOOL(show_icon_names);
742                 READ_BOOL(show_copy_path);
743
744                 READ_BOOL(tree_descend_subdirs);
745                 READ_BOOL(lazy_image_sync);
746                 READ_BOOL(update_on_time_change);
747
748                 READ_UINT_CLAMP(duplicates_similarity_threshold, 0, 100);
749
750                 READ_BOOL(progressive_key_scrolling);
751
752                 READ_BOOL(enable_metadata_dirs);
753                 READ_BOOL(save_metadata_in_image_file);
754
755                 READ_BOOL(mousewheel_scrolls);
756
757                 READ_INT(open_recent_list_maxsize);
758                 READ_INT(dnd_icon_size);
759                 READ_BOOL(place_dialogs_under_mouse);
760
761                 /* startup options */
762                 
763                 COMPAT_READ_BOOL(startup_path_enable, startup.restore_path); /* 2008/05/11 */
764                 READ_BOOL(startup.restore_path);
765
766                 READ_BOOL(startup.use_last_path);
767
768                 COMPAT_READ_CHAR(startup_path, startup.path); /* 2008/05/11 */
769                 READ_CHAR(startup.path);
770         
771                 /* layout options */
772
773                 READ_INT(layout.style);
774                 READ_CHAR(layout.order);
775                 
776                 COMPAT_READ_UINT(layout.view_as_icons, layout.file_view_type); /* 2008/05/03 */
777
778                 READ_UINT(layout.dir_view_type);
779                 READ_UINT(layout.file_view_type);
780                 READ_BOOL(layout.show_marks);
781                 READ_BOOL(layout.show_thumbnails);
782                 READ_BOOL(layout.show_directory_date);
783                 READ_CHAR(layout.home_path);
784
785                 /* window positions */
786
787                 READ_BOOL(layout.save_window_positions);
788
789                 READ_INT(layout.main_window.x);
790                 READ_INT(layout.main_window.y);
791                 READ_INT(layout.main_window.w);
792                 READ_INT(layout.main_window.h);
793                 READ_BOOL(layout.main_window.maximized);
794                 READ_INT(layout.main_window.hdivider_pos);
795                 READ_INT(layout.main_window.vdivider_pos);
796
797                 READ_INT(layout.float_window.x);
798                 READ_INT(layout.float_window.y);
799                 READ_INT(layout.float_window.w);
800                 READ_INT(layout.float_window.h);
801                 READ_INT(layout.float_window.vdivider_pos);
802         
803                 READ_INT(layout.properties_window.w);
804                 READ_INT(layout.properties_window.h);
805
806                 READ_BOOL(layout.tools_float);
807                 READ_BOOL(layout.tools_hidden);
808                 READ_BOOL(layout.tools_restore_state);
809                 READ_BOOL(layout.toolbar_hidden);
810
811                 /* panels */
812                 READ_BOOL(panels.exif.enabled);
813                 READ_INT_CLAMP(panels.exif.width, PANEL_MIN_WIDTH, PANEL_MAX_WIDTH);
814                 READ_BOOL(panels.info.enabled);
815                 READ_INT_CLAMP(panels.info.width, PANEL_MIN_WIDTH, PANEL_MAX_WIDTH);
816                 READ_BOOL(panels.sort.enabled);
817                 READ_INT(panels.sort.action_state);
818                 READ_INT(panels.sort.mode_state);
819                 READ_INT(panels.sort.selection_state);
820
821                 /* properties dialog options */
822                 READ_CHAR(properties.tabs_order);
823
824                 /* image options */
825                 if (g_ascii_strcasecmp(option, "image.zoom_mode") == 0)
826                         {
827                         if (g_ascii_strcasecmp(value, "original") == 0)
828                                 options->image.zoom_mode = ZOOM_RESET_ORIGINAL;
829                         else if (g_ascii_strcasecmp(value, "fit") == 0)
830                                 options->image.zoom_mode = ZOOM_RESET_FIT_WINDOW;
831                         else if (g_ascii_strcasecmp(value, "dont_change") == 0)
832                                 options->image.zoom_mode = ZOOM_RESET_NONE;
833                         continue;
834                         }
835                 READ_BOOL(image.zoom_2pass);
836                 READ_BOOL(image.zoom_to_fit_allow_expand);
837                 READ_BOOL(image.fit_window_to_image);
838                 READ_BOOL(image.limit_window_size);
839                 READ_INT(image.max_window_size);
840                 READ_BOOL(image.limit_autofit_size);
841                 READ_INT(image.max_autofit_size);
842                 READ_UINT_CLAMP(image.scroll_reset_method, 0, PR_SCROLL_RESET_COUNT - 1);
843                 READ_INT(image.tile_cache_max);
844                 READ_INT(image.image_cache_max);
845                 READ_UINT_CLAMP(image.zoom_quality, GDK_INTERP_NEAREST, GDK_INTERP_HYPER);
846                 READ_UINT_CLAMP(image.dither_quality, GDK_RGB_DITHER_NONE, GDK_RGB_DITHER_MAX);
847                 READ_INT(image.zoom_increment);
848                 READ_BOOL(image.enable_read_ahead);
849                 READ_BOOL(image.exif_rotate_enable);
850                 READ_BOOL(image.use_custom_border_color);
851                 READ_COLOR(image.border_color);
852                 READ_INT_CLAMP(image.read_buffer_size, IMAGE_LOADER_READ_BUFFER_SIZE_MIN, IMAGE_LOADER_READ_BUFFER_SIZE_MAX);
853                 READ_INT_CLAMP(image.idle_read_loop_count, IMAGE_LOADER_IDLE_READ_LOOP_COUNT_MIN, IMAGE_LOADER_IDLE_READ_LOOP_COUNT_MAX);
854
855
856                 /* thumbnails options */
857                 READ_INT_CLAMP(thumbnails.max_width, 16, 512);
858                 READ_INT_CLAMP(thumbnails.max_height, 16, 512);
859
860                 READ_BOOL(thumbnails.enable_caching);
861                 READ_BOOL(thumbnails.cache_into_dirs);
862                 READ_BOOL(thumbnails.fast);
863                 READ_BOOL(thumbnails.use_xvpics);
864                 READ_BOOL(thumbnails.spec_standard);
865                 READ_UINT_CLAMP(thumbnails.quality, GDK_INTERP_NEAREST, GDK_INTERP_HYPER);
866
867                 /* file sorting options */
868                 READ_UINT(file_sort.method);
869                 READ_BOOL(file_sort.ascending);
870                 READ_BOOL(file_sort.case_sensitive);
871
872                 /* file operations options */
873                 READ_BOOL(file_ops.enable_in_place_rename);
874                 READ_BOOL(file_ops.confirm_delete);
875                 READ_BOOL(file_ops.enable_delete_key);
876                 READ_BOOL(file_ops.safe_delete_enable);
877                 READ_CHAR(file_ops.safe_delete_path);
878                 READ_INT(file_ops.safe_delete_folder_maxsize);
879
880                 /* fullscreen options */
881                 READ_INT(fullscreen.screen);
882                 READ_BOOL(fullscreen.clean_flip);
883                 READ_BOOL(fullscreen.disable_saver);
884                 READ_BOOL(fullscreen.above);
885
886                 /* histogram */
887                 READ_UINT(histogram.last_channel_mode);
888                 READ_UINT(histogram.last_log_mode);
889
890                 /* image overlay */
891                 COMPAT_READ_UINT(image_overlay.common.enabled, image_overlay.common.state); /* 2008-05-12 */
892                 READ_UINT(image_overlay.common.state);
893                 COMPAT_READ_BOOL(fullscreen.show_info, image_overlay.common.show_at_startup); /* 2008-04-21 */
894                 READ_BOOL(image_overlay.common.show_at_startup);
895                 COMPAT_READ_CHAR(fullscreen.info, image_overlay.common.template_string); /* 2008-04-21 */
896                 READ_CHAR(image_overlay.common.template_string);
897
898                 READ_INT(image_overlay.common.x);
899                 READ_INT(image_overlay.common.y);
900
901
902                 /* slideshow options */
903                 READ_INT_UNIT(slideshow.delay, SLIDESHOW_SUBSECOND_PRECISION);
904                 READ_BOOL(slideshow.random);
905                 READ_BOOL(slideshow.repeat);
906
907                 /* collection options */
908
909                 READ_BOOL(collections.rectangular_selection);
910
911                 /* filtering options */
912
913                 READ_BOOL(file_filter.show_hidden_files);
914                 READ_BOOL(file_filter.show_dot_directory);
915                 READ_BOOL(file_filter.disable);
916
917                 if (g_ascii_strcasecmp(option, "file_filter.ext") == 0)
918                         {
919                         filter_parse(value_all);
920                         continue;
921                         }
922
923                 if (g_ascii_strcasecmp(option, "sidecar.ext") == 0)
924                         {
925                         sidecar_ext_parse(value_all, TRUE);
926                         continue;
927                         }
928
929                 /* Color Profiles */
930
931                 READ_BOOL(color_profile.enabled);
932                 READ_BOOL(color_profile.use_image);
933                 READ_INT(color_profile.input_type);
934
935                 if (is_numbered_option(option, "color_profile.input_file_", &i))
936                         {
937                         if (i > 0 && i <= COLOR_PROFILE_INPUTS)
938                                 {
939                                 i--;
940                                 read_char_option(f, option, option, value, &options->color_profile.input_file[i]);
941                                 }
942                         continue;
943                         }
944
945                 if (is_numbered_option(option, "color_profile.input_name_", &i))
946                         {
947                         if (i > 0 && i <= COLOR_PROFILE_INPUTS)
948                                 {
949                                 i--;
950                                 read_char_option(f, option, option, value, &options->color_profile.input_name[i]);
951                                 }
952                         continue;
953                         }
954
955                 READ_INT(color_profile.screen_type);
956                 READ_CHAR(color_profile.screen_file);
957
958                 /* Shell command */
959                 READ_CHAR(shell.path);
960                 READ_CHAR(shell.options);
961
962                 /* Helpers */
963                 READ_CHAR(helpers.html_browser.command_name);
964                 READ_CHAR(helpers.html_browser.command_line);
965
966                 /* External Programs */
967
968                 if (is_numbered_option(option, "external_", &i))
969                         {
970                         if (i > 0 && i <= GQ_EDITOR_SLOTS)
971                                 {
972                                 const gchar *ptr;
973
974                                 i--;
975                                 editor_set_name(i, quoted_value(value_all, &ptr));
976                                 editor_set_command(i, quoted_value(ptr, NULL));
977                                 }
978                         continue;
979                         }
980
981                 /* Exif */
982                 if (0 == g_ascii_strncasecmp(option, "exif.display.", 13))
983                         {
984                         for (i = 0; ExifUIList[i].key; i++)
985                                 if (0 == g_ascii_strcasecmp(option + 13, ExifUIList[i].key))
986                                         ExifUIList[i].current = strtol(value, NULL, 10);
987                         continue;
988                         }
989                 
990                 /* Documentation */
991                 READ_CHAR(documentation.helpdir);
992                 READ_CHAR(documentation.htmldir);
993                 
994                 }
995
996         fclose(f);
997         return TRUE;
998 }
999
1000 void load_options(ConfOptions *options)
1001 {
1002         gboolean success;
1003         gchar *rc_path;
1004
1005         if (isdir(GQ_SYSTEM_WIDE_DIR))
1006                 {
1007                 rc_path = g_build_filename(GQ_SYSTEM_WIDE_DIR, RC_FILE_NAME, NULL);
1008                 success = load_options_from(rc_path, options);
1009                 DEBUG_1("Loading options from %s ... %s", rc_path, success ? "done" : "failed");
1010                 g_free(rc_path);
1011                 }
1012         
1013         rc_path = g_build_filename(homedir(), GQ_RC_DIR, RC_FILE_NAME, NULL);
1014         success = load_options_from(rc_path, options);
1015         DEBUG_1("Loading options from %s ... %s", rc_path, success ? "done" : "failed");
1016         g_free(rc_path);
1017 }