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