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