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.
22 #include "preferences.h"
29 #include <gdk-pixbuf/gdk-pixbuf.h>
32 #include <glib-object.h>
35 #include <gspell/gspell.h>
46 #include <pango/pango.h>
49 #include "bar-keywords.h"
51 #include "color-man.h"
56 #include "filefilter.h"
57 #include "fullscreen.h"
58 #include "image-overlay.h"
62 #include "layout-util.h"
64 #include "main-defines.h"
70 #include "pixbuf-util.h"
72 #include "slideshow.h"
73 #include "third-party/zonedetect.h"
77 #include "ui-fileops.h"
79 #include "ui-tabcomp.h"
80 #include "ui-utildlg.h"
87 constexpr gint PRE_FORMATTED_COLUMNS = 5;
88 constexpr gint KEYWORD_DIALOG_WIDTH = 400;
95 EDITOR_NAME_MAX_LENGTH = 32,
96 EDITOR_COMMAND_MAX_LENGTH = 1024
99 static void image_overlay_set_text_colors();
101 GtkWidget *keyword_text;
102 static void config_tab_keywords_save();
110 static ThumbSize thumb_size_list[] =
145 FILETYPES_COLUMN_ENABLED = 0,
146 FILETYPES_COLUMN_FILTER,
147 FILETYPES_COLUMN_DESCRIPTION,
148 FILETYPES_COLUMN_CLASS,
149 FILETYPES_COLUMN_WRITABLE,
150 FILETYPES_COLUMN_SIDECAR,
151 FILETYPES_COLUMN_COUNT
154 const gchar *format_class_list[] = {
165 /* config memory values */
166 static ConfOptions *c_options = nullptr;
173 static GtkWidget *configwindow = nullptr;
174 static GtkListStore *filter_store = nullptr;
175 static GtkTreeStore *accel_store = nullptr;
177 static GtkWidget *safe_delete_path_entry;
179 static GtkWidget *color_profile_input_file_entry[COLOR_PROFILE_INPUTS];
180 static GtkWidget *color_profile_input_name_entry[COLOR_PROFILE_INPUTS];
181 static GtkWidget *color_profile_screen_file_entry;
182 static GtkWidget *external_preview_select_entry;
183 static GtkWidget *external_preview_extract_entry;
185 static GtkWidget *sidecar_ext_entry;
186 static GtkWidget *help_search_engine_entry;
188 static GtkWidget *log_window_f1_entry;
192 CONFIG_WINDOW_DEF_WIDTH = 700,
193 CONFIG_WINDOW_DEF_HEIGHT = 600
197 *-----------------------------------------------------------------------------
198 * option widget callbacks (private)
199 *-----------------------------------------------------------------------------
202 static void zoom_increment_cb(GtkWidget *spin, gpointer)
204 c_options->image.zoom_increment = static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) * 100.0 + 0.01);
207 static void slideshow_delay_hours_cb(GtkWidget *spin, gpointer)
209 gint mins_secs_tenths;
212 mins_secs_tenths = c_options->slideshow.delay %
213 (3600 * SLIDESHOW_SUBSECOND_PRECISION);
215 delay = (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
216 (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
219 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
220 SLIDESHOW_SUBSECOND_PRECISION;
223 static void slideshow_delay_minutes_cb(GtkWidget *spin, gpointer)
229 hours = c_options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
230 secs_tenths = c_options->slideshow.delay % (60 * SLIDESHOW_SUBSECOND_PRECISION);
232 delay = hours * (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
233 (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
234 (60 * SLIDESHOW_SUBSECOND_PRECISION) + secs_tenths);
236 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
237 SLIDESHOW_SUBSECOND_PRECISION;
240 static void slideshow_delay_seconds_cb(GtkWidget *spin, gpointer)
245 hours_mins = c_options->slideshow.delay / (60 * SLIDESHOW_SUBSECOND_PRECISION);
247 delay = (hours_mins * (60 * SLIDESHOW_SUBSECOND_PRECISION)) +
248 static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
249 static_cast<gdouble>(SLIDESHOW_SUBSECOND_PRECISION) + 0.01);
251 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
252 SLIDESHOW_SUBSECOND_PRECISION;
256 *-----------------------------------------------------------------------------
257 * sync program to config window routine (private)
258 *-----------------------------------------------------------------------------
261 void config_entry_to_option(GtkWidget *entry, gchar **option, gchar *(*func)(const gchar *))
267 buf = gq_gtk_entry_get_text(GTK_ENTRY(entry));
268 if (buf && strlen(buf) > 0)
273 *option = g_strdup(buf);
278 static gboolean accel_apply_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
283 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, AE_KEY, &accel, -1);
285 if (accel_path && accel_path[0])
288 gtk_accelerator_parse(accel, &key.accel_key, &key.accel_mods);
289 gtk_accel_map_change_entry(accel_path, key.accel_key, key.accel_mods, TRUE);
299 static void config_window_apply()
301 gboolean refresh = FALSE;
306 config_entry_to_option(safe_delete_path_entry, &options->file_ops.safe_delete_path, remove_trailing_slash);
308 if (options->file_filter.show_hidden_files != c_options->file_filter.show_hidden_files) refresh = TRUE;
309 if (options->file_filter.show_parent_directory != c_options->file_filter.show_parent_directory) refresh = TRUE;
310 if (options->file_filter.show_dot_directory != c_options->file_filter.show_dot_directory) refresh = TRUE;
311 if (options->file_sort.case_sensitive != c_options->file_sort.case_sensitive) refresh = TRUE;
312 if (options->file_filter.disable_file_extension_checks != c_options->file_filter.disable_file_extension_checks) refresh = TRUE;
313 if (options->file_filter.disable != c_options->file_filter.disable) refresh = TRUE;
315 options->file_ops.confirm_delete = c_options->file_ops.confirm_delete;
316 options->file_ops.enable_delete_key = c_options->file_ops.enable_delete_key;
317 options->file_ops.confirm_move_to_trash = c_options->file_ops.confirm_move_to_trash;
318 options->file_ops.use_system_trash = c_options->file_ops.use_system_trash;
319 options->file_ops.no_trash = c_options->file_ops.no_trash;
320 options->file_ops.safe_delete_folder_maxsize = c_options->file_ops.safe_delete_folder_maxsize;
321 options->tools_restore_state = c_options->tools_restore_state;
322 options->save_window_positions = c_options->save_window_positions;
323 options->use_saved_window_positions_for_new_windows = c_options->use_saved_window_positions_for_new_windows;
324 options->save_window_workspace = c_options->save_window_workspace;
325 options->save_dialog_window_positions = c_options->save_dialog_window_positions;
326 options->show_window_ids = c_options->show_window_ids;
327 options->image.scroll_reset_method = c_options->image.scroll_reset_method;
328 options->image.zoom_2pass = c_options->image.zoom_2pass;
329 options->image.fit_window_to_image = c_options->image.fit_window_to_image;
330 options->image.limit_window_size = c_options->image.limit_window_size;
331 options->image.zoom_to_fit_allow_expand = c_options->image.zoom_to_fit_allow_expand;
332 options->image.max_window_size = c_options->image.max_window_size;
333 options->image.limit_autofit_size = c_options->image.limit_autofit_size;
334 options->image.max_autofit_size = c_options->image.max_autofit_size;
335 options->image.max_enlargement_size = c_options->image.max_enlargement_size;
336 options->image.tile_size = c_options->image.tile_size;
337 options->progressive_key_scrolling = c_options->progressive_key_scrolling;
338 options->keyboard_scroll_step = c_options->keyboard_scroll_step;
340 if (options->thumbnails.max_width != c_options->thumbnails.max_width
341 || options->thumbnails.max_height != c_options->thumbnails.max_height
342 || options->thumbnails.quality != c_options->thumbnails.quality)
344 thumb_format_changed = TRUE;
346 options->thumbnails.max_width = c_options->thumbnails.max_width;
347 options->thumbnails.max_height = c_options->thumbnails.max_height;
348 options->thumbnails.quality = c_options->thumbnails.quality;
350 options->thumbnails.enable_caching = c_options->thumbnails.enable_caching;
351 options->thumbnails.cache_into_dirs = c_options->thumbnails.cache_into_dirs;
352 options->thumbnails.use_exif = c_options->thumbnails.use_exif;
353 options->thumbnails.use_color_management = c_options->thumbnails.use_color_management;
354 options->thumbnails.collection_preview = c_options->thumbnails.collection_preview;
355 options->thumbnails.use_ft_metadata = c_options->thumbnails.use_ft_metadata;
356 options->thumbnails.spec_standard = c_options->thumbnails.spec_standard;
357 options->metadata.enable_metadata_dirs = c_options->metadata.enable_metadata_dirs;
358 options->file_filter.show_hidden_files = c_options->file_filter.show_hidden_files;
359 options->file_filter.show_parent_directory = c_options->file_filter.show_parent_directory;
360 options->file_filter.show_dot_directory = c_options->file_filter.show_dot_directory;
361 options->file_filter.disable_file_extension_checks = c_options->file_filter.disable_file_extension_checks;
363 options->file_sort.case_sensitive = c_options->file_sort.case_sensitive;
364 options->file_filter.disable = c_options->file_filter.disable;
366 config_entry_to_option(sidecar_ext_entry, &options->sidecar.ext, nullptr);
367 sidecar_ext_parse(options->sidecar.ext);
369 options->slideshow.random = c_options->slideshow.random;
370 options->slideshow.repeat = c_options->slideshow.repeat;
371 options->slideshow.delay = c_options->slideshow.delay;
373 options->mousewheel_scrolls = c_options->mousewheel_scrolls;
374 options->image_lm_click_nav = c_options->image_lm_click_nav;
375 options->image_l_click_archive = c_options->image_l_click_archive;
376 options->image_l_click_video = c_options->image_l_click_video;
377 options->image_l_click_video_editor = c_options->image_l_click_video_editor;
379 options->file_ops.enable_in_place_rename = c_options->file_ops.enable_in_place_rename;
381 options->image.tile_cache_max = c_options->image.tile_cache_max;
382 options->image.image_cache_max = c_options->image.image_cache_max;
384 options->image.zoom_quality = c_options->image.zoom_quality;
386 options->image.zoom_increment = c_options->image.zoom_increment;
388 options->image.zoom_style = c_options->image.zoom_style;
390 options->image.enable_read_ahead = c_options->image.enable_read_ahead;
392 options->appimage_notifications = c_options->appimage_notifications;
395 if (options->image.use_custom_border_color != c_options->image.use_custom_border_color
396 || options->image.use_custom_border_color_in_fullscreen != c_options->image.use_custom_border_color_in_fullscreen
397 || !gdk_rgba_equal(&options->image.border_color, &c_options->image.border_color))
399 options->image.use_custom_border_color_in_fullscreen = c_options->image.use_custom_border_color_in_fullscreen;
400 options->image.use_custom_border_color = c_options->image.use_custom_border_color;
401 options->image.border_color = c_options->image.border_color;
402 layout_colors_update();
403 view_window_colors_update();
406 options->image.alpha_color_1 = c_options->image.alpha_color_1;
407 options->image.alpha_color_2 = c_options->image.alpha_color_2;
409 options->fullscreen.screen = c_options->fullscreen.screen;
410 options->fullscreen.clean_flip = c_options->fullscreen.clean_flip;
411 options->fullscreen.disable_saver = c_options->fullscreen.disable_saver;
412 options->fullscreen.above = c_options->fullscreen.above;
413 if (c_options->image_overlay.template_string)
414 set_image_overlay_template_string(&options->image_overlay.template_string,
415 c_options->image_overlay.template_string);
416 if (c_options->image_overlay.font)
417 set_image_overlay_font_string(&options->image_overlay.font,
418 c_options->image_overlay.font);
419 options->image_overlay.text_red = c_options->image_overlay.text_red;
420 options->image_overlay.text_green = c_options->image_overlay.text_green;
421 options->image_overlay.text_blue = c_options->image_overlay.text_blue;
422 options->image_overlay.text_alpha = c_options->image_overlay.text_alpha;
423 options->image_overlay.background_red = c_options->image_overlay.background_red;
424 options->image_overlay.background_green = c_options->image_overlay.background_green;
425 options->image_overlay.background_blue = c_options->image_overlay.background_blue;
426 options->image_overlay.background_alpha = c_options->image_overlay.background_alpha;
427 options->image_overlay.font = c_options->image_overlay.font;
428 options->update_on_time_change = c_options->update_on_time_change;
430 options->duplicates_similarity_threshold = c_options->duplicates_similarity_threshold;
431 options->rot_invariant_sim = c_options->rot_invariant_sim;
433 options->tree_descend_subdirs = c_options->tree_descend_subdirs;
435 options->view_dir_list_single_click_enter = c_options->view_dir_list_single_click_enter;
436 options->circular_selection_lists = c_options->circular_selection_lists;
438 options->open_recent_list_maxsize = c_options->open_recent_list_maxsize;
439 options->recent_folder_image_list_maxsize = c_options->recent_folder_image_list_maxsize;
440 options->dnd_icon_size = c_options->dnd_icon_size;
441 options->clipboard_selection = c_options->clipboard_selection;
442 options->dnd_default_action = c_options->dnd_default_action;
444 options->metadata.save_in_image_file = c_options->metadata.save_in_image_file;
445 options->metadata.save_legacy_IPTC = c_options->metadata.save_legacy_IPTC;
446 options->metadata.warn_on_write_problems = c_options->metadata.warn_on_write_problems;
447 options->metadata.save_legacy_format = c_options->metadata.save_legacy_format;
448 options->metadata.sync_grouped_files = c_options->metadata.sync_grouped_files;
449 options->metadata.confirm_write = c_options->metadata.confirm_write;
450 options->metadata.sidecar_extended_name = c_options->metadata.sidecar_extended_name;
451 options->metadata.confirm_timeout = c_options->metadata.confirm_timeout;
452 options->metadata.confirm_after_timeout = c_options->metadata.confirm_after_timeout;
453 options->metadata.confirm_on_image_change = c_options->metadata.confirm_on_image_change;
454 options->metadata.confirm_on_dir_change = c_options->metadata.confirm_on_dir_change;
455 options->metadata.keywords_case_sensitive = c_options->metadata.keywords_case_sensitive;
456 options->metadata.write_orientation = c_options->metadata.write_orientation;
457 options->metadata.check_spelling = c_options->metadata.check_spelling;
458 options->stereo.mode = (c_options->stereo.mode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
459 (c_options->stereo.tmp.mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
460 (c_options->stereo.tmp.flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
461 (c_options->stereo.tmp.mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
462 (c_options->stereo.tmp.flip_left ? PR_STEREO_FLIP_LEFT : 0) |
463 (c_options->stereo.tmp.swap ? PR_STEREO_SWAP : 0) |
464 (c_options->stereo.tmp.temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
465 options->stereo.fsmode = (c_options->stereo.fsmode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
466 (c_options->stereo.tmp.fs_mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
467 (c_options->stereo.tmp.fs_flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
468 (c_options->stereo.tmp.fs_mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
469 (c_options->stereo.tmp.fs_flip_left ? PR_STEREO_FLIP_LEFT : 0) |
470 (c_options->stereo.tmp.fs_swap ? PR_STEREO_SWAP : 0) |
471 (c_options->stereo.tmp.fs_temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
472 options->stereo.enable_fsmode = c_options->stereo.enable_fsmode;
473 options->stereo.fixed_w = c_options->stereo.fixed_w;
474 options->stereo.fixed_h = c_options->stereo.fixed_h;
475 options->stereo.fixed_x1 = c_options->stereo.fixed_x1;
476 options->stereo.fixed_y1 = c_options->stereo.fixed_y1;
477 options->stereo.fixed_x2 = c_options->stereo.fixed_x2;
478 options->stereo.fixed_y2 = c_options->stereo.fixed_y2;
480 options->info_keywords.height = c_options->info_keywords.height;
481 options->info_title.height = c_options->info_title.height;
482 options->info_comment.height = c_options->info_comment.height;
483 options->info_rating.height = c_options->info_rating.height;
485 options->show_predefined_keyword_tree = c_options->show_predefined_keyword_tree;
486 options->expand_menu_toolbar = c_options->expand_menu_toolbar;
487 options->hamburger_menu = c_options->hamburger_menu;
489 options->selectable_bars.menu_bar = c_options->selectable_bars.menu_bar;
490 options->selectable_bars.tool_bar = c_options->selectable_bars.tool_bar;
491 options->selectable_bars.status_bar = c_options->selectable_bars.status_bar;
493 options->marks_save = c_options->marks_save;
494 options->with_rename = c_options->with_rename;
495 options->collections_duplicates = c_options->collections_duplicates;
496 options->collections_on_top = c_options->collections_on_top;
497 options->hide_window_in_fullscreen = c_options->hide_window_in_fullscreen;
498 config_entry_to_option(help_search_engine_entry, &options->help_search_engine, nullptr);
500 options->external_preview.enable = c_options->external_preview.enable;
501 config_entry_to_option(external_preview_select_entry, &options->external_preview.select, nullptr);
502 config_entry_to_option(external_preview_extract_entry, &options->external_preview.extract, nullptr);
504 options->read_metadata_in_idle = c_options->read_metadata_in_idle;
506 options->star_rating.star = c_options->star_rating.star;
507 options->star_rating.rejected = c_options->star_rating.rejected;
509 options->threads.duplicates = c_options->threads.duplicates > 0 ? c_options->threads.duplicates : -1;
511 options->alternate_similarity_algorithm.enabled = c_options->alternate_similarity_algorithm.enabled;
512 options->alternate_similarity_algorithm.grayscale = c_options->alternate_similarity_algorithm.grayscale;
515 set_debug_level(debug_c);
516 config_entry_to_option(log_window_f1_entry, &options->log_window.action, nullptr);
520 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
522 config_entry_to_option(color_profile_input_name_entry[i], &options->color_profile.input_name[i], nullptr);
523 config_entry_to_option(color_profile_input_file_entry[i], &options->color_profile.input_file[i], nullptr);
525 config_entry_to_option(color_profile_screen_file_entry, &options->color_profile.screen_file, nullptr);
526 options->color_profile.use_x11_screen_profile = c_options->color_profile.use_x11_screen_profile;
527 if (options->color_profile.render_intent != c_options->color_profile.render_intent)
529 options->color_profile.render_intent = c_options->color_profile.render_intent;
534 options->mouse_button_8 = c_options->mouse_button_8;
535 options->mouse_button_9 = c_options->mouse_button_9;
537 options->override_disable_gpu = c_options->override_disable_gpu;
539 config_tab_keywords_save();
541 image_options_sync();
546 layout_refresh(nullptr);
549 if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, nullptr);
551 toolbar_apply(TOOLBAR_MAIN);
552 toolbar_apply(TOOLBAR_STATUS);
556 *-----------------------------------------------------------------------------
557 * config window main button callbacks (private)
558 *-----------------------------------------------------------------------------
561 static void config_window_close_cb(GtkWidget *, gpointer)
563 gq_gtk_widget_destroy(configwindow);
564 configwindow = nullptr;
565 filter_store = nullptr;
568 static void config_window_help_cb(GtkWidget *, gpointer data)
570 auto notebook = static_cast<GtkWidget *>(data);
573 static const gchar *html_section[] =
575 "GuideOptionsGeneral.html",
576 "GuideOptionsImage.html",
577 "GuideOptionsOSD.html",
578 "GuideOptionsWindow.html",
579 "GuideOptionsKeyboard.html",
580 "GuideOptionsFiltering.html",
581 "GuideOptionsMetadata.html",
582 "GuideOptionsKeywords.html",
583 "GuideOptionsColor.html",
584 "GuideOptionsStereo.html",
585 "GuideOptionsBehavior.html",
586 "GuideOptionsToolbar.html",
587 "GuideOptionsToolbar.html",
588 "GuideOptionsAdvanced.html"
591 i = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
592 help_window_show(html_section[i]);
595 static gboolean config_window_delete(GtkWidget *, GdkEventAny *, gpointer)
597 config_window_close_cb(nullptr, nullptr);
601 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
604 auto notebook = static_cast<GtkNotebook *>(data);
612 lw = static_cast<LayoutWindow *>(layout_window_list->data);
614 window = gtk_widget_get_window(widget);
615 gdk_window_get_root_origin(window, &x, &y);
616 w = gdk_window_get_width(window);
617 h = gdk_window_get_height(window);
618 page_number = gtk_notebook_get_current_page(notebook);
620 lw->options.preferences_window.x = x;
621 lw->options.preferences_window.y = y;
622 lw->options.preferences_window.w = w;
623 lw->options.preferences_window.h = h;
624 lw->options.preferences_window.page_number = page_number;
626 config_window_apply();
627 layout_util_sync(lw);
628 save_options(options);
629 config_window_close_cb(nullptr, nullptr);
633 *-----------------------------------------------------------------------------
634 * config window setup (private)
635 *-----------------------------------------------------------------------------
638 static void quality_menu_cb(GtkWidget *combo, gpointer data)
640 auto option = static_cast<gint *>(data);
642 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
646 *option = GDK_INTERP_NEAREST;
649 *option = GDK_INTERP_TILES;
652 *option = GDK_INTERP_BILINEAR;
657 static void dnd_default_action_selection_menu_cb(GtkWidget *combo, gpointer data)
659 auto option = static_cast<gint *>(data);
661 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
665 *option = DND_ACTION_ASK;
668 *option = DND_ACTION_COPY;
671 *option = DND_ACTION_MOVE;
675 static void clipboard_selection_menu_cb(GtkWidget *combo, gpointer data)
677 auto option = static_cast<gint *>(data);
679 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
682 *option = CLIPBOARD_PRIMARY;
685 *option = CLIPBOARD_CLIPBOARD;
688 *option = CLIPBOARD_BOTH;
691 *option = CLIPBOARD_BOTH;
695 static void add_quality_menu(GtkWidget *table, gint column, gint row, const gchar *text,
696 guint option, guint *option_c)
703 pref_table_label(table, column, row, text, GTK_ALIGN_START);
705 combo = gtk_combo_box_text_new();
707 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Nearest (worst, but fastest)"));
708 if (option == GDK_INTERP_NEAREST) current = 0;
709 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Tiles"));
710 if (option == GDK_INTERP_TILES) current = 1;
711 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Bilinear (best, but slowest)"));
712 if (option == GDK_INTERP_BILINEAR) current = 2;
714 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
716 g_signal_connect(G_OBJECT(combo), "changed",
717 G_CALLBACK(quality_menu_cb), option_c);
719 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
720 gtk_widget_show(combo);
723 static void add_dnd_default_action_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, DnDAction option, DnDAction *option_c)
730 pref_table_label(table, column, row, text, GTK_ALIGN_START);
732 combo = gtk_combo_box_text_new();
734 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Ask"));
735 if (option == DND_ACTION_ASK) current = 0;
736 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Copy"));
737 if (option == DND_ACTION_COPY) current = 1;
738 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Move"));
739 if (option == DND_ACTION_MOVE) current = 2;
741 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
743 g_signal_connect(G_OBJECT(combo), "changed",
744 G_CALLBACK(dnd_default_action_selection_menu_cb), option_c);
746 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
747 gtk_widget_show(combo);
750 static void add_clipboard_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
751 gint option, gint *option_c)
758 pref_table_label(table, column, row, text, GTK_ALIGN_START);
760 combo = gtk_combo_box_text_new();
762 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Primary"));
763 if (option == CLIPBOARD_PRIMARY) current = 0;
764 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Clipboard"));
765 if (option == CLIPBOARD_CLIPBOARD) current = 1;
766 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Both"));
767 if (option == CLIPBOARD_BOTH) current = 2;
769 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
771 g_signal_connect(G_OBJECT(combo), "changed",
772 G_CALLBACK(clipboard_selection_menu_cb), option_c);
774 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
775 gtk_widget_show(combo);
778 static void zoom_style_selection_menu_cb(GtkWidget *combo, gpointer data)
780 auto option = static_cast<gint *>(data);
782 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
785 *option = ZOOM_GEOMETRIC;
788 *option = ZOOM_ARITHMETIC;
791 *option = ZOOM_GEOMETRIC;
795 static void add_zoom_style_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, ZoomStyle option, ZoomStyle *option_c)
802 pref_table_label(table, column, row, text, GTK_ALIGN_START);
804 combo = gtk_combo_box_text_new();
806 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Geometric"));
807 if (option == ZOOM_GEOMETRIC) current = 0;
808 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Arithmetic"));
809 if (option == ZOOM_ARITHMETIC) current = 1;
811 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
813 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(zoom_style_selection_menu_cb), option_c);
815 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
816 gtk_widget_show(combo);
819 static void mouse_buttons_selection_menu_cb(GtkWidget *combo, gpointer data)
821 ActionItem *action_item = nullptr;
822 auto option = static_cast<gchar **>(data);
827 label = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
829 list = get_action_items();
834 action_item = static_cast<ActionItem *>(work->data);
835 if (g_strcmp0(action_item->label, label) == 0)
843 *option = g_strdup(action_item->name);
845 action_items_free(list);
848 static void add_mouse_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, gchar *option, gchar **option_c)
850 ActionItem *action_item;
859 pref_table_label(table, column, row, text, GTK_ALIGN_START);
861 combo = gtk_combo_box_text_new();
863 list = get_action_items();
867 action_item = static_cast<ActionItem *>(work->data);
868 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), action_item->label);
870 if (g_strcmp0(action_item->name, option) == 0)
878 action_items_free(list);
880 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
882 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(mouse_buttons_selection_menu_cb), option_c);
884 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
885 gtk_widget_show(combo);
888 static void thumb_size_menu_cb(GtkWidget *combo, gpointer)
892 n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
895 if (static_cast<guint>(n) < sizeof(thumb_size_list) / sizeof(ThumbSize))
897 c_options->thumbnails.max_width = thumb_size_list[n].w;
898 c_options->thumbnails.max_height = thumb_size_list[n].h;
902 c_options->thumbnails.max_width = options->thumbnails.max_width;
903 c_options->thumbnails.max_height = options->thumbnails.max_height;
907 static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *text)
913 c_options->thumbnails.max_width = options->thumbnails.max_width;
914 c_options->thumbnails.max_height = options->thumbnails.max_height;
916 pref_table_label(table, column, row, text, GTK_ALIGN_START);
918 combo = gtk_combo_box_text_new();
921 for (i = 0; static_cast<guint>(i) < sizeof(thumb_size_list) / sizeof(ThumbSize); i++)
927 w = thumb_size_list[i].w;
928 h = thumb_size_list[i].h;
930 buf = g_strdup_printf("%d x %d", w, h);
931 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
934 if (w == options->thumbnails.max_width && h == options->thumbnails.max_height) current = i;
941 buf = g_strdup_printf("%s %d x %d", _("Custom"), options->thumbnails.max_width, options->thumbnails.max_height);
942 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
948 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
949 g_signal_connect(G_OBJECT(combo), "changed",
950 G_CALLBACK(thumb_size_menu_cb), NULL);
952 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
953 gtk_widget_show(combo);
956 static void stereo_mode_menu_cb(GtkWidget *combo, gpointer data)
958 auto option = static_cast<gint *>(data);
960 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
964 *option = PR_STEREO_NONE;
967 *option = PR_STEREO_ANAGLYPH_RC;
970 *option = PR_STEREO_ANAGLYPH_GM;
973 *option = PR_STEREO_ANAGLYPH_YB;
976 *option = PR_STEREO_ANAGLYPH_GRAY_RC;
979 *option = PR_STEREO_ANAGLYPH_GRAY_GM;
982 *option = PR_STEREO_ANAGLYPH_GRAY_YB;
985 *option = PR_STEREO_ANAGLYPH_DB_RC;
988 *option = PR_STEREO_ANAGLYPH_DB_GM;
991 *option = PR_STEREO_ANAGLYPH_DB_YB;
994 *option = PR_STEREO_HORIZ;
997 *option = PR_STEREO_HORIZ | PR_STEREO_HALF;
1000 *option = PR_STEREO_VERT;
1003 *option = PR_STEREO_VERT | PR_STEREO_HALF;
1006 *option = PR_STEREO_FIXED;
1011 static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1012 gint option, gint *option_c, gboolean add_fixed)
1019 pref_table_label(table, column, row, text, GTK_ALIGN_START);
1021 combo = gtk_combo_box_text_new();
1023 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Single image"));
1025 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Red-Cyan"));
1026 if (option & PR_STEREO_ANAGLYPH_RC) current = 1;
1027 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Green-Magenta"));
1028 if (option & PR_STEREO_ANAGLYPH_GM) current = 2;
1029 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Yellow-Blue"));
1030 if (option & PR_STEREO_ANAGLYPH_YB) current = 3;
1031 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Red-Cyan"));
1032 if (option & PR_STEREO_ANAGLYPH_GRAY_RC) current = 4;
1033 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Green-Magenta"));
1034 if (option & PR_STEREO_ANAGLYPH_GRAY_GM) current = 5;
1035 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Yellow-Blue"));
1036 if (option & PR_STEREO_ANAGLYPH_GRAY_YB) current = 6;
1037 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Red-Cyan"));
1038 if (option & PR_STEREO_ANAGLYPH_DB_RC) current = 7;
1039 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Green-Magenta"));
1040 if (option & PR_STEREO_ANAGLYPH_DB_GM) current = 8;
1041 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Yellow-Blue"));
1042 if (option & PR_STEREO_ANAGLYPH_DB_YB) current = 9;
1044 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side"));
1045 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side Half size"));
1046 if (option & PR_STEREO_HORIZ)
1049 if (option & PR_STEREO_HALF) current = 11;
1052 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom"));
1053 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom Half size"));
1054 if (option & PR_STEREO_VERT)
1057 if (option & PR_STEREO_HALF) current = 13;
1062 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Fixed position"));
1063 if (option & PR_STEREO_FIXED) current = 14;
1066 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1068 g_signal_connect(G_OBJECT(combo), "changed",
1069 G_CALLBACK(stereo_mode_menu_cb), option_c);
1071 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1072 gtk_widget_show(combo);
1075 static void video_menu_cb(GtkWidget *combo, gpointer data)
1077 auto option = static_cast<gchar **>(data);
1079 auto ed = static_cast<EditorDescription *>(g_list_nth_data(editor_list_get(), gtk_combo_box_get_active(GTK_COMBO_BOX(combo))));
1083 static void video_menu_populate(gpointer data, gpointer user_data)
1085 auto combo = static_cast<GtkWidget *>(user_data);
1086 auto ed = static_cast<EditorDescription *>(data);
1088 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), ed->name);
1091 static void add_video_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1092 gchar *option, gchar **option_c)
1096 /* use lists since they are sorted */
1097 GList *eds = editor_list_get();
1101 pref_table_label(table, column, row, text, GTK_ALIGN_START);
1103 combo = gtk_combo_box_text_new();
1104 g_list_foreach(eds,video_menu_populate,combo);
1105 current = option ? g_list_index(eds,g_hash_table_lookup(editors,option)): -1;
1107 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1109 g_signal_connect(G_OBJECT(combo), "changed",
1110 G_CALLBACK(video_menu_cb), option_c);
1112 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1113 gtk_widget_show(combo);
1116 static void filter_store_populate()
1120 if (!filter_store) return;
1122 gtk_list_store_clear(filter_store);
1124 work = filter_get_list();
1130 fe = static_cast<FilterEntry *>(work->data);
1133 gtk_list_store_append(filter_store, &iter);
1134 gtk_list_store_set(filter_store, &iter, 0, fe, -1);
1138 static void filter_store_ext_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1140 auto model = static_cast<GtkWidget *>(data);
1141 auto fe = static_cast<FilterEntry *>(data);
1145 if (!new_text || strlen(new_text) < 1) return;
1147 tpath = gtk_tree_path_new_from_string(path_str);
1148 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1149 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1151 g_free(fe->extensions);
1152 fe->extensions = g_strdup(new_text);
1154 gtk_tree_path_free(tpath);
1158 static void filter_store_class_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1160 auto model = static_cast<GtkWidget *>(data);
1161 auto fe = static_cast<FilterEntry *>(data);
1166 if (!new_text || !new_text[0]) return;
1168 tpath = gtk_tree_path_new_from_string(path_str);
1169 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1170 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1172 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
1174 if (strcmp(new_text, _(format_class_list[i])) == 0)
1176 fe->file_class = static_cast<FileFormatClass>(i);
1181 gtk_tree_path_free(tpath);
1185 static void filter_store_desc_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1187 auto model = static_cast<GtkWidget *>(data);
1192 if (!new_text || !new_text[0]) return;
1194 tpath = gtk_tree_path_new_from_string(path_str);
1195 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1196 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1198 g_free(fe->description);
1199 fe->description = g_strdup(new_text);
1201 gtk_tree_path_free(tpath);
1204 static void filter_store_enable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1206 auto model = static_cast<GtkWidget *>(data);
1211 tpath = gtk_tree_path_new_from_string(path_str);
1212 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1213 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1215 fe->enabled = !fe->enabled;
1217 gtk_tree_path_free(tpath);
1221 static void filter_store_writable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1223 auto model = static_cast<GtkWidget *>(data);
1228 tpath = gtk_tree_path_new_from_string(path_str);
1229 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1230 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1232 fe->writable = !fe->writable;
1233 if (fe->writable) fe->allow_sidecar = FALSE;
1235 gtk_tree_path_free(tpath);
1239 static void filter_store_sidecar_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1241 auto model = static_cast<GtkWidget *>(data);
1246 tpath = gtk_tree_path_new_from_string(path_str);
1247 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1248 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1250 fe->allow_sidecar = !fe->allow_sidecar;
1251 if (fe->allow_sidecar) fe->writable = FALSE;
1253 gtk_tree_path_free(tpath);
1257 static void filter_set_func(GtkTreeViewColumn *, GtkCellRenderer *cell,
1258 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1262 gtk_tree_model_get(tree_model, iter, 0, &fe, -1);
1264 switch (GPOINTER_TO_INT(data))
1267 g_object_set(GTK_CELL_RENDERER(cell),
1268 "active", fe->enabled, NULL);
1271 g_object_set(GTK_CELL_RENDERER(cell),
1272 "text", fe->extensions, NULL);
1274 case FE_DESCRIPTION:
1275 g_object_set(GTK_CELL_RENDERER(cell),
1276 "text", fe->description, NULL);
1279 g_object_set(GTK_CELL_RENDERER(cell),
1280 "text", _(format_class_list[fe->file_class]), NULL);
1283 g_object_set(GTK_CELL_RENDERER(cell),
1284 "active", fe->writable, NULL);
1286 case FE_ALLOW_SIDECAR:
1287 g_object_set(GTK_CELL_RENDERER(cell),
1288 "active", fe->allow_sidecar, NULL);
1293 static gboolean filter_add_scroll(gpointer data)
1297 GtkCellRenderer *cell;
1298 GtkTreeViewColumn *column;
1301 GtkTreeModel *store;
1303 FilterEntry *filter;
1305 rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(filter_store), nullptr);
1306 path = gtk_tree_path_new_from_indices(rows-1, -1);
1308 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data), 0);
1310 list_cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1311 cell = static_cast<GtkCellRenderer *>(g_list_last(list_cells)->data);
1313 store = gtk_tree_view_get_model(GTK_TREE_VIEW(data));
1314 valid = gtk_tree_model_get_iter_first(store, &iter);
1318 gtk_tree_model_get(store, &iter, 0, &filter, -1);
1320 if (g_strcmp0(filter->extensions, ".new") == 0)
1322 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1326 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1329 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1330 path, column, FALSE, 0.0, 0.0 );
1331 gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(data),
1332 path, column, cell, TRUE);
1334 gtk_tree_path_free(path);
1335 g_list_free(list_cells);
1337 return(G_SOURCE_REMOVE);
1340 static void filter_add_cb(GtkWidget *, gpointer data)
1342 filter_add_unique("description", ".new", FORMAT_CLASS_IMAGE, TRUE, FALSE, TRUE);
1343 filter_store_populate();
1345 g_idle_add(static_cast<GSourceFunc>(filter_add_scroll), data);
1348 static void filter_remove_cb(GtkWidget *, gpointer data)
1350 auto filter_view = static_cast<GtkWidget *>(data);
1351 GtkTreeSelection *selection;
1355 if (!filter_store) return;
1356 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
1357 if (!gtk_tree_selection_get_selected(selection, nullptr, &iter)) return;
1358 gtk_tree_model_get(GTK_TREE_MODEL(filter_store), &iter, 0, &fe, -1);
1361 filter_remove_entry(fe);
1363 filter_store_populate();
1366 static gboolean filter_default_ok_scroll(GtkTreeView *data)
1370 GtkTreeViewColumn *column;
1372 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(filter_store), &iter);
1373 path = gtk_tree_model_get_path(GTK_TREE_MODEL(filter_store), &iter);
1374 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1376 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1380 gtk_tree_path_free(path);
1382 return(G_SOURCE_REMOVE);
1385 static void filter_default_ok_cb(GenericDialog *gd, gpointer)
1388 filter_add_defaults();
1390 filter_store_populate();
1392 g_idle_add(reinterpret_cast<GSourceFunc>(filter_default_ok_scroll), gd->data);
1395 static void dummy_cancel_cb(GenericDialog *, gpointer)
1397 /* no op, only so cancel button appears */
1400 static void filter_default_cb(GtkWidget *widget, gpointer data)
1404 gd = generic_dialog_new(_("Reset filters"),
1405 "reset_filter", widget, TRUE,
1406 dummy_cancel_cb, data);
1407 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset filters"),
1408 _("This will reset the file filters to the defaults.\nContinue?"), TRUE);
1409 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", filter_default_ok_cb, TRUE);
1410 gtk_widget_show(gd->dialog);
1413 static void filter_disable_cb(GtkWidget *widget, gpointer data)
1415 auto frame = static_cast<GtkWidget *>(data);
1417 gtk_widget_set_sensitive(frame,
1418 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1421 static void safe_delete_view_cb(GtkWidget *, gpointer)
1423 layout_set_path(nullptr, gq_gtk_entry_get_text(GTK_ENTRY(safe_delete_path_entry)));
1426 static void safe_delete_clear_ok_cb(GenericDialog *, gpointer)
1428 file_util_trash_clear();
1431 static void safe_delete_clear_cb(GtkWidget *widget, gpointer)
1435 gd = generic_dialog_new(_("Clear trash"),
1436 "clear_trash", widget, TRUE,
1437 dummy_cancel_cb, nullptr);
1438 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Clear trash"),
1439 _("This will remove the trash contents."), FALSE);
1440 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", safe_delete_clear_ok_cb, TRUE);
1441 entry = gtk_entry_new();
1442 gtk_widget_set_can_focus(entry, FALSE);
1443 gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
1444 if (options->file_ops.safe_delete_path) gq_gtk_entry_set_text(GTK_ENTRY(entry), options->file_ops.safe_delete_path);
1445 gq_gtk_box_pack_start(GTK_BOX(gd->vbox), entry, FALSE, FALSE, 0);
1446 gtk_widget_show(entry);
1447 gtk_widget_show(gd->dialog);
1450 static void image_overlay_template_view_changed_cb(GtkWidget *, gpointer data)
1452 GtkWidget *pTextView;
1453 GtkTextBuffer *pTextBuffer;
1457 pTextView = GTK_WIDGET(data);
1459 pTextBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pTextView));
1460 gtk_text_buffer_get_start_iter(pTextBuffer, &iStart);
1461 gtk_text_buffer_get_end_iter(pTextBuffer, &iEnd);
1463 set_image_overlay_template_string(&c_options->image_overlay.template_string,
1464 gtk_text_buffer_get_text(pTextBuffer, &iStart, &iEnd, TRUE));
1467 static void image_overlay_default_template_ok_cb(GenericDialog *, gpointer data)
1469 auto text_view = static_cast<GtkTextView *>(data);
1470 GtkTextBuffer *buffer;
1472 set_default_image_overlay_template_string(&options->image_overlay.template_string);
1473 if (!configwindow) return;
1475 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
1476 gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
1479 static void image_overlay_default_template_cb(GtkWidget *widget, gpointer data)
1483 gd = generic_dialog_new(_("Reset image overlay template string"),
1484 "reset_image_overlay_template_string", widget, TRUE,
1485 dummy_cancel_cb, data);
1486 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset image overlay template string"),
1487 _("This will reset the image overlay template string to the default.\nContinue?"), TRUE);
1488 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", image_overlay_default_template_ok_cb, TRUE);
1489 gtk_widget_show(gd->dialog);
1492 static void image_overlay_help_cb(GtkWidget *, gpointer)
1494 help_window_show("GuideOptionsOSD.html");
1497 static void font_activated_cb(GtkFontChooser *widget, gchar *fontname, gpointer)
1499 g_free(c_options->image_overlay.font);
1500 c_options->image_overlay.font = g_strdup(fontname);
1503 gq_gtk_widget_destroy(GTK_WIDGET(widget));
1506 static void font_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1510 g_free(c_options->image_overlay.font);
1511 c_options->image_overlay.font = g_strdup(options->image_overlay.font);
1513 if (response_id == GTK_RESPONSE_OK)
1515 font = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(dialog));
1516 g_free(c_options->image_overlay.font);
1517 c_options->image_overlay.font = g_strdup(font);
1521 gq_gtk_widget_destroy(GTK_WIDGET(dialog));
1524 static void image_overlay_set_font_cb(GtkWidget *widget, gpointer)
1528 dialog = gtk_font_chooser_dialog_new(_("Image Overlay Font"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1529 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1530 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), options->image_overlay.font);
1532 g_signal_connect(dialog, "font-activated", G_CALLBACK(font_activated_cb), nullptr);
1533 g_signal_connect(dialog, "response", G_CALLBACK(font_response_cb), nullptr);
1535 gtk_widget_show(dialog);
1538 static void text_color_activated_cb(GtkColorChooser *chooser, GdkRGBA *color, gpointer)
1540 c_options->image_overlay.text_red = color->red * 255;
1541 c_options->image_overlay.text_green = color->green * 255;
1542 c_options->image_overlay.text_blue = color->blue * 255;
1543 c_options->image_overlay.text_alpha = color->alpha * 255;
1545 gq_gtk_widget_destroy(GTK_WIDGET(chooser));
1548 static void text_color_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1552 c_options->image_overlay.text_red = options->image_overlay.text_red;
1553 c_options->image_overlay.text_green = options->image_overlay.text_green;
1554 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
1555 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
1557 if (response_id == GTK_RESPONSE_OK)
1559 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1560 c_options->image_overlay.text_red = color.red * 255;
1561 c_options->image_overlay.text_green = color.green * 255;
1562 c_options->image_overlay.text_blue = color.blue * 255;
1563 c_options->image_overlay.text_alpha = color.alpha * 255;
1566 gq_gtk_widget_destroy(GTK_WIDGET(dialog));
1569 static void image_overlay_set_text_color_cb(GtkWidget *widget, gpointer)
1574 dialog = gtk_color_chooser_dialog_new(_("Image Overlay Text Color"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1575 color.red = options->image_overlay.text_red;
1576 color.green = options->image_overlay.text_green;
1577 color.blue = options->image_overlay.text_blue;
1578 color.alpha = options->image_overlay.text_alpha;
1579 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1580 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1582 g_signal_connect(dialog, "color-activated", G_CALLBACK(text_color_activated_cb), nullptr);
1583 g_signal_connect(dialog, "response", G_CALLBACK(text_color_response_cb), nullptr);
1585 gtk_widget_show(dialog);
1588 static void bg_color_activated_cb(GtkColorChooser *chooser, GdkRGBA *color, gpointer)
1590 c_options->image_overlay.background_red = color->red * 255;
1591 c_options->image_overlay.background_green = color->green * 255;
1592 c_options->image_overlay.background_blue = color->blue * 255;
1593 c_options->image_overlay.background_alpha = color->alpha * 255;
1595 gq_gtk_widget_destroy(GTK_WIDGET(chooser));
1598 static void bg_color_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1602 c_options->image_overlay.background_red = options->image_overlay.background_red;
1603 c_options->image_overlay.background_green = options->image_overlay.background_green;
1604 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
1605 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
1607 if (response_id == GTK_RESPONSE_OK)
1609 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1610 c_options->image_overlay.background_red = color.red * 255;
1611 c_options->image_overlay.background_green = color.green * 255;
1612 c_options->image_overlay.background_blue = color.blue * 255;
1613 c_options->image_overlay.background_alpha = color.alpha * 255;
1615 gq_gtk_widget_destroy(GTK_WIDGET(dialog));
1618 static void image_overlay_set_background_color_cb(GtkWidget *widget, gpointer)
1623 dialog = gtk_color_chooser_dialog_new(_("Image Overlay Background Color"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1624 color.red = options->image_overlay.background_red;
1625 color.green = options->image_overlay.background_green;
1626 color.blue = options->image_overlay.background_blue;
1627 color.alpha = options->image_overlay.background_alpha;
1628 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1629 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1631 g_signal_connect(dialog, "color-activated", G_CALLBACK(bg_color_activated_cb), nullptr);
1632 g_signal_connect(dialog, "response", G_CALLBACK(bg_color_response_cb), nullptr);
1634 gtk_widget_show(dialog);
1637 static void accel_store_populate()
1643 const gchar *accel_path;
1647 if (!accel_store || !layout_window_list || !layout_window_list->data) return;
1649 gtk_tree_store_clear(accel_store);
1650 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 */
1652 g_assert(lw && lw->ui_manager);
1653 groups = gtk_ui_manager_get_action_groups(lw->ui_manager);
1656 actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
1659 action = GTK_ACTION(actions->data);
1660 accel_path = gtk_action_get_accel_path(action);
1661 if (accel_path && gtk_accel_map_lookup_entry(accel_path, &key))
1667 g_object_get(action,
1668 "tooltip", &tooltip,
1672 if (pango_parse_markup(label, -1, '_', nullptr, &label2, nullptr, nullptr) && label2)
1678 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1682 gtk_tree_store_append(accel_store, &iter, nullptr);
1683 gtk_tree_store_set(accel_store, &iter,
1686 AE_TOOLTIP, tooltip ? tooltip : "",
1687 AE_ACCEL, accel_path,
1695 actions = actions->next;
1698 groups = groups->next;
1702 static void accel_store_cleared_cb(GtkCellRendererAccel *, gchar *, gpointer)
1707 static gboolean accel_remove_key_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer data)
1709 auto accel1 = static_cast<gchar *>(data);
1714 gtk_tree_model_get(model, iter, AE_KEY, &accel2, -1);
1716 gtk_accelerator_parse(accel1, &key1.accel_key, &key1.accel_mods);
1717 gtk_accelerator_parse(accel2, &key2.accel_key, &key2.accel_mods);
1719 if (key1.accel_key == key2.accel_key && key1.accel_mods == key2.accel_mods)
1721 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1722 DEBUG_1("accelerator key '%s' is already used, removing.", accel1);
1731 static void accel_store_edited_cb(GtkCellRendererAccel *, gchar *path_string, guint accel_key, GdkModifierType accel_mods, guint, gpointer)
1733 auto model = reinterpret_cast<GtkTreeModel *>(accel_store);
1737 GtkAccelKey old_key;
1739 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1741 gtk_tree_model_get_iter(model, &iter, path);
1742 gtk_tree_model_get(model, &iter, AE_ACCEL, &accel_path, -1);
1744 /* test if the accelerator can be stored without conflicts*/
1745 gtk_accel_map_lookup_entry(accel_path, &old_key);
1747 /* change the key and read it back (change may fail on keys hardcoded in gtk)*/
1748 gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, TRUE);
1749 gtk_accel_map_lookup_entry(accel_path, &key);
1751 /* restore the original for now, the key will be really changed when the changes are confirmed */
1752 gtk_accel_map_change_entry(accel_path, old_key.accel_key, old_key.accel_mods, TRUE);
1754 acc = gtk_accelerator_name(key.accel_key, key.accel_mods);
1755 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, acc);
1757 gtk_tree_store_set(accel_store, &iter, AE_KEY, acc, -1);
1758 gtk_tree_path_free(path);
1762 static gboolean accel_default_scroll(GtkTreeView *data)
1766 GtkTreeViewColumn *column;
1768 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(accel_store), &iter);
1769 path = gtk_tree_model_get_path(GTK_TREE_MODEL(accel_store), &iter);
1770 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1772 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1776 gtk_tree_path_free(path);
1778 return(G_SOURCE_REMOVE);
1781 static void accel_default_cb(GtkWidget *, gpointer data)
1783 accel_store_populate();
1785 g_idle_add(reinterpret_cast<GSourceFunc>(accel_default_scroll), data);
1788 void accel_clear_selection(GtkTreeModel *, GtkTreePath *, GtkTreeIter *iter, gpointer)
1790 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1793 void accel_reset_selection(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
1799 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, -1);
1800 gtk_accel_map_lookup_entry(accel_path, &key);
1801 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1803 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, accel);
1805 gtk_tree_store_set(accel_store, iter, AE_KEY, accel, -1);
1810 static void accel_clear_cb(GtkWidget *, gpointer data)
1812 GtkTreeSelection *selection;
1814 if (!accel_store) return;
1815 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1816 gtk_tree_selection_selected_foreach(selection, &accel_clear_selection, nullptr);
1819 static void accel_reset_cb(GtkWidget *, gpointer data)
1821 GtkTreeSelection *selection;
1823 if (!accel_store) return;
1824 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1825 gtk_tree_selection_selected_foreach(selection, &accel_reset_selection, nullptr);
1830 static GtkWidget *scrolled_notebook_page(GtkWidget *notebook, const gchar *title)
1834 GtkWidget *scrolled;
1835 GtkWidget *viewport;
1837 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
1838 gtk_container_set_border_width(GTK_CONTAINER(scrolled), PREF_PAD_BORDER);
1839 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1840 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1841 label = gtk_label_new(title);
1842 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, label);
1843 gtk_widget_show(scrolled);
1845 viewport = gtk_viewport_new(nullptr, nullptr);
1846 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1847 gq_gtk_container_add(GTK_WIDGET(scrolled), viewport);
1848 gtk_widget_show(viewport);
1850 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1851 gq_gtk_container_add(GTK_WIDGET(viewport), vbox);
1852 gtk_widget_show(vbox);
1857 static void cache_standard_cb(GtkWidget *widget, gpointer)
1859 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1861 c_options->thumbnails.spec_standard =TRUE;
1862 c_options->thumbnails.cache_into_dirs = FALSE;
1866 static void cache_geeqie_cb(GtkWidget *widget, gpointer)
1868 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1870 c_options->thumbnails.spec_standard =FALSE;
1871 c_options->thumbnails.cache_into_dirs = FALSE;
1875 static void cache_local_cb(GtkWidget *widget, gpointer)
1877 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1879 c_options->thumbnails.cache_into_dirs = TRUE;
1880 c_options->thumbnails.spec_standard =FALSE;
1884 static void help_search_engine_entry_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1886 if (pos == GTK_ENTRY_ICON_PRIMARY)
1888 gq_gtk_entry_set_text(GTK_ENTRY(userdata), HELP_SEARCH_ENGINE);
1892 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "");
1896 static void star_rating_star_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1898 gchar *rating_symbol;
1900 if (pos == GTK_ENTRY_ICON_PRIMARY)
1902 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_STAR);
1903 gq_gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1904 g_free(rating_symbol);
1908 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1909 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1910 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1914 static void star_rating_rejected_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1916 gchar *rating_symbol;
1918 if (pos == GTK_ENTRY_ICON_PRIMARY)
1920 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_REJECTED);
1921 gq_gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1922 g_free(rating_symbol);
1926 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1927 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1928 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1932 static guint star_rating_symbol_test(GtkWidget *, gpointer data)
1934 auto hbox = static_cast<GtkContainer *>(data);
1935 GString *str = g_string_new(nullptr);
1936 GtkEntry *hex_code_entry;
1937 gchar *hex_code_full;
1940 guint64 hex_value = 0;
1942 list = gtk_container_get_children(hbox);
1944 hex_code_entry = static_cast<GtkEntry *>(g_list_nth_data(list, 2));
1945 hex_code_full = g_strdup(gq_gtk_entry_get_text(hex_code_entry));
1947 hex_code = g_strsplit(hex_code_full, "+", 2);
1948 if (hex_code[0] && hex_code[1])
1950 hex_value = strtoull(hex_code[1], nullptr, 16);
1952 if (!hex_value || hex_value > 0x10FFFF)
1954 hex_value = 0x003F; // Unicode 'Question Mark'
1956 str = g_string_append_unichar(str, static_cast<gunichar>(hex_value));
1957 gtk_label_set_text(static_cast<GtkLabel *>(g_list_nth_data(list, 1)), str->str);
1959 g_strfreev(hex_code);
1960 g_string_free(str, TRUE);
1961 g_free(hex_code_full);
1966 static void star_rating_star_test_cb(GtkWidget *widget, gpointer data)
1968 guint64 star_symbol;
1970 star_symbol = star_rating_symbol_test(widget, data);
1971 c_options->star_rating.star = star_symbol;
1974 static void star_rating_rejected_test_cb(GtkWidget *widget, gpointer data)
1976 guint64 rejected_symbol;
1978 rejected_symbol = star_rating_symbol_test(widget, data);
1979 c_options->star_rating.rejected = rejected_symbol;
1982 /* general options tab */
1983 static void timezone_database_install_cb(GtkWidget *widget, gpointer data);
1987 GCancellable *cancellable;
1989 GtkWidget *progress;
1991 GFile *timezone_database_gq;
1992 gchar *timezone_database_user;
1995 static void config_tab_general(GtkWidget *notebook)
2000 GtkWidget *group_frame;
2001 GtkWidget *subgroup;
2003 GtkWidget *ct_button;
2010 GtkWidget *star_rating_entry;
2012 gchar *rating_symbol;
2015 gchar *download_locn;
2016 GNetworkMonitor *net_mon;
2017 GSocketConnectable *tz_org;
2018 gboolean internet_available = FALSE;
2021 vbox = scrolled_notebook_page(notebook, _("General"));
2023 group = pref_group_new(vbox, FALSE, _("Thumbnails"), GTK_ORIENTATION_VERTICAL);
2025 table = pref_table_new(group, 2, 2, FALSE, FALSE);
2026 add_thumb_size_menu(table, 0, 0, _("Size:"));
2027 add_quality_menu(table, 0, 1, _("Quality:"), options->thumbnails.quality, &c_options->thumbnails.quality);
2029 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2030 pref_label_new(hbox, _("Custom size: "));
2031 pref_spin_new_int(hbox, _("Width:"), nullptr, 1, 512, 1, options->thumbnails.max_width, &c_options->thumbnails.max_width);
2032 pref_spin_new_int(hbox, _("Height:"), nullptr, 1, 512, 1, options->thumbnails.max_height, &c_options->thumbnails.max_height);
2034 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails and sim. files"),
2035 options->thumbnails.enable_caching, &c_options->thumbnails.enable_caching);
2037 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2038 pref_checkbox_link_sensitivity(ct_button, subgroup);
2040 c_options->thumbnails.spec_standard = options->thumbnails.spec_standard;
2041 c_options->thumbnails.cache_into_dirs = options->thumbnails.cache_into_dirs;
2042 group_frame = pref_frame_new(subgroup, TRUE, _("Use Geeqie thumbnail style and cache"),
2043 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2044 button = pref_radiobutton_new(group_frame, nullptr, get_thumbnails_cache_dir(),
2045 !options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2046 G_CALLBACK(cache_geeqie_cb), nullptr);
2048 group_frame = pref_frame_new(subgroup, TRUE,
2049 _("Store thumbnails local to image folder (non-standard)"),
2050 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2051 pref_radiobutton_new(group_frame, button, "*/.thumbnails",
2052 !options->thumbnails.spec_standard && options->thumbnails.cache_into_dirs,
2053 G_CALLBACK(cache_local_cb), nullptr);
2055 group_frame = pref_frame_new(subgroup, TRUE,
2056 _("Use standard thumbnail style and cache, shared with other applications"),
2057 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2058 pref_radiobutton_new(group_frame, button, get_thumbnails_standard_cache_dir(),
2059 options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2060 G_CALLBACK(cache_standard_cb), nullptr);
2062 pref_checkbox_new_int(group, _("Use EXIF thumbnails when available (EXIF thumbnails may be outdated)"),
2063 options->thumbnails.use_exif, &c_options->thumbnails.use_exif);
2065 pref_checkbox_new_int(group, _("Thumbnail color management"),
2066 options->thumbnails.use_color_management, &c_options->thumbnails.use_color_management);
2068 spin = pref_spin_new_int(group, _("Collection preview:"), nullptr,
2070 options->thumbnails.collection_preview, &c_options->thumbnails.collection_preview);
2071 gtk_widget_set_tooltip_text(spin, _("The maximum number of thumbnails shown in a Collection preview montage"));
2073 #if HAVE_FFMPEGTHUMBNAILER_METADATA
2074 pref_checkbox_new_int(group, _("Use embedded metadata in video files as thumbnails when available"),
2075 options->thumbnails.use_ft_metadata, &c_options->thumbnails.use_ft_metadata);
2078 pref_spacer(group, PREF_PAD_GROUP);
2080 group = pref_group_new(vbox, FALSE, _("Star Rating"), GTK_ORIENTATION_VERTICAL);
2082 c_options->star_rating.star = options->star_rating.star;
2083 c_options->star_rating.rejected = options->star_rating.rejected;
2085 str = g_string_new(nullptr);
2086 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2087 pref_label_new(hbox, _("Star character: "));
2088 str = g_string_append_unichar(str, options->star_rating.star);
2089 pref_label_new(hbox, g_strdup(str->str));
2090 rating_symbol = g_strdup_printf("U+%X", options->star_rating.star);
2091 star_rating_entry = gtk_entry_new();
2092 gq_gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2093 gq_gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2094 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2095 gtk_widget_show(star_rating_entry);
2096 button = pref_button_new(nullptr, nullptr, _("Set"),
2097 G_CALLBACK(star_rating_star_test_cb), hbox);
2098 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2099 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2100 gtk_widget_show(button);
2101 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."));
2102 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2103 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2104 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2105 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2106 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2107 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2108 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2109 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2110 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2111 G_CALLBACK(star_rating_star_icon_cb),
2114 g_string_free(str, TRUE);
2115 g_free(rating_symbol);
2117 str = g_string_new(nullptr);
2118 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2119 pref_label_new(hbox, _("Rejected character: "));
2120 str = g_string_append_unichar(str, options->star_rating.rejected);
2121 pref_label_new(hbox, g_strdup(str->str));
2122 rating_symbol = g_strdup_printf("U+%X", options->star_rating.rejected);
2123 star_rating_entry = gtk_entry_new();
2124 gq_gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2125 gq_gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2126 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2127 gtk_widget_show(star_rating_entry);
2128 button = pref_button_new(nullptr, nullptr, _("Set"),
2129 G_CALLBACK(star_rating_rejected_test_cb), hbox);
2130 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2131 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2132 gtk_widget_show(button);
2133 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."));
2134 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2135 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2136 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2137 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2138 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2139 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2140 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2141 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2142 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2143 G_CALLBACK(star_rating_rejected_icon_cb),
2146 g_string_free(str, TRUE);
2147 g_free(rating_symbol);
2149 pref_spacer(group, PREF_PAD_GROUP);
2151 group = pref_group_new(vbox, FALSE, _("Slide show"), GTK_ORIENTATION_VERTICAL);
2153 c_options->slideshow.delay = options->slideshow.delay;
2154 hours = options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2155 remainder = options->slideshow.delay % (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2156 minutes = remainder / (60 * SLIDESHOW_SUBSECOND_PRECISION);
2157 seconds = static_cast<gdouble>(remainder % (60 * SLIDESHOW_SUBSECOND_PRECISION)) /
2158 SLIDESHOW_SUBSECOND_PRECISION;
2160 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2162 spin = pref_spin_new(hbox, _("Delay between image change hrs:mins:secs.dec"), nullptr,
2164 options->slideshow.delay ? hours : 0.0,
2165 G_CALLBACK(slideshow_delay_hours_cb), nullptr);
2166 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2167 spin = pref_spin_new(hbox, ":" , nullptr,
2169 options->slideshow.delay ? minutes: 0.0,
2170 G_CALLBACK(slideshow_delay_minutes_cb), nullptr);
2171 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2172 spin = pref_spin_new(hbox, ":", nullptr,
2173 SLIDESHOW_MIN_SECONDS, 59, 1.0, 1,
2174 options->slideshow.delay ? seconds : 10.0,
2175 G_CALLBACK(slideshow_delay_seconds_cb), nullptr);
2176 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2178 pref_checkbox_new_int(group, _("Random"), options->slideshow.random, &c_options->slideshow.random);
2179 pref_checkbox_new_int(group, _("Repeat"), options->slideshow.repeat, &c_options->slideshow.repeat);
2181 pref_spacer(group, PREF_PAD_GROUP);
2183 group = pref_group_new(vbox, FALSE, _("Image loading and caching"), GTK_ORIENTATION_VERTICAL);
2185 pref_spin_new_int(group, _("Decoded image cache size (MiB):"), nullptr,
2186 0, 99999, 1, options->image.image_cache_max, &c_options->image.image_cache_max);
2187 pref_checkbox_new_int(group, _("Preload next image"),
2188 options->image.enable_read_ahead, &c_options->image.enable_read_ahead);
2190 pref_checkbox_new_int(group, _("Refresh on file change"),
2191 options->update_on_time_change, &c_options->update_on_time_change);
2194 pref_spacer(group, PREF_PAD_GROUP);
2196 group = pref_group_new(vbox, FALSE, _("Menu style"), GTK_ORIENTATION_VERTICAL);
2198 pref_checkbox_new_int(group, _("☰ style menu button (NOTE! Geeqie must be restarted for change to take effect)"),
2199 options->hamburger_menu, &c_options->hamburger_menu);
2200 gtk_widget_set_tooltip_text(group, _("Use a ☰ style menu button instead of the classic style across the top of the frame"));
2202 pref_spacer(group, PREF_PAD_GROUP);
2204 group = pref_group_new(vbox, FALSE, _("Expand toolbar"), GTK_ORIENTATION_VERTICAL);
2206 pref_checkbox_new_int(group, _("Expand menu/toolbar (NOTE! Geeqie must be restarted for change to take effect)"),
2207 options->expand_menu_toolbar, &c_options->expand_menu_toolbar);
2208 gtk_widget_set_tooltip_text(group, _("Expand the menu/toolbar to the full width of the window"));
2210 pref_spacer(group, PREF_PAD_GROUP);
2212 group = pref_group_new(vbox, FALSE, _("Hide Selectable Bars"), GTK_ORIENTATION_VERTICAL);
2214 pref_checkbox_new_int(group, _("Menu bar"),
2215 options->selectable_bars.menu_bar, &c_options->selectable_bars.menu_bar);
2217 pref_checkbox_new_int(group, _("Tool bar"),
2218 options->selectable_bars.tool_bar, &c_options->selectable_bars.tool_bar);
2220 pref_checkbox_new_int(group, _("Status bar"),
2221 options->selectable_bars.status_bar, &c_options->selectable_bars.status_bar);
2222 gtk_widget_set_tooltip_text(group, _("The Hide Selectable Bars menu item (default keystroke is control-backtick) will toggle the display of the bars selected here"));
2224 pref_spacer(group, PREF_PAD_GROUP);
2226 if (g_getenv("APPDIR") && strstr(g_getenv("APPDIR"), "/tmp/.mount_Geeqie"))
2228 group = pref_group_new(vbox, FALSE, _("AppImage updates notifications"), GTK_ORIENTATION_VERTICAL);
2229 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2230 pref_checkbox_new_int(group, _("Enable"), options->appimage_notifications, &c_options->appimage_notifications);
2231 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"));
2233 pref_spacer(group, PREF_PAD_GROUP);
2237 net_mon = g_network_monitor_get_default();
2238 tz_org = g_network_address_parse_uri(TIMEZONE_DATABASE_WEB, 80, nullptr);
2241 internet_available = g_network_monitor_can_reach(net_mon, tz_org, nullptr, nullptr);
2242 g_object_unref(tz_org);
2245 group = pref_group_new(vbox, FALSE, _("Timezone database"), GTK_ORIENTATION_VERTICAL);
2246 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2248 if (!internet_available)
2250 gtk_widget_set_sensitive(group, FALSE);
2253 tz = g_new0(TZData, 1);
2255 path = path_from_utf8(TIMEZONE_DATABASE_WEB);
2256 basename = g_path_get_basename(path);
2257 tz->timezone_database_user = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
2261 if (isfile(tz->timezone_database_user))
2263 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Update"), G_CALLBACK(timezone_database_install_cb), tz);
2267 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Install"), G_CALLBACK(timezone_database_install_cb), tz);
2270 download_locn = g_strconcat(_("Download database from: "), TIMEZONE_DATABASE_WEB, NULL);
2271 pref_label_new(GTK_WIDGET(hbox), download_locn);
2272 g_free(download_locn);
2274 if (!internet_available)
2276 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"));
2280 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"));
2282 gtk_widget_show(button);
2284 pref_spacer(group, PREF_PAD_GROUP);
2286 group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL);
2288 help_search_engine_entry = gtk_entry_new();
2289 gq_gtk_entry_set_text(GTK_ENTRY(help_search_engine_entry), options->help_search_engine);
2290 gq_gtk_box_pack_start(GTK_BOX(group), help_search_engine_entry, FALSE, FALSE, 0);
2291 gtk_widget_show(help_search_engine_entry);
2293 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"));
2295 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2296 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2297 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2298 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2299 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2300 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2301 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2302 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2303 g_signal_connect(GTK_ENTRY(help_search_engine_entry), "icon-press",
2304 G_CALLBACK(help_search_engine_entry_icon_cb),
2305 help_search_engine_entry);
2309 static void config_tab_image(GtkWidget *notebook)
2314 GtkWidget *ct_button;
2315 GtkWidget *enlargement_button;
2319 vbox = scrolled_notebook_page(notebook, _("Image"));
2321 group = pref_group_new(vbox, FALSE, _("Zoom"), GTK_ORIENTATION_VERTICAL);
2323 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2324 add_quality_menu(table, 0, 0, _("Quality:"), options->image.zoom_quality, &c_options->image.zoom_quality);
2326 pref_checkbox_new_int(group, _("Two pass rendering (apply HQ zoom and color correction in second pass)"),
2327 options->image.zoom_2pass, &c_options->image.zoom_2pass);
2329 c_options->image.zoom_increment = options->image.zoom_increment;
2330 spin = pref_spin_new(group, _("Zoom increment:"), nullptr,
2331 0.01, 4.0, 0.01, 2, static_cast<gdouble>(options->image.zoom_increment) / 100.0,
2332 G_CALLBACK(zoom_increment_cb), nullptr);
2333 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2335 c_options->image.zoom_style = options->image.zoom_style;
2336 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2337 add_zoom_style_selection_menu(table, 0, 0, _("Zoom style:"), options->image.zoom_style, &c_options->image.zoom_style);
2339 group = pref_group_new(vbox, FALSE, _("Fit image to window"), GTK_ORIENTATION_VERTICAL);
2341 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2342 enlargement_button = pref_checkbox_new_int(hbox, _("Allow enlargement of image (max. size in %)"),
2343 options->image.zoom_to_fit_allow_expand, &c_options->image.zoom_to_fit_allow_expand);
2344 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2346 options->image.max_enlargement_size, &c_options->image.max_enlargement_size);
2347 pref_checkbox_link_sensitivity(enlargement_button, spin);
2348 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."));
2350 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2351 ct_button = pref_checkbox_new_int(hbox, _("Virtual window size (%% of actual window):"),
2352 options->image.limit_autofit_size, &c_options->image.limit_autofit_size);
2353 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2355 options->image.max_autofit_size, &c_options->image.max_autofit_size);
2356 pref_checkbox_link_sensitivity(ct_button, spin);
2357 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."));
2359 group = pref_group_new(vbox, FALSE, _("Tile size"), GTK_ORIENTATION_VERTICAL);
2361 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2362 spin = pref_spin_new_int(hbox, _("Pixels"), _("(Requires restart)"),
2364 options->image.tile_size, &c_options->image.tile_size);
2365 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."));
2367 group = pref_group_new(vbox, FALSE, _("Appearance"), GTK_ORIENTATION_VERTICAL);
2369 pref_checkbox_new_int(group, _("Use custom border color in window mode"),
2370 options->image.use_custom_border_color, &c_options->image.use_custom_border_color);
2372 pref_checkbox_new_int(group, _("Use custom border color in fullscreen mode"),
2373 options->image.use_custom_border_color_in_fullscreen, &c_options->image.use_custom_border_color_in_fullscreen);
2375 pref_color_button_new(group, _("Border color"), &options->image.border_color,
2376 G_CALLBACK(pref_color_button_set_cb), &c_options->image.border_color);
2378 c_options->image.border_color = options->image.border_color;
2380 pref_color_button_new(group, _("Alpha channel color 1"), &options->image.alpha_color_1,
2381 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_1);
2383 pref_color_button_new(group, _("Alpha channel color 2"), &options->image.alpha_color_2,
2384 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_2);
2386 c_options->image.alpha_color_1 = options->image.alpha_color_1;
2387 c_options->image.alpha_color_2 = options->image.alpha_color_2;
2392 static void save_default_window_layout_cb(GtkWidget *, gpointer)
2394 LayoutWindow *lw = nullptr;
2395 gchar *default_path;
2398 /* Get current lw */
2401 tmp_id = lw->options.id;
2402 lw->options.id = g_strdup("");
2404 default_path = g_build_filename(get_rc_dir(), DEFAULT_WINDOW_LAYOUT, NULL);
2405 save_default_layout_options_to_file(default_path, options, lw);
2406 g_free(lw->options.id);
2407 lw->options.id = tmp_id;
2408 g_free(default_path);
2411 static gboolean popover_cb(gpointer data)
2413 auto popover = static_cast<GtkPopover *>(data);
2415 gtk_popover_popdown(popover);
2420 static void default_layout_changed_cb(GtkWidget *, GtkPopover *popover)
2422 gtk_popover_popup(popover);
2424 g_timeout_add(2000, popover_cb, popover);
2427 static GtkWidget *create_popover(GtkWidget *parent, GtkWidget *child, GtkPositionType pos)
2431 popover = gtk_popover_new(parent);
2432 gtk_popover_set_position(GTK_POPOVER (popover), pos);
2433 gq_gtk_container_add(GTK_WIDGET(popover), child);
2434 gtk_container_set_border_width(GTK_CONTAINER (popover), 6);
2435 gtk_widget_show (child);
2440 static void config_tab_windows(GtkWidget *notebook)
2445 GtkWidget *subgroup;
2447 GtkWidget *checkbox;
2448 GtkWidget *ct_button;
2451 vbox = scrolled_notebook_page(notebook, _("Windows"));
2453 group = pref_group_new(vbox, FALSE, _("State"), GTK_ORIENTATION_VERTICAL);
2455 ct_button = pref_checkbox_new_int(group, _("Remember session"),
2456 options->save_window_positions, &c_options->save_window_positions);
2458 checkbox = pref_checkbox_new_int(group, _("Use saved window positions also for new windows"),
2459 options->use_saved_window_positions_for_new_windows, &c_options->use_saved_window_positions_for_new_windows);
2460 pref_checkbox_link_sensitivity(ct_button, checkbox);
2462 checkbox = pref_checkbox_new_int(group, _("Remember window workspace"),
2463 options->save_window_workspace, &c_options->save_window_workspace);
2464 pref_checkbox_link_sensitivity(ct_button, checkbox);
2466 pref_checkbox_new_int(group, _("Remember tool state (float/hidden)"),
2467 options->tools_restore_state, &c_options->tools_restore_state);
2469 pref_checkbox_new_int(group, _("Remember dialog window positions"),
2470 options->save_dialog_window_positions, &c_options->save_dialog_window_positions);
2472 pref_checkbox_new_int(group, _("Show window IDs"),
2473 options->show_window_ids, &c_options->show_window_ids);
2475 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2476 pref_label_new(subgroup, _("Use current layout for default: "));
2477 button = pref_button_new(subgroup, nullptr, _("Set"), G_CALLBACK(save_default_window_layout_cb), nullptr);
2481 popover = create_popover(button, gtk_label_new(_("Current window layout\nhas been set as default")), GTK_POS_TOP);
2482 gtk_popover_set_modal(GTK_POPOVER (popover), FALSE);
2483 g_signal_connect(button, "clicked", G_CALLBACK(default_layout_changed_cb), popover);
2485 group = pref_group_new(vbox, FALSE, _("Size"), GTK_ORIENTATION_VERTICAL);
2487 pref_checkbox_new_int(group, _("Fit window to image when tools are hidden/floating"),
2488 options->image.fit_window_to_image, &c_options->image.fit_window_to_image);
2490 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2491 ct_button = pref_checkbox_new_int(hbox, _("Limit size when auto-sizing window (%):"),
2492 options->image.limit_window_size, &c_options->image.limit_window_size);
2493 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2495 options->image.max_window_size, &c_options->image.max_window_size);
2496 pref_checkbox_link_sensitivity(ct_button, spin);
2498 group = pref_group_new(vbox, FALSE, _("Full screen"), GTK_ORIENTATION_VERTICAL);
2500 c_options->fullscreen.screen = options->fullscreen.screen;
2501 c_options->fullscreen.above = options->fullscreen.above;
2502 hbox = fullscreen_prefs_selection_new(_("Location:"), &c_options->fullscreen.screen, &c_options->fullscreen.above);
2503 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2504 gtk_widget_show(hbox);
2506 pref_checkbox_new_int(group, _("Smooth image flip"),
2507 options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
2508 pref_checkbox_new_int(group, _("Disable screen saver"),
2509 options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
2512 static void config_tab_osd(GtkWidget *notebook)
2518 GtkWidget *image_overlay_template_view;
2519 GtkWidget *scrolled;
2520 GtkWidget *scrolled_pre_formatted;
2521 GtkTextBuffer *buffer;
2523 GtkWidget *subgroup;
2525 vbox = scrolled_notebook_page(notebook, _("OSD"));
2527 image_overlay_template_view = gtk_text_view_new();
2529 group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
2531 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2533 scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
2534 gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
2535 gq_gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
2536 gtk_widget_show(scrolled_pre_formatted);
2537 gtk_widget_show(subgroup);
2539 pref_line(group, PREF_PAD_GAP);
2541 pref_label_new(group, _("Image overlay template"));
2543 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2544 gtk_widget_set_size_request(scrolled, 200, 150);
2545 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2546 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
2547 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2548 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
2549 gtk_widget_show(scrolled);
2551 gtk_widget_set_tooltip_markup(image_overlay_template_view,
2552 _("Extensive formatting options are shown in the Help file"));
2554 gq_gtk_container_add(GTK_WIDGET(scrolled), image_overlay_template_view);
2555 gtk_widget_show(image_overlay_template_view);
2557 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2559 button = pref_button_new(nullptr, GQ_ICON_SELECT_FONT, _("Font"),
2560 G_CALLBACK(image_overlay_set_font_cb), notebook);
2562 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2563 gtk_widget_show(button);
2565 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Text"), G_CALLBACK(image_overlay_set_text_color_cb), nullptr);
2566 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2567 gtk_widget_show(button);
2569 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Background"), G_CALLBACK(image_overlay_set_background_color_cb), nullptr);
2570 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2571 gtk_widget_show(button);
2572 image_overlay_set_text_colors();
2574 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2575 G_CALLBACK(image_overlay_default_template_cb), image_overlay_template_view);
2576 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2577 gtk_widget_show(button);
2579 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
2580 G_CALLBACK(image_overlay_help_cb), nullptr);
2581 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2582 gtk_widget_show(button);
2584 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_overlay_template_view));
2585 if (options->image_overlay.template_string) gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
2586 g_signal_connect(G_OBJECT(buffer), "changed",
2587 G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
2589 pref_line(group, PREF_PAD_GAP);
2591 group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL);
2592 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2593 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2594 gtk_widget_show(hbox);
2595 label = gtk_label_new(_("%Exif.Image.Orientation%"));
2596 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2597 gtk_widget_show(label);
2598 pref_spacer(group,TRUE);
2600 group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL);
2601 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2602 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2603 gtk_widget_show(hbox);
2604 label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
2605 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2606 gtk_widget_show(label);
2607 pref_spacer(group,TRUE);
2609 group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL);
2610 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2611 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2612 gtk_widget_show(hbox);
2613 label = gtk_label_new(_("%path:39%"));
2614 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2615 gtk_widget_show(label);
2616 pref_spacer(group,TRUE);
2618 group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL);
2619 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2620 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2621 gtk_widget_show(hbox);
2622 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%"));
2623 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2624 gtk_widget_show(label);
2625 pref_spacer(group,TRUE);
2627 group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL);
2628 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2629 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2630 gtk_widget_show(hbox);
2631 label = gtk_label_new(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
2632 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2633 gtk_widget_show(label);
2636 static GtkTreeModel *create_class_model()
2638 GtkListStore *model;
2642 /* create list store */
2643 model = gtk_list_store_new(1, G_TYPE_STRING);
2644 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
2646 gtk_list_store_append(model, &iter);
2647 gtk_list_store_set(model, &iter, 0, _(format_class_list[i]), -1);
2649 return GTK_TREE_MODEL (model);
2654 static gint filter_table_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
2656 gint n = GPOINTER_TO_INT(data);
2658 FilterEntry *filter_a;
2659 FilterEntry *filter_b;
2661 gtk_tree_model_get(model, a, 0, &filter_a, -1);
2662 gtk_tree_model_get(model, b, 0, &filter_b, -1);
2666 case FILETYPES_COLUMN_ENABLED:
2668 ret = filter_a->enabled - filter_b->enabled;
2671 case FILETYPES_COLUMN_FILTER:
2673 ret = g_utf8_collate(filter_a->extensions, filter_b->extensions);
2676 case FILETYPES_COLUMN_DESCRIPTION:
2678 ret = g_utf8_collate(filter_a->description, filter_b->description);
2681 case FILETYPES_COLUMN_CLASS:
2683 ret = g_strcmp0(format_class_list[filter_a->file_class], format_class_list[filter_b->file_class]);
2686 case FILETYPES_COLUMN_WRITABLE:
2688 ret = filter_a->writable - filter_b->writable;
2691 case FILETYPES_COLUMN_SIDECAR:
2693 ret = filter_a->allow_sidecar - filter_b->allow_sidecar;
2697 g_return_val_if_reached(0);
2703 static gboolean search_function_cb(GtkTreeModel *model, gint, const gchar *key, GtkTreeIter *iter, gpointer)
2706 gboolean ret = TRUE;
2708 gtk_tree_model_get(model, iter, 0, &fe, -1);
2710 if (g_strstr_len(fe->extensions, -1, key))
2718 static void config_tab_files(GtkWidget *notebook)
2725 GtkWidget *ct_button;
2726 GtkWidget *scrolled;
2727 GtkWidget *filter_view;
2728 GtkCellRenderer *renderer;
2729 GtkTreeSelection *selection;
2730 GtkTreeViewColumn *column;
2732 vbox = scrolled_notebook_page(notebook, _("File Filters"));
2734 group = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2736 pref_checkbox_new_int(group, _("Show hidden files or folders"),
2737 options->file_filter.show_hidden_files, &c_options->file_filter.show_hidden_files);
2738 pref_checkbox_new_int(group, _("Show parent folder (..)"),
2739 options->file_filter.show_parent_directory, &c_options->file_filter.show_parent_directory);
2740 pref_checkbox_new_int(group, _("Case sensitive sort (Search and Collection windows, and tab completion)"), options->file_sort.case_sensitive, &c_options->file_sort.case_sensitive);
2741 pref_checkbox_new_int(group, _("Disable file extension checks"),
2742 options->file_filter.disable_file_extension_checks, &c_options->file_filter.disable_file_extension_checks);
2744 ct_button = pref_checkbox_new_int(group, _("Disable File Filtering"),
2745 options->file_filter.disable, &c_options->file_filter.disable);
2748 group = pref_group_new(vbox, FALSE, _("Grouping sidecar extensions"), GTK_ORIENTATION_VERTICAL);
2750 sidecar_ext_entry = gtk_entry_new();
2751 gq_gtk_entry_set_text(GTK_ENTRY(sidecar_ext_entry), options->sidecar.ext);
2752 gq_gtk_box_pack_start(GTK_BOX(group), sidecar_ext_entry, FALSE, FALSE, 0);
2753 gtk_widget_show(sidecar_ext_entry);
2755 group = pref_group_new(vbox, TRUE, _("File types"), GTK_ORIENTATION_VERTICAL);
2757 frame = pref_group_parent(group);
2758 g_signal_connect(G_OBJECT(ct_button), "toggled",
2759 G_CALLBACK(filter_disable_cb), frame);
2760 gtk_widget_set_sensitive(frame, !options->file_filter.disable);
2762 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2763 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2764 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2765 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2766 gtk_widget_show(scrolled);
2768 filter_store = gtk_list_store_new(1, G_TYPE_POINTER);
2769 filter_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
2770 g_object_unref(filter_store);
2771 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
2772 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2774 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), FALSE);
2776 column = gtk_tree_view_column_new();
2777 gtk_tree_view_column_set_title(column, _("Enabled"));
2778 gtk_tree_view_column_set_resizable(column, TRUE);
2780 renderer = gtk_cell_renderer_toggle_new();
2781 g_signal_connect(G_OBJECT(renderer), "toggled",
2782 G_CALLBACK(filter_store_enable_cb), filter_store);
2783 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2784 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2785 GINT_TO_POINTER(FE_ENABLE), nullptr);
2786 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);
2787 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_ENABLED);
2788 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2790 column = gtk_tree_view_column_new();
2791 gtk_tree_view_column_set_title(column, _("Filter"));
2792 gtk_tree_view_column_set_resizable(column, TRUE);
2793 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);
2794 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_FILTER);
2796 renderer = gtk_cell_renderer_text_new();
2797 g_signal_connect(G_OBJECT(renderer), "edited",
2798 G_CALLBACK(filter_store_ext_edit_cb), filter_store);
2799 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2800 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2801 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2802 GINT_TO_POINTER(FE_EXTENSION), nullptr);
2803 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2805 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), TRUE);
2806 gtk_tree_view_set_search_column(GTK_TREE_VIEW(filter_view), FILETYPES_COLUMN_FILTER);
2807 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(filter_view), search_function_cb, nullptr, nullptr);
2809 column = gtk_tree_view_column_new();
2810 gtk_tree_view_column_set_title(column, _("Description"));
2811 gtk_tree_view_column_set_resizable(column, TRUE);
2812 gtk_tree_view_column_set_fixed_width(column, 200);
2813 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2815 renderer = gtk_cell_renderer_text_new();
2816 g_signal_connect(G_OBJECT(renderer), "edited",
2817 G_CALLBACK(filter_store_desc_edit_cb), filter_store);
2818 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2819 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2820 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2821 GINT_TO_POINTER(FE_DESCRIPTION), nullptr);
2822 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2823 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);
2824 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_DESCRIPTION);
2826 column = gtk_tree_view_column_new();
2827 gtk_tree_view_column_set_title(column, _("Class"));
2828 gtk_tree_view_column_set_resizable(column, TRUE);
2829 renderer = gtk_cell_renderer_combo_new();
2830 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE,
2831 "model", create_class_model(),
2836 g_signal_connect(G_OBJECT(renderer), "edited",
2837 G_CALLBACK(filter_store_class_edit_cb), filter_store);
2838 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2839 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2840 GINT_TO_POINTER(FE_CLASS), nullptr);
2841 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2842 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);
2843 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_CLASS);
2845 column = gtk_tree_view_column_new();
2846 gtk_tree_view_column_set_title(column, _("Writable"));
2847 gtk_tree_view_column_set_resizable(column, FALSE);
2848 renderer = gtk_cell_renderer_toggle_new();
2849 g_signal_connect(G_OBJECT(renderer), "toggled",
2850 G_CALLBACK(filter_store_writable_cb), filter_store);
2851 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2852 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2853 GINT_TO_POINTER(FE_WRITABLE), nullptr);
2854 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2855 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);
2856 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_WRITABLE);
2858 column = gtk_tree_view_column_new();
2859 gtk_tree_view_column_set_title(column, _("Sidecar is allowed"));
2860 gtk_tree_view_column_set_resizable(column, FALSE);
2861 renderer = gtk_cell_renderer_toggle_new();
2862 g_signal_connect(G_OBJECT(renderer), "toggled",
2863 G_CALLBACK(filter_store_sidecar_cb), filter_store);
2864 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2865 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2866 GINT_TO_POINTER(FE_ALLOW_SIDECAR), nullptr);
2867 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2868 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);
2869 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_SIDECAR);
2871 filter_store_populate();
2872 gq_gtk_container_add(GTK_WIDGET(scrolled), filter_view);
2873 gtk_widget_show(filter_view);
2875 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2877 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2878 G_CALLBACK(filter_default_cb), filter_view);
2879 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2880 gtk_widget_show(button);
2882 button = pref_button_new(nullptr, GQ_ICON_REMOVE, _("Remove"),
2883 G_CALLBACK(filter_remove_cb), filter_view);
2884 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2885 gtk_widget_show(button);
2887 button = pref_button_new(nullptr, GQ_ICON_ADD, _("Add"),
2888 G_CALLBACK(filter_add_cb), filter_view);
2889 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2890 gtk_widget_show(button);
2894 static void config_tab_metadata(GtkWidget *notebook)
2899 GtkWidget *ct_button;
2901 GtkWidget *tmp_widget;
2903 GtkWidget *text_label;
2905 vbox = scrolled_notebook_page(notebook, _("Metadata"));
2908 group = pref_group_new(vbox, FALSE, _("Metadata writing sequence"), GTK_ORIENTATION_VERTICAL);
2910 label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2912 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."));
2913 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2914 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2916 gtk_widget_set_tooltip_text(label, _("A flowchart of the sequence is shown in the Help file"));
2918 ct_button = pref_checkbox_new_int(group, "", options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2919 text_label = gtk_bin_get_child(GTK_BIN(ct_button));
2920 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"));
2921 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2923 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)"));
2924 gtk_widget_set_tooltip_markup(ct_button, markup);
2928 gtk_widget_set_sensitive(ct_button, FALSE);
2931 tmp_widget = pref_checkbox_new_int(group, "", options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2932 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2933 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)"));
2934 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2937 label = pref_label_new(group, "");
2938 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(), "/");
2939 gtk_label_set_markup (GTK_LABEL(label), markup);
2942 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2943 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2944 gtk_widget_set_margin_start(label, 22);
2945 pref_spacer(group, PREF_PAD_GROUP);
2947 group = pref_group_new(vbox, FALSE, _("Step 1 Options:"), GTK_ORIENTATION_VERTICAL);
2949 gtk_widget_set_sensitive(group, FALSE);
2952 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2953 pref_checkbox_link_sensitivity(ct_button, hbox);
2955 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);
2956 gtk_widget_set_tooltip_text(tmp_widget, _("A simplified conversion list is in the Help file"));
2958 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);
2960 pref_checkbox_new_int(hbox, _("Ask before writing to image files"), options->metadata.confirm_write, &c_options->metadata.confirm_write);
2962 tmp_widget= pref_checkbox_new_int(hbox, "", options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2963 gtk_widget_set_tooltip_text(tmp_widget, _("This file naming convention is used by Darktable"));
2964 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2965 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", ")");
2966 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2969 pref_spacer(group, PREF_PAD_GROUP);
2971 group = pref_group_new(vbox, FALSE, _("Steps 2 and 3 Option:"), GTK_ORIENTATION_VERTICAL);
2973 gtk_widget_set_sensitive(group, FALSE);
2976 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);
2978 pref_spacer(group, PREF_PAD_GROUP);
2980 group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2981 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);
2982 gtk_widget_set_tooltip_text(tmp_widget, _("See the Help file for a list of the tags used"));
2984 tmp_widget = pref_checkbox_new_int(group, _("Permit Keywords to be case-sensitive"), options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2985 gtk_widget_set_tooltip_text(tmp_widget, _("When selected, \"Place\" and \"place\" are two different keywords"));
2987 ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"), options->metadata.write_orientation, &c_options->metadata.write_orientation);
2988 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"));
2991 gtk_widget_set_sensitive(ct_button, FALSE);
2994 pref_spacer(group, PREF_PAD_GROUP);
2996 group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2998 ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"), options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
3000 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3001 pref_checkbox_link_sensitivity(ct_button, hbox);
3003 pref_spin_new_int(hbox, _("Timeout (seconds):"), nullptr, 0, 900, 1, options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
3005 pref_checkbox_new_int(group, _("Write metadata on image change"), options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
3007 pref_checkbox_new_int(group, _("Write metadata on directory change"), options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
3009 pref_spacer(group, PREF_PAD_GROUP);
3012 group = pref_group_new(vbox, FALSE, _("Spelling checks"), GTK_ORIENTATION_VERTICAL);
3014 ct_button = pref_checkbox_new_int(group, _("Check spelling - Requires restart"), options->metadata.check_spelling, &c_options->metadata.check_spelling);
3015 gtk_widget_set_tooltip_text(ct_button, _("Spelling checks are performed on info sidebar panes Comment, Headline and Title"));
3018 pref_spacer(group, PREF_PAD_GROUP);
3020 group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
3022 ct_button = pref_checkbox_new_int(group, _("Read metadata in background"), options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
3023 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");
3028 struct KeywordFindData
3035 GtkWidget *button_close;
3036 GtkWidget *button_stop;
3037 GtkWidget *button_start;
3038 GtkWidget *progress;
3046 guint idle_id; /* event source id */
3049 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
3051 GList *list_d = nullptr;
3052 GList *list_f = nullptr;
3056 filelist_read(dir_fd, &list_f, &list_d);
3060 filelist_read(dir_fd, &list_f, nullptr);
3063 list_f = filelist_filter(list_f, FALSE);
3064 list_d = filelist_filter(list_d, TRUE);
3066 kfd->list = g_list_concat(list_f, kfd->list);
3067 kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
3070 static void keywords_find_reset(KeywordFindData *kfd)
3072 filelist_free(kfd->list);
3073 kfd->list = nullptr;
3075 filelist_free(kfd->list_dir);
3076 kfd->list_dir = nullptr;
3079 static void keywords_find_close_cb(GenericDialog *, gpointer data)
3081 auto kfd = static_cast<KeywordFindData *>(data);
3083 if (!gtk_widget_get_sensitive(kfd->button_close)) return;
3085 keywords_find_reset(kfd);
3086 generic_dialog_close(kfd->gd);
3090 static void keywords_find_finish(KeywordFindData *kfd)
3092 keywords_find_reset(kfd);
3094 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
3095 gtk_spinner_stop(GTK_SPINNER(kfd->spinner));
3097 gtk_widget_set_sensitive(kfd->group, TRUE);
3098 gtk_widget_set_sensitive(kfd->button_start, TRUE);
3099 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3100 gtk_widget_set_sensitive(kfd->button_close, TRUE);
3103 static void keywords_find_stop_cb(GenericDialog *, gpointer data)
3105 auto kfd = static_cast<KeywordFindData *>(data);
3107 g_idle_remove_by_data(kfd);
3109 keywords_find_finish(kfd);
3112 static gboolean keywords_find_file(gpointer data)
3114 auto kfd = static_cast<KeywordFindData *>(data);
3116 GtkTextBuffer *buffer;
3124 fd = static_cast<FileData *>(kfd->list->data);
3125 kfd->list = g_list_remove(kfd->list, fd);
3127 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
3128 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3132 gtk_text_buffer_get_end_iter(buffer, &iter);
3133 tmp = g_strconcat(static_cast<const gchar *>(keywords->data), "\n", NULL);
3134 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3136 keywords = keywords->next;
3139 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
3140 file_data_unref(fd);
3141 g_list_free_full(keywords, g_free);
3143 return (G_SOURCE_CONTINUE);
3150 fd = static_cast<FileData *>(kfd->list_dir->data);
3151 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
3153 keywords_find_folder(kfd, fd);
3155 file_data_unref(fd);
3157 return G_SOURCE_CONTINUE;
3160 keywords_find_finish(kfd);
3162 return G_SOURCE_REMOVE;
3165 static void keywords_find_start_cb(GenericDialog *, gpointer data)
3167 auto kfd = static_cast<KeywordFindData *>(data);
3170 if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
3172 path = remove_trailing_slash((gq_gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
3173 parse_out_relatives(path);
3177 warning_dialog(_("Invalid folder"),
3178 _("The specified folder can not be found."),
3179 GQ_ICON_DIALOG_WARNING, kfd->gd->dialog);
3185 gtk_widget_set_sensitive(kfd->group, FALSE);
3186 gtk_widget_set_sensitive(kfd->button_start, FALSE);
3187 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
3188 gtk_widget_set_sensitive(kfd->button_close, FALSE);
3189 gtk_spinner_start(GTK_SPINNER(kfd->spinner));
3191 dir_fd = file_data_new_dir(path);
3192 keywords_find_folder(kfd, dir_fd);
3193 file_data_unref(dir_fd);
3194 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
3200 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
3202 KeywordFindData *kfd;
3206 kfd = g_new0(KeywordFindData, 1);
3208 kfd->gd = generic_dialog_new(_("Search for keywords"),
3209 "search_for_keywords",
3212 gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
3213 kfd->gd->cancel_cb = keywords_find_close_cb;
3214 kfd->button_close = generic_dialog_add_button(kfd->gd, GQ_ICON_CLOSE, _("Close"),
3215 keywords_find_close_cb, FALSE);
3216 kfd->button_start = generic_dialog_add_button(kfd->gd, GQ_ICON_OK, _("S_tart"),
3217 keywords_find_start_cb, FALSE);
3218 kfd->button_stop = generic_dialog_add_button(kfd->gd, GQ_ICON_STOP, _("Stop"),
3219 keywords_find_stop_cb, FALSE);
3220 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3222 generic_dialog_add_message(kfd->gd, nullptr, _("Search for keywords"), nullptr, FALSE);
3224 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3225 pref_spacer(hbox, PREF_PAD_INDENT);
3226 kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3228 hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3229 pref_label_new(hbox, _("Folder:"));
3231 label = tab_completion_new(&kfd->entry, path, nullptr, nullptr, nullptr, nullptr);
3232 tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
3233 gq_gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3234 gtk_widget_show(label);
3236 pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
3238 pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
3239 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3241 kfd->progress = gtk_entry_new();
3242 gtk_widget_set_can_focus(kfd->progress, FALSE);
3243 gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
3244 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
3245 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
3246 gtk_widget_show(kfd->progress);
3248 kfd->spinner = gtk_spinner_new();
3249 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
3250 gtk_widget_show(kfd->spinner);
3252 kfd->list = nullptr;
3254 gtk_widget_show(kfd->gd->dialog);
3257 static void keywords_find_cb(GtkWidget *widget, gpointer)
3259 const gchar *path = layout_get_path(nullptr);
3261 if (!path || !*path) path = homedir();
3262 keywords_find_dialog(widget, path);
3265 static void config_tab_keywords_save()
3269 GtkTextBuffer *buffer;
3270 GList *kw_list = nullptr;
3276 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3277 gtk_text_buffer_get_bounds(buffer, &start, &end);
3279 buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3281 kw_split = strtok(buffer_text, "\n");
3282 while (kw_split != nullptr)
3288 if (g_strcmp0(static_cast<const gchar *>(work->data), kw_split) == 0)
3297 kw_list = g_list_append(kw_list, g_strdup(kw_split));
3299 kw_split = strtok(nullptr, "\n");
3302 keyword_list_set(kw_list);
3304 g_list_free_full(kw_list, g_free);
3305 g_free(buffer_text);
3308 static void config_tab_keywords(GtkWidget *notebook)
3314 GtkWidget *scrolled;
3316 GtkTextBuffer *buffer;
3319 GspellTextView *gspell_view;
3322 vbox = scrolled_notebook_page(notebook, _("Keywords"));
3324 group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
3326 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3328 button = pref_button_new(hbox, GQ_ICON_RUN, _("Search"),
3329 G_CALLBACK(keywords_find_cb), keyword_text);
3330 gtk_widget_set_tooltip_text(button, _("Search for existing keywords"));
3333 keyword_text = gtk_text_view_new();
3334 gtk_widget_set_size_request(keyword_text, 20, 20);
3335 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3336 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3337 gtk_widget_show(scrolled);
3340 if (options->metadata.check_spelling)
3342 gspell_view = gspell_text_view_get_from_gtk_text_view(GTK_TEXT_VIEW(keyword_text));
3343 gspell_text_view_basic_setup(gspell_view);
3347 gq_gtk_container_add(GTK_WIDGET(scrolled), keyword_text);
3348 gtk_widget_show(keyword_text);
3350 gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
3352 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3353 gtk_text_buffer_create_tag(buffer, "monospace",
3354 "family", "monospace", NULL);
3356 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3357 gtk_text_buffer_get_start_iter(buffer, &iter);
3358 gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3361 path = g_build_filename(get_rc_dir(), "keywords", NULL);
3363 GList *kwl = keyword_list_get();
3364 kwl = g_list_first(kwl);
3367 gtk_text_buffer_get_end_iter (buffer, &iter);
3368 tmp = g_strconcat(static_cast<const gchar *>(kwl->data), "\n", NULL);
3369 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3374 gtk_text_buffer_set_modified(buffer, FALSE);
3381 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3383 auto option = static_cast<gint *>(data);
3385 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3389 *option = INTENT_PERCEPTUAL;
3392 *option = INTENT_RELATIVE_COLORIMETRIC;
3395 *option = INTENT_SATURATION;
3398 *option = INTENT_ABSOLUTE_COLORIMETRIC;
3403 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3404 gint option, gint *option_c)
3411 pref_table_label(table, column, row, text, GTK_ALIGN_START);
3413 combo = gtk_combo_box_text_new();
3415 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3416 if (option == INTENT_PERCEPTUAL) current = 0;
3417 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3418 if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3419 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3420 if (option == INTENT_SATURATION) current = 2;
3421 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3422 if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3424 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3426 gtk_widget_set_tooltip_text(combo,_("Refer to the lcms documentation for the defaults used when the selected Intent is not available"));
3428 g_signal_connect(G_OBJECT(combo), "changed",
3429 G_CALLBACK(intent_menu_cb), option_c);
3431 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
3432 gtk_widget_show(combo);
3436 static void config_tab_color(GtkWidget *notebook)
3445 vbox = scrolled_notebook_page(notebook, _("Color management"));
3447 group = pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3449 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3452 table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3453 gtk_grid_set_column_spacing(GTK_GRID(table), PREF_PAD_GAP);
3455 label = pref_table_label(table, 0, 0, _("Type"), GTK_ALIGN_START);
3456 pref_label_bold(label, TRUE, FALSE);
3458 label = pref_table_label(table, 1, 0, _("Menu name"), GTK_ALIGN_START);
3459 pref_label_bold(label, TRUE, FALSE);
3461 label = pref_table_label(table, 2, 0, _("File"), GTK_ALIGN_START);
3462 pref_label_bold(label, TRUE, FALSE);
3464 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3469 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3470 pref_table_label(table, 0, i + 1, buf, GTK_ALIGN_END);
3473 entry = gtk_entry_new();
3474 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3475 if (options->color_profile.input_name[i])
3477 gq_gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3479 gq_gtk_grid_attach(GTK_GRID(table), entry, 1, 2, i + 1, i + 2, static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3480 gtk_widget_show(entry);
3481 color_profile_input_name_entry[i] = entry;
3483 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], nullptr, ".icc", "ICC Files", nullptr);
3484 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3485 gtk_widget_set_size_request(entry, 160, -1);
3486 gq_gtk_grid_attach(GTK_GRID(table), tabcomp, 2, 3, i + 1, i + 2, static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3487 gtk_widget_show(tabcomp);
3488 color_profile_input_file_entry[i] = entry;
3491 group = pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3493 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3495 pref_checkbox_new_int(group, _("Use system screen profile if available"),
3496 options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3498 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3500 pref_table_label(table, 0, 0, _("Screen:"), GTK_ALIGN_END);
3501 tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3502 options->color_profile.screen_file, nullptr, ".icc", "ICC Files", nullptr);
3503 tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3504 gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3506 add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3508 gq_gtk_grid_attach(GTK_GRID(table), tabcomp, 1, 2, 0, 1, static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3510 gtk_widget_show(tabcomp);
3513 /* advanced entry tab */
3514 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer)
3516 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3518 c_options->file_ops.use_system_trash = FALSE;
3519 c_options->file_ops.no_trash = FALSE;
3523 static void use_system_trash_cb(GtkWidget *widget, gpointer)
3525 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3527 c_options->file_ops.use_system_trash = TRUE;
3528 c_options->file_ops.no_trash = FALSE;
3532 static void use_no_cache_cb(GtkWidget *widget, gpointer)
3534 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3536 c_options->file_ops.no_trash = TRUE;
3540 static void config_tab_behavior(GtkWidget *notebook)
3547 GtkWidget *ct_button;
3551 GtkWidget *with_rename;
3552 GtkWidget *collections_on_top;
3553 GtkWidget *hide_window_in_fullscreen;
3554 GtkWidget *checkbox;
3557 vbox = scrolled_notebook_page(notebook, _("Behavior"));
3559 group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3561 pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3562 options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3563 pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3564 options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3565 pref_checkbox_new_int(group, _("Enable Delete key"),
3566 options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3568 ct_button = pref_radiobutton_new(group, nullptr, _("Use Geeqie trash location"),
3569 !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),nullptr);
3571 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3572 pref_checkbox_link_sensitivity(ct_button, hbox);
3574 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3575 pref_label_new(hbox, _("Folder:"));
3577 tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, nullptr, nullptr, nullptr, nullptr);
3578 tab_completion_add_select_button(safe_delete_path_entry, nullptr, TRUE);
3579 gq_gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3580 gtk_widget_show(tabcomp);
3582 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3583 pref_checkbox_link_sensitivity(ct_button, hbox);
3585 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3586 spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MiB"),
3587 0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3588 gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3589 button = pref_button_new(nullptr, nullptr, _("View"),
3590 G_CALLBACK(safe_delete_view_cb), nullptr);
3591 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3592 gtk_widget_show(button);
3594 button = pref_button_new(nullptr, GQ_ICON_CLEAR, nullptr,
3595 G_CALLBACK(safe_delete_clear_cb), nullptr);
3596 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3598 c_options->file_ops.no_trash = options->file_ops.no_trash;
3599 c_options->file_ops.use_system_trash = options->file_ops.use_system_trash;
3601 pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3602 options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), nullptr);
3604 pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3605 options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), nullptr);
3607 gtk_widget_show(button);
3609 pref_spacer(group, PREF_PAD_GROUP);
3612 group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3614 pref_checkbox_new_int(group, _("Descend folders in tree view"),
3615 options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3617 pref_checkbox_new_int(group, _("In place renaming"),
3618 options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3620 pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3621 options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3623 tmp = pref_checkbox_new_int(group, _("Circular selection lists"),
3624 options->circular_selection_lists, &c_options->circular_selection_lists);
3625 gtk_widget_set_tooltip_text(tmp, _("Traverse selection lists in a circular manner"));
3627 marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3628 options->marks_save, &c_options->marks_save);
3629 gtk_widget_set_tooltip_text(marks,_("Note that marks linked to a keyword will be saved irrespective of this setting"));
3631 with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3632 options->with_rename, &c_options->with_rename);
3633 gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3635 collections_on_top = pref_checkbox_new_int(group, _("Permit duplicates in Collections"),
3636 options->collections_duplicates, &c_options->collections_duplicates);
3637 gtk_widget_set_tooltip_text(collections_on_top, _("Allow the same image to be in a Collection more than once"));
3639 collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3640 options->collections_on_top, &c_options->collections_on_top);
3641 gtk_widget_set_tooltip_text(collections_on_top, _("Open collections window on top"));
3643 hide_window_in_fullscreen = pref_checkbox_new_int(group, _("Hide window in fullscreen"),
3644 options->hide_window_in_fullscreen, &c_options->hide_window_in_fullscreen);
3645 gtk_widget_set_tooltip_text(hide_window_in_fullscreen,"When alt-tabbing, prevent Geeqie window showing twice");
3647 pref_spin_new_int(group, _("Recent folder list maximum size"), nullptr,
3648 1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3650 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);
3651 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."));
3653 pref_spin_new_int(group, _("Drag'n drop icon size"), nullptr,
3654 16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3656 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3657 add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
3659 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3660 add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3662 pref_spacer(group, PREF_PAD_GROUP);
3664 group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3666 pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3667 options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3668 pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), nullptr,
3669 1, 32, 1, options->keyboard_scroll_step, reinterpret_cast<int *>(&c_options->keyboard_scroll_step));
3670 pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3671 options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3672 pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3673 options->image_lm_click_nav, &c_options->image_lm_click_nav);
3674 pref_checkbox_new_int(group, _("Open archive by left click on image"),
3675 options->image_l_click_archive, &c_options->image_l_click_archive);
3676 pref_checkbox_new_int(group, _("Play video by left click on image"),
3677 options->image_l_click_video, &c_options->image_l_click_video);
3678 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3679 add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3681 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3682 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3683 add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3684 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3685 add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3687 pref_spacer(group, PREF_PAD_GROUP);
3689 group = pref_group_new(vbox, FALSE, _("GPU"), GTK_ORIENTATION_VERTICAL);
3691 checkbox = pref_checkbox_new_int(group, _("Override disable GPU"),
3692 options->override_disable_gpu, &c_options->override_disable_gpu);
3693 gtk_widget_set_tooltip_text(checkbox, _("Contact the developers for usage"));
3696 pref_spacer(group, PREF_PAD_GROUP);
3698 group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3700 pref_spin_new_int(group, _("Debug level:"), nullptr,
3701 DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3703 pref_checkbox_new_int(group, _("Timer data"),
3704 options->log_window.timer_data, &c_options->log_window.timer_data);
3706 pref_spin_new_int(group, _("Log Window max. lines:"), nullptr,
3707 1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3709 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3710 pref_label_new(hbox, _("Log Window F1 command: "));
3711 log_window_f1_entry = gtk_entry_new();
3712 gq_gtk_entry_set_text(GTK_ENTRY(log_window_f1_entry), options->log_window.action);
3713 gq_gtk_box_pack_start(GTK_BOX(hbox), log_window_f1_entry, FALSE, FALSE, 0);
3714 gtk_entry_set_width_chars(GTK_ENTRY(log_window_f1_entry), 15);
3715 gtk_widget_show(log_window_f1_entry);
3719 /* accelerators tab */
3721 static gboolean accel_search_function_cb(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer)
3723 gboolean ret = TRUE;
3728 gtk_tree_model_get(model, iter, column, &text, -1);
3729 text_nocase = g_utf8_casefold(text, -1);
3730 key_nocase = g_utf8_casefold(key, -1);
3732 if (g_strstr_len(text_nocase, -1, key_nocase))
3739 g_free(text_nocase);
3744 static void accel_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *, GtkTreeViewColumn *column, gpointer)
3749 list = gtk_tree_view_get_columns(tree_view);
3750 col_num = g_list_index(list, column);
3754 gtk_tree_view_set_search_column(tree_view, col_num);
3757 static void config_tab_accelerators(GtkWidget *notebook)
3763 GtkWidget *scrolled;
3764 GtkWidget *accel_view;
3765 GtkCellRenderer *renderer;
3766 GtkTreeSelection *selection;
3767 GtkTreeViewColumn *column;
3769 vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3771 group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3773 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3774 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3775 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3776 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3777 gtk_widget_show(scrolled);
3779 accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3781 accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3782 g_object_unref(accel_store);
3783 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3784 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3786 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3788 renderer = gtk_cell_renderer_text_new();
3790 column = gtk_tree_view_column_new_with_attributes(_("Action"),
3795 gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3796 gtk_tree_view_column_set_resizable(column, TRUE);
3797 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3800 renderer = gtk_cell_renderer_accel_new();
3801 g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3802 G_CALLBACK(accel_store_cleared_cb), accel_store);
3803 g_signal_connect(G_OBJECT(renderer), "accel-edited",
3804 G_CALLBACK(accel_store_edited_cb), accel_store);
3807 g_object_set (renderer,
3809 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3812 column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3817 gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3818 gtk_tree_view_column_set_resizable(column, TRUE);
3819 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3821 renderer = gtk_cell_renderer_text_new();
3823 column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3828 gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3829 gtk_tree_view_column_set_resizable(column, TRUE);
3830 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3832 renderer = gtk_cell_renderer_text_new();
3834 column = gtk_tree_view_column_new_with_attributes("Accel",
3839 gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3840 gtk_tree_view_column_set_resizable(column, TRUE);
3841 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3843 /* Search on text in column */
3844 gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(accel_view), TRUE);
3845 g_signal_connect(accel_view, "row_activated", G_CALLBACK(accel_row_activated_cb), accel_store);
3846 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), TRUE);
3847 gtk_tree_view_set_search_column(GTK_TREE_VIEW(accel_view), AE_TOOLTIP);
3848 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(accel_view), accel_search_function_cb, nullptr, nullptr);
3850 accel_store_populate();
3851 gq_gtk_container_add(GTK_WIDGET(scrolled), accel_view);
3852 gtk_widget_show(accel_view);
3854 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3856 button = pref_button_new(nullptr, nullptr, _("Defaults"),
3857 G_CALLBACK(accel_default_cb), accel_view);
3858 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3859 gtk_widget_show(button);
3861 button = pref_button_new(nullptr, nullptr, _("Reset selected"),
3862 G_CALLBACK(accel_reset_cb), accel_view);
3863 gtk_widget_set_tooltip_text(button, _("Will only reset changes made before the settings are saved"));
3864 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3865 gtk_widget_show(button);
3867 button = pref_button_new(nullptr, nullptr, _("Clear selected"),
3868 G_CALLBACK(accel_clear_cb), accel_view);
3869 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3870 gtk_widget_show(button);
3873 /* toolbar main tab */
3874 static void config_tab_toolbar_main(GtkWidget *notebook)
3877 GtkWidget *toolbardata;
3880 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3882 vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3884 toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3885 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3886 gtk_widget_show(vbox);
3889 /* toolbar status tab */
3890 static void config_tab_toolbar_status(GtkWidget *notebook)
3893 GtkWidget *toolbardata;
3896 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3898 vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3900 toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3901 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3902 gtk_widget_show(vbox);
3906 static gint extension_sort_cb(gconstpointer a, gconstpointer b)
3908 return g_strcmp0(static_cast<const gchar *>(a), static_cast<const gchar *>(b));
3911 static void config_tab_advanced(GtkWidget *notebook)
3914 GdkPixbufFormat *fm;
3916 GList *extensions_list = nullptr;
3917 GSList *formats_list;
3918 GString *types_string = g_string_new(nullptr);
3919 GtkWidget *alternate_checkbox;
3920 GtkWidget *dupes_threads_spin;
3922 GtkWidget *subgroup;
3924 GtkWidget *threads_string_label;
3925 GtkWidget *types_string_label;
3928 vbox = scrolled_notebook_page(notebook, _("Advanced"));
3929 group = pref_group_new(vbox, FALSE, _("External preview extraction"), GTK_ORIENTATION_VERTICAL);
3931 pref_checkbox_new_int(group, _("Use external preview extraction - Requires restart"), options->external_preview.enable, &c_options->external_preview.enable);
3933 pref_spacer(group, PREF_PAD_GROUP);
3935 formats_list = gdk_pixbuf_get_formats();
3937 while (formats_list)
3939 fm = static_cast<GdkPixbufFormat *>(formats_list->data);
3940 extensions = gdk_pixbuf_format_get_extensions(fm);
3943 while (extensions[i])
3945 extensions_list = g_list_insert_sorted(extensions_list, g_strdup(extensions[i]), extension_sort_cb);
3949 g_strfreev(extensions);
3950 formats_list = formats_list->next;
3953 while (extensions_list)
3955 if (types_string->len == 0)
3957 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3961 types_string = g_string_append(types_string, ", ");
3962 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3965 extensions_list = extensions_list->next;
3968 types_string = g_string_prepend(types_string, _("Usable file types:\n"));
3969 types_string_label = pref_label_new(group, types_string->str);
3970 gtk_label_set_line_wrap(GTK_LABEL(types_string_label), TRUE);
3972 pref_spacer(group, PREF_PAD_GROUP);
3974 group = pref_group_new(vbox, FALSE, _("File identification tool"), GTK_ORIENTATION_VERTICAL);
3975 external_preview_select_entry = gtk_entry_new();
3976 tabcomp = tab_completion_new(&external_preview_select_entry, options->external_preview.select, nullptr, nullptr, nullptr, nullptr);
3977 tab_completion_add_select_button(external_preview_select_entry, _("Select file identification tool"), FALSE);
3978 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3979 gtk_widget_show(tabcomp);
3981 group = pref_group_new(vbox, FALSE, _("Preview extraction tool"), GTK_ORIENTATION_VERTICAL);
3982 external_preview_extract_entry = gtk_entry_new();
3983 tabcomp = tab_completion_new(&external_preview_extract_entry, options->external_preview.extract, nullptr, nullptr, nullptr, nullptr);
3984 tab_completion_add_select_button(external_preview_extract_entry, _("Select preview extraction tool"), FALSE);
3985 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3986 gtk_widget_show(tabcomp);
3988 gtk_widget_show(vbox);
3990 g_slist_free(formats_list);
3991 g_list_free_full(extensions_list, g_free);
3992 g_string_free(types_string, TRUE);
3994 pref_spacer(group, PREF_PAD_GROUP);
3996 pref_line(vbox, PREF_PAD_SPACE);
3997 group = pref_group_new(vbox, FALSE, _("Thread pool limits"), GTK_ORIENTATION_VERTICAL);
3999 threads_string_label = pref_label_new(group, _("This option limits the number of threads (or cpu cores) that Geeqie will use when running duplicate checks.\nThe value 0 means all available cores will be used."));
4000 gtk_label_set_line_wrap(GTK_LABEL(threads_string_label), TRUE);
4002 pref_spacer(vbox, PREF_PAD_GROUP);
4004 dupes_threads_spin = pref_spin_new_int(vbox, _("Duplicate check:"), _("max. threads"), 0, get_cpu_cores(), 1, options->threads.duplicates, &c_options->threads.duplicates);
4005 gtk_widget_set_tooltip_markup(dupes_threads_spin, _("Set to 0 for unlimited"));
4007 pref_spacer(group, PREF_PAD_GROUP);
4009 pref_line(vbox, PREF_PAD_SPACE);
4011 group = pref_group_new(vbox, FALSE, _("Alternate similarity alogorithm"), GTK_ORIENTATION_VERTICAL);
4013 alternate_checkbox = pref_checkbox_new_int(group, _("Enable alternate similarity algorithm"), options->alternate_similarity_algorithm.enabled, &c_options->alternate_similarity_algorithm.enabled);
4015 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4016 pref_checkbox_link_sensitivity(alternate_checkbox, subgroup);
4018 alternate_checkbox = pref_checkbox_new_int(subgroup, _("Use grayscale"), options->alternate_similarity_algorithm.grayscale, &c_options->alternate_similarity_algorithm.grayscale);
4019 gtk_widget_set_tooltip_text(alternate_checkbox, _("Reduce fingerprint to grayscale"));
4023 static void config_tab_stereo(GtkWidget *notebook)
4031 GtkWidget *fs_button;
4032 vbox = scrolled_notebook_page(notebook, _("Stereo"));
4034 group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
4036 table = pref_table_new(group, 2, 1, FALSE, FALSE);
4037 add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
4039 table = pref_table_new(group, 2, 2, TRUE, FALSE);
4040 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4041 pref_checkbox_new_int(box, _("Mirror left image"),
4042 options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
4043 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4044 pref_checkbox_new_int(box, _("Flip left image"),
4045 options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
4046 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4047 pref_checkbox_new_int(box, _("Mirror right image"),
4048 options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
4049 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4050 pref_checkbox_new_int(box, _("Flip right image"),
4051 options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
4052 pref_checkbox_new_int(group, _("Swap left and right images"),
4053 options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
4054 pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
4055 options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
4057 group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
4058 fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
4059 options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
4060 box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4061 pref_checkbox_link_sensitivity(fs_button, box2);
4062 table = pref_table_new(box2, 2, 1, FALSE, FALSE);
4063 add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
4064 table = pref_table_new(box2, 2, 2, TRUE, FALSE);
4065 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4066 pref_checkbox_new_int(box, _("Mirror left image"),
4067 options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
4068 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4069 pref_checkbox_new_int(box, _("Flip left image"),
4070 options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
4071 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4072 pref_checkbox_new_int(box, _("Mirror right image"),
4073 options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
4074 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4075 pref_checkbox_new_int(box, _("Flip right image"),
4076 options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
4077 pref_checkbox_new_int(box2, _("Swap left and right images"),
4078 options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
4079 pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
4080 options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
4082 group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
4083 table = pref_table_new(group2, 5, 3, FALSE, FALSE);
4084 pref_table_spin_new_int(table, 0, 0, _("Width"), nullptr,
4085 1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
4086 pref_table_spin_new_int(table, 3, 0, _("Height"), nullptr,
4087 1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
4088 pref_table_spin_new_int(table, 0, 1, _("Left X"), nullptr,
4089 0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
4090 pref_table_spin_new_int(table, 3, 1, _("Left Y"), nullptr,
4091 0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
4092 pref_table_spin_new_int(table, 0, 2, _("Right X"), nullptr,
4093 0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
4094 pref_table_spin_new_int(table, 3, 2, _("Right Y"), nullptr,
4095 0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
4099 /* Main preferences window */
4100 static void config_window_create(LayoutWindow *lw)
4102 GtkWidget *win_vbox;
4104 GtkWidget *notebook;
4106 GtkWidget *ct_button;
4108 if (!c_options) c_options = init_options(nullptr);
4110 configwindow = window_new("preferences", PIXBUF_INLINE_ICON_CONFIG, nullptr, _("Preferences"));
4111 DEBUG_NAME(configwindow);
4112 gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
4113 g_signal_connect(G_OBJECT(configwindow), "delete_event",
4114 G_CALLBACK(config_window_delete), NULL);
4115 if (options->save_dialog_window_positions)
4117 gtk_window_resize(GTK_WINDOW(configwindow), lw->options.preferences_window.w, lw->options.preferences_window.h);
4118 gq_gtk_window_move(GTK_WINDOW(configwindow), lw->options.preferences_window.x, lw->options.preferences_window.y);
4122 gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
4124 gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
4125 gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
4127 win_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4128 gq_gtk_container_add(GTK_WIDGET(configwindow), win_vbox);
4129 gtk_widget_show(win_vbox);
4131 notebook = gtk_notebook_new();
4132 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
4133 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
4134 gq_gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
4136 config_tab_general(notebook);
4137 config_tab_image(notebook);
4138 config_tab_osd(notebook);
4139 config_tab_windows(notebook);
4140 config_tab_accelerators(notebook);
4141 config_tab_files(notebook);
4142 config_tab_metadata(notebook);
4143 config_tab_keywords(notebook);
4144 config_tab_color(notebook);
4145 config_tab_stereo(notebook);
4146 config_tab_behavior(notebook);
4147 config_tab_toolbar_main(notebook);
4148 config_tab_toolbar_status(notebook);
4149 config_tab_advanced(notebook);
4151 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), lw->options.preferences_window.page_number);
4153 hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
4154 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
4155 gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
4156 gq_gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
4157 gtk_widget_show(hbox);
4159 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
4160 G_CALLBACK(config_window_help_cb), notebook);
4161 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4162 gtk_widget_set_can_default(button, TRUE);
4163 gtk_widget_show(button);
4165 button = pref_button_new(nullptr, GQ_ICON_OK, "OK",
4166 G_CALLBACK(config_window_ok_cb), notebook);
4167 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4168 gtk_widget_set_can_default(button, TRUE);
4169 gtk_widget_grab_default(button);
4170 gtk_widget_show(button);
4174 button = pref_button_new(nullptr, GQ_ICON_CANCEL, _("Cancel"),
4175 G_CALLBACK(config_window_close_cb), nullptr);
4176 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4177 gtk_widget_set_can_default(button, TRUE);
4178 gtk_widget_show(button);
4180 if (!generic_dialog_get_alternative_button_order(configwindow))
4182 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
4185 gtk_widget_show(notebook);
4187 gtk_widget_show(configwindow);
4191 *-----------------------------------------------------------------------------
4192 * config window show (public)
4193 *-----------------------------------------------------------------------------
4196 void show_config_window(LayoutWindow *lw)
4200 gtk_window_present(GTK_WINDOW(configwindow));
4204 config_window_create(lw);
4213 void show_about_window(LayoutWindow *lw)
4215 GDataInputStream *data_stream;
4216 GInputStream *in_stream_authors;
4217 GInputStream *in_stream_translators;
4219 GdkPixbuf *pixbuf_icon;
4220 GdkPixbuf *pixbuf_logo;
4224 gchar *authors[1000];
4226 gchar *timezone_path;
4235 copyright = g_string_new(_("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"));
4237 timezone_path = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
4238 if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
4240 cd = ZDOpenDatabase(timezone_path);
4243 copyright = g_string_append(copyright, ZDGetNotice(cd));
4247 log_printf("Error: Init of timezone database %s failed\n", timezone_path);
4249 ZDCloseDatabase(cd);
4251 g_free(timezone_path);
4253 copyright = g_string_append(copyright, _("\n\nSome icons by https://www.flaticon.com"));
4255 in_stream_authors = g_resources_open_stream(GQ_RESOURCE_PATH_CREDITS "/authors", G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4257 data_stream = g_data_input_stream_new(in_stream_authors);
4259 authors[0] = nullptr;
4260 while ((author_line = g_data_input_stream_read_line(G_DATA_INPUT_STREAM(data_stream), &length, nullptr, nullptr)))
4262 authors[i_authors] = g_strdup(author_line);
4264 g_free(author_line);
4266 authors[i_authors] = nullptr;
4268 g_input_stream_close(in_stream_authors, nullptr, nullptr);
4270 constexpr auto translators_path = GQ_RESOURCE_PATH_CREDITS "/translators";
4272 g_resources_get_info(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &size, &flags, nullptr);
4274 in_stream_translators = g_resources_open_stream(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4275 translators = static_cast<gchar *>(g_malloc0(size));
4276 g_input_stream_read_all(in_stream_translators, translators, size, &bytes_read, nullptr, nullptr);
4277 g_input_stream_close(in_stream_translators, nullptr, nullptr);
4279 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);
4281 artists[0] = g_strdup("Néstor Díaz Valencia <nestor@estudionexos.com>");
4282 artists[1] = nullptr;
4284 pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
4285 pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
4286 gtk_show_about_dialog(GTK_WINDOW(lw->window),
4287 "title", _("About Geeqie"),
4289 "program-name", GQ_APPNAME,
4291 "logo", pixbuf_logo,
4292 "icon", pixbuf_icon,
4293 "website", GQ_WEBSITE,
4294 "website-label", _("Website"),
4295 "comments", comment,
4298 "translator-credits", translators,
4299 "wrap-license", TRUE,
4300 "license", copyright->str,
4303 g_string_free(copyright, TRUE);
4305 while(n < i_authors)
4313 g_free(translators);
4314 g_object_unref(data_stream);
4315 g_object_unref(in_stream_authors);
4316 g_object_unref(in_stream_translators);
4319 static void image_overlay_set_text_colors()
4321 c_options->image_overlay.text_red = options->image_overlay.text_red;
4322 c_options->image_overlay.text_green = options->image_overlay.text_green;
4323 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
4324 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
4325 c_options->image_overlay.background_red = options->image_overlay.background_red;
4326 c_options->image_overlay.background_green = options->image_overlay.background_green;
4327 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
4328 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
4332 *-----------------------------------------------------------------------------
4333 * timezone database routines
4334 *-----------------------------------------------------------------------------
4337 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
4339 GError *error = nullptr;
4340 auto tz = static_cast<TZData *>(data);
4341 gchar *tmp_filename;
4342 gchar *timezone_bin;
4343 gchar *tmp_dir = nullptr;
4346 if (!g_cancellable_is_cancelled(tz->cancellable))
4348 generic_dialog_close(tz->gd);
4352 if (g_file_copy_finish(G_FILE(source_object), res, &error))
4354 tmp_filename = g_file_get_path(tz->tmp_g_file);
4355 fd = file_data_new_simple(tmp_filename);
4356 tmp_dir = open_archive(fd);
4360 timezone_bin = g_build_filename(tmp_dir, TIMEZONE_DATABASE_VERSION, TIMEZONE_DATABASE_FILE, NULL);
4361 if (isfile(timezone_bin))
4363 move_file(timezone_bin, tz->timezone_database_user);
4367 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4370 g_free(timezone_bin);
4371 g_free(tmp_dir); // The folder in /tmp is deleted in exit_program_final()
4375 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4377 g_free(tmp_filename);
4378 file_data_unref(fd);
4382 file_util_warning_dialog(_("Error: Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4385 g_file_delete(tz->tmp_g_file, nullptr, &error);
4386 g_object_unref(tz->tmp_g_file);
4387 tz->tmp_g_file = nullptr;
4388 g_object_unref(tz->cancellable);
4389 g_object_unref(tz->timezone_database_gq);
4392 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
4394 auto tz = static_cast<TZData *>(data);
4396 if (!g_cancellable_is_cancelled(tz->cancellable))
4398 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), static_cast<gdouble>(current_num_bytes) / total_num_bytes);
4402 static void timezone_cancel_button_cb(GenericDialog *, gpointer data)
4404 auto tz = static_cast<TZData *>(data);
4406 g_cancellable_cancel(tz->cancellable);
4409 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
4411 auto tz = static_cast<TZData *>(data);
4412 GError *error = nullptr;
4413 GFileIOStream *io_stream;
4420 tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
4424 file_util_warning_dialog(_("Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4425 log_printf("Error: Download timezone database failed:\n%s", error->message);
4426 g_error_free(error);
4427 g_object_unref(tz->tmp_g_file);
4431 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE_WEB);
4433 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", nullptr, TRUE, timezone_cancel_button_cb, tz);
4435 generic_dialog_add_message(tz->gd, GQ_ICON_DIALOG_INFO, _("Downloading timezone database"), nullptr, FALSE);
4437 tz->progress = gtk_progress_bar_new();
4438 gq_gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
4439 gtk_widget_show(tz->progress);
4441 gtk_widget_show(tz->gd->dialog);
4442 tz->cancellable = g_cancellable_new();
4443 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);
4445 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
4448 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */