2 * Copyright (C) 2006 John Ellis
3 * Copyright (C) 2008 - 2016 The Geeqie Team
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.
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.
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.
23 #include "preferences.h"
25 #include "bar-keywords.h"
27 //~ #include "authors.h"
28 //~ #include "authors.c"
29 //~ #include "translators.h"
30 //~ #include "translators.c"
33 #include "filefilter.h"
34 #include "fullscreen.h"
36 #include "image-overlay.h"
37 #include "color-man.h"
39 #include "layout-util.h"
43 #include "pixbuf-util.h"
45 #include "slideshow.h"
49 #include "ui-fileops.h"
51 #include "ui-spinner.h"
52 #include "ui-tabcomp.h"
54 #include "zonedetect.h"
65 #include <gspell/gspell.h>
68 #define EDITOR_NAME_MAX_LENGTH 32
69 #define EDITOR_COMMAND_MAX_LENGTH 1024
71 static void image_overlay_set_text_colours();
73 GtkWidget *keyword_text;
74 static void config_tab_keywords_save();
82 static ThumbSize thumb_size_list[] =
117 FILETYPES_COLUMN_ENABLED = 0,
118 FILETYPES_COLUMN_FILTER,
119 FILETYPES_COLUMN_DESCRIPTION,
120 FILETYPES_COLUMN_CLASS,
121 FILETYPES_COLUMN_WRITABLE,
122 FILETYPES_COLUMN_SIDECAR,
123 FILETYPES_COLUMN_COUNT
126 const gchar *format_class_list[] = {
137 /* config memory values */
138 static ConfOptions *c_options = nullptr;
145 static GtkWidget *configwindow = nullptr;
146 static GtkListStore *filter_store = nullptr;
147 static GtkTreeStore *accel_store = nullptr;
149 static GtkWidget *safe_delete_path_entry;
151 static GtkWidget *color_profile_input_file_entry[COLOR_PROFILE_INPUTS];
152 static GtkWidget *color_profile_input_name_entry[COLOR_PROFILE_INPUTS];
153 static GtkWidget *color_profile_screen_file_entry;
154 static GtkWidget *external_preview_select_entry;
155 static GtkWidget *external_preview_extract_entry;
157 static GtkWidget *sidecar_ext_entry;
158 static GtkWidget *help_search_engine_entry;
161 #define CONFIG_WINDOW_DEF_WIDTH 700
162 #define CONFIG_WINDOW_DEF_HEIGHT 600
165 *-----------------------------------------------------------------------------
166 * option widget callbacks (private)
167 *-----------------------------------------------------------------------------
170 static void zoom_increment_cb(GtkWidget *spin, gpointer UNUSED(data))
172 c_options->image.zoom_increment = static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) * 100.0 + 0.01);
175 static void slideshow_delay_hours_cb(GtkWidget *spin, gpointer UNUSED(data))
177 gint mins_secs_tenths, delay;
179 mins_secs_tenths = c_options->slideshow.delay %
180 (3600 * SLIDESHOW_SUBSECOND_PRECISION);
182 delay = (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
183 (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
186 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
187 SLIDESHOW_SUBSECOND_PRECISION;
190 static void slideshow_delay_minutes_cb(GtkWidget *spin, gpointer UNUSED(data))
192 gint hours, secs_tenths, delay;
194 hours = c_options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
195 secs_tenths = c_options->slideshow.delay % (60 * SLIDESHOW_SUBSECOND_PRECISION);
197 delay = hours * (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
198 (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
199 (60 * SLIDESHOW_SUBSECOND_PRECISION) + secs_tenths);
201 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
202 SLIDESHOW_SUBSECOND_PRECISION;
205 static void slideshow_delay_seconds_cb(GtkWidget *spin, gpointer UNUSED(data))
207 gint hours_mins, delay;
209 hours_mins = c_options->slideshow.delay / (60 * SLIDESHOW_SUBSECOND_PRECISION);
211 delay = (hours_mins * (60 * SLIDESHOW_SUBSECOND_PRECISION)) +
212 static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
213 static_cast<gdouble>(SLIDESHOW_SUBSECOND_PRECISION) + 0.01);
215 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
216 SLIDESHOW_SUBSECOND_PRECISION;
220 *-----------------------------------------------------------------------------
221 * sync program to config window routine (private)
222 *-----------------------------------------------------------------------------
225 void config_entry_to_option(GtkWidget *entry, gchar **option, gchar *(*func)(const gchar *))
231 buf = gtk_entry_get_text(GTK_ENTRY(entry));
232 if (buf && strlen(buf) > 0)
237 *option = g_strdup(buf);
242 static gboolean accel_apply_cb(GtkTreeModel *model, GtkTreePath *UNUSED(path), GtkTreeIter *iter, gpointer UNUSED(data))
244 gchar *accel_path, *accel;
246 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, AE_KEY, &accel, -1);
248 if (accel_path && accel_path[0])
251 gtk_accelerator_parse(accel, &key.accel_key, &key.accel_mods);
252 gtk_accel_map_change_entry(accel_path, key.accel_key, key.accel_mods, TRUE);
262 static void config_window_apply()
264 gboolean refresh = FALSE;
269 config_entry_to_option(safe_delete_path_entry, &options->file_ops.safe_delete_path, remove_trailing_slash);
271 if (options->file_filter.show_hidden_files != c_options->file_filter.show_hidden_files) refresh = TRUE;
272 if (options->file_filter.show_parent_directory != c_options->file_filter.show_parent_directory) refresh = TRUE;
273 if (options->file_filter.show_dot_directory != c_options->file_filter.show_dot_directory) refresh = TRUE;
274 if (options->file_sort.case_sensitive != c_options->file_sort.case_sensitive) refresh = TRUE;
275 if (options->file_sort.natural != c_options->file_sort.natural) refresh = TRUE;
276 if (options->file_filter.disable_file_extension_checks != c_options->file_filter.disable_file_extension_checks) refresh = TRUE;
277 if (options->file_filter.disable != c_options->file_filter.disable) refresh = TRUE;
279 options->file_ops.confirm_delete = c_options->file_ops.confirm_delete;
280 options->file_ops.enable_delete_key = c_options->file_ops.enable_delete_key;
281 options->file_ops.confirm_move_to_trash = c_options->file_ops.confirm_move_to_trash;
282 options->file_ops.use_system_trash = c_options->file_ops.use_system_trash;
283 options->file_ops.no_trash = c_options->file_ops.no_trash;
284 options->file_ops.safe_delete_folder_maxsize = c_options->file_ops.safe_delete_folder_maxsize;
285 options->tools_restore_state = c_options->tools_restore_state;
286 options->save_window_positions = c_options->save_window_positions;
287 options->use_saved_window_positions_for_new_windows = c_options->use_saved_window_positions_for_new_windows;
288 options->save_window_workspace = c_options->save_window_workspace;
289 options->save_dialog_window_positions = c_options->save_dialog_window_positions;
290 options->show_window_ids = c_options->show_window_ids;
291 options->image.scroll_reset_method = c_options->image.scroll_reset_method;
292 options->image.zoom_2pass = c_options->image.zoom_2pass;
293 options->image.fit_window_to_image = c_options->image.fit_window_to_image;
294 options->image.limit_window_size = c_options->image.limit_window_size;
295 options->image.zoom_to_fit_allow_expand = c_options->image.zoom_to_fit_allow_expand;
296 options->image.max_window_size = c_options->image.max_window_size;
297 options->image.limit_autofit_size = c_options->image.limit_autofit_size;
298 options->image.max_autofit_size = c_options->image.max_autofit_size;
299 options->image.max_enlargement_size = c_options->image.max_enlargement_size;
300 //~ options->image.use_clutter_renderer = c_options->image.use_clutter_renderer;
301 options->image.tile_size = c_options->image.tile_size;
302 options->progressive_key_scrolling = c_options->progressive_key_scrolling;
303 options->keyboard_scroll_step = c_options->keyboard_scroll_step;
305 if (options->thumbnails.max_width != c_options->thumbnails.max_width
306 || options->thumbnails.max_height != c_options->thumbnails.max_height
307 || options->thumbnails.quality != c_options->thumbnails.quality)
309 thumb_format_changed = TRUE;
311 options->thumbnails.max_width = c_options->thumbnails.max_width;
312 options->thumbnails.max_height = c_options->thumbnails.max_height;
313 options->thumbnails.quality = c_options->thumbnails.quality;
315 options->thumbnails.enable_caching = c_options->thumbnails.enable_caching;
316 options->thumbnails.cache_into_dirs = c_options->thumbnails.cache_into_dirs;
317 options->thumbnails.use_exif = c_options->thumbnails.use_exif;
318 options->thumbnails.use_color_management = c_options->thumbnails.use_color_management;
319 options->thumbnails.collection_preview = c_options->thumbnails.collection_preview;
320 options->thumbnails.use_ft_metadata = c_options->thumbnails.use_ft_metadata;
321 // options->thumbnails.use_ft_metadata_small = c_options->thumbnails.use_ft_metadata_small;
322 options->thumbnails.spec_standard = c_options->thumbnails.spec_standard;
323 options->metadata.enable_metadata_dirs = c_options->metadata.enable_metadata_dirs;
324 options->file_filter.show_hidden_files = c_options->file_filter.show_hidden_files;
325 options->file_filter.show_parent_directory = c_options->file_filter.show_parent_directory;
326 options->file_filter.show_dot_directory = c_options->file_filter.show_dot_directory;
327 options->file_filter.disable_file_extension_checks = c_options->file_filter.disable_file_extension_checks;
329 options->file_sort.case_sensitive = c_options->file_sort.case_sensitive;
330 options->file_sort.natural = c_options->file_sort.natural;
331 options->file_filter.disable = c_options->file_filter.disable;
333 config_entry_to_option(sidecar_ext_entry, &options->sidecar.ext, nullptr);
334 sidecar_ext_parse(options->sidecar.ext);
336 options->slideshow.random = c_options->slideshow.random;
337 options->slideshow.repeat = c_options->slideshow.repeat;
338 options->slideshow.delay = c_options->slideshow.delay;
340 options->mousewheel_scrolls = c_options->mousewheel_scrolls;
341 options->image_lm_click_nav = c_options->image_lm_click_nav;
342 options->image_l_click_archive = c_options->image_l_click_archive;
343 options->image_l_click_video = c_options->image_l_click_video;
344 options->image_l_click_video_editor = c_options->image_l_click_video_editor;
346 options->file_ops.enable_in_place_rename = c_options->file_ops.enable_in_place_rename;
348 options->image.tile_cache_max = c_options->image.tile_cache_max;
349 options->image.image_cache_max = c_options->image.image_cache_max;
351 options->image.zoom_quality = c_options->image.zoom_quality;
353 options->image.zoom_increment = c_options->image.zoom_increment;
355 options->image.zoom_style = c_options->image.zoom_style;
357 options->image.enable_read_ahead = c_options->image.enable_read_ahead;
359 options->appimage_notifications = c_options->appimage_notifications;
362 if (options->image.use_custom_border_color != c_options->image.use_custom_border_color
363 || options->image.use_custom_border_color_in_fullscreen != c_options->image.use_custom_border_color_in_fullscreen
364 || !gdk_color_equal(&options->image.border_color, &c_options->image.border_color))
366 options->image.use_custom_border_color_in_fullscreen = c_options->image.use_custom_border_color_in_fullscreen;
367 options->image.use_custom_border_color = c_options->image.use_custom_border_color;
368 options->image.border_color = c_options->image.border_color;
369 layout_colors_update();
370 view_window_colors_update();
373 options->image.alpha_color_1 = c_options->image.alpha_color_1;
374 options->image.alpha_color_2 = c_options->image.alpha_color_2;
376 options->fullscreen.screen = c_options->fullscreen.screen;
377 options->fullscreen.clean_flip = c_options->fullscreen.clean_flip;
378 options->fullscreen.disable_saver = c_options->fullscreen.disable_saver;
379 options->fullscreen.above = c_options->fullscreen.above;
380 if (c_options->image_overlay.template_string)
381 set_image_overlay_template_string(&options->image_overlay.template_string,
382 c_options->image_overlay.template_string);
383 if (c_options->image_overlay.font)
384 set_image_overlay_font_string(&options->image_overlay.font,
385 c_options->image_overlay.font);
386 options->image_overlay.text_red = c_options->image_overlay.text_red;
387 options->image_overlay.text_green = c_options->image_overlay.text_green;
388 options->image_overlay.text_blue = c_options->image_overlay.text_blue;
389 options->image_overlay.text_alpha = c_options->image_overlay.text_alpha;
390 options->image_overlay.background_red = c_options->image_overlay.background_red;
391 options->image_overlay.background_green = c_options->image_overlay.background_green;
392 options->image_overlay.background_blue = c_options->image_overlay.background_blue;
393 options->image_overlay.background_alpha = c_options->image_overlay.background_alpha;
394 options->update_on_time_change = c_options->update_on_time_change;
396 options->duplicates_similarity_threshold = c_options->duplicates_similarity_threshold;
397 options->rot_invariant_sim = c_options->rot_invariant_sim;
399 options->tree_descend_subdirs = c_options->tree_descend_subdirs;
401 options->view_dir_list_single_click_enter = c_options->view_dir_list_single_click_enter;
402 options->circular_selection_lists = c_options->circular_selection_lists;
404 options->open_recent_list_maxsize = c_options->open_recent_list_maxsize;
405 options->recent_folder_image_list_maxsize = c_options->recent_folder_image_list_maxsize;
406 options->dnd_icon_size = c_options->dnd_icon_size;
407 options->clipboard_selection = c_options->clipboard_selection;
408 options->dnd_default_action = c_options->dnd_default_action;
410 options->metadata.save_in_image_file = c_options->metadata.save_in_image_file;
411 options->metadata.save_legacy_IPTC = c_options->metadata.save_legacy_IPTC;
412 options->metadata.warn_on_write_problems = c_options->metadata.warn_on_write_problems;
413 options->metadata.save_legacy_format = c_options->metadata.save_legacy_format;
414 options->metadata.sync_grouped_files = c_options->metadata.sync_grouped_files;
415 options->metadata.confirm_write = c_options->metadata.confirm_write;
416 options->metadata.sidecar_extended_name = c_options->metadata.sidecar_extended_name;
417 options->metadata.confirm_timeout = c_options->metadata.confirm_timeout;
418 options->metadata.confirm_after_timeout = c_options->metadata.confirm_after_timeout;
419 options->metadata.confirm_on_image_change = c_options->metadata.confirm_on_image_change;
420 options->metadata.confirm_on_dir_change = c_options->metadata.confirm_on_dir_change;
421 options->metadata.keywords_case_sensitive = c_options->metadata.keywords_case_sensitive;
422 options->metadata.write_orientation = c_options->metadata.write_orientation;
423 options->metadata.check_spelling = c_options->metadata.check_spelling;
424 options->stereo.mode = (c_options->stereo.mode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
425 (c_options->stereo.tmp.mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
426 (c_options->stereo.tmp.flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
427 (c_options->stereo.tmp.mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
428 (c_options->stereo.tmp.flip_left ? PR_STEREO_FLIP_LEFT : 0) |
429 (c_options->stereo.tmp.swap ? PR_STEREO_SWAP : 0) |
430 (c_options->stereo.tmp.temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
431 options->stereo.fsmode = (c_options->stereo.fsmode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
432 (c_options->stereo.tmp.fs_mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
433 (c_options->stereo.tmp.fs_flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
434 (c_options->stereo.tmp.fs_mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
435 (c_options->stereo.tmp.fs_flip_left ? PR_STEREO_FLIP_LEFT : 0) |
436 (c_options->stereo.tmp.fs_swap ? PR_STEREO_SWAP : 0) |
437 (c_options->stereo.tmp.fs_temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
438 options->stereo.enable_fsmode = c_options->stereo.enable_fsmode;
439 options->stereo.fixed_w = c_options->stereo.fixed_w;
440 options->stereo.fixed_h = c_options->stereo.fixed_h;
441 options->stereo.fixed_x1 = c_options->stereo.fixed_x1;
442 options->stereo.fixed_y1 = c_options->stereo.fixed_y1;
443 options->stereo.fixed_x2 = c_options->stereo.fixed_x2;
444 options->stereo.fixed_y2 = c_options->stereo.fixed_y2;
446 options->info_keywords.height = c_options->info_keywords.height;
447 options->info_title.height = c_options->info_title.height;
448 options->info_comment.height = c_options->info_comment.height;
449 options->info_rating.height = c_options->info_rating.height;
451 options->show_predefined_keyword_tree = c_options->show_predefined_keyword_tree;
452 options->expand_menu_toolbar = c_options->expand_menu_toolbar;
454 options->marks_save = c_options->marks_save;
455 options->with_rename = c_options->with_rename;
456 options->collections_on_top = c_options->collections_on_top;
457 options->hide_window_in_fullscreen = c_options->hide_window_in_fullscreen;
458 config_entry_to_option(help_search_engine_entry, &options->help_search_engine, nullptr);
460 options->external_preview.enable = c_options->external_preview.enable;
461 config_entry_to_option(external_preview_select_entry, &options->external_preview.select, nullptr);
462 config_entry_to_option(external_preview_extract_entry, &options->external_preview.extract, nullptr);
464 options->read_metadata_in_idle = c_options->read_metadata_in_idle;
466 options->star_rating.star = c_options->star_rating.star;
467 options->star_rating.rejected = c_options->star_rating.rejected;
469 options->threads.duplicates = c_options->threads.duplicates > 0 ? c_options->threads.duplicates : -1;
472 set_debug_level(debug_c);
476 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
478 config_entry_to_option(color_profile_input_name_entry[i], &options->color_profile.input_name[i], nullptr);
479 config_entry_to_option(color_profile_input_file_entry[i], &options->color_profile.input_file[i], nullptr);
481 config_entry_to_option(color_profile_screen_file_entry, &options->color_profile.screen_file, nullptr);
482 options->color_profile.use_x11_screen_profile = c_options->color_profile.use_x11_screen_profile;
483 if (options->color_profile.render_intent != c_options->color_profile.render_intent)
485 options->color_profile.render_intent = c_options->color_profile.render_intent;
490 options->mouse_button_8 = c_options->mouse_button_8;
491 options->mouse_button_9 = c_options->mouse_button_9;
493 options->override_disable_gpu = c_options->override_disable_gpu;
495 config_tab_keywords_save();
497 image_options_sync();
502 layout_refresh(nullptr);
505 if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, nullptr);
507 toolbar_apply(TOOLBAR_MAIN);
508 toolbar_apply(TOOLBAR_STATUS);
512 *-----------------------------------------------------------------------------
513 * config window main button callbacks (private)
514 *-----------------------------------------------------------------------------
517 static void config_window_close_cb(GtkWidget *UNUSED(widget), gpointer UNUSED(data))
519 gtk_widget_destroy(configwindow);
520 configwindow = nullptr;
521 filter_store = nullptr;
524 static void config_window_help_cb(GtkWidget *UNUSED(widget), gpointer data)
526 auto notebook = static_cast<GtkWidget *>(data);
529 static const gchar *html_section[] =
531 "GuideOptionsGeneral.html",
532 "GuideOptionsImage.html",
533 "GuideOptionsOSD.html",
534 "GuideOptionsWindow.html",
535 "GuideOptionsKeyboard.html",
536 "GuideOptionsFiltering.html",
537 "GuideOptionsMetadata.html",
538 "GuideOptionsKeywords.html",
539 "GuideOptionsColor.html",
540 "GuideOptionsStereo.html",
541 "GuideOptionsBehavior.html",
542 "GuideOptionsToolbar.html",
543 "GuideOptionsToolbar.html",
544 "GuideOptionsAdvanced.html"
547 i = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
548 help_window_show(html_section[i]);
551 static gboolean config_window_delete(GtkWidget *UNUSED(widget), GdkEventAny *UNUSED(event), gpointer UNUSED(data))
553 config_window_close_cb(nullptr, nullptr);
557 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
560 auto notebook = static_cast<GtkNotebook *>(data);
568 lw = static_cast<LayoutWindow *>(layout_window_list->data);
570 window = gtk_widget_get_window(widget);
571 gdk_window_get_root_origin(window, &x, &y);
572 w = gdk_window_get_width(window);
573 h = gdk_window_get_height(window);
574 page_number = gtk_notebook_get_current_page(notebook);
576 lw->options.preferences_window.x = x;
577 lw->options.preferences_window.y = y;
578 lw->options.preferences_window.w = w;
579 lw->options.preferences_window.h = h;
580 lw->options.preferences_window.page_number = page_number;
582 config_window_apply();
583 layout_util_sync(lw);
584 save_options(options);
585 config_window_close_cb(nullptr, nullptr);
589 *-----------------------------------------------------------------------------
590 * config window setup (private)
591 *-----------------------------------------------------------------------------
594 static void quality_menu_cb(GtkWidget *combo, gpointer data)
596 auto option = static_cast<gint *>(data);
598 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
602 *option = GDK_INTERP_NEAREST;
605 *option = GDK_INTERP_TILES;
608 *option = GDK_INTERP_BILINEAR;
613 static void dnd_default_action_selection_menu_cb(GtkWidget *combo, gpointer data)
615 auto option = static_cast<gint *>(data);
617 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
621 *option = DND_ACTION_ASK;
624 *option = DND_ACTION_COPY;
627 *option = DND_ACTION_MOVE;
631 static void clipboard_selection_menu_cb(GtkWidget *combo, gpointer data)
633 auto option = static_cast<gint *>(data);
635 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
638 *option = CLIPBOARD_PRIMARY;
641 *option = CLIPBOARD_CLIPBOARD;
644 *option = CLIPBOARD_BOTH;
647 *option = CLIPBOARD_BOTH;
651 static void add_quality_menu(GtkWidget *table, gint column, gint row, const gchar *text,
652 guint option, guint *option_c)
659 pref_table_label(table, column, row, text, 0.0);
661 combo = gtk_combo_box_text_new();
663 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Nearest (worst, but fastest)"));
664 if (option == GDK_INTERP_NEAREST) current = 0;
665 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Tiles"));
666 if (option == GDK_INTERP_TILES) current = 1;
667 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Bilinear (best, but slowest)"));
668 if (option == GDK_INTERP_BILINEAR) current = 2;
670 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
672 g_signal_connect(G_OBJECT(combo), "changed",
673 G_CALLBACK(quality_menu_cb), option_c);
675 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
676 gtk_widget_show(combo);
679 static void add_dnd_default_action_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, DnDAction option, DnDAction *option_c)
686 pref_table_label(table, column, row, text, 0.0);
688 combo = gtk_combo_box_text_new();
690 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Ask"));
691 if (option == DND_ACTION_ASK) current = 0;
692 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Copy"));
693 if (option == DND_ACTION_COPY) current = 1;
694 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Move"));
695 if (option == DND_ACTION_MOVE) current = 2;
697 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
699 g_signal_connect(G_OBJECT(combo), "changed",
700 G_CALLBACK(dnd_default_action_selection_menu_cb), option_c);
702 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
703 gtk_widget_show(combo);
706 static void add_clipboard_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
707 gint option, gint *option_c)
714 pref_table_label(table, column, row, text, 0.0);
716 combo = gtk_combo_box_text_new();
718 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Primary"));
719 if (option == CLIPBOARD_PRIMARY) current = 0;
720 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Clipboard"));
721 if (option == CLIPBOARD_CLIPBOARD) current = 1;
722 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Both"));
723 if (option == CLIPBOARD_BOTH) current = 2;
725 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
727 g_signal_connect(G_OBJECT(combo), "changed",
728 G_CALLBACK(clipboard_selection_menu_cb), option_c);
730 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
731 gtk_widget_show(combo);
734 static void zoom_style_selection_menu_cb(GtkWidget *combo, gpointer data)
736 auto option = static_cast<gint *>(data);
738 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
741 *option = ZOOM_GEOMETRIC;
744 *option = ZOOM_ARITHMETIC;
747 *option = ZOOM_GEOMETRIC;
751 static void add_zoom_style_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, ZoomStyle option, ZoomStyle *option_c)
758 pref_table_label(table, column, row, text, 0.0);
760 combo = gtk_combo_box_text_new();
762 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Geometric"));
763 if (option == ZOOM_GEOMETRIC) current = 0;
764 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Arithmetic"));
765 if (option == ZOOM_ARITHMETIC) current = 1;
767 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
769 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(zoom_style_selection_menu_cb), option_c);
771 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
772 gtk_widget_show(combo);
775 struct UseableMouseItems
777 const gchar *name; /* GtkActionEntry terminology */
779 const gchar *stock_id;
782 static const UseableMouseItems useable_mouse_items[] = {
784 {"FirstImage", N_("First Image"), GTK_STOCK_GOTO_TOP},
785 {"PrevImage", N_("Previous Image"), GTK_STOCK_GO_UP},
786 {"NextImage", N_("Next Image"), GTK_STOCK_GO_DOWN},
787 {"LastImage", N_("Last Image"), GTK_STOCK_GOTO_BOTTOM},
788 {"Back", N_("Back"), GTK_STOCK_GO_BACK},
789 {"Forward", N_("Forward"), GTK_STOCK_GO_FORWARD},
790 {"Home", N_("Home"), GTK_STOCK_HOME},
791 {"Up", N_("Up"), GTK_STOCK_GO_UP},
792 {"FirstPage", N_("First page"), GTK_STOCK_MEDIA_PREVIOUS},
793 {"LastPage", N_("Last Page"), GTK_STOCK_MEDIA_NEXT},
794 {"NextPage", N_("Next page"), GTK_STOCK_MEDIA_FORWARD},
795 {"PrevPage", N_("Previous Page"), GTK_STOCK_MEDIA_REWIND},
796 {"NewWindow", N_("New _window"), GTK_STOCK_NEW},
797 {"NewCollection", N_("New collection"), GTK_STOCK_INDEX},
798 {"OpenCollection", N_("Open collection"), GTK_STOCK_OPEN},
799 {"Search", N_("Search"), GTK_STOCK_FIND},
800 {"FindDupes", N_("Find duplicates"), GTK_STOCK_FIND},
801 {"NewFolder", N_("New folder"),GTK_STOCK_DIRECTORY},
802 {"Copy", N_("Copy"), GTK_STOCK_COPY},
803 {"Move", N_("Move"), PIXBUF_INLINE_ICON_MOVE},
804 {"Rename", N_("Rename"), PIXBUF_INLINE_ICON_RENAME},
805 {"Delete", N_("Delete"), GTK_STOCK_DELETE},
806 {"CloseWindow", N_("Close Window"), GTK_STOCK_CLOSE},
807 {"PanView", N_("Pan view"), PIXBUF_INLINE_ICON_PANORAMA},
808 {"SelectAll", N_("Select all"), PIXBUF_INLINE_ICON_SELECT_ALL},
809 {"SelectNone", N_("Select none"), PIXBUF_INLINE_ICON_SELECT_NONE},
810 {"SelectInvert", N_("Select invert"), PIXBUF_INLINE_ICON_SELECT_INVERT},
811 {"ShowFileFilter", N_("Show file filter"), PIXBUF_INLINE_ICON_FILE_FILTER},
812 {"RectangularSelection", N_("Select rectangle"), PIXBUF_INLINE_ICON_SELECT_RECTANGLE},
813 {"Print", N_("Print"), GTK_STOCK_PRINT},
814 {"Preferences", N_("Preferences"), GTK_STOCK_PREFERENCES},
815 {"LayoutConfig", N_("Configure this window"), GTK_STOCK_PREFERENCES},
816 {"Maintenance", N_("Cache maintenance"), PIXBUF_INLINE_ICON_MAINTENANCE},
817 {"RotateCW", N_("Rotate clockwise 90°"), PIXBUF_INLINE_ICON_CW},
818 {"RotateCCW", N_("Rotate counterclockwise 90°"), PIXBUF_INLINE_ICON_CCW},
819 {"Rotate180", N_("Rotate 180°"), PIXBUF_INLINE_ICON_180},
820 {"Mirror", N_("Mirror"), PIXBUF_INLINE_ICON_MIRROR},
821 {"Flip", N_("Flip"), PIXBUF_INLINE_ICON_FLIP},
822 {"AlterNone", N_("Original state"), PIXBUF_INLINE_ICON_ORIGINAL},
823 {"ZoomIn", N_("Zoom in"), GTK_STOCK_ZOOM_IN},
824 {"ZoomOut", N_("Zoom out"), GTK_STOCK_ZOOM_OUT},
825 {"Zoom100", N_("Zoom 1:1"), GTK_STOCK_ZOOM_100},
826 {"ZoomFit", N_("Zoom to fit"), GTK_STOCK_ZOOM_FIT},
827 {"ZoomFillHor", N_("Fit Horizontaly"), PIXBUF_INLINE_ICON_ZOOMFILLHOR},
828 {"ZoomFillVert", N_("Fit vertically"), PIXBUF_INLINE_ICON_ZOOMFILLVERT},
829 {"Zoom200", N_("Zoom 2:1"), GTK_STOCK_FILE},
830 {"Zoom300", N_("Zoom 3:1"), GTK_STOCK_FILE},
831 {"Zoom400", N_("Zoom 4:1"), GTK_STOCK_FILE},
832 {"Zoom50", N_("Zoom 1:2"), GTK_STOCK_FILE},
833 {"Zoom33", N_("Zoom1:3"), GTK_STOCK_FILE},
834 {"Zoom25", N_("Zoom 1:4"), GTK_STOCK_FILE},
835 {"ConnectZoomIn", N_("Connected Zoom in"), GTK_STOCK_ZOOM_IN},
836 {"SplitPaneSync", N_("Split Pane Sync"), PIXBUF_INLINE_SPLIT_PANE_SYNC},
837 {"Grayscale", N_("Grayscale"), PIXBUF_INLINE_ICON_GRAYSCALE},
838 {"OverUnderExposed", N_("Over Under Exposed"), PIXBUF_INLINE_ICON_EXPOSURE},
839 {"HideTools", N_("Hide file list"), PIXBUF_INLINE_ICON_HIDETOOLS},
840 {"SlideShowPause", N_("Pause slideshow"), GTK_STOCK_MEDIA_PAUSE},
841 {"SlideShowFaster", N_("Slideshow Faster"), GTK_STOCK_FILE},
842 {"SlideShowSlower", N_("Slideshow Slower"), GTK_STOCK_FILE},
843 {"Refresh", N_("Refresh"), GTK_STOCK_REFRESH},
844 {"HelpContents", N_("Help"), GTK_STOCK_HELP},
845 {"ExifWin", N_("Exif window"), PIXBUF_INLINE_ICON_EXIF},
846 {"Thumbnails", N_("Show thumbnails"), PIXBUF_INLINE_ICON_THUMB},
847 {"ShowMarks", N_("Show marks"), PIXBUF_INLINE_ICON_MARKS},
848 {"DrawRectangle", N_("Draw Rectangle"), PIXBUF_INLINE_ICON_DRAW_RECTANGLE},
849 {"FloatTools", N_("Float file list"), PIXBUF_INLINE_ICON_FLOAT},
850 {"SBar", N_("Info sidebar"), PIXBUF_INLINE_ICON_INFO},
851 {"SBarSort", N_("Sort manager"), PIXBUF_INLINE_ICON_SORT},
852 {"Quit", N_("Quit"), GTK_STOCK_QUIT},
853 {nullptr, nullptr, nullptr}
856 static void mouse_buttons_selection_menu_cb(GtkWidget *combo, gpointer data)
858 auto option = static_cast<gchar **>(data);
861 label = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
863 const UseableMouseItems *list = useable_mouse_items;
867 if (g_strcmp0(list->label, label) == 0)
875 *option = g_strdup(list->name);
879 static void add_mouse_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
880 gchar *option, gchar **option_c)
888 pref_table_label(table, column, row, text, 0.0);
890 combo = gtk_combo_box_text_new();
892 const UseableMouseItems *list = useable_mouse_items;
896 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), list->label);
897 if (g_strcmp0(list->name, option) == 0)
905 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
907 g_signal_connect(G_OBJECT(combo), "changed",
908 G_CALLBACK(mouse_buttons_selection_menu_cb), option_c);
910 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
911 gtk_widget_show(combo);
914 static void thumb_size_menu_cb(GtkWidget *combo, gpointer UNUSED(data))
918 n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
921 if (static_cast<guint>(n) < sizeof(thumb_size_list) / sizeof(ThumbSize))
923 c_options->thumbnails.max_width = thumb_size_list[n].w;
924 c_options->thumbnails.max_height = thumb_size_list[n].h;
928 c_options->thumbnails.max_width = options->thumbnails.max_width;
929 c_options->thumbnails.max_height = options->thumbnails.max_height;
933 static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *text)
939 c_options->thumbnails.max_width = options->thumbnails.max_width;
940 c_options->thumbnails.max_height = options->thumbnails.max_height;
942 pref_table_label(table, column, row, text, 0.0);
944 combo = gtk_combo_box_text_new();
947 for (i = 0; static_cast<guint>(i) < sizeof(thumb_size_list) / sizeof(ThumbSize); i++)
952 w = thumb_size_list[i].w;
953 h = thumb_size_list[i].h;
955 buf = g_strdup_printf("%d x %d", w, h);
956 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
959 if (w == options->thumbnails.max_width && h == options->thumbnails.max_height) current = i;
966 buf = g_strdup_printf("%s %d x %d", _("Custom"), options->thumbnails.max_width, options->thumbnails.max_height);
967 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
973 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
974 g_signal_connect(G_OBJECT(combo), "changed",
975 G_CALLBACK(thumb_size_menu_cb), NULL);
977 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
978 gtk_widget_show(combo);
981 static void stereo_mode_menu_cb(GtkWidget *combo, gpointer data)
983 auto option = static_cast<gint *>(data);
985 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
989 *option = PR_STEREO_NONE;
992 *option = PR_STEREO_ANAGLYPH_RC;
995 *option = PR_STEREO_ANAGLYPH_GM;
998 *option = PR_STEREO_ANAGLYPH_YB;
1001 *option = PR_STEREO_ANAGLYPH_GRAY_RC;
1004 *option = PR_STEREO_ANAGLYPH_GRAY_GM;
1007 *option = PR_STEREO_ANAGLYPH_GRAY_YB;
1010 *option = PR_STEREO_ANAGLYPH_DB_RC;
1013 *option = PR_STEREO_ANAGLYPH_DB_GM;
1016 *option = PR_STEREO_ANAGLYPH_DB_YB;
1019 *option = PR_STEREO_HORIZ;
1022 *option = PR_STEREO_HORIZ | PR_STEREO_HALF;
1025 *option = PR_STEREO_VERT;
1028 *option = PR_STEREO_VERT | PR_STEREO_HALF;
1031 *option = PR_STEREO_FIXED;
1036 static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1037 gint option, gint *option_c, gboolean add_fixed)
1044 pref_table_label(table, column, row, text, 0.0);
1046 combo = gtk_combo_box_text_new();
1048 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Single image"));
1050 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Red-Cyan"));
1051 if (option & PR_STEREO_ANAGLYPH_RC) current = 1;
1052 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Green-Magenta"));
1053 if (option & PR_STEREO_ANAGLYPH_GM) current = 2;
1054 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Yellow-Blue"));
1055 if (option & PR_STEREO_ANAGLYPH_YB) current = 3;
1056 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Red-Cyan"));
1057 if (option & PR_STEREO_ANAGLYPH_GRAY_RC) current = 4;
1058 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Green-Magenta"));
1059 if (option & PR_STEREO_ANAGLYPH_GRAY_GM) current = 5;
1060 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Yellow-Blue"));
1061 if (option & PR_STEREO_ANAGLYPH_GRAY_YB) current = 6;
1062 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Red-Cyan"));
1063 if (option & PR_STEREO_ANAGLYPH_DB_RC) current = 7;
1064 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Green-Magenta"));
1065 if (option & PR_STEREO_ANAGLYPH_DB_GM) current = 8;
1066 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Yellow-Blue"));
1067 if (option & PR_STEREO_ANAGLYPH_DB_YB) current = 9;
1069 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side"));
1070 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side Half size"));
1071 if (option & PR_STEREO_HORIZ)
1074 if (option & PR_STEREO_HALF) current = 11;
1077 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom"));
1078 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom Half size"));
1079 if (option & PR_STEREO_VERT)
1082 if (option & PR_STEREO_HALF) current = 13;
1087 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Fixed position"));
1088 if (option & PR_STEREO_FIXED) current = 14;
1091 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1093 g_signal_connect(G_OBJECT(combo), "changed",
1094 G_CALLBACK(stereo_mode_menu_cb), option_c);
1096 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1097 gtk_widget_show(combo);
1100 static void video_menu_cb(GtkWidget *combo, gpointer data)
1102 auto option = static_cast<gchar **>(data);
1104 auto ed = static_cast<EditorDescription *>(g_list_nth_data(editor_list_get(), gtk_combo_box_get_active(GTK_COMBO_BOX(combo))));
1108 static void video_menu_populate(gpointer data, gpointer user_data)
1110 auto combo = static_cast<GtkWidget *>(user_data);
1111 auto ed = static_cast<EditorDescription *>(data);
1113 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), ed->name);
1116 static void add_video_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1117 gchar *option, gchar **option_c)
1121 /* use lists since they are sorted */
1122 GList *eds = editor_list_get();
1126 pref_table_label(table, column, row, text, 0.0);
1128 combo = gtk_combo_box_text_new();
1129 g_list_foreach(eds,video_menu_populate,combo);
1130 current = option ? g_list_index(eds,g_hash_table_lookup(editors,option)): -1;
1132 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1134 g_signal_connect(G_OBJECT(combo), "changed",
1135 G_CALLBACK(video_menu_cb), option_c);
1137 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1138 gtk_widget_show(combo);
1141 static void filter_store_populate()
1145 if (!filter_store) return;
1147 gtk_list_store_clear(filter_store);
1149 work = filter_get_list();
1155 fe = static_cast<FilterEntry *>(work->data);
1158 gtk_list_store_append(filter_store, &iter);
1159 gtk_list_store_set(filter_store, &iter, 0, fe, -1);
1163 static void filter_store_ext_edit_cb(GtkCellRendererText *UNUSED(cell), gchar *path_str,
1164 gchar *new_text, gpointer data)
1166 auto model = static_cast<GtkWidget *>(data);
1167 auto fe = static_cast<FilterEntry *>(data);
1171 if (!new_text || strlen(new_text) < 1) return;
1173 tpath = gtk_tree_path_new_from_string(path_str);
1174 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1175 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1177 g_free(fe->extensions);
1178 fe->extensions = g_strdup(new_text);
1180 gtk_tree_path_free(tpath);
1184 static void filter_store_class_edit_cb(GtkCellRendererText *UNUSED(cell), gchar *path_str,
1185 gchar *new_text, gpointer data)
1187 auto model = static_cast<GtkWidget *>(data);
1188 auto fe = static_cast<FilterEntry *>(data);
1193 if (!new_text || !new_text[0]) return;
1195 tpath = gtk_tree_path_new_from_string(path_str);
1196 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1197 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1199 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
1201 if (strcmp(new_text, _(format_class_list[i])) == 0)
1203 fe->file_class = static_cast<FileFormatClass>(i);
1208 gtk_tree_path_free(tpath);
1212 static void filter_store_desc_edit_cb(GtkCellRendererText *UNUSED(cell), gchar *path_str,
1213 gchar *new_text, gpointer data)
1215 auto model = static_cast<GtkWidget *>(data);
1220 if (!new_text || !new_text[0]) return;
1222 tpath = gtk_tree_path_new_from_string(path_str);
1223 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1224 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1226 g_free(fe->description);
1227 fe->description = g_strdup(new_text);
1229 gtk_tree_path_free(tpath);
1232 static void filter_store_enable_cb(GtkCellRendererToggle *UNUSED(renderer),
1233 gchar *path_str, gpointer data)
1235 auto model = static_cast<GtkWidget *>(data);
1240 tpath = gtk_tree_path_new_from_string(path_str);
1241 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1242 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1244 fe->enabled = !fe->enabled;
1246 gtk_tree_path_free(tpath);
1250 static void filter_store_writable_cb(GtkCellRendererToggle *UNUSED(renderer),
1251 gchar *path_str, gpointer data)
1253 auto model = static_cast<GtkWidget *>(data);
1258 tpath = gtk_tree_path_new_from_string(path_str);
1259 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1260 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1262 fe->writable = !fe->writable;
1263 if (fe->writable) fe->allow_sidecar = FALSE;
1265 gtk_tree_path_free(tpath);
1269 static void filter_store_sidecar_cb(GtkCellRendererToggle *UNUSED(renderer),
1270 gchar *path_str, gpointer data)
1272 auto model = static_cast<GtkWidget *>(data);
1277 tpath = gtk_tree_path_new_from_string(path_str);
1278 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1279 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1281 fe->allow_sidecar = !fe->allow_sidecar;
1282 if (fe->allow_sidecar) fe->writable = FALSE;
1284 gtk_tree_path_free(tpath);
1288 static void filter_set_func(GtkTreeViewColumn *UNUSED(tree_column), GtkCellRenderer *cell,
1289 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1293 gtk_tree_model_get(tree_model, iter, 0, &fe, -1);
1295 switch (GPOINTER_TO_INT(data))
1298 g_object_set(GTK_CELL_RENDERER(cell),
1299 "active", fe->enabled, NULL);
1302 g_object_set(GTK_CELL_RENDERER(cell),
1303 "text", fe->extensions, NULL);
1305 case FE_DESCRIPTION:
1306 g_object_set(GTK_CELL_RENDERER(cell),
1307 "text", fe->description, NULL);
1310 g_object_set(GTK_CELL_RENDERER(cell),
1311 "text", _(format_class_list[fe->file_class]), NULL);
1314 g_object_set(GTK_CELL_RENDERER(cell),
1315 "active", fe->writable, NULL);
1317 case FE_ALLOW_SIDECAR:
1318 g_object_set(GTK_CELL_RENDERER(cell),
1319 "active", fe->allow_sidecar, NULL);
1324 static gboolean filter_add_scroll(gpointer data)
1328 GtkCellRenderer *cell;
1329 GtkTreeViewColumn *column;
1332 GtkTreeModel *store;
1334 FilterEntry *filter;
1336 rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(filter_store), nullptr);
1337 path = gtk_tree_path_new_from_indices(rows-1, -1);
1339 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data), 0);
1341 list_cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1342 cell = static_cast<GtkCellRenderer *>(g_list_last(list_cells)->data);
1344 store = gtk_tree_view_get_model(GTK_TREE_VIEW(data));
1345 valid = gtk_tree_model_get_iter_first(store, &iter);
1349 gtk_tree_model_get(store, &iter, 0, &filter, -1);
1351 if (g_strcmp0(filter->extensions, ".new") == 0)
1353 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1357 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1360 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1361 path, column, FALSE, 0.0, 0.0 );
1362 gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(data),
1363 path, column, cell, TRUE);
1365 gtk_tree_path_free(path);
1366 g_list_free(list_cells);
1368 return(G_SOURCE_REMOVE);
1371 static void filter_add_cb(GtkWidget *UNUSED(widget), gpointer data)
1373 filter_add_unique("description", ".new", FORMAT_CLASS_IMAGE, TRUE, FALSE, TRUE);
1374 filter_store_populate();
1376 g_idle_add(static_cast<GSourceFunc>(filter_add_scroll), data);
1379 static void filter_remove_cb(GtkWidget *UNUSED(widget), gpointer data)
1381 auto filter_view = static_cast<GtkWidget *>(data);
1382 GtkTreeSelection *selection;
1386 if (!filter_store) return;
1387 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
1388 if (!gtk_tree_selection_get_selected(selection, nullptr, &iter)) return;
1389 gtk_tree_model_get(GTK_TREE_MODEL(filter_store), &iter, 0, &fe, -1);
1392 filter_remove_entry(fe);
1394 filter_store_populate();
1397 static gboolean filter_default_ok_scroll(GtkTreeView *data)
1401 GtkTreeViewColumn *column;
1403 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(filter_store), &iter);
1404 path = gtk_tree_model_get_path(GTK_TREE_MODEL(filter_store), &iter);
1405 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1407 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1411 gtk_tree_path_free(path);
1413 return(G_SOURCE_REMOVE);
1416 static void filter_default_ok_cb(GenericDialog *gd, gpointer UNUSED(data))
1419 filter_add_defaults();
1421 filter_store_populate();
1423 g_idle_add(reinterpret_cast<GSourceFunc>(filter_default_ok_scroll), gd->data);
1426 static void dummy_cancel_cb(GenericDialog *UNUSED(gd), gpointer UNUSED(data))
1428 /* no op, only so cancel button appears */
1431 static void filter_default_cb(GtkWidget *widget, gpointer data)
1435 gd = generic_dialog_new(_("Reset filters"),
1436 "reset_filter", widget, TRUE,
1437 dummy_cancel_cb, data);
1438 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION, _("Reset filters"),
1439 _("This will reset the file filters to the defaults.\nContinue?"), TRUE);
1440 generic_dialog_add_button(gd, GTK_STOCK_OK, nullptr, filter_default_ok_cb, TRUE);
1441 gtk_widget_show(gd->dialog);
1444 static void filter_disable_cb(GtkWidget *widget, gpointer data)
1446 auto frame = static_cast<GtkWidget *>(data);
1448 gtk_widget_set_sensitive(frame,
1449 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1452 static void safe_delete_view_cb(GtkWidget *UNUSED(widget), gpointer UNUSED(data))
1454 layout_set_path(nullptr, gtk_entry_get_text(GTK_ENTRY(safe_delete_path_entry)));
1457 static void safe_delete_clear_ok_cb(GenericDialog *UNUSED(gd), gpointer UNUSED(data))
1459 file_util_trash_clear();
1462 static void safe_delete_clear_cb(GtkWidget *widget, gpointer UNUSED(data))
1466 gd = generic_dialog_new(_("Clear trash"),
1467 "clear_trash", widget, TRUE,
1468 dummy_cancel_cb, nullptr);
1469 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION, _("Clear trash"),
1470 _("This will remove the trash contents."), FALSE);
1471 generic_dialog_add_button(gd, GTK_STOCK_OK, nullptr, safe_delete_clear_ok_cb, TRUE);
1472 entry = gtk_entry_new();
1473 gtk_widget_set_can_focus(entry, FALSE);
1474 gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
1475 if (options->file_ops.safe_delete_path) gtk_entry_set_text(GTK_ENTRY(entry), options->file_ops.safe_delete_path);
1476 gtk_box_pack_start(GTK_BOX(gd->vbox), entry, FALSE, FALSE, 0);
1477 gtk_widget_show(entry);
1478 gtk_widget_show(gd->dialog);
1481 static void image_overlay_template_view_changed_cb(GtkWidget *UNUSED(widget), gpointer data)
1483 GtkWidget *pTextView;
1484 GtkTextBuffer *pTextBuffer;
1488 pTextView = GTK_WIDGET(data);
1490 pTextBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pTextView));
1491 gtk_text_buffer_get_start_iter(pTextBuffer, &iStart);
1492 gtk_text_buffer_get_end_iter(pTextBuffer, &iEnd);
1494 set_image_overlay_template_string(&c_options->image_overlay.template_string,
1495 gtk_text_buffer_get_text(pTextBuffer, &iStart, &iEnd, TRUE));
1498 static void image_overlay_default_template_ok_cb(GenericDialog *UNUSED(gd), gpointer data)
1500 auto text_view = static_cast<GtkTextView *>(data);
1501 GtkTextBuffer *buffer;
1503 set_default_image_overlay_template_string(&options->image_overlay.template_string);
1504 if (!configwindow) return;
1506 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
1507 gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
1510 static void image_overlay_default_template_cb(GtkWidget *widget, gpointer data)
1514 gd = generic_dialog_new(_("Reset image overlay template string"),
1515 "reset_image_overlay_template_string", widget, TRUE,
1516 dummy_cancel_cb, data);
1517 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION, _("Reset image overlay template string"),
1518 _("This will reset the image overlay template string to the default.\nContinue?"), TRUE);
1519 generic_dialog_add_button(gd, GTK_STOCK_OK, nullptr, image_overlay_default_template_ok_cb, TRUE);
1520 gtk_widget_show(gd->dialog);
1523 static void image_overlay_help_cb(GtkWidget *UNUSED(widget), gpointer UNUSED(data))
1525 help_window_show("GuideOptionsOSD.html");
1528 static void image_overlay_set_font_cb(GtkWidget *widget, gpointer UNUSED(data))
1532 PangoFontDescription *font_desc;
1534 dialog = gtk_font_chooser_dialog_new("Image Overlay Font", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1535 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), options->image_overlay.font);
1537 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1539 font_desc = gtk_font_chooser_get_font_desc(GTK_FONT_CHOOSER(dialog));
1540 font = pango_font_description_to_string(font_desc);
1541 g_free(c_options->image_overlay.font);
1542 c_options->image_overlay.font = g_strdup(font);
1546 gtk_widget_destroy(dialog);
1549 static void image_overlay_set_text_colour_cb(GtkWidget *widget, gpointer UNUSED(data))
1554 dialog = gtk_color_chooser_dialog_new("Image Overlay Text Colour", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1555 colour.red = options->image_overlay.text_red;
1556 colour.green = options->image_overlay.text_green;
1557 colour.blue = options->image_overlay.text_blue;
1558 colour.alpha = options->image_overlay.text_alpha;
1559 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1560 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1562 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1564 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1565 c_options->image_overlay.text_red = colour.red*255;
1566 c_options->image_overlay.text_green = colour.green*255;
1567 c_options->image_overlay.text_blue = colour.blue*255;
1568 c_options->image_overlay.text_alpha = colour.alpha*255;
1570 gtk_widget_destroy(dialog);
1574 static void image_overlay_set_background_colour_cb(GtkWidget *widget, gpointer UNUSED(data))
1579 dialog = gtk_color_chooser_dialog_new("Image Overlay Background Colour", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1580 colour.red = options->image_overlay.background_red;
1581 colour.green = options->image_overlay.background_green;
1582 colour.blue = options->image_overlay.background_blue;
1583 colour.alpha = options->image_overlay.background_alpha;
1584 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1585 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1587 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1589 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1590 c_options->image_overlay.background_red = colour.red*255;
1591 c_options->image_overlay.background_green = colour.green*255;
1592 c_options->image_overlay.background_blue = colour.blue*255;
1593 c_options->image_overlay.background_alpha = colour.alpha*255;
1595 gtk_widget_destroy(dialog);
1598 static void accel_store_populate()
1601 GList *groups, *actions;
1603 const gchar *accel_path;
1607 if (!accel_store || !layout_window_list || !layout_window_list->data) return;
1609 gtk_tree_store_clear(accel_store);
1610 lw = static_cast<LayoutWindow *>(layout_window_list->data); /* get the actions from the first window, it should not matter, they should be the same in all windows */
1612 g_assert(lw && lw->ui_manager);
1613 groups = gtk_ui_manager_get_action_groups(lw->ui_manager);
1616 actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
1619 action = GTK_ACTION(actions->data);
1620 accel_path = gtk_action_get_accel_path(action);
1621 if (accel_path && gtk_accel_map_lookup_entry(accel_path, &key))
1623 gchar *label, *label2, *tooltip, *accel;
1624 g_object_get(action,
1625 "tooltip", &tooltip,
1629 if (pango_parse_markup(label, -1, '_', nullptr, &label2, nullptr, nullptr) && label2)
1635 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1639 gtk_tree_store_append(accel_store, &iter, nullptr);
1640 gtk_tree_store_set(accel_store, &iter,
1643 AE_TOOLTIP, tooltip ? tooltip : "",
1644 AE_ACCEL, accel_path,
1652 actions = actions->next;
1655 groups = groups->next;
1659 static void accel_store_cleared_cb(GtkCellRendererAccel *UNUSED(accel), gchar *UNUSED(path_string), gpointer UNUSED(user_data))
1664 static gboolean accel_remove_key_cb(GtkTreeModel *model, GtkTreePath *UNUSED(path), GtkTreeIter *iter, gpointer data)
1666 auto accel1 = static_cast<gchar *>(data);
1671 gtk_tree_model_get(model, iter, AE_KEY, &accel2, -1);
1673 gtk_accelerator_parse(accel1, &key1.accel_key, &key1.accel_mods);
1674 gtk_accelerator_parse(accel2, &key2.accel_key, &key2.accel_mods);
1676 if (key1.accel_key == key2.accel_key && key1.accel_mods == key2.accel_mods)
1678 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1679 DEBUG_1("accelerator key '%s' is already used, removing.", accel1);
1688 static void accel_store_edited_cb(GtkCellRendererAccel *UNUSED(accel), gchar *path_string, guint accel_key, GdkModifierType accel_mods, guint UNUSED(hardware_keycode), gpointer UNUSED(user_data))
1690 auto model = reinterpret_cast<GtkTreeModel *>(accel_store);
1694 GtkAccelKey old_key, key;
1695 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1697 gtk_tree_model_get_iter(model, &iter, path);
1698 gtk_tree_model_get(model, &iter, AE_ACCEL, &accel_path, -1);
1700 /* test if the accelerator can be stored without conflicts*/
1701 gtk_accel_map_lookup_entry(accel_path, &old_key);
1703 /* change the key and read it back (change may fail on keys hardcoded in gtk)*/
1704 gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, TRUE);
1705 gtk_accel_map_lookup_entry(accel_path, &key);
1707 /* restore the original for now, the key will be really changed when the changes are confirmed */
1708 gtk_accel_map_change_entry(accel_path, old_key.accel_key, old_key.accel_mods, TRUE);
1710 acc = gtk_accelerator_name(key.accel_key, key.accel_mods);
1711 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, acc);
1713 gtk_tree_store_set(accel_store, &iter, AE_KEY, acc, -1);
1714 gtk_tree_path_free(path);
1718 static gboolean accel_default_scroll(GtkTreeView *data)
1722 GtkTreeViewColumn *column;
1724 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(accel_store), &iter);
1725 path = gtk_tree_model_get_path(GTK_TREE_MODEL(accel_store), &iter);
1726 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1728 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1732 gtk_tree_path_free(path);
1734 return(G_SOURCE_REMOVE);
1737 static void accel_default_cb(GtkWidget *UNUSED(widget), gpointer data)
1739 accel_store_populate();
1741 g_idle_add(reinterpret_cast<GSourceFunc>(accel_default_scroll), data);
1744 void accel_clear_selection(GtkTreeModel *UNUSED(model), GtkTreePath *UNUSED(path), GtkTreeIter *iter, gpointer UNUSED(data))
1746 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1749 void accel_reset_selection(GtkTreeModel *model, GtkTreePath *UNUSED(path), GtkTreeIter *iter, gpointer UNUSED(data))
1752 gchar *accel_path, *accel;
1754 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, -1);
1755 gtk_accel_map_lookup_entry(accel_path, &key);
1756 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1758 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, accel);
1760 gtk_tree_store_set(accel_store, iter, AE_KEY, accel, -1);
1765 static void accel_clear_cb(GtkWidget *UNUSED(widget), gpointer data)
1767 GtkTreeSelection *selection;
1769 if (!accel_store) return;
1770 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1771 gtk_tree_selection_selected_foreach(selection, &accel_clear_selection, nullptr);
1774 static void accel_reset_cb(GtkWidget *UNUSED(widget), gpointer data)
1776 GtkTreeSelection *selection;
1778 if (!accel_store) return;
1779 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1780 gtk_tree_selection_selected_foreach(selection, &accel_reset_selection, nullptr);
1785 static GtkWidget *scrolled_notebook_page(GtkWidget *notebook, const gchar *title)
1789 GtkWidget *scrolled;
1790 GtkWidget *viewport;
1792 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
1793 gtk_container_set_border_width(GTK_CONTAINER(scrolled), PREF_PAD_BORDER);
1794 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1795 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1796 label = gtk_label_new(title);
1797 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, label);
1798 gtk_widget_show(scrolled);
1800 viewport = gtk_viewport_new(nullptr, nullptr);
1801 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1802 gtk_container_add(GTK_CONTAINER(scrolled), viewport);
1803 gtk_widget_show(viewport);
1805 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1806 gtk_container_add(GTK_CONTAINER(viewport), vbox);
1807 gtk_widget_show(vbox);
1812 static void cache_standard_cb(GtkWidget *widget, gpointer UNUSED(data))
1814 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1816 c_options->thumbnails.spec_standard =TRUE;
1817 c_options->thumbnails.cache_into_dirs = FALSE;
1821 static void cache_geeqie_cb(GtkWidget *widget, gpointer UNUSED(data))
1823 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1825 c_options->thumbnails.spec_standard =FALSE;
1826 c_options->thumbnails.cache_into_dirs = FALSE;
1830 static void cache_local_cb(GtkWidget *widget, gpointer UNUSED(data))
1832 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1834 c_options->thumbnails.cache_into_dirs = TRUE;
1835 c_options->thumbnails.spec_standard =FALSE;
1839 static void help_search_engine_entry_icon_cb(GtkEntry *UNUSED(entry), GtkEntryIconPosition pos,
1840 GdkEvent *UNUSED(event), gpointer userdata)
1842 if (pos == GTK_ENTRY_ICON_PRIMARY)
1844 gtk_entry_set_text(GTK_ENTRY(userdata), HELP_SEARCH_ENGINE);
1848 gtk_entry_set_text(GTK_ENTRY(userdata), "");
1852 static void star_rating_star_icon_cb(GtkEntry *UNUSED(entry), GtkEntryIconPosition pos,
1853 GdkEvent *UNUSED(event), gpointer userdata)
1855 gchar *rating_symbol;
1857 if (pos == GTK_ENTRY_ICON_PRIMARY)
1859 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_STAR);
1860 gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1861 g_free(rating_symbol);
1865 gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1866 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1867 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1871 static void star_rating_rejected_icon_cb(GtkEntry *UNUSED(entry), GtkEntryIconPosition pos,
1872 GdkEvent *UNUSED(event), gpointer userdata)
1874 gchar *rating_symbol;
1876 if (pos == GTK_ENTRY_ICON_PRIMARY)
1878 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_REJECTED);
1879 gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1880 g_free(rating_symbol);
1884 gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1885 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1886 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1890 static guint star_rating_symbol_test(GtkWidget *UNUSED(widget), gpointer data)
1892 auto hbox = static_cast<GtkContainer *>(data);
1893 GString *str = g_string_new(nullptr);
1894 GtkEntry *hex_code_entry;
1895 gchar *hex_code_full;
1898 guint64 hex_value = 0;
1900 list = gtk_container_get_children(hbox);
1902 hex_code_entry = static_cast<GtkEntry *>(g_list_nth_data(list, 2));
1903 hex_code_full = g_strdup(gtk_entry_get_text(hex_code_entry));
1905 hex_code = g_strsplit(hex_code_full, "+", 2);
1906 if (hex_code[0] && hex_code[1])
1908 hex_value = strtoull(hex_code[1], nullptr, 16);
1910 if (!hex_value || hex_value > 0x10FFFF)
1912 hex_value = 0x003F; // Unicode 'Question Mark'
1914 str = g_string_append_unichar(str, static_cast<gunichar>(hex_value));
1915 gtk_label_set_text(static_cast<GtkLabel *>(g_list_nth_data(list, 1)), str->str);
1917 g_strfreev(hex_code);
1918 g_string_free(str, TRUE);
1919 g_free(hex_code_full);
1924 static void star_rating_star_test_cb(GtkWidget *widget, gpointer data)
1926 guint64 star_symbol;
1928 star_symbol = star_rating_symbol_test(widget, data);
1929 c_options->star_rating.star = star_symbol;
1932 static void star_rating_rejected_test_cb(GtkWidget *widget, gpointer data)
1934 guint64 rejected_symbol;
1936 rejected_symbol = star_rating_symbol_test(widget, data);
1937 c_options->star_rating.rejected = rejected_symbol;
1940 /* general options tab */
1941 static void timezone_database_install_cb(GtkWidget *widget, gpointer data);
1945 GCancellable *cancellable;
1947 GtkWidget *progress;
1949 GFile *timezone_database_gq;
1950 gchar *timezone_database_user;
1953 static void config_tab_general(GtkWidget *notebook)
1958 GtkWidget *group_frame;
1959 GtkWidget *subgroup;
1961 GtkWidget *ct_button;
1964 gint hours, minutes, remainder;
1966 GtkWidget *star_rating_entry;
1968 gchar *rating_symbol;
1971 gchar *download_locn;
1972 GNetworkMonitor *net_mon;
1973 GSocketConnectable *tz_org;
1974 gboolean internet_available = FALSE;
1977 vbox = scrolled_notebook_page(notebook, _("General"));
1979 group = pref_group_new(vbox, FALSE, _("Thumbnails"), GTK_ORIENTATION_VERTICAL);
1981 table = pref_table_new(group, 2, 2, FALSE, FALSE);
1982 add_thumb_size_menu(table, 0, 0, _("Size:"));
1983 add_quality_menu(table, 0, 1, _("Quality:"), options->thumbnails.quality, &c_options->thumbnails.quality);
1985 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1986 pref_label_new(hbox, _("Custom size: "));
1987 pref_spin_new_int(hbox, _("Width:"), nullptr, 1, 512, 1, options->thumbnails.max_width, &c_options->thumbnails.max_width);
1988 pref_spin_new_int(hbox, _("Height:"), nullptr, 1, 512, 1, options->thumbnails.max_height, &c_options->thumbnails.max_height);
1990 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails and sim. files"),
1991 options->thumbnails.enable_caching, &c_options->thumbnails.enable_caching);
1993 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1994 pref_checkbox_link_sensitivity(ct_button, subgroup);
1996 c_options->thumbnails.spec_standard = options->thumbnails.spec_standard;
1997 c_options->thumbnails.cache_into_dirs = options->thumbnails.cache_into_dirs;
1998 group_frame = pref_frame_new(subgroup, TRUE, _("Use Geeqie thumbnail style and cache"),
1999 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2000 button = pref_radiobutton_new(group_frame, nullptr, get_thumbnails_cache_dir(),
2001 !options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2002 G_CALLBACK(cache_geeqie_cb), nullptr);
2004 group_frame = pref_frame_new(subgroup, TRUE,
2005 _("Store thumbnails local to image folder (non-standard)"),
2006 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2007 pref_radiobutton_new(group_frame, button, "*/.thumbnails",
2008 !options->thumbnails.spec_standard && options->thumbnails.cache_into_dirs,
2009 G_CALLBACK(cache_local_cb), nullptr);
2011 group_frame = pref_frame_new(subgroup, TRUE,
2012 _("Use standard thumbnail style and cache, shared with other applications"),
2013 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2014 pref_radiobutton_new(group_frame, button, get_thumbnails_standard_cache_dir(),
2015 options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2016 G_CALLBACK(cache_standard_cb), nullptr);
2018 pref_checkbox_new_int(group, _("Use EXIF thumbnails when available (EXIF thumbnails may be outdated)"),
2019 options->thumbnails.use_exif, &c_options->thumbnails.use_exif);
2021 pref_checkbox_new_int(group, _("Thumbnail color management"),
2022 options->thumbnails.use_color_management, &c_options->thumbnails.use_color_management);
2024 spin = pref_spin_new_int(group, _("Collection preview:"), nullptr,
2026 options->thumbnails.collection_preview, &c_options->thumbnails.collection_preview);
2027 gtk_widget_set_tooltip_text(spin, _("The maximum number of thumbnails shown in a Collection preview montage"));
2029 #ifdef HAVE_FFMPEGTHUMBNAILER_METADATA
2030 pref_checkbox_new_int(group, _("Use embedded metadata in video files as thumbnails when available"),
2031 options->thumbnails.use_ft_metadata, &c_options->thumbnails.use_ft_metadata);
2033 // pref_checkbox_new_int(group, _("Ignore embedded metadata if size is too small"),
2034 // options->thumbnails.use_ft_metadata_small, &c_options->thumbnails.use_ft_metadata_small);
2037 pref_spacer(group, PREF_PAD_GROUP);
2039 group = pref_group_new(vbox, FALSE, _("Star Rating"), GTK_ORIENTATION_VERTICAL);
2041 c_options->star_rating.star = options->star_rating.star;
2042 c_options->star_rating.rejected = options->star_rating.rejected;
2044 str = g_string_new(nullptr);
2045 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2046 pref_label_new(hbox, _("Star character: "));
2047 str = g_string_append_unichar(str, options->star_rating.star);
2048 pref_label_new(hbox, g_strdup(str->str));
2049 rating_symbol = g_strdup_printf("U+%X", options->star_rating.star);
2050 star_rating_entry = gtk_entry_new();
2051 gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2052 gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2053 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2054 gtk_widget_show(star_rating_entry);
2055 button = pref_button_new(nullptr, nullptr, _("Set"), FALSE,
2056 G_CALLBACK(star_rating_star_test_cb), hbox);
2057 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2058 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2059 gtk_widget_show(button);
2060 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."));
2061 gtk_entry_set_icon_from_stock(GTK_ENTRY(star_rating_entry),
2062 GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
2063 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2064 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2065 gtk_entry_set_icon_from_stock(GTK_ENTRY(star_rating_entry),
2066 GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_REVERT_TO_SAVED);
2067 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2068 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2069 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2070 G_CALLBACK(star_rating_star_icon_cb),
2073 g_string_free(str, TRUE);
2074 g_free(rating_symbol);
2076 str = g_string_new(nullptr);
2077 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2078 pref_label_new(hbox, _("Rejected character: "));
2079 str = g_string_append_unichar(str, options->star_rating.rejected);
2080 pref_label_new(hbox, g_strdup(str->str));
2081 rating_symbol = g_strdup_printf("U+%X", options->star_rating.rejected);
2082 star_rating_entry = gtk_entry_new();
2083 gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2084 gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2085 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2086 gtk_widget_show(star_rating_entry);
2087 button = pref_button_new(nullptr, nullptr, _("Set"), FALSE,
2088 G_CALLBACK(star_rating_rejected_test_cb), hbox);
2089 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2090 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2091 gtk_widget_show(button);
2092 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."));
2093 gtk_entry_set_icon_from_stock(GTK_ENTRY(star_rating_entry),
2094 GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
2095 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2096 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2097 gtk_entry_set_icon_from_stock(GTK_ENTRY(star_rating_entry),
2098 GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_REVERT_TO_SAVED);
2099 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2100 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2101 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2102 G_CALLBACK(star_rating_rejected_icon_cb),
2105 g_string_free(str, TRUE);
2106 g_free(rating_symbol);
2108 pref_spacer(group, PREF_PAD_GROUP);
2110 group = pref_group_new(vbox, FALSE, _("Slide show"), GTK_ORIENTATION_VERTICAL);
2112 c_options->slideshow.delay = options->slideshow.delay;
2113 hours = options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2114 remainder = options->slideshow.delay % (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2115 minutes = remainder / (60 * SLIDESHOW_SUBSECOND_PRECISION);
2116 seconds = static_cast<gdouble>(remainder % (60 * SLIDESHOW_SUBSECOND_PRECISION)) /
2117 SLIDESHOW_SUBSECOND_PRECISION;
2119 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2121 spin = pref_spin_new(hbox, _("Delay between image change hrs:mins:secs.dec"), nullptr,
2123 options->slideshow.delay ? hours : 0.0,
2124 G_CALLBACK(slideshow_delay_hours_cb), nullptr);
2125 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2126 spin = pref_spin_new(hbox, ":" , nullptr,
2128 options->slideshow.delay ? minutes: 0.0,
2129 G_CALLBACK(slideshow_delay_minutes_cb), nullptr);
2130 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2131 spin = pref_spin_new(hbox, ":", nullptr,
2132 SLIDESHOW_MIN_SECONDS, 59, 1.0, 1,
2133 options->slideshow.delay ? seconds : 10.0,
2134 G_CALLBACK(slideshow_delay_seconds_cb), nullptr);
2135 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2137 pref_checkbox_new_int(group, _("Random"), options->slideshow.random, &c_options->slideshow.random);
2138 pref_checkbox_new_int(group, _("Repeat"), options->slideshow.repeat, &c_options->slideshow.repeat);
2140 pref_spacer(group, PREF_PAD_GROUP);
2142 group = pref_group_new(vbox, FALSE, _("Image loading and caching"), GTK_ORIENTATION_VERTICAL);
2144 pref_spin_new_int(group, _("Decoded image cache size (MiB):"), nullptr,
2145 0, 99999, 1, options->image.image_cache_max, &c_options->image.image_cache_max);
2146 pref_checkbox_new_int(group, _("Preload next image"),
2147 options->image.enable_read_ahead, &c_options->image.enable_read_ahead);
2149 pref_checkbox_new_int(group, _("Refresh on file change"),
2150 options->update_on_time_change, &c_options->update_on_time_change);
2153 pref_spacer(group, PREF_PAD_GROUP);
2155 group = pref_group_new(vbox, FALSE, _("Expand menu and toolbar"), GTK_ORIENTATION_VERTICAL);
2157 pref_checkbox_new_int(group, _("Expand menu and toolbar (NOTE! Geeqie must be restarted for change to take effect)"),
2158 options->expand_menu_toolbar, &c_options->expand_menu_toolbar);
2159 gtk_widget_set_tooltip_text(group, _("Expand the menu and toolbar to the full width of the window"));
2161 pref_spacer(group, PREF_PAD_GROUP);
2163 if (g_getenv("APPDIR") && strstr(g_getenv("APPDIR"), "/tmp/.mount_Geeqie"))
2165 group = pref_group_new(vbox, FALSE, _("AppImage updates notifications"), GTK_ORIENTATION_VERTICAL);
2166 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2167 pref_checkbox_new_int(group, _("Enable"), options->appimage_notifications, &c_options->appimage_notifications);
2168 gtk_widget_set_tooltip_text(group, _("Show a notification on start-up if the server has a newer version than the current. Requires an Internet connection"));
2170 pref_spacer(group, PREF_PAD_GROUP);
2174 net_mon = g_network_monitor_get_default();
2175 tz_org = g_network_address_parse_uri(TIMEZONE_DATABASE_WEB, 80, nullptr);
2178 internet_available = g_network_monitor_can_reach(net_mon, tz_org, nullptr, nullptr);
2179 g_object_unref(tz_org);
2182 group = pref_group_new(vbox, FALSE, _("Timezone database"), GTK_ORIENTATION_VERTICAL);
2183 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2185 if (!internet_available)
2187 gtk_widget_set_sensitive(group, FALSE);
2190 tz = g_new0(TZData, 1);
2192 path = path_from_utf8(TIMEZONE_DATABASE_WEB);
2193 basename = g_path_get_basename(path);
2194 tz->timezone_database_user = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
2198 if (isfile(tz->timezone_database_user))
2200 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Update"), FALSE, G_CALLBACK(timezone_database_install_cb), tz);
2204 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Install"), FALSE, G_CALLBACK(timezone_database_install_cb), tz);
2207 download_locn = g_strconcat(_("Download database from: "), TIMEZONE_DATABASE_WEB, NULL);
2208 pref_label_new(GTK_WIDGET(hbox), download_locn);
2209 g_free(download_locn);
2211 if (!internet_available)
2213 gtk_widget_set_tooltip_text(button, _("No Internet connection!\nThe timezone database is used to display exif time and date\ncorrected for UTC offset and Daylight Saving Time"));
2217 gtk_widget_set_tooltip_text(button, _("The timezone database is used to display exif time and date\ncorrected for UTC offset and Daylight Saving Time"));
2219 gtk_widget_show(button);
2221 pref_spacer(group, PREF_PAD_GROUP);
2223 group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL);
2225 help_search_engine_entry = gtk_entry_new();
2226 gtk_entry_set_text(GTK_ENTRY(help_search_engine_entry), options->help_search_engine);
2227 gtk_box_pack_start(GTK_BOX(group), help_search_engine_entry, FALSE, FALSE, 0);
2228 gtk_widget_show(help_search_engine_entry);
2230 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"));
2232 gtk_entry_set_icon_from_stock(GTK_ENTRY(help_search_engine_entry),
2233 GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
2234 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2235 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2236 gtk_entry_set_icon_from_stock(GTK_ENTRY(help_search_engine_entry),
2237 GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_REVERT_TO_SAVED);
2238 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2239 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2240 g_signal_connect(GTK_ENTRY(help_search_engine_entry), "icon-press",
2241 G_CALLBACK(help_search_engine_entry_icon_cb),
2242 help_search_engine_entry);
2246 static void config_tab_image(GtkWidget *notebook)
2251 GtkWidget *ct_button;
2252 GtkWidget *enlargement_button;
2255 //~ #ifdef HAVE_CLUTTER
2256 //~ GtkWidget *gpu_accel;
2259 vbox = scrolled_notebook_page(notebook, _("Image"));
2261 group = pref_group_new(vbox, FALSE, _("Zoom"), GTK_ORIENTATION_VERTICAL);
2263 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2264 add_quality_menu(table, 0, 0, _("Quality:"), options->image.zoom_quality, &c_options->image.zoom_quality);
2265 //~ if (options->image.use_clutter_renderer && !options->disable_gpu)
2267 //~ gtk_widget_set_sensitive(table, FALSE);
2270 //~ #ifdef HAVE_CLUTTER
2271 //~ gpu_accel = pref_checkbox_new_int(group, _("Use GPU acceleration via Clutter library (Requires restart)"),
2272 //~ options->image.use_clutter_renderer, &c_options->image.use_clutter_renderer);
2273 //~ if (options->disable_gpu && !options->override_disable_gpu)
2275 //~ gtk_widget_set_sensitive(gpu_accel, FALSE);
2279 pref_checkbox_new_int(group, _("Two pass rendering (apply HQ zoom and color correction in second pass)"),
2280 options->image.zoom_2pass, &c_options->image.zoom_2pass);
2281 //~ if (options->image.use_clutter_renderer && !options->disable_gpu)
2283 //~ gtk_widget_set_sensitive(two_pass, FALSE);
2286 c_options->image.zoom_increment = options->image.zoom_increment;
2287 spin = pref_spin_new(group, _("Zoom increment:"), nullptr,
2288 0.01, 4.0, 0.01, 2, static_cast<gdouble>(options->image.zoom_increment) / 100.0,
2289 G_CALLBACK(zoom_increment_cb), nullptr);
2290 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2292 c_options->image.zoom_style = options->image.zoom_style;
2293 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2294 add_zoom_style_selection_menu(table, 0, 0, _("Zoom style:"), options->image.zoom_style, &c_options->image.zoom_style);
2296 group = pref_group_new(vbox, FALSE, _("Fit image to window"), GTK_ORIENTATION_VERTICAL);
2298 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2299 enlargement_button = pref_checkbox_new_int(hbox, _("Allow enlargement of image (max. size in %)"),
2300 options->image.zoom_to_fit_allow_expand, &c_options->image.zoom_to_fit_allow_expand);
2301 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2303 options->image.max_enlargement_size, &c_options->image.max_enlargement_size);
2304 pref_checkbox_link_sensitivity(enlargement_button, spin);
2305 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."));
2307 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2308 ct_button = pref_checkbox_new_int(hbox, _("Virtual window size (% of actual window):"),
2309 options->image.limit_autofit_size, &c_options->image.limit_autofit_size);
2310 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2312 options->image.max_autofit_size, &c_options->image.max_autofit_size);
2313 pref_checkbox_link_sensitivity(ct_button, spin);
2314 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."));
2316 group = pref_group_new(vbox, FALSE, _("Tile size"), GTK_ORIENTATION_VERTICAL);
2317 //~ gtk_widget_set_sensitive(group, !options->image.use_clutter_renderer);
2319 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2320 spin = pref_spin_new_int(hbox, _("Pixels"), _("(Requires restart)"),
2322 options->image.tile_size, &c_options->image.tile_size);
2323 gtk_widget_set_tooltip_text(GTK_WIDGET(hbox), _("This value changes the size of the tiles large images are split into. Increasing the size of the tiles will reduce the tiling effect seen on image changes, but will also slightly increase the delay before the first part of a large image is seen."));
2325 group = pref_group_new(vbox, FALSE, _("Appearance"), GTK_ORIENTATION_VERTICAL);
2327 pref_checkbox_new_int(group, _("Use custom border color in window mode"),
2328 options->image.use_custom_border_color, &c_options->image.use_custom_border_color);
2330 pref_checkbox_new_int(group, _("Use custom border color in fullscreen mode"),
2331 options->image.use_custom_border_color_in_fullscreen, &c_options->image.use_custom_border_color_in_fullscreen);
2333 pref_color_button_new(group, _("Border color"), &options->image.border_color,
2334 G_CALLBACK(pref_color_button_set_cb), &c_options->image.border_color);
2336 c_options->image.border_color = options->image.border_color;
2338 pref_color_button_new(group, _("Alpha channel color 1"), &options->image.alpha_color_1,
2339 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_1);
2341 pref_color_button_new(group, _("Alpha channel color 2"), &options->image.alpha_color_2,
2342 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_2);
2344 c_options->image.alpha_color_1 = options->image.alpha_color_1;
2345 c_options->image.alpha_color_2 = options->image.alpha_color_2;
2350 static void save_default_window_layout_cb(GtkWidget *UNUSED(widget), gpointer UNUSED(data))
2352 LayoutWindow *lw = nullptr;
2353 gchar *default_path;
2356 /* Get current lw */
2359 tmp_id = lw->options.id;
2360 lw->options.id = g_strdup("lw_default");
2362 default_path = g_build_filename(get_rc_dir(), DEFAULT_WINDOW_LAYOUT, NULL);
2363 save_default_layout_options_to_file(default_path, options, lw);
2364 g_free(lw->options.id);
2365 lw->options.id = tmp_id;
2366 g_free(default_path);
2369 static gboolean popover_cb(gpointer data)
2371 auto popover = static_cast<GtkPopover *>(data);
2373 gtk_popover_popdown(popover);
2378 static void default_layout_changed_cb(GtkWidget *UNUSED(button), GtkPopover *popover)
2380 gtk_popover_popup(popover);
2382 g_timeout_add(2000, popover_cb, popover);
2385 static GtkWidget *create_popover(GtkWidget *parent, GtkWidget *child, GtkPositionType pos)
2389 popover = gtk_popover_new(parent);
2390 gtk_popover_set_position(GTK_POPOVER (popover), pos);
2391 gtk_container_add (GTK_CONTAINER(popover), child);
2392 gtk_container_set_border_width(GTK_CONTAINER (popover), 6);
2393 gtk_widget_show (child);
2398 static void config_tab_windows(GtkWidget *notebook)
2403 GtkWidget *subgroup;
2405 GtkWidget *ct_button;
2408 vbox = scrolled_notebook_page(notebook, _("Windows"));
2410 group = pref_group_new(vbox, FALSE, _("State"), GTK_ORIENTATION_VERTICAL);
2412 ct_button = pref_checkbox_new_int(group, _("Remember session"),
2413 options->save_window_positions, &c_options->save_window_positions);
2415 button = pref_checkbox_new_int(group, _("Use saved window positions also for new windows"),
2416 options->use_saved_window_positions_for_new_windows, &c_options->use_saved_window_positions_for_new_windows);
2417 pref_checkbox_link_sensitivity(ct_button, button);
2419 button = pref_checkbox_new_int(group, _("Remember window workspace"),
2420 options->save_window_workspace, &c_options->save_window_workspace);
2421 pref_checkbox_link_sensitivity(ct_button, button);
2423 pref_checkbox_new_int(group, _("Remember tool state (float/hidden)"),
2424 options->tools_restore_state, &c_options->tools_restore_state);
2426 pref_checkbox_new_int(group, _("Remember dialog window positions"),
2427 options->save_dialog_window_positions, &c_options->save_dialog_window_positions);
2429 pref_checkbox_new_int(group, _("Show window IDs"),
2430 options->show_window_ids, &c_options->show_window_ids);
2432 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2433 pref_label_new(subgroup, _("Use current layout for default: "));
2434 button = pref_button_new(subgroup, nullptr, _("Set"), FALSE, G_CALLBACK(save_default_window_layout_cb), nullptr);
2438 popover = create_popover(button, gtk_label_new(_("Current window layout\nhas been set as default")), GTK_POS_TOP);
2439 gtk_popover_set_modal(GTK_POPOVER (popover), FALSE);
2440 g_signal_connect(button, "clicked", G_CALLBACK(default_layout_changed_cb), popover);
2442 group = pref_group_new(vbox, FALSE, _("Size"), GTK_ORIENTATION_VERTICAL);
2444 pref_checkbox_new_int(group, _("Fit window to image when tools are hidden/floating"),
2445 options->image.fit_window_to_image, &c_options->image.fit_window_to_image);
2447 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2448 ct_button = pref_checkbox_new_int(hbox, _("Limit size when auto-sizing window (%):"),
2449 options->image.limit_window_size, &c_options->image.limit_window_size);
2450 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2452 options->image.max_window_size, &c_options->image.max_window_size);
2453 pref_checkbox_link_sensitivity(ct_button, spin);
2455 group = pref_group_new(vbox, FALSE, _("Full screen"), GTK_ORIENTATION_VERTICAL);
2457 c_options->fullscreen.screen = options->fullscreen.screen;
2458 c_options->fullscreen.above = options->fullscreen.above;
2459 hbox = fullscreen_prefs_selection_new(_("Location:"), &c_options->fullscreen.screen, &c_options->fullscreen.above);
2460 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2461 gtk_widget_show(hbox);
2463 pref_checkbox_new_int(group, _("Smooth image flip"),
2464 options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
2465 pref_checkbox_new_int(group, _("Disable screen saver"),
2466 options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
2469 #define PRE_FORMATTED_COLUMNS 5
2470 static void config_tab_osd(GtkWidget *notebook)
2476 GtkWidget *image_overlay_template_view;
2477 GtkWidget *scrolled;
2478 GtkWidget *scrolled_pre_formatted;
2479 GtkTextBuffer *buffer;
2481 GtkWidget *subgroup;
2483 vbox = scrolled_notebook_page(notebook, _("OSD"));
2485 image_overlay_template_view = gtk_text_view_new();
2487 group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
2489 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2491 scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
2492 gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
2493 gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
2494 gtk_widget_show(scrolled_pre_formatted);
2495 gtk_widget_show(subgroup);
2497 pref_line(group, PREF_PAD_GAP);
2499 pref_label_new(group, _("Image overlay template"));
2501 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
2502 gtk_widget_set_size_request(scrolled, 200, 150);
2503 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2504 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
2505 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2506 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
2507 gtk_widget_show(scrolled);
2509 gtk_widget_set_tooltip_markup(image_overlay_template_view,
2510 _("Extensive formatting options are shown in the Help file"));
2512 gtk_container_add(GTK_CONTAINER(scrolled), image_overlay_template_view);
2513 gtk_widget_show(image_overlay_template_view);
2515 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2517 button = pref_button_new(nullptr, GTK_STOCK_SELECT_FONT, _("Font"), FALSE,
2518 G_CALLBACK(image_overlay_set_font_cb), notebook);
2520 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2521 gtk_widget_show(button);
2523 button = pref_button_new(nullptr, GTK_STOCK_COLOR_PICKER, _("Text"), FALSE,
2524 G_CALLBACK(image_overlay_set_text_colour_cb), nullptr);
2525 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2526 gtk_widget_show(button);
2528 button = pref_button_new(nullptr, GTK_STOCK_COLOR_PICKER, _("Background"), FALSE,
2529 G_CALLBACK(image_overlay_set_background_colour_cb), nullptr);
2530 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2531 gtk_widget_show(button);
2532 image_overlay_set_text_colours();
2534 button = pref_button_new(nullptr, nullptr, _("Defaults"), FALSE,
2535 G_CALLBACK(image_overlay_default_template_cb), image_overlay_template_view);
2536 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2537 gtk_widget_show(button);
2539 button = pref_button_new(nullptr, GTK_STOCK_HELP, nullptr, FALSE,
2540 G_CALLBACK(image_overlay_help_cb), nullptr);
2541 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2542 gtk_widget_show(button);
2544 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_overlay_template_view));
2545 if (options->image_overlay.template_string) gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
2546 g_signal_connect(G_OBJECT(buffer), "changed",
2547 G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
2549 pref_line(group, PREF_PAD_GAP);
2551 group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL);
2552 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2553 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2554 gtk_widget_show(hbox);
2555 label = gtk_label_new(_("%Exif.Image.Orientation%"));
2556 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2557 gtk_widget_show(label);
2558 pref_spacer(group,TRUE);
2560 group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL);
2561 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2562 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2563 gtk_widget_show(hbox);
2564 label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
2565 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2566 gtk_widget_show(label);
2567 pref_spacer(group,TRUE);
2569 group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL);
2570 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2571 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2572 gtk_widget_show(hbox);
2573 label = gtk_label_new(_("%path:39%"));
2574 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2575 gtk_widget_show(label);
2576 pref_spacer(group,TRUE);
2578 group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL);
2579 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2580 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2581 gtk_widget_show(hbox);
2582 label = gtk_label_new(_("Text shown only if the field is non-null:\n%formatted.Aperture:F no. * setting%\n %formatted.Aperture:10:F no. * setting%"));
2583 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2584 gtk_widget_show(label);
2585 pref_spacer(group,TRUE);
2587 group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL);
2588 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2589 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2590 gtk_widget_show(hbox);
2591 label = gtk_label_new(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
2592 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2593 gtk_widget_show(label);
2596 static GtkTreeModel *create_class_model()
2598 GtkListStore *model;
2602 /* create list store */
2603 model = gtk_list_store_new(1, G_TYPE_STRING);
2604 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
2606 gtk_list_store_append(model, &iter);
2607 gtk_list_store_set(model, &iter, 0, _(format_class_list[i]), -1);
2609 return GTK_TREE_MODEL (model);
2614 static gint filter_table_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
2616 gint n = GPOINTER_TO_INT(data);
2618 FilterEntry *filter_a;
2619 FilterEntry *filter_b;
2621 gtk_tree_model_get(model, a, 0, &filter_a, -1);
2622 gtk_tree_model_get(model, b, 0, &filter_b, -1);
2626 case FILETYPES_COLUMN_ENABLED:
2628 ret = filter_a->enabled - filter_b->enabled;
2631 case FILETYPES_COLUMN_FILTER:
2633 ret = g_utf8_collate(filter_a->extensions, filter_b->extensions);
2636 case FILETYPES_COLUMN_DESCRIPTION:
2638 ret = g_utf8_collate(filter_a->description, filter_b->description);
2641 case FILETYPES_COLUMN_CLASS:
2643 ret = g_strcmp0(format_class_list[filter_a->file_class], format_class_list[filter_b->file_class]);
2646 case FILETYPES_COLUMN_WRITABLE:
2648 ret = filter_a->writable - filter_b->writable;
2651 case FILETYPES_COLUMN_SIDECAR:
2653 ret = filter_a->allow_sidecar - filter_b->allow_sidecar;
2657 g_return_val_if_reached(0);
2663 static gboolean search_function_cb(GtkTreeModel *model, gint UNUSED(column), const gchar *key, GtkTreeIter *iter, gpointer UNUSED(search_data))
2666 gboolean ret = TRUE;
2668 gtk_tree_model_get(model, iter, 0, &fe, -1);
2670 if (g_strstr_len(fe->extensions, -1, key))
2678 static void config_tab_files(GtkWidget *notebook)
2685 GtkWidget *ct_button;
2686 GtkWidget *scrolled;
2687 GtkWidget *filter_view;
2688 GtkCellRenderer *renderer;
2689 GtkTreeSelection *selection;
2690 GtkTreeViewColumn *column;
2692 vbox = scrolled_notebook_page(notebook, _("File Filters"));
2694 group = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2696 pref_checkbox_new_int(group, _("Show hidden files or folders"),
2697 options->file_filter.show_hidden_files, &c_options->file_filter.show_hidden_files);
2698 pref_checkbox_new_int(group, _("Show parent folder (..)"),
2699 options->file_filter.show_parent_directory, &c_options->file_filter.show_parent_directory);
2700 pref_checkbox_new_int(group, _("Case sensitive sort"),
2701 options->file_sort.case_sensitive, &c_options->file_sort.case_sensitive);
2702 pref_checkbox_new_int(group, _("Natural sort order - Requires restart"),
2703 options->file_sort.natural, &c_options->file_sort.natural);
2704 pref_checkbox_new_int(group, _("Disable file extension checks"),
2705 options->file_filter.disable_file_extension_checks, &c_options->file_filter.disable_file_extension_checks);
2707 ct_button = pref_checkbox_new_int(group, _("Disable File Filtering"),
2708 options->file_filter.disable, &c_options->file_filter.disable);
2711 group = pref_group_new(vbox, FALSE, _("Grouping sidecar extensions"), GTK_ORIENTATION_VERTICAL);
2713 sidecar_ext_entry = gtk_entry_new();
2714 gtk_entry_set_text(GTK_ENTRY(sidecar_ext_entry), options->sidecar.ext);
2715 gtk_box_pack_start(GTK_BOX(group), sidecar_ext_entry, FALSE, FALSE, 0);
2716 gtk_widget_show(sidecar_ext_entry);
2718 group = pref_group_new(vbox, TRUE, _("File types"), GTK_ORIENTATION_VERTICAL);
2720 frame = pref_group_parent(group);
2721 g_signal_connect(G_OBJECT(ct_button), "toggled",
2722 G_CALLBACK(filter_disable_cb), frame);
2723 gtk_widget_set_sensitive(frame, !options->file_filter.disable);
2725 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
2726 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2727 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2728 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2729 gtk_widget_show(scrolled);
2731 filter_store = gtk_list_store_new(1, G_TYPE_POINTER);
2732 filter_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
2733 g_object_unref(filter_store);
2734 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
2735 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2737 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), FALSE);
2739 column = gtk_tree_view_column_new();
2740 gtk_tree_view_column_set_title(column, _("Enabled"));
2741 gtk_tree_view_column_set_resizable(column, TRUE);
2743 renderer = gtk_cell_renderer_toggle_new();
2744 g_signal_connect(G_OBJECT(renderer), "toggled",
2745 G_CALLBACK(filter_store_enable_cb), filter_store);
2746 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2747 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2748 GINT_TO_POINTER(FE_ENABLE), nullptr);
2749 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_ENABLED, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_ENABLED), nullptr);
2750 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_ENABLED);
2751 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2753 column = gtk_tree_view_column_new();
2754 gtk_tree_view_column_set_title(column, _("Filter"));
2755 gtk_tree_view_column_set_resizable(column, TRUE);
2756 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_FILTER, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_FILTER), nullptr);
2757 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_FILTER);
2759 renderer = gtk_cell_renderer_text_new();
2760 g_signal_connect(G_OBJECT(renderer), "edited",
2761 G_CALLBACK(filter_store_ext_edit_cb), filter_store);
2762 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2763 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2764 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2765 GINT_TO_POINTER(FE_EXTENSION), nullptr);
2766 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2768 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), TRUE);
2769 gtk_tree_view_set_search_column(GTK_TREE_VIEW(filter_view), FILETYPES_COLUMN_FILTER);
2770 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(filter_view), search_function_cb, nullptr, nullptr);
2772 column = gtk_tree_view_column_new();
2773 gtk_tree_view_column_set_title(column, _("Description"));
2774 gtk_tree_view_column_set_resizable(column, TRUE);
2775 gtk_tree_view_column_set_fixed_width(column, 200);
2776 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2778 renderer = gtk_cell_renderer_text_new();
2779 g_signal_connect(G_OBJECT(renderer), "edited",
2780 G_CALLBACK(filter_store_desc_edit_cb), filter_store);
2781 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2782 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2783 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2784 GINT_TO_POINTER(FE_DESCRIPTION), nullptr);
2785 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2786 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_DESCRIPTION, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_DESCRIPTION), nullptr);
2787 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_DESCRIPTION);
2789 column = gtk_tree_view_column_new();
2790 gtk_tree_view_column_set_title(column, _("Class"));
2791 gtk_tree_view_column_set_resizable(column, TRUE);
2792 renderer = gtk_cell_renderer_combo_new();
2793 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE,
2794 "model", create_class_model(),
2799 g_signal_connect(G_OBJECT(renderer), "edited",
2800 G_CALLBACK(filter_store_class_edit_cb), filter_store);
2801 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2802 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2803 GINT_TO_POINTER(FE_CLASS), nullptr);
2804 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2805 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_CLASS, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_CLASS), nullptr);
2806 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_CLASS);
2808 column = gtk_tree_view_column_new();
2809 gtk_tree_view_column_set_title(column, _("Writable"));
2810 gtk_tree_view_column_set_resizable(column, FALSE);
2811 renderer = gtk_cell_renderer_toggle_new();
2812 g_signal_connect(G_OBJECT(renderer), "toggled",
2813 G_CALLBACK(filter_store_writable_cb), filter_store);
2814 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2815 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2816 GINT_TO_POINTER(FE_WRITABLE), nullptr);
2817 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2818 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_WRITABLE, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_WRITABLE), nullptr);
2819 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_WRITABLE);
2821 column = gtk_tree_view_column_new();
2822 gtk_tree_view_column_set_title(column, _("Sidecar is allowed"));
2823 gtk_tree_view_column_set_resizable(column, FALSE);
2824 renderer = gtk_cell_renderer_toggle_new();
2825 g_signal_connect(G_OBJECT(renderer), "toggled",
2826 G_CALLBACK(filter_store_sidecar_cb), filter_store);
2827 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2828 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2829 GINT_TO_POINTER(FE_ALLOW_SIDECAR), nullptr);
2830 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2831 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_SIDECAR, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_SIDECAR), nullptr);
2832 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_SIDECAR);
2834 filter_store_populate();
2835 gtk_container_add(GTK_CONTAINER(scrolled), filter_view);
2836 gtk_widget_show(filter_view);
2838 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2840 button = pref_button_new(nullptr, nullptr, _("Defaults"), FALSE,
2841 G_CALLBACK(filter_default_cb), filter_view);
2842 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2843 gtk_widget_show(button);
2845 button = pref_button_new(nullptr, GTK_STOCK_REMOVE, nullptr, FALSE,
2846 G_CALLBACK(filter_remove_cb), filter_view);
2847 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2848 gtk_widget_show(button);
2850 button = pref_button_new(nullptr, GTK_STOCK_ADD, nullptr, FALSE,
2851 G_CALLBACK(filter_add_cb), filter_view);
2852 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2853 gtk_widget_show(button);
2857 static void config_tab_metadata(GtkWidget *notebook)
2862 GtkWidget *ct_button;
2864 GtkWidget *tmp_widget;
2866 GtkWidget *text_label;
2868 vbox = scrolled_notebook_page(notebook, _("Metadata"));
2871 group = pref_group_new(vbox, FALSE, _("Metadata writing sequence"), GTK_ORIENTATION_VERTICAL);
2873 label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2875 label = pref_label_new(group, _("When writing metadata, Geeqie will follow these steps, if selected. This process will stop when the first successful write occurs."));
2876 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2877 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2879 gtk_widget_set_tooltip_text(label, _("A flowchart of the sequence is shown in the Help file"));
2881 ct_button = pref_checkbox_new_int(group, "", options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2882 text_label = gtk_bin_get_child(GTK_BIN(ct_button));
2883 markup = g_markup_printf_escaped ("<span weight=\"bold\">%s</span>%s", _("Step 1"), _(") Save metadata in either the image file or the sidecar file, according to the XMP standard"));
2884 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2886 markup = g_markup_printf_escaped ("%s<span style=\"italic\">%s</span>%s<span style=\"italic\">%s</span>%s", _("The destination is dependent on the settings in the "), _("Writable"), _(" and "), _("Sidecar Is Allowed"), _(" columns of the File Filters tab)"));
2887 gtk_widget_set_tooltip_markup(ct_button, markup);
2891 gtk_widget_set_sensitive(ct_button, FALSE);
2894 tmp_widget = pref_checkbox_new_int(group, "", options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2895 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2896 markup = g_markup_printf_escaped ("<span weight=\"bold\">%s</span>%s<span style=\"italic\">%s</span>%s", _("Step 2"), _(") Save metadata in the folder "),".metadata,", _(" local to the image folder (non-standard)"));
2897 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2900 label = pref_label_new(group, "");
2901 markup = g_markup_printf_escaped ("<span weight=\"bold\">%s</span>%s<span style=\"italic\">%s</span>%s", _("Step 3"), _(") Save metadata in Geeqie private directory "), get_metadata_cache_dir(), "/");
2902 gtk_label_set_markup (GTK_LABEL(label), markup);
2905 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2906 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2907 gtk_misc_set_padding(GTK_MISC(label), 22, 0);
2908 pref_spacer(group, PREF_PAD_GROUP);
2910 group = pref_group_new(vbox, FALSE, _("Step 1 Options:"), GTK_ORIENTATION_VERTICAL);
2912 gtk_widget_set_sensitive(group, FALSE);
2915 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2916 pref_checkbox_link_sensitivity(ct_button, hbox);
2918 tmp_widget= pref_checkbox_new_int(hbox, _("Store metadata also in IPTC tags (converted according to the IPTC4XMP standard)"), options->metadata.save_legacy_IPTC, &c_options->metadata.save_legacy_IPTC);
2919 gtk_widget_set_tooltip_text(tmp_widget, _("A simplified conversion list is in the Help file"));
2921 pref_checkbox_new_int(hbox, _("Warn if the image or sidecar file is not writable"), options->metadata.warn_on_write_problems, &c_options->metadata.warn_on_write_problems);
2923 pref_checkbox_new_int(hbox, _("Ask before writing to image files"), options->metadata.confirm_write, &c_options->metadata.confirm_write);
2925 tmp_widget= pref_checkbox_new_int(hbox, "", options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2926 gtk_widget_set_tooltip_text(tmp_widget, _("This file naming convention is used by Darktable"));
2927 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2928 markup = g_markup_printf_escaped ("%s<span style=\"italic\">%s</span>%s<span style=\"italic\">%s</span>%s", _("Create sidecar files named "), "image.ext.xmp", _(" (as opposed to the normal "), "image.xmp", ")");
2929 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2932 pref_spacer(group, PREF_PAD_GROUP);
2934 group = pref_group_new(vbox, FALSE, _("Steps 2 and 3 Option:"), GTK_ORIENTATION_VERTICAL);
2936 gtk_widget_set_sensitive(group, FALSE);
2939 pref_checkbox_new_int(group, _("Use GQview legacy metadata format instead of XMP (supports only Keywords and Comments)"), options->metadata.save_legacy_format, &c_options->metadata.save_legacy_format);
2941 pref_spacer(group, PREF_PAD_GROUP);
2943 group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2944 tmp_widget = pref_checkbox_new_int(group, _("Write the same description tags to all grouped sidecars"), options->metadata.sync_grouped_files, &c_options->metadata.sync_grouped_files);
2945 gtk_widget_set_tooltip_text(tmp_widget, _("See the Help file for a list of the tags used"));
2947 tmp_widget = pref_checkbox_new_int(group, _("Permit Keywords to be case-sensitive"), options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2948 gtk_widget_set_tooltip_text(tmp_widget, _("When selected, \"Place\" and \"place\" are two different keywords"));
2950 ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"), options->metadata.write_orientation, &c_options->metadata.write_orientation);
2951 gtk_widget_set_tooltip_text(ct_button, _("If checked, the results of orientation commands (Rotate, Mirror and Flip) issued on an image will be written to metadata\nNote: If this option is not checked, the results of orientation commands will be lost when Geeqie closes"));
2954 gtk_widget_set_sensitive(ct_button, FALSE);
2957 pref_spacer(group, PREF_PAD_GROUP);
2959 group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2961 ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"), options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
2963 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2964 pref_checkbox_link_sensitivity(ct_button, hbox);
2966 pref_spin_new_int(hbox, _("Timeout (seconds):"), nullptr, 0, 900, 1, options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
2968 pref_checkbox_new_int(group, _("Write metadata on image change"), options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
2970 pref_checkbox_new_int(group, _("Write metadata on directory change"), options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
2972 pref_spacer(group, PREF_PAD_GROUP);
2975 group = pref_group_new(vbox, FALSE, _("Spelling checks"), GTK_ORIENTATION_VERTICAL);
2977 ct_button = pref_checkbox_new_int(group, _("Check spelling - Requires restart"), options->metadata.check_spelling, &c_options->metadata.check_spelling);
2978 gtk_widget_set_tooltip_text(ct_button, _("Spelling checks are performed on info sidebar panes Comment, Headline and Title"));
2981 pref_spacer(group, PREF_PAD_GROUP);
2983 group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
2985 ct_button = pref_checkbox_new_int(group, _("Read metadata in background"), options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
2986 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");
2991 struct KeywordFindData
2998 GtkWidget *button_close;
2999 GtkWidget *button_stop;
3000 GtkWidget *button_start;
3001 GtkWidget *progress;
3009 guint idle_id; /* event source id */
3012 #define KEYWORD_DIALOG_WIDTH 400
3014 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
3016 GList *list_d = nullptr;
3017 GList *list_f = nullptr;
3021 filelist_read(dir_fd, &list_f, &list_d);
3025 filelist_read(dir_fd, &list_f, nullptr);
3028 list_f = filelist_filter(list_f, FALSE);
3029 list_d = filelist_filter(list_d, TRUE);
3031 kfd->list = g_list_concat(list_f, kfd->list);
3032 kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
3035 static void keywords_find_reset(KeywordFindData *kfd)
3037 filelist_free(kfd->list);
3038 kfd->list = nullptr;
3040 filelist_free(kfd->list_dir);
3041 kfd->list_dir = nullptr;
3044 static void keywords_find_close_cb(GenericDialog *UNUSED(fd), gpointer data)
3046 auto kfd = static_cast<KeywordFindData *>(data);
3048 if (!gtk_widget_get_sensitive(kfd->button_close)) return;
3050 keywords_find_reset(kfd);
3051 generic_dialog_close(kfd->gd);
3055 static void keywords_find_finish(KeywordFindData *kfd)
3057 keywords_find_reset(kfd);
3059 gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
3060 spinner_set_interval(kfd->spinner, -1);
3062 gtk_widget_set_sensitive(kfd->group, TRUE);
3063 gtk_widget_set_sensitive(kfd->button_start, TRUE);
3064 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3065 gtk_widget_set_sensitive(kfd->button_close, TRUE);
3068 static void keywords_find_stop_cb(GenericDialog *UNUSED(fd), gpointer data)
3070 auto kfd = static_cast<KeywordFindData *>(data);
3072 g_idle_remove_by_data(kfd);
3074 keywords_find_finish(kfd);
3077 static gboolean keywords_find_file(gpointer data)
3079 auto kfd = static_cast<KeywordFindData *>(data);
3081 GtkTextBuffer *buffer;
3089 fd = static_cast<FileData *>(kfd->list->data);
3090 kfd->list = g_list_remove(kfd->list, fd);
3092 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
3093 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3097 gtk_text_buffer_get_end_iter(buffer, &iter);
3098 tmp = g_strconcat(static_cast<const gchar *>(keywords->data), "\n", NULL);
3099 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3101 keywords = keywords->next;
3104 gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
3105 file_data_unref(fd);
3106 string_list_free(keywords);
3108 return (G_SOURCE_CONTINUE);
3110 else if (kfd->list_dir)
3114 fd = static_cast<FileData *>(kfd->list_dir->data);
3115 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
3117 keywords_find_folder(kfd, fd);
3119 file_data_unref(fd);
3121 return G_SOURCE_CONTINUE;
3124 keywords_find_finish(kfd);
3126 return G_SOURCE_REMOVE;
3129 static void keywords_find_start_cb(GenericDialog *UNUSED(fd), gpointer data)
3131 auto kfd = static_cast<KeywordFindData *>(data);
3134 if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
3136 path = remove_trailing_slash((gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
3137 parse_out_relatives(path);
3141 warning_dialog(_("Invalid folder"),
3142 _("The specified folder can not be found."),
3143 GTK_STOCK_DIALOG_WARNING, kfd->gd->dialog);
3149 gtk_widget_set_sensitive(kfd->group, FALSE);
3150 gtk_widget_set_sensitive(kfd->button_start, FALSE);
3151 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
3152 gtk_widget_set_sensitive(kfd->button_close, FALSE);
3153 spinner_set_interval(kfd->spinner, SPINNER_SPEED);
3155 dir_fd = file_data_new_dir(path);
3156 keywords_find_folder(kfd, dir_fd);
3157 file_data_unref(dir_fd);
3158 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
3164 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
3166 KeywordFindData *kfd;
3170 kfd = g_new0(KeywordFindData, 1);
3172 kfd->gd = generic_dialog_new(_("Search for keywords"),
3173 "search_for_keywords",
3176 gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
3177 kfd->gd->cancel_cb = keywords_find_close_cb;
3178 kfd->button_close = generic_dialog_add_button(kfd->gd, GTK_STOCK_CLOSE, nullptr,
3179 keywords_find_close_cb, FALSE);
3180 kfd->button_start = generic_dialog_add_button(kfd->gd, GTK_STOCK_OK, _("S_tart"),
3181 keywords_find_start_cb, FALSE);
3182 kfd->button_stop = generic_dialog_add_button(kfd->gd, GTK_STOCK_STOP, nullptr,
3183 keywords_find_stop_cb, FALSE);
3184 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3186 generic_dialog_add_message(kfd->gd, nullptr, _("Search for keywords"), nullptr, FALSE);
3188 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3189 pref_spacer(hbox, PREF_PAD_INDENT);
3190 kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3192 hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3193 pref_label_new(hbox, _("Folder:"));
3195 label = tab_completion_new(&kfd->entry, path, nullptr, nullptr, nullptr, nullptr);
3196 tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
3197 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3198 gtk_widget_show(label);
3200 pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
3202 pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
3203 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3205 kfd->progress = gtk_entry_new();
3206 gtk_widget_set_can_focus(kfd->progress, FALSE);
3207 gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
3208 gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
3209 gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
3210 gtk_widget_show(kfd->progress);
3212 kfd->spinner = spinner_new(nullptr, -1);
3213 gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
3214 gtk_widget_show(kfd->spinner);
3216 kfd->list = nullptr;
3218 gtk_widget_show(kfd->gd->dialog);
3221 static void keywords_find_cb(GtkWidget *widget, gpointer UNUSED(data))
3223 const gchar *path = layout_get_path(nullptr);
3225 if (!path || !*path) path = homedir();
3226 keywords_find_dialog(widget, path);
3229 static void config_tab_keywords_save()
3231 GtkTextIter start, end;
3232 GtkTextBuffer *buffer;
3233 GList *kw_list = nullptr;
3239 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3240 gtk_text_buffer_get_bounds(buffer, &start, &end);
3242 buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3244 kw_split = strtok(buffer_text, "\n");
3245 while (kw_split != nullptr)
3251 if (g_strcmp0(static_cast<const gchar *>(work->data), kw_split) == 0)
3260 kw_list = g_list_append(kw_list, g_strdup(kw_split));
3262 kw_split = strtok(nullptr, "\n");
3265 keyword_list_set(kw_list);
3267 string_list_free(kw_list);
3268 g_free(buffer_text);
3271 static void config_tab_keywords(GtkWidget *notebook)
3277 GtkWidget *scrolled;
3279 GtkTextBuffer *buffer;
3282 GspellTextView *gspell_view;
3285 vbox = scrolled_notebook_page(notebook, _("Keywords"));
3287 group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
3289 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3291 button = pref_button_new(hbox, GTK_STOCK_EXECUTE, _("Search"), FALSE,
3292 G_CALLBACK(keywords_find_cb), keyword_text);
3293 gtk_widget_set_tooltip_text(button, "Search for existing keywords");
3296 keyword_text = gtk_text_view_new();
3297 gtk_widget_set_size_request(keyword_text, 20, 20);
3298 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
3299 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3300 gtk_widget_show(scrolled);
3303 if (options->metadata.check_spelling)
3305 gspell_view = gspell_text_view_get_from_gtk_text_view(GTK_TEXT_VIEW(keyword_text));
3306 gspell_text_view_basic_setup(gspell_view);
3310 gtk_container_add(GTK_CONTAINER(scrolled), keyword_text);
3311 gtk_widget_show(keyword_text);
3313 gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
3315 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3316 gtk_text_buffer_create_tag(buffer, "monospace",
3317 "family", "monospace", NULL);
3319 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3320 gtk_text_buffer_get_start_iter(buffer, &iter);
3321 gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3324 path = g_build_filename(get_rc_dir(), "keywords", NULL);
3326 GList *kwl = keyword_list_get();
3327 kwl = g_list_first(kwl);
3330 gtk_text_buffer_get_end_iter (buffer, &iter);
3331 tmp = g_strconcat(static_cast<const gchar *>(kwl->data), "\n", NULL);
3332 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3337 gtk_text_buffer_set_modified(buffer, FALSE);
3344 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3346 auto option = static_cast<gint *>(data);
3348 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3352 *option = INTENT_PERCEPTUAL;
3355 *option = INTENT_RELATIVE_COLORIMETRIC;
3358 *option = INTENT_SATURATION;
3361 *option = INTENT_ABSOLUTE_COLORIMETRIC;
3366 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3367 gint option, gint *option_c)
3374 pref_table_label(table, column, row, text, 0.0);
3376 combo = gtk_combo_box_text_new();
3378 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3379 if (option == INTENT_PERCEPTUAL) current = 0;
3380 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3381 if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3382 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3383 if (option == INTENT_SATURATION) current = 2;
3384 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3385 if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3387 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3389 gtk_widget_set_tooltip_text(combo,"Refer to the lcms documentation for the defaults used when the selected Intent is not available");
3391 g_signal_connect(G_OBJECT(combo), "changed",
3392 G_CALLBACK(intent_menu_cb), option_c);
3394 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
3395 gtk_widget_show(combo);
3399 static void config_tab_color(GtkWidget *notebook)
3408 vbox = scrolled_notebook_page(notebook, _("Color management"));
3410 group = pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3412 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3415 table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3416 gtk_table_set_col_spacings(GTK_TABLE(table), PREF_PAD_GAP);
3418 label = pref_table_label(table, 0, 0, _("Type"), 0.0);
3419 pref_label_bold(label, TRUE, FALSE);
3421 label = pref_table_label(table, 1, 0, _("Menu name"), 0.0);
3422 pref_label_bold(label, TRUE, FALSE);
3424 label = pref_table_label(table, 2, 0, _("File"), 0.0);
3425 pref_label_bold(label, TRUE, FALSE);
3427 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3432 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3433 pref_table_label(table, 0, i + 1, buf, 1.0);
3436 entry = gtk_entry_new();
3437 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3438 if (options->color_profile.input_name[i])
3440 gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3442 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, i + 1, i + 2,
3443 static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3444 gtk_widget_show(entry);
3445 color_profile_input_name_entry[i] = entry;
3447 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], nullptr, ".icc", "ICC Files", nullptr);
3448 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3449 gtk_widget_set_size_request(entry, 160, -1);
3450 gtk_table_attach(GTK_TABLE(table), tabcomp, 2, 3, i + 1, i + 2,
3451 static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3452 gtk_widget_show(tabcomp);
3453 color_profile_input_file_entry[i] = entry;
3456 group = pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3458 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3460 pref_checkbox_new_int(group, _("Use system screen profile if available"),
3461 options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3463 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3465 pref_table_label(table, 0, 0, _("Screen:"), 1.0);
3466 tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3467 options->color_profile.screen_file, nullptr, ".icc", "ICC Files", nullptr);
3468 tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3469 gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3471 add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3473 gtk_table_attach(GTK_TABLE(table), tabcomp, 1, 2,
3475 static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3477 gtk_widget_show(tabcomp);
3480 /* advanced entry tab */
3481 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer UNUSED(data))
3483 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3485 c_options->file_ops.use_system_trash = FALSE;
3486 c_options->file_ops.no_trash = FALSE;
3490 static void use_system_trash_cb(GtkWidget *widget, gpointer UNUSED(data))
3492 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3494 c_options->file_ops.use_system_trash = TRUE;
3495 c_options->file_ops.no_trash = FALSE;
3499 static void use_no_cache_cb(GtkWidget *widget, gpointer UNUSED(data))
3501 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3503 c_options->file_ops.no_trash = TRUE;
3507 static void config_tab_behavior(GtkWidget *notebook)
3514 GtkWidget *ct_button;
3518 GtkWidget *with_rename;
3519 GtkWidget *collections_on_top;
3520 GtkWidget *hide_window_in_fullscreen;
3521 GtkWidget *checkbox;
3524 vbox = scrolled_notebook_page(notebook, _("Behavior"));
3526 group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3528 pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3529 options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3530 pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3531 options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3532 pref_checkbox_new_int(group, _("Enable Delete key"),
3533 options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3535 ct_button = pref_radiobutton_new(group, nullptr, _("Use Geeqie trash location"),
3536 !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),nullptr);
3538 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3539 pref_checkbox_link_sensitivity(ct_button, hbox);
3541 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3542 pref_label_new(hbox, _("Folder:"));
3544 tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, nullptr, nullptr, nullptr, nullptr);
3545 tab_completion_add_select_button(safe_delete_path_entry, nullptr, TRUE);
3546 gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3547 gtk_widget_show(tabcomp);
3549 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3550 pref_checkbox_link_sensitivity(ct_button, hbox);
3552 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3553 spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MiB"),
3554 0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3555 gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3556 button = pref_button_new(nullptr, nullptr, _("View"), FALSE,
3557 G_CALLBACK(safe_delete_view_cb), nullptr);
3558 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3559 gtk_widget_show(button);
3561 button = pref_button_new(nullptr, GTK_STOCK_CLEAR, nullptr, FALSE,
3562 G_CALLBACK(safe_delete_clear_cb), nullptr);
3563 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3565 c_options->file_ops.no_trash = options->file_ops.no_trash;
3566 c_options->file_ops.use_system_trash = options->file_ops.use_system_trash;
3568 pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3569 options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), nullptr);
3571 pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3572 options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), nullptr);
3574 gtk_widget_show(button);
3576 pref_spacer(group, PREF_PAD_GROUP);
3579 group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3581 pref_checkbox_new_int(group, _("Descend folders in tree view"),
3582 options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3584 pref_checkbox_new_int(group, _("In place renaming"),
3585 options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3587 pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3588 options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3590 tmp = pref_checkbox_new_int(group, _("Circular selection lists"),
3591 options->circular_selection_lists, &c_options->circular_selection_lists);
3592 gtk_widget_set_tooltip_text(tmp, _("Traverse selection lists in a circular manner"));
3594 marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3595 options->marks_save, &c_options->marks_save);
3596 gtk_widget_set_tooltip_text(marks,"Note that marks linked to a keyword will be saved irrespective of this setting");
3598 with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3599 options->with_rename, &c_options->with_rename);
3600 gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3602 collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3603 options->collections_on_top, &c_options->collections_on_top);
3604 gtk_widget_set_tooltip_text(collections_on_top,"Open collections window on top");
3606 hide_window_in_fullscreen = pref_checkbox_new_int(group, _("Hide window in fullscreen"),
3607 options->hide_window_in_fullscreen, &c_options->hide_window_in_fullscreen);
3608 gtk_widget_set_tooltip_text(hide_window_in_fullscreen,"When alt-tabbing, prevent Geeqie window showing twice");
3610 pref_spin_new_int(group, _("Recent folder list maximum size"), nullptr,
3611 1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3613 tmp = pref_spin_new_int(group, _("Recent folder-image list maximum size"), nullptr, 0, 50, 1, options->recent_folder_image_list_maxsize, &c_options->recent_folder_image_list_maxsize);
3614 gtk_widget_set_tooltip_text(tmp, _("List of the last image viewed in each recent folder.\nRe-opening a folder will set focus to the last image viewed."));
3616 pref_spin_new_int(group, _("Drag'n drop icon size"), nullptr,
3617 16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3619 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3620 add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
3622 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3623 add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3625 pref_spacer(group, PREF_PAD_GROUP);
3627 group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3629 pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3630 options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3631 pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), nullptr,
3632 1, 32, 1, options->keyboard_scroll_step, reinterpret_cast<int *>(&c_options->keyboard_scroll_step));
3633 pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3634 options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3635 pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3636 options->image_lm_click_nav, &c_options->image_lm_click_nav);
3637 pref_checkbox_new_int(group, _("Open archive by left click on image"),
3638 options->image_l_click_archive, &c_options->image_l_click_archive);
3639 pref_checkbox_new_int(group, _("Play video by left click on image"),
3640 options->image_l_click_video, &c_options->image_l_click_video);
3641 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3642 add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3644 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3645 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3646 add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3647 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3648 add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3650 pref_spacer(group, PREF_PAD_GROUP);
3652 group = pref_group_new(vbox, FALSE, _("GPU"), GTK_ORIENTATION_VERTICAL);
3654 checkbox = pref_checkbox_new_int(group, _("Override disable GPU"),
3655 options->override_disable_gpu, &c_options->override_disable_gpu);
3656 gtk_widget_set_tooltip_text(checkbox, "Contact the developers for usage");
3659 pref_spacer(group, PREF_PAD_GROUP);
3661 group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3663 pref_spin_new_int(group, _("Debug level:"), nullptr,
3664 DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3666 pref_checkbox_new_int(group, _("Timer data"),
3667 options->log_window.timer_data, &c_options->log_window.timer_data);
3669 pref_spin_new_int(group, _("Log Window max. lines:"), nullptr,
3670 1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3674 /* accelerators tab */
3675 static void config_tab_accelerators(GtkWidget *notebook)
3681 GtkWidget *scrolled;
3682 GtkWidget *accel_view;
3683 GtkCellRenderer *renderer;
3684 GtkTreeSelection *selection;
3685 GtkTreeViewColumn *column;
3687 vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3689 group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3691 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
3692 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3693 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3694 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3695 gtk_widget_show(scrolled);
3697 accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3699 accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3700 g_object_unref(accel_store);
3701 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3702 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3704 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3706 renderer = gtk_cell_renderer_text_new();
3708 column = gtk_tree_view_column_new_with_attributes(_("Action"),
3713 gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3714 gtk_tree_view_column_set_resizable(column, TRUE);
3715 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3718 renderer = gtk_cell_renderer_accel_new();
3719 g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3720 G_CALLBACK(accel_store_cleared_cb), accel_store);
3721 g_signal_connect(G_OBJECT(renderer), "accel-edited",
3722 G_CALLBACK(accel_store_edited_cb), accel_store);
3725 g_object_set (renderer,
3727 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3730 column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3735 gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3736 gtk_tree_view_column_set_resizable(column, TRUE);
3737 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3739 renderer = gtk_cell_renderer_text_new();
3741 column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3746 gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3747 gtk_tree_view_column_set_resizable(column, TRUE);
3748 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3750 renderer = gtk_cell_renderer_text_new();
3752 column = gtk_tree_view_column_new_with_attributes("Accel",
3757 gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3758 gtk_tree_view_column_set_resizable(column, TRUE);
3759 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3761 accel_store_populate();
3762 gtk_container_add(GTK_CONTAINER(scrolled), accel_view);
3763 gtk_widget_show(accel_view);
3765 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3767 button = pref_button_new(nullptr, nullptr, _("Defaults"), FALSE,
3768 G_CALLBACK(accel_default_cb), accel_view);
3769 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3770 gtk_widget_show(button);
3772 button = pref_button_new(nullptr, nullptr, _("Reset selected"), FALSE,
3773 G_CALLBACK(accel_reset_cb), accel_view);
3774 gtk_widget_set_tooltip_text(button, _("Will only reset changes made before the settings are saved"));
3775 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3776 gtk_widget_show(button);
3778 button = pref_button_new(nullptr, nullptr, _("Clear selected"), FALSE,
3779 G_CALLBACK(accel_clear_cb), accel_view);
3780 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3781 gtk_widget_show(button);
3784 /* toolbar main tab */
3785 static void config_tab_toolbar_main(GtkWidget *notebook)
3788 GtkWidget *toolbardata;
3791 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3793 vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3795 toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3796 gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3797 gtk_widget_show(vbox);
3800 /* toolbar status tab */
3801 static void config_tab_toolbar_status(GtkWidget *notebook)
3804 GtkWidget *toolbardata;
3807 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3809 vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3811 toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3812 gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3813 gtk_widget_show(vbox);
3817 static gint extension_sort_cb(gconstpointer a, gconstpointer b)
3819 return g_strcmp0(static_cast<const gchar *>(a), static_cast<const gchar *>(b));
3822 static void config_tab_advanced(GtkWidget *notebook)
3826 GSList *formats_list;
3827 GList *extensions_list = nullptr;
3830 GdkPixbufFormat *fm;
3832 GString *types_string = g_string_new(nullptr);
3833 GtkWidget *types_string_label;
3834 GtkWidget *threads_string_label;
3835 GtkWidget *dupes_threads_spin;
3837 vbox = scrolled_notebook_page(notebook, _("Advanced"));
3838 group = pref_group_new(vbox, FALSE, _("External preview extraction"), GTK_ORIENTATION_VERTICAL);
3840 pref_checkbox_new_int(group, _("Use external preview extraction - Requires restart"), options->external_preview.enable, &c_options->external_preview.enable);
3842 pref_spacer(group, PREF_PAD_GROUP);
3844 formats_list = gdk_pixbuf_get_formats();
3846 while (formats_list)
3848 fm = static_cast<GdkPixbufFormat *>(formats_list->data);
3849 extensions = gdk_pixbuf_format_get_extensions(fm);
3852 while (extensions[i])
3854 extensions_list = g_list_insert_sorted(extensions_list, g_strdup(extensions[i]), extension_sort_cb);
3858 g_strfreev(extensions);
3859 formats_list = formats_list->next;
3862 while (extensions_list)
3864 if (types_string->len == 0)
3866 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3870 types_string = g_string_append(types_string, ", ");
3871 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3874 extensions_list = extensions_list->next;
3877 types_string = g_string_prepend(types_string, _("Usable file types:\n"));
3878 types_string_label = pref_label_new(group, types_string->str);
3879 gtk_label_set_line_wrap(GTK_LABEL(types_string_label), TRUE);
3881 pref_spacer(group, PREF_PAD_GROUP);
3883 group = pref_group_new(vbox, FALSE, _("File identification tool"), GTK_ORIENTATION_VERTICAL);
3884 external_preview_select_entry = gtk_entry_new();
3885 tabcomp = tab_completion_new(&external_preview_select_entry, options->external_preview.select, nullptr, nullptr, nullptr, nullptr);
3886 tab_completion_add_select_button(external_preview_select_entry, _("Select file identification tool"), FALSE);
3887 gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3888 gtk_widget_show(tabcomp);
3890 group = pref_group_new(vbox, FALSE, _("Preview extraction tool"), GTK_ORIENTATION_VERTICAL);
3891 external_preview_extract_entry = gtk_entry_new();
3892 tabcomp = tab_completion_new(&external_preview_extract_entry, options->external_preview.extract, nullptr, nullptr, nullptr, nullptr);
3893 tab_completion_add_select_button(external_preview_extract_entry, _("Select preview extraction tool"), FALSE);
3894 gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3895 gtk_widget_show(tabcomp);
3897 gtk_widget_show(vbox);
3899 g_slist_free(formats_list);
3900 string_list_free(extensions_list);
3901 g_string_free(types_string, TRUE);
3903 pref_spacer(group, PREF_PAD_GROUP);
3905 pref_line(vbox, PREF_PAD_SPACE);
3906 group = pref_group_new(vbox, FALSE, _("Thread pool limits"), GTK_ORIENTATION_VERTICAL);
3908 threads_string_label = pref_label_new(group, "This option limits the number of threads (or cpu cores)\nthat Geeqie will use when running duplicate checks. The default value is 0, which means all available cores will be used.");
3909 gtk_label_set_line_wrap(GTK_LABEL(threads_string_label), TRUE);
3911 pref_spacer(vbox, PREF_PAD_GROUP);
3913 dupes_threads_spin = pref_spin_new_int(vbox, _("Duplicate check:"), _("max. threads"), 0, get_cpu_cores(), 1, options->threads.duplicates, &c_options->threads.duplicates);
3914 gtk_widget_set_tooltip_markup(dupes_threads_spin, _("Set to 0 for unlimited"));
3918 static void config_tab_stereo(GtkWidget *notebook)
3926 GtkWidget *fs_button;
3927 vbox = scrolled_notebook_page(notebook, _("Stereo"));
3929 group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
3931 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3932 add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
3934 table = pref_table_new(group, 2, 2, TRUE, FALSE);
3935 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3936 pref_checkbox_new_int(box, _("Mirror left image"),
3937 options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
3938 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3939 pref_checkbox_new_int(box, _("Flip left image"),
3940 options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
3941 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3942 pref_checkbox_new_int(box, _("Mirror right image"),
3943 options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
3944 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3945 pref_checkbox_new_int(box, _("Flip right image"),
3946 options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
3947 pref_checkbox_new_int(group, _("Swap left and right images"),
3948 options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
3949 pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
3950 options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
3952 group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
3953 fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
3954 options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
3955 box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
3956 pref_checkbox_link_sensitivity(fs_button, box2);
3957 table = pref_table_new(box2, 2, 1, FALSE, FALSE);
3958 add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
3959 table = pref_table_new(box2, 2, 2, TRUE, FALSE);
3960 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3961 pref_checkbox_new_int(box, _("Mirror left image"),
3962 options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
3963 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3964 pref_checkbox_new_int(box, _("Flip left image"),
3965 options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
3966 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3967 pref_checkbox_new_int(box, _("Mirror right image"),
3968 options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
3969 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3970 pref_checkbox_new_int(box, _("Flip right image"),
3971 options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
3972 pref_checkbox_new_int(box2, _("Swap left and right images"),
3973 options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
3974 pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
3975 options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
3977 group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
3978 table = pref_table_new(group2, 5, 3, FALSE, FALSE);
3979 pref_table_spin_new_int(table, 0, 0, _("Width"), nullptr,
3980 1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
3981 pref_table_spin_new_int(table, 3, 0, _("Height"), nullptr,
3982 1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
3983 pref_table_spin_new_int(table, 0, 1, _("Left X"), nullptr,
3984 0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
3985 pref_table_spin_new_int(table, 3, 1, _("Left Y"), nullptr,
3986 0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
3987 pref_table_spin_new_int(table, 0, 2, _("Right X"), nullptr,
3988 0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
3989 pref_table_spin_new_int(table, 3, 2, _("Right Y"), nullptr,
3990 0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
3994 /* Main preferences window */
3995 static void config_window_create(LayoutWindow *lw)
3997 GtkWidget *win_vbox;
3999 GtkWidget *notebook;
4001 GtkWidget *ct_button;
4003 if (!c_options) c_options = init_options(nullptr);
4005 configwindow = window_new(GTK_WINDOW_TOPLEVEL, "preferences", PIXBUF_INLINE_ICON_CONFIG, nullptr, _("Preferences"));
4006 DEBUG_NAME(configwindow);
4007 gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
4008 g_signal_connect(G_OBJECT(configwindow), "delete_event",
4009 G_CALLBACK(config_window_delete), NULL);
4010 if (options->save_dialog_window_positions)
4012 gtk_window_resize(GTK_WINDOW(configwindow), lw->options.preferences_window.w, lw->options.preferences_window.h);
4013 gtk_window_move(GTK_WINDOW(configwindow), lw->options.preferences_window.x, lw->options.preferences_window.y);
4017 gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
4019 gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
4020 gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
4022 win_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4023 gtk_container_add(GTK_CONTAINER(configwindow), win_vbox);
4024 gtk_widget_show(win_vbox);
4026 notebook = gtk_notebook_new();
4027 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
4028 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
4029 gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
4031 config_tab_general(notebook);
4032 config_tab_image(notebook);
4033 config_tab_osd(notebook);
4034 config_tab_windows(notebook);
4035 config_tab_accelerators(notebook);
4036 config_tab_files(notebook);
4037 config_tab_metadata(notebook);
4038 config_tab_keywords(notebook);
4039 config_tab_color(notebook);
4040 config_tab_stereo(notebook);
4041 config_tab_behavior(notebook);
4042 config_tab_toolbar_main(notebook);
4043 config_tab_toolbar_status(notebook);
4044 config_tab_advanced(notebook);
4046 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), lw->options.preferences_window.page_number);
4048 hbox = gtk_hbutton_box_new();
4049 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
4050 gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
4051 gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
4052 gtk_widget_show(hbox);
4054 button = pref_button_new(nullptr, GTK_STOCK_HELP, nullptr, FALSE,
4055 G_CALLBACK(config_window_help_cb), notebook);
4056 gtk_container_add(GTK_CONTAINER(hbox), button);
4057 gtk_widget_set_can_default(button, TRUE);
4058 gtk_widget_show(button);
4060 button = pref_button_new(nullptr, GTK_STOCK_OK, nullptr, FALSE,
4061 G_CALLBACK(config_window_ok_cb), notebook);
4062 gtk_container_add(GTK_CONTAINER(hbox), button);
4063 gtk_widget_set_can_default(button, TRUE);
4064 gtk_widget_grab_default(button);
4065 gtk_widget_show(button);
4069 button = pref_button_new(nullptr, GTK_STOCK_CANCEL, nullptr, FALSE,
4070 G_CALLBACK(config_window_close_cb), nullptr);
4071 gtk_container_add(GTK_CONTAINER(hbox), button);
4072 gtk_widget_set_can_default(button, TRUE);
4073 gtk_widget_show(button);
4075 if (!generic_dialog_get_alternative_button_order(configwindow))
4077 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
4080 gtk_widget_show(notebook);
4082 gtk_widget_show(configwindow);
4086 *-----------------------------------------------------------------------------
4087 * config window show (public)
4088 *-----------------------------------------------------------------------------
4091 void show_config_window(LayoutWindow *lw)
4095 gtk_window_present(GTK_WINDOW(configwindow));
4099 config_window_create(lw);
4108 void show_about_window(LayoutWindow *lw)
4110 GDataInputStream *data_stream;
4111 GInputStream *in_stream_authors;
4112 GInputStream *in_stream_translators;
4114 GdkPixbuf *pixbuf_icon;
4115 GdkPixbuf *pixbuf_logo;
4119 gchar *authors[1000];
4120 gchar *authors_path;
4122 gchar *timezone_path;
4124 gchar *translators_path;
4132 copyright = g_string_new(nullptr);
4133 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");
4135 timezone_path = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
4136 if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
4138 cd = ZDOpenDatabase(timezone_path);
4141 copyright = g_string_append(copyright, ZDGetNotice(cd));
4145 log_printf("Error: Init of timezone database %s failed\n", timezone_path);
4147 ZDCloseDatabase(cd);
4149 g_free(timezone_path);
4151 authors_path = g_build_filename(GQ_RESOURCE_PATH_CREDITS, "authors", NULL);
4153 in_stream_authors = g_resources_open_stream(authors_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4155 data_stream = g_data_input_stream_new(in_stream_authors);
4157 authors[0] = nullptr;
4158 while ((author_line = g_data_input_stream_read_line(G_DATA_INPUT_STREAM(data_stream), &length, nullptr, nullptr)))
4160 authors[i_authors] = g_strdup(author_line);
4162 g_free(author_line);
4164 authors[i_authors] = nullptr;
4166 g_input_stream_close(in_stream_authors, nullptr, nullptr);
4168 translators_path = g_build_filename(GQ_RESOURCE_PATH_CREDITS, "translators", NULL);
4170 g_resources_get_info(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &size, &flags, nullptr);
4172 in_stream_translators = g_resources_open_stream(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4173 translators = static_cast<gchar *>(g_malloc0(size));
4174 g_input_stream_read_all(in_stream_translators, translators, size, &bytes_read, nullptr, nullptr);
4175 g_input_stream_close(in_stream_translators, nullptr, nullptr);
4177 comment = g_strconcat("Project created by John Ellis\nGQview 1998\nGeeqie 2007\n\n\nDevelopment and bug reports:\n", GQ_EMAIL_ADDRESS, "\nhttps://github.com/BestImageViewer/geeqie/issues",NULL);
4179 artists[0] = g_strdup("Néstor Díaz Valencia <nestor@estudionexos.com>");
4180 artists[1] = nullptr;
4182 pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
4183 pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
4184 gtk_show_about_dialog(GTK_WINDOW(lw->window),
4185 "title", _("About Geeqie"),
4187 "program-name", GQ_APPNAME,
4189 "logo", pixbuf_logo,
4190 "icon", pixbuf_icon,
4191 "website", GQ_WEBSITE,
4192 "website-label", "Website",
4193 "comments", comment,
4196 "translator-credits", translators,
4197 "wrap-license", TRUE,
4198 "license", copyright->str,
4201 g_string_free(copyright, TRUE);
4203 while(n < i_authors)
4210 g_free(authors_path);
4212 g_free(translators);
4213 g_free(translators_path);
4214 g_object_unref(data_stream);
4215 g_object_unref(in_stream_authors);
4216 g_object_unref(in_stream_translators);
4219 static void image_overlay_set_text_colours()
4221 c_options->image_overlay.text_red = options->image_overlay.text_red;
4222 c_options->image_overlay.text_green = options->image_overlay.text_green;
4223 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
4224 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
4225 c_options->image_overlay.background_red = options->image_overlay.background_red;
4226 c_options->image_overlay.background_green = options->image_overlay.background_green;
4227 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
4228 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
4232 *-----------------------------------------------------------------------------
4233 * timezone database routines
4234 *-----------------------------------------------------------------------------
4237 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
4239 GError *error = nullptr;
4240 auto tz = static_cast<TZData *>(data);
4241 gchar *tmp_filename;
4242 gchar *timezone_bin;
4243 gchar *tmp_dir = nullptr;
4246 if (!g_cancellable_is_cancelled(tz->cancellable))
4248 generic_dialog_close(tz->gd);
4252 if (g_file_copy_finish(G_FILE(source_object), res, &error))
4254 tmp_filename = g_file_get_path(tz->tmp_g_file);
4255 fd = file_data_new_simple(tmp_filename);
4256 tmp_dir = open_archive(fd);
4260 timezone_bin = g_build_filename(tmp_dir, TIMEZONE_DATABASE_VERSION, TIMEZONE_DATABASE_FILE, NULL);
4261 if (isfile(timezone_bin))
4263 move_file(timezone_bin, tz->timezone_database_user);
4267 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GTK_STOCK_DIALOG_WARNING, nullptr);
4270 g_free(timezone_bin);
4271 g_free(tmp_dir); // The folder in /tmp is deleted in exit_program_final()
4275 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GTK_STOCK_DIALOG_WARNING, nullptr);
4277 g_free(tmp_filename);
4278 file_data_unref(fd);
4282 file_util_warning_dialog(_("Error: Timezone database download failed"), error->message, GTK_STOCK_DIALOG_ERROR, nullptr);
4285 g_file_delete(tz->tmp_g_file, nullptr, &error);
4286 g_object_unref(tz->tmp_g_file);
4287 tz->tmp_g_file = nullptr;
4288 g_object_unref(tz->cancellable);
4289 g_object_unref(tz->timezone_database_gq);
4292 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
4294 auto tz = static_cast<TZData *>(data);
4296 if (!g_cancellable_is_cancelled(tz->cancellable))
4298 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), static_cast<gdouble>(current_num_bytes) / total_num_bytes);
4302 static void timezone_cancel_button_cb(GenericDialog *UNUSED(gd), gpointer data)
4304 auto tz = static_cast<TZData *>(data);
4306 g_cancellable_cancel(tz->cancellable);
4309 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
4311 auto tz = static_cast<TZData *>(data);
4312 GError *error = nullptr;
4313 GFileIOStream *io_stream;
4320 tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
4324 file_util_warning_dialog(_("Timezone database download failed"), error->message, GTK_STOCK_DIALOG_ERROR, nullptr);
4325 log_printf("Error: Download timezone database failed:\n%s", error->message);
4326 g_error_free(error);
4327 g_object_unref(tz->tmp_g_file);
4331 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE_WEB);
4333 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", nullptr, TRUE, timezone_cancel_button_cb, tz);
4335 generic_dialog_add_message(tz->gd, GTK_STOCK_DIALOG_INFO, _("Downloading timezone database"), nullptr, FALSE);
4337 tz->progress = gtk_progress_bar_new();
4338 gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
4339 gtk_widget_show(tz->progress);
4341 gtk_widget_show(tz->gd->dialog);
4342 tz->cancellable = g_cancellable_new();
4343 g_file_copy_async(tz->timezone_database_gq, tz->tmp_g_file, G_FILE_COPY_OVERWRITE, G_PRIORITY_LOW, tz->cancellable, timezone_progress_cb, tz, timezone_async_ready_cb, tz);
4345 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
4348 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */