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