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