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_colours();
68 GtkWidget *keyword_text;
69 static void config_tab_keywords_save();
77 static ThumbSize thumb_size_list[] =
112 FILETYPES_COLUMN_ENABLED = 0,
113 FILETYPES_COLUMN_FILTER,
114 FILETYPES_COLUMN_DESCRIPTION,
115 FILETYPES_COLUMN_CLASS,
116 FILETYPES_COLUMN_WRITABLE,
117 FILETYPES_COLUMN_SIDECAR,
118 FILETYPES_COLUMN_COUNT
121 const gchar *format_class_list[] = {
132 /* config memory values */
133 static ConfOptions *c_options = nullptr;
140 static GtkWidget *configwindow = nullptr;
141 static GtkListStore *filter_store = nullptr;
142 static GtkTreeStore *accel_store = nullptr;
144 static GtkWidget *safe_delete_path_entry;
146 static GtkWidget *color_profile_input_file_entry[COLOR_PROFILE_INPUTS];
147 static GtkWidget *color_profile_input_name_entry[COLOR_PROFILE_INPUTS];
148 static GtkWidget *color_profile_screen_file_entry;
149 static GtkWidget *external_preview_select_entry;
150 static GtkWidget *external_preview_extract_entry;
152 static GtkWidget *sidecar_ext_entry;
153 static GtkWidget *help_search_engine_entry;
156 #define CONFIG_WINDOW_DEF_WIDTH 700
157 #define CONFIG_WINDOW_DEF_HEIGHT 600
160 *-----------------------------------------------------------------------------
161 * option widget callbacks (private)
162 *-----------------------------------------------------------------------------
165 static void zoom_increment_cb(GtkWidget *spin, gpointer)
167 c_options->image.zoom_increment = static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) * 100.0 + 0.01);
170 static void slideshow_delay_hours_cb(GtkWidget *spin, gpointer)
172 gint mins_secs_tenths, delay;
174 mins_secs_tenths = c_options->slideshow.delay %
175 (3600 * SLIDESHOW_SUBSECOND_PRECISION);
177 delay = (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
178 (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
181 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
182 SLIDESHOW_SUBSECOND_PRECISION;
185 static void slideshow_delay_minutes_cb(GtkWidget *spin, gpointer)
187 gint hours, secs_tenths, delay;
189 hours = c_options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
190 secs_tenths = c_options->slideshow.delay % (60 * SLIDESHOW_SUBSECOND_PRECISION);
192 delay = hours * (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
193 (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
194 (60 * SLIDESHOW_SUBSECOND_PRECISION) + secs_tenths);
196 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
197 SLIDESHOW_SUBSECOND_PRECISION;
200 static void slideshow_delay_seconds_cb(GtkWidget *spin, gpointer)
202 gint hours_mins, delay;
204 hours_mins = c_options->slideshow.delay / (60 * SLIDESHOW_SUBSECOND_PRECISION);
206 delay = (hours_mins * (60 * SLIDESHOW_SUBSECOND_PRECISION)) +
207 static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
208 static_cast<gdouble>(SLIDESHOW_SUBSECOND_PRECISION) + 0.01);
210 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
211 SLIDESHOW_SUBSECOND_PRECISION;
215 *-----------------------------------------------------------------------------
216 * sync program to config window routine (private)
217 *-----------------------------------------------------------------------------
220 void config_entry_to_option(GtkWidget *entry, gchar **option, gchar *(*func)(const gchar *))
226 buf = gtk_entry_get_text(GTK_ENTRY(entry));
227 if (buf && strlen(buf) > 0)
232 *option = g_strdup(buf);
237 static gboolean accel_apply_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
239 gchar *accel_path, *accel;
241 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, AE_KEY, &accel, -1);
243 if (accel_path && accel_path[0])
246 gtk_accelerator_parse(accel, &key.accel_key, &key.accel_mods);
247 gtk_accel_map_change_entry(accel_path, key.accel_key, key.accel_mods, TRUE);
257 static void config_window_apply()
259 gboolean refresh = FALSE;
264 config_entry_to_option(safe_delete_path_entry, &options->file_ops.safe_delete_path, remove_trailing_slash);
266 if (options->file_filter.show_hidden_files != c_options->file_filter.show_hidden_files) refresh = TRUE;
267 if (options->file_filter.show_parent_directory != c_options->file_filter.show_parent_directory) refresh = TRUE;
268 if (options->file_filter.show_dot_directory != c_options->file_filter.show_dot_directory) refresh = TRUE;
269 if (options->file_sort.case_sensitive != c_options->file_sort.case_sensitive) refresh = TRUE;
270 if (options->file_filter.disable_file_extension_checks != c_options->file_filter.disable_file_extension_checks) refresh = TRUE;
271 if (options->file_filter.disable != c_options->file_filter.disable) refresh = TRUE;
273 options->file_ops.confirm_delete = c_options->file_ops.confirm_delete;
274 options->file_ops.enable_delete_key = c_options->file_ops.enable_delete_key;
275 options->file_ops.confirm_move_to_trash = c_options->file_ops.confirm_move_to_trash;
276 options->file_ops.use_system_trash = c_options->file_ops.use_system_trash;
277 options->file_ops.no_trash = c_options->file_ops.no_trash;
278 options->file_ops.safe_delete_folder_maxsize = c_options->file_ops.safe_delete_folder_maxsize;
279 options->tools_restore_state = c_options->tools_restore_state;
280 options->save_window_positions = c_options->save_window_positions;
281 options->use_saved_window_positions_for_new_windows = c_options->use_saved_window_positions_for_new_windows;
282 options->save_window_workspace = c_options->save_window_workspace;
283 options->save_dialog_window_positions = c_options->save_dialog_window_positions;
284 options->show_window_ids = c_options->show_window_ids;
285 options->image.scroll_reset_method = c_options->image.scroll_reset_method;
286 options->image.zoom_2pass = c_options->image.zoom_2pass;
287 options->image.fit_window_to_image = c_options->image.fit_window_to_image;
288 options->image.limit_window_size = c_options->image.limit_window_size;
289 options->image.zoom_to_fit_allow_expand = c_options->image.zoom_to_fit_allow_expand;
290 options->image.max_window_size = c_options->image.max_window_size;
291 options->image.limit_autofit_size = c_options->image.limit_autofit_size;
292 options->image.max_autofit_size = c_options->image.max_autofit_size;
293 options->image.max_enlargement_size = c_options->image.max_enlargement_size;
294 options->image.tile_size = c_options->image.tile_size;
295 options->progressive_key_scrolling = c_options->progressive_key_scrolling;
296 options->keyboard_scroll_step = c_options->keyboard_scroll_step;
298 if (options->thumbnails.max_width != c_options->thumbnails.max_width
299 || options->thumbnails.max_height != c_options->thumbnails.max_height
300 || options->thumbnails.quality != c_options->thumbnails.quality)
302 thumb_format_changed = TRUE;
304 options->thumbnails.max_width = c_options->thumbnails.max_width;
305 options->thumbnails.max_height = c_options->thumbnails.max_height;
306 options->thumbnails.quality = c_options->thumbnails.quality;
308 options->thumbnails.enable_caching = c_options->thumbnails.enable_caching;
309 options->thumbnails.cache_into_dirs = c_options->thumbnails.cache_into_dirs;
310 options->thumbnails.use_exif = c_options->thumbnails.use_exif;
311 options->thumbnails.use_color_management = c_options->thumbnails.use_color_management;
312 options->thumbnails.collection_preview = c_options->thumbnails.collection_preview;
313 options->thumbnails.use_ft_metadata = c_options->thumbnails.use_ft_metadata;
314 options->thumbnails.spec_standard = c_options->thumbnails.spec_standard;
315 options->metadata.enable_metadata_dirs = c_options->metadata.enable_metadata_dirs;
316 options->file_filter.show_hidden_files = c_options->file_filter.show_hidden_files;
317 options->file_filter.show_parent_directory = c_options->file_filter.show_parent_directory;
318 options->file_filter.show_dot_directory = c_options->file_filter.show_dot_directory;
319 options->file_filter.disable_file_extension_checks = c_options->file_filter.disable_file_extension_checks;
321 options->file_sort.case_sensitive = c_options->file_sort.case_sensitive;
322 options->file_filter.disable = c_options->file_filter.disable;
324 config_entry_to_option(sidecar_ext_entry, &options->sidecar.ext, nullptr);
325 sidecar_ext_parse(options->sidecar.ext);
327 options->slideshow.random = c_options->slideshow.random;
328 options->slideshow.repeat = c_options->slideshow.repeat;
329 options->slideshow.delay = c_options->slideshow.delay;
331 options->mousewheel_scrolls = c_options->mousewheel_scrolls;
332 options->image_lm_click_nav = c_options->image_lm_click_nav;
333 options->image_l_click_archive = c_options->image_l_click_archive;
334 options->image_l_click_video = c_options->image_l_click_video;
335 options->image_l_click_video_editor = c_options->image_l_click_video_editor;
337 options->file_ops.enable_in_place_rename = c_options->file_ops.enable_in_place_rename;
339 options->image.tile_cache_max = c_options->image.tile_cache_max;
340 options->image.image_cache_max = c_options->image.image_cache_max;
342 options->image.zoom_quality = c_options->image.zoom_quality;
344 options->image.zoom_increment = c_options->image.zoom_increment;
346 options->image.zoom_style = c_options->image.zoom_style;
348 options->image.enable_read_ahead = c_options->image.enable_read_ahead;
350 options->appimage_notifications = c_options->appimage_notifications;
353 if (options->image.use_custom_border_color != c_options->image.use_custom_border_color
354 || options->image.use_custom_border_color_in_fullscreen != c_options->image.use_custom_border_color_in_fullscreen
355 || !gdk_color_equal(&options->image.border_color, &c_options->image.border_color))
357 options->image.use_custom_border_color_in_fullscreen = c_options->image.use_custom_border_color_in_fullscreen;
358 options->image.use_custom_border_color = c_options->image.use_custom_border_color;
359 options->image.border_color = c_options->image.border_color;
360 layout_colors_update();
361 view_window_colors_update();
364 options->image.alpha_color_1 = c_options->image.alpha_color_1;
365 options->image.alpha_color_2 = c_options->image.alpha_color_2;
367 options->fullscreen.screen = c_options->fullscreen.screen;
368 options->fullscreen.clean_flip = c_options->fullscreen.clean_flip;
369 options->fullscreen.disable_saver = c_options->fullscreen.disable_saver;
370 options->fullscreen.above = c_options->fullscreen.above;
371 if (c_options->image_overlay.template_string)
372 set_image_overlay_template_string(&options->image_overlay.template_string,
373 c_options->image_overlay.template_string);
374 if (c_options->image_overlay.font)
375 set_image_overlay_font_string(&options->image_overlay.font,
376 c_options->image_overlay.font);
377 options->image_overlay.text_red = c_options->image_overlay.text_red;
378 options->image_overlay.text_green = c_options->image_overlay.text_green;
379 options->image_overlay.text_blue = c_options->image_overlay.text_blue;
380 options->image_overlay.text_alpha = c_options->image_overlay.text_alpha;
381 options->image_overlay.background_red = c_options->image_overlay.background_red;
382 options->image_overlay.background_green = c_options->image_overlay.background_green;
383 options->image_overlay.background_blue = c_options->image_overlay.background_blue;
384 options->image_overlay.background_alpha = c_options->image_overlay.background_alpha;
385 options->update_on_time_change = c_options->update_on_time_change;
387 options->duplicates_similarity_threshold = c_options->duplicates_similarity_threshold;
388 options->rot_invariant_sim = c_options->rot_invariant_sim;
390 options->tree_descend_subdirs = c_options->tree_descend_subdirs;
392 options->view_dir_list_single_click_enter = c_options->view_dir_list_single_click_enter;
393 options->circular_selection_lists = c_options->circular_selection_lists;
395 options->open_recent_list_maxsize = c_options->open_recent_list_maxsize;
396 options->recent_folder_image_list_maxsize = c_options->recent_folder_image_list_maxsize;
397 options->dnd_icon_size = c_options->dnd_icon_size;
398 options->clipboard_selection = c_options->clipboard_selection;
399 options->dnd_default_action = c_options->dnd_default_action;
401 options->metadata.save_in_image_file = c_options->metadata.save_in_image_file;
402 options->metadata.save_legacy_IPTC = c_options->metadata.save_legacy_IPTC;
403 options->metadata.warn_on_write_problems = c_options->metadata.warn_on_write_problems;
404 options->metadata.save_legacy_format = c_options->metadata.save_legacy_format;
405 options->metadata.sync_grouped_files = c_options->metadata.sync_grouped_files;
406 options->metadata.confirm_write = c_options->metadata.confirm_write;
407 options->metadata.sidecar_extended_name = c_options->metadata.sidecar_extended_name;
408 options->metadata.confirm_timeout = c_options->metadata.confirm_timeout;
409 options->metadata.confirm_after_timeout = c_options->metadata.confirm_after_timeout;
410 options->metadata.confirm_on_image_change = c_options->metadata.confirm_on_image_change;
411 options->metadata.confirm_on_dir_change = c_options->metadata.confirm_on_dir_change;
412 options->metadata.keywords_case_sensitive = c_options->metadata.keywords_case_sensitive;
413 options->metadata.write_orientation = c_options->metadata.write_orientation;
414 options->metadata.check_spelling = c_options->metadata.check_spelling;
415 options->stereo.mode = (c_options->stereo.mode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
416 (c_options->stereo.tmp.mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
417 (c_options->stereo.tmp.flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
418 (c_options->stereo.tmp.mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
419 (c_options->stereo.tmp.flip_left ? PR_STEREO_FLIP_LEFT : 0) |
420 (c_options->stereo.tmp.swap ? PR_STEREO_SWAP : 0) |
421 (c_options->stereo.tmp.temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
422 options->stereo.fsmode = (c_options->stereo.fsmode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
423 (c_options->stereo.tmp.fs_mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
424 (c_options->stereo.tmp.fs_flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
425 (c_options->stereo.tmp.fs_mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
426 (c_options->stereo.tmp.fs_flip_left ? PR_STEREO_FLIP_LEFT : 0) |
427 (c_options->stereo.tmp.fs_swap ? PR_STEREO_SWAP : 0) |
428 (c_options->stereo.tmp.fs_temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
429 options->stereo.enable_fsmode = c_options->stereo.enable_fsmode;
430 options->stereo.fixed_w = c_options->stereo.fixed_w;
431 options->stereo.fixed_h = c_options->stereo.fixed_h;
432 options->stereo.fixed_x1 = c_options->stereo.fixed_x1;
433 options->stereo.fixed_y1 = c_options->stereo.fixed_y1;
434 options->stereo.fixed_x2 = c_options->stereo.fixed_x2;
435 options->stereo.fixed_y2 = c_options->stereo.fixed_y2;
437 options->info_keywords.height = c_options->info_keywords.height;
438 options->info_title.height = c_options->info_title.height;
439 options->info_comment.height = c_options->info_comment.height;
440 options->info_rating.height = c_options->info_rating.height;
442 options->show_predefined_keyword_tree = c_options->show_predefined_keyword_tree;
443 options->expand_menu_toolbar = c_options->expand_menu_toolbar;
445 options->selectable_bars.menu_bar = c_options->selectable_bars.menu_bar;
446 options->selectable_bars.tool_bar = c_options->selectable_bars.tool_bar;
447 options->selectable_bars.status_bar = c_options->selectable_bars.status_bar;
449 options->marks_save = c_options->marks_save;
450 options->with_rename = c_options->with_rename;
451 options->collections_duplicates = c_options->collections_duplicates;
452 options->collections_on_top = c_options->collections_on_top;
453 options->hide_window_in_fullscreen = c_options->hide_window_in_fullscreen;
454 config_entry_to_option(help_search_engine_entry, &options->help_search_engine, nullptr);
456 options->external_preview.enable = c_options->external_preview.enable;
457 config_entry_to_option(external_preview_select_entry, &options->external_preview.select, nullptr);
458 config_entry_to_option(external_preview_extract_entry, &options->external_preview.extract, nullptr);
460 options->read_metadata_in_idle = c_options->read_metadata_in_idle;
462 options->star_rating.star = c_options->star_rating.star;
463 options->star_rating.rejected = c_options->star_rating.rejected;
465 options->threads.duplicates = c_options->threads.duplicates > 0 ? c_options->threads.duplicates : -1;
468 set_debug_level(debug_c);
472 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
474 config_entry_to_option(color_profile_input_name_entry[i], &options->color_profile.input_name[i], nullptr);
475 config_entry_to_option(color_profile_input_file_entry[i], &options->color_profile.input_file[i], nullptr);
477 config_entry_to_option(color_profile_screen_file_entry, &options->color_profile.screen_file, nullptr);
478 options->color_profile.use_x11_screen_profile = c_options->color_profile.use_x11_screen_profile;
479 if (options->color_profile.render_intent != c_options->color_profile.render_intent)
481 options->color_profile.render_intent = c_options->color_profile.render_intent;
486 options->mouse_button_8 = c_options->mouse_button_8;
487 options->mouse_button_9 = c_options->mouse_button_9;
489 options->override_disable_gpu = c_options->override_disable_gpu;
491 config_tab_keywords_save();
493 image_options_sync();
498 layout_refresh(nullptr);
501 if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, nullptr);
503 toolbar_apply(TOOLBAR_MAIN);
504 toolbar_apply(TOOLBAR_STATUS);
508 *-----------------------------------------------------------------------------
509 * config window main button callbacks (private)
510 *-----------------------------------------------------------------------------
513 static void config_window_close_cb(GtkWidget *, gpointer)
515 gtk_widget_destroy(configwindow);
516 configwindow = nullptr;
517 filter_store = nullptr;
520 static void config_window_help_cb(GtkWidget *, gpointer data)
522 auto notebook = static_cast<GtkWidget *>(data);
525 static const gchar *html_section[] =
527 "GuideOptionsGeneral.html",
528 "GuideOptionsImage.html",
529 "GuideOptionsOSD.html",
530 "GuideOptionsWindow.html",
531 "GuideOptionsKeyboard.html",
532 "GuideOptionsFiltering.html",
533 "GuideOptionsMetadata.html",
534 "GuideOptionsKeywords.html",
535 "GuideOptionsColor.html",
536 "GuideOptionsStereo.html",
537 "GuideOptionsBehavior.html",
538 "GuideOptionsToolbar.html",
539 "GuideOptionsToolbar.html",
540 "GuideOptionsAdvanced.html"
543 i = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
544 help_window_show(html_section[i]);
547 static gboolean config_window_delete(GtkWidget *, GdkEventAny *, gpointer)
549 config_window_close_cb(nullptr, nullptr);
553 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
556 auto notebook = static_cast<GtkNotebook *>(data);
564 lw = static_cast<LayoutWindow *>(layout_window_list->data);
566 window = gtk_widget_get_window(widget);
567 gdk_window_get_root_origin(window, &x, &y);
568 w = gdk_window_get_width(window);
569 h = gdk_window_get_height(window);
570 page_number = gtk_notebook_get_current_page(notebook);
572 lw->options.preferences_window.x = x;
573 lw->options.preferences_window.y = y;
574 lw->options.preferences_window.w = w;
575 lw->options.preferences_window.h = h;
576 lw->options.preferences_window.page_number = page_number;
578 config_window_apply();
579 layout_util_sync(lw);
580 save_options(options);
581 config_window_close_cb(nullptr, nullptr);
585 *-----------------------------------------------------------------------------
586 * config window setup (private)
587 *-----------------------------------------------------------------------------
590 static void quality_menu_cb(GtkWidget *combo, gpointer data)
592 auto option = static_cast<gint *>(data);
594 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
598 *option = GDK_INTERP_NEAREST;
601 *option = GDK_INTERP_TILES;
604 *option = GDK_INTERP_BILINEAR;
609 static void dnd_default_action_selection_menu_cb(GtkWidget *combo, gpointer data)
611 auto option = static_cast<gint *>(data);
613 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
617 *option = DND_ACTION_ASK;
620 *option = DND_ACTION_COPY;
623 *option = DND_ACTION_MOVE;
627 static void clipboard_selection_menu_cb(GtkWidget *combo, gpointer data)
629 auto option = static_cast<gint *>(data);
631 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
634 *option = CLIPBOARD_PRIMARY;
637 *option = CLIPBOARD_CLIPBOARD;
640 *option = CLIPBOARD_BOTH;
643 *option = CLIPBOARD_BOTH;
647 static void add_quality_menu(GtkWidget *table, gint column, gint row, const gchar *text,
648 guint option, guint *option_c)
655 pref_table_label(table, column, row, text, GTK_ALIGN_START);
657 combo = gtk_combo_box_text_new();
659 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Nearest (worst, but fastest)"));
660 if (option == GDK_INTERP_NEAREST) current = 0;
661 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Tiles"));
662 if (option == GDK_INTERP_TILES) current = 1;
663 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Bilinear (best, but slowest)"));
664 if (option == GDK_INTERP_BILINEAR) current = 2;
666 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
668 g_signal_connect(G_OBJECT(combo), "changed",
669 G_CALLBACK(quality_menu_cb), option_c);
671 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
672 gtk_widget_show(combo);
675 static void add_dnd_default_action_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, DnDAction option, DnDAction *option_c)
682 pref_table_label(table, column, row, text, GTK_ALIGN_START);
684 combo = gtk_combo_box_text_new();
686 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Ask"));
687 if (option == DND_ACTION_ASK) current = 0;
688 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Copy"));
689 if (option == DND_ACTION_COPY) current = 1;
690 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Move"));
691 if (option == DND_ACTION_MOVE) current = 2;
693 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
695 g_signal_connect(G_OBJECT(combo), "changed",
696 G_CALLBACK(dnd_default_action_selection_menu_cb), option_c);
698 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
699 gtk_widget_show(combo);
702 static void add_clipboard_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
703 gint option, gint *option_c)
710 pref_table_label(table, column, row, text, GTK_ALIGN_START);
712 combo = gtk_combo_box_text_new();
714 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Primary"));
715 if (option == CLIPBOARD_PRIMARY) current = 0;
716 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Clipboard"));
717 if (option == CLIPBOARD_CLIPBOARD) current = 1;
718 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Both"));
719 if (option == CLIPBOARD_BOTH) current = 2;
721 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
723 g_signal_connect(G_OBJECT(combo), "changed",
724 G_CALLBACK(clipboard_selection_menu_cb), option_c);
726 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
727 gtk_widget_show(combo);
730 static void zoom_style_selection_menu_cb(GtkWidget *combo, gpointer data)
732 auto option = static_cast<gint *>(data);
734 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
737 *option = ZOOM_GEOMETRIC;
740 *option = ZOOM_ARITHMETIC;
743 *option = ZOOM_GEOMETRIC;
747 static void add_zoom_style_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, ZoomStyle option, ZoomStyle *option_c)
754 pref_table_label(table, column, row, text, GTK_ALIGN_START);
756 combo = gtk_combo_box_text_new();
758 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Geometric"));
759 if (option == ZOOM_GEOMETRIC) current = 0;
760 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Arithmetic"));
761 if (option == ZOOM_ARITHMETIC) current = 1;
763 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
765 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(zoom_style_selection_menu_cb), option_c);
767 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
768 gtk_widget_show(combo);
771 static void mouse_buttons_selection_menu_cb(GtkWidget *combo, gpointer data)
773 ActionItem *action_item = nullptr;
774 auto option = static_cast<gchar **>(data);
779 label = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
781 list = get_action_items();
786 action_item = static_cast<ActionItem *>(work->data);
787 if (g_strcmp0(action_item->label, label) == 0)
795 *option = g_strdup(action_item->name);
797 action_items_free(list);
800 static void add_mouse_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, gchar *option, gchar **option_c)
802 ActionItem *action_item;
811 pref_table_label(table, column, row, text, GTK_ALIGN_START);
813 combo = gtk_combo_box_text_new();
815 list = get_action_items();
819 action_item = static_cast<ActionItem *>(work->data);
820 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), action_item->label);
822 if (g_strcmp0(action_item->name, option) == 0)
830 action_items_free(list);
832 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
834 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(mouse_buttons_selection_menu_cb), option_c);
836 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
837 gtk_widget_show(combo);
840 static void thumb_size_menu_cb(GtkWidget *combo, gpointer)
844 n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
847 if (static_cast<guint>(n) < sizeof(thumb_size_list) / sizeof(ThumbSize))
849 c_options->thumbnails.max_width = thumb_size_list[n].w;
850 c_options->thumbnails.max_height = thumb_size_list[n].h;
854 c_options->thumbnails.max_width = options->thumbnails.max_width;
855 c_options->thumbnails.max_height = options->thumbnails.max_height;
859 static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *text)
865 c_options->thumbnails.max_width = options->thumbnails.max_width;
866 c_options->thumbnails.max_height = options->thumbnails.max_height;
868 pref_table_label(table, column, row, text, GTK_ALIGN_START);
870 combo = gtk_combo_box_text_new();
873 for (i = 0; static_cast<guint>(i) < sizeof(thumb_size_list) / sizeof(ThumbSize); i++)
878 w = thumb_size_list[i].w;
879 h = thumb_size_list[i].h;
881 buf = g_strdup_printf("%d x %d", w, h);
882 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
885 if (w == options->thumbnails.max_width && h == options->thumbnails.max_height) current = i;
892 buf = g_strdup_printf("%s %d x %d", _("Custom"), options->thumbnails.max_width, options->thumbnails.max_height);
893 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
899 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
900 g_signal_connect(G_OBJECT(combo), "changed",
901 G_CALLBACK(thumb_size_menu_cb), NULL);
903 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
904 gtk_widget_show(combo);
907 static void stereo_mode_menu_cb(GtkWidget *combo, gpointer data)
909 auto option = static_cast<gint *>(data);
911 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
915 *option = PR_STEREO_NONE;
918 *option = PR_STEREO_ANAGLYPH_RC;
921 *option = PR_STEREO_ANAGLYPH_GM;
924 *option = PR_STEREO_ANAGLYPH_YB;
927 *option = PR_STEREO_ANAGLYPH_GRAY_RC;
930 *option = PR_STEREO_ANAGLYPH_GRAY_GM;
933 *option = PR_STEREO_ANAGLYPH_GRAY_YB;
936 *option = PR_STEREO_ANAGLYPH_DB_RC;
939 *option = PR_STEREO_ANAGLYPH_DB_GM;
942 *option = PR_STEREO_ANAGLYPH_DB_YB;
945 *option = PR_STEREO_HORIZ;
948 *option = PR_STEREO_HORIZ | PR_STEREO_HALF;
951 *option = PR_STEREO_VERT;
954 *option = PR_STEREO_VERT | PR_STEREO_HALF;
957 *option = PR_STEREO_FIXED;
962 static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const gchar *text,
963 gint option, gint *option_c, gboolean add_fixed)
970 pref_table_label(table, column, row, text, GTK_ALIGN_START);
972 combo = gtk_combo_box_text_new();
974 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Single image"));
976 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Red-Cyan"));
977 if (option & PR_STEREO_ANAGLYPH_RC) current = 1;
978 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Green-Magenta"));
979 if (option & PR_STEREO_ANAGLYPH_GM) current = 2;
980 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Yellow-Blue"));
981 if (option & PR_STEREO_ANAGLYPH_YB) current = 3;
982 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Red-Cyan"));
983 if (option & PR_STEREO_ANAGLYPH_GRAY_RC) current = 4;
984 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Green-Magenta"));
985 if (option & PR_STEREO_ANAGLYPH_GRAY_GM) current = 5;
986 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Yellow-Blue"));
987 if (option & PR_STEREO_ANAGLYPH_GRAY_YB) current = 6;
988 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Red-Cyan"));
989 if (option & PR_STEREO_ANAGLYPH_DB_RC) current = 7;
990 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Green-Magenta"));
991 if (option & PR_STEREO_ANAGLYPH_DB_GM) current = 8;
992 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Yellow-Blue"));
993 if (option & PR_STEREO_ANAGLYPH_DB_YB) current = 9;
995 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side"));
996 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side Half size"));
997 if (option & PR_STEREO_HORIZ)
1000 if (option & PR_STEREO_HALF) current = 11;
1003 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom"));
1004 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom Half size"));
1005 if (option & PR_STEREO_VERT)
1008 if (option & PR_STEREO_HALF) current = 13;
1013 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Fixed position"));
1014 if (option & PR_STEREO_FIXED) current = 14;
1017 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1019 g_signal_connect(G_OBJECT(combo), "changed",
1020 G_CALLBACK(stereo_mode_menu_cb), option_c);
1022 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1023 gtk_widget_show(combo);
1026 static void video_menu_cb(GtkWidget *combo, gpointer data)
1028 auto option = static_cast<gchar **>(data);
1030 auto ed = static_cast<EditorDescription *>(g_list_nth_data(editor_list_get(), gtk_combo_box_get_active(GTK_COMBO_BOX(combo))));
1034 static void video_menu_populate(gpointer data, gpointer user_data)
1036 auto combo = static_cast<GtkWidget *>(user_data);
1037 auto ed = static_cast<EditorDescription *>(data);
1039 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), ed->name);
1042 static void add_video_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1043 gchar *option, gchar **option_c)
1047 /* use lists since they are sorted */
1048 GList *eds = editor_list_get();
1052 pref_table_label(table, column, row, text, GTK_ALIGN_START);
1054 combo = gtk_combo_box_text_new();
1055 g_list_foreach(eds,video_menu_populate,combo);
1056 current = option ? g_list_index(eds,g_hash_table_lookup(editors,option)): -1;
1058 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1060 g_signal_connect(G_OBJECT(combo), "changed",
1061 G_CALLBACK(video_menu_cb), option_c);
1063 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1064 gtk_widget_show(combo);
1067 static void filter_store_populate()
1071 if (!filter_store) return;
1073 gtk_list_store_clear(filter_store);
1075 work = filter_get_list();
1081 fe = static_cast<FilterEntry *>(work->data);
1084 gtk_list_store_append(filter_store, &iter);
1085 gtk_list_store_set(filter_store, &iter, 0, fe, -1);
1089 static void filter_store_ext_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1091 auto model = static_cast<GtkWidget *>(data);
1092 auto fe = static_cast<FilterEntry *>(data);
1096 if (!new_text || strlen(new_text) < 1) return;
1098 tpath = gtk_tree_path_new_from_string(path_str);
1099 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1100 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1102 g_free(fe->extensions);
1103 fe->extensions = g_strdup(new_text);
1105 gtk_tree_path_free(tpath);
1109 static void filter_store_class_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1111 auto model = static_cast<GtkWidget *>(data);
1112 auto fe = static_cast<FilterEntry *>(data);
1117 if (!new_text || !new_text[0]) return;
1119 tpath = gtk_tree_path_new_from_string(path_str);
1120 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1121 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1123 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
1125 if (strcmp(new_text, _(format_class_list[i])) == 0)
1127 fe->file_class = static_cast<FileFormatClass>(i);
1132 gtk_tree_path_free(tpath);
1136 static void filter_store_desc_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1138 auto model = static_cast<GtkWidget *>(data);
1143 if (!new_text || !new_text[0]) return;
1145 tpath = gtk_tree_path_new_from_string(path_str);
1146 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1147 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1149 g_free(fe->description);
1150 fe->description = g_strdup(new_text);
1152 gtk_tree_path_free(tpath);
1155 static void filter_store_enable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1157 auto model = static_cast<GtkWidget *>(data);
1162 tpath = gtk_tree_path_new_from_string(path_str);
1163 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1164 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1166 fe->enabled = !fe->enabled;
1168 gtk_tree_path_free(tpath);
1172 static void filter_store_writable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1174 auto model = static_cast<GtkWidget *>(data);
1179 tpath = gtk_tree_path_new_from_string(path_str);
1180 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1181 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1183 fe->writable = !fe->writable;
1184 if (fe->writable) fe->allow_sidecar = FALSE;
1186 gtk_tree_path_free(tpath);
1190 static void filter_store_sidecar_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1192 auto model = static_cast<GtkWidget *>(data);
1197 tpath = gtk_tree_path_new_from_string(path_str);
1198 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1199 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1201 fe->allow_sidecar = !fe->allow_sidecar;
1202 if (fe->allow_sidecar) fe->writable = FALSE;
1204 gtk_tree_path_free(tpath);
1208 static void filter_set_func(GtkTreeViewColumn *, GtkCellRenderer *cell,
1209 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1213 gtk_tree_model_get(tree_model, iter, 0, &fe, -1);
1215 switch (GPOINTER_TO_INT(data))
1218 g_object_set(GTK_CELL_RENDERER(cell),
1219 "active", fe->enabled, NULL);
1222 g_object_set(GTK_CELL_RENDERER(cell),
1223 "text", fe->extensions, NULL);
1225 case FE_DESCRIPTION:
1226 g_object_set(GTK_CELL_RENDERER(cell),
1227 "text", fe->description, NULL);
1230 g_object_set(GTK_CELL_RENDERER(cell),
1231 "text", _(format_class_list[fe->file_class]), NULL);
1234 g_object_set(GTK_CELL_RENDERER(cell),
1235 "active", fe->writable, NULL);
1237 case FE_ALLOW_SIDECAR:
1238 g_object_set(GTK_CELL_RENDERER(cell),
1239 "active", fe->allow_sidecar, NULL);
1244 static gboolean filter_add_scroll(gpointer data)
1248 GtkCellRenderer *cell;
1249 GtkTreeViewColumn *column;
1252 GtkTreeModel *store;
1254 FilterEntry *filter;
1256 rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(filter_store), nullptr);
1257 path = gtk_tree_path_new_from_indices(rows-1, -1);
1259 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data), 0);
1261 list_cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1262 cell = static_cast<GtkCellRenderer *>(g_list_last(list_cells)->data);
1264 store = gtk_tree_view_get_model(GTK_TREE_VIEW(data));
1265 valid = gtk_tree_model_get_iter_first(store, &iter);
1269 gtk_tree_model_get(store, &iter, 0, &filter, -1);
1271 if (g_strcmp0(filter->extensions, ".new") == 0)
1273 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1277 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1280 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1281 path, column, FALSE, 0.0, 0.0 );
1282 gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(data),
1283 path, column, cell, TRUE);
1285 gtk_tree_path_free(path);
1286 g_list_free(list_cells);
1288 return(G_SOURCE_REMOVE);
1291 static void filter_add_cb(GtkWidget *, gpointer data)
1293 filter_add_unique("description", ".new", FORMAT_CLASS_IMAGE, TRUE, FALSE, TRUE);
1294 filter_store_populate();
1296 g_idle_add(static_cast<GSourceFunc>(filter_add_scroll), data);
1299 static void filter_remove_cb(GtkWidget *, gpointer data)
1301 auto filter_view = static_cast<GtkWidget *>(data);
1302 GtkTreeSelection *selection;
1306 if (!filter_store) return;
1307 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
1308 if (!gtk_tree_selection_get_selected(selection, nullptr, &iter)) return;
1309 gtk_tree_model_get(GTK_TREE_MODEL(filter_store), &iter, 0, &fe, -1);
1312 filter_remove_entry(fe);
1314 filter_store_populate();
1317 static gboolean filter_default_ok_scroll(GtkTreeView *data)
1321 GtkTreeViewColumn *column;
1323 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(filter_store), &iter);
1324 path = gtk_tree_model_get_path(GTK_TREE_MODEL(filter_store), &iter);
1325 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1327 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1331 gtk_tree_path_free(path);
1333 return(G_SOURCE_REMOVE);
1336 static void filter_default_ok_cb(GenericDialog *gd, gpointer)
1339 filter_add_defaults();
1341 filter_store_populate();
1343 g_idle_add(reinterpret_cast<GSourceFunc>(filter_default_ok_scroll), gd->data);
1346 static void dummy_cancel_cb(GenericDialog *, gpointer)
1348 /* no op, only so cancel button appears */
1351 static void filter_default_cb(GtkWidget *widget, gpointer data)
1355 gd = generic_dialog_new(_("Reset filters"),
1356 "reset_filter", widget, TRUE,
1357 dummy_cancel_cb, data);
1358 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset filters"),
1359 _("This will reset the file filters to the defaults.\nContinue?"), TRUE);
1360 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", filter_default_ok_cb, TRUE);
1361 gtk_widget_show(gd->dialog);
1364 static void filter_disable_cb(GtkWidget *widget, gpointer data)
1366 auto frame = static_cast<GtkWidget *>(data);
1368 gtk_widget_set_sensitive(frame,
1369 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1372 static void safe_delete_view_cb(GtkWidget *, gpointer)
1374 layout_set_path(nullptr, gtk_entry_get_text(GTK_ENTRY(safe_delete_path_entry)));
1377 static void safe_delete_clear_ok_cb(GenericDialog *, gpointer)
1379 file_util_trash_clear();
1382 static void safe_delete_clear_cb(GtkWidget *widget, gpointer)
1386 gd = generic_dialog_new(_("Clear trash"),
1387 "clear_trash", widget, TRUE,
1388 dummy_cancel_cb, nullptr);
1389 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Clear trash"),
1390 _("This will remove the trash contents."), FALSE);
1391 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", safe_delete_clear_ok_cb, TRUE);
1392 entry = gtk_entry_new();
1393 gtk_widget_set_can_focus(entry, FALSE);
1394 gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
1395 if (options->file_ops.safe_delete_path) gtk_entry_set_text(GTK_ENTRY(entry), options->file_ops.safe_delete_path);
1396 gtk_box_pack_start(GTK_BOX(gd->vbox), entry, FALSE, FALSE, 0);
1397 gtk_widget_show(entry);
1398 gtk_widget_show(gd->dialog);
1401 static void image_overlay_template_view_changed_cb(GtkWidget *, gpointer data)
1403 GtkWidget *pTextView;
1404 GtkTextBuffer *pTextBuffer;
1408 pTextView = GTK_WIDGET(data);
1410 pTextBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pTextView));
1411 gtk_text_buffer_get_start_iter(pTextBuffer, &iStart);
1412 gtk_text_buffer_get_end_iter(pTextBuffer, &iEnd);
1414 set_image_overlay_template_string(&c_options->image_overlay.template_string,
1415 gtk_text_buffer_get_text(pTextBuffer, &iStart, &iEnd, TRUE));
1418 static void image_overlay_default_template_ok_cb(GenericDialog *, gpointer data)
1420 auto text_view = static_cast<GtkTextView *>(data);
1421 GtkTextBuffer *buffer;
1423 set_default_image_overlay_template_string(&options->image_overlay.template_string);
1424 if (!configwindow) return;
1426 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
1427 gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
1430 static void image_overlay_default_template_cb(GtkWidget *widget, gpointer data)
1434 gd = generic_dialog_new(_("Reset image overlay template string"),
1435 "reset_image_overlay_template_string", widget, TRUE,
1436 dummy_cancel_cb, data);
1437 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset image overlay template string"),
1438 _("This will reset the image overlay template string to the default.\nContinue?"), TRUE);
1439 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", image_overlay_default_template_ok_cb, TRUE);
1440 gtk_widget_show(gd->dialog);
1443 static void image_overlay_help_cb(GtkWidget *, gpointer)
1445 help_window_show("GuideOptionsOSD.html");
1448 static void image_overlay_set_font_cb(GtkWidget *widget, gpointer)
1452 PangoFontDescription *font_desc;
1454 dialog = gtk_font_chooser_dialog_new("Image Overlay Font", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1455 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), options->image_overlay.font);
1457 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1459 font_desc = gtk_font_chooser_get_font_desc(GTK_FONT_CHOOSER(dialog));
1460 font = pango_font_description_to_string(font_desc);
1461 g_free(c_options->image_overlay.font);
1462 c_options->image_overlay.font = g_strdup(font);
1466 gtk_widget_destroy(dialog);
1469 static void image_overlay_set_text_colour_cb(GtkWidget *widget, gpointer)
1474 dialog = gtk_color_chooser_dialog_new("Image Overlay Text Colour", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1475 colour.red = options->image_overlay.text_red;
1476 colour.green = options->image_overlay.text_green;
1477 colour.blue = options->image_overlay.text_blue;
1478 colour.alpha = options->image_overlay.text_alpha;
1479 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1480 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1482 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1484 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1485 c_options->image_overlay.text_red = colour.red*255;
1486 c_options->image_overlay.text_green = colour.green*255;
1487 c_options->image_overlay.text_blue = colour.blue*255;
1488 c_options->image_overlay.text_alpha = colour.alpha*255;
1490 gtk_widget_destroy(dialog);
1494 static void image_overlay_set_background_colour_cb(GtkWidget *widget, gpointer)
1499 dialog = gtk_color_chooser_dialog_new("Image Overlay Background Colour", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1500 colour.red = options->image_overlay.background_red;
1501 colour.green = options->image_overlay.background_green;
1502 colour.blue = options->image_overlay.background_blue;
1503 colour.alpha = options->image_overlay.background_alpha;
1504 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1505 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1507 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1509 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1510 c_options->image_overlay.background_red = colour.red*255;
1511 c_options->image_overlay.background_green = colour.green*255;
1512 c_options->image_overlay.background_blue = colour.blue*255;
1513 c_options->image_overlay.background_alpha = colour.alpha*255;
1515 gtk_widget_destroy(dialog);
1518 static void accel_store_populate()
1521 GList *groups, *actions;
1523 const gchar *accel_path;
1527 if (!accel_store || !layout_window_list || !layout_window_list->data) return;
1529 gtk_tree_store_clear(accel_store);
1530 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 */
1532 g_assert(lw && lw->ui_manager);
1533 groups = gtk_ui_manager_get_action_groups(lw->ui_manager);
1536 actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
1539 action = GTK_ACTION(actions->data);
1540 accel_path = gtk_action_get_accel_path(action);
1541 if (accel_path && gtk_accel_map_lookup_entry(accel_path, &key))
1543 gchar *label, *label2, *tooltip, *accel;
1544 g_object_get(action,
1545 "tooltip", &tooltip,
1549 if (pango_parse_markup(label, -1, '_', nullptr, &label2, nullptr, nullptr) && label2)
1555 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1559 gtk_tree_store_append(accel_store, &iter, nullptr);
1560 gtk_tree_store_set(accel_store, &iter,
1563 AE_TOOLTIP, tooltip ? tooltip : "",
1564 AE_ACCEL, accel_path,
1572 actions = actions->next;
1575 groups = groups->next;
1579 static void accel_store_cleared_cb(GtkCellRendererAccel *, gchar *, gpointer)
1584 static gboolean accel_remove_key_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer data)
1586 auto accel1 = static_cast<gchar *>(data);
1591 gtk_tree_model_get(model, iter, AE_KEY, &accel2, -1);
1593 gtk_accelerator_parse(accel1, &key1.accel_key, &key1.accel_mods);
1594 gtk_accelerator_parse(accel2, &key2.accel_key, &key2.accel_mods);
1596 if (key1.accel_key == key2.accel_key && key1.accel_mods == key2.accel_mods)
1598 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1599 DEBUG_1("accelerator key '%s' is already used, removing.", accel1);
1608 static void accel_store_edited_cb(GtkCellRendererAccel *, gchar *path_string, guint accel_key, GdkModifierType accel_mods, guint, gpointer)
1610 auto model = reinterpret_cast<GtkTreeModel *>(accel_store);
1614 GtkAccelKey old_key, key;
1615 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1617 gtk_tree_model_get_iter(model, &iter, path);
1618 gtk_tree_model_get(model, &iter, AE_ACCEL, &accel_path, -1);
1620 /* test if the accelerator can be stored without conflicts*/
1621 gtk_accel_map_lookup_entry(accel_path, &old_key);
1623 /* change the key and read it back (change may fail on keys hardcoded in gtk)*/
1624 gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, TRUE);
1625 gtk_accel_map_lookup_entry(accel_path, &key);
1627 /* restore the original for now, the key will be really changed when the changes are confirmed */
1628 gtk_accel_map_change_entry(accel_path, old_key.accel_key, old_key.accel_mods, TRUE);
1630 acc = gtk_accelerator_name(key.accel_key, key.accel_mods);
1631 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, acc);
1633 gtk_tree_store_set(accel_store, &iter, AE_KEY, acc, -1);
1634 gtk_tree_path_free(path);
1638 static gboolean accel_default_scroll(GtkTreeView *data)
1642 GtkTreeViewColumn *column;
1644 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(accel_store), &iter);
1645 path = gtk_tree_model_get_path(GTK_TREE_MODEL(accel_store), &iter);
1646 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1648 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1652 gtk_tree_path_free(path);
1654 return(G_SOURCE_REMOVE);
1657 static void accel_default_cb(GtkWidget *, gpointer data)
1659 accel_store_populate();
1661 g_idle_add(reinterpret_cast<GSourceFunc>(accel_default_scroll), data);
1664 void accel_clear_selection(GtkTreeModel *, GtkTreePath *, GtkTreeIter *iter, gpointer)
1666 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1669 void accel_reset_selection(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
1672 gchar *accel_path, *accel;
1674 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, -1);
1675 gtk_accel_map_lookup_entry(accel_path, &key);
1676 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1678 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, accel);
1680 gtk_tree_store_set(accel_store, iter, AE_KEY, accel, -1);
1685 static void accel_clear_cb(GtkWidget *, gpointer data)
1687 GtkTreeSelection *selection;
1689 if (!accel_store) return;
1690 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1691 gtk_tree_selection_selected_foreach(selection, &accel_clear_selection, nullptr);
1694 static void accel_reset_cb(GtkWidget *, gpointer data)
1696 GtkTreeSelection *selection;
1698 if (!accel_store) return;
1699 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1700 gtk_tree_selection_selected_foreach(selection, &accel_reset_selection, nullptr);
1705 static GtkWidget *scrolled_notebook_page(GtkWidget *notebook, const gchar *title)
1709 GtkWidget *scrolled;
1710 GtkWidget *viewport;
1712 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
1713 gtk_container_set_border_width(GTK_CONTAINER(scrolled), PREF_PAD_BORDER);
1714 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1715 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1716 label = gtk_label_new(title);
1717 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, label);
1718 gtk_widget_show(scrolled);
1720 viewport = gtk_viewport_new(nullptr, nullptr);
1721 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1722 gtk_container_add(GTK_CONTAINER(scrolled), viewport);
1723 gtk_widget_show(viewport);
1725 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1726 gtk_container_add(GTK_CONTAINER(viewport), vbox);
1727 gtk_widget_show(vbox);
1732 static void cache_standard_cb(GtkWidget *widget, gpointer)
1734 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1736 c_options->thumbnails.spec_standard =TRUE;
1737 c_options->thumbnails.cache_into_dirs = FALSE;
1741 static void cache_geeqie_cb(GtkWidget *widget, gpointer)
1743 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1745 c_options->thumbnails.spec_standard =FALSE;
1746 c_options->thumbnails.cache_into_dirs = FALSE;
1750 static void cache_local_cb(GtkWidget *widget, gpointer)
1752 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1754 c_options->thumbnails.cache_into_dirs = TRUE;
1755 c_options->thumbnails.spec_standard =FALSE;
1759 static void help_search_engine_entry_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1761 if (pos == GTK_ENTRY_ICON_PRIMARY)
1763 gtk_entry_set_text(GTK_ENTRY(userdata), HELP_SEARCH_ENGINE);
1767 gtk_entry_set_text(GTK_ENTRY(userdata), "");
1771 static void star_rating_star_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1773 gchar *rating_symbol;
1775 if (pos == GTK_ENTRY_ICON_PRIMARY)
1777 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_STAR);
1778 gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1779 g_free(rating_symbol);
1783 gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1784 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1785 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1789 static void star_rating_rejected_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1791 gchar *rating_symbol;
1793 if (pos == GTK_ENTRY_ICON_PRIMARY)
1795 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_REJECTED);
1796 gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1797 g_free(rating_symbol);
1801 gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1802 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1803 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1807 static guint star_rating_symbol_test(GtkWidget *, gpointer data)
1809 auto hbox = static_cast<GtkContainer *>(data);
1810 GString *str = g_string_new(nullptr);
1811 GtkEntry *hex_code_entry;
1812 gchar *hex_code_full;
1815 guint64 hex_value = 0;
1817 list = gtk_container_get_children(hbox);
1819 hex_code_entry = static_cast<GtkEntry *>(g_list_nth_data(list, 2));
1820 hex_code_full = g_strdup(gtk_entry_get_text(hex_code_entry));
1822 hex_code = g_strsplit(hex_code_full, "+", 2);
1823 if (hex_code[0] && hex_code[1])
1825 hex_value = strtoull(hex_code[1], nullptr, 16);
1827 if (!hex_value || hex_value > 0x10FFFF)
1829 hex_value = 0x003F; // Unicode 'Question Mark'
1831 str = g_string_append_unichar(str, static_cast<gunichar>(hex_value));
1832 gtk_label_set_text(static_cast<GtkLabel *>(g_list_nth_data(list, 1)), str->str);
1834 g_strfreev(hex_code);
1835 g_string_free(str, TRUE);
1836 g_free(hex_code_full);
1841 static void star_rating_star_test_cb(GtkWidget *widget, gpointer data)
1843 guint64 star_symbol;
1845 star_symbol = star_rating_symbol_test(widget, data);
1846 c_options->star_rating.star = star_symbol;
1849 static void star_rating_rejected_test_cb(GtkWidget *widget, gpointer data)
1851 guint64 rejected_symbol;
1853 rejected_symbol = star_rating_symbol_test(widget, data);
1854 c_options->star_rating.rejected = rejected_symbol;
1857 /* general options tab */
1858 static void timezone_database_install_cb(GtkWidget *widget, gpointer data);
1862 GCancellable *cancellable;
1864 GtkWidget *progress;
1866 GFile *timezone_database_gq;
1867 gchar *timezone_database_user;
1870 static void config_tab_general(GtkWidget *notebook)
1875 GtkWidget *group_frame;
1876 GtkWidget *subgroup;
1878 GtkWidget *ct_button;
1881 gint hours, minutes, remainder;
1883 GtkWidget *star_rating_entry;
1885 gchar *rating_symbol;
1888 gchar *download_locn;
1889 GNetworkMonitor *net_mon;
1890 GSocketConnectable *tz_org;
1891 gboolean internet_available = FALSE;
1894 vbox = scrolled_notebook_page(notebook, _("General"));
1896 group = pref_group_new(vbox, FALSE, _("Thumbnails"), GTK_ORIENTATION_VERTICAL);
1898 table = pref_table_new(group, 2, 2, FALSE, FALSE);
1899 add_thumb_size_menu(table, 0, 0, _("Size:"));
1900 add_quality_menu(table, 0, 1, _("Quality:"), options->thumbnails.quality, &c_options->thumbnails.quality);
1902 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1903 pref_label_new(hbox, _("Custom size: "));
1904 pref_spin_new_int(hbox, _("Width:"), nullptr, 1, 512, 1, options->thumbnails.max_width, &c_options->thumbnails.max_width);
1905 pref_spin_new_int(hbox, _("Height:"), nullptr, 1, 512, 1, options->thumbnails.max_height, &c_options->thumbnails.max_height);
1907 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails and sim. files"),
1908 options->thumbnails.enable_caching, &c_options->thumbnails.enable_caching);
1910 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1911 pref_checkbox_link_sensitivity(ct_button, subgroup);
1913 c_options->thumbnails.spec_standard = options->thumbnails.spec_standard;
1914 c_options->thumbnails.cache_into_dirs = options->thumbnails.cache_into_dirs;
1915 group_frame = pref_frame_new(subgroup, TRUE, _("Use Geeqie thumbnail style and cache"),
1916 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1917 button = pref_radiobutton_new(group_frame, nullptr, get_thumbnails_cache_dir(),
1918 !options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
1919 G_CALLBACK(cache_geeqie_cb), nullptr);
1921 group_frame = pref_frame_new(subgroup, TRUE,
1922 _("Store thumbnails local to image folder (non-standard)"),
1923 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1924 pref_radiobutton_new(group_frame, button, "*/.thumbnails",
1925 !options->thumbnails.spec_standard && options->thumbnails.cache_into_dirs,
1926 G_CALLBACK(cache_local_cb), nullptr);
1928 group_frame = pref_frame_new(subgroup, TRUE,
1929 _("Use standard thumbnail style and cache, shared with other applications"),
1930 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1931 pref_radiobutton_new(group_frame, button, get_thumbnails_standard_cache_dir(),
1932 options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
1933 G_CALLBACK(cache_standard_cb), nullptr);
1935 pref_checkbox_new_int(group, _("Use EXIF thumbnails when available (EXIF thumbnails may be outdated)"),
1936 options->thumbnails.use_exif, &c_options->thumbnails.use_exif);
1938 pref_checkbox_new_int(group, _("Thumbnail color management"),
1939 options->thumbnails.use_color_management, &c_options->thumbnails.use_color_management);
1941 spin = pref_spin_new_int(group, _("Collection preview:"), nullptr,
1943 options->thumbnails.collection_preview, &c_options->thumbnails.collection_preview);
1944 gtk_widget_set_tooltip_text(spin, _("The maximum number of thumbnails shown in a Collection preview montage"));
1946 #ifdef HAVE_FFMPEGTHUMBNAILER_METADATA
1947 pref_checkbox_new_int(group, _("Use embedded metadata in video files as thumbnails when available"),
1948 options->thumbnails.use_ft_metadata, &c_options->thumbnails.use_ft_metadata);
1951 pref_spacer(group, PREF_PAD_GROUP);
1953 group = pref_group_new(vbox, FALSE, _("Star Rating"), GTK_ORIENTATION_VERTICAL);
1955 c_options->star_rating.star = options->star_rating.star;
1956 c_options->star_rating.rejected = options->star_rating.rejected;
1958 str = g_string_new(nullptr);
1959 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1960 pref_label_new(hbox, _("Star character: "));
1961 str = g_string_append_unichar(str, options->star_rating.star);
1962 pref_label_new(hbox, g_strdup(str->str));
1963 rating_symbol = g_strdup_printf("U+%X", options->star_rating.star);
1964 star_rating_entry = gtk_entry_new();
1965 gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
1966 gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
1967 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
1968 gtk_widget_show(star_rating_entry);
1969 button = pref_button_new(nullptr, nullptr, _("Set"),
1970 G_CALLBACK(star_rating_star_test_cb), hbox);
1971 gtk_widget_set_tooltip_text(button, _("Display selected character"));
1972 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
1973 gtk_widget_show(button);
1974 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."));
1975 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
1976 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
1977 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
1978 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
1979 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
1980 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
1981 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
1982 GTK_ENTRY_ICON_PRIMARY, _("Default"));
1983 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
1984 G_CALLBACK(star_rating_star_icon_cb),
1987 g_string_free(str, TRUE);
1988 g_free(rating_symbol);
1990 str = g_string_new(nullptr);
1991 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1992 pref_label_new(hbox, _("Rejected character: "));
1993 str = g_string_append_unichar(str, options->star_rating.rejected);
1994 pref_label_new(hbox, g_strdup(str->str));
1995 rating_symbol = g_strdup_printf("U+%X", options->star_rating.rejected);
1996 star_rating_entry = gtk_entry_new();
1997 gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
1998 gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
1999 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2000 gtk_widget_show(star_rating_entry);
2001 button = pref_button_new(nullptr, nullptr, _("Set"),
2002 G_CALLBACK(star_rating_rejected_test_cb), hbox);
2003 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2004 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2005 gtk_widget_show(button);
2006 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."));
2007 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2008 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2009 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2010 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2011 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2012 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2013 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2014 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2015 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2016 G_CALLBACK(star_rating_rejected_icon_cb),
2019 g_string_free(str, TRUE);
2020 g_free(rating_symbol);
2022 pref_spacer(group, PREF_PAD_GROUP);
2024 group = pref_group_new(vbox, FALSE, _("Slide show"), GTK_ORIENTATION_VERTICAL);
2026 c_options->slideshow.delay = options->slideshow.delay;
2027 hours = options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2028 remainder = options->slideshow.delay % (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2029 minutes = remainder / (60 * SLIDESHOW_SUBSECOND_PRECISION);
2030 seconds = static_cast<gdouble>(remainder % (60 * SLIDESHOW_SUBSECOND_PRECISION)) /
2031 SLIDESHOW_SUBSECOND_PRECISION;
2033 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2035 spin = pref_spin_new(hbox, _("Delay between image change hrs:mins:secs.dec"), nullptr,
2037 options->slideshow.delay ? hours : 0.0,
2038 G_CALLBACK(slideshow_delay_hours_cb), nullptr);
2039 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2040 spin = pref_spin_new(hbox, ":" , nullptr,
2042 options->slideshow.delay ? minutes: 0.0,
2043 G_CALLBACK(slideshow_delay_minutes_cb), nullptr);
2044 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2045 spin = pref_spin_new(hbox, ":", nullptr,
2046 SLIDESHOW_MIN_SECONDS, 59, 1.0, 1,
2047 options->slideshow.delay ? seconds : 10.0,
2048 G_CALLBACK(slideshow_delay_seconds_cb), nullptr);
2049 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2051 pref_checkbox_new_int(group, _("Random"), options->slideshow.random, &c_options->slideshow.random);
2052 pref_checkbox_new_int(group, _("Repeat"), options->slideshow.repeat, &c_options->slideshow.repeat);
2054 pref_spacer(group, PREF_PAD_GROUP);
2056 group = pref_group_new(vbox, FALSE, _("Image loading and caching"), GTK_ORIENTATION_VERTICAL);
2058 pref_spin_new_int(group, _("Decoded image cache size (MiB):"), nullptr,
2059 0, 99999, 1, options->image.image_cache_max, &c_options->image.image_cache_max);
2060 pref_checkbox_new_int(group, _("Preload next image"),
2061 options->image.enable_read_ahead, &c_options->image.enable_read_ahead);
2063 pref_checkbox_new_int(group, _("Refresh on file change"),
2064 options->update_on_time_change, &c_options->update_on_time_change);
2067 pref_spacer(group, PREF_PAD_GROUP);
2069 group = pref_group_new(vbox, FALSE, _("Expand menu and toolbar"), GTK_ORIENTATION_VERTICAL);
2071 pref_checkbox_new_int(group, _("Expand menu and toolbar (NOTE! Geeqie must be restarted for change to take effect)"),
2072 options->expand_menu_toolbar, &c_options->expand_menu_toolbar);
2073 gtk_widget_set_tooltip_text(group, _("Expand the menu and toolbar to the full width of the window"));
2075 pref_spacer(group, PREF_PAD_GROUP);
2077 group = pref_group_new(vbox, FALSE, _("Hide Selectable Bars"), GTK_ORIENTATION_VERTICAL);
2079 pref_checkbox_new_int(group, _("Menu bar"),
2080 options->selectable_bars.menu_bar, &c_options->selectable_bars.menu_bar);
2082 pref_checkbox_new_int(group, _("Tool bar"),
2083 options->selectable_bars.tool_bar, &c_options->selectable_bars.tool_bar);
2085 pref_checkbox_new_int(group, _("Status bar"),
2086 options->selectable_bars.status_bar, &c_options->selectable_bars.status_bar);
2087 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"));
2089 pref_spacer(group, PREF_PAD_GROUP);
2091 if (g_getenv("APPDIR") && strstr(g_getenv("APPDIR"), "/tmp/.mount_Geeqie"))
2093 group = pref_group_new(vbox, FALSE, _("AppImage updates notifications"), GTK_ORIENTATION_VERTICAL);
2094 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2095 pref_checkbox_new_int(group, _("Enable"), options->appimage_notifications, &c_options->appimage_notifications);
2096 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"));
2098 pref_spacer(group, PREF_PAD_GROUP);
2102 net_mon = g_network_monitor_get_default();
2103 tz_org = g_network_address_parse_uri(TIMEZONE_DATABASE_WEB, 80, nullptr);
2106 internet_available = g_network_monitor_can_reach(net_mon, tz_org, nullptr, nullptr);
2107 g_object_unref(tz_org);
2110 group = pref_group_new(vbox, FALSE, _("Timezone database"), GTK_ORIENTATION_VERTICAL);
2111 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2113 if (!internet_available)
2115 gtk_widget_set_sensitive(group, FALSE);
2118 tz = g_new0(TZData, 1);
2120 path = path_from_utf8(TIMEZONE_DATABASE_WEB);
2121 basename = g_path_get_basename(path);
2122 tz->timezone_database_user = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
2126 if (isfile(tz->timezone_database_user))
2128 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Update"), G_CALLBACK(timezone_database_install_cb), tz);
2132 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Install"), G_CALLBACK(timezone_database_install_cb), tz);
2135 download_locn = g_strconcat(_("Download database from: "), TIMEZONE_DATABASE_WEB, NULL);
2136 pref_label_new(GTK_WIDGET(hbox), download_locn);
2137 g_free(download_locn);
2139 if (!internet_available)
2141 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"));
2145 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"));
2147 gtk_widget_show(button);
2149 pref_spacer(group, PREF_PAD_GROUP);
2151 group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL);
2153 help_search_engine_entry = gtk_entry_new();
2154 gtk_entry_set_text(GTK_ENTRY(help_search_engine_entry), options->help_search_engine);
2155 gtk_box_pack_start(GTK_BOX(group), help_search_engine_entry, FALSE, FALSE, 0);
2156 gtk_widget_show(help_search_engine_entry);
2158 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"));
2160 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2161 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2162 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2163 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2164 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2165 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2166 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2167 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2168 g_signal_connect(GTK_ENTRY(help_search_engine_entry), "icon-press",
2169 G_CALLBACK(help_search_engine_entry_icon_cb),
2170 help_search_engine_entry);
2174 static void config_tab_image(GtkWidget *notebook)
2179 GtkWidget *ct_button;
2180 GtkWidget *enlargement_button;
2184 vbox = scrolled_notebook_page(notebook, _("Image"));
2186 group = pref_group_new(vbox, FALSE, _("Zoom"), GTK_ORIENTATION_VERTICAL);
2188 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2189 add_quality_menu(table, 0, 0, _("Quality:"), options->image.zoom_quality, &c_options->image.zoom_quality);
2191 pref_checkbox_new_int(group, _("Two pass rendering (apply HQ zoom and color correction in second pass)"),
2192 options->image.zoom_2pass, &c_options->image.zoom_2pass);
2194 c_options->image.zoom_increment = options->image.zoom_increment;
2195 spin = pref_spin_new(group, _("Zoom increment:"), nullptr,
2196 0.01, 4.0, 0.01, 2, static_cast<gdouble>(options->image.zoom_increment) / 100.0,
2197 G_CALLBACK(zoom_increment_cb), nullptr);
2198 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2200 c_options->image.zoom_style = options->image.zoom_style;
2201 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2202 add_zoom_style_selection_menu(table, 0, 0, _("Zoom style:"), options->image.zoom_style, &c_options->image.zoom_style);
2204 group = pref_group_new(vbox, FALSE, _("Fit image to window"), GTK_ORIENTATION_VERTICAL);
2206 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2207 enlargement_button = pref_checkbox_new_int(hbox, _("Allow enlargement of image (max. size in %)"),
2208 options->image.zoom_to_fit_allow_expand, &c_options->image.zoom_to_fit_allow_expand);
2209 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2211 options->image.max_enlargement_size, &c_options->image.max_enlargement_size);
2212 pref_checkbox_link_sensitivity(enlargement_button, spin);
2213 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."));
2215 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2216 ct_button = pref_checkbox_new_int(hbox, _("Virtual window size (% of actual window):"),
2217 options->image.limit_autofit_size, &c_options->image.limit_autofit_size);
2218 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2220 options->image.max_autofit_size, &c_options->image.max_autofit_size);
2221 pref_checkbox_link_sensitivity(ct_button, spin);
2222 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."));
2224 group = pref_group_new(vbox, FALSE, _("Tile size"), GTK_ORIENTATION_VERTICAL);
2226 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2227 spin = pref_spin_new_int(hbox, _("Pixels"), _("(Requires restart)"),
2229 options->image.tile_size, &c_options->image.tile_size);
2230 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."));
2232 group = pref_group_new(vbox, FALSE, _("Appearance"), GTK_ORIENTATION_VERTICAL);
2234 pref_checkbox_new_int(group, _("Use custom border color in window mode"),
2235 options->image.use_custom_border_color, &c_options->image.use_custom_border_color);
2237 pref_checkbox_new_int(group, _("Use custom border color in fullscreen mode"),
2238 options->image.use_custom_border_color_in_fullscreen, &c_options->image.use_custom_border_color_in_fullscreen);
2240 pref_color_button_new(group, _("Border color"), &options->image.border_color,
2241 G_CALLBACK(pref_color_button_set_cb), &c_options->image.border_color);
2243 c_options->image.border_color = options->image.border_color;
2245 pref_color_button_new(group, _("Alpha channel color 1"), &options->image.alpha_color_1,
2246 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_1);
2248 pref_color_button_new(group, _("Alpha channel color 2"), &options->image.alpha_color_2,
2249 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_2);
2251 c_options->image.alpha_color_1 = options->image.alpha_color_1;
2252 c_options->image.alpha_color_2 = options->image.alpha_color_2;
2257 static void save_default_window_layout_cb(GtkWidget *, gpointer)
2259 LayoutWindow *lw = nullptr;
2260 gchar *default_path;
2263 /* Get current lw */
2266 tmp_id = lw->options.id;
2267 lw->options.id = g_strdup("lw_default");
2269 default_path = g_build_filename(get_rc_dir(), DEFAULT_WINDOW_LAYOUT, NULL);
2270 save_default_layout_options_to_file(default_path, options, lw);
2271 g_free(lw->options.id);
2272 lw->options.id = tmp_id;
2273 g_free(default_path);
2276 static gboolean popover_cb(gpointer data)
2278 auto popover = static_cast<GtkPopover *>(data);
2280 gtk_popover_popdown(popover);
2285 static void default_layout_changed_cb(GtkWidget *, GtkPopover *popover)
2287 gtk_popover_popup(popover);
2289 g_timeout_add(2000, popover_cb, popover);
2292 static GtkWidget *create_popover(GtkWidget *parent, GtkWidget *child, GtkPositionType pos)
2296 popover = gtk_popover_new(parent);
2297 gtk_popover_set_position(GTK_POPOVER (popover), pos);
2298 gtk_container_add (GTK_CONTAINER(popover), child);
2299 gtk_container_set_border_width(GTK_CONTAINER (popover), 6);
2300 gtk_widget_show (child);
2305 static void config_tab_windows(GtkWidget *notebook)
2310 GtkWidget *subgroup;
2312 GtkWidget *ct_button;
2315 vbox = scrolled_notebook_page(notebook, _("Windows"));
2317 group = pref_group_new(vbox, FALSE, _("State"), GTK_ORIENTATION_VERTICAL);
2319 ct_button = pref_checkbox_new_int(group, _("Remember session"),
2320 options->save_window_positions, &c_options->save_window_positions);
2322 button = pref_checkbox_new_int(group, _("Use saved window positions also for new windows"),
2323 options->use_saved_window_positions_for_new_windows, &c_options->use_saved_window_positions_for_new_windows);
2324 pref_checkbox_link_sensitivity(ct_button, button);
2326 button = pref_checkbox_new_int(group, _("Remember window workspace"),
2327 options->save_window_workspace, &c_options->save_window_workspace);
2328 pref_checkbox_link_sensitivity(ct_button, button);
2330 pref_checkbox_new_int(group, _("Remember tool state (float/hidden)"),
2331 options->tools_restore_state, &c_options->tools_restore_state);
2333 pref_checkbox_new_int(group, _("Remember dialog window positions"),
2334 options->save_dialog_window_positions, &c_options->save_dialog_window_positions);
2336 pref_checkbox_new_int(group, _("Show window IDs"),
2337 options->show_window_ids, &c_options->show_window_ids);
2339 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2340 pref_label_new(subgroup, _("Use current layout for default: "));
2341 button = pref_button_new(subgroup, nullptr, _("Set"), G_CALLBACK(save_default_window_layout_cb), nullptr);
2345 popover = create_popover(button, gtk_label_new(_("Current window layout\nhas been set as default")), GTK_POS_TOP);
2346 gtk_popover_set_modal(GTK_POPOVER (popover), FALSE);
2347 g_signal_connect(button, "clicked", G_CALLBACK(default_layout_changed_cb), popover);
2349 group = pref_group_new(vbox, FALSE, _("Size"), GTK_ORIENTATION_VERTICAL);
2351 pref_checkbox_new_int(group, _("Fit window to image when tools are hidden/floating"),
2352 options->image.fit_window_to_image, &c_options->image.fit_window_to_image);
2354 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2355 ct_button = pref_checkbox_new_int(hbox, _("Limit size when auto-sizing window (%):"),
2356 options->image.limit_window_size, &c_options->image.limit_window_size);
2357 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2359 options->image.max_window_size, &c_options->image.max_window_size);
2360 pref_checkbox_link_sensitivity(ct_button, spin);
2362 group = pref_group_new(vbox, FALSE, _("Full screen"), GTK_ORIENTATION_VERTICAL);
2364 c_options->fullscreen.screen = options->fullscreen.screen;
2365 c_options->fullscreen.above = options->fullscreen.above;
2366 hbox = fullscreen_prefs_selection_new(_("Location:"), &c_options->fullscreen.screen, &c_options->fullscreen.above);
2367 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2368 gtk_widget_show(hbox);
2370 pref_checkbox_new_int(group, _("Smooth image flip"),
2371 options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
2372 pref_checkbox_new_int(group, _("Disable screen saver"),
2373 options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
2376 #define PRE_FORMATTED_COLUMNS 5
2377 static void config_tab_osd(GtkWidget *notebook)
2383 GtkWidget *image_overlay_template_view;
2384 GtkWidget *scrolled;
2385 GtkWidget *scrolled_pre_formatted;
2386 GtkTextBuffer *buffer;
2388 GtkWidget *subgroup;
2390 vbox = scrolled_notebook_page(notebook, _("OSD"));
2392 image_overlay_template_view = gtk_text_view_new();
2394 group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
2396 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2398 scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
2399 gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
2400 gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
2401 gtk_widget_show(scrolled_pre_formatted);
2402 gtk_widget_show(subgroup);
2404 pref_line(group, PREF_PAD_GAP);
2406 pref_label_new(group, _("Image overlay template"));
2408 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
2409 gtk_widget_set_size_request(scrolled, 200, 150);
2410 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2411 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
2412 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2413 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
2414 gtk_widget_show(scrolled);
2416 gtk_widget_set_tooltip_markup(image_overlay_template_view,
2417 _("Extensive formatting options are shown in the Help file"));
2419 gtk_container_add(GTK_CONTAINER(scrolled), image_overlay_template_view);
2420 gtk_widget_show(image_overlay_template_view);
2422 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2424 button = pref_button_new(nullptr, GQ_ICON_SELECT_FONT, _("Font"),
2425 G_CALLBACK(image_overlay_set_font_cb), notebook);
2427 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2428 gtk_widget_show(button);
2430 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Text"),
2431 G_CALLBACK(image_overlay_set_text_colour_cb), nullptr);
2432 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2433 gtk_widget_show(button);
2435 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Background"),
2436 G_CALLBACK(image_overlay_set_background_colour_cb), nullptr);
2437 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2438 gtk_widget_show(button);
2439 image_overlay_set_text_colours();
2441 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2442 G_CALLBACK(image_overlay_default_template_cb), image_overlay_template_view);
2443 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2444 gtk_widget_show(button);
2446 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
2447 G_CALLBACK(image_overlay_help_cb), nullptr);
2448 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2449 gtk_widget_show(button);
2451 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_overlay_template_view));
2452 if (options->image_overlay.template_string) gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
2453 g_signal_connect(G_OBJECT(buffer), "changed",
2454 G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
2456 pref_line(group, PREF_PAD_GAP);
2458 group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL);
2459 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2460 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2461 gtk_widget_show(hbox);
2462 label = gtk_label_new(_("%Exif.Image.Orientation%"));
2463 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2464 gtk_widget_show(label);
2465 pref_spacer(group,TRUE);
2467 group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL);
2468 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2469 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2470 gtk_widget_show(hbox);
2471 label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
2472 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2473 gtk_widget_show(label);
2474 pref_spacer(group,TRUE);
2476 group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL);
2477 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2478 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2479 gtk_widget_show(hbox);
2480 label = gtk_label_new(_("%path:39%"));
2481 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2482 gtk_widget_show(label);
2483 pref_spacer(group,TRUE);
2485 group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL);
2486 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2487 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2488 gtk_widget_show(hbox);
2489 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%"));
2490 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2491 gtk_widget_show(label);
2492 pref_spacer(group,TRUE);
2494 group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL);
2495 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2496 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2497 gtk_widget_show(hbox);
2498 label = gtk_label_new(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
2499 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2500 gtk_widget_show(label);
2503 static GtkTreeModel *create_class_model()
2505 GtkListStore *model;
2509 /* create list store */
2510 model = gtk_list_store_new(1, G_TYPE_STRING);
2511 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
2513 gtk_list_store_append(model, &iter);
2514 gtk_list_store_set(model, &iter, 0, _(format_class_list[i]), -1);
2516 return GTK_TREE_MODEL (model);
2521 static gint filter_table_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
2523 gint n = GPOINTER_TO_INT(data);
2525 FilterEntry *filter_a;
2526 FilterEntry *filter_b;
2528 gtk_tree_model_get(model, a, 0, &filter_a, -1);
2529 gtk_tree_model_get(model, b, 0, &filter_b, -1);
2533 case FILETYPES_COLUMN_ENABLED:
2535 ret = filter_a->enabled - filter_b->enabled;
2538 case FILETYPES_COLUMN_FILTER:
2540 ret = g_utf8_collate(filter_a->extensions, filter_b->extensions);
2543 case FILETYPES_COLUMN_DESCRIPTION:
2545 ret = g_utf8_collate(filter_a->description, filter_b->description);
2548 case FILETYPES_COLUMN_CLASS:
2550 ret = g_strcmp0(format_class_list[filter_a->file_class], format_class_list[filter_b->file_class]);
2553 case FILETYPES_COLUMN_WRITABLE:
2555 ret = filter_a->writable - filter_b->writable;
2558 case FILETYPES_COLUMN_SIDECAR:
2560 ret = filter_a->allow_sidecar - filter_b->allow_sidecar;
2564 g_return_val_if_reached(0);
2570 static gboolean search_function_cb(GtkTreeModel *model, gint, const gchar *key, GtkTreeIter *iter, gpointer)
2573 gboolean ret = TRUE;
2575 gtk_tree_model_get(model, iter, 0, &fe, -1);
2577 if (g_strstr_len(fe->extensions, -1, key))
2585 static void config_tab_files(GtkWidget *notebook)
2592 GtkWidget *ct_button;
2593 GtkWidget *scrolled;
2594 GtkWidget *filter_view;
2595 GtkCellRenderer *renderer;
2596 GtkTreeSelection *selection;
2597 GtkTreeViewColumn *column;
2599 vbox = scrolled_notebook_page(notebook, _("File Filters"));
2601 group = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2603 pref_checkbox_new_int(group, _("Show hidden files or folders"),
2604 options->file_filter.show_hidden_files, &c_options->file_filter.show_hidden_files);
2605 pref_checkbox_new_int(group, _("Show parent folder (..)"),
2606 options->file_filter.show_parent_directory, &c_options->file_filter.show_parent_directory);
2607 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);
2608 pref_checkbox_new_int(group, _("Disable file extension checks"),
2609 options->file_filter.disable_file_extension_checks, &c_options->file_filter.disable_file_extension_checks);
2611 ct_button = pref_checkbox_new_int(group, _("Disable File Filtering"),
2612 options->file_filter.disable, &c_options->file_filter.disable);
2615 group = pref_group_new(vbox, FALSE, _("Grouping sidecar extensions"), GTK_ORIENTATION_VERTICAL);
2617 sidecar_ext_entry = gtk_entry_new();
2618 gtk_entry_set_text(GTK_ENTRY(sidecar_ext_entry), options->sidecar.ext);
2619 gtk_box_pack_start(GTK_BOX(group), sidecar_ext_entry, FALSE, FALSE, 0);
2620 gtk_widget_show(sidecar_ext_entry);
2622 group = pref_group_new(vbox, TRUE, _("File types"), GTK_ORIENTATION_VERTICAL);
2624 frame = pref_group_parent(group);
2625 g_signal_connect(G_OBJECT(ct_button), "toggled",
2626 G_CALLBACK(filter_disable_cb), frame);
2627 gtk_widget_set_sensitive(frame, !options->file_filter.disable);
2629 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
2630 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2631 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2632 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2633 gtk_widget_show(scrolled);
2635 filter_store = gtk_list_store_new(1, G_TYPE_POINTER);
2636 filter_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
2637 g_object_unref(filter_store);
2638 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
2639 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2641 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), FALSE);
2643 column = gtk_tree_view_column_new();
2644 gtk_tree_view_column_set_title(column, _("Enabled"));
2645 gtk_tree_view_column_set_resizable(column, TRUE);
2647 renderer = gtk_cell_renderer_toggle_new();
2648 g_signal_connect(G_OBJECT(renderer), "toggled",
2649 G_CALLBACK(filter_store_enable_cb), filter_store);
2650 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2651 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2652 GINT_TO_POINTER(FE_ENABLE), nullptr);
2653 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);
2654 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_ENABLED);
2655 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2657 column = gtk_tree_view_column_new();
2658 gtk_tree_view_column_set_title(column, _("Filter"));
2659 gtk_tree_view_column_set_resizable(column, TRUE);
2660 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);
2661 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_FILTER);
2663 renderer = gtk_cell_renderer_text_new();
2664 g_signal_connect(G_OBJECT(renderer), "edited",
2665 G_CALLBACK(filter_store_ext_edit_cb), filter_store);
2666 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2667 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2668 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2669 GINT_TO_POINTER(FE_EXTENSION), nullptr);
2670 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2672 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), TRUE);
2673 gtk_tree_view_set_search_column(GTK_TREE_VIEW(filter_view), FILETYPES_COLUMN_FILTER);
2674 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(filter_view), search_function_cb, nullptr, nullptr);
2676 column = gtk_tree_view_column_new();
2677 gtk_tree_view_column_set_title(column, _("Description"));
2678 gtk_tree_view_column_set_resizable(column, TRUE);
2679 gtk_tree_view_column_set_fixed_width(column, 200);
2680 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2682 renderer = gtk_cell_renderer_text_new();
2683 g_signal_connect(G_OBJECT(renderer), "edited",
2684 G_CALLBACK(filter_store_desc_edit_cb), filter_store);
2685 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2686 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2687 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2688 GINT_TO_POINTER(FE_DESCRIPTION), nullptr);
2689 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2690 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);
2691 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_DESCRIPTION);
2693 column = gtk_tree_view_column_new();
2694 gtk_tree_view_column_set_title(column, _("Class"));
2695 gtk_tree_view_column_set_resizable(column, TRUE);
2696 renderer = gtk_cell_renderer_combo_new();
2697 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE,
2698 "model", create_class_model(),
2703 g_signal_connect(G_OBJECT(renderer), "edited",
2704 G_CALLBACK(filter_store_class_edit_cb), filter_store);
2705 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2706 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2707 GINT_TO_POINTER(FE_CLASS), nullptr);
2708 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2709 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);
2710 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_CLASS);
2712 column = gtk_tree_view_column_new();
2713 gtk_tree_view_column_set_title(column, _("Writable"));
2714 gtk_tree_view_column_set_resizable(column, FALSE);
2715 renderer = gtk_cell_renderer_toggle_new();
2716 g_signal_connect(G_OBJECT(renderer), "toggled",
2717 G_CALLBACK(filter_store_writable_cb), filter_store);
2718 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2719 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2720 GINT_TO_POINTER(FE_WRITABLE), nullptr);
2721 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2722 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);
2723 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_WRITABLE);
2725 column = gtk_tree_view_column_new();
2726 gtk_tree_view_column_set_title(column, _("Sidecar is allowed"));
2727 gtk_tree_view_column_set_resizable(column, FALSE);
2728 renderer = gtk_cell_renderer_toggle_new();
2729 g_signal_connect(G_OBJECT(renderer), "toggled",
2730 G_CALLBACK(filter_store_sidecar_cb), filter_store);
2731 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2732 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2733 GINT_TO_POINTER(FE_ALLOW_SIDECAR), nullptr);
2734 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2735 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);
2736 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_SIDECAR);
2738 filter_store_populate();
2739 gtk_container_add(GTK_CONTAINER(scrolled), filter_view);
2740 gtk_widget_show(filter_view);
2742 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2744 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2745 G_CALLBACK(filter_default_cb), filter_view);
2746 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2747 gtk_widget_show(button);
2749 button = pref_button_new(nullptr, GQ_ICON_REMOVE, _("Remove"),
2750 G_CALLBACK(filter_remove_cb), filter_view);
2751 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2752 gtk_widget_show(button);
2754 button = pref_button_new(nullptr, GQ_ICON_ADD, _("Add"),
2755 G_CALLBACK(filter_add_cb), filter_view);
2756 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2757 gtk_widget_show(button);
2761 static void config_tab_metadata(GtkWidget *notebook)
2766 GtkWidget *ct_button;
2768 GtkWidget *tmp_widget;
2770 GtkWidget *text_label;
2772 vbox = scrolled_notebook_page(notebook, _("Metadata"));
2775 group = pref_group_new(vbox, FALSE, _("Metadata writing sequence"), GTK_ORIENTATION_VERTICAL);
2777 label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2779 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."));
2780 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2781 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2783 gtk_widget_set_tooltip_text(label, _("A flowchart of the sequence is shown in the Help file"));
2785 ct_button = pref_checkbox_new_int(group, "", options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2786 text_label = gtk_bin_get_child(GTK_BIN(ct_button));
2787 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"));
2788 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2790 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)"));
2791 gtk_widget_set_tooltip_markup(ct_button, markup);
2795 gtk_widget_set_sensitive(ct_button, FALSE);
2798 tmp_widget = pref_checkbox_new_int(group, "", options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2799 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2800 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)"));
2801 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2804 label = pref_label_new(group, "");
2805 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(), "/");
2806 gtk_label_set_markup (GTK_LABEL(label), markup);
2809 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2810 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2811 gtk_widget_set_margin_start(label, 22);
2812 pref_spacer(group, PREF_PAD_GROUP);
2814 group = pref_group_new(vbox, FALSE, _("Step 1 Options:"), GTK_ORIENTATION_VERTICAL);
2816 gtk_widget_set_sensitive(group, FALSE);
2819 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2820 pref_checkbox_link_sensitivity(ct_button, hbox);
2822 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);
2823 gtk_widget_set_tooltip_text(tmp_widget, _("A simplified conversion list is in the Help file"));
2825 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);
2827 pref_checkbox_new_int(hbox, _("Ask before writing to image files"), options->metadata.confirm_write, &c_options->metadata.confirm_write);
2829 tmp_widget= pref_checkbox_new_int(hbox, "", options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2830 gtk_widget_set_tooltip_text(tmp_widget, _("This file naming convention is used by Darktable"));
2831 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2832 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", ")");
2833 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2836 pref_spacer(group, PREF_PAD_GROUP);
2838 group = pref_group_new(vbox, FALSE, _("Steps 2 and 3 Option:"), GTK_ORIENTATION_VERTICAL);
2840 gtk_widget_set_sensitive(group, FALSE);
2843 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);
2845 pref_spacer(group, PREF_PAD_GROUP);
2847 group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2848 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);
2849 gtk_widget_set_tooltip_text(tmp_widget, _("See the Help file for a list of the tags used"));
2851 tmp_widget = pref_checkbox_new_int(group, _("Permit Keywords to be case-sensitive"), options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2852 gtk_widget_set_tooltip_text(tmp_widget, _("When selected, \"Place\" and \"place\" are two different keywords"));
2854 ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"), options->metadata.write_orientation, &c_options->metadata.write_orientation);
2855 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"));
2858 gtk_widget_set_sensitive(ct_button, FALSE);
2861 pref_spacer(group, PREF_PAD_GROUP);
2863 group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2865 ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"), options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
2867 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2868 pref_checkbox_link_sensitivity(ct_button, hbox);
2870 pref_spin_new_int(hbox, _("Timeout (seconds):"), nullptr, 0, 900, 1, options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
2872 pref_checkbox_new_int(group, _("Write metadata on image change"), options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
2874 pref_checkbox_new_int(group, _("Write metadata on directory change"), options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
2876 pref_spacer(group, PREF_PAD_GROUP);
2879 group = pref_group_new(vbox, FALSE, _("Spelling checks"), GTK_ORIENTATION_VERTICAL);
2881 ct_button = pref_checkbox_new_int(group, _("Check spelling - Requires restart"), options->metadata.check_spelling, &c_options->metadata.check_spelling);
2882 gtk_widget_set_tooltip_text(ct_button, _("Spelling checks are performed on info sidebar panes Comment, Headline and Title"));
2885 pref_spacer(group, PREF_PAD_GROUP);
2887 group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
2889 ct_button = pref_checkbox_new_int(group, _("Read metadata in background"), options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
2890 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");
2895 struct KeywordFindData
2902 GtkWidget *button_close;
2903 GtkWidget *button_stop;
2904 GtkWidget *button_start;
2905 GtkWidget *progress;
2913 guint idle_id; /* event source id */
2916 #define KEYWORD_DIALOG_WIDTH 400
2918 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
2920 GList *list_d = nullptr;
2921 GList *list_f = nullptr;
2925 filelist_read(dir_fd, &list_f, &list_d);
2929 filelist_read(dir_fd, &list_f, nullptr);
2932 list_f = filelist_filter(list_f, FALSE);
2933 list_d = filelist_filter(list_d, TRUE);
2935 kfd->list = g_list_concat(list_f, kfd->list);
2936 kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
2939 static void keywords_find_reset(KeywordFindData *kfd)
2941 filelist_free(kfd->list);
2942 kfd->list = nullptr;
2944 filelist_free(kfd->list_dir);
2945 kfd->list_dir = nullptr;
2948 static void keywords_find_close_cb(GenericDialog *, gpointer data)
2950 auto kfd = static_cast<KeywordFindData *>(data);
2952 if (!gtk_widget_get_sensitive(kfd->button_close)) return;
2954 keywords_find_reset(kfd);
2955 generic_dialog_close(kfd->gd);
2959 static void keywords_find_finish(KeywordFindData *kfd)
2961 keywords_find_reset(kfd);
2963 gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
2964 gtk_spinner_stop(GTK_SPINNER(kfd->spinner));
2966 gtk_widget_set_sensitive(kfd->group, TRUE);
2967 gtk_widget_set_sensitive(kfd->button_start, TRUE);
2968 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
2969 gtk_widget_set_sensitive(kfd->button_close, TRUE);
2972 static void keywords_find_stop_cb(GenericDialog *, gpointer data)
2974 auto kfd = static_cast<KeywordFindData *>(data);
2976 g_idle_remove_by_data(kfd);
2978 keywords_find_finish(kfd);
2981 static gboolean keywords_find_file(gpointer data)
2983 auto kfd = static_cast<KeywordFindData *>(data);
2985 GtkTextBuffer *buffer;
2993 fd = static_cast<FileData *>(kfd->list->data);
2994 kfd->list = g_list_remove(kfd->list, fd);
2996 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
2997 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3001 gtk_text_buffer_get_end_iter(buffer, &iter);
3002 tmp = g_strconcat(static_cast<const gchar *>(keywords->data), "\n", NULL);
3003 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3005 keywords = keywords->next;
3008 gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
3009 file_data_unref(fd);
3010 g_list_free_full(keywords, g_free);
3012 return (G_SOURCE_CONTINUE);
3014 else if (kfd->list_dir)
3018 fd = static_cast<FileData *>(kfd->list_dir->data);
3019 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
3021 keywords_find_folder(kfd, fd);
3023 file_data_unref(fd);
3025 return G_SOURCE_CONTINUE;
3028 keywords_find_finish(kfd);
3030 return G_SOURCE_REMOVE;
3033 static void keywords_find_start_cb(GenericDialog *, gpointer data)
3035 auto kfd = static_cast<KeywordFindData *>(data);
3038 if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
3040 path = remove_trailing_slash((gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
3041 parse_out_relatives(path);
3045 warning_dialog(_("Invalid folder"),
3046 _("The specified folder can not be found."),
3047 GQ_ICON_DIALOG_WARNING, kfd->gd->dialog);
3053 gtk_widget_set_sensitive(kfd->group, FALSE);
3054 gtk_widget_set_sensitive(kfd->button_start, FALSE);
3055 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
3056 gtk_widget_set_sensitive(kfd->button_close, FALSE);
3057 gtk_spinner_start(GTK_SPINNER(kfd->spinner));
3059 dir_fd = file_data_new_dir(path);
3060 keywords_find_folder(kfd, dir_fd);
3061 file_data_unref(dir_fd);
3062 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
3068 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
3070 KeywordFindData *kfd;
3074 kfd = g_new0(KeywordFindData, 1);
3076 kfd->gd = generic_dialog_new(_("Search for keywords"),
3077 "search_for_keywords",
3080 gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
3081 kfd->gd->cancel_cb = keywords_find_close_cb;
3082 kfd->button_close = generic_dialog_add_button(kfd->gd, GQ_ICON_CLOSE, _("Close"),
3083 keywords_find_close_cb, FALSE);
3084 kfd->button_start = generic_dialog_add_button(kfd->gd, GQ_ICON_OK, _("S_tart"),
3085 keywords_find_start_cb, FALSE);
3086 kfd->button_stop = generic_dialog_add_button(kfd->gd, GQ_ICON_STOP, _("Stop"),
3087 keywords_find_stop_cb, FALSE);
3088 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3090 generic_dialog_add_message(kfd->gd, nullptr, _("Search for keywords"), nullptr, FALSE);
3092 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3093 pref_spacer(hbox, PREF_PAD_INDENT);
3094 kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3096 hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3097 pref_label_new(hbox, _("Folder:"));
3099 label = tab_completion_new(&kfd->entry, path, nullptr, nullptr, nullptr, nullptr);
3100 tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
3101 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3102 gtk_widget_show(label);
3104 pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
3106 pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
3107 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3109 kfd->progress = gtk_entry_new();
3110 gtk_widget_set_can_focus(kfd->progress, FALSE);
3111 gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
3112 gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
3113 gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
3114 gtk_widget_show(kfd->progress);
3116 kfd->spinner = gtk_spinner_new();
3117 gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
3118 gtk_widget_show(kfd->spinner);
3120 kfd->list = nullptr;
3122 gtk_widget_show(kfd->gd->dialog);
3125 static void keywords_find_cb(GtkWidget *widget, gpointer)
3127 const gchar *path = layout_get_path(nullptr);
3129 if (!path || !*path) path = homedir();
3130 keywords_find_dialog(widget, path);
3133 static void config_tab_keywords_save()
3135 GtkTextIter start, end;
3136 GtkTextBuffer *buffer;
3137 GList *kw_list = nullptr;
3143 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3144 gtk_text_buffer_get_bounds(buffer, &start, &end);
3146 buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3148 kw_split = strtok(buffer_text, "\n");
3149 while (kw_split != nullptr)
3155 if (g_strcmp0(static_cast<const gchar *>(work->data), kw_split) == 0)
3164 kw_list = g_list_append(kw_list, g_strdup(kw_split));
3166 kw_split = strtok(nullptr, "\n");
3169 keyword_list_set(kw_list);
3171 g_list_free_full(kw_list, g_free);
3172 g_free(buffer_text);
3175 static void config_tab_keywords(GtkWidget *notebook)
3181 GtkWidget *scrolled;
3183 GtkTextBuffer *buffer;
3186 GspellTextView *gspell_view;
3189 vbox = scrolled_notebook_page(notebook, _("Keywords"));
3191 group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
3193 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3195 button = pref_button_new(hbox, GQ_ICON_RUN, _("Search"),
3196 G_CALLBACK(keywords_find_cb), keyword_text);
3197 gtk_widget_set_tooltip_text(button, "Search for existing keywords");
3200 keyword_text = gtk_text_view_new();
3201 gtk_widget_set_size_request(keyword_text, 20, 20);
3202 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
3203 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3204 gtk_widget_show(scrolled);
3207 if (options->metadata.check_spelling)
3209 gspell_view = gspell_text_view_get_from_gtk_text_view(GTK_TEXT_VIEW(keyword_text));
3210 gspell_text_view_basic_setup(gspell_view);
3214 gtk_container_add(GTK_CONTAINER(scrolled), keyword_text);
3215 gtk_widget_show(keyword_text);
3217 gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
3219 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3220 gtk_text_buffer_create_tag(buffer, "monospace",
3221 "family", "monospace", NULL);
3223 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3224 gtk_text_buffer_get_start_iter(buffer, &iter);
3225 gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3228 path = g_build_filename(get_rc_dir(), "keywords", NULL);
3230 GList *kwl = keyword_list_get();
3231 kwl = g_list_first(kwl);
3234 gtk_text_buffer_get_end_iter (buffer, &iter);
3235 tmp = g_strconcat(static_cast<const gchar *>(kwl->data), "\n", NULL);
3236 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3241 gtk_text_buffer_set_modified(buffer, FALSE);
3248 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3250 auto option = static_cast<gint *>(data);
3252 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3256 *option = INTENT_PERCEPTUAL;
3259 *option = INTENT_RELATIVE_COLORIMETRIC;
3262 *option = INTENT_SATURATION;
3265 *option = INTENT_ABSOLUTE_COLORIMETRIC;
3270 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3271 gint option, gint *option_c)
3278 pref_table_label(table, column, row, text, GTK_ALIGN_START);
3280 combo = gtk_combo_box_text_new();
3282 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3283 if (option == INTENT_PERCEPTUAL) current = 0;
3284 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3285 if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3286 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3287 if (option == INTENT_SATURATION) current = 2;
3288 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3289 if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3291 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3293 gtk_widget_set_tooltip_text(combo,"Refer to the lcms documentation for the defaults used when the selected Intent is not available");
3295 g_signal_connect(G_OBJECT(combo), "changed",
3296 G_CALLBACK(intent_menu_cb), option_c);
3298 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
3299 gtk_widget_show(combo);
3303 static void config_tab_color(GtkWidget *notebook)
3312 vbox = scrolled_notebook_page(notebook, _("Color management"));
3314 group = pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3316 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3319 table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3320 gtk_table_set_col_spacings(GTK_TABLE(table), PREF_PAD_GAP);
3322 label = pref_table_label(table, 0, 0, _("Type"), GTK_ALIGN_START);
3323 pref_label_bold(label, TRUE, FALSE);
3325 label = pref_table_label(table, 1, 0, _("Menu name"), GTK_ALIGN_START);
3326 pref_label_bold(label, TRUE, FALSE);
3328 label = pref_table_label(table, 2, 0, _("File"), GTK_ALIGN_START);
3329 pref_label_bold(label, TRUE, FALSE);
3331 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3336 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3337 pref_table_label(table, 0, i + 1, buf, GTK_ALIGN_END);
3340 entry = gtk_entry_new();
3341 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3342 if (options->color_profile.input_name[i])
3344 gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3346 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, i + 1, i + 2,
3347 static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3348 gtk_widget_show(entry);
3349 color_profile_input_name_entry[i] = entry;
3351 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], nullptr, ".icc", "ICC Files", nullptr);
3352 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3353 gtk_widget_set_size_request(entry, 160, -1);
3354 gtk_table_attach(GTK_TABLE(table), tabcomp, 2, 3, i + 1, i + 2,
3355 static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3356 gtk_widget_show(tabcomp);
3357 color_profile_input_file_entry[i] = entry;
3360 group = pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3362 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3364 pref_checkbox_new_int(group, _("Use system screen profile if available"),
3365 options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3367 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3369 pref_table_label(table, 0, 0, _("Screen:"), GTK_ALIGN_END);
3370 tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3371 options->color_profile.screen_file, nullptr, ".icc", "ICC Files", nullptr);
3372 tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3373 gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3375 add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3377 gtk_table_attach(GTK_TABLE(table), tabcomp, 1, 2,
3379 static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3381 gtk_widget_show(tabcomp);
3384 /* advanced entry tab */
3385 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer)
3387 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3389 c_options->file_ops.use_system_trash = FALSE;
3390 c_options->file_ops.no_trash = FALSE;
3394 static void use_system_trash_cb(GtkWidget *widget, gpointer)
3396 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3398 c_options->file_ops.use_system_trash = TRUE;
3399 c_options->file_ops.no_trash = FALSE;
3403 static void use_no_cache_cb(GtkWidget *widget, gpointer)
3405 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3407 c_options->file_ops.no_trash = TRUE;
3411 static void config_tab_behavior(GtkWidget *notebook)
3418 GtkWidget *ct_button;
3422 GtkWidget *with_rename;
3423 GtkWidget *collections_on_top;
3424 GtkWidget *hide_window_in_fullscreen;
3425 GtkWidget *checkbox;
3428 vbox = scrolled_notebook_page(notebook, _("Behavior"));
3430 group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3432 pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3433 options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3434 pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3435 options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3436 pref_checkbox_new_int(group, _("Enable Delete key"),
3437 options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3439 ct_button = pref_radiobutton_new(group, nullptr, _("Use Geeqie trash location"),
3440 !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),nullptr);
3442 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3443 pref_checkbox_link_sensitivity(ct_button, hbox);
3445 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3446 pref_label_new(hbox, _("Folder:"));
3448 tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, nullptr, nullptr, nullptr, nullptr);
3449 tab_completion_add_select_button(safe_delete_path_entry, nullptr, TRUE);
3450 gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3451 gtk_widget_show(tabcomp);
3453 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3454 pref_checkbox_link_sensitivity(ct_button, hbox);
3456 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3457 spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MiB"),
3458 0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3459 gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3460 button = pref_button_new(nullptr, nullptr, _("View"),
3461 G_CALLBACK(safe_delete_view_cb), nullptr);
3462 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3463 gtk_widget_show(button);
3465 button = pref_button_new(nullptr, GQ_ICON_CLEAR, nullptr,
3466 G_CALLBACK(safe_delete_clear_cb), nullptr);
3467 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3469 c_options->file_ops.no_trash = options->file_ops.no_trash;
3470 c_options->file_ops.use_system_trash = options->file_ops.use_system_trash;
3472 pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3473 options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), nullptr);
3475 pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3476 options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), nullptr);
3478 gtk_widget_show(button);
3480 pref_spacer(group, PREF_PAD_GROUP);
3483 group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3485 pref_checkbox_new_int(group, _("Descend folders in tree view"),
3486 options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3488 pref_checkbox_new_int(group, _("In place renaming"),
3489 options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3491 pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3492 options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3494 tmp = pref_checkbox_new_int(group, _("Circular selection lists"),
3495 options->circular_selection_lists, &c_options->circular_selection_lists);
3496 gtk_widget_set_tooltip_text(tmp, _("Traverse selection lists in a circular manner"));
3498 marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3499 options->marks_save, &c_options->marks_save);
3500 gtk_widget_set_tooltip_text(marks,"Note that marks linked to a keyword will be saved irrespective of this setting");
3502 with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3503 options->with_rename, &c_options->with_rename);
3504 gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3506 collections_on_top = pref_checkbox_new_int(group, _("Permit duplicates in Collections"),
3507 options->collections_duplicates, &c_options->collections_duplicates);
3508 gtk_widget_set_tooltip_text(collections_on_top,"Allow the same image to be in a Collection more than once");
3510 collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3511 options->collections_on_top, &c_options->collections_on_top);
3512 gtk_widget_set_tooltip_text(collections_on_top,"Open collections window on top");
3514 hide_window_in_fullscreen = pref_checkbox_new_int(group, _("Hide window in fullscreen"),
3515 options->hide_window_in_fullscreen, &c_options->hide_window_in_fullscreen);
3516 gtk_widget_set_tooltip_text(hide_window_in_fullscreen,"When alt-tabbing, prevent Geeqie window showing twice");
3518 pref_spin_new_int(group, _("Recent folder list maximum size"), nullptr,
3519 1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3521 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);
3522 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."));
3524 pref_spin_new_int(group, _("Drag'n drop icon size"), nullptr,
3525 16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3527 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3528 add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
3530 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3531 add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3533 pref_spacer(group, PREF_PAD_GROUP);
3535 group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3537 pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3538 options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3539 pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), nullptr,
3540 1, 32, 1, options->keyboard_scroll_step, reinterpret_cast<int *>(&c_options->keyboard_scroll_step));
3541 pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3542 options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3543 pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3544 options->image_lm_click_nav, &c_options->image_lm_click_nav);
3545 pref_checkbox_new_int(group, _("Open archive by left click on image"),
3546 options->image_l_click_archive, &c_options->image_l_click_archive);
3547 pref_checkbox_new_int(group, _("Play video by left click on image"),
3548 options->image_l_click_video, &c_options->image_l_click_video);
3549 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3550 add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3552 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3553 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3554 add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3555 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3556 add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3558 pref_spacer(group, PREF_PAD_GROUP);
3560 group = pref_group_new(vbox, FALSE, _("GPU"), GTK_ORIENTATION_VERTICAL);
3562 checkbox = pref_checkbox_new_int(group, _("Override disable GPU"),
3563 options->override_disable_gpu, &c_options->override_disable_gpu);
3564 gtk_widget_set_tooltip_text(checkbox, "Contact the developers for usage");
3567 pref_spacer(group, PREF_PAD_GROUP);
3569 group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3571 pref_spin_new_int(group, _("Debug level:"), nullptr,
3572 DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3574 pref_checkbox_new_int(group, _("Timer data"),
3575 options->log_window.timer_data, &c_options->log_window.timer_data);
3577 pref_spin_new_int(group, _("Log Window max. lines:"), nullptr,
3578 1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3582 /* accelerators tab */
3584 static gboolean accel_search_function_cb(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer)
3586 gboolean ret = TRUE;
3591 gtk_tree_model_get(model, iter, column, &text, -1);
3592 text_nocase = g_utf8_casefold(text, -1);
3593 key_nocase = g_utf8_casefold(key, -1);
3595 if (g_strstr_len(text_nocase, -1, key_nocase))
3602 g_free(text_nocase);
3607 static void accel_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *, GtkTreeViewColumn *column, gpointer)
3612 list = gtk_tree_view_get_columns(tree_view);
3613 col_num = g_list_index(list, column);
3617 gtk_tree_view_set_search_column(tree_view, col_num);
3620 static void config_tab_accelerators(GtkWidget *notebook)
3626 GtkWidget *scrolled;
3627 GtkWidget *accel_view;
3628 GtkCellRenderer *renderer;
3629 GtkTreeSelection *selection;
3630 GtkTreeViewColumn *column;
3632 vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3634 group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3636 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
3637 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3638 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3639 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3640 gtk_widget_show(scrolled);
3642 accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3644 accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3645 g_object_unref(accel_store);
3646 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3647 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3649 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3651 renderer = gtk_cell_renderer_text_new();
3653 column = gtk_tree_view_column_new_with_attributes(_("Action"),
3658 gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3659 gtk_tree_view_column_set_resizable(column, TRUE);
3660 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3663 renderer = gtk_cell_renderer_accel_new();
3664 g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3665 G_CALLBACK(accel_store_cleared_cb), accel_store);
3666 g_signal_connect(G_OBJECT(renderer), "accel-edited",
3667 G_CALLBACK(accel_store_edited_cb), accel_store);
3670 g_object_set (renderer,
3672 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3675 column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3680 gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3681 gtk_tree_view_column_set_resizable(column, TRUE);
3682 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3684 renderer = gtk_cell_renderer_text_new();
3686 column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3691 gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3692 gtk_tree_view_column_set_resizable(column, TRUE);
3693 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3695 renderer = gtk_cell_renderer_text_new();
3697 column = gtk_tree_view_column_new_with_attributes("Accel",
3702 gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3703 gtk_tree_view_column_set_resizable(column, TRUE);
3704 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3706 /* Search on text in column */
3707 gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(accel_view), TRUE);
3708 g_signal_connect(accel_view, "row_activated", G_CALLBACK(accel_row_activated_cb), accel_store);
3709 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), TRUE);
3710 gtk_tree_view_set_search_column(GTK_TREE_VIEW(accel_view), AE_TOOLTIP);
3711 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(accel_view), accel_search_function_cb, nullptr, nullptr);
3713 accel_store_populate();
3714 gtk_container_add(GTK_CONTAINER(scrolled), accel_view);
3715 gtk_widget_show(accel_view);
3717 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3719 button = pref_button_new(nullptr, nullptr, _("Defaults"),
3720 G_CALLBACK(accel_default_cb), accel_view);
3721 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3722 gtk_widget_show(button);
3724 button = pref_button_new(nullptr, nullptr, _("Reset selected"),
3725 G_CALLBACK(accel_reset_cb), accel_view);
3726 gtk_widget_set_tooltip_text(button, _("Will only reset changes made before the settings are saved"));
3727 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3728 gtk_widget_show(button);
3730 button = pref_button_new(nullptr, nullptr, _("Clear selected"),
3731 G_CALLBACK(accel_clear_cb), accel_view);
3732 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3733 gtk_widget_show(button);
3736 /* toolbar main tab */
3737 static void config_tab_toolbar_main(GtkWidget *notebook)
3740 GtkWidget *toolbardata;
3743 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3745 vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3747 toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3748 gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3749 gtk_widget_show(vbox);
3752 /* toolbar status tab */
3753 static void config_tab_toolbar_status(GtkWidget *notebook)
3756 GtkWidget *toolbardata;
3759 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3761 vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3763 toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3764 gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3765 gtk_widget_show(vbox);
3769 static gint extension_sort_cb(gconstpointer a, gconstpointer b)
3771 return g_strcmp0(static_cast<const gchar *>(a), static_cast<const gchar *>(b));
3774 static void config_tab_advanced(GtkWidget *notebook)
3778 GSList *formats_list;
3779 GList *extensions_list = nullptr;
3782 GdkPixbufFormat *fm;
3784 GString *types_string = g_string_new(nullptr);
3785 GtkWidget *types_string_label;
3786 GtkWidget *threads_string_label;
3787 GtkWidget *dupes_threads_spin;
3789 vbox = scrolled_notebook_page(notebook, _("Advanced"));
3790 group = pref_group_new(vbox, FALSE, _("External preview extraction"), GTK_ORIENTATION_VERTICAL);
3792 pref_checkbox_new_int(group, _("Use external preview extraction - Requires restart"), options->external_preview.enable, &c_options->external_preview.enable);
3794 pref_spacer(group, PREF_PAD_GROUP);
3796 formats_list = gdk_pixbuf_get_formats();
3798 while (formats_list)
3800 fm = static_cast<GdkPixbufFormat *>(formats_list->data);
3801 extensions = gdk_pixbuf_format_get_extensions(fm);
3804 while (extensions[i])
3806 extensions_list = g_list_insert_sorted(extensions_list, g_strdup(extensions[i]), extension_sort_cb);
3810 g_strfreev(extensions);
3811 formats_list = formats_list->next;
3814 while (extensions_list)
3816 if (types_string->len == 0)
3818 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3822 types_string = g_string_append(types_string, ", ");
3823 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3826 extensions_list = extensions_list->next;
3829 types_string = g_string_prepend(types_string, _("Usable file types:\n"));
3830 types_string_label = pref_label_new(group, types_string->str);
3831 gtk_label_set_line_wrap(GTK_LABEL(types_string_label), TRUE);
3833 pref_spacer(group, PREF_PAD_GROUP);
3835 group = pref_group_new(vbox, FALSE, _("File identification tool"), GTK_ORIENTATION_VERTICAL);
3836 external_preview_select_entry = gtk_entry_new();
3837 tabcomp = tab_completion_new(&external_preview_select_entry, options->external_preview.select, nullptr, nullptr, nullptr, nullptr);
3838 tab_completion_add_select_button(external_preview_select_entry, _("Select file identification tool"), FALSE);
3839 gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3840 gtk_widget_show(tabcomp);
3842 group = pref_group_new(vbox, FALSE, _("Preview extraction tool"), GTK_ORIENTATION_VERTICAL);
3843 external_preview_extract_entry = gtk_entry_new();
3844 tabcomp = tab_completion_new(&external_preview_extract_entry, options->external_preview.extract, nullptr, nullptr, nullptr, nullptr);
3845 tab_completion_add_select_button(external_preview_extract_entry, _("Select preview extraction tool"), FALSE);
3846 gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3847 gtk_widget_show(tabcomp);
3849 gtk_widget_show(vbox);
3851 g_slist_free(formats_list);
3852 g_list_free_full(extensions_list, g_free);
3853 g_string_free(types_string, TRUE);
3855 pref_spacer(group, PREF_PAD_GROUP);
3857 pref_line(vbox, PREF_PAD_SPACE);
3858 group = pref_group_new(vbox, FALSE, _("Thread pool limits"), GTK_ORIENTATION_VERTICAL);
3860 threads_string_label = pref_label_new(group, "This option limits the number of threads (or cpu cores)\nthat Geeqie will use when running duplicate checks. The default value is 0, which means all available cores will be used.");
3861 gtk_label_set_line_wrap(GTK_LABEL(threads_string_label), TRUE);
3863 pref_spacer(vbox, PREF_PAD_GROUP);
3865 dupes_threads_spin = pref_spin_new_int(vbox, _("Duplicate check:"), _("max. threads"), 0, get_cpu_cores(), 1, options->threads.duplicates, &c_options->threads.duplicates);
3866 gtk_widget_set_tooltip_markup(dupes_threads_spin, _("Set to 0 for unlimited"));
3870 static void config_tab_stereo(GtkWidget *notebook)
3878 GtkWidget *fs_button;
3879 vbox = scrolled_notebook_page(notebook, _("Stereo"));
3881 group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
3883 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3884 add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
3886 table = pref_table_new(group, 2, 2, TRUE, FALSE);
3887 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3888 pref_checkbox_new_int(box, _("Mirror left image"),
3889 options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
3890 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3891 pref_checkbox_new_int(box, _("Flip left image"),
3892 options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
3893 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3894 pref_checkbox_new_int(box, _("Mirror right image"),
3895 options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
3896 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3897 pref_checkbox_new_int(box, _("Flip right image"),
3898 options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
3899 pref_checkbox_new_int(group, _("Swap left and right images"),
3900 options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
3901 pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
3902 options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
3904 group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
3905 fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
3906 options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
3907 box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
3908 pref_checkbox_link_sensitivity(fs_button, box2);
3909 table = pref_table_new(box2, 2, 1, FALSE, FALSE);
3910 add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
3911 table = pref_table_new(box2, 2, 2, TRUE, FALSE);
3912 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3913 pref_checkbox_new_int(box, _("Mirror left image"),
3914 options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
3915 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3916 pref_checkbox_new_int(box, _("Flip left image"),
3917 options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
3918 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3919 pref_checkbox_new_int(box, _("Mirror right image"),
3920 options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
3921 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3922 pref_checkbox_new_int(box, _("Flip right image"),
3923 options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
3924 pref_checkbox_new_int(box2, _("Swap left and right images"),
3925 options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
3926 pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
3927 options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
3929 group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
3930 table = pref_table_new(group2, 5, 3, FALSE, FALSE);
3931 pref_table_spin_new_int(table, 0, 0, _("Width"), nullptr,
3932 1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
3933 pref_table_spin_new_int(table, 3, 0, _("Height"), nullptr,
3934 1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
3935 pref_table_spin_new_int(table, 0, 1, _("Left X"), nullptr,
3936 0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
3937 pref_table_spin_new_int(table, 3, 1, _("Left Y"), nullptr,
3938 0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
3939 pref_table_spin_new_int(table, 0, 2, _("Right X"), nullptr,
3940 0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
3941 pref_table_spin_new_int(table, 3, 2, _("Right Y"), nullptr,
3942 0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
3946 /* Main preferences window */
3947 static void config_window_create(LayoutWindow *lw)
3949 GtkWidget *win_vbox;
3951 GtkWidget *notebook;
3953 GtkWidget *ct_button;
3955 if (!c_options) c_options = init_options(nullptr);
3957 configwindow = window_new(GTK_WINDOW_TOPLEVEL, "preferences", PIXBUF_INLINE_ICON_CONFIG, nullptr, _("Preferences"));
3958 DEBUG_NAME(configwindow);
3959 gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
3960 g_signal_connect(G_OBJECT(configwindow), "delete_event",
3961 G_CALLBACK(config_window_delete), NULL);
3962 if (options->save_dialog_window_positions)
3964 gtk_window_resize(GTK_WINDOW(configwindow), lw->options.preferences_window.w, lw->options.preferences_window.h);
3965 gtk_window_move(GTK_WINDOW(configwindow), lw->options.preferences_window.x, lw->options.preferences_window.y);
3969 gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
3971 gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
3972 gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
3974 win_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
3975 gtk_container_add(GTK_CONTAINER(configwindow), win_vbox);
3976 gtk_widget_show(win_vbox);
3978 notebook = gtk_notebook_new();
3979 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
3980 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
3981 gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
3983 config_tab_general(notebook);
3984 config_tab_image(notebook);
3985 config_tab_osd(notebook);
3986 config_tab_windows(notebook);
3987 config_tab_accelerators(notebook);
3988 config_tab_files(notebook);
3989 config_tab_metadata(notebook);
3990 config_tab_keywords(notebook);
3991 config_tab_color(notebook);
3992 config_tab_stereo(notebook);
3993 config_tab_behavior(notebook);
3994 config_tab_toolbar_main(notebook);
3995 config_tab_toolbar_status(notebook);
3996 config_tab_advanced(notebook);
3998 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), lw->options.preferences_window.page_number);
4000 hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
4001 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
4002 gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
4003 gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
4004 gtk_widget_show(hbox);
4006 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
4007 G_CALLBACK(config_window_help_cb), notebook);
4008 gtk_container_add(GTK_CONTAINER(hbox), button);
4009 gtk_widget_set_can_default(button, TRUE);
4010 gtk_widget_show(button);
4012 button = pref_button_new(nullptr, GQ_ICON_OK, "OK",
4013 G_CALLBACK(config_window_ok_cb), notebook);
4014 gtk_container_add(GTK_CONTAINER(hbox), button);
4015 gtk_widget_set_can_default(button, TRUE);
4016 gtk_widget_grab_default(button);
4017 gtk_widget_show(button);
4021 button = pref_button_new(nullptr, GQ_ICON_CANCEL, _("Cancel"),
4022 G_CALLBACK(config_window_close_cb), nullptr);
4023 gtk_container_add(GTK_CONTAINER(hbox), button);
4024 gtk_widget_set_can_default(button, TRUE);
4025 gtk_widget_show(button);
4027 if (!generic_dialog_get_alternative_button_order(configwindow))
4029 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
4032 gtk_widget_show(notebook);
4034 gtk_widget_show(configwindow);
4038 *-----------------------------------------------------------------------------
4039 * config window show (public)
4040 *-----------------------------------------------------------------------------
4043 void show_config_window(LayoutWindow *lw)
4047 gtk_window_present(GTK_WINDOW(configwindow));
4051 config_window_create(lw);
4060 void show_about_window(LayoutWindow *lw)
4062 GDataInputStream *data_stream;
4063 GInputStream *in_stream_authors;
4064 GInputStream *in_stream_translators;
4066 GdkPixbuf *pixbuf_icon;
4067 GdkPixbuf *pixbuf_logo;
4071 gchar *authors[1000];
4073 gchar *timezone_path;
4082 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");
4084 timezone_path = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
4085 if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
4087 cd = ZDOpenDatabase(timezone_path);
4090 copyright = g_string_append(copyright, ZDGetNotice(cd));
4094 log_printf("Error: Init of timezone database %s failed\n", timezone_path);
4096 ZDCloseDatabase(cd);
4098 g_free(timezone_path);
4100 in_stream_authors = g_resources_open_stream(GQ_RESOURCE_PATH_CREDITS "/authors", G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4102 data_stream = g_data_input_stream_new(in_stream_authors);
4104 authors[0] = nullptr;
4105 while ((author_line = g_data_input_stream_read_line(G_DATA_INPUT_STREAM(data_stream), &length, nullptr, nullptr)))
4107 authors[i_authors] = g_strdup(author_line);
4109 g_free(author_line);
4111 authors[i_authors] = nullptr;
4113 g_input_stream_close(in_stream_authors, nullptr, nullptr);
4115 constexpr auto translators_path = GQ_RESOURCE_PATH_CREDITS "/translators";
4117 g_resources_get_info(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &size, &flags, nullptr);
4119 in_stream_translators = g_resources_open_stream(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4120 translators = static_cast<gchar *>(g_malloc0(size));
4121 g_input_stream_read_all(in_stream_translators, translators, size, &bytes_read, nullptr, nullptr);
4122 g_input_stream_close(in_stream_translators, nullptr, nullptr);
4124 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);
4126 artists[0] = g_strdup("Néstor Díaz Valencia <nestor@estudionexos.com>");
4127 artists[1] = nullptr;
4129 pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
4130 pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
4131 gtk_show_about_dialog(GTK_WINDOW(lw->window),
4132 "title", _("About Geeqie"),
4134 "program-name", GQ_APPNAME,
4136 "logo", pixbuf_logo,
4137 "icon", pixbuf_icon,
4138 "website", GQ_WEBSITE,
4139 "website-label", "Website",
4140 "comments", comment,
4143 "translator-credits", translators,
4144 "wrap-license", TRUE,
4145 "license", copyright->str,
4148 g_string_free(copyright, TRUE);
4150 while(n < i_authors)
4158 g_free(translators);
4159 g_object_unref(data_stream);
4160 g_object_unref(in_stream_authors);
4161 g_object_unref(in_stream_translators);
4164 static void image_overlay_set_text_colours()
4166 c_options->image_overlay.text_red = options->image_overlay.text_red;
4167 c_options->image_overlay.text_green = options->image_overlay.text_green;
4168 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
4169 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
4170 c_options->image_overlay.background_red = options->image_overlay.background_red;
4171 c_options->image_overlay.background_green = options->image_overlay.background_green;
4172 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
4173 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
4177 *-----------------------------------------------------------------------------
4178 * timezone database routines
4179 *-----------------------------------------------------------------------------
4182 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
4184 GError *error = nullptr;
4185 auto tz = static_cast<TZData *>(data);
4186 gchar *tmp_filename;
4187 gchar *timezone_bin;
4188 gchar *tmp_dir = nullptr;
4191 if (!g_cancellable_is_cancelled(tz->cancellable))
4193 generic_dialog_close(tz->gd);
4197 if (g_file_copy_finish(G_FILE(source_object), res, &error))
4199 tmp_filename = g_file_get_path(tz->tmp_g_file);
4200 fd = file_data_new_simple(tmp_filename);
4201 tmp_dir = open_archive(fd);
4205 timezone_bin = g_build_filename(tmp_dir, TIMEZONE_DATABASE_VERSION, TIMEZONE_DATABASE_FILE, NULL);
4206 if (isfile(timezone_bin))
4208 move_file(timezone_bin, tz->timezone_database_user);
4212 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4215 g_free(timezone_bin);
4216 g_free(tmp_dir); // The folder in /tmp is deleted in exit_program_final()
4220 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4222 g_free(tmp_filename);
4223 file_data_unref(fd);
4227 file_util_warning_dialog(_("Error: Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4230 g_file_delete(tz->tmp_g_file, nullptr, &error);
4231 g_object_unref(tz->tmp_g_file);
4232 tz->tmp_g_file = nullptr;
4233 g_object_unref(tz->cancellable);
4234 g_object_unref(tz->timezone_database_gq);
4237 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
4239 auto tz = static_cast<TZData *>(data);
4241 if (!g_cancellable_is_cancelled(tz->cancellable))
4243 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), static_cast<gdouble>(current_num_bytes) / total_num_bytes);
4247 static void timezone_cancel_button_cb(GenericDialog *, gpointer data)
4249 auto tz = static_cast<TZData *>(data);
4251 g_cancellable_cancel(tz->cancellable);
4254 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
4256 auto tz = static_cast<TZData *>(data);
4257 GError *error = nullptr;
4258 GFileIOStream *io_stream;
4265 tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
4269 file_util_warning_dialog(_("Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4270 log_printf("Error: Download timezone database failed:\n%s", error->message);
4271 g_error_free(error);
4272 g_object_unref(tz->tmp_g_file);
4276 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE_WEB);
4278 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", nullptr, TRUE, timezone_cancel_button_cb, tz);
4280 generic_dialog_add_message(tz->gd, GQ_ICON_DIALOG_INFO, _("Downloading timezone database"), nullptr, FALSE);
4282 tz->progress = gtk_progress_bar_new();
4283 gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
4284 gtk_widget_show(tz->progress);
4286 gtk_widget_show(tz->gd->dialog);
4287 tz->cancellable = g_cancellable_new();
4288 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);
4290 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
4293 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */