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 EDITOR_NAME_MAX_LENGTH = 32,
88 EDITOR_COMMAND_MAX_LENGTH = 1024
91 static void image_overlay_set_text_colors();
93 GtkWidget *keyword_text;
94 static void config_tab_keywords_save();
102 static ThumbSize thumb_size_list[] =
137 FILETYPES_COLUMN_ENABLED = 0,
138 FILETYPES_COLUMN_FILTER,
139 FILETYPES_COLUMN_DESCRIPTION,
140 FILETYPES_COLUMN_CLASS,
141 FILETYPES_COLUMN_WRITABLE,
142 FILETYPES_COLUMN_SIDECAR,
143 FILETYPES_COLUMN_COUNT
146 const gchar *format_class_list[] = {
157 /* config memory values */
158 static ConfOptions *c_options = nullptr;
165 static GtkWidget *configwindow = nullptr;
166 static GtkListStore *filter_store = nullptr;
167 static GtkTreeStore *accel_store = nullptr;
169 static GtkWidget *safe_delete_path_entry;
171 static GtkWidget *color_profile_input_file_entry[COLOR_PROFILE_INPUTS];
172 static GtkWidget *color_profile_input_name_entry[COLOR_PROFILE_INPUTS];
173 static GtkWidget *color_profile_screen_file_entry;
174 static GtkWidget *external_preview_select_entry;
175 static GtkWidget *external_preview_extract_entry;
177 static GtkWidget *sidecar_ext_entry;
178 static GtkWidget *help_search_engine_entry;
180 static GtkWidget *log_window_f1_entry;
184 CONFIG_WINDOW_DEF_WIDTH = 700,
185 CONFIG_WINDOW_DEF_HEIGHT = 600
189 *-----------------------------------------------------------------------------
190 * option widget callbacks (private)
191 *-----------------------------------------------------------------------------
194 static void zoom_increment_cb(GtkWidget *spin, gpointer)
196 c_options->image.zoom_increment = static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) * 100.0 + 0.01);
199 static void slideshow_delay_hours_cb(GtkWidget *spin, gpointer)
201 gint mins_secs_tenths;
204 mins_secs_tenths = c_options->slideshow.delay %
205 (3600 * SLIDESHOW_SUBSECOND_PRECISION);
207 delay = (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
208 (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
211 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
212 SLIDESHOW_SUBSECOND_PRECISION;
215 static void slideshow_delay_minutes_cb(GtkWidget *spin, gpointer)
221 hours = c_options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
222 secs_tenths = c_options->slideshow.delay % (60 * SLIDESHOW_SUBSECOND_PRECISION);
224 delay = hours * (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
225 (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
226 (60 * SLIDESHOW_SUBSECOND_PRECISION) + secs_tenths);
228 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
229 SLIDESHOW_SUBSECOND_PRECISION;
232 static void slideshow_delay_seconds_cb(GtkWidget *spin, gpointer)
237 hours_mins = c_options->slideshow.delay / (60 * SLIDESHOW_SUBSECOND_PRECISION);
239 delay = (hours_mins * (60 * SLIDESHOW_SUBSECOND_PRECISION)) +
240 static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
241 static_cast<gdouble>(SLIDESHOW_SUBSECOND_PRECISION) + 0.01);
243 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
244 SLIDESHOW_SUBSECOND_PRECISION;
248 *-----------------------------------------------------------------------------
249 * sync program to config window routine (private)
250 *-----------------------------------------------------------------------------
253 void config_entry_to_option(GtkWidget *entry, gchar **option, gchar *(*func)(const gchar *))
259 buf = gq_gtk_entry_get_text(GTK_ENTRY(entry));
260 if (buf && strlen(buf) > 0)
265 *option = g_strdup(buf);
270 static gboolean accel_apply_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
275 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, AE_KEY, &accel, -1);
277 if (accel_path && accel_path[0])
280 gtk_accelerator_parse(accel, &key.accel_key, &key.accel_mods);
281 gtk_accel_map_change_entry(accel_path, key.accel_key, key.accel_mods, TRUE);
291 static void config_window_apply()
293 gboolean refresh = FALSE;
298 config_entry_to_option(safe_delete_path_entry, &options->file_ops.safe_delete_path, remove_trailing_slash);
300 if (options->file_filter.show_hidden_files != c_options->file_filter.show_hidden_files) refresh = TRUE;
301 if (options->file_filter.show_parent_directory != c_options->file_filter.show_parent_directory) refresh = TRUE;
302 if (options->file_filter.show_dot_directory != c_options->file_filter.show_dot_directory) refresh = TRUE;
303 if (options->file_sort.case_sensitive != c_options->file_sort.case_sensitive) refresh = TRUE;
304 if (options->file_filter.disable_file_extension_checks != c_options->file_filter.disable_file_extension_checks) refresh = TRUE;
305 if (options->file_filter.disable != c_options->file_filter.disable) refresh = TRUE;
307 options->file_ops.confirm_delete = c_options->file_ops.confirm_delete;
308 options->file_ops.enable_delete_key = c_options->file_ops.enable_delete_key;
309 options->file_ops.confirm_move_to_trash = c_options->file_ops.confirm_move_to_trash;
310 options->file_ops.use_system_trash = c_options->file_ops.use_system_trash;
311 options->file_ops.no_trash = c_options->file_ops.no_trash;
312 options->file_ops.safe_delete_folder_maxsize = c_options->file_ops.safe_delete_folder_maxsize;
313 options->tools_restore_state = c_options->tools_restore_state;
314 options->save_window_positions = c_options->save_window_positions;
315 options->use_saved_window_positions_for_new_windows = c_options->use_saved_window_positions_for_new_windows;
316 options->save_window_workspace = c_options->save_window_workspace;
317 options->save_dialog_window_positions = c_options->save_dialog_window_positions;
318 options->show_window_ids = c_options->show_window_ids;
319 options->image.scroll_reset_method = c_options->image.scroll_reset_method;
320 options->image.zoom_2pass = c_options->image.zoom_2pass;
321 options->image.fit_window_to_image = c_options->image.fit_window_to_image;
322 options->image.limit_window_size = c_options->image.limit_window_size;
323 options->image.zoom_to_fit_allow_expand = c_options->image.zoom_to_fit_allow_expand;
324 options->image.max_window_size = c_options->image.max_window_size;
325 options->image.limit_autofit_size = c_options->image.limit_autofit_size;
326 options->image.max_autofit_size = c_options->image.max_autofit_size;
327 options->image.max_enlargement_size = c_options->image.max_enlargement_size;
328 options->image.tile_size = c_options->image.tile_size;
329 options->progressive_key_scrolling = c_options->progressive_key_scrolling;
330 options->keyboard_scroll_step = c_options->keyboard_scroll_step;
332 if (options->thumbnails.max_width != c_options->thumbnails.max_width
333 || options->thumbnails.max_height != c_options->thumbnails.max_height
334 || options->thumbnails.quality != c_options->thumbnails.quality)
336 thumb_format_changed = TRUE;
338 options->thumbnails.max_width = c_options->thumbnails.max_width;
339 options->thumbnails.max_height = c_options->thumbnails.max_height;
340 options->thumbnails.quality = c_options->thumbnails.quality;
342 options->thumbnails.enable_caching = c_options->thumbnails.enable_caching;
343 options->thumbnails.cache_into_dirs = c_options->thumbnails.cache_into_dirs;
344 options->thumbnails.use_exif = c_options->thumbnails.use_exif;
345 options->thumbnails.use_color_management = c_options->thumbnails.use_color_management;
346 options->thumbnails.collection_preview = c_options->thumbnails.collection_preview;
347 options->thumbnails.use_ft_metadata = c_options->thumbnails.use_ft_metadata;
348 options->thumbnails.spec_standard = c_options->thumbnails.spec_standard;
349 options->metadata.enable_metadata_dirs = c_options->metadata.enable_metadata_dirs;
350 options->file_filter.show_hidden_files = c_options->file_filter.show_hidden_files;
351 options->file_filter.show_parent_directory = c_options->file_filter.show_parent_directory;
352 options->file_filter.show_dot_directory = c_options->file_filter.show_dot_directory;
353 options->file_filter.disable_file_extension_checks = c_options->file_filter.disable_file_extension_checks;
355 options->file_sort.case_sensitive = c_options->file_sort.case_sensitive;
356 options->file_filter.disable = c_options->file_filter.disable;
358 config_entry_to_option(sidecar_ext_entry, &options->sidecar.ext, nullptr);
359 sidecar_ext_parse(options->sidecar.ext);
361 options->slideshow.random = c_options->slideshow.random;
362 options->slideshow.repeat = c_options->slideshow.repeat;
363 options->slideshow.delay = c_options->slideshow.delay;
365 options->mousewheel_scrolls = c_options->mousewheel_scrolls;
366 options->image_lm_click_nav = c_options->image_lm_click_nav;
367 options->image_l_click_archive = c_options->image_l_click_archive;
368 options->image_l_click_video = c_options->image_l_click_video;
369 options->image_l_click_video_editor = c_options->image_l_click_video_editor;
371 options->file_ops.enable_in_place_rename = c_options->file_ops.enable_in_place_rename;
373 options->image.tile_cache_max = c_options->image.tile_cache_max;
374 options->image.image_cache_max = c_options->image.image_cache_max;
376 options->image.zoom_quality = c_options->image.zoom_quality;
378 options->image.zoom_increment = c_options->image.zoom_increment;
380 options->image.zoom_style = c_options->image.zoom_style;
382 options->image.enable_read_ahead = c_options->image.enable_read_ahead;
384 options->appimage_notifications = c_options->appimage_notifications;
387 if (options->image.use_custom_border_color != c_options->image.use_custom_border_color
388 || options->image.use_custom_border_color_in_fullscreen != c_options->image.use_custom_border_color_in_fullscreen
389 || !gdk_rgba_equal(&options->image.border_color, &c_options->image.border_color))
391 options->image.use_custom_border_color_in_fullscreen = c_options->image.use_custom_border_color_in_fullscreen;
392 options->image.use_custom_border_color = c_options->image.use_custom_border_color;
393 options->image.border_color = c_options->image.border_color;
394 layout_colors_update();
395 view_window_colors_update();
398 options->image.alpha_color_1 = c_options->image.alpha_color_1;
399 options->image.alpha_color_2 = c_options->image.alpha_color_2;
401 options->fullscreen.screen = c_options->fullscreen.screen;
402 options->fullscreen.clean_flip = c_options->fullscreen.clean_flip;
403 options->fullscreen.disable_saver = c_options->fullscreen.disable_saver;
404 options->fullscreen.above = c_options->fullscreen.above;
405 if (c_options->image_overlay.template_string)
406 set_image_overlay_template_string(&options->image_overlay.template_string,
407 c_options->image_overlay.template_string);
408 if (c_options->image_overlay.font)
409 set_image_overlay_font_string(&options->image_overlay.font,
410 c_options->image_overlay.font);
411 options->image_overlay.text_red = c_options->image_overlay.text_red;
412 options->image_overlay.text_green = c_options->image_overlay.text_green;
413 options->image_overlay.text_blue = c_options->image_overlay.text_blue;
414 options->image_overlay.text_alpha = c_options->image_overlay.text_alpha;
415 options->image_overlay.background_red = c_options->image_overlay.background_red;
416 options->image_overlay.background_green = c_options->image_overlay.background_green;
417 options->image_overlay.background_blue = c_options->image_overlay.background_blue;
418 options->image_overlay.background_alpha = c_options->image_overlay.background_alpha;
419 options->image_overlay.font = c_options->image_overlay.font;
420 options->update_on_time_change = c_options->update_on_time_change;
422 options->duplicates_similarity_threshold = c_options->duplicates_similarity_threshold;
423 options->rot_invariant_sim = c_options->rot_invariant_sim;
425 options->tree_descend_subdirs = c_options->tree_descend_subdirs;
427 options->view_dir_list_single_click_enter = c_options->view_dir_list_single_click_enter;
428 options->circular_selection_lists = c_options->circular_selection_lists;
430 options->open_recent_list_maxsize = c_options->open_recent_list_maxsize;
431 options->recent_folder_image_list_maxsize = c_options->recent_folder_image_list_maxsize;
432 options->dnd_icon_size = c_options->dnd_icon_size;
433 options->clipboard_selection = c_options->clipboard_selection;
434 options->dnd_default_action = c_options->dnd_default_action;
436 options->metadata.save_in_image_file = c_options->metadata.save_in_image_file;
437 options->metadata.save_legacy_IPTC = c_options->metadata.save_legacy_IPTC;
438 options->metadata.warn_on_write_problems = c_options->metadata.warn_on_write_problems;
439 options->metadata.save_legacy_format = c_options->metadata.save_legacy_format;
440 options->metadata.sync_grouped_files = c_options->metadata.sync_grouped_files;
441 options->metadata.confirm_write = c_options->metadata.confirm_write;
442 options->metadata.sidecar_extended_name = c_options->metadata.sidecar_extended_name;
443 options->metadata.confirm_timeout = c_options->metadata.confirm_timeout;
444 options->metadata.confirm_after_timeout = c_options->metadata.confirm_after_timeout;
445 options->metadata.confirm_on_image_change = c_options->metadata.confirm_on_image_change;
446 options->metadata.confirm_on_dir_change = c_options->metadata.confirm_on_dir_change;
447 options->metadata.keywords_case_sensitive = c_options->metadata.keywords_case_sensitive;
448 options->metadata.write_orientation = c_options->metadata.write_orientation;
449 options->metadata.check_spelling = c_options->metadata.check_spelling;
450 options->stereo.mode = (c_options->stereo.mode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
451 (c_options->stereo.tmp.mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
452 (c_options->stereo.tmp.flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
453 (c_options->stereo.tmp.mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
454 (c_options->stereo.tmp.flip_left ? PR_STEREO_FLIP_LEFT : 0) |
455 (c_options->stereo.tmp.swap ? PR_STEREO_SWAP : 0) |
456 (c_options->stereo.tmp.temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
457 options->stereo.fsmode = (c_options->stereo.fsmode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
458 (c_options->stereo.tmp.fs_mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
459 (c_options->stereo.tmp.fs_flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
460 (c_options->stereo.tmp.fs_mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
461 (c_options->stereo.tmp.fs_flip_left ? PR_STEREO_FLIP_LEFT : 0) |
462 (c_options->stereo.tmp.fs_swap ? PR_STEREO_SWAP : 0) |
463 (c_options->stereo.tmp.fs_temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
464 options->stereo.enable_fsmode = c_options->stereo.enable_fsmode;
465 options->stereo.fixed_w = c_options->stereo.fixed_w;
466 options->stereo.fixed_h = c_options->stereo.fixed_h;
467 options->stereo.fixed_x1 = c_options->stereo.fixed_x1;
468 options->stereo.fixed_y1 = c_options->stereo.fixed_y1;
469 options->stereo.fixed_x2 = c_options->stereo.fixed_x2;
470 options->stereo.fixed_y2 = c_options->stereo.fixed_y2;
472 options->info_keywords.height = c_options->info_keywords.height;
473 options->info_title.height = c_options->info_title.height;
474 options->info_comment.height = c_options->info_comment.height;
475 options->info_rating.height = c_options->info_rating.height;
477 options->show_predefined_keyword_tree = c_options->show_predefined_keyword_tree;
478 options->expand_menu_toolbar = c_options->expand_menu_toolbar;
479 options->hamburger_menu = c_options->hamburger_menu;
481 options->selectable_bars.menu_bar = c_options->selectable_bars.menu_bar;
482 options->selectable_bars.tool_bar = c_options->selectable_bars.tool_bar;
483 options->selectable_bars.status_bar = c_options->selectable_bars.status_bar;
485 options->marks_save = c_options->marks_save;
486 options->with_rename = c_options->with_rename;
487 options->collections_duplicates = c_options->collections_duplicates;
488 options->collections_on_top = c_options->collections_on_top;
489 options->hide_window_in_fullscreen = c_options->hide_window_in_fullscreen;
490 config_entry_to_option(help_search_engine_entry, &options->help_search_engine, nullptr);
492 options->external_preview.enable = c_options->external_preview.enable;
493 config_entry_to_option(external_preview_select_entry, &options->external_preview.select, nullptr);
494 config_entry_to_option(external_preview_extract_entry, &options->external_preview.extract, nullptr);
496 options->read_metadata_in_idle = c_options->read_metadata_in_idle;
498 options->star_rating.star = c_options->star_rating.star;
499 options->star_rating.rejected = c_options->star_rating.rejected;
501 options->threads.duplicates = c_options->threads.duplicates > 0 ? c_options->threads.duplicates : -1;
503 options->alternate_similarity_algorithm.enabled = c_options->alternate_similarity_algorithm.enabled;
504 options->alternate_similarity_algorithm.grayscale = c_options->alternate_similarity_algorithm.grayscale;
507 set_debug_level(debug_c);
508 config_entry_to_option(log_window_f1_entry, &options->log_window.action, nullptr);
512 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
514 config_entry_to_option(color_profile_input_name_entry[i], &options->color_profile.input_name[i], nullptr);
515 config_entry_to_option(color_profile_input_file_entry[i], &options->color_profile.input_file[i], nullptr);
517 config_entry_to_option(color_profile_screen_file_entry, &options->color_profile.screen_file, nullptr);
518 options->color_profile.use_x11_screen_profile = c_options->color_profile.use_x11_screen_profile;
519 if (options->color_profile.render_intent != c_options->color_profile.render_intent)
521 options->color_profile.render_intent = c_options->color_profile.render_intent;
526 options->mouse_button_8 = c_options->mouse_button_8;
527 options->mouse_button_9 = c_options->mouse_button_9;
529 options->override_disable_gpu = c_options->override_disable_gpu;
531 config_tab_keywords_save();
533 image_options_sync();
538 layout_refresh(nullptr);
541 if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, nullptr);
543 toolbar_apply(TOOLBAR_MAIN);
544 toolbar_apply(TOOLBAR_STATUS);
548 *-----------------------------------------------------------------------------
549 * config window main button callbacks (private)
550 *-----------------------------------------------------------------------------
553 static void config_window_close_cb(GtkWidget *, gpointer)
555 gq_gtk_widget_destroy(configwindow);
556 configwindow = nullptr;
557 filter_store = nullptr;
560 static void config_window_help_cb(GtkWidget *, gpointer data)
562 auto notebook = static_cast<GtkWidget *>(data);
565 static const gchar *html_section[] =
567 "GuideOptionsGeneral.html",
568 "GuideOptionsImage.html",
569 "GuideOptionsOSD.html",
570 "GuideOptionsWindow.html",
571 "GuideOptionsKeyboard.html",
572 "GuideOptionsFiltering.html",
573 "GuideOptionsMetadata.html",
574 "GuideOptionsKeywords.html",
575 "GuideOptionsColor.html",
576 "GuideOptionsStereo.html",
577 "GuideOptionsBehavior.html",
578 "GuideOptionsToolbar.html",
579 "GuideOptionsToolbar.html",
580 "GuideOptionsAdvanced.html"
583 i = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
584 help_window_show(html_section[i]);
587 static gboolean config_window_delete(GtkWidget *, GdkEventAny *, gpointer)
589 config_window_close_cb(nullptr, nullptr);
593 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
596 auto notebook = static_cast<GtkNotebook *>(data);
604 lw = static_cast<LayoutWindow *>(layout_window_list->data);
606 window = gtk_widget_get_window(widget);
607 gdk_window_get_root_origin(window, &x, &y);
608 w = gdk_window_get_width(window);
609 h = gdk_window_get_height(window);
610 page_number = gtk_notebook_get_current_page(notebook);
612 lw->options.preferences_window.x = x;
613 lw->options.preferences_window.y = y;
614 lw->options.preferences_window.w = w;
615 lw->options.preferences_window.h = h;
616 lw->options.preferences_window.page_number = page_number;
618 config_window_apply();
619 layout_util_sync(lw);
620 save_options(options);
621 config_window_close_cb(nullptr, nullptr);
625 *-----------------------------------------------------------------------------
626 * config window setup (private)
627 *-----------------------------------------------------------------------------
630 static void quality_menu_cb(GtkWidget *combo, gpointer data)
632 auto option = static_cast<gint *>(data);
634 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
638 *option = GDK_INTERP_NEAREST;
641 *option = GDK_INTERP_TILES;
644 *option = GDK_INTERP_BILINEAR;
649 static void dnd_default_action_selection_menu_cb(GtkWidget *combo, gpointer data)
651 auto option = static_cast<gint *>(data);
653 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
657 *option = DND_ACTION_ASK;
660 *option = DND_ACTION_COPY;
663 *option = DND_ACTION_MOVE;
667 static void clipboard_selection_menu_cb(GtkWidget *combo, gpointer data)
669 auto option = static_cast<gint *>(data);
671 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
674 *option = CLIPBOARD_PRIMARY;
677 *option = CLIPBOARD_CLIPBOARD;
680 *option = CLIPBOARD_BOTH;
683 *option = CLIPBOARD_BOTH;
687 static void add_quality_menu(GtkWidget *table, gint column, gint row, const gchar *text,
688 guint option, guint *option_c)
695 pref_table_label(table, column, row, text, GTK_ALIGN_START);
697 combo = gtk_combo_box_text_new();
699 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Nearest (worst, but fastest)"));
700 if (option == GDK_INTERP_NEAREST) current = 0;
701 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Tiles"));
702 if (option == GDK_INTERP_TILES) current = 1;
703 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Bilinear (best, but slowest)"));
704 if (option == GDK_INTERP_BILINEAR) current = 2;
706 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
708 g_signal_connect(G_OBJECT(combo), "changed",
709 G_CALLBACK(quality_menu_cb), option_c);
711 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
712 gtk_widget_show(combo);
715 static void add_dnd_default_action_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, DnDAction option, DnDAction *option_c)
722 pref_table_label(table, column, row, text, GTK_ALIGN_START);
724 combo = gtk_combo_box_text_new();
726 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Ask"));
727 if (option == DND_ACTION_ASK) current = 0;
728 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Copy"));
729 if (option == DND_ACTION_COPY) current = 1;
730 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Move"));
731 if (option == DND_ACTION_MOVE) current = 2;
733 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
735 g_signal_connect(G_OBJECT(combo), "changed",
736 G_CALLBACK(dnd_default_action_selection_menu_cb), option_c);
738 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
739 gtk_widget_show(combo);
742 static void add_clipboard_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
743 gint option, gint *option_c)
750 pref_table_label(table, column, row, text, GTK_ALIGN_START);
752 combo = gtk_combo_box_text_new();
754 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Primary"));
755 if (option == CLIPBOARD_PRIMARY) current = 0;
756 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Clipboard"));
757 if (option == CLIPBOARD_CLIPBOARD) current = 1;
758 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Both"));
759 if (option == CLIPBOARD_BOTH) current = 2;
761 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
763 g_signal_connect(G_OBJECT(combo), "changed",
764 G_CALLBACK(clipboard_selection_menu_cb), option_c);
766 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
767 gtk_widget_show(combo);
770 static void zoom_style_selection_menu_cb(GtkWidget *combo, gpointer data)
772 auto option = static_cast<gint *>(data);
774 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
777 *option = ZOOM_GEOMETRIC;
780 *option = ZOOM_ARITHMETIC;
783 *option = ZOOM_GEOMETRIC;
787 static void add_zoom_style_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, ZoomStyle option, ZoomStyle *option_c)
794 pref_table_label(table, column, row, text, GTK_ALIGN_START);
796 combo = gtk_combo_box_text_new();
798 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Geometric"));
799 if (option == ZOOM_GEOMETRIC) current = 0;
800 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Arithmetic"));
801 if (option == ZOOM_ARITHMETIC) current = 1;
803 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
805 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(zoom_style_selection_menu_cb), option_c);
807 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
808 gtk_widget_show(combo);
811 static void mouse_buttons_selection_menu_cb(GtkWidget *combo, gpointer data)
813 ActionItem *action_item = nullptr;
814 auto option = static_cast<gchar **>(data);
819 label = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
821 list = get_action_items();
826 action_item = static_cast<ActionItem *>(work->data);
827 if (g_strcmp0(action_item->label, label) == 0)
835 *option = g_strdup(action_item->name);
837 action_items_free(list);
840 static void add_mouse_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, gchar *option, gchar **option_c)
842 ActionItem *action_item;
851 pref_table_label(table, column, row, text, GTK_ALIGN_START);
853 combo = gtk_combo_box_text_new();
855 list = get_action_items();
859 action_item = static_cast<ActionItem *>(work->data);
860 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), action_item->label);
862 if (g_strcmp0(action_item->name, option) == 0)
870 action_items_free(list);
872 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
874 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(mouse_buttons_selection_menu_cb), option_c);
876 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
877 gtk_widget_show(combo);
880 static void thumb_size_menu_cb(GtkWidget *combo, gpointer)
884 n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
887 if (static_cast<guint>(n) < sizeof(thumb_size_list) / sizeof(ThumbSize))
889 c_options->thumbnails.max_width = thumb_size_list[n].w;
890 c_options->thumbnails.max_height = thumb_size_list[n].h;
894 c_options->thumbnails.max_width = options->thumbnails.max_width;
895 c_options->thumbnails.max_height = options->thumbnails.max_height;
899 static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *text)
905 c_options->thumbnails.max_width = options->thumbnails.max_width;
906 c_options->thumbnails.max_height = options->thumbnails.max_height;
908 pref_table_label(table, column, row, text, GTK_ALIGN_START);
910 combo = gtk_combo_box_text_new();
913 for (i = 0; static_cast<guint>(i) < sizeof(thumb_size_list) / sizeof(ThumbSize); i++)
919 w = thumb_size_list[i].w;
920 h = thumb_size_list[i].h;
922 buf = g_strdup_printf("%d x %d", w, h);
923 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
926 if (w == options->thumbnails.max_width && h == options->thumbnails.max_height) current = i;
933 buf = g_strdup_printf("%s %d x %d", _("Custom"), options->thumbnails.max_width, options->thumbnails.max_height);
934 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
940 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
941 g_signal_connect(G_OBJECT(combo), "changed",
942 G_CALLBACK(thumb_size_menu_cb), NULL);
944 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
945 gtk_widget_show(combo);
948 static void stereo_mode_menu_cb(GtkWidget *combo, gpointer data)
950 auto option = static_cast<gint *>(data);
952 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
956 *option = PR_STEREO_NONE;
959 *option = PR_STEREO_ANAGLYPH_RC;
962 *option = PR_STEREO_ANAGLYPH_GM;
965 *option = PR_STEREO_ANAGLYPH_YB;
968 *option = PR_STEREO_ANAGLYPH_GRAY_RC;
971 *option = PR_STEREO_ANAGLYPH_GRAY_GM;
974 *option = PR_STEREO_ANAGLYPH_GRAY_YB;
977 *option = PR_STEREO_ANAGLYPH_DB_RC;
980 *option = PR_STEREO_ANAGLYPH_DB_GM;
983 *option = PR_STEREO_ANAGLYPH_DB_YB;
986 *option = PR_STEREO_HORIZ;
989 *option = PR_STEREO_HORIZ | PR_STEREO_HALF;
992 *option = PR_STEREO_VERT;
995 *option = PR_STEREO_VERT | PR_STEREO_HALF;
998 *option = PR_STEREO_FIXED;
1003 static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1004 gint option, gint *option_c, gboolean add_fixed)
1011 pref_table_label(table, column, row, text, GTK_ALIGN_START);
1013 combo = gtk_combo_box_text_new();
1015 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Single image"));
1017 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Red-Cyan"));
1018 if (option & PR_STEREO_ANAGLYPH_RC) current = 1;
1019 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Green-Magenta"));
1020 if (option & PR_STEREO_ANAGLYPH_GM) current = 2;
1021 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Yellow-Blue"));
1022 if (option & PR_STEREO_ANAGLYPH_YB) current = 3;
1023 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Red-Cyan"));
1024 if (option & PR_STEREO_ANAGLYPH_GRAY_RC) current = 4;
1025 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Green-Magenta"));
1026 if (option & PR_STEREO_ANAGLYPH_GRAY_GM) current = 5;
1027 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Yellow-Blue"));
1028 if (option & PR_STEREO_ANAGLYPH_GRAY_YB) current = 6;
1029 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Red-Cyan"));
1030 if (option & PR_STEREO_ANAGLYPH_DB_RC) current = 7;
1031 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Green-Magenta"));
1032 if (option & PR_STEREO_ANAGLYPH_DB_GM) current = 8;
1033 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Yellow-Blue"));
1034 if (option & PR_STEREO_ANAGLYPH_DB_YB) current = 9;
1036 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side"));
1037 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side Half size"));
1038 if (option & PR_STEREO_HORIZ)
1041 if (option & PR_STEREO_HALF) current = 11;
1044 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom"));
1045 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom Half size"));
1046 if (option & PR_STEREO_VERT)
1049 if (option & PR_STEREO_HALF) current = 13;
1054 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Fixed position"));
1055 if (option & PR_STEREO_FIXED) current = 14;
1058 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1060 g_signal_connect(G_OBJECT(combo), "changed",
1061 G_CALLBACK(stereo_mode_menu_cb), option_c);
1063 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1064 gtk_widget_show(combo);
1067 static void video_menu_cb(GtkWidget *combo, gpointer data)
1069 auto option = static_cast<gchar **>(data);
1071 auto ed = static_cast<EditorDescription *>(g_list_nth_data(editor_list_get(), gtk_combo_box_get_active(GTK_COMBO_BOX(combo))));
1075 static void video_menu_populate(gpointer data, gpointer user_data)
1077 auto combo = static_cast<GtkWidget *>(user_data);
1078 auto ed = static_cast<EditorDescription *>(data);
1080 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), ed->name);
1083 static void add_video_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1084 gchar *option, gchar **option_c)
1088 /* use lists since they are sorted */
1089 GList *eds = editor_list_get();
1093 pref_table_label(table, column, row, text, GTK_ALIGN_START);
1095 combo = gtk_combo_box_text_new();
1096 g_list_foreach(eds,video_menu_populate,combo);
1097 current = option ? g_list_index(eds,g_hash_table_lookup(editors,option)): -1;
1099 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1101 g_signal_connect(G_OBJECT(combo), "changed",
1102 G_CALLBACK(video_menu_cb), option_c);
1104 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1105 gtk_widget_show(combo);
1108 static void filter_store_populate()
1112 if (!filter_store) return;
1114 gtk_list_store_clear(filter_store);
1116 work = filter_get_list();
1122 fe = static_cast<FilterEntry *>(work->data);
1125 gtk_list_store_append(filter_store, &iter);
1126 gtk_list_store_set(filter_store, &iter, 0, fe, -1);
1130 static void filter_store_ext_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1132 auto model = static_cast<GtkWidget *>(data);
1133 auto fe = static_cast<FilterEntry *>(data);
1137 if (!new_text || strlen(new_text) < 1) return;
1139 tpath = gtk_tree_path_new_from_string(path_str);
1140 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1141 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1143 g_free(fe->extensions);
1144 fe->extensions = g_strdup(new_text);
1146 gtk_tree_path_free(tpath);
1150 static void filter_store_class_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1152 auto model = static_cast<GtkWidget *>(data);
1153 auto fe = static_cast<FilterEntry *>(data);
1158 if (!new_text || !new_text[0]) return;
1160 tpath = gtk_tree_path_new_from_string(path_str);
1161 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1162 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1164 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
1166 if (strcmp(new_text, _(format_class_list[i])) == 0)
1168 fe->file_class = static_cast<FileFormatClass>(i);
1173 gtk_tree_path_free(tpath);
1177 static void filter_store_desc_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1179 auto model = static_cast<GtkWidget *>(data);
1184 if (!new_text || !new_text[0]) return;
1186 tpath = gtk_tree_path_new_from_string(path_str);
1187 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1188 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1190 g_free(fe->description);
1191 fe->description = g_strdup(new_text);
1193 gtk_tree_path_free(tpath);
1196 static void filter_store_enable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1198 auto model = static_cast<GtkWidget *>(data);
1203 tpath = gtk_tree_path_new_from_string(path_str);
1204 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1205 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1207 fe->enabled = !fe->enabled;
1209 gtk_tree_path_free(tpath);
1213 static void filter_store_writable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1215 auto model = static_cast<GtkWidget *>(data);
1220 tpath = gtk_tree_path_new_from_string(path_str);
1221 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1222 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1224 fe->writable = !fe->writable;
1225 if (fe->writable) fe->allow_sidecar = FALSE;
1227 gtk_tree_path_free(tpath);
1231 static void filter_store_sidecar_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1233 auto model = static_cast<GtkWidget *>(data);
1238 tpath = gtk_tree_path_new_from_string(path_str);
1239 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1240 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1242 fe->allow_sidecar = !fe->allow_sidecar;
1243 if (fe->allow_sidecar) fe->writable = FALSE;
1245 gtk_tree_path_free(tpath);
1249 static void filter_set_func(GtkTreeViewColumn *, GtkCellRenderer *cell,
1250 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1254 gtk_tree_model_get(tree_model, iter, 0, &fe, -1);
1256 switch (GPOINTER_TO_INT(data))
1259 g_object_set(GTK_CELL_RENDERER(cell),
1260 "active", fe->enabled, NULL);
1263 g_object_set(GTK_CELL_RENDERER(cell),
1264 "text", fe->extensions, NULL);
1266 case FE_DESCRIPTION:
1267 g_object_set(GTK_CELL_RENDERER(cell),
1268 "text", fe->description, NULL);
1271 g_object_set(GTK_CELL_RENDERER(cell),
1272 "text", _(format_class_list[fe->file_class]), NULL);
1275 g_object_set(GTK_CELL_RENDERER(cell),
1276 "active", fe->writable, NULL);
1278 case FE_ALLOW_SIDECAR:
1279 g_object_set(GTK_CELL_RENDERER(cell),
1280 "active", fe->allow_sidecar, NULL);
1285 static gboolean filter_add_scroll(gpointer data)
1289 GtkCellRenderer *cell;
1290 GtkTreeViewColumn *column;
1293 GtkTreeModel *store;
1295 FilterEntry *filter;
1297 rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(filter_store), nullptr);
1298 path = gtk_tree_path_new_from_indices(rows-1, -1);
1300 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data), 0);
1302 list_cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1303 cell = static_cast<GtkCellRenderer *>(g_list_last(list_cells)->data);
1305 store = gtk_tree_view_get_model(GTK_TREE_VIEW(data));
1306 valid = gtk_tree_model_get_iter_first(store, &iter);
1310 gtk_tree_model_get(store, &iter, 0, &filter, -1);
1312 if (g_strcmp0(filter->extensions, ".new") == 0)
1314 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1318 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1321 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1322 path, column, FALSE, 0.0, 0.0 );
1323 gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(data),
1324 path, column, cell, TRUE);
1326 gtk_tree_path_free(path);
1327 g_list_free(list_cells);
1329 return(G_SOURCE_REMOVE);
1332 static void filter_add_cb(GtkWidget *, gpointer data)
1334 filter_add_unique("description", ".new", FORMAT_CLASS_IMAGE, TRUE, FALSE, TRUE);
1335 filter_store_populate();
1337 g_idle_add(static_cast<GSourceFunc>(filter_add_scroll), data);
1340 static void filter_remove_cb(GtkWidget *, gpointer data)
1342 auto filter_view = static_cast<GtkWidget *>(data);
1343 GtkTreeSelection *selection;
1347 if (!filter_store) return;
1348 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
1349 if (!gtk_tree_selection_get_selected(selection, nullptr, &iter)) return;
1350 gtk_tree_model_get(GTK_TREE_MODEL(filter_store), &iter, 0, &fe, -1);
1353 filter_remove_entry(fe);
1355 filter_store_populate();
1358 static gboolean filter_default_ok_scroll(GtkTreeView *data)
1362 GtkTreeViewColumn *column;
1364 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(filter_store), &iter);
1365 path = gtk_tree_model_get_path(GTK_TREE_MODEL(filter_store), &iter);
1366 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1368 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1372 gtk_tree_path_free(path);
1374 return(G_SOURCE_REMOVE);
1377 static void filter_default_ok_cb(GenericDialog *gd, gpointer)
1380 filter_add_defaults();
1382 filter_store_populate();
1384 g_idle_add(reinterpret_cast<GSourceFunc>(filter_default_ok_scroll), gd->data);
1387 static void dummy_cancel_cb(GenericDialog *, gpointer)
1389 /* no op, only so cancel button appears */
1392 static void filter_default_cb(GtkWidget *widget, gpointer data)
1396 gd = generic_dialog_new(_("Reset filters"),
1397 "reset_filter", widget, TRUE,
1398 dummy_cancel_cb, data);
1399 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset filters"),
1400 _("This will reset the file filters to the defaults.\nContinue?"), TRUE);
1401 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", filter_default_ok_cb, TRUE);
1402 gtk_widget_show(gd->dialog);
1405 static void filter_disable_cb(GtkWidget *widget, gpointer data)
1407 auto frame = static_cast<GtkWidget *>(data);
1409 gtk_widget_set_sensitive(frame,
1410 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1413 static void safe_delete_view_cb(GtkWidget *, gpointer)
1415 layout_set_path(nullptr, gq_gtk_entry_get_text(GTK_ENTRY(safe_delete_path_entry)));
1418 static void safe_delete_clear_ok_cb(GenericDialog *, gpointer)
1420 file_util_trash_clear();
1423 static void safe_delete_clear_cb(GtkWidget *widget, gpointer)
1427 gd = generic_dialog_new(_("Clear trash"),
1428 "clear_trash", widget, TRUE,
1429 dummy_cancel_cb, nullptr);
1430 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Clear trash"),
1431 _("This will remove the trash contents."), FALSE);
1432 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", safe_delete_clear_ok_cb, TRUE);
1433 entry = gtk_entry_new();
1434 gtk_widget_set_can_focus(entry, FALSE);
1435 gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
1436 if (options->file_ops.safe_delete_path) gq_gtk_entry_set_text(GTK_ENTRY(entry), options->file_ops.safe_delete_path);
1437 gq_gtk_box_pack_start(GTK_BOX(gd->vbox), entry, FALSE, FALSE, 0);
1438 gtk_widget_show(entry);
1439 gtk_widget_show(gd->dialog);
1442 static void image_overlay_template_view_changed_cb(GtkWidget *, gpointer data)
1444 GtkWidget *pTextView;
1445 GtkTextBuffer *pTextBuffer;
1449 pTextView = GTK_WIDGET(data);
1451 pTextBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pTextView));
1452 gtk_text_buffer_get_start_iter(pTextBuffer, &iStart);
1453 gtk_text_buffer_get_end_iter(pTextBuffer, &iEnd);
1455 set_image_overlay_template_string(&c_options->image_overlay.template_string,
1456 gtk_text_buffer_get_text(pTextBuffer, &iStart, &iEnd, TRUE));
1459 static void image_overlay_default_template_ok_cb(GenericDialog *, gpointer data)
1461 auto text_view = static_cast<GtkTextView *>(data);
1462 GtkTextBuffer *buffer;
1464 set_default_image_overlay_template_string(&options->image_overlay.template_string);
1465 if (!configwindow) return;
1467 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
1468 gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
1471 static void image_overlay_default_template_cb(GtkWidget *widget, gpointer data)
1475 gd = generic_dialog_new(_("Reset image overlay template string"),
1476 "reset_image_overlay_template_string", widget, TRUE,
1477 dummy_cancel_cb, data);
1478 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset image overlay template string"),
1479 _("This will reset the image overlay template string to the default.\nContinue?"), TRUE);
1480 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", image_overlay_default_template_ok_cb, TRUE);
1481 gtk_widget_show(gd->dialog);
1484 static void image_overlay_help_cb(GtkWidget *, gpointer)
1486 help_window_show("GuideOptionsOSD.html");
1489 static void font_activated_cb(GtkFontChooser *widget, gchar *fontname, gpointer)
1491 g_free(c_options->image_overlay.font);
1492 c_options->image_overlay.font = g_strdup(fontname);
1495 gq_gtk_widget_destroy(GTK_WIDGET(widget));
1498 static void font_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1502 g_free(c_options->image_overlay.font);
1503 c_options->image_overlay.font = g_strdup(options->image_overlay.font);
1505 if (response_id == GTK_RESPONSE_OK)
1507 font = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(dialog));
1508 g_free(c_options->image_overlay.font);
1509 c_options->image_overlay.font = g_strdup(font);
1513 gq_gtk_widget_destroy(GTK_WIDGET(dialog));
1516 static void image_overlay_set_font_cb(GtkWidget *widget, gpointer)
1520 dialog = gtk_font_chooser_dialog_new(_("Image Overlay Font"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1521 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1522 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), options->image_overlay.font);
1524 g_signal_connect(dialog, "font-activated", G_CALLBACK(font_activated_cb), nullptr);
1525 g_signal_connect(dialog, "response", G_CALLBACK(font_response_cb), nullptr);
1527 gtk_widget_show(dialog);
1530 static void text_color_activated_cb(GtkColorChooser *chooser, GdkRGBA *color, gpointer)
1532 c_options->image_overlay.text_red = color->red * 255;
1533 c_options->image_overlay.text_green = color->green * 255;
1534 c_options->image_overlay.text_blue = color->blue * 255;
1535 c_options->image_overlay.text_alpha = color->alpha * 255;
1537 gq_gtk_widget_destroy(GTK_WIDGET(chooser));
1540 static void text_color_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1544 c_options->image_overlay.text_red = options->image_overlay.text_red;
1545 c_options->image_overlay.text_green = options->image_overlay.text_green;
1546 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
1547 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
1549 if (response_id == GTK_RESPONSE_OK)
1551 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1552 c_options->image_overlay.text_red = color.red * 255;
1553 c_options->image_overlay.text_green = color.green * 255;
1554 c_options->image_overlay.text_blue = color.blue * 255;
1555 c_options->image_overlay.text_alpha = color.alpha * 255;
1558 gq_gtk_widget_destroy(GTK_WIDGET(dialog));
1561 static void image_overlay_set_text_color_cb(GtkWidget *widget, gpointer)
1566 dialog = gtk_color_chooser_dialog_new(_("Image Overlay Text Color"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1567 color.red = options->image_overlay.text_red;
1568 color.green = options->image_overlay.text_green;
1569 color.blue = options->image_overlay.text_blue;
1570 color.alpha = options->image_overlay.text_alpha;
1571 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1572 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1574 g_signal_connect(dialog, "color-activated", G_CALLBACK(text_color_activated_cb), nullptr);
1575 g_signal_connect(dialog, "response", G_CALLBACK(text_color_response_cb), nullptr);
1577 gtk_widget_show(dialog);
1580 static void bg_color_activated_cb(GtkColorChooser *chooser, GdkRGBA *color, gpointer)
1582 c_options->image_overlay.background_red = color->red * 255;
1583 c_options->image_overlay.background_green = color->green * 255;
1584 c_options->image_overlay.background_blue = color->blue * 255;
1585 c_options->image_overlay.background_alpha = color->alpha * 255;
1587 gq_gtk_widget_destroy(GTK_WIDGET(chooser));
1590 static void bg_color_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1594 c_options->image_overlay.background_red = options->image_overlay.background_red;
1595 c_options->image_overlay.background_green = options->image_overlay.background_green;
1596 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
1597 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
1599 if (response_id == GTK_RESPONSE_OK)
1601 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1602 c_options->image_overlay.background_red = color.red * 255;
1603 c_options->image_overlay.background_green = color.green * 255;
1604 c_options->image_overlay.background_blue = color.blue * 255;
1605 c_options->image_overlay.background_alpha = color.alpha * 255;
1607 gq_gtk_widget_destroy(GTK_WIDGET(dialog));
1610 static void image_overlay_set_background_color_cb(GtkWidget *widget, gpointer)
1615 dialog = gtk_color_chooser_dialog_new(_("Image Overlay Background Color"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1616 color.red = options->image_overlay.background_red;
1617 color.green = options->image_overlay.background_green;
1618 color.blue = options->image_overlay.background_blue;
1619 color.alpha = options->image_overlay.background_alpha;
1620 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1621 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1623 g_signal_connect(dialog, "color-activated", G_CALLBACK(bg_color_activated_cb), nullptr);
1624 g_signal_connect(dialog, "response", G_CALLBACK(bg_color_response_cb), nullptr);
1626 gtk_widget_show(dialog);
1629 static void accel_store_populate()
1635 const gchar *accel_path;
1639 if (!accel_store || !layout_window_list || !layout_window_list->data) return;
1641 gtk_tree_store_clear(accel_store);
1642 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 */
1644 g_assert(lw && lw->ui_manager);
1645 groups = gtk_ui_manager_get_action_groups(lw->ui_manager);
1648 actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
1651 action = GTK_ACTION(actions->data);
1652 accel_path = gtk_action_get_accel_path(action);
1653 if (accel_path && gtk_accel_map_lookup_entry(accel_path, &key))
1659 g_object_get(action,
1660 "tooltip", &tooltip,
1664 if (pango_parse_markup(label, -1, '_', nullptr, &label2, nullptr, nullptr) && label2)
1670 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1674 gtk_tree_store_append(accel_store, &iter, nullptr);
1675 gtk_tree_store_set(accel_store, &iter,
1678 AE_TOOLTIP, tooltip ? tooltip : "",
1679 AE_ACCEL, accel_path,
1687 actions = actions->next;
1690 groups = groups->next;
1694 static void accel_store_cleared_cb(GtkCellRendererAccel *, gchar *, gpointer)
1699 static gboolean accel_remove_key_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer data)
1701 auto accel1 = static_cast<gchar *>(data);
1706 gtk_tree_model_get(model, iter, AE_KEY, &accel2, -1);
1708 gtk_accelerator_parse(accel1, &key1.accel_key, &key1.accel_mods);
1709 gtk_accelerator_parse(accel2, &key2.accel_key, &key2.accel_mods);
1711 if (key1.accel_key == key2.accel_key && key1.accel_mods == key2.accel_mods)
1713 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1714 DEBUG_1("accelerator key '%s' is already used, removing.", accel1);
1723 static void accel_store_edited_cb(GtkCellRendererAccel *, gchar *path_string, guint accel_key, GdkModifierType accel_mods, guint, gpointer)
1725 auto model = reinterpret_cast<GtkTreeModel *>(accel_store);
1729 GtkAccelKey old_key;
1731 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1733 gtk_tree_model_get_iter(model, &iter, path);
1734 gtk_tree_model_get(model, &iter, AE_ACCEL, &accel_path, -1);
1736 /* test if the accelerator can be stored without conflicts*/
1737 gtk_accel_map_lookup_entry(accel_path, &old_key);
1739 /* change the key and read it back (change may fail on keys hardcoded in gtk)*/
1740 gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, TRUE);
1741 gtk_accel_map_lookup_entry(accel_path, &key);
1743 /* restore the original for now, the key will be really changed when the changes are confirmed */
1744 gtk_accel_map_change_entry(accel_path, old_key.accel_key, old_key.accel_mods, TRUE);
1746 acc = gtk_accelerator_name(key.accel_key, key.accel_mods);
1747 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, acc);
1749 gtk_tree_store_set(accel_store, &iter, AE_KEY, acc, -1);
1750 gtk_tree_path_free(path);
1754 static gboolean accel_default_scroll(GtkTreeView *data)
1758 GtkTreeViewColumn *column;
1760 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(accel_store), &iter);
1761 path = gtk_tree_model_get_path(GTK_TREE_MODEL(accel_store), &iter);
1762 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1764 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1768 gtk_tree_path_free(path);
1770 return(G_SOURCE_REMOVE);
1773 static void accel_default_cb(GtkWidget *, gpointer data)
1775 accel_store_populate();
1777 g_idle_add(reinterpret_cast<GSourceFunc>(accel_default_scroll), data);
1780 void accel_clear_selection(GtkTreeModel *, GtkTreePath *, GtkTreeIter *iter, gpointer)
1782 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1785 void accel_reset_selection(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
1791 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, -1);
1792 gtk_accel_map_lookup_entry(accel_path, &key);
1793 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1795 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, accel);
1797 gtk_tree_store_set(accel_store, iter, AE_KEY, accel, -1);
1802 static void accel_clear_cb(GtkWidget *, gpointer data)
1804 GtkTreeSelection *selection;
1806 if (!accel_store) return;
1807 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1808 gtk_tree_selection_selected_foreach(selection, &accel_clear_selection, nullptr);
1811 static void accel_reset_cb(GtkWidget *, gpointer data)
1813 GtkTreeSelection *selection;
1815 if (!accel_store) return;
1816 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1817 gtk_tree_selection_selected_foreach(selection, &accel_reset_selection, nullptr);
1822 static GtkWidget *scrolled_notebook_page(GtkWidget *notebook, const gchar *title)
1826 GtkWidget *scrolled;
1827 GtkWidget *viewport;
1829 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
1830 gtk_container_set_border_width(GTK_CONTAINER(scrolled), PREF_PAD_BORDER);
1831 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1832 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1833 label = gtk_label_new(title);
1834 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, label);
1835 gtk_widget_show(scrolled);
1837 viewport = gtk_viewport_new(nullptr, nullptr);
1838 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1839 gq_gtk_container_add(GTK_WIDGET(scrolled), viewport);
1840 gtk_widget_show(viewport);
1842 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1843 gq_gtk_container_add(GTK_WIDGET(viewport), vbox);
1844 gtk_widget_show(vbox);
1849 static void cache_standard_cb(GtkWidget *widget, gpointer)
1851 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1853 c_options->thumbnails.spec_standard =TRUE;
1854 c_options->thumbnails.cache_into_dirs = FALSE;
1858 static void cache_geeqie_cb(GtkWidget *widget, gpointer)
1860 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1862 c_options->thumbnails.spec_standard =FALSE;
1863 c_options->thumbnails.cache_into_dirs = FALSE;
1867 static void cache_local_cb(GtkWidget *widget, gpointer)
1869 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1871 c_options->thumbnails.cache_into_dirs = TRUE;
1872 c_options->thumbnails.spec_standard =FALSE;
1876 static void help_search_engine_entry_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1878 if (pos == GTK_ENTRY_ICON_PRIMARY)
1880 gq_gtk_entry_set_text(GTK_ENTRY(userdata), HELP_SEARCH_ENGINE);
1884 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "");
1888 static void star_rating_star_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1890 gchar *rating_symbol;
1892 if (pos == GTK_ENTRY_ICON_PRIMARY)
1894 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_STAR);
1895 gq_gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1896 g_free(rating_symbol);
1900 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1901 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1902 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1906 static void star_rating_rejected_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1908 gchar *rating_symbol;
1910 if (pos == GTK_ENTRY_ICON_PRIMARY)
1912 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_REJECTED);
1913 gq_gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1914 g_free(rating_symbol);
1918 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1919 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1920 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1924 static guint star_rating_symbol_test(GtkWidget *, gpointer data)
1926 auto hbox = static_cast<GtkContainer *>(data);
1927 GString *str = g_string_new(nullptr);
1928 GtkEntry *hex_code_entry;
1929 gchar *hex_code_full;
1932 guint64 hex_value = 0;
1934 list = gtk_container_get_children(hbox);
1936 hex_code_entry = static_cast<GtkEntry *>(g_list_nth_data(list, 2));
1937 hex_code_full = g_strdup(gq_gtk_entry_get_text(hex_code_entry));
1939 hex_code = g_strsplit(hex_code_full, "+", 2);
1940 if (hex_code[0] && hex_code[1])
1942 hex_value = strtoull(hex_code[1], nullptr, 16);
1944 if (!hex_value || hex_value > 0x10FFFF)
1946 hex_value = 0x003F; // Unicode 'Question Mark'
1948 str = g_string_append_unichar(str, static_cast<gunichar>(hex_value));
1949 gtk_label_set_text(static_cast<GtkLabel *>(g_list_nth_data(list, 1)), str->str);
1951 g_strfreev(hex_code);
1952 g_string_free(str, TRUE);
1953 g_free(hex_code_full);
1958 static void star_rating_star_test_cb(GtkWidget *widget, gpointer data)
1960 guint64 star_symbol;
1962 star_symbol = star_rating_symbol_test(widget, data);
1963 c_options->star_rating.star = star_symbol;
1966 static void star_rating_rejected_test_cb(GtkWidget *widget, gpointer data)
1968 guint64 rejected_symbol;
1970 rejected_symbol = star_rating_symbol_test(widget, data);
1971 c_options->star_rating.rejected = rejected_symbol;
1974 /* general options tab */
1975 static void timezone_database_install_cb(GtkWidget *widget, gpointer data);
1979 GCancellable *cancellable;
1981 GtkWidget *progress;
1983 GFile *timezone_database_gq;
1984 gchar *timezone_database_user;
1987 static void config_tab_general(GtkWidget *notebook)
1992 GtkWidget *group_frame;
1993 GtkWidget *subgroup;
1995 GtkWidget *ct_button;
2002 GtkWidget *star_rating_entry;
2004 gchar *rating_symbol;
2007 gchar *download_locn;
2008 GNetworkMonitor *net_mon;
2009 GSocketConnectable *tz_org;
2010 gboolean internet_available = FALSE;
2013 vbox = scrolled_notebook_page(notebook, _("General"));
2015 group = pref_group_new(vbox, FALSE, _("Thumbnails"), GTK_ORIENTATION_VERTICAL);
2017 table = pref_table_new(group, 2, 2, FALSE, FALSE);
2018 add_thumb_size_menu(table, 0, 0, _("Size:"));
2019 add_quality_menu(table, 0, 1, _("Quality:"), options->thumbnails.quality, &c_options->thumbnails.quality);
2021 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2022 pref_label_new(hbox, _("Custom size: "));
2023 pref_spin_new_int(hbox, _("Width:"), nullptr, 1, 512, 1, options->thumbnails.max_width, &c_options->thumbnails.max_width);
2024 pref_spin_new_int(hbox, _("Height:"), nullptr, 1, 512, 1, options->thumbnails.max_height, &c_options->thumbnails.max_height);
2026 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails and sim. files"),
2027 options->thumbnails.enable_caching, &c_options->thumbnails.enable_caching);
2029 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2030 pref_checkbox_link_sensitivity(ct_button, subgroup);
2032 c_options->thumbnails.spec_standard = options->thumbnails.spec_standard;
2033 c_options->thumbnails.cache_into_dirs = options->thumbnails.cache_into_dirs;
2034 group_frame = pref_frame_new(subgroup, TRUE, _("Use Geeqie thumbnail style and cache"),
2035 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2036 button = pref_radiobutton_new(group_frame, nullptr, get_thumbnails_cache_dir(),
2037 !options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2038 G_CALLBACK(cache_geeqie_cb), nullptr);
2040 group_frame = pref_frame_new(subgroup, TRUE,
2041 _("Store thumbnails local to image folder (non-standard)"),
2042 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2043 pref_radiobutton_new(group_frame, button, "*/.thumbnails",
2044 !options->thumbnails.spec_standard && options->thumbnails.cache_into_dirs,
2045 G_CALLBACK(cache_local_cb), nullptr);
2047 group_frame = pref_frame_new(subgroup, TRUE,
2048 _("Use standard thumbnail style and cache, shared with other applications"),
2049 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2050 pref_radiobutton_new(group_frame, button, get_thumbnails_standard_cache_dir(),
2051 options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2052 G_CALLBACK(cache_standard_cb), nullptr);
2054 pref_checkbox_new_int(group, _("Use EXIF thumbnails when available (EXIF thumbnails may be outdated)"),
2055 options->thumbnails.use_exif, &c_options->thumbnails.use_exif);
2057 pref_checkbox_new_int(group, _("Thumbnail color management"),
2058 options->thumbnails.use_color_management, &c_options->thumbnails.use_color_management);
2060 spin = pref_spin_new_int(group, _("Collection preview:"), nullptr,
2062 options->thumbnails.collection_preview, &c_options->thumbnails.collection_preview);
2063 gtk_widget_set_tooltip_text(spin, _("The maximum number of thumbnails shown in a Collection preview montage"));
2065 #if HAVE_FFMPEGTHUMBNAILER_METADATA
2066 pref_checkbox_new_int(group, _("Use embedded metadata in video files as thumbnails when available"),
2067 options->thumbnails.use_ft_metadata, &c_options->thumbnails.use_ft_metadata);
2070 pref_spacer(group, PREF_PAD_GROUP);
2072 group = pref_group_new(vbox, FALSE, _("Star Rating"), GTK_ORIENTATION_VERTICAL);
2074 c_options->star_rating.star = options->star_rating.star;
2075 c_options->star_rating.rejected = options->star_rating.rejected;
2077 str = g_string_new(nullptr);
2078 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2079 pref_label_new(hbox, _("Star character: "));
2080 str = g_string_append_unichar(str, options->star_rating.star);
2081 pref_label_new(hbox, g_strdup(str->str));
2082 rating_symbol = g_strdup_printf("U+%X", options->star_rating.star);
2083 star_rating_entry = gtk_entry_new();
2084 gq_gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2085 gq_gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2086 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2087 gtk_widget_show(star_rating_entry);
2088 button = pref_button_new(nullptr, nullptr, _("Set"),
2089 G_CALLBACK(star_rating_star_test_cb), hbox);
2090 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2091 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2092 gtk_widget_show(button);
2093 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."));
2094 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2095 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2096 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2097 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2098 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2099 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2100 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2101 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2102 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2103 G_CALLBACK(star_rating_star_icon_cb),
2106 g_string_free(str, TRUE);
2107 g_free(rating_symbol);
2109 str = g_string_new(nullptr);
2110 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2111 pref_label_new(hbox, _("Rejected character: "));
2112 str = g_string_append_unichar(str, options->star_rating.rejected);
2113 pref_label_new(hbox, g_strdup(str->str));
2114 rating_symbol = g_strdup_printf("U+%X", options->star_rating.rejected);
2115 star_rating_entry = gtk_entry_new();
2116 gq_gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2117 gq_gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2118 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2119 gtk_widget_show(star_rating_entry);
2120 button = pref_button_new(nullptr, nullptr, _("Set"),
2121 G_CALLBACK(star_rating_rejected_test_cb), hbox);
2122 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2123 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2124 gtk_widget_show(button);
2125 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."));
2126 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2127 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2128 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2129 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2130 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2131 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2132 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2133 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2134 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2135 G_CALLBACK(star_rating_rejected_icon_cb),
2138 g_string_free(str, TRUE);
2139 g_free(rating_symbol);
2141 pref_spacer(group, PREF_PAD_GROUP);
2143 group = pref_group_new(vbox, FALSE, _("Slide show"), GTK_ORIENTATION_VERTICAL);
2145 c_options->slideshow.delay = options->slideshow.delay;
2146 hours = options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2147 remainder = options->slideshow.delay % (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2148 minutes = remainder / (60 * SLIDESHOW_SUBSECOND_PRECISION);
2149 seconds = static_cast<gdouble>(remainder % (60 * SLIDESHOW_SUBSECOND_PRECISION)) /
2150 SLIDESHOW_SUBSECOND_PRECISION;
2152 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2154 spin = pref_spin_new(hbox, _("Delay between image change hrs:mins:secs.dec"), nullptr,
2156 options->slideshow.delay ? hours : 0.0,
2157 G_CALLBACK(slideshow_delay_hours_cb), nullptr);
2158 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2159 spin = pref_spin_new(hbox, ":" , nullptr,
2161 options->slideshow.delay ? minutes: 0.0,
2162 G_CALLBACK(slideshow_delay_minutes_cb), nullptr);
2163 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2164 spin = pref_spin_new(hbox, ":", nullptr,
2165 SLIDESHOW_MIN_SECONDS, 59, 1.0, 1,
2166 options->slideshow.delay ? seconds : 10.0,
2167 G_CALLBACK(slideshow_delay_seconds_cb), nullptr);
2168 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2170 pref_checkbox_new_int(group, _("Random"), options->slideshow.random, &c_options->slideshow.random);
2171 pref_checkbox_new_int(group, _("Repeat"), options->slideshow.repeat, &c_options->slideshow.repeat);
2173 pref_spacer(group, PREF_PAD_GROUP);
2175 group = pref_group_new(vbox, FALSE, _("Image loading and caching"), GTK_ORIENTATION_VERTICAL);
2177 pref_spin_new_int(group, _("Decoded image cache size (MiB):"), nullptr,
2178 0, 99999, 1, options->image.image_cache_max, &c_options->image.image_cache_max);
2179 pref_checkbox_new_int(group, _("Preload next image"),
2180 options->image.enable_read_ahead, &c_options->image.enable_read_ahead);
2182 pref_checkbox_new_int(group, _("Refresh on file change"),
2183 options->update_on_time_change, &c_options->update_on_time_change);
2186 pref_spacer(group, PREF_PAD_GROUP);
2188 group = pref_group_new(vbox, FALSE, _("Menu style"), GTK_ORIENTATION_VERTICAL);
2190 pref_checkbox_new_int(group, _("☰ style menu button (NOTE! Geeqie must be restarted for change to take effect)"),
2191 options->hamburger_menu, &c_options->hamburger_menu);
2192 gtk_widget_set_tooltip_text(group, _("Use a ☰ style menu button instead of the classic style across the top of the frame"));
2194 pref_spacer(group, PREF_PAD_GROUP);
2196 group = pref_group_new(vbox, FALSE, _("Expand toolbar"), GTK_ORIENTATION_VERTICAL);
2198 pref_checkbox_new_int(group, _("Expand menu/toolbar (NOTE! Geeqie must be restarted for change to take effect)"),
2199 options->expand_menu_toolbar, &c_options->expand_menu_toolbar);
2200 gtk_widget_set_tooltip_text(group, _("Expand the menu/toolbar to the full width of the window"));
2202 pref_spacer(group, PREF_PAD_GROUP);
2204 group = pref_group_new(vbox, FALSE, _("Hide Selectable Bars"), GTK_ORIENTATION_VERTICAL);
2206 pref_checkbox_new_int(group, _("Menu bar"),
2207 options->selectable_bars.menu_bar, &c_options->selectable_bars.menu_bar);
2209 pref_checkbox_new_int(group, _("Tool bar"),
2210 options->selectable_bars.tool_bar, &c_options->selectable_bars.tool_bar);
2212 pref_checkbox_new_int(group, _("Status bar"),
2213 options->selectable_bars.status_bar, &c_options->selectable_bars.status_bar);
2214 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"));
2216 pref_spacer(group, PREF_PAD_GROUP);
2218 if (g_getenv("APPDIR") && strstr(g_getenv("APPDIR"), "/tmp/.mount_Geeqie"))
2220 group = pref_group_new(vbox, FALSE, _("AppImage updates notifications"), GTK_ORIENTATION_VERTICAL);
2221 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2222 pref_checkbox_new_int(group, _("Enable"), options->appimage_notifications, &c_options->appimage_notifications);
2223 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"));
2225 pref_spacer(group, PREF_PAD_GROUP);
2229 net_mon = g_network_monitor_get_default();
2230 tz_org = g_network_address_parse_uri(TIMEZONE_DATABASE_WEB, 80, nullptr);
2233 internet_available = g_network_monitor_can_reach(net_mon, tz_org, nullptr, nullptr);
2234 g_object_unref(tz_org);
2237 group = pref_group_new(vbox, FALSE, _("Timezone database"), GTK_ORIENTATION_VERTICAL);
2238 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2240 if (!internet_available)
2242 gtk_widget_set_sensitive(group, FALSE);
2245 tz = g_new0(TZData, 1);
2247 path = path_from_utf8(TIMEZONE_DATABASE_WEB);
2248 basename = g_path_get_basename(path);
2249 tz->timezone_database_user = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
2253 if (isfile(tz->timezone_database_user))
2255 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Update"), G_CALLBACK(timezone_database_install_cb), tz);
2259 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Install"), G_CALLBACK(timezone_database_install_cb), tz);
2262 download_locn = g_strconcat(_("Download database from: "), TIMEZONE_DATABASE_WEB, NULL);
2263 pref_label_new(GTK_WIDGET(hbox), download_locn);
2264 g_free(download_locn);
2266 if (!internet_available)
2268 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"));
2272 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"));
2274 gtk_widget_show(button);
2276 pref_spacer(group, PREF_PAD_GROUP);
2278 group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL);
2280 help_search_engine_entry = gtk_entry_new();
2281 gq_gtk_entry_set_text(GTK_ENTRY(help_search_engine_entry), options->help_search_engine);
2282 gq_gtk_box_pack_start(GTK_BOX(group), help_search_engine_entry, FALSE, FALSE, 0);
2283 gtk_widget_show(help_search_engine_entry);
2285 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"));
2287 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2288 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2289 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2290 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2291 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2292 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2293 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2294 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2295 g_signal_connect(GTK_ENTRY(help_search_engine_entry), "icon-press",
2296 G_CALLBACK(help_search_engine_entry_icon_cb),
2297 help_search_engine_entry);
2301 static void config_tab_image(GtkWidget *notebook)
2306 GtkWidget *ct_button;
2307 GtkWidget *enlargement_button;
2311 vbox = scrolled_notebook_page(notebook, _("Image"));
2313 group = pref_group_new(vbox, FALSE, _("Zoom"), GTK_ORIENTATION_VERTICAL);
2315 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2316 add_quality_menu(table, 0, 0, _("Quality:"), options->image.zoom_quality, &c_options->image.zoom_quality);
2318 pref_checkbox_new_int(group, _("Two pass rendering (apply HQ zoom and color correction in second pass)"),
2319 options->image.zoom_2pass, &c_options->image.zoom_2pass);
2321 c_options->image.zoom_increment = options->image.zoom_increment;
2322 spin = pref_spin_new(group, _("Zoom increment:"), nullptr,
2323 0.01, 4.0, 0.01, 2, static_cast<gdouble>(options->image.zoom_increment) / 100.0,
2324 G_CALLBACK(zoom_increment_cb), nullptr);
2325 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2327 c_options->image.zoom_style = options->image.zoom_style;
2328 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2329 add_zoom_style_selection_menu(table, 0, 0, _("Zoom style:"), options->image.zoom_style, &c_options->image.zoom_style);
2331 group = pref_group_new(vbox, FALSE, _("Fit image to window"), GTK_ORIENTATION_VERTICAL);
2333 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2334 enlargement_button = pref_checkbox_new_int(hbox, _("Allow enlargement of image (max. size in %)"),
2335 options->image.zoom_to_fit_allow_expand, &c_options->image.zoom_to_fit_allow_expand);
2336 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2338 options->image.max_enlargement_size, &c_options->image.max_enlargement_size);
2339 pref_checkbox_link_sensitivity(enlargement_button, spin);
2340 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."));
2342 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2343 ct_button = pref_checkbox_new_int(hbox, _("Virtual window size (%% of actual window):"),
2344 options->image.limit_autofit_size, &c_options->image.limit_autofit_size);
2345 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2347 options->image.max_autofit_size, &c_options->image.max_autofit_size);
2348 pref_checkbox_link_sensitivity(ct_button, spin);
2349 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."));
2351 group = pref_group_new(vbox, FALSE, _("Tile size"), GTK_ORIENTATION_VERTICAL);
2353 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2354 spin = pref_spin_new_int(hbox, _("Pixels"), _("(Requires restart)"),
2356 options->image.tile_size, &c_options->image.tile_size);
2357 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."));
2359 group = pref_group_new(vbox, FALSE, _("Appearance"), GTK_ORIENTATION_VERTICAL);
2361 pref_checkbox_new_int(group, _("Use custom border color in window mode"),
2362 options->image.use_custom_border_color, &c_options->image.use_custom_border_color);
2364 pref_checkbox_new_int(group, _("Use custom border color in fullscreen mode"),
2365 options->image.use_custom_border_color_in_fullscreen, &c_options->image.use_custom_border_color_in_fullscreen);
2367 pref_color_button_new(group, _("Border color"), &options->image.border_color,
2368 G_CALLBACK(pref_color_button_set_cb), &c_options->image.border_color);
2370 c_options->image.border_color = options->image.border_color;
2372 pref_color_button_new(group, _("Alpha channel color 1"), &options->image.alpha_color_1,
2373 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_1);
2375 pref_color_button_new(group, _("Alpha channel color 2"), &options->image.alpha_color_2,
2376 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_2);
2378 c_options->image.alpha_color_1 = options->image.alpha_color_1;
2379 c_options->image.alpha_color_2 = options->image.alpha_color_2;
2384 static void save_default_window_layout_cb(GtkWidget *, gpointer)
2386 LayoutWindow *lw = nullptr;
2387 gchar *default_path;
2390 /* Get current lw */
2393 tmp_id = lw->options.id;
2394 lw->options.id = g_strdup("");
2396 default_path = g_build_filename(get_rc_dir(), DEFAULT_WINDOW_LAYOUT, NULL);
2397 save_default_layout_options_to_file(default_path, options, lw);
2398 g_free(lw->options.id);
2399 lw->options.id = tmp_id;
2400 g_free(default_path);
2403 static gboolean popover_cb(gpointer data)
2405 auto popover = static_cast<GtkPopover *>(data);
2407 gtk_popover_popdown(popover);
2412 static void default_layout_changed_cb(GtkWidget *, GtkPopover *popover)
2414 gtk_popover_popup(popover);
2416 g_timeout_add(2000, popover_cb, popover);
2419 static GtkWidget *create_popover(GtkWidget *parent, GtkWidget *child, GtkPositionType pos)
2423 popover = gtk_popover_new(parent);
2424 gtk_popover_set_position(GTK_POPOVER (popover), pos);
2425 gq_gtk_container_add(GTK_WIDGET(popover), child);
2426 gtk_container_set_border_width(GTK_CONTAINER (popover), 6);
2427 gtk_widget_show (child);
2432 static void config_tab_windows(GtkWidget *notebook)
2437 GtkWidget *subgroup;
2439 GtkWidget *checkbox;
2440 GtkWidget *ct_button;
2443 vbox = scrolled_notebook_page(notebook, _("Windows"));
2445 group = pref_group_new(vbox, FALSE, _("State"), GTK_ORIENTATION_VERTICAL);
2447 ct_button = pref_checkbox_new_int(group, _("Remember session"),
2448 options->save_window_positions, &c_options->save_window_positions);
2450 checkbox = pref_checkbox_new_int(group, _("Use saved window positions also for new windows"),
2451 options->use_saved_window_positions_for_new_windows, &c_options->use_saved_window_positions_for_new_windows);
2452 pref_checkbox_link_sensitivity(ct_button, checkbox);
2454 checkbox = pref_checkbox_new_int(group, _("Remember window workspace"),
2455 options->save_window_workspace, &c_options->save_window_workspace);
2456 pref_checkbox_link_sensitivity(ct_button, checkbox);
2458 pref_checkbox_new_int(group, _("Remember tool state (float/hidden)"),
2459 options->tools_restore_state, &c_options->tools_restore_state);
2461 pref_checkbox_new_int(group, _("Remember dialog window positions"),
2462 options->save_dialog_window_positions, &c_options->save_dialog_window_positions);
2464 pref_checkbox_new_int(group, _("Show window IDs"),
2465 options->show_window_ids, &c_options->show_window_ids);
2467 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2468 pref_label_new(subgroup, _("Use current layout for default: "));
2469 button = pref_button_new(subgroup, nullptr, _("Set"), G_CALLBACK(save_default_window_layout_cb), nullptr);
2473 popover = create_popover(button, gtk_label_new(_("Current window layout\nhas been set as default")), GTK_POS_TOP);
2474 gtk_popover_set_modal(GTK_POPOVER (popover), FALSE);
2475 g_signal_connect(button, "clicked", G_CALLBACK(default_layout_changed_cb), popover);
2477 group = pref_group_new(vbox, FALSE, _("Size"), GTK_ORIENTATION_VERTICAL);
2479 pref_checkbox_new_int(group, _("Fit window to image when tools are hidden/floating"),
2480 options->image.fit_window_to_image, &c_options->image.fit_window_to_image);
2482 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2483 ct_button = pref_checkbox_new_int(hbox, _("Limit size when auto-sizing window (%):"),
2484 options->image.limit_window_size, &c_options->image.limit_window_size);
2485 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2487 options->image.max_window_size, &c_options->image.max_window_size);
2488 pref_checkbox_link_sensitivity(ct_button, spin);
2490 group = pref_group_new(vbox, FALSE, _("Full screen"), GTK_ORIENTATION_VERTICAL);
2492 c_options->fullscreen.screen = options->fullscreen.screen;
2493 c_options->fullscreen.above = options->fullscreen.above;
2494 hbox = fullscreen_prefs_selection_new(_("Location:"), &c_options->fullscreen.screen, &c_options->fullscreen.above);
2495 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2496 gtk_widget_show(hbox);
2498 pref_checkbox_new_int(group, _("Smooth image flip"),
2499 options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
2500 pref_checkbox_new_int(group, _("Disable screen saver"),
2501 options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
2505 PRE_FORMATTED_COLUMNS = 5
2507 static void config_tab_osd(GtkWidget *notebook)
2513 GtkWidget *image_overlay_template_view;
2514 GtkWidget *scrolled;
2515 GtkWidget *scrolled_pre_formatted;
2516 GtkTextBuffer *buffer;
2518 GtkWidget *subgroup;
2520 vbox = scrolled_notebook_page(notebook, _("OSD"));
2522 image_overlay_template_view = gtk_text_view_new();
2524 group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
2526 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2528 scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
2529 gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
2530 gq_gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
2531 gtk_widget_show(scrolled_pre_formatted);
2532 gtk_widget_show(subgroup);
2534 pref_line(group, PREF_PAD_GAP);
2536 pref_label_new(group, _("Image overlay template"));
2538 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2539 gtk_widget_set_size_request(scrolled, 200, 150);
2540 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2541 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
2542 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2543 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
2544 gtk_widget_show(scrolled);
2546 gtk_widget_set_tooltip_markup(image_overlay_template_view,
2547 _("Extensive formatting options are shown in the Help file"));
2549 gq_gtk_container_add(GTK_WIDGET(scrolled), image_overlay_template_view);
2550 gtk_widget_show(image_overlay_template_view);
2552 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2554 button = pref_button_new(nullptr, GQ_ICON_SELECT_FONT, _("Font"),
2555 G_CALLBACK(image_overlay_set_font_cb), notebook);
2557 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2558 gtk_widget_show(button);
2560 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Text"), G_CALLBACK(image_overlay_set_text_color_cb), nullptr);
2561 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2562 gtk_widget_show(button);
2564 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Background"), G_CALLBACK(image_overlay_set_background_color_cb), nullptr);
2565 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2566 gtk_widget_show(button);
2567 image_overlay_set_text_colors();
2569 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2570 G_CALLBACK(image_overlay_default_template_cb), image_overlay_template_view);
2571 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2572 gtk_widget_show(button);
2574 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
2575 G_CALLBACK(image_overlay_help_cb), nullptr);
2576 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2577 gtk_widget_show(button);
2579 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_overlay_template_view));
2580 if (options->image_overlay.template_string) gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
2581 g_signal_connect(G_OBJECT(buffer), "changed",
2582 G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
2584 pref_line(group, PREF_PAD_GAP);
2586 group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL);
2587 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2588 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2589 gtk_widget_show(hbox);
2590 label = gtk_label_new(_("%Exif.Image.Orientation%"));
2591 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2592 gtk_widget_show(label);
2593 pref_spacer(group,TRUE);
2595 group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL);
2596 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2597 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2598 gtk_widget_show(hbox);
2599 label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
2600 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2601 gtk_widget_show(label);
2602 pref_spacer(group,TRUE);
2604 group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL);
2605 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2606 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2607 gtk_widget_show(hbox);
2608 label = gtk_label_new(_("%path:39%"));
2609 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2610 gtk_widget_show(label);
2611 pref_spacer(group,TRUE);
2613 group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL);
2614 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2615 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2616 gtk_widget_show(hbox);
2617 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%"));
2618 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2619 gtk_widget_show(label);
2620 pref_spacer(group,TRUE);
2622 group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL);
2623 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2624 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2625 gtk_widget_show(hbox);
2626 label = gtk_label_new(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
2627 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2628 gtk_widget_show(label);
2631 static GtkTreeModel *create_class_model()
2633 GtkListStore *model;
2637 /* create list store */
2638 model = gtk_list_store_new(1, G_TYPE_STRING);
2639 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
2641 gtk_list_store_append(model, &iter);
2642 gtk_list_store_set(model, &iter, 0, _(format_class_list[i]), -1);
2644 return GTK_TREE_MODEL (model);
2649 static gint filter_table_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
2651 gint n = GPOINTER_TO_INT(data);
2653 FilterEntry *filter_a;
2654 FilterEntry *filter_b;
2656 gtk_tree_model_get(model, a, 0, &filter_a, -1);
2657 gtk_tree_model_get(model, b, 0, &filter_b, -1);
2661 case FILETYPES_COLUMN_ENABLED:
2663 ret = filter_a->enabled - filter_b->enabled;
2666 case FILETYPES_COLUMN_FILTER:
2668 ret = g_utf8_collate(filter_a->extensions, filter_b->extensions);
2671 case FILETYPES_COLUMN_DESCRIPTION:
2673 ret = g_utf8_collate(filter_a->description, filter_b->description);
2676 case FILETYPES_COLUMN_CLASS:
2678 ret = g_strcmp0(format_class_list[filter_a->file_class], format_class_list[filter_b->file_class]);
2681 case FILETYPES_COLUMN_WRITABLE:
2683 ret = filter_a->writable - filter_b->writable;
2686 case FILETYPES_COLUMN_SIDECAR:
2688 ret = filter_a->allow_sidecar - filter_b->allow_sidecar;
2692 g_return_val_if_reached(0);
2698 static gboolean search_function_cb(GtkTreeModel *model, gint, const gchar *key, GtkTreeIter *iter, gpointer)
2701 gboolean ret = TRUE;
2703 gtk_tree_model_get(model, iter, 0, &fe, -1);
2705 if (g_strstr_len(fe->extensions, -1, key))
2713 static void config_tab_files(GtkWidget *notebook)
2720 GtkWidget *ct_button;
2721 GtkWidget *scrolled;
2722 GtkWidget *filter_view;
2723 GtkCellRenderer *renderer;
2724 GtkTreeSelection *selection;
2725 GtkTreeViewColumn *column;
2727 vbox = scrolled_notebook_page(notebook, _("File Filters"));
2729 group = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2731 pref_checkbox_new_int(group, _("Show hidden files or folders"),
2732 options->file_filter.show_hidden_files, &c_options->file_filter.show_hidden_files);
2733 pref_checkbox_new_int(group, _("Show parent folder (..)"),
2734 options->file_filter.show_parent_directory, &c_options->file_filter.show_parent_directory);
2735 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);
2736 pref_checkbox_new_int(group, _("Disable file extension checks"),
2737 options->file_filter.disable_file_extension_checks, &c_options->file_filter.disable_file_extension_checks);
2739 ct_button = pref_checkbox_new_int(group, _("Disable File Filtering"),
2740 options->file_filter.disable, &c_options->file_filter.disable);
2743 group = pref_group_new(vbox, FALSE, _("Grouping sidecar extensions"), GTK_ORIENTATION_VERTICAL);
2745 sidecar_ext_entry = gtk_entry_new();
2746 gq_gtk_entry_set_text(GTK_ENTRY(sidecar_ext_entry), options->sidecar.ext);
2747 gq_gtk_box_pack_start(GTK_BOX(group), sidecar_ext_entry, FALSE, FALSE, 0);
2748 gtk_widget_show(sidecar_ext_entry);
2750 group = pref_group_new(vbox, TRUE, _("File types"), GTK_ORIENTATION_VERTICAL);
2752 frame = pref_group_parent(group);
2753 g_signal_connect(G_OBJECT(ct_button), "toggled",
2754 G_CALLBACK(filter_disable_cb), frame);
2755 gtk_widget_set_sensitive(frame, !options->file_filter.disable);
2757 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2758 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2759 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2760 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2761 gtk_widget_show(scrolled);
2763 filter_store = gtk_list_store_new(1, G_TYPE_POINTER);
2764 filter_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
2765 g_object_unref(filter_store);
2766 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
2767 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2769 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), FALSE);
2771 column = gtk_tree_view_column_new();
2772 gtk_tree_view_column_set_title(column, _("Enabled"));
2773 gtk_tree_view_column_set_resizable(column, TRUE);
2775 renderer = gtk_cell_renderer_toggle_new();
2776 g_signal_connect(G_OBJECT(renderer), "toggled",
2777 G_CALLBACK(filter_store_enable_cb), filter_store);
2778 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2779 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2780 GINT_TO_POINTER(FE_ENABLE), nullptr);
2781 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);
2782 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_ENABLED);
2783 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2785 column = gtk_tree_view_column_new();
2786 gtk_tree_view_column_set_title(column, _("Filter"));
2787 gtk_tree_view_column_set_resizable(column, TRUE);
2788 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);
2789 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_FILTER);
2791 renderer = gtk_cell_renderer_text_new();
2792 g_signal_connect(G_OBJECT(renderer), "edited",
2793 G_CALLBACK(filter_store_ext_edit_cb), filter_store);
2794 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2795 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2796 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2797 GINT_TO_POINTER(FE_EXTENSION), nullptr);
2798 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2800 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), TRUE);
2801 gtk_tree_view_set_search_column(GTK_TREE_VIEW(filter_view), FILETYPES_COLUMN_FILTER);
2802 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(filter_view), search_function_cb, nullptr, nullptr);
2804 column = gtk_tree_view_column_new();
2805 gtk_tree_view_column_set_title(column, _("Description"));
2806 gtk_tree_view_column_set_resizable(column, TRUE);
2807 gtk_tree_view_column_set_fixed_width(column, 200);
2808 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2810 renderer = gtk_cell_renderer_text_new();
2811 g_signal_connect(G_OBJECT(renderer), "edited",
2812 G_CALLBACK(filter_store_desc_edit_cb), filter_store);
2813 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2814 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2815 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2816 GINT_TO_POINTER(FE_DESCRIPTION), nullptr);
2817 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2818 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(filter_store), FILETYPES_COLUMN_DESCRIPTION, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_DESCRIPTION), nullptr);
2819 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_DESCRIPTION);
2821 column = gtk_tree_view_column_new();
2822 gtk_tree_view_column_set_title(column, _("Class"));
2823 gtk_tree_view_column_set_resizable(column, TRUE);
2824 renderer = gtk_cell_renderer_combo_new();
2825 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE,
2826 "model", create_class_model(),
2831 g_signal_connect(G_OBJECT(renderer), "edited",
2832 G_CALLBACK(filter_store_class_edit_cb), filter_store);
2833 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2834 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2835 GINT_TO_POINTER(FE_CLASS), nullptr);
2836 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2837 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);
2838 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_CLASS);
2840 column = gtk_tree_view_column_new();
2841 gtk_tree_view_column_set_title(column, _("Writable"));
2842 gtk_tree_view_column_set_resizable(column, FALSE);
2843 renderer = gtk_cell_renderer_toggle_new();
2844 g_signal_connect(G_OBJECT(renderer), "toggled",
2845 G_CALLBACK(filter_store_writable_cb), filter_store);
2846 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2847 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2848 GINT_TO_POINTER(FE_WRITABLE), nullptr);
2849 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2850 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);
2851 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_WRITABLE);
2853 column = gtk_tree_view_column_new();
2854 gtk_tree_view_column_set_title(column, _("Sidecar is allowed"));
2855 gtk_tree_view_column_set_resizable(column, FALSE);
2856 renderer = gtk_cell_renderer_toggle_new();
2857 g_signal_connect(G_OBJECT(renderer), "toggled",
2858 G_CALLBACK(filter_store_sidecar_cb), filter_store);
2859 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2860 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2861 GINT_TO_POINTER(FE_ALLOW_SIDECAR), nullptr);
2862 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2863 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);
2864 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_SIDECAR);
2866 filter_store_populate();
2867 gq_gtk_container_add(GTK_WIDGET(scrolled), filter_view);
2868 gtk_widget_show(filter_view);
2870 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2872 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2873 G_CALLBACK(filter_default_cb), filter_view);
2874 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2875 gtk_widget_show(button);
2877 button = pref_button_new(nullptr, GQ_ICON_REMOVE, _("Remove"),
2878 G_CALLBACK(filter_remove_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_ADD, _("Add"),
2883 G_CALLBACK(filter_add_cb), filter_view);
2884 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2885 gtk_widget_show(button);
2889 static void config_tab_metadata(GtkWidget *notebook)
2894 GtkWidget *ct_button;
2896 GtkWidget *tmp_widget;
2898 GtkWidget *text_label;
2900 vbox = scrolled_notebook_page(notebook, _("Metadata"));
2903 group = pref_group_new(vbox, FALSE, _("Metadata writing sequence"), GTK_ORIENTATION_VERTICAL);
2905 label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2907 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."));
2908 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2909 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2911 gtk_widget_set_tooltip_text(label, _("A flowchart of the sequence is shown in the Help file"));
2913 ct_button = pref_checkbox_new_int(group, "", options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2914 text_label = gtk_bin_get_child(GTK_BIN(ct_button));
2915 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"));
2916 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2918 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)"));
2919 gtk_widget_set_tooltip_markup(ct_button, markup);
2923 gtk_widget_set_sensitive(ct_button, FALSE);
2926 tmp_widget = pref_checkbox_new_int(group, "", options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2927 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2928 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)"));
2929 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2932 label = pref_label_new(group, "");
2933 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(), "/");
2934 gtk_label_set_markup (GTK_LABEL(label), markup);
2937 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2938 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2939 gtk_widget_set_margin_start(label, 22);
2940 pref_spacer(group, PREF_PAD_GROUP);
2942 group = pref_group_new(vbox, FALSE, _("Step 1 Options:"), GTK_ORIENTATION_VERTICAL);
2944 gtk_widget_set_sensitive(group, FALSE);
2947 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2948 pref_checkbox_link_sensitivity(ct_button, hbox);
2950 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);
2951 gtk_widget_set_tooltip_text(tmp_widget, _("A simplified conversion list is in the Help file"));
2953 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);
2955 pref_checkbox_new_int(hbox, _("Ask before writing to image files"), options->metadata.confirm_write, &c_options->metadata.confirm_write);
2957 tmp_widget= pref_checkbox_new_int(hbox, "", options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2958 gtk_widget_set_tooltip_text(tmp_widget, _("This file naming convention is used by Darktable"));
2959 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2960 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", ")");
2961 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2964 pref_spacer(group, PREF_PAD_GROUP);
2966 group = pref_group_new(vbox, FALSE, _("Steps 2 and 3 Option:"), GTK_ORIENTATION_VERTICAL);
2968 gtk_widget_set_sensitive(group, FALSE);
2971 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);
2973 pref_spacer(group, PREF_PAD_GROUP);
2975 group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2976 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);
2977 gtk_widget_set_tooltip_text(tmp_widget, _("See the Help file for a list of the tags used"));
2979 tmp_widget = pref_checkbox_new_int(group, _("Permit Keywords to be case-sensitive"), options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2980 gtk_widget_set_tooltip_text(tmp_widget, _("When selected, \"Place\" and \"place\" are two different keywords"));
2982 ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"), options->metadata.write_orientation, &c_options->metadata.write_orientation);
2983 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"));
2986 gtk_widget_set_sensitive(ct_button, FALSE);
2989 pref_spacer(group, PREF_PAD_GROUP);
2991 group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2993 ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"), options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
2995 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2996 pref_checkbox_link_sensitivity(ct_button, hbox);
2998 pref_spin_new_int(hbox, _("Timeout (seconds):"), nullptr, 0, 900, 1, options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
3000 pref_checkbox_new_int(group, _("Write metadata on image change"), options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
3002 pref_checkbox_new_int(group, _("Write metadata on directory change"), options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
3004 pref_spacer(group, PREF_PAD_GROUP);
3007 group = pref_group_new(vbox, FALSE, _("Spelling checks"), GTK_ORIENTATION_VERTICAL);
3009 ct_button = pref_checkbox_new_int(group, _("Check spelling - Requires restart"), options->metadata.check_spelling, &c_options->metadata.check_spelling);
3010 gtk_widget_set_tooltip_text(ct_button, _("Spelling checks are performed on info sidebar panes Comment, Headline and Title"));
3013 pref_spacer(group, PREF_PAD_GROUP);
3015 group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
3017 ct_button = pref_checkbox_new_int(group, _("Read metadata in background"), options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
3018 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");
3023 struct KeywordFindData
3030 GtkWidget *button_close;
3031 GtkWidget *button_stop;
3032 GtkWidget *button_start;
3033 GtkWidget *progress;
3041 guint idle_id; /* event source id */
3045 KEYWORD_DIALOG_WIDTH = 400
3048 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
3050 GList *list_d = nullptr;
3051 GList *list_f = nullptr;
3055 filelist_read(dir_fd, &list_f, &list_d);
3059 filelist_read(dir_fd, &list_f, nullptr);
3062 list_f = filelist_filter(list_f, FALSE);
3063 list_d = filelist_filter(list_d, TRUE);
3065 kfd->list = g_list_concat(list_f, kfd->list);
3066 kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
3069 static void keywords_find_reset(KeywordFindData *kfd)
3071 filelist_free(kfd->list);
3072 kfd->list = nullptr;
3074 filelist_free(kfd->list_dir);
3075 kfd->list_dir = nullptr;
3078 static void keywords_find_close_cb(GenericDialog *, gpointer data)
3080 auto kfd = static_cast<KeywordFindData *>(data);
3082 if (!gtk_widget_get_sensitive(kfd->button_close)) return;
3084 keywords_find_reset(kfd);
3085 generic_dialog_close(kfd->gd);
3089 static void keywords_find_finish(KeywordFindData *kfd)
3091 keywords_find_reset(kfd);
3093 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
3094 gtk_spinner_stop(GTK_SPINNER(kfd->spinner));
3096 gtk_widget_set_sensitive(kfd->group, TRUE);
3097 gtk_widget_set_sensitive(kfd->button_start, TRUE);
3098 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3099 gtk_widget_set_sensitive(kfd->button_close, TRUE);
3102 static void keywords_find_stop_cb(GenericDialog *, gpointer data)
3104 auto kfd = static_cast<KeywordFindData *>(data);
3106 g_idle_remove_by_data(kfd);
3108 keywords_find_finish(kfd);
3111 static gboolean keywords_find_file(gpointer data)
3113 auto kfd = static_cast<KeywordFindData *>(data);
3115 GtkTextBuffer *buffer;
3123 fd = static_cast<FileData *>(kfd->list->data);
3124 kfd->list = g_list_remove(kfd->list, fd);
3126 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
3127 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3131 gtk_text_buffer_get_end_iter(buffer, &iter);
3132 tmp = g_strconcat(static_cast<const gchar *>(keywords->data), "\n", NULL);
3133 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3135 keywords = keywords->next;
3138 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
3139 file_data_unref(fd);
3140 g_list_free_full(keywords, g_free);
3142 return (G_SOURCE_CONTINUE);
3149 fd = static_cast<FileData *>(kfd->list_dir->data);
3150 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
3152 keywords_find_folder(kfd, fd);
3154 file_data_unref(fd);
3156 return G_SOURCE_CONTINUE;
3159 keywords_find_finish(kfd);
3161 return G_SOURCE_REMOVE;
3164 static void keywords_find_start_cb(GenericDialog *, gpointer data)
3166 auto kfd = static_cast<KeywordFindData *>(data);
3169 if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
3171 path = remove_trailing_slash((gq_gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
3172 parse_out_relatives(path);
3176 warning_dialog(_("Invalid folder"),
3177 _("The specified folder can not be found."),
3178 GQ_ICON_DIALOG_WARNING, kfd->gd->dialog);
3184 gtk_widget_set_sensitive(kfd->group, FALSE);
3185 gtk_widget_set_sensitive(kfd->button_start, FALSE);
3186 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
3187 gtk_widget_set_sensitive(kfd->button_close, FALSE);
3188 gtk_spinner_start(GTK_SPINNER(kfd->spinner));
3190 dir_fd = file_data_new_dir(path);
3191 keywords_find_folder(kfd, dir_fd);
3192 file_data_unref(dir_fd);
3193 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
3199 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
3201 KeywordFindData *kfd;
3205 kfd = g_new0(KeywordFindData, 1);
3207 kfd->gd = generic_dialog_new(_("Search for keywords"),
3208 "search_for_keywords",
3211 gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
3212 kfd->gd->cancel_cb = keywords_find_close_cb;
3213 kfd->button_close = generic_dialog_add_button(kfd->gd, GQ_ICON_CLOSE, _("Close"),
3214 keywords_find_close_cb, FALSE);
3215 kfd->button_start = generic_dialog_add_button(kfd->gd, GQ_ICON_OK, _("S_tart"),
3216 keywords_find_start_cb, FALSE);
3217 kfd->button_stop = generic_dialog_add_button(kfd->gd, GQ_ICON_STOP, _("Stop"),
3218 keywords_find_stop_cb, FALSE);
3219 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3221 generic_dialog_add_message(kfd->gd, nullptr, _("Search for keywords"), nullptr, FALSE);
3223 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3224 pref_spacer(hbox, PREF_PAD_INDENT);
3225 kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3227 hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3228 pref_label_new(hbox, _("Folder:"));
3230 label = tab_completion_new(&kfd->entry, path, nullptr, nullptr, nullptr, nullptr);
3231 tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
3232 gq_gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3233 gtk_widget_show(label);
3235 pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
3237 pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
3238 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3240 kfd->progress = gtk_entry_new();
3241 gtk_widget_set_can_focus(kfd->progress, FALSE);
3242 gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
3243 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
3244 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
3245 gtk_widget_show(kfd->progress);
3247 kfd->spinner = gtk_spinner_new();
3248 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
3249 gtk_widget_show(kfd->spinner);
3251 kfd->list = nullptr;
3253 gtk_widget_show(kfd->gd->dialog);
3256 static void keywords_find_cb(GtkWidget *widget, gpointer)
3258 const gchar *path = layout_get_path(nullptr);
3260 if (!path || !*path) path = homedir();
3261 keywords_find_dialog(widget, path);
3264 static void config_tab_keywords_save()
3268 GtkTextBuffer *buffer;
3269 GList *kw_list = nullptr;
3275 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3276 gtk_text_buffer_get_bounds(buffer, &start, &end);
3278 buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3280 kw_split = strtok(buffer_text, "\n");
3281 while (kw_split != nullptr)
3287 if (g_strcmp0(static_cast<const gchar *>(work->data), kw_split) == 0)
3296 kw_list = g_list_append(kw_list, g_strdup(kw_split));
3298 kw_split = strtok(nullptr, "\n");
3301 keyword_list_set(kw_list);
3303 g_list_free_full(kw_list, g_free);
3304 g_free(buffer_text);
3307 static void config_tab_keywords(GtkWidget *notebook)
3313 GtkWidget *scrolled;
3315 GtkTextBuffer *buffer;
3318 GspellTextView *gspell_view;
3321 vbox = scrolled_notebook_page(notebook, _("Keywords"));
3323 group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
3325 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3327 button = pref_button_new(hbox, GQ_ICON_RUN, _("Search"),
3328 G_CALLBACK(keywords_find_cb), keyword_text);
3329 gtk_widget_set_tooltip_text(button, _("Search for existing keywords"));
3332 keyword_text = gtk_text_view_new();
3333 gtk_widget_set_size_request(keyword_text, 20, 20);
3334 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3335 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3336 gtk_widget_show(scrolled);
3339 if (options->metadata.check_spelling)
3341 gspell_view = gspell_text_view_get_from_gtk_text_view(GTK_TEXT_VIEW(keyword_text));
3342 gspell_text_view_basic_setup(gspell_view);
3346 gq_gtk_container_add(GTK_WIDGET(scrolled), keyword_text);
3347 gtk_widget_show(keyword_text);
3349 gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
3351 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3352 gtk_text_buffer_create_tag(buffer, "monospace",
3353 "family", "monospace", NULL);
3355 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3356 gtk_text_buffer_get_start_iter(buffer, &iter);
3357 gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3360 path = g_build_filename(get_rc_dir(), "keywords", NULL);
3362 GList *kwl = keyword_list_get();
3363 kwl = g_list_first(kwl);
3366 gtk_text_buffer_get_end_iter (buffer, &iter);
3367 tmp = g_strconcat(static_cast<const gchar *>(kwl->data), "\n", NULL);
3368 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3373 gtk_text_buffer_set_modified(buffer, FALSE);
3380 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3382 auto option = static_cast<gint *>(data);
3384 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3388 *option = INTENT_PERCEPTUAL;
3391 *option = INTENT_RELATIVE_COLORIMETRIC;
3394 *option = INTENT_SATURATION;
3397 *option = INTENT_ABSOLUTE_COLORIMETRIC;
3402 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3403 gint option, gint *option_c)
3410 pref_table_label(table, column, row, text, GTK_ALIGN_START);
3412 combo = gtk_combo_box_text_new();
3414 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3415 if (option == INTENT_PERCEPTUAL) current = 0;
3416 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3417 if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3418 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3419 if (option == INTENT_SATURATION) current = 2;
3420 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3421 if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3423 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3425 gtk_widget_set_tooltip_text(combo,_("Refer to the lcms documentation for the defaults used when the selected Intent is not available"));
3427 g_signal_connect(G_OBJECT(combo), "changed",
3428 G_CALLBACK(intent_menu_cb), option_c);
3430 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
3431 gtk_widget_show(combo);
3435 static void config_tab_color(GtkWidget *notebook)
3444 vbox = scrolled_notebook_page(notebook, _("Color management"));
3446 group = pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3448 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3451 table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3452 gtk_grid_set_column_spacing(GTK_GRID(table), PREF_PAD_GAP);
3454 label = pref_table_label(table, 0, 0, _("Type"), GTK_ALIGN_START);
3455 pref_label_bold(label, TRUE, FALSE);
3457 label = pref_table_label(table, 1, 0, _("Menu name"), GTK_ALIGN_START);
3458 pref_label_bold(label, TRUE, FALSE);
3460 label = pref_table_label(table, 2, 0, _("File"), GTK_ALIGN_START);
3461 pref_label_bold(label, TRUE, FALSE);
3463 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3468 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3469 pref_table_label(table, 0, i + 1, buf, GTK_ALIGN_END);
3472 entry = gtk_entry_new();
3473 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3474 if (options->color_profile.input_name[i])
3476 gq_gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3478 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);
3479 gtk_widget_show(entry);
3480 color_profile_input_name_entry[i] = entry;
3482 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], nullptr, ".icc", "ICC Files", nullptr);
3483 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3484 gtk_widget_set_size_request(entry, 160, -1);
3485 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);
3486 gtk_widget_show(tabcomp);
3487 color_profile_input_file_entry[i] = entry;
3490 group = pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3492 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3494 pref_checkbox_new_int(group, _("Use system screen profile if available"),
3495 options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3497 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3499 pref_table_label(table, 0, 0, _("Screen:"), GTK_ALIGN_END);
3500 tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3501 options->color_profile.screen_file, nullptr, ".icc", "ICC Files", nullptr);
3502 tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3503 gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3505 add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3507 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);
3509 gtk_widget_show(tabcomp);
3512 /* advanced entry tab */
3513 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer)
3515 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3517 c_options->file_ops.use_system_trash = FALSE;
3518 c_options->file_ops.no_trash = FALSE;
3522 static void use_system_trash_cb(GtkWidget *widget, gpointer)
3524 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3526 c_options->file_ops.use_system_trash = TRUE;
3527 c_options->file_ops.no_trash = FALSE;
3531 static void use_no_cache_cb(GtkWidget *widget, gpointer)
3533 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3535 c_options->file_ops.no_trash = TRUE;
3539 static void config_tab_behavior(GtkWidget *notebook)
3546 GtkWidget *ct_button;
3550 GtkWidget *with_rename;
3551 GtkWidget *collections_on_top;
3552 GtkWidget *hide_window_in_fullscreen;
3553 GtkWidget *checkbox;
3556 vbox = scrolled_notebook_page(notebook, _("Behavior"));
3558 group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3560 pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3561 options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3562 pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3563 options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3564 pref_checkbox_new_int(group, _("Enable Delete key"),
3565 options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3567 ct_button = pref_radiobutton_new(group, nullptr, _("Use Geeqie trash location"),
3568 !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),nullptr);
3570 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3571 pref_checkbox_link_sensitivity(ct_button, hbox);
3573 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3574 pref_label_new(hbox, _("Folder:"));
3576 tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, nullptr, nullptr, nullptr, nullptr);
3577 tab_completion_add_select_button(safe_delete_path_entry, nullptr, TRUE);
3578 gq_gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3579 gtk_widget_show(tabcomp);
3581 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3582 pref_checkbox_link_sensitivity(ct_button, hbox);
3584 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3585 spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MiB"),
3586 0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3587 gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3588 button = pref_button_new(nullptr, nullptr, _("View"),
3589 G_CALLBACK(safe_delete_view_cb), nullptr);
3590 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3591 gtk_widget_show(button);
3593 button = pref_button_new(nullptr, GQ_ICON_CLEAR, nullptr,
3594 G_CALLBACK(safe_delete_clear_cb), nullptr);
3595 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3597 c_options->file_ops.no_trash = options->file_ops.no_trash;
3598 c_options->file_ops.use_system_trash = options->file_ops.use_system_trash;
3600 pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3601 options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), nullptr);
3603 pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3604 options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), nullptr);
3606 gtk_widget_show(button);
3608 pref_spacer(group, PREF_PAD_GROUP);
3611 group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3613 pref_checkbox_new_int(group, _("Descend folders in tree view"),
3614 options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3616 pref_checkbox_new_int(group, _("In place renaming"),
3617 options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3619 pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3620 options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3622 tmp = pref_checkbox_new_int(group, _("Circular selection lists"),
3623 options->circular_selection_lists, &c_options->circular_selection_lists);
3624 gtk_widget_set_tooltip_text(tmp, _("Traverse selection lists in a circular manner"));
3626 marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3627 options->marks_save, &c_options->marks_save);
3628 gtk_widget_set_tooltip_text(marks,_("Note that marks linked to a keyword will be saved irrespective of this setting"));
3630 with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3631 options->with_rename, &c_options->with_rename);
3632 gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3634 collections_on_top = pref_checkbox_new_int(group, _("Permit duplicates in Collections"),
3635 options->collections_duplicates, &c_options->collections_duplicates);
3636 gtk_widget_set_tooltip_text(collections_on_top, _("Allow the same image to be in a Collection more than once"));
3638 collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3639 options->collections_on_top, &c_options->collections_on_top);
3640 gtk_widget_set_tooltip_text(collections_on_top, _("Open collections window on top"));
3642 hide_window_in_fullscreen = pref_checkbox_new_int(group, _("Hide window in fullscreen"),
3643 options->hide_window_in_fullscreen, &c_options->hide_window_in_fullscreen);
3644 gtk_widget_set_tooltip_text(hide_window_in_fullscreen,"When alt-tabbing, prevent Geeqie window showing twice");
3646 pref_spin_new_int(group, _("Recent folder list maximum size"), nullptr,
3647 1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3649 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);
3650 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."));
3652 pref_spin_new_int(group, _("Drag'n drop icon size"), nullptr,
3653 16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3655 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3656 add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
3658 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3659 add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3661 pref_spacer(group, PREF_PAD_GROUP);
3663 group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3665 pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3666 options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3667 pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), nullptr,
3668 1, 32, 1, options->keyboard_scroll_step, reinterpret_cast<int *>(&c_options->keyboard_scroll_step));
3669 pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3670 options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3671 pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3672 options->image_lm_click_nav, &c_options->image_lm_click_nav);
3673 pref_checkbox_new_int(group, _("Open archive by left click on image"),
3674 options->image_l_click_archive, &c_options->image_l_click_archive);
3675 pref_checkbox_new_int(group, _("Play video by left click on image"),
3676 options->image_l_click_video, &c_options->image_l_click_video);
3677 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3678 add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3680 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3681 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3682 add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3683 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3684 add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3686 pref_spacer(group, PREF_PAD_GROUP);
3688 group = pref_group_new(vbox, FALSE, _("GPU"), GTK_ORIENTATION_VERTICAL);
3690 checkbox = pref_checkbox_new_int(group, _("Override disable GPU"),
3691 options->override_disable_gpu, &c_options->override_disable_gpu);
3692 gtk_widget_set_tooltip_text(checkbox, _("Contact the developers for usage"));
3695 pref_spacer(group, PREF_PAD_GROUP);
3697 group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3699 pref_spin_new_int(group, _("Debug level:"), nullptr,
3700 DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3702 pref_checkbox_new_int(group, _("Timer data"),
3703 options->log_window.timer_data, &c_options->log_window.timer_data);
3705 pref_spin_new_int(group, _("Log Window max. lines:"), nullptr,
3706 1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3708 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3709 pref_label_new(hbox, _("Log Window F1 command: "));
3710 log_window_f1_entry = gtk_entry_new();
3711 gq_gtk_entry_set_text(GTK_ENTRY(log_window_f1_entry), options->log_window.action);
3712 gq_gtk_box_pack_start(GTK_BOX(hbox), log_window_f1_entry, FALSE, FALSE, 0);
3713 gtk_entry_set_width_chars(GTK_ENTRY(log_window_f1_entry), 15);
3714 gtk_widget_show(log_window_f1_entry);
3718 /* accelerators tab */
3720 static gboolean accel_search_function_cb(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer)
3722 gboolean ret = TRUE;
3727 gtk_tree_model_get(model, iter, column, &text, -1);
3728 text_nocase = g_utf8_casefold(text, -1);
3729 key_nocase = g_utf8_casefold(key, -1);
3731 if (g_strstr_len(text_nocase, -1, key_nocase))
3738 g_free(text_nocase);
3743 static void accel_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *, GtkTreeViewColumn *column, gpointer)
3748 list = gtk_tree_view_get_columns(tree_view);
3749 col_num = g_list_index(list, column);
3753 gtk_tree_view_set_search_column(tree_view, col_num);
3756 static void config_tab_accelerators(GtkWidget *notebook)
3762 GtkWidget *scrolled;
3763 GtkWidget *accel_view;
3764 GtkCellRenderer *renderer;
3765 GtkTreeSelection *selection;
3766 GtkTreeViewColumn *column;
3768 vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3770 group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3772 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3773 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3774 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3775 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3776 gtk_widget_show(scrolled);
3778 accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3780 accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3781 g_object_unref(accel_store);
3782 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3783 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3785 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3787 renderer = gtk_cell_renderer_text_new();
3789 column = gtk_tree_view_column_new_with_attributes(_("Action"),
3794 gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3795 gtk_tree_view_column_set_resizable(column, TRUE);
3796 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3799 renderer = gtk_cell_renderer_accel_new();
3800 g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3801 G_CALLBACK(accel_store_cleared_cb), accel_store);
3802 g_signal_connect(G_OBJECT(renderer), "accel-edited",
3803 G_CALLBACK(accel_store_edited_cb), accel_store);
3806 g_object_set (renderer,
3808 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3811 column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3816 gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3817 gtk_tree_view_column_set_resizable(column, TRUE);
3818 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3820 renderer = gtk_cell_renderer_text_new();
3822 column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3827 gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3828 gtk_tree_view_column_set_resizable(column, TRUE);
3829 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3831 renderer = gtk_cell_renderer_text_new();
3833 column = gtk_tree_view_column_new_with_attributes("Accel",
3838 gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3839 gtk_tree_view_column_set_resizable(column, TRUE);
3840 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3842 /* Search on text in column */
3843 gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(accel_view), TRUE);
3844 g_signal_connect(accel_view, "row_activated", G_CALLBACK(accel_row_activated_cb), accel_store);
3845 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), TRUE);
3846 gtk_tree_view_set_search_column(GTK_TREE_VIEW(accel_view), AE_TOOLTIP);
3847 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(accel_view), accel_search_function_cb, nullptr, nullptr);
3849 accel_store_populate();
3850 gq_gtk_container_add(GTK_WIDGET(scrolled), accel_view);
3851 gtk_widget_show(accel_view);
3853 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3855 button = pref_button_new(nullptr, nullptr, _("Defaults"),
3856 G_CALLBACK(accel_default_cb), accel_view);
3857 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3858 gtk_widget_show(button);
3860 button = pref_button_new(nullptr, nullptr, _("Reset selected"),
3861 G_CALLBACK(accel_reset_cb), accel_view);
3862 gtk_widget_set_tooltip_text(button, _("Will only reset changes made before the settings are saved"));
3863 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3864 gtk_widget_show(button);
3866 button = pref_button_new(nullptr, nullptr, _("Clear selected"),
3867 G_CALLBACK(accel_clear_cb), accel_view);
3868 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3869 gtk_widget_show(button);
3872 /* toolbar main tab */
3873 static void config_tab_toolbar_main(GtkWidget *notebook)
3876 GtkWidget *toolbardata;
3879 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3881 vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3883 toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3884 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3885 gtk_widget_show(vbox);
3888 /* toolbar status tab */
3889 static void config_tab_toolbar_status(GtkWidget *notebook)
3892 GtkWidget *toolbardata;
3895 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3897 vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3899 toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3900 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3901 gtk_widget_show(vbox);
3905 static gint extension_sort_cb(gconstpointer a, gconstpointer b)
3907 return g_strcmp0(static_cast<const gchar *>(a), static_cast<const gchar *>(b));
3910 static void config_tab_advanced(GtkWidget *notebook)
3913 GdkPixbufFormat *fm;
3915 GList *extensions_list = nullptr;
3916 GSList *formats_list;
3917 GString *types_string = g_string_new(nullptr);
3918 GtkWidget *alternate_checkbox;
3919 GtkWidget *dupes_threads_spin;
3921 GtkWidget *subgroup;
3923 GtkWidget *threads_string_label;
3924 GtkWidget *types_string_label;
3927 vbox = scrolled_notebook_page(notebook, _("Advanced"));
3928 group = pref_group_new(vbox, FALSE, _("External preview extraction"), GTK_ORIENTATION_VERTICAL);
3930 pref_checkbox_new_int(group, _("Use external preview extraction - Requires restart"), options->external_preview.enable, &c_options->external_preview.enable);
3932 pref_spacer(group, PREF_PAD_GROUP);
3934 formats_list = gdk_pixbuf_get_formats();
3936 while (formats_list)
3938 fm = static_cast<GdkPixbufFormat *>(formats_list->data);
3939 extensions = gdk_pixbuf_format_get_extensions(fm);
3942 while (extensions[i])
3944 extensions_list = g_list_insert_sorted(extensions_list, g_strdup(extensions[i]), extension_sort_cb);
3948 g_strfreev(extensions);
3949 formats_list = formats_list->next;
3952 while (extensions_list)
3954 if (types_string->len == 0)
3956 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3960 types_string = g_string_append(types_string, ", ");
3961 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3964 extensions_list = extensions_list->next;
3967 types_string = g_string_prepend(types_string, _("Usable file types:\n"));
3968 types_string_label = pref_label_new(group, types_string->str);
3969 gtk_label_set_line_wrap(GTK_LABEL(types_string_label), TRUE);
3971 pref_spacer(group, PREF_PAD_GROUP);
3973 group = pref_group_new(vbox, FALSE, _("File identification tool"), GTK_ORIENTATION_VERTICAL);
3974 external_preview_select_entry = gtk_entry_new();
3975 tabcomp = tab_completion_new(&external_preview_select_entry, options->external_preview.select, nullptr, nullptr, nullptr, nullptr);
3976 tab_completion_add_select_button(external_preview_select_entry, _("Select file identification tool"), FALSE);
3977 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3978 gtk_widget_show(tabcomp);
3980 group = pref_group_new(vbox, FALSE, _("Preview extraction tool"), GTK_ORIENTATION_VERTICAL);
3981 external_preview_extract_entry = gtk_entry_new();
3982 tabcomp = tab_completion_new(&external_preview_extract_entry, options->external_preview.extract, nullptr, nullptr, nullptr, nullptr);
3983 tab_completion_add_select_button(external_preview_extract_entry, _("Select preview extraction tool"), FALSE);
3984 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3985 gtk_widget_show(tabcomp);
3987 gtk_widget_show(vbox);
3989 g_slist_free(formats_list);
3990 g_list_free_full(extensions_list, g_free);
3991 g_string_free(types_string, TRUE);
3993 pref_spacer(group, PREF_PAD_GROUP);
3995 pref_line(vbox, PREF_PAD_SPACE);
3996 group = pref_group_new(vbox, FALSE, _("Thread pool limits"), GTK_ORIENTATION_VERTICAL);
3998 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."));
3999 gtk_label_set_line_wrap(GTK_LABEL(threads_string_label), TRUE);
4001 pref_spacer(vbox, PREF_PAD_GROUP);
4003 dupes_threads_spin = pref_spin_new_int(vbox, _("Duplicate check:"), _("max. threads"), 0, get_cpu_cores(), 1, options->threads.duplicates, &c_options->threads.duplicates);
4004 gtk_widget_set_tooltip_markup(dupes_threads_spin, _("Set to 0 for unlimited"));
4006 pref_spacer(group, PREF_PAD_GROUP);
4008 pref_line(vbox, PREF_PAD_SPACE);
4010 group = pref_group_new(vbox, FALSE, _("Alternate similarity alogorithm"), GTK_ORIENTATION_VERTICAL);
4012 alternate_checkbox = pref_checkbox_new_int(group, _("Enable alternate similarity algorithm"), options->alternate_similarity_algorithm.enabled, &c_options->alternate_similarity_algorithm.enabled);
4014 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4015 pref_checkbox_link_sensitivity(alternate_checkbox, subgroup);
4017 alternate_checkbox = pref_checkbox_new_int(subgroup, _("Use grayscale"), options->alternate_similarity_algorithm.grayscale, &c_options->alternate_similarity_algorithm.grayscale);
4018 gtk_widget_set_tooltip_text(alternate_checkbox, _("Reduce fingerprint to grayscale"));
4022 static void config_tab_stereo(GtkWidget *notebook)
4030 GtkWidget *fs_button;
4031 vbox = scrolled_notebook_page(notebook, _("Stereo"));
4033 group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
4035 table = pref_table_new(group, 2, 1, FALSE, FALSE);
4036 add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
4038 table = pref_table_new(group, 2, 2, TRUE, FALSE);
4039 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4040 pref_checkbox_new_int(box, _("Mirror left image"),
4041 options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
4042 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4043 pref_checkbox_new_int(box, _("Flip left image"),
4044 options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
4045 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4046 pref_checkbox_new_int(box, _("Mirror right image"),
4047 options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
4048 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4049 pref_checkbox_new_int(box, _("Flip right image"),
4050 options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
4051 pref_checkbox_new_int(group, _("Swap left and right images"),
4052 options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
4053 pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
4054 options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
4056 group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
4057 fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
4058 options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
4059 box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4060 pref_checkbox_link_sensitivity(fs_button, box2);
4061 table = pref_table_new(box2, 2, 1, FALSE, FALSE);
4062 add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
4063 table = pref_table_new(box2, 2, 2, TRUE, FALSE);
4064 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4065 pref_checkbox_new_int(box, _("Mirror left image"),
4066 options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
4067 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4068 pref_checkbox_new_int(box, _("Flip left image"),
4069 options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
4070 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4071 pref_checkbox_new_int(box, _("Mirror right image"),
4072 options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
4073 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4074 pref_checkbox_new_int(box, _("Flip right image"),
4075 options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
4076 pref_checkbox_new_int(box2, _("Swap left and right images"),
4077 options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
4078 pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
4079 options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
4081 group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
4082 table = pref_table_new(group2, 5, 3, FALSE, FALSE);
4083 pref_table_spin_new_int(table, 0, 0, _("Width"), nullptr,
4084 1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
4085 pref_table_spin_new_int(table, 3, 0, _("Height"), nullptr,
4086 1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
4087 pref_table_spin_new_int(table, 0, 1, _("Left X"), nullptr,
4088 0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
4089 pref_table_spin_new_int(table, 3, 1, _("Left Y"), nullptr,
4090 0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
4091 pref_table_spin_new_int(table, 0, 2, _("Right X"), nullptr,
4092 0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
4093 pref_table_spin_new_int(table, 3, 2, _("Right Y"), nullptr,
4094 0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
4098 /* Main preferences window */
4099 static void config_window_create(LayoutWindow *lw)
4101 GtkWidget *win_vbox;
4103 GtkWidget *notebook;
4105 GtkWidget *ct_button;
4107 if (!c_options) c_options = init_options(nullptr);
4109 configwindow = window_new("preferences", PIXBUF_INLINE_ICON_CONFIG, nullptr, _("Preferences"));
4110 DEBUG_NAME(configwindow);
4111 gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
4112 g_signal_connect(G_OBJECT(configwindow), "delete_event",
4113 G_CALLBACK(config_window_delete), NULL);
4114 if (options->save_dialog_window_positions)
4116 gtk_window_resize(GTK_WINDOW(configwindow), lw->options.preferences_window.w, lw->options.preferences_window.h);
4117 gq_gtk_window_move(GTK_WINDOW(configwindow), lw->options.preferences_window.x, lw->options.preferences_window.y);
4121 gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
4123 gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
4124 gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
4126 win_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4127 gq_gtk_container_add(GTK_WIDGET(configwindow), win_vbox);
4128 gtk_widget_show(win_vbox);
4130 notebook = gtk_notebook_new();
4131 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
4132 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
4133 gq_gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
4135 config_tab_general(notebook);
4136 config_tab_image(notebook);
4137 config_tab_osd(notebook);
4138 config_tab_windows(notebook);
4139 config_tab_accelerators(notebook);
4140 config_tab_files(notebook);
4141 config_tab_metadata(notebook);
4142 config_tab_keywords(notebook);
4143 config_tab_color(notebook);
4144 config_tab_stereo(notebook);
4145 config_tab_behavior(notebook);
4146 config_tab_toolbar_main(notebook);
4147 config_tab_toolbar_status(notebook);
4148 config_tab_advanced(notebook);
4150 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), lw->options.preferences_window.page_number);
4152 hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
4153 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
4154 gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
4155 gq_gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
4156 gtk_widget_show(hbox);
4158 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
4159 G_CALLBACK(config_window_help_cb), notebook);
4160 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4161 gtk_widget_set_can_default(button, TRUE);
4162 gtk_widget_show(button);
4164 button = pref_button_new(nullptr, GQ_ICON_OK, "OK",
4165 G_CALLBACK(config_window_ok_cb), notebook);
4166 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4167 gtk_widget_set_can_default(button, TRUE);
4168 gtk_widget_grab_default(button);
4169 gtk_widget_show(button);
4173 button = pref_button_new(nullptr, GQ_ICON_CANCEL, _("Cancel"),
4174 G_CALLBACK(config_window_close_cb), nullptr);
4175 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4176 gtk_widget_set_can_default(button, TRUE);
4177 gtk_widget_show(button);
4179 if (!generic_dialog_get_alternative_button_order(configwindow))
4181 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
4184 gtk_widget_show(notebook);
4186 gtk_widget_show(configwindow);
4190 *-----------------------------------------------------------------------------
4191 * config window show (public)
4192 *-----------------------------------------------------------------------------
4195 void show_config_window(LayoutWindow *lw)
4199 gtk_window_present(GTK_WINDOW(configwindow));
4203 config_window_create(lw);
4212 void show_about_window(LayoutWindow *lw)
4214 GDataInputStream *data_stream;
4215 GInputStream *in_stream_authors;
4216 GInputStream *in_stream_translators;
4218 GdkPixbuf *pixbuf_icon;
4219 GdkPixbuf *pixbuf_logo;
4223 gchar *authors[1000];
4225 gchar *timezone_path;
4234 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"));
4236 timezone_path = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
4237 if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
4239 cd = ZDOpenDatabase(timezone_path);
4242 copyright = g_string_append(copyright, ZDGetNotice(cd));
4246 log_printf("Error: Init of timezone database %s failed\n", timezone_path);
4248 ZDCloseDatabase(cd);
4250 g_free(timezone_path);
4252 copyright = g_string_append(copyright, _("\n\nSome icons by https://www.flaticon.com"));
4254 in_stream_authors = g_resources_open_stream(GQ_RESOURCE_PATH_CREDITS "/authors", G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4256 data_stream = g_data_input_stream_new(in_stream_authors);
4258 authors[0] = nullptr;
4259 while ((author_line = g_data_input_stream_read_line(G_DATA_INPUT_STREAM(data_stream), &length, nullptr, nullptr)))
4261 authors[i_authors] = g_strdup(author_line);
4263 g_free(author_line);
4265 authors[i_authors] = nullptr;
4267 g_input_stream_close(in_stream_authors, nullptr, nullptr);
4269 constexpr auto translators_path = GQ_RESOURCE_PATH_CREDITS "/translators";
4271 g_resources_get_info(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &size, &flags, nullptr);
4273 in_stream_translators = g_resources_open_stream(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4274 translators = static_cast<gchar *>(g_malloc0(size));
4275 g_input_stream_read_all(in_stream_translators, translators, size, &bytes_read, nullptr, nullptr);
4276 g_input_stream_close(in_stream_translators, nullptr, nullptr);
4278 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);
4280 artists[0] = g_strdup("Néstor Díaz Valencia <nestor@estudionexos.com>");
4281 artists[1] = nullptr;
4283 pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
4284 pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
4285 gtk_show_about_dialog(GTK_WINDOW(lw->window),
4286 "title", _("About Geeqie"),
4288 "program-name", GQ_APPNAME,
4290 "logo", pixbuf_logo,
4291 "icon", pixbuf_icon,
4292 "website", GQ_WEBSITE,
4293 "website-label", _("Website"),
4294 "comments", comment,
4297 "translator-credits", translators,
4298 "wrap-license", TRUE,
4299 "license", copyright->str,
4302 g_string_free(copyright, TRUE);
4304 while(n < i_authors)
4312 g_free(translators);
4313 g_object_unref(data_stream);
4314 g_object_unref(in_stream_authors);
4315 g_object_unref(in_stream_translators);
4318 static void image_overlay_set_text_colors()
4320 c_options->image_overlay.text_red = options->image_overlay.text_red;
4321 c_options->image_overlay.text_green = options->image_overlay.text_green;
4322 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
4323 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
4324 c_options->image_overlay.background_red = options->image_overlay.background_red;
4325 c_options->image_overlay.background_green = options->image_overlay.background_green;
4326 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
4327 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
4331 *-----------------------------------------------------------------------------
4332 * timezone database routines
4333 *-----------------------------------------------------------------------------
4336 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
4338 GError *error = nullptr;
4339 auto tz = static_cast<TZData *>(data);
4340 gchar *tmp_filename;
4341 gchar *timezone_bin;
4342 gchar *tmp_dir = nullptr;
4345 if (!g_cancellable_is_cancelled(tz->cancellable))
4347 generic_dialog_close(tz->gd);
4351 if (g_file_copy_finish(G_FILE(source_object), res, &error))
4353 tmp_filename = g_file_get_path(tz->tmp_g_file);
4354 fd = file_data_new_simple(tmp_filename);
4355 tmp_dir = open_archive(fd);
4359 timezone_bin = g_build_filename(tmp_dir, TIMEZONE_DATABASE_VERSION, TIMEZONE_DATABASE_FILE, NULL);
4360 if (isfile(timezone_bin))
4362 move_file(timezone_bin, tz->timezone_database_user);
4366 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4369 g_free(timezone_bin);
4370 g_free(tmp_dir); // The folder in /tmp is deleted in exit_program_final()
4374 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4376 g_free(tmp_filename);
4377 file_data_unref(fd);
4381 file_util_warning_dialog(_("Error: Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4384 g_file_delete(tz->tmp_g_file, nullptr, &error);
4385 g_object_unref(tz->tmp_g_file);
4386 tz->tmp_g_file = nullptr;
4387 g_object_unref(tz->cancellable);
4388 g_object_unref(tz->timezone_database_gq);
4391 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
4393 auto tz = static_cast<TZData *>(data);
4395 if (!g_cancellable_is_cancelled(tz->cancellable))
4397 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), static_cast<gdouble>(current_num_bytes) / total_num_bytes);
4401 static void timezone_cancel_button_cb(GenericDialog *, gpointer data)
4403 auto tz = static_cast<TZData *>(data);
4405 g_cancellable_cancel(tz->cancellable);
4408 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
4410 auto tz = static_cast<TZData *>(data);
4411 GError *error = nullptr;
4412 GFileIOStream *io_stream;
4419 tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
4423 file_util_warning_dialog(_("Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4424 log_printf("Error: Download timezone database failed:\n%s", error->message);
4425 g_error_free(error);
4426 g_object_unref(tz->tmp_g_file);
4430 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE_WEB);
4432 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", nullptr, TRUE, timezone_cancel_button_cb, tz);
4434 generic_dialog_add_message(tz->gd, GQ_ICON_DIALOG_INFO, _("Downloading timezone database"), nullptr, FALSE);
4436 tz->progress = gtk_progress_bar_new();
4437 gq_gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
4438 gtk_widget_show(tz->progress);
4440 gtk_widget_show(tz->gd->dialog);
4441 tz->cancellable = g_cancellable_new();
4442 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);
4444 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
4447 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */