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