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