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