fixed spelling mistakes
[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 program 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 #if GTK_CHECK_VERSION(3,16,0)
2671         gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2672         gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2673 #else
2674         gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2675 #endif
2676
2677         ct_button = pref_checkbox_new_int(group, _("1) Save metadata in image files, or sidecar files, according to the XMP standard"),
2678                               options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2679 #ifndef HAVE_EXIV2
2680         gtk_widget_set_sensitive(ct_button, FALSE);
2681 #endif
2682
2683         pref_checkbox_new_int(group, _("2) Save metadata in '.metadata' folder, local to image folder (non-standard)"),
2684                               options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2685
2686         text = g_strdup_printf(_("3) Save metadata in Geeqie private directory '%s'"), get_metadata_cache_dir());
2687         label = pref_label_new(group, text);
2688 #if GTK_CHECK_VERSION(3,16,0)
2689         gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2690         gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2691 #else
2692         gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2693 #endif
2694         gtk_misc_set_padding(GTK_MISC(label), 22, 0);
2695         g_free(text);
2696
2697         group = pref_group_new(vbox, FALSE, _("Step 1: Write to image files"), GTK_ORIENTATION_VERTICAL);
2698 #ifndef HAVE_EXIV2
2699         gtk_widget_set_sensitive(group, FALSE);
2700 #endif
2701
2702         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2703         pref_checkbox_link_sensitivity(ct_button, hbox);
2704
2705         pref_checkbox_new_int(hbox, _("Store metadata also in legacy IPTC tags (converted according to IPTC4XMP standard)"),
2706                               options->metadata.save_legacy_IPTC, &c_options->metadata.save_legacy_IPTC);
2707
2708         pref_checkbox_new_int(hbox, _("Warn if the image files are unwritable"),
2709                               options->metadata.warn_on_write_problems, &c_options->metadata.warn_on_write_problems);
2710
2711         pref_checkbox_new_int(hbox, _("Ask before writing to image files"),
2712                               options->metadata.confirm_write, &c_options->metadata.confirm_write);
2713
2714         pref_checkbox_new_int(hbox, _("Create sidecar files named image.ext.xmp (as opposed to image.xmp)"),
2715                               options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2716
2717         group = pref_group_new(vbox, FALSE, _("Step 2 and 3: write to Geeqie private files"), GTK_ORIENTATION_VERTICAL);
2718 #ifndef HAVE_EXIV2
2719         gtk_widget_set_sensitive(group, FALSE);
2720 #endif
2721
2722         pref_checkbox_new_int(group, _("Use GQview legacy metadata format (supports only keywords and comments) instead of XMP"),
2723                               options->metadata.save_legacy_format, &c_options->metadata.save_legacy_format);
2724
2725
2726         group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2727         pref_checkbox_new_int(group, _("Write the same description tags (keywords, comment, etc.) to all grouped sidecars"),
2728                               options->metadata.sync_grouped_files, &c_options->metadata.sync_grouped_files);
2729
2730         pref_checkbox_new_int(group, _("Allow keywords to differ only in case"),
2731                               options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2732
2733         ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"),
2734                               options->metadata.write_orientation, &c_options->metadata.write_orientation);
2735 #ifndef HAVE_EXIV2
2736         gtk_widget_set_sensitive(ct_button, FALSE);
2737 #endif
2738
2739         group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2740
2741         ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"),
2742                               options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
2743
2744         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2745         pref_checkbox_link_sensitivity(ct_button, hbox);
2746
2747         pref_spin_new_int(hbox, _("Timeout (seconds):"), NULL, 0, 900, 1,
2748                               options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
2749
2750         pref_checkbox_new_int(group, _("Write metadata on image change"),
2751                               options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
2752
2753         pref_checkbox_new_int(group, _("Write metadata on directory change"),
2754                               options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
2755
2756         group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
2757
2758         ct_button = pref_checkbox_new_int(group, _("Read metadata in background"),
2759                                           options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
2760         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");
2761 }
2762
2763 /* keywords tab */
2764
2765 typedef struct _KeywordFindData KeywordFindData;
2766 struct _KeywordFindData
2767 {
2768         GenericDialog *gd;
2769
2770         GList *list;
2771         GList *list_dir;
2772
2773         GtkWidget *button_close;
2774         GtkWidget *button_stop;
2775         GtkWidget *button_start;
2776         GtkWidget *progress;
2777         GtkWidget *spinner;
2778
2779         GtkWidget *group;
2780         GtkWidget *entry;
2781
2782         gboolean recurse;
2783
2784         guint idle_id; /* event source id */
2785 };
2786
2787 #define KEYWORD_DIALOG_WIDTH 400
2788
2789 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
2790 {
2791         GList *list_d = NULL;
2792         GList *list_f = NULL;
2793
2794         if (kfd->recurse)
2795                 {
2796                 filelist_read(dir_fd, &list_f, &list_d);
2797                 }
2798         else
2799                 {
2800                 filelist_read(dir_fd, &list_f, NULL);
2801                 }
2802
2803         list_f = filelist_filter(list_f, FALSE);
2804         list_d = filelist_filter(list_d, TRUE);
2805
2806         kfd->list = g_list_concat(list_f, kfd->list);
2807         kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
2808 }
2809
2810 static void keywords_find_reset(KeywordFindData *kfd)
2811 {
2812         filelist_free(kfd->list);
2813         kfd->list = NULL;
2814
2815         filelist_free(kfd->list_dir);
2816         kfd->list_dir = NULL;
2817 }
2818
2819 static void keywords_find_close_cb(GenericDialog *fd, gpointer data)
2820 {
2821         KeywordFindData *kfd = data;
2822
2823         if (!gtk_widget_get_sensitive(kfd->button_close)) return;
2824
2825         keywords_find_reset(kfd);
2826         generic_dialog_close(kfd->gd);
2827         g_free(kfd);
2828 }
2829
2830 static void keywords_find_finish(KeywordFindData *kfd)
2831 {
2832         keywords_find_reset(kfd);
2833
2834         gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
2835         spinner_set_interval(kfd->spinner, -1);
2836
2837         gtk_widget_set_sensitive(kfd->group, TRUE);
2838         gtk_widget_set_sensitive(kfd->button_start, TRUE);
2839         gtk_widget_set_sensitive(kfd->button_stop, FALSE);
2840         gtk_widget_set_sensitive(kfd->button_close, TRUE);
2841 }
2842
2843 static void keywords_find_stop_cb(GenericDialog *fd, gpointer data)
2844 {
2845         KeywordFindData *kfd = data;
2846
2847         g_idle_remove_by_data(kfd);
2848
2849         keywords_find_finish(kfd);
2850 }
2851
2852 static gboolean keywords_find_file(gpointer data)
2853 {
2854         KeywordFindData *kfd = data;
2855         GtkTextIter iter;
2856         GtkTextBuffer *buffer;
2857         gchar *tmp;
2858         GList *keywords;
2859
2860         if (kfd->list)
2861                 {
2862                 FileData *fd;
2863
2864                 fd = kfd->list->data;
2865                 kfd->list = g_list_remove(kfd->list, fd);
2866
2867                 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
2868                 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
2869
2870                 while (keywords)
2871                         {
2872                         gtk_text_buffer_get_end_iter(buffer, &iter);
2873                         tmp = g_strconcat(keywords->data, "\n", NULL);
2874                         gtk_text_buffer_insert(buffer, &iter, tmp, -1);
2875                         g_free(tmp);
2876                         keywords = keywords->next;
2877                         }
2878
2879                 gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
2880                 file_data_unref(fd);
2881                 string_list_free(keywords);
2882
2883                 return (TRUE);
2884                 }
2885         else if (kfd->list_dir)
2886                 {
2887                 FileData *fd;
2888
2889                 fd = kfd->list_dir->data;
2890                 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
2891
2892                 keywords_find_folder(kfd, fd);
2893
2894                 file_data_unref(fd);
2895
2896                 return TRUE;
2897                 }
2898
2899         keywords_find_finish(kfd);
2900
2901         return FALSE;
2902 }
2903
2904 static void keywords_find_start_cb(GenericDialog *fd, gpointer data)
2905 {
2906         KeywordFindData *kfd = data;
2907         gchar *path;
2908
2909         if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
2910
2911         path = remove_trailing_slash((gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
2912         parse_out_relatives(path);
2913
2914         if (!isdir(path))
2915                 {
2916                 warning_dialog(_("Invalid folder"),
2917                                 _("The specified folder can not be found."),
2918                                 GTK_STOCK_DIALOG_WARNING, kfd->gd->dialog);
2919                 }
2920         else
2921                 {
2922                 FileData *dir_fd;
2923
2924                 gtk_widget_set_sensitive(kfd->group, FALSE);
2925                 gtk_widget_set_sensitive(kfd->button_start, FALSE);
2926                 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
2927                 gtk_widget_set_sensitive(kfd->button_close, FALSE);
2928                 spinner_set_interval(kfd->spinner, SPINNER_SPEED);
2929
2930                 dir_fd = file_data_new_dir(path);
2931                 keywords_find_folder(kfd, dir_fd);
2932                 file_data_unref(dir_fd);
2933                 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
2934                 }
2935
2936         g_free(path);
2937 }
2938
2939 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
2940 {
2941         KeywordFindData *kfd;
2942         GtkWidget *hbox;
2943         GtkWidget *label;
2944
2945         kfd = g_new0(KeywordFindData, 1);
2946
2947         kfd->gd = generic_dialog_new(_("Search for keywords"),
2948                                                                         "search_for_keywords",
2949                                                                         widget, FALSE,
2950                                                                         NULL, kfd);
2951         gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
2952         kfd->gd->cancel_cb = keywords_find_close_cb;
2953         kfd->button_close = generic_dialog_add_button(kfd->gd, GTK_STOCK_CLOSE, NULL,
2954                                                      keywords_find_close_cb, FALSE);
2955         kfd->button_start = generic_dialog_add_button(kfd->gd, GTK_STOCK_OK, _("S_tart"),
2956                                                      keywords_find_start_cb, FALSE);
2957         kfd->button_stop = generic_dialog_add_button(kfd->gd, GTK_STOCK_STOP, NULL,
2958                                                     keywords_find_stop_cb, FALSE);
2959         gtk_widget_set_sensitive(kfd->button_stop, FALSE);
2960
2961         generic_dialog_add_message(kfd->gd, NULL, _("Search for keywords"), NULL, FALSE);
2962
2963         hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
2964         pref_spacer(hbox, PREF_PAD_INDENT);
2965         kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2966
2967         hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2968         pref_label_new(hbox, _("Folder:"));
2969
2970         label = tab_completion_new(&kfd->entry, path, NULL, NULL, NULL, NULL);
2971         tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
2972         gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
2973         gtk_widget_show(label);
2974
2975         pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
2976
2977         pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
2978         hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2979
2980         kfd->progress = gtk_entry_new();
2981         gtk_widget_set_can_focus(kfd->progress, FALSE);
2982         gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
2983         gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
2984         gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
2985         gtk_widget_show(kfd->progress);
2986
2987         kfd->spinner = spinner_new(NULL, -1);
2988         gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
2989         gtk_widget_show(kfd->spinner);
2990
2991         kfd->list = NULL;
2992
2993         gtk_widget_show(kfd->gd->dialog);
2994 }
2995
2996 static void keywords_find_cb(GtkWidget *widget, gpointer data)
2997 {
2998         const gchar *path = layout_get_path(NULL);
2999
3000         if (!path || !*path) path = homedir();
3001         keywords_find_dialog(widget, path);
3002 }
3003
3004 static void config_tab_keywords_save()
3005 {
3006         GtkTextIter start, end;
3007         GtkTextBuffer *buffer;
3008         GList *kw_list = NULL;
3009         GList *work;
3010         gchar *buffer_text;
3011         gchar *kw_split;
3012         gboolean found;
3013
3014         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3015         gtk_text_buffer_get_bounds(buffer, &start, &end);
3016
3017         buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3018
3019         kw_split = strtok(buffer_text, "\n");
3020         while (kw_split != NULL)
3021                 {
3022                 work = kw_list;
3023                 found = FALSE;
3024                 while (work)
3025                         {
3026                         if (g_strcmp0(work->data, kw_split) == 0)
3027                                 {
3028                                 found = TRUE;
3029                                 break;
3030                                 }
3031                         work = work->next;
3032                         }
3033                 if (!found)
3034                         {
3035                         kw_list = g_list_append(kw_list, g_strdup(kw_split));
3036                         }
3037                 kw_split = strtok(NULL, "\n");
3038                 }
3039
3040         keyword_list_set(kw_list);
3041
3042         string_list_free(kw_list);
3043         g_free(buffer_text);
3044 }
3045
3046 static void config_tab_keywords(GtkWidget *notebook)
3047 {
3048         GtkWidget *hbox;
3049         GtkWidget *vbox;
3050         GtkWidget *group;
3051         GtkWidget *button;
3052         GtkWidget *scrolled;
3053         GtkTextIter iter;
3054         GtkTextBuffer *buffer;
3055         gchar *tmp;
3056
3057         vbox = scrolled_notebook_page(notebook, _("Keywords"));
3058
3059         group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
3060
3061         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3062
3063         button = pref_button_new(hbox, GTK_STOCK_EXECUTE, _("Search"), FALSE,
3064                                    G_CALLBACK(keywords_find_cb), keyword_text);
3065         gtk_widget_set_tooltip_text(button, "Search for existing keywords");
3066
3067
3068         keyword_text = gtk_text_view_new();
3069         gtk_widget_set_size_request(keyword_text, 20, 20);
3070         scrolled = gtk_scrolled_window_new(NULL, NULL);
3071         gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3072         gtk_widget_show(scrolled);
3073
3074         gtk_container_add(GTK_CONTAINER(scrolled), keyword_text);
3075         gtk_widget_show(keyword_text);
3076
3077         gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
3078
3079         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3080         gtk_text_buffer_create_tag(buffer, "monospace",
3081                                 "family", "monospace", NULL);
3082
3083         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3084         gtk_text_buffer_get_start_iter(buffer, &iter);
3085         gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3086         gchar *path;
3087
3088         path = g_build_filename(get_rc_dir(), "keywords", NULL);
3089
3090         GList *kwl = keyword_list_get();
3091         kwl = g_list_first(kwl);
3092         while (kwl)
3093         {
3094                 gtk_text_buffer_get_end_iter (buffer, &iter);
3095             tmp = g_strconcat(kwl->data, "\n", NULL);
3096                 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3097                 kwl = kwl->next;
3098                 g_free(tmp);
3099         }
3100
3101         gtk_text_buffer_set_modified(buffer, FALSE);
3102
3103         g_free(path);
3104 }
3105
3106 /* metadata tab */
3107 #ifdef HAVE_LCMS
3108 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3109 {
3110         gint *option = data;
3111
3112         switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3113                 {
3114                 case 0:
3115                 default:
3116                         *option = INTENT_PERCEPTUAL;
3117                         break;
3118                 case 1:
3119                         *option = INTENT_RELATIVE_COLORIMETRIC;
3120                         break;
3121                 case 2:
3122                         *option = INTENT_SATURATION;
3123                         break;
3124                 case 3:
3125                         *option = INTENT_ABSOLUTE_COLORIMETRIC;
3126                         break;
3127                 }
3128 }
3129
3130 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3131                              gint option, gint *option_c)
3132 {
3133         GtkWidget *combo;
3134         gint current = 0;
3135
3136         *option_c = option;
3137
3138         pref_table_label(table, column, row, text, 0.0);
3139
3140         combo = gtk_combo_box_text_new();
3141
3142         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3143         if (option == INTENT_PERCEPTUAL) current = 0;
3144         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3145         if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3146         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3147         if (option == INTENT_SATURATION) current = 2;
3148         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3149         if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3150
3151         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3152
3153         gtk_widget_set_tooltip_text(combo,"Refer to the lcms documentation for the defaults used when the selected Intent is not available");
3154
3155         g_signal_connect(G_OBJECT(combo), "changed",
3156                          G_CALLBACK(intent_menu_cb), option_c);
3157
3158         gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, 0, 0, 0);
3159         gtk_widget_show(combo);
3160 }
3161 #endif
3162
3163 static void config_tab_color(GtkWidget *notebook)
3164 {
3165         GtkWidget *label;
3166         GtkWidget *vbox;
3167         GtkWidget *group;
3168         GtkWidget *tabcomp;
3169         GtkWidget *table;
3170         gint i;
3171
3172         vbox = scrolled_notebook_page(notebook, _("Color management"));
3173
3174         group =  pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3175 #ifndef HAVE_LCMS
3176         gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3177 #endif
3178
3179         table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3180         gtk_table_set_col_spacings(GTK_TABLE(table), PREF_PAD_GAP);
3181
3182         label = pref_table_label(table, 0, 0, _("Type"), 0.0);
3183         pref_label_bold(label, TRUE, FALSE);
3184
3185         label = pref_table_label(table, 1, 0, _("Menu name"), 0.0);
3186         pref_label_bold(label, TRUE, FALSE);
3187
3188         label = pref_table_label(table, 2, 0, _("File"), 0.0);
3189         pref_label_bold(label, TRUE, FALSE);
3190
3191         for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3192                 {
3193                 GtkWidget *entry;
3194                 gchar *buf;
3195
3196                 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3197                 pref_table_label(table, 0, i + 1, buf, 1.0);
3198                 g_free(buf);
3199
3200                 entry = gtk_entry_new();
3201                 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3202                 if (options->color_profile.input_name[i])
3203                         {
3204                         gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3205                         }
3206                 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, i + 1, i + 2,
3207                                  GTK_FILL | GTK_EXPAND, 0, 0, 0);
3208                 gtk_widget_show(entry);
3209                 color_profile_input_name_entry[i] = entry;
3210
3211                 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], NULL, ".icc", "ICC Files", NULL);
3212                 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3213                 gtk_widget_set_size_request(entry, 160, -1);
3214                 gtk_table_attach(GTK_TABLE(table), tabcomp, 2, 3, i + 1, i + 2,
3215                                  GTK_FILL | GTK_EXPAND, 0, 0, 0);
3216                 gtk_widget_show(tabcomp);
3217                 color_profile_input_file_entry[i] = entry;
3218                 }
3219
3220         group =  pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3221 #ifndef HAVE_LCMS
3222         gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3223 #endif
3224         pref_checkbox_new_int(group, _("Use system screen profile if available"),
3225                               options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3226
3227         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3228
3229         pref_table_label(table, 0, 0, _("Screen:"), 1.0);
3230         tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3231                                      options->color_profile.screen_file, NULL, ".icc", "ICC Files", NULL);
3232         tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3233         gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3234 #ifdef HAVE_LCMS
3235         add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3236 #endif
3237         gtk_table_attach(GTK_TABLE(table), tabcomp, 1, 2,
3238                          0, 1,
3239                          GTK_FILL | GTK_EXPAND, 0, 0, 0);
3240
3241         gtk_widget_show(tabcomp);
3242 }
3243
3244 /* advanced entry tab */
3245 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer data)
3246 {
3247         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3248                 {
3249                 c_options->file_ops.use_system_trash = FALSE;
3250                 c_options->file_ops.no_trash = FALSE;
3251                 }
3252 }
3253
3254 static void use_system_trash_cb(GtkWidget *widget, gpointer data)
3255 {
3256         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3257                 {
3258                 c_options->file_ops.use_system_trash = TRUE;
3259                 c_options->file_ops.no_trash = FALSE;
3260                 }
3261 }
3262
3263 static void use_no_cache_cb(GtkWidget *widget, gpointer data)
3264 {
3265         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3266                 {
3267                 c_options->file_ops.no_trash = TRUE;
3268                 }
3269 }
3270
3271 static void config_tab_behavior(GtkWidget *notebook)
3272 {
3273         GtkWidget *hbox;
3274         GtkWidget *vbox;
3275         GtkWidget *group;
3276         GtkWidget *button;
3277         GtkWidget *tabcomp;
3278         GtkWidget *ct_button;
3279         GtkWidget *spin;
3280         GtkWidget *table;
3281         GtkWidget *marks;
3282         GtkWidget *with_rename;
3283         GtkWidget *collections_on_top;
3284         GtkWidget *hide_window_in_fullscreen;
3285         GtkWidget *checkbox;
3286
3287         vbox = scrolled_notebook_page(notebook, _("Behavior"));
3288
3289         group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3290
3291         pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3292                               options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3293         pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3294                               options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3295         pref_checkbox_new_int(group, _("Enable Delete key"),
3296                               options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3297
3298         ct_button = pref_radiobutton_new(group, NULL, _("Use Geeqie trash location"),
3299                                         !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),NULL);
3300
3301         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3302         pref_checkbox_link_sensitivity(ct_button, hbox);
3303
3304         pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3305         pref_label_new(hbox, _("Folder:"));
3306
3307         tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, NULL, NULL, NULL, NULL);
3308         tab_completion_add_select_button(safe_delete_path_entry, NULL, TRUE);
3309         gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3310         gtk_widget_show(tabcomp);
3311
3312         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3313         pref_checkbox_link_sensitivity(ct_button, hbox);
3314
3315         pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3316         spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MB"),
3317                                  0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3318         gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3319         button = pref_button_new(NULL, NULL, _("View"), FALSE,
3320                                  G_CALLBACK(safe_delete_view_cb), NULL);
3321         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3322         gtk_widget_show(button);
3323
3324         button = pref_button_new(NULL, GTK_STOCK_CLEAR, NULL, FALSE,
3325                                  G_CALLBACK(safe_delete_clear_cb), NULL);
3326         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3327         pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3328                                         options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), NULL);
3329
3330         pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3331                         options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), NULL);
3332
3333         gtk_widget_show(button);
3334
3335         pref_spacer(group, PREF_PAD_GROUP);
3336
3337
3338         group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3339
3340         pref_checkbox_new_int(group, _("Descend folders in tree view"),
3341                               options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3342
3343         pref_checkbox_new_int(group, _("In place renaming"),
3344                               options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3345
3346         pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3347                               options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3348
3349         marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3350                                 options->marks_save, &c_options->marks_save);
3351         gtk_widget_set_tooltip_text(marks,"Note that marks linked to a keyword will be saved irrespective of this setting");
3352
3353         with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3354                                 options->with_rename, &c_options->with_rename);
3355         gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3356
3357         collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3358                                 options->collections_on_top, &c_options->collections_on_top);
3359         gtk_widget_set_tooltip_text(collections_on_top,"Open collections window on top");
3360
3361         hide_window_in_fullscreen = pref_checkbox_new_int(group, _("Hide window in fullscreen"),
3362                                 options->hide_window_in_fullscreen, &c_options->hide_window_in_fullscreen);
3363         gtk_widget_set_tooltip_text(hide_window_in_fullscreen,"When alt-tabbing, prevent Geeqie window showing twice");
3364
3365         pref_spin_new_int(group, _("Recent folder list maximum size"), NULL,
3366                           1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3367
3368         pref_spin_new_int(group, _("Drag'n drop icon size"), NULL,
3369                           16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3370
3371         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3372         add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
3373
3374         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3375         add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3376
3377         pref_spacer(group, PREF_PAD_GROUP);
3378
3379         group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3380
3381         pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3382                               options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3383         pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), NULL,
3384                           1, 32, 1, options->keyboard_scroll_step, (int *)&c_options->keyboard_scroll_step);
3385         pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3386                               options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3387         pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3388                               options->image_lm_click_nav, &c_options->image_lm_click_nav);
3389         pref_checkbox_new_int(group, _("Play video by left click on image"),
3390                               options->image_l_click_video, &c_options->image_l_click_video);
3391         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3392         add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3393
3394         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3395         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3396         add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3397         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3398         add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3399
3400         pref_spacer(group, PREF_PAD_GROUP);
3401
3402         group = pref_group_new(vbox, FALSE, _("GPU"), GTK_ORIENTATION_VERTICAL);
3403
3404         checkbox = pref_checkbox_new_int(group, _("Override disable GPU"),
3405                                 options->override_disable_gpu, &c_options->override_disable_gpu);
3406         gtk_widget_set_tooltip_text(checkbox, "Contact the developers for usage");
3407
3408 #ifdef DEBUG
3409         pref_spacer(group, PREF_PAD_GROUP);
3410
3411         group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3412
3413         pref_spin_new_int(group, _("Debug level:"), NULL,
3414                           DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3415
3416         pref_checkbox_new_int(group, _("Timer data"),
3417                         options->log_window.timer_data, &c_options->log_window.timer_data);
3418
3419         pref_spin_new_int(group, _("Log Window max. lines:"), NULL,
3420                           1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3421 #endif
3422 }
3423
3424 /* accelerators tab */
3425 static void config_tab_accelerators(GtkWidget *notebook)
3426 {
3427         GtkWidget *hbox;
3428         GtkWidget *vbox;
3429         GtkWidget *group;
3430         GtkWidget *button;
3431         GtkWidget *scrolled;
3432         GtkWidget *accel_view;
3433         GtkCellRenderer *renderer;
3434         GtkTreeSelection *selection;
3435         GtkTreeViewColumn *column;
3436
3437         vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3438
3439         group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3440
3441         scrolled = gtk_scrolled_window_new(NULL, NULL);
3442         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3443         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3444         gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3445         gtk_widget_show(scrolled);
3446
3447         accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3448
3449         accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3450         g_object_unref(accel_store);
3451         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3452         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3453
3454         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3455
3456         renderer = gtk_cell_renderer_text_new();
3457
3458         column = gtk_tree_view_column_new_with_attributes(_("Action"),
3459                                                           renderer,
3460                                                           "text", AE_ACTION,
3461                                                           NULL);
3462
3463         gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3464         gtk_tree_view_column_set_resizable(column, TRUE);
3465         gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3466
3467
3468         renderer = gtk_cell_renderer_accel_new();
3469         g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3470                          G_CALLBACK(accel_store_cleared_cb), accel_store);
3471         g_signal_connect(G_OBJECT(renderer), "accel-edited",
3472                          G_CALLBACK(accel_store_edited_cb), accel_store);
3473
3474
3475         g_object_set (renderer,
3476                       "editable", TRUE,
3477                       "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3478                       NULL);
3479
3480         column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3481                                                           renderer,
3482                                                           "text", AE_KEY,
3483                                                           NULL);
3484
3485         gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3486         gtk_tree_view_column_set_resizable(column, TRUE);
3487         gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3488
3489         renderer = gtk_cell_renderer_text_new();
3490
3491         column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3492                                                           renderer,
3493                                                           "text", AE_TOOLTIP,
3494                                                           NULL);
3495
3496         gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3497         gtk_tree_view_column_set_resizable(column, TRUE);
3498         gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3499
3500         renderer = gtk_cell_renderer_text_new();
3501
3502         column = gtk_tree_view_column_new_with_attributes("Accel",
3503                                                           renderer,
3504                                                           "text", AE_ACCEL,
3505                                                           NULL);
3506
3507         gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3508         gtk_tree_view_column_set_resizable(column, TRUE);
3509         gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3510
3511         accel_store_populate();
3512         gtk_container_add(GTK_CONTAINER(scrolled), accel_view);
3513         gtk_widget_show(accel_view);
3514
3515         hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3516
3517         button = pref_button_new(NULL, NULL, _("Defaults"), FALSE,
3518                                  G_CALLBACK(accel_default_cb), accel_view);
3519         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3520         gtk_widget_show(button);
3521
3522         button = pref_button_new(NULL, NULL, _("Reset selected"), FALSE,
3523                                  G_CALLBACK(accel_reset_cb), accel_view);
3524         gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3525         gtk_widget_show(button);
3526 }
3527
3528 /* toolbar main tab */
3529 static void config_tab_toolbar_main(GtkWidget *notebook)
3530 {
3531         GtkWidget *vbox;
3532         GtkWidget *toolbardata;
3533         LayoutWindow *lw;
3534
3535         lw = layout_window_list->data;
3536
3537         vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3538
3539         toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3540         gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3541         gtk_widget_show(vbox);
3542 }
3543
3544 /* toolbar status tab */
3545 static void config_tab_toolbar_status(GtkWidget *notebook)
3546 {
3547         GtkWidget *vbox;
3548         GtkWidget *toolbardata;
3549         LayoutWindow *lw;
3550
3551         lw = layout_window_list->data;
3552
3553         vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3554
3555         toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3556         gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3557         gtk_widget_show(vbox);
3558 }
3559
3560 /* stereo tab */
3561 static void config_tab_stereo(GtkWidget *notebook)
3562 {
3563         GtkWidget *vbox;
3564         GtkWidget *group;
3565         GtkWidget *group2;
3566         GtkWidget *table;
3567         GtkWidget *box;
3568         GtkWidget *box2;
3569         GtkWidget *fs_button;
3570         vbox = scrolled_notebook_page(notebook, _("Stereo"));
3571
3572         group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
3573
3574         table = pref_table_new(group, 2, 1, FALSE, FALSE);
3575         add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
3576
3577         table = pref_table_new(group, 2, 2, TRUE, FALSE);
3578         box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, NULL);
3579         pref_checkbox_new_int(box, _("Mirror left image"),
3580                               options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
3581         box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, NULL);
3582         pref_checkbox_new_int(box, _("Flip left image"),
3583                               options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
3584         box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, NULL);
3585         pref_checkbox_new_int(box, _("Mirror right image"),
3586                               options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
3587         box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, NULL);
3588         pref_checkbox_new_int(box, _("Flip right image"),
3589                               options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
3590         pref_checkbox_new_int(group, _("Swap left and right images"),
3591                               options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
3592         pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
3593                               options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
3594
3595         group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
3596         fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
3597                               options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
3598         box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
3599         pref_checkbox_link_sensitivity(fs_button, box2);
3600         table = pref_table_new(box2, 2, 1, FALSE, FALSE);
3601         add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
3602         table = pref_table_new(box2, 2, 2, TRUE, FALSE);
3603         box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, NULL);
3604         pref_checkbox_new_int(box, _("Mirror left image"),
3605                               options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
3606         box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, NULL);
3607         pref_checkbox_new_int(box, _("Flip left image"),
3608                               options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
3609         box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, NULL);
3610         pref_checkbox_new_int(box, _("Mirror right image"),
3611                               options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
3612         box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, NULL);
3613         pref_checkbox_new_int(box, _("Flip right image"),
3614                               options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
3615         pref_checkbox_new_int(box2, _("Swap left and right images"),
3616                               options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
3617         pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
3618                               options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
3619
3620         group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
3621         table = pref_table_new(group2, 5, 3, FALSE, FALSE);
3622         pref_table_spin_new_int(table, 0, 0, _("Width"), NULL,
3623                           1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
3624         pref_table_spin_new_int(table, 3, 0,  _("Height"), NULL,
3625                           1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
3626         pref_table_spin_new_int(table, 0, 1,  _("Left X"), NULL,
3627                           0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
3628         pref_table_spin_new_int(table, 3, 1,  _("Left Y"), NULL,
3629                           0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
3630         pref_table_spin_new_int(table, 0, 2,  _("Right X"), NULL,
3631                           0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
3632         pref_table_spin_new_int(table, 3, 2,  _("Right Y"), NULL,
3633                           0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
3634
3635 }
3636
3637 /* Main preferences window */
3638 static void config_window_create(void)
3639 {
3640         GtkWidget *win_vbox;
3641         GtkWidget *hbox;
3642         GtkWidget *notebook;
3643         GtkWidget *button;
3644         GtkWidget *ct_button;
3645
3646         if (!c_options) c_options = init_options(NULL);
3647
3648         configwindow = window_new(GTK_WINDOW_TOPLEVEL, "preferences", PIXBUF_INLINE_ICON_CONFIG, NULL, _("Preferences"));
3649         DEBUG_NAME(configwindow);
3650         gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
3651         g_signal_connect(G_OBJECT(configwindow), "delete_event",
3652                          G_CALLBACK(config_window_delete), NULL);
3653         gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
3654         gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
3655         gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
3656
3657         win_vbox = gtk_vbox_new(FALSE, PREF_PAD_SPACE);
3658         gtk_container_add(GTK_CONTAINER(configwindow), win_vbox);
3659         gtk_widget_show(win_vbox);
3660
3661         notebook = gtk_notebook_new();
3662         gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
3663         gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
3664         gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
3665
3666         config_tab_general(notebook);
3667         config_tab_image(notebook);
3668         config_tab_osd(notebook);
3669         config_tab_windows(notebook);
3670         config_tab_accelerators(notebook);
3671         config_tab_files(notebook);
3672         config_tab_metadata(notebook);
3673         config_tab_keywords(notebook);
3674         config_tab_color(notebook);
3675         config_tab_stereo(notebook);
3676         config_tab_behavior(notebook);
3677         config_tab_toolbar_main(notebook);
3678         config_tab_toolbar_status(notebook);
3679
3680         hbox = gtk_hbutton_box_new();
3681         gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
3682         gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
3683         gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
3684         gtk_widget_show(hbox);
3685
3686         button = pref_button_new(NULL, GTK_STOCK_HELP, NULL, FALSE,
3687                                  G_CALLBACK(config_window_help_cb), notebook);
3688         gtk_container_add(GTK_CONTAINER(hbox), button);
3689         gtk_widget_set_can_default(button, TRUE);
3690         gtk_widget_show(button);
3691
3692         button = pref_button_new(NULL, GTK_STOCK_OK, NULL, FALSE,
3693                                  G_CALLBACK(config_window_ok_cb), NULL);
3694         gtk_container_add(GTK_CONTAINER(hbox), button);
3695         gtk_widget_set_can_default(button, TRUE);
3696         gtk_widget_grab_default(button);
3697         gtk_widget_show(button);
3698
3699         ct_button = button;
3700
3701         button = pref_button_new(NULL, GTK_STOCK_CANCEL, NULL, FALSE,
3702                                  G_CALLBACK(config_window_close_cb), NULL);
3703         gtk_container_add(GTK_CONTAINER(hbox), button);
3704         gtk_widget_set_can_default(button, TRUE);
3705         gtk_widget_show(button);
3706
3707         if (!generic_dialog_get_alternative_button_order(configwindow))
3708                 {
3709                 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
3710                 }
3711
3712         gtk_widget_show(notebook);
3713
3714         gtk_widget_show(configwindow);
3715 }
3716
3717 /*
3718  *-----------------------------------------------------------------------------
3719  * config window show (public)
3720  *-----------------------------------------------------------------------------
3721  */
3722
3723 void show_config_window(void)
3724 {
3725         if (configwindow)
3726                 {
3727                 gtk_window_present(GTK_WINDOW(configwindow));
3728                 return;
3729                 }
3730
3731         config_window_create();
3732 }
3733
3734 /*
3735  *-----------------
3736  * about window
3737  *-----------------
3738  */
3739
3740 void show_about_window(LayoutWindow *lw)
3741 {
3742         GdkPixbuf *pixbuf_logo;
3743         GdkPixbuf *pixbuf_icon;
3744         gchar *authors[1000];
3745         gchar *comment;
3746         gint i_authors = 0;
3747         gchar *path;
3748         GString *copyright;
3749         gchar *timezone_path;
3750         gchar *basename;
3751         ZoneDetect *cd;
3752         FILE *fp = NULL;
3753 #define LINE_LENGTH 1000
3754         gchar line[LINE_LENGTH];
3755
3756         copyright = g_string_new(NULL);
3757         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");
3758
3759         path = path_from_utf8(TIMEZONE_DATABASE);
3760         basename = g_path_get_basename(path);
3761         timezone_path = g_build_filename(get_rc_dir(), basename, NULL);
3762         if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
3763                 {
3764                 cd = ZDOpenDatabase(timezone_path);
3765                 if (cd)
3766                         {
3767                         copyright = g_string_append(copyright, ZDGetNotice(cd));
3768                         ZDCloseDatabase(cd);
3769                         }
3770                 }
3771         g_free(path);
3772         g_free(timezone_path);
3773         g_free(basename);
3774
3775         authors[0] = NULL;
3776         path = g_build_filename(GQ_HELPDIR, "AUTHORS", NULL);
3777         fp = fopen(path, "r");
3778         if (fp)
3779                 {
3780                 while(fgets(line, LINE_LENGTH, fp))
3781                         {
3782                         /* get rid of ending \n from fgets */
3783                         line[strlen(line) - 1] = '\0';
3784                         authors[i_authors] = g_strdup(line);
3785                         i_authors++;
3786                         }
3787                 authors[i_authors] = NULL;
3788                 fclose(fp);
3789                 }
3790         g_free(path);
3791
3792         comment = g_strconcat("Development and bug reports:\n", GQ_EMAIL_ADDRESS,
3793                                                 "\nhttps://github.com/BestImageViewer/geeqie/issues",NULL);
3794
3795         pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
3796         pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
3797         gtk_show_about_dialog(GTK_WINDOW(lw->window),
3798                 "title", _("About Geeqie"),
3799                 "resizable", TRUE,
3800                 "program-name", GQ_APPNAME,
3801                 "version", VERSION,
3802                 "logo", pixbuf_logo,
3803                 "icon", pixbuf_icon,
3804                 "website", GQ_WEBSITE,
3805                 "website-label", "Website",
3806                 "comments", comment,
3807                 "authors", authors,
3808                 "translator-credits", _("translator-credits"),
3809                 "wrap-license", TRUE,
3810                 "license", copyright->str,
3811                 NULL);
3812
3813         g_string_free(copyright, TRUE);
3814
3815         gint n = 0;
3816         while(n < i_authors)
3817                 {
3818                 g_free(authors[n]);
3819                 n++;
3820                 }
3821         g_free(comment);
3822
3823         return;
3824 }
3825
3826 static void image_overlay_set_text_colours()
3827 {
3828         c_options->image_overlay.text_red = options->image_overlay.text_red;
3829         c_options->image_overlay.text_green = options->image_overlay.text_green;
3830         c_options->image_overlay.text_blue = options->image_overlay.text_blue;
3831         c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
3832         c_options->image_overlay.background_red = options->image_overlay.background_red;
3833         c_options->image_overlay.background_green = options->image_overlay.background_green;
3834         c_options->image_overlay.background_blue = options->image_overlay.background_blue;
3835         c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
3836 }
3837
3838 /*
3839  *-----------------------------------------------------------------------------
3840  * timezone database routines
3841  *-----------------------------------------------------------------------------
3842  */
3843
3844 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
3845 {
3846         GError *error = NULL;
3847         TZData *tz = data;
3848         gchar *tmp_filename;
3849
3850         if (!g_cancellable_is_cancelled(tz->cancellable))
3851                 {
3852                 generic_dialog_close(tz->gd);
3853                 }
3854
3855
3856         if (g_file_copy_finish(G_FILE(source_object), res, &error))
3857                 {
3858                 tmp_filename = g_file_get_parse_name(tz->tmp_g_file);
3859                 move_file(tmp_filename, tz->timezone_database_user);
3860                 g_free(tmp_filename);
3861                 }
3862         else
3863                 {
3864                 file_util_warning_dialog(_("Timezone database download failed"), error->message, GTK_STOCK_DIALOG_ERROR, NULL);
3865                 }
3866
3867         g_file_delete(tz->tmp_g_file, NULL, &error);
3868         g_object_unref(tz->tmp_g_file);
3869         tz->tmp_g_file = NULL;
3870         g_object_unref(tz->cancellable);
3871         g_object_unref(tz->timezone_database_gq);
3872 }
3873
3874 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
3875 {
3876         TZData *tz = data;
3877
3878         if (!g_cancellable_is_cancelled(tz->cancellable))
3879                 {
3880                 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), (gdouble)current_num_bytes / total_num_bytes);
3881                 }
3882 }
3883
3884 static void timezone_cancel_button_cb(GenericDialog *gd, gpointer data)
3885 {
3886         TZData *tz = data;
3887
3888         g_cancellable_cancel(tz->cancellable);
3889 }
3890
3891 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
3892 {
3893         TZData *tz = data;
3894         GError *error = NULL;
3895         GFileIOStream *io_stream;
3896
3897         if (tz->tmp_g_file)
3898                 {
3899                 return;
3900                 }
3901
3902         tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
3903
3904         if (error)
3905                 {
3906                 file_util_warning_dialog(_("Timezone database download failed"), error->message, GTK_STOCK_DIALOG_ERROR, NULL);
3907                 log_printf("Error: Download timezone database failed:\n%s", error->message);
3908                 g_error_free(error);
3909                 g_object_unref(tz->tmp_g_file);
3910                 }
3911         else
3912                 {
3913                 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE);
3914
3915                 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", NULL, TRUE, timezone_cancel_button_cb, tz);
3916
3917                 generic_dialog_add_message(tz->gd, GTK_STOCK_DIALOG_INFO, _("Downloading timezone database"), NULL, FALSE);
3918
3919                 tz->progress = gtk_progress_bar_new();
3920                 gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
3921                 gtk_widget_show(tz->progress);
3922
3923                 gtk_widget_show(tz->gd->dialog);
3924                 tz->cancellable = g_cancellable_new();
3925                 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);
3926
3927                 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
3928                 }
3929 }
3930 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */