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