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