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