2 * Copyright (C) 2006 John Ellis
3 * Copyright (C) 2008 - 2016 The Geeqie Team
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "preferences.h"
29 #include <gdk-pixbuf/gdk-pixbuf.h>
32 #include <glib-object.h>
35 #include <gspell/gspell.h>
46 #include <pango/pango.h>
49 #include "bar-keywords.h"
51 #include "color-man.h"
56 #include "filefilter.h"
57 #include "fullscreen.h"
58 #include "image-overlay.h"
62 #include "layout-util.h"
64 #include "main-defines.h"
70 #include "pixbuf-util.h"
72 #include "slideshow.h"
73 #include "third-party/zonedetect.h"
77 #include "ui-fileops.h"
79 #include "ui-tabcomp.h"
80 #include "ui-utildlg.h"
87 constexpr gint PRE_FORMATTED_COLUMNS = 5;
88 constexpr gint KEYWORD_DIALOG_WIDTH = 400;
95 EDITOR_NAME_MAX_LENGTH = 32,
96 EDITOR_COMMAND_MAX_LENGTH = 1024
99 static void image_overlay_set_text_colors();
101 GtkWidget *keyword_text;
102 static void config_tab_keywords_save();
110 static ThumbSize thumb_size_list[] =
146 FILETYPES_COLUMN_ENABLED = 0,
147 FILETYPES_COLUMN_FILTER,
148 FILETYPES_COLUMN_DESCRIPTION,
149 FILETYPES_COLUMN_CLASS,
150 FILETYPES_COLUMN_WRITABLE,
151 FILETYPES_COLUMN_SIDECAR,
152 FILETYPES_COLUMN_COUNT
155 const gchar *format_class_list[] = {
166 /* config memory values */
167 static ConfOptions *c_options = nullptr;
174 static GtkWidget *configwindow = nullptr;
175 static GtkListStore *filter_store = nullptr;
176 static GtkTreeStore *accel_store = nullptr;
178 static GtkWidget *safe_delete_path_entry;
180 static GtkWidget *color_profile_input_file_entry[COLOR_PROFILE_INPUTS];
181 static GtkWidget *color_profile_input_name_entry[COLOR_PROFILE_INPUTS];
182 static GtkWidget *color_profile_screen_file_entry;
183 static GtkWidget *external_preview_select_entry;
184 static GtkWidget *external_preview_extract_entry;
186 static GtkWidget *sidecar_ext_entry;
187 static GtkWidget *help_search_engine_entry;
189 static GtkWidget *log_window_f1_entry;
193 CONFIG_WINDOW_DEF_WIDTH = 700,
194 CONFIG_WINDOW_DEF_HEIGHT = 600
198 *-----------------------------------------------------------------------------
199 * option widget callbacks (private)
200 *-----------------------------------------------------------------------------
203 static void zoom_increment_cb(GtkWidget *spin, gpointer)
205 c_options->image.zoom_increment = static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) * 100.0 + 0.01);
208 static void slideshow_delay_hours_cb(GtkWidget *spin, gpointer)
210 gint mins_secs_tenths;
213 mins_secs_tenths = c_options->slideshow.delay %
214 (3600 * SLIDESHOW_SUBSECOND_PRECISION);
216 delay = (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
217 (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
220 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
221 SLIDESHOW_SUBSECOND_PRECISION;
224 static void slideshow_delay_minutes_cb(GtkWidget *spin, gpointer)
230 hours = c_options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
231 secs_tenths = c_options->slideshow.delay % (60 * SLIDESHOW_SUBSECOND_PRECISION);
233 delay = hours * (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
234 (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
235 (60 * SLIDESHOW_SUBSECOND_PRECISION) + secs_tenths);
237 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
238 SLIDESHOW_SUBSECOND_PRECISION;
241 static void slideshow_delay_seconds_cb(GtkWidget *spin, gpointer)
246 hours_mins = c_options->slideshow.delay / (60 * SLIDESHOW_SUBSECOND_PRECISION);
248 delay = (hours_mins * (60 * SLIDESHOW_SUBSECOND_PRECISION)) +
249 static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
250 static_cast<gdouble>(SLIDESHOW_SUBSECOND_PRECISION) + 0.01);
252 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
253 SLIDESHOW_SUBSECOND_PRECISION;
257 *-----------------------------------------------------------------------------
258 * sync program to config window routine (private)
259 *-----------------------------------------------------------------------------
262 void config_entry_to_option(GtkWidget *entry, gchar **option, gchar *(*func)(const gchar *))
268 buf = gq_gtk_entry_get_text(GTK_ENTRY(entry));
269 if (buf && strlen(buf) > 0)
274 *option = g_strdup(buf);
279 static gboolean accel_apply_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
284 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, AE_KEY, &accel, -1);
286 if (accel_path && accel_path[0])
289 gtk_accelerator_parse(accel, &key.accel_key, &key.accel_mods);
290 gtk_accel_map_change_entry(accel_path, key.accel_key, key.accel_mods, TRUE);
300 static void config_window_apply()
302 gboolean refresh = FALSE;
307 config_entry_to_option(safe_delete_path_entry, &options->file_ops.safe_delete_path, remove_trailing_slash);
309 if (options->file_filter.show_hidden_files != c_options->file_filter.show_hidden_files) refresh = TRUE;
310 if (options->file_filter.show_parent_directory != c_options->file_filter.show_parent_directory) refresh = TRUE;
311 if (options->file_filter.show_dot_directory != c_options->file_filter.show_dot_directory) refresh = TRUE;
312 if (options->file_sort.case_sensitive != c_options->file_sort.case_sensitive) refresh = TRUE;
313 if (options->file_filter.disable_file_extension_checks != c_options->file_filter.disable_file_extension_checks) refresh = TRUE;
314 if (options->file_filter.disable != c_options->file_filter.disable) refresh = TRUE;
316 options->file_ops.confirm_delete = c_options->file_ops.confirm_delete;
317 options->file_ops.enable_delete_key = c_options->file_ops.enable_delete_key;
318 options->file_ops.confirm_move_to_trash = c_options->file_ops.confirm_move_to_trash;
319 options->file_ops.use_system_trash = c_options->file_ops.use_system_trash;
320 options->file_ops.no_trash = c_options->file_ops.no_trash;
321 options->file_ops.safe_delete_folder_maxsize = c_options->file_ops.safe_delete_folder_maxsize;
322 options->tools_restore_state = c_options->tools_restore_state;
323 options->save_window_positions = c_options->save_window_positions;
324 options->use_saved_window_positions_for_new_windows = c_options->use_saved_window_positions_for_new_windows;
325 options->save_window_workspace = c_options->save_window_workspace;
326 options->save_dialog_window_positions = c_options->save_dialog_window_positions;
327 options->show_window_ids = c_options->show_window_ids;
328 options->image.scroll_reset_method = c_options->image.scroll_reset_method;
329 options->image.zoom_2pass = c_options->image.zoom_2pass;
330 options->image.fit_window_to_image = c_options->image.fit_window_to_image;
331 options->image.limit_window_size = c_options->image.limit_window_size;
332 options->image.zoom_to_fit_allow_expand = c_options->image.zoom_to_fit_allow_expand;
333 options->image.max_window_size = c_options->image.max_window_size;
334 options->image.limit_autofit_size = c_options->image.limit_autofit_size;
335 options->image.max_autofit_size = c_options->image.max_autofit_size;
336 options->image.max_enlargement_size = c_options->image.max_enlargement_size;
337 options->image.tile_size = c_options->image.tile_size;
338 options->progressive_key_scrolling = c_options->progressive_key_scrolling;
339 options->keyboard_scroll_step = c_options->keyboard_scroll_step;
341 if (options->thumbnails.max_width != c_options->thumbnails.max_width
342 || options->thumbnails.max_height != c_options->thumbnails.max_height
343 || options->thumbnails.quality != c_options->thumbnails.quality)
345 thumb_format_changed = TRUE;
347 options->thumbnails.max_width = c_options->thumbnails.max_width;
348 options->thumbnails.max_height = c_options->thumbnails.max_height;
349 options->thumbnails.quality = c_options->thumbnails.quality;
351 options->thumbnails.enable_caching = c_options->thumbnails.enable_caching;
352 options->thumbnails.cache_into_dirs = c_options->thumbnails.cache_into_dirs;
353 options->thumbnails.use_exif = c_options->thumbnails.use_exif;
354 options->thumbnails.use_color_management = c_options->thumbnails.use_color_management;
355 options->thumbnails.collection_preview = c_options->thumbnails.collection_preview;
356 options->thumbnails.use_ft_metadata = c_options->thumbnails.use_ft_metadata;
357 options->thumbnails.spec_standard = c_options->thumbnails.spec_standard;
358 options->metadata.enable_metadata_dirs = c_options->metadata.enable_metadata_dirs;
359 options->file_filter.show_hidden_files = c_options->file_filter.show_hidden_files;
360 options->file_filter.show_parent_directory = c_options->file_filter.show_parent_directory;
361 options->file_filter.show_dot_directory = c_options->file_filter.show_dot_directory;
362 options->file_filter.disable_file_extension_checks = c_options->file_filter.disable_file_extension_checks;
364 options->file_sort.case_sensitive = c_options->file_sort.case_sensitive;
365 options->file_filter.disable = c_options->file_filter.disable;
367 config_entry_to_option(sidecar_ext_entry, &options->sidecar.ext, nullptr);
368 sidecar_ext_parse(options->sidecar.ext);
370 options->slideshow.random = c_options->slideshow.random;
371 options->slideshow.repeat = c_options->slideshow.repeat;
372 options->slideshow.delay = c_options->slideshow.delay;
374 options->mousewheel_scrolls = c_options->mousewheel_scrolls;
375 options->image_lm_click_nav = c_options->image_lm_click_nav;
376 options->image_l_click_archive = c_options->image_l_click_archive;
377 options->image_l_click_video = c_options->image_l_click_video;
378 options->image_l_click_video_editor = c_options->image_l_click_video_editor;
380 options->file_ops.enable_in_place_rename = c_options->file_ops.enable_in_place_rename;
382 options->image.tile_cache_max = c_options->image.tile_cache_max;
383 options->image.image_cache_max = c_options->image.image_cache_max;
385 options->image.zoom_quality = c_options->image.zoom_quality;
387 options->image.zoom_increment = c_options->image.zoom_increment;
389 options->image.zoom_style = c_options->image.zoom_style;
391 options->image.enable_read_ahead = c_options->image.enable_read_ahead;
393 options->appimage_notifications = c_options->appimage_notifications;
396 if (options->image.use_custom_border_color != c_options->image.use_custom_border_color
397 || options->image.use_custom_border_color_in_fullscreen != c_options->image.use_custom_border_color_in_fullscreen
398 || !gdk_rgba_equal(&options->image.border_color, &c_options->image.border_color))
400 options->image.use_custom_border_color_in_fullscreen = c_options->image.use_custom_border_color_in_fullscreen;
401 options->image.use_custom_border_color = c_options->image.use_custom_border_color;
402 options->image.border_color = c_options->image.border_color;
403 layout_colors_update();
404 view_window_colors_update();
407 options->image.alpha_color_1 = c_options->image.alpha_color_1;
408 options->image.alpha_color_2 = c_options->image.alpha_color_2;
410 options->fullscreen.screen = c_options->fullscreen.screen;
411 options->fullscreen.clean_flip = c_options->fullscreen.clean_flip;
412 options->fullscreen.disable_saver = c_options->fullscreen.disable_saver;
413 options->fullscreen.above = c_options->fullscreen.above;
414 if (c_options->image_overlay.template_string)
415 set_image_overlay_template_string(&options->image_overlay.template_string,
416 c_options->image_overlay.template_string);
417 if (c_options->image_overlay.font)
418 set_image_overlay_font_string(&options->image_overlay.font,
419 c_options->image_overlay.font);
420 options->image_overlay.text_red = c_options->image_overlay.text_red;
421 options->image_overlay.text_green = c_options->image_overlay.text_green;
422 options->image_overlay.text_blue = c_options->image_overlay.text_blue;
423 options->image_overlay.text_alpha = c_options->image_overlay.text_alpha;
424 options->image_overlay.background_red = c_options->image_overlay.background_red;
425 options->image_overlay.background_green = c_options->image_overlay.background_green;
426 options->image_overlay.background_blue = c_options->image_overlay.background_blue;
427 options->image_overlay.background_alpha = c_options->image_overlay.background_alpha;
428 options->image_overlay.font = c_options->image_overlay.font;
429 options->update_on_time_change = c_options->update_on_time_change;
431 options->duplicates_similarity_threshold = c_options->duplicates_similarity_threshold;
432 options->rot_invariant_sim = c_options->rot_invariant_sim;
434 options->tree_descend_subdirs = c_options->tree_descend_subdirs;
436 options->view_dir_list_single_click_enter = c_options->view_dir_list_single_click_enter;
437 options->circular_selection_lists = c_options->circular_selection_lists;
439 options->open_recent_list_maxsize = c_options->open_recent_list_maxsize;
440 options->recent_folder_image_list_maxsize = c_options->recent_folder_image_list_maxsize;
441 options->dnd_icon_size = c_options->dnd_icon_size;
442 options->clipboard_selection = c_options->clipboard_selection;
443 options->dnd_default_action = c_options->dnd_default_action;
445 options->metadata.save_in_image_file = c_options->metadata.save_in_image_file;
446 options->metadata.save_legacy_IPTC = c_options->metadata.save_legacy_IPTC;
447 options->metadata.warn_on_write_problems = c_options->metadata.warn_on_write_problems;
448 options->metadata.save_legacy_format = c_options->metadata.save_legacy_format;
449 options->metadata.sync_grouped_files = c_options->metadata.sync_grouped_files;
450 options->metadata.confirm_write = c_options->metadata.confirm_write;
451 options->metadata.sidecar_extended_name = c_options->metadata.sidecar_extended_name;
452 options->metadata.confirm_timeout = c_options->metadata.confirm_timeout;
453 options->metadata.confirm_after_timeout = c_options->metadata.confirm_after_timeout;
454 options->metadata.confirm_on_image_change = c_options->metadata.confirm_on_image_change;
455 options->metadata.confirm_on_dir_change = c_options->metadata.confirm_on_dir_change;
456 options->metadata.keywords_case_sensitive = c_options->metadata.keywords_case_sensitive;
457 options->metadata.write_orientation = c_options->metadata.write_orientation;
458 options->metadata.check_spelling = c_options->metadata.check_spelling;
459 options->stereo.mode = (c_options->stereo.mode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
460 (c_options->stereo.tmp.mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
461 (c_options->stereo.tmp.flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
462 (c_options->stereo.tmp.mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
463 (c_options->stereo.tmp.flip_left ? PR_STEREO_FLIP_LEFT : 0) |
464 (c_options->stereo.tmp.swap ? PR_STEREO_SWAP : 0) |
465 (c_options->stereo.tmp.temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
466 options->stereo.fsmode = (c_options->stereo.fsmode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
467 (c_options->stereo.tmp.fs_mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
468 (c_options->stereo.tmp.fs_flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
469 (c_options->stereo.tmp.fs_mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
470 (c_options->stereo.tmp.fs_flip_left ? PR_STEREO_FLIP_LEFT : 0) |
471 (c_options->stereo.tmp.fs_swap ? PR_STEREO_SWAP : 0) |
472 (c_options->stereo.tmp.fs_temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
473 options->stereo.enable_fsmode = c_options->stereo.enable_fsmode;
474 options->stereo.fixed_w = c_options->stereo.fixed_w;
475 options->stereo.fixed_h = c_options->stereo.fixed_h;
476 options->stereo.fixed_x1 = c_options->stereo.fixed_x1;
477 options->stereo.fixed_y1 = c_options->stereo.fixed_y1;
478 options->stereo.fixed_x2 = c_options->stereo.fixed_x2;
479 options->stereo.fixed_y2 = c_options->stereo.fixed_y2;
481 options->info_keywords.height = c_options->info_keywords.height;
482 options->info_title.height = c_options->info_title.height;
483 options->info_comment.height = c_options->info_comment.height;
484 options->info_rating.height = c_options->info_rating.height;
486 options->show_predefined_keyword_tree = c_options->show_predefined_keyword_tree;
487 options->expand_menu_toolbar = c_options->expand_menu_toolbar;
488 options->hamburger_menu = c_options->hamburger_menu;
490 options->selectable_bars.menu_bar = c_options->selectable_bars.menu_bar;
491 options->selectable_bars.tool_bar = c_options->selectable_bars.tool_bar;
492 options->selectable_bars.status_bar = c_options->selectable_bars.status_bar;
494 options->marks_save = c_options->marks_save;
495 options->with_rename = c_options->with_rename;
496 options->collections_duplicates = c_options->collections_duplicates;
497 options->collections_on_top = c_options->collections_on_top;
498 options->hide_window_in_fullscreen = c_options->hide_window_in_fullscreen;
499 config_entry_to_option(help_search_engine_entry, &options->help_search_engine, nullptr);
501 options->external_preview.enable = c_options->external_preview.enable;
502 config_entry_to_option(external_preview_select_entry, &options->external_preview.select, nullptr);
503 config_entry_to_option(external_preview_extract_entry, &options->external_preview.extract, nullptr);
505 options->read_metadata_in_idle = c_options->read_metadata_in_idle;
507 options->star_rating.star = c_options->star_rating.star;
508 options->star_rating.rejected = c_options->star_rating.rejected;
510 options->threads.duplicates = c_options->threads.duplicates > 0 ? c_options->threads.duplicates : -1;
512 options->alternate_similarity_algorithm.enabled = c_options->alternate_similarity_algorithm.enabled;
513 options->alternate_similarity_algorithm.grayscale = c_options->alternate_similarity_algorithm.grayscale;
516 set_debug_level(debug_c);
517 config_entry_to_option(log_window_f1_entry, &options->log_window.action, nullptr);
521 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
523 config_entry_to_option(color_profile_input_name_entry[i], &options->color_profile.input_name[i], nullptr);
524 config_entry_to_option(color_profile_input_file_entry[i], &options->color_profile.input_file[i], nullptr);
526 config_entry_to_option(color_profile_screen_file_entry, &options->color_profile.screen_file, nullptr);
527 options->color_profile.use_x11_screen_profile = c_options->color_profile.use_x11_screen_profile;
528 if (options->color_profile.render_intent != c_options->color_profile.render_intent)
530 options->color_profile.render_intent = c_options->color_profile.render_intent;
535 options->mouse_button_8 = c_options->mouse_button_8;
536 options->mouse_button_9 = c_options->mouse_button_9;
538 options->override_disable_gpu = c_options->override_disable_gpu;
540 config_tab_keywords_save();
542 image_options_sync();
547 layout_refresh(nullptr);
550 if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, nullptr);
552 toolbar_apply(TOOLBAR_MAIN);
553 toolbar_apply(TOOLBAR_STATUS);
557 *-----------------------------------------------------------------------------
558 * config window main button callbacks (private)
559 *-----------------------------------------------------------------------------
562 static void config_window_close_cb(GtkWidget *, gpointer)
564 gq_gtk_widget_destroy(configwindow);
565 configwindow = nullptr;
566 filter_store = nullptr;
569 static void config_window_help_cb(GtkWidget *, gpointer data)
571 auto notebook = static_cast<GtkWidget *>(data);
574 static const gchar *html_section[] =
576 "GuideOptionsGeneral.html",
577 "GuideOptionsImage.html",
578 "GuideOptionsOSD.html",
579 "GuideOptionsWindow.html",
580 "GuideOptionsKeyboard.html",
581 "GuideOptionsFiltering.html",
582 "GuideOptionsMetadata.html",
583 "GuideOptionsKeywords.html",
584 "GuideOptionsColor.html",
585 "GuideOptionsStereo.html",
586 "GuideOptionsBehavior.html",
587 "GuideOptionsToolbar.html",
588 "GuideOptionsToolbar.html",
589 "GuideOptionsAdvanced.html"
592 i = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
593 help_window_show(html_section[i]);
596 static gboolean config_window_delete(GtkWidget *, GdkEventAny *, gpointer)
598 config_window_close_cb(nullptr, nullptr);
602 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
605 auto notebook = static_cast<GtkNotebook *>(data);
613 lw = static_cast<LayoutWindow *>(layout_window_list->data);
615 window = gtk_widget_get_window(widget);
616 gdk_window_get_root_origin(window, &x, &y);
617 w = gdk_window_get_width(window);
618 h = gdk_window_get_height(window);
619 page_number = gtk_notebook_get_current_page(notebook);
621 lw->options.preferences_window.x = x;
622 lw->options.preferences_window.y = y;
623 lw->options.preferences_window.w = w;
624 lw->options.preferences_window.h = h;
625 lw->options.preferences_window.page_number = page_number;
627 config_window_apply();
628 layout_util_sync(lw);
629 save_options(options);
630 config_window_close_cb(nullptr, nullptr);
634 *-----------------------------------------------------------------------------
635 * config window setup (private)
636 *-----------------------------------------------------------------------------
639 static void quality_menu_cb(GtkWidget *combo, gpointer data)
641 auto option = static_cast<gint *>(data);
643 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
647 *option = GDK_INTERP_NEAREST;
650 *option = GDK_INTERP_TILES;
653 *option = GDK_INTERP_BILINEAR;
658 static void dnd_default_action_selection_menu_cb(GtkWidget *combo, gpointer data)
660 auto option = static_cast<gint *>(data);
662 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
666 *option = DND_ACTION_ASK;
669 *option = DND_ACTION_COPY;
672 *option = DND_ACTION_MOVE;
676 static void clipboard_selection_menu_cb(GtkWidget *combo, gpointer data)
678 auto option = static_cast<gint *>(data);
680 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
683 *option = CLIPBOARD_PRIMARY;
686 *option = CLIPBOARD_CLIPBOARD;
689 *option = CLIPBOARD_BOTH;
692 *option = CLIPBOARD_BOTH;
696 static void add_quality_menu(GtkWidget *table, gint column, gint row, const gchar *text,
697 guint option, guint *option_c)
704 pref_table_label(table, column, row, text, GTK_ALIGN_START);
706 combo = gtk_combo_box_text_new();
708 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Nearest (worst, but fastest)"));
709 if (option == GDK_INTERP_NEAREST) current = 0;
710 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Tiles"));
711 if (option == GDK_INTERP_TILES) current = 1;
712 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Bilinear (best, but slowest)"));
713 if (option == GDK_INTERP_BILINEAR) current = 2;
715 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
717 g_signal_connect(G_OBJECT(combo), "changed",
718 G_CALLBACK(quality_menu_cb), option_c);
720 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
721 gtk_widget_show(combo);
724 static void add_dnd_default_action_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, DnDAction option, DnDAction *option_c)
731 pref_table_label(table, column, row, text, GTK_ALIGN_START);
733 combo = gtk_combo_box_text_new();
735 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Ask"));
736 if (option == DND_ACTION_ASK) current = 0;
737 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Copy"));
738 if (option == DND_ACTION_COPY) current = 1;
739 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Move"));
740 if (option == DND_ACTION_MOVE) current = 2;
742 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
744 g_signal_connect(G_OBJECT(combo), "changed",
745 G_CALLBACK(dnd_default_action_selection_menu_cb), option_c);
747 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
748 gtk_widget_show(combo);
751 static void add_clipboard_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
752 gint option, gint *option_c)
759 pref_table_label(table, column, row, text, GTK_ALIGN_START);
761 combo = gtk_combo_box_text_new();
763 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Primary"));
764 if (option == CLIPBOARD_PRIMARY) current = 0;
765 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Clipboard"));
766 if (option == CLIPBOARD_CLIPBOARD) current = 1;
767 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Both"));
768 if (option == CLIPBOARD_BOTH) current = 2;
770 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
772 g_signal_connect(G_OBJECT(combo), "changed",
773 G_CALLBACK(clipboard_selection_menu_cb), option_c);
775 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
776 gtk_widget_show(combo);
779 static void zoom_style_selection_menu_cb(GtkWidget *combo, gpointer data)
781 auto option = static_cast<gint *>(data);
783 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
786 *option = ZOOM_GEOMETRIC;
789 *option = ZOOM_ARITHMETIC;
792 *option = ZOOM_GEOMETRIC;
796 static void add_zoom_style_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, ZoomStyle option, ZoomStyle *option_c)
803 pref_table_label(table, column, row, text, GTK_ALIGN_START);
805 combo = gtk_combo_box_text_new();
807 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Geometric"));
808 if (option == ZOOM_GEOMETRIC) current = 0;
809 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Arithmetic"));
810 if (option == ZOOM_ARITHMETIC) current = 1;
812 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
814 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(zoom_style_selection_menu_cb), option_c);
816 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
817 gtk_widget_show(combo);
820 static void mouse_buttons_selection_menu_cb(GtkWidget *combo, gpointer data)
822 ActionItem *action_item = nullptr;
823 auto option = static_cast<gchar **>(data);
828 label = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
830 list = get_action_items();
835 action_item = static_cast<ActionItem *>(work->data);
836 if (g_strcmp0(action_item->label, label) == 0)
844 *option = g_strdup(action_item->name);
846 action_items_free(list);
849 static void add_mouse_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, gchar *option, gchar **option_c)
851 ActionItem *action_item;
860 pref_table_label(table, column, row, text, GTK_ALIGN_START);
862 combo = gtk_combo_box_text_new();
864 list = get_action_items();
868 action_item = static_cast<ActionItem *>(work->data);
869 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), action_item->label);
871 if (g_strcmp0(action_item->name, option) == 0)
879 action_items_free(list);
881 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
883 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(mouse_buttons_selection_menu_cb), option_c);
885 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
886 gtk_widget_show(combo);
889 static void thumb_size_menu_cb(GtkWidget *combo, gpointer)
893 n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
896 if (static_cast<guint>(n) < sizeof(thumb_size_list) / sizeof(ThumbSize))
898 c_options->thumbnails.max_width = thumb_size_list[n].w;
899 c_options->thumbnails.max_height = thumb_size_list[n].h;
903 c_options->thumbnails.max_width = options->thumbnails.max_width;
904 c_options->thumbnails.max_height = options->thumbnails.max_height;
908 static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *text)
914 c_options->thumbnails.max_width = options->thumbnails.max_width;
915 c_options->thumbnails.max_height = options->thumbnails.max_height;
917 pref_table_label(table, column, row, text, GTK_ALIGN_START);
919 combo = gtk_combo_box_text_new();
922 for (i = 0; static_cast<guint>(i) < sizeof(thumb_size_list) / sizeof(ThumbSize); i++)
928 w = thumb_size_list[i].w;
929 h = thumb_size_list[i].h;
931 buf = g_strdup_printf("%d x %d", w, h);
932 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
935 if (w == options->thumbnails.max_width && h == options->thumbnails.max_height) current = i;
942 buf = g_strdup_printf("%s %d x %d", _("Custom"), options->thumbnails.max_width, options->thumbnails.max_height);
943 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
949 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
950 g_signal_connect(G_OBJECT(combo), "changed",
951 G_CALLBACK(thumb_size_menu_cb), NULL);
953 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
954 gtk_widget_show(combo);
957 static void stereo_mode_menu_cb(GtkWidget *combo, gpointer data)
959 auto option = static_cast<gint *>(data);
961 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
965 *option = PR_STEREO_NONE;
968 *option = PR_STEREO_ANAGLYPH_RC;
971 *option = PR_STEREO_ANAGLYPH_GM;
974 *option = PR_STEREO_ANAGLYPH_YB;
977 *option = PR_STEREO_ANAGLYPH_GRAY_RC;
980 *option = PR_STEREO_ANAGLYPH_GRAY_GM;
983 *option = PR_STEREO_ANAGLYPH_GRAY_YB;
986 *option = PR_STEREO_ANAGLYPH_DB_RC;
989 *option = PR_STEREO_ANAGLYPH_DB_GM;
992 *option = PR_STEREO_ANAGLYPH_DB_YB;
995 *option = PR_STEREO_HORIZ;
998 *option = PR_STEREO_HORIZ | PR_STEREO_HALF;
1001 *option = PR_STEREO_VERT;
1004 *option = PR_STEREO_VERT | PR_STEREO_HALF;
1007 *option = PR_STEREO_FIXED;
1012 static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1013 gint option, gint *option_c, gboolean add_fixed)
1020 pref_table_label(table, column, row, text, GTK_ALIGN_START);
1022 combo = gtk_combo_box_text_new();
1024 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Single image"));
1026 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Red-Cyan"));
1027 if (option & PR_STEREO_ANAGLYPH_RC) current = 1;
1028 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Green-Magenta"));
1029 if (option & PR_STEREO_ANAGLYPH_GM) current = 2;
1030 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Yellow-Blue"));
1031 if (option & PR_STEREO_ANAGLYPH_YB) current = 3;
1032 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Red-Cyan"));
1033 if (option & PR_STEREO_ANAGLYPH_GRAY_RC) current = 4;
1034 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Green-Magenta"));
1035 if (option & PR_STEREO_ANAGLYPH_GRAY_GM) current = 5;
1036 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Yellow-Blue"));
1037 if (option & PR_STEREO_ANAGLYPH_GRAY_YB) current = 6;
1038 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Red-Cyan"));
1039 if (option & PR_STEREO_ANAGLYPH_DB_RC) current = 7;
1040 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Green-Magenta"));
1041 if (option & PR_STEREO_ANAGLYPH_DB_GM) current = 8;
1042 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Yellow-Blue"));
1043 if (option & PR_STEREO_ANAGLYPH_DB_YB) current = 9;
1045 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side"));
1046 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side Half size"));
1047 if (option & PR_STEREO_HORIZ)
1050 if (option & PR_STEREO_HALF) current = 11;
1053 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom"));
1054 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom Half size"));
1055 if (option & PR_STEREO_VERT)
1058 if (option & PR_STEREO_HALF) current = 13;
1063 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Fixed position"));
1064 if (option & PR_STEREO_FIXED) current = 14;
1067 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1069 g_signal_connect(G_OBJECT(combo), "changed",
1070 G_CALLBACK(stereo_mode_menu_cb), option_c);
1072 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1073 gtk_widget_show(combo);
1076 static void video_menu_cb(GtkWidget *combo, gpointer data)
1078 auto option = static_cast<gchar **>(data);
1080 auto ed = static_cast<EditorDescription *>(g_list_nth_data(editor_list_get(), gtk_combo_box_get_active(GTK_COMBO_BOX(combo))));
1084 static void video_menu_populate(gpointer data, gpointer user_data)
1086 auto combo = static_cast<GtkWidget *>(user_data);
1087 auto ed = static_cast<EditorDescription *>(data);
1089 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), ed->name);
1092 static void add_video_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1093 gchar *option, gchar **option_c)
1097 /* use lists since they are sorted */
1098 GList *eds = editor_list_get();
1102 pref_table_label(table, column, row, text, GTK_ALIGN_START);
1104 combo = gtk_combo_box_text_new();
1105 g_list_foreach(eds,video_menu_populate,combo);
1106 current = option ? g_list_index(eds,g_hash_table_lookup(editors,option)): -1;
1108 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1110 g_signal_connect(G_OBJECT(combo), "changed",
1111 G_CALLBACK(video_menu_cb), option_c);
1113 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1114 gtk_widget_show(combo);
1117 static void filter_store_populate()
1121 if (!filter_store) return;
1123 gtk_list_store_clear(filter_store);
1125 work = filter_get_list();
1131 fe = static_cast<FilterEntry *>(work->data);
1134 gtk_list_store_append(filter_store, &iter);
1135 gtk_list_store_set(filter_store, &iter, 0, fe, -1);
1139 static void filter_store_ext_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1141 auto model = static_cast<GtkWidget *>(data);
1142 auto fe = static_cast<FilterEntry *>(data);
1146 if (!new_text || strlen(new_text) < 1) return;
1148 tpath = gtk_tree_path_new_from_string(path_str);
1149 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1150 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1152 g_free(fe->extensions);
1153 fe->extensions = g_strdup(new_text);
1155 gtk_tree_path_free(tpath);
1159 static void filter_store_class_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1161 auto model = static_cast<GtkWidget *>(data);
1162 auto fe = static_cast<FilterEntry *>(data);
1167 if (!new_text || !new_text[0]) return;
1169 tpath = gtk_tree_path_new_from_string(path_str);
1170 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1171 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1173 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
1175 if (strcmp(new_text, _(format_class_list[i])) == 0)
1177 fe->file_class = static_cast<FileFormatClass>(i);
1182 gtk_tree_path_free(tpath);
1186 static void filter_store_desc_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1188 auto model = static_cast<GtkWidget *>(data);
1193 if (!new_text || !new_text[0]) return;
1195 tpath = gtk_tree_path_new_from_string(path_str);
1196 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1197 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1199 g_free(fe->description);
1200 fe->description = g_strdup(new_text);
1202 gtk_tree_path_free(tpath);
1205 static void filter_store_enable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1207 auto model = static_cast<GtkWidget *>(data);
1212 tpath = gtk_tree_path_new_from_string(path_str);
1213 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1214 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1216 fe->enabled = !fe->enabled;
1218 gtk_tree_path_free(tpath);
1222 static void filter_store_writable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1224 auto model = static_cast<GtkWidget *>(data);
1229 tpath = gtk_tree_path_new_from_string(path_str);
1230 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1231 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1233 fe->writable = !fe->writable;
1234 if (fe->writable) fe->allow_sidecar = FALSE;
1236 gtk_tree_path_free(tpath);
1240 static void filter_store_sidecar_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1242 auto model = static_cast<GtkWidget *>(data);
1247 tpath = gtk_tree_path_new_from_string(path_str);
1248 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1249 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1251 fe->allow_sidecar = !fe->allow_sidecar;
1252 if (fe->allow_sidecar) fe->writable = FALSE;
1254 gtk_tree_path_free(tpath);
1258 static void filter_set_func(GtkTreeViewColumn *, GtkCellRenderer *cell,
1259 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1263 gtk_tree_model_get(tree_model, iter, 0, &fe, -1);
1265 switch (GPOINTER_TO_INT(data))
1268 g_object_set(GTK_CELL_RENDERER(cell),
1269 "active", fe->enabled, NULL);
1272 g_object_set(GTK_CELL_RENDERER(cell),
1273 "text", fe->extensions, NULL);
1275 case FE_DESCRIPTION:
1276 g_object_set(GTK_CELL_RENDERER(cell),
1277 "text", fe->description, NULL);
1280 g_object_set(GTK_CELL_RENDERER(cell),
1281 "text", _(format_class_list[fe->file_class]), NULL);
1284 g_object_set(GTK_CELL_RENDERER(cell),
1285 "active", fe->writable, NULL);
1287 case FE_ALLOW_SIDECAR:
1288 g_object_set(GTK_CELL_RENDERER(cell),
1289 "active", fe->allow_sidecar, NULL);
1294 static gboolean filter_add_scroll(gpointer data)
1298 GtkCellRenderer *cell;
1299 GtkTreeViewColumn *column;
1302 GtkTreeModel *store;
1304 FilterEntry *filter;
1306 rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(filter_store), nullptr);
1307 path = gtk_tree_path_new_from_indices(rows-1, -1);
1309 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data), 0);
1311 list_cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1312 cell = static_cast<GtkCellRenderer *>(g_list_last(list_cells)->data);
1314 store = gtk_tree_view_get_model(GTK_TREE_VIEW(data));
1315 valid = gtk_tree_model_get_iter_first(store, &iter);
1319 gtk_tree_model_get(store, &iter, 0, &filter, -1);
1321 if (g_strcmp0(filter->extensions, ".new") == 0)
1323 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1327 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1330 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1331 path, column, FALSE, 0.0, 0.0 );
1332 gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(data),
1333 path, column, cell, TRUE);
1335 gtk_tree_path_free(path);
1336 g_list_free(list_cells);
1338 return(G_SOURCE_REMOVE);
1341 static void filter_add_cb(GtkWidget *, gpointer data)
1343 filter_add_unique("description", ".new", FORMAT_CLASS_IMAGE, TRUE, FALSE, TRUE);
1344 filter_store_populate();
1346 g_idle_add(static_cast<GSourceFunc>(filter_add_scroll), data);
1349 static void filter_remove_cb(GtkWidget *, gpointer data)
1351 auto filter_view = static_cast<GtkWidget *>(data);
1352 GtkTreeSelection *selection;
1356 if (!filter_store) return;
1357 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
1358 if (!gtk_tree_selection_get_selected(selection, nullptr, &iter)) return;
1359 gtk_tree_model_get(GTK_TREE_MODEL(filter_store), &iter, 0, &fe, -1);
1362 filter_remove_entry(fe);
1364 filter_store_populate();
1367 static gboolean filter_default_ok_scroll(GtkTreeView *data)
1371 GtkTreeViewColumn *column;
1373 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(filter_store), &iter);
1374 path = gtk_tree_model_get_path(GTK_TREE_MODEL(filter_store), &iter);
1375 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1377 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1381 gtk_tree_path_free(path);
1383 return(G_SOURCE_REMOVE);
1386 static void filter_default_ok_cb(GenericDialog *gd, gpointer)
1389 filter_add_defaults();
1391 filter_store_populate();
1393 g_idle_add(reinterpret_cast<GSourceFunc>(filter_default_ok_scroll), gd->data);
1396 static void dummy_cancel_cb(GenericDialog *, gpointer)
1398 /* no op, only so cancel button appears */
1401 static void filter_default_cb(GtkWidget *widget, gpointer data)
1405 gd = generic_dialog_new(_("Reset filters"),
1406 "reset_filter", widget, TRUE,
1407 dummy_cancel_cb, data);
1408 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset filters"),
1409 _("This will reset the file filters to the defaults.\nContinue?"), TRUE);
1410 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", filter_default_ok_cb, TRUE);
1411 gtk_widget_show(gd->dialog);
1414 static void filter_disable_cb(GtkWidget *widget, gpointer data)
1416 auto frame = static_cast<GtkWidget *>(data);
1418 gtk_widget_set_sensitive(frame,
1419 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1422 static void safe_delete_view_cb(GtkWidget *, gpointer)
1424 layout_set_path(nullptr, gq_gtk_entry_get_text(GTK_ENTRY(safe_delete_path_entry)));
1427 static void safe_delete_clear_ok_cb(GenericDialog *, gpointer)
1429 file_util_trash_clear();
1432 static void safe_delete_clear_cb(GtkWidget *widget, gpointer)
1436 gd = generic_dialog_new(_("Clear trash"),
1437 "clear_trash", widget, TRUE,
1438 dummy_cancel_cb, nullptr);
1439 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Clear trash"),
1440 _("This will remove the trash contents."), FALSE);
1441 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", safe_delete_clear_ok_cb, TRUE);
1442 entry = gtk_entry_new();
1443 gtk_widget_set_can_focus(entry, FALSE);
1444 gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
1445 if (options->file_ops.safe_delete_path) gq_gtk_entry_set_text(GTK_ENTRY(entry), options->file_ops.safe_delete_path);
1446 gq_gtk_box_pack_start(GTK_BOX(gd->vbox), entry, FALSE, FALSE, 0);
1447 gtk_widget_show(entry);
1448 gtk_widget_show(gd->dialog);
1451 static void image_overlay_template_view_changed_cb(GtkWidget *, gpointer data)
1453 GtkWidget *pTextView;
1454 GtkTextBuffer *pTextBuffer;
1458 pTextView = GTK_WIDGET(data);
1460 pTextBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pTextView));
1461 gtk_text_buffer_get_start_iter(pTextBuffer, &iStart);
1462 gtk_text_buffer_get_end_iter(pTextBuffer, &iEnd);
1464 set_image_overlay_template_string(&c_options->image_overlay.template_string,
1465 gtk_text_buffer_get_text(pTextBuffer, &iStart, &iEnd, TRUE));
1468 static void image_overlay_default_template_ok_cb(GenericDialog *, gpointer data)
1470 auto text_view = static_cast<GtkTextView *>(data);
1471 GtkTextBuffer *buffer;
1473 set_default_image_overlay_template_string(&options->image_overlay.template_string);
1474 if (!configwindow) return;
1476 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
1477 gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
1480 static void image_overlay_default_template_cb(GtkWidget *widget, gpointer data)
1484 gd = generic_dialog_new(_("Reset image overlay template string"),
1485 "reset_image_overlay_template_string", widget, TRUE,
1486 dummy_cancel_cb, data);
1487 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset image overlay template string"),
1488 _("This will reset the image overlay template string to the default.\nContinue?"), TRUE);
1489 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", image_overlay_default_template_ok_cb, TRUE);
1490 gtk_widget_show(gd->dialog);
1493 static void image_overlay_help_cb(GtkWidget *, gpointer)
1495 help_window_show("GuideOptionsOSD.html");
1498 static void font_activated_cb(GtkFontChooser *widget, gchar *fontname, gpointer)
1500 g_free(c_options->image_overlay.font);
1501 c_options->image_overlay.font = g_strdup(fontname);
1504 gq_gtk_widget_destroy(GTK_WIDGET(widget));
1507 static void font_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1511 g_free(c_options->image_overlay.font);
1512 c_options->image_overlay.font = g_strdup(options->image_overlay.font);
1514 if (response_id == GTK_RESPONSE_OK)
1516 font = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(dialog));
1517 g_free(c_options->image_overlay.font);
1518 c_options->image_overlay.font = g_strdup(font);
1522 gq_gtk_widget_destroy(GTK_WIDGET(dialog));
1525 static void image_overlay_set_font_cb(GtkWidget *widget, gpointer)
1529 dialog = gtk_font_chooser_dialog_new(_("Image Overlay Font"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1530 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1531 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), options->image_overlay.font);
1533 g_signal_connect(dialog, "font-activated", G_CALLBACK(font_activated_cb), nullptr);
1534 g_signal_connect(dialog, "response", G_CALLBACK(font_response_cb), nullptr);
1536 gtk_widget_show(dialog);
1539 static void text_color_activated_cb(GtkColorChooser *chooser, GdkRGBA *color, gpointer)
1541 c_options->image_overlay.text_red = color->red * 255;
1542 c_options->image_overlay.text_green = color->green * 255;
1543 c_options->image_overlay.text_blue = color->blue * 255;
1544 c_options->image_overlay.text_alpha = color->alpha * 255;
1546 gq_gtk_widget_destroy(GTK_WIDGET(chooser));
1549 static void text_color_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1553 c_options->image_overlay.text_red = options->image_overlay.text_red;
1554 c_options->image_overlay.text_green = options->image_overlay.text_green;
1555 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
1556 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
1558 if (response_id == GTK_RESPONSE_OK)
1560 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1561 c_options->image_overlay.text_red = color.red * 255;
1562 c_options->image_overlay.text_green = color.green * 255;
1563 c_options->image_overlay.text_blue = color.blue * 255;
1564 c_options->image_overlay.text_alpha = color.alpha * 255;
1567 gq_gtk_widget_destroy(GTK_WIDGET(dialog));
1570 static void image_overlay_set_text_color_cb(GtkWidget *widget, gpointer)
1575 dialog = gtk_color_chooser_dialog_new(_("Image Overlay Text Color"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1576 color.red = options->image_overlay.text_red;
1577 color.green = options->image_overlay.text_green;
1578 color.blue = options->image_overlay.text_blue;
1579 color.alpha = options->image_overlay.text_alpha;
1580 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1581 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1583 g_signal_connect(dialog, "color-activated", G_CALLBACK(text_color_activated_cb), nullptr);
1584 g_signal_connect(dialog, "response", G_CALLBACK(text_color_response_cb), nullptr);
1586 gtk_widget_show(dialog);
1589 static void bg_color_activated_cb(GtkColorChooser *chooser, GdkRGBA *color, gpointer)
1591 c_options->image_overlay.background_red = color->red * 255;
1592 c_options->image_overlay.background_green = color->green * 255;
1593 c_options->image_overlay.background_blue = color->blue * 255;
1594 c_options->image_overlay.background_alpha = color->alpha * 255;
1596 gq_gtk_widget_destroy(GTK_WIDGET(chooser));
1599 static void bg_color_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1603 c_options->image_overlay.background_red = options->image_overlay.background_red;
1604 c_options->image_overlay.background_green = options->image_overlay.background_green;
1605 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
1606 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
1608 if (response_id == GTK_RESPONSE_OK)
1610 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1611 c_options->image_overlay.background_red = color.red * 255;
1612 c_options->image_overlay.background_green = color.green * 255;
1613 c_options->image_overlay.background_blue = color.blue * 255;
1614 c_options->image_overlay.background_alpha = color.alpha * 255;
1616 gq_gtk_widget_destroy(GTK_WIDGET(dialog));
1619 static void image_overlay_set_background_color_cb(GtkWidget *widget, gpointer)
1624 dialog = gtk_color_chooser_dialog_new(_("Image Overlay Background Color"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1625 color.red = options->image_overlay.background_red;
1626 color.green = options->image_overlay.background_green;
1627 color.blue = options->image_overlay.background_blue;
1628 color.alpha = options->image_overlay.background_alpha;
1629 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1630 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1632 g_signal_connect(dialog, "color-activated", G_CALLBACK(bg_color_activated_cb), nullptr);
1633 g_signal_connect(dialog, "response", G_CALLBACK(bg_color_response_cb), nullptr);
1635 gtk_widget_show(dialog);
1638 static void accel_store_populate()
1644 const gchar *accel_path;
1645 const gchar *icon_name;
1649 if (!accel_store || !layout_window_list || !layout_window_list->data) return;
1651 gtk_tree_store_clear(accel_store);
1652 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 */
1654 g_assert(lw && lw->ui_manager);
1655 groups = gtk_ui_manager_get_action_groups(lw->ui_manager);
1658 actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
1661 action = GTK_ACTION(actions->data);
1662 accel_path = gtk_action_get_accel_path(action);
1663 if (accel_path && gtk_accel_map_lookup_entry(accel_path, &key))
1669 g_object_get(action,
1670 "tooltip", &tooltip,
1674 if (pango_parse_markup(label, -1, '_', nullptr, &label2, nullptr, nullptr) && label2)
1680 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1681 icon_name = gtk_action_get_icon_name(action);
1685 gtk_tree_store_append(accel_store, &iter, nullptr);
1686 gtk_tree_store_set(accel_store, &iter,
1689 AE_TOOLTIP, tooltip ? tooltip : "",
1690 AE_ACCEL, accel_path,
1699 actions = actions->next;
1702 groups = groups->next;
1706 static void accel_store_cleared_cb(GtkCellRendererAccel *, gchar *, gpointer)
1711 static gboolean accel_remove_key_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer data)
1713 auto accel1 = static_cast<gchar *>(data);
1718 gtk_tree_model_get(model, iter, AE_KEY, &accel2, -1);
1720 gtk_accelerator_parse(accel1, &key1.accel_key, &key1.accel_mods);
1721 gtk_accelerator_parse(accel2, &key2.accel_key, &key2.accel_mods);
1723 if (key1.accel_key == key2.accel_key && key1.accel_mods == key2.accel_mods)
1725 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1726 DEBUG_1("accelerator key '%s' is already used, removing.", accel1);
1735 static void accel_store_edited_cb(GtkCellRendererAccel *, gchar *path_string, guint accel_key, GdkModifierType accel_mods, guint, gpointer)
1737 auto model = reinterpret_cast<GtkTreeModel *>(accel_store);
1741 GtkAccelKey old_key;
1743 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1745 gtk_tree_model_get_iter(model, &iter, path);
1746 gtk_tree_model_get(model, &iter, AE_ACCEL, &accel_path, -1);
1748 /* test if the accelerator can be stored without conflicts*/
1749 gtk_accel_map_lookup_entry(accel_path, &old_key);
1751 /* change the key and read it back (change may fail on keys hardcoded in gtk)*/
1752 gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, TRUE);
1753 gtk_accel_map_lookup_entry(accel_path, &key);
1755 /* restore the original for now, the key will be really changed when the changes are confirmed */
1756 gtk_accel_map_change_entry(accel_path, old_key.accel_key, old_key.accel_mods, TRUE);
1758 acc = gtk_accelerator_name(key.accel_key, key.accel_mods);
1759 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, acc);
1761 gtk_tree_store_set(accel_store, &iter, AE_KEY, acc, -1);
1762 gtk_tree_path_free(path);
1766 static gboolean accel_default_scroll(GtkTreeView *data)
1770 GtkTreeViewColumn *column;
1772 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(accel_store), &iter);
1773 path = gtk_tree_model_get_path(GTK_TREE_MODEL(accel_store), &iter);
1774 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1776 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1780 gtk_tree_path_free(path);
1782 return(G_SOURCE_REMOVE);
1785 static void accel_default_cb(GtkWidget *, gpointer data)
1787 accel_store_populate();
1789 g_idle_add(reinterpret_cast<GSourceFunc>(accel_default_scroll), data);
1792 void accel_clear_selection(GtkTreeModel *, GtkTreePath *, GtkTreeIter *iter, gpointer)
1794 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1797 void accel_reset_selection(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
1803 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, -1);
1804 gtk_accel_map_lookup_entry(accel_path, &key);
1805 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1807 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, accel);
1809 gtk_tree_store_set(accel_store, iter, AE_KEY, accel, -1);
1814 static void accel_clear_cb(GtkWidget *, gpointer data)
1816 GtkTreeSelection *selection;
1818 if (!accel_store) return;
1819 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1820 gtk_tree_selection_selected_foreach(selection, &accel_clear_selection, nullptr);
1823 static void accel_reset_cb(GtkWidget *, gpointer data)
1825 GtkTreeSelection *selection;
1827 if (!accel_store) return;
1828 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1829 gtk_tree_selection_selected_foreach(selection, &accel_reset_selection, nullptr);
1834 static GtkWidget *scrolled_notebook_page(GtkWidget *notebook, const gchar *title)
1838 GtkWidget *scrolled;
1839 GtkWidget *viewport;
1841 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
1842 gtk_container_set_border_width(GTK_CONTAINER(scrolled), PREF_PAD_BORDER);
1843 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1844 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1845 label = gtk_label_new(title);
1846 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, label);
1847 gtk_widget_show(scrolled);
1849 viewport = gtk_viewport_new(nullptr, nullptr);
1850 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1851 gq_gtk_container_add(GTK_WIDGET(scrolled), viewport);
1852 gtk_widget_show(viewport);
1854 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1855 gq_gtk_container_add(GTK_WIDGET(viewport), vbox);
1856 gtk_widget_show(vbox);
1861 static void cache_standard_cb(GtkWidget *widget, gpointer)
1863 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1865 c_options->thumbnails.spec_standard =TRUE;
1866 c_options->thumbnails.cache_into_dirs = FALSE;
1870 static void cache_geeqie_cb(GtkWidget *widget, gpointer)
1872 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1874 c_options->thumbnails.spec_standard =FALSE;
1875 c_options->thumbnails.cache_into_dirs = FALSE;
1879 static void cache_local_cb(GtkWidget *widget, gpointer)
1881 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1883 c_options->thumbnails.cache_into_dirs = TRUE;
1884 c_options->thumbnails.spec_standard =FALSE;
1888 static void help_search_engine_entry_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1890 if (pos == GTK_ENTRY_ICON_PRIMARY)
1892 gq_gtk_entry_set_text(GTK_ENTRY(userdata), HELP_SEARCH_ENGINE);
1896 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "");
1900 static void star_rating_star_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1902 gchar *rating_symbol;
1904 if (pos == GTK_ENTRY_ICON_PRIMARY)
1906 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_STAR);
1907 gq_gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1908 g_free(rating_symbol);
1912 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1913 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1914 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1918 static void star_rating_rejected_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1920 gchar *rating_symbol;
1922 if (pos == GTK_ENTRY_ICON_PRIMARY)
1924 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_REJECTED);
1925 gq_gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1926 g_free(rating_symbol);
1930 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1931 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1932 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1936 static guint star_rating_symbol_test(GtkWidget *, gpointer data)
1938 auto hbox = static_cast<GtkContainer *>(data);
1939 GString *str = g_string_new(nullptr);
1940 GtkEntry *hex_code_entry;
1941 gchar *hex_code_full;
1944 guint64 hex_value = 0;
1946 list = gtk_container_get_children(hbox);
1948 hex_code_entry = static_cast<GtkEntry *>(g_list_nth_data(list, 2));
1949 hex_code_full = g_strdup(gq_gtk_entry_get_text(hex_code_entry));
1951 hex_code = g_strsplit(hex_code_full, "+", 2);
1952 if (hex_code[0] && hex_code[1])
1954 hex_value = strtoull(hex_code[1], nullptr, 16);
1956 if (!hex_value || hex_value > 0x10FFFF)
1958 hex_value = 0x003F; // Unicode 'Question Mark'
1960 str = g_string_append_unichar(str, static_cast<gunichar>(hex_value));
1961 gtk_label_set_text(static_cast<GtkLabel *>(g_list_nth_data(list, 1)), str->str);
1963 g_strfreev(hex_code);
1964 g_string_free(str, TRUE);
1965 g_free(hex_code_full);
1970 static void star_rating_star_test_cb(GtkWidget *widget, gpointer data)
1972 guint64 star_symbol;
1974 star_symbol = star_rating_symbol_test(widget, data);
1975 c_options->star_rating.star = star_symbol;
1978 static void star_rating_rejected_test_cb(GtkWidget *widget, gpointer data)
1980 guint64 rejected_symbol;
1982 rejected_symbol = star_rating_symbol_test(widget, data);
1983 c_options->star_rating.rejected = rejected_symbol;
1986 /* general options tab */
1987 static void timezone_database_install_cb(GtkWidget *widget, gpointer data);
1991 GCancellable *cancellable;
1993 GtkWidget *progress;
1995 GFile *timezone_database_gq;
1996 gchar *timezone_database_user;
1999 static void config_tab_general(GtkWidget *notebook)
2004 GtkWidget *group_frame;
2005 GtkWidget *subgroup;
2007 GtkWidget *ct_button;
2014 GtkWidget *star_rating_entry;
2016 gchar *rating_symbol;
2019 gchar *download_locn;
2020 GNetworkMonitor *net_mon;
2021 GSocketConnectable *tz_org;
2022 gboolean internet_available = FALSE;
2025 vbox = scrolled_notebook_page(notebook, _("General"));
2027 group = pref_group_new(vbox, FALSE, _("Thumbnails"), GTK_ORIENTATION_VERTICAL);
2029 table = pref_table_new(group, 2, 2, FALSE, FALSE);
2030 add_thumb_size_menu(table, 0, 0, _("Size:"));
2031 add_quality_menu(table, 0, 1, _("Quality:"), options->thumbnails.quality, &c_options->thumbnails.quality);
2033 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2034 pref_label_new(hbox, _("Custom size: "));
2035 pref_spin_new_int(hbox, _("Width:"), nullptr, 1, 512, 1, options->thumbnails.max_width, &c_options->thumbnails.max_width);
2036 pref_spin_new_int(hbox, _("Height:"), nullptr, 1, 512, 1, options->thumbnails.max_height, &c_options->thumbnails.max_height);
2038 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails and sim. files"),
2039 options->thumbnails.enable_caching, &c_options->thumbnails.enable_caching);
2041 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2042 pref_checkbox_link_sensitivity(ct_button, subgroup);
2044 c_options->thumbnails.spec_standard = options->thumbnails.spec_standard;
2045 c_options->thumbnails.cache_into_dirs = options->thumbnails.cache_into_dirs;
2046 group_frame = pref_frame_new(subgroup, TRUE, _("Use Geeqie thumbnail style and cache"),
2047 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2048 button = pref_radiobutton_new(group_frame, nullptr, get_thumbnails_cache_dir(),
2049 !options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2050 G_CALLBACK(cache_geeqie_cb), nullptr);
2052 group_frame = pref_frame_new(subgroup, TRUE,
2053 _("Store thumbnails local to image folder (non-standard)"),
2054 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2055 pref_radiobutton_new(group_frame, button, "*/.thumbnails",
2056 !options->thumbnails.spec_standard && options->thumbnails.cache_into_dirs,
2057 G_CALLBACK(cache_local_cb), nullptr);
2059 group_frame = pref_frame_new(subgroup, TRUE,
2060 _("Use standard thumbnail style and cache, shared with other applications"),
2061 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2062 pref_radiobutton_new(group_frame, button, get_thumbnails_standard_cache_dir(),
2063 options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2064 G_CALLBACK(cache_standard_cb), nullptr);
2066 pref_checkbox_new_int(group, _("Use EXIF thumbnails when available (EXIF thumbnails may be outdated)"),
2067 options->thumbnails.use_exif, &c_options->thumbnails.use_exif);
2069 pref_checkbox_new_int(group, _("Thumbnail color management"),
2070 options->thumbnails.use_color_management, &c_options->thumbnails.use_color_management);
2072 spin = pref_spin_new_int(group, _("Collection preview:"), nullptr,
2074 options->thumbnails.collection_preview, &c_options->thumbnails.collection_preview);
2075 gtk_widget_set_tooltip_text(spin, _("The maximum number of thumbnails shown in a Collection preview montage"));
2077 #if HAVE_FFMPEGTHUMBNAILER_METADATA
2078 pref_checkbox_new_int(group, _("Use embedded metadata in video files as thumbnails when available"),
2079 options->thumbnails.use_ft_metadata, &c_options->thumbnails.use_ft_metadata);
2082 pref_spacer(group, PREF_PAD_GROUP);
2084 group = pref_group_new(vbox, FALSE, _("Star Rating"), GTK_ORIENTATION_VERTICAL);
2086 c_options->star_rating.star = options->star_rating.star;
2087 c_options->star_rating.rejected = options->star_rating.rejected;
2089 str = g_string_new(nullptr);
2090 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2091 pref_label_new(hbox, _("Star character: "));
2092 str = g_string_append_unichar(str, options->star_rating.star);
2093 pref_label_new(hbox, g_strdup(str->str));
2094 rating_symbol = g_strdup_printf("U+%X", options->star_rating.star);
2095 star_rating_entry = gtk_entry_new();
2096 gq_gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2097 gq_gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2098 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2099 gtk_widget_show(star_rating_entry);
2100 button = pref_button_new(nullptr, nullptr, _("Set"),
2101 G_CALLBACK(star_rating_star_test_cb), hbox);
2102 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2103 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2104 gtk_widget_show(button);
2105 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."));
2106 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2107 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2108 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2109 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2110 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2111 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2112 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2113 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2114 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2115 G_CALLBACK(star_rating_star_icon_cb),
2118 g_string_free(str, TRUE);
2119 g_free(rating_symbol);
2121 str = g_string_new(nullptr);
2122 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2123 pref_label_new(hbox, _("Rejected character: "));
2124 str = g_string_append_unichar(str, options->star_rating.rejected);
2125 pref_label_new(hbox, g_strdup(str->str));
2126 rating_symbol = g_strdup_printf("U+%X", options->star_rating.rejected);
2127 star_rating_entry = gtk_entry_new();
2128 gq_gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2129 gq_gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2130 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2131 gtk_widget_show(star_rating_entry);
2132 button = pref_button_new(nullptr, nullptr, _("Set"),
2133 G_CALLBACK(star_rating_rejected_test_cb), hbox);
2134 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2135 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2136 gtk_widget_show(button);
2137 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."));
2138 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2139 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2140 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2141 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2142 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2143 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2144 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2145 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2146 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2147 G_CALLBACK(star_rating_rejected_icon_cb),
2150 g_string_free(str, TRUE);
2151 g_free(rating_symbol);
2153 pref_spacer(group, PREF_PAD_GROUP);
2155 group = pref_group_new(vbox, FALSE, _("Slide show"), GTK_ORIENTATION_VERTICAL);
2157 c_options->slideshow.delay = options->slideshow.delay;
2158 hours = options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2159 remainder = options->slideshow.delay % (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2160 minutes = remainder / (60 * SLIDESHOW_SUBSECOND_PRECISION);
2161 seconds = static_cast<gdouble>(remainder % (60 * SLIDESHOW_SUBSECOND_PRECISION)) /
2162 SLIDESHOW_SUBSECOND_PRECISION;
2164 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2166 spin = pref_spin_new(hbox, _("Delay between image change hrs:mins:secs.dec"), nullptr,
2168 options->slideshow.delay ? hours : 0.0,
2169 G_CALLBACK(slideshow_delay_hours_cb), nullptr);
2170 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2171 spin = pref_spin_new(hbox, ":" , nullptr,
2173 options->slideshow.delay ? minutes: 0.0,
2174 G_CALLBACK(slideshow_delay_minutes_cb), nullptr);
2175 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2176 spin = pref_spin_new(hbox, ":", nullptr,
2177 SLIDESHOW_MIN_SECONDS, 59, 1.0, 1,
2178 options->slideshow.delay ? seconds : 10.0,
2179 G_CALLBACK(slideshow_delay_seconds_cb), nullptr);
2180 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2182 pref_checkbox_new_int(group, _("Random"), options->slideshow.random, &c_options->slideshow.random);
2183 pref_checkbox_new_int(group, _("Repeat"), options->slideshow.repeat, &c_options->slideshow.repeat);
2185 pref_spacer(group, PREF_PAD_GROUP);
2187 group = pref_group_new(vbox, FALSE, _("Image loading and caching"), GTK_ORIENTATION_VERTICAL);
2189 pref_spin_new_int(group, _("Decoded image cache size (MiB):"), nullptr,
2190 0, 99999, 1, options->image.image_cache_max, &c_options->image.image_cache_max);
2191 pref_checkbox_new_int(group, _("Preload next image"),
2192 options->image.enable_read_ahead, &c_options->image.enable_read_ahead);
2194 pref_checkbox_new_int(group, _("Refresh on file change"),
2195 options->update_on_time_change, &c_options->update_on_time_change);
2198 pref_spacer(group, PREF_PAD_GROUP);
2200 group = pref_group_new(vbox, FALSE, _("Menu style"), GTK_ORIENTATION_VERTICAL);
2202 pref_checkbox_new_int(group, _("☰ style menu button (NOTE! Geeqie must be restarted for change to take effect)"),
2203 options->hamburger_menu, &c_options->hamburger_menu);
2204 gtk_widget_set_tooltip_text(group, _("Use a ☰ style menu button instead of the classic style across the top of the frame"));
2206 pref_spacer(group, PREF_PAD_GROUP);
2208 group = pref_group_new(vbox, FALSE, _("Expand toolbar"), GTK_ORIENTATION_VERTICAL);
2210 pref_checkbox_new_int(group, _("Expand menu/toolbar (NOTE! Geeqie must be restarted for change to take effect)"),
2211 options->expand_menu_toolbar, &c_options->expand_menu_toolbar);
2212 gtk_widget_set_tooltip_text(group, _("Expand the menu/toolbar to the full width of the window"));
2214 pref_spacer(group, PREF_PAD_GROUP);
2216 group = pref_group_new(vbox, FALSE, _("Hide Selectable Bars"), GTK_ORIENTATION_VERTICAL);
2218 pref_checkbox_new_int(group, _("Menu bar"),
2219 options->selectable_bars.menu_bar, &c_options->selectable_bars.menu_bar);
2221 pref_checkbox_new_int(group, _("Tool bar"),
2222 options->selectable_bars.tool_bar, &c_options->selectable_bars.tool_bar);
2224 pref_checkbox_new_int(group, _("Status bar"),
2225 options->selectable_bars.status_bar, &c_options->selectable_bars.status_bar);
2226 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"));
2228 pref_spacer(group, PREF_PAD_GROUP);
2230 if (g_getenv("APPDIR") && strstr(g_getenv("APPDIR"), "/tmp/.mount_Geeqie"))
2232 group = pref_group_new(vbox, FALSE, _("AppImage updates notifications"), GTK_ORIENTATION_VERTICAL);
2233 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2234 pref_checkbox_new_int(group, _("Enable"), options->appimage_notifications, &c_options->appimage_notifications);
2235 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"));
2237 pref_spacer(group, PREF_PAD_GROUP);
2241 net_mon = g_network_monitor_get_default();
2242 tz_org = g_network_address_parse_uri(TIMEZONE_DATABASE_WEB, 80, nullptr);
2245 internet_available = g_network_monitor_can_reach(net_mon, tz_org, nullptr, nullptr);
2246 g_object_unref(tz_org);
2249 group = pref_group_new(vbox, FALSE, _("Timezone database"), GTK_ORIENTATION_VERTICAL);
2250 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2252 if (!internet_available)
2254 gtk_widget_set_sensitive(group, FALSE);
2257 tz = g_new0(TZData, 1);
2259 path = path_from_utf8(TIMEZONE_DATABASE_WEB);
2260 basename = g_path_get_basename(path);
2261 tz->timezone_database_user = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
2265 if (isfile(tz->timezone_database_user))
2267 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Update"), G_CALLBACK(timezone_database_install_cb), tz);
2271 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Install"), G_CALLBACK(timezone_database_install_cb), tz);
2274 download_locn = g_strconcat(_("Download database from: "), TIMEZONE_DATABASE_WEB, NULL);
2275 pref_label_new(GTK_WIDGET(hbox), download_locn);
2276 g_free(download_locn);
2278 if (!internet_available)
2280 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"));
2284 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"));
2286 gtk_widget_show(button);
2288 pref_spacer(group, PREF_PAD_GROUP);
2290 group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL);
2292 help_search_engine_entry = gtk_entry_new();
2293 gq_gtk_entry_set_text(GTK_ENTRY(help_search_engine_entry), options->help_search_engine);
2294 gq_gtk_box_pack_start(GTK_BOX(group), help_search_engine_entry, FALSE, FALSE, 0);
2295 gtk_widget_show(help_search_engine_entry);
2297 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"));
2299 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2300 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2301 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2302 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2303 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2304 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2305 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2306 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2307 g_signal_connect(GTK_ENTRY(help_search_engine_entry), "icon-press",
2308 G_CALLBACK(help_search_engine_entry_icon_cb),
2309 help_search_engine_entry);
2313 static void config_tab_image(GtkWidget *notebook)
2318 GtkWidget *ct_button;
2319 GtkWidget *enlargement_button;
2323 vbox = scrolled_notebook_page(notebook, _("Image"));
2325 group = pref_group_new(vbox, FALSE, _("Zoom"), GTK_ORIENTATION_VERTICAL);
2327 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2328 add_quality_menu(table, 0, 0, _("Quality:"), options->image.zoom_quality, &c_options->image.zoom_quality);
2330 pref_checkbox_new_int(group, _("Two pass rendering (apply HQ zoom and color correction in second pass)"),
2331 options->image.zoom_2pass, &c_options->image.zoom_2pass);
2333 c_options->image.zoom_increment = options->image.zoom_increment;
2334 spin = pref_spin_new(group, _("Zoom increment:"), nullptr,
2335 0.01, 4.0, 0.01, 2, static_cast<gdouble>(options->image.zoom_increment) / 100.0,
2336 G_CALLBACK(zoom_increment_cb), nullptr);
2337 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2339 c_options->image.zoom_style = options->image.zoom_style;
2340 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2341 add_zoom_style_selection_menu(table, 0, 0, _("Zoom style:"), options->image.zoom_style, &c_options->image.zoom_style);
2343 group = pref_group_new(vbox, FALSE, _("Fit image to window"), GTK_ORIENTATION_VERTICAL);
2345 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2346 enlargement_button = pref_checkbox_new_int(hbox, _("Allow enlargement of image (max. size in %)"),
2347 options->image.zoom_to_fit_allow_expand, &c_options->image.zoom_to_fit_allow_expand);
2348 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2350 options->image.max_enlargement_size, &c_options->image.max_enlargement_size);
2351 pref_checkbox_link_sensitivity(enlargement_button, spin);
2352 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."));
2354 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2355 ct_button = pref_checkbox_new_int(hbox, _("Virtual window size (%% of actual window):"),
2356 options->image.limit_autofit_size, &c_options->image.limit_autofit_size);
2357 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2359 options->image.max_autofit_size, &c_options->image.max_autofit_size);
2360 pref_checkbox_link_sensitivity(ct_button, spin);
2361 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."));
2363 group = pref_group_new(vbox, FALSE, _("Tile size"), GTK_ORIENTATION_VERTICAL);
2365 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2366 spin = pref_spin_new_int(hbox, _("Pixels"), _("(Requires restart)"),
2368 options->image.tile_size, &c_options->image.tile_size);
2369 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."));
2371 group = pref_group_new(vbox, FALSE, _("Appearance"), GTK_ORIENTATION_VERTICAL);
2373 pref_checkbox_new_int(group, _("Use custom border color in window mode"),
2374 options->image.use_custom_border_color, &c_options->image.use_custom_border_color);
2376 pref_checkbox_new_int(group, _("Use custom border color in fullscreen mode"),
2377 options->image.use_custom_border_color_in_fullscreen, &c_options->image.use_custom_border_color_in_fullscreen);
2379 pref_color_button_new(group, _("Border color"), &options->image.border_color,
2380 G_CALLBACK(pref_color_button_set_cb), &c_options->image.border_color);
2382 c_options->image.border_color = options->image.border_color;
2384 pref_color_button_new(group, _("Alpha channel color 1"), &options->image.alpha_color_1,
2385 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_1);
2387 pref_color_button_new(group, _("Alpha channel color 2"), &options->image.alpha_color_2,
2388 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_2);
2390 c_options->image.alpha_color_1 = options->image.alpha_color_1;
2391 c_options->image.alpha_color_2 = options->image.alpha_color_2;
2396 static void save_default_window_layout_cb(GtkWidget *, gpointer)
2398 LayoutWindow *lw = nullptr;
2399 gchar *default_path;
2402 /* Get current lw */
2405 tmp_id = lw->options.id;
2406 lw->options.id = g_strdup("");
2408 default_path = g_build_filename(get_rc_dir(), DEFAULT_WINDOW_LAYOUT, NULL);
2409 save_default_layout_options_to_file(default_path, options, lw);
2410 g_free(lw->options.id);
2411 lw->options.id = tmp_id;
2412 g_free(default_path);
2415 static gboolean popover_cb(gpointer data)
2417 auto popover = static_cast<GtkPopover *>(data);
2419 gtk_popover_popdown(popover);
2424 static void default_layout_changed_cb(GtkWidget *, GtkPopover *popover)
2426 gtk_popover_popup(popover);
2428 g_timeout_add(2000, popover_cb, popover);
2431 static GtkWidget *create_popover(GtkWidget *parent, GtkWidget *child, GtkPositionType pos)
2435 popover = gtk_popover_new(parent);
2436 gtk_popover_set_position(GTK_POPOVER (popover), pos);
2437 gq_gtk_container_add(GTK_WIDGET(popover), child);
2438 gtk_container_set_border_width(GTK_CONTAINER (popover), 6);
2439 gtk_widget_show (child);
2444 static void config_tab_windows(GtkWidget *notebook)
2449 GtkWidget *subgroup;
2451 GtkWidget *checkbox;
2452 GtkWidget *ct_button;
2455 vbox = scrolled_notebook_page(notebook, _("Windows"));
2457 group = pref_group_new(vbox, FALSE, _("State"), GTK_ORIENTATION_VERTICAL);
2459 ct_button = pref_checkbox_new_int(group, _("Remember session"),
2460 options->save_window_positions, &c_options->save_window_positions);
2462 checkbox = pref_checkbox_new_int(group, _("Use saved window positions also for new windows"),
2463 options->use_saved_window_positions_for_new_windows, &c_options->use_saved_window_positions_for_new_windows);
2464 pref_checkbox_link_sensitivity(ct_button, checkbox);
2466 checkbox = pref_checkbox_new_int(group, _("Remember window workspace"),
2467 options->save_window_workspace, &c_options->save_window_workspace);
2468 pref_checkbox_link_sensitivity(ct_button, checkbox);
2470 pref_checkbox_new_int(group, _("Remember tool state (float/hidden)"),
2471 options->tools_restore_state, &c_options->tools_restore_state);
2473 pref_checkbox_new_int(group, _("Remember dialog window positions"),
2474 options->save_dialog_window_positions, &c_options->save_dialog_window_positions);
2476 pref_checkbox_new_int(group, _("Show window IDs"),
2477 options->show_window_ids, &c_options->show_window_ids);
2479 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2480 pref_label_new(subgroup, _("Use current layout for default: "));
2481 button = pref_button_new(subgroup, nullptr, _("Set"), G_CALLBACK(save_default_window_layout_cb), nullptr);
2485 popover = create_popover(button, gtk_label_new(_("Current window layout\nhas been set as default")), GTK_POS_TOP);
2486 gtk_popover_set_modal(GTK_POPOVER (popover), FALSE);
2487 g_signal_connect(button, "clicked", G_CALLBACK(default_layout_changed_cb), popover);
2489 group = pref_group_new(vbox, FALSE, _("Size"), GTK_ORIENTATION_VERTICAL);
2491 pref_checkbox_new_int(group, _("Fit window to image when tools are hidden/floating"),
2492 options->image.fit_window_to_image, &c_options->image.fit_window_to_image);
2494 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2495 ct_button = pref_checkbox_new_int(hbox, _("Limit size when auto-sizing window (%):"),
2496 options->image.limit_window_size, &c_options->image.limit_window_size);
2497 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2499 options->image.max_window_size, &c_options->image.max_window_size);
2500 pref_checkbox_link_sensitivity(ct_button, spin);
2502 group = pref_group_new(vbox, FALSE, _("Full screen"), GTK_ORIENTATION_VERTICAL);
2504 c_options->fullscreen.screen = options->fullscreen.screen;
2505 c_options->fullscreen.above = options->fullscreen.above;
2506 hbox = fullscreen_prefs_selection_new(_("Location:"), &c_options->fullscreen.screen, &c_options->fullscreen.above);
2507 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2508 gtk_widget_show(hbox);
2510 pref_checkbox_new_int(group, _("Smooth image flip"),
2511 options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
2512 pref_checkbox_new_int(group, _("Disable screen saver"),
2513 options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
2516 static void config_tab_osd(GtkWidget *notebook)
2522 GtkWidget *image_overlay_template_view;
2523 GtkWidget *scrolled;
2524 GtkWidget *scrolled_pre_formatted;
2525 GtkTextBuffer *buffer;
2527 GtkWidget *subgroup;
2529 vbox = scrolled_notebook_page(notebook, _("OSD"));
2531 image_overlay_template_view = gtk_text_view_new();
2533 group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
2535 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2537 scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
2538 gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
2539 gq_gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
2540 gtk_widget_show(scrolled_pre_formatted);
2541 gtk_widget_show(subgroup);
2543 pref_line(group, PREF_PAD_GAP);
2545 pref_label_new(group, _("Image overlay template"));
2547 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2548 gtk_widget_set_size_request(scrolled, 200, 150);
2549 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2550 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
2551 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2552 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
2553 gtk_widget_show(scrolled);
2555 gtk_widget_set_tooltip_markup(image_overlay_template_view,
2556 _("Extensive formatting options are shown in the Help file"));
2558 gq_gtk_container_add(GTK_WIDGET(scrolled), image_overlay_template_view);
2559 gtk_widget_show(image_overlay_template_view);
2561 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2563 button = pref_button_new(nullptr, GQ_ICON_SELECT_FONT, _("Font"),
2564 G_CALLBACK(image_overlay_set_font_cb), notebook);
2566 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2567 gtk_widget_show(button);
2569 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Text"), G_CALLBACK(image_overlay_set_text_color_cb), nullptr);
2570 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2571 gtk_widget_show(button);
2573 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Background"), G_CALLBACK(image_overlay_set_background_color_cb), nullptr);
2574 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2575 gtk_widget_show(button);
2576 image_overlay_set_text_colors();
2578 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2579 G_CALLBACK(image_overlay_default_template_cb), image_overlay_template_view);
2580 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2581 gtk_widget_show(button);
2583 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
2584 G_CALLBACK(image_overlay_help_cb), nullptr);
2585 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2586 gtk_widget_show(button);
2588 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_overlay_template_view));
2589 if (options->image_overlay.template_string) gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
2590 g_signal_connect(G_OBJECT(buffer), "changed",
2591 G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
2593 pref_line(group, PREF_PAD_GAP);
2595 group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), 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(_("%Exif.Image.Orientation%"));
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 separators"), 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(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
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, _("Field maximum length"), 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(_("%path:39%"));
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, _("Pre- and post- text"), 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(_("Text shown only if the field is non-null:\n%formatted.Aperture:F no. * setting%\n %formatted.Aperture:10:F no. * setting%"));
2627 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2628 gtk_widget_show(label);
2629 pref_spacer(group,TRUE);
2631 group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL);
2632 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2633 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2634 gtk_widget_show(hbox);
2635 label = gtk_label_new(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
2636 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2637 gtk_widget_show(label);
2640 static GtkTreeModel *create_class_model()
2642 GtkListStore *model;
2646 /* create list store */
2647 model = gtk_list_store_new(1, G_TYPE_STRING);
2648 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
2650 gtk_list_store_append(model, &iter);
2651 gtk_list_store_set(model, &iter, 0, _(format_class_list[i]), -1);
2653 return GTK_TREE_MODEL (model);
2658 static gint filter_table_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
2660 gint n = GPOINTER_TO_INT(data);
2662 FilterEntry *filter_a;
2663 FilterEntry *filter_b;
2665 gtk_tree_model_get(model, a, 0, &filter_a, -1);
2666 gtk_tree_model_get(model, b, 0, &filter_b, -1);
2670 case FILETYPES_COLUMN_ENABLED:
2672 ret = filter_a->enabled - filter_b->enabled;
2675 case FILETYPES_COLUMN_FILTER:
2677 ret = g_utf8_collate(filter_a->extensions, filter_b->extensions);
2680 case FILETYPES_COLUMN_DESCRIPTION:
2682 ret = g_utf8_collate(filter_a->description, filter_b->description);
2685 case FILETYPES_COLUMN_CLASS:
2687 ret = g_strcmp0(format_class_list[filter_a->file_class], format_class_list[filter_b->file_class]);
2690 case FILETYPES_COLUMN_WRITABLE:
2692 ret = filter_a->writable - filter_b->writable;
2695 case FILETYPES_COLUMN_SIDECAR:
2697 ret = filter_a->allow_sidecar - filter_b->allow_sidecar;
2701 g_return_val_if_reached(0);
2707 static gboolean search_function_cb(GtkTreeModel *model, gint, const gchar *key, GtkTreeIter *iter, gpointer)
2710 gboolean ret = TRUE;
2712 gtk_tree_model_get(model, iter, 0, &fe, -1);
2714 if (g_strstr_len(fe->extensions, -1, key))
2722 static void config_tab_files(GtkWidget *notebook)
2729 GtkWidget *ct_button;
2730 GtkWidget *scrolled;
2731 GtkWidget *filter_view;
2732 GtkCellRenderer *renderer;
2733 GtkTreeSelection *selection;
2734 GtkTreeViewColumn *column;
2736 vbox = scrolled_notebook_page(notebook, _("File Filters"));
2738 group = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2740 pref_checkbox_new_int(group, _("Show hidden files or folders"),
2741 options->file_filter.show_hidden_files, &c_options->file_filter.show_hidden_files);
2742 pref_checkbox_new_int(group, _("Show parent folder (..)"),
2743 options->file_filter.show_parent_directory, &c_options->file_filter.show_parent_directory);
2744 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);
2745 pref_checkbox_new_int(group, _("Disable file extension checks"),
2746 options->file_filter.disable_file_extension_checks, &c_options->file_filter.disable_file_extension_checks);
2748 ct_button = pref_checkbox_new_int(group, _("Disable File Filtering"),
2749 options->file_filter.disable, &c_options->file_filter.disable);
2752 group = pref_group_new(vbox, FALSE, _("Grouping sidecar extensions"), GTK_ORIENTATION_VERTICAL);
2754 sidecar_ext_entry = gtk_entry_new();
2755 gq_gtk_entry_set_text(GTK_ENTRY(sidecar_ext_entry), options->sidecar.ext);
2756 gq_gtk_box_pack_start(GTK_BOX(group), sidecar_ext_entry, FALSE, FALSE, 0);
2757 gtk_widget_show(sidecar_ext_entry);
2759 group = pref_group_new(vbox, TRUE, _("File types"), GTK_ORIENTATION_VERTICAL);
2761 frame = pref_group_parent(group);
2762 g_signal_connect(G_OBJECT(ct_button), "toggled",
2763 G_CALLBACK(filter_disable_cb), frame);
2764 gtk_widget_set_sensitive(frame, !options->file_filter.disable);
2766 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2767 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2768 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2769 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2770 gtk_widget_show(scrolled);
2772 filter_store = gtk_list_store_new(1, G_TYPE_POINTER);
2773 filter_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
2774 g_object_unref(filter_store);
2775 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
2776 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2778 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), FALSE);
2780 column = gtk_tree_view_column_new();
2781 gtk_tree_view_column_set_title(column, _("Enabled"));
2782 gtk_tree_view_column_set_resizable(column, TRUE);
2784 renderer = gtk_cell_renderer_toggle_new();
2785 g_signal_connect(G_OBJECT(renderer), "toggled",
2786 G_CALLBACK(filter_store_enable_cb), filter_store);
2787 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2788 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2789 GINT_TO_POINTER(FE_ENABLE), nullptr);
2790 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);
2791 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_ENABLED);
2792 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2794 column = gtk_tree_view_column_new();
2795 gtk_tree_view_column_set_title(column, _("Filter"));
2796 gtk_tree_view_column_set_resizable(column, TRUE);
2797 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);
2798 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_FILTER);
2800 renderer = gtk_cell_renderer_text_new();
2801 g_signal_connect(G_OBJECT(renderer), "edited",
2802 G_CALLBACK(filter_store_ext_edit_cb), filter_store);
2803 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2804 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2805 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2806 GINT_TO_POINTER(FE_EXTENSION), nullptr);
2807 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2809 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), TRUE);
2810 gtk_tree_view_set_search_column(GTK_TREE_VIEW(filter_view), FILETYPES_COLUMN_FILTER);
2811 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(filter_view), search_function_cb, nullptr, nullptr);
2813 column = gtk_tree_view_column_new();
2814 gtk_tree_view_column_set_title(column, _("Description"));
2815 gtk_tree_view_column_set_resizable(column, TRUE);
2816 gtk_tree_view_column_set_fixed_width(column, 200);
2817 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2819 renderer = gtk_cell_renderer_text_new();
2820 g_signal_connect(G_OBJECT(renderer), "edited",
2821 G_CALLBACK(filter_store_desc_edit_cb), filter_store);
2822 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2823 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2824 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2825 GINT_TO_POINTER(FE_DESCRIPTION), nullptr);
2826 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2827 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);
2828 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_DESCRIPTION);
2830 column = gtk_tree_view_column_new();
2831 gtk_tree_view_column_set_title(column, _("Class"));
2832 gtk_tree_view_column_set_resizable(column, TRUE);
2833 renderer = gtk_cell_renderer_combo_new();
2834 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE,
2835 "model", create_class_model(),
2840 g_signal_connect(G_OBJECT(renderer), "edited",
2841 G_CALLBACK(filter_store_class_edit_cb), filter_store);
2842 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2843 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2844 GINT_TO_POINTER(FE_CLASS), nullptr);
2845 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2846 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);
2847 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_CLASS);
2849 column = gtk_tree_view_column_new();
2850 gtk_tree_view_column_set_title(column, _("Writable"));
2851 gtk_tree_view_column_set_resizable(column, FALSE);
2852 renderer = gtk_cell_renderer_toggle_new();
2853 g_signal_connect(G_OBJECT(renderer), "toggled",
2854 G_CALLBACK(filter_store_writable_cb), filter_store);
2855 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2856 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2857 GINT_TO_POINTER(FE_WRITABLE), nullptr);
2858 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2859 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);
2860 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_WRITABLE);
2862 column = gtk_tree_view_column_new();
2863 gtk_tree_view_column_set_title(column, _("Sidecar is allowed"));
2864 gtk_tree_view_column_set_resizable(column, FALSE);
2865 renderer = gtk_cell_renderer_toggle_new();
2866 g_signal_connect(G_OBJECT(renderer), "toggled",
2867 G_CALLBACK(filter_store_sidecar_cb), filter_store);
2868 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2869 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2870 GINT_TO_POINTER(FE_ALLOW_SIDECAR), nullptr);
2871 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2872 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);
2873 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_SIDECAR);
2875 filter_store_populate();
2876 gq_gtk_container_add(GTK_WIDGET(scrolled), filter_view);
2877 gtk_widget_show(filter_view);
2879 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2881 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2882 G_CALLBACK(filter_default_cb), filter_view);
2883 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2884 gtk_widget_show(button);
2886 button = pref_button_new(nullptr, GQ_ICON_REMOVE, _("Remove"),
2887 G_CALLBACK(filter_remove_cb), filter_view);
2888 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2889 gtk_widget_show(button);
2891 button = pref_button_new(nullptr, GQ_ICON_ADD, _("Add"),
2892 G_CALLBACK(filter_add_cb), filter_view);
2893 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2894 gtk_widget_show(button);
2898 static void config_tab_metadata(GtkWidget *notebook)
2903 GtkWidget *ct_button;
2905 GtkWidget *tmp_widget;
2907 GtkWidget *text_label;
2909 vbox = scrolled_notebook_page(notebook, _("Metadata"));
2912 group = pref_group_new(vbox, FALSE, _("Metadata writing sequence"), GTK_ORIENTATION_VERTICAL);
2914 label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2916 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."));
2917 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2918 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2920 gtk_widget_set_tooltip_text(label, _("A flowchart of the sequence is shown in the Help file"));
2922 ct_button = pref_checkbox_new_int(group, "", options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2923 text_label = gtk_bin_get_child(GTK_BIN(ct_button));
2924 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"));
2925 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2927 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)"));
2928 gtk_widget_set_tooltip_markup(ct_button, markup);
2932 gtk_widget_set_sensitive(ct_button, FALSE);
2935 tmp_widget = pref_checkbox_new_int(group, "", options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2936 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2937 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)"));
2938 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2941 label = pref_label_new(group, "");
2942 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(), "/");
2943 gtk_label_set_markup (GTK_LABEL(label), markup);
2946 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2947 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2948 gtk_widget_set_margin_start(label, 22);
2949 pref_spacer(group, PREF_PAD_GROUP);
2951 group = pref_group_new(vbox, FALSE, _("Step 1 Options:"), GTK_ORIENTATION_VERTICAL);
2953 gtk_widget_set_sensitive(group, FALSE);
2956 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2957 pref_checkbox_link_sensitivity(ct_button, hbox);
2959 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);
2960 gtk_widget_set_tooltip_text(tmp_widget, _("A simplified conversion list is in the Help file"));
2962 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);
2964 pref_checkbox_new_int(hbox, _("Ask before writing to image files"), options->metadata.confirm_write, &c_options->metadata.confirm_write);
2966 tmp_widget= pref_checkbox_new_int(hbox, "", options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2967 gtk_widget_set_tooltip_text(tmp_widget, _("This file naming convention is used by Darktable"));
2968 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2969 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", ")");
2970 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2973 pref_spacer(group, PREF_PAD_GROUP);
2975 group = pref_group_new(vbox, FALSE, _("Steps 2 and 3 Option:"), GTK_ORIENTATION_VERTICAL);
2977 gtk_widget_set_sensitive(group, FALSE);
2980 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);
2982 pref_spacer(group, PREF_PAD_GROUP);
2984 group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2985 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);
2986 gtk_widget_set_tooltip_text(tmp_widget, _("See the Help file for a list of the tags used"));
2988 tmp_widget = pref_checkbox_new_int(group, _("Permit Keywords to be case-sensitive"), options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2989 gtk_widget_set_tooltip_text(tmp_widget, _("When selected, \"Place\" and \"place\" are two different keywords"));
2991 ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"), options->metadata.write_orientation, &c_options->metadata.write_orientation);
2992 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"));
2995 gtk_widget_set_sensitive(ct_button, FALSE);
2998 pref_spacer(group, PREF_PAD_GROUP);
3000 group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
3002 ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"), options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
3004 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3005 pref_checkbox_link_sensitivity(ct_button, hbox);
3007 pref_spin_new_int(hbox, _("Timeout (seconds):"), nullptr, 0, 900, 1, options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
3009 pref_checkbox_new_int(group, _("Write metadata on image change"), options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
3011 pref_checkbox_new_int(group, _("Write metadata on directory change"), options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
3013 pref_spacer(group, PREF_PAD_GROUP);
3016 group = pref_group_new(vbox, FALSE, _("Spelling checks"), GTK_ORIENTATION_VERTICAL);
3018 ct_button = pref_checkbox_new_int(group, _("Check spelling - Requires restart"), options->metadata.check_spelling, &c_options->metadata.check_spelling);
3019 gtk_widget_set_tooltip_text(ct_button, _("Spelling checks are performed on info sidebar panes Comment, Headline and Title"));
3022 pref_spacer(group, PREF_PAD_GROUP);
3024 group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
3026 ct_button = pref_checkbox_new_int(group, _("Read metadata in background"), options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
3027 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");
3032 struct KeywordFindData
3039 GtkWidget *button_close;
3040 GtkWidget *button_stop;
3041 GtkWidget *button_start;
3042 GtkWidget *progress;
3050 guint idle_id; /* event source id */
3053 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
3055 GList *list_d = nullptr;
3056 GList *list_f = nullptr;
3060 filelist_read(dir_fd, &list_f, &list_d);
3064 filelist_read(dir_fd, &list_f, nullptr);
3067 list_f = filelist_filter(list_f, FALSE);
3068 list_d = filelist_filter(list_d, TRUE);
3070 kfd->list = g_list_concat(list_f, kfd->list);
3071 kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
3074 static void keywords_find_reset(KeywordFindData *kfd)
3076 filelist_free(kfd->list);
3077 kfd->list = nullptr;
3079 filelist_free(kfd->list_dir);
3080 kfd->list_dir = nullptr;
3083 static void keywords_find_close_cb(GenericDialog *, gpointer data)
3085 auto kfd = static_cast<KeywordFindData *>(data);
3087 if (!gtk_widget_get_sensitive(kfd->button_close)) return;
3089 keywords_find_reset(kfd);
3090 generic_dialog_close(kfd->gd);
3094 static void keywords_find_finish(KeywordFindData *kfd)
3096 keywords_find_reset(kfd);
3098 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
3099 gtk_spinner_stop(GTK_SPINNER(kfd->spinner));
3101 gtk_widget_set_sensitive(kfd->group, TRUE);
3102 gtk_widget_set_sensitive(kfd->button_start, TRUE);
3103 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3104 gtk_widget_set_sensitive(kfd->button_close, TRUE);
3107 static void keywords_find_stop_cb(GenericDialog *, gpointer data)
3109 auto kfd = static_cast<KeywordFindData *>(data);
3111 g_idle_remove_by_data(kfd);
3113 keywords_find_finish(kfd);
3116 static gboolean keywords_find_file(gpointer data)
3118 auto kfd = static_cast<KeywordFindData *>(data);
3120 GtkTextBuffer *buffer;
3128 fd = static_cast<FileData *>(kfd->list->data);
3129 kfd->list = g_list_remove(kfd->list, fd);
3131 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
3132 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3136 gtk_text_buffer_get_end_iter(buffer, &iter);
3137 tmp = g_strconcat(static_cast<const gchar *>(keywords->data), "\n", NULL);
3138 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3140 keywords = keywords->next;
3143 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
3144 file_data_unref(fd);
3145 g_list_free_full(keywords, g_free);
3147 return (G_SOURCE_CONTINUE);
3154 fd = static_cast<FileData *>(kfd->list_dir->data);
3155 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
3157 keywords_find_folder(kfd, fd);
3159 file_data_unref(fd);
3161 return G_SOURCE_CONTINUE;
3164 keywords_find_finish(kfd);
3166 return G_SOURCE_REMOVE;
3169 static void keywords_find_start_cb(GenericDialog *, gpointer data)
3171 auto kfd = static_cast<KeywordFindData *>(data);
3174 if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
3176 path = remove_trailing_slash((gq_gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
3177 parse_out_relatives(path);
3181 warning_dialog(_("Invalid folder"),
3182 _("The specified folder can not be found."),
3183 GQ_ICON_DIALOG_WARNING, kfd->gd->dialog);
3189 gtk_widget_set_sensitive(kfd->group, FALSE);
3190 gtk_widget_set_sensitive(kfd->button_start, FALSE);
3191 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
3192 gtk_widget_set_sensitive(kfd->button_close, FALSE);
3193 gtk_spinner_start(GTK_SPINNER(kfd->spinner));
3195 dir_fd = file_data_new_dir(path);
3196 keywords_find_folder(kfd, dir_fd);
3197 file_data_unref(dir_fd);
3198 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
3204 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
3206 KeywordFindData *kfd;
3210 kfd = g_new0(KeywordFindData, 1);
3212 kfd->gd = generic_dialog_new(_("Search for keywords"),
3213 "search_for_keywords",
3216 gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
3217 kfd->gd->cancel_cb = keywords_find_close_cb;
3218 kfd->button_close = generic_dialog_add_button(kfd->gd, GQ_ICON_CLOSE, _("Close"),
3219 keywords_find_close_cb, FALSE);
3220 kfd->button_start = generic_dialog_add_button(kfd->gd, GQ_ICON_OK, _("S_tart"),
3221 keywords_find_start_cb, FALSE);
3222 kfd->button_stop = generic_dialog_add_button(kfd->gd, GQ_ICON_STOP, _("Stop"),
3223 keywords_find_stop_cb, FALSE);
3224 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3226 generic_dialog_add_message(kfd->gd, nullptr, _("Search for keywords"), nullptr, FALSE);
3228 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3229 pref_spacer(hbox, PREF_PAD_INDENT);
3230 kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3232 hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3233 pref_label_new(hbox, _("Folder:"));
3235 label = tab_completion_new(&kfd->entry, path, nullptr, nullptr, nullptr, nullptr);
3236 tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
3237 gq_gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3238 gtk_widget_show(label);
3240 pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
3242 pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
3243 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3245 kfd->progress = gtk_entry_new();
3246 gtk_widget_set_can_focus(kfd->progress, FALSE);
3247 gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
3248 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
3249 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
3250 gtk_widget_show(kfd->progress);
3252 kfd->spinner = gtk_spinner_new();
3253 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
3254 gtk_widget_show(kfd->spinner);
3256 kfd->list = nullptr;
3258 gtk_widget_show(kfd->gd->dialog);
3261 static void keywords_find_cb(GtkWidget *widget, gpointer)
3263 const gchar *path = layout_get_path(nullptr);
3265 if (!path || !*path) path = homedir();
3266 keywords_find_dialog(widget, path);
3269 static void config_tab_keywords_save()
3273 GtkTextBuffer *buffer;
3274 GList *kw_list = nullptr;
3280 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3281 gtk_text_buffer_get_bounds(buffer, &start, &end);
3283 buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3285 kw_split = strtok(buffer_text, "\n");
3286 while (kw_split != nullptr)
3292 if (g_strcmp0(static_cast<const gchar *>(work->data), kw_split) == 0)
3301 kw_list = g_list_append(kw_list, g_strdup(kw_split));
3303 kw_split = strtok(nullptr, "\n");
3306 keyword_list_set(kw_list);
3308 g_list_free_full(kw_list, g_free);
3309 g_free(buffer_text);
3312 static void config_tab_keywords(GtkWidget *notebook)
3318 GtkWidget *scrolled;
3320 GtkTextBuffer *buffer;
3323 GspellTextView *gspell_view;
3326 vbox = scrolled_notebook_page(notebook, _("Keywords"));
3328 group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
3330 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3332 button = pref_button_new(hbox, GQ_ICON_RUN, _("Search"),
3333 G_CALLBACK(keywords_find_cb), keyword_text);
3334 gtk_widget_set_tooltip_text(button, _("Search for existing keywords"));
3337 keyword_text = gtk_text_view_new();
3338 gtk_widget_set_size_request(keyword_text, 20, 20);
3339 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3340 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3341 gtk_widget_show(scrolled);
3344 if (options->metadata.check_spelling)
3346 gspell_view = gspell_text_view_get_from_gtk_text_view(GTK_TEXT_VIEW(keyword_text));
3347 gspell_text_view_basic_setup(gspell_view);
3351 gq_gtk_container_add(GTK_WIDGET(scrolled), keyword_text);
3352 gtk_widget_show(keyword_text);
3354 gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
3356 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3357 gtk_text_buffer_create_tag(buffer, "monospace",
3358 "family", "monospace", NULL);
3360 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3361 gtk_text_buffer_get_start_iter(buffer, &iter);
3362 gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3365 path = g_build_filename(get_rc_dir(), "keywords", NULL);
3367 GList *kwl = keyword_list_get();
3368 kwl = g_list_first(kwl);
3371 gtk_text_buffer_get_end_iter (buffer, &iter);
3372 tmp = g_strconcat(static_cast<const gchar *>(kwl->data), "\n", NULL);
3373 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3378 gtk_text_buffer_set_modified(buffer, FALSE);
3385 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3387 auto option = static_cast<gint *>(data);
3389 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3393 *option = INTENT_PERCEPTUAL;
3396 *option = INTENT_RELATIVE_COLORIMETRIC;
3399 *option = INTENT_SATURATION;
3402 *option = INTENT_ABSOLUTE_COLORIMETRIC;
3407 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3408 gint option, gint *option_c)
3415 pref_table_label(table, column, row, text, GTK_ALIGN_START);
3417 combo = gtk_combo_box_text_new();
3419 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3420 if (option == INTENT_PERCEPTUAL) current = 0;
3421 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3422 if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3423 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3424 if (option == INTENT_SATURATION) current = 2;
3425 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3426 if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3428 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3430 gtk_widget_set_tooltip_text(combo,_("Refer to the lcms documentation for the defaults used when the selected Intent is not available"));
3432 g_signal_connect(G_OBJECT(combo), "changed",
3433 G_CALLBACK(intent_menu_cb), option_c);
3435 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
3436 gtk_widget_show(combo);
3440 static void config_tab_color(GtkWidget *notebook)
3449 vbox = scrolled_notebook_page(notebook, _("Color management"));
3451 group = pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3453 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3456 table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3457 gtk_grid_set_column_spacing(GTK_GRID(table), PREF_PAD_GAP);
3459 label = pref_table_label(table, 0, 0, _("Type"), GTK_ALIGN_START);
3460 pref_label_bold(label, TRUE, FALSE);
3462 label = pref_table_label(table, 1, 0, _("Menu name"), GTK_ALIGN_START);
3463 pref_label_bold(label, TRUE, FALSE);
3465 label = pref_table_label(table, 2, 0, _("File"), GTK_ALIGN_START);
3466 pref_label_bold(label, TRUE, FALSE);
3468 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3473 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3474 pref_table_label(table, 0, i + 1, buf, GTK_ALIGN_END);
3477 entry = gtk_entry_new();
3478 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3479 if (options->color_profile.input_name[i])
3481 gq_gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3483 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);
3484 gtk_widget_show(entry);
3485 color_profile_input_name_entry[i] = entry;
3487 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], nullptr, ".icc", "ICC Files", nullptr);
3488 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3489 gtk_widget_set_size_request(entry, 160, -1);
3490 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);
3491 gtk_widget_show(tabcomp);
3492 color_profile_input_file_entry[i] = entry;
3495 group = pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3497 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3499 pref_checkbox_new_int(group, _("Use system screen profile if available"),
3500 options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3502 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3504 pref_table_label(table, 0, 0, _("Screen:"), GTK_ALIGN_END);
3505 tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3506 options->color_profile.screen_file, nullptr, ".icc", "ICC Files", nullptr);
3507 tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3508 gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3510 add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3512 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);
3514 gtk_widget_show(tabcomp);
3517 /* advanced entry tab */
3518 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer)
3520 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3522 c_options->file_ops.use_system_trash = FALSE;
3523 c_options->file_ops.no_trash = FALSE;
3527 static void use_system_trash_cb(GtkWidget *widget, gpointer)
3529 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3531 c_options->file_ops.use_system_trash = TRUE;
3532 c_options->file_ops.no_trash = FALSE;
3536 static void use_no_cache_cb(GtkWidget *widget, gpointer)
3538 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3540 c_options->file_ops.no_trash = TRUE;
3544 static void config_tab_behavior(GtkWidget *notebook)
3551 GtkWidget *ct_button;
3555 GtkWidget *with_rename;
3556 GtkWidget *collections_on_top;
3557 GtkWidget *hide_window_in_fullscreen;
3558 GtkWidget *checkbox;
3561 vbox = scrolled_notebook_page(notebook, _("Behavior"));
3563 group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3565 pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3566 options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3567 pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3568 options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3569 pref_checkbox_new_int(group, _("Enable Delete key"),
3570 options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3572 ct_button = pref_radiobutton_new(group, nullptr, _("Use Geeqie trash location"),
3573 !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),nullptr);
3575 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3576 pref_checkbox_link_sensitivity(ct_button, hbox);
3578 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3579 pref_label_new(hbox, _("Folder:"));
3581 tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, nullptr, nullptr, nullptr, nullptr);
3582 tab_completion_add_select_button(safe_delete_path_entry, nullptr, TRUE);
3583 gq_gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3584 gtk_widget_show(tabcomp);
3586 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3587 pref_checkbox_link_sensitivity(ct_button, hbox);
3589 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3590 spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MiB"),
3591 0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3592 gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3593 button = pref_button_new(nullptr, nullptr, _("View"),
3594 G_CALLBACK(safe_delete_view_cb), nullptr);
3595 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3596 gtk_widget_show(button);
3598 button = pref_button_new(nullptr, GQ_ICON_CLEAR, nullptr,
3599 G_CALLBACK(safe_delete_clear_cb), nullptr);
3600 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3602 c_options->file_ops.no_trash = options->file_ops.no_trash;
3603 c_options->file_ops.use_system_trash = options->file_ops.use_system_trash;
3605 pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3606 options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), nullptr);
3608 pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3609 options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), nullptr);
3611 gtk_widget_show(button);
3613 pref_spacer(group, PREF_PAD_GROUP);
3616 group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3618 pref_checkbox_new_int(group, _("Descend folders in tree view"),
3619 options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3621 pref_checkbox_new_int(group, _("In place renaming"),
3622 options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3624 pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3625 options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3627 tmp = pref_checkbox_new_int(group, _("Circular selection lists"),
3628 options->circular_selection_lists, &c_options->circular_selection_lists);
3629 gtk_widget_set_tooltip_text(tmp, _("Traverse selection lists in a circular manner"));
3631 marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3632 options->marks_save, &c_options->marks_save);
3633 gtk_widget_set_tooltip_text(marks,_("Note that marks linked to a keyword will be saved irrespective of this setting"));
3635 with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3636 options->with_rename, &c_options->with_rename);
3637 gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3639 collections_on_top = pref_checkbox_new_int(group, _("Permit duplicates in Collections"),
3640 options->collections_duplicates, &c_options->collections_duplicates);
3641 gtk_widget_set_tooltip_text(collections_on_top, _("Allow the same image to be in a Collection more than once"));
3643 collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3644 options->collections_on_top, &c_options->collections_on_top);
3645 gtk_widget_set_tooltip_text(collections_on_top, _("Open collections window on top"));
3647 hide_window_in_fullscreen = pref_checkbox_new_int(group, _("Hide window in fullscreen"),
3648 options->hide_window_in_fullscreen, &c_options->hide_window_in_fullscreen);
3649 gtk_widget_set_tooltip_text(hide_window_in_fullscreen,"When alt-tabbing, prevent Geeqie window showing twice");
3651 pref_spin_new_int(group, _("Recent folder list maximum size"), nullptr,
3652 1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3654 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);
3655 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."));
3657 pref_spin_new_int(group, _("Drag'n drop icon size"), nullptr,
3658 16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3660 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3661 add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
3663 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3664 add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3666 pref_spacer(group, PREF_PAD_GROUP);
3668 group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3670 pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3671 options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3672 pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), nullptr,
3673 1, 32, 1, options->keyboard_scroll_step, reinterpret_cast<int *>(&c_options->keyboard_scroll_step));
3674 pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3675 options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3676 pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3677 options->image_lm_click_nav, &c_options->image_lm_click_nav);
3678 pref_checkbox_new_int(group, _("Open archive by left click on image"),
3679 options->image_l_click_archive, &c_options->image_l_click_archive);
3680 pref_checkbox_new_int(group, _("Play video by left click on image"),
3681 options->image_l_click_video, &c_options->image_l_click_video);
3682 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3683 add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3685 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3686 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3687 add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3688 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3689 add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3691 pref_spacer(group, PREF_PAD_GROUP);
3693 group = pref_group_new(vbox, FALSE, _("GPU"), GTK_ORIENTATION_VERTICAL);
3695 checkbox = pref_checkbox_new_int(group, _("Override disable GPU"),
3696 options->override_disable_gpu, &c_options->override_disable_gpu);
3697 gtk_widget_set_tooltip_text(checkbox, _("Contact the developers for usage"));
3700 pref_spacer(group, PREF_PAD_GROUP);
3702 group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3704 pref_spin_new_int(group, _("Debug level:"), nullptr,
3705 DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3707 pref_checkbox_new_int(group, _("Timer data"),
3708 options->log_window.timer_data, &c_options->log_window.timer_data);
3710 pref_spin_new_int(group, _("Log Window max. lines:"), nullptr,
3711 1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3713 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3714 pref_label_new(hbox, _("Log Window F1 command: "));
3715 log_window_f1_entry = gtk_entry_new();
3716 gq_gtk_entry_set_text(GTK_ENTRY(log_window_f1_entry), options->log_window.action);
3717 gq_gtk_box_pack_start(GTK_BOX(hbox), log_window_f1_entry, FALSE, FALSE, 0);
3718 gtk_entry_set_width_chars(GTK_ENTRY(log_window_f1_entry), 15);
3719 gtk_widget_show(log_window_f1_entry);
3723 /* accelerators tab */
3725 static gboolean accel_search_function_cb(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer)
3727 gboolean ret = TRUE;
3732 gtk_tree_model_get(model, iter, column, &text, -1);
3733 text_nocase = g_utf8_casefold(text, -1);
3734 key_nocase = g_utf8_casefold(key, -1);
3736 if (g_strstr_len(text_nocase, -1, key_nocase))
3743 g_free(text_nocase);
3748 static void accel_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *, GtkTreeViewColumn *column, gpointer)
3753 list = gtk_tree_view_get_columns(tree_view);
3754 col_num = g_list_index(list, column);
3758 gtk_tree_view_set_search_column(tree_view, col_num);
3761 static void config_tab_accelerators(GtkWidget *notebook)
3767 GtkWidget *scrolled;
3768 GtkWidget *accel_view;
3769 GtkCellRenderer *renderer;
3770 GtkTreeSelection *selection;
3771 GtkTreeViewColumn *column;
3773 vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3775 group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3777 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3778 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3779 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3780 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3781 gtk_widget_show(scrolled);
3783 accel_store = gtk_tree_store_new(5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3785 accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3786 g_object_unref(accel_store);
3787 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3788 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3790 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3792 renderer = gtk_cell_renderer_text_new();
3794 column = gtk_tree_view_column_new_with_attributes(_("Action"),
3799 gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3800 gtk_tree_view_column_set_resizable(column, TRUE);
3801 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3804 renderer = gtk_cell_renderer_accel_new();
3805 g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3806 G_CALLBACK(accel_store_cleared_cb), accel_store);
3807 g_signal_connect(G_OBJECT(renderer), "accel-edited",
3808 G_CALLBACK(accel_store_edited_cb), accel_store);
3811 g_object_set (renderer,
3813 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3816 column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3821 gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3822 gtk_tree_view_column_set_resizable(column, TRUE);
3823 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3825 renderer = gtk_cell_renderer_text_new();
3827 column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3832 gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3833 gtk_tree_view_column_set_resizable(column, TRUE);
3834 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3836 renderer = gtk_cell_renderer_text_new();
3838 column = gtk_tree_view_column_new_with_attributes("Accel",
3843 gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3844 gtk_tree_view_column_set_resizable(column, TRUE);
3845 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3847 column = gtk_tree_view_column_new_with_attributes("Icon",
3852 gtk_tree_view_column_set_sort_column_id(column, AE_ICON);
3853 gtk_tree_view_column_set_resizable(column, TRUE);
3854 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3856 /* Search on text in column */
3857 gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(accel_view), TRUE);
3858 g_signal_connect(accel_view, "row_activated", G_CALLBACK(accel_row_activated_cb), accel_store);
3859 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), TRUE);
3860 gtk_tree_view_set_search_column(GTK_TREE_VIEW(accel_view), AE_TOOLTIP);
3861 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(accel_view), accel_search_function_cb, nullptr, nullptr);
3863 accel_store_populate();
3864 gq_gtk_container_add(GTK_WIDGET(scrolled), accel_view);
3865 gtk_widget_show(accel_view);
3867 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3869 button = pref_button_new(nullptr, nullptr, _("Defaults"),
3870 G_CALLBACK(accel_default_cb), accel_view);
3871 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3872 gtk_widget_show(button);
3874 button = pref_button_new(nullptr, nullptr, _("Reset selected"),
3875 G_CALLBACK(accel_reset_cb), accel_view);
3876 gtk_widget_set_tooltip_text(button, _("Will only reset changes made before the settings are saved"));
3877 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3878 gtk_widget_show(button);
3880 button = pref_button_new(nullptr, nullptr, _("Clear selected"),
3881 G_CALLBACK(accel_clear_cb), accel_view);
3882 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3883 gtk_widget_show(button);
3886 /* toolbar main tab */
3887 static void config_tab_toolbar_main(GtkWidget *notebook)
3890 GtkWidget *toolbardata;
3893 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3895 vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3897 toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3898 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3899 gtk_widget_show(vbox);
3902 /* toolbar status tab */
3903 static void config_tab_toolbar_status(GtkWidget *notebook)
3906 GtkWidget *toolbardata;
3909 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3911 vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3913 toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3914 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3915 gtk_widget_show(vbox);
3919 static gint extension_sort_cb(gconstpointer a, gconstpointer b)
3921 return g_strcmp0(static_cast<const gchar *>(a), static_cast<const gchar *>(b));
3924 static void config_tab_advanced(GtkWidget *notebook)
3927 GdkPixbufFormat *fm;
3929 GList *extensions_list = nullptr;
3930 GSList *formats_list;
3931 GString *types_string = g_string_new(nullptr);
3932 GtkWidget *alternate_checkbox;
3933 GtkWidget *dupes_threads_spin;
3935 GtkWidget *subgroup;
3937 GtkWidget *threads_string_label;
3938 GtkWidget *types_string_label;
3941 vbox = scrolled_notebook_page(notebook, _("Advanced"));
3942 group = pref_group_new(vbox, FALSE, _("External preview extraction"), GTK_ORIENTATION_VERTICAL);
3944 pref_checkbox_new_int(group, _("Use external preview extraction - Requires restart"), options->external_preview.enable, &c_options->external_preview.enable);
3946 pref_spacer(group, PREF_PAD_GROUP);
3948 formats_list = gdk_pixbuf_get_formats();
3950 while (formats_list)
3952 fm = static_cast<GdkPixbufFormat *>(formats_list->data);
3953 extensions = gdk_pixbuf_format_get_extensions(fm);
3956 while (extensions[i])
3958 extensions_list = g_list_insert_sorted(extensions_list, g_strdup(extensions[i]), extension_sort_cb);
3962 g_strfreev(extensions);
3963 formats_list = formats_list->next;
3966 while (extensions_list)
3968 if (types_string->len == 0)
3970 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3974 types_string = g_string_append(types_string, ", ");
3975 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3978 extensions_list = extensions_list->next;
3981 types_string = g_string_prepend(types_string, _("Usable file types:\n"));
3982 types_string_label = pref_label_new(group, types_string->str);
3983 gtk_label_set_line_wrap(GTK_LABEL(types_string_label), TRUE);
3985 pref_spacer(group, PREF_PAD_GROUP);
3987 group = pref_group_new(vbox, FALSE, _("File identification tool"), GTK_ORIENTATION_VERTICAL);
3988 external_preview_select_entry = gtk_entry_new();
3989 tabcomp = tab_completion_new(&external_preview_select_entry, options->external_preview.select, nullptr, nullptr, nullptr, nullptr);
3990 tab_completion_add_select_button(external_preview_select_entry, _("Select file identification tool"), FALSE);
3991 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3992 gtk_widget_show(tabcomp);
3994 group = pref_group_new(vbox, FALSE, _("Preview extraction tool"), GTK_ORIENTATION_VERTICAL);
3995 external_preview_extract_entry = gtk_entry_new();
3996 tabcomp = tab_completion_new(&external_preview_extract_entry, options->external_preview.extract, nullptr, nullptr, nullptr, nullptr);
3997 tab_completion_add_select_button(external_preview_extract_entry, _("Select preview extraction tool"), FALSE);
3998 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3999 gtk_widget_show(tabcomp);
4001 gtk_widget_show(vbox);
4003 g_slist_free(formats_list);
4004 g_list_free_full(extensions_list, g_free);
4005 g_string_free(types_string, TRUE);
4007 pref_spacer(group, PREF_PAD_GROUP);
4009 pref_line(vbox, PREF_PAD_SPACE);
4010 group = pref_group_new(vbox, FALSE, _("Thread pool limits"), GTK_ORIENTATION_VERTICAL);
4012 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."));
4013 gtk_label_set_line_wrap(GTK_LABEL(threads_string_label), TRUE);
4015 pref_spacer(vbox, PREF_PAD_GROUP);
4017 dupes_threads_spin = pref_spin_new_int(vbox, _("Duplicate check:"), _("max. threads"), 0, get_cpu_cores(), 1, options->threads.duplicates, &c_options->threads.duplicates);
4018 gtk_widget_set_tooltip_markup(dupes_threads_spin, _("Set to 0 for unlimited"));
4020 pref_spacer(group, PREF_PAD_GROUP);
4022 pref_line(vbox, PREF_PAD_SPACE);
4024 group = pref_group_new(vbox, FALSE, _("Alternate similarity alogorithm"), GTK_ORIENTATION_VERTICAL);
4026 alternate_checkbox = pref_checkbox_new_int(group, _("Enable alternate similarity algorithm"), options->alternate_similarity_algorithm.enabled, &c_options->alternate_similarity_algorithm.enabled);
4028 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
4029 pref_checkbox_link_sensitivity(alternate_checkbox, subgroup);
4031 alternate_checkbox = pref_checkbox_new_int(subgroup, _("Use grayscale"), options->alternate_similarity_algorithm.grayscale, &c_options->alternate_similarity_algorithm.grayscale);
4032 gtk_widget_set_tooltip_text(alternate_checkbox, _("Reduce fingerprint to grayscale"));
4036 static void config_tab_stereo(GtkWidget *notebook)
4044 GtkWidget *fs_button;
4045 vbox = scrolled_notebook_page(notebook, _("Stereo"));
4047 group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
4049 table = pref_table_new(group, 2, 1, FALSE, FALSE);
4050 add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
4052 table = pref_table_new(group, 2, 2, TRUE, FALSE);
4053 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4054 pref_checkbox_new_int(box, _("Mirror left image"),
4055 options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
4056 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4057 pref_checkbox_new_int(box, _("Flip left image"),
4058 options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
4059 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4060 pref_checkbox_new_int(box, _("Mirror right image"),
4061 options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
4062 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4063 pref_checkbox_new_int(box, _("Flip right image"),
4064 options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
4065 pref_checkbox_new_int(group, _("Swap left and right images"),
4066 options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
4067 pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
4068 options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
4070 group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
4071 fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
4072 options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
4073 box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4074 pref_checkbox_link_sensitivity(fs_button, box2);
4075 table = pref_table_new(box2, 2, 1, FALSE, FALSE);
4076 add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
4077 table = pref_table_new(box2, 2, 2, TRUE, FALSE);
4078 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4079 pref_checkbox_new_int(box, _("Mirror left image"),
4080 options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
4081 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4082 pref_checkbox_new_int(box, _("Flip left image"),
4083 options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
4084 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4085 pref_checkbox_new_int(box, _("Mirror right image"),
4086 options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
4087 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4088 pref_checkbox_new_int(box, _("Flip right image"),
4089 options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
4090 pref_checkbox_new_int(box2, _("Swap left and right images"),
4091 options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
4092 pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
4093 options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
4095 group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
4096 table = pref_table_new(group2, 5, 3, FALSE, FALSE);
4097 pref_table_spin_new_int(table, 0, 0, _("Width"), nullptr,
4098 1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
4099 pref_table_spin_new_int(table, 3, 0, _("Height"), nullptr,
4100 1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
4101 pref_table_spin_new_int(table, 0, 1, _("Left X"), nullptr,
4102 0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
4103 pref_table_spin_new_int(table, 3, 1, _("Left Y"), nullptr,
4104 0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
4105 pref_table_spin_new_int(table, 0, 2, _("Right X"), nullptr,
4106 0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
4107 pref_table_spin_new_int(table, 3, 2, _("Right Y"), nullptr,
4108 0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
4112 /* Main preferences window */
4113 static void config_window_create(LayoutWindow *lw)
4115 GtkWidget *win_vbox;
4117 GtkWidget *notebook;
4119 GtkWidget *ct_button;
4121 if (!c_options) c_options = init_options(nullptr);
4123 configwindow = window_new("preferences", PIXBUF_INLINE_ICON_CONFIG, nullptr, _("Preferences"));
4124 DEBUG_NAME(configwindow);
4125 gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
4126 g_signal_connect(G_OBJECT(configwindow), "delete_event",
4127 G_CALLBACK(config_window_delete), NULL);
4128 if (options->save_dialog_window_positions)
4130 gtk_window_resize(GTK_WINDOW(configwindow), lw->options.preferences_window.w, lw->options.preferences_window.h);
4131 gq_gtk_window_move(GTK_WINDOW(configwindow), lw->options.preferences_window.x, lw->options.preferences_window.y);
4135 gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
4137 gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
4138 gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
4140 win_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4141 gq_gtk_container_add(GTK_WIDGET(configwindow), win_vbox);
4142 gtk_widget_show(win_vbox);
4144 notebook = gtk_notebook_new();
4145 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
4146 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
4147 gq_gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
4149 config_tab_general(notebook);
4150 config_tab_image(notebook);
4151 config_tab_osd(notebook);
4152 config_tab_windows(notebook);
4153 config_tab_accelerators(notebook);
4154 config_tab_files(notebook);
4155 config_tab_metadata(notebook);
4156 config_tab_keywords(notebook);
4157 config_tab_color(notebook);
4158 config_tab_stereo(notebook);
4159 config_tab_behavior(notebook);
4160 config_tab_toolbar_main(notebook);
4161 config_tab_toolbar_status(notebook);
4162 config_tab_advanced(notebook);
4164 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), lw->options.preferences_window.page_number);
4166 hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
4167 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
4168 gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
4169 gq_gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
4170 gtk_widget_show(hbox);
4172 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
4173 G_CALLBACK(config_window_help_cb), notebook);
4174 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4175 gtk_widget_set_can_default(button, TRUE);
4176 gtk_widget_show(button);
4178 button = pref_button_new(nullptr, GQ_ICON_OK, "OK",
4179 G_CALLBACK(config_window_ok_cb), notebook);
4180 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4181 gtk_widget_set_can_default(button, TRUE);
4182 gtk_widget_grab_default(button);
4183 gtk_widget_show(button);
4187 button = pref_button_new(nullptr, GQ_ICON_CANCEL, _("Cancel"),
4188 G_CALLBACK(config_window_close_cb), nullptr);
4189 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4190 gtk_widget_set_can_default(button, TRUE);
4191 gtk_widget_show(button);
4193 if (!generic_dialog_get_alternative_button_order(configwindow))
4195 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
4198 gtk_widget_show(notebook);
4200 gtk_widget_show(configwindow);
4204 *-----------------------------------------------------------------------------
4205 * config window show (public)
4206 *-----------------------------------------------------------------------------
4209 void show_config_window(LayoutWindow *lw)
4213 gtk_window_present(GTK_WINDOW(configwindow));
4217 config_window_create(lw);
4226 void show_about_window(LayoutWindow *lw)
4228 GDataInputStream *data_stream;
4229 GInputStream *in_stream_authors;
4230 GInputStream *in_stream_translators;
4232 GdkPixbuf *pixbuf_icon;
4233 GdkPixbuf *pixbuf_logo;
4237 gchar *authors[1000];
4239 gchar *timezone_path;
4248 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"));
4250 timezone_path = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
4251 if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
4253 cd = ZDOpenDatabase(timezone_path);
4256 copyright = g_string_append(copyright, ZDGetNotice(cd));
4260 log_printf("Error: Init of timezone database %s failed\n", timezone_path);
4262 ZDCloseDatabase(cd);
4264 g_free(timezone_path);
4266 copyright = g_string_append(copyright, _("\n\nSome icons by https://www.flaticon.com"));
4268 in_stream_authors = g_resources_open_stream(GQ_RESOURCE_PATH_CREDITS "/authors", G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4270 data_stream = g_data_input_stream_new(in_stream_authors);
4272 authors[0] = nullptr;
4273 while ((author_line = g_data_input_stream_read_line(G_DATA_INPUT_STREAM(data_stream), &length, nullptr, nullptr)))
4275 authors[i_authors] = g_strdup(author_line);
4277 g_free(author_line);
4279 authors[i_authors] = nullptr;
4281 g_input_stream_close(in_stream_authors, nullptr, nullptr);
4283 constexpr auto translators_path = GQ_RESOURCE_PATH_CREDITS "/translators";
4285 g_resources_get_info(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &size, &flags, nullptr);
4287 in_stream_translators = g_resources_open_stream(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4288 translators = static_cast<gchar *>(g_malloc0(size));
4289 g_input_stream_read_all(in_stream_translators, translators, size, &bytes_read, nullptr, nullptr);
4290 g_input_stream_close(in_stream_translators, nullptr, nullptr);
4292 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);
4294 artists[0] = g_strdup("Néstor Díaz Valencia <nestor@estudionexos.com>");
4295 artists[1] = nullptr;
4297 pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
4298 pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
4299 gtk_show_about_dialog(GTK_WINDOW(lw->window),
4300 "title", _("About Geeqie"),
4302 "program-name", GQ_APPNAME,
4304 "logo", pixbuf_logo,
4305 "icon", pixbuf_icon,
4306 "website", GQ_WEBSITE,
4307 "website-label", _("Website"),
4308 "comments", comment,
4311 "translator-credits", translators,
4312 "wrap-license", TRUE,
4313 "license", copyright->str,
4316 g_string_free(copyright, TRUE);
4318 while(n < i_authors)
4326 g_free(translators);
4327 g_object_unref(data_stream);
4328 g_object_unref(in_stream_authors);
4329 g_object_unref(in_stream_translators);
4332 static void image_overlay_set_text_colors()
4334 c_options->image_overlay.text_red = options->image_overlay.text_red;
4335 c_options->image_overlay.text_green = options->image_overlay.text_green;
4336 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
4337 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
4338 c_options->image_overlay.background_red = options->image_overlay.background_red;
4339 c_options->image_overlay.background_green = options->image_overlay.background_green;
4340 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
4341 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
4345 *-----------------------------------------------------------------------------
4346 * timezone database routines
4347 *-----------------------------------------------------------------------------
4350 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
4352 GError *error = nullptr;
4353 auto tz = static_cast<TZData *>(data);
4354 gchar *tmp_filename;
4355 gchar *timezone_bin;
4356 gchar *tmp_dir = nullptr;
4359 if (!g_cancellable_is_cancelled(tz->cancellable))
4361 generic_dialog_close(tz->gd);
4365 if (g_file_copy_finish(G_FILE(source_object), res, &error))
4367 tmp_filename = g_file_get_path(tz->tmp_g_file);
4368 fd = file_data_new_simple(tmp_filename);
4369 tmp_dir = open_archive(fd);
4373 timezone_bin = g_build_filename(tmp_dir, TIMEZONE_DATABASE_VERSION, TIMEZONE_DATABASE_FILE, NULL);
4374 if (isfile(timezone_bin))
4376 move_file(timezone_bin, tz->timezone_database_user);
4380 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4383 g_free(timezone_bin);
4384 g_free(tmp_dir); // The folder in /tmp is deleted in exit_program_final()
4388 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4390 g_free(tmp_filename);
4391 file_data_unref(fd);
4395 file_util_warning_dialog(_("Error: Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4398 g_file_delete(tz->tmp_g_file, nullptr, &error);
4399 g_object_unref(tz->tmp_g_file);
4400 tz->tmp_g_file = nullptr;
4401 g_object_unref(tz->cancellable);
4402 g_object_unref(tz->timezone_database_gq);
4405 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
4407 auto tz = static_cast<TZData *>(data);
4409 if (!g_cancellable_is_cancelled(tz->cancellable))
4411 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), static_cast<gdouble>(current_num_bytes) / total_num_bytes);
4415 static void timezone_cancel_button_cb(GenericDialog *, gpointer data)
4417 auto tz = static_cast<TZData *>(data);
4419 g_cancellable_cancel(tz->cancellable);
4422 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
4424 auto tz = static_cast<TZData *>(data);
4425 GError *error = nullptr;
4426 GFileIOStream *io_stream;
4433 tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
4437 file_util_warning_dialog(_("Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4438 log_printf("Error: Download timezone database failed:\n%s", error->message);
4439 g_error_free(error);
4440 g_object_unref(tz->tmp_g_file);
4444 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE_WEB);
4446 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", nullptr, TRUE, timezone_cancel_button_cb, tz);
4448 generic_dialog_add_message(tz->gd, GQ_ICON_DIALOG_INFO, _("Downloading timezone database"), nullptr, FALSE);
4450 tz->progress = gtk_progress_bar_new();
4451 gq_gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
4452 gtk_widget_show(tz->progress);
4454 gtk_widget_show(tz->gd->dialog);
4455 tz->cancellable = g_cancellable_new();
4456 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);
4458 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
4461 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */