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