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