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