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