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