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