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