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