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