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