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