97a6c39b4cdfe2e9ba48abf884b71f165f4fa6d9
[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, _("Files"));
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         gchar *text;
2917
2918         vbox = scrolled_notebook_page(notebook, _("Metadata"));
2919
2920
2921         group = pref_group_new(vbox, FALSE, _("Metadata writing process"), GTK_ORIENTATION_VERTICAL);
2922 #ifndef HAVE_EXIV2
2923         label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2924 #endif
2925         label = pref_label_new(group, _("Metadata are written in the following order. The process ends after first success."));
2926 #if GTK_CHECK_VERSION(3,16,0)
2927         gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2928         gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2929 #else
2930         gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2931 #endif
2932
2933         ct_button = pref_checkbox_new_int(group, _("1) Save metadata in image files, or sidecar files, according to the XMP standard"),
2934                               options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2935 #ifndef HAVE_EXIV2
2936         gtk_widget_set_sensitive(ct_button, FALSE);
2937 #endif
2938
2939         pref_checkbox_new_int(group, _("2) Save metadata in '.metadata' folder, local to image folder (non-standard)"),
2940                               options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2941
2942         text = g_strdup_printf(_("3) Save metadata in Geeqie private directory '%s'"), get_metadata_cache_dir());
2943         label = pref_label_new(group, text);
2944 #if GTK_CHECK_VERSION(3,16,0)
2945         gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2946         gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2947 #else
2948         gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2949 #endif
2950         gtk_misc_set_padding(GTK_MISC(label), 22, 0);
2951         g_free(text);
2952
2953         group = pref_group_new(vbox, FALSE, _("Step 1: Write to image files"), GTK_ORIENTATION_VERTICAL);
2954 #ifndef HAVE_EXIV2
2955         gtk_widget_set_sensitive(group, FALSE);
2956 #endif
2957
2958         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2959         pref_checkbox_link_sensitivity(ct_button, hbox);
2960
2961         pref_checkbox_new_int(hbox, _("Store metadata also in legacy IPTC tags (converted according to IPTC4XMP standard)"),
2962                               options->metadata.save_legacy_IPTC, &c_options->metadata.save_legacy_IPTC);
2963
2964         pref_checkbox_new_int(hbox, _("Warn if the image files are unwritable"),
2965                               options->metadata.warn_on_write_problems, &c_options->metadata.warn_on_write_problems);
2966
2967         pref_checkbox_new_int(hbox, _("Ask before writing to image files"),
2968                               options->metadata.confirm_write, &c_options->metadata.confirm_write);
2969
2970         pref_checkbox_new_int(hbox, _("Create sidecar files named image.ext.xmp (as opposed to image.xmp)"),
2971                               options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2972
2973         group = pref_group_new(vbox, FALSE, _("Step 2 and 3: write to Geeqie private files"), GTK_ORIENTATION_VERTICAL);
2974 #ifndef HAVE_EXIV2
2975         gtk_widget_set_sensitive(group, FALSE);
2976 #endif
2977
2978         pref_checkbox_new_int(group, _("Use GQview legacy metadata format (supports only keywords and comments) instead of XMP"),
2979                               options->metadata.save_legacy_format, &c_options->metadata.save_legacy_format);
2980
2981
2982         group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2983         pref_checkbox_new_int(group, _("Write the same description tags (keywords, comment, etc.) to all grouped sidecars"),
2984                               options->metadata.sync_grouped_files, &c_options->metadata.sync_grouped_files);
2985
2986         pref_checkbox_new_int(group, _("Allow keywords to differ only in case"),
2987                               options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2988
2989         ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"),
2990                               options->metadata.write_orientation, &c_options->metadata.write_orientation);
2991 #ifndef HAVE_EXIV2
2992         gtk_widget_set_sensitive(ct_button, FALSE);
2993 #endif
2994
2995         group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2996
2997         ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"),
2998                               options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
2999
3000         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3001         pref_checkbox_link_sensitivity(ct_button, hbox);
3002
3003         pref_spin_new_int(hbox, _("Timeout (seconds):"), NULL, 0, 900, 1,
3004                               options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
3005
3006         pref_checkbox_new_int(group, _("Write metadata on image change"),
3007                               options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
3008
3009         pref_checkbox_new_int(group, _("Write metadata on directory change"),
3010                               options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
3011
3012 #ifdef HAVE_SPELL
3013 #if GTK_CHECK_VERSION(3,20,0)
3014         group = pref_group_new(vbox, FALSE, _("Spelling checks"), GTK_ORIENTATION_VERTICAL);
3015
3016         ct_button = pref_checkbox_new_int(group, _("Check spelling -Requires restart"), options->metadata.check_spelling, &c_options->metadata.check_spelling);
3017 #endif
3018 #endif
3019
3020         group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
3021
3022         ct_button = pref_checkbox_new_int(group, _("Read metadata in background"),
3023                                           options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
3024         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");
3025 }
3026
3027 /* keywords tab */
3028
3029 typedef struct _KeywordFindData KeywordFindData;
3030 struct _KeywordFindData
3031 {
3032         GenericDialog *gd;
3033
3034         GList *list;
3035         GList *list_dir;
3036
3037         GtkWidget *button_close;
3038         GtkWidget *button_stop;
3039         GtkWidget *button_start;
3040         GtkWidget *progress;
3041         GtkWidget *spinner;
3042
3043         GtkWidget *group;
3044         GtkWidget *entry;
3045
3046         gboolean recurse;
3047
3048         guint idle_id; /* event source id */
3049 };
3050
3051 #define KEYWORD_DIALOG_WIDTH 400
3052
3053 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
3054 {
3055         GList *list_d = NULL;
3056         GList *list_f = NULL;
3057
3058         if (kfd->recurse)
3059                 {
3060                 filelist_read(dir_fd, &list_f, &list_d);
3061                 }
3062         else
3063                 {
3064                 filelist_read(dir_fd, &list_f, NULL);
3065                 }
3066
3067         list_f = filelist_filter(list_f, FALSE);
3068         list_d = filelist_filter(list_d, TRUE);
3069
3070         kfd->list = g_list_concat(list_f, kfd->list);
3071         kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
3072 }
3073
3074 static void keywords_find_reset(KeywordFindData *kfd)
3075 {
3076         filelist_free(kfd->list);
3077         kfd->list = NULL;
3078
3079         filelist_free(kfd->list_dir);
3080         kfd->list_dir = NULL;
3081 }
3082
3083 static void keywords_find_close_cb(GenericDialog *fd, gpointer data)
3084 {
3085         KeywordFindData *kfd = data;
3086
3087         if (!gtk_widget_get_sensitive(kfd->button_close)) return;
3088
3089         keywords_find_reset(kfd);
3090         generic_dialog_close(kfd->gd);
3091         g_free(kfd);
3092 }
3093
3094 static void keywords_find_finish(KeywordFindData *kfd)
3095 {
3096         keywords_find_reset(kfd);
3097
3098         gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
3099         spinner_set_interval(kfd->spinner, -1);
3100
3101         gtk_widget_set_sensitive(kfd->group, TRUE);
3102         gtk_widget_set_sensitive(kfd->button_start, TRUE);
3103         gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3104         gtk_widget_set_sensitive(kfd->button_close, TRUE);
3105 }
3106
3107 static void keywords_find_stop_cb(GenericDialog *fd, gpointer data)
3108 {
3109         KeywordFindData *kfd = data;
3110
3111         g_idle_remove_by_data(kfd);
3112
3113         keywords_find_finish(kfd);
3114 }
3115
3116 static gboolean keywords_find_file(gpointer data)
3117 {
3118         KeywordFindData *kfd = data;
3119         GtkTextIter iter;
3120         GtkTextBuffer *buffer;
3121         gchar *tmp;
3122         GList *keywords;
3123
3124         if (kfd->list)
3125                 {
3126                 FileData *fd;
3127
3128                 fd = kfd->list->data;
3129                 kfd->list = g_list_remove(kfd->list, fd);
3130
3131                 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
3132                 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3133
3134                 while (keywords)
3135                         {
3136                         gtk_text_buffer_get_end_iter(buffer, &iter);
3137                         tmp = g_strconcat(keywords->data, "\n", NULL);
3138                         gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3139                         g_free(tmp);
3140                         keywords = keywords->next;
3141                         }
3142
3143                 gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
3144                 file_data_unref(fd);
3145                 string_list_free(keywords);
3146
3147                 return (TRUE);
3148                 }
3149         else if (kfd->list_dir)
3150                 {
3151                 FileData *fd;
3152
3153                 fd = kfd->list_dir->data;
3154                 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
3155
3156                 keywords_find_folder(kfd, fd);
3157
3158                 file_data_unref(fd);
3159
3160                 return TRUE;
3161                 }
3162
3163         keywords_find_finish(kfd);
3164
3165         return FALSE;
3166 }
3167
3168 static void keywords_find_start_cb(GenericDialog *fd, gpointer data)
3169 {
3170         KeywordFindData *kfd = data;
3171         gchar *path;
3172
3173         if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
3174
3175         path = remove_trailing_slash((gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
3176         parse_out_relatives(path);
3177
3178         if (!isdir(path))
3179                 {
3180                 warning_dialog(_("Invalid folder"),
3181                                 _("The specified folder can not be found."),
3182                                 GTK_STOCK_DIALOG_WARNING, kfd->gd->dialog);
3183                 }
3184         else
3185                 {
3186                 FileData *dir_fd;
3187
3188                 gtk_widget_set_sensitive(kfd->group, FALSE);
3189                 gtk_widget_set_sensitive(kfd->button_start, FALSE);
3190                 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
3191                 gtk_widget_set_sensitive(kfd->button_close, FALSE);
3192                 spinner_set_interval(kfd->spinner, SPINNER_SPEED);
3193
3194                 dir_fd = file_data_new_dir(path);
3195                 keywords_find_folder(kfd, dir_fd);
3196                 file_data_unref(dir_fd);
3197                 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
3198                 }
3199
3200         g_free(path);
3201 }
3202
3203 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
3204 {
3205         KeywordFindData *kfd;
3206         GtkWidget *hbox;
3207         GtkWidget *label;
3208
3209         kfd = g_new0(KeywordFindData, 1);
3210
3211         kfd->gd = generic_dialog_new(_("Search for keywords"),
3212                                                                         "search_for_keywords",
3213                                                                         widget, FALSE,
3214                                                                         NULL, kfd);
3215         gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
3216         kfd->gd->cancel_cb = keywords_find_close_cb;
3217         kfd->button_close = generic_dialog_add_button(kfd->gd, GTK_STOCK_CLOSE, NULL,
3218                                                      keywords_find_close_cb, FALSE);
3219         kfd->button_start = generic_dialog_add_button(kfd->gd, GTK_STOCK_OK, _("S_tart"),
3220                                                      keywords_find_start_cb, FALSE);
3221         kfd->button_stop = generic_dialog_add_button(kfd->gd, GTK_STOCK_STOP, NULL,
3222                                                     keywords_find_stop_cb, FALSE);
3223         gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3224
3225         generic_dialog_add_message(kfd->gd, NULL, _("Search for keywords"), NULL, FALSE);
3226
3227         hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3228         pref_spacer(hbox, PREF_PAD_INDENT);
3229         kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3230
3231         hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3232         pref_label_new(hbox, _("Folder:"));
3233
3234         label = tab_completion_new(&kfd->entry, path, NULL, NULL, NULL, NULL);
3235         tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
3236         gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3237         gtk_widget_show(label);
3238
3239         pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
3240
3241         pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
3242         hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3243
3244         kfd->progress = gtk_entry_new();
3245         gtk_widget_set_can_focus(kfd->progress, FALSE);
3246         gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
3247         gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
3248         gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
3249         gtk_widget_show(kfd->progress);
3250
3251         kfd->spinner = spinner_new(NULL, -1);
3252         gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
3253         gtk_widget_show(kfd->spinner);
3254
3255         kfd->list = NULL;
3256
3257         gtk_widget_show(kfd->gd->dialog);
3258 }
3259
3260 static void keywords_find_cb(GtkWidget *widget, gpointer data)
3261 {
3262         const gchar *path = layout_get_path(NULL);
3263
3264         if (!path || !*path) path = homedir();
3265         keywords_find_dialog(widget, path);
3266 }
3267
3268 static void config_tab_keywords_save()
3269 {
3270         GtkTextIter start, end;
3271         GtkTextBuffer *buffer;
3272         GList *kw_list = NULL;
3273         GList *work;
3274         gchar *buffer_text;
3275         gchar *kw_split;
3276         gboolean found;
3277
3278         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3279         gtk_text_buffer_get_bounds(buffer, &start, &end);
3280
3281         buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3282
3283         kw_split = strtok(buffer_text, "\n");
3284         while (kw_split != NULL)
3285                 {
3286                 work = kw_list;
3287                 found = FALSE;
3288                 while (work)
3289                         {
3290                         if (g_strcmp0(work->data, kw_split) == 0)
3291                                 {
3292                                 found = TRUE;
3293                                 break;
3294                                 }
3295                         work = work->next;
3296                         }
3297                 if (!found)
3298                         {
3299                         kw_list = g_list_append(kw_list, g_strdup(kw_split));
3300                         }
3301                 kw_split = strtok(NULL, "\n");
3302                 }
3303
3304         keyword_list_set(kw_list);
3305
3306         string_list_free(kw_list);
3307         g_free(buffer_text);
3308 }
3309
3310 static void config_tab_keywords(GtkWidget *notebook)
3311 {
3312         GtkWidget *hbox;
3313         GtkWidget *vbox;
3314         GtkWidget *group;
3315         GtkWidget *button;
3316         GtkWidget *scrolled;
3317         GtkTextIter iter;
3318         GtkTextBuffer *buffer;
3319         gchar *tmp;
3320 #ifdef HAVE_SPELL
3321         GspellTextView *gspell_view;
3322 #endif
3323
3324         vbox = scrolled_notebook_page(notebook, _("Keywords"));
3325
3326         group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
3327
3328         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3329
3330         button = pref_button_new(hbox, GTK_STOCK_EXECUTE, _("Search"), FALSE,
3331                                    G_CALLBACK(keywords_find_cb), keyword_text);
3332         gtk_widget_set_tooltip_text(button, "Search for existing keywords");
3333
3334
3335         keyword_text = gtk_text_view_new();
3336         gtk_widget_set_size_request(keyword_text, 20, 20);
3337         scrolled = gtk_scrolled_window_new(NULL, NULL);
3338         gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3339         gtk_widget_show(scrolled);
3340
3341 #ifdef HAVE_SPELL
3342 #if GTK_CHECK_VERSION(3,20,0)
3343         if (options->metadata.check_spelling)
3344                 {
3345                 gspell_view = gspell_text_view_get_from_gtk_text_view(GTK_TEXT_VIEW(keyword_text));
3346                 gspell_text_view_basic_setup(gspell_view);
3347                 }
3348 #endif
3349 #endif
3350
3351         gtk_container_add(GTK_CONTAINER(scrolled), keyword_text);
3352         gtk_widget_show(keyword_text);
3353
3354         gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
3355
3356         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3357         gtk_text_buffer_create_tag(buffer, "monospace",
3358                                 "family", "monospace", NULL);
3359
3360         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3361         gtk_text_buffer_get_start_iter(buffer, &iter);
3362         gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3363         gchar *path;
3364
3365         path = g_build_filename(get_rc_dir(), "keywords", NULL);
3366
3367         GList *kwl = keyword_list_get();
3368         kwl = g_list_first(kwl);
3369         while (kwl)
3370         {
3371                 gtk_text_buffer_get_end_iter (buffer, &iter);
3372             tmp = g_strconcat(kwl->data, "\n", NULL);
3373                 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3374                 kwl = kwl->next;
3375                 g_free(tmp);
3376         }
3377
3378         gtk_text_buffer_set_modified(buffer, FALSE);
3379
3380         g_free(path);
3381 }
3382
3383 /* metadata tab */
3384 #ifdef HAVE_LCMS
3385 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3386 {
3387         gint *option = data;
3388
3389         switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3390                 {
3391                 case 0:
3392                 default:
3393                         *option = INTENT_PERCEPTUAL;
3394                         break;
3395                 case 1:
3396                         *option = INTENT_RELATIVE_COLORIMETRIC;
3397                         break;
3398                 case 2:
3399                         *option = INTENT_SATURATION;
3400                         break;
3401                 case 3:
3402                         *option = INTENT_ABSOLUTE_COLORIMETRIC;
3403                         break;
3404                 }
3405 }
3406
3407 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3408                              gint option, gint *option_c)
3409 {
3410         GtkWidget *combo;
3411         gint current = 0;
3412
3413         *option_c = option;
3414
3415         pref_table_label(table, column, row, text, 0.0);
3416
3417         combo = gtk_combo_box_text_new();
3418
3419         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3420         if (option == INTENT_PERCEPTUAL) current = 0;
3421         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3422         if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3423         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3424         if (option == INTENT_SATURATION) current = 2;
3425         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3426         if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3427
3428         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3429
3430         gtk_widget_set_tooltip_text(combo,"Refer to the lcms documentation for the defaults used when the selected Intent is not available");
3431
3432         g_signal_connect(G_OBJECT(combo), "changed",
3433                          G_CALLBACK(intent_menu_cb), option_c);
3434
3435         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, 0, 0, 0);
3436         gtk_widget_show(combo);
3437 }
3438 #endif
3439
3440 static void config_tab_color(GtkWidget *notebook)
3441 {
3442         GtkWidget *label;
3443         GtkWidget *vbox;
3444         GtkWidget *group;
3445         GtkWidget *tabcomp;
3446         GtkWidget *table;
3447         gint i;
3448
3449         vbox = scrolled_notebook_page(notebook, _("Color management"));
3450
3451         group =  pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3452 #ifndef HAVE_LCMS
3453         gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3454 #endif
3455
3456         table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3457         gtk_table_set_col_spacings(GTK_TABLE(table), PREF_PAD_GAP);
3458
3459         label = pref_table_label(table, 0, 0, _("Type"), 0.0);
3460         pref_label_bold(label, TRUE, FALSE);
3461
3462         label = pref_table_label(table, 1, 0, _("Menu name"), 0.0);
3463         pref_label_bold(label, TRUE, FALSE);
3464
3465         label = pref_table_label(table, 2, 0, _("File"), 0.0);
3466         pref_label_bold(label, TRUE, FALSE);
3467
3468         for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3469                 {
3470                 GtkWidget *entry;
3471                 gchar *buf;
3472
3473                 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3474                 pref_table_label(table, 0, i + 1, buf, 1.0);
3475                 g_free(buf);
3476
3477                 entry = gtk_entry_new();
3478                 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3479                 if (options->color_profile.input_name[i])
3480                         {
3481                         gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3482                         }
3483                 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, i + 1, i + 2,
3484                                  GTK_FILL | GTK_EXPAND, 0, 0, 0);
3485                 gtk_widget_show(entry);
3486                 color_profile_input_name_entry[i] = entry;
3487
3488                 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], NULL, ".icc", "ICC Files", NULL);
3489                 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3490                 gtk_widget_set_size_request(entry, 160, -1);
3491                 gtk_table_attach(GTK_TABLE(table), tabcomp, 2, 3, i + 1, i + 2,
3492                                  GTK_FILL | GTK_EXPAND, 0, 0, 0);
3493                 gtk_widget_show(tabcomp);
3494                 color_profile_input_file_entry[i] = entry;
3495                 }
3496
3497         group =  pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3498 #ifndef HAVE_LCMS
3499         gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3500 #endif
3501         pref_checkbox_new_int(group, _("Use system screen profile if available"),
3502                               options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3503
3504         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3505
3506         pref_table_label(table, 0, 0, _("Screen:"), 1.0);
3507         tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3508                                      options->color_profile.screen_file, NULL, ".icc", "ICC Files", NULL);
3509         tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3510         gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3511 #ifdef HAVE_LCMS
3512         add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3513 #endif
3514         gtk_table_attach(GTK_TABLE(table), tabcomp, 1, 2,
3515                          0, 1,
3516                          GTK_FILL | GTK_EXPAND, 0, 0, 0);
3517
3518         gtk_widget_show(tabcomp);
3519 }
3520
3521 /* advanced entry tab */
3522 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer data)
3523 {
3524         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3525                 {
3526                 c_options->file_ops.use_system_trash = FALSE;
3527                 c_options->file_ops.no_trash = FALSE;
3528                 }
3529 }
3530
3531 static void use_system_trash_cb(GtkWidget *widget, gpointer data)
3532 {
3533         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3534                 {
3535                 c_options->file_ops.use_system_trash = TRUE;
3536                 c_options->file_ops.no_trash = FALSE;
3537                 }
3538 }
3539
3540 static void use_no_cache_cb(GtkWidget *widget, gpointer data)
3541 {
3542         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3543                 {
3544                 c_options->file_ops.no_trash = TRUE;
3545                 }
3546 }
3547
3548 static void config_tab_behavior(GtkWidget *notebook)
3549 {
3550         GtkWidget *hbox;
3551         GtkWidget *vbox;
3552         GtkWidget *group;
3553         GtkWidget *button;
3554         GtkWidget *tabcomp;
3555         GtkWidget *ct_button;
3556         GtkWidget *spin;
3557         GtkWidget *table;
3558         GtkWidget *marks;
3559         GtkWidget *with_rename;
3560         GtkWidget *collections_on_top;
3561         GtkWidget *hide_window_in_fullscreen;
3562         GtkWidget *checkbox;
3563         GtkWidget *tmp;
3564
3565         vbox = scrolled_notebook_page(notebook, _("Behavior"));
3566
3567         group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3568
3569         pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3570                               options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3571         pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3572                               options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3573         pref_checkbox_new_int(group, _("Enable Delete key"),
3574                               options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3575
3576         ct_button = pref_radiobutton_new(group, NULL, _("Use Geeqie trash location"),
3577                                         !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),NULL);
3578
3579         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3580         pref_checkbox_link_sensitivity(ct_button, hbox);
3581
3582         pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3583         pref_label_new(hbox, _("Folder:"));
3584
3585         tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, NULL, NULL, NULL, NULL);
3586         tab_completion_add_select_button(safe_delete_path_entry, NULL, TRUE);
3587         gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3588         gtk_widget_show(tabcomp);
3589
3590         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3591         pref_checkbox_link_sensitivity(ct_button, hbox);
3592
3593         pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3594         spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MiB"),
3595                                  0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3596         gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3597         button = pref_button_new(NULL, NULL, _("View"), FALSE,
3598                                  G_CALLBACK(safe_delete_view_cb), NULL);
3599         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3600         gtk_widget_show(button);
3601
3602         button = pref_button_new(NULL, GTK_STOCK_CLEAR, NULL, FALSE,
3603                                  G_CALLBACK(safe_delete_clear_cb), NULL);
3604         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3605
3606         c_options->file_ops.no_trash = options->file_ops.no_trash;
3607         c_options->file_ops.use_system_trash = options->file_ops.use_system_trash;
3608
3609         pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3610                                         options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), NULL);
3611
3612         pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3613                         options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), NULL);
3614
3615         gtk_widget_show(button);
3616
3617         pref_spacer(group, PREF_PAD_GROUP);
3618
3619
3620         group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3621
3622         pref_checkbox_new_int(group, _("Descend folders in tree view"),
3623                               options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3624
3625         pref_checkbox_new_int(group, _("In place renaming"),
3626                               options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3627
3628         pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3629                               options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3630
3631         tmp = pref_checkbox_new_int(group, _("Circular selection lists"),
3632                               options->circular_selection_lists, &c_options->circular_selection_lists);
3633         gtk_widget_set_tooltip_text(tmp, _("Traverse selection lists in a circular manner"));
3634
3635         marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3636                                 options->marks_save, &c_options->marks_save);
3637         gtk_widget_set_tooltip_text(marks,"Note that marks linked to a keyword will be saved irrespective of this setting");
3638
3639         with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3640                                 options->with_rename, &c_options->with_rename);
3641         gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3642
3643         collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3644                                 options->collections_on_top, &c_options->collections_on_top);
3645         gtk_widget_set_tooltip_text(collections_on_top,"Open collections window on top");
3646
3647         hide_window_in_fullscreen = pref_checkbox_new_int(group, _("Hide window in fullscreen"),
3648                                 options->hide_window_in_fullscreen, &c_options->hide_window_in_fullscreen);
3649         gtk_widget_set_tooltip_text(hide_window_in_fullscreen,"When alt-tabbing, prevent Geeqie window showing twice");
3650
3651         pref_spin_new_int(group, _("Recent folder list maximum size"), NULL,
3652                           1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3653
3654         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);
3655         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."));
3656
3657         pref_spin_new_int(group, _("Drag'n drop icon size"), NULL,
3658                           16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3659
3660         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3661         add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
3662
3663         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3664         add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3665
3666         pref_spacer(group, PREF_PAD_GROUP);
3667
3668         group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3669
3670         pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3671                               options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3672         pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), NULL,
3673                           1, 32, 1, options->keyboard_scroll_step, (int *)&c_options->keyboard_scroll_step);
3674         pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3675                               options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3676         pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3677                               options->image_lm_click_nav, &c_options->image_lm_click_nav);
3678         pref_checkbox_new_int(group, _("Open archive by left click on image"),
3679                               options->image_l_click_archive, &c_options->image_l_click_archive);
3680         pref_checkbox_new_int(group, _("Play video by left click on image"),
3681                               options->image_l_click_video, &c_options->image_l_click_video);
3682         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3683         add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3684
3685         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3686         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3687         add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3688         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3689         add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3690
3691         pref_spacer(group, PREF_PAD_GROUP);
3692
3693         group = pref_group_new(vbox, FALSE, _("GPU"), GTK_ORIENTATION_VERTICAL);
3694
3695         checkbox = pref_checkbox_new_int(group, _("Override disable GPU"),
3696                                 options->override_disable_gpu, &c_options->override_disable_gpu);
3697         gtk_widget_set_tooltip_text(checkbox, "Contact the developers for usage");
3698
3699 #ifdef DEBUG
3700         pref_spacer(group, PREF_PAD_GROUP);
3701
3702         group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3703
3704         pref_spin_new_int(group, _("Debug level:"), NULL,
3705                           DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3706
3707         pref_checkbox_new_int(group, _("Timer data"),
3708                         options->log_window.timer_data, &c_options->log_window.timer_data);
3709
3710         pref_spin_new_int(group, _("Log Window max. lines:"), NULL,
3711                           1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3712 #endif
3713 }
3714
3715 /* accelerators tab */
3716 static void config_tab_accelerators(GtkWidget *notebook)
3717 {
3718         GtkWidget *hbox;
3719         GtkWidget *vbox;
3720         GtkWidget *group;
3721         GtkWidget *button;
3722         GtkWidget *scrolled;
3723         GtkWidget *accel_view;
3724         GtkCellRenderer *renderer;
3725         GtkTreeSelection *selection;
3726         GtkTreeViewColumn *column;
3727
3728         vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3729
3730         group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3731
3732         scrolled = gtk_scrolled_window_new(NULL, NULL);
3733         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3734         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3735         gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3736         gtk_widget_show(scrolled);
3737
3738         accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3739
3740         accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3741         g_object_unref(accel_store);
3742         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3743         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3744
3745         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3746
3747         renderer = gtk_cell_renderer_text_new();
3748
3749         column = gtk_tree_view_column_new_with_attributes(_("Action"),
3750                                                           renderer,
3751                                                           "text", AE_ACTION,
3752                                                           NULL);
3753
3754         gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3755         gtk_tree_view_column_set_resizable(column, TRUE);
3756         gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3757
3758
3759         renderer = gtk_cell_renderer_accel_new();
3760         g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3761                          G_CALLBACK(accel_store_cleared_cb), accel_store);
3762         g_signal_connect(G_OBJECT(renderer), "accel-edited",
3763                          G_CALLBACK(accel_store_edited_cb), accel_store);
3764
3765
3766         g_object_set (renderer,
3767                       "editable", TRUE,
3768                       "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3769                       NULL);
3770
3771         column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3772                                                           renderer,
3773                                                           "text", AE_KEY,
3774                                                           NULL);
3775
3776         gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3777         gtk_tree_view_column_set_resizable(column, TRUE);
3778         gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3779
3780         renderer = gtk_cell_renderer_text_new();
3781
3782         column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3783                                                           renderer,
3784                                                           "text", AE_TOOLTIP,
3785                                                           NULL);
3786
3787         gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3788         gtk_tree_view_column_set_resizable(column, TRUE);
3789         gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3790
3791         renderer = gtk_cell_renderer_text_new();
3792
3793         column = gtk_tree_view_column_new_with_attributes("Accel",
3794                                                           renderer,
3795                                                           "text", AE_ACCEL,
3796                                                           NULL);
3797
3798         gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3799         gtk_tree_view_column_set_resizable(column, TRUE);
3800         gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3801
3802         accel_store_populate();
3803         gtk_container_add(GTK_CONTAINER(scrolled), accel_view);
3804         gtk_widget_show(accel_view);
3805
3806         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3807
3808         button = pref_button_new(NULL, NULL, _("Defaults"), FALSE,
3809                                  G_CALLBACK(accel_default_cb), accel_view);
3810         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3811         gtk_widget_show(button);
3812
3813         button = pref_button_new(NULL, NULL, _("Reset selected"), FALSE,
3814                                  G_CALLBACK(accel_reset_cb), accel_view);
3815         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3816         gtk_widget_show(button);
3817 }
3818
3819 /* toolbar main tab */
3820 static void config_tab_toolbar_main(GtkWidget *notebook)
3821 {
3822         GtkWidget *vbox;
3823         GtkWidget *toolbardata;
3824         LayoutWindow *lw;
3825
3826         lw = layout_window_list->data;
3827
3828         vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3829
3830         toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3831         gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3832         gtk_widget_show(vbox);
3833 }
3834
3835 /* toolbar status tab */
3836 static void config_tab_toolbar_status(GtkWidget *notebook)
3837 {
3838         GtkWidget *vbox;
3839         GtkWidget *toolbardata;
3840         LayoutWindow *lw;
3841
3842         lw = layout_window_list->data;
3843
3844         vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3845
3846         toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3847         gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3848         gtk_widget_show(vbox);
3849 }
3850
3851 /* advanced tab */
3852 static gint extension_sort_cb(gconstpointer a, gconstpointer b)
3853 {
3854         return g_strcmp0((gchar *)a, (gchar *)b);
3855 }
3856
3857 static void config_tab_advanced(GtkWidget *notebook)
3858 {
3859         GtkWidget *vbox;
3860         GtkWidget *group;
3861         GSList *formats_list;
3862         GList *extensions_list = NULL;
3863         gchar **extensions;
3864         GtkWidget *tabcomp;
3865         GdkPixbufFormat *fm;
3866         gint i;
3867         GString *types_string = g_string_new(NULL);
3868         GtkWidget *types_string_label;
3869         GtkWidget *threads_string_label;
3870         GtkWidget *dupes_threads_spin;
3871
3872         vbox = scrolled_notebook_page(notebook, _("Advanced"));
3873         group = pref_group_new(vbox, FALSE, _("External preview extraction"), GTK_ORIENTATION_VERTICAL);
3874
3875         pref_checkbox_new_int(group, _("Use external preview extraction -  Requires restart"), options->external_preview.enable, &c_options->external_preview.enable);
3876
3877         pref_spacer(group, PREF_PAD_GROUP);
3878
3879         formats_list = gdk_pixbuf_get_formats();
3880
3881         while (formats_list)
3882                 {
3883                 fm = formats_list->data;
3884                 extensions = gdk_pixbuf_format_get_extensions(fm);
3885
3886                 i = 0;
3887                 while (extensions[i])
3888                         {
3889                         extensions_list = g_list_insert_sorted(extensions_list, g_strdup(extensions[i]), extension_sort_cb);
3890                         i++;
3891                         }
3892
3893                 g_strfreev(extensions);
3894                 formats_list = formats_list->next;
3895                 }
3896
3897         while (extensions_list)
3898                 {
3899                 if (types_string->len == 0)
3900                         {
3901                         types_string = g_string_append(types_string, extensions_list->data);
3902                         }
3903                 else
3904                         {
3905                         types_string = g_string_append(types_string, ", ");
3906                         types_string = g_string_append(types_string, extensions_list->data);
3907                         }
3908
3909                 extensions_list = extensions_list->next;
3910                 }
3911
3912         types_string = g_string_prepend(types_string, _("Usable file types:\n"));
3913         types_string_label = pref_label_new(group, types_string->str);
3914         gtk_label_set_line_wrap(GTK_LABEL(types_string_label), TRUE);
3915
3916         pref_spacer(group, PREF_PAD_GROUP);
3917
3918         group = pref_group_new(vbox, FALSE, _("File identification tool"), GTK_ORIENTATION_VERTICAL);
3919         external_preview_select_entry = gtk_entry_new();
3920         tabcomp = tab_completion_new(&external_preview_select_entry, options->external_preview.select, NULL, NULL, NULL, NULL);
3921         tab_completion_add_select_button(external_preview_select_entry, _("Select file identification tool"), FALSE);
3922         gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3923         gtk_widget_show(tabcomp);
3924
3925         group = pref_group_new(vbox, FALSE, _("Preview extraction tool"), GTK_ORIENTATION_VERTICAL);
3926         external_preview_extract_entry = gtk_entry_new();
3927         tabcomp = tab_completion_new(&external_preview_extract_entry, options->external_preview.extract, NULL, NULL, NULL, NULL);
3928         tab_completion_add_select_button(external_preview_extract_entry, _("Select preview extraction tool"), FALSE);
3929         gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3930         gtk_widget_show(tabcomp);
3931
3932         gtk_widget_show(vbox);
3933
3934         g_slist_free(formats_list);
3935         string_list_free(extensions_list);
3936         g_string_free(types_string, TRUE);
3937
3938         pref_spacer(group, PREF_PAD_GROUP);
3939
3940         pref_line(vbox, PREF_PAD_SPACE);
3941         group = pref_group_new(vbox, FALSE, _("Thread pool limits"), GTK_ORIENTATION_VERTICAL);
3942
3943         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.");
3944         gtk_label_set_line_wrap(GTK_LABEL(threads_string_label), TRUE);
3945
3946         pref_spacer(vbox, PREF_PAD_GROUP);
3947
3948         dupes_threads_spin = pref_spin_new_int(vbox, _("Duplicate check:"), _("max. threads"), 0, get_cpu_cores(), 1, options->threads.duplicates, &c_options->threads.duplicates);
3949         gtk_widget_set_tooltip_markup(dupes_threads_spin, _("Set to 0 for unlimited"));
3950 }
3951
3952 /* stereo tab */
3953 static void config_tab_stereo(GtkWidget *notebook)
3954 {
3955         GtkWidget *vbox;
3956         GtkWidget *group;
3957         GtkWidget *group2;
3958         GtkWidget *table;
3959         GtkWidget *box;
3960         GtkWidget *box2;
3961         GtkWidget *fs_button;
3962         vbox = scrolled_notebook_page(notebook, _("Stereo"));
3963
3964         group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
3965
3966         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3967         add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
3968
3969         table = pref_table_new(group, 2, 2, TRUE, FALSE);
3970         box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, NULL);
3971         pref_checkbox_new_int(box, _("Mirror left image"),
3972                               options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
3973         box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, NULL);
3974         pref_checkbox_new_int(box, _("Flip left image"),
3975                               options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
3976         box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, NULL);
3977         pref_checkbox_new_int(box, _("Mirror right image"),
3978                               options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
3979         box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, NULL);
3980         pref_checkbox_new_int(box, _("Flip right image"),
3981                               options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
3982         pref_checkbox_new_int(group, _("Swap left and right images"),
3983                               options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
3984         pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
3985                               options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
3986
3987         group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
3988         fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
3989                               options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
3990         box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
3991         pref_checkbox_link_sensitivity(fs_button, box2);
3992         table = pref_table_new(box2, 2, 1, FALSE, FALSE);
3993         add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
3994         table = pref_table_new(box2, 2, 2, TRUE, FALSE);
3995         box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, NULL);
3996         pref_checkbox_new_int(box, _("Mirror left image"),
3997                               options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
3998         box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, NULL);
3999         pref_checkbox_new_int(box, _("Flip left image"),
4000                               options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
4001         box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, NULL);
4002         pref_checkbox_new_int(box, _("Mirror right image"),
4003                               options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
4004         box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, NULL);
4005         pref_checkbox_new_int(box, _("Flip right image"),
4006                               options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
4007         pref_checkbox_new_int(box2, _("Swap left and right images"),
4008                               options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
4009         pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
4010                               options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
4011
4012         group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
4013         table = pref_table_new(group2, 5, 3, FALSE, FALSE);
4014         pref_table_spin_new_int(table, 0, 0, _("Width"), NULL,
4015                           1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
4016         pref_table_spin_new_int(table, 3, 0,  _("Height"), NULL,
4017                           1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
4018         pref_table_spin_new_int(table, 0, 1,  _("Left X"), NULL,
4019                           0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
4020         pref_table_spin_new_int(table, 3, 1,  _("Left Y"), NULL,
4021                           0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
4022         pref_table_spin_new_int(table, 0, 2,  _("Right X"), NULL,
4023                           0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
4024         pref_table_spin_new_int(table, 3, 2,  _("Right Y"), NULL,
4025                           0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
4026
4027 }
4028
4029 /* Main preferences window */
4030 static void config_window_create(LayoutWindow *lw)
4031 {
4032         GtkWidget *win_vbox;
4033         GtkWidget *hbox;
4034         GtkWidget *notebook;
4035         GtkWidget *button;
4036         GtkWidget *ct_button;
4037
4038         if (!c_options) c_options = init_options(NULL);
4039
4040         configwindow = window_new(GTK_WINDOW_TOPLEVEL, "preferences", PIXBUF_INLINE_ICON_CONFIG, NULL, _("Preferences"));
4041         DEBUG_NAME(configwindow);
4042         gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
4043         g_signal_connect(G_OBJECT(configwindow), "delete_event",
4044                          G_CALLBACK(config_window_delete), NULL);
4045         if (options->save_dialog_window_positions)
4046                 {
4047                 gtk_window_resize(GTK_WINDOW(configwindow), lw->options.preferences_window.w, lw->options.preferences_window.h);
4048                 gtk_window_move(GTK_WINDOW(configwindow), lw->options.preferences_window.x, lw->options.preferences_window.y);
4049                 }
4050         else
4051                 {
4052                 gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
4053                 }
4054         gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
4055         gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
4056
4057         win_vbox = gtk_vbox_new(FALSE, PREF_PAD_SPACE);
4058         gtk_container_add(GTK_CONTAINER(configwindow), win_vbox);
4059         gtk_widget_show(win_vbox);
4060
4061         notebook = gtk_notebook_new();
4062         gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
4063         gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
4064         gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
4065
4066         config_tab_general(notebook);
4067         config_tab_image(notebook);
4068         config_tab_osd(notebook);
4069         config_tab_windows(notebook);
4070         config_tab_accelerators(notebook);
4071         config_tab_files(notebook);
4072         config_tab_metadata(notebook);
4073         config_tab_keywords(notebook);
4074         config_tab_color(notebook);
4075         config_tab_stereo(notebook);
4076         config_tab_behavior(notebook);
4077         config_tab_toolbar_main(notebook);
4078         config_tab_toolbar_status(notebook);
4079         config_tab_advanced(notebook);
4080
4081         gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), lw->options.preferences_window.page_number);
4082
4083         hbox = gtk_hbutton_box_new();
4084         gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
4085         gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
4086         gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
4087         gtk_widget_show(hbox);
4088
4089         button = pref_button_new(NULL, GTK_STOCK_HELP, NULL, FALSE,
4090                                  G_CALLBACK(config_window_help_cb), notebook);
4091         gtk_container_add(GTK_CONTAINER(hbox), button);
4092         gtk_widget_set_can_default(button, TRUE);
4093         gtk_widget_show(button);
4094
4095         button = pref_button_new(NULL, GTK_STOCK_OK, NULL, FALSE,
4096                                  G_CALLBACK(config_window_ok_cb), notebook);
4097         gtk_container_add(GTK_CONTAINER(hbox), button);
4098         gtk_widget_set_can_default(button, TRUE);
4099         gtk_widget_grab_default(button);
4100         gtk_widget_show(button);
4101
4102         ct_button = button;
4103
4104         button = pref_button_new(NULL, GTK_STOCK_CANCEL, NULL, FALSE,
4105                                  G_CALLBACK(config_window_close_cb), NULL);
4106         gtk_container_add(GTK_CONTAINER(hbox), button);
4107         gtk_widget_set_can_default(button, TRUE);
4108         gtk_widget_show(button);
4109
4110         if (!generic_dialog_get_alternative_button_order(configwindow))
4111                 {
4112                 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
4113                 }
4114
4115         gtk_widget_show(notebook);
4116
4117         gtk_widget_show(configwindow);
4118 }
4119
4120 /*
4121  *-----------------------------------------------------------------------------
4122  * config window show (public)
4123  *-----------------------------------------------------------------------------
4124  */
4125
4126 void show_config_window(LayoutWindow *lw)
4127 {
4128         if (configwindow)
4129                 {
4130                 gtk_window_present(GTK_WINDOW(configwindow));
4131                 return;
4132                 }
4133
4134         config_window_create(lw);
4135 }
4136
4137 /*
4138  *-----------------
4139  * about window
4140  *-----------------
4141  */
4142
4143 void show_about_window(LayoutWindow *lw)
4144 {
4145         GdkPixbuf *pixbuf_logo;
4146         GdkPixbuf *pixbuf_icon;
4147         gchar *authors[1000];
4148         gchar *comment;
4149         gint i_authors = 0;
4150         gchar *path;
4151         GString *copyright;
4152         gchar *timezone_path;
4153         ZoneDetect *cd;
4154         FILE *fp = NULL;
4155 #define LINE_LENGTH 1000
4156         gchar line[LINE_LENGTH];
4157
4158         copyright = g_string_new(NULL);
4159         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");
4160
4161         timezone_path = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
4162         if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
4163                 {
4164                 cd = ZDOpenDatabase(timezone_path);
4165                 if (cd)
4166                         {
4167                         copyright = g_string_append(copyright, ZDGetNotice(cd));
4168                         }
4169                 else
4170                         {
4171                         log_printf("Error: Init of timezone database %s failed\n", timezone_path);
4172                         }
4173                 ZDCloseDatabase(cd);
4174                 }
4175         g_free(timezone_path);
4176
4177         authors[0] = NULL;
4178         path = g_build_filename(gq_helpdir, "AUTHORS", NULL);
4179         fp = fopen(path, "r");
4180         if (fp)
4181                 {
4182                 while(fgets(line, LINE_LENGTH, fp))
4183                         {
4184                         /* get rid of ending \n from fgets */
4185                         line[strlen(line) - 1] = '\0';
4186                         authors[i_authors] = g_strdup(line);
4187                         i_authors++;
4188                         }
4189                 authors[i_authors] = NULL;
4190                 fclose(fp);
4191                 }
4192         g_free(path);
4193
4194         comment = g_strconcat("Development and bug reports:\n", GQ_EMAIL_ADDRESS,
4195                                                 "\nhttps://github.com/BestImageViewer/geeqie/issues",NULL);
4196
4197         pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
4198         pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
4199         gtk_show_about_dialog(GTK_WINDOW(lw->window),
4200                 "title", _("About Geeqie"),
4201                 "resizable", TRUE,
4202                 "program-name", GQ_APPNAME,
4203                 "version", VERSION,
4204                 "logo", pixbuf_logo,
4205                 "icon", pixbuf_icon,
4206                 "website", GQ_WEBSITE,
4207                 "website-label", "Website",
4208                 "comments", comment,
4209                 "authors", authors,
4210                 "translator-credits", _("translator-credits"),
4211                 "wrap-license", TRUE,
4212                 "license", copyright->str,
4213                 NULL);
4214
4215         g_string_free(copyright, TRUE);
4216
4217         gint n = 0;
4218         while(n < i_authors)
4219                 {
4220                 g_free(authors[n]);
4221                 n++;
4222                 }
4223         g_free(comment);
4224
4225         return;
4226 }
4227
4228 static void image_overlay_set_text_colours()
4229 {
4230         c_options->image_overlay.text_red = options->image_overlay.text_red;
4231         c_options->image_overlay.text_green = options->image_overlay.text_green;
4232         c_options->image_overlay.text_blue = options->image_overlay.text_blue;
4233         c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
4234         c_options->image_overlay.background_red = options->image_overlay.background_red;
4235         c_options->image_overlay.background_green = options->image_overlay.background_green;
4236         c_options->image_overlay.background_blue = options->image_overlay.background_blue;
4237         c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
4238 }
4239
4240 /*
4241  *-----------------------------------------------------------------------------
4242  * timezone database routines
4243  *-----------------------------------------------------------------------------
4244  */
4245
4246 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
4247 {
4248         GError *error = NULL;
4249         TZData *tz = data;
4250         gchar *tmp_filename;
4251         gchar *timezone_bin;
4252         gchar *tmp_dir = NULL;
4253         FileData *fd;
4254
4255         if (!g_cancellable_is_cancelled(tz->cancellable))
4256                 {
4257                 generic_dialog_close(tz->gd);
4258                 }
4259
4260
4261         if (g_file_copy_finish(G_FILE(source_object), res, &error))
4262                 {
4263                 tmp_filename = g_file_get_path(tz->tmp_g_file);
4264                 fd = file_data_new_simple(tmp_filename);
4265                 tmp_dir = open_archive(fd);
4266
4267                 if (tmp_dir)
4268                         {
4269                         timezone_bin = g_build_filename(tmp_dir, TIMEZONE_DATABASE_VERSION, TIMEZONE_DATABASE_FILE, NULL);
4270                         if (isfile(timezone_bin))
4271                                 {
4272                                 move_file(timezone_bin, tz->timezone_database_user);
4273                                 }
4274                         else
4275                                 {
4276                                 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GTK_STOCK_DIALOG_WARNING, NULL);
4277                                 }
4278
4279                         g_free(timezone_bin);
4280                         g_free(tmp_dir); // The folder in /tmp is deleted in exit_program_final()
4281                         }
4282                 else
4283                         {
4284                         warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GTK_STOCK_DIALOG_WARNING, NULL);
4285                         }
4286                 g_free(tmp_filename);
4287                 file_data_unref(fd);
4288                 }
4289         else
4290                 {
4291                 file_util_warning_dialog(_("Error: Timezone database download failed"), error->message, GTK_STOCK_DIALOG_ERROR, NULL);
4292                 }
4293
4294         g_file_delete(tz->tmp_g_file, NULL, &error);
4295         g_object_unref(tz->tmp_g_file);
4296         tz->tmp_g_file = NULL;
4297         g_object_unref(tz->cancellable);
4298         g_object_unref(tz->timezone_database_gq);
4299 }
4300
4301 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
4302 {
4303         TZData *tz = data;
4304
4305         if (!g_cancellable_is_cancelled(tz->cancellable))
4306                 {
4307                 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), (gdouble)current_num_bytes / total_num_bytes);
4308                 }
4309 }
4310
4311 static void timezone_cancel_button_cb(GenericDialog *gd, gpointer data)
4312 {
4313         TZData *tz = data;
4314
4315         g_cancellable_cancel(tz->cancellable);
4316 }
4317
4318 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
4319 {
4320         TZData *tz = data;
4321         GError *error = NULL;
4322         GFileIOStream *io_stream;
4323
4324         if (tz->tmp_g_file)
4325                 {
4326                 return;
4327                 }
4328
4329         tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
4330
4331         if (error)
4332                 {
4333                 file_util_warning_dialog(_("Timezone database download failed"), error->message, GTK_STOCK_DIALOG_ERROR, NULL);
4334                 log_printf("Error: Download timezone database failed:\n%s", error->message);
4335                 g_error_free(error);
4336                 g_object_unref(tz->tmp_g_file);
4337                 }
4338         else
4339                 {
4340                 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE_WEB);
4341
4342                 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", NULL, TRUE, timezone_cancel_button_cb, tz);
4343
4344                 generic_dialog_add_message(tz->gd, GTK_STOCK_DIALOG_INFO, _("Downloading timezone database"), NULL, FALSE);
4345
4346                 tz->progress = gtk_progress_bar_new();
4347                 gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
4348                 gtk_widget_show(tz->progress);
4349
4350                 gtk_widget_show(tz->gd->dialog);
4351                 tz->cancellable = g_cancellable_new();
4352                 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);
4353
4354                 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
4355                 }
4356 }
4357 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */