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