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