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