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