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