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