2 * Copyright (C) 2006 John Ellis
3 * Copyright (C) 2008 - 2016 The Geeqie Team
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "preferences.h"
25 #include "bar-keywords.h"
27 #include "color-man.h"
30 #include "filefilter.h"
31 #include "fullscreen.h"
33 #include "image-overlay.h"
35 #include "layout-util.h"
39 #include "pixbuf-util.h"
41 #include "slideshow.h"
45 #include "ui-fileops.h"
47 #include "ui-tabcomp.h"
49 #include "zonedetect.h"
60 #include <gspell/gspell.h>
63 #define EDITOR_NAME_MAX_LENGTH 32
64 #define EDITOR_COMMAND_MAX_LENGTH 1024
66 static void image_overlay_set_text_colors();
68 GtkWidget *keyword_text;
69 static void config_tab_keywords_save();
77 static ThumbSize thumb_size_list[] =
112 FILETYPES_COLUMN_ENABLED = 0,
113 FILETYPES_COLUMN_FILTER,
114 FILETYPES_COLUMN_DESCRIPTION,
115 FILETYPES_COLUMN_CLASS,
116 FILETYPES_COLUMN_WRITABLE,
117 FILETYPES_COLUMN_SIDECAR,
118 FILETYPES_COLUMN_COUNT
121 const gchar *format_class_list[] = {
132 /* config memory values */
133 static ConfOptions *c_options = nullptr;
140 static GtkWidget *configwindow = nullptr;
141 static GtkListStore *filter_store = nullptr;
142 static GtkTreeStore *accel_store = nullptr;
144 static GtkWidget *safe_delete_path_entry;
146 static GtkWidget *color_profile_input_file_entry[COLOR_PROFILE_INPUTS];
147 static GtkWidget *color_profile_input_name_entry[COLOR_PROFILE_INPUTS];
148 static GtkWidget *color_profile_screen_file_entry;
149 static GtkWidget *external_preview_select_entry;
150 static GtkWidget *external_preview_extract_entry;
152 static GtkWidget *sidecar_ext_entry;
153 static GtkWidget *help_search_engine_entry;
156 #define CONFIG_WINDOW_DEF_WIDTH 700
157 #define CONFIG_WINDOW_DEF_HEIGHT 600
160 *-----------------------------------------------------------------------------
161 * option widget callbacks (private)
162 *-----------------------------------------------------------------------------
165 static void zoom_increment_cb(GtkWidget *spin, gpointer)
167 c_options->image.zoom_increment = static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) * 100.0 + 0.01);
170 static void slideshow_delay_hours_cb(GtkWidget *spin, gpointer)
172 gint mins_secs_tenths, delay;
174 mins_secs_tenths = c_options->slideshow.delay %
175 (3600 * SLIDESHOW_SUBSECOND_PRECISION);
177 delay = (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
178 (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
181 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
182 SLIDESHOW_SUBSECOND_PRECISION;
185 static void slideshow_delay_minutes_cb(GtkWidget *spin, gpointer)
187 gint hours, secs_tenths, delay;
189 hours = c_options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
190 secs_tenths = c_options->slideshow.delay % (60 * SLIDESHOW_SUBSECOND_PRECISION);
192 delay = hours * (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
193 (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
194 (60 * SLIDESHOW_SUBSECOND_PRECISION) + secs_tenths);
196 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
197 SLIDESHOW_SUBSECOND_PRECISION;
200 static void slideshow_delay_seconds_cb(GtkWidget *spin, gpointer)
202 gint hours_mins, delay;
204 hours_mins = c_options->slideshow.delay / (60 * SLIDESHOW_SUBSECOND_PRECISION);
206 delay = (hours_mins * (60 * SLIDESHOW_SUBSECOND_PRECISION)) +
207 static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
208 static_cast<gdouble>(SLIDESHOW_SUBSECOND_PRECISION) + 0.01);
210 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
211 SLIDESHOW_SUBSECOND_PRECISION;
215 *-----------------------------------------------------------------------------
216 * sync program to config window routine (private)
217 *-----------------------------------------------------------------------------
220 void config_entry_to_option(GtkWidget *entry, gchar **option, gchar *(*func)(const gchar *))
226 buf = gq_gtk_entry_get_text(GTK_ENTRY(entry));
227 if (buf && strlen(buf) > 0)
232 *option = g_strdup(buf);
237 static gboolean accel_apply_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
239 gchar *accel_path, *accel;
241 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, AE_KEY, &accel, -1);
243 if (accel_path && accel_path[0])
246 gtk_accelerator_parse(accel, &key.accel_key, &key.accel_mods);
247 gtk_accel_map_change_entry(accel_path, key.accel_key, key.accel_mods, TRUE);
257 static void config_window_apply()
259 gboolean refresh = FALSE;
264 config_entry_to_option(safe_delete_path_entry, &options->file_ops.safe_delete_path, remove_trailing_slash);
266 if (options->file_filter.show_hidden_files != c_options->file_filter.show_hidden_files) refresh = TRUE;
267 if (options->file_filter.show_parent_directory != c_options->file_filter.show_parent_directory) refresh = TRUE;
268 if (options->file_filter.show_dot_directory != c_options->file_filter.show_dot_directory) refresh = TRUE;
269 if (options->file_sort.case_sensitive != c_options->file_sort.case_sensitive) refresh = TRUE;
270 if (options->file_filter.disable_file_extension_checks != c_options->file_filter.disable_file_extension_checks) refresh = TRUE;
271 if (options->file_filter.disable != c_options->file_filter.disable) refresh = TRUE;
273 options->file_ops.confirm_delete = c_options->file_ops.confirm_delete;
274 options->file_ops.enable_delete_key = c_options->file_ops.enable_delete_key;
275 options->file_ops.confirm_move_to_trash = c_options->file_ops.confirm_move_to_trash;
276 options->file_ops.use_system_trash = c_options->file_ops.use_system_trash;
277 options->file_ops.no_trash = c_options->file_ops.no_trash;
278 options->file_ops.safe_delete_folder_maxsize = c_options->file_ops.safe_delete_folder_maxsize;
279 options->tools_restore_state = c_options->tools_restore_state;
280 options->save_window_positions = c_options->save_window_positions;
281 options->use_saved_window_positions_for_new_windows = c_options->use_saved_window_positions_for_new_windows;
282 options->save_window_workspace = c_options->save_window_workspace;
283 options->save_dialog_window_positions = c_options->save_dialog_window_positions;
284 options->show_window_ids = c_options->show_window_ids;
285 options->image.scroll_reset_method = c_options->image.scroll_reset_method;
286 options->image.zoom_2pass = c_options->image.zoom_2pass;
287 options->image.fit_window_to_image = c_options->image.fit_window_to_image;
288 options->image.limit_window_size = c_options->image.limit_window_size;
289 options->image.zoom_to_fit_allow_expand = c_options->image.zoom_to_fit_allow_expand;
290 options->image.max_window_size = c_options->image.max_window_size;
291 options->image.limit_autofit_size = c_options->image.limit_autofit_size;
292 options->image.max_autofit_size = c_options->image.max_autofit_size;
293 options->image.max_enlargement_size = c_options->image.max_enlargement_size;
294 options->image.tile_size = c_options->image.tile_size;
295 options->progressive_key_scrolling = c_options->progressive_key_scrolling;
296 options->keyboard_scroll_step = c_options->keyboard_scroll_step;
298 if (options->thumbnails.max_width != c_options->thumbnails.max_width
299 || options->thumbnails.max_height != c_options->thumbnails.max_height
300 || options->thumbnails.quality != c_options->thumbnails.quality)
302 thumb_format_changed = TRUE;
304 options->thumbnails.max_width = c_options->thumbnails.max_width;
305 options->thumbnails.max_height = c_options->thumbnails.max_height;
306 options->thumbnails.quality = c_options->thumbnails.quality;
308 options->thumbnails.enable_caching = c_options->thumbnails.enable_caching;
309 options->thumbnails.cache_into_dirs = c_options->thumbnails.cache_into_dirs;
310 options->thumbnails.use_exif = c_options->thumbnails.use_exif;
311 options->thumbnails.use_color_management = c_options->thumbnails.use_color_management;
312 options->thumbnails.collection_preview = c_options->thumbnails.collection_preview;
313 options->thumbnails.use_ft_metadata = c_options->thumbnails.use_ft_metadata;
314 options->thumbnails.spec_standard = c_options->thumbnails.spec_standard;
315 options->metadata.enable_metadata_dirs = c_options->metadata.enable_metadata_dirs;
316 options->file_filter.show_hidden_files = c_options->file_filter.show_hidden_files;
317 options->file_filter.show_parent_directory = c_options->file_filter.show_parent_directory;
318 options->file_filter.show_dot_directory = c_options->file_filter.show_dot_directory;
319 options->file_filter.disable_file_extension_checks = c_options->file_filter.disable_file_extension_checks;
321 options->file_sort.case_sensitive = c_options->file_sort.case_sensitive;
322 options->file_filter.disable = c_options->file_filter.disable;
324 config_entry_to_option(sidecar_ext_entry, &options->sidecar.ext, nullptr);
325 sidecar_ext_parse(options->sidecar.ext);
327 options->slideshow.random = c_options->slideshow.random;
328 options->slideshow.repeat = c_options->slideshow.repeat;
329 options->slideshow.delay = c_options->slideshow.delay;
331 options->mousewheel_scrolls = c_options->mousewheel_scrolls;
332 options->image_lm_click_nav = c_options->image_lm_click_nav;
333 options->image_l_click_archive = c_options->image_l_click_archive;
334 options->image_l_click_video = c_options->image_l_click_video;
335 options->image_l_click_video_editor = c_options->image_l_click_video_editor;
337 options->file_ops.enable_in_place_rename = c_options->file_ops.enable_in_place_rename;
339 options->image.tile_cache_max = c_options->image.tile_cache_max;
340 options->image.image_cache_max = c_options->image.image_cache_max;
342 options->image.zoom_quality = c_options->image.zoom_quality;
344 options->image.zoom_increment = c_options->image.zoom_increment;
346 options->image.zoom_style = c_options->image.zoom_style;
348 options->image.enable_read_ahead = c_options->image.enable_read_ahead;
350 options->appimage_notifications = c_options->appimage_notifications;
353 if (options->image.use_custom_border_color != c_options->image.use_custom_border_color
354 || options->image.use_custom_border_color_in_fullscreen != c_options->image.use_custom_border_color_in_fullscreen
355 || !gdk_rgba_equal(&options->image.border_color, &c_options->image.border_color))
357 options->image.use_custom_border_color_in_fullscreen = c_options->image.use_custom_border_color_in_fullscreen;
358 options->image.use_custom_border_color = c_options->image.use_custom_border_color;
359 options->image.border_color = c_options->image.border_color;
360 layout_colors_update();
361 view_window_colors_update();
364 options->image.alpha_color_1 = c_options->image.alpha_color_1;
365 options->image.alpha_color_2 = c_options->image.alpha_color_2;
367 options->fullscreen.screen = c_options->fullscreen.screen;
368 options->fullscreen.clean_flip = c_options->fullscreen.clean_flip;
369 options->fullscreen.disable_saver = c_options->fullscreen.disable_saver;
370 options->fullscreen.above = c_options->fullscreen.above;
371 if (c_options->image_overlay.template_string)
372 set_image_overlay_template_string(&options->image_overlay.template_string,
373 c_options->image_overlay.template_string);
374 if (c_options->image_overlay.font)
375 set_image_overlay_font_string(&options->image_overlay.font,
376 c_options->image_overlay.font);
377 options->image_overlay.text_red = c_options->image_overlay.text_red;
378 options->image_overlay.text_green = c_options->image_overlay.text_green;
379 options->image_overlay.text_blue = c_options->image_overlay.text_blue;
380 options->image_overlay.text_alpha = c_options->image_overlay.text_alpha;
381 options->image_overlay.background_red = c_options->image_overlay.background_red;
382 options->image_overlay.background_green = c_options->image_overlay.background_green;
383 options->image_overlay.background_blue = c_options->image_overlay.background_blue;
384 options->image_overlay.background_alpha = c_options->image_overlay.background_alpha;
385 options->image_overlay.font = c_options->image_overlay.font;
386 options->update_on_time_change = c_options->update_on_time_change;
388 options->duplicates_similarity_threshold = c_options->duplicates_similarity_threshold;
389 options->rot_invariant_sim = c_options->rot_invariant_sim;
391 options->tree_descend_subdirs = c_options->tree_descend_subdirs;
393 options->view_dir_list_single_click_enter = c_options->view_dir_list_single_click_enter;
394 options->circular_selection_lists = c_options->circular_selection_lists;
396 options->open_recent_list_maxsize = c_options->open_recent_list_maxsize;
397 options->recent_folder_image_list_maxsize = c_options->recent_folder_image_list_maxsize;
398 options->dnd_icon_size = c_options->dnd_icon_size;
399 options->clipboard_selection = c_options->clipboard_selection;
400 options->dnd_default_action = c_options->dnd_default_action;
402 options->metadata.save_in_image_file = c_options->metadata.save_in_image_file;
403 options->metadata.save_legacy_IPTC = c_options->metadata.save_legacy_IPTC;
404 options->metadata.warn_on_write_problems = c_options->metadata.warn_on_write_problems;
405 options->metadata.save_legacy_format = c_options->metadata.save_legacy_format;
406 options->metadata.sync_grouped_files = c_options->metadata.sync_grouped_files;
407 options->metadata.confirm_write = c_options->metadata.confirm_write;
408 options->metadata.sidecar_extended_name = c_options->metadata.sidecar_extended_name;
409 options->metadata.confirm_timeout = c_options->metadata.confirm_timeout;
410 options->metadata.confirm_after_timeout = c_options->metadata.confirm_after_timeout;
411 options->metadata.confirm_on_image_change = c_options->metadata.confirm_on_image_change;
412 options->metadata.confirm_on_dir_change = c_options->metadata.confirm_on_dir_change;
413 options->metadata.keywords_case_sensitive = c_options->metadata.keywords_case_sensitive;
414 options->metadata.write_orientation = c_options->metadata.write_orientation;
415 options->metadata.check_spelling = c_options->metadata.check_spelling;
416 options->stereo.mode = (c_options->stereo.mode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
417 (c_options->stereo.tmp.mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
418 (c_options->stereo.tmp.flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
419 (c_options->stereo.tmp.mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
420 (c_options->stereo.tmp.flip_left ? PR_STEREO_FLIP_LEFT : 0) |
421 (c_options->stereo.tmp.swap ? PR_STEREO_SWAP : 0) |
422 (c_options->stereo.tmp.temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
423 options->stereo.fsmode = (c_options->stereo.fsmode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
424 (c_options->stereo.tmp.fs_mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
425 (c_options->stereo.tmp.fs_flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
426 (c_options->stereo.tmp.fs_mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
427 (c_options->stereo.tmp.fs_flip_left ? PR_STEREO_FLIP_LEFT : 0) |
428 (c_options->stereo.tmp.fs_swap ? PR_STEREO_SWAP : 0) |
429 (c_options->stereo.tmp.fs_temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
430 options->stereo.enable_fsmode = c_options->stereo.enable_fsmode;
431 options->stereo.fixed_w = c_options->stereo.fixed_w;
432 options->stereo.fixed_h = c_options->stereo.fixed_h;
433 options->stereo.fixed_x1 = c_options->stereo.fixed_x1;
434 options->stereo.fixed_y1 = c_options->stereo.fixed_y1;
435 options->stereo.fixed_x2 = c_options->stereo.fixed_x2;
436 options->stereo.fixed_y2 = c_options->stereo.fixed_y2;
438 options->info_keywords.height = c_options->info_keywords.height;
439 options->info_title.height = c_options->info_title.height;
440 options->info_comment.height = c_options->info_comment.height;
441 options->info_rating.height = c_options->info_rating.height;
443 options->show_predefined_keyword_tree = c_options->show_predefined_keyword_tree;
444 options->expand_menu_toolbar = c_options->expand_menu_toolbar;
446 options->selectable_bars.menu_bar = c_options->selectable_bars.menu_bar;
447 options->selectable_bars.tool_bar = c_options->selectable_bars.tool_bar;
448 options->selectable_bars.status_bar = c_options->selectable_bars.status_bar;
450 options->marks_save = c_options->marks_save;
451 options->with_rename = c_options->with_rename;
452 options->collections_duplicates = c_options->collections_duplicates;
453 options->collections_on_top = c_options->collections_on_top;
454 options->hide_window_in_fullscreen = c_options->hide_window_in_fullscreen;
455 config_entry_to_option(help_search_engine_entry, &options->help_search_engine, nullptr);
457 options->external_preview.enable = c_options->external_preview.enable;
458 config_entry_to_option(external_preview_select_entry, &options->external_preview.select, nullptr);
459 config_entry_to_option(external_preview_extract_entry, &options->external_preview.extract, nullptr);
461 options->read_metadata_in_idle = c_options->read_metadata_in_idle;
463 options->star_rating.star = c_options->star_rating.star;
464 options->star_rating.rejected = c_options->star_rating.rejected;
466 options->threads.duplicates = c_options->threads.duplicates > 0 ? c_options->threads.duplicates : -1;
469 set_debug_level(debug_c);
473 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
475 config_entry_to_option(color_profile_input_name_entry[i], &options->color_profile.input_name[i], nullptr);
476 config_entry_to_option(color_profile_input_file_entry[i], &options->color_profile.input_file[i], nullptr);
478 config_entry_to_option(color_profile_screen_file_entry, &options->color_profile.screen_file, nullptr);
479 options->color_profile.use_x11_screen_profile = c_options->color_profile.use_x11_screen_profile;
480 if (options->color_profile.render_intent != c_options->color_profile.render_intent)
482 options->color_profile.render_intent = c_options->color_profile.render_intent;
487 options->mouse_button_8 = c_options->mouse_button_8;
488 options->mouse_button_9 = c_options->mouse_button_9;
490 options->override_disable_gpu = c_options->override_disable_gpu;
492 config_tab_keywords_save();
494 image_options_sync();
499 layout_refresh(nullptr);
502 if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, nullptr);
504 toolbar_apply(TOOLBAR_MAIN);
505 toolbar_apply(TOOLBAR_STATUS);
509 *-----------------------------------------------------------------------------
510 * config window main button callbacks (private)
511 *-----------------------------------------------------------------------------
514 static void config_window_close_cb(GtkWidget *, gpointer)
516 gq_gtk_widget_destroy(configwindow);
517 configwindow = nullptr;
518 filter_store = nullptr;
521 static void config_window_help_cb(GtkWidget *, gpointer data)
523 auto notebook = static_cast<GtkWidget *>(data);
526 static const gchar *html_section[] =
528 "GuideOptionsGeneral.html",
529 "GuideOptionsImage.html",
530 "GuideOptionsOSD.html",
531 "GuideOptionsWindow.html",
532 "GuideOptionsKeyboard.html",
533 "GuideOptionsFiltering.html",
534 "GuideOptionsMetadata.html",
535 "GuideOptionsKeywords.html",
536 "GuideOptionsColor.html",
537 "GuideOptionsStereo.html",
538 "GuideOptionsBehavior.html",
539 "GuideOptionsToolbar.html",
540 "GuideOptionsToolbar.html",
541 "GuideOptionsAdvanced.html"
544 i = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
545 help_window_show(html_section[i]);
548 static gboolean config_window_delete(GtkWidget *, GdkEventAny *, gpointer)
550 config_window_close_cb(nullptr, nullptr);
554 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
557 auto notebook = static_cast<GtkNotebook *>(data);
565 lw = static_cast<LayoutWindow *>(layout_window_list->data);
567 window = gtk_widget_get_window(widget);
568 gdk_window_get_root_origin(window, &x, &y);
569 w = gdk_window_get_width(window);
570 h = gdk_window_get_height(window);
571 page_number = gtk_notebook_get_current_page(notebook);
573 lw->options.preferences_window.x = x;
574 lw->options.preferences_window.y = y;
575 lw->options.preferences_window.w = w;
576 lw->options.preferences_window.h = h;
577 lw->options.preferences_window.page_number = page_number;
579 config_window_apply();
580 layout_util_sync(lw);
581 save_options(options);
582 config_window_close_cb(nullptr, nullptr);
586 *-----------------------------------------------------------------------------
587 * config window setup (private)
588 *-----------------------------------------------------------------------------
591 static void quality_menu_cb(GtkWidget *combo, gpointer data)
593 auto option = static_cast<gint *>(data);
595 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
599 *option = GDK_INTERP_NEAREST;
602 *option = GDK_INTERP_TILES;
605 *option = GDK_INTERP_BILINEAR;
610 static void dnd_default_action_selection_menu_cb(GtkWidget *combo, gpointer data)
612 auto option = static_cast<gint *>(data);
614 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
618 *option = DND_ACTION_ASK;
621 *option = DND_ACTION_COPY;
624 *option = DND_ACTION_MOVE;
628 static void clipboard_selection_menu_cb(GtkWidget *combo, gpointer data)
630 auto option = static_cast<gint *>(data);
632 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
635 *option = CLIPBOARD_PRIMARY;
638 *option = CLIPBOARD_CLIPBOARD;
641 *option = CLIPBOARD_BOTH;
644 *option = CLIPBOARD_BOTH;
648 static void add_quality_menu(GtkWidget *table, gint column, gint row, const gchar *text,
649 guint option, guint *option_c)
656 pref_table_label(table, column, row, text, GTK_ALIGN_START);
658 combo = gtk_combo_box_text_new();
660 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Nearest (worst, but fastest)"));
661 if (option == GDK_INTERP_NEAREST) current = 0;
662 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Tiles"));
663 if (option == GDK_INTERP_TILES) current = 1;
664 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Bilinear (best, but slowest)"));
665 if (option == GDK_INTERP_BILINEAR) current = 2;
667 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
669 g_signal_connect(G_OBJECT(combo), "changed",
670 G_CALLBACK(quality_menu_cb), option_c);
672 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
673 gtk_widget_show(combo);
676 static void add_dnd_default_action_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, DnDAction option, DnDAction *option_c)
683 pref_table_label(table, column, row, text, GTK_ALIGN_START);
685 combo = gtk_combo_box_text_new();
687 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Ask"));
688 if (option == DND_ACTION_ASK) current = 0;
689 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Copy"));
690 if (option == DND_ACTION_COPY) current = 1;
691 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Move"));
692 if (option == DND_ACTION_MOVE) current = 2;
694 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
696 g_signal_connect(G_OBJECT(combo), "changed",
697 G_CALLBACK(dnd_default_action_selection_menu_cb), option_c);
699 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
700 gtk_widget_show(combo);
703 static void add_clipboard_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
704 gint option, gint *option_c)
711 pref_table_label(table, column, row, text, GTK_ALIGN_START);
713 combo = gtk_combo_box_text_new();
715 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Primary"));
716 if (option == CLIPBOARD_PRIMARY) current = 0;
717 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Clipboard"));
718 if (option == CLIPBOARD_CLIPBOARD) current = 1;
719 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Both"));
720 if (option == CLIPBOARD_BOTH) current = 2;
722 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
724 g_signal_connect(G_OBJECT(combo), "changed",
725 G_CALLBACK(clipboard_selection_menu_cb), option_c);
727 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
728 gtk_widget_show(combo);
731 static void zoom_style_selection_menu_cb(GtkWidget *combo, gpointer data)
733 auto option = static_cast<gint *>(data);
735 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
738 *option = ZOOM_GEOMETRIC;
741 *option = ZOOM_ARITHMETIC;
744 *option = ZOOM_GEOMETRIC;
748 static void add_zoom_style_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, ZoomStyle option, ZoomStyle *option_c)
755 pref_table_label(table, column, row, text, GTK_ALIGN_START);
757 combo = gtk_combo_box_text_new();
759 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Geometric"));
760 if (option == ZOOM_GEOMETRIC) current = 0;
761 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Arithmetic"));
762 if (option == ZOOM_ARITHMETIC) current = 1;
764 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
766 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(zoom_style_selection_menu_cb), option_c);
768 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
769 gtk_widget_show(combo);
772 static void mouse_buttons_selection_menu_cb(GtkWidget *combo, gpointer data)
774 ActionItem *action_item = nullptr;
775 auto option = static_cast<gchar **>(data);
780 label = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
782 list = get_action_items();
787 action_item = static_cast<ActionItem *>(work->data);
788 if (g_strcmp0(action_item->label, label) == 0)
796 *option = g_strdup(action_item->name);
798 action_items_free(list);
801 static void add_mouse_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, gchar *option, gchar **option_c)
803 ActionItem *action_item;
812 pref_table_label(table, column, row, text, GTK_ALIGN_START);
814 combo = gtk_combo_box_text_new();
816 list = get_action_items();
820 action_item = static_cast<ActionItem *>(work->data);
821 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), action_item->label);
823 if (g_strcmp0(action_item->name, option) == 0)
831 action_items_free(list);
833 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
835 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(mouse_buttons_selection_menu_cb), option_c);
837 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
838 gtk_widget_show(combo);
841 static void thumb_size_menu_cb(GtkWidget *combo, gpointer)
845 n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
848 if (static_cast<guint>(n) < sizeof(thumb_size_list) / sizeof(ThumbSize))
850 c_options->thumbnails.max_width = thumb_size_list[n].w;
851 c_options->thumbnails.max_height = thumb_size_list[n].h;
855 c_options->thumbnails.max_width = options->thumbnails.max_width;
856 c_options->thumbnails.max_height = options->thumbnails.max_height;
860 static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *text)
866 c_options->thumbnails.max_width = options->thumbnails.max_width;
867 c_options->thumbnails.max_height = options->thumbnails.max_height;
869 pref_table_label(table, column, row, text, GTK_ALIGN_START);
871 combo = gtk_combo_box_text_new();
874 for (i = 0; static_cast<guint>(i) < sizeof(thumb_size_list) / sizeof(ThumbSize); i++)
879 w = thumb_size_list[i].w;
880 h = thumb_size_list[i].h;
882 buf = g_strdup_printf("%d x %d", w, h);
883 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
886 if (w == options->thumbnails.max_width && h == options->thumbnails.max_height) current = i;
893 buf = g_strdup_printf("%s %d x %d", _("Custom"), options->thumbnails.max_width, options->thumbnails.max_height);
894 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
900 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
901 g_signal_connect(G_OBJECT(combo), "changed",
902 G_CALLBACK(thumb_size_menu_cb), NULL);
904 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
905 gtk_widget_show(combo);
908 static void stereo_mode_menu_cb(GtkWidget *combo, gpointer data)
910 auto option = static_cast<gint *>(data);
912 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
916 *option = PR_STEREO_NONE;
919 *option = PR_STEREO_ANAGLYPH_RC;
922 *option = PR_STEREO_ANAGLYPH_GM;
925 *option = PR_STEREO_ANAGLYPH_YB;
928 *option = PR_STEREO_ANAGLYPH_GRAY_RC;
931 *option = PR_STEREO_ANAGLYPH_GRAY_GM;
934 *option = PR_STEREO_ANAGLYPH_GRAY_YB;
937 *option = PR_STEREO_ANAGLYPH_DB_RC;
940 *option = PR_STEREO_ANAGLYPH_DB_GM;
943 *option = PR_STEREO_ANAGLYPH_DB_YB;
946 *option = PR_STEREO_HORIZ;
949 *option = PR_STEREO_HORIZ | PR_STEREO_HALF;
952 *option = PR_STEREO_VERT;
955 *option = PR_STEREO_VERT | PR_STEREO_HALF;
958 *option = PR_STEREO_FIXED;
963 static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const gchar *text,
964 gint option, gint *option_c, gboolean add_fixed)
971 pref_table_label(table, column, row, text, GTK_ALIGN_START);
973 combo = gtk_combo_box_text_new();
975 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Single image"));
977 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Red-Cyan"));
978 if (option & PR_STEREO_ANAGLYPH_RC) current = 1;
979 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Green-Magenta"));
980 if (option & PR_STEREO_ANAGLYPH_GM) current = 2;
981 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Yellow-Blue"));
982 if (option & PR_STEREO_ANAGLYPH_YB) current = 3;
983 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Red-Cyan"));
984 if (option & PR_STEREO_ANAGLYPH_GRAY_RC) current = 4;
985 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Green-Magenta"));
986 if (option & PR_STEREO_ANAGLYPH_GRAY_GM) current = 5;
987 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Yellow-Blue"));
988 if (option & PR_STEREO_ANAGLYPH_GRAY_YB) current = 6;
989 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Red-Cyan"));
990 if (option & PR_STEREO_ANAGLYPH_DB_RC) current = 7;
991 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Green-Magenta"));
992 if (option & PR_STEREO_ANAGLYPH_DB_GM) current = 8;
993 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Yellow-Blue"));
994 if (option & PR_STEREO_ANAGLYPH_DB_YB) current = 9;
996 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side"));
997 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side Half size"));
998 if (option & PR_STEREO_HORIZ)
1001 if (option & PR_STEREO_HALF) current = 11;
1004 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom"));
1005 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom Half size"));
1006 if (option & PR_STEREO_VERT)
1009 if (option & PR_STEREO_HALF) current = 13;
1014 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Fixed position"));
1015 if (option & PR_STEREO_FIXED) current = 14;
1018 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1020 g_signal_connect(G_OBJECT(combo), "changed",
1021 G_CALLBACK(stereo_mode_menu_cb), option_c);
1023 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1024 gtk_widget_show(combo);
1027 static void video_menu_cb(GtkWidget *combo, gpointer data)
1029 auto option = static_cast<gchar **>(data);
1031 auto ed = static_cast<EditorDescription *>(g_list_nth_data(editor_list_get(), gtk_combo_box_get_active(GTK_COMBO_BOX(combo))));
1035 static void video_menu_populate(gpointer data, gpointer user_data)
1037 auto combo = static_cast<GtkWidget *>(user_data);
1038 auto ed = static_cast<EditorDescription *>(data);
1040 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), ed->name);
1043 static void add_video_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1044 gchar *option, gchar **option_c)
1048 /* use lists since they are sorted */
1049 GList *eds = editor_list_get();
1053 pref_table_label(table, column, row, text, GTK_ALIGN_START);
1055 combo = gtk_combo_box_text_new();
1056 g_list_foreach(eds,video_menu_populate,combo);
1057 current = option ? g_list_index(eds,g_hash_table_lookup(editors,option)): -1;
1059 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1061 g_signal_connect(G_OBJECT(combo), "changed",
1062 G_CALLBACK(video_menu_cb), option_c);
1064 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1065 gtk_widget_show(combo);
1068 static void filter_store_populate()
1072 if (!filter_store) return;
1074 gtk_list_store_clear(filter_store);
1076 work = filter_get_list();
1082 fe = static_cast<FilterEntry *>(work->data);
1085 gtk_list_store_append(filter_store, &iter);
1086 gtk_list_store_set(filter_store, &iter, 0, fe, -1);
1090 static void filter_store_ext_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1092 auto model = static_cast<GtkWidget *>(data);
1093 auto fe = static_cast<FilterEntry *>(data);
1097 if (!new_text || strlen(new_text) < 1) return;
1099 tpath = gtk_tree_path_new_from_string(path_str);
1100 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1101 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1103 g_free(fe->extensions);
1104 fe->extensions = g_strdup(new_text);
1106 gtk_tree_path_free(tpath);
1110 static void filter_store_class_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1112 auto model = static_cast<GtkWidget *>(data);
1113 auto fe = static_cast<FilterEntry *>(data);
1118 if (!new_text || !new_text[0]) return;
1120 tpath = gtk_tree_path_new_from_string(path_str);
1121 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1122 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1124 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
1126 if (strcmp(new_text, _(format_class_list[i])) == 0)
1128 fe->file_class = static_cast<FileFormatClass>(i);
1133 gtk_tree_path_free(tpath);
1137 static void filter_store_desc_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1139 auto model = static_cast<GtkWidget *>(data);
1144 if (!new_text || !new_text[0]) return;
1146 tpath = gtk_tree_path_new_from_string(path_str);
1147 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1148 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1150 g_free(fe->description);
1151 fe->description = g_strdup(new_text);
1153 gtk_tree_path_free(tpath);
1156 static void filter_store_enable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1158 auto model = static_cast<GtkWidget *>(data);
1163 tpath = gtk_tree_path_new_from_string(path_str);
1164 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1165 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1167 fe->enabled = !fe->enabled;
1169 gtk_tree_path_free(tpath);
1173 static void filter_store_writable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1175 auto model = static_cast<GtkWidget *>(data);
1180 tpath = gtk_tree_path_new_from_string(path_str);
1181 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1182 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1184 fe->writable = !fe->writable;
1185 if (fe->writable) fe->allow_sidecar = FALSE;
1187 gtk_tree_path_free(tpath);
1191 static void filter_store_sidecar_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1193 auto model = static_cast<GtkWidget *>(data);
1198 tpath = gtk_tree_path_new_from_string(path_str);
1199 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1200 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1202 fe->allow_sidecar = !fe->allow_sidecar;
1203 if (fe->allow_sidecar) fe->writable = FALSE;
1205 gtk_tree_path_free(tpath);
1209 static void filter_set_func(GtkTreeViewColumn *, GtkCellRenderer *cell,
1210 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1214 gtk_tree_model_get(tree_model, iter, 0, &fe, -1);
1216 switch (GPOINTER_TO_INT(data))
1219 g_object_set(GTK_CELL_RENDERER(cell),
1220 "active", fe->enabled, NULL);
1223 g_object_set(GTK_CELL_RENDERER(cell),
1224 "text", fe->extensions, NULL);
1226 case FE_DESCRIPTION:
1227 g_object_set(GTK_CELL_RENDERER(cell),
1228 "text", fe->description, NULL);
1231 g_object_set(GTK_CELL_RENDERER(cell),
1232 "text", _(format_class_list[fe->file_class]), NULL);
1235 g_object_set(GTK_CELL_RENDERER(cell),
1236 "active", fe->writable, NULL);
1238 case FE_ALLOW_SIDECAR:
1239 g_object_set(GTK_CELL_RENDERER(cell),
1240 "active", fe->allow_sidecar, NULL);
1245 static gboolean filter_add_scroll(gpointer data)
1249 GtkCellRenderer *cell;
1250 GtkTreeViewColumn *column;
1253 GtkTreeModel *store;
1255 FilterEntry *filter;
1257 rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(filter_store), nullptr);
1258 path = gtk_tree_path_new_from_indices(rows-1, -1);
1260 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data), 0);
1262 list_cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1263 cell = static_cast<GtkCellRenderer *>(g_list_last(list_cells)->data);
1265 store = gtk_tree_view_get_model(GTK_TREE_VIEW(data));
1266 valid = gtk_tree_model_get_iter_first(store, &iter);
1270 gtk_tree_model_get(store, &iter, 0, &filter, -1);
1272 if (g_strcmp0(filter->extensions, ".new") == 0)
1274 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1278 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1281 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1282 path, column, FALSE, 0.0, 0.0 );
1283 gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(data),
1284 path, column, cell, TRUE);
1286 gtk_tree_path_free(path);
1287 g_list_free(list_cells);
1289 return(G_SOURCE_REMOVE);
1292 static void filter_add_cb(GtkWidget *, gpointer data)
1294 filter_add_unique("description", ".new", FORMAT_CLASS_IMAGE, TRUE, FALSE, TRUE);
1295 filter_store_populate();
1297 g_idle_add(static_cast<GSourceFunc>(filter_add_scroll), data);
1300 static void filter_remove_cb(GtkWidget *, gpointer data)
1302 auto filter_view = static_cast<GtkWidget *>(data);
1303 GtkTreeSelection *selection;
1307 if (!filter_store) return;
1308 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
1309 if (!gtk_tree_selection_get_selected(selection, nullptr, &iter)) return;
1310 gtk_tree_model_get(GTK_TREE_MODEL(filter_store), &iter, 0, &fe, -1);
1313 filter_remove_entry(fe);
1315 filter_store_populate();
1318 static gboolean filter_default_ok_scroll(GtkTreeView *data)
1322 GtkTreeViewColumn *column;
1324 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(filter_store), &iter);
1325 path = gtk_tree_model_get_path(GTK_TREE_MODEL(filter_store), &iter);
1326 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1328 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1332 gtk_tree_path_free(path);
1334 return(G_SOURCE_REMOVE);
1337 static void filter_default_ok_cb(GenericDialog *gd, gpointer)
1340 filter_add_defaults();
1342 filter_store_populate();
1344 g_idle_add(reinterpret_cast<GSourceFunc>(filter_default_ok_scroll), gd->data);
1347 static void dummy_cancel_cb(GenericDialog *, gpointer)
1349 /* no op, only so cancel button appears */
1352 static void filter_default_cb(GtkWidget *widget, gpointer data)
1356 gd = generic_dialog_new(_("Reset filters"),
1357 "reset_filter", widget, TRUE,
1358 dummy_cancel_cb, data);
1359 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset filters"),
1360 _("This will reset the file filters to the defaults.\nContinue?"), TRUE);
1361 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", filter_default_ok_cb, TRUE);
1362 gtk_widget_show(gd->dialog);
1365 static void filter_disable_cb(GtkWidget *widget, gpointer data)
1367 auto frame = static_cast<GtkWidget *>(data);
1369 gtk_widget_set_sensitive(frame,
1370 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1373 static void safe_delete_view_cb(GtkWidget *, gpointer)
1375 layout_set_path(nullptr, gq_gtk_entry_get_text(GTK_ENTRY(safe_delete_path_entry)));
1378 static void safe_delete_clear_ok_cb(GenericDialog *, gpointer)
1380 file_util_trash_clear();
1383 static void safe_delete_clear_cb(GtkWidget *widget, gpointer)
1387 gd = generic_dialog_new(_("Clear trash"),
1388 "clear_trash", widget, TRUE,
1389 dummy_cancel_cb, nullptr);
1390 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Clear trash"),
1391 _("This will remove the trash contents."), FALSE);
1392 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", safe_delete_clear_ok_cb, TRUE);
1393 entry = gtk_entry_new();
1394 gtk_widget_set_can_focus(entry, FALSE);
1395 gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
1396 if (options->file_ops.safe_delete_path) gq_gtk_entry_set_text(GTK_ENTRY(entry), options->file_ops.safe_delete_path);
1397 gq_gtk_box_pack_start(GTK_BOX(gd->vbox), entry, FALSE, FALSE, 0);
1398 gtk_widget_show(entry);
1399 gtk_widget_show(gd->dialog);
1402 static void image_overlay_template_view_changed_cb(GtkWidget *, gpointer data)
1404 GtkWidget *pTextView;
1405 GtkTextBuffer *pTextBuffer;
1409 pTextView = GTK_WIDGET(data);
1411 pTextBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pTextView));
1412 gtk_text_buffer_get_start_iter(pTextBuffer, &iStart);
1413 gtk_text_buffer_get_end_iter(pTextBuffer, &iEnd);
1415 set_image_overlay_template_string(&c_options->image_overlay.template_string,
1416 gtk_text_buffer_get_text(pTextBuffer, &iStart, &iEnd, TRUE));
1419 static void image_overlay_default_template_ok_cb(GenericDialog *, gpointer data)
1421 auto text_view = static_cast<GtkTextView *>(data);
1422 GtkTextBuffer *buffer;
1424 set_default_image_overlay_template_string(&options->image_overlay.template_string);
1425 if (!configwindow) return;
1427 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
1428 gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
1431 static void image_overlay_default_template_cb(GtkWidget *widget, gpointer data)
1435 gd = generic_dialog_new(_("Reset image overlay template string"),
1436 "reset_image_overlay_template_string", widget, TRUE,
1437 dummy_cancel_cb, data);
1438 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset image overlay template string"),
1439 _("This will reset the image overlay template string to the default.\nContinue?"), TRUE);
1440 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", image_overlay_default_template_ok_cb, TRUE);
1441 gtk_widget_show(gd->dialog);
1444 static void image_overlay_help_cb(GtkWidget *, gpointer)
1446 help_window_show("GuideOptionsOSD.html");
1449 static void font_activated_cb(GtkFontChooser *widget, gchar *fontname, gpointer)
1451 g_free(c_options->image_overlay.font);
1452 c_options->image_overlay.font = g_strdup(fontname);
1455 gq_gtk_widget_destroy(GTK_WIDGET(widget));
1458 static void font_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1462 g_free(c_options->image_overlay.font);
1463 c_options->image_overlay.font = g_strdup(options->image_overlay.font);
1465 if (response_id == GTK_RESPONSE_OK)
1467 font = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(dialog));
1468 g_free(c_options->image_overlay.font);
1469 c_options->image_overlay.font = g_strdup(font);
1473 g_object_unref(dialog);
1476 static void image_overlay_set_font_cb(GtkWidget *widget, gpointer)
1480 dialog = gtk_font_chooser_dialog_new("Image Overlay Font", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1481 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1482 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), options->image_overlay.font);
1484 g_signal_connect(dialog, "font-activated", G_CALLBACK(font_activated_cb), nullptr);
1485 g_signal_connect(dialog, "response", G_CALLBACK(font_response_cb), nullptr);
1487 gtk_widget_show(dialog);
1490 static void text_color_activated_cb(GtkColorChooser *chooser, GdkRGBA *color, gpointer)
1492 c_options->image_overlay.text_red = color->red * 255;
1493 c_options->image_overlay.text_green = color->green * 255;
1494 c_options->image_overlay.text_blue = color->blue * 255;
1495 c_options->image_overlay.text_alpha = color->alpha * 255;
1497 gq_gtk_widget_destroy(GTK_WIDGET(chooser));
1500 static void text_color_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1504 c_options->image_overlay.text_red = options->image_overlay.text_red;
1505 c_options->image_overlay.text_green = options->image_overlay.text_green;
1506 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
1507 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
1509 if (response_id == GTK_RESPONSE_OK)
1511 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1512 c_options->image_overlay.text_red = color.red * 255;
1513 c_options->image_overlay.text_green = color.green * 255;
1514 c_options->image_overlay.text_blue = color.blue * 255;
1515 c_options->image_overlay.text_alpha = color.alpha * 255;
1518 g_object_unref(dialog);
1521 static void image_overlay_set_text_color_cb(GtkWidget *widget, gpointer)
1526 dialog = gtk_color_chooser_dialog_new(_("Image Overlay Text Color"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1527 color.red = options->image_overlay.text_red;
1528 color.green = options->image_overlay.text_green;
1529 color.blue = options->image_overlay.text_blue;
1530 color.alpha = options->image_overlay.text_alpha;
1531 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1532 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1534 g_signal_connect(dialog, "color-activated", G_CALLBACK(text_color_activated_cb), nullptr);
1535 g_signal_connect(dialog, "response", G_CALLBACK(text_color_response_cb), nullptr);
1537 gtk_widget_show(dialog);
1540 static void bg_color_activated_cb(GtkColorChooser *chooser, GdkRGBA *color, gpointer)
1542 c_options->image_overlay.background_red = color->red * 255;
1543 c_options->image_overlay.background_green = color->green * 255;
1544 c_options->image_overlay.background_blue = color->blue * 255;
1545 c_options->image_overlay.background_alpha = color->alpha * 255;
1547 gq_gtk_widget_destroy(GTK_WIDGET(chooser));
1550 static void bg_color_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1554 c_options->image_overlay.background_red = options->image_overlay.background_red;
1555 c_options->image_overlay.background_green = options->image_overlay.background_green;
1556 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
1557 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
1559 if (response_id == GTK_RESPONSE_OK)
1561 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1562 c_options->image_overlay.background_red = color.red * 255;
1563 c_options->image_overlay.background_green = color.green * 255;
1564 c_options->image_overlay.background_blue = color.blue * 255;
1565 c_options->image_overlay.background_alpha = color.alpha * 255;
1567 gq_gtk_widget_destroy(GTK_WIDGET(dialog));
1570 static void image_overlay_set_background_color_cb(GtkWidget *widget, gpointer)
1575 dialog = gtk_color_chooser_dialog_new(_("Image Overlay Background Color"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1576 color.red = options->image_overlay.background_red;
1577 color.green = options->image_overlay.background_green;
1578 color.blue = options->image_overlay.background_blue;
1579 color.alpha = options->image_overlay.background_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(bg_color_activated_cb), nullptr);
1584 g_signal_connect(dialog, "response", G_CALLBACK(bg_color_response_cb), nullptr);
1586 gtk_widget_show(dialog);
1589 static void accel_store_populate()
1592 GList *groups, *actions;
1594 const gchar *accel_path;
1598 if (!accel_store || !layout_window_list || !layout_window_list->data) return;
1600 gtk_tree_store_clear(accel_store);
1601 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 */
1603 g_assert(lw && lw->ui_manager);
1604 groups = gtk_ui_manager_get_action_groups(lw->ui_manager);
1607 actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
1610 action = GTK_ACTION(actions->data);
1611 accel_path = gtk_action_get_accel_path(action);
1612 if (accel_path && gtk_accel_map_lookup_entry(accel_path, &key))
1614 gchar *label, *label2, *tooltip, *accel;
1615 g_object_get(action,
1616 "tooltip", &tooltip,
1620 if (pango_parse_markup(label, -1, '_', nullptr, &label2, nullptr, nullptr) && label2)
1626 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1630 gtk_tree_store_append(accel_store, &iter, nullptr);
1631 gtk_tree_store_set(accel_store, &iter,
1634 AE_TOOLTIP, tooltip ? tooltip : "",
1635 AE_ACCEL, accel_path,
1643 actions = actions->next;
1646 groups = groups->next;
1650 static void accel_store_cleared_cb(GtkCellRendererAccel *, gchar *, gpointer)
1655 static gboolean accel_remove_key_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer data)
1657 auto accel1 = static_cast<gchar *>(data);
1662 gtk_tree_model_get(model, iter, AE_KEY, &accel2, -1);
1664 gtk_accelerator_parse(accel1, &key1.accel_key, &key1.accel_mods);
1665 gtk_accelerator_parse(accel2, &key2.accel_key, &key2.accel_mods);
1667 if (key1.accel_key == key2.accel_key && key1.accel_mods == key2.accel_mods)
1669 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1670 DEBUG_1("accelerator key '%s' is already used, removing.", accel1);
1679 static void accel_store_edited_cb(GtkCellRendererAccel *, gchar *path_string, guint accel_key, GdkModifierType accel_mods, guint, gpointer)
1681 auto model = reinterpret_cast<GtkTreeModel *>(accel_store);
1685 GtkAccelKey old_key, key;
1686 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1688 gtk_tree_model_get_iter(model, &iter, path);
1689 gtk_tree_model_get(model, &iter, AE_ACCEL, &accel_path, -1);
1691 /* test if the accelerator can be stored without conflicts*/
1692 gtk_accel_map_lookup_entry(accel_path, &old_key);
1694 /* change the key and read it back (change may fail on keys hardcoded in gtk)*/
1695 gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, TRUE);
1696 gtk_accel_map_lookup_entry(accel_path, &key);
1698 /* restore the original for now, the key will be really changed when the changes are confirmed */
1699 gtk_accel_map_change_entry(accel_path, old_key.accel_key, old_key.accel_mods, TRUE);
1701 acc = gtk_accelerator_name(key.accel_key, key.accel_mods);
1702 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, acc);
1704 gtk_tree_store_set(accel_store, &iter, AE_KEY, acc, -1);
1705 gtk_tree_path_free(path);
1709 static gboolean accel_default_scroll(GtkTreeView *data)
1713 GtkTreeViewColumn *column;
1715 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(accel_store), &iter);
1716 path = gtk_tree_model_get_path(GTK_TREE_MODEL(accel_store), &iter);
1717 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1719 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1723 gtk_tree_path_free(path);
1725 return(G_SOURCE_REMOVE);
1728 static void accel_default_cb(GtkWidget *, gpointer data)
1730 accel_store_populate();
1732 g_idle_add(reinterpret_cast<GSourceFunc>(accel_default_scroll), data);
1735 void accel_clear_selection(GtkTreeModel *, GtkTreePath *, GtkTreeIter *iter, gpointer)
1737 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1740 void accel_reset_selection(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
1743 gchar *accel_path, *accel;
1745 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, -1);
1746 gtk_accel_map_lookup_entry(accel_path, &key);
1747 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1749 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, accel);
1751 gtk_tree_store_set(accel_store, iter, AE_KEY, accel, -1);
1756 static void accel_clear_cb(GtkWidget *, gpointer data)
1758 GtkTreeSelection *selection;
1760 if (!accel_store) return;
1761 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1762 gtk_tree_selection_selected_foreach(selection, &accel_clear_selection, nullptr);
1765 static void accel_reset_cb(GtkWidget *, gpointer data)
1767 GtkTreeSelection *selection;
1769 if (!accel_store) return;
1770 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1771 gtk_tree_selection_selected_foreach(selection, &accel_reset_selection, nullptr);
1776 static GtkWidget *scrolled_notebook_page(GtkWidget *notebook, const gchar *title)
1780 GtkWidget *scrolled;
1781 GtkWidget *viewport;
1783 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
1784 gtk_container_set_border_width(GTK_CONTAINER(scrolled), PREF_PAD_BORDER);
1785 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1786 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1787 label = gtk_label_new(title);
1788 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, label);
1789 gtk_widget_show(scrolled);
1791 viewport = gtk_viewport_new(nullptr, nullptr);
1792 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1793 gq_gtk_container_add(GTK_WIDGET(scrolled), viewport);
1794 gtk_widget_show(viewport);
1796 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1797 gq_gtk_container_add(GTK_WIDGET(viewport), vbox);
1798 gtk_widget_show(vbox);
1803 static void cache_standard_cb(GtkWidget *widget, gpointer)
1805 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1807 c_options->thumbnails.spec_standard =TRUE;
1808 c_options->thumbnails.cache_into_dirs = FALSE;
1812 static void cache_geeqie_cb(GtkWidget *widget, gpointer)
1814 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1816 c_options->thumbnails.spec_standard =FALSE;
1817 c_options->thumbnails.cache_into_dirs = FALSE;
1821 static void cache_local_cb(GtkWidget *widget, gpointer)
1823 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1825 c_options->thumbnails.cache_into_dirs = TRUE;
1826 c_options->thumbnails.spec_standard =FALSE;
1830 static void help_search_engine_entry_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1832 if (pos == GTK_ENTRY_ICON_PRIMARY)
1834 gq_gtk_entry_set_text(GTK_ENTRY(userdata), HELP_SEARCH_ENGINE);
1838 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "");
1842 static void star_rating_star_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1844 gchar *rating_symbol;
1846 if (pos == GTK_ENTRY_ICON_PRIMARY)
1848 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_STAR);
1849 gq_gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1850 g_free(rating_symbol);
1854 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1855 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1856 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1860 static void star_rating_rejected_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1862 gchar *rating_symbol;
1864 if (pos == GTK_ENTRY_ICON_PRIMARY)
1866 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_REJECTED);
1867 gq_gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1868 g_free(rating_symbol);
1872 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1873 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1874 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1878 static guint star_rating_symbol_test(GtkWidget *, gpointer data)
1880 auto hbox = static_cast<GtkContainer *>(data);
1881 GString *str = g_string_new(nullptr);
1882 GtkEntry *hex_code_entry;
1883 gchar *hex_code_full;
1886 guint64 hex_value = 0;
1888 list = gtk_container_get_children(hbox);
1890 hex_code_entry = static_cast<GtkEntry *>(g_list_nth_data(list, 2));
1891 hex_code_full = g_strdup(gq_gtk_entry_get_text(hex_code_entry));
1893 hex_code = g_strsplit(hex_code_full, "+", 2);
1894 if (hex_code[0] && hex_code[1])
1896 hex_value = strtoull(hex_code[1], nullptr, 16);
1898 if (!hex_value || hex_value > 0x10FFFF)
1900 hex_value = 0x003F; // Unicode 'Question Mark'
1902 str = g_string_append_unichar(str, static_cast<gunichar>(hex_value));
1903 gtk_label_set_text(static_cast<GtkLabel *>(g_list_nth_data(list, 1)), str->str);
1905 g_strfreev(hex_code);
1906 g_string_free(str, TRUE);
1907 g_free(hex_code_full);
1912 static void star_rating_star_test_cb(GtkWidget *widget, gpointer data)
1914 guint64 star_symbol;
1916 star_symbol = star_rating_symbol_test(widget, data);
1917 c_options->star_rating.star = star_symbol;
1920 static void star_rating_rejected_test_cb(GtkWidget *widget, gpointer data)
1922 guint64 rejected_symbol;
1924 rejected_symbol = star_rating_symbol_test(widget, data);
1925 c_options->star_rating.rejected = rejected_symbol;
1928 /* general options tab */
1929 static void timezone_database_install_cb(GtkWidget *widget, gpointer data);
1933 GCancellable *cancellable;
1935 GtkWidget *progress;
1937 GFile *timezone_database_gq;
1938 gchar *timezone_database_user;
1941 static void config_tab_general(GtkWidget *notebook)
1946 GtkWidget *group_frame;
1947 GtkWidget *subgroup;
1949 GtkWidget *ct_button;
1952 gint hours, minutes, remainder;
1954 GtkWidget *star_rating_entry;
1956 gchar *rating_symbol;
1959 gchar *download_locn;
1960 GNetworkMonitor *net_mon;
1961 GSocketConnectable *tz_org;
1962 gboolean internet_available = FALSE;
1965 vbox = scrolled_notebook_page(notebook, _("General"));
1967 group = pref_group_new(vbox, FALSE, _("Thumbnails"), GTK_ORIENTATION_VERTICAL);
1969 table = pref_table_new(group, 2, 2, FALSE, FALSE);
1970 add_thumb_size_menu(table, 0, 0, _("Size:"));
1971 add_quality_menu(table, 0, 1, _("Quality:"), options->thumbnails.quality, &c_options->thumbnails.quality);
1973 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1974 pref_label_new(hbox, _("Custom size: "));
1975 pref_spin_new_int(hbox, _("Width:"), nullptr, 1, 512, 1, options->thumbnails.max_width, &c_options->thumbnails.max_width);
1976 pref_spin_new_int(hbox, _("Height:"), nullptr, 1, 512, 1, options->thumbnails.max_height, &c_options->thumbnails.max_height);
1978 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails and sim. files"),
1979 options->thumbnails.enable_caching, &c_options->thumbnails.enable_caching);
1981 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1982 pref_checkbox_link_sensitivity(ct_button, subgroup);
1984 c_options->thumbnails.spec_standard = options->thumbnails.spec_standard;
1985 c_options->thumbnails.cache_into_dirs = options->thumbnails.cache_into_dirs;
1986 group_frame = pref_frame_new(subgroup, TRUE, _("Use Geeqie thumbnail style and cache"),
1987 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1988 button = pref_radiobutton_new(group_frame, nullptr, get_thumbnails_cache_dir(),
1989 !options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
1990 G_CALLBACK(cache_geeqie_cb), nullptr);
1992 group_frame = pref_frame_new(subgroup, TRUE,
1993 _("Store thumbnails local to image folder (non-standard)"),
1994 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1995 pref_radiobutton_new(group_frame, button, "*/.thumbnails",
1996 !options->thumbnails.spec_standard && options->thumbnails.cache_into_dirs,
1997 G_CALLBACK(cache_local_cb), nullptr);
1999 group_frame = pref_frame_new(subgroup, TRUE,
2000 _("Use standard thumbnail style and cache, shared with other applications"),
2001 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2002 pref_radiobutton_new(group_frame, button, get_thumbnails_standard_cache_dir(),
2003 options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2004 G_CALLBACK(cache_standard_cb), nullptr);
2006 pref_checkbox_new_int(group, _("Use EXIF thumbnails when available (EXIF thumbnails may be outdated)"),
2007 options->thumbnails.use_exif, &c_options->thumbnails.use_exif);
2009 pref_checkbox_new_int(group, _("Thumbnail color management"),
2010 options->thumbnails.use_color_management, &c_options->thumbnails.use_color_management);
2012 spin = pref_spin_new_int(group, _("Collection preview:"), nullptr,
2014 options->thumbnails.collection_preview, &c_options->thumbnails.collection_preview);
2015 gtk_widget_set_tooltip_text(spin, _("The maximum number of thumbnails shown in a Collection preview montage"));
2017 #ifdef HAVE_FFMPEGTHUMBNAILER_METADATA
2018 pref_checkbox_new_int(group, _("Use embedded metadata in video files as thumbnails when available"),
2019 options->thumbnails.use_ft_metadata, &c_options->thumbnails.use_ft_metadata);
2022 pref_spacer(group, PREF_PAD_GROUP);
2024 group = pref_group_new(vbox, FALSE, _("Star Rating"), GTK_ORIENTATION_VERTICAL);
2026 c_options->star_rating.star = options->star_rating.star;
2027 c_options->star_rating.rejected = options->star_rating.rejected;
2029 str = g_string_new(nullptr);
2030 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2031 pref_label_new(hbox, _("Star character: "));
2032 str = g_string_append_unichar(str, options->star_rating.star);
2033 pref_label_new(hbox, g_strdup(str->str));
2034 rating_symbol = g_strdup_printf("U+%X", options->star_rating.star);
2035 star_rating_entry = gtk_entry_new();
2036 gq_gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2037 gq_gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2038 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2039 gtk_widget_show(star_rating_entry);
2040 button = pref_button_new(nullptr, nullptr, _("Set"),
2041 G_CALLBACK(star_rating_star_test_cb), hbox);
2042 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2043 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2044 gtk_widget_show(button);
2045 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."));
2046 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2047 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2048 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2049 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2050 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2051 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2052 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2053 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2054 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2055 G_CALLBACK(star_rating_star_icon_cb),
2058 g_string_free(str, TRUE);
2059 g_free(rating_symbol);
2061 str = g_string_new(nullptr);
2062 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2063 pref_label_new(hbox, _("Rejected character: "));
2064 str = g_string_append_unichar(str, options->star_rating.rejected);
2065 pref_label_new(hbox, g_strdup(str->str));
2066 rating_symbol = g_strdup_printf("U+%X", options->star_rating.rejected);
2067 star_rating_entry = gtk_entry_new();
2068 gq_gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2069 gq_gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2070 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2071 gtk_widget_show(star_rating_entry);
2072 button = pref_button_new(nullptr, nullptr, _("Set"),
2073 G_CALLBACK(star_rating_rejected_test_cb), hbox);
2074 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2075 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2076 gtk_widget_show(button);
2077 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."));
2078 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2079 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2080 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2081 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2082 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2083 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2084 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2085 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2086 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2087 G_CALLBACK(star_rating_rejected_icon_cb),
2090 g_string_free(str, TRUE);
2091 g_free(rating_symbol);
2093 pref_spacer(group, PREF_PAD_GROUP);
2095 group = pref_group_new(vbox, FALSE, _("Slide show"), GTK_ORIENTATION_VERTICAL);
2097 c_options->slideshow.delay = options->slideshow.delay;
2098 hours = options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2099 remainder = options->slideshow.delay % (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2100 minutes = remainder / (60 * SLIDESHOW_SUBSECOND_PRECISION);
2101 seconds = static_cast<gdouble>(remainder % (60 * SLIDESHOW_SUBSECOND_PRECISION)) /
2102 SLIDESHOW_SUBSECOND_PRECISION;
2104 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2106 spin = pref_spin_new(hbox, _("Delay between image change hrs:mins:secs.dec"), nullptr,
2108 options->slideshow.delay ? hours : 0.0,
2109 G_CALLBACK(slideshow_delay_hours_cb), nullptr);
2110 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2111 spin = pref_spin_new(hbox, ":" , nullptr,
2113 options->slideshow.delay ? minutes: 0.0,
2114 G_CALLBACK(slideshow_delay_minutes_cb), nullptr);
2115 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2116 spin = pref_spin_new(hbox, ":", nullptr,
2117 SLIDESHOW_MIN_SECONDS, 59, 1.0, 1,
2118 options->slideshow.delay ? seconds : 10.0,
2119 G_CALLBACK(slideshow_delay_seconds_cb), nullptr);
2120 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2122 pref_checkbox_new_int(group, _("Random"), options->slideshow.random, &c_options->slideshow.random);
2123 pref_checkbox_new_int(group, _("Repeat"), options->slideshow.repeat, &c_options->slideshow.repeat);
2125 pref_spacer(group, PREF_PAD_GROUP);
2127 group = pref_group_new(vbox, FALSE, _("Image loading and caching"), GTK_ORIENTATION_VERTICAL);
2129 pref_spin_new_int(group, _("Decoded image cache size (MiB):"), nullptr,
2130 0, 99999, 1, options->image.image_cache_max, &c_options->image.image_cache_max);
2131 pref_checkbox_new_int(group, _("Preload next image"),
2132 options->image.enable_read_ahead, &c_options->image.enable_read_ahead);
2134 pref_checkbox_new_int(group, _("Refresh on file change"),
2135 options->update_on_time_change, &c_options->update_on_time_change);
2138 pref_spacer(group, PREF_PAD_GROUP);
2140 group = pref_group_new(vbox, FALSE, _("Expand menu and toolbar"), GTK_ORIENTATION_VERTICAL);
2142 pref_checkbox_new_int(group, _("Expand menu and toolbar (NOTE! Geeqie must be restarted for change to take effect)"),
2143 options->expand_menu_toolbar, &c_options->expand_menu_toolbar);
2144 gtk_widget_set_tooltip_text(group, _("Expand the menu and toolbar to the full width of the window"));
2146 pref_spacer(group, PREF_PAD_GROUP);
2148 group = pref_group_new(vbox, FALSE, _("Hide Selectable Bars"), GTK_ORIENTATION_VERTICAL);
2150 pref_checkbox_new_int(group, _("Menu bar"),
2151 options->selectable_bars.menu_bar, &c_options->selectable_bars.menu_bar);
2153 pref_checkbox_new_int(group, _("Tool bar"),
2154 options->selectable_bars.tool_bar, &c_options->selectable_bars.tool_bar);
2156 pref_checkbox_new_int(group, _("Status bar"),
2157 options->selectable_bars.status_bar, &c_options->selectable_bars.status_bar);
2158 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"));
2160 pref_spacer(group, PREF_PAD_GROUP);
2162 if (g_getenv("APPDIR") && strstr(g_getenv("APPDIR"), "/tmp/.mount_Geeqie"))
2164 group = pref_group_new(vbox, FALSE, _("AppImage updates notifications"), GTK_ORIENTATION_VERTICAL);
2165 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2166 pref_checkbox_new_int(group, _("Enable"), options->appimage_notifications, &c_options->appimage_notifications);
2167 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"));
2169 pref_spacer(group, PREF_PAD_GROUP);
2173 net_mon = g_network_monitor_get_default();
2174 tz_org = g_network_address_parse_uri(TIMEZONE_DATABASE_WEB, 80, nullptr);
2177 internet_available = g_network_monitor_can_reach(net_mon, tz_org, nullptr, nullptr);
2178 g_object_unref(tz_org);
2181 group = pref_group_new(vbox, FALSE, _("Timezone database"), GTK_ORIENTATION_VERTICAL);
2182 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2184 if (!internet_available)
2186 gtk_widget_set_sensitive(group, FALSE);
2189 tz = g_new0(TZData, 1);
2191 path = path_from_utf8(TIMEZONE_DATABASE_WEB);
2192 basename = g_path_get_basename(path);
2193 tz->timezone_database_user = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
2197 if (isfile(tz->timezone_database_user))
2199 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Update"), G_CALLBACK(timezone_database_install_cb), tz);
2203 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Install"), G_CALLBACK(timezone_database_install_cb), tz);
2206 download_locn = g_strconcat(_("Download database from: "), TIMEZONE_DATABASE_WEB, NULL);
2207 pref_label_new(GTK_WIDGET(hbox), download_locn);
2208 g_free(download_locn);
2210 if (!internet_available)
2212 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"));
2216 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"));
2218 gtk_widget_show(button);
2220 pref_spacer(group, PREF_PAD_GROUP);
2222 group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL);
2224 help_search_engine_entry = gtk_entry_new();
2225 gq_gtk_entry_set_text(GTK_ENTRY(help_search_engine_entry), options->help_search_engine);
2226 gq_gtk_box_pack_start(GTK_BOX(group), help_search_engine_entry, FALSE, FALSE, 0);
2227 gtk_widget_show(help_search_engine_entry);
2229 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"));
2231 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2232 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2233 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2234 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2235 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2236 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2237 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2238 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2239 g_signal_connect(GTK_ENTRY(help_search_engine_entry), "icon-press",
2240 G_CALLBACK(help_search_engine_entry_icon_cb),
2241 help_search_engine_entry);
2245 static void config_tab_image(GtkWidget *notebook)
2250 GtkWidget *ct_button;
2251 GtkWidget *enlargement_button;
2255 vbox = scrolled_notebook_page(notebook, _("Image"));
2257 group = pref_group_new(vbox, FALSE, _("Zoom"), GTK_ORIENTATION_VERTICAL);
2259 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2260 add_quality_menu(table, 0, 0, _("Quality:"), options->image.zoom_quality, &c_options->image.zoom_quality);
2262 pref_checkbox_new_int(group, _("Two pass rendering (apply HQ zoom and color correction in second pass)"),
2263 options->image.zoom_2pass, &c_options->image.zoom_2pass);
2265 c_options->image.zoom_increment = options->image.zoom_increment;
2266 spin = pref_spin_new(group, _("Zoom increment:"), nullptr,
2267 0.01, 4.0, 0.01, 2, static_cast<gdouble>(options->image.zoom_increment) / 100.0,
2268 G_CALLBACK(zoom_increment_cb), nullptr);
2269 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2271 c_options->image.zoom_style = options->image.zoom_style;
2272 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2273 add_zoom_style_selection_menu(table, 0, 0, _("Zoom style:"), options->image.zoom_style, &c_options->image.zoom_style);
2275 group = pref_group_new(vbox, FALSE, _("Fit image to window"), GTK_ORIENTATION_VERTICAL);
2277 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2278 enlargement_button = pref_checkbox_new_int(hbox, _("Allow enlargement of image (max. size in %)"),
2279 options->image.zoom_to_fit_allow_expand, &c_options->image.zoom_to_fit_allow_expand);
2280 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2282 options->image.max_enlargement_size, &c_options->image.max_enlargement_size);
2283 pref_checkbox_link_sensitivity(enlargement_button, spin);
2284 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."));
2286 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2287 ct_button = pref_checkbox_new_int(hbox, _("Virtual window size (% of actual window):"),
2288 options->image.limit_autofit_size, &c_options->image.limit_autofit_size);
2289 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2291 options->image.max_autofit_size, &c_options->image.max_autofit_size);
2292 pref_checkbox_link_sensitivity(ct_button, spin);
2293 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."));
2295 group = pref_group_new(vbox, FALSE, _("Tile size"), GTK_ORIENTATION_VERTICAL);
2297 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2298 spin = pref_spin_new_int(hbox, _("Pixels"), _("(Requires restart)"),
2300 options->image.tile_size, &c_options->image.tile_size);
2301 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."));
2303 group = pref_group_new(vbox, FALSE, _("Appearance"), GTK_ORIENTATION_VERTICAL);
2305 pref_checkbox_new_int(group, _("Use custom border color in window mode"),
2306 options->image.use_custom_border_color, &c_options->image.use_custom_border_color);
2308 pref_checkbox_new_int(group, _("Use custom border color in fullscreen mode"),
2309 options->image.use_custom_border_color_in_fullscreen, &c_options->image.use_custom_border_color_in_fullscreen);
2311 pref_color_button_new(group, _("Border color"), &options->image.border_color,
2312 G_CALLBACK(pref_color_button_set_cb), &c_options->image.border_color);
2314 c_options->image.border_color = options->image.border_color;
2316 pref_color_button_new(group, _("Alpha channel color 1"), &options->image.alpha_color_1,
2317 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_1);
2319 pref_color_button_new(group, _("Alpha channel color 2"), &options->image.alpha_color_2,
2320 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_2);
2322 c_options->image.alpha_color_1 = options->image.alpha_color_1;
2323 c_options->image.alpha_color_2 = options->image.alpha_color_2;
2328 static void save_default_window_layout_cb(GtkWidget *, gpointer)
2330 LayoutWindow *lw = nullptr;
2331 gchar *default_path;
2334 /* Get current lw */
2337 tmp_id = lw->options.id;
2338 lw->options.id = g_strdup("");
2340 default_path = g_build_filename(get_rc_dir(), DEFAULT_WINDOW_LAYOUT, NULL);
2341 save_default_layout_options_to_file(default_path, options, lw);
2342 g_free(lw->options.id);
2343 lw->options.id = tmp_id;
2344 g_free(default_path);
2347 static gboolean popover_cb(gpointer data)
2349 auto popover = static_cast<GtkPopover *>(data);
2351 gtk_popover_popdown(popover);
2356 static void default_layout_changed_cb(GtkWidget *, GtkPopover *popover)
2358 gtk_popover_popup(popover);
2360 g_timeout_add(2000, popover_cb, popover);
2363 static GtkWidget *create_popover(GtkWidget *parent, GtkWidget *child, GtkPositionType pos)
2367 popover = gtk_popover_new(parent);
2368 gtk_popover_set_position(GTK_POPOVER (popover), pos);
2369 gq_gtk_container_add(GTK_WIDGET(popover), child);
2370 gtk_container_set_border_width(GTK_CONTAINER (popover), 6);
2371 gtk_widget_show (child);
2376 static void config_tab_windows(GtkWidget *notebook)
2381 GtkWidget *subgroup;
2383 GtkWidget *ct_button;
2386 vbox = scrolled_notebook_page(notebook, _("Windows"));
2388 group = pref_group_new(vbox, FALSE, _("State"), GTK_ORIENTATION_VERTICAL);
2390 ct_button = pref_checkbox_new_int(group, _("Remember session"),
2391 options->save_window_positions, &c_options->save_window_positions);
2393 button = pref_checkbox_new_int(group, _("Use saved window positions also for new windows"),
2394 options->use_saved_window_positions_for_new_windows, &c_options->use_saved_window_positions_for_new_windows);
2395 pref_checkbox_link_sensitivity(ct_button, button);
2397 button = pref_checkbox_new_int(group, _("Remember window workspace"),
2398 options->save_window_workspace, &c_options->save_window_workspace);
2399 pref_checkbox_link_sensitivity(ct_button, button);
2401 pref_checkbox_new_int(group, _("Remember tool state (float/hidden)"),
2402 options->tools_restore_state, &c_options->tools_restore_state);
2404 pref_checkbox_new_int(group, _("Remember dialog window positions"),
2405 options->save_dialog_window_positions, &c_options->save_dialog_window_positions);
2407 pref_checkbox_new_int(group, _("Show window IDs"),
2408 options->show_window_ids, &c_options->show_window_ids);
2410 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2411 pref_label_new(subgroup, _("Use current layout for default: "));
2412 button = pref_button_new(subgroup, nullptr, _("Set"), G_CALLBACK(save_default_window_layout_cb), nullptr);
2416 popover = create_popover(button, gtk_label_new(_("Current window layout\nhas been set as default")), GTK_POS_TOP);
2417 gtk_popover_set_modal(GTK_POPOVER (popover), FALSE);
2418 g_signal_connect(button, "clicked", G_CALLBACK(default_layout_changed_cb), popover);
2420 group = pref_group_new(vbox, FALSE, _("Size"), GTK_ORIENTATION_VERTICAL);
2422 pref_checkbox_new_int(group, _("Fit window to image when tools are hidden/floating"),
2423 options->image.fit_window_to_image, &c_options->image.fit_window_to_image);
2425 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2426 ct_button = pref_checkbox_new_int(hbox, _("Limit size when auto-sizing window (%):"),
2427 options->image.limit_window_size, &c_options->image.limit_window_size);
2428 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2430 options->image.max_window_size, &c_options->image.max_window_size);
2431 pref_checkbox_link_sensitivity(ct_button, spin);
2433 group = pref_group_new(vbox, FALSE, _("Full screen"), GTK_ORIENTATION_VERTICAL);
2435 c_options->fullscreen.screen = options->fullscreen.screen;
2436 c_options->fullscreen.above = options->fullscreen.above;
2437 hbox = fullscreen_prefs_selection_new(_("Location:"), &c_options->fullscreen.screen, &c_options->fullscreen.above);
2438 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2439 gtk_widget_show(hbox);
2441 pref_checkbox_new_int(group, _("Smooth image flip"),
2442 options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
2443 pref_checkbox_new_int(group, _("Disable screen saver"),
2444 options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
2447 #define PRE_FORMATTED_COLUMNS 5
2448 static void config_tab_osd(GtkWidget *notebook)
2454 GtkWidget *image_overlay_template_view;
2455 GtkWidget *scrolled;
2456 GtkWidget *scrolled_pre_formatted;
2457 GtkTextBuffer *buffer;
2459 GtkWidget *subgroup;
2461 vbox = scrolled_notebook_page(notebook, _("OSD"));
2463 image_overlay_template_view = gtk_text_view_new();
2465 group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
2467 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2469 scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
2470 gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
2471 gq_gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
2472 gtk_widget_show(scrolled_pre_formatted);
2473 gtk_widget_show(subgroup);
2475 pref_line(group, PREF_PAD_GAP);
2477 pref_label_new(group, _("Image overlay template"));
2479 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2480 gtk_widget_set_size_request(scrolled, 200, 150);
2481 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2482 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
2483 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2484 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
2485 gtk_widget_show(scrolled);
2487 gtk_widget_set_tooltip_markup(image_overlay_template_view,
2488 _("Extensive formatting options are shown in the Help file"));
2490 gq_gtk_container_add(GTK_WIDGET(scrolled), image_overlay_template_view);
2491 gtk_widget_show(image_overlay_template_view);
2493 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2495 button = pref_button_new(nullptr, GQ_ICON_SELECT_FONT, _("Font"),
2496 G_CALLBACK(image_overlay_set_font_cb), notebook);
2498 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2499 gtk_widget_show(button);
2501 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Text"), G_CALLBACK(image_overlay_set_text_color_cb), nullptr);
2502 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2503 gtk_widget_show(button);
2505 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Background"), G_CALLBACK(image_overlay_set_background_color_cb), nullptr);
2506 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2507 gtk_widget_show(button);
2508 image_overlay_set_text_colors();
2510 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2511 G_CALLBACK(image_overlay_default_template_cb), image_overlay_template_view);
2512 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2513 gtk_widget_show(button);
2515 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
2516 G_CALLBACK(image_overlay_help_cb), nullptr);
2517 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2518 gtk_widget_show(button);
2520 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_overlay_template_view));
2521 if (options->image_overlay.template_string) gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
2522 g_signal_connect(G_OBJECT(buffer), "changed",
2523 G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
2525 pref_line(group, PREF_PAD_GAP);
2527 group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL);
2528 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2529 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2530 gtk_widget_show(hbox);
2531 label = gtk_label_new(_("%Exif.Image.Orientation%"));
2532 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2533 gtk_widget_show(label);
2534 pref_spacer(group,TRUE);
2536 group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL);
2537 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2538 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2539 gtk_widget_show(hbox);
2540 label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
2541 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2542 gtk_widget_show(label);
2543 pref_spacer(group,TRUE);
2545 group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL);
2546 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2547 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2548 gtk_widget_show(hbox);
2549 label = gtk_label_new(_("%path:39%"));
2550 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2551 gtk_widget_show(label);
2552 pref_spacer(group,TRUE);
2554 group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL);
2555 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2556 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2557 gtk_widget_show(hbox);
2558 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%"));
2559 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2560 gtk_widget_show(label);
2561 pref_spacer(group,TRUE);
2563 group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL);
2564 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2565 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2566 gtk_widget_show(hbox);
2567 label = gtk_label_new(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
2568 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2569 gtk_widget_show(label);
2572 static GtkTreeModel *create_class_model()
2574 GtkListStore *model;
2578 /* create list store */
2579 model = gtk_list_store_new(1, G_TYPE_STRING);
2580 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
2582 gtk_list_store_append(model, &iter);
2583 gtk_list_store_set(model, &iter, 0, _(format_class_list[i]), -1);
2585 return GTK_TREE_MODEL (model);
2590 static gint filter_table_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
2592 gint n = GPOINTER_TO_INT(data);
2594 FilterEntry *filter_a;
2595 FilterEntry *filter_b;
2597 gtk_tree_model_get(model, a, 0, &filter_a, -1);
2598 gtk_tree_model_get(model, b, 0, &filter_b, -1);
2602 case FILETYPES_COLUMN_ENABLED:
2604 ret = filter_a->enabled - filter_b->enabled;
2607 case FILETYPES_COLUMN_FILTER:
2609 ret = g_utf8_collate(filter_a->extensions, filter_b->extensions);
2612 case FILETYPES_COLUMN_DESCRIPTION:
2614 ret = g_utf8_collate(filter_a->description, filter_b->description);
2617 case FILETYPES_COLUMN_CLASS:
2619 ret = g_strcmp0(format_class_list[filter_a->file_class], format_class_list[filter_b->file_class]);
2622 case FILETYPES_COLUMN_WRITABLE:
2624 ret = filter_a->writable - filter_b->writable;
2627 case FILETYPES_COLUMN_SIDECAR:
2629 ret = filter_a->allow_sidecar - filter_b->allow_sidecar;
2633 g_return_val_if_reached(0);
2639 static gboolean search_function_cb(GtkTreeModel *model, gint, const gchar *key, GtkTreeIter *iter, gpointer)
2642 gboolean ret = TRUE;
2644 gtk_tree_model_get(model, iter, 0, &fe, -1);
2646 if (g_strstr_len(fe->extensions, -1, key))
2654 static void config_tab_files(GtkWidget *notebook)
2661 GtkWidget *ct_button;
2662 GtkWidget *scrolled;
2663 GtkWidget *filter_view;
2664 GtkCellRenderer *renderer;
2665 GtkTreeSelection *selection;
2666 GtkTreeViewColumn *column;
2668 vbox = scrolled_notebook_page(notebook, _("File Filters"));
2670 group = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2672 pref_checkbox_new_int(group, _("Show hidden files or folders"),
2673 options->file_filter.show_hidden_files, &c_options->file_filter.show_hidden_files);
2674 pref_checkbox_new_int(group, _("Show parent folder (..)"),
2675 options->file_filter.show_parent_directory, &c_options->file_filter.show_parent_directory);
2676 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);
2677 pref_checkbox_new_int(group, _("Disable file extension checks"),
2678 options->file_filter.disable_file_extension_checks, &c_options->file_filter.disable_file_extension_checks);
2680 ct_button = pref_checkbox_new_int(group, _("Disable File Filtering"),
2681 options->file_filter.disable, &c_options->file_filter.disable);
2684 group = pref_group_new(vbox, FALSE, _("Grouping sidecar extensions"), GTK_ORIENTATION_VERTICAL);
2686 sidecar_ext_entry = gtk_entry_new();
2687 gq_gtk_entry_set_text(GTK_ENTRY(sidecar_ext_entry), options->sidecar.ext);
2688 gq_gtk_box_pack_start(GTK_BOX(group), sidecar_ext_entry, FALSE, FALSE, 0);
2689 gtk_widget_show(sidecar_ext_entry);
2691 group = pref_group_new(vbox, TRUE, _("File types"), GTK_ORIENTATION_VERTICAL);
2693 frame = pref_group_parent(group);
2694 g_signal_connect(G_OBJECT(ct_button), "toggled",
2695 G_CALLBACK(filter_disable_cb), frame);
2696 gtk_widget_set_sensitive(frame, !options->file_filter.disable);
2698 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2699 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2700 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2701 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2702 gtk_widget_show(scrolled);
2704 filter_store = gtk_list_store_new(1, G_TYPE_POINTER);
2705 filter_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
2706 g_object_unref(filter_store);
2707 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
2708 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2710 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), FALSE);
2712 column = gtk_tree_view_column_new();
2713 gtk_tree_view_column_set_title(column, _("Enabled"));
2714 gtk_tree_view_column_set_resizable(column, TRUE);
2716 renderer = gtk_cell_renderer_toggle_new();
2717 g_signal_connect(G_OBJECT(renderer), "toggled",
2718 G_CALLBACK(filter_store_enable_cb), filter_store);
2719 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2720 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2721 GINT_TO_POINTER(FE_ENABLE), nullptr);
2722 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);
2723 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_ENABLED);
2724 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2726 column = gtk_tree_view_column_new();
2727 gtk_tree_view_column_set_title(column, _("Filter"));
2728 gtk_tree_view_column_set_resizable(column, TRUE);
2729 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);
2730 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_FILTER);
2732 renderer = gtk_cell_renderer_text_new();
2733 g_signal_connect(G_OBJECT(renderer), "edited",
2734 G_CALLBACK(filter_store_ext_edit_cb), filter_store);
2735 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2736 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2737 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2738 GINT_TO_POINTER(FE_EXTENSION), nullptr);
2739 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2741 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), TRUE);
2742 gtk_tree_view_set_search_column(GTK_TREE_VIEW(filter_view), FILETYPES_COLUMN_FILTER);
2743 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(filter_view), search_function_cb, nullptr, nullptr);
2745 column = gtk_tree_view_column_new();
2746 gtk_tree_view_column_set_title(column, _("Description"));
2747 gtk_tree_view_column_set_resizable(column, TRUE);
2748 gtk_tree_view_column_set_fixed_width(column, 200);
2749 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2751 renderer = gtk_cell_renderer_text_new();
2752 g_signal_connect(G_OBJECT(renderer), "edited",
2753 G_CALLBACK(filter_store_desc_edit_cb), filter_store);
2754 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2755 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2756 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2757 GINT_TO_POINTER(FE_DESCRIPTION), nullptr);
2758 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2759 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);
2760 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_DESCRIPTION);
2762 column = gtk_tree_view_column_new();
2763 gtk_tree_view_column_set_title(column, _("Class"));
2764 gtk_tree_view_column_set_resizable(column, TRUE);
2765 renderer = gtk_cell_renderer_combo_new();
2766 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE,
2767 "model", create_class_model(),
2772 g_signal_connect(G_OBJECT(renderer), "edited",
2773 G_CALLBACK(filter_store_class_edit_cb), filter_store);
2774 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2775 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2776 GINT_TO_POINTER(FE_CLASS), nullptr);
2777 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2778 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);
2779 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_CLASS);
2781 column = gtk_tree_view_column_new();
2782 gtk_tree_view_column_set_title(column, _("Writable"));
2783 gtk_tree_view_column_set_resizable(column, FALSE);
2784 renderer = gtk_cell_renderer_toggle_new();
2785 g_signal_connect(G_OBJECT(renderer), "toggled",
2786 G_CALLBACK(filter_store_writable_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_WRITABLE), nullptr);
2790 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2791 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);
2792 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_WRITABLE);
2794 column = gtk_tree_view_column_new();
2795 gtk_tree_view_column_set_title(column, _("Sidecar is allowed"));
2796 gtk_tree_view_column_set_resizable(column, FALSE);
2797 renderer = gtk_cell_renderer_toggle_new();
2798 g_signal_connect(G_OBJECT(renderer), "toggled",
2799 G_CALLBACK(filter_store_sidecar_cb), filter_store);
2800 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2801 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2802 GINT_TO_POINTER(FE_ALLOW_SIDECAR), nullptr);
2803 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2804 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);
2805 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_SIDECAR);
2807 filter_store_populate();
2808 gq_gtk_container_add(GTK_WIDGET(scrolled), filter_view);
2809 gtk_widget_show(filter_view);
2811 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2813 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2814 G_CALLBACK(filter_default_cb), filter_view);
2815 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2816 gtk_widget_show(button);
2818 button = pref_button_new(nullptr, GQ_ICON_REMOVE, _("Remove"),
2819 G_CALLBACK(filter_remove_cb), filter_view);
2820 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2821 gtk_widget_show(button);
2823 button = pref_button_new(nullptr, GQ_ICON_ADD, _("Add"),
2824 G_CALLBACK(filter_add_cb), filter_view);
2825 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2826 gtk_widget_show(button);
2830 static void config_tab_metadata(GtkWidget *notebook)
2835 GtkWidget *ct_button;
2837 GtkWidget *tmp_widget;
2839 GtkWidget *text_label;
2841 vbox = scrolled_notebook_page(notebook, _("Metadata"));
2844 group = pref_group_new(vbox, FALSE, _("Metadata writing sequence"), GTK_ORIENTATION_VERTICAL);
2846 label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2848 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."));
2849 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2850 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2852 gtk_widget_set_tooltip_text(label, _("A flowchart of the sequence is shown in the Help file"));
2854 ct_button = pref_checkbox_new_int(group, "", options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2855 text_label = gtk_bin_get_child(GTK_BIN(ct_button));
2856 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"));
2857 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2859 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)"));
2860 gtk_widget_set_tooltip_markup(ct_button, markup);
2864 gtk_widget_set_sensitive(ct_button, FALSE);
2867 tmp_widget = pref_checkbox_new_int(group, "", options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2868 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2869 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)"));
2870 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2873 label = pref_label_new(group, "");
2874 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(), "/");
2875 gtk_label_set_markup (GTK_LABEL(label), markup);
2878 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2879 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2880 gtk_widget_set_margin_start(label, 22);
2881 pref_spacer(group, PREF_PAD_GROUP);
2883 group = pref_group_new(vbox, FALSE, _("Step 1 Options:"), GTK_ORIENTATION_VERTICAL);
2885 gtk_widget_set_sensitive(group, FALSE);
2888 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2889 pref_checkbox_link_sensitivity(ct_button, hbox);
2891 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);
2892 gtk_widget_set_tooltip_text(tmp_widget, _("A simplified conversion list is in the Help file"));
2894 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);
2896 pref_checkbox_new_int(hbox, _("Ask before writing to image files"), options->metadata.confirm_write, &c_options->metadata.confirm_write);
2898 tmp_widget= pref_checkbox_new_int(hbox, "", options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2899 gtk_widget_set_tooltip_text(tmp_widget, _("This file naming convention is used by Darktable"));
2900 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2901 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", ")");
2902 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2905 pref_spacer(group, PREF_PAD_GROUP);
2907 group = pref_group_new(vbox, FALSE, _("Steps 2 and 3 Option:"), GTK_ORIENTATION_VERTICAL);
2909 gtk_widget_set_sensitive(group, FALSE);
2912 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);
2914 pref_spacer(group, PREF_PAD_GROUP);
2916 group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2917 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);
2918 gtk_widget_set_tooltip_text(tmp_widget, _("See the Help file for a list of the tags used"));
2920 tmp_widget = pref_checkbox_new_int(group, _("Permit Keywords to be case-sensitive"), options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2921 gtk_widget_set_tooltip_text(tmp_widget, _("When selected, \"Place\" and \"place\" are two different keywords"));
2923 ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"), options->metadata.write_orientation, &c_options->metadata.write_orientation);
2924 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"));
2927 gtk_widget_set_sensitive(ct_button, FALSE);
2930 pref_spacer(group, PREF_PAD_GROUP);
2932 group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2934 ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"), options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
2936 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2937 pref_checkbox_link_sensitivity(ct_button, hbox);
2939 pref_spin_new_int(hbox, _("Timeout (seconds):"), nullptr, 0, 900, 1, options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
2941 pref_checkbox_new_int(group, _("Write metadata on image change"), options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
2943 pref_checkbox_new_int(group, _("Write metadata on directory change"), options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
2945 pref_spacer(group, PREF_PAD_GROUP);
2948 group = pref_group_new(vbox, FALSE, _("Spelling checks"), GTK_ORIENTATION_VERTICAL);
2950 ct_button = pref_checkbox_new_int(group, _("Check spelling - Requires restart"), options->metadata.check_spelling, &c_options->metadata.check_spelling);
2951 gtk_widget_set_tooltip_text(ct_button, _("Spelling checks are performed on info sidebar panes Comment, Headline and Title"));
2954 pref_spacer(group, PREF_PAD_GROUP);
2956 group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
2958 ct_button = pref_checkbox_new_int(group, _("Read metadata in background"), options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
2959 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");
2964 struct KeywordFindData
2971 GtkWidget *button_close;
2972 GtkWidget *button_stop;
2973 GtkWidget *button_start;
2974 GtkWidget *progress;
2982 guint idle_id; /* event source id */
2985 #define KEYWORD_DIALOG_WIDTH 400
2987 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
2989 GList *list_d = nullptr;
2990 GList *list_f = nullptr;
2994 filelist_read(dir_fd, &list_f, &list_d);
2998 filelist_read(dir_fd, &list_f, nullptr);
3001 list_f = filelist_filter(list_f, FALSE);
3002 list_d = filelist_filter(list_d, TRUE);
3004 kfd->list = g_list_concat(list_f, kfd->list);
3005 kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
3008 static void keywords_find_reset(KeywordFindData *kfd)
3010 filelist_free(kfd->list);
3011 kfd->list = nullptr;
3013 filelist_free(kfd->list_dir);
3014 kfd->list_dir = nullptr;
3017 static void keywords_find_close_cb(GenericDialog *, gpointer data)
3019 auto kfd = static_cast<KeywordFindData *>(data);
3021 if (!gtk_widget_get_sensitive(kfd->button_close)) return;
3023 keywords_find_reset(kfd);
3024 generic_dialog_close(kfd->gd);
3028 static void keywords_find_finish(KeywordFindData *kfd)
3030 keywords_find_reset(kfd);
3032 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
3033 gtk_spinner_stop(GTK_SPINNER(kfd->spinner));
3035 gtk_widget_set_sensitive(kfd->group, TRUE);
3036 gtk_widget_set_sensitive(kfd->button_start, TRUE);
3037 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3038 gtk_widget_set_sensitive(kfd->button_close, TRUE);
3041 static void keywords_find_stop_cb(GenericDialog *, gpointer data)
3043 auto kfd = static_cast<KeywordFindData *>(data);
3045 g_idle_remove_by_data(kfd);
3047 keywords_find_finish(kfd);
3050 static gboolean keywords_find_file(gpointer data)
3052 auto kfd = static_cast<KeywordFindData *>(data);
3054 GtkTextBuffer *buffer;
3062 fd = static_cast<FileData *>(kfd->list->data);
3063 kfd->list = g_list_remove(kfd->list, fd);
3065 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
3066 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3070 gtk_text_buffer_get_end_iter(buffer, &iter);
3071 tmp = g_strconcat(static_cast<const gchar *>(keywords->data), "\n", NULL);
3072 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3074 keywords = keywords->next;
3077 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
3078 file_data_unref(fd);
3079 g_list_free_full(keywords, g_free);
3081 return (G_SOURCE_CONTINUE);
3083 else if (kfd->list_dir)
3087 fd = static_cast<FileData *>(kfd->list_dir->data);
3088 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
3090 keywords_find_folder(kfd, fd);
3092 file_data_unref(fd);
3094 return G_SOURCE_CONTINUE;
3097 keywords_find_finish(kfd);
3099 return G_SOURCE_REMOVE;
3102 static void keywords_find_start_cb(GenericDialog *, gpointer data)
3104 auto kfd = static_cast<KeywordFindData *>(data);
3107 if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
3109 path = remove_trailing_slash((gq_gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
3110 parse_out_relatives(path);
3114 warning_dialog(_("Invalid folder"),
3115 _("The specified folder can not be found."),
3116 GQ_ICON_DIALOG_WARNING, kfd->gd->dialog);
3122 gtk_widget_set_sensitive(kfd->group, FALSE);
3123 gtk_widget_set_sensitive(kfd->button_start, FALSE);
3124 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
3125 gtk_widget_set_sensitive(kfd->button_close, FALSE);
3126 gtk_spinner_start(GTK_SPINNER(kfd->spinner));
3128 dir_fd = file_data_new_dir(path);
3129 keywords_find_folder(kfd, dir_fd);
3130 file_data_unref(dir_fd);
3131 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
3137 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
3139 KeywordFindData *kfd;
3143 kfd = g_new0(KeywordFindData, 1);
3145 kfd->gd = generic_dialog_new(_("Search for keywords"),
3146 "search_for_keywords",
3149 gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
3150 kfd->gd->cancel_cb = keywords_find_close_cb;
3151 kfd->button_close = generic_dialog_add_button(kfd->gd, GQ_ICON_CLOSE, _("Close"),
3152 keywords_find_close_cb, FALSE);
3153 kfd->button_start = generic_dialog_add_button(kfd->gd, GQ_ICON_OK, _("S_tart"),
3154 keywords_find_start_cb, FALSE);
3155 kfd->button_stop = generic_dialog_add_button(kfd->gd, GQ_ICON_STOP, _("Stop"),
3156 keywords_find_stop_cb, FALSE);
3157 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3159 generic_dialog_add_message(kfd->gd, nullptr, _("Search for keywords"), nullptr, FALSE);
3161 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3162 pref_spacer(hbox, PREF_PAD_INDENT);
3163 kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3165 hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3166 pref_label_new(hbox, _("Folder:"));
3168 label = tab_completion_new(&kfd->entry, path, nullptr, nullptr, nullptr, nullptr);
3169 tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
3170 gq_gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3171 gtk_widget_show(label);
3173 pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
3175 pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
3176 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3178 kfd->progress = gtk_entry_new();
3179 gtk_widget_set_can_focus(kfd->progress, FALSE);
3180 gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
3181 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
3182 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
3183 gtk_widget_show(kfd->progress);
3185 kfd->spinner = gtk_spinner_new();
3186 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
3187 gtk_widget_show(kfd->spinner);
3189 kfd->list = nullptr;
3191 gtk_widget_show(kfd->gd->dialog);
3194 static void keywords_find_cb(GtkWidget *widget, gpointer)
3196 const gchar *path = layout_get_path(nullptr);
3198 if (!path || !*path) path = homedir();
3199 keywords_find_dialog(widget, path);
3202 static void config_tab_keywords_save()
3204 GtkTextIter start, end;
3205 GtkTextBuffer *buffer;
3206 GList *kw_list = nullptr;
3212 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3213 gtk_text_buffer_get_bounds(buffer, &start, &end);
3215 buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3217 kw_split = strtok(buffer_text, "\n");
3218 while (kw_split != nullptr)
3224 if (g_strcmp0(static_cast<const gchar *>(work->data), kw_split) == 0)
3233 kw_list = g_list_append(kw_list, g_strdup(kw_split));
3235 kw_split = strtok(nullptr, "\n");
3238 keyword_list_set(kw_list);
3240 g_list_free_full(kw_list, g_free);
3241 g_free(buffer_text);
3244 static void config_tab_keywords(GtkWidget *notebook)
3250 GtkWidget *scrolled;
3252 GtkTextBuffer *buffer;
3255 GspellTextView *gspell_view;
3258 vbox = scrolled_notebook_page(notebook, _("Keywords"));
3260 group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
3262 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3264 button = pref_button_new(hbox, GQ_ICON_RUN, _("Search"),
3265 G_CALLBACK(keywords_find_cb), keyword_text);
3266 gtk_widget_set_tooltip_text(button, "Search for existing keywords");
3269 keyword_text = gtk_text_view_new();
3270 gtk_widget_set_size_request(keyword_text, 20, 20);
3271 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3272 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3273 gtk_widget_show(scrolled);
3276 if (options->metadata.check_spelling)
3278 gspell_view = gspell_text_view_get_from_gtk_text_view(GTK_TEXT_VIEW(keyword_text));
3279 gspell_text_view_basic_setup(gspell_view);
3283 gq_gtk_container_add(GTK_WIDGET(scrolled), keyword_text);
3284 gtk_widget_show(keyword_text);
3286 gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
3288 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3289 gtk_text_buffer_create_tag(buffer, "monospace",
3290 "family", "monospace", NULL);
3292 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3293 gtk_text_buffer_get_start_iter(buffer, &iter);
3294 gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3297 path = g_build_filename(get_rc_dir(), "keywords", NULL);
3299 GList *kwl = keyword_list_get();
3300 kwl = g_list_first(kwl);
3303 gtk_text_buffer_get_end_iter (buffer, &iter);
3304 tmp = g_strconcat(static_cast<const gchar *>(kwl->data), "\n", NULL);
3305 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3310 gtk_text_buffer_set_modified(buffer, FALSE);
3317 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3319 auto option = static_cast<gint *>(data);
3321 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3325 *option = INTENT_PERCEPTUAL;
3328 *option = INTENT_RELATIVE_COLORIMETRIC;
3331 *option = INTENT_SATURATION;
3334 *option = INTENT_ABSOLUTE_COLORIMETRIC;
3339 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3340 gint option, gint *option_c)
3347 pref_table_label(table, column, row, text, GTK_ALIGN_START);
3349 combo = gtk_combo_box_text_new();
3351 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3352 if (option == INTENT_PERCEPTUAL) current = 0;
3353 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3354 if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3355 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3356 if (option == INTENT_SATURATION) current = 2;
3357 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3358 if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3360 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3362 gtk_widget_set_tooltip_text(combo,"Refer to the lcms documentation for the defaults used when the selected Intent is not available");
3364 g_signal_connect(G_OBJECT(combo), "changed",
3365 G_CALLBACK(intent_menu_cb), option_c);
3367 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
3368 gtk_widget_show(combo);
3372 static void config_tab_color(GtkWidget *notebook)
3381 vbox = scrolled_notebook_page(notebook, _("Color management"));
3383 group = pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3385 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3388 table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3389 gtk_grid_set_column_spacing(GTK_GRID(table), PREF_PAD_GAP);
3391 label = pref_table_label(table, 0, 0, _("Type"), GTK_ALIGN_START);
3392 pref_label_bold(label, TRUE, FALSE);
3394 label = pref_table_label(table, 1, 0, _("Menu name"), GTK_ALIGN_START);
3395 pref_label_bold(label, TRUE, FALSE);
3397 label = pref_table_label(table, 2, 0, _("File"), GTK_ALIGN_START);
3398 pref_label_bold(label, TRUE, FALSE);
3400 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3405 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3406 pref_table_label(table, 0, i + 1, buf, GTK_ALIGN_END);
3409 entry = gtk_entry_new();
3410 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3411 if (options->color_profile.input_name[i])
3413 gq_gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3415 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);
3416 gtk_widget_show(entry);
3417 color_profile_input_name_entry[i] = entry;
3419 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], nullptr, ".icc", "ICC Files", nullptr);
3420 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3421 gtk_widget_set_size_request(entry, 160, -1);
3422 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);
3423 gtk_widget_show(tabcomp);
3424 color_profile_input_file_entry[i] = entry;
3427 group = pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3429 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3431 pref_checkbox_new_int(group, _("Use system screen profile if available"),
3432 options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3434 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3436 pref_table_label(table, 0, 0, _("Screen:"), GTK_ALIGN_END);
3437 tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3438 options->color_profile.screen_file, nullptr, ".icc", "ICC Files", nullptr);
3439 tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3440 gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3442 add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3444 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);
3446 gtk_widget_show(tabcomp);
3449 /* advanced entry tab */
3450 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer)
3452 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3454 c_options->file_ops.use_system_trash = FALSE;
3455 c_options->file_ops.no_trash = FALSE;
3459 static void use_system_trash_cb(GtkWidget *widget, gpointer)
3461 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3463 c_options->file_ops.use_system_trash = TRUE;
3464 c_options->file_ops.no_trash = FALSE;
3468 static void use_no_cache_cb(GtkWidget *widget, gpointer)
3470 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3472 c_options->file_ops.no_trash = TRUE;
3476 static void config_tab_behavior(GtkWidget *notebook)
3483 GtkWidget *ct_button;
3487 GtkWidget *with_rename;
3488 GtkWidget *collections_on_top;
3489 GtkWidget *hide_window_in_fullscreen;
3490 GtkWidget *checkbox;
3493 vbox = scrolled_notebook_page(notebook, _("Behavior"));
3495 group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3497 pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3498 options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3499 pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3500 options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3501 pref_checkbox_new_int(group, _("Enable Delete key"),
3502 options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3504 ct_button = pref_radiobutton_new(group, nullptr, _("Use Geeqie trash location"),
3505 !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),nullptr);
3507 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3508 pref_checkbox_link_sensitivity(ct_button, hbox);
3510 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3511 pref_label_new(hbox, _("Folder:"));
3513 tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, nullptr, nullptr, nullptr, nullptr);
3514 tab_completion_add_select_button(safe_delete_path_entry, nullptr, TRUE);
3515 gq_gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3516 gtk_widget_show(tabcomp);
3518 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3519 pref_checkbox_link_sensitivity(ct_button, hbox);
3521 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3522 spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MiB"),
3523 0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3524 gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3525 button = pref_button_new(nullptr, nullptr, _("View"),
3526 G_CALLBACK(safe_delete_view_cb), nullptr);
3527 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3528 gtk_widget_show(button);
3530 button = pref_button_new(nullptr, GQ_ICON_CLEAR, nullptr,
3531 G_CALLBACK(safe_delete_clear_cb), nullptr);
3532 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3534 c_options->file_ops.no_trash = options->file_ops.no_trash;
3535 c_options->file_ops.use_system_trash = options->file_ops.use_system_trash;
3537 pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3538 options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), nullptr);
3540 pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3541 options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), nullptr);
3543 gtk_widget_show(button);
3545 pref_spacer(group, PREF_PAD_GROUP);
3548 group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3550 pref_checkbox_new_int(group, _("Descend folders in tree view"),
3551 options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3553 pref_checkbox_new_int(group, _("In place renaming"),
3554 options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3556 pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3557 options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3559 tmp = pref_checkbox_new_int(group, _("Circular selection lists"),
3560 options->circular_selection_lists, &c_options->circular_selection_lists);
3561 gtk_widget_set_tooltip_text(tmp, _("Traverse selection lists in a circular manner"));
3563 marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3564 options->marks_save, &c_options->marks_save);
3565 gtk_widget_set_tooltip_text(marks,"Note that marks linked to a keyword will be saved irrespective of this setting");
3567 with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3568 options->with_rename, &c_options->with_rename);
3569 gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3571 collections_on_top = pref_checkbox_new_int(group, _("Permit duplicates in Collections"),
3572 options->collections_duplicates, &c_options->collections_duplicates);
3573 gtk_widget_set_tooltip_text(collections_on_top,"Allow the same image to be in a Collection more than once");
3575 collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3576 options->collections_on_top, &c_options->collections_on_top);
3577 gtk_widget_set_tooltip_text(collections_on_top,"Open collections window on top");
3579 hide_window_in_fullscreen = pref_checkbox_new_int(group, _("Hide window in fullscreen"),
3580 options->hide_window_in_fullscreen, &c_options->hide_window_in_fullscreen);
3581 gtk_widget_set_tooltip_text(hide_window_in_fullscreen,"When alt-tabbing, prevent Geeqie window showing twice");
3583 pref_spin_new_int(group, _("Recent folder list maximum size"), nullptr,
3584 1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3586 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);
3587 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."));
3589 pref_spin_new_int(group, _("Drag'n drop icon size"), nullptr,
3590 16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3592 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3593 add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
3595 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3596 add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3598 pref_spacer(group, PREF_PAD_GROUP);
3600 group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3602 pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3603 options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3604 pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), nullptr,
3605 1, 32, 1, options->keyboard_scroll_step, reinterpret_cast<int *>(&c_options->keyboard_scroll_step));
3606 pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3607 options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3608 pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3609 options->image_lm_click_nav, &c_options->image_lm_click_nav);
3610 pref_checkbox_new_int(group, _("Open archive by left click on image"),
3611 options->image_l_click_archive, &c_options->image_l_click_archive);
3612 pref_checkbox_new_int(group, _("Play video by left click on image"),
3613 options->image_l_click_video, &c_options->image_l_click_video);
3614 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3615 add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3617 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3618 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3619 add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3620 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3621 add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3623 pref_spacer(group, PREF_PAD_GROUP);
3625 group = pref_group_new(vbox, FALSE, _("GPU"), GTK_ORIENTATION_VERTICAL);
3627 checkbox = pref_checkbox_new_int(group, _("Override disable GPU"),
3628 options->override_disable_gpu, &c_options->override_disable_gpu);
3629 gtk_widget_set_tooltip_text(checkbox, "Contact the developers for usage");
3632 pref_spacer(group, PREF_PAD_GROUP);
3634 group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3636 pref_spin_new_int(group, _("Debug level:"), nullptr,
3637 DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3639 pref_checkbox_new_int(group, _("Timer data"),
3640 options->log_window.timer_data, &c_options->log_window.timer_data);
3642 pref_spin_new_int(group, _("Log Window max. lines:"), nullptr,
3643 1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3647 /* accelerators tab */
3649 static gboolean accel_search_function_cb(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer)
3651 gboolean ret = TRUE;
3656 gtk_tree_model_get(model, iter, column, &text, -1);
3657 text_nocase = g_utf8_casefold(text, -1);
3658 key_nocase = g_utf8_casefold(key, -1);
3660 if (g_strstr_len(text_nocase, -1, key_nocase))
3667 g_free(text_nocase);
3672 static void accel_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *, GtkTreeViewColumn *column, gpointer)
3677 list = gtk_tree_view_get_columns(tree_view);
3678 col_num = g_list_index(list, column);
3682 gtk_tree_view_set_search_column(tree_view, col_num);
3685 static void config_tab_accelerators(GtkWidget *notebook)
3691 GtkWidget *scrolled;
3692 GtkWidget *accel_view;
3693 GtkCellRenderer *renderer;
3694 GtkTreeSelection *selection;
3695 GtkTreeViewColumn *column;
3697 vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3699 group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3701 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3702 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3703 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3704 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3705 gtk_widget_show(scrolled);
3707 accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3709 accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3710 g_object_unref(accel_store);
3711 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3712 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3714 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3716 renderer = gtk_cell_renderer_text_new();
3718 column = gtk_tree_view_column_new_with_attributes(_("Action"),
3723 gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3724 gtk_tree_view_column_set_resizable(column, TRUE);
3725 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3728 renderer = gtk_cell_renderer_accel_new();
3729 g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3730 G_CALLBACK(accel_store_cleared_cb), accel_store);
3731 g_signal_connect(G_OBJECT(renderer), "accel-edited",
3732 G_CALLBACK(accel_store_edited_cb), accel_store);
3735 g_object_set (renderer,
3737 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3740 column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3745 gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3746 gtk_tree_view_column_set_resizable(column, TRUE);
3747 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3749 renderer = gtk_cell_renderer_text_new();
3751 column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3756 gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3757 gtk_tree_view_column_set_resizable(column, TRUE);
3758 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3760 renderer = gtk_cell_renderer_text_new();
3762 column = gtk_tree_view_column_new_with_attributes("Accel",
3767 gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3768 gtk_tree_view_column_set_resizable(column, TRUE);
3769 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3771 /* Search on text in column */
3772 gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(accel_view), TRUE);
3773 g_signal_connect(accel_view, "row_activated", G_CALLBACK(accel_row_activated_cb), accel_store);
3774 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), TRUE);
3775 gtk_tree_view_set_search_column(GTK_TREE_VIEW(accel_view), AE_TOOLTIP);
3776 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(accel_view), accel_search_function_cb, nullptr, nullptr);
3778 accel_store_populate();
3779 gq_gtk_container_add(GTK_WIDGET(scrolled), accel_view);
3780 gtk_widget_show(accel_view);
3782 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3784 button = pref_button_new(nullptr, nullptr, _("Defaults"),
3785 G_CALLBACK(accel_default_cb), accel_view);
3786 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3787 gtk_widget_show(button);
3789 button = pref_button_new(nullptr, nullptr, _("Reset selected"),
3790 G_CALLBACK(accel_reset_cb), accel_view);
3791 gtk_widget_set_tooltip_text(button, _("Will only reset changes made before the settings are saved"));
3792 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3793 gtk_widget_show(button);
3795 button = pref_button_new(nullptr, nullptr, _("Clear selected"),
3796 G_CALLBACK(accel_clear_cb), accel_view);
3797 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3798 gtk_widget_show(button);
3801 /* toolbar main tab */
3802 static void config_tab_toolbar_main(GtkWidget *notebook)
3805 GtkWidget *toolbardata;
3808 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3810 vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3812 toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3813 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3814 gtk_widget_show(vbox);
3817 /* toolbar status tab */
3818 static void config_tab_toolbar_status(GtkWidget *notebook)
3821 GtkWidget *toolbardata;
3824 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3826 vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3828 toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3829 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3830 gtk_widget_show(vbox);
3834 static gint extension_sort_cb(gconstpointer a, gconstpointer b)
3836 return g_strcmp0(static_cast<const gchar *>(a), static_cast<const gchar *>(b));
3839 static void config_tab_advanced(GtkWidget *notebook)
3843 GSList *formats_list;
3844 GList *extensions_list = nullptr;
3847 GdkPixbufFormat *fm;
3849 GString *types_string = g_string_new(nullptr);
3850 GtkWidget *types_string_label;
3851 GtkWidget *threads_string_label;
3852 GtkWidget *dupes_threads_spin;
3854 vbox = scrolled_notebook_page(notebook, _("Advanced"));
3855 group = pref_group_new(vbox, FALSE, _("External preview extraction"), GTK_ORIENTATION_VERTICAL);
3857 pref_checkbox_new_int(group, _("Use external preview extraction - Requires restart"), options->external_preview.enable, &c_options->external_preview.enable);
3859 pref_spacer(group, PREF_PAD_GROUP);
3861 formats_list = gdk_pixbuf_get_formats();
3863 while (formats_list)
3865 fm = static_cast<GdkPixbufFormat *>(formats_list->data);
3866 extensions = gdk_pixbuf_format_get_extensions(fm);
3869 while (extensions[i])
3871 extensions_list = g_list_insert_sorted(extensions_list, g_strdup(extensions[i]), extension_sort_cb);
3875 g_strfreev(extensions);
3876 formats_list = formats_list->next;
3879 while (extensions_list)
3881 if (types_string->len == 0)
3883 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3887 types_string = g_string_append(types_string, ", ");
3888 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3891 extensions_list = extensions_list->next;
3894 types_string = g_string_prepend(types_string, _("Usable file types:\n"));
3895 types_string_label = pref_label_new(group, types_string->str);
3896 gtk_label_set_line_wrap(GTK_LABEL(types_string_label), TRUE);
3898 pref_spacer(group, PREF_PAD_GROUP);
3900 group = pref_group_new(vbox, FALSE, _("File identification tool"), GTK_ORIENTATION_VERTICAL);
3901 external_preview_select_entry = gtk_entry_new();
3902 tabcomp = tab_completion_new(&external_preview_select_entry, options->external_preview.select, nullptr, nullptr, nullptr, nullptr);
3903 tab_completion_add_select_button(external_preview_select_entry, _("Select file identification tool"), FALSE);
3904 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3905 gtk_widget_show(tabcomp);
3907 group = pref_group_new(vbox, FALSE, _("Preview extraction tool"), GTK_ORIENTATION_VERTICAL);
3908 external_preview_extract_entry = gtk_entry_new();
3909 tabcomp = tab_completion_new(&external_preview_extract_entry, options->external_preview.extract, nullptr, nullptr, nullptr, nullptr);
3910 tab_completion_add_select_button(external_preview_extract_entry, _("Select preview extraction tool"), FALSE);
3911 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3912 gtk_widget_show(tabcomp);
3914 gtk_widget_show(vbox);
3916 g_slist_free(formats_list);
3917 g_list_free_full(extensions_list, g_free);
3918 g_string_free(types_string, TRUE);
3920 pref_spacer(group, PREF_PAD_GROUP);
3922 pref_line(vbox, PREF_PAD_SPACE);
3923 group = pref_group_new(vbox, FALSE, _("Thread pool limits"), GTK_ORIENTATION_VERTICAL);
3925 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.");
3926 gtk_label_set_line_wrap(GTK_LABEL(threads_string_label), TRUE);
3928 pref_spacer(vbox, PREF_PAD_GROUP);
3930 dupes_threads_spin = pref_spin_new_int(vbox, _("Duplicate check:"), _("max. threads"), 0, get_cpu_cores(), 1, options->threads.duplicates, &c_options->threads.duplicates);
3931 gtk_widget_set_tooltip_markup(dupes_threads_spin, _("Set to 0 for unlimited"));
3935 static void config_tab_stereo(GtkWidget *notebook)
3943 GtkWidget *fs_button;
3944 vbox = scrolled_notebook_page(notebook, _("Stereo"));
3946 group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
3948 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3949 add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
3951 table = pref_table_new(group, 2, 2, TRUE, FALSE);
3952 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3953 pref_checkbox_new_int(box, _("Mirror left image"),
3954 options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
3955 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3956 pref_checkbox_new_int(box, _("Flip left image"),
3957 options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
3958 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3959 pref_checkbox_new_int(box, _("Mirror right image"),
3960 options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
3961 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3962 pref_checkbox_new_int(box, _("Flip right image"),
3963 options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
3964 pref_checkbox_new_int(group, _("Swap left and right images"),
3965 options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
3966 pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
3967 options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
3969 group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
3970 fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
3971 options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
3972 box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
3973 pref_checkbox_link_sensitivity(fs_button, box2);
3974 table = pref_table_new(box2, 2, 1, FALSE, FALSE);
3975 add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
3976 table = pref_table_new(box2, 2, 2, TRUE, FALSE);
3977 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3978 pref_checkbox_new_int(box, _("Mirror left image"),
3979 options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
3980 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3981 pref_checkbox_new_int(box, _("Flip left image"),
3982 options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
3983 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3984 pref_checkbox_new_int(box, _("Mirror right image"),
3985 options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
3986 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3987 pref_checkbox_new_int(box, _("Flip right image"),
3988 options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
3989 pref_checkbox_new_int(box2, _("Swap left and right images"),
3990 options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
3991 pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
3992 options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
3994 group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
3995 table = pref_table_new(group2, 5, 3, FALSE, FALSE);
3996 pref_table_spin_new_int(table, 0, 0, _("Width"), nullptr,
3997 1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
3998 pref_table_spin_new_int(table, 3, 0, _("Height"), nullptr,
3999 1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
4000 pref_table_spin_new_int(table, 0, 1, _("Left X"), nullptr,
4001 0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
4002 pref_table_spin_new_int(table, 3, 1, _("Left Y"), nullptr,
4003 0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
4004 pref_table_spin_new_int(table, 0, 2, _("Right X"), nullptr,
4005 0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
4006 pref_table_spin_new_int(table, 3, 2, _("Right Y"), nullptr,
4007 0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
4011 /* Main preferences window */
4012 static void config_window_create(LayoutWindow *lw)
4014 GtkWidget *win_vbox;
4016 GtkWidget *notebook;
4018 GtkWidget *ct_button;
4020 if (!c_options) c_options = init_options(nullptr);
4022 configwindow = window_new("preferences", PIXBUF_INLINE_ICON_CONFIG, nullptr, _("Preferences"));
4023 DEBUG_NAME(configwindow);
4024 gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
4025 g_signal_connect(G_OBJECT(configwindow), "delete_event",
4026 G_CALLBACK(config_window_delete), NULL);
4027 if (options->save_dialog_window_positions)
4029 gtk_window_resize(GTK_WINDOW(configwindow), lw->options.preferences_window.w, lw->options.preferences_window.h);
4030 gq_gtk_window_move(GTK_WINDOW(configwindow), lw->options.preferences_window.x, lw->options.preferences_window.y);
4034 gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
4036 gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
4037 gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
4039 win_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4040 gq_gtk_container_add(GTK_WIDGET(configwindow), win_vbox);
4041 gtk_widget_show(win_vbox);
4043 notebook = gtk_notebook_new();
4044 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
4045 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
4046 gq_gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
4048 config_tab_general(notebook);
4049 config_tab_image(notebook);
4050 config_tab_osd(notebook);
4051 config_tab_windows(notebook);
4052 config_tab_accelerators(notebook);
4053 config_tab_files(notebook);
4054 config_tab_metadata(notebook);
4055 config_tab_keywords(notebook);
4056 config_tab_color(notebook);
4057 config_tab_stereo(notebook);
4058 config_tab_behavior(notebook);
4059 config_tab_toolbar_main(notebook);
4060 config_tab_toolbar_status(notebook);
4061 config_tab_advanced(notebook);
4063 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), lw->options.preferences_window.page_number);
4065 hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
4066 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
4067 gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
4068 gq_gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
4069 gtk_widget_show(hbox);
4071 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
4072 G_CALLBACK(config_window_help_cb), notebook);
4073 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4074 gtk_widget_set_can_default(button, TRUE);
4075 gtk_widget_show(button);
4077 button = pref_button_new(nullptr, GQ_ICON_OK, "OK",
4078 G_CALLBACK(config_window_ok_cb), notebook);
4079 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4080 gtk_widget_set_can_default(button, TRUE);
4081 gtk_widget_grab_default(button);
4082 gtk_widget_show(button);
4086 button = pref_button_new(nullptr, GQ_ICON_CANCEL, _("Cancel"),
4087 G_CALLBACK(config_window_close_cb), nullptr);
4088 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4089 gtk_widget_set_can_default(button, TRUE);
4090 gtk_widget_show(button);
4092 if (!generic_dialog_get_alternative_button_order(configwindow))
4094 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
4097 gtk_widget_show(notebook);
4099 gtk_widget_show(configwindow);
4103 *-----------------------------------------------------------------------------
4104 * config window show (public)
4105 *-----------------------------------------------------------------------------
4108 void show_config_window(LayoutWindow *lw)
4112 gtk_window_present(GTK_WINDOW(configwindow));
4116 config_window_create(lw);
4125 void show_about_window(LayoutWindow *lw)
4127 GDataInputStream *data_stream;
4128 GInputStream *in_stream_authors;
4129 GInputStream *in_stream_translators;
4131 GdkPixbuf *pixbuf_icon;
4132 GdkPixbuf *pixbuf_logo;
4136 gchar *authors[1000];
4138 gchar *timezone_path;
4147 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");
4149 timezone_path = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
4150 if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
4152 cd = ZDOpenDatabase(timezone_path);
4155 copyright = g_string_append(copyright, ZDGetNotice(cd));
4159 log_printf("Error: Init of timezone database %s failed\n", timezone_path);
4161 ZDCloseDatabase(cd);
4163 g_free(timezone_path);
4165 copyright = g_string_append(copyright, "\n\nSome icons by https://www.flaticon.com");
4167 in_stream_authors = g_resources_open_stream(GQ_RESOURCE_PATH_CREDITS "/authors", G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4169 data_stream = g_data_input_stream_new(in_stream_authors);
4171 authors[0] = nullptr;
4172 while ((author_line = g_data_input_stream_read_line(G_DATA_INPUT_STREAM(data_stream), &length, nullptr, nullptr)))
4174 authors[i_authors] = g_strdup(author_line);
4176 g_free(author_line);
4178 authors[i_authors] = nullptr;
4180 g_input_stream_close(in_stream_authors, nullptr, nullptr);
4182 constexpr auto translators_path = GQ_RESOURCE_PATH_CREDITS "/translators";
4184 g_resources_get_info(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &size, &flags, nullptr);
4186 in_stream_translators = g_resources_open_stream(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4187 translators = static_cast<gchar *>(g_malloc0(size));
4188 g_input_stream_read_all(in_stream_translators, translators, size, &bytes_read, nullptr, nullptr);
4189 g_input_stream_close(in_stream_translators, nullptr, nullptr);
4191 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);
4193 artists[0] = g_strdup("Néstor Díaz Valencia <nestor@estudionexos.com>");
4194 artists[1] = nullptr;
4196 pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
4197 pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
4198 gtk_show_about_dialog(GTK_WINDOW(lw->window),
4199 "title", _("About Geeqie"),
4201 "program-name", GQ_APPNAME,
4203 "logo", pixbuf_logo,
4204 "icon", pixbuf_icon,
4205 "website", GQ_WEBSITE,
4206 "website-label", "Website",
4207 "comments", comment,
4210 "translator-credits", translators,
4211 "wrap-license", TRUE,
4212 "license", copyright->str,
4215 g_string_free(copyright, TRUE);
4217 while(n < i_authors)
4225 g_free(translators);
4226 g_object_unref(data_stream);
4227 g_object_unref(in_stream_authors);
4228 g_object_unref(in_stream_translators);
4231 static void image_overlay_set_text_colors()
4233 c_options->image_overlay.text_red = options->image_overlay.text_red;
4234 c_options->image_overlay.text_green = options->image_overlay.text_green;
4235 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
4236 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
4237 c_options->image_overlay.background_red = options->image_overlay.background_red;
4238 c_options->image_overlay.background_green = options->image_overlay.background_green;
4239 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
4240 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
4244 *-----------------------------------------------------------------------------
4245 * timezone database routines
4246 *-----------------------------------------------------------------------------
4249 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
4251 GError *error = nullptr;
4252 auto tz = static_cast<TZData *>(data);
4253 gchar *tmp_filename;
4254 gchar *timezone_bin;
4255 gchar *tmp_dir = nullptr;
4258 if (!g_cancellable_is_cancelled(tz->cancellable))
4260 generic_dialog_close(tz->gd);
4264 if (g_file_copy_finish(G_FILE(source_object), res, &error))
4266 tmp_filename = g_file_get_path(tz->tmp_g_file);
4267 fd = file_data_new_simple(tmp_filename);
4268 tmp_dir = open_archive(fd);
4272 timezone_bin = g_build_filename(tmp_dir, TIMEZONE_DATABASE_VERSION, TIMEZONE_DATABASE_FILE, NULL);
4273 if (isfile(timezone_bin))
4275 move_file(timezone_bin, tz->timezone_database_user);
4279 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4282 g_free(timezone_bin);
4283 g_free(tmp_dir); // The folder in /tmp is deleted in exit_program_final()
4287 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4289 g_free(tmp_filename);
4290 file_data_unref(fd);
4294 file_util_warning_dialog(_("Error: Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4297 g_file_delete(tz->tmp_g_file, nullptr, &error);
4298 g_object_unref(tz->tmp_g_file);
4299 tz->tmp_g_file = nullptr;
4300 g_object_unref(tz->cancellable);
4301 g_object_unref(tz->timezone_database_gq);
4304 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
4306 auto tz = static_cast<TZData *>(data);
4308 if (!g_cancellable_is_cancelled(tz->cancellable))
4310 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), static_cast<gdouble>(current_num_bytes) / total_num_bytes);
4314 static void timezone_cancel_button_cb(GenericDialog *, gpointer data)
4316 auto tz = static_cast<TZData *>(data);
4318 g_cancellable_cancel(tz->cancellable);
4321 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
4323 auto tz = static_cast<TZData *>(data);
4324 GError *error = nullptr;
4325 GFileIOStream *io_stream;
4332 tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
4336 file_util_warning_dialog(_("Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4337 log_printf("Error: Download timezone database failed:\n%s", error->message);
4338 g_error_free(error);
4339 g_object_unref(tz->tmp_g_file);
4343 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE_WEB);
4345 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", nullptr, TRUE, timezone_cancel_button_cb, tz);
4347 generic_dialog_add_message(tz->gd, GQ_ICON_DIALOG_INFO, _("Downloading timezone database"), nullptr, FALSE);
4349 tz->progress = gtk_progress_bar_new();
4350 gq_gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
4351 gtk_widget_show(tz->progress);
4353 gtk_widget_show(tz->gd->dialog);
4354 tz->cancellable = g_cancellable_new();
4355 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);
4357 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
4360 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */