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