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