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;
155 static GtkWidget *log_window_f1_entry;
158 #define CONFIG_WINDOW_DEF_WIDTH 700
159 #define CONFIG_WINDOW_DEF_HEIGHT 600
162 *-----------------------------------------------------------------------------
163 * option widget callbacks (private)
164 *-----------------------------------------------------------------------------
167 static void zoom_increment_cb(GtkWidget *spin, gpointer)
169 c_options->image.zoom_increment = static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) * 100.0 + 0.01);
172 static void slideshow_delay_hours_cb(GtkWidget *spin, gpointer)
174 gint mins_secs_tenths, delay;
176 mins_secs_tenths = c_options->slideshow.delay %
177 (3600 * SLIDESHOW_SUBSECOND_PRECISION);
179 delay = (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
180 (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
183 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
184 SLIDESHOW_SUBSECOND_PRECISION;
187 static void slideshow_delay_minutes_cb(GtkWidget *spin, gpointer)
189 gint hours, secs_tenths, delay;
191 hours = c_options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
192 secs_tenths = c_options->slideshow.delay % (60 * SLIDESHOW_SUBSECOND_PRECISION);
194 delay = hours * (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
195 (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
196 (60 * SLIDESHOW_SUBSECOND_PRECISION) + secs_tenths);
198 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
199 SLIDESHOW_SUBSECOND_PRECISION;
202 static void slideshow_delay_seconds_cb(GtkWidget *spin, gpointer)
204 gint hours_mins, delay;
206 hours_mins = c_options->slideshow.delay / (60 * SLIDESHOW_SUBSECOND_PRECISION);
208 delay = (hours_mins * (60 * SLIDESHOW_SUBSECOND_PRECISION)) +
209 static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
210 static_cast<gdouble>(SLIDESHOW_SUBSECOND_PRECISION) + 0.01);
212 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
213 SLIDESHOW_SUBSECOND_PRECISION;
217 *-----------------------------------------------------------------------------
218 * sync program to config window routine (private)
219 *-----------------------------------------------------------------------------
222 void config_entry_to_option(GtkWidget *entry, gchar **option, gchar *(*func)(const gchar *))
228 buf = gq_gtk_entry_get_text(GTK_ENTRY(entry));
229 if (buf && strlen(buf) > 0)
234 *option = g_strdup(buf);
239 static gboolean accel_apply_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
241 gchar *accel_path, *accel;
243 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, AE_KEY, &accel, -1);
245 if (accel_path && accel_path[0])
248 gtk_accelerator_parse(accel, &key.accel_key, &key.accel_mods);
249 gtk_accel_map_change_entry(accel_path, key.accel_key, key.accel_mods, TRUE);
259 static void config_window_apply()
261 gboolean refresh = FALSE;
266 config_entry_to_option(safe_delete_path_entry, &options->file_ops.safe_delete_path, remove_trailing_slash);
268 if (options->file_filter.show_hidden_files != c_options->file_filter.show_hidden_files) refresh = TRUE;
269 if (options->file_filter.show_parent_directory != c_options->file_filter.show_parent_directory) refresh = TRUE;
270 if (options->file_filter.show_dot_directory != c_options->file_filter.show_dot_directory) refresh = TRUE;
271 if (options->file_sort.case_sensitive != c_options->file_sort.case_sensitive) refresh = TRUE;
272 if (options->file_filter.disable_file_extension_checks != c_options->file_filter.disable_file_extension_checks) refresh = TRUE;
273 if (options->file_filter.disable != c_options->file_filter.disable) refresh = TRUE;
275 options->file_ops.confirm_delete = c_options->file_ops.confirm_delete;
276 options->file_ops.enable_delete_key = c_options->file_ops.enable_delete_key;
277 options->file_ops.confirm_move_to_trash = c_options->file_ops.confirm_move_to_trash;
278 options->file_ops.use_system_trash = c_options->file_ops.use_system_trash;
279 options->file_ops.no_trash = c_options->file_ops.no_trash;
280 options->file_ops.safe_delete_folder_maxsize = c_options->file_ops.safe_delete_folder_maxsize;
281 options->tools_restore_state = c_options->tools_restore_state;
282 options->save_window_positions = c_options->save_window_positions;
283 options->use_saved_window_positions_for_new_windows = c_options->use_saved_window_positions_for_new_windows;
284 options->save_window_workspace = c_options->save_window_workspace;
285 options->save_dialog_window_positions = c_options->save_dialog_window_positions;
286 options->show_window_ids = c_options->show_window_ids;
287 options->image.scroll_reset_method = c_options->image.scroll_reset_method;
288 options->image.zoom_2pass = c_options->image.zoom_2pass;
289 options->image.fit_window_to_image = c_options->image.fit_window_to_image;
290 options->image.limit_window_size = c_options->image.limit_window_size;
291 options->image.zoom_to_fit_allow_expand = c_options->image.zoom_to_fit_allow_expand;
292 options->image.max_window_size = c_options->image.max_window_size;
293 options->image.limit_autofit_size = c_options->image.limit_autofit_size;
294 options->image.max_autofit_size = c_options->image.max_autofit_size;
295 options->image.max_enlargement_size = c_options->image.max_enlargement_size;
296 options->image.tile_size = c_options->image.tile_size;
297 options->progressive_key_scrolling = c_options->progressive_key_scrolling;
298 options->keyboard_scroll_step = c_options->keyboard_scroll_step;
300 if (options->thumbnails.max_width != c_options->thumbnails.max_width
301 || options->thumbnails.max_height != c_options->thumbnails.max_height
302 || options->thumbnails.quality != c_options->thumbnails.quality)
304 thumb_format_changed = TRUE;
306 options->thumbnails.max_width = c_options->thumbnails.max_width;
307 options->thumbnails.max_height = c_options->thumbnails.max_height;
308 options->thumbnails.quality = c_options->thumbnails.quality;
310 options->thumbnails.enable_caching = c_options->thumbnails.enable_caching;
311 options->thumbnails.cache_into_dirs = c_options->thumbnails.cache_into_dirs;
312 options->thumbnails.use_exif = c_options->thumbnails.use_exif;
313 options->thumbnails.use_color_management = c_options->thumbnails.use_color_management;
314 options->thumbnails.collection_preview = c_options->thumbnails.collection_preview;
315 options->thumbnails.use_ft_metadata = c_options->thumbnails.use_ft_metadata;
316 options->thumbnails.spec_standard = c_options->thumbnails.spec_standard;
317 options->metadata.enable_metadata_dirs = c_options->metadata.enable_metadata_dirs;
318 options->file_filter.show_hidden_files = c_options->file_filter.show_hidden_files;
319 options->file_filter.show_parent_directory = c_options->file_filter.show_parent_directory;
320 options->file_filter.show_dot_directory = c_options->file_filter.show_dot_directory;
321 options->file_filter.disable_file_extension_checks = c_options->file_filter.disable_file_extension_checks;
323 options->file_sort.case_sensitive = c_options->file_sort.case_sensitive;
324 options->file_filter.disable = c_options->file_filter.disable;
326 config_entry_to_option(sidecar_ext_entry, &options->sidecar.ext, nullptr);
327 sidecar_ext_parse(options->sidecar.ext);
329 options->slideshow.random = c_options->slideshow.random;
330 options->slideshow.repeat = c_options->slideshow.repeat;
331 options->slideshow.delay = c_options->slideshow.delay;
333 options->mousewheel_scrolls = c_options->mousewheel_scrolls;
334 options->image_lm_click_nav = c_options->image_lm_click_nav;
335 options->image_l_click_archive = c_options->image_l_click_archive;
336 options->image_l_click_video = c_options->image_l_click_video;
337 options->image_l_click_video_editor = c_options->image_l_click_video_editor;
339 options->file_ops.enable_in_place_rename = c_options->file_ops.enable_in_place_rename;
341 options->image.tile_cache_max = c_options->image.tile_cache_max;
342 options->image.image_cache_max = c_options->image.image_cache_max;
344 options->image.zoom_quality = c_options->image.zoom_quality;
346 options->image.zoom_increment = c_options->image.zoom_increment;
348 options->image.zoom_style = c_options->image.zoom_style;
350 options->image.enable_read_ahead = c_options->image.enable_read_ahead;
352 options->appimage_notifications = c_options->appimage_notifications;
355 if (options->image.use_custom_border_color != c_options->image.use_custom_border_color
356 || options->image.use_custom_border_color_in_fullscreen != c_options->image.use_custom_border_color_in_fullscreen
357 || !gdk_rgba_equal(&options->image.border_color, &c_options->image.border_color))
359 options->image.use_custom_border_color_in_fullscreen = c_options->image.use_custom_border_color_in_fullscreen;
360 options->image.use_custom_border_color = c_options->image.use_custom_border_color;
361 options->image.border_color = c_options->image.border_color;
362 layout_colors_update();
363 view_window_colors_update();
366 options->image.alpha_color_1 = c_options->image.alpha_color_1;
367 options->image.alpha_color_2 = c_options->image.alpha_color_2;
369 options->fullscreen.screen = c_options->fullscreen.screen;
370 options->fullscreen.clean_flip = c_options->fullscreen.clean_flip;
371 options->fullscreen.disable_saver = c_options->fullscreen.disable_saver;
372 options->fullscreen.above = c_options->fullscreen.above;
373 if (c_options->image_overlay.template_string)
374 set_image_overlay_template_string(&options->image_overlay.template_string,
375 c_options->image_overlay.template_string);
376 if (c_options->image_overlay.font)
377 set_image_overlay_font_string(&options->image_overlay.font,
378 c_options->image_overlay.font);
379 options->image_overlay.text_red = c_options->image_overlay.text_red;
380 options->image_overlay.text_green = c_options->image_overlay.text_green;
381 options->image_overlay.text_blue = c_options->image_overlay.text_blue;
382 options->image_overlay.text_alpha = c_options->image_overlay.text_alpha;
383 options->image_overlay.background_red = c_options->image_overlay.background_red;
384 options->image_overlay.background_green = c_options->image_overlay.background_green;
385 options->image_overlay.background_blue = c_options->image_overlay.background_blue;
386 options->image_overlay.background_alpha = c_options->image_overlay.background_alpha;
387 options->image_overlay.font = c_options->image_overlay.font;
388 options->update_on_time_change = c_options->update_on_time_change;
390 options->duplicates_similarity_threshold = c_options->duplicates_similarity_threshold;
391 options->rot_invariant_sim = c_options->rot_invariant_sim;
393 options->tree_descend_subdirs = c_options->tree_descend_subdirs;
395 options->view_dir_list_single_click_enter = c_options->view_dir_list_single_click_enter;
396 options->circular_selection_lists = c_options->circular_selection_lists;
398 options->open_recent_list_maxsize = c_options->open_recent_list_maxsize;
399 options->recent_folder_image_list_maxsize = c_options->recent_folder_image_list_maxsize;
400 options->dnd_icon_size = c_options->dnd_icon_size;
401 options->clipboard_selection = c_options->clipboard_selection;
402 options->dnd_default_action = c_options->dnd_default_action;
404 options->metadata.save_in_image_file = c_options->metadata.save_in_image_file;
405 options->metadata.save_legacy_IPTC = c_options->metadata.save_legacy_IPTC;
406 options->metadata.warn_on_write_problems = c_options->metadata.warn_on_write_problems;
407 options->metadata.save_legacy_format = c_options->metadata.save_legacy_format;
408 options->metadata.sync_grouped_files = c_options->metadata.sync_grouped_files;
409 options->metadata.confirm_write = c_options->metadata.confirm_write;
410 options->metadata.sidecar_extended_name = c_options->metadata.sidecar_extended_name;
411 options->metadata.confirm_timeout = c_options->metadata.confirm_timeout;
412 options->metadata.confirm_after_timeout = c_options->metadata.confirm_after_timeout;
413 options->metadata.confirm_on_image_change = c_options->metadata.confirm_on_image_change;
414 options->metadata.confirm_on_dir_change = c_options->metadata.confirm_on_dir_change;
415 options->metadata.keywords_case_sensitive = c_options->metadata.keywords_case_sensitive;
416 options->metadata.write_orientation = c_options->metadata.write_orientation;
417 options->metadata.check_spelling = c_options->metadata.check_spelling;
418 options->stereo.mode = (c_options->stereo.mode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
419 (c_options->stereo.tmp.mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
420 (c_options->stereo.tmp.flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
421 (c_options->stereo.tmp.mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
422 (c_options->stereo.tmp.flip_left ? PR_STEREO_FLIP_LEFT : 0) |
423 (c_options->stereo.tmp.swap ? PR_STEREO_SWAP : 0) |
424 (c_options->stereo.tmp.temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
425 options->stereo.fsmode = (c_options->stereo.fsmode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
426 (c_options->stereo.tmp.fs_mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
427 (c_options->stereo.tmp.fs_flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
428 (c_options->stereo.tmp.fs_mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
429 (c_options->stereo.tmp.fs_flip_left ? PR_STEREO_FLIP_LEFT : 0) |
430 (c_options->stereo.tmp.fs_swap ? PR_STEREO_SWAP : 0) |
431 (c_options->stereo.tmp.fs_temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
432 options->stereo.enable_fsmode = c_options->stereo.enable_fsmode;
433 options->stereo.fixed_w = c_options->stereo.fixed_w;
434 options->stereo.fixed_h = c_options->stereo.fixed_h;
435 options->stereo.fixed_x1 = c_options->stereo.fixed_x1;
436 options->stereo.fixed_y1 = c_options->stereo.fixed_y1;
437 options->stereo.fixed_x2 = c_options->stereo.fixed_x2;
438 options->stereo.fixed_y2 = c_options->stereo.fixed_y2;
440 options->info_keywords.height = c_options->info_keywords.height;
441 options->info_title.height = c_options->info_title.height;
442 options->info_comment.height = c_options->info_comment.height;
443 options->info_rating.height = c_options->info_rating.height;
445 options->show_predefined_keyword_tree = c_options->show_predefined_keyword_tree;
446 options->expand_menu_toolbar = c_options->expand_menu_toolbar;
447 options->hamburger_menu = c_options->hamburger_menu;
449 options->selectable_bars.menu_bar = c_options->selectable_bars.menu_bar;
450 options->selectable_bars.tool_bar = c_options->selectable_bars.tool_bar;
451 options->selectable_bars.status_bar = c_options->selectable_bars.status_bar;
453 options->marks_save = c_options->marks_save;
454 options->with_rename = c_options->with_rename;
455 options->collections_duplicates = c_options->collections_duplicates;
456 options->collections_on_top = c_options->collections_on_top;
457 options->hide_window_in_fullscreen = c_options->hide_window_in_fullscreen;
458 config_entry_to_option(help_search_engine_entry, &options->help_search_engine, nullptr);
460 options->external_preview.enable = c_options->external_preview.enable;
461 config_entry_to_option(external_preview_select_entry, &options->external_preview.select, nullptr);
462 config_entry_to_option(external_preview_extract_entry, &options->external_preview.extract, nullptr);
464 options->read_metadata_in_idle = c_options->read_metadata_in_idle;
466 options->star_rating.star = c_options->star_rating.star;
467 options->star_rating.rejected = c_options->star_rating.rejected;
469 options->threads.duplicates = c_options->threads.duplicates > 0 ? c_options->threads.duplicates : -1;
471 options->alternate_similarity_algorithm.enabled = c_options->alternate_similarity_algorithm.enabled;
472 options->alternate_similarity_algorithm.grayscale = c_options->alternate_similarity_algorithm.grayscale;
475 set_debug_level(debug_c);
476 config_entry_to_option(log_window_f1_entry, &options->log_window.action, nullptr);
480 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
482 config_entry_to_option(color_profile_input_name_entry[i], &options->color_profile.input_name[i], nullptr);
483 config_entry_to_option(color_profile_input_file_entry[i], &options->color_profile.input_file[i], nullptr);
485 config_entry_to_option(color_profile_screen_file_entry, &options->color_profile.screen_file, nullptr);
486 options->color_profile.use_x11_screen_profile = c_options->color_profile.use_x11_screen_profile;
487 if (options->color_profile.render_intent != c_options->color_profile.render_intent)
489 options->color_profile.render_intent = c_options->color_profile.render_intent;
494 options->mouse_button_8 = c_options->mouse_button_8;
495 options->mouse_button_9 = c_options->mouse_button_9;
497 options->override_disable_gpu = c_options->override_disable_gpu;
499 config_tab_keywords_save();
501 image_options_sync();
506 layout_refresh(nullptr);
509 if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, nullptr);
511 toolbar_apply(TOOLBAR_MAIN);
512 toolbar_apply(TOOLBAR_STATUS);
516 *-----------------------------------------------------------------------------
517 * config window main button callbacks (private)
518 *-----------------------------------------------------------------------------
521 static void config_window_close_cb(GtkWidget *, gpointer)
523 gq_gtk_widget_destroy(configwindow);
524 configwindow = nullptr;
525 filter_store = nullptr;
528 static void config_window_help_cb(GtkWidget *, gpointer data)
530 auto notebook = static_cast<GtkWidget *>(data);
533 static const gchar *html_section[] =
535 "GuideOptionsGeneral.html",
536 "GuideOptionsImage.html",
537 "GuideOptionsOSD.html",
538 "GuideOptionsWindow.html",
539 "GuideOptionsKeyboard.html",
540 "GuideOptionsFiltering.html",
541 "GuideOptionsMetadata.html",
542 "GuideOptionsKeywords.html",
543 "GuideOptionsColor.html",
544 "GuideOptionsStereo.html",
545 "GuideOptionsBehavior.html",
546 "GuideOptionsToolbar.html",
547 "GuideOptionsToolbar.html",
548 "GuideOptionsAdvanced.html"
551 i = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
552 help_window_show(html_section[i]);
555 static gboolean config_window_delete(GtkWidget *, GdkEventAny *, gpointer)
557 config_window_close_cb(nullptr, nullptr);
561 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
564 auto notebook = static_cast<GtkNotebook *>(data);
572 lw = static_cast<LayoutWindow *>(layout_window_list->data);
574 window = gtk_widget_get_window(widget);
575 gdk_window_get_root_origin(window, &x, &y);
576 w = gdk_window_get_width(window);
577 h = gdk_window_get_height(window);
578 page_number = gtk_notebook_get_current_page(notebook);
580 lw->options.preferences_window.x = x;
581 lw->options.preferences_window.y = y;
582 lw->options.preferences_window.w = w;
583 lw->options.preferences_window.h = h;
584 lw->options.preferences_window.page_number = page_number;
586 config_window_apply();
587 layout_util_sync(lw);
588 save_options(options);
589 config_window_close_cb(nullptr, nullptr);
593 *-----------------------------------------------------------------------------
594 * config window setup (private)
595 *-----------------------------------------------------------------------------
598 static void quality_menu_cb(GtkWidget *combo, gpointer data)
600 auto option = static_cast<gint *>(data);
602 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
606 *option = GDK_INTERP_NEAREST;
609 *option = GDK_INTERP_TILES;
612 *option = GDK_INTERP_BILINEAR;
617 static void dnd_default_action_selection_menu_cb(GtkWidget *combo, gpointer data)
619 auto option = static_cast<gint *>(data);
621 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
625 *option = DND_ACTION_ASK;
628 *option = DND_ACTION_COPY;
631 *option = DND_ACTION_MOVE;
635 static void clipboard_selection_menu_cb(GtkWidget *combo, gpointer data)
637 auto option = static_cast<gint *>(data);
639 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
642 *option = CLIPBOARD_PRIMARY;
645 *option = CLIPBOARD_CLIPBOARD;
648 *option = CLIPBOARD_BOTH;
651 *option = CLIPBOARD_BOTH;
655 static void add_quality_menu(GtkWidget *table, gint column, gint row, const gchar *text,
656 guint option, guint *option_c)
663 pref_table_label(table, column, row, text, GTK_ALIGN_START);
665 combo = gtk_combo_box_text_new();
667 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Nearest (worst, but fastest)"));
668 if (option == GDK_INTERP_NEAREST) current = 0;
669 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Tiles"));
670 if (option == GDK_INTERP_TILES) current = 1;
671 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Bilinear (best, but slowest)"));
672 if (option == GDK_INTERP_BILINEAR) current = 2;
674 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
676 g_signal_connect(G_OBJECT(combo), "changed",
677 G_CALLBACK(quality_menu_cb), option_c);
679 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
680 gtk_widget_show(combo);
683 static void add_dnd_default_action_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, DnDAction option, DnDAction *option_c)
690 pref_table_label(table, column, row, text, GTK_ALIGN_START);
692 combo = gtk_combo_box_text_new();
694 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Ask"));
695 if (option == DND_ACTION_ASK) current = 0;
696 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Copy"));
697 if (option == DND_ACTION_COPY) current = 1;
698 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Move"));
699 if (option == DND_ACTION_MOVE) current = 2;
701 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
703 g_signal_connect(G_OBJECT(combo), "changed",
704 G_CALLBACK(dnd_default_action_selection_menu_cb), option_c);
706 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
707 gtk_widget_show(combo);
710 static void add_clipboard_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
711 gint option, gint *option_c)
718 pref_table_label(table, column, row, text, GTK_ALIGN_START);
720 combo = gtk_combo_box_text_new();
722 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Primary"));
723 if (option == CLIPBOARD_PRIMARY) current = 0;
724 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Clipboard"));
725 if (option == CLIPBOARD_CLIPBOARD) current = 1;
726 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Both"));
727 if (option == CLIPBOARD_BOTH) current = 2;
729 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
731 g_signal_connect(G_OBJECT(combo), "changed",
732 G_CALLBACK(clipboard_selection_menu_cb), option_c);
734 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
735 gtk_widget_show(combo);
738 static void zoom_style_selection_menu_cb(GtkWidget *combo, gpointer data)
740 auto option = static_cast<gint *>(data);
742 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
745 *option = ZOOM_GEOMETRIC;
748 *option = ZOOM_ARITHMETIC;
751 *option = ZOOM_GEOMETRIC;
755 static void add_zoom_style_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, ZoomStyle option, ZoomStyle *option_c)
762 pref_table_label(table, column, row, text, GTK_ALIGN_START);
764 combo = gtk_combo_box_text_new();
766 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Geometric"));
767 if (option == ZOOM_GEOMETRIC) current = 0;
768 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Arithmetic"));
769 if (option == ZOOM_ARITHMETIC) current = 1;
771 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
773 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(zoom_style_selection_menu_cb), option_c);
775 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
776 gtk_widget_show(combo);
779 static void mouse_buttons_selection_menu_cb(GtkWidget *combo, gpointer data)
781 ActionItem *action_item = nullptr;
782 auto option = static_cast<gchar **>(data);
787 label = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
789 list = get_action_items();
794 action_item = static_cast<ActionItem *>(work->data);
795 if (g_strcmp0(action_item->label, label) == 0)
803 *option = g_strdup(action_item->name);
805 action_items_free(list);
808 static void add_mouse_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, gchar *option, gchar **option_c)
810 ActionItem *action_item;
819 pref_table_label(table, column, row, text, GTK_ALIGN_START);
821 combo = gtk_combo_box_text_new();
823 list = get_action_items();
827 action_item = static_cast<ActionItem *>(work->data);
828 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), action_item->label);
830 if (g_strcmp0(action_item->name, option) == 0)
838 action_items_free(list);
840 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
842 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(mouse_buttons_selection_menu_cb), option_c);
844 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
845 gtk_widget_show(combo);
848 static void thumb_size_menu_cb(GtkWidget *combo, gpointer)
852 n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
855 if (static_cast<guint>(n) < sizeof(thumb_size_list) / sizeof(ThumbSize))
857 c_options->thumbnails.max_width = thumb_size_list[n].w;
858 c_options->thumbnails.max_height = thumb_size_list[n].h;
862 c_options->thumbnails.max_width = options->thumbnails.max_width;
863 c_options->thumbnails.max_height = options->thumbnails.max_height;
867 static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *text)
873 c_options->thumbnails.max_width = options->thumbnails.max_width;
874 c_options->thumbnails.max_height = options->thumbnails.max_height;
876 pref_table_label(table, column, row, text, GTK_ALIGN_START);
878 combo = gtk_combo_box_text_new();
881 for (i = 0; static_cast<guint>(i) < sizeof(thumb_size_list) / sizeof(ThumbSize); i++)
886 w = thumb_size_list[i].w;
887 h = thumb_size_list[i].h;
889 buf = g_strdup_printf("%d x %d", w, h);
890 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
893 if (w == options->thumbnails.max_width && h == options->thumbnails.max_height) current = i;
900 buf = g_strdup_printf("%s %d x %d", _("Custom"), options->thumbnails.max_width, options->thumbnails.max_height);
901 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
907 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
908 g_signal_connect(G_OBJECT(combo), "changed",
909 G_CALLBACK(thumb_size_menu_cb), NULL);
911 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
912 gtk_widget_show(combo);
915 static void stereo_mode_menu_cb(GtkWidget *combo, gpointer data)
917 auto option = static_cast<gint *>(data);
919 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
923 *option = PR_STEREO_NONE;
926 *option = PR_STEREO_ANAGLYPH_RC;
929 *option = PR_STEREO_ANAGLYPH_GM;
932 *option = PR_STEREO_ANAGLYPH_YB;
935 *option = PR_STEREO_ANAGLYPH_GRAY_RC;
938 *option = PR_STEREO_ANAGLYPH_GRAY_GM;
941 *option = PR_STEREO_ANAGLYPH_GRAY_YB;
944 *option = PR_STEREO_ANAGLYPH_DB_RC;
947 *option = PR_STEREO_ANAGLYPH_DB_GM;
950 *option = PR_STEREO_ANAGLYPH_DB_YB;
953 *option = PR_STEREO_HORIZ;
956 *option = PR_STEREO_HORIZ | PR_STEREO_HALF;
959 *option = PR_STEREO_VERT;
962 *option = PR_STEREO_VERT | PR_STEREO_HALF;
965 *option = PR_STEREO_FIXED;
970 static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const gchar *text,
971 gint option, gint *option_c, gboolean add_fixed)
978 pref_table_label(table, column, row, text, GTK_ALIGN_START);
980 combo = gtk_combo_box_text_new();
982 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Single image"));
984 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Red-Cyan"));
985 if (option & PR_STEREO_ANAGLYPH_RC) current = 1;
986 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Green-Magenta"));
987 if (option & PR_STEREO_ANAGLYPH_GM) current = 2;
988 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Yellow-Blue"));
989 if (option & PR_STEREO_ANAGLYPH_YB) current = 3;
990 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Red-Cyan"));
991 if (option & PR_STEREO_ANAGLYPH_GRAY_RC) current = 4;
992 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Green-Magenta"));
993 if (option & PR_STEREO_ANAGLYPH_GRAY_GM) current = 5;
994 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Yellow-Blue"));
995 if (option & PR_STEREO_ANAGLYPH_GRAY_YB) current = 6;
996 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Red-Cyan"));
997 if (option & PR_STEREO_ANAGLYPH_DB_RC) current = 7;
998 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Green-Magenta"));
999 if (option & PR_STEREO_ANAGLYPH_DB_GM) current = 8;
1000 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Yellow-Blue"));
1001 if (option & PR_STEREO_ANAGLYPH_DB_YB) current = 9;
1003 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side"));
1004 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side Half size"));
1005 if (option & PR_STEREO_HORIZ)
1008 if (option & PR_STEREO_HALF) current = 11;
1011 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom"));
1012 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom Half size"));
1013 if (option & PR_STEREO_VERT)
1016 if (option & PR_STEREO_HALF) current = 13;
1021 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Fixed position"));
1022 if (option & PR_STEREO_FIXED) current = 14;
1025 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1027 g_signal_connect(G_OBJECT(combo), "changed",
1028 G_CALLBACK(stereo_mode_menu_cb), option_c);
1030 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1031 gtk_widget_show(combo);
1034 static void video_menu_cb(GtkWidget *combo, gpointer data)
1036 auto option = static_cast<gchar **>(data);
1038 auto ed = static_cast<EditorDescription *>(g_list_nth_data(editor_list_get(), gtk_combo_box_get_active(GTK_COMBO_BOX(combo))));
1042 static void video_menu_populate(gpointer data, gpointer user_data)
1044 auto combo = static_cast<GtkWidget *>(user_data);
1045 auto ed = static_cast<EditorDescription *>(data);
1047 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), ed->name);
1050 static void add_video_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1051 gchar *option, gchar **option_c)
1055 /* use lists since they are sorted */
1056 GList *eds = editor_list_get();
1060 pref_table_label(table, column, row, text, GTK_ALIGN_START);
1062 combo = gtk_combo_box_text_new();
1063 g_list_foreach(eds,video_menu_populate,combo);
1064 current = option ? g_list_index(eds,g_hash_table_lookup(editors,option)): -1;
1066 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1068 g_signal_connect(G_OBJECT(combo), "changed",
1069 G_CALLBACK(video_menu_cb), option_c);
1071 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1072 gtk_widget_show(combo);
1075 static void filter_store_populate()
1079 if (!filter_store) return;
1081 gtk_list_store_clear(filter_store);
1083 work = filter_get_list();
1089 fe = static_cast<FilterEntry *>(work->data);
1092 gtk_list_store_append(filter_store, &iter);
1093 gtk_list_store_set(filter_store, &iter, 0, fe, -1);
1097 static void filter_store_ext_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1099 auto model = static_cast<GtkWidget *>(data);
1100 auto fe = static_cast<FilterEntry *>(data);
1104 if (!new_text || strlen(new_text) < 1) return;
1106 tpath = gtk_tree_path_new_from_string(path_str);
1107 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1108 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1110 g_free(fe->extensions);
1111 fe->extensions = g_strdup(new_text);
1113 gtk_tree_path_free(tpath);
1117 static void filter_store_class_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1119 auto model = static_cast<GtkWidget *>(data);
1120 auto fe = static_cast<FilterEntry *>(data);
1125 if (!new_text || !new_text[0]) return;
1127 tpath = gtk_tree_path_new_from_string(path_str);
1128 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1129 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1131 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
1133 if (strcmp(new_text, _(format_class_list[i])) == 0)
1135 fe->file_class = static_cast<FileFormatClass>(i);
1140 gtk_tree_path_free(tpath);
1144 static void filter_store_desc_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1146 auto model = static_cast<GtkWidget *>(data);
1151 if (!new_text || !new_text[0]) return;
1153 tpath = gtk_tree_path_new_from_string(path_str);
1154 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1155 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1157 g_free(fe->description);
1158 fe->description = g_strdup(new_text);
1160 gtk_tree_path_free(tpath);
1163 static void filter_store_enable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1165 auto model = static_cast<GtkWidget *>(data);
1170 tpath = gtk_tree_path_new_from_string(path_str);
1171 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1172 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1174 fe->enabled = !fe->enabled;
1176 gtk_tree_path_free(tpath);
1180 static void filter_store_writable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1182 auto model = static_cast<GtkWidget *>(data);
1187 tpath = gtk_tree_path_new_from_string(path_str);
1188 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1189 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1191 fe->writable = !fe->writable;
1192 if (fe->writable) fe->allow_sidecar = FALSE;
1194 gtk_tree_path_free(tpath);
1198 static void filter_store_sidecar_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1200 auto model = static_cast<GtkWidget *>(data);
1205 tpath = gtk_tree_path_new_from_string(path_str);
1206 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1207 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1209 fe->allow_sidecar = !fe->allow_sidecar;
1210 if (fe->allow_sidecar) fe->writable = FALSE;
1212 gtk_tree_path_free(tpath);
1216 static void filter_set_func(GtkTreeViewColumn *, GtkCellRenderer *cell,
1217 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1221 gtk_tree_model_get(tree_model, iter, 0, &fe, -1);
1223 switch (GPOINTER_TO_INT(data))
1226 g_object_set(GTK_CELL_RENDERER(cell),
1227 "active", fe->enabled, NULL);
1230 g_object_set(GTK_CELL_RENDERER(cell),
1231 "text", fe->extensions, NULL);
1233 case FE_DESCRIPTION:
1234 g_object_set(GTK_CELL_RENDERER(cell),
1235 "text", fe->description, NULL);
1238 g_object_set(GTK_CELL_RENDERER(cell),
1239 "text", _(format_class_list[fe->file_class]), NULL);
1242 g_object_set(GTK_CELL_RENDERER(cell),
1243 "active", fe->writable, NULL);
1245 case FE_ALLOW_SIDECAR:
1246 g_object_set(GTK_CELL_RENDERER(cell),
1247 "active", fe->allow_sidecar, NULL);
1252 static gboolean filter_add_scroll(gpointer data)
1256 GtkCellRenderer *cell;
1257 GtkTreeViewColumn *column;
1260 GtkTreeModel *store;
1262 FilterEntry *filter;
1264 rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(filter_store), nullptr);
1265 path = gtk_tree_path_new_from_indices(rows-1, -1);
1267 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data), 0);
1269 list_cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1270 cell = static_cast<GtkCellRenderer *>(g_list_last(list_cells)->data);
1272 store = gtk_tree_view_get_model(GTK_TREE_VIEW(data));
1273 valid = gtk_tree_model_get_iter_first(store, &iter);
1277 gtk_tree_model_get(store, &iter, 0, &filter, -1);
1279 if (g_strcmp0(filter->extensions, ".new") == 0)
1281 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1285 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1288 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1289 path, column, FALSE, 0.0, 0.0 );
1290 gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(data),
1291 path, column, cell, TRUE);
1293 gtk_tree_path_free(path);
1294 g_list_free(list_cells);
1296 return(G_SOURCE_REMOVE);
1299 static void filter_add_cb(GtkWidget *, gpointer data)
1301 filter_add_unique("description", ".new", FORMAT_CLASS_IMAGE, TRUE, FALSE, TRUE);
1302 filter_store_populate();
1304 g_idle_add(static_cast<GSourceFunc>(filter_add_scroll), data);
1307 static void filter_remove_cb(GtkWidget *, gpointer data)
1309 auto filter_view = static_cast<GtkWidget *>(data);
1310 GtkTreeSelection *selection;
1314 if (!filter_store) return;
1315 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
1316 if (!gtk_tree_selection_get_selected(selection, nullptr, &iter)) return;
1317 gtk_tree_model_get(GTK_TREE_MODEL(filter_store), &iter, 0, &fe, -1);
1320 filter_remove_entry(fe);
1322 filter_store_populate();
1325 static gboolean filter_default_ok_scroll(GtkTreeView *data)
1329 GtkTreeViewColumn *column;
1331 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(filter_store), &iter);
1332 path = gtk_tree_model_get_path(GTK_TREE_MODEL(filter_store), &iter);
1333 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1335 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1339 gtk_tree_path_free(path);
1341 return(G_SOURCE_REMOVE);
1344 static void filter_default_ok_cb(GenericDialog *gd, gpointer)
1347 filter_add_defaults();
1349 filter_store_populate();
1351 g_idle_add(reinterpret_cast<GSourceFunc>(filter_default_ok_scroll), gd->data);
1354 static void dummy_cancel_cb(GenericDialog *, gpointer)
1356 /* no op, only so cancel button appears */
1359 static void filter_default_cb(GtkWidget *widget, gpointer data)
1363 gd = generic_dialog_new(_("Reset filters"),
1364 "reset_filter", widget, TRUE,
1365 dummy_cancel_cb, data);
1366 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset filters"),
1367 _("This will reset the file filters to the defaults.\nContinue?"), TRUE);
1368 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", filter_default_ok_cb, TRUE);
1369 gtk_widget_show(gd->dialog);
1372 static void filter_disable_cb(GtkWidget *widget, gpointer data)
1374 auto frame = static_cast<GtkWidget *>(data);
1376 gtk_widget_set_sensitive(frame,
1377 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1380 static void safe_delete_view_cb(GtkWidget *, gpointer)
1382 layout_set_path(nullptr, gq_gtk_entry_get_text(GTK_ENTRY(safe_delete_path_entry)));
1385 static void safe_delete_clear_ok_cb(GenericDialog *, gpointer)
1387 file_util_trash_clear();
1390 static void safe_delete_clear_cb(GtkWidget *widget, gpointer)
1394 gd = generic_dialog_new(_("Clear trash"),
1395 "clear_trash", widget, TRUE,
1396 dummy_cancel_cb, nullptr);
1397 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Clear trash"),
1398 _("This will remove the trash contents."), FALSE);
1399 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", safe_delete_clear_ok_cb, TRUE);
1400 entry = gtk_entry_new();
1401 gtk_widget_set_can_focus(entry, FALSE);
1402 gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
1403 if (options->file_ops.safe_delete_path) gq_gtk_entry_set_text(GTK_ENTRY(entry), options->file_ops.safe_delete_path);
1404 gq_gtk_box_pack_start(GTK_BOX(gd->vbox), entry, FALSE, FALSE, 0);
1405 gtk_widget_show(entry);
1406 gtk_widget_show(gd->dialog);
1409 static void image_overlay_template_view_changed_cb(GtkWidget *, gpointer data)
1411 GtkWidget *pTextView;
1412 GtkTextBuffer *pTextBuffer;
1416 pTextView = GTK_WIDGET(data);
1418 pTextBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pTextView));
1419 gtk_text_buffer_get_start_iter(pTextBuffer, &iStart);
1420 gtk_text_buffer_get_end_iter(pTextBuffer, &iEnd);
1422 set_image_overlay_template_string(&c_options->image_overlay.template_string,
1423 gtk_text_buffer_get_text(pTextBuffer, &iStart, &iEnd, TRUE));
1426 static void image_overlay_default_template_ok_cb(GenericDialog *, gpointer data)
1428 auto text_view = static_cast<GtkTextView *>(data);
1429 GtkTextBuffer *buffer;
1431 set_default_image_overlay_template_string(&options->image_overlay.template_string);
1432 if (!configwindow) return;
1434 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
1435 gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
1438 static void image_overlay_default_template_cb(GtkWidget *widget, gpointer data)
1442 gd = generic_dialog_new(_("Reset image overlay template string"),
1443 "reset_image_overlay_template_string", widget, TRUE,
1444 dummy_cancel_cb, data);
1445 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset image overlay template string"),
1446 _("This will reset the image overlay template string to the default.\nContinue?"), TRUE);
1447 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", image_overlay_default_template_ok_cb, TRUE);
1448 gtk_widget_show(gd->dialog);
1451 static void image_overlay_help_cb(GtkWidget *, gpointer)
1453 help_window_show("GuideOptionsOSD.html");
1456 static void font_activated_cb(GtkFontChooser *widget, gchar *fontname, gpointer)
1458 g_free(c_options->image_overlay.font);
1459 c_options->image_overlay.font = g_strdup(fontname);
1462 gq_gtk_widget_destroy(GTK_WIDGET(widget));
1465 static void font_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1469 g_free(c_options->image_overlay.font);
1470 c_options->image_overlay.font = g_strdup(options->image_overlay.font);
1472 if (response_id == GTK_RESPONSE_OK)
1474 font = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(dialog));
1475 g_free(c_options->image_overlay.font);
1476 c_options->image_overlay.font = g_strdup(font);
1480 g_object_unref(dialog);
1483 static void image_overlay_set_font_cb(GtkWidget *widget, gpointer)
1487 dialog = gtk_font_chooser_dialog_new("Image Overlay Font", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1488 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1489 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), options->image_overlay.font);
1491 g_signal_connect(dialog, "font-activated", G_CALLBACK(font_activated_cb), nullptr);
1492 g_signal_connect(dialog, "response", G_CALLBACK(font_response_cb), nullptr);
1494 gtk_widget_show(dialog);
1497 static void text_color_activated_cb(GtkColorChooser *chooser, GdkRGBA *color, gpointer)
1499 c_options->image_overlay.text_red = color->red * 255;
1500 c_options->image_overlay.text_green = color->green * 255;
1501 c_options->image_overlay.text_blue = color->blue * 255;
1502 c_options->image_overlay.text_alpha = color->alpha * 255;
1504 gq_gtk_widget_destroy(GTK_WIDGET(chooser));
1507 static void text_color_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1511 c_options->image_overlay.text_red = options->image_overlay.text_red;
1512 c_options->image_overlay.text_green = options->image_overlay.text_green;
1513 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
1514 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
1516 if (response_id == GTK_RESPONSE_OK)
1518 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1519 c_options->image_overlay.text_red = color.red * 255;
1520 c_options->image_overlay.text_green = color.green * 255;
1521 c_options->image_overlay.text_blue = color.blue * 255;
1522 c_options->image_overlay.text_alpha = color.alpha * 255;
1525 g_object_unref(dialog);
1528 static void image_overlay_set_text_color_cb(GtkWidget *widget, gpointer)
1533 dialog = gtk_color_chooser_dialog_new(_("Image Overlay Text Color"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1534 color.red = options->image_overlay.text_red;
1535 color.green = options->image_overlay.text_green;
1536 color.blue = options->image_overlay.text_blue;
1537 color.alpha = options->image_overlay.text_alpha;
1538 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1539 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1541 g_signal_connect(dialog, "color-activated", G_CALLBACK(text_color_activated_cb), nullptr);
1542 g_signal_connect(dialog, "response", G_CALLBACK(text_color_response_cb), nullptr);
1544 gtk_widget_show(dialog);
1547 static void bg_color_activated_cb(GtkColorChooser *chooser, GdkRGBA *color, gpointer)
1549 c_options->image_overlay.background_red = color->red * 255;
1550 c_options->image_overlay.background_green = color->green * 255;
1551 c_options->image_overlay.background_blue = color->blue * 255;
1552 c_options->image_overlay.background_alpha = color->alpha * 255;
1554 gq_gtk_widget_destroy(GTK_WIDGET(chooser));
1557 static void bg_color_response_cb(GtkDialog *dialog, gint response_id, gpointer)
1561 c_options->image_overlay.background_red = options->image_overlay.background_red;
1562 c_options->image_overlay.background_green = options->image_overlay.background_green;
1563 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
1564 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
1566 if (response_id == GTK_RESPONSE_OK)
1568 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1569 c_options->image_overlay.background_red = color.red * 255;
1570 c_options->image_overlay.background_green = color.green * 255;
1571 c_options->image_overlay.background_blue = color.blue * 255;
1572 c_options->image_overlay.background_alpha = color.alpha * 255;
1574 gq_gtk_widget_destroy(GTK_WIDGET(dialog));
1577 static void image_overlay_set_background_color_cb(GtkWidget *widget, gpointer)
1582 dialog = gtk_color_chooser_dialog_new(_("Image Overlay Background Color"), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1583 color.red = options->image_overlay.background_red;
1584 color.green = options->image_overlay.background_green;
1585 color.blue = options->image_overlay.background_blue;
1586 color.alpha = options->image_overlay.background_alpha;
1587 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &color);
1588 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1590 g_signal_connect(dialog, "color-activated", G_CALLBACK(bg_color_activated_cb), nullptr);
1591 g_signal_connect(dialog, "response", G_CALLBACK(bg_color_response_cb), nullptr);
1593 gtk_widget_show(dialog);
1596 static void accel_store_populate()
1599 GList *groups, *actions;
1601 const gchar *accel_path;
1605 if (!accel_store || !layout_window_list || !layout_window_list->data) return;
1607 gtk_tree_store_clear(accel_store);
1608 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 */
1610 g_assert(lw && lw->ui_manager);
1611 groups = gtk_ui_manager_get_action_groups(lw->ui_manager);
1614 actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
1617 action = GTK_ACTION(actions->data);
1618 accel_path = gtk_action_get_accel_path(action);
1619 if (accel_path && gtk_accel_map_lookup_entry(accel_path, &key))
1621 gchar *label, *label2, *tooltip, *accel;
1622 g_object_get(action,
1623 "tooltip", &tooltip,
1627 if (pango_parse_markup(label, -1, '_', nullptr, &label2, nullptr, nullptr) && label2)
1633 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1637 gtk_tree_store_append(accel_store, &iter, nullptr);
1638 gtk_tree_store_set(accel_store, &iter,
1641 AE_TOOLTIP, tooltip ? tooltip : "",
1642 AE_ACCEL, accel_path,
1650 actions = actions->next;
1653 groups = groups->next;
1657 static void accel_store_cleared_cb(GtkCellRendererAccel *, gchar *, gpointer)
1662 static gboolean accel_remove_key_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer data)
1664 auto accel1 = static_cast<gchar *>(data);
1669 gtk_tree_model_get(model, iter, AE_KEY, &accel2, -1);
1671 gtk_accelerator_parse(accel1, &key1.accel_key, &key1.accel_mods);
1672 gtk_accelerator_parse(accel2, &key2.accel_key, &key2.accel_mods);
1674 if (key1.accel_key == key2.accel_key && key1.accel_mods == key2.accel_mods)
1676 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1677 DEBUG_1("accelerator key '%s' is already used, removing.", accel1);
1686 static void accel_store_edited_cb(GtkCellRendererAccel *, gchar *path_string, guint accel_key, GdkModifierType accel_mods, guint, gpointer)
1688 auto model = reinterpret_cast<GtkTreeModel *>(accel_store);
1692 GtkAccelKey old_key, key;
1693 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1695 gtk_tree_model_get_iter(model, &iter, path);
1696 gtk_tree_model_get(model, &iter, AE_ACCEL, &accel_path, -1);
1698 /* test if the accelerator can be stored without conflicts*/
1699 gtk_accel_map_lookup_entry(accel_path, &old_key);
1701 /* change the key and read it back (change may fail on keys hardcoded in gtk)*/
1702 gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, TRUE);
1703 gtk_accel_map_lookup_entry(accel_path, &key);
1705 /* restore the original for now, the key will be really changed when the changes are confirmed */
1706 gtk_accel_map_change_entry(accel_path, old_key.accel_key, old_key.accel_mods, TRUE);
1708 acc = gtk_accelerator_name(key.accel_key, key.accel_mods);
1709 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, acc);
1711 gtk_tree_store_set(accel_store, &iter, AE_KEY, acc, -1);
1712 gtk_tree_path_free(path);
1716 static gboolean accel_default_scroll(GtkTreeView *data)
1720 GtkTreeViewColumn *column;
1722 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(accel_store), &iter);
1723 path = gtk_tree_model_get_path(GTK_TREE_MODEL(accel_store), &iter);
1724 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1726 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1730 gtk_tree_path_free(path);
1732 return(G_SOURCE_REMOVE);
1735 static void accel_default_cb(GtkWidget *, gpointer data)
1737 accel_store_populate();
1739 g_idle_add(reinterpret_cast<GSourceFunc>(accel_default_scroll), data);
1742 void accel_clear_selection(GtkTreeModel *, GtkTreePath *, GtkTreeIter *iter, gpointer)
1744 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1747 void accel_reset_selection(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
1750 gchar *accel_path, *accel;
1752 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, -1);
1753 gtk_accel_map_lookup_entry(accel_path, &key);
1754 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1756 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, accel);
1758 gtk_tree_store_set(accel_store, iter, AE_KEY, accel, -1);
1763 static void accel_clear_cb(GtkWidget *, gpointer data)
1765 GtkTreeSelection *selection;
1767 if (!accel_store) return;
1768 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1769 gtk_tree_selection_selected_foreach(selection, &accel_clear_selection, nullptr);
1772 static void accel_reset_cb(GtkWidget *, gpointer data)
1774 GtkTreeSelection *selection;
1776 if (!accel_store) return;
1777 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1778 gtk_tree_selection_selected_foreach(selection, &accel_reset_selection, nullptr);
1783 static GtkWidget *scrolled_notebook_page(GtkWidget *notebook, const gchar *title)
1787 GtkWidget *scrolled;
1788 GtkWidget *viewport;
1790 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
1791 gtk_container_set_border_width(GTK_CONTAINER(scrolled), PREF_PAD_BORDER);
1792 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1793 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1794 label = gtk_label_new(title);
1795 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, label);
1796 gtk_widget_show(scrolled);
1798 viewport = gtk_viewport_new(nullptr, nullptr);
1799 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1800 gq_gtk_container_add(GTK_WIDGET(scrolled), viewport);
1801 gtk_widget_show(viewport);
1803 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1804 gq_gtk_container_add(GTK_WIDGET(viewport), vbox);
1805 gtk_widget_show(vbox);
1810 static void cache_standard_cb(GtkWidget *widget, gpointer)
1812 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1814 c_options->thumbnails.spec_standard =TRUE;
1815 c_options->thumbnails.cache_into_dirs = FALSE;
1819 static void cache_geeqie_cb(GtkWidget *widget, gpointer)
1821 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1823 c_options->thumbnails.spec_standard =FALSE;
1824 c_options->thumbnails.cache_into_dirs = FALSE;
1828 static void cache_local_cb(GtkWidget *widget, gpointer)
1830 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1832 c_options->thumbnails.cache_into_dirs = TRUE;
1833 c_options->thumbnails.spec_standard =FALSE;
1837 static void help_search_engine_entry_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1839 if (pos == GTK_ENTRY_ICON_PRIMARY)
1841 gq_gtk_entry_set_text(GTK_ENTRY(userdata), HELP_SEARCH_ENGINE);
1845 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "");
1849 static void star_rating_star_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1851 gchar *rating_symbol;
1853 if (pos == GTK_ENTRY_ICON_PRIMARY)
1855 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_STAR);
1856 gq_gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1857 g_free(rating_symbol);
1861 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1862 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1863 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1867 static void star_rating_rejected_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1869 gchar *rating_symbol;
1871 if (pos == GTK_ENTRY_ICON_PRIMARY)
1873 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_REJECTED);
1874 gq_gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1875 g_free(rating_symbol);
1879 gq_gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1880 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1881 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1885 static guint star_rating_symbol_test(GtkWidget *, gpointer data)
1887 auto hbox = static_cast<GtkContainer *>(data);
1888 GString *str = g_string_new(nullptr);
1889 GtkEntry *hex_code_entry;
1890 gchar *hex_code_full;
1893 guint64 hex_value = 0;
1895 list = gtk_container_get_children(hbox);
1897 hex_code_entry = static_cast<GtkEntry *>(g_list_nth_data(list, 2));
1898 hex_code_full = g_strdup(gq_gtk_entry_get_text(hex_code_entry));
1900 hex_code = g_strsplit(hex_code_full, "+", 2);
1901 if (hex_code[0] && hex_code[1])
1903 hex_value = strtoull(hex_code[1], nullptr, 16);
1905 if (!hex_value || hex_value > 0x10FFFF)
1907 hex_value = 0x003F; // Unicode 'Question Mark'
1909 str = g_string_append_unichar(str, static_cast<gunichar>(hex_value));
1910 gtk_label_set_text(static_cast<GtkLabel *>(g_list_nth_data(list, 1)), str->str);
1912 g_strfreev(hex_code);
1913 g_string_free(str, TRUE);
1914 g_free(hex_code_full);
1919 static void star_rating_star_test_cb(GtkWidget *widget, gpointer data)
1921 guint64 star_symbol;
1923 star_symbol = star_rating_symbol_test(widget, data);
1924 c_options->star_rating.star = star_symbol;
1927 static void star_rating_rejected_test_cb(GtkWidget *widget, gpointer data)
1929 guint64 rejected_symbol;
1931 rejected_symbol = star_rating_symbol_test(widget, data);
1932 c_options->star_rating.rejected = rejected_symbol;
1935 /* general options tab */
1936 static void timezone_database_install_cb(GtkWidget *widget, gpointer data);
1940 GCancellable *cancellable;
1942 GtkWidget *progress;
1944 GFile *timezone_database_gq;
1945 gchar *timezone_database_user;
1948 static void config_tab_general(GtkWidget *notebook)
1953 GtkWidget *group_frame;
1954 GtkWidget *subgroup;
1956 GtkWidget *ct_button;
1959 gint hours, minutes, remainder;
1961 GtkWidget *star_rating_entry;
1963 gchar *rating_symbol;
1966 gchar *download_locn;
1967 GNetworkMonitor *net_mon;
1968 GSocketConnectable *tz_org;
1969 gboolean internet_available = FALSE;
1972 vbox = scrolled_notebook_page(notebook, _("General"));
1974 group = pref_group_new(vbox, FALSE, _("Thumbnails"), GTK_ORIENTATION_VERTICAL);
1976 table = pref_table_new(group, 2, 2, FALSE, FALSE);
1977 add_thumb_size_menu(table, 0, 0, _("Size:"));
1978 add_quality_menu(table, 0, 1, _("Quality:"), options->thumbnails.quality, &c_options->thumbnails.quality);
1980 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1981 pref_label_new(hbox, _("Custom size: "));
1982 pref_spin_new_int(hbox, _("Width:"), nullptr, 1, 512, 1, options->thumbnails.max_width, &c_options->thumbnails.max_width);
1983 pref_spin_new_int(hbox, _("Height:"), nullptr, 1, 512, 1, options->thumbnails.max_height, &c_options->thumbnails.max_height);
1985 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails and sim. files"),
1986 options->thumbnails.enable_caching, &c_options->thumbnails.enable_caching);
1988 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1989 pref_checkbox_link_sensitivity(ct_button, subgroup);
1991 c_options->thumbnails.spec_standard = options->thumbnails.spec_standard;
1992 c_options->thumbnails.cache_into_dirs = options->thumbnails.cache_into_dirs;
1993 group_frame = pref_frame_new(subgroup, TRUE, _("Use Geeqie thumbnail style and cache"),
1994 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1995 button = pref_radiobutton_new(group_frame, nullptr, get_thumbnails_cache_dir(),
1996 !options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
1997 G_CALLBACK(cache_geeqie_cb), nullptr);
1999 group_frame = pref_frame_new(subgroup, TRUE,
2000 _("Store thumbnails local to image folder (non-standard)"),
2001 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2002 pref_radiobutton_new(group_frame, button, "*/.thumbnails",
2003 !options->thumbnails.spec_standard && options->thumbnails.cache_into_dirs,
2004 G_CALLBACK(cache_local_cb), nullptr);
2006 group_frame = pref_frame_new(subgroup, TRUE,
2007 _("Use standard thumbnail style and cache, shared with other applications"),
2008 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2009 pref_radiobutton_new(group_frame, button, get_thumbnails_standard_cache_dir(),
2010 options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2011 G_CALLBACK(cache_standard_cb), nullptr);
2013 pref_checkbox_new_int(group, _("Use EXIF thumbnails when available (EXIF thumbnails may be outdated)"),
2014 options->thumbnails.use_exif, &c_options->thumbnails.use_exif);
2016 pref_checkbox_new_int(group, _("Thumbnail color management"),
2017 options->thumbnails.use_color_management, &c_options->thumbnails.use_color_management);
2019 spin = pref_spin_new_int(group, _("Collection preview:"), nullptr,
2021 options->thumbnails.collection_preview, &c_options->thumbnails.collection_preview);
2022 gtk_widget_set_tooltip_text(spin, _("The maximum number of thumbnails shown in a Collection preview montage"));
2024 #ifdef HAVE_FFMPEGTHUMBNAILER_METADATA
2025 pref_checkbox_new_int(group, _("Use embedded metadata in video files as thumbnails when available"),
2026 options->thumbnails.use_ft_metadata, &c_options->thumbnails.use_ft_metadata);
2029 pref_spacer(group, PREF_PAD_GROUP);
2031 group = pref_group_new(vbox, FALSE, _("Star Rating"), GTK_ORIENTATION_VERTICAL);
2033 c_options->star_rating.star = options->star_rating.star;
2034 c_options->star_rating.rejected = options->star_rating.rejected;
2036 str = g_string_new(nullptr);
2037 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2038 pref_label_new(hbox, _("Star character: "));
2039 str = g_string_append_unichar(str, options->star_rating.star);
2040 pref_label_new(hbox, g_strdup(str->str));
2041 rating_symbol = g_strdup_printf("U+%X", options->star_rating.star);
2042 star_rating_entry = gtk_entry_new();
2043 gq_gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2044 gq_gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2045 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2046 gtk_widget_show(star_rating_entry);
2047 button = pref_button_new(nullptr, nullptr, _("Set"),
2048 G_CALLBACK(star_rating_star_test_cb), hbox);
2049 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2050 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2051 gtk_widget_show(button);
2052 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."));
2053 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2054 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2055 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2056 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2057 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2058 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2059 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2060 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2061 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2062 G_CALLBACK(star_rating_star_icon_cb),
2065 g_string_free(str, TRUE);
2066 g_free(rating_symbol);
2068 str = g_string_new(nullptr);
2069 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2070 pref_label_new(hbox, _("Rejected character: "));
2071 str = g_string_append_unichar(str, options->star_rating.rejected);
2072 pref_label_new(hbox, g_strdup(str->str));
2073 rating_symbol = g_strdup_printf("U+%X", options->star_rating.rejected);
2074 star_rating_entry = gtk_entry_new();
2075 gq_gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2076 gq_gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2077 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2078 gtk_widget_show(star_rating_entry);
2079 button = pref_button_new(nullptr, nullptr, _("Set"),
2080 G_CALLBACK(star_rating_rejected_test_cb), hbox);
2081 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2082 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2083 gtk_widget_show(button);
2084 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."));
2085 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2086 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2087 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2088 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2089 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2090 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2091 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2092 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2093 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2094 G_CALLBACK(star_rating_rejected_icon_cb),
2097 g_string_free(str, TRUE);
2098 g_free(rating_symbol);
2100 pref_spacer(group, PREF_PAD_GROUP);
2102 group = pref_group_new(vbox, FALSE, _("Slide show"), GTK_ORIENTATION_VERTICAL);
2104 c_options->slideshow.delay = options->slideshow.delay;
2105 hours = options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2106 remainder = options->slideshow.delay % (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2107 minutes = remainder / (60 * SLIDESHOW_SUBSECOND_PRECISION);
2108 seconds = static_cast<gdouble>(remainder % (60 * SLIDESHOW_SUBSECOND_PRECISION)) /
2109 SLIDESHOW_SUBSECOND_PRECISION;
2111 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2113 spin = pref_spin_new(hbox, _("Delay between image change hrs:mins:secs.dec"), nullptr,
2115 options->slideshow.delay ? hours : 0.0,
2116 G_CALLBACK(slideshow_delay_hours_cb), nullptr);
2117 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2118 spin = pref_spin_new(hbox, ":" , nullptr,
2120 options->slideshow.delay ? minutes: 0.0,
2121 G_CALLBACK(slideshow_delay_minutes_cb), nullptr);
2122 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2123 spin = pref_spin_new(hbox, ":", nullptr,
2124 SLIDESHOW_MIN_SECONDS, 59, 1.0, 1,
2125 options->slideshow.delay ? seconds : 10.0,
2126 G_CALLBACK(slideshow_delay_seconds_cb), nullptr);
2127 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2129 pref_checkbox_new_int(group, _("Random"), options->slideshow.random, &c_options->slideshow.random);
2130 pref_checkbox_new_int(group, _("Repeat"), options->slideshow.repeat, &c_options->slideshow.repeat);
2132 pref_spacer(group, PREF_PAD_GROUP);
2134 group = pref_group_new(vbox, FALSE, _("Image loading and caching"), GTK_ORIENTATION_VERTICAL);
2136 pref_spin_new_int(group, _("Decoded image cache size (MiB):"), nullptr,
2137 0, 99999, 1, options->image.image_cache_max, &c_options->image.image_cache_max);
2138 pref_checkbox_new_int(group, _("Preload next image"),
2139 options->image.enable_read_ahead, &c_options->image.enable_read_ahead);
2141 pref_checkbox_new_int(group, _("Refresh on file change"),
2142 options->update_on_time_change, &c_options->update_on_time_change);
2145 pref_spacer(group, PREF_PAD_GROUP);
2147 group = pref_group_new(vbox, FALSE, _("Menu style"), GTK_ORIENTATION_VERTICAL);
2149 pref_checkbox_new_int(group, _("☰ style menu button (NOTE! Geeqie must be restarted for change to take effect)"),
2150 options->hamburger_menu, &c_options->hamburger_menu);
2151 gtk_widget_set_tooltip_text(group, _("Use a ☰ style menu button instead of the classic style across the top of the frame"));
2153 pref_spacer(group, PREF_PAD_GROUP);
2155 group = pref_group_new(vbox, FALSE, _("Expand toolbar"), GTK_ORIENTATION_VERTICAL);
2157 pref_checkbox_new_int(group, _("Expand menu/toolbar (NOTE! Geeqie must be restarted for change to take effect)"),
2158 options->expand_menu_toolbar, &c_options->expand_menu_toolbar);
2159 gtk_widget_set_tooltip_text(group, _("Expand the menu/toolbar to the full width of the window"));
2161 pref_spacer(group, PREF_PAD_GROUP);
2163 group = pref_group_new(vbox, FALSE, _("Hide Selectable Bars"), GTK_ORIENTATION_VERTICAL);
2165 pref_checkbox_new_int(group, _("Menu bar"),
2166 options->selectable_bars.menu_bar, &c_options->selectable_bars.menu_bar);
2168 pref_checkbox_new_int(group, _("Tool bar"),
2169 options->selectable_bars.tool_bar, &c_options->selectable_bars.tool_bar);
2171 pref_checkbox_new_int(group, _("Status bar"),
2172 options->selectable_bars.status_bar, &c_options->selectable_bars.status_bar);
2173 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"));
2175 pref_spacer(group, PREF_PAD_GROUP);
2177 if (g_getenv("APPDIR") && strstr(g_getenv("APPDIR"), "/tmp/.mount_Geeqie"))
2179 group = pref_group_new(vbox, FALSE, _("AppImage updates notifications"), GTK_ORIENTATION_VERTICAL);
2180 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2181 pref_checkbox_new_int(group, _("Enable"), options->appimage_notifications, &c_options->appimage_notifications);
2182 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"));
2184 pref_spacer(group, PREF_PAD_GROUP);
2188 net_mon = g_network_monitor_get_default();
2189 tz_org = g_network_address_parse_uri(TIMEZONE_DATABASE_WEB, 80, nullptr);
2192 internet_available = g_network_monitor_can_reach(net_mon, tz_org, nullptr, nullptr);
2193 g_object_unref(tz_org);
2196 group = pref_group_new(vbox, FALSE, _("Timezone database"), GTK_ORIENTATION_VERTICAL);
2197 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2199 if (!internet_available)
2201 gtk_widget_set_sensitive(group, FALSE);
2204 tz = g_new0(TZData, 1);
2206 path = path_from_utf8(TIMEZONE_DATABASE_WEB);
2207 basename = g_path_get_basename(path);
2208 tz->timezone_database_user = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
2212 if (isfile(tz->timezone_database_user))
2214 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Update"), G_CALLBACK(timezone_database_install_cb), tz);
2218 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Install"), G_CALLBACK(timezone_database_install_cb), tz);
2221 download_locn = g_strconcat(_("Download database from: "), TIMEZONE_DATABASE_WEB, NULL);
2222 pref_label_new(GTK_WIDGET(hbox), download_locn);
2223 g_free(download_locn);
2225 if (!internet_available)
2227 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"));
2231 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"));
2233 gtk_widget_show(button);
2235 pref_spacer(group, PREF_PAD_GROUP);
2237 group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL);
2239 help_search_engine_entry = gtk_entry_new();
2240 gq_gtk_entry_set_text(GTK_ENTRY(help_search_engine_entry), options->help_search_engine);
2241 gq_gtk_box_pack_start(GTK_BOX(group), help_search_engine_entry, FALSE, FALSE, 0);
2242 gtk_widget_show(help_search_engine_entry);
2244 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"));
2246 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2247 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2248 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2249 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2250 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2251 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2252 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2253 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2254 g_signal_connect(GTK_ENTRY(help_search_engine_entry), "icon-press",
2255 G_CALLBACK(help_search_engine_entry_icon_cb),
2256 help_search_engine_entry);
2260 static void config_tab_image(GtkWidget *notebook)
2265 GtkWidget *ct_button;
2266 GtkWidget *enlargement_button;
2270 vbox = scrolled_notebook_page(notebook, _("Image"));
2272 group = pref_group_new(vbox, FALSE, _("Zoom"), GTK_ORIENTATION_VERTICAL);
2274 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2275 add_quality_menu(table, 0, 0, _("Quality:"), options->image.zoom_quality, &c_options->image.zoom_quality);
2277 pref_checkbox_new_int(group, _("Two pass rendering (apply HQ zoom and color correction in second pass)"),
2278 options->image.zoom_2pass, &c_options->image.zoom_2pass);
2280 c_options->image.zoom_increment = options->image.zoom_increment;
2281 spin = pref_spin_new(group, _("Zoom increment:"), nullptr,
2282 0.01, 4.0, 0.01, 2, static_cast<gdouble>(options->image.zoom_increment) / 100.0,
2283 G_CALLBACK(zoom_increment_cb), nullptr);
2284 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2286 c_options->image.zoom_style = options->image.zoom_style;
2287 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2288 add_zoom_style_selection_menu(table, 0, 0, _("Zoom style:"), options->image.zoom_style, &c_options->image.zoom_style);
2290 group = pref_group_new(vbox, FALSE, _("Fit image to window"), GTK_ORIENTATION_VERTICAL);
2292 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2293 enlargement_button = pref_checkbox_new_int(hbox, _("Allow enlargement of image (max. size in %)"),
2294 options->image.zoom_to_fit_allow_expand, &c_options->image.zoom_to_fit_allow_expand);
2295 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2297 options->image.max_enlargement_size, &c_options->image.max_enlargement_size);
2298 pref_checkbox_link_sensitivity(enlargement_button, spin);
2299 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."));
2301 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2302 ct_button = pref_checkbox_new_int(hbox, _("Virtual window size (% of actual window):"),
2303 options->image.limit_autofit_size, &c_options->image.limit_autofit_size);
2304 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2306 options->image.max_autofit_size, &c_options->image.max_autofit_size);
2307 pref_checkbox_link_sensitivity(ct_button, spin);
2308 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."));
2310 group = pref_group_new(vbox, FALSE, _("Tile size"), GTK_ORIENTATION_VERTICAL);
2312 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2313 spin = pref_spin_new_int(hbox, _("Pixels"), _("(Requires restart)"),
2315 options->image.tile_size, &c_options->image.tile_size);
2316 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."));
2318 group = pref_group_new(vbox, FALSE, _("Appearance"), GTK_ORIENTATION_VERTICAL);
2320 pref_checkbox_new_int(group, _("Use custom border color in window mode"),
2321 options->image.use_custom_border_color, &c_options->image.use_custom_border_color);
2323 pref_checkbox_new_int(group, _("Use custom border color in fullscreen mode"),
2324 options->image.use_custom_border_color_in_fullscreen, &c_options->image.use_custom_border_color_in_fullscreen);
2326 pref_color_button_new(group, _("Border color"), &options->image.border_color,
2327 G_CALLBACK(pref_color_button_set_cb), &c_options->image.border_color);
2329 c_options->image.border_color = options->image.border_color;
2331 pref_color_button_new(group, _("Alpha channel color 1"), &options->image.alpha_color_1,
2332 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_1);
2334 pref_color_button_new(group, _("Alpha channel color 2"), &options->image.alpha_color_2,
2335 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_2);
2337 c_options->image.alpha_color_1 = options->image.alpha_color_1;
2338 c_options->image.alpha_color_2 = options->image.alpha_color_2;
2343 static void save_default_window_layout_cb(GtkWidget *, gpointer)
2345 LayoutWindow *lw = nullptr;
2346 gchar *default_path;
2349 /* Get current lw */
2352 tmp_id = lw->options.id;
2353 lw->options.id = g_strdup("");
2355 default_path = g_build_filename(get_rc_dir(), DEFAULT_WINDOW_LAYOUT, NULL);
2356 save_default_layout_options_to_file(default_path, options, lw);
2357 g_free(lw->options.id);
2358 lw->options.id = tmp_id;
2359 g_free(default_path);
2362 static gboolean popover_cb(gpointer data)
2364 auto popover = static_cast<GtkPopover *>(data);
2366 gtk_popover_popdown(popover);
2371 static void default_layout_changed_cb(GtkWidget *, GtkPopover *popover)
2373 gtk_popover_popup(popover);
2375 g_timeout_add(2000, popover_cb, popover);
2378 static GtkWidget *create_popover(GtkWidget *parent, GtkWidget *child, GtkPositionType pos)
2382 popover = gtk_popover_new(parent);
2383 gtk_popover_set_position(GTK_POPOVER (popover), pos);
2384 gq_gtk_container_add(GTK_WIDGET(popover), child);
2385 gtk_container_set_border_width(GTK_CONTAINER (popover), 6);
2386 gtk_widget_show (child);
2391 static void config_tab_windows(GtkWidget *notebook)
2396 GtkWidget *subgroup;
2398 GtkWidget *ct_button;
2401 vbox = scrolled_notebook_page(notebook, _("Windows"));
2403 group = pref_group_new(vbox, FALSE, _("State"), GTK_ORIENTATION_VERTICAL);
2405 ct_button = pref_checkbox_new_int(group, _("Remember session"),
2406 options->save_window_positions, &c_options->save_window_positions);
2408 button = pref_checkbox_new_int(group, _("Use saved window positions also for new windows"),
2409 options->use_saved_window_positions_for_new_windows, &c_options->use_saved_window_positions_for_new_windows);
2410 pref_checkbox_link_sensitivity(ct_button, button);
2412 button = pref_checkbox_new_int(group, _("Remember window workspace"),
2413 options->save_window_workspace, &c_options->save_window_workspace);
2414 pref_checkbox_link_sensitivity(ct_button, button);
2416 pref_checkbox_new_int(group, _("Remember tool state (float/hidden)"),
2417 options->tools_restore_state, &c_options->tools_restore_state);
2419 pref_checkbox_new_int(group, _("Remember dialog window positions"),
2420 options->save_dialog_window_positions, &c_options->save_dialog_window_positions);
2422 pref_checkbox_new_int(group, _("Show window IDs"),
2423 options->show_window_ids, &c_options->show_window_ids);
2425 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2426 pref_label_new(subgroup, _("Use current layout for default: "));
2427 button = pref_button_new(subgroup, nullptr, _("Set"), G_CALLBACK(save_default_window_layout_cb), nullptr);
2431 popover = create_popover(button, gtk_label_new(_("Current window layout\nhas been set as default")), GTK_POS_TOP);
2432 gtk_popover_set_modal(GTK_POPOVER (popover), FALSE);
2433 g_signal_connect(button, "clicked", G_CALLBACK(default_layout_changed_cb), popover);
2435 group = pref_group_new(vbox, FALSE, _("Size"), GTK_ORIENTATION_VERTICAL);
2437 pref_checkbox_new_int(group, _("Fit window to image when tools are hidden/floating"),
2438 options->image.fit_window_to_image, &c_options->image.fit_window_to_image);
2440 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2441 ct_button = pref_checkbox_new_int(hbox, _("Limit size when auto-sizing window (%):"),
2442 options->image.limit_window_size, &c_options->image.limit_window_size);
2443 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2445 options->image.max_window_size, &c_options->image.max_window_size);
2446 pref_checkbox_link_sensitivity(ct_button, spin);
2448 group = pref_group_new(vbox, FALSE, _("Full screen"), GTK_ORIENTATION_VERTICAL);
2450 c_options->fullscreen.screen = options->fullscreen.screen;
2451 c_options->fullscreen.above = options->fullscreen.above;
2452 hbox = fullscreen_prefs_selection_new(_("Location:"), &c_options->fullscreen.screen, &c_options->fullscreen.above);
2453 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2454 gtk_widget_show(hbox);
2456 pref_checkbox_new_int(group, _("Smooth image flip"),
2457 options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
2458 pref_checkbox_new_int(group, _("Disable screen saver"),
2459 options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
2462 #define PRE_FORMATTED_COLUMNS 5
2463 static void config_tab_osd(GtkWidget *notebook)
2469 GtkWidget *image_overlay_template_view;
2470 GtkWidget *scrolled;
2471 GtkWidget *scrolled_pre_formatted;
2472 GtkTextBuffer *buffer;
2474 GtkWidget *subgroup;
2476 vbox = scrolled_notebook_page(notebook, _("OSD"));
2478 image_overlay_template_view = gtk_text_view_new();
2480 group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
2482 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2484 scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
2485 gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
2486 gq_gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
2487 gtk_widget_show(scrolled_pre_formatted);
2488 gtk_widget_show(subgroup);
2490 pref_line(group, PREF_PAD_GAP);
2492 pref_label_new(group, _("Image overlay template"));
2494 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2495 gtk_widget_set_size_request(scrolled, 200, 150);
2496 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2497 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
2498 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2499 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
2500 gtk_widget_show(scrolled);
2502 gtk_widget_set_tooltip_markup(image_overlay_template_view,
2503 _("Extensive formatting options are shown in the Help file"));
2505 gq_gtk_container_add(GTK_WIDGET(scrolled), image_overlay_template_view);
2506 gtk_widget_show(image_overlay_template_view);
2508 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2510 button = pref_button_new(nullptr, GQ_ICON_SELECT_FONT, _("Font"),
2511 G_CALLBACK(image_overlay_set_font_cb), notebook);
2513 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2514 gtk_widget_show(button);
2516 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Text"), G_CALLBACK(image_overlay_set_text_color_cb), nullptr);
2517 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2518 gtk_widget_show(button);
2520 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Background"), G_CALLBACK(image_overlay_set_background_color_cb), nullptr);
2521 gq_gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2522 gtk_widget_show(button);
2523 image_overlay_set_text_colors();
2525 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2526 G_CALLBACK(image_overlay_default_template_cb), image_overlay_template_view);
2527 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2528 gtk_widget_show(button);
2530 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
2531 G_CALLBACK(image_overlay_help_cb), nullptr);
2532 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2533 gtk_widget_show(button);
2535 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_overlay_template_view));
2536 if (options->image_overlay.template_string) gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
2537 g_signal_connect(G_OBJECT(buffer), "changed",
2538 G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
2540 pref_line(group, PREF_PAD_GAP);
2542 group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL);
2543 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2544 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2545 gtk_widget_show(hbox);
2546 label = gtk_label_new(_("%Exif.Image.Orientation%"));
2547 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2548 gtk_widget_show(label);
2549 pref_spacer(group,TRUE);
2551 group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL);
2552 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2553 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2554 gtk_widget_show(hbox);
2555 label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
2556 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2557 gtk_widget_show(label);
2558 pref_spacer(group,TRUE);
2560 group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL);
2561 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2562 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2563 gtk_widget_show(hbox);
2564 label = gtk_label_new(_("%path:39%"));
2565 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2566 gtk_widget_show(label);
2567 pref_spacer(group,TRUE);
2569 group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL);
2570 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2571 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2572 gtk_widget_show(hbox);
2573 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%"));
2574 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2575 gtk_widget_show(label);
2576 pref_spacer(group,TRUE);
2578 group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL);
2579 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2580 gq_gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2581 gtk_widget_show(hbox);
2582 label = gtk_label_new(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
2583 gq_gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2584 gtk_widget_show(label);
2587 static GtkTreeModel *create_class_model()
2589 GtkListStore *model;
2593 /* create list store */
2594 model = gtk_list_store_new(1, G_TYPE_STRING);
2595 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
2597 gtk_list_store_append(model, &iter);
2598 gtk_list_store_set(model, &iter, 0, _(format_class_list[i]), -1);
2600 return GTK_TREE_MODEL (model);
2605 static gint filter_table_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
2607 gint n = GPOINTER_TO_INT(data);
2609 FilterEntry *filter_a;
2610 FilterEntry *filter_b;
2612 gtk_tree_model_get(model, a, 0, &filter_a, -1);
2613 gtk_tree_model_get(model, b, 0, &filter_b, -1);
2617 case FILETYPES_COLUMN_ENABLED:
2619 ret = filter_a->enabled - filter_b->enabled;
2622 case FILETYPES_COLUMN_FILTER:
2624 ret = g_utf8_collate(filter_a->extensions, filter_b->extensions);
2627 case FILETYPES_COLUMN_DESCRIPTION:
2629 ret = g_utf8_collate(filter_a->description, filter_b->description);
2632 case FILETYPES_COLUMN_CLASS:
2634 ret = g_strcmp0(format_class_list[filter_a->file_class], format_class_list[filter_b->file_class]);
2637 case FILETYPES_COLUMN_WRITABLE:
2639 ret = filter_a->writable - filter_b->writable;
2642 case FILETYPES_COLUMN_SIDECAR:
2644 ret = filter_a->allow_sidecar - filter_b->allow_sidecar;
2648 g_return_val_if_reached(0);
2654 static gboolean search_function_cb(GtkTreeModel *model, gint, const gchar *key, GtkTreeIter *iter, gpointer)
2657 gboolean ret = TRUE;
2659 gtk_tree_model_get(model, iter, 0, &fe, -1);
2661 if (g_strstr_len(fe->extensions, -1, key))
2669 static void config_tab_files(GtkWidget *notebook)
2676 GtkWidget *ct_button;
2677 GtkWidget *scrolled;
2678 GtkWidget *filter_view;
2679 GtkCellRenderer *renderer;
2680 GtkTreeSelection *selection;
2681 GtkTreeViewColumn *column;
2683 vbox = scrolled_notebook_page(notebook, _("File Filters"));
2685 group = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2687 pref_checkbox_new_int(group, _("Show hidden files or folders"),
2688 options->file_filter.show_hidden_files, &c_options->file_filter.show_hidden_files);
2689 pref_checkbox_new_int(group, _("Show parent folder (..)"),
2690 options->file_filter.show_parent_directory, &c_options->file_filter.show_parent_directory);
2691 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);
2692 pref_checkbox_new_int(group, _("Disable file extension checks"),
2693 options->file_filter.disable_file_extension_checks, &c_options->file_filter.disable_file_extension_checks);
2695 ct_button = pref_checkbox_new_int(group, _("Disable File Filtering"),
2696 options->file_filter.disable, &c_options->file_filter.disable);
2699 group = pref_group_new(vbox, FALSE, _("Grouping sidecar extensions"), GTK_ORIENTATION_VERTICAL);
2701 sidecar_ext_entry = gtk_entry_new();
2702 gq_gtk_entry_set_text(GTK_ENTRY(sidecar_ext_entry), options->sidecar.ext);
2703 gq_gtk_box_pack_start(GTK_BOX(group), sidecar_ext_entry, FALSE, FALSE, 0);
2704 gtk_widget_show(sidecar_ext_entry);
2706 group = pref_group_new(vbox, TRUE, _("File types"), GTK_ORIENTATION_VERTICAL);
2708 frame = pref_group_parent(group);
2709 g_signal_connect(G_OBJECT(ct_button), "toggled",
2710 G_CALLBACK(filter_disable_cb), frame);
2711 gtk_widget_set_sensitive(frame, !options->file_filter.disable);
2713 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
2714 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2715 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2716 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2717 gtk_widget_show(scrolled);
2719 filter_store = gtk_list_store_new(1, G_TYPE_POINTER);
2720 filter_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
2721 g_object_unref(filter_store);
2722 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
2723 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2725 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), FALSE);
2727 column = gtk_tree_view_column_new();
2728 gtk_tree_view_column_set_title(column, _("Enabled"));
2729 gtk_tree_view_column_set_resizable(column, TRUE);
2731 renderer = gtk_cell_renderer_toggle_new();
2732 g_signal_connect(G_OBJECT(renderer), "toggled",
2733 G_CALLBACK(filter_store_enable_cb), filter_store);
2734 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2735 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2736 GINT_TO_POINTER(FE_ENABLE), nullptr);
2737 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);
2738 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_ENABLED);
2739 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2741 column = gtk_tree_view_column_new();
2742 gtk_tree_view_column_set_title(column, _("Filter"));
2743 gtk_tree_view_column_set_resizable(column, TRUE);
2744 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);
2745 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_FILTER);
2747 renderer = gtk_cell_renderer_text_new();
2748 g_signal_connect(G_OBJECT(renderer), "edited",
2749 G_CALLBACK(filter_store_ext_edit_cb), filter_store);
2750 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2751 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2752 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2753 GINT_TO_POINTER(FE_EXTENSION), nullptr);
2754 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2756 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), TRUE);
2757 gtk_tree_view_set_search_column(GTK_TREE_VIEW(filter_view), FILETYPES_COLUMN_FILTER);
2758 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(filter_view), search_function_cb, nullptr, nullptr);
2760 column = gtk_tree_view_column_new();
2761 gtk_tree_view_column_set_title(column, _("Description"));
2762 gtk_tree_view_column_set_resizable(column, TRUE);
2763 gtk_tree_view_column_set_fixed_width(column, 200);
2764 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2766 renderer = gtk_cell_renderer_text_new();
2767 g_signal_connect(G_OBJECT(renderer), "edited",
2768 G_CALLBACK(filter_store_desc_edit_cb), filter_store);
2769 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2770 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2771 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2772 GINT_TO_POINTER(FE_DESCRIPTION), nullptr);
2773 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2774 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);
2775 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_DESCRIPTION);
2777 column = gtk_tree_view_column_new();
2778 gtk_tree_view_column_set_title(column, _("Class"));
2779 gtk_tree_view_column_set_resizable(column, TRUE);
2780 renderer = gtk_cell_renderer_combo_new();
2781 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE,
2782 "model", create_class_model(),
2787 g_signal_connect(G_OBJECT(renderer), "edited",
2788 G_CALLBACK(filter_store_class_edit_cb), filter_store);
2789 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2790 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2791 GINT_TO_POINTER(FE_CLASS), nullptr);
2792 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2793 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);
2794 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_CLASS);
2796 column = gtk_tree_view_column_new();
2797 gtk_tree_view_column_set_title(column, _("Writable"));
2798 gtk_tree_view_column_set_resizable(column, FALSE);
2799 renderer = gtk_cell_renderer_toggle_new();
2800 g_signal_connect(G_OBJECT(renderer), "toggled",
2801 G_CALLBACK(filter_store_writable_cb), filter_store);
2802 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2803 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2804 GINT_TO_POINTER(FE_WRITABLE), nullptr);
2805 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2806 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);
2807 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_WRITABLE);
2809 column = gtk_tree_view_column_new();
2810 gtk_tree_view_column_set_title(column, _("Sidecar is allowed"));
2811 gtk_tree_view_column_set_resizable(column, FALSE);
2812 renderer = gtk_cell_renderer_toggle_new();
2813 g_signal_connect(G_OBJECT(renderer), "toggled",
2814 G_CALLBACK(filter_store_sidecar_cb), filter_store);
2815 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2816 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2817 GINT_TO_POINTER(FE_ALLOW_SIDECAR), nullptr);
2818 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2819 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);
2820 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_SIDECAR);
2822 filter_store_populate();
2823 gq_gtk_container_add(GTK_WIDGET(scrolled), filter_view);
2824 gtk_widget_show(filter_view);
2826 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2828 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2829 G_CALLBACK(filter_default_cb), filter_view);
2830 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2831 gtk_widget_show(button);
2833 button = pref_button_new(nullptr, GQ_ICON_REMOVE, _("Remove"),
2834 G_CALLBACK(filter_remove_cb), filter_view);
2835 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2836 gtk_widget_show(button);
2838 button = pref_button_new(nullptr, GQ_ICON_ADD, _("Add"),
2839 G_CALLBACK(filter_add_cb), filter_view);
2840 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2841 gtk_widget_show(button);
2845 static void config_tab_metadata(GtkWidget *notebook)
2850 GtkWidget *ct_button;
2852 GtkWidget *tmp_widget;
2854 GtkWidget *text_label;
2856 vbox = scrolled_notebook_page(notebook, _("Metadata"));
2859 group = pref_group_new(vbox, FALSE, _("Metadata writing sequence"), GTK_ORIENTATION_VERTICAL);
2861 label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2863 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."));
2864 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2865 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2867 gtk_widget_set_tooltip_text(label, _("A flowchart of the sequence is shown in the Help file"));
2869 ct_button = pref_checkbox_new_int(group, "", options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2870 text_label = gtk_bin_get_child(GTK_BIN(ct_button));
2871 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"));
2872 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2874 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)"));
2875 gtk_widget_set_tooltip_markup(ct_button, markup);
2879 gtk_widget_set_sensitive(ct_button, FALSE);
2882 tmp_widget = pref_checkbox_new_int(group, "", options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2883 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2884 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)"));
2885 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2888 label = pref_label_new(group, "");
2889 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(), "/");
2890 gtk_label_set_markup (GTK_LABEL(label), markup);
2893 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2894 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2895 gtk_widget_set_margin_start(label, 22);
2896 pref_spacer(group, PREF_PAD_GROUP);
2898 group = pref_group_new(vbox, FALSE, _("Step 1 Options:"), GTK_ORIENTATION_VERTICAL);
2900 gtk_widget_set_sensitive(group, FALSE);
2903 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2904 pref_checkbox_link_sensitivity(ct_button, hbox);
2906 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);
2907 gtk_widget_set_tooltip_text(tmp_widget, _("A simplified conversion list is in the Help file"));
2909 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);
2911 pref_checkbox_new_int(hbox, _("Ask before writing to image files"), options->metadata.confirm_write, &c_options->metadata.confirm_write);
2913 tmp_widget= pref_checkbox_new_int(hbox, "", options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2914 gtk_widget_set_tooltip_text(tmp_widget, _("This file naming convention is used by Darktable"));
2915 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2916 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", ")");
2917 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2920 pref_spacer(group, PREF_PAD_GROUP);
2922 group = pref_group_new(vbox, FALSE, _("Steps 2 and 3 Option:"), GTK_ORIENTATION_VERTICAL);
2924 gtk_widget_set_sensitive(group, FALSE);
2927 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);
2929 pref_spacer(group, PREF_PAD_GROUP);
2931 group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2932 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);
2933 gtk_widget_set_tooltip_text(tmp_widget, _("See the Help file for a list of the tags used"));
2935 tmp_widget = pref_checkbox_new_int(group, _("Permit Keywords to be case-sensitive"), options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2936 gtk_widget_set_tooltip_text(tmp_widget, _("When selected, \"Place\" and \"place\" are two different keywords"));
2938 ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"), options->metadata.write_orientation, &c_options->metadata.write_orientation);
2939 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"));
2942 gtk_widget_set_sensitive(ct_button, FALSE);
2945 pref_spacer(group, PREF_PAD_GROUP);
2947 group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2949 ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"), options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
2951 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2952 pref_checkbox_link_sensitivity(ct_button, hbox);
2954 pref_spin_new_int(hbox, _("Timeout (seconds):"), nullptr, 0, 900, 1, options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
2956 pref_checkbox_new_int(group, _("Write metadata on image change"), options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
2958 pref_checkbox_new_int(group, _("Write metadata on directory change"), options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
2960 pref_spacer(group, PREF_PAD_GROUP);
2963 group = pref_group_new(vbox, FALSE, _("Spelling checks"), GTK_ORIENTATION_VERTICAL);
2965 ct_button = pref_checkbox_new_int(group, _("Check spelling - Requires restart"), options->metadata.check_spelling, &c_options->metadata.check_spelling);
2966 gtk_widget_set_tooltip_text(ct_button, _("Spelling checks are performed on info sidebar panes Comment, Headline and Title"));
2969 pref_spacer(group, PREF_PAD_GROUP);
2971 group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
2973 ct_button = pref_checkbox_new_int(group, _("Read metadata in background"), options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
2974 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");
2979 struct KeywordFindData
2986 GtkWidget *button_close;
2987 GtkWidget *button_stop;
2988 GtkWidget *button_start;
2989 GtkWidget *progress;
2997 guint idle_id; /* event source id */
3000 #define KEYWORD_DIALOG_WIDTH 400
3002 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
3004 GList *list_d = nullptr;
3005 GList *list_f = nullptr;
3009 filelist_read(dir_fd, &list_f, &list_d);
3013 filelist_read(dir_fd, &list_f, nullptr);
3016 list_f = filelist_filter(list_f, FALSE);
3017 list_d = filelist_filter(list_d, TRUE);
3019 kfd->list = g_list_concat(list_f, kfd->list);
3020 kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
3023 static void keywords_find_reset(KeywordFindData *kfd)
3025 filelist_free(kfd->list);
3026 kfd->list = nullptr;
3028 filelist_free(kfd->list_dir);
3029 kfd->list_dir = nullptr;
3032 static void keywords_find_close_cb(GenericDialog *, gpointer data)
3034 auto kfd = static_cast<KeywordFindData *>(data);
3036 if (!gtk_widget_get_sensitive(kfd->button_close)) return;
3038 keywords_find_reset(kfd);
3039 generic_dialog_close(kfd->gd);
3043 static void keywords_find_finish(KeywordFindData *kfd)
3045 keywords_find_reset(kfd);
3047 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
3048 gtk_spinner_stop(GTK_SPINNER(kfd->spinner));
3050 gtk_widget_set_sensitive(kfd->group, TRUE);
3051 gtk_widget_set_sensitive(kfd->button_start, TRUE);
3052 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3053 gtk_widget_set_sensitive(kfd->button_close, TRUE);
3056 static void keywords_find_stop_cb(GenericDialog *, gpointer data)
3058 auto kfd = static_cast<KeywordFindData *>(data);
3060 g_idle_remove_by_data(kfd);
3062 keywords_find_finish(kfd);
3065 static gboolean keywords_find_file(gpointer data)
3067 auto kfd = static_cast<KeywordFindData *>(data);
3069 GtkTextBuffer *buffer;
3077 fd = static_cast<FileData *>(kfd->list->data);
3078 kfd->list = g_list_remove(kfd->list, fd);
3080 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
3081 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3085 gtk_text_buffer_get_end_iter(buffer, &iter);
3086 tmp = g_strconcat(static_cast<const gchar *>(keywords->data), "\n", NULL);
3087 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3089 keywords = keywords->next;
3092 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
3093 file_data_unref(fd);
3094 g_list_free_full(keywords, g_free);
3096 return (G_SOURCE_CONTINUE);
3103 fd = static_cast<FileData *>(kfd->list_dir->data);
3104 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
3106 keywords_find_folder(kfd, fd);
3108 file_data_unref(fd);
3110 return G_SOURCE_CONTINUE;
3113 keywords_find_finish(kfd);
3115 return G_SOURCE_REMOVE;
3118 static void keywords_find_start_cb(GenericDialog *, gpointer data)
3120 auto kfd = static_cast<KeywordFindData *>(data);
3123 if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
3125 path = remove_trailing_slash((gq_gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
3126 parse_out_relatives(path);
3130 warning_dialog(_("Invalid folder"),
3131 _("The specified folder can not be found."),
3132 GQ_ICON_DIALOG_WARNING, kfd->gd->dialog);
3138 gtk_widget_set_sensitive(kfd->group, FALSE);
3139 gtk_widget_set_sensitive(kfd->button_start, FALSE);
3140 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
3141 gtk_widget_set_sensitive(kfd->button_close, FALSE);
3142 gtk_spinner_start(GTK_SPINNER(kfd->spinner));
3144 dir_fd = file_data_new_dir(path);
3145 keywords_find_folder(kfd, dir_fd);
3146 file_data_unref(dir_fd);
3147 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
3153 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
3155 KeywordFindData *kfd;
3159 kfd = g_new0(KeywordFindData, 1);
3161 kfd->gd = generic_dialog_new(_("Search for keywords"),
3162 "search_for_keywords",
3165 gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
3166 kfd->gd->cancel_cb = keywords_find_close_cb;
3167 kfd->button_close = generic_dialog_add_button(kfd->gd, GQ_ICON_CLOSE, _("Close"),
3168 keywords_find_close_cb, FALSE);
3169 kfd->button_start = generic_dialog_add_button(kfd->gd, GQ_ICON_OK, _("S_tart"),
3170 keywords_find_start_cb, FALSE);
3171 kfd->button_stop = generic_dialog_add_button(kfd->gd, GQ_ICON_STOP, _("Stop"),
3172 keywords_find_stop_cb, FALSE);
3173 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3175 generic_dialog_add_message(kfd->gd, nullptr, _("Search for keywords"), nullptr, FALSE);
3177 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3178 pref_spacer(hbox, PREF_PAD_INDENT);
3179 kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3181 hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3182 pref_label_new(hbox, _("Folder:"));
3184 label = tab_completion_new(&kfd->entry, path, nullptr, nullptr, nullptr, nullptr);
3185 tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
3186 gq_gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3187 gtk_widget_show(label);
3189 pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
3191 pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
3192 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3194 kfd->progress = gtk_entry_new();
3195 gtk_widget_set_can_focus(kfd->progress, FALSE);
3196 gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
3197 gq_gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
3198 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
3199 gtk_widget_show(kfd->progress);
3201 kfd->spinner = gtk_spinner_new();
3202 gq_gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
3203 gtk_widget_show(kfd->spinner);
3205 kfd->list = nullptr;
3207 gtk_widget_show(kfd->gd->dialog);
3210 static void keywords_find_cb(GtkWidget *widget, gpointer)
3212 const gchar *path = layout_get_path(nullptr);
3214 if (!path || !*path) path = homedir();
3215 keywords_find_dialog(widget, path);
3218 static void config_tab_keywords_save()
3220 GtkTextIter start, end;
3221 GtkTextBuffer *buffer;
3222 GList *kw_list = nullptr;
3228 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3229 gtk_text_buffer_get_bounds(buffer, &start, &end);
3231 buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3233 kw_split = strtok(buffer_text, "\n");
3234 while (kw_split != nullptr)
3240 if (g_strcmp0(static_cast<const gchar *>(work->data), kw_split) == 0)
3249 kw_list = g_list_append(kw_list, g_strdup(kw_split));
3251 kw_split = strtok(nullptr, "\n");
3254 keyword_list_set(kw_list);
3256 g_list_free_full(kw_list, g_free);
3257 g_free(buffer_text);
3260 static void config_tab_keywords(GtkWidget *notebook)
3266 GtkWidget *scrolled;
3268 GtkTextBuffer *buffer;
3271 GspellTextView *gspell_view;
3274 vbox = scrolled_notebook_page(notebook, _("Keywords"));
3276 group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
3278 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3280 button = pref_button_new(hbox, GQ_ICON_RUN, _("Search"),
3281 G_CALLBACK(keywords_find_cb), keyword_text);
3282 gtk_widget_set_tooltip_text(button, "Search for existing keywords");
3285 keyword_text = gtk_text_view_new();
3286 gtk_widget_set_size_request(keyword_text, 20, 20);
3287 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3288 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3289 gtk_widget_show(scrolled);
3292 if (options->metadata.check_spelling)
3294 gspell_view = gspell_text_view_get_from_gtk_text_view(GTK_TEXT_VIEW(keyword_text));
3295 gspell_text_view_basic_setup(gspell_view);
3299 gq_gtk_container_add(GTK_WIDGET(scrolled), keyword_text);
3300 gtk_widget_show(keyword_text);
3302 gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
3304 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3305 gtk_text_buffer_create_tag(buffer, "monospace",
3306 "family", "monospace", NULL);
3308 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3309 gtk_text_buffer_get_start_iter(buffer, &iter);
3310 gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3313 path = g_build_filename(get_rc_dir(), "keywords", NULL);
3315 GList *kwl = keyword_list_get();
3316 kwl = g_list_first(kwl);
3319 gtk_text_buffer_get_end_iter (buffer, &iter);
3320 tmp = g_strconcat(static_cast<const gchar *>(kwl->data), "\n", NULL);
3321 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3326 gtk_text_buffer_set_modified(buffer, FALSE);
3333 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3335 auto option = static_cast<gint *>(data);
3337 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3341 *option = INTENT_PERCEPTUAL;
3344 *option = INTENT_RELATIVE_COLORIMETRIC;
3347 *option = INTENT_SATURATION;
3350 *option = INTENT_ABSOLUTE_COLORIMETRIC;
3355 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3356 gint option, gint *option_c)
3363 pref_table_label(table, column, row, text, GTK_ALIGN_START);
3365 combo = gtk_combo_box_text_new();
3367 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3368 if (option == INTENT_PERCEPTUAL) current = 0;
3369 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3370 if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3371 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3372 if (option == INTENT_SATURATION) current = 2;
3373 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3374 if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3376 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3378 gtk_widget_set_tooltip_text(combo,"Refer to the lcms documentation for the defaults used when the selected Intent is not available");
3380 g_signal_connect(G_OBJECT(combo), "changed",
3381 G_CALLBACK(intent_menu_cb), option_c);
3383 gq_gtk_grid_attach(GTK_GRID(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
3384 gtk_widget_show(combo);
3388 static void config_tab_color(GtkWidget *notebook)
3397 vbox = scrolled_notebook_page(notebook, _("Color management"));
3399 group = pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3401 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3404 table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3405 gtk_grid_set_column_spacing(GTK_GRID(table), PREF_PAD_GAP);
3407 label = pref_table_label(table, 0, 0, _("Type"), GTK_ALIGN_START);
3408 pref_label_bold(label, TRUE, FALSE);
3410 label = pref_table_label(table, 1, 0, _("Menu name"), GTK_ALIGN_START);
3411 pref_label_bold(label, TRUE, FALSE);
3413 label = pref_table_label(table, 2, 0, _("File"), GTK_ALIGN_START);
3414 pref_label_bold(label, TRUE, FALSE);
3416 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3421 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3422 pref_table_label(table, 0, i + 1, buf, GTK_ALIGN_END);
3425 entry = gtk_entry_new();
3426 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3427 if (options->color_profile.input_name[i])
3429 gq_gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3431 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);
3432 gtk_widget_show(entry);
3433 color_profile_input_name_entry[i] = entry;
3435 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], nullptr, ".icc", "ICC Files", nullptr);
3436 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3437 gtk_widget_set_size_request(entry, 160, -1);
3438 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);
3439 gtk_widget_show(tabcomp);
3440 color_profile_input_file_entry[i] = entry;
3443 group = pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3445 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3447 pref_checkbox_new_int(group, _("Use system screen profile if available"),
3448 options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3450 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3452 pref_table_label(table, 0, 0, _("Screen:"), GTK_ALIGN_END);
3453 tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3454 options->color_profile.screen_file, nullptr, ".icc", "ICC Files", nullptr);
3455 tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3456 gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3458 add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3460 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);
3462 gtk_widget_show(tabcomp);
3465 /* advanced entry tab */
3466 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer)
3468 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3470 c_options->file_ops.use_system_trash = FALSE;
3471 c_options->file_ops.no_trash = FALSE;
3475 static void use_system_trash_cb(GtkWidget *widget, gpointer)
3477 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3479 c_options->file_ops.use_system_trash = TRUE;
3480 c_options->file_ops.no_trash = FALSE;
3484 static void use_no_cache_cb(GtkWidget *widget, gpointer)
3486 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3488 c_options->file_ops.no_trash = TRUE;
3492 static void config_tab_behavior(GtkWidget *notebook)
3499 GtkWidget *ct_button;
3503 GtkWidget *with_rename;
3504 GtkWidget *collections_on_top;
3505 GtkWidget *hide_window_in_fullscreen;
3506 GtkWidget *checkbox;
3509 vbox = scrolled_notebook_page(notebook, _("Behavior"));
3511 group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3513 pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3514 options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3515 pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3516 options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3517 pref_checkbox_new_int(group, _("Enable Delete key"),
3518 options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3520 ct_button = pref_radiobutton_new(group, nullptr, _("Use Geeqie trash location"),
3521 !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),nullptr);
3523 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3524 pref_checkbox_link_sensitivity(ct_button, hbox);
3526 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3527 pref_label_new(hbox, _("Folder:"));
3529 tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, nullptr, nullptr, nullptr, nullptr);
3530 tab_completion_add_select_button(safe_delete_path_entry, nullptr, TRUE);
3531 gq_gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3532 gtk_widget_show(tabcomp);
3534 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3535 pref_checkbox_link_sensitivity(ct_button, hbox);
3537 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3538 spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MiB"),
3539 0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3540 gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3541 button = pref_button_new(nullptr, nullptr, _("View"),
3542 G_CALLBACK(safe_delete_view_cb), nullptr);
3543 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3544 gtk_widget_show(button);
3546 button = pref_button_new(nullptr, GQ_ICON_CLEAR, nullptr,
3547 G_CALLBACK(safe_delete_clear_cb), nullptr);
3548 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3550 c_options->file_ops.no_trash = options->file_ops.no_trash;
3551 c_options->file_ops.use_system_trash = options->file_ops.use_system_trash;
3553 pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3554 options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), nullptr);
3556 pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3557 options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), nullptr);
3559 gtk_widget_show(button);
3561 pref_spacer(group, PREF_PAD_GROUP);
3564 group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3566 pref_checkbox_new_int(group, _("Descend folders in tree view"),
3567 options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3569 pref_checkbox_new_int(group, _("In place renaming"),
3570 options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3572 pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3573 options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3575 tmp = pref_checkbox_new_int(group, _("Circular selection lists"),
3576 options->circular_selection_lists, &c_options->circular_selection_lists);
3577 gtk_widget_set_tooltip_text(tmp, _("Traverse selection lists in a circular manner"));
3579 marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3580 options->marks_save, &c_options->marks_save);
3581 gtk_widget_set_tooltip_text(marks,"Note that marks linked to a keyword will be saved irrespective of this setting");
3583 with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3584 options->with_rename, &c_options->with_rename);
3585 gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3587 collections_on_top = pref_checkbox_new_int(group, _("Permit duplicates in Collections"),
3588 options->collections_duplicates, &c_options->collections_duplicates);
3589 gtk_widget_set_tooltip_text(collections_on_top,"Allow the same image to be in a Collection more than once");
3591 collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3592 options->collections_on_top, &c_options->collections_on_top);
3593 gtk_widget_set_tooltip_text(collections_on_top,"Open collections window on top");
3595 hide_window_in_fullscreen = pref_checkbox_new_int(group, _("Hide window in fullscreen"),
3596 options->hide_window_in_fullscreen, &c_options->hide_window_in_fullscreen);
3597 gtk_widget_set_tooltip_text(hide_window_in_fullscreen,"When alt-tabbing, prevent Geeqie window showing twice");
3599 pref_spin_new_int(group, _("Recent folder list maximum size"), nullptr,
3600 1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3602 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);
3603 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."));
3605 pref_spin_new_int(group, _("Drag'n drop icon size"), nullptr,
3606 16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3608 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3609 add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
3611 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3612 add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3614 pref_spacer(group, PREF_PAD_GROUP);
3616 group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3618 pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3619 options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3620 pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), nullptr,
3621 1, 32, 1, options->keyboard_scroll_step, reinterpret_cast<int *>(&c_options->keyboard_scroll_step));
3622 pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3623 options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3624 pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3625 options->image_lm_click_nav, &c_options->image_lm_click_nav);
3626 pref_checkbox_new_int(group, _("Open archive by left click on image"),
3627 options->image_l_click_archive, &c_options->image_l_click_archive);
3628 pref_checkbox_new_int(group, _("Play video by left click on image"),
3629 options->image_l_click_video, &c_options->image_l_click_video);
3630 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3631 add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3633 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3634 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3635 add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3636 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3637 add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3639 pref_spacer(group, PREF_PAD_GROUP);
3641 group = pref_group_new(vbox, FALSE, _("GPU"), GTK_ORIENTATION_VERTICAL);
3643 checkbox = pref_checkbox_new_int(group, _("Override disable GPU"),
3644 options->override_disable_gpu, &c_options->override_disable_gpu);
3645 gtk_widget_set_tooltip_text(checkbox, "Contact the developers for usage");
3648 pref_spacer(group, PREF_PAD_GROUP);
3650 group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3652 pref_spin_new_int(group, _("Debug level:"), nullptr,
3653 DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3655 pref_checkbox_new_int(group, _("Timer data"),
3656 options->log_window.timer_data, &c_options->log_window.timer_data);
3658 pref_spin_new_int(group, _("Log Window max. lines:"), nullptr,
3659 1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3661 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3662 pref_label_new(hbox, _("Log Window F1 command: "));
3663 log_window_f1_entry = gtk_entry_new();
3664 gq_gtk_entry_set_text(GTK_ENTRY(log_window_f1_entry), options->log_window.action);
3665 gq_gtk_box_pack_start(GTK_BOX(hbox), log_window_f1_entry, FALSE, FALSE, 0);
3666 gtk_entry_set_width_chars(GTK_ENTRY(log_window_f1_entry), 15);
3667 gtk_widget_show(log_window_f1_entry);
3671 /* accelerators tab */
3673 static gboolean accel_search_function_cb(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer)
3675 gboolean ret = TRUE;
3680 gtk_tree_model_get(model, iter, column, &text, -1);
3681 text_nocase = g_utf8_casefold(text, -1);
3682 key_nocase = g_utf8_casefold(key, -1);
3684 if (g_strstr_len(text_nocase, -1, key_nocase))
3691 g_free(text_nocase);
3696 static void accel_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *, GtkTreeViewColumn *column, gpointer)
3701 list = gtk_tree_view_get_columns(tree_view);
3702 col_num = g_list_index(list, column);
3706 gtk_tree_view_set_search_column(tree_view, col_num);
3709 static void config_tab_accelerators(GtkWidget *notebook)
3715 GtkWidget *scrolled;
3716 GtkWidget *accel_view;
3717 GtkCellRenderer *renderer;
3718 GtkTreeSelection *selection;
3719 GtkTreeViewColumn *column;
3721 vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3723 group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3725 scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
3726 gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3727 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3728 gq_gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3729 gtk_widget_show(scrolled);
3731 accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3733 accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3734 g_object_unref(accel_store);
3735 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3736 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3738 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3740 renderer = gtk_cell_renderer_text_new();
3742 column = gtk_tree_view_column_new_with_attributes(_("Action"),
3747 gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3748 gtk_tree_view_column_set_resizable(column, TRUE);
3749 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3752 renderer = gtk_cell_renderer_accel_new();
3753 g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3754 G_CALLBACK(accel_store_cleared_cb), accel_store);
3755 g_signal_connect(G_OBJECT(renderer), "accel-edited",
3756 G_CALLBACK(accel_store_edited_cb), accel_store);
3759 g_object_set (renderer,
3761 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3764 column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3769 gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3770 gtk_tree_view_column_set_resizable(column, TRUE);
3771 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3773 renderer = gtk_cell_renderer_text_new();
3775 column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3780 gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3781 gtk_tree_view_column_set_resizable(column, TRUE);
3782 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3784 renderer = gtk_cell_renderer_text_new();
3786 column = gtk_tree_view_column_new_with_attributes("Accel",
3791 gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3792 gtk_tree_view_column_set_resizable(column, TRUE);
3793 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3795 /* Search on text in column */
3796 gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(accel_view), TRUE);
3797 g_signal_connect(accel_view, "row_activated", G_CALLBACK(accel_row_activated_cb), accel_store);
3798 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), TRUE);
3799 gtk_tree_view_set_search_column(GTK_TREE_VIEW(accel_view), AE_TOOLTIP);
3800 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(accel_view), accel_search_function_cb, nullptr, nullptr);
3802 accel_store_populate();
3803 gq_gtk_container_add(GTK_WIDGET(scrolled), accel_view);
3804 gtk_widget_show(accel_view);
3806 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3808 button = pref_button_new(nullptr, nullptr, _("Defaults"),
3809 G_CALLBACK(accel_default_cb), accel_view);
3810 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3811 gtk_widget_show(button);
3813 button = pref_button_new(nullptr, nullptr, _("Reset selected"),
3814 G_CALLBACK(accel_reset_cb), accel_view);
3815 gtk_widget_set_tooltip_text(button, _("Will only reset changes made before the settings are saved"));
3816 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3817 gtk_widget_show(button);
3819 button = pref_button_new(nullptr, nullptr, _("Clear selected"),
3820 G_CALLBACK(accel_clear_cb), accel_view);
3821 gq_gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3822 gtk_widget_show(button);
3825 /* toolbar main tab */
3826 static void config_tab_toolbar_main(GtkWidget *notebook)
3829 GtkWidget *toolbardata;
3832 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3834 vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3836 toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3837 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3838 gtk_widget_show(vbox);
3841 /* toolbar status tab */
3842 static void config_tab_toolbar_status(GtkWidget *notebook)
3845 GtkWidget *toolbardata;
3848 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3850 vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3852 toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3853 gq_gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3854 gtk_widget_show(vbox);
3858 static gint extension_sort_cb(gconstpointer a, gconstpointer b)
3860 return g_strcmp0(static_cast<const gchar *>(a), static_cast<const gchar *>(b));
3863 static void config_tab_advanced(GtkWidget *notebook)
3866 GdkPixbufFormat *fm;
3868 GList *extensions_list = nullptr;
3869 GSList *formats_list;
3870 GString *types_string = g_string_new(nullptr);
3871 GtkWidget *alternate_checkbox;
3872 GtkWidget *dupes_threads_spin;
3874 GtkWidget *subgroup;
3876 GtkWidget *threads_string_label;
3877 GtkWidget *types_string_label;
3880 vbox = scrolled_notebook_page(notebook, _("Advanced"));
3881 group = pref_group_new(vbox, FALSE, _("External preview extraction"), GTK_ORIENTATION_VERTICAL);
3883 pref_checkbox_new_int(group, _("Use external preview extraction - Requires restart"), options->external_preview.enable, &c_options->external_preview.enable);
3885 pref_spacer(group, PREF_PAD_GROUP);
3887 formats_list = gdk_pixbuf_get_formats();
3889 while (formats_list)
3891 fm = static_cast<GdkPixbufFormat *>(formats_list->data);
3892 extensions = gdk_pixbuf_format_get_extensions(fm);
3895 while (extensions[i])
3897 extensions_list = g_list_insert_sorted(extensions_list, g_strdup(extensions[i]), extension_sort_cb);
3901 g_strfreev(extensions);
3902 formats_list = formats_list->next;
3905 while (extensions_list)
3907 if (types_string->len == 0)
3909 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3913 types_string = g_string_append(types_string, ", ");
3914 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3917 extensions_list = extensions_list->next;
3920 types_string = g_string_prepend(types_string, _("Usable file types:\n"));
3921 types_string_label = pref_label_new(group, types_string->str);
3922 gtk_label_set_line_wrap(GTK_LABEL(types_string_label), TRUE);
3924 pref_spacer(group, PREF_PAD_GROUP);
3926 group = pref_group_new(vbox, FALSE, _("File identification tool"), GTK_ORIENTATION_VERTICAL);
3927 external_preview_select_entry = gtk_entry_new();
3928 tabcomp = tab_completion_new(&external_preview_select_entry, options->external_preview.select, nullptr, nullptr, nullptr, nullptr);
3929 tab_completion_add_select_button(external_preview_select_entry, _("Select file identification tool"), FALSE);
3930 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3931 gtk_widget_show(tabcomp);
3933 group = pref_group_new(vbox, FALSE, _("Preview extraction tool"), GTK_ORIENTATION_VERTICAL);
3934 external_preview_extract_entry = gtk_entry_new();
3935 tabcomp = tab_completion_new(&external_preview_extract_entry, options->external_preview.extract, nullptr, nullptr, nullptr, nullptr);
3936 tab_completion_add_select_button(external_preview_extract_entry, _("Select preview extraction tool"), FALSE);
3937 gq_gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3938 gtk_widget_show(tabcomp);
3940 gtk_widget_show(vbox);
3942 g_slist_free(formats_list);
3943 g_list_free_full(extensions_list, g_free);
3944 g_string_free(types_string, TRUE);
3946 pref_spacer(group, PREF_PAD_GROUP);
3948 pref_line(vbox, PREF_PAD_SPACE);
3949 group = pref_group_new(vbox, FALSE, _("Thread pool limits"), GTK_ORIENTATION_VERTICAL);
3951 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.");
3952 gtk_label_set_line_wrap(GTK_LABEL(threads_string_label), TRUE);
3954 pref_spacer(vbox, PREF_PAD_GROUP);
3956 dupes_threads_spin = pref_spin_new_int(vbox, _("Duplicate check:"), _("max. threads"), 0, get_cpu_cores(), 1, options->threads.duplicates, &c_options->threads.duplicates);
3957 gtk_widget_set_tooltip_markup(dupes_threads_spin, _("Set to 0 for unlimited"));
3959 pref_spacer(group, PREF_PAD_GROUP);
3961 pref_line(vbox, PREF_PAD_SPACE);
3963 group = pref_group_new(vbox, FALSE, _("Alternate similarity alogorithm"), GTK_ORIENTATION_VERTICAL);
3965 alternate_checkbox = pref_checkbox_new_int(group, _("Enable alternate similarity algorithm"), options->alternate_similarity_algorithm.enabled, &c_options->alternate_similarity_algorithm.enabled);
3967 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3968 pref_checkbox_link_sensitivity(alternate_checkbox, subgroup);
3970 alternate_checkbox = pref_checkbox_new_int(subgroup, _("Use grayscale"), options->alternate_similarity_algorithm.grayscale, &c_options->alternate_similarity_algorithm.grayscale);
3971 gtk_widget_set_tooltip_text(alternate_checkbox, _("Reduce fingerprint to grayscale"));
3975 static void config_tab_stereo(GtkWidget *notebook)
3983 GtkWidget *fs_button;
3984 vbox = scrolled_notebook_page(notebook, _("Stereo"));
3986 group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
3988 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3989 add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
3991 table = pref_table_new(group, 2, 2, TRUE, FALSE);
3992 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3993 pref_checkbox_new_int(box, _("Mirror left image"),
3994 options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
3995 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3996 pref_checkbox_new_int(box, _("Flip left image"),
3997 options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
3998 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3999 pref_checkbox_new_int(box, _("Mirror right image"),
4000 options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
4001 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4002 pref_checkbox_new_int(box, _("Flip right image"),
4003 options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
4004 pref_checkbox_new_int(group, _("Swap left and right images"),
4005 options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
4006 pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
4007 options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
4009 group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
4010 fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
4011 options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
4012 box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4013 pref_checkbox_link_sensitivity(fs_button, box2);
4014 table = pref_table_new(box2, 2, 1, FALSE, FALSE);
4015 add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
4016 table = pref_table_new(box2, 2, 2, TRUE, FALSE);
4017 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4018 pref_checkbox_new_int(box, _("Mirror left image"),
4019 options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
4020 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
4021 pref_checkbox_new_int(box, _("Flip left image"),
4022 options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
4023 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4024 pref_checkbox_new_int(box, _("Mirror right image"),
4025 options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
4026 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
4027 pref_checkbox_new_int(box, _("Flip right image"),
4028 options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
4029 pref_checkbox_new_int(box2, _("Swap left and right images"),
4030 options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
4031 pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
4032 options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
4034 group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
4035 table = pref_table_new(group2, 5, 3, FALSE, FALSE);
4036 pref_table_spin_new_int(table, 0, 0, _("Width"), nullptr,
4037 1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
4038 pref_table_spin_new_int(table, 3, 0, _("Height"), nullptr,
4039 1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
4040 pref_table_spin_new_int(table, 0, 1, _("Left X"), nullptr,
4041 0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
4042 pref_table_spin_new_int(table, 3, 1, _("Left Y"), nullptr,
4043 0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
4044 pref_table_spin_new_int(table, 0, 2, _("Right X"), nullptr,
4045 0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
4046 pref_table_spin_new_int(table, 3, 2, _("Right Y"), nullptr,
4047 0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
4051 /* Main preferences window */
4052 static void config_window_create(LayoutWindow *lw)
4054 GtkWidget *win_vbox;
4056 GtkWidget *notebook;
4058 GtkWidget *ct_button;
4060 if (!c_options) c_options = init_options(nullptr);
4062 configwindow = window_new("preferences", PIXBUF_INLINE_ICON_CONFIG, nullptr, _("Preferences"));
4063 DEBUG_NAME(configwindow);
4064 gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
4065 g_signal_connect(G_OBJECT(configwindow), "delete_event",
4066 G_CALLBACK(config_window_delete), NULL);
4067 if (options->save_dialog_window_positions)
4069 gtk_window_resize(GTK_WINDOW(configwindow), lw->options.preferences_window.w, lw->options.preferences_window.h);
4070 gq_gtk_window_move(GTK_WINDOW(configwindow), lw->options.preferences_window.x, lw->options.preferences_window.y);
4074 gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
4076 gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
4077 gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
4079 win_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4080 gq_gtk_container_add(GTK_WIDGET(configwindow), win_vbox);
4081 gtk_widget_show(win_vbox);
4083 notebook = gtk_notebook_new();
4084 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
4085 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
4086 gq_gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
4088 config_tab_general(notebook);
4089 config_tab_image(notebook);
4090 config_tab_osd(notebook);
4091 config_tab_windows(notebook);
4092 config_tab_accelerators(notebook);
4093 config_tab_files(notebook);
4094 config_tab_metadata(notebook);
4095 config_tab_keywords(notebook);
4096 config_tab_color(notebook);
4097 config_tab_stereo(notebook);
4098 config_tab_behavior(notebook);
4099 config_tab_toolbar_main(notebook);
4100 config_tab_toolbar_status(notebook);
4101 config_tab_advanced(notebook);
4103 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), lw->options.preferences_window.page_number);
4105 hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
4106 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
4107 gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
4108 gq_gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
4109 gtk_widget_show(hbox);
4111 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
4112 G_CALLBACK(config_window_help_cb), notebook);
4113 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4114 gtk_widget_set_can_default(button, TRUE);
4115 gtk_widget_show(button);
4117 button = pref_button_new(nullptr, GQ_ICON_OK, "OK",
4118 G_CALLBACK(config_window_ok_cb), notebook);
4119 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4120 gtk_widget_set_can_default(button, TRUE);
4121 gtk_widget_grab_default(button);
4122 gtk_widget_show(button);
4126 button = pref_button_new(nullptr, GQ_ICON_CANCEL, _("Cancel"),
4127 G_CALLBACK(config_window_close_cb), nullptr);
4128 gq_gtk_container_add(GTK_WIDGET(hbox), button);
4129 gtk_widget_set_can_default(button, TRUE);
4130 gtk_widget_show(button);
4132 if (!generic_dialog_get_alternative_button_order(configwindow))
4134 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
4137 gtk_widget_show(notebook);
4139 gtk_widget_show(configwindow);
4143 *-----------------------------------------------------------------------------
4144 * config window show (public)
4145 *-----------------------------------------------------------------------------
4148 void show_config_window(LayoutWindow *lw)
4152 gtk_window_present(GTK_WINDOW(configwindow));
4156 config_window_create(lw);
4165 void show_about_window(LayoutWindow *lw)
4167 GDataInputStream *data_stream;
4168 GInputStream *in_stream_authors;
4169 GInputStream *in_stream_translators;
4171 GdkPixbuf *pixbuf_icon;
4172 GdkPixbuf *pixbuf_logo;
4176 gchar *authors[1000];
4178 gchar *timezone_path;
4187 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");
4189 timezone_path = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
4190 if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
4192 cd = ZDOpenDatabase(timezone_path);
4195 copyright = g_string_append(copyright, ZDGetNotice(cd));
4199 log_printf("Error: Init of timezone database %s failed\n", timezone_path);
4201 ZDCloseDatabase(cd);
4203 g_free(timezone_path);
4205 copyright = g_string_append(copyright, "\n\nSome icons by https://www.flaticon.com");
4207 in_stream_authors = g_resources_open_stream(GQ_RESOURCE_PATH_CREDITS "/authors", G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4209 data_stream = g_data_input_stream_new(in_stream_authors);
4211 authors[0] = nullptr;
4212 while ((author_line = g_data_input_stream_read_line(G_DATA_INPUT_STREAM(data_stream), &length, nullptr, nullptr)))
4214 authors[i_authors] = g_strdup(author_line);
4216 g_free(author_line);
4218 authors[i_authors] = nullptr;
4220 g_input_stream_close(in_stream_authors, nullptr, nullptr);
4222 constexpr auto translators_path = GQ_RESOURCE_PATH_CREDITS "/translators";
4224 g_resources_get_info(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &size, &flags, nullptr);
4226 in_stream_translators = g_resources_open_stream(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4227 translators = static_cast<gchar *>(g_malloc0(size));
4228 g_input_stream_read_all(in_stream_translators, translators, size, &bytes_read, nullptr, nullptr);
4229 g_input_stream_close(in_stream_translators, nullptr, nullptr);
4231 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);
4233 artists[0] = g_strdup("Néstor Díaz Valencia <nestor@estudionexos.com>");
4234 artists[1] = nullptr;
4236 pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
4237 pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
4238 gtk_show_about_dialog(GTK_WINDOW(lw->window),
4239 "title", _("About Geeqie"),
4241 "program-name", GQ_APPNAME,
4243 "logo", pixbuf_logo,
4244 "icon", pixbuf_icon,
4245 "website", GQ_WEBSITE,
4246 "website-label", "Website",
4247 "comments", comment,
4250 "translator-credits", translators,
4251 "wrap-license", TRUE,
4252 "license", copyright->str,
4255 g_string_free(copyright, TRUE);
4257 while(n < i_authors)
4265 g_free(translators);
4266 g_object_unref(data_stream);
4267 g_object_unref(in_stream_authors);
4268 g_object_unref(in_stream_translators);
4271 static void image_overlay_set_text_colors()
4273 c_options->image_overlay.text_red = options->image_overlay.text_red;
4274 c_options->image_overlay.text_green = options->image_overlay.text_green;
4275 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
4276 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
4277 c_options->image_overlay.background_red = options->image_overlay.background_red;
4278 c_options->image_overlay.background_green = options->image_overlay.background_green;
4279 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
4280 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
4284 *-----------------------------------------------------------------------------
4285 * timezone database routines
4286 *-----------------------------------------------------------------------------
4289 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
4291 GError *error = nullptr;
4292 auto tz = static_cast<TZData *>(data);
4293 gchar *tmp_filename;
4294 gchar *timezone_bin;
4295 gchar *tmp_dir = nullptr;
4298 if (!g_cancellable_is_cancelled(tz->cancellable))
4300 generic_dialog_close(tz->gd);
4304 if (g_file_copy_finish(G_FILE(source_object), res, &error))
4306 tmp_filename = g_file_get_path(tz->tmp_g_file);
4307 fd = file_data_new_simple(tmp_filename);
4308 tmp_dir = open_archive(fd);
4312 timezone_bin = g_build_filename(tmp_dir, TIMEZONE_DATABASE_VERSION, TIMEZONE_DATABASE_FILE, NULL);
4313 if (isfile(timezone_bin))
4315 move_file(timezone_bin, tz->timezone_database_user);
4319 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4322 g_free(timezone_bin);
4323 g_free(tmp_dir); // The folder in /tmp is deleted in exit_program_final()
4327 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4329 g_free(tmp_filename);
4330 file_data_unref(fd);
4334 file_util_warning_dialog(_("Error: Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4337 g_file_delete(tz->tmp_g_file, nullptr, &error);
4338 g_object_unref(tz->tmp_g_file);
4339 tz->tmp_g_file = nullptr;
4340 g_object_unref(tz->cancellable);
4341 g_object_unref(tz->timezone_database_gq);
4344 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
4346 auto tz = static_cast<TZData *>(data);
4348 if (!g_cancellable_is_cancelled(tz->cancellable))
4350 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), static_cast<gdouble>(current_num_bytes) / total_num_bytes);
4354 static void timezone_cancel_button_cb(GenericDialog *, gpointer data)
4356 auto tz = static_cast<TZData *>(data);
4358 g_cancellable_cancel(tz->cancellable);
4361 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
4363 auto tz = static_cast<TZData *>(data);
4364 GError *error = nullptr;
4365 GFileIOStream *io_stream;
4372 tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
4376 file_util_warning_dialog(_("Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4377 log_printf("Error: Download timezone database failed:\n%s", error->message);
4378 g_error_free(error);
4379 g_object_unref(tz->tmp_g_file);
4383 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE_WEB);
4385 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", nullptr, TRUE, timezone_cancel_button_cb, tz);
4387 generic_dialog_add_message(tz->gd, GQ_ICON_DIALOG_INFO, _("Downloading timezone database"), nullptr, FALSE);
4389 tz->progress = gtk_progress_bar_new();
4390 gq_gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
4391 gtk_widget_show(tz->progress);
4393 gtk_widget_show(tz->gd->dialog);
4394 tz->cancellable = g_cancellable_new();
4395 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);
4397 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
4400 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */