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