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