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