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