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