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