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