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