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