7 * This software is released under the GNU General Public License (GNU GPL).
8 * Please read the included file COPYING for more information.
9 * This software comes with no warranty of any kind, use at your own risk!
12 #include <glib/gstdio.h>
20 #include "secure_save.h"
21 #include "slideshow.h"
22 #include "ui_fileops.h"
26 *-----------------------------------------------------------------------------
27 * line write/parse routines (private)
28 *-----------------------------------------------------------------------------
32 returns text without quotes or NULL for empty or broken string
33 any text up to first '"' is skipped
34 tail is set to point at the char after the second '"'
39 gchar *quoted_value(const gchar *text, const gchar **tail)
43 gint l = strlen(text);
46 if (tail) *tail = text;
48 if (l == 0) return retval;
50 while (c < l && text[c] !='"') c++;
59 if (text[e-1] != '\\' && text[e] == '"') break;
66 gchar *substring = g_strndup(ptr, e - c);
70 retval = g_strcompress(substring);
75 if (tail) *tail = text + e + 1;
78 /* for compatibility with older formats (<0.3.7)
79 * read a line without quotes too */
82 while (c < l && text[c] !=' ' && text[c] !=8 && text[c] != '\n') c++;
85 retval = g_strndup(text, c);
87 if (tail) *tail = text + c;
93 gchar *escquote_value(const gchar *text)
97 if (!text) return g_strdup("\"\"");
99 e = g_strescape(text, "");
102 gchar *retval = g_strdup_printf("\"%s\"", e);
106 return g_strdup("\"\"");
109 static void write_char_option(SecureSaveInfo *ssi, gchar *label, gchar *text)
111 gchar *escval = escquote_value(text);
113 secure_fprintf(ssi, "%s: %s\n", label, escval);
117 static void read_char_option(FILE *f, gchar *option, gchar *label, gchar *value, gchar **text)
119 if (text && strcasecmp(option, label) == 0)
122 *text = quoted_value(value, NULL);
126 /* Since gdk_color_to_string() is only available since gtk 2.12
127 * here is an equivalent stub function. */
128 static gchar *color_to_string(GdkColor *color)
130 return g_strdup_printf("#%04X%04X%04X", color->red, color->green, color->blue);
133 static void write_color_option(SecureSaveInfo *ssi, gchar *label, GdkColor *color)
137 gchar *colorstring = color_to_string(color);
139 write_char_option(ssi, label, colorstring);
143 secure_fprintf(ssi, "%s: \n", label);
146 static void read_color_option(FILE *f, gchar *option, gchar *label, gchar *value, GdkColor *color)
148 if (color && strcasecmp(option, label) == 0)
150 gchar *colorstr = quoted_value(value, NULL);
151 if (colorstr) gdk_color_parse(colorstr, color);
157 static void write_int_option(SecureSaveInfo *ssi, gchar *label, gint n)
159 secure_fprintf(ssi, "%s: %d\n", label, n);
162 static void read_int_option(FILE *f, gchar *option, gchar *label, gchar *value, gint *n)
164 if (n && strcasecmp(option, label) == 0)
166 *n = strtol(value, NULL, 10);
170 static void read_uint_option(FILE *f, gchar *option, gchar *label, gchar *value, guint *n)
172 if (n && strcasecmp(option, label) == 0)
174 *n = strtoul(value, NULL, 10);
180 static void read_int_option_clamp(FILE *f, gchar *option, gchar *label, gchar *value, gint *n, gint min, gint max)
182 if (n && strcasecmp(option, label) == 0)
184 *n = CLAMP(strtol(value, NULL, 10), min, max);
189 static void write_int_unit_option(SecureSaveInfo *ssi, gchar *label, gint n, gint subunits)
204 secure_fprintf(ssi, "%s: %d.%d\n", label, l, r);
207 static void read_int_unit_option(FILE *f, gchar *option, gchar *label, gchar *value, gint *n, gint subunits)
209 if (n && strcasecmp(option, label) == 0)
215 while (*ptr != '\0' && *ptr != '.') ptr++;
219 l = strtol(value, NULL, 10);
222 r = strtol(ptr, NULL, 10);
226 l = strtol(value, NULL, 10);
230 *n = l * subunits + r;
234 static void write_bool_option(SecureSaveInfo *ssi, gchar *label, gint n)
236 secure_fprintf(ssi, "%s: ", label);
237 if (n) secure_fprintf(ssi, "true\n"); else secure_fprintf(ssi, "false\n");
240 static void read_bool_option(FILE *f, gchar *option, gchar *label, gchar *value, gint *n)
242 if (n && strcasecmp(option, label) == 0)
244 if (strcasecmp(value, "true") == 0 || strcmp(value, "1") == 0)
252 *-----------------------------------------------------------------------------
253 * save configuration (public)
254 *-----------------------------------------------------------------------------
257 void save_options(void)
264 rc_path = g_strconcat(homedir(), "/", GQ_RC_DIR, "/", RC_FILE_NAME, NULL);
266 rc_pathl = path_from_utf8(rc_path);
267 ssi = secure_open(rc_pathl);
273 buf = g_strdup_printf(_("error saving config file: %s\n"), rc_path);
281 #define WRITE_BOOL(_name_) write_bool_option(ssi, #_name_, options->_name_)
282 #define WRITE_INT(_name_) write_int_option(ssi, #_name_, options->_name_)
283 #define WRITE_INT_UNIT(_name_, _unit_) write_int_unit_option(ssi, #_name_, options->_name_, _unit_)
284 #define WRITE_CHAR(_name_) write_char_option(ssi, #_name_, options->_name_)
285 #define WRITE_COLOR(_name_) write_color_option(ssi, #_name_, &options->_name_)
287 #define WRITE_SEPARATOR() secure_fputc(ssi, '\n')
288 #define WRITE_SUBTITLE(_title_) secure_fprintf(ssi, "\n\n##### "_title_" #####\n\n")
290 secure_fprintf(ssi, "######################################################################\n");
291 secure_fprintf(ssi, "# %30s config file version %7s #\n", GQ_APPNAME, VERSION);
292 secure_fprintf(ssi, "######################################################################\n");
295 secure_fprintf(ssi, "# Note: This file is autogenerated. Options can be changed here,\n");
296 secure_fprintf(ssi, "# but user comments and formatting will be lost.\n");
299 WRITE_SUBTITLE("General Options");
301 WRITE_BOOL(show_icon_names);
304 WRITE_BOOL(tree_descend_subdirs);
305 WRITE_BOOL(lazy_image_sync);
306 WRITE_BOOL(update_on_time_change);
309 WRITE_BOOL(startup_path_enable);
310 WRITE_CHAR(startup_path);
312 WRITE_BOOL(progressive_key_scrolling);
313 WRITE_BOOL(enable_metadata_dirs);
315 WRITE_INT(duplicates_similarity_threshold);
318 WRITE_BOOL(mousewheel_scrolls);
319 WRITE_INT(open_recent_list_maxsize);
320 WRITE_BOOL(place_dialogs_under_mouse);
323 WRITE_SUBTITLE("File operations Options");
325 WRITE_BOOL(file_ops.enable_in_place_rename);
326 WRITE_BOOL(file_ops.confirm_delete);
327 WRITE_BOOL(file_ops.enable_delete_key);
328 WRITE_BOOL(file_ops.safe_delete_enable);
329 WRITE_CHAR(file_ops.safe_delete_path);
330 WRITE_INT(file_ops.safe_delete_folder_maxsize);
333 WRITE_SUBTITLE("Layout Options");
335 WRITE_INT(layout.style);
336 WRITE_CHAR(layout.order);
337 WRITE_BOOL(layout.view_as_icons);
338 WRITE_BOOL(layout.view_as_tree);
339 WRITE_BOOL(layout.show_thumbnails);
342 WRITE_BOOL(layout.save_window_positions);
345 WRITE_INT(layout.main_window.x);
346 WRITE_INT(layout.main_window.y);
347 WRITE_INT(layout.main_window.w);
348 WRITE_INT(layout.main_window.h);
349 WRITE_BOOL(layout.main_window.maximized);
350 WRITE_INT(layout.main_window.hdivider_pos);
351 WRITE_INT(layout.main_window.vdivider_pos);
354 WRITE_INT(layout.float_window.x);
355 WRITE_INT(layout.float_window.y);
356 WRITE_INT(layout.float_window.w);
357 WRITE_INT(layout.float_window.h);
358 WRITE_INT(layout.float_window.vdivider_pos);
361 WRITE_BOOL(layout.tools_float);
362 WRITE_BOOL(layout.tools_hidden);
363 WRITE_BOOL(layout.tools_restore_state);
366 WRITE_BOOL(layout.toolbar_hidden);
369 WRITE_SUBTITLE("Image Options");
371 secure_fprintf(ssi, "image.zoom_mode: ");
372 if (options->image.zoom_mode == ZOOM_RESET_ORIGINAL) secure_fprintf(ssi, "original\n");
373 if (options->image.zoom_mode == ZOOM_RESET_FIT_WINDOW) secure_fprintf(ssi, "fit\n");
374 if (options->image.zoom_mode == ZOOM_RESET_NONE) secure_fprintf(ssi, "dont_change\n");
375 WRITE_BOOL(image.zoom_2pass);
376 WRITE_BOOL(image.zoom_to_fit_allow_expand);
377 WRITE_INT(image.zoom_quality);
378 WRITE_INT(image.zoom_increment);
379 WRITE_BOOL(image.fit_window_to_image);
380 WRITE_BOOL(image.limit_window_size);
381 WRITE_INT(image.max_window_size);
382 WRITE_BOOL(image.limit_autofit_size);
383 WRITE_INT(image.max_autofit_size);
384 WRITE_INT(image.scroll_reset_method);
385 WRITE_INT(image.tile_cache_max);
386 WRITE_INT(image.dither_quality);
387 WRITE_BOOL(image.enable_read_ahead);
388 WRITE_BOOL(image.exif_rotate_enable);
389 WRITE_BOOL(image.use_custom_border_color);
390 WRITE_COLOR(image.border_color);
393 WRITE_SUBTITLE("Thumbnails Options");
395 WRITE_INT(thumbnails.max_width);
396 WRITE_INT(thumbnails.max_height);
397 WRITE_BOOL(thumbnails.enable_caching);
398 WRITE_BOOL(thumbnails.cache_into_dirs);
399 WRITE_BOOL(thumbnails.fast);
400 WRITE_BOOL(thumbnails.use_xvpics);
401 WRITE_BOOL(thumbnails.spec_standard);
402 WRITE_INT(thumbnails.quality);
405 WRITE_SUBTITLE("File sorting Options");
407 WRITE_INT(file_sort.method);
408 WRITE_BOOL(file_sort.ascending);
409 WRITE_BOOL(file_sort.case_sensitive);
412 WRITE_SUBTITLE("Fullscreen Options");
414 WRITE_INT(fullscreen.screen);
415 WRITE_BOOL(fullscreen.clean_flip);
416 WRITE_BOOL(fullscreen.disable_saver);
417 WRITE_BOOL(fullscreen.above);
418 WRITE_BOOL(fullscreen.show_info);
419 WRITE_CHAR(fullscreen.info);
421 WRITE_SUBTITLE("Slideshow Options");
423 WRITE_INT_UNIT(slideshow.delay, SLIDESHOW_SUBSECOND_PRECISION);
424 WRITE_BOOL(slideshow.random);
425 WRITE_BOOL(slideshow.repeat);
428 WRITE_SUBTITLE("Collection Options");
430 WRITE_BOOL(collections.rectangular_selection);
433 WRITE_SUBTITLE("Filtering Options");
435 WRITE_BOOL(file_filter.show_hidden_files);
436 WRITE_BOOL(file_filter.show_dot_directory);
437 WRITE_BOOL(file_filter.disable);
440 filter_write_list(ssi);
443 WRITE_SUBTITLE("Sidecars Options");
445 sidecar_ext_write(ssi);
448 WRITE_SUBTITLE("Color Profiles");
451 secure_fprintf(ssi, "# NOTICE: %s was not built with support for color profiles,\n"
452 "# color profile options will have no effect.\n\n", GQ_APPNAME);
455 WRITE_BOOL(color_profile.enabled);
456 WRITE_BOOL(color_profile.use_image);
457 WRITE_INT(color_profile.input_type);
460 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
464 buf = g_strdup_printf("color_profile.input_file_%d", i + 1);
465 write_char_option(ssi, buf, options->color_profile.input_file[i]);
468 buf = g_strdup_printf("color_profile.input_name_%d", i + 1);
469 write_char_option(ssi, buf, options->color_profile.input_name[i]);
474 WRITE_INT(color_profile.screen_type);
475 WRITE_CHAR(color_profile.screen_file);
477 WRITE_SUBTITLE("External Programs");
478 secure_fprintf(ssi, "# Maximum of %d programs (external_1 through external_%d)\n", GQ_EDITOR_GENERIC_SLOTS, GQ_EDITOR_GENERIC_SLOTS);
479 secure_fprintf(ssi, "# external_%d through external_%d are used for file ops\n", GQ_EDITOR_GENERIC_SLOTS + 1, GQ_EDITOR_SLOTS);
480 secure_fprintf(ssi, "# format: external_n: \"menu name\" \"command line\"\n\n");
482 for (i = 0; i < GQ_EDITOR_SLOTS; i++)
484 if (i == GQ_EDITOR_GENERIC_SLOTS) secure_fputc(ssi, '\n');
485 gchar *qname = escquote_value(options->editor_name[i]);
486 gchar *qcommand = escquote_value(options->editor_command[i]);
487 secure_fprintf(ssi, "external_%d: %s %s\n", i+1, qname, qcommand);
493 WRITE_SUBTITLE("Exif Options");
494 secure_fprintf(ssi, "# Display: 0: never\n"
497 for (i = 0; ExifUIList[i].key; i++)
499 secure_fprintf(ssi, "exif.display.");
500 write_int_option(ssi, (gchar *)ExifUIList[i].key, ExifUIList[i].current);
506 secure_fprintf(ssi, "######################################################################\n");
507 secure_fprintf(ssi, "# end of config file #\n");
508 secure_fprintf(ssi, "######################################################################\n");
511 if (secure_close(ssi))
515 buf = g_strdup_printf(_("error saving config file: %s\nerror: %s\n"), rc_path,
516 secsave_strerror(secsave_errno));
528 *-----------------------------------------------------------------------------
529 * load configuration (public)
530 *-----------------------------------------------------------------------------
533 void load_options(void)
541 gchar value_all[1024];
544 for (i = 0; ExifUIList[i].key; i++)
545 ExifUIList[i].current = ExifUIList[i].default_value;
547 rc_path = g_strconcat(homedir(), "/", GQ_RC_DIR, "/", RC_FILE_NAME, NULL);
549 rc_pathl = path_from_utf8(rc_path);
550 f = fopen(rc_pathl,"r");
558 while (fgets(s_buf, sizeof(s_buf), f))
560 gchar *option_start, *value_start;
563 while(g_ascii_isspace(*p)) p++;
564 if (!*p || *p == '\n' || *p == '#') continue;
566 while(*p && *p != ':') p++;
570 strncpy(option, option_start, sizeof(option));
571 while(g_ascii_isspace(*p)) p++;
573 strncpy(value_all, value_start, sizeof(value_all));
574 while(*p && !g_ascii_isspace(*p) && *p != '\n') p++;
576 strncpy(value, value_start, sizeof(value));
578 #define READ_BOOL(_name_) read_bool_option(f, option, #_name_, value, &options->_name_)
579 #define READ_INT(_name_) read_int_option(f, option, #_name_, value, &options->_name_)
580 #define READ_UINT(_name_) read_uint_option(f, option, #_name_, value, &options->_name_)
581 #define READ_INT_CLAMP(_name_, _min_, _max_) read_int_option_clamp(f, option, #_name_, value, &options->_name_, _min_, _max_)
582 #define READ_INT_UNIT(_name_, _unit_) read_int_unit_option(f, option, #_name_, value, &options->_name_, _unit_)
583 #define READ_CHAR(_name_) read_char_option(f, option, #_name_, value_all, &options->_name_)
584 #define READ_COLOR(_name_) read_color_option(f, option, #_name_, value, &options->_name_)
586 /* general options */
587 READ_BOOL(show_icon_names);
588 READ_BOOL(show_icon_names);
590 READ_BOOL(tree_descend_subdirs);
591 READ_BOOL(lazy_image_sync);
592 READ_BOOL(update_on_time_change);
594 READ_BOOL(startup_path_enable);
595 READ_CHAR(startup_path);
597 READ_INT(duplicates_similarity_threshold);
599 READ_BOOL(progressive_key_scrolling);
601 READ_BOOL(enable_metadata_dirs);
603 READ_BOOL(mousewheel_scrolls);
605 READ_INT(open_recent_list_maxsize);
607 READ_BOOL(place_dialogs_under_mouse);
612 READ_INT(layout.style);
613 READ_CHAR(layout.order);
614 READ_BOOL(layout.view_as_icons);
615 READ_BOOL(layout.view_as_tree);
616 READ_BOOL(layout.show_thumbnails);
618 /* window positions */
620 READ_BOOL(layout.save_window_positions);
622 READ_INT(layout.main_window.x);
623 READ_INT(layout.main_window.y);
624 READ_INT(layout.main_window.w);
625 READ_INT(layout.main_window.h);
626 READ_BOOL(layout.main_window.maximized);
627 READ_INT(layout.float_window.x);
628 READ_INT(layout.float_window.y);
629 READ_INT(layout.float_window.w);
630 READ_INT(layout.float_window.h);
631 READ_INT(layout.float_window.vdivider_pos);
632 READ_INT(layout.main_window.hdivider_pos);
633 READ_INT(layout.main_window.vdivider_pos);
634 READ_BOOL(layout.tools_float);
635 READ_BOOL(layout.tools_hidden);
636 READ_BOOL(layout.tools_restore_state);
637 READ_BOOL(layout.toolbar_hidden);
641 if (strcasecmp(option, "image.zoom_mode") == 0)
643 if (strcasecmp(value, "original") == 0) options->image.zoom_mode = ZOOM_RESET_ORIGINAL;
644 if (strcasecmp(value, "fit") == 0) options->image.zoom_mode = ZOOM_RESET_FIT_WINDOW;
645 if (strcasecmp(value, "dont_change") == 0) options->image.zoom_mode = ZOOM_RESET_NONE;
647 READ_BOOL(image.zoom_2pass);
648 READ_BOOL(image.zoom_to_fit_allow_expand);
649 READ_BOOL(image.fit_window_to_image);
650 READ_BOOL(image.limit_window_size);
651 READ_INT(image.max_window_size);
652 READ_BOOL(image.limit_autofit_size);
653 READ_INT(image.max_autofit_size);
654 READ_INT(image.scroll_reset_method);
655 READ_INT(image.tile_cache_max);
656 READ_INT_CLAMP(image.zoom_quality, GDK_INTERP_NEAREST, GDK_INTERP_HYPER);
657 READ_INT_CLAMP(image.dither_quality, GDK_RGB_DITHER_NONE, GDK_RGB_DITHER_MAX);
658 READ_INT(image.zoom_increment);
659 READ_BOOL(image.enable_read_ahead);
660 READ_BOOL(image.exif_rotate_enable);
661 READ_BOOL(image.use_custom_border_color);
662 READ_COLOR(image.border_color);
665 /* thumbnails options */
666 READ_INT_CLAMP(thumbnails.max_width, 16, 512);
667 READ_INT_CLAMP(thumbnails.max_height, 16, 512);
669 READ_BOOL(thumbnails.enable_caching);
670 READ_BOOL(thumbnails.cache_into_dirs);
671 READ_BOOL(thumbnails.fast);
672 READ_BOOL(thumbnails.use_xvpics);
673 READ_BOOL(thumbnails.spec_standard);
674 READ_INT_CLAMP(thumbnails.quality, GDK_INTERP_NEAREST, GDK_INTERP_HYPER);
676 /* file sorting options */
677 READ_UINT(file_sort.method);
678 READ_BOOL(file_sort.ascending);
679 READ_BOOL(file_sort.case_sensitive);
681 /* file operations options */
682 READ_BOOL(file_ops.enable_in_place_rename);
683 READ_BOOL(file_ops.confirm_delete);
684 READ_BOOL(file_ops.enable_delete_key);
685 READ_BOOL(file_ops.safe_delete_enable);
686 READ_CHAR(file_ops.safe_delete_path);
687 READ_INT(file_ops.safe_delete_folder_maxsize);
689 /* fullscreen options */
690 READ_INT(fullscreen.screen);
691 READ_BOOL(fullscreen.clean_flip);
692 READ_BOOL(fullscreen.disable_saver);
693 READ_BOOL(fullscreen.above);
694 READ_BOOL(fullscreen.show_info);
695 READ_CHAR(fullscreen.info);
697 /* slideshow options */
699 READ_INT_UNIT(slideshow.delay, SLIDESHOW_SUBSECOND_PRECISION);
700 READ_BOOL(slideshow.random);
701 READ_BOOL(slideshow.repeat);
703 /* collection options */
705 READ_BOOL(collections.rectangular_selection);
707 /* filtering options */
709 READ_BOOL(file_filter.show_hidden_files);
710 READ_BOOL(file_filter.show_dot_directory);
711 READ_BOOL(file_filter.disable);
713 if (strcasecmp(option, "file_filter.ext") == 0)
715 filter_parse(value_all);
718 if (strcasecmp(option, "sidecar.ext") == 0)
720 sidecar_ext_parse(value_all, TRUE);
725 READ_BOOL(color_profile.enabled);
726 READ_BOOL(color_profile.use_image);
727 READ_INT(color_profile.input_type);
729 if (strncasecmp(option, "color_profile.input_file_", 25) == 0)
731 i = strtol(option + 25, NULL, 0) - 1;
732 if (i >= 0 && i < COLOR_PROFILE_INPUTS)
734 read_char_option(f, option, option, value, &options->color_profile.input_file[i]);
737 if (strncasecmp(option, "color_profile.input_name_", 25) == 0)
739 i = strtol(option + 25, NULL, 0) - 1;
740 if (i >= 0 && i < COLOR_PROFILE_INPUTS)
742 read_char_option(f, option, option, value, &options->color_profile.input_name[i]);
746 READ_INT(color_profile.screen_type);
747 READ_CHAR(color_profile.screen_file);
749 /* External Programs */
751 if (strncasecmp(option, "external_", 9) == 0)
753 i = strtol(option + 9, NULL, 0);
754 if (i > 0 && i <= GQ_EDITOR_SLOTS)
758 g_free(options->editor_name[i]);
759 g_free(options->editor_command[i]);
761 options->editor_name[i] = quoted_value(value_all, &ptr);
762 options->editor_command[i] = quoted_value(ptr, NULL);
767 if (0 == strncasecmp(option, "exif.display.", 13))
769 for (i = 0; ExifUIList[i].key; i++)
770 if (0 == strcasecmp(option + 13, ExifUIList[i].key))
771 ExifUIList[i].current = strtol(value, NULL, 10);