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