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