load_options_from():
[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] != '\n' && !g_ascii_isspace(text[c])) 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 #define OPTION_READ_BUFFER_SIZE 1024
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[OPTION_READ_BUFFER_SIZE];
652         gchar value_all[OPTION_READ_BUFFER_SIZE];
653         gchar *option;
654         gchar *value;
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 *value_end;
665                 gchar *p = s_buf;
666
667                 /* skip empty lines and comments */
668                 while (g_ascii_isspace(*p)) p++;
669                 if (!*p || *p == '\n' || *p == '#') continue;
670
671                 /* parse option name */
672                 option = p;
673                 while (g_ascii_isalnum(*p) || *p == '_' || *p == '.') p++;
674                 if (!*p) continue;
675                 *p = '\0';
676                 p++;
677
678                 /* search for value start, name and value are normally separated by ': '
679                  * but we allow relaxed syntax here, so '=', ':=' or just a tab will work too */
680                 while (*p == ':' || g_ascii_isspace(*p) || *p == '=') p++;
681                 value = p;
682
683                 while (*p && !g_ascii_isspace(*p) && *p != '\n') p++;
684                 value_end = p; /* value part up to the first whitespace or end of line */
685                 while (*p != '\0') p++;
686                 memcpy(value_all, value, 1 + p - value);
687
688                 *value_end = '\0';
689
690 #define READ_BOOL(_name_) if (read_bool_option(f, option, #_name_, value, &options->_name_)) continue;
691 #define READ_INT(_name_) if (read_int_option(f, option, #_name_, value, &options->_name_)) continue;
692 #define READ_UINT(_name_) if (read_uint_option(f, option, #_name_, value, &options->_name_)) continue;
693 #define READ_INT_CLAMP(_name_, _min_, _max_) if (read_int_option_clamp(f, option, #_name_, value, &options->_name_, _min_, _max_)) continue;
694 #define READ_INT_UNIT(_name_, _unit_) if (read_int_unit_option(f, option, #_name_, value, &options->_name_, _unit_)) continue;
695 #define READ_CHAR(_name_) if (read_char_option(f, option, #_name_, value_all, &options->_name_)) continue;
696 #define READ_COLOR(_name_) if (read_color_option(f, option, #_name_, value, &options->_name_)) continue;
697
698 #define COMPAT_READ_BOOL(_oldname_, _name_) if (read_bool_option(f, option, #_oldname_, value, &options->_name_)) continue;
699 #define COMPAT_READ_INT(_oldname_, _name_) if (read_int_option(f, option, #_oldname_, value, &options->_name_)) continue;
700 #define COMPAT_READ_UINT(_oldname_, _name_) if (read_uint_option(f, option, #_oldname_, value, &options->_name_)) continue;
701 #define COMPAT_READ_INT_CLAMP(_oldname_, _name_, _min_, _max_) if (read_int_option_clamp(f, option, #_oldname_, value, &options->_name_, _min_, _max_)) continue;
702 #define COMPAT_READ_INT_UNIT(_oldname_, _name_, _unit_) if (read_int_unit_option(f, option, #_oldname_, value, &options->_name_, _unit_)) continue;
703 #define COMPAT_READ_CHAR(_oldname_, _name_) if (read_char_option(f, option, #_oldname_, value_all, &options->_name_)) continue;
704 #define COMPAT_READ_COLOR(_oldname_, _name_) if (read_color_option(f, option, #_oldname_, value, &options->_name_)) continue;
705
706                 /* general options */
707                 READ_BOOL(show_icon_names);
708                 READ_BOOL(show_copy_path);
709
710                 READ_BOOL(tree_descend_subdirs);
711                 READ_BOOL(lazy_image_sync);
712                 READ_BOOL(update_on_time_change);
713
714                 READ_INT(duplicates_similarity_threshold);
715
716                 READ_BOOL(progressive_key_scrolling);
717
718                 READ_BOOL(enable_metadata_dirs);
719                 READ_BOOL(save_metadata_in_image_file);
720
721                 READ_BOOL(mousewheel_scrolls);
722
723                 READ_INT(open_recent_list_maxsize);
724                 READ_INT(dnd_icon_size);
725                 READ_BOOL(place_dialogs_under_mouse);
726
727                 /* startup options */
728                 
729                 COMPAT_READ_BOOL(startup_path_enable, startup.restore_path); /* 2008/05/11 */
730                 READ_BOOL(startup.restore_path);
731
732                 READ_BOOL(startup.use_last_path);
733
734                 COMPAT_READ_CHAR(startup_path, startup.path); /* 2008/05/11 */
735                 READ_CHAR(startup.path);
736         
737                 /* layout options */
738
739                 READ_INT(layout.style);
740                 READ_CHAR(layout.order);
741                 
742                 COMPAT_READ_UINT(layout.view_as_icons, layout.file_view_type); /* 2008/05/03 */
743
744                 READ_UINT(layout.dir_view_type);
745                 READ_UINT(layout.file_view_type);
746                 READ_BOOL(layout.show_marks);
747                 READ_BOOL(layout.show_thumbnails);
748
749                 /* window positions */
750
751                 READ_BOOL(layout.save_window_positions);
752
753                 READ_INT(layout.main_window.x);
754                 READ_INT(layout.main_window.y);
755                 READ_INT(layout.main_window.w);
756                 READ_INT(layout.main_window.h);
757                 READ_BOOL(layout.main_window.maximized);
758                 READ_INT(layout.main_window.hdivider_pos);
759                 READ_INT(layout.main_window.vdivider_pos);
760
761                 READ_INT(layout.float_window.x);
762                 READ_INT(layout.float_window.y);
763                 READ_INT(layout.float_window.w);
764                 READ_INT(layout.float_window.h);
765                 READ_INT(layout.float_window.vdivider_pos);
766         
767                 READ_INT(layout.properties_window.w);
768                 READ_INT(layout.properties_window.h);
769
770                 READ_BOOL(layout.tools_float);
771                 READ_BOOL(layout.tools_hidden);
772                 READ_BOOL(layout.tools_restore_state);
773                 READ_BOOL(layout.toolbar_hidden);
774
775                 /* panels */
776                 READ_BOOL(panels.exif.enabled);
777                 READ_INT_CLAMP(panels.exif.width, PANEL_MIN_WIDTH, PANEL_MAX_WIDTH);
778                 READ_BOOL(panels.info.enabled);
779                 READ_INT_CLAMP(panels.info.width, PANEL_MIN_WIDTH, PANEL_MAX_WIDTH);
780                 READ_BOOL(panels.sort.enabled);
781                 READ_INT(panels.sort.action_state);
782                 READ_INT(panels.sort.mode_state);
783                 READ_INT(panels.sort.selection_state);
784
785                 /* properties dialog options */
786                 READ_CHAR(properties.tabs_order);
787
788                 /* image options */
789                 if (g_ascii_strcasecmp(option, "image.zoom_mode") == 0)
790                         {
791                         if (g_ascii_strcasecmp(value, "original") == 0)
792                                 options->image.zoom_mode = ZOOM_RESET_ORIGINAL;
793                         else if (g_ascii_strcasecmp(value, "fit") == 0)
794                                 options->image.zoom_mode = ZOOM_RESET_FIT_WINDOW;
795                         else if (g_ascii_strcasecmp(value, "dont_change") == 0)
796                                 options->image.zoom_mode = ZOOM_RESET_NONE;
797                         continue;
798                         }
799                 READ_BOOL(image.zoom_2pass);
800                 READ_BOOL(image.zoom_to_fit_allow_expand);
801                 READ_BOOL(image.fit_window_to_image);
802                 READ_BOOL(image.limit_window_size);
803                 READ_INT(image.max_window_size);
804                 READ_BOOL(image.limit_autofit_size);
805                 READ_INT(image.max_autofit_size);
806                 READ_INT(image.scroll_reset_method);
807                 READ_INT(image.tile_cache_max);
808                 READ_INT_CLAMP(image.zoom_quality, GDK_INTERP_NEAREST, GDK_INTERP_HYPER);
809                 READ_INT_CLAMP(image.dither_quality, GDK_RGB_DITHER_NONE, GDK_RGB_DITHER_MAX);
810                 READ_INT(image.zoom_increment);
811                 READ_BOOL(image.enable_read_ahead);
812                 READ_BOOL(image.exif_rotate_enable);
813                 READ_BOOL(image.use_custom_border_color);
814                 READ_COLOR(image.border_color);
815                 READ_INT_CLAMP(image.read_buffer_size, IMAGE_LOADER_READ_BUFFER_SIZE_MIN, IMAGE_LOADER_READ_BUFFER_SIZE_MAX);
816                 READ_INT_CLAMP(image.idle_read_loop_count, IMAGE_LOADER_IDLE_READ_LOOP_COUNT_MIN, IMAGE_LOADER_IDLE_READ_LOOP_COUNT_MAX);
817
818
819                 /* thumbnails options */
820                 READ_INT_CLAMP(thumbnails.max_width, 16, 512);
821                 READ_INT_CLAMP(thumbnails.max_height, 16, 512);
822
823                 READ_BOOL(thumbnails.enable_caching);
824                 READ_BOOL(thumbnails.cache_into_dirs);
825                 READ_BOOL(thumbnails.fast);
826                 READ_BOOL(thumbnails.use_xvpics);
827                 READ_BOOL(thumbnails.spec_standard);
828                 READ_INT_CLAMP(thumbnails.quality, GDK_INTERP_NEAREST, GDK_INTERP_HYPER);
829
830                 /* file sorting options */
831                 READ_UINT(file_sort.method);
832                 READ_BOOL(file_sort.ascending);
833                 READ_BOOL(file_sort.case_sensitive);
834
835                 /* file operations options */
836                 READ_BOOL(file_ops.enable_in_place_rename);
837                 READ_BOOL(file_ops.confirm_delete);
838                 READ_BOOL(file_ops.enable_delete_key);
839                 READ_BOOL(file_ops.safe_delete_enable);
840                 READ_CHAR(file_ops.safe_delete_path);
841                 READ_INT(file_ops.safe_delete_folder_maxsize);
842
843                 /* fullscreen options */
844                 READ_INT(fullscreen.screen);
845                 READ_BOOL(fullscreen.clean_flip);
846                 READ_BOOL(fullscreen.disable_saver);
847                 READ_BOOL(fullscreen.above);
848
849                 /* histogram */
850                 READ_UINT(histogram.last_channel_mode);
851                 READ_UINT(histogram.last_log_mode);
852
853                 /* image overlay */
854                 COMPAT_READ_UINT(image_overlay.common.enabled, image_overlay.common.state); /* 2008-05-12 */ 
855                 READ_UINT(image_overlay.common.state);
856                 COMPAT_READ_BOOL(fullscreen.show_info, image_overlay.common.show_at_startup); /* 2008-04-21 */
857                 READ_BOOL(image_overlay.common.show_at_startup);
858                 COMPAT_READ_CHAR(fullscreen.info, image_overlay.common.template_string); /* 2008-04-21 */
859                 READ_CHAR(image_overlay.common.template_string);
860
861                 READ_INT(image_overlay.common.x);
862                 READ_INT(image_overlay.common.y);
863
864
865                 /* slideshow options */
866                 READ_INT_UNIT(slideshow.delay, SLIDESHOW_SUBSECOND_PRECISION);
867                 READ_BOOL(slideshow.random);
868                 READ_BOOL(slideshow.repeat);
869
870                 /* collection options */
871
872                 READ_BOOL(collections.rectangular_selection);
873
874                 /* filtering options */
875
876                 READ_BOOL(file_filter.show_hidden_files);
877                 READ_BOOL(file_filter.show_dot_directory);
878                 READ_BOOL(file_filter.disable);
879
880                 if (g_ascii_strcasecmp(option, "file_filter.ext") == 0)
881                         {
882                         filter_parse(value_all);
883                         continue;
884                         }
885
886                 if (g_ascii_strcasecmp(option, "sidecar.ext") == 0)
887                         {
888                         sidecar_ext_parse(value_all, TRUE);
889                         continue;
890                         }
891
892                 /* Color Profiles */
893
894                 READ_BOOL(color_profile.enabled);
895                 READ_BOOL(color_profile.use_image);
896                 READ_INT(color_profile.input_type);
897
898                 if (is_numbered_option(option, "color_profile.input_file_", &i))
899                         {
900                         if (i > 0 && i <= COLOR_PROFILE_INPUTS)
901                                 {
902                                 i--;
903                                 read_char_option(f, option, option, value, &options->color_profile.input_file[i]);
904                                 }
905                         continue;
906                         }
907
908                 if (is_numbered_option(option, "color_profile.input_name_", &i))
909                         {
910                         if (i > 0 && i <= COLOR_PROFILE_INPUTS)
911                                 {
912                                 i--;
913                                 read_char_option(f, option, option, value, &options->color_profile.input_name[i]);
914                                 }
915                         continue;
916                         }
917
918                 READ_INT(color_profile.screen_type);
919                 READ_CHAR(color_profile.screen_file);
920
921                 /* Shell command */
922                 READ_CHAR(shell.path);
923                 READ_CHAR(shell.options);
924
925                 /* External Programs */
926
927                 if (is_numbered_option(option, "external_", &i))
928                         {
929                         if (i > 0 && i <= GQ_EDITOR_SLOTS)
930                                 {
931                                 const gchar *ptr;
932
933                                 i--;
934                                 editor_set_name(i, quoted_value(value_all, &ptr));
935                                 editor_set_command(i, quoted_value(ptr, NULL));
936                                 }
937                         continue;
938                         }
939
940                 /* Exif */
941                 if (0 == g_ascii_strncasecmp(option, "exif.display.", 13))
942                         {
943                         for (i = 0; ExifUIList[i].key; i++)
944                                 if (0 == g_ascii_strcasecmp(option + 13, ExifUIList[i].key))
945                                         ExifUIList[i].current = strtol(value, NULL, 10);
946                         continue;
947                         }
948                 }
949
950         fclose(f);
951         return TRUE;
952 }
953
954 void load_options(ConfOptions *options)
955 {
956         gboolean success;
957         gchar *rc_path;
958
959         if (isdir(GQ_SYSTEM_WIDE_DIR))
960                 {
961                 rc_path = g_build_filename(GQ_SYSTEM_WIDE_DIR, RC_FILE_NAME, NULL);
962                 success = load_options_from(rc_path, options);
963                 DEBUG_1("Loading options from %s ... %s", rc_path, success ? "done" : "failed");
964                 g_free(rc_path);
965                 }
966         
967         rc_path = g_build_filename(homedir(), GQ_RC_DIR, RC_FILE_NAME, NULL);
968         success = load_options_from(rc_path, options);
969         DEBUG_1("Loading options from %s ... %s", rc_path, success ? "done" : "failed");
970         g_free(rc_path);
971 }