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