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