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