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