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