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