ac28876e52d5507a4238295042fcea0d221753f1
[geeqie.git] / src / preferences.cc
1 /*
2  * Copyright (C) 2006 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "main.h"
23 #include "preferences.h"
24
25 #include "bar-keywords.h"
26 #include "cache.h"
27 //~ #include "authors.h"
28 //~ #include "authors.c"
29 //~ #include "translators.h"
30 //~ #include "translators.c"
31 #include "editors.h"
32 #include "filedata.h"
33 #include "filefilter.h"
34 #include "fullscreen.h"
35 #include "image.h"
36 #include "image-overlay.h"
37 #include "color-man.h"
38 #include "img-view.h"
39 #include "layout-util.h"
40 #include "metadata.h"
41 #include "misc.h"
42 #include "osd.h"
43 #include "pixbuf-util.h"
44 #include "rcfile.h"
45 #include "slideshow.h"
46 #include "toolbar.h"
47 #include "trash.h"
48 #include "utilops.h"
49 #include "ui-fileops.h"
50 #include "ui-misc.h"
51 #include "ui-spinner.h"
52 #include "ui-tabcomp.h"
53 #include "window.h"
54 #include "zonedetect.h"
55
56 #ifdef HAVE_LCMS
57 #ifdef HAVE_LCMS2
58 #include <lcms2.h>
59 #else
60 #include <lcms.h>
61 #endif
62 #endif
63
64 #ifdef HAVE_SPELL
65 #include <gspell/gspell.h>
66 #endif
67
68 #define EDITOR_NAME_MAX_LENGTH 32
69 #define EDITOR_COMMAND_MAX_LENGTH 1024
70
71 static void image_overlay_set_text_colours();
72
73 GtkWidget *keyword_text;
74 static void config_tab_keywords_save();
75
76 struct ThumbSize
77 {
78         gint w;
79         gint h;
80 };
81
82 static ThumbSize thumb_size_list[] =
83 {
84         { 24, 24 },
85         { 32, 32 },
86         { 48, 48 },
87         { 64, 64 },
88         { 96, 72 },
89         { 96, 96 },
90         { 128, 96 },
91         { 128, 128 },
92         { 160, 120 },
93         { 160, 160 },
94         { 192, 144 },
95         { 192, 192 },
96         { 256, 192 },
97         { 256, 256 }
98 };
99
100 enum {
101         FE_ENABLE,
102         FE_EXTENSION,
103         FE_DESCRIPTION,
104         FE_CLASS,
105         FE_WRITABLE,
106         FE_ALLOW_SIDECAR
107 };
108
109 enum {
110         AE_ACTION,
111         AE_KEY,
112         AE_TOOLTIP,
113         AE_ACCEL
114 };
115
116 enum {
117         FILETYPES_COLUMN_ENABLED = 0,
118         FILETYPES_COLUMN_FILTER,
119         FILETYPES_COLUMN_DESCRIPTION,
120         FILETYPES_COLUMN_CLASS,
121         FILETYPES_COLUMN_WRITABLE,
122         FILETYPES_COLUMN_SIDECAR,
123         FILETYPES_COLUMN_COUNT
124 };
125
126 const gchar *format_class_list[] = {
127         N_("Unknown"),
128         N_("Image"),
129         N_("RAW Image"),
130         N_("Metadata"),
131         N_("Video"),
132         N_("Collection"),
133         N_("Document"),
134         N_("Archive")
135         };
136
137 /* config memory values */
138 static ConfOptions *c_options = nullptr;
139
140
141 #ifdef DEBUG
142 static gint debug_c;
143 #endif
144
145 static GtkWidget *configwindow = nullptr;
146 static GtkListStore *filter_store = nullptr;
147 static GtkTreeStore *accel_store = nullptr;
148
149 static GtkWidget *safe_delete_path_entry;
150
151 static GtkWidget *color_profile_input_file_entry[COLOR_PROFILE_INPUTS];
152 static GtkWidget *color_profile_input_name_entry[COLOR_PROFILE_INPUTS];
153 static GtkWidget *color_profile_screen_file_entry;
154 static GtkWidget *external_preview_select_entry;
155 static GtkWidget *external_preview_extract_entry;
156
157 static GtkWidget *sidecar_ext_entry;
158 static GtkWidget *help_search_engine_entry;
159
160
161 #define CONFIG_WINDOW_DEF_WIDTH         700
162 #define CONFIG_WINDOW_DEF_HEIGHT        600
163
164 /*
165  *-----------------------------------------------------------------------------
166  * option widget callbacks (private)
167  *-----------------------------------------------------------------------------
168  */
169
170 static void zoom_increment_cb(GtkWidget *spin, gpointer UNUSED(data))
171 {
172         c_options->image.zoom_increment = static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) * 100.0 + 0.01);
173 }
174
175 static void slideshow_delay_hours_cb(GtkWidget *spin, gpointer UNUSED(data))
176 {
177         gint mins_secs_tenths, delay;
178
179         mins_secs_tenths = c_options->slideshow.delay %
180                                                 (3600 * SLIDESHOW_SUBSECOND_PRECISION);
181
182         delay = (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
183                                                                 (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
184                                                                 mins_secs_tenths);
185
186         c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
187                                                                                                         SLIDESHOW_SUBSECOND_PRECISION;
188 }
189
190 static void slideshow_delay_minutes_cb(GtkWidget *spin, gpointer UNUSED(data))
191 {
192         gint hours, secs_tenths, delay;
193
194         hours = c_options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
195         secs_tenths = c_options->slideshow.delay % (60 * SLIDESHOW_SUBSECOND_PRECISION);
196
197         delay = hours * (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
198                                         (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
199                                         (60 * SLIDESHOW_SUBSECOND_PRECISION) + secs_tenths);
200
201         c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
202                                                                                                         SLIDESHOW_SUBSECOND_PRECISION;
203 }
204
205 static void slideshow_delay_seconds_cb(GtkWidget *spin, gpointer UNUSED(data))
206 {
207         gint hours_mins, delay;
208
209         hours_mins = c_options->slideshow.delay / (60 * SLIDESHOW_SUBSECOND_PRECISION);
210
211         delay = (hours_mins * (60 * SLIDESHOW_SUBSECOND_PRECISION)) +
212                                                         static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
213                                                         static_cast<gdouble>(SLIDESHOW_SUBSECOND_PRECISION) + 0.01);
214
215         c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
216                                                                                                         SLIDESHOW_SUBSECOND_PRECISION;
217 }
218
219 /*
220  *-----------------------------------------------------------------------------
221  * sync program to config window routine (private)
222  *-----------------------------------------------------------------------------
223  */
224
225 void config_entry_to_option(GtkWidget *entry, gchar **option, gchar *(*func)(const gchar *))
226 {
227         const gchar *buf;
228
229         g_free(*option);
230         *option = nullptr;
231         buf = gtk_entry_get_text(GTK_ENTRY(entry));
232         if (buf && strlen(buf) > 0)
233                 {
234                 if (func)
235                         *option = func(buf);
236                 else
237                         *option = g_strdup(buf);
238                 }
239 }
240
241
242 static gboolean accel_apply_cb(GtkTreeModel *model, GtkTreePath *UNUSED(path), GtkTreeIter *iter, gpointer UNUSED(data))
243 {
244         gchar *accel_path, *accel;
245
246         gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, AE_KEY, &accel, -1);
247
248         if (accel_path && accel_path[0])
249                 {
250                 GtkAccelKey key;
251                 gtk_accelerator_parse(accel, &key.accel_key, &key.accel_mods);
252                 gtk_accel_map_change_entry(accel_path, key.accel_key, key.accel_mods, TRUE);
253                 }
254
255         g_free(accel_path);
256         g_free(accel);
257
258         return FALSE;
259 }
260
261
262 static void config_window_apply()
263 {
264         gboolean refresh = FALSE;
265 #ifdef HAVE_LCMS2
266         int i = 0;
267 #endif
268
269         config_entry_to_option(safe_delete_path_entry, &options->file_ops.safe_delete_path, remove_trailing_slash);
270
271         if (options->file_filter.show_hidden_files != c_options->file_filter.show_hidden_files) refresh = TRUE;
272         if (options->file_filter.show_parent_directory != c_options->file_filter.show_parent_directory) refresh = TRUE;
273         if (options->file_filter.show_dot_directory != c_options->file_filter.show_dot_directory) refresh = TRUE;
274         if (options->file_sort.case_sensitive != c_options->file_sort.case_sensitive) refresh = TRUE;
275         if (options->file_sort.natural != c_options->file_sort.natural) refresh = TRUE;
276         if (options->file_filter.disable_file_extension_checks != c_options->file_filter.disable_file_extension_checks) refresh = TRUE;
277         if (options->file_filter.disable != c_options->file_filter.disable) refresh = TRUE;
278
279         options->file_ops.confirm_delete = c_options->file_ops.confirm_delete;
280         options->file_ops.enable_delete_key = c_options->file_ops.enable_delete_key;
281         options->file_ops.confirm_move_to_trash = c_options->file_ops.confirm_move_to_trash;
282         options->file_ops.use_system_trash = c_options->file_ops.use_system_trash;
283         options->file_ops.no_trash = c_options->file_ops.no_trash;
284         options->file_ops.safe_delete_folder_maxsize = c_options->file_ops.safe_delete_folder_maxsize;
285         options->tools_restore_state = c_options->tools_restore_state;
286         options->save_window_positions = c_options->save_window_positions;
287         options->use_saved_window_positions_for_new_windows = c_options->use_saved_window_positions_for_new_windows;
288         options->save_window_workspace = c_options->save_window_workspace;
289         options->save_dialog_window_positions = c_options->save_dialog_window_positions;
290         options->show_window_ids = c_options->show_window_ids;
291         options->image.scroll_reset_method = c_options->image.scroll_reset_method;
292         options->image.zoom_2pass = c_options->image.zoom_2pass;
293         options->image.fit_window_to_image = c_options->image.fit_window_to_image;
294         options->image.limit_window_size = c_options->image.limit_window_size;
295         options->image.zoom_to_fit_allow_expand = c_options->image.zoom_to_fit_allow_expand;
296         options->image.max_window_size = c_options->image.max_window_size;
297         options->image.limit_autofit_size = c_options->image.limit_autofit_size;
298         options->image.max_autofit_size = c_options->image.max_autofit_size;
299         options->image.max_enlargement_size = c_options->image.max_enlargement_size;
300         //~ options->image.use_clutter_renderer = c_options->image.use_clutter_renderer;
301         options->image.tile_size = c_options->image.tile_size;
302         options->progressive_key_scrolling = c_options->progressive_key_scrolling;
303         options->keyboard_scroll_step = c_options->keyboard_scroll_step;
304
305         if (options->thumbnails.max_width != c_options->thumbnails.max_width
306             || options->thumbnails.max_height != c_options->thumbnails.max_height
307             || options->thumbnails.quality != c_options->thumbnails.quality)
308                 {
309                 thumb_format_changed = TRUE;
310                 refresh = TRUE;
311                 options->thumbnails.max_width = c_options->thumbnails.max_width;
312                 options->thumbnails.max_height = c_options->thumbnails.max_height;
313                 options->thumbnails.quality = c_options->thumbnails.quality;
314                 }
315         options->thumbnails.enable_caching = c_options->thumbnails.enable_caching;
316         options->thumbnails.cache_into_dirs = c_options->thumbnails.cache_into_dirs;
317         options->thumbnails.use_exif = c_options->thumbnails.use_exif;
318         options->thumbnails.use_color_management = c_options->thumbnails.use_color_management;
319         options->thumbnails.collection_preview = c_options->thumbnails.collection_preview;
320         options->thumbnails.use_ft_metadata = c_options->thumbnails.use_ft_metadata;
321 //      options->thumbnails.use_ft_metadata_small = c_options->thumbnails.use_ft_metadata_small;
322         options->thumbnails.spec_standard = c_options->thumbnails.spec_standard;
323         options->metadata.enable_metadata_dirs = c_options->metadata.enable_metadata_dirs;
324         options->file_filter.show_hidden_files = c_options->file_filter.show_hidden_files;
325         options->file_filter.show_parent_directory = c_options->file_filter.show_parent_directory;
326         options->file_filter.show_dot_directory = c_options->file_filter.show_dot_directory;
327         options->file_filter.disable_file_extension_checks = c_options->file_filter.disable_file_extension_checks;
328
329         options->file_sort.case_sensitive = c_options->file_sort.case_sensitive;
330         options->file_sort.natural = c_options->file_sort.natural;
331         options->file_filter.disable = c_options->file_filter.disable;
332
333         config_entry_to_option(sidecar_ext_entry, &options->sidecar.ext, nullptr);
334         sidecar_ext_parse(options->sidecar.ext);
335
336         options->slideshow.random = c_options->slideshow.random;
337         options->slideshow.repeat = c_options->slideshow.repeat;
338         options->slideshow.delay = c_options->slideshow.delay;
339
340         options->mousewheel_scrolls = c_options->mousewheel_scrolls;
341         options->image_lm_click_nav = c_options->image_lm_click_nav;
342         options->image_l_click_archive = c_options->image_l_click_archive;
343         options->image_l_click_video = c_options->image_l_click_video;
344         options->image_l_click_video_editor = c_options->image_l_click_video_editor;
345
346         options->file_ops.enable_in_place_rename = c_options->file_ops.enable_in_place_rename;
347
348         options->image.tile_cache_max = c_options->image.tile_cache_max;
349         options->image.image_cache_max = c_options->image.image_cache_max;
350
351         options->image.zoom_quality = c_options->image.zoom_quality;
352
353         options->image.zoom_increment = c_options->image.zoom_increment;
354
355         options->image.zoom_style = c_options->image.zoom_style;
356
357         options->image.enable_read_ahead = c_options->image.enable_read_ahead;
358
359         options->appimage_notifications = c_options->appimage_notifications;
360
361
362         if (options->image.use_custom_border_color != c_options->image.use_custom_border_color
363             || options->image.use_custom_border_color_in_fullscreen != c_options->image.use_custom_border_color_in_fullscreen
364             || !gdk_color_equal(&options->image.border_color, &c_options->image.border_color))
365                 {
366                 options->image.use_custom_border_color_in_fullscreen = c_options->image.use_custom_border_color_in_fullscreen;
367                 options->image.use_custom_border_color = c_options->image.use_custom_border_color;
368                 options->image.border_color = c_options->image.border_color;
369                 layout_colors_update();
370                 view_window_colors_update();
371                 }
372
373         options->image.alpha_color_1 = c_options->image.alpha_color_1;
374         options->image.alpha_color_2 = c_options->image.alpha_color_2;
375
376         options->fullscreen.screen = c_options->fullscreen.screen;
377         options->fullscreen.clean_flip = c_options->fullscreen.clean_flip;
378         options->fullscreen.disable_saver = c_options->fullscreen.disable_saver;
379         options->fullscreen.above = c_options->fullscreen.above;
380         if (c_options->image_overlay.template_string)
381                 set_image_overlay_template_string(&options->image_overlay.template_string,
382                                                   c_options->image_overlay.template_string);
383         if (c_options->image_overlay.font)
384                 set_image_overlay_font_string(&options->image_overlay.font,
385                                                   c_options->image_overlay.font);
386         options->image_overlay.text_red = c_options->image_overlay.text_red;
387         options->image_overlay.text_green = c_options->image_overlay.text_green;
388         options->image_overlay.text_blue = c_options->image_overlay.text_blue;
389         options->image_overlay.text_alpha = c_options->image_overlay.text_alpha;
390         options->image_overlay.background_red = c_options->image_overlay.background_red;
391         options->image_overlay.background_green = c_options->image_overlay.background_green;
392         options->image_overlay.background_blue = c_options->image_overlay.background_blue;
393         options->image_overlay.background_alpha = c_options->image_overlay.background_alpha;
394         options->update_on_time_change = c_options->update_on_time_change;
395
396         options->duplicates_similarity_threshold = c_options->duplicates_similarity_threshold;
397         options->rot_invariant_sim = c_options->rot_invariant_sim;
398
399         options->tree_descend_subdirs = c_options->tree_descend_subdirs;
400
401         options->view_dir_list_single_click_enter = c_options->view_dir_list_single_click_enter;
402         options->circular_selection_lists = c_options->circular_selection_lists;
403
404         options->open_recent_list_maxsize = c_options->open_recent_list_maxsize;
405         options->recent_folder_image_list_maxsize = c_options->recent_folder_image_list_maxsize;
406         options->dnd_icon_size = c_options->dnd_icon_size;
407         options->clipboard_selection = c_options->clipboard_selection;
408         options->dnd_default_action = c_options->dnd_default_action;
409
410         options->metadata.save_in_image_file = c_options->metadata.save_in_image_file;
411         options->metadata.save_legacy_IPTC = c_options->metadata.save_legacy_IPTC;
412         options->metadata.warn_on_write_problems = c_options->metadata.warn_on_write_problems;
413         options->metadata.save_legacy_format = c_options->metadata.save_legacy_format;
414         options->metadata.sync_grouped_files = c_options->metadata.sync_grouped_files;
415         options->metadata.confirm_write = c_options->metadata.confirm_write;
416         options->metadata.sidecar_extended_name = c_options->metadata.sidecar_extended_name;
417         options->metadata.confirm_timeout = c_options->metadata.confirm_timeout;
418         options->metadata.confirm_after_timeout = c_options->metadata.confirm_after_timeout;
419         options->metadata.confirm_on_image_change = c_options->metadata.confirm_on_image_change;
420         options->metadata.confirm_on_dir_change = c_options->metadata.confirm_on_dir_change;
421         options->metadata.keywords_case_sensitive = c_options->metadata.keywords_case_sensitive;
422         options->metadata.write_orientation = c_options->metadata.write_orientation;
423         options->metadata.check_spelling = c_options->metadata.check_spelling;
424         options->stereo.mode = (c_options->stereo.mode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
425                                (c_options->stereo.tmp.mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
426                                (c_options->stereo.tmp.flip_right   ? PR_STEREO_FLIP_RIGHT : 0) |
427                                (c_options->stereo.tmp.mirror_left  ? PR_STEREO_MIRROR_LEFT : 0) |
428                                (c_options->stereo.tmp.flip_left    ? PR_STEREO_FLIP_LEFT : 0) |
429                                (c_options->stereo.tmp.swap         ? PR_STEREO_SWAP : 0) |
430                                (c_options->stereo.tmp.temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
431         options->stereo.fsmode = (c_options->stereo.fsmode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
432                                (c_options->stereo.tmp.fs_mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
433                                (c_options->stereo.tmp.fs_flip_right   ? PR_STEREO_FLIP_RIGHT : 0) |
434                                (c_options->stereo.tmp.fs_mirror_left  ? PR_STEREO_MIRROR_LEFT : 0) |
435                                (c_options->stereo.tmp.fs_flip_left    ? PR_STEREO_FLIP_LEFT : 0) |
436                                (c_options->stereo.tmp.fs_swap         ? PR_STEREO_SWAP : 0) |
437                                (c_options->stereo.tmp.fs_temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
438         options->stereo.enable_fsmode = c_options->stereo.enable_fsmode;
439         options->stereo.fixed_w = c_options->stereo.fixed_w;
440         options->stereo.fixed_h = c_options->stereo.fixed_h;
441         options->stereo.fixed_x1 = c_options->stereo.fixed_x1;
442         options->stereo.fixed_y1 = c_options->stereo.fixed_y1;
443         options->stereo.fixed_x2 = c_options->stereo.fixed_x2;
444         options->stereo.fixed_y2 = c_options->stereo.fixed_y2;
445
446         options->info_keywords.height = c_options->info_keywords.height;
447         options->info_title.height = c_options->info_title.height;
448         options->info_comment.height = c_options->info_comment.height;
449         options->info_rating.height = c_options->info_rating.height;
450
451         options->show_predefined_keyword_tree = c_options->show_predefined_keyword_tree;
452         options->expand_menu_toolbar = c_options->expand_menu_toolbar;
453
454         options->marks_save = c_options->marks_save;
455         options->with_rename = c_options->with_rename;
456         options->collections_on_top = c_options->collections_on_top;
457         options->hide_window_in_fullscreen = c_options->hide_window_in_fullscreen;
458         config_entry_to_option(help_search_engine_entry, &options->help_search_engine, nullptr);
459
460         options->external_preview.enable = c_options->external_preview.enable;
461         config_entry_to_option(external_preview_select_entry, &options->external_preview.select, nullptr);
462         config_entry_to_option(external_preview_extract_entry, &options->external_preview.extract, nullptr);
463
464         options->read_metadata_in_idle = c_options->read_metadata_in_idle;
465
466         options->star_rating.star = c_options->star_rating.star;
467         options->star_rating.rejected = c_options->star_rating.rejected;
468
469         options->threads.duplicates = c_options->threads.duplicates > 0 ? c_options->threads.duplicates : -1;
470
471 #ifdef DEBUG
472         set_debug_level(debug_c);
473 #endif
474
475 #ifdef HAVE_LCMS
476         for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
477                 {
478                 config_entry_to_option(color_profile_input_name_entry[i], &options->color_profile.input_name[i], nullptr);
479                 config_entry_to_option(color_profile_input_file_entry[i], &options->color_profile.input_file[i], nullptr);
480                 }
481         config_entry_to_option(color_profile_screen_file_entry, &options->color_profile.screen_file, nullptr);
482         options->color_profile.use_x11_screen_profile = c_options->color_profile.use_x11_screen_profile;
483         if (options->color_profile.render_intent != c_options->color_profile.render_intent)
484                 {
485                 options->color_profile.render_intent = c_options->color_profile.render_intent;
486                 color_man_update();
487                 }
488 #endif
489
490         options->mouse_button_8 = c_options->mouse_button_8;
491         options->mouse_button_9 = c_options->mouse_button_9;
492
493         options->override_disable_gpu = c_options->override_disable_gpu;
494
495         config_tab_keywords_save();
496
497         image_options_sync();
498
499         if (refresh)
500                 {
501                 filter_rebuild();
502                 layout_refresh(nullptr);
503                 }
504
505         if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, nullptr);
506
507         toolbar_apply(TOOLBAR_MAIN);
508         toolbar_apply(TOOLBAR_STATUS);
509 }
510
511 /*
512  *-----------------------------------------------------------------------------
513  * config window main button callbacks (private)
514  *-----------------------------------------------------------------------------
515  */
516
517 static void config_window_close_cb(GtkWidget *UNUSED(widget), gpointer UNUSED(data))
518 {
519         gtk_widget_destroy(configwindow);
520         configwindow = nullptr;
521         filter_store = nullptr;
522 }
523
524 static void config_window_help_cb(GtkWidget *UNUSED(widget), gpointer data)
525 {
526         auto notebook = static_cast<GtkWidget *>(data);
527         gint i;
528
529         static const gchar *html_section[] =
530         {
531         "GuideOptionsGeneral.html",
532         "GuideOptionsImage.html",
533         "GuideOptionsOSD.html",
534         "GuideOptionsWindow.html",
535         "GuideOptionsKeyboard.html",
536         "GuideOptionsFiltering.html",
537         "GuideOptionsMetadata.html",
538         "GuideOptionsKeywords.html",
539         "GuideOptionsColor.html",
540         "GuideOptionsStereo.html",
541         "GuideOptionsBehavior.html",
542         "GuideOptionsToolbar.html",
543         "GuideOptionsToolbar.html",
544         "GuideOptionsAdvanced.html"
545         };
546
547         i = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
548         help_window_show(html_section[i]);
549 }
550
551 static gboolean config_window_delete(GtkWidget *UNUSED(widget), GdkEventAny *UNUSED(event), gpointer UNUSED(data))
552 {
553         config_window_close_cb(nullptr, nullptr);
554         return TRUE;
555 }
556
557 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
558 {
559         LayoutWindow *lw;
560         auto notebook = static_cast<GtkNotebook *>(data);
561         GdkWindow *window;
562         gint x;
563         gint y;
564         gint w;
565         gint h;
566         gint page_number;
567
568         lw = static_cast<LayoutWindow *>(layout_window_list->data);
569
570         window = gtk_widget_get_window(widget);
571         gdk_window_get_root_origin(window, &x, &y);
572         w = gdk_window_get_width(window);
573         h = gdk_window_get_height(window);
574         page_number = gtk_notebook_get_current_page(notebook);
575
576         lw->options.preferences_window.x = x;
577         lw->options.preferences_window.y = y;
578         lw->options.preferences_window.w = w;
579         lw->options.preferences_window.h = h;
580         lw->options.preferences_window.page_number = page_number;
581
582         config_window_apply();
583         layout_util_sync(lw);
584         save_options(options);
585         config_window_close_cb(nullptr, nullptr);
586 }
587
588 /*
589  *-----------------------------------------------------------------------------
590  * config window setup (private)
591  *-----------------------------------------------------------------------------
592  */
593
594 static void quality_menu_cb(GtkWidget *combo, gpointer data)
595 {
596         auto option = static_cast<gint *>(data);
597
598         switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
599                 {
600                 case 0:
601                 default:
602                         *option = GDK_INTERP_NEAREST;
603                         break;
604                 case 1:
605                         *option = GDK_INTERP_TILES;
606                         break;
607                 case 2:
608                         *option = GDK_INTERP_BILINEAR;
609                         break;
610                 }
611 }
612
613 static void dnd_default_action_selection_menu_cb(GtkWidget *combo, gpointer data)
614 {
615         auto option = static_cast<gint *>(data);
616
617         switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
618                 {
619                 case 0:
620                 default:
621                         *option = DND_ACTION_ASK;
622                         break;
623                 case 1:
624                         *option = DND_ACTION_COPY;
625                         break;
626                 case 2:
627                         *option = DND_ACTION_MOVE;
628                         break;
629                 }
630 }
631 static void clipboard_selection_menu_cb(GtkWidget *combo, gpointer data)
632 {
633         auto option = static_cast<gint *>(data);
634
635         switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
636                 {
637                 case 0:
638                         *option = CLIPBOARD_PRIMARY;
639                         break;
640                 case 1:
641                         *option = CLIPBOARD_CLIPBOARD;
642                         break;
643                 case 2:
644                         *option = CLIPBOARD_BOTH;
645                         break;
646                 default:
647                         *option = CLIPBOARD_BOTH;
648                 }
649 }
650
651 static void add_quality_menu(GtkWidget *table, gint column, gint row, const gchar *text,
652                              guint option, guint *option_c)
653 {
654         GtkWidget *combo;
655         gint current = 0;
656
657         *option_c = option;
658
659         pref_table_label(table, column, row, text, 0.0);
660
661         combo = gtk_combo_box_text_new();
662
663         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Nearest (worst, but fastest)"));
664         if (option == GDK_INTERP_NEAREST) current = 0;
665         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Tiles"));
666         if (option == GDK_INTERP_TILES) current = 1;
667         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Bilinear (best, but slowest)"));
668         if (option == GDK_INTERP_BILINEAR) current = 2;
669
670         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
671
672         g_signal_connect(G_OBJECT(combo), "changed",
673                          G_CALLBACK(quality_menu_cb), option_c);
674
675         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
676         gtk_widget_show(combo);
677 }
678
679 static void add_dnd_default_action_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, DnDAction option, DnDAction *option_c)
680 {
681         GtkWidget *combo;
682         gint current = 0;
683
684         *option_c = option;
685
686         pref_table_label(table, column, row, text, 0.0);
687
688         combo = gtk_combo_box_text_new();
689
690         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Ask"));
691         if (option == DND_ACTION_ASK) current = 0;
692         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Copy"));
693         if (option == DND_ACTION_COPY) current = 1;
694         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Move"));
695         if (option == DND_ACTION_MOVE) current = 2;
696
697         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
698
699         g_signal_connect(G_OBJECT(combo), "changed",
700                          G_CALLBACK(dnd_default_action_selection_menu_cb), option_c);
701
702         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
703         gtk_widget_show(combo);
704 }
705
706 static void add_clipboard_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
707                              gint option, gint *option_c)
708 {
709         GtkWidget *combo;
710         gint current = 0;
711
712         *option_c = option;
713
714         pref_table_label(table, column, row, text, 0.0);
715
716         combo = gtk_combo_box_text_new();
717
718         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Primary"));
719         if (option == CLIPBOARD_PRIMARY) current = 0;
720         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Clipboard"));
721         if (option == CLIPBOARD_CLIPBOARD) current = 1;
722         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Both"));
723         if (option == CLIPBOARD_BOTH) current = 2;
724
725         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
726
727         g_signal_connect(G_OBJECT(combo), "changed",
728                          G_CALLBACK(clipboard_selection_menu_cb), option_c);
729
730         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
731         gtk_widget_show(combo);
732 }
733
734 static void zoom_style_selection_menu_cb(GtkWidget *combo, gpointer data)
735 {
736         auto option = static_cast<gint *>(data);
737
738         switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
739                 {
740                 case 0:
741                         *option = ZOOM_GEOMETRIC;
742                         break;
743                 case 1:
744                         *option = ZOOM_ARITHMETIC;
745                         break;
746                 default:
747                         *option = ZOOM_GEOMETRIC;
748                 }
749 }
750
751 static void add_zoom_style_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, ZoomStyle option, ZoomStyle *option_c)
752 {
753         GtkWidget *combo;
754         gint current = 0;
755
756         *option_c = option;
757
758         pref_table_label(table, column, row, text, 0.0);
759
760         combo = gtk_combo_box_text_new();
761
762         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Geometric"));
763         if (option == ZOOM_GEOMETRIC) current = 0;
764         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Arithmetic"));
765         if (option == ZOOM_ARITHMETIC) current = 1;
766
767         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
768
769         g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(zoom_style_selection_menu_cb), option_c);
770
771         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
772         gtk_widget_show(combo);
773 }
774
775 struct UseableMouseItems
776 {
777         const gchar *name; /* GtkActionEntry terminology */
778         const gchar *label;
779         const gchar *stock_id;
780 };
781
782 static const UseableMouseItems useable_mouse_items[] = {
783         {"", "", nullptr},
784         {"FirstImage",  N_("First Image"), GTK_STOCK_GOTO_TOP},
785         {"PrevImage",   N_("Previous Image"), GTK_STOCK_GO_UP},
786         {"NextImage",   N_("Next Image"), GTK_STOCK_GO_DOWN},
787         {"LastImage",   N_("Last Image"), GTK_STOCK_GOTO_BOTTOM},
788         {"Back",        N_("Back"), GTK_STOCK_GO_BACK},
789         {"Forward",     N_("Forward"), GTK_STOCK_GO_FORWARD},
790         {"Home",        N_("Home"), GTK_STOCK_HOME},
791         {"Up",  N_("Up"), GTK_STOCK_GO_UP},
792         {"FirstPage",   N_("First page"), GTK_STOCK_MEDIA_PREVIOUS},
793         {"LastPage",    N_("Last Page"), GTK_STOCK_MEDIA_NEXT},
794         {"NextPage",    N_("Next page"), GTK_STOCK_MEDIA_FORWARD},
795         {"PrevPage",    N_("Previous Page"), GTK_STOCK_MEDIA_REWIND},
796         {"NewWindow",   N_("New _window"), GTK_STOCK_NEW},
797         {"NewCollection",       N_("New collection"), GTK_STOCK_INDEX},
798         {"OpenCollection",      N_("Open collection"), GTK_STOCK_OPEN},
799         {"Search",      N_("Search"), GTK_STOCK_FIND},
800         {"FindDupes",   N_("Find duplicates"), GTK_STOCK_FIND},
801         {"NewFolder",   N_("New folder"),GTK_STOCK_DIRECTORY},
802         {"Copy",        N_("Copy"), GTK_STOCK_COPY},
803         {"Move",        N_("Move"), PIXBUF_INLINE_ICON_MOVE},
804         {"Rename",      N_("Rename"), PIXBUF_INLINE_ICON_RENAME},
805         {"Delete",      N_("Delete"), GTK_STOCK_DELETE},
806         {"CloseWindow", N_("Close Window"), GTK_STOCK_CLOSE},
807         {"PanView",     N_("Pan view"), PIXBUF_INLINE_ICON_PANORAMA},
808         {"SelectAll",   N_("Select all"), PIXBUF_INLINE_ICON_SELECT_ALL},
809         {"SelectNone",  N_("Select none"), PIXBUF_INLINE_ICON_SELECT_NONE},
810         {"SelectInvert",        N_("Select invert"), PIXBUF_INLINE_ICON_SELECT_INVERT},
811         {"ShowFileFilter",      N_("Show file filter"), PIXBUF_INLINE_ICON_FILE_FILTER},
812         {"RectangularSelection",        N_("Select rectangle"), PIXBUF_INLINE_ICON_SELECT_RECTANGLE},
813         {"Print",       N_("Print"), GTK_STOCK_PRINT},
814         {"Preferences", N_("Preferences"), GTK_STOCK_PREFERENCES},
815         {"LayoutConfig",        N_("Configure this window"), GTK_STOCK_PREFERENCES},
816         {"Maintenance", N_("Cache maintenance"), PIXBUF_INLINE_ICON_MAINTENANCE},
817         {"RotateCW",    N_("Rotate clockwise 90°"), PIXBUF_INLINE_ICON_CW},
818         {"RotateCCW",   N_("Rotate counterclockwise 90°"), PIXBUF_INLINE_ICON_CCW},
819         {"Rotate180",   N_("Rotate 180°"), PIXBUF_INLINE_ICON_180},
820         {"Mirror",      N_("Mirror"), PIXBUF_INLINE_ICON_MIRROR},
821         {"Flip",        N_("Flip"), PIXBUF_INLINE_ICON_FLIP},
822         {"AlterNone",   N_("Original state"), PIXBUF_INLINE_ICON_ORIGINAL},
823         {"ZoomIn",      N_("Zoom in"), GTK_STOCK_ZOOM_IN},
824         {"ZoomOut",     N_("Zoom out"), GTK_STOCK_ZOOM_OUT},
825         {"Zoom100",     N_("Zoom 1:1"), GTK_STOCK_ZOOM_100},
826         {"ZoomFit",     N_("Zoom to fit"), GTK_STOCK_ZOOM_FIT},
827         {"ZoomFillHor", N_("Fit Horizontaly"), PIXBUF_INLINE_ICON_ZOOMFILLHOR},
828         {"ZoomFillVert",        N_("Fit vertically"), PIXBUF_INLINE_ICON_ZOOMFILLVERT},
829         {"Zoom200",     N_("Zoom 2:1"), GTK_STOCK_FILE},
830         {"Zoom300",     N_("Zoom 3:1"), GTK_STOCK_FILE},
831         {"Zoom400",     N_("Zoom 4:1"), GTK_STOCK_FILE},
832         {"Zoom50",      N_("Zoom 1:2"), GTK_STOCK_FILE},
833         {"Zoom33",      N_("Zoom1:3"), GTK_STOCK_FILE},
834         {"Zoom25",      N_("Zoom 1:4"), GTK_STOCK_FILE},
835         {"ConnectZoomIn",       N_("Connected Zoom in"), GTK_STOCK_ZOOM_IN},
836         {"SplitPaneSync",       N_("Split Pane Sync"), PIXBUF_INLINE_SPLIT_PANE_SYNC},
837         {"Grayscale",   N_("Grayscale"), PIXBUF_INLINE_ICON_GRAYSCALE},
838         {"OverUnderExposed",    N_("Over Under Exposed"), PIXBUF_INLINE_ICON_EXPOSURE},
839         {"HideTools",   N_("Hide file list"), PIXBUF_INLINE_ICON_HIDETOOLS},
840         {"SlideShowPause",      N_("Pause slideshow"), GTK_STOCK_MEDIA_PAUSE},
841         {"SlideShowFaster",     N_("Slideshow Faster"), GTK_STOCK_FILE},
842         {"SlideShowSlower",     N_("Slideshow Slower"), GTK_STOCK_FILE},
843         {"Refresh",     N_("Refresh"), GTK_STOCK_REFRESH},
844         {"HelpContents",        N_("Help"), GTK_STOCK_HELP},
845         {"ExifWin",     N_("Exif window"), PIXBUF_INLINE_ICON_EXIF},
846         {"Thumbnails",  N_("Show thumbnails"), PIXBUF_INLINE_ICON_THUMB},
847         {"ShowMarks",   N_("Show marks"), PIXBUF_INLINE_ICON_MARKS},
848         {"DrawRectangle",       N_("Draw Rectangle"), PIXBUF_INLINE_ICON_DRAW_RECTANGLE},
849         {"FloatTools",  N_("Float file list"), PIXBUF_INLINE_ICON_FLOAT},
850         {"SBar",        N_("Info sidebar"), PIXBUF_INLINE_ICON_INFO},
851         {"SBarSort",    N_("Sort manager"), PIXBUF_INLINE_ICON_SORT},
852         {"Quit",        N_("Quit"), GTK_STOCK_QUIT},
853         {nullptr,               nullptr, nullptr}
854 };
855
856 static void mouse_buttons_selection_menu_cb(GtkWidget *combo, gpointer data)
857 {
858         auto option = static_cast<gchar **>(data);
859         gchar *label;
860
861         label = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
862
863         const UseableMouseItems *list = useable_mouse_items;
864
865         while (list->name)
866                 {
867                 if (g_strcmp0(list->label, label) == 0)
868                         {
869                         break;
870                         }
871                 list++;
872                 }
873
874         g_free(*option);
875         *option = g_strdup(list->name);
876         g_free(label);
877 }
878
879 static void add_mouse_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
880                              gchar *option, gchar **option_c)
881 {
882         GtkWidget *combo;
883         gint current = 0;
884         gint i = 0;
885
886         *option_c = option;
887
888         pref_table_label(table, column, row, text, 0.0);
889
890         combo = gtk_combo_box_text_new();
891
892         const UseableMouseItems *list = useable_mouse_items;
893
894         while (list->name)
895                 {
896                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), list->label);
897                 if (g_strcmp0(list->name, option) == 0)
898                         {
899                         current = i;
900                         }
901                 i++;
902                 list++;
903                 }
904
905         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
906
907         g_signal_connect(G_OBJECT(combo), "changed",
908                          G_CALLBACK(mouse_buttons_selection_menu_cb), option_c);
909
910         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
911         gtk_widget_show(combo);
912 }
913
914 static void thumb_size_menu_cb(GtkWidget *combo, gpointer UNUSED(data))
915 {
916         gint n;
917
918         n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
919         if (n < 0) return;
920
921         if (static_cast<guint>(n) < sizeof(thumb_size_list) / sizeof(ThumbSize))
922                 {
923                 c_options->thumbnails.max_width = thumb_size_list[n].w;
924                 c_options->thumbnails.max_height = thumb_size_list[n].h;
925                 }
926         else
927                 {
928                 c_options->thumbnails.max_width = options->thumbnails.max_width;
929                 c_options->thumbnails.max_height = options->thumbnails.max_height;
930                 }
931 }
932
933 static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *text)
934 {
935         GtkWidget *combo;
936         gint current;
937         gint i;
938
939         c_options->thumbnails.max_width = options->thumbnails.max_width;
940         c_options->thumbnails.max_height = options->thumbnails.max_height;
941
942         pref_table_label(table, column, row, text, 0.0);
943
944         combo = gtk_combo_box_text_new();
945
946         current = -1;
947         for (i = 0; static_cast<guint>(i) < sizeof(thumb_size_list) / sizeof(ThumbSize); i++)
948                 {
949                 gint w, h;
950                 gchar *buf;
951
952                 w = thumb_size_list[i].w;
953                 h = thumb_size_list[i].h;
954
955                 buf = g_strdup_printf("%d x %d", w, h);
956                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
957                 g_free(buf);
958
959                 if (w == options->thumbnails.max_width && h == options->thumbnails.max_height) current = i;
960                 }
961
962         if (current == -1)
963                 {
964                 gchar *buf;
965
966                 buf = g_strdup_printf("%s %d x %d", _("Custom"), options->thumbnails.max_width, options->thumbnails.max_height);
967                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
968                 g_free(buf);
969
970                 current = i;
971                 }
972
973         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
974         g_signal_connect(G_OBJECT(combo), "changed",
975                          G_CALLBACK(thumb_size_menu_cb), NULL);
976
977         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
978         gtk_widget_show(combo);
979 }
980
981 static void stereo_mode_menu_cb(GtkWidget *combo, gpointer data)
982 {
983         auto option = static_cast<gint *>(data);
984
985         switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
986                 {
987                 case 0:
988                 default:
989                         *option = PR_STEREO_NONE;
990                         break;
991                 case 1:
992                         *option = PR_STEREO_ANAGLYPH_RC;
993                         break;
994                 case 2:
995                         *option = PR_STEREO_ANAGLYPH_GM;
996                         break;
997                 case 3:
998                         *option = PR_STEREO_ANAGLYPH_YB;
999                         break;
1000                 case 4:
1001                         *option = PR_STEREO_ANAGLYPH_GRAY_RC;
1002                         break;
1003                 case 5:
1004                         *option = PR_STEREO_ANAGLYPH_GRAY_GM;
1005                         break;
1006                 case 6:
1007                         *option = PR_STEREO_ANAGLYPH_GRAY_YB;
1008                         break;
1009                 case 7:
1010                         *option = PR_STEREO_ANAGLYPH_DB_RC;
1011                         break;
1012                 case 8:
1013                         *option = PR_STEREO_ANAGLYPH_DB_GM;
1014                         break;
1015                 case 9:
1016                         *option = PR_STEREO_ANAGLYPH_DB_YB;
1017                         break;
1018                 case 10:
1019                         *option = PR_STEREO_HORIZ;
1020                         break;
1021                 case 11:
1022                         *option = PR_STEREO_HORIZ | PR_STEREO_HALF;
1023                         break;
1024                 case 12:
1025                         *option = PR_STEREO_VERT;
1026                         break;
1027                 case 13:
1028                         *option = PR_STEREO_VERT | PR_STEREO_HALF;
1029                         break;
1030                 case 14:
1031                         *option = PR_STEREO_FIXED;
1032                         break;
1033                 }
1034 }
1035
1036 static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1037                              gint option, gint *option_c, gboolean add_fixed)
1038 {
1039         GtkWidget *combo;
1040         gint current = 0;
1041
1042         *option_c = option;
1043
1044         pref_table_label(table, column, row, text, 0.0);
1045
1046         combo = gtk_combo_box_text_new();
1047
1048         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Single image"));
1049
1050         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Red-Cyan"));
1051         if (option & PR_STEREO_ANAGLYPH_RC) current = 1;
1052         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Green-Magenta"));
1053         if (option & PR_STEREO_ANAGLYPH_GM) current = 2;
1054         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Yellow-Blue"));
1055         if (option & PR_STEREO_ANAGLYPH_YB) current = 3;
1056         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Red-Cyan"));
1057         if (option & PR_STEREO_ANAGLYPH_GRAY_RC) current = 4;
1058         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Green-Magenta"));
1059         if (option & PR_STEREO_ANAGLYPH_GRAY_GM) current = 5;
1060         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Yellow-Blue"));
1061         if (option & PR_STEREO_ANAGLYPH_GRAY_YB) current = 6;
1062         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Red-Cyan"));
1063         if (option & PR_STEREO_ANAGLYPH_DB_RC) current = 7;
1064         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Green-Magenta"));
1065         if (option & PR_STEREO_ANAGLYPH_DB_GM) current = 8;
1066         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Yellow-Blue"));
1067         if (option & PR_STEREO_ANAGLYPH_DB_YB) current = 9;
1068
1069         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side"));
1070         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side Half size"));
1071         if (option & PR_STEREO_HORIZ)
1072                 {
1073                 current = 10;
1074                 if (option & PR_STEREO_HALF) current = 11;
1075                 }
1076
1077         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom"));
1078         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom Half size"));
1079         if (option & PR_STEREO_VERT)
1080                 {
1081                 current = 12;
1082                 if (option & PR_STEREO_HALF) current = 13;
1083                 }
1084
1085         if (add_fixed)
1086                 {
1087                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Fixed position"));
1088                 if (option & PR_STEREO_FIXED) current = 14;
1089                 }
1090
1091         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1092
1093         g_signal_connect(G_OBJECT(combo), "changed",
1094                          G_CALLBACK(stereo_mode_menu_cb), option_c);
1095
1096         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1097         gtk_widget_show(combo);
1098 }
1099
1100 static void video_menu_cb(GtkWidget *combo, gpointer data)
1101 {
1102         auto option = static_cast<gchar **>(data);
1103
1104         auto ed = static_cast<EditorDescription *>(g_list_nth_data(editor_list_get(), gtk_combo_box_get_active(GTK_COMBO_BOX(combo))));
1105         *option = ed->key;
1106 }
1107
1108 static void video_menu_populate(gpointer data, gpointer user_data)
1109 {
1110         auto combo = static_cast<GtkWidget *>(user_data);
1111         auto ed = static_cast<EditorDescription *>(data);
1112
1113         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), ed->name);
1114 }
1115
1116 static void add_video_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1117                              gchar *option, gchar **option_c)
1118 {
1119         GtkWidget *combo;
1120         gint current;
1121 /* use lists since they are sorted */
1122         GList *eds = editor_list_get();
1123
1124         *option_c = option;
1125
1126         pref_table_label(table, column, row, text, 0.0);
1127
1128         combo = gtk_combo_box_text_new();
1129         g_list_foreach(eds,video_menu_populate,combo);
1130         current = option ? g_list_index(eds,g_hash_table_lookup(editors,option)): -1;
1131
1132         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1133
1134         g_signal_connect(G_OBJECT(combo), "changed",
1135                          G_CALLBACK(video_menu_cb), option_c);
1136
1137         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1138         gtk_widget_show(combo);
1139 }
1140
1141 static void filter_store_populate()
1142 {
1143         GList *work;
1144
1145         if (!filter_store) return;
1146
1147         gtk_list_store_clear(filter_store);
1148
1149         work = filter_get_list();
1150         while (work)
1151                 {
1152                 FilterEntry *fe;
1153                 GtkTreeIter iter;
1154
1155                 fe = static_cast<FilterEntry *>(work->data);
1156                 work = work->next;
1157
1158                 gtk_list_store_append(filter_store, &iter);
1159                 gtk_list_store_set(filter_store, &iter, 0, fe, -1);
1160                 }
1161 }
1162
1163 static void filter_store_ext_edit_cb(GtkCellRendererText *UNUSED(cell), gchar *path_str,
1164                                      gchar *new_text, gpointer data)
1165 {
1166         auto model = static_cast<GtkWidget *>(data);
1167         auto fe = static_cast<FilterEntry *>(data);
1168         GtkTreePath *tpath;
1169         GtkTreeIter iter;
1170
1171         if (!new_text || strlen(new_text) < 1) return;
1172
1173         tpath = gtk_tree_path_new_from_string(path_str);
1174         gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1175         gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1176
1177         g_free(fe->extensions);
1178         fe->extensions = g_strdup(new_text);
1179
1180         gtk_tree_path_free(tpath);
1181         filter_rebuild();
1182 }
1183
1184 static void filter_store_class_edit_cb(GtkCellRendererText *UNUSED(cell), gchar *path_str,
1185                                        gchar *new_text, gpointer data)
1186 {
1187         auto model = static_cast<GtkWidget *>(data);
1188         auto fe = static_cast<FilterEntry *>(data);
1189         GtkTreePath *tpath;
1190         GtkTreeIter iter;
1191         gint i;
1192
1193         if (!new_text || !new_text[0]) return;
1194
1195         tpath = gtk_tree_path_new_from_string(path_str);
1196         gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1197         gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1198
1199         for (i = 0; i < FILE_FORMAT_CLASSES; i++)
1200                 {
1201                 if (strcmp(new_text, _(format_class_list[i])) == 0)
1202                         {
1203                         fe->file_class = static_cast<FileFormatClass>(i);
1204                         break;
1205                         }
1206                 }
1207
1208         gtk_tree_path_free(tpath);
1209         filter_rebuild();
1210 }
1211
1212 static void filter_store_desc_edit_cb(GtkCellRendererText *UNUSED(cell), gchar *path_str,
1213                                       gchar *new_text, gpointer data)
1214 {
1215         auto model = static_cast<GtkWidget *>(data);
1216         FilterEntry *fe;
1217         GtkTreePath *tpath;
1218         GtkTreeIter iter;
1219
1220         if (!new_text || !new_text[0]) return;
1221
1222         tpath = gtk_tree_path_new_from_string(path_str);
1223         gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1224         gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1225
1226         g_free(fe->description);
1227         fe->description = g_strdup(new_text);
1228
1229         gtk_tree_path_free(tpath);
1230 }
1231
1232 static void filter_store_enable_cb(GtkCellRendererToggle *UNUSED(renderer),
1233                                    gchar *path_str, gpointer data)
1234 {
1235         auto model = static_cast<GtkWidget *>(data);
1236         FilterEntry *fe;
1237         GtkTreePath *tpath;
1238         GtkTreeIter iter;
1239
1240         tpath = gtk_tree_path_new_from_string(path_str);
1241         gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1242         gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1243
1244         fe->enabled = !fe->enabled;
1245
1246         gtk_tree_path_free(tpath);
1247         filter_rebuild();
1248 }
1249
1250 static void filter_store_writable_cb(GtkCellRendererToggle *UNUSED(renderer),
1251                                      gchar *path_str, gpointer data)
1252 {
1253         auto model = static_cast<GtkWidget *>(data);
1254         FilterEntry *fe;
1255         GtkTreePath *tpath;
1256         GtkTreeIter iter;
1257
1258         tpath = gtk_tree_path_new_from_string(path_str);
1259         gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1260         gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1261
1262         fe->writable = !fe->writable;
1263         if (fe->writable) fe->allow_sidecar = FALSE;
1264
1265         gtk_tree_path_free(tpath);
1266         filter_rebuild();
1267 }
1268
1269 static void filter_store_sidecar_cb(GtkCellRendererToggle *UNUSED(renderer),
1270                                     gchar *path_str, gpointer data)
1271 {
1272         auto model = static_cast<GtkWidget *>(data);
1273         FilterEntry *fe;
1274         GtkTreePath *tpath;
1275         GtkTreeIter iter;
1276
1277         tpath = gtk_tree_path_new_from_string(path_str);
1278         gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1279         gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1280
1281         fe->allow_sidecar = !fe->allow_sidecar;
1282         if (fe->allow_sidecar) fe->writable = FALSE;
1283
1284         gtk_tree_path_free(tpath);
1285         filter_rebuild();
1286 }
1287
1288 static void filter_set_func(GtkTreeViewColumn *UNUSED(tree_column), GtkCellRenderer *cell,
1289                             GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1290 {
1291         FilterEntry *fe;
1292
1293         gtk_tree_model_get(tree_model, iter, 0, &fe, -1);
1294
1295         switch (GPOINTER_TO_INT(data))
1296                 {
1297                 case FE_ENABLE:
1298                         g_object_set(GTK_CELL_RENDERER(cell),
1299                                      "active", fe->enabled, NULL);
1300                         break;
1301                 case FE_EXTENSION:
1302                         g_object_set(GTK_CELL_RENDERER(cell),
1303                                      "text", fe->extensions, NULL);
1304                         break;
1305                 case FE_DESCRIPTION:
1306                         g_object_set(GTK_CELL_RENDERER(cell),
1307                                      "text", fe->description, NULL);
1308                         break;
1309                 case FE_CLASS:
1310                         g_object_set(GTK_CELL_RENDERER(cell),
1311                                      "text", _(format_class_list[fe->file_class]), NULL);
1312                         break;
1313                 case FE_WRITABLE:
1314                         g_object_set(GTK_CELL_RENDERER(cell),
1315                                      "active", fe->writable, NULL);
1316                         break;
1317                 case FE_ALLOW_SIDECAR:
1318                         g_object_set(GTK_CELL_RENDERER(cell),
1319                                      "active", fe->allow_sidecar, NULL);
1320                         break;
1321                 }
1322 }
1323
1324 static gboolean filter_add_scroll(gpointer data)
1325 {
1326         GtkTreePath *path;
1327         GList *list_cells;
1328         GtkCellRenderer *cell;
1329         GtkTreeViewColumn *column;
1330         gint rows;
1331         GtkTreeIter iter;
1332         GtkTreeModel *store;
1333         gboolean valid;
1334         FilterEntry *filter;
1335
1336         rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(filter_store), nullptr);
1337         path = gtk_tree_path_new_from_indices(rows-1, -1);
1338
1339         column = gtk_tree_view_get_column(GTK_TREE_VIEW(data), 0);
1340
1341         list_cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1342         cell = static_cast<GtkCellRenderer *>(g_list_last(list_cells)->data);
1343
1344         store = gtk_tree_view_get_model(GTK_TREE_VIEW(data));
1345         valid = gtk_tree_model_get_iter_first(store, &iter);
1346
1347         while (valid)
1348                 {
1349                 gtk_tree_model_get(store, &iter, 0, &filter, -1);
1350
1351                 if (g_strcmp0(filter->extensions, ".new") == 0)
1352                         {
1353                         path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1354                         break;
1355                         }
1356
1357                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1358                 }
1359
1360         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1361                                                                 path, column, FALSE, 0.0, 0.0 );
1362         gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(data),
1363                                                                 path, column, cell, TRUE);
1364
1365         gtk_tree_path_free(path);
1366         g_list_free(list_cells);
1367
1368         return(G_SOURCE_REMOVE);
1369 }
1370
1371 static void filter_add_cb(GtkWidget *UNUSED(widget), gpointer data)
1372 {
1373         filter_add_unique("description", ".new", FORMAT_CLASS_IMAGE, TRUE, FALSE, TRUE);
1374         filter_store_populate();
1375
1376         g_idle_add(static_cast<GSourceFunc>(filter_add_scroll), data);
1377 }
1378
1379 static void filter_remove_cb(GtkWidget *UNUSED(widget), gpointer data)
1380 {
1381         auto filter_view = static_cast<GtkWidget *>(data);
1382         GtkTreeSelection *selection;
1383         GtkTreeIter iter;
1384         FilterEntry *fe;
1385
1386         if (!filter_store) return;
1387         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
1388         if (!gtk_tree_selection_get_selected(selection, nullptr, &iter)) return;
1389         gtk_tree_model_get(GTK_TREE_MODEL(filter_store), &iter, 0, &fe, -1);
1390         if (!fe) return;
1391
1392         filter_remove_entry(fe);
1393         filter_rebuild();
1394         filter_store_populate();
1395 }
1396
1397 static gboolean filter_default_ok_scroll(GtkTreeView *data)
1398 {
1399         GtkTreeIter iter;
1400         GtkTreePath *path;
1401         GtkTreeViewColumn *column;
1402
1403         gtk_tree_model_get_iter_first(GTK_TREE_MODEL(filter_store), &iter);
1404         path = gtk_tree_model_get_path(GTK_TREE_MODEL(filter_store), &iter);
1405         column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1406
1407         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1408                                      path, column,
1409                                      FALSE, 0.0, 0.0);
1410
1411         gtk_tree_path_free(path);
1412
1413         return(G_SOURCE_REMOVE);
1414 }
1415
1416 static void filter_default_ok_cb(GenericDialog *gd, gpointer UNUSED(data))
1417 {
1418         filter_reset();
1419         filter_add_defaults();
1420         filter_rebuild();
1421         filter_store_populate();
1422
1423         g_idle_add(reinterpret_cast<GSourceFunc>(filter_default_ok_scroll), gd->data);
1424 }
1425
1426 static void dummy_cancel_cb(GenericDialog *UNUSED(gd), gpointer UNUSED(data))
1427 {
1428         /* no op, only so cancel button appears */
1429 }
1430
1431 static void filter_default_cb(GtkWidget *widget, gpointer data)
1432 {
1433         GenericDialog *gd;
1434
1435         gd = generic_dialog_new(_("Reset filters"),
1436                                 "reset_filter", widget, TRUE,
1437                                 dummy_cancel_cb, data);
1438         generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION, _("Reset filters"),
1439                                    _("This will reset the file filters to the defaults.\nContinue?"), TRUE);
1440         generic_dialog_add_button(gd, GTK_STOCK_OK, nullptr, filter_default_ok_cb, TRUE);
1441         gtk_widget_show(gd->dialog);
1442 }
1443
1444 static void filter_disable_cb(GtkWidget *widget, gpointer data)
1445 {
1446         auto frame = static_cast<GtkWidget *>(data);
1447
1448         gtk_widget_set_sensitive(frame,
1449                                  !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1450 }
1451
1452 static void safe_delete_view_cb(GtkWidget *UNUSED(widget), gpointer UNUSED(data))
1453 {
1454         layout_set_path(nullptr, gtk_entry_get_text(GTK_ENTRY(safe_delete_path_entry)));
1455 }
1456
1457 static void safe_delete_clear_ok_cb(GenericDialog *UNUSED(gd), gpointer UNUSED(data))
1458 {
1459         file_util_trash_clear();
1460 }
1461
1462 static void safe_delete_clear_cb(GtkWidget *widget, gpointer UNUSED(data))
1463 {
1464         GenericDialog *gd;
1465         GtkWidget *entry;
1466         gd = generic_dialog_new(_("Clear trash"),
1467                                 "clear_trash", widget, TRUE,
1468                                 dummy_cancel_cb, nullptr);
1469         generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION, _("Clear trash"),
1470                                     _("This will remove the trash contents."), FALSE);
1471         generic_dialog_add_button(gd, GTK_STOCK_OK, nullptr, safe_delete_clear_ok_cb, TRUE);
1472         entry = gtk_entry_new();
1473         gtk_widget_set_can_focus(entry, FALSE);
1474         gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
1475         if (options->file_ops.safe_delete_path) gtk_entry_set_text(GTK_ENTRY(entry), options->file_ops.safe_delete_path);
1476         gtk_box_pack_start(GTK_BOX(gd->vbox), entry, FALSE, FALSE, 0);
1477         gtk_widget_show(entry);
1478         gtk_widget_show(gd->dialog);
1479 }
1480
1481 static void image_overlay_template_view_changed_cb(GtkWidget *UNUSED(widget), gpointer data)
1482 {
1483         GtkWidget *pTextView;
1484         GtkTextBuffer *pTextBuffer;
1485         GtkTextIter iStart;
1486         GtkTextIter iEnd;
1487
1488         pTextView = GTK_WIDGET(data);
1489
1490         pTextBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pTextView));
1491         gtk_text_buffer_get_start_iter(pTextBuffer, &iStart);
1492         gtk_text_buffer_get_end_iter(pTextBuffer, &iEnd);
1493
1494         set_image_overlay_template_string(&c_options->image_overlay.template_string,
1495                                           gtk_text_buffer_get_text(pTextBuffer, &iStart, &iEnd, TRUE));
1496 }
1497
1498 static void image_overlay_default_template_ok_cb(GenericDialog *UNUSED(gd), gpointer data)
1499 {
1500         auto text_view = static_cast<GtkTextView *>(data);
1501         GtkTextBuffer *buffer;
1502
1503         set_default_image_overlay_template_string(&options->image_overlay.template_string);
1504         if (!configwindow) return;
1505
1506         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
1507         gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
1508 }
1509
1510 static void image_overlay_default_template_cb(GtkWidget *widget, gpointer data)
1511 {
1512         GenericDialog *gd;
1513
1514         gd = generic_dialog_new(_("Reset image overlay template string"),
1515                                 "reset_image_overlay_template_string", widget, TRUE,
1516                                 dummy_cancel_cb, data);
1517         generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION, _("Reset image overlay template string"),
1518                                    _("This will reset the image overlay template string to the default.\nContinue?"), TRUE);
1519         generic_dialog_add_button(gd, GTK_STOCK_OK, nullptr, image_overlay_default_template_ok_cb, TRUE);
1520         gtk_widget_show(gd->dialog);
1521 }
1522
1523 static void image_overlay_help_cb(GtkWidget *UNUSED(widget), gpointer UNUSED(data))
1524 {
1525         help_window_show("GuideOptionsOSD.html");
1526 }
1527
1528 static void image_overlay_set_font_cb(GtkWidget *widget, gpointer UNUSED(data))
1529 {
1530         GtkWidget *dialog;
1531         char *font;
1532         PangoFontDescription *font_desc;
1533
1534         dialog = gtk_font_chooser_dialog_new("Image Overlay Font", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1535         gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), options->image_overlay.font);
1536
1537         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1538                 {
1539                 font_desc = gtk_font_chooser_get_font_desc(GTK_FONT_CHOOSER(dialog));
1540                 font = pango_font_description_to_string(font_desc);
1541                 g_free(c_options->image_overlay.font);
1542                 c_options->image_overlay.font = g_strdup(font);
1543                 g_free(font);
1544                 }
1545
1546         gtk_widget_destroy(dialog);
1547 }
1548
1549 static void image_overlay_set_text_colour_cb(GtkWidget *widget, gpointer UNUSED(data))
1550 {
1551         GtkWidget *dialog;
1552         GdkRGBA colour;
1553
1554         dialog = gtk_color_chooser_dialog_new("Image Overlay Text Colour", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1555         colour.red = options->image_overlay.text_red;
1556         colour.green = options->image_overlay.text_green;
1557         colour.blue = options->image_overlay.text_blue;
1558         colour.alpha = options->image_overlay.text_alpha;
1559         gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1560         gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1561
1562         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1563                 {
1564                 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1565                 c_options->image_overlay.text_red = colour.red*255;
1566                 c_options->image_overlay.text_green = colour.green*255;
1567                 c_options->image_overlay.text_blue = colour.blue*255;
1568                 c_options->image_overlay.text_alpha = colour.alpha*255;
1569                 }
1570         gtk_widget_destroy(dialog);
1571 }
1572
1573
1574 static void image_overlay_set_background_colour_cb(GtkWidget *widget, gpointer UNUSED(data))
1575 {
1576         GtkWidget *dialog;
1577         GdkRGBA colour;
1578
1579         dialog = gtk_color_chooser_dialog_new("Image Overlay Background Colour", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1580         colour.red = options->image_overlay.background_red;
1581         colour.green = options->image_overlay.background_green;
1582         colour.blue = options->image_overlay.background_blue;
1583         colour.alpha = options->image_overlay.background_alpha;
1584         gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1585         gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1586
1587         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1588                 {
1589                 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1590                 c_options->image_overlay.background_red = colour.red*255;
1591                 c_options->image_overlay.background_green = colour.green*255;
1592                 c_options->image_overlay.background_blue = colour.blue*255;
1593                 c_options->image_overlay.background_alpha = colour.alpha*255;
1594                 }
1595         gtk_widget_destroy(dialog);
1596 }
1597
1598 static void accel_store_populate()
1599 {
1600         LayoutWindow *lw;
1601         GList *groups, *actions;
1602         GtkAction *action;
1603         const gchar *accel_path;
1604         GtkAccelKey key;
1605         GtkTreeIter iter;
1606
1607         if (!accel_store || !layout_window_list || !layout_window_list->data) return;
1608
1609         gtk_tree_store_clear(accel_store);
1610         lw = static_cast<LayoutWindow *>(layout_window_list->data); /* get the actions from the first window, it should not matter, they should be the same in all windows */
1611
1612         g_assert(lw && lw->ui_manager);
1613         groups = gtk_ui_manager_get_action_groups(lw->ui_manager);
1614         while (groups)
1615                 {
1616                 actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
1617                 while (actions)
1618                         {
1619                         action = GTK_ACTION(actions->data);
1620                         accel_path = gtk_action_get_accel_path(action);
1621                         if (accel_path && gtk_accel_map_lookup_entry(accel_path, &key))
1622                                 {
1623                                 gchar *label, *label2, *tooltip, *accel;
1624                                 g_object_get(action,
1625                                              "tooltip", &tooltip,
1626                                              "label", &label,
1627                                              NULL);
1628
1629                                 if (pango_parse_markup(label, -1, '_', nullptr, &label2, nullptr, nullptr) && label2)
1630                                         {
1631                                         g_free(label);
1632                                         label = label2;
1633                                         }
1634
1635                                 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1636
1637                                 if (tooltip)
1638                                         {
1639                                         gtk_tree_store_append(accel_store, &iter, nullptr);
1640                                         gtk_tree_store_set(accel_store, &iter,
1641                                                            AE_ACTION, label,
1642                                                            AE_KEY, accel,
1643                                                            AE_TOOLTIP, tooltip ? tooltip : "",
1644                                                            AE_ACCEL, accel_path,
1645                                                            -1);
1646                                         }
1647
1648                                 g_free(accel);
1649                                 g_free(label);
1650                                 g_free(tooltip);
1651                                 }
1652                         actions = actions->next;
1653                         }
1654
1655                 groups = groups->next;
1656                 }
1657 }
1658
1659 static void accel_store_cleared_cb(GtkCellRendererAccel *UNUSED(accel), gchar *UNUSED(path_string), gpointer UNUSED(user_data))
1660 {
1661
1662 }
1663
1664 static gboolean accel_remove_key_cb(GtkTreeModel *model, GtkTreePath *UNUSED(path), GtkTreeIter *iter, gpointer data)
1665 {
1666         auto accel1 = static_cast<gchar *>(data);
1667         gchar *accel2;
1668         GtkAccelKey key1;
1669         GtkAccelKey key2;
1670
1671         gtk_tree_model_get(model, iter, AE_KEY, &accel2, -1);
1672
1673         gtk_accelerator_parse(accel1, &key1.accel_key, &key1.accel_mods);
1674         gtk_accelerator_parse(accel2, &key2.accel_key, &key2.accel_mods);
1675
1676         if (key1.accel_key == key2.accel_key && key1.accel_mods == key2.accel_mods)
1677                 {
1678                 gtk_tree_store_set(accel_store, iter, AE_KEY, "",  -1);
1679                 DEBUG_1("accelerator key '%s' is already used, removing.", accel1);
1680                 }
1681
1682         g_free(accel2);
1683
1684         return FALSE;
1685 }
1686
1687
1688 static void accel_store_edited_cb(GtkCellRendererAccel *UNUSED(accel), gchar *path_string, guint accel_key, GdkModifierType accel_mods, guint UNUSED(hardware_keycode), gpointer UNUSED(user_data))
1689 {
1690         auto model = reinterpret_cast<GtkTreeModel *>(accel_store);
1691         GtkTreeIter iter;
1692         gchar *acc;
1693         gchar *accel_path;
1694         GtkAccelKey old_key, key;
1695         GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1696
1697         gtk_tree_model_get_iter(model, &iter, path);
1698         gtk_tree_model_get(model, &iter, AE_ACCEL, &accel_path, -1);
1699
1700         /* test if the accelerator can be stored without conflicts*/
1701         gtk_accel_map_lookup_entry(accel_path, &old_key);
1702
1703         /* change the key and read it back (change may fail on keys hardcoded in gtk)*/
1704         gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, TRUE);
1705         gtk_accel_map_lookup_entry(accel_path, &key);
1706
1707         /* restore the original for now, the key will be really changed when the changes are confirmed */
1708         gtk_accel_map_change_entry(accel_path, old_key.accel_key, old_key.accel_mods, TRUE);
1709
1710         acc = gtk_accelerator_name(key.accel_key, key.accel_mods);
1711         gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, acc);
1712
1713         gtk_tree_store_set(accel_store, &iter, AE_KEY, acc, -1);
1714         gtk_tree_path_free(path);
1715         g_free(acc);
1716 }
1717
1718 static gboolean accel_default_scroll(GtkTreeView *data)
1719 {
1720         GtkTreeIter iter;
1721         GtkTreePath *path;
1722         GtkTreeViewColumn *column;
1723
1724         gtk_tree_model_get_iter_first(GTK_TREE_MODEL(accel_store), &iter);
1725         path = gtk_tree_model_get_path(GTK_TREE_MODEL(accel_store), &iter);
1726         column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1727
1728         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1729                                      path, column,
1730                                      FALSE, 0.0, 0.0);
1731
1732         gtk_tree_path_free(path);
1733
1734         return(G_SOURCE_REMOVE);
1735 }
1736
1737 static void accel_default_cb(GtkWidget *UNUSED(widget), gpointer data)
1738 {
1739         accel_store_populate();
1740
1741         g_idle_add(reinterpret_cast<GSourceFunc>(accel_default_scroll), data);
1742 }
1743
1744 void accel_clear_selection(GtkTreeModel *UNUSED(model), GtkTreePath *UNUSED(path), GtkTreeIter *iter, gpointer UNUSED(data))
1745 {
1746         gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1747 }
1748
1749 void accel_reset_selection(GtkTreeModel *model, GtkTreePath *UNUSED(path), GtkTreeIter *iter, gpointer UNUSED(data))
1750 {
1751         GtkAccelKey key;
1752         gchar *accel_path, *accel;
1753
1754         gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, -1);
1755         gtk_accel_map_lookup_entry(accel_path, &key);
1756         accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1757
1758         gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, accel);
1759
1760         gtk_tree_store_set(accel_store, iter, AE_KEY, accel, -1);
1761         g_free(accel_path);
1762         g_free(accel);
1763 }
1764
1765 static void accel_clear_cb(GtkWidget *UNUSED(widget), gpointer data)
1766 {
1767         GtkTreeSelection *selection;
1768
1769         if (!accel_store) return;
1770         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1771         gtk_tree_selection_selected_foreach(selection, &accel_clear_selection, nullptr);
1772 }
1773
1774 static void accel_reset_cb(GtkWidget *UNUSED(widget), gpointer data)
1775 {
1776         GtkTreeSelection *selection;
1777
1778         if (!accel_store) return;
1779         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1780         gtk_tree_selection_selected_foreach(selection, &accel_reset_selection, nullptr);
1781 }
1782
1783
1784
1785 static GtkWidget *scrolled_notebook_page(GtkWidget *notebook, const gchar *title)
1786 {
1787         GtkWidget *label;
1788         GtkWidget *vbox;
1789         GtkWidget *scrolled;
1790         GtkWidget *viewport;
1791
1792         scrolled = gtk_scrolled_window_new(nullptr, nullptr);
1793         gtk_container_set_border_width(GTK_CONTAINER(scrolled), PREF_PAD_BORDER);
1794         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1795                                        GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1796         label = gtk_label_new(title);
1797         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, label);
1798         gtk_widget_show(scrolled);
1799
1800         viewport = gtk_viewport_new(nullptr, nullptr);
1801         gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1802         gtk_container_add(GTK_CONTAINER(scrolled), viewport);
1803         gtk_widget_show(viewport);
1804
1805         vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1806         gtk_container_add(GTK_CONTAINER(viewport), vbox);
1807         gtk_widget_show(vbox);
1808
1809         return vbox;
1810 }
1811
1812 static void cache_standard_cb(GtkWidget *widget, gpointer UNUSED(data))
1813 {
1814         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1815                 {
1816                 c_options->thumbnails.spec_standard =TRUE;
1817                 c_options->thumbnails.cache_into_dirs = FALSE;
1818                 }
1819 }
1820
1821 static void cache_geeqie_cb(GtkWidget *widget, gpointer UNUSED(data))
1822 {
1823         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1824                 {
1825                 c_options->thumbnails.spec_standard =FALSE;
1826                 c_options->thumbnails.cache_into_dirs = FALSE;
1827                 }
1828 }
1829
1830 static void cache_local_cb(GtkWidget *widget, gpointer UNUSED(data))
1831 {
1832         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1833                 {
1834                 c_options->thumbnails.cache_into_dirs = TRUE;
1835                 c_options->thumbnails.spec_standard =FALSE;
1836                 }
1837 }
1838
1839 static void help_search_engine_entry_icon_cb(GtkEntry *UNUSED(entry), GtkEntryIconPosition pos,
1840                                                                         GdkEvent *UNUSED(event), gpointer userdata)
1841 {
1842         if (pos == GTK_ENTRY_ICON_PRIMARY)
1843                 {
1844                 gtk_entry_set_text(GTK_ENTRY(userdata), HELP_SEARCH_ENGINE);
1845                 }
1846         else
1847                 {
1848                 gtk_entry_set_text(GTK_ENTRY(userdata), "");
1849                 }
1850 }
1851
1852 static void star_rating_star_icon_cb(GtkEntry *UNUSED(entry), GtkEntryIconPosition pos,
1853                                                                         GdkEvent *UNUSED(event), gpointer userdata)
1854 {
1855         gchar *rating_symbol;
1856
1857         if (pos == GTK_ENTRY_ICON_PRIMARY)
1858                 {
1859                 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_STAR);
1860                 gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1861                 g_free(rating_symbol);
1862                 }
1863         else
1864                 {
1865                 gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1866                 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1867                 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1868                 }
1869 }
1870
1871 static void star_rating_rejected_icon_cb(GtkEntry *UNUSED(entry), GtkEntryIconPosition pos,
1872                                                                         GdkEvent *UNUSED(event), gpointer userdata)
1873 {
1874         gchar *rating_symbol;
1875
1876         if (pos == GTK_ENTRY_ICON_PRIMARY)
1877                 {
1878                 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_REJECTED);
1879                 gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1880                 g_free(rating_symbol);
1881                 }
1882         else
1883                 {
1884                 gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1885                 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1886                 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1887                 }
1888 }
1889
1890 static guint star_rating_symbol_test(GtkWidget *UNUSED(widget), gpointer data)
1891 {
1892         auto hbox = static_cast<GtkContainer *>(data);
1893         GString *str = g_string_new(nullptr);
1894         GtkEntry *hex_code_entry;
1895         gchar *hex_code_full;
1896         gchar **hex_code;
1897         GList *list;
1898         guint64 hex_value = 0;
1899
1900         list = gtk_container_get_children(hbox);
1901
1902         hex_code_entry = static_cast<GtkEntry *>(g_list_nth_data(list, 2));
1903         hex_code_full = g_strdup(gtk_entry_get_text(hex_code_entry));
1904
1905         hex_code = g_strsplit(hex_code_full, "+", 2);
1906         if (hex_code[0] && hex_code[1])
1907                 {
1908                 hex_value = strtoull(hex_code[1], nullptr, 16);
1909                 }
1910         if (!hex_value || hex_value > 0x10FFFF)
1911                 {
1912                 hex_value = 0x003F; // Unicode 'Question Mark'
1913                 }
1914         str = g_string_append_unichar(str, static_cast<gunichar>(hex_value));
1915         gtk_label_set_text(static_cast<GtkLabel *>(g_list_nth_data(list, 1)), str->str);
1916
1917         g_strfreev(hex_code);
1918         g_string_free(str, TRUE);
1919         g_free(hex_code_full);
1920
1921         return hex_value;
1922 }
1923
1924 static void star_rating_star_test_cb(GtkWidget *widget, gpointer data)
1925 {
1926         guint64 star_symbol;
1927
1928         star_symbol = star_rating_symbol_test(widget, data);
1929         c_options->star_rating.star = star_symbol;
1930 }
1931
1932 static void star_rating_rejected_test_cb(GtkWidget *widget, gpointer data)
1933 {
1934         guint64 rejected_symbol;
1935
1936         rejected_symbol = star_rating_symbol_test(widget, data);
1937         c_options->star_rating.rejected = rejected_symbol;
1938 }
1939
1940 /* general options tab */
1941 static void timezone_database_install_cb(GtkWidget *widget, gpointer data);
1942 struct TZData
1943 {
1944         GenericDialog *gd;
1945         GCancellable *cancellable;
1946
1947         GtkWidget *progress;
1948         GFile *tmp_g_file;
1949         GFile *timezone_database_gq;
1950         gchar *timezone_database_user;
1951 };
1952
1953 static void config_tab_general(GtkWidget *notebook)
1954 {
1955         GtkWidget *vbox;
1956         GtkWidget *hbox;
1957         GtkWidget *group;
1958         GtkWidget *group_frame;
1959         GtkWidget *subgroup;
1960         GtkWidget *button;
1961         GtkWidget *ct_button;
1962         GtkWidget *table;
1963         GtkWidget *spin;
1964         gint hours, minutes, remainder;
1965         gdouble seconds;
1966         GtkWidget *star_rating_entry;
1967         GString *str;
1968         gchar *rating_symbol;
1969         gchar *path;
1970         gchar *basename;
1971         gchar *download_locn;
1972         GNetworkMonitor *net_mon;
1973         GSocketConnectable *tz_org;
1974         gboolean internet_available = FALSE;
1975         TZData *tz;
1976
1977         vbox = scrolled_notebook_page(notebook, _("General"));
1978
1979         group = pref_group_new(vbox, FALSE, _("Thumbnails"), GTK_ORIENTATION_VERTICAL);
1980
1981         table = pref_table_new(group, 2, 2, FALSE, FALSE);
1982         add_thumb_size_menu(table, 0, 0, _("Size:"));
1983         add_quality_menu(table, 0, 1, _("Quality:"), options->thumbnails.quality, &c_options->thumbnails.quality);
1984
1985         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1986         pref_label_new(hbox, _("Custom size: "));
1987         pref_spin_new_int(hbox, _("Width:"), nullptr, 1, 512, 1, options->thumbnails.max_width, &c_options->thumbnails.max_width);
1988         pref_spin_new_int(hbox, _("Height:"), nullptr, 1, 512, 1, options->thumbnails.max_height, &c_options->thumbnails.max_height);
1989
1990         ct_button = pref_checkbox_new_int(group, _("Cache thumbnails and sim. files"),
1991                                           options->thumbnails.enable_caching, &c_options->thumbnails.enable_caching);
1992
1993         subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1994         pref_checkbox_link_sensitivity(ct_button, subgroup);
1995
1996         c_options->thumbnails.spec_standard = options->thumbnails.spec_standard;
1997         c_options->thumbnails.cache_into_dirs = options->thumbnails.cache_into_dirs;
1998         group_frame = pref_frame_new(subgroup, TRUE, _("Use Geeqie thumbnail style and cache"),
1999                                                                                 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2000         button = pref_radiobutton_new(group_frame, nullptr,  get_thumbnails_cache_dir(),
2001                                                         !options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2002                                                         G_CALLBACK(cache_geeqie_cb), nullptr);
2003
2004         group_frame = pref_frame_new(subgroup, TRUE,
2005                                                         _("Store thumbnails local to image folder (non-standard)"),
2006                                                         GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2007         pref_radiobutton_new(group_frame, button, "*/.thumbnails",
2008                                                         !options->thumbnails.spec_standard && options->thumbnails.cache_into_dirs,
2009                                                         G_CALLBACK(cache_local_cb), nullptr);
2010
2011         group_frame = pref_frame_new(subgroup, TRUE,
2012                                                         _("Use standard thumbnail style and cache, shared with other applications"),
2013                                                         GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2014         pref_radiobutton_new(group_frame, button, get_thumbnails_standard_cache_dir(),
2015                                                         options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2016                                                         G_CALLBACK(cache_standard_cb), nullptr);
2017
2018         pref_checkbox_new_int(group, _("Use EXIF thumbnails when available (EXIF thumbnails may be outdated)"),
2019                               options->thumbnails.use_exif, &c_options->thumbnails.use_exif);
2020
2021         pref_checkbox_new_int(group, _("Thumbnail color management"),
2022                                 options->thumbnails.use_color_management, &c_options->thumbnails.use_color_management);
2023
2024         spin = pref_spin_new_int(group, _("Collection preview:"), nullptr,
2025                                  1, 999, 1,
2026                                  options->thumbnails.collection_preview, &c_options->thumbnails.collection_preview);
2027         gtk_widget_set_tooltip_text(spin, _("The maximum number of thumbnails shown in a Collection preview montage"));
2028
2029 #ifdef HAVE_FFMPEGTHUMBNAILER_METADATA
2030         pref_checkbox_new_int(group, _("Use embedded metadata in video files as thumbnails when available"),
2031                               options->thumbnails.use_ft_metadata, &c_options->thumbnails.use_ft_metadata);
2032
2033 //      pref_checkbox_new_int(group, _("Ignore embedded metadata if size is too small"),
2034 //                            options->thumbnails.use_ft_metadata_small, &c_options->thumbnails.use_ft_metadata_small);
2035 #endif
2036
2037         pref_spacer(group, PREF_PAD_GROUP);
2038
2039         group = pref_group_new(vbox, FALSE, _("Star Rating"), GTK_ORIENTATION_VERTICAL);
2040
2041         c_options->star_rating.star = options->star_rating.star;
2042         c_options->star_rating.rejected = options->star_rating.rejected;
2043
2044         str = g_string_new(nullptr);
2045         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2046         pref_label_new(hbox, _("Star character: "));
2047         str = g_string_append_unichar(str, options->star_rating.star);
2048         pref_label_new(hbox, g_strdup(str->str));
2049         rating_symbol = g_strdup_printf("U+%X", options->star_rating.star);
2050         star_rating_entry = gtk_entry_new();
2051         gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2052         gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2053         gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2054         gtk_widget_show(star_rating_entry);
2055         button = pref_button_new(nullptr, nullptr, _("Set"), FALSE,
2056                                         G_CALLBACK(star_rating_star_test_cb), hbox);
2057         gtk_widget_set_tooltip_text(button, _("Display selected character"));
2058         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2059         gtk_widget_show(button);
2060         gtk_widget_set_tooltip_text(star_rating_entry, _("Hexadecimal representation of a Unicode character. A list of all Unicode characters may be found on the Internet."));
2061         gtk_entry_set_icon_from_stock(GTK_ENTRY(star_rating_entry),
2062                                                 GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
2063         gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2064                                                 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2065         gtk_entry_set_icon_from_stock(GTK_ENTRY(star_rating_entry),
2066                                                 GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_REVERT_TO_SAVED);
2067         gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2068                                                 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2069         g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2070                                                 G_CALLBACK(star_rating_star_icon_cb),
2071                                                 star_rating_entry);
2072
2073         g_string_free(str, TRUE);
2074         g_free(rating_symbol);
2075
2076         str = g_string_new(nullptr);
2077         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2078         pref_label_new(hbox, _("Rejected character: "));
2079         str = g_string_append_unichar(str, options->star_rating.rejected);
2080         pref_label_new(hbox, g_strdup(str->str));
2081         rating_symbol = g_strdup_printf("U+%X", options->star_rating.rejected);
2082         star_rating_entry = gtk_entry_new();
2083         gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2084         gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2085         gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2086         gtk_widget_show(star_rating_entry);
2087         button = pref_button_new(nullptr, nullptr, _("Set"), FALSE,
2088                                         G_CALLBACK(star_rating_rejected_test_cb), hbox);
2089         gtk_widget_set_tooltip_text(button, _("Display selected character"));
2090         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2091         gtk_widget_show(button);
2092         gtk_widget_set_tooltip_text(star_rating_entry, _("Hexadecimal representation of a Unicode character. A list of all Unicode characters may be found on the Internet."));
2093         gtk_entry_set_icon_from_stock(GTK_ENTRY(star_rating_entry),
2094                                                 GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
2095         gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2096                                                 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2097         gtk_entry_set_icon_from_stock(GTK_ENTRY(star_rating_entry),
2098                                                 GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_REVERT_TO_SAVED);
2099         gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2100                                                 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2101         g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2102                                                 G_CALLBACK(star_rating_rejected_icon_cb),
2103                                                 star_rating_entry);
2104
2105         g_string_free(str, TRUE);
2106         g_free(rating_symbol);
2107
2108         pref_spacer(group, PREF_PAD_GROUP);
2109
2110         group = pref_group_new(vbox, FALSE, _("Slide show"), GTK_ORIENTATION_VERTICAL);
2111
2112         c_options->slideshow.delay = options->slideshow.delay;
2113         hours = options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2114         remainder = options->slideshow.delay % (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2115         minutes = remainder / (60 * SLIDESHOW_SUBSECOND_PRECISION);
2116         seconds = static_cast<gdouble>(remainder % (60 * SLIDESHOW_SUBSECOND_PRECISION)) /
2117                                                                                         SLIDESHOW_SUBSECOND_PRECISION;
2118
2119         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2120
2121         spin = pref_spin_new(hbox, _("Delay between image change hrs:mins:secs.dec"), nullptr,
2122                                                                                 0, 23, 1.0, 0,
2123                                                                                 options->slideshow.delay ? hours : 0.0,
2124                                                                                 G_CALLBACK(slideshow_delay_hours_cb), nullptr);
2125         gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2126         spin = pref_spin_new(hbox, ":" , nullptr,
2127                                                                                 0, 59, 1.0, 0,
2128                                                                                 options->slideshow.delay ? minutes: 0.0,
2129                                                                                 G_CALLBACK(slideshow_delay_minutes_cb), nullptr);
2130         gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2131         spin = pref_spin_new(hbox, ":", nullptr,
2132                                                                                 SLIDESHOW_MIN_SECONDS, 59, 1.0, 1,
2133                                                                                 options->slideshow.delay ? seconds : 10.0,
2134                                                                                 G_CALLBACK(slideshow_delay_seconds_cb), nullptr);
2135         gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2136
2137         pref_checkbox_new_int(group, _("Random"), options->slideshow.random, &c_options->slideshow.random);
2138         pref_checkbox_new_int(group, _("Repeat"), options->slideshow.repeat, &c_options->slideshow.repeat);
2139
2140         pref_spacer(group, PREF_PAD_GROUP);
2141
2142         group = pref_group_new(vbox, FALSE, _("Image loading and caching"), GTK_ORIENTATION_VERTICAL);
2143
2144         pref_spin_new_int(group, _("Decoded image cache size (MiB):"), nullptr,
2145                           0, 99999, 1, options->image.image_cache_max, &c_options->image.image_cache_max);
2146         pref_checkbox_new_int(group, _("Preload next image"),
2147                               options->image.enable_read_ahead, &c_options->image.enable_read_ahead);
2148
2149         pref_checkbox_new_int(group, _("Refresh on file change"),
2150                               options->update_on_time_change, &c_options->update_on_time_change);
2151
2152
2153         pref_spacer(group, PREF_PAD_GROUP);
2154
2155         group = pref_group_new(vbox, FALSE, _("Expand menu and toolbar"), GTK_ORIENTATION_VERTICAL);
2156
2157         pref_checkbox_new_int(group, _("Expand menu and toolbar (NOTE! Geeqie must be restarted for change to take effect)"),
2158                                 options->expand_menu_toolbar, &c_options->expand_menu_toolbar);
2159         gtk_widget_set_tooltip_text(group, _("Expand the menu and toolbar to the full width of the window"));
2160
2161         pref_spacer(group, PREF_PAD_GROUP);
2162
2163         if (g_getenv("APPDIR") && strstr(g_getenv("APPDIR"), "/tmp/.mount_Geeqie"))
2164                 {
2165                 group = pref_group_new(vbox, FALSE, _("AppImage updates notifications"), GTK_ORIENTATION_VERTICAL);
2166                 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2167                 pref_checkbox_new_int(group, _("Enable"), options->appimage_notifications, &c_options->appimage_notifications);
2168                 gtk_widget_set_tooltip_text(group, _("Show a notification on start-up if the server has a newer version than the current. Requires an Internet connection"));
2169
2170                 pref_spacer(group, PREF_PAD_GROUP);
2171                 }
2172
2173
2174         net_mon = g_network_monitor_get_default();
2175         tz_org = g_network_address_parse_uri(TIMEZONE_DATABASE_WEB, 80, nullptr);
2176         if (tz_org)
2177                 {
2178                 internet_available = g_network_monitor_can_reach(net_mon, tz_org, nullptr, nullptr);
2179                 g_object_unref(tz_org);
2180                 }
2181
2182         group = pref_group_new(vbox, FALSE, _("Timezone database"), GTK_ORIENTATION_VERTICAL);
2183         hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2184
2185         if (!internet_available)
2186                 {
2187                 gtk_widget_set_sensitive(group, FALSE);
2188                 }
2189
2190         tz = g_new0(TZData, 1);
2191
2192         path = path_from_utf8(TIMEZONE_DATABASE_WEB);
2193         basename = g_path_get_basename(path);
2194         tz->timezone_database_user = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
2195         g_free(path);
2196         g_free(basename);
2197
2198         if (isfile(tz->timezone_database_user))
2199                 {
2200                 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Update"), FALSE, G_CALLBACK(timezone_database_install_cb), tz);
2201                 }
2202         else
2203                 {
2204                 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Install"), FALSE, G_CALLBACK(timezone_database_install_cb), tz);
2205                 }
2206
2207         download_locn = g_strconcat(_("Download database from: "), TIMEZONE_DATABASE_WEB, NULL);
2208         pref_label_new(GTK_WIDGET(hbox), download_locn);
2209         g_free(download_locn);
2210
2211         if (!internet_available)
2212                 {
2213                 gtk_widget_set_tooltip_text(button, _("No Internet connection!\nThe timezone database is used to display exif time and date\ncorrected for UTC offset and Daylight Saving Time"));
2214                 }
2215         else
2216                 {
2217                 gtk_widget_set_tooltip_text(button, _("The timezone database is used to display exif time and date\ncorrected for UTC offset and Daylight Saving Time"));
2218                 }
2219         gtk_widget_show(button);
2220
2221         pref_spacer(group, PREF_PAD_GROUP);
2222
2223         group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL);
2224
2225         help_search_engine_entry = gtk_entry_new();
2226         gtk_entry_set_text(GTK_ENTRY(help_search_engine_entry), options->help_search_engine);
2227         gtk_box_pack_start(GTK_BOX(group), help_search_engine_entry, FALSE, FALSE, 0);
2228         gtk_widget_show(help_search_engine_entry);
2229
2230         gtk_widget_set_tooltip_text(help_search_engine_entry, _("The format varies between search engines, e.g the format may be:\nhttps://www.search_engine.com/search?q=site:geeqie.org/help\nhttps://www.search_engine.com/?q=site:geeqie.org/help"));
2231
2232         gtk_entry_set_icon_from_stock(GTK_ENTRY(help_search_engine_entry),
2233                                                 GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
2234         gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2235                                                 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2236         gtk_entry_set_icon_from_stock(GTK_ENTRY(help_search_engine_entry),
2237                                                 GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_REVERT_TO_SAVED);
2238         gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2239                                                 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2240         g_signal_connect(GTK_ENTRY(help_search_engine_entry), "icon-press",
2241                                                 G_CALLBACK(help_search_engine_entry_icon_cb),
2242                                                 help_search_engine_entry);
2243 }
2244
2245 /* image tab */
2246 static void config_tab_image(GtkWidget *notebook)
2247 {
2248         GtkWidget *hbox;
2249         GtkWidget *vbox;
2250         GtkWidget *group;
2251         GtkWidget *ct_button;
2252         GtkWidget *enlargement_button;
2253         GtkWidget *table;
2254         GtkWidget *spin;
2255 //~ #ifdef HAVE_CLUTTER
2256         //~ GtkWidget *gpu_accel;
2257 //~ #endif
2258
2259         vbox = scrolled_notebook_page(notebook, _("Image"));
2260
2261         group = pref_group_new(vbox, FALSE, _("Zoom"), GTK_ORIENTATION_VERTICAL);
2262
2263         table = pref_table_new(group, 2, 1, FALSE, FALSE);
2264         add_quality_menu(table, 0, 0, _("Quality:"), options->image.zoom_quality, &c_options->image.zoom_quality);
2265         //~ if (options->image.use_clutter_renderer && !options->disable_gpu)
2266                 //~ {
2267                 //~ gtk_widget_set_sensitive(table, FALSE);
2268                 //~ }
2269
2270 //~ #ifdef HAVE_CLUTTER
2271         //~ gpu_accel = pref_checkbox_new_int(group, _("Use GPU acceleration via Clutter library (Requires restart)"),
2272                               //~ options->image.use_clutter_renderer, &c_options->image.use_clutter_renderer);
2273         //~ if (options->disable_gpu && !options->override_disable_gpu)
2274                 //~ {
2275                 //~ gtk_widget_set_sensitive(gpu_accel, FALSE);
2276                 //~ }
2277 //~ #endif
2278
2279         pref_checkbox_new_int(group, _("Two pass rendering (apply HQ zoom and color correction in second pass)"),
2280                               options->image.zoom_2pass, &c_options->image.zoom_2pass);
2281         //~ if (options->image.use_clutter_renderer && !options->disable_gpu)
2282                 //~ {
2283                 //~ gtk_widget_set_sensitive(two_pass, FALSE);
2284                 //~ }
2285
2286         c_options->image.zoom_increment = options->image.zoom_increment;
2287         spin = pref_spin_new(group, _("Zoom increment:"), nullptr,
2288                              0.01, 4.0, 0.01, 2, static_cast<gdouble>(options->image.zoom_increment) / 100.0,
2289                              G_CALLBACK(zoom_increment_cb), nullptr);
2290         gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2291
2292         c_options->image.zoom_style = options->image.zoom_style;
2293         table = pref_table_new(group, 2, 1, FALSE, FALSE);
2294         add_zoom_style_selection_menu(table, 0, 0, _("Zoom style:"), options->image.zoom_style, &c_options->image.zoom_style);
2295
2296         group = pref_group_new(vbox, FALSE, _("Fit image to window"), GTK_ORIENTATION_VERTICAL);
2297
2298         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2299         enlargement_button = pref_checkbox_new_int(hbox, _("Allow enlargement of image (max. size in %)"),
2300                               options->image.zoom_to_fit_allow_expand, &c_options->image.zoom_to_fit_allow_expand);
2301         spin = pref_spin_new_int(hbox, nullptr, nullptr,
2302                                  100, 999, 1,
2303                                  options->image.max_enlargement_size, &c_options->image.max_enlargement_size);
2304         pref_checkbox_link_sensitivity(enlargement_button, spin);
2305         gtk_widget_set_tooltip_text(GTK_WIDGET(hbox), _("Enable this to allow Geeqie to increase the image size for images that are smaller than the current view area when the zoom is set to \"Fit image to window\". This value sets the maximum expansion permitted in percent i.e. 100% is full-size."));
2306
2307         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2308         ct_button = pref_checkbox_new_int(hbox, _("Virtual window size (% of actual window):"),
2309                                           options->image.limit_autofit_size, &c_options->image.limit_autofit_size);
2310         spin = pref_spin_new_int(hbox, nullptr, nullptr,
2311                                  10, 150, 1,
2312                                  options->image.max_autofit_size, &c_options->image.max_autofit_size);
2313         pref_checkbox_link_sensitivity(ct_button, spin);
2314         gtk_widget_set_tooltip_text(GTK_WIDGET(hbox), _("This value will set the virtual size of the window when \"Fit image to window\" is set. Instead of using the actual size of the window, the specified percentage of the window will be used. It allows one to keep a border around the image (values lower than 100%) or to auto zoom the image (values greater than 100%). It affects fullscreen mode too."));
2315
2316         group = pref_group_new(vbox, FALSE, _("Tile size"), GTK_ORIENTATION_VERTICAL);
2317         //~ gtk_widget_set_sensitive(group, !options->image.use_clutter_renderer);
2318
2319         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2320         spin = pref_spin_new_int(hbox, _("Pixels"), _("(Requires restart)"),
2321                                  128, 4096, 128,
2322                                  options->image.tile_size, &c_options->image.tile_size);
2323         gtk_widget_set_tooltip_text(GTK_WIDGET(hbox), _("This value changes the size of the tiles large images are split into. Increasing the size of the tiles will reduce the tiling effect seen on image changes, but will also slightly increase the delay before the first part of a large image is seen."));
2324
2325         group = pref_group_new(vbox, FALSE, _("Appearance"), GTK_ORIENTATION_VERTICAL);
2326
2327         pref_checkbox_new_int(group, _("Use custom border color in window mode"),
2328                               options->image.use_custom_border_color, &c_options->image.use_custom_border_color);
2329
2330         pref_checkbox_new_int(group, _("Use custom border color in fullscreen mode"),
2331                               options->image.use_custom_border_color_in_fullscreen, &c_options->image.use_custom_border_color_in_fullscreen);
2332
2333         pref_color_button_new(group, _("Border color"), &options->image.border_color,
2334                               G_CALLBACK(pref_color_button_set_cb), &c_options->image.border_color);
2335
2336         c_options->image.border_color = options->image.border_color;
2337
2338         pref_color_button_new(group, _("Alpha channel color 1"), &options->image.alpha_color_1,
2339                               G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_1);
2340
2341         pref_color_button_new(group, _("Alpha channel color 2"), &options->image.alpha_color_2,
2342                               G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_2);
2343
2344         c_options->image.alpha_color_1 = options->image.alpha_color_1;
2345         c_options->image.alpha_color_2 = options->image.alpha_color_2;
2346 }
2347
2348 /* windows tab */
2349
2350 static void save_default_window_layout_cb(GtkWidget *UNUSED(widget), gpointer UNUSED(data))
2351 {
2352         LayoutWindow *lw = nullptr;
2353         gchar *default_path;
2354         gchar *tmp_id;
2355
2356         /* Get current lw */
2357         layout_valid(&lw);
2358
2359         tmp_id = lw->options.id;
2360         lw->options.id = g_strdup("lw_default");
2361
2362         default_path = g_build_filename(get_rc_dir(), DEFAULT_WINDOW_LAYOUT, NULL);
2363         save_default_layout_options_to_file(default_path, options, lw);
2364         g_free(lw->options.id);
2365         lw->options.id = tmp_id;
2366         g_free(default_path);
2367 }
2368
2369 static gboolean popover_cb(gpointer data)
2370 {
2371         auto popover = static_cast<GtkPopover *>(data);
2372
2373         gtk_popover_popdown(popover);
2374
2375         return FALSE;
2376 }
2377
2378 static void default_layout_changed_cb(GtkWidget *UNUSED(button), GtkPopover *popover)
2379 {
2380         gtk_popover_popup(popover);
2381
2382         g_timeout_add(2000, popover_cb, popover);
2383 }
2384
2385 static GtkWidget *create_popover(GtkWidget *parent, GtkWidget *child, GtkPositionType pos)
2386 {
2387         GtkWidget *popover;
2388
2389         popover = gtk_popover_new(parent);
2390         gtk_popover_set_position(GTK_POPOVER (popover), pos);
2391         gtk_container_add (GTK_CONTAINER(popover), child);
2392         gtk_container_set_border_width(GTK_CONTAINER (popover), 6);
2393         gtk_widget_show (child);
2394
2395         return popover;
2396 }
2397
2398 static void config_tab_windows(GtkWidget *notebook)
2399 {
2400         GtkWidget *hbox;
2401         GtkWidget *vbox;
2402         GtkWidget *group;
2403         GtkWidget *subgroup;
2404         GtkWidget *button;
2405         GtkWidget *ct_button;
2406         GtkWidget *spin;
2407
2408         vbox = scrolled_notebook_page(notebook, _("Windows"));
2409
2410         group = pref_group_new(vbox, FALSE, _("State"), GTK_ORIENTATION_VERTICAL);
2411
2412         ct_button = pref_checkbox_new_int(group, _("Remember session"),
2413                                           options->save_window_positions, &c_options->save_window_positions);
2414
2415         button = pref_checkbox_new_int(group, _("Use saved window positions also for new windows"),
2416                                        options->use_saved_window_positions_for_new_windows, &c_options->use_saved_window_positions_for_new_windows);
2417         pref_checkbox_link_sensitivity(ct_button, button);
2418
2419         button = pref_checkbox_new_int(group, _("Remember window workspace"),
2420                               options->save_window_workspace, &c_options->save_window_workspace);
2421         pref_checkbox_link_sensitivity(ct_button, button);
2422
2423         pref_checkbox_new_int(group, _("Remember tool state (float/hidden)"),
2424                               options->tools_restore_state, &c_options->tools_restore_state);
2425
2426         pref_checkbox_new_int(group, _("Remember dialog window positions"),
2427                               options->save_dialog_window_positions, &c_options->save_dialog_window_positions);
2428
2429         pref_checkbox_new_int(group, _("Show window IDs"),
2430                               options->show_window_ids, &c_options->show_window_ids);
2431
2432         subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2433         pref_label_new(subgroup, _("Use current layout for default: "));
2434         button = pref_button_new(subgroup, nullptr, _("Set"), FALSE, G_CALLBACK(save_default_window_layout_cb), nullptr);
2435
2436         GtkWidget *popover;
2437
2438         popover = create_popover(button, gtk_label_new(_("Current window layout\nhas been set as default")), GTK_POS_TOP);
2439         gtk_popover_set_modal(GTK_POPOVER (popover), FALSE);
2440         g_signal_connect(button, "clicked", G_CALLBACK(default_layout_changed_cb), popover);
2441
2442         group = pref_group_new(vbox, FALSE, _("Size"), GTK_ORIENTATION_VERTICAL);
2443
2444         pref_checkbox_new_int(group, _("Fit window to image when tools are hidden/floating"),
2445                               options->image.fit_window_to_image, &c_options->image.fit_window_to_image);
2446
2447         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2448         ct_button = pref_checkbox_new_int(hbox, _("Limit size when auto-sizing window (%):"),
2449                                           options->image.limit_window_size, &c_options->image.limit_window_size);
2450         spin = pref_spin_new_int(hbox, nullptr, nullptr,
2451                                  10, 150, 1,
2452                                  options->image.max_window_size, &c_options->image.max_window_size);
2453         pref_checkbox_link_sensitivity(ct_button, spin);
2454
2455         group = pref_group_new(vbox, FALSE, _("Full screen"), GTK_ORIENTATION_VERTICAL);
2456
2457         c_options->fullscreen.screen = options->fullscreen.screen;
2458         c_options->fullscreen.above = options->fullscreen.above;
2459         hbox = fullscreen_prefs_selection_new(_("Location:"), &c_options->fullscreen.screen, &c_options->fullscreen.above);
2460         gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2461         gtk_widget_show(hbox);
2462
2463         pref_checkbox_new_int(group, _("Smooth image flip"),
2464                               options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
2465         pref_checkbox_new_int(group, _("Disable screen saver"),
2466                               options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
2467 }
2468
2469 #define PRE_FORMATTED_COLUMNS 5
2470 static void config_tab_osd(GtkWidget *notebook)
2471 {
2472         GtkWidget *hbox;
2473         GtkWidget *vbox;
2474         GtkWidget *group;
2475         GtkWidget *button;
2476         GtkWidget *image_overlay_template_view;
2477         GtkWidget *scrolled;
2478         GtkWidget *scrolled_pre_formatted;
2479         GtkTextBuffer *buffer;
2480         GtkWidget *label;
2481         GtkWidget *subgroup;
2482
2483         vbox = scrolled_notebook_page(notebook, _("OSD"));
2484
2485         image_overlay_template_view = gtk_text_view_new();
2486
2487         group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
2488
2489         subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2490
2491         scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
2492         gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
2493         gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
2494         gtk_widget_show(scrolled_pre_formatted);
2495         gtk_widget_show(subgroup);
2496
2497         pref_line(group, PREF_PAD_GAP);
2498
2499         pref_label_new(group, _("Image overlay template"));
2500
2501         scrolled = gtk_scrolled_window_new(nullptr, nullptr);
2502         gtk_widget_set_size_request(scrolled, 200, 150);
2503         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2504         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
2505                                                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2506         gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
2507         gtk_widget_show(scrolled);
2508
2509         gtk_widget_set_tooltip_markup(image_overlay_template_view,
2510                                         _("Extensive formatting options are shown in the Help file"));
2511
2512         gtk_container_add(GTK_CONTAINER(scrolled), image_overlay_template_view);
2513         gtk_widget_show(image_overlay_template_view);
2514
2515         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2516
2517         button = pref_button_new(nullptr, GTK_STOCK_SELECT_FONT, _("Font"), FALSE,
2518                                  G_CALLBACK(image_overlay_set_font_cb), notebook);
2519
2520         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2521         gtk_widget_show(button);
2522
2523         button = pref_button_new(nullptr, GTK_STOCK_COLOR_PICKER, _("Text"), FALSE,
2524                                  G_CALLBACK(image_overlay_set_text_colour_cb), nullptr);
2525         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2526         gtk_widget_show(button);
2527
2528         button = pref_button_new(nullptr, GTK_STOCK_COLOR_PICKER, _("Background"), FALSE,
2529                                  G_CALLBACK(image_overlay_set_background_colour_cb), nullptr);
2530         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2531         gtk_widget_show(button);
2532         image_overlay_set_text_colours();
2533
2534         button = pref_button_new(nullptr, nullptr, _("Defaults"), FALSE,
2535                                  G_CALLBACK(image_overlay_default_template_cb), image_overlay_template_view);
2536         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2537         gtk_widget_show(button);
2538
2539         button = pref_button_new(nullptr, GTK_STOCK_HELP, nullptr, FALSE,
2540                                  G_CALLBACK(image_overlay_help_cb), nullptr);
2541         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2542         gtk_widget_show(button);
2543
2544         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_overlay_template_view));
2545         if (options->image_overlay.template_string) gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
2546         g_signal_connect(G_OBJECT(buffer), "changed",
2547                          G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
2548
2549         pref_line(group, PREF_PAD_GAP);
2550
2551         group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL);
2552         hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2553         gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2554         gtk_widget_show(hbox);
2555         label = gtk_label_new(_("%Exif.Image.Orientation%"));
2556         gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2557         gtk_widget_show(label);
2558         pref_spacer(group,TRUE);
2559
2560         group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL);
2561         hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2562         gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2563         gtk_widget_show(hbox);
2564         label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
2565         gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2566         gtk_widget_show(label);
2567         pref_spacer(group,TRUE);
2568
2569         group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL);
2570         hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2571         gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2572         gtk_widget_show(hbox);
2573         label = gtk_label_new(_("%path:39%"));
2574         gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2575         gtk_widget_show(label);
2576         pref_spacer(group,TRUE);
2577
2578         group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL);
2579         hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2580         gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2581         gtk_widget_show(hbox);
2582         label = gtk_label_new(_("Text shown only if the field is non-null:\n%formatted.Aperture:F no. * setting%\n %formatted.Aperture:10:F no. * setting%"));
2583         gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2584         gtk_widget_show(label);
2585         pref_spacer(group,TRUE);
2586
2587         group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL);
2588         hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2589         gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2590         gtk_widget_show(hbox);
2591         label = gtk_label_new(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
2592         gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2593         gtk_widget_show(label);
2594 }
2595
2596 static GtkTreeModel *create_class_model()
2597 {
2598         GtkListStore *model;
2599         GtkTreeIter iter;
2600         gint i;
2601
2602         /* create list store */
2603         model = gtk_list_store_new(1, G_TYPE_STRING);
2604         for (i = 0; i < FILE_FORMAT_CLASSES; i++)
2605                 {
2606                 gtk_list_store_append(model, &iter);
2607                 gtk_list_store_set(model, &iter, 0, _(format_class_list[i]), -1);
2608                 }
2609         return GTK_TREE_MODEL (model);
2610 }
2611
2612
2613 /* filtering tab */
2614 static gint filter_table_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
2615 {
2616         gint n = GPOINTER_TO_INT(data);
2617         gint ret = 0;
2618         FilterEntry *filter_a;
2619         FilterEntry *filter_b;
2620
2621         gtk_tree_model_get(model, a, 0, &filter_a, -1);
2622         gtk_tree_model_get(model, b, 0, &filter_b, -1);
2623
2624         switch (n)
2625                 {
2626                 case FILETYPES_COLUMN_ENABLED:
2627                         {
2628                         ret = filter_a->enabled - filter_b->enabled;
2629                         break;
2630                         }
2631                 case FILETYPES_COLUMN_FILTER:
2632                         {
2633                         ret = g_utf8_collate(filter_a->extensions, filter_b->extensions);
2634                         break;
2635                         }
2636                 case FILETYPES_COLUMN_DESCRIPTION:
2637                         {
2638                         ret = g_utf8_collate(filter_a->description, filter_b->description);
2639                         break;
2640                         }
2641                 case FILETYPES_COLUMN_CLASS:
2642                         {
2643                         ret = g_strcmp0(format_class_list[filter_a->file_class], format_class_list[filter_b->file_class]);
2644                         break;
2645                         }
2646                 case FILETYPES_COLUMN_WRITABLE:
2647                         {
2648                         ret = filter_a->writable - filter_b->writable;
2649                         break;
2650                         }
2651                 case FILETYPES_COLUMN_SIDECAR:
2652                         {
2653                         ret = filter_a->allow_sidecar - filter_b->allow_sidecar;
2654                         break;
2655                         }
2656                 default:
2657                         g_return_val_if_reached(0);
2658                 }
2659
2660         return ret;
2661 }
2662
2663 static gboolean search_function_cb(GtkTreeModel *model, gint UNUSED(column), const gchar *key, GtkTreeIter *iter, gpointer UNUSED(search_data))
2664 {
2665         FilterEntry *fe;
2666         gboolean ret = TRUE;
2667
2668         gtk_tree_model_get(model, iter, 0, &fe, -1);
2669
2670         if (g_strstr_len(fe->extensions, -1, key))
2671                 {
2672                 ret = FALSE;
2673                 }
2674
2675         return ret;
2676 }
2677
2678 static void config_tab_files(GtkWidget *notebook)
2679 {
2680         GtkWidget *hbox;
2681         GtkWidget *frame;
2682         GtkWidget *vbox;
2683         GtkWidget *group;
2684         GtkWidget *button;
2685         GtkWidget *ct_button;
2686         GtkWidget *scrolled;
2687         GtkWidget *filter_view;
2688         GtkCellRenderer *renderer;
2689         GtkTreeSelection *selection;
2690         GtkTreeViewColumn *column;
2691
2692         vbox = scrolled_notebook_page(notebook, _("File Filters"));
2693
2694         group = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2695
2696         pref_checkbox_new_int(group, _("Show hidden files or folders"),
2697                               options->file_filter.show_hidden_files, &c_options->file_filter.show_hidden_files);
2698         pref_checkbox_new_int(group, _("Show parent folder (..)"),
2699                               options->file_filter.show_parent_directory, &c_options->file_filter.show_parent_directory);
2700         pref_checkbox_new_int(group, _("Case sensitive sort"),
2701                               options->file_sort.case_sensitive, &c_options->file_sort.case_sensitive);
2702         pref_checkbox_new_int(group, _("Natural sort order - Requires restart"),
2703                                           options->file_sort.natural, &c_options->file_sort.natural);
2704         pref_checkbox_new_int(group, _("Disable file extension checks"),
2705                               options->file_filter.disable_file_extension_checks, &c_options->file_filter.disable_file_extension_checks);
2706
2707         ct_button = pref_checkbox_new_int(group, _("Disable File Filtering"),
2708                                           options->file_filter.disable, &c_options->file_filter.disable);
2709
2710
2711         group = pref_group_new(vbox, FALSE, _("Grouping sidecar extensions"), GTK_ORIENTATION_VERTICAL);
2712
2713         sidecar_ext_entry = gtk_entry_new();
2714         gtk_entry_set_text(GTK_ENTRY(sidecar_ext_entry), options->sidecar.ext);
2715         gtk_box_pack_start(GTK_BOX(group), sidecar_ext_entry, FALSE, FALSE, 0);
2716         gtk_widget_show(sidecar_ext_entry);
2717
2718         group = pref_group_new(vbox, TRUE, _("File types"), GTK_ORIENTATION_VERTICAL);
2719
2720         frame = pref_group_parent(group);
2721         g_signal_connect(G_OBJECT(ct_button), "toggled",
2722                          G_CALLBACK(filter_disable_cb), frame);
2723         gtk_widget_set_sensitive(frame, !options->file_filter.disable);
2724
2725         scrolled = gtk_scrolled_window_new(nullptr, nullptr);
2726         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2727         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2728         gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2729         gtk_widget_show(scrolled);
2730
2731         filter_store = gtk_list_store_new(1, G_TYPE_POINTER);
2732         filter_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
2733         g_object_unref(filter_store);
2734         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
2735         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2736
2737         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), FALSE);
2738
2739         column = gtk_tree_view_column_new();
2740         gtk_tree_view_column_set_title(column, _("Enabled"));
2741         gtk_tree_view_column_set_resizable(column, TRUE);
2742
2743         renderer = gtk_cell_renderer_toggle_new();
2744         g_signal_connect(G_OBJECT(renderer), "toggled",
2745                          G_CALLBACK(filter_store_enable_cb), filter_store);
2746         gtk_tree_view_column_pack_start(column, renderer, FALSE);
2747         gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2748                                                 GINT_TO_POINTER(FE_ENABLE), nullptr);
2749         gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_ENABLED, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_ENABLED), nullptr);
2750         gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_ENABLED);
2751         gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2752
2753         column = gtk_tree_view_column_new();
2754         gtk_tree_view_column_set_title(column, _("Filter"));
2755         gtk_tree_view_column_set_resizable(column, TRUE);
2756         gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_FILTER, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_FILTER), nullptr);
2757         gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_FILTER);
2758
2759         renderer = gtk_cell_renderer_text_new();
2760         g_signal_connect(G_OBJECT(renderer), "edited",
2761                          G_CALLBACK(filter_store_ext_edit_cb), filter_store);
2762         gtk_tree_view_column_pack_start(column, renderer, TRUE);
2763         g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2764         gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2765                                                 GINT_TO_POINTER(FE_EXTENSION), nullptr);
2766         gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2767
2768         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), TRUE);
2769         gtk_tree_view_set_search_column(GTK_TREE_VIEW(filter_view), FILETYPES_COLUMN_FILTER);
2770         gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(filter_view), search_function_cb, nullptr, nullptr);
2771
2772         column = gtk_tree_view_column_new();
2773         gtk_tree_view_column_set_title(column, _("Description"));
2774         gtk_tree_view_column_set_resizable(column, TRUE);
2775         gtk_tree_view_column_set_fixed_width(column, 200);
2776         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2777
2778         renderer = gtk_cell_renderer_text_new();
2779         g_signal_connect(G_OBJECT(renderer), "edited",
2780                          G_CALLBACK(filter_store_desc_edit_cb), filter_store);
2781         g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2782         gtk_tree_view_column_pack_start(column, renderer, FALSE);
2783         gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2784                                                 GINT_TO_POINTER(FE_DESCRIPTION), nullptr);
2785         gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2786         gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_DESCRIPTION, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_DESCRIPTION), nullptr);
2787         gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_DESCRIPTION);
2788
2789         column = gtk_tree_view_column_new();
2790         gtk_tree_view_column_set_title(column, _("Class"));
2791         gtk_tree_view_column_set_resizable(column, TRUE);
2792         renderer = gtk_cell_renderer_combo_new();
2793         g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE,
2794                                          "model", create_class_model(),
2795                                          "text-column", 0,
2796                                          "has-entry", FALSE,
2797                                          NULL);
2798
2799         g_signal_connect(G_OBJECT(renderer), "edited",
2800                          G_CALLBACK(filter_store_class_edit_cb), filter_store);
2801         gtk_tree_view_column_pack_start(column, renderer, TRUE);
2802         gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2803                                                 GINT_TO_POINTER(FE_CLASS), nullptr);
2804         gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2805         gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_CLASS, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_CLASS), nullptr);
2806         gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_CLASS);
2807
2808         column = gtk_tree_view_column_new();
2809         gtk_tree_view_column_set_title(column, _("Writable"));
2810         gtk_tree_view_column_set_resizable(column, FALSE);
2811         renderer = gtk_cell_renderer_toggle_new();
2812         g_signal_connect(G_OBJECT(renderer), "toggled",
2813                          G_CALLBACK(filter_store_writable_cb), filter_store);
2814         gtk_tree_view_column_pack_start(column, renderer, FALSE);
2815         gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2816                                                 GINT_TO_POINTER(FE_WRITABLE), nullptr);
2817         gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2818         gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_WRITABLE, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_WRITABLE), nullptr);
2819         gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_WRITABLE);
2820
2821         column = gtk_tree_view_column_new();
2822         gtk_tree_view_column_set_title(column, _("Sidecar is allowed"));
2823         gtk_tree_view_column_set_resizable(column, FALSE);
2824         renderer = gtk_cell_renderer_toggle_new();
2825         g_signal_connect(G_OBJECT(renderer), "toggled",
2826                          G_CALLBACK(filter_store_sidecar_cb), filter_store);
2827         gtk_tree_view_column_pack_start(column, renderer, FALSE);
2828         gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2829                                                 GINT_TO_POINTER(FE_ALLOW_SIDECAR), nullptr);
2830         gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2831         gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_SIDECAR, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_SIDECAR), nullptr);
2832         gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_SIDECAR);
2833
2834         filter_store_populate();
2835         gtk_container_add(GTK_CONTAINER(scrolled), filter_view);
2836         gtk_widget_show(filter_view);
2837
2838         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2839
2840         button = pref_button_new(nullptr, nullptr, _("Defaults"), FALSE,
2841                                  G_CALLBACK(filter_default_cb), filter_view);
2842         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2843         gtk_widget_show(button);
2844
2845         button = pref_button_new(nullptr, GTK_STOCK_REMOVE, nullptr, FALSE,
2846                                  G_CALLBACK(filter_remove_cb), filter_view);
2847         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2848         gtk_widget_show(button);
2849
2850         button = pref_button_new(nullptr, GTK_STOCK_ADD, nullptr, FALSE,
2851                                  G_CALLBACK(filter_add_cb), filter_view);
2852         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2853         gtk_widget_show(button);
2854 }
2855
2856 /* metadata tab */
2857 static void config_tab_metadata(GtkWidget *notebook)
2858 {
2859         GtkWidget *vbox;
2860         GtkWidget *hbox;
2861         GtkWidget *group;
2862         GtkWidget *ct_button;
2863         GtkWidget *label;
2864         GtkWidget *tmp_widget;
2865         char *markup;
2866         GtkWidget *text_label;
2867
2868         vbox = scrolled_notebook_page(notebook, _("Metadata"));
2869
2870
2871         group = pref_group_new(vbox, FALSE, _("Metadata writing sequence"), GTK_ORIENTATION_VERTICAL);
2872 #ifndef HAVE_EXIV2
2873         label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2874 #endif
2875         label = pref_label_new(group, _("When writing metadata, Geeqie will follow these steps, if selected. This process will stop when the first successful write occurs."));
2876         gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2877         gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2878
2879         gtk_widget_set_tooltip_text(label, _("A flowchart of the sequence is shown in the Help file"));
2880
2881         ct_button = pref_checkbox_new_int(group, "", options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2882         text_label = gtk_bin_get_child(GTK_BIN(ct_button));
2883         markup = g_markup_printf_escaped ("<span weight=\"bold\">%s</span>%s", _("Step 1"), _(") Save metadata in either the image file or the sidecar file, according to the XMP standard"));
2884         gtk_label_set_markup (GTK_LABEL(text_label), markup);
2885         g_free(markup);
2886         markup = g_markup_printf_escaped ("%s<span style=\"italic\">%s</span>%s<span style=\"italic\">%s</span>%s", _("The destination is dependent on the settings in the "), _("Writable"), _(" and "), _("Sidecar Is Allowed"), _(" columns of the File Filters tab)"));
2887         gtk_widget_set_tooltip_markup(ct_button, markup);
2888         g_free(markup);
2889
2890 #ifndef HAVE_EXIV2
2891         gtk_widget_set_sensitive(ct_button, FALSE);
2892 #endif
2893
2894         tmp_widget = pref_checkbox_new_int(group, "", options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2895         text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2896         markup = g_markup_printf_escaped ("<span weight=\"bold\">%s</span>%s<span style=\"italic\">%s</span>%s", _("Step 2"), _(") Save metadata in the folder "),".metadata,", _(" local to the image folder (non-standard)"));
2897         gtk_label_set_markup (GTK_LABEL(text_label), markup);
2898         g_free(markup);
2899
2900         label = pref_label_new(group, "");
2901         markup = g_markup_printf_escaped ("<span weight=\"bold\">%s</span>%s<span style=\"italic\">%s</span>%s", _("Step 3"), _(") Save metadata in Geeqie private directory "), get_metadata_cache_dir(), "/");
2902         gtk_label_set_markup (GTK_LABEL(label), markup);
2903         g_free(markup);
2904
2905         gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2906         gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2907         gtk_misc_set_padding(GTK_MISC(label), 22, 0);
2908         pref_spacer(group, PREF_PAD_GROUP);
2909
2910         group = pref_group_new(vbox, FALSE, _("Step 1 Options:"), GTK_ORIENTATION_VERTICAL);
2911 #ifndef HAVE_EXIV2
2912         gtk_widget_set_sensitive(group, FALSE);
2913 #endif
2914
2915         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2916         pref_checkbox_link_sensitivity(ct_button, hbox);
2917
2918         tmp_widget=     pref_checkbox_new_int(hbox, _("Store metadata also in IPTC tags (converted according to the IPTC4XMP standard)"), options->metadata.save_legacy_IPTC, &c_options->metadata.save_legacy_IPTC);
2919         gtk_widget_set_tooltip_text(tmp_widget, _("A simplified conversion list is in the Help file"));
2920
2921         pref_checkbox_new_int(hbox, _("Warn if the image or sidecar file is not writable"), options->metadata.warn_on_write_problems, &c_options->metadata.warn_on_write_problems);
2922
2923         pref_checkbox_new_int(hbox, _("Ask before writing to image files"), options->metadata.confirm_write, &c_options->metadata.confirm_write);
2924
2925         tmp_widget=     pref_checkbox_new_int(hbox, "", options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2926         gtk_widget_set_tooltip_text(tmp_widget, _("This file naming convention is used by Darktable"));
2927         text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2928         markup = g_markup_printf_escaped ("%s<span style=\"italic\">%s</span>%s<span style=\"italic\">%s</span>%s", _("Create sidecar files named "), "image.ext.xmp", _(" (as opposed to the normal "), "image.xmp", ")");
2929         gtk_label_set_markup (GTK_LABEL(text_label), markup);
2930         g_free(markup);
2931
2932         pref_spacer(group, PREF_PAD_GROUP);
2933
2934         group = pref_group_new(vbox, FALSE, _("Steps 2 and 3 Option:"), GTK_ORIENTATION_VERTICAL);
2935 #ifndef HAVE_EXIV2
2936         gtk_widget_set_sensitive(group, FALSE);
2937 #endif
2938
2939         pref_checkbox_new_int(group, _("Use GQview legacy metadata format instead of XMP (supports only Keywords and Comments)"), options->metadata.save_legacy_format, &c_options->metadata.save_legacy_format);
2940
2941         pref_spacer(group, PREF_PAD_GROUP);
2942
2943         group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2944         tmp_widget = pref_checkbox_new_int(group, _("Write the same description tags to all grouped sidecars"), options->metadata.sync_grouped_files, &c_options->metadata.sync_grouped_files);
2945         gtk_widget_set_tooltip_text(tmp_widget, _("See the Help file for a list of the tags used"));
2946
2947         tmp_widget = pref_checkbox_new_int(group, _("Permit Keywords to be case-sensitive"), options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2948         gtk_widget_set_tooltip_text(tmp_widget, _("When selected, \"Place\" and \"place\" are two different keywords"));
2949
2950         ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"), options->metadata.write_orientation, &c_options->metadata.write_orientation);
2951         gtk_widget_set_tooltip_text(ct_button, _("If checked, the results of orientation commands (Rotate, Mirror and Flip) issued on an image will be written to metadata\nNote: If this option is not checked, the results of orientation commands will be lost when Geeqie closes"));
2952
2953 #ifndef HAVE_EXIV2
2954         gtk_widget_set_sensitive(ct_button, FALSE);
2955 #endif
2956
2957         pref_spacer(group, PREF_PAD_GROUP);
2958
2959         group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2960
2961         ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"), options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
2962
2963         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2964         pref_checkbox_link_sensitivity(ct_button, hbox);
2965
2966         pref_spin_new_int(hbox, _("Timeout (seconds):"), nullptr, 0, 900, 1, options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
2967
2968         pref_checkbox_new_int(group, _("Write metadata on image change"), options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
2969
2970         pref_checkbox_new_int(group, _("Write metadata on directory change"), options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
2971
2972         pref_spacer(group, PREF_PAD_GROUP);
2973
2974 #ifdef HAVE_SPELL
2975         group = pref_group_new(vbox, FALSE, _("Spelling checks"), GTK_ORIENTATION_VERTICAL);
2976
2977         ct_button = pref_checkbox_new_int(group, _("Check spelling - Requires restart"), options->metadata.check_spelling, &c_options->metadata.check_spelling);
2978         gtk_widget_set_tooltip_text(ct_button, _("Spelling checks are performed on info sidebar panes Comment, Headline and Title"));
2979 #endif
2980
2981         pref_spacer(group, PREF_PAD_GROUP);
2982
2983         group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
2984
2985         ct_button = pref_checkbox_new_int(group, _("Read metadata in background"), options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
2986         gtk_widget_set_tooltip_text(ct_button,"On folder change, read DateTimeOriginal, DateTimeDigitized and Star Rating in the idle loop.\nIf this is not selected, initial loading of the folder will be faster but sorting on these items will be slower");
2987 }
2988
2989 /* keywords tab */
2990
2991 struct KeywordFindData
2992 {
2993         GenericDialog *gd;
2994
2995         GList *list;
2996         GList *list_dir;
2997
2998         GtkWidget *button_close;
2999         GtkWidget *button_stop;
3000         GtkWidget *button_start;
3001         GtkWidget *progress;
3002         GtkWidget *spinner;
3003
3004         GtkWidget *group;
3005         GtkWidget *entry;
3006
3007         gboolean recurse;
3008
3009         guint idle_id; /* event source id */
3010 };
3011
3012 #define KEYWORD_DIALOG_WIDTH 400
3013
3014 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
3015 {
3016         GList *list_d = nullptr;
3017         GList *list_f = nullptr;
3018
3019         if (kfd->recurse)
3020                 {
3021                 filelist_read(dir_fd, &list_f, &list_d);
3022                 }
3023         else
3024                 {
3025                 filelist_read(dir_fd, &list_f, nullptr);
3026                 }
3027
3028         list_f = filelist_filter(list_f, FALSE);
3029         list_d = filelist_filter(list_d, TRUE);
3030
3031         kfd->list = g_list_concat(list_f, kfd->list);
3032         kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
3033 }
3034
3035 static void keywords_find_reset(KeywordFindData *kfd)
3036 {
3037         filelist_free(kfd->list);
3038         kfd->list = nullptr;
3039
3040         filelist_free(kfd->list_dir);
3041         kfd->list_dir = nullptr;
3042 }
3043
3044 static void keywords_find_close_cb(GenericDialog *UNUSED(fd), gpointer data)
3045 {
3046         auto kfd = static_cast<KeywordFindData *>(data);
3047
3048         if (!gtk_widget_get_sensitive(kfd->button_close)) return;
3049
3050         keywords_find_reset(kfd);
3051         generic_dialog_close(kfd->gd);
3052         g_free(kfd);
3053 }
3054
3055 static void keywords_find_finish(KeywordFindData *kfd)
3056 {
3057         keywords_find_reset(kfd);
3058
3059         gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
3060         spinner_set_interval(kfd->spinner, -1);
3061
3062         gtk_widget_set_sensitive(kfd->group, TRUE);
3063         gtk_widget_set_sensitive(kfd->button_start, TRUE);
3064         gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3065         gtk_widget_set_sensitive(kfd->button_close, TRUE);
3066 }
3067
3068 static void keywords_find_stop_cb(GenericDialog *UNUSED(fd), gpointer data)
3069 {
3070         auto kfd = static_cast<KeywordFindData *>(data);
3071
3072         g_idle_remove_by_data(kfd);
3073
3074         keywords_find_finish(kfd);
3075 }
3076
3077 static gboolean keywords_find_file(gpointer data)
3078 {
3079         auto kfd = static_cast<KeywordFindData *>(data);
3080         GtkTextIter iter;
3081         GtkTextBuffer *buffer;
3082         gchar *tmp;
3083         GList *keywords;
3084
3085         if (kfd->list)
3086                 {
3087                 FileData *fd;
3088
3089                 fd = static_cast<FileData *>(kfd->list->data);
3090                 kfd->list = g_list_remove(kfd->list, fd);
3091
3092                 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
3093                 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3094
3095                 while (keywords)
3096                         {
3097                         gtk_text_buffer_get_end_iter(buffer, &iter);
3098                         tmp = g_strconcat(static_cast<const gchar *>(keywords->data), "\n", NULL);
3099                         gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3100                         g_free(tmp);
3101                         keywords = keywords->next;
3102                         }
3103
3104                 gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
3105                 file_data_unref(fd);
3106                 string_list_free(keywords);
3107
3108                 return (G_SOURCE_CONTINUE);
3109                 }
3110         else if (kfd->list_dir)
3111                 {
3112                 FileData *fd;
3113
3114                 fd = static_cast<FileData *>(kfd->list_dir->data);
3115                 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
3116
3117                 keywords_find_folder(kfd, fd);
3118
3119                 file_data_unref(fd);
3120
3121                 return G_SOURCE_CONTINUE;
3122                 }
3123
3124         keywords_find_finish(kfd);
3125
3126         return G_SOURCE_REMOVE;
3127 }
3128
3129 static void keywords_find_start_cb(GenericDialog *UNUSED(fd), gpointer data)
3130 {
3131         auto kfd = static_cast<KeywordFindData *>(data);
3132         gchar *path;
3133
3134         if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
3135
3136         path = remove_trailing_slash((gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
3137         parse_out_relatives(path);
3138
3139         if (!isdir(path))
3140                 {
3141                 warning_dialog(_("Invalid folder"),
3142                                 _("The specified folder can not be found."),
3143                                 GTK_STOCK_DIALOG_WARNING, kfd->gd->dialog);
3144                 }
3145         else
3146                 {
3147                 FileData *dir_fd;
3148
3149                 gtk_widget_set_sensitive(kfd->group, FALSE);
3150                 gtk_widget_set_sensitive(kfd->button_start, FALSE);
3151                 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
3152                 gtk_widget_set_sensitive(kfd->button_close, FALSE);
3153                 spinner_set_interval(kfd->spinner, SPINNER_SPEED);
3154
3155                 dir_fd = file_data_new_dir(path);
3156                 keywords_find_folder(kfd, dir_fd);
3157                 file_data_unref(dir_fd);
3158                 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
3159                 }
3160
3161         g_free(path);
3162 }
3163
3164 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
3165 {
3166         KeywordFindData *kfd;
3167         GtkWidget *hbox;
3168         GtkWidget *label;
3169
3170         kfd = g_new0(KeywordFindData, 1);
3171
3172         kfd->gd = generic_dialog_new(_("Search for keywords"),
3173                                                                         "search_for_keywords",
3174                                                                         widget, FALSE,
3175                                                                         nullptr, kfd);
3176         gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
3177         kfd->gd->cancel_cb = keywords_find_close_cb;
3178         kfd->button_close = generic_dialog_add_button(kfd->gd, GTK_STOCK_CLOSE, nullptr,
3179                                                      keywords_find_close_cb, FALSE);
3180         kfd->button_start = generic_dialog_add_button(kfd->gd, GTK_STOCK_OK, _("S_tart"),
3181                                                      keywords_find_start_cb, FALSE);
3182         kfd->button_stop = generic_dialog_add_button(kfd->gd, GTK_STOCK_STOP, nullptr,
3183                                                     keywords_find_stop_cb, FALSE);
3184         gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3185
3186         generic_dialog_add_message(kfd->gd, nullptr, _("Search for keywords"), nullptr, FALSE);
3187
3188         hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3189         pref_spacer(hbox, PREF_PAD_INDENT);
3190         kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3191
3192         hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3193         pref_label_new(hbox, _("Folder:"));
3194
3195         label = tab_completion_new(&kfd->entry, path, nullptr, nullptr, nullptr, nullptr);
3196         tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
3197         gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3198         gtk_widget_show(label);
3199
3200         pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
3201
3202         pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
3203         hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3204
3205         kfd->progress = gtk_entry_new();
3206         gtk_widget_set_can_focus(kfd->progress, FALSE);
3207         gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
3208         gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
3209         gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
3210         gtk_widget_show(kfd->progress);
3211
3212         kfd->spinner = spinner_new(nullptr, -1);
3213         gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
3214         gtk_widget_show(kfd->spinner);
3215
3216         kfd->list = nullptr;
3217
3218         gtk_widget_show(kfd->gd->dialog);
3219 }
3220
3221 static void keywords_find_cb(GtkWidget *widget, gpointer UNUSED(data))
3222 {
3223         const gchar *path = layout_get_path(nullptr);
3224
3225         if (!path || !*path) path = homedir();
3226         keywords_find_dialog(widget, path);
3227 }
3228
3229 static void config_tab_keywords_save()
3230 {
3231         GtkTextIter start, end;
3232         GtkTextBuffer *buffer;
3233         GList *kw_list = nullptr;
3234         GList *work;
3235         gchar *buffer_text;
3236         gchar *kw_split;
3237         gboolean found;
3238
3239         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3240         gtk_text_buffer_get_bounds(buffer, &start, &end);
3241
3242         buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3243
3244         kw_split = strtok(buffer_text, "\n");
3245         while (kw_split != nullptr)
3246                 {
3247                 work = kw_list;
3248                 found = FALSE;
3249                 while (work)
3250                         {
3251                         if (g_strcmp0(static_cast<const gchar *>(work->data), kw_split) == 0)
3252                                 {
3253                                 found = TRUE;
3254                                 break;
3255                                 }
3256                         work = work->next;
3257                         }
3258                 if (!found)
3259                         {
3260                         kw_list = g_list_append(kw_list, g_strdup(kw_split));
3261                         }
3262                 kw_split = strtok(nullptr, "\n");
3263                 }
3264
3265         keyword_list_set(kw_list);
3266
3267         string_list_free(kw_list);
3268         g_free(buffer_text);
3269 }
3270
3271 static void config_tab_keywords(GtkWidget *notebook)
3272 {
3273         GtkWidget *hbox;
3274         GtkWidget *vbox;
3275         GtkWidget *group;
3276         GtkWidget *button;
3277         GtkWidget *scrolled;
3278         GtkTextIter iter;
3279         GtkTextBuffer *buffer;
3280         gchar *tmp;
3281 #ifdef HAVE_SPELL
3282         GspellTextView *gspell_view;
3283 #endif
3284
3285         vbox = scrolled_notebook_page(notebook, _("Keywords"));
3286
3287         group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
3288
3289         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3290
3291         button = pref_button_new(hbox, GTK_STOCK_EXECUTE, _("Search"), FALSE,
3292                                    G_CALLBACK(keywords_find_cb), keyword_text);
3293         gtk_widget_set_tooltip_text(button, "Search for existing keywords");
3294
3295
3296         keyword_text = gtk_text_view_new();
3297         gtk_widget_set_size_request(keyword_text, 20, 20);
3298         scrolled = gtk_scrolled_window_new(nullptr, nullptr);
3299         gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3300         gtk_widget_show(scrolled);
3301
3302 #ifdef HAVE_SPELL
3303         if (options->metadata.check_spelling)
3304                 {
3305                 gspell_view = gspell_text_view_get_from_gtk_text_view(GTK_TEXT_VIEW(keyword_text));
3306                 gspell_text_view_basic_setup(gspell_view);
3307                 }
3308 #endif
3309
3310         gtk_container_add(GTK_CONTAINER(scrolled), keyword_text);
3311         gtk_widget_show(keyword_text);
3312
3313         gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
3314
3315         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3316         gtk_text_buffer_create_tag(buffer, "monospace",
3317                                 "family", "monospace", NULL);
3318
3319         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3320         gtk_text_buffer_get_start_iter(buffer, &iter);
3321         gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3322         gchar *path;
3323
3324         path = g_build_filename(get_rc_dir(), "keywords", NULL);
3325
3326         GList *kwl = keyword_list_get();
3327         kwl = g_list_first(kwl);
3328         while (kwl)
3329         {
3330                 gtk_text_buffer_get_end_iter (buffer, &iter);
3331             tmp = g_strconcat(static_cast<const gchar *>(kwl->data), "\n", NULL);
3332                 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3333                 kwl = kwl->next;
3334                 g_free(tmp);
3335         }
3336
3337         gtk_text_buffer_set_modified(buffer, FALSE);
3338
3339         g_free(path);
3340 }
3341
3342 /* metadata tab */
3343 #ifdef HAVE_LCMS
3344 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3345 {
3346         auto option = static_cast<gint *>(data);
3347
3348         switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3349                 {
3350                 case 0:
3351                 default:
3352                         *option = INTENT_PERCEPTUAL;
3353                         break;
3354                 case 1:
3355                         *option = INTENT_RELATIVE_COLORIMETRIC;
3356                         break;
3357                 case 2:
3358                         *option = INTENT_SATURATION;
3359                         break;
3360                 case 3:
3361                         *option = INTENT_ABSOLUTE_COLORIMETRIC;
3362                         break;
3363                 }
3364 }
3365
3366 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3367                              gint option, gint *option_c)
3368 {
3369         GtkWidget *combo;
3370         gint current = 0;
3371
3372         *option_c = option;
3373
3374         pref_table_label(table, column, row, text, 0.0);
3375
3376         combo = gtk_combo_box_text_new();
3377
3378         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3379         if (option == INTENT_PERCEPTUAL) current = 0;
3380         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3381         if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3382         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3383         if (option == INTENT_SATURATION) current = 2;
3384         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3385         if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3386
3387         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3388
3389         gtk_widget_set_tooltip_text(combo,"Refer to the lcms documentation for the defaults used when the selected Intent is not available");
3390
3391         g_signal_connect(G_OBJECT(combo), "changed",
3392                          G_CALLBACK(intent_menu_cb), option_c);
3393
3394         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
3395         gtk_widget_show(combo);
3396 }
3397 #endif
3398
3399 static void config_tab_color(GtkWidget *notebook)
3400 {
3401         GtkWidget *label;
3402         GtkWidget *vbox;
3403         GtkWidget *group;
3404         GtkWidget *tabcomp;
3405         GtkWidget *table;
3406         gint i;
3407
3408         vbox = scrolled_notebook_page(notebook, _("Color management"));
3409
3410         group =  pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3411 #ifndef HAVE_LCMS
3412         gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3413 #endif
3414
3415         table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3416         gtk_table_set_col_spacings(GTK_TABLE(table), PREF_PAD_GAP);
3417
3418         label = pref_table_label(table, 0, 0, _("Type"), 0.0);
3419         pref_label_bold(label, TRUE, FALSE);
3420
3421         label = pref_table_label(table, 1, 0, _("Menu name"), 0.0);
3422         pref_label_bold(label, TRUE, FALSE);
3423
3424         label = pref_table_label(table, 2, 0, _("File"), 0.0);
3425         pref_label_bold(label, TRUE, FALSE);
3426
3427         for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3428                 {
3429                 GtkWidget *entry;
3430                 gchar *buf;
3431
3432                 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3433                 pref_table_label(table, 0, i + 1, buf, 1.0);
3434                 g_free(buf);
3435
3436                 entry = gtk_entry_new();
3437                 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3438                 if (options->color_profile.input_name[i])
3439                         {
3440                         gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3441                         }
3442                 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, i + 1, i + 2,
3443                                  static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3444                 gtk_widget_show(entry);
3445                 color_profile_input_name_entry[i] = entry;
3446
3447                 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], nullptr, ".icc", "ICC Files", nullptr);
3448                 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3449                 gtk_widget_set_size_request(entry, 160, -1);
3450                 gtk_table_attach(GTK_TABLE(table), tabcomp, 2, 3, i + 1, i + 2,
3451                                  static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3452                 gtk_widget_show(tabcomp);
3453                 color_profile_input_file_entry[i] = entry;
3454                 }
3455
3456         group =  pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3457 #ifndef HAVE_LCMS
3458         gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3459 #endif
3460         pref_checkbox_new_int(group, _("Use system screen profile if available"),
3461                               options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3462
3463         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3464
3465         pref_table_label(table, 0, 0, _("Screen:"), 1.0);
3466         tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3467                                      options->color_profile.screen_file, nullptr, ".icc", "ICC Files", nullptr);
3468         tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3469         gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3470 #ifdef HAVE_LCMS
3471         add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3472 #endif
3473         gtk_table_attach(GTK_TABLE(table), tabcomp, 1, 2,
3474                          0, 1,
3475                          static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3476
3477         gtk_widget_show(tabcomp);
3478 }
3479
3480 /* advanced entry tab */
3481 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer UNUSED(data))
3482 {
3483         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3484                 {
3485                 c_options->file_ops.use_system_trash = FALSE;
3486                 c_options->file_ops.no_trash = FALSE;
3487                 }
3488 }
3489
3490 static void use_system_trash_cb(GtkWidget *widget, gpointer UNUSED(data))
3491 {
3492         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3493                 {
3494                 c_options->file_ops.use_system_trash = TRUE;
3495                 c_options->file_ops.no_trash = FALSE;
3496                 }
3497 }
3498
3499 static void use_no_cache_cb(GtkWidget *widget, gpointer UNUSED(data))
3500 {
3501         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3502                 {
3503                 c_options->file_ops.no_trash = TRUE;
3504                 }
3505 }
3506
3507 static void config_tab_behavior(GtkWidget *notebook)
3508 {
3509         GtkWidget *hbox;
3510         GtkWidget *vbox;
3511         GtkWidget *group;
3512         GtkWidget *button;
3513         GtkWidget *tabcomp;
3514         GtkWidget *ct_button;
3515         GtkWidget *spin;
3516         GtkWidget *table;
3517         GtkWidget *marks;
3518         GtkWidget *with_rename;
3519         GtkWidget *collections_on_top;
3520         GtkWidget *hide_window_in_fullscreen;
3521         GtkWidget *checkbox;
3522         GtkWidget *tmp;
3523
3524         vbox = scrolled_notebook_page(notebook, _("Behavior"));
3525
3526         group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3527
3528         pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3529                               options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3530         pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3531                               options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3532         pref_checkbox_new_int(group, _("Enable Delete key"),
3533                               options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3534
3535         ct_button = pref_radiobutton_new(group, nullptr, _("Use Geeqie trash location"),
3536                                         !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),nullptr);
3537
3538         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3539         pref_checkbox_link_sensitivity(ct_button, hbox);
3540
3541         pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3542         pref_label_new(hbox, _("Folder:"));
3543
3544         tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, nullptr, nullptr, nullptr, nullptr);
3545         tab_completion_add_select_button(safe_delete_path_entry, nullptr, TRUE);
3546         gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3547         gtk_widget_show(tabcomp);
3548
3549         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3550         pref_checkbox_link_sensitivity(ct_button, hbox);
3551
3552         pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3553         spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MiB"),
3554                                  0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3555         gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3556         button = pref_button_new(nullptr, nullptr, _("View"), FALSE,
3557                                  G_CALLBACK(safe_delete_view_cb), nullptr);
3558         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3559         gtk_widget_show(button);
3560
3561         button = pref_button_new(nullptr, GTK_STOCK_CLEAR, nullptr, FALSE,
3562                                  G_CALLBACK(safe_delete_clear_cb), nullptr);
3563         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3564
3565         c_options->file_ops.no_trash = options->file_ops.no_trash;
3566         c_options->file_ops.use_system_trash = options->file_ops.use_system_trash;
3567
3568         pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3569                                         options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), nullptr);
3570
3571         pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3572                         options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), nullptr);
3573
3574         gtk_widget_show(button);
3575
3576         pref_spacer(group, PREF_PAD_GROUP);
3577
3578
3579         group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3580
3581         pref_checkbox_new_int(group, _("Descend folders in tree view"),
3582                               options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3583
3584         pref_checkbox_new_int(group, _("In place renaming"),
3585                               options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3586
3587         pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3588                               options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3589
3590         tmp = pref_checkbox_new_int(group, _("Circular selection lists"),
3591                               options->circular_selection_lists, &c_options->circular_selection_lists);
3592         gtk_widget_set_tooltip_text(tmp, _("Traverse selection lists in a circular manner"));
3593
3594         marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3595                                 options->marks_save, &c_options->marks_save);
3596         gtk_widget_set_tooltip_text(marks,"Note that marks linked to a keyword will be saved irrespective of this setting");
3597
3598         with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3599                                 options->with_rename, &c_options->with_rename);
3600         gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3601
3602         collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3603                                 options->collections_on_top, &c_options->collections_on_top);
3604         gtk_widget_set_tooltip_text(collections_on_top,"Open collections window on top");
3605
3606         hide_window_in_fullscreen = pref_checkbox_new_int(group, _("Hide window in fullscreen"),
3607                                 options->hide_window_in_fullscreen, &c_options->hide_window_in_fullscreen);
3608         gtk_widget_set_tooltip_text(hide_window_in_fullscreen,"When alt-tabbing, prevent Geeqie window showing twice");
3609
3610         pref_spin_new_int(group, _("Recent folder list maximum size"), nullptr,
3611                           1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3612
3613         tmp = pref_spin_new_int(group, _("Recent folder-image list maximum size"), nullptr, 0, 50, 1, options->recent_folder_image_list_maxsize, &c_options->recent_folder_image_list_maxsize);
3614         gtk_widget_set_tooltip_text(tmp, _("List of the last image viewed in each recent folder.\nRe-opening a folder will set focus to the last image viewed."));
3615
3616         pref_spin_new_int(group, _("Drag'n drop icon size"), nullptr,
3617                           16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3618
3619         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3620         add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
3621
3622         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3623         add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3624
3625         pref_spacer(group, PREF_PAD_GROUP);
3626
3627         group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3628
3629         pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3630                               options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3631         pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), nullptr,
3632                           1, 32, 1, options->keyboard_scroll_step, reinterpret_cast<int *>(&c_options->keyboard_scroll_step));
3633         pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3634                               options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3635         pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3636                               options->image_lm_click_nav, &c_options->image_lm_click_nav);
3637         pref_checkbox_new_int(group, _("Open archive by left click on image"),
3638                               options->image_l_click_archive, &c_options->image_l_click_archive);
3639         pref_checkbox_new_int(group, _("Play video by left click on image"),
3640                               options->image_l_click_video, &c_options->image_l_click_video);
3641         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3642         add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3643
3644         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3645         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3646         add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3647         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3648         add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3649
3650         pref_spacer(group, PREF_PAD_GROUP);
3651
3652         group = pref_group_new(vbox, FALSE, _("GPU"), GTK_ORIENTATION_VERTICAL);
3653
3654         checkbox = pref_checkbox_new_int(group, _("Override disable GPU"),
3655                                 options->override_disable_gpu, &c_options->override_disable_gpu);
3656         gtk_widget_set_tooltip_text(checkbox, "Contact the developers for usage");
3657
3658 #ifdef DEBUG
3659         pref_spacer(group, PREF_PAD_GROUP);
3660
3661         group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3662
3663         pref_spin_new_int(group, _("Debug level:"), nullptr,
3664                           DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3665
3666         pref_checkbox_new_int(group, _("Timer data"),
3667                         options->log_window.timer_data, &c_options->log_window.timer_data);
3668
3669         pref_spin_new_int(group, _("Log Window max. lines:"), nullptr,
3670                           1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3671 #endif
3672 }
3673
3674 /* accelerators tab */
3675 static void config_tab_accelerators(GtkWidget *notebook)
3676 {
3677         GtkWidget *hbox;
3678         GtkWidget *vbox;
3679         GtkWidget *group;
3680         GtkWidget *button;
3681         GtkWidget *scrolled;
3682         GtkWidget *accel_view;
3683         GtkCellRenderer *renderer;
3684         GtkTreeSelection *selection;
3685         GtkTreeViewColumn *column;
3686
3687         vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3688
3689         group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3690
3691         scrolled = gtk_scrolled_window_new(nullptr, nullptr);
3692         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3693         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3694         gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3695         gtk_widget_show(scrolled);
3696
3697         accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3698
3699         accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3700         g_object_unref(accel_store);
3701         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3702         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3703
3704         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3705
3706         renderer = gtk_cell_renderer_text_new();
3707
3708         column = gtk_tree_view_column_new_with_attributes(_("Action"),
3709                                                           renderer,
3710                                                           "text", AE_ACTION,
3711                                                           NULL);
3712
3713         gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3714         gtk_tree_view_column_set_resizable(column, TRUE);
3715         gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3716
3717
3718         renderer = gtk_cell_renderer_accel_new();
3719         g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3720                          G_CALLBACK(accel_store_cleared_cb), accel_store);
3721         g_signal_connect(G_OBJECT(renderer), "accel-edited",
3722                          G_CALLBACK(accel_store_edited_cb), accel_store);
3723
3724
3725         g_object_set (renderer,
3726                       "editable", TRUE,
3727                       "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3728                       NULL);
3729
3730         column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3731                                                           renderer,
3732                                                           "text", AE_KEY,
3733                                                           NULL);
3734
3735         gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3736         gtk_tree_view_column_set_resizable(column, TRUE);
3737         gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3738
3739         renderer = gtk_cell_renderer_text_new();
3740
3741         column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3742                                                           renderer,
3743                                                           "text", AE_TOOLTIP,
3744                                                           NULL);
3745
3746         gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3747         gtk_tree_view_column_set_resizable(column, TRUE);
3748         gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3749
3750         renderer = gtk_cell_renderer_text_new();
3751
3752         column = gtk_tree_view_column_new_with_attributes("Accel",
3753                                                           renderer,
3754                                                           "text", AE_ACCEL,
3755                                                           NULL);
3756
3757         gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3758         gtk_tree_view_column_set_resizable(column, TRUE);
3759         gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3760
3761         accel_store_populate();
3762         gtk_container_add(GTK_CONTAINER(scrolled), accel_view);
3763         gtk_widget_show(accel_view);
3764
3765         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3766
3767         button = pref_button_new(nullptr, nullptr, _("Defaults"), FALSE,
3768                                  G_CALLBACK(accel_default_cb), accel_view);
3769         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3770         gtk_widget_show(button);
3771
3772         button = pref_button_new(nullptr, nullptr, _("Reset selected"), FALSE,
3773                                  G_CALLBACK(accel_reset_cb), accel_view);
3774         gtk_widget_set_tooltip_text(button, _("Will only reset changes made before the settings are saved"));
3775         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3776         gtk_widget_show(button);
3777
3778         button = pref_button_new(nullptr, nullptr, _("Clear selected"), FALSE,
3779                                  G_CALLBACK(accel_clear_cb), accel_view);
3780         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3781         gtk_widget_show(button);
3782 }
3783
3784 /* toolbar main tab */
3785 static void config_tab_toolbar_main(GtkWidget *notebook)
3786 {
3787         GtkWidget *vbox;
3788         GtkWidget *toolbardata;
3789         LayoutWindow *lw;
3790
3791         lw = static_cast<LayoutWindow *>(layout_window_list->data);
3792
3793         vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3794
3795         toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3796         gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3797         gtk_widget_show(vbox);
3798 }
3799
3800 /* toolbar status tab */
3801 static void config_tab_toolbar_status(GtkWidget *notebook)
3802 {
3803         GtkWidget *vbox;
3804         GtkWidget *toolbardata;
3805         LayoutWindow *lw;
3806
3807         lw = static_cast<LayoutWindow *>(layout_window_list->data);
3808
3809         vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3810
3811         toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3812         gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3813         gtk_widget_show(vbox);
3814 }
3815
3816 /* advanced tab */
3817 static gint extension_sort_cb(gconstpointer a, gconstpointer b)
3818 {
3819         return g_strcmp0(static_cast<const gchar *>(a), static_cast<const gchar *>(b));
3820 }
3821
3822 static void config_tab_advanced(GtkWidget *notebook)
3823 {
3824         GtkWidget *vbox;
3825         GtkWidget *group;
3826         GSList *formats_list;
3827         GList *extensions_list = nullptr;
3828         gchar **extensions;
3829         GtkWidget *tabcomp;
3830         GdkPixbufFormat *fm;
3831         gint i;
3832         GString *types_string = g_string_new(nullptr);
3833         GtkWidget *types_string_label;
3834         GtkWidget *threads_string_label;
3835         GtkWidget *dupes_threads_spin;
3836
3837         vbox = scrolled_notebook_page(notebook, _("Advanced"));
3838         group = pref_group_new(vbox, FALSE, _("External preview extraction"), GTK_ORIENTATION_VERTICAL);
3839
3840         pref_checkbox_new_int(group, _("Use external preview extraction -  Requires restart"), options->external_preview.enable, &c_options->external_preview.enable);
3841
3842         pref_spacer(group, PREF_PAD_GROUP);
3843
3844         formats_list = gdk_pixbuf_get_formats();
3845
3846         while (formats_list)
3847                 {
3848                 fm = static_cast<GdkPixbufFormat *>(formats_list->data);
3849                 extensions = gdk_pixbuf_format_get_extensions(fm);
3850
3851                 i = 0;
3852                 while (extensions[i])
3853                         {
3854                         extensions_list = g_list_insert_sorted(extensions_list, g_strdup(extensions[i]), extension_sort_cb);
3855                         i++;
3856                         }
3857
3858                 g_strfreev(extensions);
3859                 formats_list = formats_list->next;
3860                 }
3861
3862         while (extensions_list)
3863                 {
3864                 if (types_string->len == 0)
3865                         {
3866                         types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3867                         }
3868                 else
3869                         {
3870                         types_string = g_string_append(types_string, ", ");
3871                         types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3872                         }
3873
3874                 extensions_list = extensions_list->next;
3875                 }
3876
3877         types_string = g_string_prepend(types_string, _("Usable file types:\n"));
3878         types_string_label = pref_label_new(group, types_string->str);
3879         gtk_label_set_line_wrap(GTK_LABEL(types_string_label), TRUE);
3880
3881         pref_spacer(group, PREF_PAD_GROUP);
3882
3883         group = pref_group_new(vbox, FALSE, _("File identification tool"), GTK_ORIENTATION_VERTICAL);
3884         external_preview_select_entry = gtk_entry_new();
3885         tabcomp = tab_completion_new(&external_preview_select_entry, options->external_preview.select, nullptr, nullptr, nullptr, nullptr);
3886         tab_completion_add_select_button(external_preview_select_entry, _("Select file identification tool"), FALSE);
3887         gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3888         gtk_widget_show(tabcomp);
3889
3890         group = pref_group_new(vbox, FALSE, _("Preview extraction tool"), GTK_ORIENTATION_VERTICAL);
3891         external_preview_extract_entry = gtk_entry_new();
3892         tabcomp = tab_completion_new(&external_preview_extract_entry, options->external_preview.extract, nullptr, nullptr, nullptr, nullptr);
3893         tab_completion_add_select_button(external_preview_extract_entry, _("Select preview extraction tool"), FALSE);
3894         gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3895         gtk_widget_show(tabcomp);
3896
3897         gtk_widget_show(vbox);
3898
3899         g_slist_free(formats_list);
3900         string_list_free(extensions_list);
3901         g_string_free(types_string, TRUE);
3902
3903         pref_spacer(group, PREF_PAD_GROUP);
3904
3905         pref_line(vbox, PREF_PAD_SPACE);
3906         group = pref_group_new(vbox, FALSE, _("Thread pool limits"), GTK_ORIENTATION_VERTICAL);
3907
3908         threads_string_label = pref_label_new(group, "This option limits the number of threads (or cpu cores)\nthat Geeqie will use when running duplicate checks. The default value is 0, which means all available cores will be used.");
3909         gtk_label_set_line_wrap(GTK_LABEL(threads_string_label), TRUE);
3910
3911         pref_spacer(vbox, PREF_PAD_GROUP);
3912
3913         dupes_threads_spin = pref_spin_new_int(vbox, _("Duplicate check:"), _("max. threads"), 0, get_cpu_cores(), 1, options->threads.duplicates, &c_options->threads.duplicates);
3914         gtk_widget_set_tooltip_markup(dupes_threads_spin, _("Set to 0 for unlimited"));
3915 }
3916
3917 /* stereo tab */
3918 static void config_tab_stereo(GtkWidget *notebook)
3919 {
3920         GtkWidget *vbox;
3921         GtkWidget *group;
3922         GtkWidget *group2;
3923         GtkWidget *table;
3924         GtkWidget *box;
3925         GtkWidget *box2;
3926         GtkWidget *fs_button;
3927         vbox = scrolled_notebook_page(notebook, _("Stereo"));
3928
3929         group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
3930
3931         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3932         add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
3933
3934         table = pref_table_new(group, 2, 2, TRUE, FALSE);
3935         box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3936         pref_checkbox_new_int(box, _("Mirror left image"),
3937                               options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
3938         box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3939         pref_checkbox_new_int(box, _("Flip left image"),
3940                               options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
3941         box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3942         pref_checkbox_new_int(box, _("Mirror right image"),
3943                               options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
3944         box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3945         pref_checkbox_new_int(box, _("Flip right image"),
3946                               options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
3947         pref_checkbox_new_int(group, _("Swap left and right images"),
3948                               options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
3949         pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
3950                               options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
3951
3952         group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
3953         fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
3954                               options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
3955         box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
3956         pref_checkbox_link_sensitivity(fs_button, box2);
3957         table = pref_table_new(box2, 2, 1, FALSE, FALSE);
3958         add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
3959         table = pref_table_new(box2, 2, 2, TRUE, FALSE);
3960         box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3961         pref_checkbox_new_int(box, _("Mirror left image"),
3962                               options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
3963         box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3964         pref_checkbox_new_int(box, _("Flip left image"),
3965                               options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
3966         box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3967         pref_checkbox_new_int(box, _("Mirror right image"),
3968                               options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
3969         box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3970         pref_checkbox_new_int(box, _("Flip right image"),
3971                               options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
3972         pref_checkbox_new_int(box2, _("Swap left and right images"),
3973                               options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
3974         pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
3975                               options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
3976
3977         group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
3978         table = pref_table_new(group2, 5, 3, FALSE, FALSE);
3979         pref_table_spin_new_int(table, 0, 0, _("Width"), nullptr,
3980                           1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
3981         pref_table_spin_new_int(table, 3, 0,  _("Height"), nullptr,
3982                           1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
3983         pref_table_spin_new_int(table, 0, 1,  _("Left X"), nullptr,
3984                           0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
3985         pref_table_spin_new_int(table, 3, 1,  _("Left Y"), nullptr,
3986                           0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
3987         pref_table_spin_new_int(table, 0, 2,  _("Right X"), nullptr,
3988                           0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
3989         pref_table_spin_new_int(table, 3, 2,  _("Right Y"), nullptr,
3990                           0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
3991
3992 }
3993
3994 /* Main preferences window */
3995 static void config_window_create(LayoutWindow *lw)
3996 {
3997         GtkWidget *win_vbox;
3998         GtkWidget *hbox;
3999         GtkWidget *notebook;
4000         GtkWidget *button;
4001         GtkWidget *ct_button;
4002
4003         if (!c_options) c_options = init_options(nullptr);
4004
4005         configwindow = window_new(GTK_WINDOW_TOPLEVEL, "preferences", PIXBUF_INLINE_ICON_CONFIG, nullptr, _("Preferences"));
4006         DEBUG_NAME(configwindow);
4007         gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
4008         g_signal_connect(G_OBJECT(configwindow), "delete_event",
4009                          G_CALLBACK(config_window_delete), NULL);
4010         if (options->save_dialog_window_positions)
4011                 {
4012                 gtk_window_resize(GTK_WINDOW(configwindow), lw->options.preferences_window.w, lw->options.preferences_window.h);
4013                 gtk_window_move(GTK_WINDOW(configwindow), lw->options.preferences_window.x, lw->options.preferences_window.y);
4014                 }
4015         else
4016                 {
4017                 gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
4018                 }
4019         gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
4020         gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
4021
4022         win_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4023         gtk_container_add(GTK_CONTAINER(configwindow), win_vbox);
4024         gtk_widget_show(win_vbox);
4025
4026         notebook = gtk_notebook_new();
4027         gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
4028         gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
4029         gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
4030
4031         config_tab_general(notebook);
4032         config_tab_image(notebook);
4033         config_tab_osd(notebook);
4034         config_tab_windows(notebook);
4035         config_tab_accelerators(notebook);
4036         config_tab_files(notebook);
4037         config_tab_metadata(notebook);
4038         config_tab_keywords(notebook);
4039         config_tab_color(notebook);
4040         config_tab_stereo(notebook);
4041         config_tab_behavior(notebook);
4042         config_tab_toolbar_main(notebook);
4043         config_tab_toolbar_status(notebook);
4044         config_tab_advanced(notebook);
4045
4046         gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), lw->options.preferences_window.page_number);
4047
4048         hbox = gtk_hbutton_box_new();
4049         gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
4050         gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
4051         gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
4052         gtk_widget_show(hbox);
4053
4054         button = pref_button_new(nullptr, GTK_STOCK_HELP, nullptr, FALSE,
4055                                  G_CALLBACK(config_window_help_cb), notebook);
4056         gtk_container_add(GTK_CONTAINER(hbox), button);
4057         gtk_widget_set_can_default(button, TRUE);
4058         gtk_widget_show(button);
4059
4060         button = pref_button_new(nullptr, GTK_STOCK_OK, nullptr, FALSE,
4061                                  G_CALLBACK(config_window_ok_cb), notebook);
4062         gtk_container_add(GTK_CONTAINER(hbox), button);
4063         gtk_widget_set_can_default(button, TRUE);
4064         gtk_widget_grab_default(button);
4065         gtk_widget_show(button);
4066
4067         ct_button = button;
4068
4069         button = pref_button_new(nullptr, GTK_STOCK_CANCEL, nullptr, FALSE,
4070                                  G_CALLBACK(config_window_close_cb), nullptr);
4071         gtk_container_add(GTK_CONTAINER(hbox), button);
4072         gtk_widget_set_can_default(button, TRUE);
4073         gtk_widget_show(button);
4074
4075         if (!generic_dialog_get_alternative_button_order(configwindow))
4076                 {
4077                 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
4078                 }
4079
4080         gtk_widget_show(notebook);
4081
4082         gtk_widget_show(configwindow);
4083 }
4084
4085 /*
4086  *-----------------------------------------------------------------------------
4087  * config window show (public)
4088  *-----------------------------------------------------------------------------
4089  */
4090
4091 void show_config_window(LayoutWindow *lw)
4092 {
4093         if (configwindow)
4094                 {
4095                 gtk_window_present(GTK_WINDOW(configwindow));
4096                 return;
4097                 }
4098
4099         config_window_create(lw);
4100 }
4101
4102 /*
4103  *-----------------
4104  * about window
4105  *-----------------
4106  */
4107
4108 void show_about_window(LayoutWindow *lw)
4109 {
4110         GDataInputStream *data_stream;
4111         GInputStream *in_stream_authors;
4112         GInputStream *in_stream_translators;
4113         GString *copyright;
4114         GdkPixbuf *pixbuf_icon;
4115         GdkPixbuf *pixbuf_logo;
4116         ZoneDetect *cd;
4117         gchar *artists[2];
4118         gchar *author_line;
4119         gchar *authors[1000];
4120         gchar *authors_path;
4121         gchar *comment;
4122         gchar *timezone_path;
4123         gchar *translators;
4124         gchar *translators_path;
4125         gint i_authors = 0;
4126         gint n = 0;
4127         gsize bytes_read;
4128         gsize length;
4129         gsize size;
4130         guint32 flags;
4131
4132         copyright = g_string_new(nullptr);
4133         copyright = g_string_append(copyright, "This program comes with absolutely no warranty.\nGNU General Public License, version 2 or later.\nSee https://www.gnu.org/licenses/old-licenses/gpl-2.0.html\n\n");
4134
4135         timezone_path = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
4136         if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
4137                 {
4138                 cd = ZDOpenDatabase(timezone_path);
4139                 if (cd)
4140                         {
4141                         copyright = g_string_append(copyright, ZDGetNotice(cd));
4142                         }
4143                 else
4144                         {
4145                         log_printf("Error: Init of timezone database %s failed\n", timezone_path);
4146                         }
4147                 ZDCloseDatabase(cd);
4148                 }
4149         g_free(timezone_path);
4150
4151         authors_path = g_build_filename(GQ_RESOURCE_PATH_CREDITS, "authors", NULL);
4152
4153         in_stream_authors = g_resources_open_stream(authors_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4154
4155         data_stream = g_data_input_stream_new(in_stream_authors);
4156
4157         authors[0] = nullptr;
4158         while ((author_line = g_data_input_stream_read_line(G_DATA_INPUT_STREAM(data_stream), &length, nullptr, nullptr)))
4159                 {
4160                 authors[i_authors] = g_strdup(author_line);
4161                 i_authors++;
4162                 g_free(author_line);
4163                 }
4164         authors[i_authors] = nullptr;
4165
4166         g_input_stream_close(in_stream_authors, nullptr, nullptr);
4167
4168         translators_path = g_build_filename(GQ_RESOURCE_PATH_CREDITS, "translators", NULL);
4169
4170         g_resources_get_info(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &size, &flags, nullptr);
4171
4172         in_stream_translators = g_resources_open_stream(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4173         translators = static_cast<gchar *>(g_malloc0(size));
4174         g_input_stream_read_all(in_stream_translators, translators, size, &bytes_read, nullptr, nullptr);
4175         g_input_stream_close(in_stream_translators, nullptr, nullptr);
4176
4177         comment = g_strconcat("Project created by John Ellis\nGQview 1998\nGeeqie 2007\n\n\nDevelopment and bug reports:\n", GQ_EMAIL_ADDRESS, "\nhttps://github.com/BestImageViewer/geeqie/issues",NULL);
4178
4179         artists[0] = g_strdup("Néstor Díaz Valencia <nestor@estudionexos.com>");
4180         artists[1] = nullptr;
4181
4182         pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
4183         pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
4184         gtk_show_about_dialog(GTK_WINDOW(lw->window),
4185                 "title", _("About Geeqie"),
4186                 "resizable", TRUE,
4187                 "program-name", GQ_APPNAME,
4188                 "version", VERSION,
4189                 "logo", pixbuf_logo,
4190                 "icon", pixbuf_icon,
4191                 "website", GQ_WEBSITE,
4192                 "website-label", "Website",
4193                 "comments", comment,
4194                 "artists", artists,
4195                 "authors", authors,
4196                 "translator-credits", translators,
4197                 "wrap-license", TRUE,
4198                 "license", copyright->str,
4199                 NULL);
4200
4201         g_string_free(copyright, TRUE);
4202
4203         while(n < i_authors)
4204                 {
4205                 g_free(authors[n]);
4206                 n++;
4207                 }
4208
4209         g_free(artists[0]);
4210         g_free(authors_path);
4211         g_free(comment);
4212         g_free(translators);
4213         g_free(translators_path);
4214         g_object_unref(data_stream);
4215         g_object_unref(in_stream_authors);
4216         g_object_unref(in_stream_translators);
4217 }
4218
4219 static void image_overlay_set_text_colours()
4220 {
4221         c_options->image_overlay.text_red = options->image_overlay.text_red;
4222         c_options->image_overlay.text_green = options->image_overlay.text_green;
4223         c_options->image_overlay.text_blue = options->image_overlay.text_blue;
4224         c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
4225         c_options->image_overlay.background_red = options->image_overlay.background_red;
4226         c_options->image_overlay.background_green = options->image_overlay.background_green;
4227         c_options->image_overlay.background_blue = options->image_overlay.background_blue;
4228         c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
4229 }
4230
4231 /*
4232  *-----------------------------------------------------------------------------
4233  * timezone database routines
4234  *-----------------------------------------------------------------------------
4235  */
4236
4237 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
4238 {
4239         GError *error = nullptr;
4240         auto tz = static_cast<TZData *>(data);
4241         gchar *tmp_filename;
4242         gchar *timezone_bin;
4243         gchar *tmp_dir = nullptr;
4244         FileData *fd;
4245
4246         if (!g_cancellable_is_cancelled(tz->cancellable))
4247                 {
4248                 generic_dialog_close(tz->gd);
4249                 }
4250
4251
4252         if (g_file_copy_finish(G_FILE(source_object), res, &error))
4253                 {
4254                 tmp_filename = g_file_get_path(tz->tmp_g_file);
4255                 fd = file_data_new_simple(tmp_filename);
4256                 tmp_dir = open_archive(fd);
4257
4258                 if (tmp_dir)
4259                         {
4260                         timezone_bin = g_build_filename(tmp_dir, TIMEZONE_DATABASE_VERSION, TIMEZONE_DATABASE_FILE, NULL);
4261                         if (isfile(timezone_bin))
4262                                 {
4263                                 move_file(timezone_bin, tz->timezone_database_user);
4264                                 }
4265                         else
4266                                 {
4267                                 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GTK_STOCK_DIALOG_WARNING, nullptr);
4268                                 }
4269
4270                         g_free(timezone_bin);
4271                         g_free(tmp_dir); // The folder in /tmp is deleted in exit_program_final()
4272                         }
4273                 else
4274                         {
4275                         warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GTK_STOCK_DIALOG_WARNING, nullptr);
4276                         }
4277                 g_free(tmp_filename);
4278                 file_data_unref(fd);
4279                 }
4280         else
4281                 {
4282                 file_util_warning_dialog(_("Error: Timezone database download failed"), error->message, GTK_STOCK_DIALOG_ERROR, nullptr);
4283                 }
4284
4285         g_file_delete(tz->tmp_g_file, nullptr, &error);
4286         g_object_unref(tz->tmp_g_file);
4287         tz->tmp_g_file = nullptr;
4288         g_object_unref(tz->cancellable);
4289         g_object_unref(tz->timezone_database_gq);
4290 }
4291
4292 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
4293 {
4294         auto tz = static_cast<TZData *>(data);
4295
4296         if (!g_cancellable_is_cancelled(tz->cancellable))
4297                 {
4298                 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), static_cast<gdouble>(current_num_bytes) / total_num_bytes);
4299                 }
4300 }
4301
4302 static void timezone_cancel_button_cb(GenericDialog *UNUSED(gd), gpointer data)
4303 {
4304         auto tz = static_cast<TZData *>(data);
4305
4306         g_cancellable_cancel(tz->cancellable);
4307 }
4308
4309 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
4310 {
4311         auto tz = static_cast<TZData *>(data);
4312         GError *error = nullptr;
4313         GFileIOStream *io_stream;
4314
4315         if (tz->tmp_g_file)
4316                 {
4317                 return;
4318                 }
4319
4320         tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
4321
4322         if (error)
4323                 {
4324                 file_util_warning_dialog(_("Timezone database download failed"), error->message, GTK_STOCK_DIALOG_ERROR, nullptr);
4325                 log_printf("Error: Download timezone database failed:\n%s", error->message);
4326                 g_error_free(error);
4327                 g_object_unref(tz->tmp_g_file);
4328                 }
4329         else
4330                 {
4331                 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE_WEB);
4332
4333                 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", nullptr, TRUE, timezone_cancel_button_cb, tz);
4334
4335                 generic_dialog_add_message(tz->gd, GTK_STOCK_DIALOG_INFO, _("Downloading timezone database"), nullptr, FALSE);
4336
4337                 tz->progress = gtk_progress_bar_new();
4338                 gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
4339                 gtk_widget_show(tz->progress);
4340
4341                 gtk_widget_show(tz->gd->dialog);
4342                 tz->cancellable = g_cancellable_new();
4343                 g_file_copy_async(tz->timezone_database_gq, tz->tmp_g_file, G_FILE_COPY_OVERWRITE, G_PRIORITY_LOW, tz->cancellable, timezone_progress_cb, tz, timezone_async_ready_cb, tz);
4344
4345                 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
4346                 }
4347 }
4348 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */