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