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