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