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