Use a specific enum for image.zoom_mode values (ZoomMode) and
[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_SEPARATOR();
393
394         WRITE_BOOL(layout.save_window_positions);
395         WRITE_SEPARATOR();
396
397         WRITE_INT(layout.main_window.x);
398         WRITE_INT(layout.main_window.y);
399         WRITE_INT(layout.main_window.w);
400         WRITE_INT(layout.main_window.h);
401         WRITE_BOOL(layout.main_window.maximized);
402         WRITE_INT(layout.main_window.hdivider_pos);
403         WRITE_INT(layout.main_window.vdivider_pos);
404         WRITE_SEPARATOR();
405
406         WRITE_INT(layout.float_window.x);
407         WRITE_INT(layout.float_window.y);
408         WRITE_INT(layout.float_window.w);
409         WRITE_INT(layout.float_window.h);
410         WRITE_INT(layout.float_window.vdivider_pos);
411         WRITE_SEPARATOR();
412
413         WRITE_INT(layout.properties_window.w);
414         WRITE_INT(layout.properties_window.h);
415         WRITE_SEPARATOR();
416
417         WRITE_BOOL(layout.tools_float);
418         WRITE_BOOL(layout.tools_hidden);
419         WRITE_BOOL(layout.tools_restore_state);
420         WRITE_SEPARATOR();
421
422         WRITE_BOOL(layout.toolbar_hidden);
423
424         WRITE_SUBTITLE("Panels Options");
425
426         WRITE_BOOL(panels.exif.enabled);
427         WRITE_INT(panels.exif.width);
428         WRITE_BOOL(panels.info.enabled);
429         WRITE_INT(panels.info.width);
430         WRITE_BOOL(panels.sort.enabled);
431         WRITE_INT(panels.sort.action_state);
432         WRITE_INT(panels.sort.mode_state);
433         WRITE_INT(panels.sort.selection_state);
434
435         WRITE_SUBTITLE("Properties dialog Options");
436         WRITE_CHAR(properties.tabs_order);
437
438         WRITE_SUBTITLE("Image Options");
439
440         secure_fprintf(ssi, "# image.zoom_mode possible values are:\n"
441                             "#   original\n"
442                             "#   fit\n"
443                             "#   dont_change\n");
444         secure_fprintf(ssi, "image.zoom_mode: ");
445         switch (options->image.zoom_mode)
446         {
447         case ZOOM_RESET_ORIGINAL: secure_fprintf(ssi, "original\n"); break;
448         case ZOOM_RESET_FIT_WINDOW: secure_fprintf(ssi, "fit\n"); break;
449         case ZOOM_RESET_NONE: secure_fprintf(ssi, "dont_change\n"); break;
450         }
451         WRITE_SEPARATOR();
452         WRITE_BOOL(image.zoom_2pass);
453         WRITE_BOOL(image.zoom_to_fit_allow_expand);
454         WRITE_UINT(image.zoom_quality);
455         WRITE_INT(image.zoom_increment);
456         WRITE_BOOL(image.fit_window_to_image);
457         WRITE_BOOL(image.limit_window_size);
458         WRITE_INT(image.max_window_size);
459         WRITE_BOOL(image.limit_autofit_size);
460         WRITE_INT(image.max_autofit_size);
461         WRITE_UINT(image.scroll_reset_method);
462         WRITE_INT(image.tile_cache_max);
463         WRITE_INT(image.image_cache_max);
464         WRITE_UINT(image.dither_quality);
465         WRITE_BOOL(image.enable_read_ahead);
466         WRITE_BOOL(image.exif_rotate_enable);
467         WRITE_BOOL(image.use_custom_border_color);
468         WRITE_COLOR(image.border_color);
469         WRITE_INT(image.read_buffer_size);
470         WRITE_INT(image.idle_read_loop_count);
471
472         WRITE_SUBTITLE("Thumbnails Options");
473
474         WRITE_INT(thumbnails.max_width);
475         WRITE_INT(thumbnails.max_height);
476         WRITE_BOOL(thumbnails.enable_caching);
477         WRITE_BOOL(thumbnails.cache_into_dirs);
478         WRITE_BOOL(thumbnails.fast);
479         WRITE_BOOL(thumbnails.use_xvpics);
480         WRITE_BOOL(thumbnails.spec_standard);
481         WRITE_UINT(thumbnails.quality);
482
483
484         WRITE_SUBTITLE("File sorting Options");
485
486         WRITE_INT(file_sort.method);
487         WRITE_BOOL(file_sort.ascending);
488         WRITE_BOOL(file_sort.case_sensitive);
489
490
491         WRITE_SUBTITLE("Fullscreen Options");
492
493         WRITE_INT(fullscreen.screen);
494         WRITE_BOOL(fullscreen.clean_flip);
495         WRITE_BOOL(fullscreen.disable_saver);
496         WRITE_BOOL(fullscreen.above);
497
498
499         WRITE_SUBTITLE("Histogram Options");
500         WRITE_UINT(histogram.last_channel_mode);
501         WRITE_UINT(histogram.last_log_mode);
502
503
504         WRITE_SUBTITLE("Image Overlay Options");
505         WRITE_UINT(image_overlay.common.state);
506         WRITE_BOOL(image_overlay.common.show_at_startup);
507         WRITE_CHAR(image_overlay.common.template_string);
508         WRITE_SEPARATOR();
509
510         secure_fprintf(ssi, "# these are relative positions:\n");
511         secure_fprintf(ssi, "# x >= 0: |x| pixels from left border\n");
512         secure_fprintf(ssi, "# x < 0 : |x| pixels from right border\n");
513         secure_fprintf(ssi, "# y >= 0: |y| pixels from top border\n");
514         secure_fprintf(ssi, "# y < 0 : |y| pixels from bottom border\n");
515         WRITE_INT(image_overlay.common.x);
516         WRITE_INT(image_overlay.common.y);
517
518
519         WRITE_SUBTITLE("Slideshow Options");
520
521         WRITE_INT_UNIT(slideshow.delay, SLIDESHOW_SUBSECOND_PRECISION);
522         WRITE_BOOL(slideshow.random);
523         WRITE_BOOL(slideshow.repeat);
524
525
526         WRITE_SUBTITLE("Collection Options");
527
528         WRITE_BOOL(collections.rectangular_selection);
529
530
531         WRITE_SUBTITLE("Filtering Options");
532
533         WRITE_BOOL(file_filter.show_hidden_files);
534         WRITE_BOOL(file_filter.show_dot_directory);
535         WRITE_BOOL(file_filter.disable);
536         WRITE_SEPARATOR();
537
538         filter_write_list(ssi);
539
540
541         WRITE_SUBTITLE("Sidecars Options");
542
543         sidecar_ext_write(ssi);
544
545
546         WRITE_SUBTITLE("Color Profiles");
547
548 #ifndef HAVE_LCMS
549         secure_fprintf(ssi, "# NOTICE: %s was not built with support for color profiles,\n"
550                             "#         color profile options will have no effect.\n\n", GQ_APPNAME);
551 #endif
552
553         WRITE_BOOL(color_profile.enabled);
554         WRITE_BOOL(color_profile.use_image);
555         WRITE_INT(color_profile.input_type);
556         WRITE_SEPARATOR();
557
558         for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
559                 {
560                 gchar *buf;
561
562                 buf = g_strdup_printf("color_profile.input_file_%d", i + 1);
563                 write_char_option(ssi, buf, options->color_profile.input_file[i]);
564                 g_free(buf);
565
566                 buf = g_strdup_printf("color_profile.input_name_%d", i + 1);
567                 write_char_option(ssi, buf, options->color_profile.input_name[i]);
568                 g_free(buf);
569                 }
570
571         WRITE_SEPARATOR();
572         WRITE_INT(color_profile.screen_type);
573         WRITE_CHAR(color_profile.screen_file);
574
575
576         WRITE_SUBTITLE("Shell command");
577         WRITE_CHAR(shell.path);
578         WRITE_CHAR(shell.options);
579
580
581         WRITE_SUBTITLE("Helpers");
582         secure_fprintf(ssi, "# Html browser\n");
583         secure_fprintf(ssi, "# command_name is: the binary's name to look for in the path\n");
584         secure_fprintf(ssi, "# If command_name is empty, the program will try various common html browsers\n");
585         secure_fprintf(ssi, "# command_line is:\n");
586         secure_fprintf(ssi, "# \"\" (empty string)  = execute binary with html file path as command line\n");
587         secure_fprintf(ssi, "# \"string\"           = execute string and use results for command line\n");
588         secure_fprintf(ssi, "# \"!string\"          = use text following ! as command line, replacing optional %%s with html file path\n");
589         WRITE_CHAR(helpers.html_browser.command_name);
590         WRITE_CHAR(helpers.html_browser.command_line);
591
592
593         WRITE_SUBTITLE("External Programs");
594         secure_fprintf(ssi, "# Maximum of %d programs (external_1 through external_%d)\n", GQ_EDITOR_GENERIC_SLOTS, GQ_EDITOR_GENERIC_SLOTS);
595         secure_fprintf(ssi, "# external_%d through external_%d are used for file ops\n", GQ_EDITOR_GENERIC_SLOTS + 1, GQ_EDITOR_SLOTS);
596         secure_fprintf(ssi, "# format: external_n: \"menu name\" \"command line\"\n\n");
597
598         for (i = 0; i < GQ_EDITOR_SLOTS; i++)
599                 {
600                 if (i == GQ_EDITOR_GENERIC_SLOTS) secure_fputc(ssi, '\n');
601                 gchar *qname = escquote_value(options->editor[i].name);
602                 gchar *qcommand = escquote_value(options->editor[i].command);
603                 secure_fprintf(ssi, "external_%d: %s %s\n", i+1, qname, qcommand);
604                 g_free(qname);
605                 g_free(qcommand);
606                 }
607
608
609         WRITE_SUBTITLE("Exif Options");
610         secure_fprintf(ssi, "# Display: 0: never\n"
611                             "#          1: if set\n"
612                             "#          2: always\n\n");
613         for (i = 0; ExifUIList[i].key; i++)
614                 {
615                 secure_fprintf(ssi, "exif.display.");
616                 write_int_option(ssi, (gchar *)ExifUIList[i].key, ExifUIList[i].current);
617                 }
618
619         WRITE_SEPARATOR();
620         WRITE_SEPARATOR();
621
622         secure_fprintf(ssi, "######################################################################\n");
623         secure_fprintf(ssi, "#                         end of config file                         #\n");
624         secure_fprintf(ssi, "######################################################################\n");
625
626
627         if (secure_close(ssi))
628                 {
629                 log_printf(_("error saving config file: %s\nerror: %s\n"), utf8_path,
630                            secsave_strerror(secsave_errno));
631                 return FALSE;
632                 }
633
634         return TRUE;
635 }
636
637 void save_options(ConfOptions *options)
638 {
639         gchar *rc_path;
640
641         rc_path = g_build_filename(homedir(), GQ_RC_DIR, RC_FILE_NAME, NULL);
642         save_options_to(rc_path, options);
643         g_free(rc_path);
644 }
645
646
647
648 /*
649  *-----------------------------------------------------------------------------
650  * load configuration (public)
651  *-----------------------------------------------------------------------------
652  */
653
654 static gboolean is_numbered_option(const gchar *option, const gchar *prefix, gint *number)
655 {
656         gsize n;
657         gsize option_len = strlen(option);
658         gsize prefix_len = strlen(prefix);
659         
660         if (option_len <= prefix_len) return FALSE;
661         if (g_ascii_strncasecmp(option, prefix, prefix_len) != 0) return FALSE;
662
663         n = prefix_len;
664         while (g_ascii_isdigit(option[n])) n++;
665         if (n < option_len) return FALSE;
666         
667         if (number) *number = atoi(option + prefix_len);
668         return TRUE;
669 }
670
671 #define OPTION_READ_BUFFER_SIZE 1024
672
673 static gboolean load_options_from(const gchar *utf8_path, ConfOptions *options)
674 {
675         FILE *f;
676         gchar *rc_pathl;
677         gchar s_buf[OPTION_READ_BUFFER_SIZE];
678         gchar value_all[OPTION_READ_BUFFER_SIZE];
679         gchar *option;
680         gchar *value;
681         gint i;
682
683         rc_pathl = path_from_utf8(utf8_path);
684         f = fopen(rc_pathl,"r");
685         g_free(rc_pathl);
686         if (!f) return FALSE;
687
688         while (fgets(s_buf, sizeof(s_buf), f))
689                 {
690                 gchar *value_end;
691                 gchar *p = s_buf;
692
693                 /* skip empty lines and comments */
694                 while (g_ascii_isspace(*p)) p++;
695                 if (!*p || *p == '\n' || *p == '#') continue;
696
697                 /* parse option name */
698                 option = p;
699                 while (g_ascii_isalnum(*p) || *p == '_' || *p == '.') p++;
700                 if (!*p) continue;
701                 *p = '\0';
702                 p++;
703
704                 /* search for value start, name and value are normally separated by ': '
705                  * but we allow relaxed syntax here, so '=', ':=' or just a tab will work too */
706                 while (*p == ':' || g_ascii_isspace(*p) || *p == '=') p++;
707                 value = p;
708
709                 while (*p && !g_ascii_isspace(*p) && *p != '\n') p++;
710                 value_end = p; /* value part up to the first whitespace or end of line */
711                 while (*p != '\0') p++;
712                 memcpy(value_all, value, 1 + p - value);
713
714                 *value_end = '\0';
715
716 #define READ_BOOL(_name_) if (read_bool_option(f, option, #_name_, value, &options->_name_)) continue;
717 #define READ_INT(_name_) if (read_int_option(f, option, #_name_, value, &options->_name_)) continue;
718 #define READ_UINT(_name_) if (read_uint_option(f, option, #_name_, value, &options->_name_)) continue;
719 #define READ_INT_CLAMP(_name_, _min_, _max_) if (read_int_option_clamp(f, option, #_name_, value, &options->_name_, _min_, _max_)) continue;
720 #define READ_UINT_CLAMP(_name_, _min_, _max_) if (read_uint_option_clamp(f, option, #_name_, value, &options->_name_, _min_, _max_)) continue;
721 #define READ_INT_UNIT(_name_, _unit_) if (read_int_unit_option(f, option, #_name_, value, &options->_name_, _unit_)) continue;
722 #define READ_CHAR(_name_) if (read_char_option(f, option, #_name_, value_all, &options->_name_)) continue;
723 #define READ_COLOR(_name_) if (read_color_option(f, option, #_name_, value, &options->_name_)) continue;
724
725 #define COMPAT_READ_BOOL(_oldname_, _name_) if (read_bool_option(f, option, #_oldname_, value, &options->_name_)) continue;
726 #define COMPAT_READ_INT(_oldname_, _name_) if (read_int_option(f, option, #_oldname_, value, &options->_name_)) continue;
727 #define COMPAT_READ_UINT(_oldname_, _name_) if (read_uint_option(f, option, #_oldname_, value, &options->_name_)) continue;
728 #define COMPAT_READ_INT_CLAMP(_oldname_, _name_, _min_, _max_) if (read_int_option_clamp(f, option, #_oldname_, value, &options->_name_, _min_, _max_)) continue;
729 #define COMPAT_READ_INT_UNIT(_oldname_, _name_, _unit_) if (read_int_unit_option(f, option, #_oldname_, value, &options->_name_, _unit_)) continue;
730 #define COMPAT_READ_CHAR(_oldname_, _name_) if (read_char_option(f, option, #_oldname_, value_all, &options->_name_)) continue;
731 #define COMPAT_READ_COLOR(_oldname_, _name_) if (read_color_option(f, option, #_oldname_, value, &options->_name_)) continue;
732
733                 /* general options */
734                 READ_BOOL(show_icon_names);
735                 READ_BOOL(show_copy_path);
736
737                 READ_BOOL(tree_descend_subdirs);
738                 READ_BOOL(lazy_image_sync);
739                 READ_BOOL(update_on_time_change);
740
741                 READ_UINT_CLAMP(duplicates_similarity_threshold, 0, 100);
742
743                 READ_BOOL(progressive_key_scrolling);
744
745                 READ_BOOL(enable_metadata_dirs);
746                 READ_BOOL(save_metadata_in_image_file);
747
748                 READ_BOOL(mousewheel_scrolls);
749
750                 READ_INT(open_recent_list_maxsize);
751                 READ_INT(dnd_icon_size);
752                 READ_BOOL(place_dialogs_under_mouse);
753
754                 /* startup options */
755                 
756                 COMPAT_READ_BOOL(startup_path_enable, startup.restore_path); /* 2008/05/11 */
757                 READ_BOOL(startup.restore_path);
758
759                 READ_BOOL(startup.use_last_path);
760
761                 COMPAT_READ_CHAR(startup_path, startup.path); /* 2008/05/11 */
762                 READ_CHAR(startup.path);
763         
764                 /* layout options */
765
766                 READ_INT(layout.style);
767                 READ_CHAR(layout.order);
768                 
769                 COMPAT_READ_UINT(layout.view_as_icons, layout.file_view_type); /* 2008/05/03 */
770
771                 READ_UINT(layout.dir_view_type);
772                 READ_UINT(layout.file_view_type);
773                 READ_BOOL(layout.show_marks);
774                 READ_BOOL(layout.show_thumbnails);
775                 READ_BOOL(layout.show_directory_date);
776
777                 /* window positions */
778
779                 READ_BOOL(layout.save_window_positions);
780
781                 READ_INT(layout.main_window.x);
782                 READ_INT(layout.main_window.y);
783                 READ_INT(layout.main_window.w);
784                 READ_INT(layout.main_window.h);
785                 READ_BOOL(layout.main_window.maximized);
786                 READ_INT(layout.main_window.hdivider_pos);
787                 READ_INT(layout.main_window.vdivider_pos);
788
789                 READ_INT(layout.float_window.x);
790                 READ_INT(layout.float_window.y);
791                 READ_INT(layout.float_window.w);
792                 READ_INT(layout.float_window.h);
793                 READ_INT(layout.float_window.vdivider_pos);
794         
795                 READ_INT(layout.properties_window.w);
796                 READ_INT(layout.properties_window.h);
797
798                 READ_BOOL(layout.tools_float);
799                 READ_BOOL(layout.tools_hidden);
800                 READ_BOOL(layout.tools_restore_state);
801                 READ_BOOL(layout.toolbar_hidden);
802
803                 /* panels */
804                 READ_BOOL(panels.exif.enabled);
805                 READ_INT_CLAMP(panels.exif.width, PANEL_MIN_WIDTH, PANEL_MAX_WIDTH);
806                 READ_BOOL(panels.info.enabled);
807                 READ_INT_CLAMP(panels.info.width, PANEL_MIN_WIDTH, PANEL_MAX_WIDTH);
808                 READ_BOOL(panels.sort.enabled);
809                 READ_INT(panels.sort.action_state);
810                 READ_INT(panels.sort.mode_state);
811                 READ_INT(panels.sort.selection_state);
812
813                 /* properties dialog options */
814                 READ_CHAR(properties.tabs_order);
815
816                 /* image options */
817                 if (g_ascii_strcasecmp(option, "image.zoom_mode") == 0)
818                         {
819                         if (g_ascii_strcasecmp(value, "original") == 0)
820                                 options->image.zoom_mode = ZOOM_RESET_ORIGINAL;
821                         else if (g_ascii_strcasecmp(value, "fit") == 0)
822                                 options->image.zoom_mode = ZOOM_RESET_FIT_WINDOW;
823                         else if (g_ascii_strcasecmp(value, "dont_change") == 0)
824                                 options->image.zoom_mode = ZOOM_RESET_NONE;
825                         continue;
826                         }
827                 READ_BOOL(image.zoom_2pass);
828                 READ_BOOL(image.zoom_to_fit_allow_expand);
829                 READ_BOOL(image.fit_window_to_image);
830                 READ_BOOL(image.limit_window_size);
831                 READ_INT(image.max_window_size);
832                 READ_BOOL(image.limit_autofit_size);
833                 READ_INT(image.max_autofit_size);
834                 READ_UINT_CLAMP(image.scroll_reset_method, 0, PR_SCROLL_RESET_COUNT - 1);
835                 READ_INT(image.tile_cache_max);
836                 READ_INT(image.image_cache_max);
837                 READ_UINT_CLAMP(image.zoom_quality, GDK_INTERP_NEAREST, GDK_INTERP_HYPER);
838                 READ_UINT_CLAMP(image.dither_quality, GDK_RGB_DITHER_NONE, GDK_RGB_DITHER_MAX);
839                 READ_INT(image.zoom_increment);
840                 READ_BOOL(image.enable_read_ahead);
841                 READ_BOOL(image.exif_rotate_enable);
842                 READ_BOOL(image.use_custom_border_color);
843                 READ_COLOR(image.border_color);
844                 READ_INT_CLAMP(image.read_buffer_size, IMAGE_LOADER_READ_BUFFER_SIZE_MIN, IMAGE_LOADER_READ_BUFFER_SIZE_MAX);
845                 READ_INT_CLAMP(image.idle_read_loop_count, IMAGE_LOADER_IDLE_READ_LOOP_COUNT_MIN, IMAGE_LOADER_IDLE_READ_LOOP_COUNT_MAX);
846
847
848                 /* thumbnails options */
849                 READ_INT_CLAMP(thumbnails.max_width, 16, 512);
850                 READ_INT_CLAMP(thumbnails.max_height, 16, 512);
851
852                 READ_BOOL(thumbnails.enable_caching);
853                 READ_BOOL(thumbnails.cache_into_dirs);
854                 READ_BOOL(thumbnails.fast);
855                 READ_BOOL(thumbnails.use_xvpics);
856                 READ_BOOL(thumbnails.spec_standard);
857                 READ_UINT_CLAMP(thumbnails.quality, GDK_INTERP_NEAREST, GDK_INTERP_HYPER);
858
859                 /* file sorting options */
860                 READ_UINT(file_sort.method);
861                 READ_BOOL(file_sort.ascending);
862                 READ_BOOL(file_sort.case_sensitive);
863
864                 /* file operations options */
865                 READ_BOOL(file_ops.enable_in_place_rename);
866                 READ_BOOL(file_ops.confirm_delete);
867                 READ_BOOL(file_ops.enable_delete_key);
868                 READ_BOOL(file_ops.safe_delete_enable);
869                 READ_CHAR(file_ops.safe_delete_path);
870                 READ_INT(file_ops.safe_delete_folder_maxsize);
871
872                 /* fullscreen options */
873                 READ_INT(fullscreen.screen);
874                 READ_BOOL(fullscreen.clean_flip);
875                 READ_BOOL(fullscreen.disable_saver);
876                 READ_BOOL(fullscreen.above);
877
878                 /* histogram */
879                 READ_UINT(histogram.last_channel_mode);
880                 READ_UINT(histogram.last_log_mode);
881
882                 /* image overlay */
883                 COMPAT_READ_UINT(image_overlay.common.enabled, image_overlay.common.state); /* 2008-05-12 */ 
884                 READ_UINT(image_overlay.common.state);
885                 COMPAT_READ_BOOL(fullscreen.show_info, image_overlay.common.show_at_startup); /* 2008-04-21 */
886                 READ_BOOL(image_overlay.common.show_at_startup);
887                 COMPAT_READ_CHAR(fullscreen.info, image_overlay.common.template_string); /* 2008-04-21 */
888                 READ_CHAR(image_overlay.common.template_string);
889
890                 READ_INT(image_overlay.common.x);
891                 READ_INT(image_overlay.common.y);
892
893
894                 /* slideshow options */
895                 READ_INT_UNIT(slideshow.delay, SLIDESHOW_SUBSECOND_PRECISION);
896                 READ_BOOL(slideshow.random);
897                 READ_BOOL(slideshow.repeat);
898
899                 /* collection options */
900
901                 READ_BOOL(collections.rectangular_selection);
902
903                 /* filtering options */
904
905                 READ_BOOL(file_filter.show_hidden_files);
906                 READ_BOOL(file_filter.show_dot_directory);
907                 READ_BOOL(file_filter.disable);
908
909                 if (g_ascii_strcasecmp(option, "file_filter.ext") == 0)
910                         {
911                         filter_parse(value_all);
912                         continue;
913                         }
914
915                 if (g_ascii_strcasecmp(option, "sidecar.ext") == 0)
916                         {
917                         sidecar_ext_parse(value_all, TRUE);
918                         continue;
919                         }
920
921                 /* Color Profiles */
922
923                 READ_BOOL(color_profile.enabled);
924                 READ_BOOL(color_profile.use_image);
925                 READ_INT(color_profile.input_type);
926
927                 if (is_numbered_option(option, "color_profile.input_file_", &i))
928                         {
929                         if (i > 0 && i <= COLOR_PROFILE_INPUTS)
930                                 {
931                                 i--;
932                                 read_char_option(f, option, option, value, &options->color_profile.input_file[i]);
933                                 }
934                         continue;
935                         }
936
937                 if (is_numbered_option(option, "color_profile.input_name_", &i))
938                         {
939                         if (i > 0 && i <= COLOR_PROFILE_INPUTS)
940                                 {
941                                 i--;
942                                 read_char_option(f, option, option, value, &options->color_profile.input_name[i]);
943                                 }
944                         continue;
945                         }
946
947                 READ_INT(color_profile.screen_type);
948                 READ_CHAR(color_profile.screen_file);
949
950                 /* Shell command */
951                 READ_CHAR(shell.path);
952                 READ_CHAR(shell.options);
953
954                 /* Helpers */
955                 READ_CHAR(helpers.html_browser.command_name);
956                 READ_CHAR(helpers.html_browser.command_line);
957
958                 /* External Programs */
959
960                 if (is_numbered_option(option, "external_", &i))
961                         {
962                         if (i > 0 && i <= GQ_EDITOR_SLOTS)
963                                 {
964                                 const gchar *ptr;
965
966                                 i--;
967                                 editor_set_name(i, quoted_value(value_all, &ptr));
968                                 editor_set_command(i, quoted_value(ptr, NULL));
969                                 }
970                         continue;
971                         }
972
973                 /* Exif */
974                 if (0 == g_ascii_strncasecmp(option, "exif.display.", 13))
975                         {
976                         for (i = 0; ExifUIList[i].key; i++)
977                                 if (0 == g_ascii_strcasecmp(option + 13, ExifUIList[i].key))
978                                         ExifUIList[i].current = strtol(value, NULL, 10);
979                         continue;
980                         }
981                 }
982
983         fclose(f);
984         return TRUE;
985 }
986
987 void load_options(ConfOptions *options)
988 {
989         gboolean success;
990         gchar *rc_path;
991
992         if (isdir(GQ_SYSTEM_WIDE_DIR))
993                 {
994                 rc_path = g_build_filename(GQ_SYSTEM_WIDE_DIR, RC_FILE_NAME, NULL);
995                 success = load_options_from(rc_path, options);
996                 DEBUG_1("Loading options from %s ... %s", rc_path, success ? "done" : "failed");
997                 g_free(rc_path);
998                 }
999         
1000         rc_path = g_build_filename(homedir(), GQ_RC_DIR, RC_FILE_NAME, NULL);
1001         success = load_options_from(rc_path, options);
1002         DEBUG_1("Loading options from %s ... %s", rc_path, success ? "done" : "failed");
1003         g_free(rc_path);
1004 }