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