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