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