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