Fix #661, #662: String comparison to translated string causes crash
[geeqie.git] / src / preferences.c
1 /*
2  * Copyright (C) 2006 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "main.h"
23 #include "preferences.h"
24
25 #include "bar_exif.h"
26 #include "bar_keywords.h"
27 #include "cache.h"
28 #include "cache_maint.h"
29 #include "dnd.h"
30 #include "editors.h"
31 #include "exif.h"
32 #include "filedata.h"
33 #include "filefilter.h"
34 #include "fullscreen.h"
35 #include "image.h"
36 #include "image-overlay.h"
37 #include "color-man.h"
38 #include "img-view.h"
39 #include "layout_config.h"
40 #include "layout_util.h"
41 #include "metadata.h"
42 #include "osd.h"
43 #include "pixbuf_util.h"
44 #include "slideshow.h"
45 #include "toolbar.h"
46 #include "trash.h"
47 #include "utilops.h"
48 #include "ui_fileops.h"
49 #include "ui_misc.h"
50 #include "ui_spinner.h"
51 #include "ui_tabcomp.h"
52 #include "ui_utildlg.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 #define EDITOR_NAME_MAX_LENGTH 32
65 #define EDITOR_COMMAND_MAX_LENGTH 1024
66
67 static void image_overlay_set_text_colours();
68
69 GtkWidget *keyword_text;
70 static void config_tab_keywords_save();
71
72 typedef struct _ThumbSize ThumbSize;
73 struct _ThumbSize
74 {
75         gint w;
76         gint h;
77 };
78
79 static ThumbSize thumb_size_list[] =
80 {
81         { 24, 24 },
82         { 32, 32 },
83         { 48, 48 },
84         { 64, 64 },
85         { 96, 72 },
86         { 96, 96 },
87         { 128, 96 },
88         { 128, 128 },
89         { 160, 120 },
90         { 160, 160 },
91         { 192, 144 },
92         { 192, 192 },
93         { 256, 192 },
94         { 256, 256 }
95 };
96
97 enum {
98         FE_ENABLE,
99         FE_EXTENSION,
100         FE_DESCRIPTION,
101         FE_CLASS,
102         FE_WRITABLE,
103         FE_ALLOW_SIDECAR
104 };
105
106 enum {
107         AE_ACTION,
108         AE_KEY,
109         AE_TOOLTIP,
110         AE_ACCEL
111 };
112
113 gchar *format_class_list[] = {
114         N_("Unknown"),
115         N_("Image"),
116         N_("RAW Image"),
117         N_("Metadata"),
118         N_("Video"),
119         N_("Collection"),
120         N_("Pdf")
121         };
122
123 /* config memory values */
124 static ConfOptions *c_options = NULL;
125
126
127 #ifdef DEBUG
128 static gint debug_c;
129 #endif
130
131 static GtkWidget *configwindow = NULL;
132 static GtkListStore *filter_store = NULL;
133 static GtkTreeStore *accel_store = NULL;
134
135 static GtkWidget *safe_delete_path_entry;
136
137 static GtkWidget *color_profile_input_file_entry[COLOR_PROFILE_INPUTS];
138 static GtkWidget *color_profile_input_name_entry[COLOR_PROFILE_INPUTS];
139 static GtkWidget *color_profile_screen_file_entry;
140
141 static GtkWidget *sidecar_ext_entry;
142 static GtkWidget *help_search_engine_entry;
143
144
145 #define CONFIG_WINDOW_DEF_WIDTH         700
146 #define CONFIG_WINDOW_DEF_HEIGHT        600
147
148 /*
149  *-----------------------------------------------------------------------------
150  * option widget callbacks (private)
151  *-----------------------------------------------------------------------------
152  */
153
154 static void zoom_increment_cb(GtkWidget *spin, gpointer data)
155 {
156         c_options->image.zoom_increment = (gint)(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) * 100.0 + 0.01);
157 }
158
159 static void slideshow_delay_hours_cb(GtkWidget *spin, gpointer data)
160 {
161         gint mins_secs_tenths, delay;
162
163         mins_secs_tenths = c_options->slideshow.delay %
164                                                 (3600 * SLIDESHOW_SUBSECOND_PRECISION);
165
166         delay = (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
167                                                                 (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
168                                                                 mins_secs_tenths);
169
170         c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
171                                                                                                         SLIDESHOW_SUBSECOND_PRECISION;
172 }
173
174 static void slideshow_delay_minutes_cb(GtkWidget *spin, gpointer data)
175 {
176         gint hours, secs_tenths, delay;
177
178         hours = c_options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
179         secs_tenths = c_options->slideshow.delay % (60 * SLIDESHOW_SUBSECOND_PRECISION);
180
181         delay = hours * (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
182                                         (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
183                                         (60 * SLIDESHOW_SUBSECOND_PRECISION) + secs_tenths);
184
185         c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
186                                                                                                         SLIDESHOW_SUBSECOND_PRECISION;
187 }
188
189 static void slideshow_delay_seconds_cb(GtkWidget *spin, gpointer data)
190 {
191         gint hours_mins, delay;
192
193         hours_mins = c_options->slideshow.delay / (60 * SLIDESHOW_SUBSECOND_PRECISION);
194
195         delay = (hours_mins * (60 * SLIDESHOW_SUBSECOND_PRECISION)) +
196                                                         (gint)(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
197                                                         (gdouble)(SLIDESHOW_SUBSECOND_PRECISION) + 0.01);
198
199         c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
200                                                                                                         SLIDESHOW_SUBSECOND_PRECISION;
201 }
202
203 /*
204  *-----------------------------------------------------------------------------
205  * sync progam to config window routine (private)
206  *-----------------------------------------------------------------------------
207  */
208
209 void config_entry_to_option(GtkWidget *entry, gchar **option, gchar *(*func)(const gchar *))
210 {
211         const gchar *buf;
212
213         g_free(*option);
214         *option = NULL;
215         buf = gtk_entry_get_text(GTK_ENTRY(entry));
216         if (buf && strlen(buf) > 0)
217                 {
218                 if (func)
219                         *option = func(buf);
220                 else
221                         *option = g_strdup(buf);
222                 }
223 }
224
225
226 static gboolean accel_apply_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
227 {
228         gchar *accel_path, *accel;
229
230         gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, AE_KEY, &accel, -1);
231
232         if (accel_path && accel_path[0])
233                 {
234                 GtkAccelKey key;
235                 gtk_accelerator_parse(accel, &key.accel_key, &key.accel_mods);
236                 gtk_accel_map_change_entry(accel_path, key.accel_key, key.accel_mods, TRUE);
237                 }
238
239         g_free(accel_path);
240         g_free(accel);
241
242         return FALSE;
243 }
244
245
246 static void config_window_apply(void)
247 {
248         gint i;
249         gboolean refresh = FALSE;
250
251         config_entry_to_option(safe_delete_path_entry, &options->file_ops.safe_delete_path, remove_trailing_slash);
252
253         if (options->file_filter.show_hidden_files != c_options->file_filter.show_hidden_files) refresh = TRUE;
254         if (options->file_filter.show_parent_directory != c_options->file_filter.show_parent_directory) refresh = TRUE;
255         if (options->file_filter.show_dot_directory != c_options->file_filter.show_dot_directory) refresh = TRUE;
256         if (options->file_sort.case_sensitive != c_options->file_sort.case_sensitive) refresh = TRUE;
257         if (options->file_sort.natural != c_options->file_sort.natural) refresh = TRUE;
258         if (options->file_filter.disable_file_extension_checks != c_options->file_filter.disable_file_extension_checks) refresh = TRUE;
259         if (options->file_filter.disable != c_options->file_filter.disable) refresh = TRUE;
260
261         options->file_ops.confirm_delete = c_options->file_ops.confirm_delete;
262         options->file_ops.enable_delete_key = c_options->file_ops.enable_delete_key;
263         options->file_ops.confirm_move_to_trash = c_options->file_ops.confirm_move_to_trash;
264         options->file_ops.use_system_trash = c_options->file_ops.use_system_trash;
265         options->file_ops.safe_delete_folder_maxsize = c_options->file_ops.safe_delete_folder_maxsize;
266         options->tools_restore_state = c_options->tools_restore_state;
267         options->save_window_positions = c_options->save_window_positions;
268         options->use_saved_window_positions_for_new_windows = c_options->use_saved_window_positions_for_new_windows;
269         options->save_dialog_window_positions = c_options->save_dialog_window_positions;
270         options->show_window_ids = c_options->show_window_ids;
271         options->image.scroll_reset_method = c_options->image.scroll_reset_method;
272         options->image.zoom_2pass = c_options->image.zoom_2pass;
273         options->image.fit_window_to_image = c_options->image.fit_window_to_image;
274         options->image.limit_window_size = c_options->image.limit_window_size;
275         options->image.zoom_to_fit_allow_expand = c_options->image.zoom_to_fit_allow_expand;
276         options->image.max_window_size = c_options->image.max_window_size;
277         options->image.limit_autofit_size = c_options->image.limit_autofit_size;
278         options->image.max_autofit_size = c_options->image.max_autofit_size;
279         options->image.max_enlargement_size = c_options->image.max_enlargement_size;
280         options->image.use_clutter_renderer = c_options->image.use_clutter_renderer;
281         options->progressive_key_scrolling = c_options->progressive_key_scrolling;
282         options->keyboard_scroll_step = c_options->keyboard_scroll_step;
283
284         if (options->thumbnails.max_width != c_options->thumbnails.max_width
285             || options->thumbnails.max_height != c_options->thumbnails.max_height
286             || options->thumbnails.quality != c_options->thumbnails.quality)
287                 {
288                 thumb_format_changed = TRUE;
289                 refresh = TRUE;
290                 options->thumbnails.max_width = c_options->thumbnails.max_width;
291                 options->thumbnails.max_height = c_options->thumbnails.max_height;
292                 options->thumbnails.quality = c_options->thumbnails.quality;
293                 }
294         options->thumbnails.enable_caching = c_options->thumbnails.enable_caching;
295         options->thumbnails.cache_into_dirs = c_options->thumbnails.cache_into_dirs;
296         options->thumbnails.use_exif = c_options->thumbnails.use_exif;
297         options->thumbnails.collection_preview = c_options->thumbnails.collection_preview;
298         options->thumbnails.use_ft_metadata = c_options->thumbnails.use_ft_metadata;
299 //      options->thumbnails.use_ft_metadata_small = c_options->thumbnails.use_ft_metadata_small;
300         options->thumbnails.spec_standard = c_options->thumbnails.spec_standard;
301         options->metadata.enable_metadata_dirs = c_options->metadata.enable_metadata_dirs;
302         options->file_filter.show_hidden_files = c_options->file_filter.show_hidden_files;
303         options->file_filter.show_parent_directory = c_options->file_filter.show_parent_directory;
304         options->file_filter.show_dot_directory = c_options->file_filter.show_dot_directory;
305         options->file_filter.disable_file_extension_checks = c_options->file_filter.disable_file_extension_checks;
306
307         options->file_sort.case_sensitive = c_options->file_sort.case_sensitive;
308         options->file_sort.natural = c_options->file_sort.natural;
309         options->file_filter.disable = c_options->file_filter.disable;
310
311         config_entry_to_option(sidecar_ext_entry, &options->sidecar.ext, NULL);
312         sidecar_ext_parse(options->sidecar.ext);
313
314         options->slideshow.random = c_options->slideshow.random;
315         options->slideshow.repeat = c_options->slideshow.repeat;
316         options->slideshow.delay = c_options->slideshow.delay;
317
318         options->mousewheel_scrolls = c_options->mousewheel_scrolls;
319         options->image_lm_click_nav = c_options->image_lm_click_nav;
320         options->image_l_click_video = c_options->image_l_click_video;
321         options->image_l_click_video_editor = c_options->image_l_click_video_editor;
322
323         options->file_ops.enable_in_place_rename = c_options->file_ops.enable_in_place_rename;
324
325         options->image.tile_cache_max = c_options->image.tile_cache_max;
326         options->image.image_cache_max = c_options->image.image_cache_max;
327
328         options->image.zoom_quality = c_options->image.zoom_quality;
329
330         options->image.zoom_increment = c_options->image.zoom_increment;
331
332         options->image.enable_read_ahead = c_options->image.enable_read_ahead;
333
334
335         if (options->image.use_custom_border_color != c_options->image.use_custom_border_color
336             || options->image.use_custom_border_color_in_fullscreen != c_options->image.use_custom_border_color_in_fullscreen
337             || !gdk_color_equal(&options->image.border_color, &c_options->image.border_color))
338                 {
339                 options->image.use_custom_border_color_in_fullscreen = c_options->image.use_custom_border_color_in_fullscreen;
340                 options->image.use_custom_border_color = c_options->image.use_custom_border_color;
341                 options->image.border_color = c_options->image.border_color;
342                 layout_colors_update();
343                 view_window_colors_update();
344                 }
345
346         options->image.alpha_color_1 = c_options->image.alpha_color_1;
347         options->image.alpha_color_2 = c_options->image.alpha_color_2;
348
349         options->fullscreen.screen = c_options->fullscreen.screen;
350         options->fullscreen.clean_flip = c_options->fullscreen.clean_flip;
351         options->fullscreen.disable_saver = c_options->fullscreen.disable_saver;
352         options->fullscreen.above = c_options->fullscreen.above;
353         if (c_options->image_overlay.template_string)
354                 set_image_overlay_template_string(&options->image_overlay.template_string,
355                                                   c_options->image_overlay.template_string);
356         if (c_options->image_overlay.font)
357                 set_image_overlay_font_string(&options->image_overlay.font,
358                                                   c_options->image_overlay.font);
359         options->image_overlay.text_red = c_options->image_overlay.text_red;
360         options->image_overlay.text_green = c_options->image_overlay.text_green;
361         options->image_overlay.text_blue = c_options->image_overlay.text_blue;
362         options->image_overlay.text_alpha = c_options->image_overlay.text_alpha;
363         options->image_overlay.background_red = c_options->image_overlay.background_red;
364         options->image_overlay.background_green = c_options->image_overlay.background_green;
365         options->image_overlay.background_blue = c_options->image_overlay.background_blue;
366         options->image_overlay.background_alpha = c_options->image_overlay.background_alpha;
367         options->update_on_time_change = c_options->update_on_time_change;
368         options->image.exif_proof_rotate_enable = c_options->image.exif_proof_rotate_enable;
369
370         options->duplicates_similarity_threshold = c_options->duplicates_similarity_threshold;
371         options->rot_invariant_sim = c_options->rot_invariant_sim;
372
373         options->tree_descend_subdirs = c_options->tree_descend_subdirs;
374
375         options->view_dir_list_single_click_enter = c_options->view_dir_list_single_click_enter;
376
377         options->open_recent_list_maxsize = c_options->open_recent_list_maxsize;
378         options->dnd_icon_size = c_options->dnd_icon_size;
379         options->clipboard_selection = c_options->clipboard_selection;
380
381         options->metadata.save_in_image_file = c_options->metadata.save_in_image_file;
382         options->metadata.save_legacy_IPTC = c_options->metadata.save_legacy_IPTC;
383         options->metadata.warn_on_write_problems = c_options->metadata.warn_on_write_problems;
384         options->metadata.save_legacy_format = c_options->metadata.save_legacy_format;
385         options->metadata.sync_grouped_files = c_options->metadata.sync_grouped_files;
386         options->metadata.confirm_write = c_options->metadata.confirm_write;
387         options->metadata.sidecar_extended_name = c_options->metadata.sidecar_extended_name;
388         options->metadata.confirm_timeout = c_options->metadata.confirm_timeout;
389         options->metadata.confirm_after_timeout = c_options->metadata.confirm_after_timeout;
390         options->metadata.confirm_on_image_change = c_options->metadata.confirm_on_image_change;
391         options->metadata.confirm_on_dir_change = c_options->metadata.confirm_on_dir_change;
392         options->metadata.keywords_case_sensitive = c_options->metadata.keywords_case_sensitive;
393         options->metadata.write_orientation = c_options->metadata.write_orientation;
394         options->stereo.mode = (c_options->stereo.mode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
395                                (c_options->stereo.tmp.mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
396                                (c_options->stereo.tmp.flip_right   ? PR_STEREO_FLIP_RIGHT : 0) |
397                                (c_options->stereo.tmp.mirror_left  ? PR_STEREO_MIRROR_LEFT : 0) |
398                                (c_options->stereo.tmp.flip_left    ? PR_STEREO_FLIP_LEFT : 0) |
399                                (c_options->stereo.tmp.swap         ? PR_STEREO_SWAP : 0) |
400                                (c_options->stereo.tmp.temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
401         options->stereo.fsmode = (c_options->stereo.fsmode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
402                                (c_options->stereo.tmp.fs_mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
403                                (c_options->stereo.tmp.fs_flip_right   ? PR_STEREO_FLIP_RIGHT : 0) |
404                                (c_options->stereo.tmp.fs_mirror_left  ? PR_STEREO_MIRROR_LEFT : 0) |
405                                (c_options->stereo.tmp.fs_flip_left    ? PR_STEREO_FLIP_LEFT : 0) |
406                                (c_options->stereo.tmp.fs_swap         ? PR_STEREO_SWAP : 0) |
407                                (c_options->stereo.tmp.fs_temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
408         options->stereo.enable_fsmode = c_options->stereo.enable_fsmode;
409         options->stereo.fixed_w = c_options->stereo.fixed_w;
410         options->stereo.fixed_h = c_options->stereo.fixed_h;
411         options->stereo.fixed_x1 = c_options->stereo.fixed_x1;
412         options->stereo.fixed_y1 = c_options->stereo.fixed_y1;
413         options->stereo.fixed_x2 = c_options->stereo.fixed_x2;
414         options->stereo.fixed_y2 = c_options->stereo.fixed_y2;
415
416         options->info_keywords.height = c_options->info_keywords.height;
417         options->info_title.height = c_options->info_title.height;
418         options->info_comment.height = c_options->info_comment.height;
419         options->info_rating.height = c_options->info_rating.height;
420
421         options->show_predefined_keyword_tree = c_options->show_predefined_keyword_tree;
422
423         options->marks_save = c_options->marks_save;
424         options->with_rename = c_options->with_rename;
425         options->collections_on_top = c_options->collections_on_top;
426         config_entry_to_option(help_search_engine_entry, &options->help_search_engine, NULL);
427
428         options->read_metadata_in_idle = c_options->read_metadata_in_idle;
429
430         options->star_rating.star = c_options->star_rating.star;
431         options->star_rating.rejected = c_options->star_rating.rejected;
432 #ifdef DEBUG
433         set_debug_level(debug_c);
434 #endif
435
436 #ifdef HAVE_LCMS
437         for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
438                 {
439                 config_entry_to_option(color_profile_input_name_entry[i], &options->color_profile.input_name[i], NULL);
440                 config_entry_to_option(color_profile_input_file_entry[i], &options->color_profile.input_file[i], NULL);
441                 }
442         config_entry_to_option(color_profile_screen_file_entry, &options->color_profile.screen_file, NULL);
443         options->color_profile.use_x11_screen_profile = c_options->color_profile.use_x11_screen_profile;
444         if (options->color_profile.render_intent != c_options->color_profile.render_intent)
445                 {
446                 options->color_profile.render_intent = c_options->color_profile.render_intent;
447                 color_man_update();
448                 }
449 #endif
450
451         config_tab_keywords_save();
452
453         image_options_sync();
454
455         if (refresh)
456                 {
457                 filter_rebuild();
458                 layout_refresh(NULL);
459                 }
460
461         if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, NULL);
462
463         toolbar_apply();
464 }
465
466 /*
467  *-----------------------------------------------------------------------------
468  * config window main button callbacks (private)
469  *-----------------------------------------------------------------------------
470  */
471
472 static void config_window_close_cb(GtkWidget *widget, gpointer data)
473 {
474         gtk_widget_destroy(configwindow);
475         configwindow = NULL;
476         filter_store = NULL;
477 }
478
479 static void config_window_help_cb(GtkWidget *widget, gpointer data)
480 {
481         GtkWidget *notebook = data;
482         gint i;
483
484         static gchar *html_section[] =
485         {
486         "GuideOptionsGeneral.html",
487         "GuideOptionsImage.html",
488         "GuideOptionsOSD.html",
489         "GuideOptionsWindow.html",
490         "GuideOptionsKeyboard.html",
491         "GuideOptionsFiltering.html",
492         "GuideOptionsMetadata.html",
493         "GuideOptionsKeywords.html",
494         "GuideOptionsColor.html",
495         "GuideOptionsStereo.html",
496         "GuideOptionsBehavior.html",
497         "GuideOptionsToolbar.html"
498         };
499
500         i = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
501         help_window_show(html_section[i]);
502 }
503
504 static gboolean config_window_delete(GtkWidget *widget, GdkEventAny *event, gpointer data)
505 {
506         config_window_close_cb(NULL, NULL);
507         return TRUE;
508 }
509
510 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
511 {
512         config_window_apply();
513         config_window_close_cb(NULL, NULL);
514 }
515
516 static void config_window_apply_cb(GtkWidget *widget, gpointer data)
517 {
518         LayoutWindow *lw;
519         lw = layout_window_list->data;
520
521         config_window_apply();
522         layout_util_sync(lw);
523 }
524
525 static void config_window_save_cb(GtkWidget *widget, gpointer data)
526 {
527         config_window_apply();
528         save_options(options);
529 }
530
531 /*
532  *-----------------------------------------------------------------------------
533  * config window setup (private)
534  *-----------------------------------------------------------------------------
535  */
536
537 static void quality_menu_cb(GtkWidget *combo, gpointer data)
538 {
539         gint *option = data;
540
541         switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
542                 {
543                 case 0:
544                 default:
545                         *option = GDK_INTERP_NEAREST;
546                         break;
547                 case 1:
548                         *option = GDK_INTERP_TILES;
549                         break;
550                 case 2:
551                         *option = GDK_INTERP_BILINEAR;
552                         break;
553                 case 3:
554                         *option = GDK_INTERP_HYPER;
555                         break;
556                 }
557 }
558
559 static void clipboard_selection_menu_cb(GtkWidget *combo, gpointer data)
560 {
561         gint *option = data;
562
563         switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
564                 {
565                 case 0:
566                 default:
567                         *option = PRIMARY;
568                         break;
569                 case 1:
570                         *option = CLIPBOARD;
571                         break;
572                 }
573 }
574
575 static void add_quality_menu(GtkWidget *table, gint column, gint row, const gchar *text,
576                              guint option, guint *option_c)
577 {
578         GtkWidget *combo;
579         gint current = 0;
580
581         *option_c = option;
582
583         pref_table_label(table, column, row, text, 0.0);
584
585         combo = gtk_combo_box_text_new();
586
587         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Nearest (worst, but fastest)"));
588         if (option == GDK_INTERP_NEAREST) current = 0;
589         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Tiles"));
590         if (option == GDK_INTERP_TILES) current = 1;
591         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Bilinear"));
592         if (option == GDK_INTERP_BILINEAR) current = 2;
593         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Hyper (best, but slowest)"));
594         if (option == GDK_INTERP_HYPER) current = 3;
595
596         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
597
598         g_signal_connect(G_OBJECT(combo), "changed",
599                          G_CALLBACK(quality_menu_cb), option_c);
600
601         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
602                          GTK_EXPAND | GTK_FILL, 0, 0, 0);
603         gtk_widget_show(combo);
604 }
605
606 static void add_clipboard_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
607                              gint option, gint *option_c)
608 {
609         GtkWidget *combo;
610         gint current = 0;
611
612         *option_c = option;
613
614         pref_table_label(table, column, row, text, 0.0);
615
616         combo = gtk_combo_box_text_new();
617
618         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("PRIMARY"));
619         if (option == PRIMARY) current = 0;
620         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("CLIPBOARD"));
621         if (option == CLIPBOARD) current = 1;
622
623         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
624
625         g_signal_connect(G_OBJECT(combo), "changed",
626                          G_CALLBACK(clipboard_selection_menu_cb), option_c);
627
628         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
629                          GTK_EXPAND | GTK_FILL, 0, 0, 0);
630         gtk_widget_show(combo);
631 }
632
633 static void thumb_size_menu_cb(GtkWidget *combo, gpointer data)
634 {
635         gint n;
636
637         n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
638         if (n < 0) return;
639
640         if ((guint) n < sizeof(thumb_size_list) / sizeof(ThumbSize))
641                 {
642                 c_options->thumbnails.max_width = thumb_size_list[n].w;
643                 c_options->thumbnails.max_height = thumb_size_list[n].h;
644                 }
645         else
646                 {
647                 c_options->thumbnails.max_width = options->thumbnails.max_width;
648                 c_options->thumbnails.max_height = options->thumbnails.max_height;
649                 }
650 }
651
652 static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *text)
653 {
654         GtkWidget *combo;
655         gint current;
656         gint i;
657
658         c_options->thumbnails.max_width = options->thumbnails.max_width;
659         c_options->thumbnails.max_height = options->thumbnails.max_height;
660
661         pref_table_label(table, column, row, text, 0.0);
662
663         combo = gtk_combo_box_text_new();
664
665         current = -1;
666         for (i = 0; (guint) i < sizeof(thumb_size_list) / sizeof(ThumbSize); i++)
667                 {
668                 gint w, h;
669                 gchar *buf;
670
671                 w = thumb_size_list[i].w;
672                 h = thumb_size_list[i].h;
673
674                 buf = g_strdup_printf("%d x %d", w, h);
675                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
676                 g_free(buf);
677
678                 if (w == options->thumbnails.max_width && h == options->thumbnails.max_height) current = i;
679                 }
680
681         if (current == -1)
682                 {
683                 gchar *buf;
684
685                 buf = g_strdup_printf("%s %d x %d", _("Custom"), options->thumbnails.max_width, options->thumbnails.max_height);
686                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
687                 g_free(buf);
688
689                 current = i;
690                 }
691
692         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
693         g_signal_connect(G_OBJECT(combo), "changed",
694                          G_CALLBACK(thumb_size_menu_cb), NULL);
695
696         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
697                          GTK_EXPAND | GTK_FILL, 0, 0, 0);
698         gtk_widget_show(combo);
699 }
700
701 static void stereo_mode_menu_cb(GtkWidget *combo, gpointer data)
702 {
703         gint *option = data;
704
705         switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
706                 {
707                 case 0:
708                 default:
709                         *option = PR_STEREO_NONE;
710                         break;
711                 case 1:
712                         *option = PR_STEREO_ANAGLYPH_RC;
713                         break;
714                 case 2:
715                         *option = PR_STEREO_ANAGLYPH_GM;
716                         break;
717                 case 3:
718                         *option = PR_STEREO_ANAGLYPH_YB;
719                         break;
720                 case 4:
721                         *option = PR_STEREO_ANAGLYPH_GRAY_RC;
722                         break;
723                 case 5:
724                         *option = PR_STEREO_ANAGLYPH_GRAY_GM;
725                         break;
726                 case 6:
727                         *option = PR_STEREO_ANAGLYPH_GRAY_YB;
728                         break;
729                 case 7:
730                         *option = PR_STEREO_ANAGLYPH_DB_RC;
731                         break;
732                 case 8:
733                         *option = PR_STEREO_ANAGLYPH_DB_GM;
734                         break;
735                 case 9:
736                         *option = PR_STEREO_ANAGLYPH_DB_YB;
737                         break;
738                 case 10:
739                         *option = PR_STEREO_HORIZ;
740                         break;
741                 case 11:
742                         *option = PR_STEREO_HORIZ | PR_STEREO_HALF;
743                         break;
744                 case 12:
745                         *option = PR_STEREO_VERT;
746                         break;
747                 case 13:
748                         *option = PR_STEREO_VERT | PR_STEREO_HALF;
749                         break;
750                 case 14:
751                         *option = PR_STEREO_FIXED;
752                         break;
753                 }
754 }
755
756 static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const gchar *text,
757                              gint option, gint *option_c, gboolean add_fixed)
758 {
759         GtkWidget *combo;
760         gint current = 0;
761
762         *option_c = option;
763
764         pref_table_label(table, column, row, text, 0.0);
765
766         combo = gtk_combo_box_text_new();
767
768         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Single image"));
769
770         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Red-Cyan"));
771         if (option & PR_STEREO_ANAGLYPH_RC) current = 1;
772         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Green-Magenta"));
773         if (option & PR_STEREO_ANAGLYPH_GM) current = 2;
774         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Yellow-Blue"));
775         if (option & PR_STEREO_ANAGLYPH_YB) current = 3;
776         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Red-Cyan"));
777         if (option & PR_STEREO_ANAGLYPH_GRAY_RC) current = 4;
778         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Green-Magenta"));
779         if (option & PR_STEREO_ANAGLYPH_GRAY_GM) current = 5;
780         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Yellow-Blue"));
781         if (option & PR_STEREO_ANAGLYPH_GRAY_YB) current = 6;
782         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Red-Cyan"));
783         if (option & PR_STEREO_ANAGLYPH_DB_RC) current = 7;
784         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Green-Magenta"));
785         if (option & PR_STEREO_ANAGLYPH_DB_GM) current = 8;
786         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Yellow-Blue"));
787         if (option & PR_STEREO_ANAGLYPH_DB_YB) current = 9;
788
789         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side"));
790         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side Half size"));
791         if (option & PR_STEREO_HORIZ)
792                 {
793                 current = 10;
794                 if (option & PR_STEREO_HALF) current = 11;
795                 }
796
797         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom"));
798         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom Half size"));
799         if (option & PR_STEREO_VERT)
800                 {
801                 current = 12;
802                 if (option & PR_STEREO_HALF) current = 13;
803                 }
804
805         if (add_fixed)
806                 {
807                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Fixed position"));
808                 if (option & PR_STEREO_FIXED) current = 14;
809                 }
810
811         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
812
813         g_signal_connect(G_OBJECT(combo), "changed",
814                          G_CALLBACK(stereo_mode_menu_cb), option_c);
815
816         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
817                          GTK_EXPAND | GTK_FILL, 0, 0, 0);
818         gtk_widget_show(combo);
819 }
820
821 static void video_menu_cb(GtkWidget *combo, gpointer data)
822 {
823         gchar **option = data;
824
825         EditorDescription *ed = g_list_nth_data(editor_list_get(), gtk_combo_box_get_active(GTK_COMBO_BOX(combo)));
826         *option = ed->key;
827 }
828
829 static void video_menu_populate(gpointer data, gpointer user_data)
830 {
831         GtkWidget *combo = user_data;
832         EditorDescription *ed = data;
833
834         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), ed->name);
835 }
836
837 static void add_video_menu(GtkWidget *table, gint column, gint row, const gchar *text,
838                              gchar *option, gchar **option_c)
839 {
840         GtkWidget *combo;
841         gint current;
842 /* use lists since they are sorted */
843         GList *eds = editor_list_get();
844
845         *option_c = option;
846
847         pref_table_label(table, column, row, text, 0.0);
848
849         combo = gtk_combo_box_text_new();
850         g_list_foreach(eds,video_menu_populate,(gpointer)combo);
851         current = option ? g_list_index(eds,g_hash_table_lookup(editors,option)): -1;
852
853         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
854
855         g_signal_connect(G_OBJECT(combo), "changed",
856                          G_CALLBACK(video_menu_cb), option_c);
857
858         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
859                          GTK_EXPAND | GTK_FILL, 0, 0, 0);
860         gtk_widget_show(combo);
861 }
862
863 static void filter_store_populate(void)
864 {
865         GList *work;
866
867         if (!filter_store) return;
868
869         gtk_list_store_clear(filter_store);
870
871         work = filter_get_list();
872         while (work)
873                 {
874                 FilterEntry *fe;
875                 GtkTreeIter iter;
876
877                 fe = work->data;
878                 work = work->next;
879
880                 gtk_list_store_append(filter_store, &iter);
881                 gtk_list_store_set(filter_store, &iter, 0, fe, -1);
882                 }
883 }
884
885 static void filter_store_ext_edit_cb(GtkCellRendererText *cell, gchar *path_str,
886                                      gchar *new_text, gpointer data)
887 {
888         GtkWidget *model = data;
889         FilterEntry *fe = data;
890         GtkTreePath *tpath;
891         GtkTreeIter iter;
892
893         if (!new_text || strlen(new_text) < 1) return;
894
895         tpath = gtk_tree_path_new_from_string(path_str);
896         gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
897         gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
898
899         g_free(fe->extensions);
900         fe->extensions = g_strdup(new_text);
901
902         gtk_tree_path_free(tpath);
903         filter_rebuild();
904 }
905
906 static void filter_store_class_edit_cb(GtkCellRendererText *cell, gchar *path_str,
907                                        gchar *new_text, gpointer data)
908 {
909         GtkWidget *model = data;
910         FilterEntry *fe = data;
911         GtkTreePath *tpath;
912         GtkTreeIter iter;
913         gint i;
914
915         if (!new_text || !new_text[0]) return;
916
917         tpath = gtk_tree_path_new_from_string(path_str);
918         gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
919         gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
920
921         for (i = 0; i < FILE_FORMAT_CLASSES; i++)
922                 {
923                 if (strcmp(new_text, _(format_class_list[i])) == 0)
924                         {
925                         fe->file_class = i;
926                         break;
927                         }
928                 }
929
930         gtk_tree_path_free(tpath);
931         filter_rebuild();
932 }
933
934 static void filter_store_desc_edit_cb(GtkCellRendererText *cell, gchar *path_str,
935                                       gchar *new_text, gpointer data)
936 {
937         GtkWidget *model = data;
938         FilterEntry *fe;
939         GtkTreePath *tpath;
940         GtkTreeIter iter;
941
942         if (!new_text || !new_text[0]) return;
943
944         tpath = gtk_tree_path_new_from_string(path_str);
945         gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
946         gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
947
948         g_free(fe->description);
949         fe->description = g_strdup(new_text);
950
951         gtk_tree_path_free(tpath);
952 }
953
954 static void filter_store_enable_cb(GtkCellRendererToggle *renderer,
955                                    gchar *path_str, gpointer data)
956 {
957         GtkWidget *model = data;
958         FilterEntry *fe;
959         GtkTreePath *tpath;
960         GtkTreeIter iter;
961
962         tpath = gtk_tree_path_new_from_string(path_str);
963         gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
964         gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
965
966         fe->enabled = !fe->enabled;
967
968         gtk_tree_path_free(tpath);
969         filter_rebuild();
970 }
971
972 static void filter_store_writable_cb(GtkCellRendererToggle *renderer,
973                                      gchar *path_str, gpointer data)
974 {
975         GtkWidget *model = data;
976         FilterEntry *fe;
977         GtkTreePath *tpath;
978         GtkTreeIter iter;
979
980         tpath = gtk_tree_path_new_from_string(path_str);
981         gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
982         gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
983
984         fe->writable = !fe->writable;
985         if (fe->writable) fe->allow_sidecar = FALSE;
986
987         gtk_tree_path_free(tpath);
988         filter_rebuild();
989 }
990
991 static void filter_store_sidecar_cb(GtkCellRendererToggle *renderer,
992                                     gchar *path_str, gpointer data)
993 {
994         GtkWidget *model = data;
995         FilterEntry *fe;
996         GtkTreePath *tpath;
997         GtkTreeIter iter;
998
999         tpath = gtk_tree_path_new_from_string(path_str);
1000         gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1001         gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1002
1003         fe->allow_sidecar = !fe->allow_sidecar;
1004         if (fe->allow_sidecar) fe->writable = FALSE;
1005
1006         gtk_tree_path_free(tpath);
1007         filter_rebuild();
1008 }
1009
1010 static void filter_set_func(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1011                             GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1012 {
1013         FilterEntry *fe;
1014
1015         gtk_tree_model_get(tree_model, iter, 0, &fe, -1);
1016
1017         switch (GPOINTER_TO_INT(data))
1018                 {
1019                 case FE_ENABLE:
1020                         g_object_set(GTK_CELL_RENDERER(cell),
1021                                      "active", fe->enabled, NULL);
1022                         break;
1023                 case FE_EXTENSION:
1024                         g_object_set(GTK_CELL_RENDERER(cell),
1025                                      "text", fe->extensions, NULL);
1026                         break;
1027                 case FE_DESCRIPTION:
1028                         g_object_set(GTK_CELL_RENDERER(cell),
1029                                      "text", fe->description, NULL);
1030                         break;
1031                 case FE_CLASS:
1032                         g_object_set(GTK_CELL_RENDERER(cell),
1033                                      "text", _(format_class_list[fe->file_class]), NULL);
1034                         break;
1035                 case FE_WRITABLE:
1036                         g_object_set(GTK_CELL_RENDERER(cell),
1037                                      "active", fe->writable, NULL);
1038                         break;
1039                 case FE_ALLOW_SIDECAR:
1040                         g_object_set(GTK_CELL_RENDERER(cell),
1041                                      "active", fe->allow_sidecar, NULL);
1042                         break;
1043                 }
1044 }
1045
1046 static gboolean filter_add_scroll(gpointer data)
1047 {
1048         GtkTreePath *path;
1049         GList *list_cells;
1050         GtkCellRenderer *cell;
1051         GtkTreeViewColumn *column;
1052         const gchar *title;
1053         guint i = 0;
1054         gint rows;
1055
1056         rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(filter_store), NULL);
1057         path = gtk_tree_path_new_from_indices(rows-1, -1);
1058
1059         column = gtk_tree_view_get_column(GTK_TREE_VIEW(data), 0);
1060
1061         list_cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1062         cell = g_list_last(list_cells)->data;
1063
1064         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1065                                                                 path, column, FALSE, 0.0, 0.0 );
1066         gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(data),
1067                                                                 path, column, cell, TRUE);
1068
1069         gtk_tree_path_free(path);
1070         g_list_free(list_cells);
1071
1072         return(FALSE);
1073 }
1074
1075 static void filter_add_cb(GtkWidget *widget, gpointer data)
1076 {
1077         filter_add_unique("description", ".new", FORMAT_CLASS_IMAGE, TRUE, FALSE, TRUE);
1078         filter_store_populate();
1079
1080         g_idle_add((GSourceFunc)filter_add_scroll, data);
1081 }
1082
1083 static void filter_remove_cb(GtkWidget *widget, gpointer data)
1084 {
1085         GtkWidget *filter_view = data;
1086         GtkTreeSelection *selection;
1087         GtkTreeIter iter;
1088         FilterEntry *fe;
1089
1090         if (!filter_store) return;
1091         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
1092         if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) return;
1093         gtk_tree_model_get(GTK_TREE_MODEL(filter_store), &iter, 0, &fe, -1);
1094         if (!fe) return;
1095
1096         filter_remove_entry(fe);
1097         filter_rebuild();
1098         filter_store_populate();
1099 }
1100
1101 static gboolean filter_default_ok_scroll(GtkTreeView *data)
1102 {
1103         GtkTreeIter iter;
1104         GtkTreePath *path;
1105         GtkTreeViewColumn *column;
1106
1107         gtk_tree_model_get_iter_first(GTK_TREE_MODEL(filter_store), &iter);
1108         path = gtk_tree_model_get_path(GTK_TREE_MODEL(filter_store), &iter);
1109         column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1110
1111         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1112                                      path, column,
1113                                      FALSE, 0.0, 0.0);
1114
1115         gtk_tree_path_free(path);
1116
1117         return(FALSE);
1118 }
1119
1120 static void filter_default_ok_cb(GenericDialog *gd, gpointer data)
1121 {
1122         filter_reset();
1123         filter_add_defaults();
1124         filter_rebuild();
1125         filter_store_populate();
1126
1127         g_idle_add((GSourceFunc)filter_default_ok_scroll, gd->data);
1128 }
1129
1130 static void dummy_cancel_cb(GenericDialog *gd, gpointer data)
1131 {
1132         /* no op, only so cancel button appears */
1133 }
1134
1135 static void filter_default_cb(GtkWidget *widget, gpointer data)
1136 {
1137         GenericDialog *gd;
1138
1139         gd = generic_dialog_new(_("Reset filters"),
1140                                 "reset_filter", widget, TRUE,
1141                                 dummy_cancel_cb, data);
1142         generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION, _("Reset filters"),
1143                                    _("This will reset the file filters to the defaults.\nContinue?"), TRUE);
1144         generic_dialog_add_button(gd, GTK_STOCK_OK, NULL, filter_default_ok_cb, TRUE);
1145         gtk_widget_show(gd->dialog);
1146 }
1147
1148 static void filter_disable_cb(GtkWidget *widget, gpointer data)
1149 {
1150         GtkWidget *frame = data;
1151
1152         gtk_widget_set_sensitive(frame,
1153                                  !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1154 }
1155
1156 static void safe_delete_view_cb(GtkWidget *widget, gpointer data)
1157 {
1158         layout_set_path(NULL, gtk_entry_get_text(GTK_ENTRY(safe_delete_path_entry)));
1159 }
1160
1161 static void safe_delete_clear_ok_cb(GenericDialog *gd, gpointer data)
1162 {
1163         file_util_trash_clear();
1164 }
1165
1166 static void safe_delete_clear_cb(GtkWidget *widget, gpointer data)
1167 {
1168         GenericDialog *gd;
1169         GtkWidget *entry;
1170         gd = generic_dialog_new(_("Clear trash"),
1171                                 "clear_trash", widget, TRUE,
1172                                 dummy_cancel_cb, NULL);
1173         generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION, _("Clear trash"),
1174                                     _("This will remove the trash contents."), FALSE);
1175         generic_dialog_add_button(gd, GTK_STOCK_OK, NULL, safe_delete_clear_ok_cb, TRUE);
1176         entry = gtk_entry_new();
1177         gtk_widget_set_can_focus(entry, FALSE);
1178         gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
1179         if (options->file_ops.safe_delete_path) gtk_entry_set_text(GTK_ENTRY(entry), options->file_ops.safe_delete_path);
1180         gtk_box_pack_start(GTK_BOX(gd->vbox), entry, FALSE, FALSE, 0);
1181         gtk_widget_show(entry);
1182         gtk_widget_show(gd->dialog);
1183 }
1184
1185 static void image_overlay_template_view_changed_cb(GtkWidget *widget, gpointer data)
1186 {
1187         GtkWidget *pTextView;
1188         GtkTextBuffer *pTextBuffer;
1189         GtkTextIter iStart;
1190         GtkTextIter iEnd;
1191
1192         pTextView = GTK_WIDGET(data);
1193
1194         pTextBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pTextView));
1195         gtk_text_buffer_get_start_iter(pTextBuffer, &iStart);
1196         gtk_text_buffer_get_end_iter(pTextBuffer, &iEnd);
1197
1198         set_image_overlay_template_string(&c_options->image_overlay.template_string,
1199                                           gtk_text_buffer_get_text(pTextBuffer, &iStart, &iEnd, TRUE));
1200 }
1201
1202 static void image_overlay_default_template_ok_cb(GenericDialog *gd, gpointer data)
1203 {
1204         GtkTextView *text_view = data;
1205         GtkTextBuffer *buffer;
1206
1207         set_default_image_overlay_template_string(&options->image_overlay.template_string);
1208         if (!configwindow) return;
1209
1210         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
1211         gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
1212 }
1213
1214 static void image_overlay_default_template_cb(GtkWidget *widget, gpointer data)
1215 {
1216         GenericDialog *gd;
1217
1218         gd = generic_dialog_new(_("Reset image overlay template string"),
1219                                 "reset_image_overlay_template_string", widget, TRUE,
1220                                 dummy_cancel_cb, data);
1221         generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION, _("Reset image overlay template string"),
1222                                    _("This will reset the image overlay template string to the default.\nContinue?"), TRUE);
1223         generic_dialog_add_button(gd, GTK_STOCK_OK, NULL, image_overlay_default_template_ok_cb, TRUE);
1224         gtk_widget_show(gd->dialog);
1225 }
1226
1227 static void image_overlay_help_cb(GtkWidget *widget, gpointer data)
1228 {
1229         help_window_show("GuideOptionsOSD.html");
1230 }
1231
1232 static void image_overlay_set_font_cb(GtkWidget *widget, gpointer data)
1233 {
1234 #if GTK_CHECK_VERSION(3,4,0)
1235         GtkWidget *dialog;
1236         char *font;
1237         PangoFontDescription *font_desc;
1238
1239         dialog = gtk_font_chooser_dialog_new("Image Overlay Font", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1240         gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), options->image_overlay.font);
1241
1242         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1243                 {
1244                 font_desc = gtk_font_chooser_get_font_desc(GTK_FONT_CHOOSER(dialog));
1245                 font = pango_font_description_to_string(font_desc);
1246                 g_free(c_options->image_overlay.font);
1247                 c_options->image_overlay.font = g_strdup(font);
1248                 g_free(font);
1249                 }
1250
1251         gtk_widget_destroy(dialog);
1252 #else
1253         const char *font;
1254
1255         font = gtk_font_button_get_font_name(GTK_FONT_BUTTON(widget));
1256         c_options->image_overlay.font = g_strdup(font);
1257 #endif
1258 }
1259
1260 static void image_overlay_set_text_colour_cb(GtkWidget *widget, gpointer data)
1261 {
1262         GtkWidget *dialog;
1263 #if GTK_CHECK_VERSION(3,4,0)
1264         GdkRGBA colour;
1265
1266         dialog = gtk_color_chooser_dialog_new("Image Overlay Text Colour", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1267         colour.red = options->image_overlay.text_red;
1268         colour.green = options->image_overlay.text_green;
1269         colour.blue = options->image_overlay.text_blue;
1270         colour.alpha = options->image_overlay.text_alpha;
1271         gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1272         gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1273
1274         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1275                 {
1276                 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1277                 c_options->image_overlay.text_red = colour.red*255;
1278                 c_options->image_overlay.text_green = colour.green*255;
1279                 c_options->image_overlay.text_blue = colour.blue*255;
1280                 c_options->image_overlay.text_alpha = colour.alpha*255;
1281                 }
1282         gtk_widget_destroy(dialog);
1283 #else
1284         GdkColor colour;
1285         GtkColorSelection *colorsel;
1286
1287         dialog = gtk_color_selection_dialog_new("Image Overlay Text Colour");
1288         gtk_window_set_keep_above(GTK_WINDOW(dialog),TRUE);
1289         colour.red = options->image_overlay.text_red*257;
1290         colour.green = options->image_overlay.text_green*257;
1291         colour.blue = options->image_overlay.text_blue*257;
1292         colorsel = GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(dialog)));
1293         gtk_color_selection_set_has_opacity_control(colorsel, TRUE);
1294         gtk_color_selection_set_current_color(colorsel, &colour);
1295         gtk_color_selection_set_current_alpha(colorsel, options->image_overlay.text_alpha*257);
1296
1297         if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
1298                 {
1299                 gtk_color_selection_get_current_color(colorsel, &colour);
1300                 c_options->image_overlay.text_red = colour.red/257;
1301                 c_options->image_overlay.text_green = colour.green/257;
1302                 c_options->image_overlay.text_blue = colour.blue/257;
1303                 c_options->image_overlay.text_alpha = gtk_color_selection_get_current_alpha(colorsel)/257;
1304                 }
1305         gtk_widget_destroy (dialog);
1306 #endif
1307 }
1308
1309
1310 static void image_overlay_set_background_colour_cb(GtkWidget *widget, gpointer data)
1311 {
1312         GtkWidget *dialog;
1313 #if GTK_CHECK_VERSION(3,4,0)
1314         GdkRGBA colour;
1315
1316         dialog = gtk_color_chooser_dialog_new("Image Overlay Background Colour", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1317         colour.red = options->image_overlay.background_red;
1318         colour.green = options->image_overlay.background_green;
1319         colour.blue = options->image_overlay.background_blue;
1320         colour.alpha = options->image_overlay.background_alpha;
1321         gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1322         gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1323
1324         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1325                 {
1326                 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1327                 c_options->image_overlay.background_red = colour.red*255;
1328                 c_options->image_overlay.background_green = colour.green*255;
1329                 c_options->image_overlay.background_blue = colour.blue*255;
1330                 c_options->image_overlay.background_alpha = colour.alpha*255;
1331                 }
1332         gtk_widget_destroy(dialog);
1333 #else
1334         GdkColor colour;
1335         GtkColorSelection *colorsel;
1336
1337         dialog = gtk_color_selection_dialog_new("Image Overlay Background Colour");
1338         gtk_window_set_keep_above(GTK_WINDOW(dialog),TRUE);
1339         colour.red = options->image_overlay.background_red*257;
1340         colour.green = options->image_overlay.background_green*257;
1341         colour.blue = options->image_overlay.background_blue*257;
1342         colorsel = GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(dialog)));
1343         gtk_color_selection_set_has_opacity_control(colorsel, TRUE);
1344         gtk_color_selection_set_current_color(colorsel, &colour);
1345         gtk_color_selection_set_current_alpha(colorsel, options->image_overlay.background_alpha*257);
1346
1347         if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
1348                 {
1349                 gtk_color_selection_get_current_color(colorsel, &colour);
1350                 c_options->image_overlay.background_red = colour.red/257;
1351                 c_options->image_overlay.background_green = colour.green/257;
1352                 c_options->image_overlay.background_blue = colour.blue/257;
1353                 c_options->image_overlay.background_alpha = gtk_color_selection_get_current_alpha(colorsel)/257;
1354                 }
1355         gtk_widget_destroy(dialog);
1356 #endif
1357 }
1358
1359 static void accel_store_populate(void)
1360 {
1361         LayoutWindow *lw;
1362         GList *groups, *actions;
1363         GtkAction *action;
1364         const gchar *accel_path;
1365         GtkAccelKey key;
1366         GtkTreeIter iter;
1367
1368         if (!accel_store || !layout_window_list || !layout_window_list->data) return;
1369
1370         gtk_tree_store_clear(accel_store);
1371         lw = layout_window_list->data; /* get the actions from the first window, it should not matter, they should be the same in all windows */
1372
1373         g_assert(lw && lw->ui_manager);
1374         groups = gtk_ui_manager_get_action_groups(lw->ui_manager);
1375         while (groups)
1376                 {
1377                 actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
1378                 while (actions)
1379                         {
1380                         action = GTK_ACTION(actions->data);
1381                         accel_path = gtk_action_get_accel_path(action);
1382                         if (accel_path && gtk_accel_map_lookup_entry(accel_path, &key))
1383                                 {
1384                                 gchar *label, *label2, *tooltip, *accel;
1385                                 g_object_get(action,
1386                                              "tooltip", &tooltip,
1387                                              "label", &label,
1388                                              NULL);
1389
1390                                 if (pango_parse_markup(label, -1, '_', NULL, &label2, NULL, NULL) && label2)
1391                                         {
1392                                         g_free(label);
1393                                         label = label2;
1394                                         }
1395
1396                                 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1397
1398                                 if (tooltip)
1399                                         {
1400                                         gtk_tree_store_append(accel_store, &iter, NULL);
1401                                         gtk_tree_store_set(accel_store, &iter,
1402                                                            AE_ACTION, label,
1403                                                            AE_KEY, accel,
1404                                                            AE_TOOLTIP, tooltip ? tooltip : "",
1405                                                            AE_ACCEL, accel_path,
1406                                                            -1);
1407                                         }
1408
1409                                 g_free(accel);
1410                                 g_free(label);
1411                                 g_free(tooltip);
1412                                 }
1413                         actions = actions->next;
1414                         }
1415
1416                 groups = groups->next;
1417                 }
1418 }
1419
1420 static void accel_store_cleared_cb(GtkCellRendererAccel *accel, gchar *path_string, gpointer user_data)
1421 {
1422
1423 }
1424
1425 static gboolean accel_remove_key_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
1426 {
1427         gchar *accel1 = data;
1428         gchar *accel2;
1429         GtkAccelKey key1;
1430         GtkAccelKey key2;
1431
1432         gtk_tree_model_get(model, iter, AE_KEY, &accel2, -1);
1433
1434         gtk_accelerator_parse(accel1, &key1.accel_key, &key1.accel_mods);
1435         gtk_accelerator_parse(accel2, &key2.accel_key, &key2.accel_mods);
1436
1437         if (key1.accel_key == key2.accel_key && key1.accel_mods == key2.accel_mods)
1438                 {
1439                 gtk_tree_store_set(accel_store, iter, AE_KEY, "",  -1);
1440                 DEBUG_1("accelerator key '%s' is already used, removing.", accel1);
1441                 }
1442
1443         g_free(accel2);
1444
1445         return FALSE;
1446 }
1447
1448
1449 static void accel_store_edited_cb(GtkCellRendererAccel *accel, gchar *path_string, guint accel_key, GdkModifierType accel_mods, guint hardware_keycode, gpointer user_data)
1450 {
1451         GtkTreeModel *model = (GtkTreeModel *)accel_store;
1452         GtkTreeIter iter;
1453         gchar *acc;
1454         gchar *accel_path;
1455         GtkAccelKey old_key, key;
1456         GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1457
1458         gtk_tree_model_get_iter(model, &iter, path);
1459         gtk_tree_model_get(model, &iter, AE_ACCEL, &accel_path, -1);
1460
1461         /* test if the accelerator can be stored without conflicts*/
1462         gtk_accel_map_lookup_entry(accel_path, &old_key);
1463
1464         /* change the key and read it back (change may fail on keys hardcoded in gtk)*/
1465         gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, TRUE);
1466         gtk_accel_map_lookup_entry(accel_path, &key);
1467
1468         /* restore the original for now, the key will be really changed when the changes are confirmed */
1469         gtk_accel_map_change_entry(accel_path, old_key.accel_key, old_key.accel_mods, TRUE);
1470
1471         acc = gtk_accelerator_name(key.accel_key, key.accel_mods);
1472         gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, acc);
1473
1474         gtk_tree_store_set(accel_store, &iter, AE_KEY, acc, -1);
1475         gtk_tree_path_free(path);
1476         g_free(acc);
1477 }
1478
1479 static gboolean accel_default_scroll(GtkTreeView *data)
1480 {
1481         GtkTreeIter iter;
1482         GtkTreePath *path;
1483         GtkTreeViewColumn *column;
1484
1485         gtk_tree_model_get_iter_first(GTK_TREE_MODEL(accel_store), &iter);
1486         path = gtk_tree_model_get_path(GTK_TREE_MODEL(accel_store), &iter);
1487         column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1488
1489         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1490                                      path, column,
1491                                      FALSE, 0.0, 0.0);
1492
1493         gtk_tree_path_free(path);
1494
1495         return(FALSE);
1496 }
1497
1498 static void accel_default_cb(GtkWidget *widget, gpointer data)
1499 {
1500         accel_store_populate();
1501
1502         g_idle_add((GSourceFunc)accel_default_scroll, data);
1503 }
1504
1505 void accel_remove_selection(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
1506 {
1507         gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1508 }
1509
1510 void accel_reset_selection(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
1511 {
1512         GtkAccelKey key;
1513         gchar *accel_path, *accel;
1514
1515         gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, -1);
1516         gtk_accel_map_lookup_entry(accel_path, &key);
1517         accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1518
1519         gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, accel);
1520
1521         gtk_tree_store_set(accel_store, iter, AE_KEY, accel, -1);
1522         g_free(accel_path);
1523         g_free(accel);
1524 }
1525
1526 static void accel_reset_cb(GtkWidget *widget, gpointer data)
1527 {
1528         GtkTreeSelection *selection;
1529
1530         if (!accel_store) return;
1531         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1532         gtk_tree_selection_selected_foreach(selection, &accel_reset_selection, NULL);
1533 }
1534
1535
1536
1537 static GtkWidget *scrolled_notebook_page(GtkWidget *notebook, const gchar *title)
1538 {
1539         GtkWidget *label;
1540         GtkWidget *vbox;
1541         GtkWidget *scrolled;
1542         GtkWidget *viewport;
1543
1544         scrolled = gtk_scrolled_window_new(NULL, NULL);
1545         gtk_container_set_border_width(GTK_CONTAINER(scrolled), PREF_PAD_BORDER);
1546         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1547                                        GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1548         label = gtk_label_new(title);
1549         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, label);
1550         gtk_widget_show(scrolled);
1551
1552         viewport = gtk_viewport_new(NULL, NULL);
1553         gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1554         gtk_container_add(GTK_CONTAINER(scrolled), viewport);
1555         gtk_widget_show(viewport);
1556
1557         vbox = gtk_vbox_new(FALSE, 0);
1558         gtk_container_add(GTK_CONTAINER(viewport), vbox);
1559         gtk_widget_show(vbox);
1560
1561         return vbox;
1562 }
1563
1564 static void cache_standard_cb(GtkWidget *widget, gpointer data)
1565 {
1566         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1567                 {
1568                 c_options->thumbnails.spec_standard =TRUE;
1569                 c_options->thumbnails.cache_into_dirs = FALSE;
1570                 }
1571 }
1572
1573 static void cache_geeqie_cb(GtkWidget *widget, gpointer data)
1574 {
1575         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1576                 {
1577                 c_options->thumbnails.spec_standard =FALSE;
1578                 c_options->thumbnails.cache_into_dirs = FALSE;
1579                 }
1580 }
1581
1582 static void cache_local_cb(GtkWidget *widget, gpointer data)
1583 {
1584         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1585                 {
1586                 c_options->thumbnails.cache_into_dirs = TRUE;
1587                 c_options->thumbnails.spec_standard =FALSE;
1588                 }
1589 }
1590
1591 static void help_search_engine_entry_icon_cb(GtkEntry *entry, GtkEntryIconPosition pos,
1592                                                                         GdkEvent *event, gpointer userdata)
1593 {
1594         if (pos == GTK_ENTRY_ICON_PRIMARY)
1595                 {
1596                 gtk_entry_set_text(GTK_ENTRY(userdata), HELP_SEARCH_ENGINE);
1597                 }
1598         else
1599                 {
1600                 gtk_entry_set_text(GTK_ENTRY(userdata), "");
1601                 }
1602 }
1603
1604 static void star_rating_star_icon_cb(GtkEntry *entry, GtkEntryIconPosition pos,
1605                                                                         GdkEvent *event, gpointer userdata)
1606 {
1607         gchar *rating_symbol;
1608
1609         if (pos == GTK_ENTRY_ICON_PRIMARY)
1610                 {
1611                 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_STAR);
1612                 gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1613                 g_free(rating_symbol);
1614                 }
1615         else
1616                 {
1617                 gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1618                 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1619                 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1620                 }
1621 }
1622
1623 static void star_rating_rejected_icon_cb(GtkEntry *entry, GtkEntryIconPosition pos,
1624                                                                         GdkEvent *event, gpointer userdata)
1625 {
1626         gchar *rating_symbol;
1627
1628         if (pos == GTK_ENTRY_ICON_PRIMARY)
1629                 {
1630                 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_REJECTED);
1631                 gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1632                 g_free(rating_symbol);
1633                 }
1634         else
1635                 {
1636                 gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1637                 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1638                 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1639                 }
1640 }
1641
1642 static guint star_rating_symbol_test(GtkWidget *widget, gpointer data)
1643 {
1644         GtkContainer *hbox = data;
1645         GString *str = g_string_new(NULL);
1646         GtkEntry *hex_code_entry;
1647         gchar *hex_code_full;
1648         gchar **hex_code;
1649         GList *list;
1650         guint64 hex_value = 0;
1651
1652         list = gtk_container_get_children(hbox);
1653
1654         hex_code_entry = g_list_nth_data(list, 2);
1655         hex_code_full = g_strdup(gtk_entry_get_text(hex_code_entry));
1656
1657         hex_code = g_strsplit(hex_code_full, "+", 2);
1658         if (hex_code[0] && hex_code[1])
1659                 {
1660                 hex_value = strtoull(hex_code[1], NULL, 16);
1661                 }
1662         if (!hex_value || hex_value > 0x10FFFF)
1663                 {
1664                 hex_value = 0x003F; // Unicode 'Question Mark'
1665                 }
1666         str = g_string_append_unichar(str, (gunichar)hex_value);
1667         gtk_label_set_text(g_list_nth_data(list, 1), str->str);
1668
1669         g_strfreev(hex_code);
1670         g_string_free(str, TRUE);
1671         g_free(hex_code_full);
1672
1673         return hex_value;
1674 }
1675
1676 static void star_rating_star_test_cb(GtkWidget *widget, gpointer data)
1677 {
1678         guint64 star_symbol;
1679
1680         star_symbol = star_rating_symbol_test(widget, data);
1681         c_options->star_rating.star = star_symbol;
1682 }
1683
1684 static void star_rating_rejected_test_cb(GtkWidget *widget, gpointer data)
1685 {
1686         guint64 rejected_symbol;
1687
1688         rejected_symbol = star_rating_symbol_test(widget, data);
1689         c_options->star_rating.rejected = rejected_symbol;
1690 }
1691
1692 /* general options tab */
1693 static void config_tab_general(GtkWidget *notebook)
1694 {
1695         GtkWidget *vbox;
1696         GtkWidget *hbox;
1697         GtkWidget *group;
1698         GtkWidget *group_frame;
1699         GtkWidget *subgroup;
1700         GtkWidget *button;
1701         GtkWidget *ct_button;
1702         GtkWidget *table;
1703         GtkWidget *spin;
1704         gint hours, minutes, remainder;
1705         gdouble seconds;
1706         GtkWidget *star_rating_entry;
1707         GString *str;
1708         gchar *rating_symbol;
1709
1710         vbox = scrolled_notebook_page(notebook, _("General"));
1711
1712         group = pref_group_new(vbox, FALSE, _("Thumbnails"), GTK_ORIENTATION_VERTICAL);
1713
1714         table = pref_table_new(group, 2, 2, FALSE, FALSE);
1715         add_thumb_size_menu(table, 0, 0, _("Size:"));
1716         add_quality_menu(table, 0, 1, _("Quality:"), options->thumbnails.quality, &c_options->thumbnails.quality);
1717
1718         ct_button = pref_checkbox_new_int(group, _("Cache thumbnails"),
1719                                           options->thumbnails.enable_caching, &c_options->thumbnails.enable_caching);
1720
1721         subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1722         pref_checkbox_link_sensitivity(ct_button, subgroup);
1723
1724         c_options->thumbnails.spec_standard = options->thumbnails.spec_standard;
1725         c_options->thumbnails.cache_into_dirs = options->thumbnails.cache_into_dirs;
1726         group_frame = pref_frame_new(subgroup, TRUE, _("Use Geeqie thumbnail style and cache"),
1727                                                                                 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1728         button = pref_radiobutton_new(group_frame, NULL,  get_thumbnails_cache_dir(),
1729                                                         !options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
1730                                                         G_CALLBACK(cache_geeqie_cb), NULL);
1731
1732         group_frame = pref_frame_new(subgroup, TRUE,
1733                                                         _("Store thumbnails local to image folder (non-standard)"),
1734                                                         GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1735         pref_radiobutton_new(group_frame, button, "*/.thumbnails",
1736                                                         !options->thumbnails.spec_standard && options->thumbnails.cache_into_dirs,
1737                                                         G_CALLBACK(cache_local_cb), NULL);
1738
1739         group_frame = pref_frame_new(subgroup, TRUE,
1740                                                         _("Use standard thumbnail style and cache, shared with other applications"),
1741                                                         GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1742         pref_radiobutton_new(group_frame, button, get_thumbnails_standard_cache_dir(),
1743                                                         options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
1744                                                         G_CALLBACK(cache_standard_cb), NULL);
1745
1746         pref_checkbox_new_int(group, _("Use EXIF thumbnails when available (EXIF thumbnails may be outdated)"),
1747                               options->thumbnails.use_exif, &c_options->thumbnails.use_exif);
1748
1749         spin = pref_spin_new_int(group, _("Collection preview:"), NULL,
1750                                  1, 999, 1,
1751                                  options->thumbnails.collection_preview, &c_options->thumbnails.collection_preview);
1752         gtk_widget_set_tooltip_text(spin, _("The maximum number of thumbnails shown in a Collection preview montage"));
1753
1754 #ifdef HAVE_FFMPEGTHUMBNAILER_METADATA
1755         pref_checkbox_new_int(group, _("Use embedded metadata in video files as thumbnails when available"),
1756                               options->thumbnails.use_ft_metadata, &c_options->thumbnails.use_ft_metadata);
1757
1758 //      pref_checkbox_new_int(group, _("Ignore embedded metadata if size is too small"),
1759 //                            options->thumbnails.use_ft_metadata_small, &c_options->thumbnails.use_ft_metadata_small);
1760 #endif
1761
1762         group = pref_group_new(vbox, FALSE, _("Star Rating"), GTK_ORIENTATION_VERTICAL);
1763
1764         c_options->star_rating.star = options->star_rating.star;
1765         c_options->star_rating.rejected = options->star_rating.rejected;
1766
1767         str = g_string_new(NULL);
1768         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1769         pref_label_new(hbox, "Star character: ");
1770         str = g_string_append_unichar(str, options->star_rating.star);
1771         pref_label_new(hbox, g_strdup(str->str));
1772         rating_symbol = g_strdup_printf("U+%X", options->star_rating.star);
1773         star_rating_entry = gtk_entry_new();
1774         gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
1775         gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
1776         gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
1777         gtk_widget_show(star_rating_entry);
1778         button = pref_button_new(NULL, NULL, _("Set"), FALSE,
1779                                         G_CALLBACK(star_rating_star_test_cb), hbox);
1780         gtk_widget_set_tooltip_text(button, _("Display selected character"));
1781         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
1782         gtk_widget_show(button);
1783         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."));
1784         gtk_entry_set_icon_from_stock(GTK_ENTRY(star_rating_entry),
1785                                                 GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
1786         gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
1787                                                 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
1788         gtk_entry_set_icon_from_stock(GTK_ENTRY(star_rating_entry),
1789                                                 GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_REVERT_TO_SAVED);
1790         gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
1791                                                 GTK_ENTRY_ICON_PRIMARY, _("Default"));
1792         g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
1793                                                 G_CALLBACK(star_rating_star_icon_cb),
1794                                                 star_rating_entry);
1795
1796         g_string_free(str, TRUE);
1797         g_free(rating_symbol);
1798
1799         str = g_string_new(NULL);
1800         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1801         pref_label_new(hbox, "Rejected character: ");
1802         str = g_string_append_unichar(str, options->star_rating.rejected);
1803         pref_label_new(hbox, g_strdup(str->str));
1804         rating_symbol = g_strdup_printf("U+%X", options->star_rating.rejected);
1805         star_rating_entry = gtk_entry_new();
1806         gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
1807         gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
1808         gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
1809         gtk_widget_show(star_rating_entry);
1810         button = pref_button_new(NULL, NULL, _("Set"), FALSE,
1811                                         G_CALLBACK(star_rating_rejected_test_cb), hbox);
1812         gtk_widget_set_tooltip_text(button, _("Display selected character"));
1813         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
1814         gtk_widget_show(button);
1815         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."));
1816         gtk_entry_set_icon_from_stock(GTK_ENTRY(star_rating_entry),
1817                                                 GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
1818         gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
1819                                                 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
1820         gtk_entry_set_icon_from_stock(GTK_ENTRY(star_rating_entry),
1821                                                 GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_REVERT_TO_SAVED);
1822         gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
1823                                                 GTK_ENTRY_ICON_PRIMARY, _("Default"));
1824         g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
1825                                                 G_CALLBACK(star_rating_rejected_icon_cb),
1826                                                 star_rating_entry);
1827
1828         g_string_free(str, TRUE);
1829         g_free(rating_symbol);
1830
1831         group = pref_group_new(vbox, FALSE, _("Slide show"), GTK_ORIENTATION_VERTICAL);
1832
1833         c_options->slideshow.delay = options->slideshow.delay;
1834         hours = options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
1835         remainder = options->slideshow.delay % (3600 * SLIDESHOW_SUBSECOND_PRECISION);
1836         minutes = remainder / (60 * SLIDESHOW_SUBSECOND_PRECISION);
1837         seconds = (gdouble)(remainder % (60 * SLIDESHOW_SUBSECOND_PRECISION)) /
1838                                                                                         SLIDESHOW_SUBSECOND_PRECISION;
1839
1840         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1841
1842         spin = pref_spin_new(hbox, _("Delay between image change hrs:mins:secs.dec"), NULL,
1843                                                                                 0, 23, 1.0, 0,
1844                                                                                 options->slideshow.delay ? hours : 0.0,
1845                                                                                 G_CALLBACK(slideshow_delay_hours_cb), NULL);
1846         gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
1847         spin = pref_spin_new(hbox, ":" , NULL,
1848                                                                                 0, 59, 1.0, 0,
1849                                                                                 options->slideshow.delay ? minutes: 0.0,
1850                                                                                 G_CALLBACK(slideshow_delay_minutes_cb), NULL);
1851         gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
1852         spin = pref_spin_new(hbox, ":", NULL,
1853                                                                                 SLIDESHOW_MIN_SECONDS, 59, 1.0, 1,
1854                                                                                 options->slideshow.delay ? seconds : 10.0,
1855                                                                                 G_CALLBACK(slideshow_delay_seconds_cb), NULL);
1856         gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
1857
1858         pref_checkbox_new_int(group, _("Random"), options->slideshow.random, &c_options->slideshow.random);
1859         pref_checkbox_new_int(group, _("Repeat"), options->slideshow.repeat, &c_options->slideshow.repeat);
1860
1861         group = pref_group_new(vbox, FALSE, _("Image loading and caching"), GTK_ORIENTATION_VERTICAL);
1862
1863         pref_spin_new_int(group, _("Decoded image cache size (Mb):"), NULL,
1864                           0, 99999, 1, options->image.image_cache_max, &c_options->image.image_cache_max);
1865         pref_checkbox_new_int(group, _("Preload next image"),
1866                               options->image.enable_read_ahead, &c_options->image.enable_read_ahead);
1867
1868         pref_checkbox_new_int(group, _("Refresh on file change"),
1869                               options->update_on_time_change, &c_options->update_on_time_change);
1870
1871         group = pref_group_new(vbox, FALSE, _("Info sidebar heights"), GTK_ORIENTATION_VERTICAL);
1872         pref_label_new(group, _("NOTE! Geeqie must be restarted for changes to take effect"));
1873         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1874         pref_spin_new_int(hbox, _("Keywords:"), NULL,
1875                                  1, 9999, 1,
1876                                  options->info_keywords.height, &c_options->info_keywords.height);
1877         pref_spin_new_int(hbox, _("Title:"), NULL,
1878                                  1, 9999, 1,
1879                                  options->info_title.height, &c_options->info_title.height);
1880         pref_spin_new_int(hbox, _("Comment:"), NULL,
1881                                  1, 9999, 1,
1882                                  options->info_comment.height, &c_options->info_comment.height);
1883         pref_spin_new_int(hbox, _("Rating:"), NULL,
1884                                  1, 9999, 1,
1885                                  options->info_rating.height, &c_options->info_rating.height);
1886
1887         group = pref_group_new(vbox, FALSE, _("Show predefined keyword tree"), GTK_ORIENTATION_VERTICAL);
1888
1889         pref_checkbox_new_int(group, _("Show predefined keyword tree (NOTE! Geeqie must be restarted for change to take effect)"),
1890                                 options->show_predefined_keyword_tree, &c_options->show_predefined_keyword_tree);
1891
1892         group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL);
1893
1894         help_search_engine_entry = gtk_entry_new();
1895         gtk_entry_set_text(GTK_ENTRY(help_search_engine_entry), options->help_search_engine);
1896         gtk_box_pack_start(GTK_BOX(group), help_search_engine_entry, FALSE, FALSE, 0);
1897         gtk_widget_show(help_search_engine_entry);
1898
1899         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"));
1900
1901         gtk_entry_set_icon_from_stock(GTK_ENTRY(help_search_engine_entry),
1902                                                 GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
1903         gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
1904                                                 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
1905         gtk_entry_set_icon_from_stock(GTK_ENTRY(help_search_engine_entry),
1906                                                 GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_REVERT_TO_SAVED);
1907         gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
1908                                                 GTK_ENTRY_ICON_PRIMARY, _("Default"));
1909         g_signal_connect(GTK_ENTRY(help_search_engine_entry), "icon-press",
1910                                                 G_CALLBACK(help_search_engine_entry_icon_cb),
1911                                                 help_search_engine_entry);
1912 }
1913
1914 /* image tab */
1915 static void config_tab_image(GtkWidget *notebook)
1916 {
1917         GtkWidget *hbox;
1918         GtkWidget *vbox;
1919         GtkWidget *group;
1920         GtkWidget *ct_button;
1921         GtkWidget *enlargement_button;
1922         GtkWidget *table;
1923         GtkWidget *spin;
1924
1925         vbox = scrolled_notebook_page(notebook, _("Image"));
1926
1927         group = pref_group_new(vbox, FALSE, _("Zoom"), GTK_ORIENTATION_VERTICAL);
1928
1929         table = pref_table_new(group, 2, 1, FALSE, FALSE);
1930         add_quality_menu(table, 0, 0, _("Quality:"), options->image.zoom_quality, &c_options->image.zoom_quality);
1931
1932 #ifdef HAVE_CLUTTER
1933         pref_checkbox_new_int(group, _("Use GPU acceleration via Clutter library"),
1934                               options->image.use_clutter_renderer, &c_options->image.use_clutter_renderer);
1935 #endif
1936
1937         pref_checkbox_new_int(group, _("Two pass rendering (apply HQ zoom and color correction in second pass)"),
1938                               options->image.zoom_2pass, &c_options->image.zoom_2pass);
1939
1940         c_options->image.zoom_increment = options->image.zoom_increment;
1941         spin = pref_spin_new(group, _("Zoom increment:"), NULL,
1942                              0.01, 4.0, 0.01, 2, (gdouble)options->image.zoom_increment / 100.0,
1943                              G_CALLBACK(zoom_increment_cb), NULL);
1944         gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
1945
1946         group = pref_group_new(vbox, FALSE, _("Fit image to window"), GTK_ORIENTATION_VERTICAL);
1947
1948         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1949         enlargement_button = pref_checkbox_new_int(hbox, _("Allow enlargement of image (max. size in %)"),
1950                               options->image.zoom_to_fit_allow_expand, &c_options->image.zoom_to_fit_allow_expand);
1951         spin = pref_spin_new_int(hbox, NULL, NULL,
1952                                  100, 999, 1,
1953                                  options->image.max_enlargement_size, &c_options->image.max_enlargement_size);
1954         pref_checkbox_link_sensitivity(enlargement_button, spin);
1955         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."));
1956
1957         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1958         ct_button = pref_checkbox_new_int(hbox, _("Virtual window size (% of actual window):"),
1959                                           options->image.limit_autofit_size, &c_options->image.limit_autofit_size);
1960         spin = pref_spin_new_int(hbox, NULL, NULL,
1961                                  10, 150, 1,
1962                                  options->image.max_autofit_size, &c_options->image.max_autofit_size);
1963         pref_checkbox_link_sensitivity(ct_button, spin);
1964         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."));
1965
1966         group = pref_group_new(vbox, FALSE, _("Appearance"), GTK_ORIENTATION_VERTICAL);
1967
1968         pref_checkbox_new_int(group, _("Use custom border color in window mode"),
1969                               options->image.use_custom_border_color, &c_options->image.use_custom_border_color);
1970
1971         pref_checkbox_new_int(group, _("Use custom border color in fullscreen mode"),
1972                               options->image.use_custom_border_color_in_fullscreen, &c_options->image.use_custom_border_color_in_fullscreen);
1973
1974         pref_color_button_new(group, _("Border color"), &options->image.border_color,
1975                               G_CALLBACK(pref_color_button_set_cb), &c_options->image.border_color);
1976
1977         c_options->image.border_color = options->image.border_color;
1978
1979         pref_color_button_new(group, _("Alpha channel color 1"), &options->image.alpha_color_1,
1980                               G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_1);
1981
1982         pref_color_button_new(group, _("Alpha channel color 2"), &options->image.alpha_color_2,
1983                               G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_2);
1984
1985         c_options->image.alpha_color_1 = options->image.alpha_color_1;
1986         c_options->image.alpha_color_2 = options->image.alpha_color_2;
1987
1988         group = pref_group_new(vbox, FALSE, _("Convenience"), GTK_ORIENTATION_VERTICAL);
1989
1990         pref_checkbox_new_int(group, _("Auto rotate proofs using Exif information"),
1991                               options->image.exif_proof_rotate_enable, &c_options->image.exif_proof_rotate_enable);
1992 }
1993
1994 /* windows tab */
1995 static void config_tab_windows(GtkWidget *notebook)
1996 {
1997         GtkWidget *hbox;
1998         GtkWidget *vbox;
1999         GtkWidget *group;
2000         GtkWidget *button;
2001         GtkWidget *ct_button;
2002         GtkWidget *spin;
2003
2004         vbox = scrolled_notebook_page(notebook, _("Windows"));
2005
2006         group = pref_group_new(vbox, FALSE, _("State"), GTK_ORIENTATION_VERTICAL);
2007
2008         ct_button = pref_checkbox_new_int(group, _("Remember window positions"),
2009                                           options->save_window_positions, &c_options->save_window_positions);
2010
2011         button = pref_checkbox_new_int(group, _("Use saved window positions also for new windows"),
2012                                        options->use_saved_window_positions_for_new_windows, &c_options->use_saved_window_positions_for_new_windows);
2013         pref_checkbox_link_sensitivity(ct_button, button);
2014
2015         pref_checkbox_new_int(group, _("Remember tool state (float/hidden)"),
2016                               options->tools_restore_state, &c_options->tools_restore_state);
2017
2018         pref_checkbox_new_int(group, _("Remember dialog window positions"),
2019                               options->save_dialog_window_positions, &c_options->save_dialog_window_positions);
2020
2021         pref_checkbox_new_int(group, _("Show window IDs"),
2022                               options->show_window_ids, &c_options->show_window_ids);
2023
2024         group = pref_group_new(vbox, FALSE, _("Size"), GTK_ORIENTATION_VERTICAL);
2025
2026         pref_checkbox_new_int(group, _("Fit window to image when tools are hidden/floating"),
2027                               options->image.fit_window_to_image, &c_options->image.fit_window_to_image);
2028
2029         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2030         ct_button = pref_checkbox_new_int(hbox, _("Limit size when auto-sizing window (%):"),
2031                                           options->image.limit_window_size, &c_options->image.limit_window_size);
2032         spin = pref_spin_new_int(hbox, NULL, NULL,
2033                                  10, 150, 1,
2034                                  options->image.max_window_size, &c_options->image.max_window_size);
2035         pref_checkbox_link_sensitivity(ct_button, spin);
2036
2037         group = pref_group_new(vbox, FALSE, _("Full screen"), GTK_ORIENTATION_VERTICAL);
2038
2039         c_options->fullscreen.screen = options->fullscreen.screen;
2040         c_options->fullscreen.above = options->fullscreen.above;
2041         hbox = fullscreen_prefs_selection_new(_("Location:"), &c_options->fullscreen.screen, &c_options->fullscreen.above);
2042         gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2043         gtk_widget_show(hbox);
2044
2045         pref_checkbox_new_int(group, _("Smooth image flip"),
2046                               options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
2047         pref_checkbox_new_int(group, _("Disable screen saver"),
2048                               options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
2049 }
2050
2051 #define PRE_FORMATTED_COLUMNS 5
2052 static void config_tab_osd(GtkWidget *notebook)
2053 {
2054         GtkWidget *hbox;
2055         GtkWidget *vbox;
2056         GtkWidget *vbox_buttons;
2057         GtkWidget *group;
2058         GtkWidget *button;
2059         GtkWidget *image_overlay_template_view;
2060         GtkWidget *scrolled;
2061         GtkWidget *scrolled_pre_formatted;
2062         GtkTextBuffer *buffer;
2063         GtkWidget *label;
2064         GtkWidget *     subgroup;
2065         gint i = 0;
2066         gint rows = 0;
2067         gint cols = 0;
2068
2069         vbox = scrolled_notebook_page(notebook, _("OSD"));
2070
2071         image_overlay_template_view = gtk_text_view_new();
2072
2073         group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
2074
2075         subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2076
2077         scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
2078         gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
2079         gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
2080         gtk_widget_show(scrolled_pre_formatted);
2081         gtk_widget_show(subgroup);
2082
2083         pref_line(group, PREF_PAD_GAP);
2084
2085         pref_label_new(group, _("Image overlay template"));
2086
2087         scrolled = gtk_scrolled_window_new(NULL, NULL);
2088         gtk_widget_set_size_request(scrolled, 200, 150);
2089         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2090         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
2091                                                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2092         gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
2093         gtk_widget_show(scrolled);
2094
2095         gtk_widget_set_tooltip_markup(image_overlay_template_view,
2096                                         _("Extensive formatting options are shown in the Help file"));
2097
2098         gtk_container_add(GTK_CONTAINER(scrolled), image_overlay_template_view);
2099         gtk_widget_show(image_overlay_template_view);
2100
2101         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2102
2103 #if GTK_CHECK_VERSION(3,4,0)
2104         button = pref_button_new(NULL, GTK_STOCK_SELECT_FONT, _("Font"), FALSE,
2105                                  G_CALLBACK(image_overlay_set_font_cb), notebook);
2106 #else
2107         button = gtk_font_button_new();
2108         gtk_font_button_set_title(GTK_FONT_BUTTON(button), "Image Overlay Font");
2109         gtk_font_button_set_font_name(GTK_FONT_BUTTON(button), options->image_overlay.font);
2110         g_signal_connect(G_OBJECT(button), "font-set",
2111                                  G_CALLBACK(image_overlay_set_font_cb),NULL);
2112 #endif
2113         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2114         gtk_widget_show(button);
2115
2116         button = pref_button_new(NULL, GTK_STOCK_COLOR_PICKER, _("Text"), FALSE,
2117                                  G_CALLBACK(image_overlay_set_text_colour_cb), NULL);
2118         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2119         gtk_widget_show(button);
2120
2121         button = pref_button_new(NULL, GTK_STOCK_COLOR_PICKER, _("Background"), FALSE,
2122                                  G_CALLBACK(image_overlay_set_background_colour_cb), NULL);
2123         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2124         gtk_widget_show(button);
2125         image_overlay_set_text_colours();
2126
2127         button = pref_button_new(NULL, NULL, _("Defaults"), FALSE,
2128                                  G_CALLBACK(image_overlay_default_template_cb), image_overlay_template_view);
2129         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2130         gtk_widget_show(button);
2131
2132         button = pref_button_new(NULL, GTK_STOCK_HELP, NULL, FALSE,
2133                                  G_CALLBACK(image_overlay_help_cb), NULL);
2134         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2135         gtk_widget_show(button);
2136
2137         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_overlay_template_view));
2138         if (options->image_overlay.template_string) gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
2139         g_signal_connect(G_OBJECT(buffer), "changed",
2140                          G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
2141
2142         pref_line(group, PREF_PAD_GAP);
2143
2144         group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL);
2145         hbox = gtk_hbox_new(FALSE, 0);
2146         gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2147         gtk_widget_show(hbox);
2148         label = gtk_label_new(_("%Exif.Image.Orientation%"));
2149         gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2150         gtk_widget_show(label);
2151         pref_spacer(group,TRUE);
2152
2153         group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL);
2154         hbox = gtk_hbox_new(FALSE, 0);
2155         gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2156         gtk_widget_show(hbox);
2157         label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
2158         gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2159         gtk_widget_show(label);
2160         pref_spacer(group,TRUE);
2161
2162         group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL);
2163         hbox = gtk_hbox_new(FALSE, 0);
2164         gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2165         gtk_widget_show(hbox);
2166         label = gtk_label_new(_("%path:39%"));
2167         gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2168         gtk_widget_show(label);
2169         pref_spacer(group,TRUE);
2170
2171         group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL);
2172         hbox = gtk_hbox_new(FALSE, 0);
2173         gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2174         gtk_widget_show(hbox);
2175         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%"));
2176         gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2177         gtk_widget_show(label);
2178         pref_spacer(group,TRUE);
2179
2180         group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL);
2181         hbox = gtk_hbox_new(FALSE, 0);
2182         gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2183         gtk_widget_show(hbox);
2184         label = gtk_label_new(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
2185         gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2186         gtk_widget_show(label);
2187 }
2188
2189 static GtkTreeModel *create_class_model(void)
2190 {
2191         GtkListStore *model;
2192         GtkTreeIter iter;
2193         gint i;
2194
2195         /* create list store */
2196         model = gtk_list_store_new(1, G_TYPE_STRING);
2197         for (i = 0; i < FILE_FORMAT_CLASSES; i++)
2198                 {
2199                 gtk_list_store_append(model, &iter);
2200                 gtk_list_store_set(model, &iter, 0, _(format_class_list[i]), -1);
2201                 }
2202         return GTK_TREE_MODEL (model);
2203 }
2204
2205
2206 /* filtering tab */
2207 static void config_tab_files(GtkWidget *notebook)
2208 {
2209         GtkWidget *hbox;
2210         GtkWidget *frame;
2211         GtkWidget *vbox;
2212         GtkWidget *group;
2213         GtkWidget *button;
2214         GtkWidget *ct_button;
2215         GtkWidget *scrolled;
2216         GtkWidget *filter_view;
2217         GtkCellRenderer *renderer;
2218         GtkTreeSelection *selection;
2219         GtkTreeViewColumn *column;
2220
2221         vbox = scrolled_notebook_page(notebook, _("Files"));
2222
2223         group = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2224
2225         pref_checkbox_new_int(group, _("Show hidden files or folders"),
2226                               options->file_filter.show_hidden_files, &c_options->file_filter.show_hidden_files);
2227         pref_checkbox_new_int(group, _("Show parent folder (..)"),
2228                               options->file_filter.show_parent_directory, &c_options->file_filter.show_parent_directory);
2229         pref_checkbox_new_int(group, _("Case sensitive sort"),
2230                               options->file_sort.case_sensitive, &c_options->file_sort.case_sensitive);
2231         pref_checkbox_new_int(group, _("Natural sort order"),
2232                                           options->file_sort.natural, &c_options->file_sort.natural);
2233         pref_checkbox_new_int(group, _("Disable file extension checks"),
2234                               options->file_filter.disable_file_extension_checks, &c_options->file_filter.disable_file_extension_checks);
2235
2236         ct_button = pref_checkbox_new_int(group, _("Disable File Filtering"),
2237                                           options->file_filter.disable, &c_options->file_filter.disable);
2238
2239
2240         group = pref_group_new(vbox, FALSE, _("Grouping sidecar extensions"), GTK_ORIENTATION_VERTICAL);
2241
2242         sidecar_ext_entry = gtk_entry_new();
2243         gtk_entry_set_text(GTK_ENTRY(sidecar_ext_entry), options->sidecar.ext);
2244         gtk_box_pack_start(GTK_BOX(group), sidecar_ext_entry, FALSE, FALSE, 0);
2245         gtk_widget_show(sidecar_ext_entry);
2246
2247         group = pref_group_new(vbox, TRUE, _("File types"), GTK_ORIENTATION_VERTICAL);
2248
2249         frame = pref_group_parent(group);
2250         g_signal_connect(G_OBJECT(ct_button), "toggled",
2251                          G_CALLBACK(filter_disable_cb), frame);
2252         gtk_widget_set_sensitive(frame, !options->file_filter.disable);
2253
2254         scrolled = gtk_scrolled_window_new(NULL, NULL);
2255         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2256         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2257         gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2258         gtk_widget_show(scrolled);
2259
2260         filter_store = gtk_list_store_new(1, G_TYPE_POINTER);
2261         filter_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
2262         g_object_unref(filter_store);
2263         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
2264         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2265
2266         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), FALSE);
2267
2268         column = gtk_tree_view_column_new();
2269         gtk_tree_view_column_set_title(column, _("Filter"));
2270         gtk_tree_view_column_set_resizable(column, TRUE);
2271
2272         renderer = gtk_cell_renderer_toggle_new();
2273         g_signal_connect(G_OBJECT(renderer), "toggled",
2274                          G_CALLBACK(filter_store_enable_cb), filter_store);
2275         gtk_tree_view_column_pack_start(column, renderer, FALSE);
2276         gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2277                                                 GINT_TO_POINTER(FE_ENABLE), NULL);
2278
2279         renderer = gtk_cell_renderer_text_new();
2280         g_signal_connect(G_OBJECT(renderer), "edited",
2281                          G_CALLBACK(filter_store_ext_edit_cb), filter_store);
2282         gtk_tree_view_column_pack_start(column, renderer, TRUE);
2283         g_object_set(G_OBJECT(renderer), "editable", (gboolean)TRUE, NULL);
2284         gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2285                                                 GINT_TO_POINTER(FE_EXTENSION), NULL);
2286         gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2287
2288         column = gtk_tree_view_column_new();
2289         gtk_tree_view_column_set_title(column, _("Description"));
2290         gtk_tree_view_column_set_resizable(column, TRUE);
2291         gtk_tree_view_column_set_fixed_width(column, 200);
2292         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2293
2294         renderer = gtk_cell_renderer_text_new();
2295         g_signal_connect(G_OBJECT(renderer), "edited",
2296                          G_CALLBACK(filter_store_desc_edit_cb), filter_store);
2297         g_object_set(G_OBJECT(renderer), "editable", (gboolean)TRUE, NULL);
2298         gtk_tree_view_column_pack_start(column, renderer, FALSE);
2299         gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2300                                                 GINT_TO_POINTER(FE_DESCRIPTION), NULL);
2301         gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2302
2303         column = gtk_tree_view_column_new();
2304         gtk_tree_view_column_set_title(column, _("Class"));
2305         gtk_tree_view_column_set_resizable(column, TRUE);
2306         renderer = gtk_cell_renderer_combo_new();
2307         g_object_set(G_OBJECT(renderer), "editable", (gboolean)TRUE,
2308                                          "model", create_class_model(),
2309                                          "text-column", 0,
2310                                          "has-entry", FALSE,
2311                                          NULL);
2312
2313         g_signal_connect(G_OBJECT(renderer), "edited",
2314                          G_CALLBACK(filter_store_class_edit_cb), filter_store);
2315         gtk_tree_view_column_pack_start(column, renderer, TRUE);
2316         gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2317                                                 GINT_TO_POINTER(FE_CLASS), NULL);
2318         gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2319
2320         column = gtk_tree_view_column_new();
2321         gtk_tree_view_column_set_title(column, _("Writable"));
2322         gtk_tree_view_column_set_resizable(column, FALSE);
2323         renderer = gtk_cell_renderer_toggle_new();
2324         g_signal_connect(G_OBJECT(renderer), "toggled",
2325                          G_CALLBACK(filter_store_writable_cb), filter_store);
2326         gtk_tree_view_column_pack_start(column, renderer, FALSE);
2327         gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2328                                                 GINT_TO_POINTER(FE_WRITABLE), NULL);
2329         gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2330
2331         column = gtk_tree_view_column_new();
2332         gtk_tree_view_column_set_title(column, _("Sidecar is allowed"));
2333         gtk_tree_view_column_set_resizable(column, FALSE);
2334         renderer = gtk_cell_renderer_toggle_new();
2335         g_signal_connect(G_OBJECT(renderer), "toggled",
2336                          G_CALLBACK(filter_store_sidecar_cb), filter_store);
2337         gtk_tree_view_column_pack_start(column, renderer, FALSE);
2338         gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2339                                                 GINT_TO_POINTER(FE_ALLOW_SIDECAR), NULL);
2340         gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2341
2342
2343         filter_store_populate();
2344         gtk_container_add(GTK_CONTAINER(scrolled), filter_view);
2345         gtk_widget_show(filter_view);
2346
2347         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2348
2349         button = pref_button_new(NULL, NULL, _("Defaults"), FALSE,
2350                                  G_CALLBACK(filter_default_cb), filter_view);
2351         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2352         gtk_widget_show(button);
2353
2354         button = pref_button_new(NULL, GTK_STOCK_REMOVE, NULL, FALSE,
2355                                  G_CALLBACK(filter_remove_cb), filter_view);
2356         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2357         gtk_widget_show(button);
2358
2359         button = pref_button_new(NULL, GTK_STOCK_ADD, NULL, FALSE,
2360                                  G_CALLBACK(filter_add_cb), filter_view);
2361         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2362         gtk_widget_show(button);
2363 }
2364
2365 /* metadata tab */
2366 static void config_tab_metadata(GtkWidget *notebook)
2367 {
2368         GtkWidget *vbox;
2369         GtkWidget *hbox;
2370         GtkWidget *group;
2371         GtkWidget *ct_button;
2372         GtkWidget *label;
2373         gchar *text;
2374
2375         vbox = scrolled_notebook_page(notebook, _("Metadata"));
2376
2377
2378         group = pref_group_new(vbox, FALSE, _("Metadata writing process"), GTK_ORIENTATION_VERTICAL);
2379 #ifndef HAVE_EXIV2
2380         label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2381 #endif
2382         label = pref_label_new(group, _("Metadata are written in the following order. The process ends after first success."));
2383         gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2384
2385         ct_button = pref_checkbox_new_int(group, _("1) Save metadata in image files, or sidecar files, according to the XMP standard"),
2386                               options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2387 #ifndef HAVE_EXIV2
2388         gtk_widget_set_sensitive(ct_button, FALSE);
2389 #endif
2390
2391         pref_checkbox_new_int(group, _("2) Save metadata in '.metadata' folder, local to image folder (non-standard)"),
2392                               options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2393
2394         text = g_strdup_printf(_("3) Save metadata in Geeqie private directory '%s'"), get_metadata_cache_dir());
2395         label = pref_label_new(group, text);
2396         gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2397         gtk_misc_set_padding(GTK_MISC(label), 22, 0);
2398         g_free(text);
2399
2400         group = pref_group_new(vbox, FALSE, _("Step 1: Write to image files"), GTK_ORIENTATION_VERTICAL);
2401 #ifndef HAVE_EXIV2
2402         gtk_widget_set_sensitive(group, FALSE);
2403 #endif
2404
2405         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2406         pref_checkbox_link_sensitivity(ct_button, hbox);
2407
2408         pref_checkbox_new_int(hbox, _("Store metadata also in legacy IPTC tags (converted according to IPTC4XMP standard)"),
2409                               options->metadata.save_legacy_IPTC, &c_options->metadata.save_legacy_IPTC);
2410
2411         pref_checkbox_new_int(hbox, _("Warn if the image files are unwritable"),
2412                               options->metadata.warn_on_write_problems, &c_options->metadata.warn_on_write_problems);
2413
2414         pref_checkbox_new_int(hbox, _("Ask before writing to image files"),
2415                               options->metadata.confirm_write, &c_options->metadata.confirm_write);
2416
2417         pref_checkbox_new_int(hbox, _("Create sidecar files named image.ext.xmp (as opposed to image.xmp)"),
2418                               options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2419
2420         group = pref_group_new(vbox, FALSE, _("Step 2 and 3: write to Geeqie private files"), GTK_ORIENTATION_VERTICAL);
2421 #ifndef HAVE_EXIV2
2422         gtk_widget_set_sensitive(group, FALSE);
2423 #endif
2424
2425         pref_checkbox_new_int(group, _("Use GQview legacy metadata format (supports only keywords and comments) instead of XMP"),
2426                               options->metadata.save_legacy_format, &c_options->metadata.save_legacy_format);
2427
2428
2429         group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2430         pref_checkbox_new_int(group, _("Write the same description tags (keywords, comment, etc.) to all grouped sidecars"),
2431                               options->metadata.sync_grouped_files, &c_options->metadata.sync_grouped_files);
2432
2433         pref_checkbox_new_int(group, _("Allow keywords to differ only in case"),
2434                               options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2435
2436         ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"),
2437                               options->metadata.write_orientation, &c_options->metadata.write_orientation);
2438 #ifndef HAVE_EXIV2
2439         gtk_widget_set_sensitive(ct_button, FALSE);
2440 #endif
2441
2442         group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2443
2444         ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"),
2445                               options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
2446
2447         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2448         pref_checkbox_link_sensitivity(ct_button, hbox);
2449
2450         pref_spin_new_int(hbox, _("Timeout (seconds):"), NULL, 0, 900, 1,
2451                               options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
2452
2453         pref_checkbox_new_int(group, _("Write metadata on image change"),
2454                               options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
2455
2456         pref_checkbox_new_int(group, _("Write metadata on directory change"),
2457                               options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
2458
2459         group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
2460
2461         ct_button = pref_checkbox_new_int(group, _("Read metadata in background"),
2462                                           options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
2463         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");
2464 }
2465
2466 /* keywords tab */
2467
2468 typedef struct _KeywordFindData KeywordFindData;
2469 struct _KeywordFindData
2470 {
2471         GenericDialog *gd;
2472
2473         GList *list;
2474         GList *list_dir;
2475
2476         GtkWidget *button_close;
2477         GtkWidget *button_stop;
2478         GtkWidget *button_start;
2479         GtkWidget *progress;
2480         GtkWidget *spinner;
2481
2482         GtkWidget *group;
2483         GtkWidget *entry;
2484
2485         gboolean recurse;
2486
2487         guint idle_id; /* event source id */
2488 };
2489
2490 #define KEYWORD_DIALOG_WIDTH 400
2491
2492 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
2493 {
2494         GList *list_d = NULL;
2495         GList *list_f = NULL;
2496
2497         if (kfd->recurse)
2498                 {
2499                 filelist_read(dir_fd, &list_f, &list_d);
2500                 }
2501         else
2502                 {
2503                 filelist_read(dir_fd, &list_f, NULL);
2504                 }
2505
2506         list_f = filelist_filter(list_f, FALSE);
2507         list_d = filelist_filter(list_d, TRUE);
2508
2509         kfd->list = g_list_concat(list_f, kfd->list);
2510         kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
2511 }
2512
2513 static void keywords_find_reset(KeywordFindData *kfd)
2514 {
2515         filelist_free(kfd->list);
2516         kfd->list = NULL;
2517
2518         filelist_free(kfd->list_dir);
2519         kfd->list_dir = NULL;
2520 }
2521
2522 static void keywords_find_close_cb(GenericDialog *fd, gpointer data)
2523 {
2524         KeywordFindData *kfd = data;
2525
2526         if (!gtk_widget_get_sensitive(kfd->button_close)) return;
2527
2528         keywords_find_reset(kfd);
2529         generic_dialog_close(kfd->gd);
2530         g_free(kfd);
2531 }
2532
2533 static void keywords_find_finish(KeywordFindData *kfd)
2534 {
2535         keywords_find_reset(kfd);
2536
2537         gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
2538         spinner_set_interval(kfd->spinner, -1);
2539
2540         gtk_widget_set_sensitive(kfd->group, TRUE);
2541         gtk_widget_set_sensitive(kfd->button_start, TRUE);
2542         gtk_widget_set_sensitive(kfd->button_stop, FALSE);
2543         gtk_widget_set_sensitive(kfd->button_close, TRUE);
2544 }
2545
2546 static void keywords_find_stop_cb(GenericDialog *fd, gpointer data)
2547 {
2548         KeywordFindData *kfd = data;
2549
2550         g_idle_remove_by_data(kfd);
2551
2552         keywords_find_finish(kfd);
2553 }
2554
2555 static gboolean keywords_find_file(gpointer data)
2556 {
2557         KeywordFindData *kfd = data;
2558         GtkTextIter iter;
2559         GtkTextBuffer *buffer;
2560         gchar *tmp;
2561         GList *keywords;
2562
2563         if (kfd->list)
2564                 {
2565                 FileData *fd;
2566
2567                 fd = kfd->list->data;
2568                 kfd->list = g_list_remove(kfd->list, fd);
2569
2570                 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
2571                 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
2572
2573                 while (keywords)
2574                         {
2575                         gtk_text_buffer_get_end_iter(buffer, &iter);
2576                         tmp = g_strconcat(keywords->data, "\n", NULL);
2577                         gtk_text_buffer_insert(buffer, &iter, tmp, -1);
2578                         g_free(tmp);
2579                         keywords = keywords->next;
2580                         }
2581
2582                 gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
2583                 file_data_unref(fd);
2584                 string_list_free(keywords);
2585
2586                 return (TRUE);
2587                 }
2588         else if (kfd->list_dir)
2589                 {
2590                 FileData *fd;
2591
2592                 fd = kfd->list_dir->data;
2593                 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
2594
2595                 keywords_find_folder(kfd, fd);
2596
2597                 file_data_unref(fd);
2598
2599                 return TRUE;
2600                 }
2601
2602         keywords_find_finish(kfd);
2603
2604         return FALSE;
2605 }
2606
2607 static void keywords_find_start_cb(GenericDialog *fd, gpointer data)
2608 {
2609         KeywordFindData *kfd = data;
2610         gchar *path;
2611
2612         if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
2613
2614         path = remove_trailing_slash((gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
2615         parse_out_relatives(path);
2616
2617         if (!isdir(path))
2618                 {
2619                 warning_dialog(_("Invalid folder"),
2620                                 _("The specified folder can not be found."),
2621                                 GTK_STOCK_DIALOG_WARNING, kfd->gd->dialog);
2622                 }
2623         else
2624                 {
2625                 FileData *dir_fd;
2626
2627                 gtk_widget_set_sensitive(kfd->group, FALSE);
2628                 gtk_widget_set_sensitive(kfd->button_start, FALSE);
2629                 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
2630                 gtk_widget_set_sensitive(kfd->button_close, FALSE);
2631                 spinner_set_interval(kfd->spinner, SPINNER_SPEED);
2632
2633                 dir_fd = file_data_new_dir(path);
2634                 keywords_find_folder(kfd, dir_fd);
2635                 file_data_unref(dir_fd);
2636                 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
2637                 }
2638
2639         g_free(path);
2640 }
2641
2642 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
2643 {
2644         KeywordFindData *kfd;
2645         GtkWidget *hbox;
2646         GtkWidget *label;
2647
2648         kfd = g_new0(KeywordFindData, 1);
2649
2650         kfd->gd = generic_dialog_new(_("Search for keywords"),
2651                                                                         "search_for_keywords",
2652                                                                         widget, FALSE,
2653                                                                         NULL, kfd);
2654         gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
2655         kfd->gd->cancel_cb = keywords_find_close_cb;
2656         kfd->button_close = generic_dialog_add_button(kfd->gd, GTK_STOCK_CLOSE, NULL,
2657                                                      keywords_find_close_cb, FALSE);
2658         kfd->button_start = generic_dialog_add_button(kfd->gd, GTK_STOCK_OK, _("S_tart"),
2659                                                      keywords_find_start_cb, FALSE);
2660         kfd->button_stop = generic_dialog_add_button(kfd->gd, GTK_STOCK_STOP, NULL,
2661                                                     keywords_find_stop_cb, FALSE);
2662         gtk_widget_set_sensitive(kfd->button_stop, FALSE);
2663
2664         generic_dialog_add_message(kfd->gd, NULL, _("Search for keywords"), NULL, FALSE);
2665
2666         hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
2667         pref_spacer(hbox, PREF_PAD_INDENT);
2668         kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2669
2670         hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2671         pref_label_new(hbox, _("Folder:"));
2672
2673         label = tab_completion_new(&kfd->entry, path, NULL, NULL, NULL, NULL);
2674         tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
2675         gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
2676         gtk_widget_show(label);
2677
2678         pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
2679
2680         pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
2681         hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2682
2683         kfd->progress = gtk_entry_new();
2684         gtk_widget_set_can_focus(kfd->progress, FALSE);
2685         gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
2686         gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
2687         gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
2688         gtk_widget_show(kfd->progress);
2689
2690         kfd->spinner = spinner_new(NULL, -1);
2691         gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
2692         gtk_widget_show(kfd->spinner);
2693
2694         kfd->list = NULL;
2695
2696         gtk_widget_show(kfd->gd->dialog);
2697 }
2698
2699 static void keywords_find_cb(GtkWidget *widget, gpointer data)
2700 {
2701         const gchar *path = layout_get_path(NULL);
2702
2703         if (!path || !*path) path = homedir();
2704         keywords_find_dialog(widget, path);
2705 }
2706
2707 static void config_tab_keywords_save()
2708 {
2709         GtkTextIter start, end;
2710         GtkTextBuffer *buffer;
2711         GList *kw_list = NULL;
2712         GList *work;
2713         gchar *buffer_text;
2714         gchar *kw_split;
2715         gboolean found;
2716
2717         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
2718         gtk_text_buffer_get_bounds(buffer, &start, &end);
2719
2720         buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
2721
2722         kw_split = strtok(buffer_text, "\n");
2723         while (kw_split != NULL)
2724                 {
2725                 work = kw_list;
2726                 found = FALSE;
2727                 while (work)
2728                         {
2729                         if (g_strcmp0(work->data, kw_split) == 0)
2730                                 {
2731                                 found = TRUE;
2732                                 break;
2733                                 }
2734                         work = work->next;
2735                         }
2736                 if (!found)
2737                         {
2738                         kw_list = g_list_append(kw_list, g_strdup(kw_split));
2739                         }
2740                 kw_split = strtok(NULL, "\n");
2741                 }
2742
2743         keyword_list_set(kw_list);
2744
2745         string_list_free(kw_list);
2746         g_free(buffer_text);
2747 }
2748
2749 static void config_tab_keywords(GtkWidget *notebook)
2750 {
2751         GtkWidget *hbox;
2752         GtkWidget *vbox;
2753         GtkWidget *group;
2754         GtkWidget *button;
2755         GtkWidget *scrolled;
2756         GtkTextIter iter;
2757         GtkTextBuffer *buffer;
2758         gchar *tmp;
2759
2760         vbox = scrolled_notebook_page(notebook, _("Keywords"));
2761
2762         group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
2763
2764         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2765
2766         button = pref_button_new(hbox, GTK_STOCK_EXECUTE, _("Search"), FALSE,
2767                                    G_CALLBACK(keywords_find_cb), keyword_text);
2768         gtk_widget_set_tooltip_text(button, "Search for existing keywords");
2769
2770
2771         keyword_text = gtk_text_view_new();
2772         gtk_widget_set_size_request(keyword_text, 20, 20);
2773         scrolled = gtk_scrolled_window_new(NULL, NULL);
2774         gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2775         gtk_widget_show(scrolled);
2776
2777         gtk_container_add(GTK_CONTAINER(scrolled), keyword_text);
2778         gtk_widget_show(keyword_text);
2779
2780         gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
2781
2782         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
2783         gtk_text_buffer_create_tag(buffer, "monospace",
2784                                 "family", "monospace", NULL);
2785
2786         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
2787         gtk_text_buffer_get_start_iter(buffer, &iter);
2788         gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
2789         gchar *path;
2790
2791         path = g_build_filename(get_rc_dir(), "keywords", NULL);
2792
2793         GList *kwl = keyword_list_get();
2794         kwl = g_list_first(kwl);
2795         while (kwl)
2796         {
2797                 gtk_text_buffer_get_end_iter (buffer, &iter);
2798             tmp = g_strconcat(kwl->data, "\n", NULL);
2799                 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
2800                 kwl = kwl->next;
2801                 g_free(tmp);
2802         }
2803
2804         gtk_text_buffer_set_modified(buffer, FALSE);
2805
2806         g_free(path);
2807 }
2808
2809 /* metadata tab */
2810 #ifdef HAVE_LCMS
2811 static void intent_menu_cb(GtkWidget *combo, gpointer data)
2812 {
2813         gint *option = data;
2814
2815         switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
2816                 {
2817                 case 0:
2818                 default:
2819                         *option = INTENT_PERCEPTUAL;
2820                         break;
2821                 case 1:
2822                         *option = INTENT_RELATIVE_COLORIMETRIC;
2823                         break;
2824                 case 2:
2825                         *option = INTENT_SATURATION;
2826                         break;
2827                 case 3:
2828                         *option = INTENT_ABSOLUTE_COLORIMETRIC;
2829                         break;
2830                 }
2831 }
2832
2833 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
2834                              gint option, gint *option_c)
2835 {
2836         GtkWidget *combo;
2837         gint current = 0;
2838
2839         *option_c = option;
2840
2841         pref_table_label(table, column, row, text, 0.0);
2842
2843         combo = gtk_combo_box_text_new();
2844
2845         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
2846         if (option == INTENT_PERCEPTUAL) current = 0;
2847         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
2848         if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
2849         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
2850         if (option == INTENT_SATURATION) current = 2;
2851         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
2852         if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
2853
2854         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
2855
2856         gtk_widget_set_tooltip_text(combo,"Refer to the lcms documentation for the defaults used when the selected Intent is not available");
2857
2858         g_signal_connect(G_OBJECT(combo), "changed",
2859                          G_CALLBACK(intent_menu_cb), option_c);
2860
2861         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
2862                          GTK_EXPAND | GTK_FILL, 0, 0, 0);
2863         gtk_widget_show(combo);
2864 }
2865 #endif
2866
2867 static void config_tab_color(GtkWidget *notebook)
2868 {
2869         GtkWidget *label;
2870         GtkWidget *vbox;
2871         GtkWidget *group;
2872         GtkWidget *tabcomp;
2873         GtkWidget *table;
2874         gint i;
2875
2876         vbox = scrolled_notebook_page(notebook, _("Color management"));
2877
2878         group =  pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
2879 #ifndef HAVE_LCMS
2880         gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
2881 #endif
2882
2883         table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
2884         gtk_table_set_col_spacings(GTK_TABLE(table), PREF_PAD_GAP);
2885
2886         label = pref_table_label(table, 0, 0, _("Type"), 0.0);
2887         pref_label_bold(label, TRUE, FALSE);
2888
2889         label = pref_table_label(table, 1, 0, _("Menu name"), 0.0);
2890         pref_label_bold(label, TRUE, FALSE);
2891
2892         label = pref_table_label(table, 2, 0, _("File"), 0.0);
2893         pref_label_bold(label, TRUE, FALSE);
2894
2895         for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
2896                 {
2897                 GtkWidget *entry;
2898                 gchar *buf;
2899
2900                 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
2901                 pref_table_label(table, 0, i + 1, buf, 1.0);
2902                 g_free(buf);
2903
2904                 entry = gtk_entry_new();
2905                 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
2906                 if (options->color_profile.input_name[i])
2907                         {
2908                         gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
2909                         }
2910                 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, i + 1, i + 2,
2911                                  GTK_FILL | GTK_EXPAND, 0, 0, 0);
2912                 gtk_widget_show(entry);
2913                 color_profile_input_name_entry[i] = entry;
2914
2915                 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], NULL, ".icc", "ICC Files", NULL);
2916                 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
2917                 gtk_widget_set_size_request(entry, 160, -1);
2918                 gtk_table_attach(GTK_TABLE(table), tabcomp, 2, 3, i + 1, i + 2,
2919                                  GTK_FILL | GTK_EXPAND, 0, 0, 0);
2920                 gtk_widget_show(tabcomp);
2921                 color_profile_input_file_entry[i] = entry;
2922                 }
2923
2924         group =  pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
2925 #ifndef HAVE_LCMS
2926         gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
2927 #endif
2928         pref_checkbox_new_int(group, _("Use system screen profile if available"),
2929                               options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
2930
2931         table = pref_table_new(group, 2, 1, FALSE, FALSE);
2932
2933         pref_table_label(table, 0, 0, _("Screen:"), 1.0);
2934         tabcomp = tab_completion_new(&color_profile_screen_file_entry,
2935                                      options->color_profile.screen_file, NULL, ".icc", "ICC Files", NULL);
2936         tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
2937         gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
2938 #ifdef HAVE_LCMS
2939         add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
2940 #endif
2941         gtk_table_attach(GTK_TABLE(table), tabcomp, 1, 2,
2942                          0, 1,
2943                          GTK_FILL | GTK_EXPAND, 0, 0, 0);
2944
2945         gtk_widget_show(tabcomp);
2946 }
2947
2948 /* advanced entry tab */
2949 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer data)
2950 {
2951         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
2952                 {
2953                 c_options->file_ops.use_system_trash = FALSE;
2954                 }
2955 }
2956
2957 static void use_system_trash_cb(GtkWidget *widget, gpointer data)
2958 {
2959         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
2960                 {
2961                 c_options->file_ops.use_system_trash = TRUE;
2962                 }
2963 }
2964
2965 static void config_tab_behavior(GtkWidget *notebook)
2966 {
2967         GtkWidget *hbox;
2968         GtkWidget *vbox;
2969         GtkWidget *group;
2970         GtkWidget *button;
2971         GtkWidget *tabcomp;
2972         GtkWidget *ct_button;
2973         GtkWidget *spin;
2974         GtkWidget *table;
2975         GtkWidget *marks;
2976         GtkWidget *with_rename;
2977         GtkWidget *collections_on_top;
2978
2979         vbox = scrolled_notebook_page(notebook, _("Behavior"));
2980
2981         group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
2982
2983         pref_checkbox_new_int(group, _("Confirm permanent file delete"),
2984                               options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
2985         pref_checkbox_new_int(group, _("Confirm move file to Trash"),
2986                               options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
2987         pref_checkbox_new_int(group, _("Enable Delete key"),
2988                               options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
2989
2990         ct_button = pref_radiobutton_new(group, NULL, _("Use Geeqie trash location"),
2991                                         !options->file_ops.use_system_trash, G_CALLBACK(use_geeqie_trash_cb),NULL);
2992
2993         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2994         pref_checkbox_link_sensitivity(ct_button, hbox);
2995
2996         pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
2997         pref_label_new(hbox, _("Folder:"));
2998
2999         tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, NULL, NULL, NULL, NULL);
3000         tab_completion_add_select_button(safe_delete_path_entry, NULL, TRUE);
3001         gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3002         gtk_widget_show(tabcomp);
3003
3004         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3005         pref_checkbox_link_sensitivity(ct_button, hbox);
3006
3007         pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3008         spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MB"),
3009                                  0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3010         gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3011         button = pref_button_new(NULL, NULL, _("View"), FALSE,
3012                                  G_CALLBACK(safe_delete_view_cb), NULL);
3013         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3014         gtk_widget_show(button);
3015
3016         button = pref_button_new(NULL, GTK_STOCK_CLEAR, NULL, FALSE,
3017                                  G_CALLBACK(safe_delete_clear_cb), NULL);
3018         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3019         pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3020                                         options->file_ops.use_system_trash, G_CALLBACK(use_system_trash_cb), NULL);
3021         gtk_widget_show(button);
3022
3023         pref_spacer(group, PREF_PAD_GROUP);
3024
3025
3026         group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3027
3028         pref_checkbox_new_int(group, _("Descend folders in tree view"),
3029                               options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3030
3031         pref_checkbox_new_int(group, _("In place renaming"),
3032                               options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3033
3034         pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3035                               options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3036
3037         marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3038                                 options->marks_save, &c_options->marks_save);
3039         gtk_widget_set_tooltip_text(marks,"Note that marks linked to a keyword will be saved irrespective of this setting");
3040
3041         with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3042                                 options->with_rename, &c_options->with_rename);
3043         gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3044
3045         collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3046                                 options->collections_on_top, &c_options->collections_on_top);
3047         gtk_widget_set_tooltip_text(collections_on_top,"Open collections window on top");
3048
3049         pref_spin_new_int(group, _("Recent folder list maximum size"), NULL,
3050                           1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3051
3052         pref_spin_new_int(group, _("Drag'n drop icon size"), NULL,
3053                           16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3054
3055         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3056         add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3057
3058         pref_spacer(group, PREF_PAD_GROUP);
3059
3060         group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3061
3062         pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3063                               options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3064         pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), NULL,
3065                           1, 32, 1, options->keyboard_scroll_step, (int *)&c_options->keyboard_scroll_step);
3066         pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3067                               options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3068         pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3069                               options->image_lm_click_nav, &c_options->image_lm_click_nav);
3070         pref_checkbox_new_int(group, _("Play video by left click on image"),
3071                               options->image_l_click_video, &c_options->image_l_click_video);
3072         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3073         add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3074
3075
3076 #ifdef DEBUG
3077         pref_spacer(group, PREF_PAD_GROUP);
3078
3079         group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3080
3081         pref_spin_new_int(group, _("Debug level:"), NULL,
3082                           DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3083
3084         pref_checkbox_new_int(group, _("Timer data"),
3085                         options->log_window.timer_data, &c_options->log_window.timer_data);
3086
3087         pref_spin_new_int(group, _("Log Window max. lines:"), NULL,
3088                           1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3089 #endif
3090 }
3091
3092 /* accelerators tab */
3093 static void config_tab_accelerators(GtkWidget *notebook)
3094 {
3095         GtkWidget *hbox;
3096         GtkWidget *vbox;
3097         GtkWidget *group;
3098         GtkWidget *button;
3099         GtkWidget *scrolled;
3100         GtkWidget *accel_view;
3101         GtkCellRenderer *renderer;
3102         GtkTreeSelection *selection;
3103         GtkTreeViewColumn *column;
3104
3105         vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3106
3107         group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3108
3109         scrolled = gtk_scrolled_window_new(NULL, NULL);
3110         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3111         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3112         gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3113         gtk_widget_show(scrolled);
3114
3115         accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3116
3117         accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3118         g_object_unref(accel_store);
3119         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3120         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3121
3122         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3123
3124         renderer = gtk_cell_renderer_text_new();
3125
3126         column = gtk_tree_view_column_new_with_attributes(_("Action"),
3127                                                           renderer,
3128                                                           "text", AE_ACTION,
3129                                                           NULL);
3130
3131         gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3132         gtk_tree_view_column_set_resizable(column, TRUE);
3133         gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3134
3135
3136         renderer = gtk_cell_renderer_accel_new();
3137         g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3138                          G_CALLBACK(accel_store_cleared_cb), accel_store);
3139         g_signal_connect(G_OBJECT(renderer), "accel-edited",
3140                          G_CALLBACK(accel_store_edited_cb), accel_store);
3141
3142
3143         g_object_set (renderer,
3144                       "editable", TRUE,
3145                       "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3146                       NULL);
3147
3148         column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3149                                                           renderer,
3150                                                           "text", AE_KEY,
3151                                                           NULL);
3152
3153         gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3154         gtk_tree_view_column_set_resizable(column, TRUE);
3155         gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3156
3157         renderer = gtk_cell_renderer_text_new();
3158
3159         column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3160                                                           renderer,
3161                                                           "text", AE_TOOLTIP,
3162                                                           NULL);
3163
3164         gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3165         gtk_tree_view_column_set_resizable(column, TRUE);
3166         gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3167
3168         renderer = gtk_cell_renderer_text_new();
3169
3170         column = gtk_tree_view_column_new_with_attributes("Accel",
3171                                                           renderer,
3172                                                           "text", AE_ACCEL,
3173                                                           NULL);
3174
3175         gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3176         gtk_tree_view_column_set_resizable(column, TRUE);
3177         gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3178
3179         accel_store_populate();
3180         gtk_container_add(GTK_CONTAINER(scrolled), accel_view);
3181         gtk_widget_show(accel_view);
3182
3183         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3184
3185         button = pref_button_new(NULL, NULL, _("Defaults"), FALSE,
3186                                  G_CALLBACK(accel_default_cb), accel_view);
3187         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3188         gtk_widget_show(button);
3189
3190         button = pref_button_new(NULL, NULL, _("Reset selected"), FALSE,
3191                                  G_CALLBACK(accel_reset_cb), accel_view);
3192         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3193         gtk_widget_show(button);
3194 }
3195
3196 /* toolbar tab */
3197 static void config_tab_toolbar(GtkWidget *notebook)
3198 {
3199         GtkWidget *vbox;
3200         GtkWidget *toolbardata;
3201         LayoutWindow *lw;
3202
3203         lw = layout_window_list->data;
3204
3205         vbox = scrolled_notebook_page(notebook, _("Toolbar"));
3206
3207         toolbardata = toolbar_select_new(lw);
3208         gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3209         gtk_widget_show(vbox);
3210 }
3211
3212 /* stereo tab */
3213 static void config_tab_stereo(GtkWidget *notebook)
3214 {
3215         GtkWidget *vbox;
3216         GtkWidget *group;
3217         GtkWidget *group2;
3218         GtkWidget *table;
3219         GtkWidget *box;
3220         GtkWidget *box2;
3221         GtkWidget *fs_button;
3222         vbox = scrolled_notebook_page(notebook, _("Stereo"));
3223
3224         group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
3225
3226         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3227         add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
3228
3229         table = pref_table_new(group, 2, 2, TRUE, FALSE);
3230         box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, NULL);
3231         pref_checkbox_new_int(box, _("Mirror left image"),
3232                               options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
3233         box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, NULL);
3234         pref_checkbox_new_int(box, _("Flip left image"),
3235                               options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
3236         box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, NULL);
3237         pref_checkbox_new_int(box, _("Mirror right image"),
3238                               options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
3239         box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, NULL);
3240         pref_checkbox_new_int(box, _("Flip right image"),
3241                               options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
3242         pref_checkbox_new_int(group, _("Swap left and right images"),
3243                               options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
3244         pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
3245                               options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
3246
3247         group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
3248         fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
3249                               options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
3250         box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
3251         pref_checkbox_link_sensitivity(fs_button, box2);
3252         table = pref_table_new(box2, 2, 1, FALSE, FALSE);
3253         add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
3254         table = pref_table_new(box2, 2, 2, TRUE, FALSE);
3255         box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, NULL);
3256         pref_checkbox_new_int(box, _("Mirror left image"),
3257                               options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
3258         box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, NULL);
3259         pref_checkbox_new_int(box, _("Flip left image"),
3260                               options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
3261         box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, NULL);
3262         pref_checkbox_new_int(box, _("Mirror right image"),
3263                               options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
3264         box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, NULL);
3265         pref_checkbox_new_int(box, _("Flip right image"),
3266                               options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
3267         pref_checkbox_new_int(box2, _("Swap left and right images"),
3268                               options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
3269         pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
3270                               options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
3271
3272         group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
3273         table = pref_table_new(group2, 5, 3, FALSE, FALSE);
3274         pref_table_spin_new_int(table, 0, 0, _("Width"), NULL,
3275                           1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
3276         pref_table_spin_new_int(table, 3, 0,  _("Height"), NULL,
3277                           1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
3278         pref_table_spin_new_int(table, 0, 1,  _("Left X"), NULL,
3279                           0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
3280         pref_table_spin_new_int(table, 3, 1,  _("Left Y"), NULL,
3281                           0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
3282         pref_table_spin_new_int(table, 0, 2,  _("Right X"), NULL,
3283                           0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
3284         pref_table_spin_new_int(table, 3, 2,  _("Right Y"), NULL,
3285                           0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
3286
3287 }
3288
3289 /* Main preferences window */
3290 static void config_window_create(void)
3291 {
3292         GtkWidget *win_vbox;
3293         GtkWidget *hbox;
3294         GtkWidget *notebook;
3295         GtkWidget *button;
3296         GtkWidget *ct_button;
3297
3298         if (!c_options) c_options = init_options(NULL);
3299
3300         configwindow = window_new(GTK_WINDOW_TOPLEVEL, "preferences", PIXBUF_INLINE_ICON_CONFIG, NULL, _("Preferences"));
3301         DEBUG_NAME(configwindow);
3302         gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
3303         g_signal_connect(G_OBJECT(configwindow), "delete_event",
3304                          G_CALLBACK(config_window_delete), NULL);
3305         gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
3306         gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
3307         gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
3308
3309         win_vbox = gtk_vbox_new(FALSE, PREF_PAD_SPACE);
3310         gtk_container_add(GTK_CONTAINER(configwindow), win_vbox);
3311         gtk_widget_show(win_vbox);
3312
3313         notebook = gtk_notebook_new();
3314         gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
3315         gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
3316         gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
3317
3318         config_tab_general(notebook);
3319         config_tab_image(notebook);
3320         config_tab_osd(notebook);
3321         config_tab_windows(notebook);
3322         config_tab_accelerators(notebook);
3323         config_tab_files(notebook);
3324         config_tab_metadata(notebook);
3325         config_tab_keywords(notebook);
3326         config_tab_color(notebook);
3327         config_tab_stereo(notebook);
3328         config_tab_behavior(notebook);
3329         config_tab_toolbar(notebook);
3330
3331         hbox = gtk_hbutton_box_new();
3332         gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
3333         gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
3334         gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
3335         gtk_widget_show(hbox);
3336
3337         button = pref_button_new(NULL, GTK_STOCK_HELP, NULL, FALSE,
3338                                  G_CALLBACK(config_window_help_cb), notebook);
3339         gtk_container_add(GTK_CONTAINER(hbox), button);
3340         gtk_widget_set_can_default(button, TRUE);
3341         gtk_widget_show(button);
3342
3343         button = pref_button_new(NULL, GTK_STOCK_OK, NULL, FALSE,
3344                                  G_CALLBACK(config_window_ok_cb), NULL);
3345         gtk_container_add(GTK_CONTAINER(hbox), button);
3346         gtk_widget_set_can_default(button, TRUE);
3347         gtk_widget_grab_default(button);
3348         gtk_widget_show(button);
3349
3350         ct_button = button;
3351
3352         button = pref_button_new(NULL, GTK_STOCK_SAVE, NULL, FALSE,
3353                                  G_CALLBACK(config_window_save_cb), NULL);
3354         gtk_container_add(GTK_CONTAINER(hbox), button);
3355         gtk_widget_set_can_default(button, TRUE);
3356         gtk_widget_show(button);
3357
3358         button = pref_button_new(NULL, GTK_STOCK_APPLY, NULL, FALSE,
3359                                  G_CALLBACK(config_window_apply_cb), NULL);
3360         gtk_container_add(GTK_CONTAINER(hbox), button);
3361         gtk_widget_set_can_default(button, TRUE);
3362         gtk_widget_show(button);
3363
3364         button = pref_button_new(NULL, GTK_STOCK_CANCEL, NULL, FALSE,
3365                                  G_CALLBACK(config_window_close_cb), NULL);
3366         gtk_container_add(GTK_CONTAINER(hbox), button);
3367         gtk_widget_set_can_default(button, TRUE);
3368         gtk_widget_show(button);
3369
3370         if (!generic_dialog_get_alternative_button_order(configwindow))
3371                 {
3372                 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
3373                 }
3374
3375         gtk_widget_show(notebook);
3376
3377         gtk_widget_show(configwindow);
3378 }
3379
3380 /*
3381  *-----------------------------------------------------------------------------
3382  * config window show (public)
3383  *-----------------------------------------------------------------------------
3384  */
3385
3386 void show_config_window(void)
3387 {
3388         if (configwindow)
3389                 {
3390                 gtk_window_present(GTK_WINDOW(configwindow));
3391                 return;
3392                 }
3393
3394         config_window_create();
3395 }
3396
3397 /*
3398  *-----------------
3399  * about window
3400  *-----------------
3401  */
3402
3403 void show_about_window(LayoutWindow *lw)
3404 {
3405         GdkPixbuf *pixbuf_logo;
3406         GdkPixbuf *pixbuf_icon;
3407         gchar *authors[1000];
3408         gchar *comment;
3409         gint i_authors = 0;
3410         gchar *path;
3411         GString *copyright;
3412         gchar *zd_path;
3413         ZoneDetect *cd;
3414         FILE *fp = NULL;
3415 #define LINE_LENGTH 1000
3416         gchar line[LINE_LENGTH];
3417
3418         copyright = g_string_new(NULL);
3419         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");
3420
3421         zd_path = g_build_filename(GQ_BIN_DIR, TIMEZONE_DATABASE, NULL);
3422         cd = ZDOpenDatabase(zd_path);
3423         if (cd)
3424                 {
3425                 copyright = g_string_append(copyright, ZDGetNotice(cd));
3426                 }
3427         ZDCloseDatabase(cd);
3428         g_free(zd_path);
3429
3430         authors[0] = NULL;
3431         path = g_build_filename(GQ_HELPDIR, "AUTHORS", NULL);
3432         fp = fopen(path, "r");
3433         if (fp)
3434                 {
3435                 while(fgets(line, LINE_LENGTH, fp))
3436                         {
3437                         /* get rid of ending \n from fgets */
3438                         line[strlen(line) - 1] = '\0';
3439                         authors[i_authors] = g_strdup(line);
3440                         i_authors++;
3441                         }
3442                 authors[i_authors] = NULL;
3443                 fclose(fp);
3444                 }
3445         g_free(path);
3446
3447         comment = g_strconcat("Development and bug reports:\n", GQ_EMAIL_ADDRESS,
3448                                                 "\nhttps://github.com/BestImageViewer/geeqie/issues",NULL);
3449
3450         pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
3451         pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
3452         gtk_show_about_dialog(GTK_WINDOW(lw->window),
3453                 "title", _("About Geeqie"),
3454                 "resizable", TRUE,
3455                 "program-name", GQ_APPNAME,
3456                 "version", VERSION,
3457                 "logo", pixbuf_logo,
3458                 "icon", pixbuf_icon,
3459                 "website", GQ_WEBSITE,
3460                 "website-label", "Website",
3461                 "comments", comment,
3462                 "authors", authors,
3463                 "translator-credits", _("translator-credits"),
3464                 "wrap-license", TRUE,
3465                 "license", copyright->str,
3466                 NULL);
3467
3468         g_string_free(copyright, TRUE);
3469
3470         gint n = 0;
3471         while(n < i_authors)
3472                 {
3473                 g_free(authors[n]);
3474                 n++;
3475                 }
3476         g_free(comment);
3477
3478         return;
3479 }
3480
3481 static void image_overlay_set_text_colours()
3482 {
3483         c_options->image_overlay.text_red = options->image_overlay.text_red;
3484         c_options->image_overlay.text_green = options->image_overlay.text_green;
3485         c_options->image_overlay.text_blue = options->image_overlay.text_blue;
3486         c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
3487         c_options->image_overlay.background_red = options->image_overlay.background_red;
3488         c_options->image_overlay.background_green = options->image_overlay.background_green;
3489         c_options->image_overlay.background_blue = options->image_overlay.background_blue;
3490         c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
3491 }
3492 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */