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