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