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