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