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