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->marks_save = c_options->marks_save;
446 options->with_rename = c_options->with_rename;
447 options->collections_on_top = c_options->collections_on_top;
448 options->hide_window_in_fullscreen = c_options->hide_window_in_fullscreen;
449 config_entry_to_option(help_search_engine_entry, &options->help_search_engine, nullptr);
451 options->external_preview.enable = c_options->external_preview.enable;
452 config_entry_to_option(external_preview_select_entry, &options->external_preview.select, nullptr);
453 config_entry_to_option(external_preview_extract_entry, &options->external_preview.extract, nullptr);
455 options->read_metadata_in_idle = c_options->read_metadata_in_idle;
457 options->star_rating.star = c_options->star_rating.star;
458 options->star_rating.rejected = c_options->star_rating.rejected;
460 options->threads.duplicates = c_options->threads.duplicates > 0 ? c_options->threads.duplicates : -1;
463 set_debug_level(debug_c);
467 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
469 config_entry_to_option(color_profile_input_name_entry[i], &options->color_profile.input_name[i], nullptr);
470 config_entry_to_option(color_profile_input_file_entry[i], &options->color_profile.input_file[i], nullptr);
472 config_entry_to_option(color_profile_screen_file_entry, &options->color_profile.screen_file, nullptr);
473 options->color_profile.use_x11_screen_profile = c_options->color_profile.use_x11_screen_profile;
474 if (options->color_profile.render_intent != c_options->color_profile.render_intent)
476 options->color_profile.render_intent = c_options->color_profile.render_intent;
481 options->mouse_button_8 = c_options->mouse_button_8;
482 options->mouse_button_9 = c_options->mouse_button_9;
484 options->override_disable_gpu = c_options->override_disable_gpu;
486 config_tab_keywords_save();
488 image_options_sync();
493 layout_refresh(nullptr);
496 if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, nullptr);
498 toolbar_apply(TOOLBAR_MAIN);
499 toolbar_apply(TOOLBAR_STATUS);
503 *-----------------------------------------------------------------------------
504 * config window main button callbacks (private)
505 *-----------------------------------------------------------------------------
508 static void config_window_close_cb(GtkWidget *, gpointer)
510 gtk_widget_destroy(configwindow);
511 configwindow = nullptr;
512 filter_store = nullptr;
515 static void config_window_help_cb(GtkWidget *, gpointer data)
517 auto notebook = static_cast<GtkWidget *>(data);
520 static const gchar *html_section[] =
522 "GuideOptionsGeneral.html",
523 "GuideOptionsImage.html",
524 "GuideOptionsOSD.html",
525 "GuideOptionsWindow.html",
526 "GuideOptionsKeyboard.html",
527 "GuideOptionsFiltering.html",
528 "GuideOptionsMetadata.html",
529 "GuideOptionsKeywords.html",
530 "GuideOptionsColor.html",
531 "GuideOptionsStereo.html",
532 "GuideOptionsBehavior.html",
533 "GuideOptionsToolbar.html",
534 "GuideOptionsToolbar.html",
535 "GuideOptionsAdvanced.html"
538 i = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
539 help_window_show(html_section[i]);
542 static gboolean config_window_delete(GtkWidget *, GdkEventAny *, gpointer)
544 config_window_close_cb(nullptr, nullptr);
548 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
551 auto notebook = static_cast<GtkNotebook *>(data);
559 lw = static_cast<LayoutWindow *>(layout_window_list->data);
561 window = gtk_widget_get_window(widget);
562 gdk_window_get_root_origin(window, &x, &y);
563 w = gdk_window_get_width(window);
564 h = gdk_window_get_height(window);
565 page_number = gtk_notebook_get_current_page(notebook);
567 lw->options.preferences_window.x = x;
568 lw->options.preferences_window.y = y;
569 lw->options.preferences_window.w = w;
570 lw->options.preferences_window.h = h;
571 lw->options.preferences_window.page_number = page_number;
573 config_window_apply();
574 layout_util_sync(lw);
575 save_options(options);
576 config_window_close_cb(nullptr, nullptr);
580 *-----------------------------------------------------------------------------
581 * config window setup (private)
582 *-----------------------------------------------------------------------------
585 static void quality_menu_cb(GtkWidget *combo, gpointer data)
587 auto option = static_cast<gint *>(data);
589 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
593 *option = GDK_INTERP_NEAREST;
596 *option = GDK_INTERP_TILES;
599 *option = GDK_INTERP_BILINEAR;
604 static void dnd_default_action_selection_menu_cb(GtkWidget *combo, gpointer data)
606 auto option = static_cast<gint *>(data);
608 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
612 *option = DND_ACTION_ASK;
615 *option = DND_ACTION_COPY;
618 *option = DND_ACTION_MOVE;
622 static void clipboard_selection_menu_cb(GtkWidget *combo, gpointer data)
624 auto option = static_cast<gint *>(data);
626 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
629 *option = CLIPBOARD_PRIMARY;
632 *option = CLIPBOARD_CLIPBOARD;
635 *option = CLIPBOARD_BOTH;
638 *option = CLIPBOARD_BOTH;
642 static void add_quality_menu(GtkWidget *table, gint column, gint row, const gchar *text,
643 guint option, guint *option_c)
650 pref_table_label(table, column, row, text, GTK_ALIGN_START);
652 combo = gtk_combo_box_text_new();
654 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Nearest (worst, but fastest)"));
655 if (option == GDK_INTERP_NEAREST) current = 0;
656 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Tiles"));
657 if (option == GDK_INTERP_TILES) current = 1;
658 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Bilinear (best, but slowest)"));
659 if (option == GDK_INTERP_BILINEAR) current = 2;
661 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
663 g_signal_connect(G_OBJECT(combo), "changed",
664 G_CALLBACK(quality_menu_cb), option_c);
666 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
667 gtk_widget_show(combo);
670 static void add_dnd_default_action_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, DnDAction option, DnDAction *option_c)
677 pref_table_label(table, column, row, text, GTK_ALIGN_START);
679 combo = gtk_combo_box_text_new();
681 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Ask"));
682 if (option == DND_ACTION_ASK) current = 0;
683 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Copy"));
684 if (option == DND_ACTION_COPY) current = 1;
685 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Move"));
686 if (option == DND_ACTION_MOVE) current = 2;
688 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
690 g_signal_connect(G_OBJECT(combo), "changed",
691 G_CALLBACK(dnd_default_action_selection_menu_cb), option_c);
693 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
694 gtk_widget_show(combo);
697 static void add_clipboard_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
698 gint option, gint *option_c)
705 pref_table_label(table, column, row, text, GTK_ALIGN_START);
707 combo = gtk_combo_box_text_new();
709 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Primary"));
710 if (option == CLIPBOARD_PRIMARY) current = 0;
711 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Clipboard"));
712 if (option == CLIPBOARD_CLIPBOARD) current = 1;
713 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Both"));
714 if (option == CLIPBOARD_BOTH) current = 2;
716 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
718 g_signal_connect(G_OBJECT(combo), "changed",
719 G_CALLBACK(clipboard_selection_menu_cb), option_c);
721 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
722 gtk_widget_show(combo);
725 static void zoom_style_selection_menu_cb(GtkWidget *combo, gpointer data)
727 auto option = static_cast<gint *>(data);
729 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
732 *option = ZOOM_GEOMETRIC;
735 *option = ZOOM_ARITHMETIC;
738 *option = ZOOM_GEOMETRIC;
742 static void add_zoom_style_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, ZoomStyle option, ZoomStyle *option_c)
749 pref_table_label(table, column, row, text, GTK_ALIGN_START);
751 combo = gtk_combo_box_text_new();
753 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Geometric"));
754 if (option == ZOOM_GEOMETRIC) current = 0;
755 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Arithmetic"));
756 if (option == ZOOM_ARITHMETIC) current = 1;
758 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
760 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(zoom_style_selection_menu_cb), option_c);
762 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
763 gtk_widget_show(combo);
766 static void mouse_buttons_selection_menu_cb(GtkWidget *combo, gpointer data)
768 ActionItem *action_item = nullptr;
769 auto option = static_cast<gchar **>(data);
774 label = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
776 list = get_action_items();
781 action_item = static_cast<ActionItem *>(work->data);
782 if (g_strcmp0(action_item->label, label) == 0)
790 *option = g_strdup(action_item->name);
792 action_items_free(list);
795 static void add_mouse_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, gchar *option, gchar **option_c)
797 ActionItem *action_item;
806 pref_table_label(table, column, row, text, GTK_ALIGN_START);
808 combo = gtk_combo_box_text_new();
810 list = get_action_items();
814 action_item = static_cast<ActionItem *>(work->data);
815 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), action_item->label);
817 if (g_strcmp0(action_item->name, option) == 0)
825 action_items_free(list);
827 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
829 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(mouse_buttons_selection_menu_cb), option_c);
831 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
832 gtk_widget_show(combo);
835 static void thumb_size_menu_cb(GtkWidget *combo, gpointer)
839 n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
842 if (static_cast<guint>(n) < sizeof(thumb_size_list) / sizeof(ThumbSize))
844 c_options->thumbnails.max_width = thumb_size_list[n].w;
845 c_options->thumbnails.max_height = thumb_size_list[n].h;
849 c_options->thumbnails.max_width = options->thumbnails.max_width;
850 c_options->thumbnails.max_height = options->thumbnails.max_height;
854 static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *text)
860 c_options->thumbnails.max_width = options->thumbnails.max_width;
861 c_options->thumbnails.max_height = options->thumbnails.max_height;
863 pref_table_label(table, column, row, text, GTK_ALIGN_START);
865 combo = gtk_combo_box_text_new();
868 for (i = 0; static_cast<guint>(i) < sizeof(thumb_size_list) / sizeof(ThumbSize); i++)
873 w = thumb_size_list[i].w;
874 h = thumb_size_list[i].h;
876 buf = g_strdup_printf("%d x %d", w, h);
877 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
880 if (w == options->thumbnails.max_width && h == options->thumbnails.max_height) current = i;
887 buf = g_strdup_printf("%s %d x %d", _("Custom"), options->thumbnails.max_width, options->thumbnails.max_height);
888 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
894 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
895 g_signal_connect(G_OBJECT(combo), "changed",
896 G_CALLBACK(thumb_size_menu_cb), NULL);
898 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
899 gtk_widget_show(combo);
902 static void stereo_mode_menu_cb(GtkWidget *combo, gpointer data)
904 auto option = static_cast<gint *>(data);
906 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
910 *option = PR_STEREO_NONE;
913 *option = PR_STEREO_ANAGLYPH_RC;
916 *option = PR_STEREO_ANAGLYPH_GM;
919 *option = PR_STEREO_ANAGLYPH_YB;
922 *option = PR_STEREO_ANAGLYPH_GRAY_RC;
925 *option = PR_STEREO_ANAGLYPH_GRAY_GM;
928 *option = PR_STEREO_ANAGLYPH_GRAY_YB;
931 *option = PR_STEREO_ANAGLYPH_DB_RC;
934 *option = PR_STEREO_ANAGLYPH_DB_GM;
937 *option = PR_STEREO_ANAGLYPH_DB_YB;
940 *option = PR_STEREO_HORIZ;
943 *option = PR_STEREO_HORIZ | PR_STEREO_HALF;
946 *option = PR_STEREO_VERT;
949 *option = PR_STEREO_VERT | PR_STEREO_HALF;
952 *option = PR_STEREO_FIXED;
957 static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const gchar *text,
958 gint option, gint *option_c, gboolean add_fixed)
965 pref_table_label(table, column, row, text, GTK_ALIGN_START);
967 combo = gtk_combo_box_text_new();
969 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Single image"));
971 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Red-Cyan"));
972 if (option & PR_STEREO_ANAGLYPH_RC) current = 1;
973 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Green-Magenta"));
974 if (option & PR_STEREO_ANAGLYPH_GM) current = 2;
975 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Yellow-Blue"));
976 if (option & PR_STEREO_ANAGLYPH_YB) current = 3;
977 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Red-Cyan"));
978 if (option & PR_STEREO_ANAGLYPH_GRAY_RC) current = 4;
979 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Green-Magenta"));
980 if (option & PR_STEREO_ANAGLYPH_GRAY_GM) current = 5;
981 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Yellow-Blue"));
982 if (option & PR_STEREO_ANAGLYPH_GRAY_YB) current = 6;
983 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Red-Cyan"));
984 if (option & PR_STEREO_ANAGLYPH_DB_RC) current = 7;
985 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Green-Magenta"));
986 if (option & PR_STEREO_ANAGLYPH_DB_GM) current = 8;
987 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Yellow-Blue"));
988 if (option & PR_STEREO_ANAGLYPH_DB_YB) current = 9;
990 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side"));
991 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side Half size"));
992 if (option & PR_STEREO_HORIZ)
995 if (option & PR_STEREO_HALF) current = 11;
998 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom"));
999 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom Half size"));
1000 if (option & PR_STEREO_VERT)
1003 if (option & PR_STEREO_HALF) current = 13;
1008 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Fixed position"));
1009 if (option & PR_STEREO_FIXED) current = 14;
1012 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1014 g_signal_connect(G_OBJECT(combo), "changed",
1015 G_CALLBACK(stereo_mode_menu_cb), option_c);
1017 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1018 gtk_widget_show(combo);
1021 static void video_menu_cb(GtkWidget *combo, gpointer data)
1023 auto option = static_cast<gchar **>(data);
1025 auto ed = static_cast<EditorDescription *>(g_list_nth_data(editor_list_get(), gtk_combo_box_get_active(GTK_COMBO_BOX(combo))));
1029 static void video_menu_populate(gpointer data, gpointer user_data)
1031 auto combo = static_cast<GtkWidget *>(user_data);
1032 auto ed = static_cast<EditorDescription *>(data);
1034 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), ed->name);
1037 static void add_video_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1038 gchar *option, gchar **option_c)
1042 /* use lists since they are sorted */
1043 GList *eds = editor_list_get();
1047 pref_table_label(table, column, row, text, GTK_ALIGN_START);
1049 combo = gtk_combo_box_text_new();
1050 g_list_foreach(eds,video_menu_populate,combo);
1051 current = option ? g_list_index(eds,g_hash_table_lookup(editors,option)): -1;
1053 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1055 g_signal_connect(G_OBJECT(combo), "changed",
1056 G_CALLBACK(video_menu_cb), option_c);
1058 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1059 gtk_widget_show(combo);
1062 static void filter_store_populate()
1066 if (!filter_store) return;
1068 gtk_list_store_clear(filter_store);
1070 work = filter_get_list();
1076 fe = static_cast<FilterEntry *>(work->data);
1079 gtk_list_store_append(filter_store, &iter);
1080 gtk_list_store_set(filter_store, &iter, 0, fe, -1);
1084 static void filter_store_ext_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1086 auto model = static_cast<GtkWidget *>(data);
1087 auto fe = static_cast<FilterEntry *>(data);
1091 if (!new_text || strlen(new_text) < 1) return;
1093 tpath = gtk_tree_path_new_from_string(path_str);
1094 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1095 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1097 g_free(fe->extensions);
1098 fe->extensions = g_strdup(new_text);
1100 gtk_tree_path_free(tpath);
1104 static void filter_store_class_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1106 auto model = static_cast<GtkWidget *>(data);
1107 auto fe = static_cast<FilterEntry *>(data);
1112 if (!new_text || !new_text[0]) return;
1114 tpath = gtk_tree_path_new_from_string(path_str);
1115 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1116 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1118 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
1120 if (strcmp(new_text, _(format_class_list[i])) == 0)
1122 fe->file_class = static_cast<FileFormatClass>(i);
1127 gtk_tree_path_free(tpath);
1131 static void filter_store_desc_edit_cb(GtkCellRendererText *, gchar *path_str, gchar *new_text, gpointer data)
1133 auto model = static_cast<GtkWidget *>(data);
1138 if (!new_text || !new_text[0]) return;
1140 tpath = gtk_tree_path_new_from_string(path_str);
1141 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1142 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1144 g_free(fe->description);
1145 fe->description = g_strdup(new_text);
1147 gtk_tree_path_free(tpath);
1150 static void filter_store_enable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1152 auto model = static_cast<GtkWidget *>(data);
1157 tpath = gtk_tree_path_new_from_string(path_str);
1158 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1159 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1161 fe->enabled = !fe->enabled;
1163 gtk_tree_path_free(tpath);
1167 static void filter_store_writable_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1169 auto model = static_cast<GtkWidget *>(data);
1174 tpath = gtk_tree_path_new_from_string(path_str);
1175 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1176 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1178 fe->writable = !fe->writable;
1179 if (fe->writable) fe->allow_sidecar = FALSE;
1181 gtk_tree_path_free(tpath);
1185 static void filter_store_sidecar_cb(GtkCellRendererToggle *, gchar *path_str, gpointer data)
1187 auto model = static_cast<GtkWidget *>(data);
1192 tpath = gtk_tree_path_new_from_string(path_str);
1193 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1194 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1196 fe->allow_sidecar = !fe->allow_sidecar;
1197 if (fe->allow_sidecar) fe->writable = FALSE;
1199 gtk_tree_path_free(tpath);
1203 static void filter_set_func(GtkTreeViewColumn *, GtkCellRenderer *cell,
1204 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1208 gtk_tree_model_get(tree_model, iter, 0, &fe, -1);
1210 switch (GPOINTER_TO_INT(data))
1213 g_object_set(GTK_CELL_RENDERER(cell),
1214 "active", fe->enabled, NULL);
1217 g_object_set(GTK_CELL_RENDERER(cell),
1218 "text", fe->extensions, NULL);
1220 case FE_DESCRIPTION:
1221 g_object_set(GTK_CELL_RENDERER(cell),
1222 "text", fe->description, NULL);
1225 g_object_set(GTK_CELL_RENDERER(cell),
1226 "text", _(format_class_list[fe->file_class]), NULL);
1229 g_object_set(GTK_CELL_RENDERER(cell),
1230 "active", fe->writable, NULL);
1232 case FE_ALLOW_SIDECAR:
1233 g_object_set(GTK_CELL_RENDERER(cell),
1234 "active", fe->allow_sidecar, NULL);
1239 static gboolean filter_add_scroll(gpointer data)
1243 GtkCellRenderer *cell;
1244 GtkTreeViewColumn *column;
1247 GtkTreeModel *store;
1249 FilterEntry *filter;
1251 rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(filter_store), nullptr);
1252 path = gtk_tree_path_new_from_indices(rows-1, -1);
1254 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data), 0);
1256 list_cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1257 cell = static_cast<GtkCellRenderer *>(g_list_last(list_cells)->data);
1259 store = gtk_tree_view_get_model(GTK_TREE_VIEW(data));
1260 valid = gtk_tree_model_get_iter_first(store, &iter);
1264 gtk_tree_model_get(store, &iter, 0, &filter, -1);
1266 if (g_strcmp0(filter->extensions, ".new") == 0)
1268 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1272 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1275 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1276 path, column, FALSE, 0.0, 0.0 );
1277 gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(data),
1278 path, column, cell, TRUE);
1280 gtk_tree_path_free(path);
1281 g_list_free(list_cells);
1283 return(G_SOURCE_REMOVE);
1286 static void filter_add_cb(GtkWidget *, gpointer data)
1288 filter_add_unique("description", ".new", FORMAT_CLASS_IMAGE, TRUE, FALSE, TRUE);
1289 filter_store_populate();
1291 g_idle_add(static_cast<GSourceFunc>(filter_add_scroll), data);
1294 static void filter_remove_cb(GtkWidget *, gpointer data)
1296 auto filter_view = static_cast<GtkWidget *>(data);
1297 GtkTreeSelection *selection;
1301 if (!filter_store) return;
1302 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
1303 if (!gtk_tree_selection_get_selected(selection, nullptr, &iter)) return;
1304 gtk_tree_model_get(GTK_TREE_MODEL(filter_store), &iter, 0, &fe, -1);
1307 filter_remove_entry(fe);
1309 filter_store_populate();
1312 static gboolean filter_default_ok_scroll(GtkTreeView *data)
1316 GtkTreeViewColumn *column;
1318 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(filter_store), &iter);
1319 path = gtk_tree_model_get_path(GTK_TREE_MODEL(filter_store), &iter);
1320 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1322 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1326 gtk_tree_path_free(path);
1328 return(G_SOURCE_REMOVE);
1331 static void filter_default_ok_cb(GenericDialog *gd, gpointer)
1334 filter_add_defaults();
1336 filter_store_populate();
1338 g_idle_add(reinterpret_cast<GSourceFunc>(filter_default_ok_scroll), gd->data);
1341 static void dummy_cancel_cb(GenericDialog *, gpointer)
1343 /* no op, only so cancel button appears */
1346 static void filter_default_cb(GtkWidget *widget, gpointer data)
1350 gd = generic_dialog_new(_("Reset filters"),
1351 "reset_filter", widget, TRUE,
1352 dummy_cancel_cb, data);
1353 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset filters"),
1354 _("This will reset the file filters to the defaults.\nContinue?"), TRUE);
1355 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", filter_default_ok_cb, TRUE);
1356 gtk_widget_show(gd->dialog);
1359 static void filter_disable_cb(GtkWidget *widget, gpointer data)
1361 auto frame = static_cast<GtkWidget *>(data);
1363 gtk_widget_set_sensitive(frame,
1364 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1367 static void safe_delete_view_cb(GtkWidget *, gpointer)
1369 layout_set_path(nullptr, gtk_entry_get_text(GTK_ENTRY(safe_delete_path_entry)));
1372 static void safe_delete_clear_ok_cb(GenericDialog *, gpointer)
1374 file_util_trash_clear();
1377 static void safe_delete_clear_cb(GtkWidget *widget, gpointer)
1381 gd = generic_dialog_new(_("Clear trash"),
1382 "clear_trash", widget, TRUE,
1383 dummy_cancel_cb, nullptr);
1384 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Clear trash"),
1385 _("This will remove the trash contents."), FALSE);
1386 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", safe_delete_clear_ok_cb, TRUE);
1387 entry = gtk_entry_new();
1388 gtk_widget_set_can_focus(entry, FALSE);
1389 gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
1390 if (options->file_ops.safe_delete_path) gtk_entry_set_text(GTK_ENTRY(entry), options->file_ops.safe_delete_path);
1391 gtk_box_pack_start(GTK_BOX(gd->vbox), entry, FALSE, FALSE, 0);
1392 gtk_widget_show(entry);
1393 gtk_widget_show(gd->dialog);
1396 static void image_overlay_template_view_changed_cb(GtkWidget *, gpointer data)
1398 GtkWidget *pTextView;
1399 GtkTextBuffer *pTextBuffer;
1403 pTextView = GTK_WIDGET(data);
1405 pTextBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pTextView));
1406 gtk_text_buffer_get_start_iter(pTextBuffer, &iStart);
1407 gtk_text_buffer_get_end_iter(pTextBuffer, &iEnd);
1409 set_image_overlay_template_string(&c_options->image_overlay.template_string,
1410 gtk_text_buffer_get_text(pTextBuffer, &iStart, &iEnd, TRUE));
1413 static void image_overlay_default_template_ok_cb(GenericDialog *, gpointer data)
1415 auto text_view = static_cast<GtkTextView *>(data);
1416 GtkTextBuffer *buffer;
1418 set_default_image_overlay_template_string(&options->image_overlay.template_string);
1419 if (!configwindow) return;
1421 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
1422 gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
1425 static void image_overlay_default_template_cb(GtkWidget *widget, gpointer data)
1429 gd = generic_dialog_new(_("Reset image overlay template string"),
1430 "reset_image_overlay_template_string", widget, TRUE,
1431 dummy_cancel_cb, data);
1432 generic_dialog_add_message(gd, GQ_ICON_DIALOG_QUESTION, _("Reset image overlay template string"),
1433 _("This will reset the image overlay template string to the default.\nContinue?"), TRUE);
1434 generic_dialog_add_button(gd, GQ_ICON_OK, "OK", image_overlay_default_template_ok_cb, TRUE);
1435 gtk_widget_show(gd->dialog);
1438 static void image_overlay_help_cb(GtkWidget *, gpointer)
1440 help_window_show("GuideOptionsOSD.html");
1443 static void image_overlay_set_font_cb(GtkWidget *widget, gpointer)
1447 PangoFontDescription *font_desc;
1449 dialog = gtk_font_chooser_dialog_new("Image Overlay Font", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1450 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), options->image_overlay.font);
1452 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1454 font_desc = gtk_font_chooser_get_font_desc(GTK_FONT_CHOOSER(dialog));
1455 font = pango_font_description_to_string(font_desc);
1456 g_free(c_options->image_overlay.font);
1457 c_options->image_overlay.font = g_strdup(font);
1461 gtk_widget_destroy(dialog);
1464 static void image_overlay_set_text_colour_cb(GtkWidget *widget, gpointer)
1469 dialog = gtk_color_chooser_dialog_new("Image Overlay Text Colour", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1470 colour.red = options->image_overlay.text_red;
1471 colour.green = options->image_overlay.text_green;
1472 colour.blue = options->image_overlay.text_blue;
1473 colour.alpha = options->image_overlay.text_alpha;
1474 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1475 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1477 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1479 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1480 c_options->image_overlay.text_red = colour.red*255;
1481 c_options->image_overlay.text_green = colour.green*255;
1482 c_options->image_overlay.text_blue = colour.blue*255;
1483 c_options->image_overlay.text_alpha = colour.alpha*255;
1485 gtk_widget_destroy(dialog);
1489 static void image_overlay_set_background_colour_cb(GtkWidget *widget, gpointer)
1494 dialog = gtk_color_chooser_dialog_new("Image Overlay Background Colour", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1495 colour.red = options->image_overlay.background_red;
1496 colour.green = options->image_overlay.background_green;
1497 colour.blue = options->image_overlay.background_blue;
1498 colour.alpha = options->image_overlay.background_alpha;
1499 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1500 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1502 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1504 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1505 c_options->image_overlay.background_red = colour.red*255;
1506 c_options->image_overlay.background_green = colour.green*255;
1507 c_options->image_overlay.background_blue = colour.blue*255;
1508 c_options->image_overlay.background_alpha = colour.alpha*255;
1510 gtk_widget_destroy(dialog);
1513 static void accel_store_populate()
1516 GList *groups, *actions;
1518 const gchar *accel_path;
1522 if (!accel_store || !layout_window_list || !layout_window_list->data) return;
1524 gtk_tree_store_clear(accel_store);
1525 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 */
1527 g_assert(lw && lw->ui_manager);
1528 groups = gtk_ui_manager_get_action_groups(lw->ui_manager);
1531 actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
1534 action = GTK_ACTION(actions->data);
1535 accel_path = gtk_action_get_accel_path(action);
1536 if (accel_path && gtk_accel_map_lookup_entry(accel_path, &key))
1538 gchar *label, *label2, *tooltip, *accel;
1539 g_object_get(action,
1540 "tooltip", &tooltip,
1544 if (pango_parse_markup(label, -1, '_', nullptr, &label2, nullptr, nullptr) && label2)
1550 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1554 gtk_tree_store_append(accel_store, &iter, nullptr);
1555 gtk_tree_store_set(accel_store, &iter,
1558 AE_TOOLTIP, tooltip ? tooltip : "",
1559 AE_ACCEL, accel_path,
1567 actions = actions->next;
1570 groups = groups->next;
1574 static void accel_store_cleared_cb(GtkCellRendererAccel *, gchar *, gpointer)
1579 static gboolean accel_remove_key_cb(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer data)
1581 auto accel1 = static_cast<gchar *>(data);
1586 gtk_tree_model_get(model, iter, AE_KEY, &accel2, -1);
1588 gtk_accelerator_parse(accel1, &key1.accel_key, &key1.accel_mods);
1589 gtk_accelerator_parse(accel2, &key2.accel_key, &key2.accel_mods);
1591 if (key1.accel_key == key2.accel_key && key1.accel_mods == key2.accel_mods)
1593 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1594 DEBUG_1("accelerator key '%s' is already used, removing.", accel1);
1603 static void accel_store_edited_cb(GtkCellRendererAccel *, gchar *path_string, guint accel_key, GdkModifierType accel_mods, guint, gpointer)
1605 auto model = reinterpret_cast<GtkTreeModel *>(accel_store);
1609 GtkAccelKey old_key, key;
1610 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1612 gtk_tree_model_get_iter(model, &iter, path);
1613 gtk_tree_model_get(model, &iter, AE_ACCEL, &accel_path, -1);
1615 /* test if the accelerator can be stored without conflicts*/
1616 gtk_accel_map_lookup_entry(accel_path, &old_key);
1618 /* change the key and read it back (change may fail on keys hardcoded in gtk)*/
1619 gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, TRUE);
1620 gtk_accel_map_lookup_entry(accel_path, &key);
1622 /* restore the original for now, the key will be really changed when the changes are confirmed */
1623 gtk_accel_map_change_entry(accel_path, old_key.accel_key, old_key.accel_mods, TRUE);
1625 acc = gtk_accelerator_name(key.accel_key, key.accel_mods);
1626 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, acc);
1628 gtk_tree_store_set(accel_store, &iter, AE_KEY, acc, -1);
1629 gtk_tree_path_free(path);
1633 static gboolean accel_default_scroll(GtkTreeView *data)
1637 GtkTreeViewColumn *column;
1639 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(accel_store), &iter);
1640 path = gtk_tree_model_get_path(GTK_TREE_MODEL(accel_store), &iter);
1641 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1643 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1647 gtk_tree_path_free(path);
1649 return(G_SOURCE_REMOVE);
1652 static void accel_default_cb(GtkWidget *, gpointer data)
1654 accel_store_populate();
1656 g_idle_add(reinterpret_cast<GSourceFunc>(accel_default_scroll), data);
1659 void accel_clear_selection(GtkTreeModel *, GtkTreePath *, GtkTreeIter *iter, gpointer)
1661 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1664 void accel_reset_selection(GtkTreeModel *model, GtkTreePath *, GtkTreeIter *iter, gpointer)
1667 gchar *accel_path, *accel;
1669 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, -1);
1670 gtk_accel_map_lookup_entry(accel_path, &key);
1671 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1673 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, accel);
1675 gtk_tree_store_set(accel_store, iter, AE_KEY, accel, -1);
1680 static void accel_clear_cb(GtkWidget *, gpointer data)
1682 GtkTreeSelection *selection;
1684 if (!accel_store) return;
1685 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1686 gtk_tree_selection_selected_foreach(selection, &accel_clear_selection, nullptr);
1689 static void accel_reset_cb(GtkWidget *, gpointer data)
1691 GtkTreeSelection *selection;
1693 if (!accel_store) return;
1694 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1695 gtk_tree_selection_selected_foreach(selection, &accel_reset_selection, nullptr);
1700 static GtkWidget *scrolled_notebook_page(GtkWidget *notebook, const gchar *title)
1704 GtkWidget *scrolled;
1705 GtkWidget *viewport;
1707 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
1708 gtk_container_set_border_width(GTK_CONTAINER(scrolled), PREF_PAD_BORDER);
1709 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1710 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1711 label = gtk_label_new(title);
1712 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, label);
1713 gtk_widget_show(scrolled);
1715 viewport = gtk_viewport_new(nullptr, nullptr);
1716 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1717 gtk_container_add(GTK_CONTAINER(scrolled), viewport);
1718 gtk_widget_show(viewport);
1720 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1721 gtk_container_add(GTK_CONTAINER(viewport), vbox);
1722 gtk_widget_show(vbox);
1727 static void cache_standard_cb(GtkWidget *widget, gpointer)
1729 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1731 c_options->thumbnails.spec_standard =TRUE;
1732 c_options->thumbnails.cache_into_dirs = FALSE;
1736 static void cache_geeqie_cb(GtkWidget *widget, gpointer)
1738 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1740 c_options->thumbnails.spec_standard =FALSE;
1741 c_options->thumbnails.cache_into_dirs = FALSE;
1745 static void cache_local_cb(GtkWidget *widget, gpointer)
1747 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1749 c_options->thumbnails.cache_into_dirs = TRUE;
1750 c_options->thumbnails.spec_standard =FALSE;
1754 static void help_search_engine_entry_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1756 if (pos == GTK_ENTRY_ICON_PRIMARY)
1758 gtk_entry_set_text(GTK_ENTRY(userdata), HELP_SEARCH_ENGINE);
1762 gtk_entry_set_text(GTK_ENTRY(userdata), "");
1766 static void star_rating_star_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1768 gchar *rating_symbol;
1770 if (pos == GTK_ENTRY_ICON_PRIMARY)
1772 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_STAR);
1773 gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1774 g_free(rating_symbol);
1778 gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1779 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1780 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1784 static void star_rating_rejected_icon_cb(GtkEntry *, GtkEntryIconPosition pos, GdkEvent *, gpointer userdata)
1786 gchar *rating_symbol;
1788 if (pos == GTK_ENTRY_ICON_PRIMARY)
1790 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_REJECTED);
1791 gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1792 g_free(rating_symbol);
1796 gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1797 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1798 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1802 static guint star_rating_symbol_test(GtkWidget *, gpointer data)
1804 auto hbox = static_cast<GtkContainer *>(data);
1805 GString *str = g_string_new(nullptr);
1806 GtkEntry *hex_code_entry;
1807 gchar *hex_code_full;
1810 guint64 hex_value = 0;
1812 list = gtk_container_get_children(hbox);
1814 hex_code_entry = static_cast<GtkEntry *>(g_list_nth_data(list, 2));
1815 hex_code_full = g_strdup(gtk_entry_get_text(hex_code_entry));
1817 hex_code = g_strsplit(hex_code_full, "+", 2);
1818 if (hex_code[0] && hex_code[1])
1820 hex_value = strtoull(hex_code[1], nullptr, 16);
1822 if (!hex_value || hex_value > 0x10FFFF)
1824 hex_value = 0x003F; // Unicode 'Question Mark'
1826 str = g_string_append_unichar(str, static_cast<gunichar>(hex_value));
1827 gtk_label_set_text(static_cast<GtkLabel *>(g_list_nth_data(list, 1)), str->str);
1829 g_strfreev(hex_code);
1830 g_string_free(str, TRUE);
1831 g_free(hex_code_full);
1836 static void star_rating_star_test_cb(GtkWidget *widget, gpointer data)
1838 guint64 star_symbol;
1840 star_symbol = star_rating_symbol_test(widget, data);
1841 c_options->star_rating.star = star_symbol;
1844 static void star_rating_rejected_test_cb(GtkWidget *widget, gpointer data)
1846 guint64 rejected_symbol;
1848 rejected_symbol = star_rating_symbol_test(widget, data);
1849 c_options->star_rating.rejected = rejected_symbol;
1852 /* general options tab */
1853 static void timezone_database_install_cb(GtkWidget *widget, gpointer data);
1857 GCancellable *cancellable;
1859 GtkWidget *progress;
1861 GFile *timezone_database_gq;
1862 gchar *timezone_database_user;
1865 static void config_tab_general(GtkWidget *notebook)
1870 GtkWidget *group_frame;
1871 GtkWidget *subgroup;
1873 GtkWidget *ct_button;
1876 gint hours, minutes, remainder;
1878 GtkWidget *star_rating_entry;
1880 gchar *rating_symbol;
1883 gchar *download_locn;
1884 GNetworkMonitor *net_mon;
1885 GSocketConnectable *tz_org;
1886 gboolean internet_available = FALSE;
1889 vbox = scrolled_notebook_page(notebook, _("General"));
1891 group = pref_group_new(vbox, FALSE, _("Thumbnails"), GTK_ORIENTATION_VERTICAL);
1893 table = pref_table_new(group, 2, 2, FALSE, FALSE);
1894 add_thumb_size_menu(table, 0, 0, _("Size:"));
1895 add_quality_menu(table, 0, 1, _("Quality:"), options->thumbnails.quality, &c_options->thumbnails.quality);
1897 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1898 pref_label_new(hbox, _("Custom size: "));
1899 pref_spin_new_int(hbox, _("Width:"), nullptr, 1, 512, 1, options->thumbnails.max_width, &c_options->thumbnails.max_width);
1900 pref_spin_new_int(hbox, _("Height:"), nullptr, 1, 512, 1, options->thumbnails.max_height, &c_options->thumbnails.max_height);
1902 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails and sim. files"),
1903 options->thumbnails.enable_caching, &c_options->thumbnails.enable_caching);
1905 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1906 pref_checkbox_link_sensitivity(ct_button, subgroup);
1908 c_options->thumbnails.spec_standard = options->thumbnails.spec_standard;
1909 c_options->thumbnails.cache_into_dirs = options->thumbnails.cache_into_dirs;
1910 group_frame = pref_frame_new(subgroup, TRUE, _("Use Geeqie thumbnail style and cache"),
1911 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1912 button = pref_radiobutton_new(group_frame, nullptr, get_thumbnails_cache_dir(),
1913 !options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
1914 G_CALLBACK(cache_geeqie_cb), nullptr);
1916 group_frame = pref_frame_new(subgroup, TRUE,
1917 _("Store thumbnails local to image folder (non-standard)"),
1918 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1919 pref_radiobutton_new(group_frame, button, "*/.thumbnails",
1920 !options->thumbnails.spec_standard && options->thumbnails.cache_into_dirs,
1921 G_CALLBACK(cache_local_cb), nullptr);
1923 group_frame = pref_frame_new(subgroup, TRUE,
1924 _("Use standard thumbnail style and cache, shared with other applications"),
1925 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1926 pref_radiobutton_new(group_frame, button, get_thumbnails_standard_cache_dir(),
1927 options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
1928 G_CALLBACK(cache_standard_cb), nullptr);
1930 pref_checkbox_new_int(group, _("Use EXIF thumbnails when available (EXIF thumbnails may be outdated)"),
1931 options->thumbnails.use_exif, &c_options->thumbnails.use_exif);
1933 pref_checkbox_new_int(group, _("Thumbnail color management"),
1934 options->thumbnails.use_color_management, &c_options->thumbnails.use_color_management);
1936 spin = pref_spin_new_int(group, _("Collection preview:"), nullptr,
1938 options->thumbnails.collection_preview, &c_options->thumbnails.collection_preview);
1939 gtk_widget_set_tooltip_text(spin, _("The maximum number of thumbnails shown in a Collection preview montage"));
1941 #ifdef HAVE_FFMPEGTHUMBNAILER_METADATA
1942 pref_checkbox_new_int(group, _("Use embedded metadata in video files as thumbnails when available"),
1943 options->thumbnails.use_ft_metadata, &c_options->thumbnails.use_ft_metadata);
1946 pref_spacer(group, PREF_PAD_GROUP);
1948 group = pref_group_new(vbox, FALSE, _("Star Rating"), GTK_ORIENTATION_VERTICAL);
1950 c_options->star_rating.star = options->star_rating.star;
1951 c_options->star_rating.rejected = options->star_rating.rejected;
1953 str = g_string_new(nullptr);
1954 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1955 pref_label_new(hbox, _("Star character: "));
1956 str = g_string_append_unichar(str, options->star_rating.star);
1957 pref_label_new(hbox, g_strdup(str->str));
1958 rating_symbol = g_strdup_printf("U+%X", options->star_rating.star);
1959 star_rating_entry = gtk_entry_new();
1960 gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
1961 gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
1962 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
1963 gtk_widget_show(star_rating_entry);
1964 button = pref_button_new(nullptr, nullptr, _("Set"),
1965 G_CALLBACK(star_rating_star_test_cb), hbox);
1966 gtk_widget_set_tooltip_text(button, _("Display selected character"));
1967 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
1968 gtk_widget_show(button);
1969 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."));
1970 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
1971 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
1972 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
1973 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
1974 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
1975 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
1976 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
1977 GTK_ENTRY_ICON_PRIMARY, _("Default"));
1978 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
1979 G_CALLBACK(star_rating_star_icon_cb),
1982 g_string_free(str, TRUE);
1983 g_free(rating_symbol);
1985 str = g_string_new(nullptr);
1986 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1987 pref_label_new(hbox, _("Rejected character: "));
1988 str = g_string_append_unichar(str, options->star_rating.rejected);
1989 pref_label_new(hbox, g_strdup(str->str));
1990 rating_symbol = g_strdup_printf("U+%X", options->star_rating.rejected);
1991 star_rating_entry = gtk_entry_new();
1992 gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
1993 gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
1994 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
1995 gtk_widget_show(star_rating_entry);
1996 button = pref_button_new(nullptr, nullptr, _("Set"),
1997 G_CALLBACK(star_rating_rejected_test_cb), hbox);
1998 gtk_widget_set_tooltip_text(button, _("Display selected character"));
1999 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2000 gtk_widget_show(button);
2001 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."));
2002 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2003 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2004 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2005 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2006 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2007 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2008 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2009 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2010 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2011 G_CALLBACK(star_rating_rejected_icon_cb),
2014 g_string_free(str, TRUE);
2015 g_free(rating_symbol);
2017 pref_spacer(group, PREF_PAD_GROUP);
2019 group = pref_group_new(vbox, FALSE, _("Slide show"), GTK_ORIENTATION_VERTICAL);
2021 c_options->slideshow.delay = options->slideshow.delay;
2022 hours = options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2023 remainder = options->slideshow.delay % (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2024 minutes = remainder / (60 * SLIDESHOW_SUBSECOND_PRECISION);
2025 seconds = static_cast<gdouble>(remainder % (60 * SLIDESHOW_SUBSECOND_PRECISION)) /
2026 SLIDESHOW_SUBSECOND_PRECISION;
2028 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2030 spin = pref_spin_new(hbox, _("Delay between image change hrs:mins:secs.dec"), nullptr,
2032 options->slideshow.delay ? hours : 0.0,
2033 G_CALLBACK(slideshow_delay_hours_cb), nullptr);
2034 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2035 spin = pref_spin_new(hbox, ":" , nullptr,
2037 options->slideshow.delay ? minutes: 0.0,
2038 G_CALLBACK(slideshow_delay_minutes_cb), nullptr);
2039 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2040 spin = pref_spin_new(hbox, ":", nullptr,
2041 SLIDESHOW_MIN_SECONDS, 59, 1.0, 1,
2042 options->slideshow.delay ? seconds : 10.0,
2043 G_CALLBACK(slideshow_delay_seconds_cb), nullptr);
2044 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2046 pref_checkbox_new_int(group, _("Random"), options->slideshow.random, &c_options->slideshow.random);
2047 pref_checkbox_new_int(group, _("Repeat"), options->slideshow.repeat, &c_options->slideshow.repeat);
2049 pref_spacer(group, PREF_PAD_GROUP);
2051 group = pref_group_new(vbox, FALSE, _("Image loading and caching"), GTK_ORIENTATION_VERTICAL);
2053 pref_spin_new_int(group, _("Decoded image cache size (MiB):"), nullptr,
2054 0, 99999, 1, options->image.image_cache_max, &c_options->image.image_cache_max);
2055 pref_checkbox_new_int(group, _("Preload next image"),
2056 options->image.enable_read_ahead, &c_options->image.enable_read_ahead);
2058 pref_checkbox_new_int(group, _("Refresh on file change"),
2059 options->update_on_time_change, &c_options->update_on_time_change);
2062 pref_spacer(group, PREF_PAD_GROUP);
2064 group = pref_group_new(vbox, FALSE, _("Expand menu and toolbar"), GTK_ORIENTATION_VERTICAL);
2066 pref_checkbox_new_int(group, _("Expand menu and toolbar (NOTE! Geeqie must be restarted for change to take effect)"),
2067 options->expand_menu_toolbar, &c_options->expand_menu_toolbar);
2068 gtk_widget_set_tooltip_text(group, _("Expand the menu and toolbar to the full width of the window"));
2070 pref_spacer(group, PREF_PAD_GROUP);
2072 if (g_getenv("APPDIR") && strstr(g_getenv("APPDIR"), "/tmp/.mount_Geeqie"))
2074 group = pref_group_new(vbox, FALSE, _("AppImage updates notifications"), GTK_ORIENTATION_VERTICAL);
2075 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2076 pref_checkbox_new_int(group, _("Enable"), options->appimage_notifications, &c_options->appimage_notifications);
2077 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"));
2079 pref_spacer(group, PREF_PAD_GROUP);
2083 net_mon = g_network_monitor_get_default();
2084 tz_org = g_network_address_parse_uri(TIMEZONE_DATABASE_WEB, 80, nullptr);
2087 internet_available = g_network_monitor_can_reach(net_mon, tz_org, nullptr, nullptr);
2088 g_object_unref(tz_org);
2091 group = pref_group_new(vbox, FALSE, _("Timezone database"), GTK_ORIENTATION_VERTICAL);
2092 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2094 if (!internet_available)
2096 gtk_widget_set_sensitive(group, FALSE);
2099 tz = g_new0(TZData, 1);
2101 path = path_from_utf8(TIMEZONE_DATABASE_WEB);
2102 basename = g_path_get_basename(path);
2103 tz->timezone_database_user = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
2107 if (isfile(tz->timezone_database_user))
2109 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Update"), G_CALLBACK(timezone_database_install_cb), tz);
2113 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Install"), G_CALLBACK(timezone_database_install_cb), tz);
2116 download_locn = g_strconcat(_("Download database from: "), TIMEZONE_DATABASE_WEB, NULL);
2117 pref_label_new(GTK_WIDGET(hbox), download_locn);
2118 g_free(download_locn);
2120 if (!internet_available)
2122 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"));
2126 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"));
2128 gtk_widget_show(button);
2130 pref_spacer(group, PREF_PAD_GROUP);
2132 group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL);
2134 help_search_engine_entry = gtk_entry_new();
2135 gtk_entry_set_text(GTK_ENTRY(help_search_engine_entry), options->help_search_engine);
2136 gtk_box_pack_start(GTK_BOX(group), help_search_engine_entry, FALSE, FALSE, 0);
2137 gtk_widget_show(help_search_engine_entry);
2139 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"));
2141 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2142 GTK_ENTRY_ICON_SECONDARY, GQ_ICON_CLEAR);
2143 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2144 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2145 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2146 GTK_ENTRY_ICON_PRIMARY, GQ_ICON_REVERT);
2147 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2148 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2149 g_signal_connect(GTK_ENTRY(help_search_engine_entry), "icon-press",
2150 G_CALLBACK(help_search_engine_entry_icon_cb),
2151 help_search_engine_entry);
2155 static void config_tab_image(GtkWidget *notebook)
2160 GtkWidget *ct_button;
2161 GtkWidget *enlargement_button;
2165 vbox = scrolled_notebook_page(notebook, _("Image"));
2167 group = pref_group_new(vbox, FALSE, _("Zoom"), GTK_ORIENTATION_VERTICAL);
2169 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2170 add_quality_menu(table, 0, 0, _("Quality:"), options->image.zoom_quality, &c_options->image.zoom_quality);
2172 pref_checkbox_new_int(group, _("Two pass rendering (apply HQ zoom and color correction in second pass)"),
2173 options->image.zoom_2pass, &c_options->image.zoom_2pass);
2175 c_options->image.zoom_increment = options->image.zoom_increment;
2176 spin = pref_spin_new(group, _("Zoom increment:"), nullptr,
2177 0.01, 4.0, 0.01, 2, static_cast<gdouble>(options->image.zoom_increment) / 100.0,
2178 G_CALLBACK(zoom_increment_cb), nullptr);
2179 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2181 c_options->image.zoom_style = options->image.zoom_style;
2182 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2183 add_zoom_style_selection_menu(table, 0, 0, _("Zoom style:"), options->image.zoom_style, &c_options->image.zoom_style);
2185 group = pref_group_new(vbox, FALSE, _("Fit image to window"), GTK_ORIENTATION_VERTICAL);
2187 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2188 enlargement_button = pref_checkbox_new_int(hbox, _("Allow enlargement of image (max. size in %)"),
2189 options->image.zoom_to_fit_allow_expand, &c_options->image.zoom_to_fit_allow_expand);
2190 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2192 options->image.max_enlargement_size, &c_options->image.max_enlargement_size);
2193 pref_checkbox_link_sensitivity(enlargement_button, spin);
2194 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."));
2196 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2197 ct_button = pref_checkbox_new_int(hbox, _("Virtual window size (% of actual window):"),
2198 options->image.limit_autofit_size, &c_options->image.limit_autofit_size);
2199 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2201 options->image.max_autofit_size, &c_options->image.max_autofit_size);
2202 pref_checkbox_link_sensitivity(ct_button, spin);
2203 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."));
2205 group = pref_group_new(vbox, FALSE, _("Tile size"), GTK_ORIENTATION_VERTICAL);
2207 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2208 spin = pref_spin_new_int(hbox, _("Pixels"), _("(Requires restart)"),
2210 options->image.tile_size, &c_options->image.tile_size);
2211 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."));
2213 group = pref_group_new(vbox, FALSE, _("Appearance"), GTK_ORIENTATION_VERTICAL);
2215 pref_checkbox_new_int(group, _("Use custom border color in window mode"),
2216 options->image.use_custom_border_color, &c_options->image.use_custom_border_color);
2218 pref_checkbox_new_int(group, _("Use custom border color in fullscreen mode"),
2219 options->image.use_custom_border_color_in_fullscreen, &c_options->image.use_custom_border_color_in_fullscreen);
2221 pref_color_button_new(group, _("Border color"), &options->image.border_color,
2222 G_CALLBACK(pref_color_button_set_cb), &c_options->image.border_color);
2224 c_options->image.border_color = options->image.border_color;
2226 pref_color_button_new(group, _("Alpha channel color 1"), &options->image.alpha_color_1,
2227 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_1);
2229 pref_color_button_new(group, _("Alpha channel color 2"), &options->image.alpha_color_2,
2230 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_2);
2232 c_options->image.alpha_color_1 = options->image.alpha_color_1;
2233 c_options->image.alpha_color_2 = options->image.alpha_color_2;
2238 static void save_default_window_layout_cb(GtkWidget *, gpointer)
2240 LayoutWindow *lw = nullptr;
2241 gchar *default_path;
2244 /* Get current lw */
2247 tmp_id = lw->options.id;
2248 lw->options.id = g_strdup("lw_default");
2250 default_path = g_build_filename(get_rc_dir(), DEFAULT_WINDOW_LAYOUT, NULL);
2251 save_default_layout_options_to_file(default_path, options, lw);
2252 g_free(lw->options.id);
2253 lw->options.id = tmp_id;
2254 g_free(default_path);
2257 static gboolean popover_cb(gpointer data)
2259 auto popover = static_cast<GtkPopover *>(data);
2261 gtk_popover_popdown(popover);
2266 static void default_layout_changed_cb(GtkWidget *, GtkPopover *popover)
2268 gtk_popover_popup(popover);
2270 g_timeout_add(2000, popover_cb, popover);
2273 static GtkWidget *create_popover(GtkWidget *parent, GtkWidget *child, GtkPositionType pos)
2277 popover = gtk_popover_new(parent);
2278 gtk_popover_set_position(GTK_POPOVER (popover), pos);
2279 gtk_container_add (GTK_CONTAINER(popover), child);
2280 gtk_container_set_border_width(GTK_CONTAINER (popover), 6);
2281 gtk_widget_show (child);
2286 static void config_tab_windows(GtkWidget *notebook)
2291 GtkWidget *subgroup;
2293 GtkWidget *ct_button;
2296 vbox = scrolled_notebook_page(notebook, _("Windows"));
2298 group = pref_group_new(vbox, FALSE, _("State"), GTK_ORIENTATION_VERTICAL);
2300 ct_button = pref_checkbox_new_int(group, _("Remember session"),
2301 options->save_window_positions, &c_options->save_window_positions);
2303 button = pref_checkbox_new_int(group, _("Use saved window positions also for new windows"),
2304 options->use_saved_window_positions_for_new_windows, &c_options->use_saved_window_positions_for_new_windows);
2305 pref_checkbox_link_sensitivity(ct_button, button);
2307 button = pref_checkbox_new_int(group, _("Remember window workspace"),
2308 options->save_window_workspace, &c_options->save_window_workspace);
2309 pref_checkbox_link_sensitivity(ct_button, button);
2311 pref_checkbox_new_int(group, _("Remember tool state (float/hidden)"),
2312 options->tools_restore_state, &c_options->tools_restore_state);
2314 pref_checkbox_new_int(group, _("Remember dialog window positions"),
2315 options->save_dialog_window_positions, &c_options->save_dialog_window_positions);
2317 pref_checkbox_new_int(group, _("Show window IDs"),
2318 options->show_window_ids, &c_options->show_window_ids);
2320 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2321 pref_label_new(subgroup, _("Use current layout for default: "));
2322 button = pref_button_new(subgroup, nullptr, _("Set"), G_CALLBACK(save_default_window_layout_cb), nullptr);
2326 popover = create_popover(button, gtk_label_new(_("Current window layout\nhas been set as default")), GTK_POS_TOP);
2327 gtk_popover_set_modal(GTK_POPOVER (popover), FALSE);
2328 g_signal_connect(button, "clicked", G_CALLBACK(default_layout_changed_cb), popover);
2330 group = pref_group_new(vbox, FALSE, _("Size"), GTK_ORIENTATION_VERTICAL);
2332 pref_checkbox_new_int(group, _("Fit window to image when tools are hidden/floating"),
2333 options->image.fit_window_to_image, &c_options->image.fit_window_to_image);
2335 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2336 ct_button = pref_checkbox_new_int(hbox, _("Limit size when auto-sizing window (%):"),
2337 options->image.limit_window_size, &c_options->image.limit_window_size);
2338 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2340 options->image.max_window_size, &c_options->image.max_window_size);
2341 pref_checkbox_link_sensitivity(ct_button, spin);
2343 group = pref_group_new(vbox, FALSE, _("Full screen"), GTK_ORIENTATION_VERTICAL);
2345 c_options->fullscreen.screen = options->fullscreen.screen;
2346 c_options->fullscreen.above = options->fullscreen.above;
2347 hbox = fullscreen_prefs_selection_new(_("Location:"), &c_options->fullscreen.screen, &c_options->fullscreen.above);
2348 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2349 gtk_widget_show(hbox);
2351 pref_checkbox_new_int(group, _("Smooth image flip"),
2352 options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
2353 pref_checkbox_new_int(group, _("Disable screen saver"),
2354 options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
2357 #define PRE_FORMATTED_COLUMNS 5
2358 static void config_tab_osd(GtkWidget *notebook)
2364 GtkWidget *image_overlay_template_view;
2365 GtkWidget *scrolled;
2366 GtkWidget *scrolled_pre_formatted;
2367 GtkTextBuffer *buffer;
2369 GtkWidget *subgroup;
2371 vbox = scrolled_notebook_page(notebook, _("OSD"));
2373 image_overlay_template_view = gtk_text_view_new();
2375 group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
2377 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2379 scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
2380 gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
2381 gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
2382 gtk_widget_show(scrolled_pre_formatted);
2383 gtk_widget_show(subgroup);
2385 pref_line(group, PREF_PAD_GAP);
2387 pref_label_new(group, _("Image overlay template"));
2389 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
2390 gtk_widget_set_size_request(scrolled, 200, 150);
2391 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2392 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
2393 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2394 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
2395 gtk_widget_show(scrolled);
2397 gtk_widget_set_tooltip_markup(image_overlay_template_view,
2398 _("Extensive formatting options are shown in the Help file"));
2400 gtk_container_add(GTK_CONTAINER(scrolled), image_overlay_template_view);
2401 gtk_widget_show(image_overlay_template_view);
2403 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2405 button = pref_button_new(nullptr, GQ_ICON_SELECT_FONT, _("Font"),
2406 G_CALLBACK(image_overlay_set_font_cb), notebook);
2408 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2409 gtk_widget_show(button);
2411 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Text"),
2412 G_CALLBACK(image_overlay_set_text_colour_cb), nullptr);
2413 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2414 gtk_widget_show(button);
2416 button = pref_button_new(nullptr, GQ_ICON_SELECT_COLOR, _("Background"),
2417 G_CALLBACK(image_overlay_set_background_colour_cb), nullptr);
2418 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2419 gtk_widget_show(button);
2420 image_overlay_set_text_colours();
2422 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2423 G_CALLBACK(image_overlay_default_template_cb), image_overlay_template_view);
2424 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2425 gtk_widget_show(button);
2427 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
2428 G_CALLBACK(image_overlay_help_cb), nullptr);
2429 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2430 gtk_widget_show(button);
2432 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_overlay_template_view));
2433 if (options->image_overlay.template_string) gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
2434 g_signal_connect(G_OBJECT(buffer), "changed",
2435 G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
2437 pref_line(group, PREF_PAD_GAP);
2439 group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL);
2440 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2441 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2442 gtk_widget_show(hbox);
2443 label = gtk_label_new(_("%Exif.Image.Orientation%"));
2444 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2445 gtk_widget_show(label);
2446 pref_spacer(group,TRUE);
2448 group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL);
2449 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2450 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2451 gtk_widget_show(hbox);
2452 label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
2453 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2454 gtk_widget_show(label);
2455 pref_spacer(group,TRUE);
2457 group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL);
2458 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2459 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2460 gtk_widget_show(hbox);
2461 label = gtk_label_new(_("%path:39%"));
2462 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2463 gtk_widget_show(label);
2464 pref_spacer(group,TRUE);
2466 group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL);
2467 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2468 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2469 gtk_widget_show(hbox);
2470 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%"));
2471 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2472 gtk_widget_show(label);
2473 pref_spacer(group,TRUE);
2475 group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL);
2476 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2477 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2478 gtk_widget_show(hbox);
2479 label = gtk_label_new(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
2480 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2481 gtk_widget_show(label);
2484 static GtkTreeModel *create_class_model()
2486 GtkListStore *model;
2490 /* create list store */
2491 model = gtk_list_store_new(1, G_TYPE_STRING);
2492 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
2494 gtk_list_store_append(model, &iter);
2495 gtk_list_store_set(model, &iter, 0, _(format_class_list[i]), -1);
2497 return GTK_TREE_MODEL (model);
2502 static gint filter_table_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
2504 gint n = GPOINTER_TO_INT(data);
2506 FilterEntry *filter_a;
2507 FilterEntry *filter_b;
2509 gtk_tree_model_get(model, a, 0, &filter_a, -1);
2510 gtk_tree_model_get(model, b, 0, &filter_b, -1);
2514 case FILETYPES_COLUMN_ENABLED:
2516 ret = filter_a->enabled - filter_b->enabled;
2519 case FILETYPES_COLUMN_FILTER:
2521 ret = g_utf8_collate(filter_a->extensions, filter_b->extensions);
2524 case FILETYPES_COLUMN_DESCRIPTION:
2526 ret = g_utf8_collate(filter_a->description, filter_b->description);
2529 case FILETYPES_COLUMN_CLASS:
2531 ret = g_strcmp0(format_class_list[filter_a->file_class], format_class_list[filter_b->file_class]);
2534 case FILETYPES_COLUMN_WRITABLE:
2536 ret = filter_a->writable - filter_b->writable;
2539 case FILETYPES_COLUMN_SIDECAR:
2541 ret = filter_a->allow_sidecar - filter_b->allow_sidecar;
2545 g_return_val_if_reached(0);
2551 static gboolean search_function_cb(GtkTreeModel *model, gint, const gchar *key, GtkTreeIter *iter, gpointer)
2554 gboolean ret = TRUE;
2556 gtk_tree_model_get(model, iter, 0, &fe, -1);
2558 if (g_strstr_len(fe->extensions, -1, key))
2566 static void config_tab_files(GtkWidget *notebook)
2573 GtkWidget *ct_button;
2574 GtkWidget *scrolled;
2575 GtkWidget *filter_view;
2576 GtkCellRenderer *renderer;
2577 GtkTreeSelection *selection;
2578 GtkTreeViewColumn *column;
2580 vbox = scrolled_notebook_page(notebook, _("File Filters"));
2582 group = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2584 pref_checkbox_new_int(group, _("Show hidden files or folders"),
2585 options->file_filter.show_hidden_files, &c_options->file_filter.show_hidden_files);
2586 pref_checkbox_new_int(group, _("Show parent folder (..)"),
2587 options->file_filter.show_parent_directory, &c_options->file_filter.show_parent_directory);
2588 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);
2589 pref_checkbox_new_int(group, _("Disable file extension checks"),
2590 options->file_filter.disable_file_extension_checks, &c_options->file_filter.disable_file_extension_checks);
2592 ct_button = pref_checkbox_new_int(group, _("Disable File Filtering"),
2593 options->file_filter.disable, &c_options->file_filter.disable);
2596 group = pref_group_new(vbox, FALSE, _("Grouping sidecar extensions"), GTK_ORIENTATION_VERTICAL);
2598 sidecar_ext_entry = gtk_entry_new();
2599 gtk_entry_set_text(GTK_ENTRY(sidecar_ext_entry), options->sidecar.ext);
2600 gtk_box_pack_start(GTK_BOX(group), sidecar_ext_entry, FALSE, FALSE, 0);
2601 gtk_widget_show(sidecar_ext_entry);
2603 group = pref_group_new(vbox, TRUE, _("File types"), GTK_ORIENTATION_VERTICAL);
2605 frame = pref_group_parent(group);
2606 g_signal_connect(G_OBJECT(ct_button), "toggled",
2607 G_CALLBACK(filter_disable_cb), frame);
2608 gtk_widget_set_sensitive(frame, !options->file_filter.disable);
2610 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
2611 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2612 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2613 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2614 gtk_widget_show(scrolled);
2616 filter_store = gtk_list_store_new(1, G_TYPE_POINTER);
2617 filter_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
2618 g_object_unref(filter_store);
2619 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
2620 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2622 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), FALSE);
2624 column = gtk_tree_view_column_new();
2625 gtk_tree_view_column_set_title(column, _("Enabled"));
2626 gtk_tree_view_column_set_resizable(column, TRUE);
2628 renderer = gtk_cell_renderer_toggle_new();
2629 g_signal_connect(G_OBJECT(renderer), "toggled",
2630 G_CALLBACK(filter_store_enable_cb), filter_store);
2631 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2632 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2633 GINT_TO_POINTER(FE_ENABLE), nullptr);
2634 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);
2635 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_ENABLED);
2636 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2638 column = gtk_tree_view_column_new();
2639 gtk_tree_view_column_set_title(column, _("Filter"));
2640 gtk_tree_view_column_set_resizable(column, TRUE);
2641 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);
2642 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_FILTER);
2644 renderer = gtk_cell_renderer_text_new();
2645 g_signal_connect(G_OBJECT(renderer), "edited",
2646 G_CALLBACK(filter_store_ext_edit_cb), filter_store);
2647 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2648 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2649 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2650 GINT_TO_POINTER(FE_EXTENSION), nullptr);
2651 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2653 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), TRUE);
2654 gtk_tree_view_set_search_column(GTK_TREE_VIEW(filter_view), FILETYPES_COLUMN_FILTER);
2655 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(filter_view), search_function_cb, nullptr, nullptr);
2657 column = gtk_tree_view_column_new();
2658 gtk_tree_view_column_set_title(column, _("Description"));
2659 gtk_tree_view_column_set_resizable(column, TRUE);
2660 gtk_tree_view_column_set_fixed_width(column, 200);
2661 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2663 renderer = gtk_cell_renderer_text_new();
2664 g_signal_connect(G_OBJECT(renderer), "edited",
2665 G_CALLBACK(filter_store_desc_edit_cb), filter_store);
2666 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2667 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2668 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2669 GINT_TO_POINTER(FE_DESCRIPTION), nullptr);
2670 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2671 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);
2672 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_DESCRIPTION);
2674 column = gtk_tree_view_column_new();
2675 gtk_tree_view_column_set_title(column, _("Class"));
2676 gtk_tree_view_column_set_resizable(column, TRUE);
2677 renderer = gtk_cell_renderer_combo_new();
2678 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE,
2679 "model", create_class_model(),
2684 g_signal_connect(G_OBJECT(renderer), "edited",
2685 G_CALLBACK(filter_store_class_edit_cb), filter_store);
2686 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2687 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2688 GINT_TO_POINTER(FE_CLASS), 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_CLASS, filter_table_sort_cb, GINT_TO_POINTER(FILETYPES_COLUMN_CLASS), nullptr);
2691 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_CLASS);
2693 column = gtk_tree_view_column_new();
2694 gtk_tree_view_column_set_title(column, _("Writable"));
2695 gtk_tree_view_column_set_resizable(column, FALSE);
2696 renderer = gtk_cell_renderer_toggle_new();
2697 g_signal_connect(G_OBJECT(renderer), "toggled",
2698 G_CALLBACK(filter_store_writable_cb), filter_store);
2699 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2700 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2701 GINT_TO_POINTER(FE_WRITABLE), nullptr);
2702 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2703 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);
2704 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_WRITABLE);
2706 column = gtk_tree_view_column_new();
2707 gtk_tree_view_column_set_title(column, _("Sidecar is allowed"));
2708 gtk_tree_view_column_set_resizable(column, FALSE);
2709 renderer = gtk_cell_renderer_toggle_new();
2710 g_signal_connect(G_OBJECT(renderer), "toggled",
2711 G_CALLBACK(filter_store_sidecar_cb), filter_store);
2712 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2713 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2714 GINT_TO_POINTER(FE_ALLOW_SIDECAR), nullptr);
2715 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2716 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);
2717 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_SIDECAR);
2719 filter_store_populate();
2720 gtk_container_add(GTK_CONTAINER(scrolled), filter_view);
2721 gtk_widget_show(filter_view);
2723 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2725 button = pref_button_new(nullptr, nullptr, _("Defaults"),
2726 G_CALLBACK(filter_default_cb), filter_view);
2727 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2728 gtk_widget_show(button);
2730 button = pref_button_new(nullptr, GQ_ICON_REMOVE, _("Remove"),
2731 G_CALLBACK(filter_remove_cb), filter_view);
2732 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2733 gtk_widget_show(button);
2735 button = pref_button_new(nullptr, GQ_ICON_ADD, _("Add"),
2736 G_CALLBACK(filter_add_cb), filter_view);
2737 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2738 gtk_widget_show(button);
2742 static void config_tab_metadata(GtkWidget *notebook)
2747 GtkWidget *ct_button;
2749 GtkWidget *tmp_widget;
2751 GtkWidget *text_label;
2753 vbox = scrolled_notebook_page(notebook, _("Metadata"));
2756 group = pref_group_new(vbox, FALSE, _("Metadata writing sequence"), GTK_ORIENTATION_VERTICAL);
2758 label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2760 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."));
2761 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2762 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2764 gtk_widget_set_tooltip_text(label, _("A flowchart of the sequence is shown in the Help file"));
2766 ct_button = pref_checkbox_new_int(group, "", options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2767 text_label = gtk_bin_get_child(GTK_BIN(ct_button));
2768 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"));
2769 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2771 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)"));
2772 gtk_widget_set_tooltip_markup(ct_button, markup);
2776 gtk_widget_set_sensitive(ct_button, FALSE);
2779 tmp_widget = pref_checkbox_new_int(group, "", options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2780 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2781 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)"));
2782 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2785 label = pref_label_new(group, "");
2786 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(), "/");
2787 gtk_label_set_markup (GTK_LABEL(label), markup);
2790 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2791 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2792 gtk_misc_set_padding(GTK_MISC(label), 22, 0);
2793 pref_spacer(group, PREF_PAD_GROUP);
2795 group = pref_group_new(vbox, FALSE, _("Step 1 Options:"), GTK_ORIENTATION_VERTICAL);
2797 gtk_widget_set_sensitive(group, FALSE);
2800 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2801 pref_checkbox_link_sensitivity(ct_button, hbox);
2803 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);
2804 gtk_widget_set_tooltip_text(tmp_widget, _("A simplified conversion list is in the Help file"));
2806 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);
2808 pref_checkbox_new_int(hbox, _("Ask before writing to image files"), options->metadata.confirm_write, &c_options->metadata.confirm_write);
2810 tmp_widget= pref_checkbox_new_int(hbox, "", options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2811 gtk_widget_set_tooltip_text(tmp_widget, _("This file naming convention is used by Darktable"));
2812 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2813 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", ")");
2814 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2817 pref_spacer(group, PREF_PAD_GROUP);
2819 group = pref_group_new(vbox, FALSE, _("Steps 2 and 3 Option:"), GTK_ORIENTATION_VERTICAL);
2821 gtk_widget_set_sensitive(group, FALSE);
2824 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);
2826 pref_spacer(group, PREF_PAD_GROUP);
2828 group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2829 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);
2830 gtk_widget_set_tooltip_text(tmp_widget, _("See the Help file for a list of the tags used"));
2832 tmp_widget = pref_checkbox_new_int(group, _("Permit Keywords to be case-sensitive"), options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2833 gtk_widget_set_tooltip_text(tmp_widget, _("When selected, \"Place\" and \"place\" are two different keywords"));
2835 ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"), options->metadata.write_orientation, &c_options->metadata.write_orientation);
2836 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"));
2839 gtk_widget_set_sensitive(ct_button, FALSE);
2842 pref_spacer(group, PREF_PAD_GROUP);
2844 group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2846 ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"), options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
2848 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2849 pref_checkbox_link_sensitivity(ct_button, hbox);
2851 pref_spin_new_int(hbox, _("Timeout (seconds):"), nullptr, 0, 900, 1, options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
2853 pref_checkbox_new_int(group, _("Write metadata on image change"), options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
2855 pref_checkbox_new_int(group, _("Write metadata on directory change"), options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
2857 pref_spacer(group, PREF_PAD_GROUP);
2860 group = pref_group_new(vbox, FALSE, _("Spelling checks"), GTK_ORIENTATION_VERTICAL);
2862 ct_button = pref_checkbox_new_int(group, _("Check spelling - Requires restart"), options->metadata.check_spelling, &c_options->metadata.check_spelling);
2863 gtk_widget_set_tooltip_text(ct_button, _("Spelling checks are performed on info sidebar panes Comment, Headline and Title"));
2866 pref_spacer(group, PREF_PAD_GROUP);
2868 group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
2870 ct_button = pref_checkbox_new_int(group, _("Read metadata in background"), options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
2871 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");
2876 struct KeywordFindData
2883 GtkWidget *button_close;
2884 GtkWidget *button_stop;
2885 GtkWidget *button_start;
2886 GtkWidget *progress;
2894 guint idle_id; /* event source id */
2897 #define KEYWORD_DIALOG_WIDTH 400
2899 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
2901 GList *list_d = nullptr;
2902 GList *list_f = nullptr;
2906 filelist_read(dir_fd, &list_f, &list_d);
2910 filelist_read(dir_fd, &list_f, nullptr);
2913 list_f = filelist_filter(list_f, FALSE);
2914 list_d = filelist_filter(list_d, TRUE);
2916 kfd->list = g_list_concat(list_f, kfd->list);
2917 kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
2920 static void keywords_find_reset(KeywordFindData *kfd)
2922 filelist_free(kfd->list);
2923 kfd->list = nullptr;
2925 filelist_free(kfd->list_dir);
2926 kfd->list_dir = nullptr;
2929 static void keywords_find_close_cb(GenericDialog *, gpointer data)
2931 auto kfd = static_cast<KeywordFindData *>(data);
2933 if (!gtk_widget_get_sensitive(kfd->button_close)) return;
2935 keywords_find_reset(kfd);
2936 generic_dialog_close(kfd->gd);
2940 static void keywords_find_finish(KeywordFindData *kfd)
2942 keywords_find_reset(kfd);
2944 gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
2945 gtk_spinner_stop(GTK_SPINNER(kfd->spinner));
2947 gtk_widget_set_sensitive(kfd->group, TRUE);
2948 gtk_widget_set_sensitive(kfd->button_start, TRUE);
2949 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
2950 gtk_widget_set_sensitive(kfd->button_close, TRUE);
2953 static void keywords_find_stop_cb(GenericDialog *, gpointer data)
2955 auto kfd = static_cast<KeywordFindData *>(data);
2957 g_idle_remove_by_data(kfd);
2959 keywords_find_finish(kfd);
2962 static gboolean keywords_find_file(gpointer data)
2964 auto kfd = static_cast<KeywordFindData *>(data);
2966 GtkTextBuffer *buffer;
2974 fd = static_cast<FileData *>(kfd->list->data);
2975 kfd->list = g_list_remove(kfd->list, fd);
2977 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
2978 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
2982 gtk_text_buffer_get_end_iter(buffer, &iter);
2983 tmp = g_strconcat(static_cast<const gchar *>(keywords->data), "\n", NULL);
2984 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
2986 keywords = keywords->next;
2989 gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
2990 file_data_unref(fd);
2991 g_list_free_full(keywords, g_free);
2993 return (G_SOURCE_CONTINUE);
2995 else if (kfd->list_dir)
2999 fd = static_cast<FileData *>(kfd->list_dir->data);
3000 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
3002 keywords_find_folder(kfd, fd);
3004 file_data_unref(fd);
3006 return G_SOURCE_CONTINUE;
3009 keywords_find_finish(kfd);
3011 return G_SOURCE_REMOVE;
3014 static void keywords_find_start_cb(GenericDialog *, gpointer data)
3016 auto kfd = static_cast<KeywordFindData *>(data);
3019 if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
3021 path = remove_trailing_slash((gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
3022 parse_out_relatives(path);
3026 warning_dialog(_("Invalid folder"),
3027 _("The specified folder can not be found."),
3028 GQ_ICON_DIALOG_WARNING, kfd->gd->dialog);
3034 gtk_widget_set_sensitive(kfd->group, FALSE);
3035 gtk_widget_set_sensitive(kfd->button_start, FALSE);
3036 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
3037 gtk_widget_set_sensitive(kfd->button_close, FALSE);
3038 gtk_spinner_start(GTK_SPINNER(kfd->spinner));
3040 dir_fd = file_data_new_dir(path);
3041 keywords_find_folder(kfd, dir_fd);
3042 file_data_unref(dir_fd);
3043 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
3049 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
3051 KeywordFindData *kfd;
3055 kfd = g_new0(KeywordFindData, 1);
3057 kfd->gd = generic_dialog_new(_("Search for keywords"),
3058 "search_for_keywords",
3061 gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
3062 kfd->gd->cancel_cb = keywords_find_close_cb;
3063 kfd->button_close = generic_dialog_add_button(kfd->gd, GQ_ICON_CLOSE, _("Close"),
3064 keywords_find_close_cb, FALSE);
3065 kfd->button_start = generic_dialog_add_button(kfd->gd, GQ_ICON_OK, _("S_tart"),
3066 keywords_find_start_cb, FALSE);
3067 kfd->button_stop = generic_dialog_add_button(kfd->gd, GQ_ICON_STOP, _("Stop"),
3068 keywords_find_stop_cb, FALSE);
3069 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3071 generic_dialog_add_message(kfd->gd, nullptr, _("Search for keywords"), nullptr, FALSE);
3073 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3074 pref_spacer(hbox, PREF_PAD_INDENT);
3075 kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3077 hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3078 pref_label_new(hbox, _("Folder:"));
3080 label = tab_completion_new(&kfd->entry, path, nullptr, nullptr, nullptr, nullptr);
3081 tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
3082 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3083 gtk_widget_show(label);
3085 pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
3087 pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
3088 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3090 kfd->progress = gtk_entry_new();
3091 gtk_widget_set_can_focus(kfd->progress, FALSE);
3092 gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
3093 gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
3094 gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
3095 gtk_widget_show(kfd->progress);
3097 kfd->spinner = gtk_spinner_new();
3098 gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
3099 gtk_widget_show(kfd->spinner);
3101 kfd->list = nullptr;
3103 gtk_widget_show(kfd->gd->dialog);
3106 static void keywords_find_cb(GtkWidget *widget, gpointer)
3108 const gchar *path = layout_get_path(nullptr);
3110 if (!path || !*path) path = homedir();
3111 keywords_find_dialog(widget, path);
3114 static void config_tab_keywords_save()
3116 GtkTextIter start, end;
3117 GtkTextBuffer *buffer;
3118 GList *kw_list = nullptr;
3124 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3125 gtk_text_buffer_get_bounds(buffer, &start, &end);
3127 buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3129 kw_split = strtok(buffer_text, "\n");
3130 while (kw_split != nullptr)
3136 if (g_strcmp0(static_cast<const gchar *>(work->data), kw_split) == 0)
3145 kw_list = g_list_append(kw_list, g_strdup(kw_split));
3147 kw_split = strtok(nullptr, "\n");
3150 keyword_list_set(kw_list);
3152 g_list_free_full(kw_list, g_free);
3153 g_free(buffer_text);
3156 static void config_tab_keywords(GtkWidget *notebook)
3162 GtkWidget *scrolled;
3164 GtkTextBuffer *buffer;
3167 GspellTextView *gspell_view;
3170 vbox = scrolled_notebook_page(notebook, _("Keywords"));
3172 group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
3174 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3176 button = pref_button_new(hbox, GQ_ICON_RUN, _("Search"),
3177 G_CALLBACK(keywords_find_cb), keyword_text);
3178 gtk_widget_set_tooltip_text(button, "Search for existing keywords");
3181 keyword_text = gtk_text_view_new();
3182 gtk_widget_set_size_request(keyword_text, 20, 20);
3183 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
3184 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3185 gtk_widget_show(scrolled);
3188 if (options->metadata.check_spelling)
3190 gspell_view = gspell_text_view_get_from_gtk_text_view(GTK_TEXT_VIEW(keyword_text));
3191 gspell_text_view_basic_setup(gspell_view);
3195 gtk_container_add(GTK_CONTAINER(scrolled), keyword_text);
3196 gtk_widget_show(keyword_text);
3198 gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
3200 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3201 gtk_text_buffer_create_tag(buffer, "monospace",
3202 "family", "monospace", NULL);
3204 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3205 gtk_text_buffer_get_start_iter(buffer, &iter);
3206 gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3209 path = g_build_filename(get_rc_dir(), "keywords", NULL);
3211 GList *kwl = keyword_list_get();
3212 kwl = g_list_first(kwl);
3215 gtk_text_buffer_get_end_iter (buffer, &iter);
3216 tmp = g_strconcat(static_cast<const gchar *>(kwl->data), "\n", NULL);
3217 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3222 gtk_text_buffer_set_modified(buffer, FALSE);
3229 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3231 auto option = static_cast<gint *>(data);
3233 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3237 *option = INTENT_PERCEPTUAL;
3240 *option = INTENT_RELATIVE_COLORIMETRIC;
3243 *option = INTENT_SATURATION;
3246 *option = INTENT_ABSOLUTE_COLORIMETRIC;
3251 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3252 gint option, gint *option_c)
3259 pref_table_label(table, column, row, text, GTK_ALIGN_START);
3261 combo = gtk_combo_box_text_new();
3263 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3264 if (option == INTENT_PERCEPTUAL) current = 0;
3265 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3266 if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3267 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3268 if (option == INTENT_SATURATION) current = 2;
3269 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3270 if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3272 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3274 gtk_widget_set_tooltip_text(combo,"Refer to the lcms documentation for the defaults used when the selected Intent is not available");
3276 g_signal_connect(G_OBJECT(combo), "changed",
3277 G_CALLBACK(intent_menu_cb), option_c);
3279 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
3280 gtk_widget_show(combo);
3284 static void config_tab_color(GtkWidget *notebook)
3293 vbox = scrolled_notebook_page(notebook, _("Color management"));
3295 group = pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3297 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3300 table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3301 gtk_table_set_col_spacings(GTK_TABLE(table), PREF_PAD_GAP);
3303 label = pref_table_label(table, 0, 0, _("Type"), GTK_ALIGN_START);
3304 pref_label_bold(label, TRUE, FALSE);
3306 label = pref_table_label(table, 1, 0, _("Menu name"), GTK_ALIGN_START);
3307 pref_label_bold(label, TRUE, FALSE);
3309 label = pref_table_label(table, 2, 0, _("File"), GTK_ALIGN_START);
3310 pref_label_bold(label, TRUE, FALSE);
3312 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3317 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3318 pref_table_label(table, 0, i + 1, buf, GTK_ALIGN_END);
3321 entry = gtk_entry_new();
3322 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3323 if (options->color_profile.input_name[i])
3325 gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3327 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, i + 1, i + 2,
3328 static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3329 gtk_widget_show(entry);
3330 color_profile_input_name_entry[i] = entry;
3332 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], nullptr, ".icc", "ICC Files", nullptr);
3333 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3334 gtk_widget_set_size_request(entry, 160, -1);
3335 gtk_table_attach(GTK_TABLE(table), tabcomp, 2, 3, i + 1, i + 2,
3336 static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3337 gtk_widget_show(tabcomp);
3338 color_profile_input_file_entry[i] = entry;
3341 group = pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3343 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3345 pref_checkbox_new_int(group, _("Use system screen profile if available"),
3346 options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3348 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3350 pref_table_label(table, 0, 0, _("Screen:"), GTK_ALIGN_END);
3351 tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3352 options->color_profile.screen_file, nullptr, ".icc", "ICC Files", nullptr);
3353 tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3354 gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3356 add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3358 gtk_table_attach(GTK_TABLE(table), tabcomp, 1, 2,
3360 static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3362 gtk_widget_show(tabcomp);
3365 /* advanced entry tab */
3366 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer)
3368 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3370 c_options->file_ops.use_system_trash = FALSE;
3371 c_options->file_ops.no_trash = FALSE;
3375 static void use_system_trash_cb(GtkWidget *widget, gpointer)
3377 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3379 c_options->file_ops.use_system_trash = TRUE;
3380 c_options->file_ops.no_trash = FALSE;
3384 static void use_no_cache_cb(GtkWidget *widget, gpointer)
3386 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3388 c_options->file_ops.no_trash = TRUE;
3392 static void config_tab_behavior(GtkWidget *notebook)
3399 GtkWidget *ct_button;
3403 GtkWidget *with_rename;
3404 GtkWidget *collections_on_top;
3405 GtkWidget *hide_window_in_fullscreen;
3406 GtkWidget *checkbox;
3409 vbox = scrolled_notebook_page(notebook, _("Behavior"));
3411 group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3413 pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3414 options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3415 pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3416 options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3417 pref_checkbox_new_int(group, _("Enable Delete key"),
3418 options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3420 ct_button = pref_radiobutton_new(group, nullptr, _("Use Geeqie trash location"),
3421 !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),nullptr);
3423 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3424 pref_checkbox_link_sensitivity(ct_button, hbox);
3426 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3427 pref_label_new(hbox, _("Folder:"));
3429 tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, nullptr, nullptr, nullptr, nullptr);
3430 tab_completion_add_select_button(safe_delete_path_entry, nullptr, TRUE);
3431 gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3432 gtk_widget_show(tabcomp);
3434 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3435 pref_checkbox_link_sensitivity(ct_button, hbox);
3437 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3438 spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MiB"),
3439 0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3440 gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3441 button = pref_button_new(nullptr, nullptr, _("View"),
3442 G_CALLBACK(safe_delete_view_cb), nullptr);
3443 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3444 gtk_widget_show(button);
3446 button = pref_button_new(nullptr, GQ_ICON_CLEAR, nullptr,
3447 G_CALLBACK(safe_delete_clear_cb), nullptr);
3448 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3450 c_options->file_ops.no_trash = options->file_ops.no_trash;
3451 c_options->file_ops.use_system_trash = options->file_ops.use_system_trash;
3453 pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3454 options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), nullptr);
3456 pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3457 options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), nullptr);
3459 gtk_widget_show(button);
3461 pref_spacer(group, PREF_PAD_GROUP);
3464 group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3466 pref_checkbox_new_int(group, _("Descend folders in tree view"),
3467 options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3469 pref_checkbox_new_int(group, _("In place renaming"),
3470 options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3472 pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3473 options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3475 tmp = pref_checkbox_new_int(group, _("Circular selection lists"),
3476 options->circular_selection_lists, &c_options->circular_selection_lists);
3477 gtk_widget_set_tooltip_text(tmp, _("Traverse selection lists in a circular manner"));
3479 marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3480 options->marks_save, &c_options->marks_save);
3481 gtk_widget_set_tooltip_text(marks,"Note that marks linked to a keyword will be saved irrespective of this setting");
3483 with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3484 options->with_rename, &c_options->with_rename);
3485 gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3487 collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3488 options->collections_on_top, &c_options->collections_on_top);
3489 gtk_widget_set_tooltip_text(collections_on_top,"Open collections window on top");
3491 hide_window_in_fullscreen = pref_checkbox_new_int(group, _("Hide window in fullscreen"),
3492 options->hide_window_in_fullscreen, &c_options->hide_window_in_fullscreen);
3493 gtk_widget_set_tooltip_text(hide_window_in_fullscreen,"When alt-tabbing, prevent Geeqie window showing twice");
3495 pref_spin_new_int(group, _("Recent folder list maximum size"), nullptr,
3496 1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3498 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);
3499 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."));
3501 pref_spin_new_int(group, _("Drag'n drop icon size"), nullptr,
3502 16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3504 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3505 add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
3507 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3508 add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3510 pref_spacer(group, PREF_PAD_GROUP);
3512 group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3514 pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3515 options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3516 pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), nullptr,
3517 1, 32, 1, options->keyboard_scroll_step, reinterpret_cast<int *>(&c_options->keyboard_scroll_step));
3518 pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3519 options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3520 pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3521 options->image_lm_click_nav, &c_options->image_lm_click_nav);
3522 pref_checkbox_new_int(group, _("Open archive by left click on image"),
3523 options->image_l_click_archive, &c_options->image_l_click_archive);
3524 pref_checkbox_new_int(group, _("Play video by left click on image"),
3525 options->image_l_click_video, &c_options->image_l_click_video);
3526 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3527 add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3529 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3530 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3531 add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3532 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3533 add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3535 pref_spacer(group, PREF_PAD_GROUP);
3537 group = pref_group_new(vbox, FALSE, _("GPU"), GTK_ORIENTATION_VERTICAL);
3539 checkbox = pref_checkbox_new_int(group, _("Override disable GPU"),
3540 options->override_disable_gpu, &c_options->override_disable_gpu);
3541 gtk_widget_set_tooltip_text(checkbox, "Contact the developers for usage");
3544 pref_spacer(group, PREF_PAD_GROUP);
3546 group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3548 pref_spin_new_int(group, _("Debug level:"), nullptr,
3549 DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3551 pref_checkbox_new_int(group, _("Timer data"),
3552 options->log_window.timer_data, &c_options->log_window.timer_data);
3554 pref_spin_new_int(group, _("Log Window max. lines:"), nullptr,
3555 1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3559 /* accelerators tab */
3561 static gboolean accel_search_function_cb(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer)
3563 gboolean ret = TRUE;
3568 gtk_tree_model_get(model, iter, column, &text, -1);
3569 text_nocase = g_utf8_casefold(text, -1);
3570 key_nocase = g_utf8_casefold(key, -1);
3572 if (g_strstr_len(text_nocase, -1, key_nocase))
3579 g_free(text_nocase);
3584 static void accel_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *, GtkTreeViewColumn *column, gpointer)
3589 list = gtk_tree_view_get_columns(tree_view);
3590 col_num = g_list_index(list, column);
3594 gtk_tree_view_set_search_column(tree_view, col_num);
3597 static void config_tab_accelerators(GtkWidget *notebook)
3603 GtkWidget *scrolled;
3604 GtkWidget *accel_view;
3605 GtkCellRenderer *renderer;
3606 GtkTreeSelection *selection;
3607 GtkTreeViewColumn *column;
3609 vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3611 group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3613 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
3614 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3615 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3616 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3617 gtk_widget_show(scrolled);
3619 accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3621 accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3622 g_object_unref(accel_store);
3623 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3624 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3626 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3628 renderer = gtk_cell_renderer_text_new();
3630 column = gtk_tree_view_column_new_with_attributes(_("Action"),
3635 gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3636 gtk_tree_view_column_set_resizable(column, TRUE);
3637 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3640 renderer = gtk_cell_renderer_accel_new();
3641 g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3642 G_CALLBACK(accel_store_cleared_cb), accel_store);
3643 g_signal_connect(G_OBJECT(renderer), "accel-edited",
3644 G_CALLBACK(accel_store_edited_cb), accel_store);
3647 g_object_set (renderer,
3649 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3652 column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3657 gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3658 gtk_tree_view_column_set_resizable(column, TRUE);
3659 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3661 renderer = gtk_cell_renderer_text_new();
3663 column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3668 gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3669 gtk_tree_view_column_set_resizable(column, TRUE);
3670 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3672 renderer = gtk_cell_renderer_text_new();
3674 column = gtk_tree_view_column_new_with_attributes("Accel",
3679 gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3680 gtk_tree_view_column_set_resizable(column, TRUE);
3681 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3683 /* Search on text in column */
3684 gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(accel_view), TRUE);
3685 g_signal_connect(accel_view, "row_activated", G_CALLBACK(accel_row_activated_cb), accel_store);
3686 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), TRUE);
3687 gtk_tree_view_set_search_column(GTK_TREE_VIEW(accel_view), AE_TOOLTIP);
3688 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(accel_view), accel_search_function_cb, nullptr, nullptr);
3690 accel_store_populate();
3691 gtk_container_add(GTK_CONTAINER(scrolled), accel_view);
3692 gtk_widget_show(accel_view);
3694 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3696 button = pref_button_new(nullptr, nullptr, _("Defaults"),
3697 G_CALLBACK(accel_default_cb), accel_view);
3698 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3699 gtk_widget_show(button);
3701 button = pref_button_new(nullptr, nullptr, _("Reset selected"),
3702 G_CALLBACK(accel_reset_cb), accel_view);
3703 gtk_widget_set_tooltip_text(button, _("Will only reset changes made before the settings are saved"));
3704 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3705 gtk_widget_show(button);
3707 button = pref_button_new(nullptr, nullptr, _("Clear selected"),
3708 G_CALLBACK(accel_clear_cb), accel_view);
3709 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3710 gtk_widget_show(button);
3713 /* toolbar main tab */
3714 static void config_tab_toolbar_main(GtkWidget *notebook)
3717 GtkWidget *toolbardata;
3720 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3722 vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3724 toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3725 gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3726 gtk_widget_show(vbox);
3729 /* toolbar status tab */
3730 static void config_tab_toolbar_status(GtkWidget *notebook)
3733 GtkWidget *toolbardata;
3736 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3738 vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3740 toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3741 gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3742 gtk_widget_show(vbox);
3746 static gint extension_sort_cb(gconstpointer a, gconstpointer b)
3748 return g_strcmp0(static_cast<const gchar *>(a), static_cast<const gchar *>(b));
3751 static void config_tab_advanced(GtkWidget *notebook)
3755 GSList *formats_list;
3756 GList *extensions_list = nullptr;
3759 GdkPixbufFormat *fm;
3761 GString *types_string = g_string_new(nullptr);
3762 GtkWidget *types_string_label;
3763 GtkWidget *threads_string_label;
3764 GtkWidget *dupes_threads_spin;
3766 vbox = scrolled_notebook_page(notebook, _("Advanced"));
3767 group = pref_group_new(vbox, FALSE, _("External preview extraction"), GTK_ORIENTATION_VERTICAL);
3769 pref_checkbox_new_int(group, _("Use external preview extraction - Requires restart"), options->external_preview.enable, &c_options->external_preview.enable);
3771 pref_spacer(group, PREF_PAD_GROUP);
3773 formats_list = gdk_pixbuf_get_formats();
3775 while (formats_list)
3777 fm = static_cast<GdkPixbufFormat *>(formats_list->data);
3778 extensions = gdk_pixbuf_format_get_extensions(fm);
3781 while (extensions[i])
3783 extensions_list = g_list_insert_sorted(extensions_list, g_strdup(extensions[i]), extension_sort_cb);
3787 g_strfreev(extensions);
3788 formats_list = formats_list->next;
3791 while (extensions_list)
3793 if (types_string->len == 0)
3795 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3799 types_string = g_string_append(types_string, ", ");
3800 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3803 extensions_list = extensions_list->next;
3806 types_string = g_string_prepend(types_string, _("Usable file types:\n"));
3807 types_string_label = pref_label_new(group, types_string->str);
3808 gtk_label_set_line_wrap(GTK_LABEL(types_string_label), TRUE);
3810 pref_spacer(group, PREF_PAD_GROUP);
3812 group = pref_group_new(vbox, FALSE, _("File identification tool"), GTK_ORIENTATION_VERTICAL);
3813 external_preview_select_entry = gtk_entry_new();
3814 tabcomp = tab_completion_new(&external_preview_select_entry, options->external_preview.select, nullptr, nullptr, nullptr, nullptr);
3815 tab_completion_add_select_button(external_preview_select_entry, _("Select file identification tool"), FALSE);
3816 gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3817 gtk_widget_show(tabcomp);
3819 group = pref_group_new(vbox, FALSE, _("Preview extraction tool"), GTK_ORIENTATION_VERTICAL);
3820 external_preview_extract_entry = gtk_entry_new();
3821 tabcomp = tab_completion_new(&external_preview_extract_entry, options->external_preview.extract, nullptr, nullptr, nullptr, nullptr);
3822 tab_completion_add_select_button(external_preview_extract_entry, _("Select preview extraction tool"), FALSE);
3823 gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3824 gtk_widget_show(tabcomp);
3826 gtk_widget_show(vbox);
3828 g_slist_free(formats_list);
3829 g_list_free_full(extensions_list, g_free);
3830 g_string_free(types_string, TRUE);
3832 pref_spacer(group, PREF_PAD_GROUP);
3834 pref_line(vbox, PREF_PAD_SPACE);
3835 group = pref_group_new(vbox, FALSE, _("Thread pool limits"), GTK_ORIENTATION_VERTICAL);
3837 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.");
3838 gtk_label_set_line_wrap(GTK_LABEL(threads_string_label), TRUE);
3840 pref_spacer(vbox, PREF_PAD_GROUP);
3842 dupes_threads_spin = pref_spin_new_int(vbox, _("Duplicate check:"), _("max. threads"), 0, get_cpu_cores(), 1, options->threads.duplicates, &c_options->threads.duplicates);
3843 gtk_widget_set_tooltip_markup(dupes_threads_spin, _("Set to 0 for unlimited"));
3847 static void config_tab_stereo(GtkWidget *notebook)
3855 GtkWidget *fs_button;
3856 vbox = scrolled_notebook_page(notebook, _("Stereo"));
3858 group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
3860 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3861 add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
3863 table = pref_table_new(group, 2, 2, TRUE, FALSE);
3864 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3865 pref_checkbox_new_int(box, _("Mirror left image"),
3866 options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
3867 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3868 pref_checkbox_new_int(box, _("Flip left image"),
3869 options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
3870 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3871 pref_checkbox_new_int(box, _("Mirror right image"),
3872 options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
3873 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3874 pref_checkbox_new_int(box, _("Flip right image"),
3875 options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
3876 pref_checkbox_new_int(group, _("Swap left and right images"),
3877 options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
3878 pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
3879 options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
3881 group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
3882 fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
3883 options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
3884 box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
3885 pref_checkbox_link_sensitivity(fs_button, box2);
3886 table = pref_table_new(box2, 2, 1, FALSE, FALSE);
3887 add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
3888 table = pref_table_new(box2, 2, 2, TRUE, FALSE);
3889 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3890 pref_checkbox_new_int(box, _("Mirror left image"),
3891 options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
3892 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3893 pref_checkbox_new_int(box, _("Flip left image"),
3894 options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
3895 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3896 pref_checkbox_new_int(box, _("Mirror right image"),
3897 options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
3898 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3899 pref_checkbox_new_int(box, _("Flip right image"),
3900 options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
3901 pref_checkbox_new_int(box2, _("Swap left and right images"),
3902 options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
3903 pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
3904 options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
3906 group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
3907 table = pref_table_new(group2, 5, 3, FALSE, FALSE);
3908 pref_table_spin_new_int(table, 0, 0, _("Width"), nullptr,
3909 1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
3910 pref_table_spin_new_int(table, 3, 0, _("Height"), nullptr,
3911 1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
3912 pref_table_spin_new_int(table, 0, 1, _("Left X"), nullptr,
3913 0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
3914 pref_table_spin_new_int(table, 3, 1, _("Left Y"), nullptr,
3915 0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
3916 pref_table_spin_new_int(table, 0, 2, _("Right X"), nullptr,
3917 0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
3918 pref_table_spin_new_int(table, 3, 2, _("Right Y"), nullptr,
3919 0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
3923 /* Main preferences window */
3924 static void config_window_create(LayoutWindow *lw)
3926 GtkWidget *win_vbox;
3928 GtkWidget *notebook;
3930 GtkWidget *ct_button;
3932 if (!c_options) c_options = init_options(nullptr);
3934 configwindow = window_new(GTK_WINDOW_TOPLEVEL, "preferences", PIXBUF_INLINE_ICON_CONFIG, nullptr, _("Preferences"));
3935 DEBUG_NAME(configwindow);
3936 gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
3937 g_signal_connect(G_OBJECT(configwindow), "delete_event",
3938 G_CALLBACK(config_window_delete), NULL);
3939 if (options->save_dialog_window_positions)
3941 gtk_window_resize(GTK_WINDOW(configwindow), lw->options.preferences_window.w, lw->options.preferences_window.h);
3942 gtk_window_move(GTK_WINDOW(configwindow), lw->options.preferences_window.x, lw->options.preferences_window.y);
3946 gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
3948 gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
3949 gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
3951 win_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
3952 gtk_container_add(GTK_CONTAINER(configwindow), win_vbox);
3953 gtk_widget_show(win_vbox);
3955 notebook = gtk_notebook_new();
3956 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
3957 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
3958 gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
3960 config_tab_general(notebook);
3961 config_tab_image(notebook);
3962 config_tab_osd(notebook);
3963 config_tab_windows(notebook);
3964 config_tab_accelerators(notebook);
3965 config_tab_files(notebook);
3966 config_tab_metadata(notebook);
3967 config_tab_keywords(notebook);
3968 config_tab_color(notebook);
3969 config_tab_stereo(notebook);
3970 config_tab_behavior(notebook);
3971 config_tab_toolbar_main(notebook);
3972 config_tab_toolbar_status(notebook);
3973 config_tab_advanced(notebook);
3975 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), lw->options.preferences_window.page_number);
3977 hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
3978 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
3979 gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
3980 gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
3981 gtk_widget_show(hbox);
3983 button = pref_button_new(nullptr, GQ_ICON_HELP, _("Help"),
3984 G_CALLBACK(config_window_help_cb), notebook);
3985 gtk_container_add(GTK_CONTAINER(hbox), button);
3986 gtk_widget_set_can_default(button, TRUE);
3987 gtk_widget_show(button);
3989 button = pref_button_new(nullptr, GQ_ICON_OK, "OK",
3990 G_CALLBACK(config_window_ok_cb), notebook);
3991 gtk_container_add(GTK_CONTAINER(hbox), button);
3992 gtk_widget_set_can_default(button, TRUE);
3993 gtk_widget_grab_default(button);
3994 gtk_widget_show(button);
3998 button = pref_button_new(nullptr, GQ_ICON_CANCEL, _("Cancel"),
3999 G_CALLBACK(config_window_close_cb), nullptr);
4000 gtk_container_add(GTK_CONTAINER(hbox), button);
4001 gtk_widget_set_can_default(button, TRUE);
4002 gtk_widget_show(button);
4004 if (!generic_dialog_get_alternative_button_order(configwindow))
4006 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
4009 gtk_widget_show(notebook);
4011 gtk_widget_show(configwindow);
4015 *-----------------------------------------------------------------------------
4016 * config window show (public)
4017 *-----------------------------------------------------------------------------
4020 void show_config_window(LayoutWindow *lw)
4024 gtk_window_present(GTK_WINDOW(configwindow));
4028 config_window_create(lw);
4037 void show_about_window(LayoutWindow *lw)
4039 GDataInputStream *data_stream;
4040 GInputStream *in_stream_authors;
4041 GInputStream *in_stream_translators;
4043 GdkPixbuf *pixbuf_icon;
4044 GdkPixbuf *pixbuf_logo;
4048 gchar *authors[1000];
4049 gchar *authors_path;
4051 gchar *timezone_path;
4053 gchar *translators_path;
4061 copyright = g_string_new(nullptr);
4062 copyright = g_string_append(copyright, "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");
4064 timezone_path = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
4065 if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
4067 cd = ZDOpenDatabase(timezone_path);
4070 copyright = g_string_append(copyright, ZDGetNotice(cd));
4074 log_printf("Error: Init of timezone database %s failed\n", timezone_path);
4076 ZDCloseDatabase(cd);
4078 g_free(timezone_path);
4080 authors_path = g_build_filename(GQ_RESOURCE_PATH_CREDITS, "authors", NULL);
4082 in_stream_authors = g_resources_open_stream(authors_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4084 data_stream = g_data_input_stream_new(in_stream_authors);
4086 authors[0] = nullptr;
4087 while ((author_line = g_data_input_stream_read_line(G_DATA_INPUT_STREAM(data_stream), &length, nullptr, nullptr)))
4089 authors[i_authors] = g_strdup(author_line);
4091 g_free(author_line);
4093 authors[i_authors] = nullptr;
4095 g_input_stream_close(in_stream_authors, nullptr, nullptr);
4097 translators_path = g_build_filename(GQ_RESOURCE_PATH_CREDITS, "translators", NULL);
4099 g_resources_get_info(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &size, &flags, nullptr);
4101 in_stream_translators = g_resources_open_stream(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4102 translators = static_cast<gchar *>(g_malloc0(size));
4103 g_input_stream_read_all(in_stream_translators, translators, size, &bytes_read, nullptr, nullptr);
4104 g_input_stream_close(in_stream_translators, nullptr, nullptr);
4106 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);
4108 artists[0] = g_strdup("Néstor Díaz Valencia <nestor@estudionexos.com>");
4109 artists[1] = nullptr;
4111 pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
4112 pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
4113 gtk_show_about_dialog(GTK_WINDOW(lw->window),
4114 "title", _("About Geeqie"),
4116 "program-name", GQ_APPNAME,
4118 "logo", pixbuf_logo,
4119 "icon", pixbuf_icon,
4120 "website", GQ_WEBSITE,
4121 "website-label", "Website",
4122 "comments", comment,
4125 "translator-credits", translators,
4126 "wrap-license", TRUE,
4127 "license", copyright->str,
4130 g_string_free(copyright, TRUE);
4132 while(n < i_authors)
4139 g_free(authors_path);
4141 g_free(translators);
4142 g_free(translators_path);
4143 g_object_unref(data_stream);
4144 g_object_unref(in_stream_authors);
4145 g_object_unref(in_stream_translators);
4148 static void image_overlay_set_text_colours()
4150 c_options->image_overlay.text_red = options->image_overlay.text_red;
4151 c_options->image_overlay.text_green = options->image_overlay.text_green;
4152 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
4153 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
4154 c_options->image_overlay.background_red = options->image_overlay.background_red;
4155 c_options->image_overlay.background_green = options->image_overlay.background_green;
4156 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
4157 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
4161 *-----------------------------------------------------------------------------
4162 * timezone database routines
4163 *-----------------------------------------------------------------------------
4166 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
4168 GError *error = nullptr;
4169 auto tz = static_cast<TZData *>(data);
4170 gchar *tmp_filename;
4171 gchar *timezone_bin;
4172 gchar *tmp_dir = nullptr;
4175 if (!g_cancellable_is_cancelled(tz->cancellable))
4177 generic_dialog_close(tz->gd);
4181 if (g_file_copy_finish(G_FILE(source_object), res, &error))
4183 tmp_filename = g_file_get_path(tz->tmp_g_file);
4184 fd = file_data_new_simple(tmp_filename);
4185 tmp_dir = open_archive(fd);
4189 timezone_bin = g_build_filename(tmp_dir, TIMEZONE_DATABASE_VERSION, TIMEZONE_DATABASE_FILE, NULL);
4190 if (isfile(timezone_bin))
4192 move_file(timezone_bin, tz->timezone_database_user);
4196 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4199 g_free(timezone_bin);
4200 g_free(tmp_dir); // The folder in /tmp is deleted in exit_program_final()
4204 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GQ_ICON_DIALOG_WARNING, nullptr);
4206 g_free(tmp_filename);
4207 file_data_unref(fd);
4211 file_util_warning_dialog(_("Error: Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4214 g_file_delete(tz->tmp_g_file, nullptr, &error);
4215 g_object_unref(tz->tmp_g_file);
4216 tz->tmp_g_file = nullptr;
4217 g_object_unref(tz->cancellable);
4218 g_object_unref(tz->timezone_database_gq);
4221 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
4223 auto tz = static_cast<TZData *>(data);
4225 if (!g_cancellable_is_cancelled(tz->cancellable))
4227 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), static_cast<gdouble>(current_num_bytes) / total_num_bytes);
4231 static void timezone_cancel_button_cb(GenericDialog *, gpointer data)
4233 auto tz = static_cast<TZData *>(data);
4235 g_cancellable_cancel(tz->cancellable);
4238 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
4240 auto tz = static_cast<TZData *>(data);
4241 GError *error = nullptr;
4242 GFileIOStream *io_stream;
4249 tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
4253 file_util_warning_dialog(_("Timezone database download failed"), error->message, GQ_ICON_DIALOG_ERROR, nullptr);
4254 log_printf("Error: Download timezone database failed:\n%s", error->message);
4255 g_error_free(error);
4256 g_object_unref(tz->tmp_g_file);
4260 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE_WEB);
4262 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", nullptr, TRUE, timezone_cancel_button_cb, tz);
4264 generic_dialog_add_message(tz->gd, GQ_ICON_DIALOG_INFO, _("Downloading timezone database"), nullptr, FALSE);
4266 tz->progress = gtk_progress_bar_new();
4267 gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
4268 gtk_widget_show(tz->progress);
4270 gtk_widget_show(tz->gd->dialog);
4271 tz->cancellable = g_cancellable_new();
4272 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);
4274 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
4277 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */