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