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"
29 #include "filefilter.h"
30 #include "fullscreen.h"
32 #include "image-overlay.h"
33 #include "color-man.h"
35 #include "layout-util.h"
39 #include "pixbuf-util.h"
41 #include "slideshow.h"
45 #include "ui-fileops.h"
47 #include "ui-spinner.h"
48 #include "ui-tabcomp.h"
50 #include "zonedetect.h"
61 #include <gspell/gspell.h>
64 #define EDITOR_NAME_MAX_LENGTH 32
65 #define EDITOR_COMMAND_MAX_LENGTH 1024
67 static void image_overlay_set_text_colours();
69 GtkWidget *keyword_text;
70 static void config_tab_keywords_save();
78 static ThumbSize thumb_size_list[] =
113 FILETYPES_COLUMN_ENABLED = 0,
114 FILETYPES_COLUMN_FILTER,
115 FILETYPES_COLUMN_DESCRIPTION,
116 FILETYPES_COLUMN_CLASS,
117 FILETYPES_COLUMN_WRITABLE,
118 FILETYPES_COLUMN_SIDECAR,
119 FILETYPES_COLUMN_COUNT
122 const gchar *format_class_list[] = {
133 /* config memory values */
134 static ConfOptions *c_options = nullptr;
141 static GtkWidget *configwindow = nullptr;
142 static GtkListStore *filter_store = nullptr;
143 static GtkTreeStore *accel_store = nullptr;
145 static GtkWidget *safe_delete_path_entry;
147 static GtkWidget *color_profile_input_file_entry[COLOR_PROFILE_INPUTS];
148 static GtkWidget *color_profile_input_name_entry[COLOR_PROFILE_INPUTS];
149 static GtkWidget *color_profile_screen_file_entry;
150 static GtkWidget *external_preview_select_entry;
151 static GtkWidget *external_preview_extract_entry;
153 static GtkWidget *sidecar_ext_entry;
154 static GtkWidget *help_search_engine_entry;
157 #define CONFIG_WINDOW_DEF_WIDTH 700
158 #define CONFIG_WINDOW_DEF_HEIGHT 600
161 *-----------------------------------------------------------------------------
162 * option widget callbacks (private)
163 *-----------------------------------------------------------------------------
166 static void zoom_increment_cb(GtkWidget *spin, gpointer UNUSED(data))
168 c_options->image.zoom_increment = static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) * 100.0 + 0.01);
171 static void slideshow_delay_hours_cb(GtkWidget *spin, gpointer UNUSED(data))
173 gint mins_secs_tenths, delay;
175 mins_secs_tenths = c_options->slideshow.delay %
176 (3600 * SLIDESHOW_SUBSECOND_PRECISION);
178 delay = (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
179 (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
182 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
183 SLIDESHOW_SUBSECOND_PRECISION;
186 static void slideshow_delay_minutes_cb(GtkWidget *spin, gpointer UNUSED(data))
188 gint hours, secs_tenths, delay;
190 hours = c_options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
191 secs_tenths = c_options->slideshow.delay % (60 * SLIDESHOW_SUBSECOND_PRECISION);
193 delay = hours * (3600 * SLIDESHOW_SUBSECOND_PRECISION) +
194 (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
195 (60 * SLIDESHOW_SUBSECOND_PRECISION) + secs_tenths);
197 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
198 SLIDESHOW_SUBSECOND_PRECISION;
201 static void slideshow_delay_seconds_cb(GtkWidget *spin, gpointer UNUSED(data))
203 gint hours_mins, delay;
205 hours_mins = c_options->slideshow.delay / (60 * SLIDESHOW_SUBSECOND_PRECISION);
207 delay = (hours_mins * (60 * SLIDESHOW_SUBSECOND_PRECISION)) +
208 static_cast<gint>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)) *
209 static_cast<gdouble>(SLIDESHOW_SUBSECOND_PRECISION) + 0.01);
211 c_options->slideshow.delay = delay > 0 ? delay : SLIDESHOW_MIN_SECONDS *
212 SLIDESHOW_SUBSECOND_PRECISION;
216 *-----------------------------------------------------------------------------
217 * sync program to config window routine (private)
218 *-----------------------------------------------------------------------------
221 void config_entry_to_option(GtkWidget *entry, gchar **option, gchar *(*func)(const gchar *))
227 buf = gtk_entry_get_text(GTK_ENTRY(entry));
228 if (buf && strlen(buf) > 0)
233 *option = g_strdup(buf);
238 static gboolean accel_apply_cb(GtkTreeModel *model, GtkTreePath *UNUSED(path), GtkTreeIter *iter, gpointer UNUSED(data))
240 gchar *accel_path, *accel;
242 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, AE_KEY, &accel, -1);
244 if (accel_path && accel_path[0])
247 gtk_accelerator_parse(accel, &key.accel_key, &key.accel_mods);
248 gtk_accel_map_change_entry(accel_path, key.accel_key, key.accel_mods, TRUE);
258 static void config_window_apply()
260 gboolean refresh = FALSE;
265 config_entry_to_option(safe_delete_path_entry, &options->file_ops.safe_delete_path, remove_trailing_slash);
267 if (options->file_filter.show_hidden_files != c_options->file_filter.show_hidden_files) refresh = TRUE;
268 if (options->file_filter.show_parent_directory != c_options->file_filter.show_parent_directory) refresh = TRUE;
269 if (options->file_filter.show_dot_directory != c_options->file_filter.show_dot_directory) refresh = TRUE;
270 if (options->file_sort.case_sensitive != c_options->file_sort.case_sensitive) refresh = TRUE;
271 if (options->file_sort.natural != c_options->file_sort.natural) refresh = TRUE;
272 if (options->file_filter.disable_file_extension_checks != c_options->file_filter.disable_file_extension_checks) refresh = TRUE;
273 if (options->file_filter.disable != c_options->file_filter.disable) refresh = TRUE;
275 options->file_ops.confirm_delete = c_options->file_ops.confirm_delete;
276 options->file_ops.enable_delete_key = c_options->file_ops.enable_delete_key;
277 options->file_ops.confirm_move_to_trash = c_options->file_ops.confirm_move_to_trash;
278 options->file_ops.use_system_trash = c_options->file_ops.use_system_trash;
279 options->file_ops.no_trash = c_options->file_ops.no_trash;
280 options->file_ops.safe_delete_folder_maxsize = c_options->file_ops.safe_delete_folder_maxsize;
281 options->tools_restore_state = c_options->tools_restore_state;
282 options->save_window_positions = c_options->save_window_positions;
283 options->use_saved_window_positions_for_new_windows = c_options->use_saved_window_positions_for_new_windows;
284 options->save_window_workspace = c_options->save_window_workspace;
285 options->save_dialog_window_positions = c_options->save_dialog_window_positions;
286 options->show_window_ids = c_options->show_window_ids;
287 options->image.scroll_reset_method = c_options->image.scroll_reset_method;
288 options->image.zoom_2pass = c_options->image.zoom_2pass;
289 options->image.fit_window_to_image = c_options->image.fit_window_to_image;
290 options->image.limit_window_size = c_options->image.limit_window_size;
291 options->image.zoom_to_fit_allow_expand = c_options->image.zoom_to_fit_allow_expand;
292 options->image.max_window_size = c_options->image.max_window_size;
293 options->image.limit_autofit_size = c_options->image.limit_autofit_size;
294 options->image.max_autofit_size = c_options->image.max_autofit_size;
295 options->image.max_enlargement_size = c_options->image.max_enlargement_size;
296 options->image.tile_size = c_options->image.tile_size;
297 options->progressive_key_scrolling = c_options->progressive_key_scrolling;
298 options->keyboard_scroll_step = c_options->keyboard_scroll_step;
300 if (options->thumbnails.max_width != c_options->thumbnails.max_width
301 || options->thumbnails.max_height != c_options->thumbnails.max_height
302 || options->thumbnails.quality != c_options->thumbnails.quality)
304 thumb_format_changed = TRUE;
306 options->thumbnails.max_width = c_options->thumbnails.max_width;
307 options->thumbnails.max_height = c_options->thumbnails.max_height;
308 options->thumbnails.quality = c_options->thumbnails.quality;
310 options->thumbnails.enable_caching = c_options->thumbnails.enable_caching;
311 options->thumbnails.cache_into_dirs = c_options->thumbnails.cache_into_dirs;
312 options->thumbnails.use_exif = c_options->thumbnails.use_exif;
313 options->thumbnails.use_color_management = c_options->thumbnails.use_color_management;
314 options->thumbnails.collection_preview = c_options->thumbnails.collection_preview;
315 options->thumbnails.use_ft_metadata = c_options->thumbnails.use_ft_metadata;
316 options->thumbnails.spec_standard = c_options->thumbnails.spec_standard;
317 options->metadata.enable_metadata_dirs = c_options->metadata.enable_metadata_dirs;
318 options->file_filter.show_hidden_files = c_options->file_filter.show_hidden_files;
319 options->file_filter.show_parent_directory = c_options->file_filter.show_parent_directory;
320 options->file_filter.show_dot_directory = c_options->file_filter.show_dot_directory;
321 options->file_filter.disable_file_extension_checks = c_options->file_filter.disable_file_extension_checks;
323 options->file_sort.case_sensitive = c_options->file_sort.case_sensitive;
324 options->file_sort.natural = c_options->file_sort.natural;
325 options->file_filter.disable = c_options->file_filter.disable;
327 config_entry_to_option(sidecar_ext_entry, &options->sidecar.ext, nullptr);
328 sidecar_ext_parse(options->sidecar.ext);
330 options->slideshow.random = c_options->slideshow.random;
331 options->slideshow.repeat = c_options->slideshow.repeat;
332 options->slideshow.delay = c_options->slideshow.delay;
334 options->mousewheel_scrolls = c_options->mousewheel_scrolls;
335 options->image_lm_click_nav = c_options->image_lm_click_nav;
336 options->image_l_click_archive = c_options->image_l_click_archive;
337 options->image_l_click_video = c_options->image_l_click_video;
338 options->image_l_click_video_editor = c_options->image_l_click_video_editor;
340 options->file_ops.enable_in_place_rename = c_options->file_ops.enable_in_place_rename;
342 options->image.tile_cache_max = c_options->image.tile_cache_max;
343 options->image.image_cache_max = c_options->image.image_cache_max;
345 options->image.zoom_quality = c_options->image.zoom_quality;
347 options->image.zoom_increment = c_options->image.zoom_increment;
349 options->image.zoom_style = c_options->image.zoom_style;
351 options->image.enable_read_ahead = c_options->image.enable_read_ahead;
353 options->appimage_notifications = c_options->appimage_notifications;
356 if (options->image.use_custom_border_color != c_options->image.use_custom_border_color
357 || options->image.use_custom_border_color_in_fullscreen != c_options->image.use_custom_border_color_in_fullscreen
358 || !gdk_color_equal(&options->image.border_color, &c_options->image.border_color))
360 options->image.use_custom_border_color_in_fullscreen = c_options->image.use_custom_border_color_in_fullscreen;
361 options->image.use_custom_border_color = c_options->image.use_custom_border_color;
362 options->image.border_color = c_options->image.border_color;
363 layout_colors_update();
364 view_window_colors_update();
367 options->image.alpha_color_1 = c_options->image.alpha_color_1;
368 options->image.alpha_color_2 = c_options->image.alpha_color_2;
370 options->fullscreen.screen = c_options->fullscreen.screen;
371 options->fullscreen.clean_flip = c_options->fullscreen.clean_flip;
372 options->fullscreen.disable_saver = c_options->fullscreen.disable_saver;
373 options->fullscreen.above = c_options->fullscreen.above;
374 if (c_options->image_overlay.template_string)
375 set_image_overlay_template_string(&options->image_overlay.template_string,
376 c_options->image_overlay.template_string);
377 if (c_options->image_overlay.font)
378 set_image_overlay_font_string(&options->image_overlay.font,
379 c_options->image_overlay.font);
380 options->image_overlay.text_red = c_options->image_overlay.text_red;
381 options->image_overlay.text_green = c_options->image_overlay.text_green;
382 options->image_overlay.text_blue = c_options->image_overlay.text_blue;
383 options->image_overlay.text_alpha = c_options->image_overlay.text_alpha;
384 options->image_overlay.background_red = c_options->image_overlay.background_red;
385 options->image_overlay.background_green = c_options->image_overlay.background_green;
386 options->image_overlay.background_blue = c_options->image_overlay.background_blue;
387 options->image_overlay.background_alpha = c_options->image_overlay.background_alpha;
388 options->update_on_time_change = c_options->update_on_time_change;
390 options->duplicates_similarity_threshold = c_options->duplicates_similarity_threshold;
391 options->rot_invariant_sim = c_options->rot_invariant_sim;
393 options->tree_descend_subdirs = c_options->tree_descend_subdirs;
395 options->view_dir_list_single_click_enter = c_options->view_dir_list_single_click_enter;
396 options->circular_selection_lists = c_options->circular_selection_lists;
398 options->open_recent_list_maxsize = c_options->open_recent_list_maxsize;
399 options->recent_folder_image_list_maxsize = c_options->recent_folder_image_list_maxsize;
400 options->dnd_icon_size = c_options->dnd_icon_size;
401 options->clipboard_selection = c_options->clipboard_selection;
402 options->dnd_default_action = c_options->dnd_default_action;
404 options->metadata.save_in_image_file = c_options->metadata.save_in_image_file;
405 options->metadata.save_legacy_IPTC = c_options->metadata.save_legacy_IPTC;
406 options->metadata.warn_on_write_problems = c_options->metadata.warn_on_write_problems;
407 options->metadata.save_legacy_format = c_options->metadata.save_legacy_format;
408 options->metadata.sync_grouped_files = c_options->metadata.sync_grouped_files;
409 options->metadata.confirm_write = c_options->metadata.confirm_write;
410 options->metadata.sidecar_extended_name = c_options->metadata.sidecar_extended_name;
411 options->metadata.confirm_timeout = c_options->metadata.confirm_timeout;
412 options->metadata.confirm_after_timeout = c_options->metadata.confirm_after_timeout;
413 options->metadata.confirm_on_image_change = c_options->metadata.confirm_on_image_change;
414 options->metadata.confirm_on_dir_change = c_options->metadata.confirm_on_dir_change;
415 options->metadata.keywords_case_sensitive = c_options->metadata.keywords_case_sensitive;
416 options->metadata.write_orientation = c_options->metadata.write_orientation;
417 options->metadata.check_spelling = c_options->metadata.check_spelling;
418 options->stereo.mode = (c_options->stereo.mode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
419 (c_options->stereo.tmp.mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
420 (c_options->stereo.tmp.flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
421 (c_options->stereo.tmp.mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
422 (c_options->stereo.tmp.flip_left ? PR_STEREO_FLIP_LEFT : 0) |
423 (c_options->stereo.tmp.swap ? PR_STEREO_SWAP : 0) |
424 (c_options->stereo.tmp.temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
425 options->stereo.fsmode = (c_options->stereo.fsmode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) |
426 (c_options->stereo.tmp.fs_mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) |
427 (c_options->stereo.tmp.fs_flip_right ? PR_STEREO_FLIP_RIGHT : 0) |
428 (c_options->stereo.tmp.fs_mirror_left ? PR_STEREO_MIRROR_LEFT : 0) |
429 (c_options->stereo.tmp.fs_flip_left ? PR_STEREO_FLIP_LEFT : 0) |
430 (c_options->stereo.tmp.fs_swap ? PR_STEREO_SWAP : 0) |
431 (c_options->stereo.tmp.fs_temp_disable ? PR_STEREO_TEMP_DISABLE : 0);
432 options->stereo.enable_fsmode = c_options->stereo.enable_fsmode;
433 options->stereo.fixed_w = c_options->stereo.fixed_w;
434 options->stereo.fixed_h = c_options->stereo.fixed_h;
435 options->stereo.fixed_x1 = c_options->stereo.fixed_x1;
436 options->stereo.fixed_y1 = c_options->stereo.fixed_y1;
437 options->stereo.fixed_x2 = c_options->stereo.fixed_x2;
438 options->stereo.fixed_y2 = c_options->stereo.fixed_y2;
440 options->info_keywords.height = c_options->info_keywords.height;
441 options->info_title.height = c_options->info_title.height;
442 options->info_comment.height = c_options->info_comment.height;
443 options->info_rating.height = c_options->info_rating.height;
445 options->show_predefined_keyword_tree = c_options->show_predefined_keyword_tree;
446 options->expand_menu_toolbar = c_options->expand_menu_toolbar;
448 options->marks_save = c_options->marks_save;
449 options->with_rename = c_options->with_rename;
450 options->collections_on_top = c_options->collections_on_top;
451 options->hide_window_in_fullscreen = c_options->hide_window_in_fullscreen;
452 config_entry_to_option(help_search_engine_entry, &options->help_search_engine, nullptr);
454 options->external_preview.enable = c_options->external_preview.enable;
455 config_entry_to_option(external_preview_select_entry, &options->external_preview.select, nullptr);
456 config_entry_to_option(external_preview_extract_entry, &options->external_preview.extract, nullptr);
458 options->read_metadata_in_idle = c_options->read_metadata_in_idle;
460 options->star_rating.star = c_options->star_rating.star;
461 options->star_rating.rejected = c_options->star_rating.rejected;
463 options->threads.duplicates = c_options->threads.duplicates > 0 ? c_options->threads.duplicates : -1;
466 set_debug_level(debug_c);
470 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
472 config_entry_to_option(color_profile_input_name_entry[i], &options->color_profile.input_name[i], nullptr);
473 config_entry_to_option(color_profile_input_file_entry[i], &options->color_profile.input_file[i], nullptr);
475 config_entry_to_option(color_profile_screen_file_entry, &options->color_profile.screen_file, nullptr);
476 options->color_profile.use_x11_screen_profile = c_options->color_profile.use_x11_screen_profile;
477 if (options->color_profile.render_intent != c_options->color_profile.render_intent)
479 options->color_profile.render_intent = c_options->color_profile.render_intent;
484 options->mouse_button_8 = c_options->mouse_button_8;
485 options->mouse_button_9 = c_options->mouse_button_9;
487 options->override_disable_gpu = c_options->override_disable_gpu;
489 config_tab_keywords_save();
491 image_options_sync();
496 layout_refresh(nullptr);
499 if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, nullptr);
501 toolbar_apply(TOOLBAR_MAIN);
502 toolbar_apply(TOOLBAR_STATUS);
506 *-----------------------------------------------------------------------------
507 * config window main button callbacks (private)
508 *-----------------------------------------------------------------------------
511 static void config_window_close_cb(GtkWidget *UNUSED(widget), gpointer UNUSED(data))
513 gtk_widget_destroy(configwindow);
514 configwindow = nullptr;
515 filter_store = nullptr;
518 static void config_window_help_cb(GtkWidget *UNUSED(widget), gpointer data)
520 auto notebook = static_cast<GtkWidget *>(data);
523 static const gchar *html_section[] =
525 "GuideOptionsGeneral.html",
526 "GuideOptionsImage.html",
527 "GuideOptionsOSD.html",
528 "GuideOptionsWindow.html",
529 "GuideOptionsKeyboard.html",
530 "GuideOptionsFiltering.html",
531 "GuideOptionsMetadata.html",
532 "GuideOptionsKeywords.html",
533 "GuideOptionsColor.html",
534 "GuideOptionsStereo.html",
535 "GuideOptionsBehavior.html",
536 "GuideOptionsToolbar.html",
537 "GuideOptionsToolbar.html",
538 "GuideOptionsAdvanced.html"
541 i = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
542 help_window_show(html_section[i]);
545 static gboolean config_window_delete(GtkWidget *UNUSED(widget), GdkEventAny *UNUSED(event), gpointer UNUSED(data))
547 config_window_close_cb(nullptr, nullptr);
551 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
554 auto notebook = static_cast<GtkNotebook *>(data);
562 lw = static_cast<LayoutWindow *>(layout_window_list->data);
564 window = gtk_widget_get_window(widget);
565 gdk_window_get_root_origin(window, &x, &y);
566 w = gdk_window_get_width(window);
567 h = gdk_window_get_height(window);
568 page_number = gtk_notebook_get_current_page(notebook);
570 lw->options.preferences_window.x = x;
571 lw->options.preferences_window.y = y;
572 lw->options.preferences_window.w = w;
573 lw->options.preferences_window.h = h;
574 lw->options.preferences_window.page_number = page_number;
576 config_window_apply();
577 layout_util_sync(lw);
578 save_options(options);
579 config_window_close_cb(nullptr, nullptr);
583 *-----------------------------------------------------------------------------
584 * config window setup (private)
585 *-----------------------------------------------------------------------------
588 static void quality_menu_cb(GtkWidget *combo, gpointer data)
590 auto option = static_cast<gint *>(data);
592 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
596 *option = GDK_INTERP_NEAREST;
599 *option = GDK_INTERP_TILES;
602 *option = GDK_INTERP_BILINEAR;
607 static void dnd_default_action_selection_menu_cb(GtkWidget *combo, gpointer data)
609 auto option = static_cast<gint *>(data);
611 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
615 *option = DND_ACTION_ASK;
618 *option = DND_ACTION_COPY;
621 *option = DND_ACTION_MOVE;
625 static void clipboard_selection_menu_cb(GtkWidget *combo, gpointer data)
627 auto option = static_cast<gint *>(data);
629 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
632 *option = CLIPBOARD_PRIMARY;
635 *option = CLIPBOARD_CLIPBOARD;
638 *option = CLIPBOARD_BOTH;
641 *option = CLIPBOARD_BOTH;
645 static void add_quality_menu(GtkWidget *table, gint column, gint row, const gchar *text,
646 guint option, guint *option_c)
653 pref_table_label(table, column, row, text, 0.0);
655 combo = gtk_combo_box_text_new();
657 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Nearest (worst, but fastest)"));
658 if (option == GDK_INTERP_NEAREST) current = 0;
659 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Tiles"));
660 if (option == GDK_INTERP_TILES) current = 1;
661 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Bilinear (best, but slowest)"));
662 if (option == GDK_INTERP_BILINEAR) current = 2;
664 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
666 g_signal_connect(G_OBJECT(combo), "changed",
667 G_CALLBACK(quality_menu_cb), option_c);
669 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
670 gtk_widget_show(combo);
673 static void add_dnd_default_action_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, DnDAction option, DnDAction *option_c)
680 pref_table_label(table, column, row, text, 0.0);
682 combo = gtk_combo_box_text_new();
684 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Ask"));
685 if (option == DND_ACTION_ASK) current = 0;
686 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Copy"));
687 if (option == DND_ACTION_COPY) current = 1;
688 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Move"));
689 if (option == DND_ACTION_MOVE) current = 2;
691 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
693 g_signal_connect(G_OBJECT(combo), "changed",
694 G_CALLBACK(dnd_default_action_selection_menu_cb), option_c);
696 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
697 gtk_widget_show(combo);
700 static void add_clipboard_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
701 gint option, gint *option_c)
708 pref_table_label(table, column, row, text, 0.0);
710 combo = gtk_combo_box_text_new();
712 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Primary"));
713 if (option == CLIPBOARD_PRIMARY) current = 0;
714 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Clipboard"));
715 if (option == CLIPBOARD_CLIPBOARD) current = 1;
716 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Both"));
717 if (option == CLIPBOARD_BOTH) current = 2;
719 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
721 g_signal_connect(G_OBJECT(combo), "changed",
722 G_CALLBACK(clipboard_selection_menu_cb), option_c);
724 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
725 gtk_widget_show(combo);
728 static void zoom_style_selection_menu_cb(GtkWidget *combo, gpointer data)
730 auto option = static_cast<gint *>(data);
732 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
735 *option = ZOOM_GEOMETRIC;
738 *option = ZOOM_ARITHMETIC;
741 *option = ZOOM_GEOMETRIC;
745 static void add_zoom_style_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text, ZoomStyle option, ZoomStyle *option_c)
752 pref_table_label(table, column, row, text, 0.0);
754 combo = gtk_combo_box_text_new();
756 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Geometric"));
757 if (option == ZOOM_GEOMETRIC) current = 0;
758 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Arithmetic"));
759 if (option == ZOOM_ARITHMETIC) current = 1;
761 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
763 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(zoom_style_selection_menu_cb), option_c);
765 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
766 gtk_widget_show(combo);
769 struct UseableMouseItems
771 const gchar *name; /* GtkActionEntry terminology */
773 const gchar *stock_id;
776 static const UseableMouseItems useable_mouse_items[] = {
778 {"FirstImage", N_("First Image"), GTK_STOCK_GOTO_TOP},
779 {"PrevImage", N_("Previous Image"), GTK_STOCK_GO_UP},
780 {"NextImage", N_("Next Image"), GTK_STOCK_GO_DOWN},
781 {"LastImage", N_("Last Image"), GTK_STOCK_GOTO_BOTTOM},
782 {"Back", N_("Back"), GTK_STOCK_GO_BACK},
783 {"Forward", N_("Forward"), GTK_STOCK_GO_FORWARD},
784 {"Home", N_("Home"), GTK_STOCK_HOME},
785 {"Up", N_("Up"), GTK_STOCK_GO_UP},
786 {"FirstPage", N_("First page"), GTK_STOCK_MEDIA_PREVIOUS},
787 {"LastPage", N_("Last Page"), GTK_STOCK_MEDIA_NEXT},
788 {"NextPage", N_("Next page"), GTK_STOCK_MEDIA_FORWARD},
789 {"PrevPage", N_("Previous Page"), GTK_STOCK_MEDIA_REWIND},
790 {"NewWindow", N_("New _window"), GTK_STOCK_NEW},
791 {"NewCollection", N_("New collection"), GTK_STOCK_INDEX},
792 {"OpenCollection", N_("Open collection"), GTK_STOCK_OPEN},
793 {"Search", N_("Search"), GTK_STOCK_FIND},
794 {"FindDupes", N_("Find duplicates"), GTK_STOCK_FIND},
795 {"NewFolder", N_("New folder"),GTK_STOCK_DIRECTORY},
796 {"Copy", N_("Copy"), GTK_STOCK_COPY},
797 {"Move", N_("Move"), PIXBUF_INLINE_ICON_MOVE},
798 {"Rename", N_("Rename"), PIXBUF_INLINE_ICON_RENAME},
799 {"Delete", N_("Delete"), GTK_STOCK_DELETE},
800 {"CloseWindow", N_("Close Window"), GTK_STOCK_CLOSE},
801 {"PanView", N_("Pan view"), PIXBUF_INLINE_ICON_PANORAMA},
802 {"SelectAll", N_("Select all"), PIXBUF_INLINE_ICON_SELECT_ALL},
803 {"SelectNone", N_("Select none"), PIXBUF_INLINE_ICON_SELECT_NONE},
804 {"SelectInvert", N_("Select invert"), PIXBUF_INLINE_ICON_SELECT_INVERT},
805 {"ShowFileFilter", N_("Show file filter"), PIXBUF_INLINE_ICON_FILE_FILTER},
806 {"RectangularSelection", N_("Select rectangle"), PIXBUF_INLINE_ICON_SELECT_RECTANGLE},
807 {"Print", N_("Print"), GTK_STOCK_PRINT},
808 {"Preferences", N_("Preferences"), GTK_STOCK_PREFERENCES},
809 {"LayoutConfig", N_("Configure this window"), GTK_STOCK_PREFERENCES},
810 {"Maintenance", N_("Cache maintenance"), PIXBUF_INLINE_ICON_MAINTENANCE},
811 {"RotateCW", N_("Rotate clockwise 90°"), PIXBUF_INLINE_ICON_CW},
812 {"RotateCCW", N_("Rotate counterclockwise 90°"), PIXBUF_INLINE_ICON_CCW},
813 {"Rotate180", N_("Rotate 180°"), PIXBUF_INLINE_ICON_180},
814 {"Mirror", N_("Mirror"), PIXBUF_INLINE_ICON_MIRROR},
815 {"Flip", N_("Flip"), PIXBUF_INLINE_ICON_FLIP},
816 {"AlterNone", N_("Original state"), PIXBUF_INLINE_ICON_ORIGINAL},
817 {"ZoomIn", N_("Zoom in"), GTK_STOCK_ZOOM_IN},
818 {"ZoomOut", N_("Zoom out"), GTK_STOCK_ZOOM_OUT},
819 {"Zoom100", N_("Zoom 1:1"), GTK_STOCK_ZOOM_100},
820 {"ZoomFit", N_("Zoom to fit"), GTK_STOCK_ZOOM_FIT},
821 {"ZoomFillHor", N_("Fit Horizontaly"), PIXBUF_INLINE_ICON_ZOOMFILLHOR},
822 {"ZoomFillVert", N_("Fit vertically"), PIXBUF_INLINE_ICON_ZOOMFILLVERT},
823 {"Zoom200", N_("Zoom 2:1"), GTK_STOCK_FILE},
824 {"Zoom300", N_("Zoom 3:1"), GTK_STOCK_FILE},
825 {"Zoom400", N_("Zoom 4:1"), GTK_STOCK_FILE},
826 {"Zoom50", N_("Zoom 1:2"), GTK_STOCK_FILE},
827 {"Zoom33", N_("Zoom1:3"), GTK_STOCK_FILE},
828 {"Zoom25", N_("Zoom 1:4"), GTK_STOCK_FILE},
829 {"ConnectZoomIn", N_("Connected Zoom in"), GTK_STOCK_ZOOM_IN},
830 {"SplitPaneSync", N_("Split Pane Sync"), PIXBUF_INLINE_SPLIT_PANE_SYNC},
831 {"Grayscale", N_("Grayscale"), PIXBUF_INLINE_ICON_GRAYSCALE},
832 {"OverUnderExposed", N_("Over Under Exposed"), PIXBUF_INLINE_ICON_EXPOSURE},
833 {"HideTools", N_("Hide file list"), PIXBUF_INLINE_ICON_HIDETOOLS},
834 {"SlideShowPause", N_("Pause slideshow"), GTK_STOCK_MEDIA_PAUSE},
835 {"SlideShowFaster", N_("Slideshow Faster"), GTK_STOCK_FILE},
836 {"SlideShowSlower", N_("Slideshow Slower"), GTK_STOCK_FILE},
837 {"Refresh", N_("Refresh"), GTK_STOCK_REFRESH},
838 {"HelpContents", N_("Help"), GTK_STOCK_HELP},
839 {"ExifWin", N_("Exif window"), PIXBUF_INLINE_ICON_EXIF},
840 {"Thumbnails", N_("Show thumbnails"), PIXBUF_INLINE_ICON_THUMB},
841 {"ShowMarks", N_("Show marks"), PIXBUF_INLINE_ICON_MARKS},
842 {"DrawRectangle", N_("Draw Rectangle"), PIXBUF_INLINE_ICON_DRAW_RECTANGLE},
843 {"FloatTools", N_("Float file list"), PIXBUF_INLINE_ICON_FLOAT},
844 {"SBar", N_("Info sidebar"), PIXBUF_INLINE_ICON_INFO},
845 {"SBarSort", N_("Sort manager"), PIXBUF_INLINE_ICON_SORT},
846 {"Quit", N_("Quit"), GTK_STOCK_QUIT},
847 {nullptr, nullptr, nullptr}
850 static void mouse_buttons_selection_menu_cb(GtkWidget *combo, gpointer data)
852 auto option = static_cast<gchar **>(data);
855 label = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
857 const UseableMouseItems *list = useable_mouse_items;
861 if (g_strcmp0(list->label, label) == 0)
869 *option = g_strdup(list->name);
873 static void add_mouse_selection_menu(GtkWidget *table, gint column, gint row, const gchar *text,
874 gchar *option, gchar **option_c)
882 pref_table_label(table, column, row, text, 0.0);
884 combo = gtk_combo_box_text_new();
886 const UseableMouseItems *list = useable_mouse_items;
890 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), list->label);
891 if (g_strcmp0(list->name, option) == 0)
899 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
901 g_signal_connect(G_OBJECT(combo), "changed",
902 G_CALLBACK(mouse_buttons_selection_menu_cb), option_c);
904 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
905 gtk_widget_show(combo);
908 static void thumb_size_menu_cb(GtkWidget *combo, gpointer UNUSED(data))
912 n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
915 if (static_cast<guint>(n) < sizeof(thumb_size_list) / sizeof(ThumbSize))
917 c_options->thumbnails.max_width = thumb_size_list[n].w;
918 c_options->thumbnails.max_height = thumb_size_list[n].h;
922 c_options->thumbnails.max_width = options->thumbnails.max_width;
923 c_options->thumbnails.max_height = options->thumbnails.max_height;
927 static void add_thumb_size_menu(GtkWidget *table, gint column, gint row, gchar *text)
933 c_options->thumbnails.max_width = options->thumbnails.max_width;
934 c_options->thumbnails.max_height = options->thumbnails.max_height;
936 pref_table_label(table, column, row, text, 0.0);
938 combo = gtk_combo_box_text_new();
941 for (i = 0; static_cast<guint>(i) < sizeof(thumb_size_list) / sizeof(ThumbSize); i++)
946 w = thumb_size_list[i].w;
947 h = thumb_size_list[i].h;
949 buf = g_strdup_printf("%d x %d", w, h);
950 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
953 if (w == options->thumbnails.max_width && h == options->thumbnails.max_height) current = i;
960 buf = g_strdup_printf("%s %d x %d", _("Custom"), options->thumbnails.max_width, options->thumbnails.max_height);
961 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf);
967 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
968 g_signal_connect(G_OBJECT(combo), "changed",
969 G_CALLBACK(thumb_size_menu_cb), NULL);
971 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
972 gtk_widget_show(combo);
975 static void stereo_mode_menu_cb(GtkWidget *combo, gpointer data)
977 auto option = static_cast<gint *>(data);
979 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
983 *option = PR_STEREO_NONE;
986 *option = PR_STEREO_ANAGLYPH_RC;
989 *option = PR_STEREO_ANAGLYPH_GM;
992 *option = PR_STEREO_ANAGLYPH_YB;
995 *option = PR_STEREO_ANAGLYPH_GRAY_RC;
998 *option = PR_STEREO_ANAGLYPH_GRAY_GM;
1001 *option = PR_STEREO_ANAGLYPH_GRAY_YB;
1004 *option = PR_STEREO_ANAGLYPH_DB_RC;
1007 *option = PR_STEREO_ANAGLYPH_DB_GM;
1010 *option = PR_STEREO_ANAGLYPH_DB_YB;
1013 *option = PR_STEREO_HORIZ;
1016 *option = PR_STEREO_HORIZ | PR_STEREO_HALF;
1019 *option = PR_STEREO_VERT;
1022 *option = PR_STEREO_VERT | PR_STEREO_HALF;
1025 *option = PR_STEREO_FIXED;
1030 static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1031 gint option, gint *option_c, gboolean add_fixed)
1038 pref_table_label(table, column, row, text, 0.0);
1040 combo = gtk_combo_box_text_new();
1042 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Single image"));
1044 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Red-Cyan"));
1045 if (option & PR_STEREO_ANAGLYPH_RC) current = 1;
1046 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Green-Magenta"));
1047 if (option & PR_STEREO_ANAGLYPH_GM) current = 2;
1048 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Yellow-Blue"));
1049 if (option & PR_STEREO_ANAGLYPH_YB) current = 3;
1050 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Red-Cyan"));
1051 if (option & PR_STEREO_ANAGLYPH_GRAY_RC) current = 4;
1052 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Green-Magenta"));
1053 if (option & PR_STEREO_ANAGLYPH_GRAY_GM) current = 5;
1054 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Gray Yellow-Blue"));
1055 if (option & PR_STEREO_ANAGLYPH_GRAY_YB) current = 6;
1056 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Red-Cyan"));
1057 if (option & PR_STEREO_ANAGLYPH_DB_RC) current = 7;
1058 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Green-Magenta"));
1059 if (option & PR_STEREO_ANAGLYPH_DB_GM) current = 8;
1060 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Anaglyph Dubois Yellow-Blue"));
1061 if (option & PR_STEREO_ANAGLYPH_DB_YB) current = 9;
1063 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side"));
1064 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Side by Side Half size"));
1065 if (option & PR_STEREO_HORIZ)
1068 if (option & PR_STEREO_HALF) current = 11;
1071 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom"));
1072 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Top - Bottom Half size"));
1073 if (option & PR_STEREO_VERT)
1076 if (option & PR_STEREO_HALF) current = 13;
1081 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Fixed position"));
1082 if (option & PR_STEREO_FIXED) current = 14;
1085 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1087 g_signal_connect(G_OBJECT(combo), "changed",
1088 G_CALLBACK(stereo_mode_menu_cb), option_c);
1090 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1091 gtk_widget_show(combo);
1094 static void video_menu_cb(GtkWidget *combo, gpointer data)
1096 auto option = static_cast<gchar **>(data);
1098 auto ed = static_cast<EditorDescription *>(g_list_nth_data(editor_list_get(), gtk_combo_box_get_active(GTK_COMBO_BOX(combo))));
1102 static void video_menu_populate(gpointer data, gpointer user_data)
1104 auto combo = static_cast<GtkWidget *>(user_data);
1105 auto ed = static_cast<EditorDescription *>(data);
1107 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), ed->name);
1110 static void add_video_menu(GtkWidget *table, gint column, gint row, const gchar *text,
1111 gchar *option, gchar **option_c)
1115 /* use lists since they are sorted */
1116 GList *eds = editor_list_get();
1120 pref_table_label(table, column, row, text, 0.0);
1122 combo = gtk_combo_box_text_new();
1123 g_list_foreach(eds,video_menu_populate,combo);
1124 current = option ? g_list_index(eds,g_hash_table_lookup(editors,option)): -1;
1126 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
1128 g_signal_connect(G_OBJECT(combo), "changed",
1129 G_CALLBACK(video_menu_cb), option_c);
1131 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
1132 gtk_widget_show(combo);
1135 static void filter_store_populate()
1139 if (!filter_store) return;
1141 gtk_list_store_clear(filter_store);
1143 work = filter_get_list();
1149 fe = static_cast<FilterEntry *>(work->data);
1152 gtk_list_store_append(filter_store, &iter);
1153 gtk_list_store_set(filter_store, &iter, 0, fe, -1);
1157 static void filter_store_ext_edit_cb(GtkCellRendererText *UNUSED(cell), gchar *path_str,
1158 gchar *new_text, gpointer data)
1160 auto model = static_cast<GtkWidget *>(data);
1161 auto fe = static_cast<FilterEntry *>(data);
1165 if (!new_text || strlen(new_text) < 1) return;
1167 tpath = gtk_tree_path_new_from_string(path_str);
1168 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1169 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1171 g_free(fe->extensions);
1172 fe->extensions = g_strdup(new_text);
1174 gtk_tree_path_free(tpath);
1178 static void filter_store_class_edit_cb(GtkCellRendererText *UNUSED(cell), gchar *path_str,
1179 gchar *new_text, gpointer data)
1181 auto model = static_cast<GtkWidget *>(data);
1182 auto fe = static_cast<FilterEntry *>(data);
1187 if (!new_text || !new_text[0]) return;
1189 tpath = gtk_tree_path_new_from_string(path_str);
1190 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1191 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1193 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
1195 if (strcmp(new_text, _(format_class_list[i])) == 0)
1197 fe->file_class = static_cast<FileFormatClass>(i);
1202 gtk_tree_path_free(tpath);
1206 static void filter_store_desc_edit_cb(GtkCellRendererText *UNUSED(cell), gchar *path_str,
1207 gchar *new_text, gpointer data)
1209 auto model = static_cast<GtkWidget *>(data);
1214 if (!new_text || !new_text[0]) return;
1216 tpath = gtk_tree_path_new_from_string(path_str);
1217 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1218 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1220 g_free(fe->description);
1221 fe->description = g_strdup(new_text);
1223 gtk_tree_path_free(tpath);
1226 static void filter_store_enable_cb(GtkCellRendererToggle *UNUSED(renderer),
1227 gchar *path_str, gpointer data)
1229 auto model = static_cast<GtkWidget *>(data);
1234 tpath = gtk_tree_path_new_from_string(path_str);
1235 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1236 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1238 fe->enabled = !fe->enabled;
1240 gtk_tree_path_free(tpath);
1244 static void filter_store_writable_cb(GtkCellRendererToggle *UNUSED(renderer),
1245 gchar *path_str, gpointer data)
1247 auto model = static_cast<GtkWidget *>(data);
1252 tpath = gtk_tree_path_new_from_string(path_str);
1253 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1254 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1256 fe->writable = !fe->writable;
1257 if (fe->writable) fe->allow_sidecar = FALSE;
1259 gtk_tree_path_free(tpath);
1263 static void filter_store_sidecar_cb(GtkCellRendererToggle *UNUSED(renderer),
1264 gchar *path_str, gpointer data)
1266 auto model = static_cast<GtkWidget *>(data);
1271 tpath = gtk_tree_path_new_from_string(path_str);
1272 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, tpath);
1273 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &fe, -1);
1275 fe->allow_sidecar = !fe->allow_sidecar;
1276 if (fe->allow_sidecar) fe->writable = FALSE;
1278 gtk_tree_path_free(tpath);
1282 static void filter_set_func(GtkTreeViewColumn *UNUSED(tree_column), GtkCellRenderer *cell,
1283 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1287 gtk_tree_model_get(tree_model, iter, 0, &fe, -1);
1289 switch (GPOINTER_TO_INT(data))
1292 g_object_set(GTK_CELL_RENDERER(cell),
1293 "active", fe->enabled, NULL);
1296 g_object_set(GTK_CELL_RENDERER(cell),
1297 "text", fe->extensions, NULL);
1299 case FE_DESCRIPTION:
1300 g_object_set(GTK_CELL_RENDERER(cell),
1301 "text", fe->description, NULL);
1304 g_object_set(GTK_CELL_RENDERER(cell),
1305 "text", _(format_class_list[fe->file_class]), NULL);
1308 g_object_set(GTK_CELL_RENDERER(cell),
1309 "active", fe->writable, NULL);
1311 case FE_ALLOW_SIDECAR:
1312 g_object_set(GTK_CELL_RENDERER(cell),
1313 "active", fe->allow_sidecar, NULL);
1318 static gboolean filter_add_scroll(gpointer data)
1322 GtkCellRenderer *cell;
1323 GtkTreeViewColumn *column;
1326 GtkTreeModel *store;
1328 FilterEntry *filter;
1330 rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(filter_store), nullptr);
1331 path = gtk_tree_path_new_from_indices(rows-1, -1);
1333 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data), 0);
1335 list_cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
1336 cell = static_cast<GtkCellRenderer *>(g_list_last(list_cells)->data);
1338 store = gtk_tree_view_get_model(GTK_TREE_VIEW(data));
1339 valid = gtk_tree_model_get_iter_first(store, &iter);
1343 gtk_tree_model_get(store, &iter, 0, &filter, -1);
1345 if (g_strcmp0(filter->extensions, ".new") == 0)
1347 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1351 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1354 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1355 path, column, FALSE, 0.0, 0.0 );
1356 gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(data),
1357 path, column, cell, TRUE);
1359 gtk_tree_path_free(path);
1360 g_list_free(list_cells);
1362 return(G_SOURCE_REMOVE);
1365 static void filter_add_cb(GtkWidget *UNUSED(widget), gpointer data)
1367 filter_add_unique("description", ".new", FORMAT_CLASS_IMAGE, TRUE, FALSE, TRUE);
1368 filter_store_populate();
1370 g_idle_add(static_cast<GSourceFunc>(filter_add_scroll), data);
1373 static void filter_remove_cb(GtkWidget *UNUSED(widget), gpointer data)
1375 auto filter_view = static_cast<GtkWidget *>(data);
1376 GtkTreeSelection *selection;
1380 if (!filter_store) return;
1381 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
1382 if (!gtk_tree_selection_get_selected(selection, nullptr, &iter)) return;
1383 gtk_tree_model_get(GTK_TREE_MODEL(filter_store), &iter, 0, &fe, -1);
1386 filter_remove_entry(fe);
1388 filter_store_populate();
1391 static gboolean filter_default_ok_scroll(GtkTreeView *data)
1395 GtkTreeViewColumn *column;
1397 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(filter_store), &iter);
1398 path = gtk_tree_model_get_path(GTK_TREE_MODEL(filter_store), &iter);
1399 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1401 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1405 gtk_tree_path_free(path);
1407 return(G_SOURCE_REMOVE);
1410 static void filter_default_ok_cb(GenericDialog *gd, gpointer UNUSED(data))
1413 filter_add_defaults();
1415 filter_store_populate();
1417 g_idle_add(reinterpret_cast<GSourceFunc>(filter_default_ok_scroll), gd->data);
1420 static void dummy_cancel_cb(GenericDialog *UNUSED(gd), gpointer UNUSED(data))
1422 /* no op, only so cancel button appears */
1425 static void filter_default_cb(GtkWidget *widget, gpointer data)
1429 gd = generic_dialog_new(_("Reset filters"),
1430 "reset_filter", widget, TRUE,
1431 dummy_cancel_cb, data);
1432 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION, _("Reset filters"),
1433 _("This will reset the file filters to the defaults.\nContinue?"), TRUE);
1434 generic_dialog_add_button(gd, GTK_STOCK_OK, nullptr, filter_default_ok_cb, TRUE);
1435 gtk_widget_show(gd->dialog);
1438 static void filter_disable_cb(GtkWidget *widget, gpointer data)
1440 auto frame = static_cast<GtkWidget *>(data);
1442 gtk_widget_set_sensitive(frame,
1443 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1446 static void safe_delete_view_cb(GtkWidget *UNUSED(widget), gpointer UNUSED(data))
1448 layout_set_path(nullptr, gtk_entry_get_text(GTK_ENTRY(safe_delete_path_entry)));
1451 static void safe_delete_clear_ok_cb(GenericDialog *UNUSED(gd), gpointer UNUSED(data))
1453 file_util_trash_clear();
1456 static void safe_delete_clear_cb(GtkWidget *widget, gpointer UNUSED(data))
1460 gd = generic_dialog_new(_("Clear trash"),
1461 "clear_trash", widget, TRUE,
1462 dummy_cancel_cb, nullptr);
1463 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION, _("Clear trash"),
1464 _("This will remove the trash contents."), FALSE);
1465 generic_dialog_add_button(gd, GTK_STOCK_OK, nullptr, safe_delete_clear_ok_cb, TRUE);
1466 entry = gtk_entry_new();
1467 gtk_widget_set_can_focus(entry, FALSE);
1468 gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
1469 if (options->file_ops.safe_delete_path) gtk_entry_set_text(GTK_ENTRY(entry), options->file_ops.safe_delete_path);
1470 gtk_box_pack_start(GTK_BOX(gd->vbox), entry, FALSE, FALSE, 0);
1471 gtk_widget_show(entry);
1472 gtk_widget_show(gd->dialog);
1475 static void image_overlay_template_view_changed_cb(GtkWidget *UNUSED(widget), gpointer data)
1477 GtkWidget *pTextView;
1478 GtkTextBuffer *pTextBuffer;
1482 pTextView = GTK_WIDGET(data);
1484 pTextBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pTextView));
1485 gtk_text_buffer_get_start_iter(pTextBuffer, &iStart);
1486 gtk_text_buffer_get_end_iter(pTextBuffer, &iEnd);
1488 set_image_overlay_template_string(&c_options->image_overlay.template_string,
1489 gtk_text_buffer_get_text(pTextBuffer, &iStart, &iEnd, TRUE));
1492 static void image_overlay_default_template_ok_cb(GenericDialog *UNUSED(gd), gpointer data)
1494 auto text_view = static_cast<GtkTextView *>(data);
1495 GtkTextBuffer *buffer;
1497 set_default_image_overlay_template_string(&options->image_overlay.template_string);
1498 if (!configwindow) return;
1500 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
1501 gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
1504 static void image_overlay_default_template_cb(GtkWidget *widget, gpointer data)
1508 gd = generic_dialog_new(_("Reset image overlay template string"),
1509 "reset_image_overlay_template_string", widget, TRUE,
1510 dummy_cancel_cb, data);
1511 generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION, _("Reset image overlay template string"),
1512 _("This will reset the image overlay template string to the default.\nContinue?"), TRUE);
1513 generic_dialog_add_button(gd, GTK_STOCK_OK, nullptr, image_overlay_default_template_ok_cb, TRUE);
1514 gtk_widget_show(gd->dialog);
1517 static void image_overlay_help_cb(GtkWidget *UNUSED(widget), gpointer UNUSED(data))
1519 help_window_show("GuideOptionsOSD.html");
1522 static void image_overlay_set_font_cb(GtkWidget *widget, gpointer UNUSED(data))
1526 PangoFontDescription *font_desc;
1528 dialog = gtk_font_chooser_dialog_new("Image Overlay Font", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1529 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), options->image_overlay.font);
1531 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1533 font_desc = gtk_font_chooser_get_font_desc(GTK_FONT_CHOOSER(dialog));
1534 font = pango_font_description_to_string(font_desc);
1535 g_free(c_options->image_overlay.font);
1536 c_options->image_overlay.font = g_strdup(font);
1540 gtk_widget_destroy(dialog);
1543 static void image_overlay_set_text_colour_cb(GtkWidget *widget, gpointer UNUSED(data))
1548 dialog = gtk_color_chooser_dialog_new("Image Overlay Text Colour", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1549 colour.red = options->image_overlay.text_red;
1550 colour.green = options->image_overlay.text_green;
1551 colour.blue = options->image_overlay.text_blue;
1552 colour.alpha = options->image_overlay.text_alpha;
1553 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1554 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1556 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1558 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1559 c_options->image_overlay.text_red = colour.red*255;
1560 c_options->image_overlay.text_green = colour.green*255;
1561 c_options->image_overlay.text_blue = colour.blue*255;
1562 c_options->image_overlay.text_alpha = colour.alpha*255;
1564 gtk_widget_destroy(dialog);
1568 static void image_overlay_set_background_colour_cb(GtkWidget *widget, gpointer UNUSED(data))
1573 dialog = gtk_color_chooser_dialog_new("Image Overlay Background Colour", GTK_WINDOW(gtk_widget_get_toplevel(widget)));
1574 colour.red = options->image_overlay.background_red;
1575 colour.green = options->image_overlay.background_green;
1576 colour.blue = options->image_overlay.background_blue;
1577 colour.alpha = options->image_overlay.background_alpha;
1578 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1579 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(dialog), TRUE);
1581 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
1583 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &colour);
1584 c_options->image_overlay.background_red = colour.red*255;
1585 c_options->image_overlay.background_green = colour.green*255;
1586 c_options->image_overlay.background_blue = colour.blue*255;
1587 c_options->image_overlay.background_alpha = colour.alpha*255;
1589 gtk_widget_destroy(dialog);
1592 static void accel_store_populate()
1595 GList *groups, *actions;
1597 const gchar *accel_path;
1601 if (!accel_store || !layout_window_list || !layout_window_list->data) return;
1603 gtk_tree_store_clear(accel_store);
1604 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 */
1606 g_assert(lw && lw->ui_manager);
1607 groups = gtk_ui_manager_get_action_groups(lw->ui_manager);
1610 actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
1613 action = GTK_ACTION(actions->data);
1614 accel_path = gtk_action_get_accel_path(action);
1615 if (accel_path && gtk_accel_map_lookup_entry(accel_path, &key))
1617 gchar *label, *label2, *tooltip, *accel;
1618 g_object_get(action,
1619 "tooltip", &tooltip,
1623 if (pango_parse_markup(label, -1, '_', nullptr, &label2, nullptr, nullptr) && label2)
1629 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1633 gtk_tree_store_append(accel_store, &iter, nullptr);
1634 gtk_tree_store_set(accel_store, &iter,
1637 AE_TOOLTIP, tooltip ? tooltip : "",
1638 AE_ACCEL, accel_path,
1646 actions = actions->next;
1649 groups = groups->next;
1653 static void accel_store_cleared_cb(GtkCellRendererAccel *UNUSED(accel), gchar *UNUSED(path_string), gpointer UNUSED(user_data))
1658 static gboolean accel_remove_key_cb(GtkTreeModel *model, GtkTreePath *UNUSED(path), GtkTreeIter *iter, gpointer data)
1660 auto accel1 = static_cast<gchar *>(data);
1665 gtk_tree_model_get(model, iter, AE_KEY, &accel2, -1);
1667 gtk_accelerator_parse(accel1, &key1.accel_key, &key1.accel_mods);
1668 gtk_accelerator_parse(accel2, &key2.accel_key, &key2.accel_mods);
1670 if (key1.accel_key == key2.accel_key && key1.accel_mods == key2.accel_mods)
1672 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1673 DEBUG_1("accelerator key '%s' is already used, removing.", accel1);
1682 static void accel_store_edited_cb(GtkCellRendererAccel *UNUSED(accel), gchar *path_string, guint accel_key, GdkModifierType accel_mods, guint UNUSED(hardware_keycode), gpointer UNUSED(user_data))
1684 auto model = reinterpret_cast<GtkTreeModel *>(accel_store);
1688 GtkAccelKey old_key, key;
1689 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1691 gtk_tree_model_get_iter(model, &iter, path);
1692 gtk_tree_model_get(model, &iter, AE_ACCEL, &accel_path, -1);
1694 /* test if the accelerator can be stored without conflicts*/
1695 gtk_accel_map_lookup_entry(accel_path, &old_key);
1697 /* change the key and read it back (change may fail on keys hardcoded in gtk)*/
1698 gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, TRUE);
1699 gtk_accel_map_lookup_entry(accel_path, &key);
1701 /* restore the original for now, the key will be really changed when the changes are confirmed */
1702 gtk_accel_map_change_entry(accel_path, old_key.accel_key, old_key.accel_mods, TRUE);
1704 acc = gtk_accelerator_name(key.accel_key, key.accel_mods);
1705 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, acc);
1707 gtk_tree_store_set(accel_store, &iter, AE_KEY, acc, -1);
1708 gtk_tree_path_free(path);
1712 static gboolean accel_default_scroll(GtkTreeView *data)
1716 GtkTreeViewColumn *column;
1718 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(accel_store), &iter);
1719 path = gtk_tree_model_get_path(GTK_TREE_MODEL(accel_store), &iter);
1720 column = gtk_tree_view_get_column(GTK_TREE_VIEW(data),0);
1722 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(data),
1726 gtk_tree_path_free(path);
1728 return(G_SOURCE_REMOVE);
1731 static void accel_default_cb(GtkWidget *UNUSED(widget), gpointer data)
1733 accel_store_populate();
1735 g_idle_add(reinterpret_cast<GSourceFunc>(accel_default_scroll), data);
1738 void accel_clear_selection(GtkTreeModel *UNUSED(model), GtkTreePath *UNUSED(path), GtkTreeIter *iter, gpointer UNUSED(data))
1740 gtk_tree_store_set(accel_store, iter, AE_KEY, "", -1);
1743 void accel_reset_selection(GtkTreeModel *model, GtkTreePath *UNUSED(path), GtkTreeIter *iter, gpointer UNUSED(data))
1746 gchar *accel_path, *accel;
1748 gtk_tree_model_get(model, iter, AE_ACCEL, &accel_path, -1);
1749 gtk_accel_map_lookup_entry(accel_path, &key);
1750 accel = gtk_accelerator_name(key.accel_key, key.accel_mods);
1752 gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_remove_key_cb, accel);
1754 gtk_tree_store_set(accel_store, iter, AE_KEY, accel, -1);
1759 static void accel_clear_cb(GtkWidget *UNUSED(widget), gpointer data)
1761 GtkTreeSelection *selection;
1763 if (!accel_store) return;
1764 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1765 gtk_tree_selection_selected_foreach(selection, &accel_clear_selection, nullptr);
1768 static void accel_reset_cb(GtkWidget *UNUSED(widget), gpointer data)
1770 GtkTreeSelection *selection;
1772 if (!accel_store) return;
1773 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
1774 gtk_tree_selection_selected_foreach(selection, &accel_reset_selection, nullptr);
1779 static GtkWidget *scrolled_notebook_page(GtkWidget *notebook, const gchar *title)
1783 GtkWidget *scrolled;
1784 GtkWidget *viewport;
1786 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
1787 gtk_container_set_border_width(GTK_CONTAINER(scrolled), PREF_PAD_BORDER);
1788 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1789 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1790 label = gtk_label_new(title);
1791 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, label);
1792 gtk_widget_show(scrolled);
1794 viewport = gtk_viewport_new(nullptr, nullptr);
1795 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
1796 gtk_container_add(GTK_CONTAINER(scrolled), viewport);
1797 gtk_widget_show(viewport);
1799 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1800 gtk_container_add(GTK_CONTAINER(viewport), vbox);
1801 gtk_widget_show(vbox);
1806 static void cache_standard_cb(GtkWidget *widget, gpointer UNUSED(data))
1808 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1810 c_options->thumbnails.spec_standard =TRUE;
1811 c_options->thumbnails.cache_into_dirs = FALSE;
1815 static void cache_geeqie_cb(GtkWidget *widget, gpointer UNUSED(data))
1817 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1819 c_options->thumbnails.spec_standard =FALSE;
1820 c_options->thumbnails.cache_into_dirs = FALSE;
1824 static void cache_local_cb(GtkWidget *widget, gpointer UNUSED(data))
1826 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1828 c_options->thumbnails.cache_into_dirs = TRUE;
1829 c_options->thumbnails.spec_standard =FALSE;
1833 static void help_search_engine_entry_icon_cb(GtkEntry *UNUSED(entry), GtkEntryIconPosition pos,
1834 GdkEvent *UNUSED(event), gpointer userdata)
1836 if (pos == GTK_ENTRY_ICON_PRIMARY)
1838 gtk_entry_set_text(GTK_ENTRY(userdata), HELP_SEARCH_ENGINE);
1842 gtk_entry_set_text(GTK_ENTRY(userdata), "");
1846 static void star_rating_star_icon_cb(GtkEntry *UNUSED(entry), GtkEntryIconPosition pos,
1847 GdkEvent *UNUSED(event), gpointer userdata)
1849 gchar *rating_symbol;
1851 if (pos == GTK_ENTRY_ICON_PRIMARY)
1853 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_STAR);
1854 gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1855 g_free(rating_symbol);
1859 gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1860 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1861 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1865 static void star_rating_rejected_icon_cb(GtkEntry *UNUSED(entry), GtkEntryIconPosition pos,
1866 GdkEvent *UNUSED(event), gpointer userdata)
1868 gchar *rating_symbol;
1870 if (pos == GTK_ENTRY_ICON_PRIMARY)
1872 rating_symbol = g_strdup_printf("U+%X", STAR_RATING_REJECTED);
1873 gtk_entry_set_text(GTK_ENTRY(userdata), rating_symbol);
1874 g_free(rating_symbol);
1878 gtk_entry_set_text(GTK_ENTRY(userdata), "U+");
1879 gtk_widget_grab_focus(GTK_WIDGET(userdata));
1880 gtk_editable_select_region(GTK_EDITABLE(userdata), 2, 2);
1884 static guint star_rating_symbol_test(GtkWidget *UNUSED(widget), gpointer data)
1886 auto hbox = static_cast<GtkContainer *>(data);
1887 GString *str = g_string_new(nullptr);
1888 GtkEntry *hex_code_entry;
1889 gchar *hex_code_full;
1892 guint64 hex_value = 0;
1894 list = gtk_container_get_children(hbox);
1896 hex_code_entry = static_cast<GtkEntry *>(g_list_nth_data(list, 2));
1897 hex_code_full = g_strdup(gtk_entry_get_text(hex_code_entry));
1899 hex_code = g_strsplit(hex_code_full, "+", 2);
1900 if (hex_code[0] && hex_code[1])
1902 hex_value = strtoull(hex_code[1], nullptr, 16);
1904 if (!hex_value || hex_value > 0x10FFFF)
1906 hex_value = 0x003F; // Unicode 'Question Mark'
1908 str = g_string_append_unichar(str, static_cast<gunichar>(hex_value));
1909 gtk_label_set_text(static_cast<GtkLabel *>(g_list_nth_data(list, 1)), str->str);
1911 g_strfreev(hex_code);
1912 g_string_free(str, TRUE);
1913 g_free(hex_code_full);
1918 static void star_rating_star_test_cb(GtkWidget *widget, gpointer data)
1920 guint64 star_symbol;
1922 star_symbol = star_rating_symbol_test(widget, data);
1923 c_options->star_rating.star = star_symbol;
1926 static void star_rating_rejected_test_cb(GtkWidget *widget, gpointer data)
1928 guint64 rejected_symbol;
1930 rejected_symbol = star_rating_symbol_test(widget, data);
1931 c_options->star_rating.rejected = rejected_symbol;
1934 /* general options tab */
1935 static void timezone_database_install_cb(GtkWidget *widget, gpointer data);
1939 GCancellable *cancellable;
1941 GtkWidget *progress;
1943 GFile *timezone_database_gq;
1944 gchar *timezone_database_user;
1947 static void config_tab_general(GtkWidget *notebook)
1952 GtkWidget *group_frame;
1953 GtkWidget *subgroup;
1955 GtkWidget *ct_button;
1958 gint hours, minutes, remainder;
1960 GtkWidget *star_rating_entry;
1962 gchar *rating_symbol;
1965 gchar *download_locn;
1966 GNetworkMonitor *net_mon;
1967 GSocketConnectable *tz_org;
1968 gboolean internet_available = FALSE;
1971 vbox = scrolled_notebook_page(notebook, _("General"));
1973 group = pref_group_new(vbox, FALSE, _("Thumbnails"), GTK_ORIENTATION_VERTICAL);
1975 table = pref_table_new(group, 2, 2, FALSE, FALSE);
1976 add_thumb_size_menu(table, 0, 0, _("Size:"));
1977 add_quality_menu(table, 0, 1, _("Quality:"), options->thumbnails.quality, &c_options->thumbnails.quality);
1979 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
1980 pref_label_new(hbox, _("Custom size: "));
1981 pref_spin_new_int(hbox, _("Width:"), nullptr, 1, 512, 1, options->thumbnails.max_width, &c_options->thumbnails.max_width);
1982 pref_spin_new_int(hbox, _("Height:"), nullptr, 1, 512, 1, options->thumbnails.max_height, &c_options->thumbnails.max_height);
1984 ct_button = pref_checkbox_new_int(group, _("Cache thumbnails and sim. files"),
1985 options->thumbnails.enable_caching, &c_options->thumbnails.enable_caching);
1987 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1988 pref_checkbox_link_sensitivity(ct_button, subgroup);
1990 c_options->thumbnails.spec_standard = options->thumbnails.spec_standard;
1991 c_options->thumbnails.cache_into_dirs = options->thumbnails.cache_into_dirs;
1992 group_frame = pref_frame_new(subgroup, TRUE, _("Use Geeqie thumbnail style and cache"),
1993 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
1994 button = pref_radiobutton_new(group_frame, nullptr, get_thumbnails_cache_dir(),
1995 !options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
1996 G_CALLBACK(cache_geeqie_cb), nullptr);
1998 group_frame = pref_frame_new(subgroup, TRUE,
1999 _("Store thumbnails local to image folder (non-standard)"),
2000 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2001 pref_radiobutton_new(group_frame, button, "*/.thumbnails",
2002 !options->thumbnails.spec_standard && options->thumbnails.cache_into_dirs,
2003 G_CALLBACK(cache_local_cb), nullptr);
2005 group_frame = pref_frame_new(subgroup, TRUE,
2006 _("Use standard thumbnail style and cache, shared with other applications"),
2007 GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2008 pref_radiobutton_new(group_frame, button, get_thumbnails_standard_cache_dir(),
2009 options->thumbnails.spec_standard && !options->thumbnails.cache_into_dirs,
2010 G_CALLBACK(cache_standard_cb), nullptr);
2012 pref_checkbox_new_int(group, _("Use EXIF thumbnails when available (EXIF thumbnails may be outdated)"),
2013 options->thumbnails.use_exif, &c_options->thumbnails.use_exif);
2015 pref_checkbox_new_int(group, _("Thumbnail color management"),
2016 options->thumbnails.use_color_management, &c_options->thumbnails.use_color_management);
2018 spin = pref_spin_new_int(group, _("Collection preview:"), nullptr,
2020 options->thumbnails.collection_preview, &c_options->thumbnails.collection_preview);
2021 gtk_widget_set_tooltip_text(spin, _("The maximum number of thumbnails shown in a Collection preview montage"));
2023 #ifdef HAVE_FFMPEGTHUMBNAILER_METADATA
2024 pref_checkbox_new_int(group, _("Use embedded metadata in video files as thumbnails when available"),
2025 options->thumbnails.use_ft_metadata, &c_options->thumbnails.use_ft_metadata);
2028 pref_spacer(group, PREF_PAD_GROUP);
2030 group = pref_group_new(vbox, FALSE, _("Star Rating"), GTK_ORIENTATION_VERTICAL);
2032 c_options->star_rating.star = options->star_rating.star;
2033 c_options->star_rating.rejected = options->star_rating.rejected;
2035 str = g_string_new(nullptr);
2036 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2037 pref_label_new(hbox, _("Star character: "));
2038 str = g_string_append_unichar(str, options->star_rating.star);
2039 pref_label_new(hbox, g_strdup(str->str));
2040 rating_symbol = g_strdup_printf("U+%X", options->star_rating.star);
2041 star_rating_entry = gtk_entry_new();
2042 gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2043 gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2044 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2045 gtk_widget_show(star_rating_entry);
2046 button = pref_button_new(nullptr, nullptr, _("Set"), FALSE,
2047 G_CALLBACK(star_rating_star_test_cb), hbox);
2048 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2049 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2050 gtk_widget_show(button);
2051 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."));
2052 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2053 GTK_ENTRY_ICON_SECONDARY, "edit-clear");
2054 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2055 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2056 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2057 GTK_ENTRY_ICON_PRIMARY, "document-revert");
2058 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2059 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2060 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2061 G_CALLBACK(star_rating_star_icon_cb),
2064 g_string_free(str, TRUE);
2065 g_free(rating_symbol);
2067 str = g_string_new(nullptr);
2068 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2069 pref_label_new(hbox, _("Rejected character: "));
2070 str = g_string_append_unichar(str, options->star_rating.rejected);
2071 pref_label_new(hbox, g_strdup(str->str));
2072 rating_symbol = g_strdup_printf("U+%X", options->star_rating.rejected);
2073 star_rating_entry = gtk_entry_new();
2074 gtk_entry_set_text(GTK_ENTRY(star_rating_entry), rating_symbol);
2075 gtk_box_pack_start(GTK_BOX(hbox), star_rating_entry, FALSE, FALSE, 0);
2076 gtk_entry_set_width_chars(GTK_ENTRY(star_rating_entry), 15);
2077 gtk_widget_show(star_rating_entry);
2078 button = pref_button_new(nullptr, nullptr, _("Set"), FALSE,
2079 G_CALLBACK(star_rating_rejected_test_cb), hbox);
2080 gtk_widget_set_tooltip_text(button, _("Display selected character"));
2081 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2082 gtk_widget_show(button);
2083 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."));
2084 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2085 GTK_ENTRY_ICON_SECONDARY, "edit-clear");
2086 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2087 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2088 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(star_rating_entry),
2089 GTK_ENTRY_ICON_PRIMARY, "document-revert");
2090 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(star_rating_entry),
2091 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2092 g_signal_connect(GTK_ENTRY(star_rating_entry), "icon-press",
2093 G_CALLBACK(star_rating_rejected_icon_cb),
2096 g_string_free(str, TRUE);
2097 g_free(rating_symbol);
2099 pref_spacer(group, PREF_PAD_GROUP);
2101 group = pref_group_new(vbox, FALSE, _("Slide show"), GTK_ORIENTATION_VERTICAL);
2103 c_options->slideshow.delay = options->slideshow.delay;
2104 hours = options->slideshow.delay / (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2105 remainder = options->slideshow.delay % (3600 * SLIDESHOW_SUBSECOND_PRECISION);
2106 minutes = remainder / (60 * SLIDESHOW_SUBSECOND_PRECISION);
2107 seconds = static_cast<gdouble>(remainder % (60 * SLIDESHOW_SUBSECOND_PRECISION)) /
2108 SLIDESHOW_SUBSECOND_PRECISION;
2110 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2112 spin = pref_spin_new(hbox, _("Delay between image change hrs:mins:secs.dec"), nullptr,
2114 options->slideshow.delay ? hours : 0.0,
2115 G_CALLBACK(slideshow_delay_hours_cb), nullptr);
2116 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2117 spin = pref_spin_new(hbox, ":" , nullptr,
2119 options->slideshow.delay ? minutes: 0.0,
2120 G_CALLBACK(slideshow_delay_minutes_cb), nullptr);
2121 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2122 spin = pref_spin_new(hbox, ":", nullptr,
2123 SLIDESHOW_MIN_SECONDS, 59, 1.0, 1,
2124 options->slideshow.delay ? seconds : 10.0,
2125 G_CALLBACK(slideshow_delay_seconds_cb), nullptr);
2126 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2128 pref_checkbox_new_int(group, _("Random"), options->slideshow.random, &c_options->slideshow.random);
2129 pref_checkbox_new_int(group, _("Repeat"), options->slideshow.repeat, &c_options->slideshow.repeat);
2131 pref_spacer(group, PREF_PAD_GROUP);
2133 group = pref_group_new(vbox, FALSE, _("Image loading and caching"), GTK_ORIENTATION_VERTICAL);
2135 pref_spin_new_int(group, _("Decoded image cache size (MiB):"), nullptr,
2136 0, 99999, 1, options->image.image_cache_max, &c_options->image.image_cache_max);
2137 pref_checkbox_new_int(group, _("Preload next image"),
2138 options->image.enable_read_ahead, &c_options->image.enable_read_ahead);
2140 pref_checkbox_new_int(group, _("Refresh on file change"),
2141 options->update_on_time_change, &c_options->update_on_time_change);
2144 pref_spacer(group, PREF_PAD_GROUP);
2146 group = pref_group_new(vbox, FALSE, _("Expand menu and toolbar"), GTK_ORIENTATION_VERTICAL);
2148 pref_checkbox_new_int(group, _("Expand menu and toolbar (NOTE! Geeqie must be restarted for change to take effect)"),
2149 options->expand_menu_toolbar, &c_options->expand_menu_toolbar);
2150 gtk_widget_set_tooltip_text(group, _("Expand the menu and toolbar to the full width of the window"));
2152 pref_spacer(group, PREF_PAD_GROUP);
2154 if (g_getenv("APPDIR") && strstr(g_getenv("APPDIR"), "/tmp/.mount_Geeqie"))
2156 group = pref_group_new(vbox, FALSE, _("AppImage updates notifications"), GTK_ORIENTATION_VERTICAL);
2157 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2158 pref_checkbox_new_int(group, _("Enable"), options->appimage_notifications, &c_options->appimage_notifications);
2159 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"));
2161 pref_spacer(group, PREF_PAD_GROUP);
2165 net_mon = g_network_monitor_get_default();
2166 tz_org = g_network_address_parse_uri(TIMEZONE_DATABASE_WEB, 80, nullptr);
2169 internet_available = g_network_monitor_can_reach(net_mon, tz_org, nullptr, nullptr);
2170 g_object_unref(tz_org);
2173 group = pref_group_new(vbox, FALSE, _("Timezone database"), GTK_ORIENTATION_VERTICAL);
2174 hbox = pref_box_new(group, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2176 if (!internet_available)
2178 gtk_widget_set_sensitive(group, FALSE);
2181 tz = g_new0(TZData, 1);
2183 path = path_from_utf8(TIMEZONE_DATABASE_WEB);
2184 basename = g_path_get_basename(path);
2185 tz->timezone_database_user = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
2189 if (isfile(tz->timezone_database_user))
2191 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Update"), FALSE, G_CALLBACK(timezone_database_install_cb), tz);
2195 button = pref_button_new(GTK_WIDGET(hbox), nullptr, _("Install"), FALSE, G_CALLBACK(timezone_database_install_cb), tz);
2198 download_locn = g_strconcat(_("Download database from: "), TIMEZONE_DATABASE_WEB, NULL);
2199 pref_label_new(GTK_WIDGET(hbox), download_locn);
2200 g_free(download_locn);
2202 if (!internet_available)
2204 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"));
2208 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"));
2210 gtk_widget_show(button);
2212 pref_spacer(group, PREF_PAD_GROUP);
2214 group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL);
2216 help_search_engine_entry = gtk_entry_new();
2217 gtk_entry_set_text(GTK_ENTRY(help_search_engine_entry), options->help_search_engine);
2218 gtk_box_pack_start(GTK_BOX(group), help_search_engine_entry, FALSE, FALSE, 0);
2219 gtk_widget_show(help_search_engine_entry);
2221 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"));
2223 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2224 GTK_ENTRY_ICON_SECONDARY, "edit-clear");
2225 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2226 GTK_ENTRY_ICON_SECONDARY, _("Clear"));
2227 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(help_search_engine_entry),
2228 GTK_ENTRY_ICON_PRIMARY, "document-revert");
2229 gtk_entry_set_icon_tooltip_text (GTK_ENTRY(help_search_engine_entry),
2230 GTK_ENTRY_ICON_PRIMARY, _("Default"));
2231 g_signal_connect(GTK_ENTRY(help_search_engine_entry), "icon-press",
2232 G_CALLBACK(help_search_engine_entry_icon_cb),
2233 help_search_engine_entry);
2237 static void config_tab_image(GtkWidget *notebook)
2242 GtkWidget *ct_button;
2243 GtkWidget *enlargement_button;
2247 vbox = scrolled_notebook_page(notebook, _("Image"));
2249 group = pref_group_new(vbox, FALSE, _("Zoom"), GTK_ORIENTATION_VERTICAL);
2251 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2252 add_quality_menu(table, 0, 0, _("Quality:"), options->image.zoom_quality, &c_options->image.zoom_quality);
2254 pref_checkbox_new_int(group, _("Two pass rendering (apply HQ zoom and color correction in second pass)"),
2255 options->image.zoom_2pass, &c_options->image.zoom_2pass);
2257 c_options->image.zoom_increment = options->image.zoom_increment;
2258 spin = pref_spin_new(group, _("Zoom increment:"), nullptr,
2259 0.01, 4.0, 0.01, 2, static_cast<gdouble>(options->image.zoom_increment) / 100.0,
2260 G_CALLBACK(zoom_increment_cb), nullptr);
2261 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
2263 c_options->image.zoom_style = options->image.zoom_style;
2264 table = pref_table_new(group, 2, 1, FALSE, FALSE);
2265 add_zoom_style_selection_menu(table, 0, 0, _("Zoom style:"), options->image.zoom_style, &c_options->image.zoom_style);
2267 group = pref_group_new(vbox, FALSE, _("Fit image to window"), GTK_ORIENTATION_VERTICAL);
2269 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2270 enlargement_button = pref_checkbox_new_int(hbox, _("Allow enlargement of image (max. size in %)"),
2271 options->image.zoom_to_fit_allow_expand, &c_options->image.zoom_to_fit_allow_expand);
2272 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2274 options->image.max_enlargement_size, &c_options->image.max_enlargement_size);
2275 pref_checkbox_link_sensitivity(enlargement_button, spin);
2276 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."));
2278 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2279 ct_button = pref_checkbox_new_int(hbox, _("Virtual window size (% of actual window):"),
2280 options->image.limit_autofit_size, &c_options->image.limit_autofit_size);
2281 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2283 options->image.max_autofit_size, &c_options->image.max_autofit_size);
2284 pref_checkbox_link_sensitivity(ct_button, spin);
2285 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."));
2287 group = pref_group_new(vbox, FALSE, _("Tile size"), GTK_ORIENTATION_VERTICAL);
2289 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2290 spin = pref_spin_new_int(hbox, _("Pixels"), _("(Requires restart)"),
2292 options->image.tile_size, &c_options->image.tile_size);
2293 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."));
2295 group = pref_group_new(vbox, FALSE, _("Appearance"), GTK_ORIENTATION_VERTICAL);
2297 pref_checkbox_new_int(group, _("Use custom border color in window mode"),
2298 options->image.use_custom_border_color, &c_options->image.use_custom_border_color);
2300 pref_checkbox_new_int(group, _("Use custom border color in fullscreen mode"),
2301 options->image.use_custom_border_color_in_fullscreen, &c_options->image.use_custom_border_color_in_fullscreen);
2303 pref_color_button_new(group, _("Border color"), &options->image.border_color,
2304 G_CALLBACK(pref_color_button_set_cb), &c_options->image.border_color);
2306 c_options->image.border_color = options->image.border_color;
2308 pref_color_button_new(group, _("Alpha channel color 1"), &options->image.alpha_color_1,
2309 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_1);
2311 pref_color_button_new(group, _("Alpha channel color 2"), &options->image.alpha_color_2,
2312 G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_2);
2314 c_options->image.alpha_color_1 = options->image.alpha_color_1;
2315 c_options->image.alpha_color_2 = options->image.alpha_color_2;
2320 static void save_default_window_layout_cb(GtkWidget *UNUSED(widget), gpointer UNUSED(data))
2322 LayoutWindow *lw = nullptr;
2323 gchar *default_path;
2326 /* Get current lw */
2329 tmp_id = lw->options.id;
2330 lw->options.id = g_strdup("lw_default");
2332 default_path = g_build_filename(get_rc_dir(), DEFAULT_WINDOW_LAYOUT, NULL);
2333 save_default_layout_options_to_file(default_path, options, lw);
2334 g_free(lw->options.id);
2335 lw->options.id = tmp_id;
2336 g_free(default_path);
2339 static gboolean popover_cb(gpointer data)
2341 auto popover = static_cast<GtkPopover *>(data);
2343 gtk_popover_popdown(popover);
2348 static void default_layout_changed_cb(GtkWidget *UNUSED(button), GtkPopover *popover)
2350 gtk_popover_popup(popover);
2352 g_timeout_add(2000, popover_cb, popover);
2355 static GtkWidget *create_popover(GtkWidget *parent, GtkWidget *child, GtkPositionType pos)
2359 popover = gtk_popover_new(parent);
2360 gtk_popover_set_position(GTK_POPOVER (popover), pos);
2361 gtk_container_add (GTK_CONTAINER(popover), child);
2362 gtk_container_set_border_width(GTK_CONTAINER (popover), 6);
2363 gtk_widget_show (child);
2368 static void config_tab_windows(GtkWidget *notebook)
2373 GtkWidget *subgroup;
2375 GtkWidget *ct_button;
2378 vbox = scrolled_notebook_page(notebook, _("Windows"));
2380 group = pref_group_new(vbox, FALSE, _("State"), GTK_ORIENTATION_VERTICAL);
2382 ct_button = pref_checkbox_new_int(group, _("Remember session"),
2383 options->save_window_positions, &c_options->save_window_positions);
2385 button = pref_checkbox_new_int(group, _("Use saved window positions also for new windows"),
2386 options->use_saved_window_positions_for_new_windows, &c_options->use_saved_window_positions_for_new_windows);
2387 pref_checkbox_link_sensitivity(ct_button, button);
2389 button = pref_checkbox_new_int(group, _("Remember window workspace"),
2390 options->save_window_workspace, &c_options->save_window_workspace);
2391 pref_checkbox_link_sensitivity(ct_button, button);
2393 pref_checkbox_new_int(group, _("Remember tool state (float/hidden)"),
2394 options->tools_restore_state, &c_options->tools_restore_state);
2396 pref_checkbox_new_int(group, _("Remember dialog window positions"),
2397 options->save_dialog_window_positions, &c_options->save_dialog_window_positions);
2399 pref_checkbox_new_int(group, _("Show window IDs"),
2400 options->show_window_ids, &c_options->show_window_ids);
2402 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2403 pref_label_new(subgroup, _("Use current layout for default: "));
2404 button = pref_button_new(subgroup, nullptr, _("Set"), FALSE, G_CALLBACK(save_default_window_layout_cb), nullptr);
2408 popover = create_popover(button, gtk_label_new(_("Current window layout\nhas been set as default")), GTK_POS_TOP);
2409 gtk_popover_set_modal(GTK_POPOVER (popover), FALSE);
2410 g_signal_connect(button, "clicked", G_CALLBACK(default_layout_changed_cb), popover);
2412 group = pref_group_new(vbox, FALSE, _("Size"), GTK_ORIENTATION_VERTICAL);
2414 pref_checkbox_new_int(group, _("Fit window to image when tools are hidden/floating"),
2415 options->image.fit_window_to_image, &c_options->image.fit_window_to_image);
2417 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2418 ct_button = pref_checkbox_new_int(hbox, _("Limit size when auto-sizing window (%):"),
2419 options->image.limit_window_size, &c_options->image.limit_window_size);
2420 spin = pref_spin_new_int(hbox, nullptr, nullptr,
2422 options->image.max_window_size, &c_options->image.max_window_size);
2423 pref_checkbox_link_sensitivity(ct_button, spin);
2425 group = pref_group_new(vbox, FALSE, _("Full screen"), GTK_ORIENTATION_VERTICAL);
2427 c_options->fullscreen.screen = options->fullscreen.screen;
2428 c_options->fullscreen.above = options->fullscreen.above;
2429 hbox = fullscreen_prefs_selection_new(_("Location:"), &c_options->fullscreen.screen, &c_options->fullscreen.above);
2430 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2431 gtk_widget_show(hbox);
2433 pref_checkbox_new_int(group, _("Smooth image flip"),
2434 options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip);
2435 pref_checkbox_new_int(group, _("Disable screen saver"),
2436 options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver);
2439 #define PRE_FORMATTED_COLUMNS 5
2440 static void config_tab_osd(GtkWidget *notebook)
2446 GtkWidget *image_overlay_template_view;
2447 GtkWidget *scrolled;
2448 GtkWidget *scrolled_pre_formatted;
2449 GtkTextBuffer *buffer;
2451 GtkWidget *subgroup;
2453 vbox = scrolled_notebook_page(notebook, _("OSD"));
2455 image_overlay_template_view = gtk_text_view_new();
2457 group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL);
2459 subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2461 scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_overlay_template_view);
2462 gtk_widget_set_size_request(scrolled_pre_formatted, 200, 150);
2463 gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
2464 gtk_widget_show(scrolled_pre_formatted);
2465 gtk_widget_show(subgroup);
2467 pref_line(group, PREF_PAD_GAP);
2469 pref_label_new(group, _("Image overlay template"));
2471 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
2472 gtk_widget_set_size_request(scrolled, 200, 150);
2473 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2474 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
2475 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2476 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5);
2477 gtk_widget_show(scrolled);
2479 gtk_widget_set_tooltip_markup(image_overlay_template_view,
2480 _("Extensive formatting options are shown in the Help file"));
2482 gtk_container_add(GTK_CONTAINER(scrolled), image_overlay_template_view);
2483 gtk_widget_show(image_overlay_template_view);
2485 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2487 button = pref_button_new(nullptr, GTK_STOCK_SELECT_FONT, _("Font"), FALSE,
2488 G_CALLBACK(image_overlay_set_font_cb), notebook);
2490 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2491 gtk_widget_show(button);
2493 button = pref_button_new(nullptr, GTK_STOCK_COLOR_PICKER, _("Text"), FALSE,
2494 G_CALLBACK(image_overlay_set_text_colour_cb), nullptr);
2495 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2496 gtk_widget_show(button);
2498 button = pref_button_new(nullptr, GTK_STOCK_COLOR_PICKER, _("Background"), FALSE,
2499 G_CALLBACK(image_overlay_set_background_colour_cb), nullptr);
2500 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2501 gtk_widget_show(button);
2502 image_overlay_set_text_colours();
2504 button = pref_button_new(nullptr, nullptr, _("Defaults"), FALSE,
2505 G_CALLBACK(image_overlay_default_template_cb), image_overlay_template_view);
2506 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2507 gtk_widget_show(button);
2509 button = pref_button_new(nullptr, GTK_STOCK_HELP, nullptr, FALSE,
2510 G_CALLBACK(image_overlay_help_cb), nullptr);
2511 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2512 gtk_widget_show(button);
2514 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_overlay_template_view));
2515 if (options->image_overlay.template_string) gtk_text_buffer_set_text(buffer, options->image_overlay.template_string, -1);
2516 g_signal_connect(G_OBJECT(buffer), "changed",
2517 G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view);
2519 pref_line(group, PREF_PAD_GAP);
2521 group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL);
2522 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2523 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2524 gtk_widget_show(hbox);
2525 label = gtk_label_new(_("%Exif.Image.Orientation%"));
2526 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2527 gtk_widget_show(label);
2528 pref_spacer(group,TRUE);
2530 group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL);
2531 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2532 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2533 gtk_widget_show(hbox);
2534 label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%"));
2535 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2536 gtk_widget_show(label);
2537 pref_spacer(group,TRUE);
2539 group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL);
2540 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2541 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2542 gtk_widget_show(hbox);
2543 label = gtk_label_new(_("%path:39%"));
2544 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2545 gtk_widget_show(label);
2546 pref_spacer(group,TRUE);
2548 group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL);
2549 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2550 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2551 gtk_widget_show(hbox);
2552 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%"));
2553 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2554 gtk_widget_show(label);
2555 pref_spacer(group,TRUE);
2557 group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL);
2558 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2559 gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0);
2560 gtk_widget_show(hbox);
2561 label = gtk_label_new(_("<b>bold</b>\n<u>underline</u>\n<i>italic</i>\n<s>strikethrough</s>"));
2562 gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0);
2563 gtk_widget_show(label);
2566 static GtkTreeModel *create_class_model()
2568 GtkListStore *model;
2572 /* create list store */
2573 model = gtk_list_store_new(1, G_TYPE_STRING);
2574 for (i = 0; i < FILE_FORMAT_CLASSES; i++)
2576 gtk_list_store_append(model, &iter);
2577 gtk_list_store_set(model, &iter, 0, _(format_class_list[i]), -1);
2579 return GTK_TREE_MODEL (model);
2584 static gint filter_table_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
2586 gint n = GPOINTER_TO_INT(data);
2588 FilterEntry *filter_a;
2589 FilterEntry *filter_b;
2591 gtk_tree_model_get(model, a, 0, &filter_a, -1);
2592 gtk_tree_model_get(model, b, 0, &filter_b, -1);
2596 case FILETYPES_COLUMN_ENABLED:
2598 ret = filter_a->enabled - filter_b->enabled;
2601 case FILETYPES_COLUMN_FILTER:
2603 ret = g_utf8_collate(filter_a->extensions, filter_b->extensions);
2606 case FILETYPES_COLUMN_DESCRIPTION:
2608 ret = g_utf8_collate(filter_a->description, filter_b->description);
2611 case FILETYPES_COLUMN_CLASS:
2613 ret = g_strcmp0(format_class_list[filter_a->file_class], format_class_list[filter_b->file_class]);
2616 case FILETYPES_COLUMN_WRITABLE:
2618 ret = filter_a->writable - filter_b->writable;
2621 case FILETYPES_COLUMN_SIDECAR:
2623 ret = filter_a->allow_sidecar - filter_b->allow_sidecar;
2627 g_return_val_if_reached(0);
2633 static gboolean search_function_cb(GtkTreeModel *model, gint UNUSED(column), const gchar *key, GtkTreeIter *iter, gpointer UNUSED(search_data))
2636 gboolean ret = TRUE;
2638 gtk_tree_model_get(model, iter, 0, &fe, -1);
2640 if (g_strstr_len(fe->extensions, -1, key))
2648 static void config_tab_files(GtkWidget *notebook)
2655 GtkWidget *ct_button;
2656 GtkWidget *scrolled;
2657 GtkWidget *filter_view;
2658 GtkCellRenderer *renderer;
2659 GtkTreeSelection *selection;
2660 GtkTreeViewColumn *column;
2662 vbox = scrolled_notebook_page(notebook, _("File Filters"));
2664 group = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
2666 pref_checkbox_new_int(group, _("Show hidden files or folders"),
2667 options->file_filter.show_hidden_files, &c_options->file_filter.show_hidden_files);
2668 pref_checkbox_new_int(group, _("Show parent folder (..)"),
2669 options->file_filter.show_parent_directory, &c_options->file_filter.show_parent_directory);
2670 pref_checkbox_new_int(group, _("Case sensitive sort"),
2671 options->file_sort.case_sensitive, &c_options->file_sort.case_sensitive);
2672 pref_checkbox_new_int(group, _("Natural sort order - Requires restart"),
2673 options->file_sort.natural, &c_options->file_sort.natural);
2674 pref_checkbox_new_int(group, _("Disable file extension checks"),
2675 options->file_filter.disable_file_extension_checks, &c_options->file_filter.disable_file_extension_checks);
2677 ct_button = pref_checkbox_new_int(group, _("Disable File Filtering"),
2678 options->file_filter.disable, &c_options->file_filter.disable);
2681 group = pref_group_new(vbox, FALSE, _("Grouping sidecar extensions"), GTK_ORIENTATION_VERTICAL);
2683 sidecar_ext_entry = gtk_entry_new();
2684 gtk_entry_set_text(GTK_ENTRY(sidecar_ext_entry), options->sidecar.ext);
2685 gtk_box_pack_start(GTK_BOX(group), sidecar_ext_entry, FALSE, FALSE, 0);
2686 gtk_widget_show(sidecar_ext_entry);
2688 group = pref_group_new(vbox, TRUE, _("File types"), GTK_ORIENTATION_VERTICAL);
2690 frame = pref_group_parent(group);
2691 g_signal_connect(G_OBJECT(ct_button), "toggled",
2692 G_CALLBACK(filter_disable_cb), frame);
2693 gtk_widget_set_sensitive(frame, !options->file_filter.disable);
2695 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
2696 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
2697 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2698 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
2699 gtk_widget_show(scrolled);
2701 filter_store = gtk_list_store_new(1, G_TYPE_POINTER);
2702 filter_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
2703 g_object_unref(filter_store);
2704 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_view));
2705 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_SINGLE);
2707 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), FALSE);
2709 column = gtk_tree_view_column_new();
2710 gtk_tree_view_column_set_title(column, _("Enabled"));
2711 gtk_tree_view_column_set_resizable(column, TRUE);
2713 renderer = gtk_cell_renderer_toggle_new();
2714 g_signal_connect(G_OBJECT(renderer), "toggled",
2715 G_CALLBACK(filter_store_enable_cb), filter_store);
2716 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2717 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2718 GINT_TO_POINTER(FE_ENABLE), nullptr);
2719 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);
2720 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_ENABLED);
2721 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2723 column = gtk_tree_view_column_new();
2724 gtk_tree_view_column_set_title(column, _("Filter"));
2725 gtk_tree_view_column_set_resizable(column, TRUE);
2726 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);
2727 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_FILTER);
2729 renderer = gtk_cell_renderer_text_new();
2730 g_signal_connect(G_OBJECT(renderer), "edited",
2731 G_CALLBACK(filter_store_ext_edit_cb), filter_store);
2732 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2733 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2734 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2735 GINT_TO_POINTER(FE_EXTENSION), nullptr);
2736 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2738 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(filter_view), TRUE);
2739 gtk_tree_view_set_search_column(GTK_TREE_VIEW(filter_view), FILETYPES_COLUMN_FILTER);
2740 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(filter_view), search_function_cb, nullptr, nullptr);
2742 column = gtk_tree_view_column_new();
2743 gtk_tree_view_column_set_title(column, _("Description"));
2744 gtk_tree_view_column_set_resizable(column, TRUE);
2745 gtk_tree_view_column_set_fixed_width(column, 200);
2746 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2748 renderer = gtk_cell_renderer_text_new();
2749 g_signal_connect(G_OBJECT(renderer), "edited",
2750 G_CALLBACK(filter_store_desc_edit_cb), filter_store);
2751 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE, NULL);
2752 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2753 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2754 GINT_TO_POINTER(FE_DESCRIPTION), nullptr);
2755 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2756 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);
2757 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_DESCRIPTION);
2759 column = gtk_tree_view_column_new();
2760 gtk_tree_view_column_set_title(column, _("Class"));
2761 gtk_tree_view_column_set_resizable(column, TRUE);
2762 renderer = gtk_cell_renderer_combo_new();
2763 g_object_set(G_OBJECT(renderer), "editable", static_cast<gboolean>TRUE,
2764 "model", create_class_model(),
2769 g_signal_connect(G_OBJECT(renderer), "edited",
2770 G_CALLBACK(filter_store_class_edit_cb), filter_store);
2771 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2772 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2773 GINT_TO_POINTER(FE_CLASS), nullptr);
2774 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2775 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);
2776 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_CLASS);
2778 column = gtk_tree_view_column_new();
2779 gtk_tree_view_column_set_title(column, _("Writable"));
2780 gtk_tree_view_column_set_resizable(column, FALSE);
2781 renderer = gtk_cell_renderer_toggle_new();
2782 g_signal_connect(G_OBJECT(renderer), "toggled",
2783 G_CALLBACK(filter_store_writable_cb), filter_store);
2784 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2785 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2786 GINT_TO_POINTER(FE_WRITABLE), nullptr);
2787 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2788 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);
2789 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_WRITABLE);
2791 column = gtk_tree_view_column_new();
2792 gtk_tree_view_column_set_title(column, _("Sidecar is allowed"));
2793 gtk_tree_view_column_set_resizable(column, FALSE);
2794 renderer = gtk_cell_renderer_toggle_new();
2795 g_signal_connect(G_OBJECT(renderer), "toggled",
2796 G_CALLBACK(filter_store_sidecar_cb), filter_store);
2797 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2798 gtk_tree_view_column_set_cell_data_func(column, renderer, filter_set_func,
2799 GINT_TO_POINTER(FE_ALLOW_SIDECAR), nullptr);
2800 gtk_tree_view_append_column(GTK_TREE_VIEW(filter_view), column);
2801 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);
2802 gtk_tree_view_column_set_sort_column_id(column, FILETYPES_COLUMN_SIDECAR);
2804 filter_store_populate();
2805 gtk_container_add(GTK_CONTAINER(scrolled), filter_view);
2806 gtk_widget_show(filter_view);
2808 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
2810 button = pref_button_new(nullptr, nullptr, _("Defaults"), FALSE,
2811 G_CALLBACK(filter_default_cb), filter_view);
2812 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2813 gtk_widget_show(button);
2815 button = pref_button_new(nullptr, GTK_STOCK_REMOVE, nullptr, FALSE,
2816 G_CALLBACK(filter_remove_cb), filter_view);
2817 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2818 gtk_widget_show(button);
2820 button = pref_button_new(nullptr, GTK_STOCK_ADD, nullptr, FALSE,
2821 G_CALLBACK(filter_add_cb), filter_view);
2822 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2823 gtk_widget_show(button);
2827 static void config_tab_metadata(GtkWidget *notebook)
2832 GtkWidget *ct_button;
2834 GtkWidget *tmp_widget;
2836 GtkWidget *text_label;
2838 vbox = scrolled_notebook_page(notebook, _("Metadata"));
2841 group = pref_group_new(vbox, FALSE, _("Metadata writing sequence"), GTK_ORIENTATION_VERTICAL);
2843 label = pref_label_new(group, _("Warning: Geeqie is built without Exiv2. Some options are disabled."));
2845 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."));
2846 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2847 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2849 gtk_widget_set_tooltip_text(label, _("A flowchart of the sequence is shown in the Help file"));
2851 ct_button = pref_checkbox_new_int(group, "", options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
2852 text_label = gtk_bin_get_child(GTK_BIN(ct_button));
2853 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"));
2854 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2856 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)"));
2857 gtk_widget_set_tooltip_markup(ct_button, markup);
2861 gtk_widget_set_sensitive(ct_button, FALSE);
2864 tmp_widget = pref_checkbox_new_int(group, "", options->metadata.enable_metadata_dirs, &c_options->metadata.enable_metadata_dirs);
2865 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2866 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)"));
2867 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2870 label = pref_label_new(group, "");
2871 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(), "/");
2872 gtk_label_set_markup (GTK_LABEL(label), markup);
2875 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
2876 gtk_label_set_yalign(GTK_LABEL(label), 0.5);
2877 gtk_misc_set_padding(GTK_MISC(label), 22, 0);
2878 pref_spacer(group, PREF_PAD_GROUP);
2880 group = pref_group_new(vbox, FALSE, _("Step 1 Options:"), GTK_ORIENTATION_VERTICAL);
2882 gtk_widget_set_sensitive(group, FALSE);
2885 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
2886 pref_checkbox_link_sensitivity(ct_button, hbox);
2888 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);
2889 gtk_widget_set_tooltip_text(tmp_widget, _("A simplified conversion list is in the Help file"));
2891 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);
2893 pref_checkbox_new_int(hbox, _("Ask before writing to image files"), options->metadata.confirm_write, &c_options->metadata.confirm_write);
2895 tmp_widget= pref_checkbox_new_int(hbox, "", options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
2896 gtk_widget_set_tooltip_text(tmp_widget, _("This file naming convention is used by Darktable"));
2897 text_label = gtk_bin_get_child(GTK_BIN(tmp_widget));
2898 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", ")");
2899 gtk_label_set_markup (GTK_LABEL(text_label), markup);
2902 pref_spacer(group, PREF_PAD_GROUP);
2904 group = pref_group_new(vbox, FALSE, _("Steps 2 and 3 Option:"), GTK_ORIENTATION_VERTICAL);
2906 gtk_widget_set_sensitive(group, FALSE);
2909 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);
2911 pref_spacer(group, PREF_PAD_GROUP);
2913 group = pref_group_new(vbox, FALSE, _("Miscellaneous"), GTK_ORIENTATION_VERTICAL);
2914 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);
2915 gtk_widget_set_tooltip_text(tmp_widget, _("See the Help file for a list of the tags used"));
2917 tmp_widget = pref_checkbox_new_int(group, _("Permit Keywords to be case-sensitive"), options->metadata.keywords_case_sensitive, &c_options->metadata.keywords_case_sensitive);
2918 gtk_widget_set_tooltip_text(tmp_widget, _("When selected, \"Place\" and \"place\" are two different keywords"));
2920 ct_button = pref_checkbox_new_int(group, _("Write altered image orientation to the metadata"), options->metadata.write_orientation, &c_options->metadata.write_orientation);
2921 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"));
2924 gtk_widget_set_sensitive(ct_button, FALSE);
2927 pref_spacer(group, PREF_PAD_GROUP);
2929 group = pref_group_new(vbox, FALSE, _("Auto-save options"), GTK_ORIENTATION_VERTICAL);
2931 ct_button = pref_checkbox_new_int(group, _("Write metadata after timeout"), options->metadata.confirm_after_timeout, &c_options->metadata.confirm_after_timeout);
2933 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
2934 pref_checkbox_link_sensitivity(ct_button, hbox);
2936 pref_spin_new_int(hbox, _("Timeout (seconds):"), nullptr, 0, 900, 1, options->metadata.confirm_timeout, &c_options->metadata.confirm_timeout);
2938 pref_checkbox_new_int(group, _("Write metadata on image change"), options->metadata.confirm_on_image_change, &c_options->metadata.confirm_on_image_change);
2940 pref_checkbox_new_int(group, _("Write metadata on directory change"), options->metadata.confirm_on_dir_change, &c_options->metadata.confirm_on_dir_change);
2942 pref_spacer(group, PREF_PAD_GROUP);
2945 group = pref_group_new(vbox, FALSE, _("Spelling checks"), GTK_ORIENTATION_VERTICAL);
2947 ct_button = pref_checkbox_new_int(group, _("Check spelling - Requires restart"), options->metadata.check_spelling, &c_options->metadata.check_spelling);
2948 gtk_widget_set_tooltip_text(ct_button, _("Spelling checks are performed on info sidebar panes Comment, Headline and Title"));
2951 pref_spacer(group, PREF_PAD_GROUP);
2953 group = pref_group_new(vbox, FALSE, _("Pre-load metadata"), GTK_ORIENTATION_VERTICAL);
2955 ct_button = pref_checkbox_new_int(group, _("Read metadata in background"), options->read_metadata_in_idle, &c_options->read_metadata_in_idle);
2956 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");
2961 struct KeywordFindData
2968 GtkWidget *button_close;
2969 GtkWidget *button_stop;
2970 GtkWidget *button_start;
2971 GtkWidget *progress;
2979 guint idle_id; /* event source id */
2982 #define KEYWORD_DIALOG_WIDTH 400
2984 static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd)
2986 GList *list_d = nullptr;
2987 GList *list_f = nullptr;
2991 filelist_read(dir_fd, &list_f, &list_d);
2995 filelist_read(dir_fd, &list_f, nullptr);
2998 list_f = filelist_filter(list_f, FALSE);
2999 list_d = filelist_filter(list_d, TRUE);
3001 kfd->list = g_list_concat(list_f, kfd->list);
3002 kfd->list_dir = g_list_concat(list_d, kfd->list_dir);
3005 static void keywords_find_reset(KeywordFindData *kfd)
3007 filelist_free(kfd->list);
3008 kfd->list = nullptr;
3010 filelist_free(kfd->list_dir);
3011 kfd->list_dir = nullptr;
3014 static void keywords_find_close_cb(GenericDialog *UNUSED(fd), gpointer data)
3016 auto kfd = static_cast<KeywordFindData *>(data);
3018 if (!gtk_widget_get_sensitive(kfd->button_close)) return;
3020 keywords_find_reset(kfd);
3021 generic_dialog_close(kfd->gd);
3025 static void keywords_find_finish(KeywordFindData *kfd)
3027 keywords_find_reset(kfd);
3029 gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done"));
3030 spinner_set_interval(kfd->spinner, -1);
3032 gtk_widget_set_sensitive(kfd->group, TRUE);
3033 gtk_widget_set_sensitive(kfd->button_start, TRUE);
3034 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3035 gtk_widget_set_sensitive(kfd->button_close, TRUE);
3038 static void keywords_find_stop_cb(GenericDialog *UNUSED(fd), gpointer data)
3040 auto kfd = static_cast<KeywordFindData *>(data);
3042 g_idle_remove_by_data(kfd);
3044 keywords_find_finish(kfd);
3047 static gboolean keywords_find_file(gpointer data)
3049 auto kfd = static_cast<KeywordFindData *>(data);
3051 GtkTextBuffer *buffer;
3059 fd = static_cast<FileData *>(kfd->list->data);
3060 kfd->list = g_list_remove(kfd->list, fd);
3062 keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
3063 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3067 gtk_text_buffer_get_end_iter(buffer, &iter);
3068 tmp = g_strconcat(static_cast<const gchar *>(keywords->data), "\n", NULL);
3069 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3071 keywords = keywords->next;
3074 gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path);
3075 file_data_unref(fd);
3076 g_list_free_full(keywords, g_free);
3078 return (G_SOURCE_CONTINUE);
3080 else if (kfd->list_dir)
3084 fd = static_cast<FileData *>(kfd->list_dir->data);
3085 kfd->list_dir = g_list_remove(kfd->list_dir, fd);
3087 keywords_find_folder(kfd, fd);
3089 file_data_unref(fd);
3091 return G_SOURCE_CONTINUE;
3094 keywords_find_finish(kfd);
3096 return G_SOURCE_REMOVE;
3099 static void keywords_find_start_cb(GenericDialog *UNUSED(fd), gpointer data)
3101 auto kfd = static_cast<KeywordFindData *>(data);
3104 if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return;
3106 path = remove_trailing_slash((gtk_entry_get_text(GTK_ENTRY(kfd->entry))));
3107 parse_out_relatives(path);
3111 warning_dialog(_("Invalid folder"),
3112 _("The specified folder can not be found."),
3113 GTK_STOCK_DIALOG_WARNING, kfd->gd->dialog);
3119 gtk_widget_set_sensitive(kfd->group, FALSE);
3120 gtk_widget_set_sensitive(kfd->button_start, FALSE);
3121 gtk_widget_set_sensitive(kfd->button_stop, TRUE);
3122 gtk_widget_set_sensitive(kfd->button_close, FALSE);
3123 spinner_set_interval(kfd->spinner, SPINNER_SPEED);
3125 dir_fd = file_data_new_dir(path);
3126 keywords_find_folder(kfd, dir_fd);
3127 file_data_unref(dir_fd);
3128 kfd->idle_id = g_idle_add(keywords_find_file, kfd);
3134 static void keywords_find_dialog(GtkWidget *widget, const gchar *path)
3136 KeywordFindData *kfd;
3140 kfd = g_new0(KeywordFindData, 1);
3142 kfd->gd = generic_dialog_new(_("Search for keywords"),
3143 "search_for_keywords",
3146 gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1);
3147 kfd->gd->cancel_cb = keywords_find_close_cb;
3148 kfd->button_close = generic_dialog_add_button(kfd->gd, GTK_STOCK_CLOSE, nullptr,
3149 keywords_find_close_cb, FALSE);
3150 kfd->button_start = generic_dialog_add_button(kfd->gd, GTK_STOCK_OK, _("S_tart"),
3151 keywords_find_start_cb, FALSE);
3152 kfd->button_stop = generic_dialog_add_button(kfd->gd, GTK_STOCK_STOP, nullptr,
3153 keywords_find_stop_cb, FALSE);
3154 gtk_widget_set_sensitive(kfd->button_stop, FALSE);
3156 generic_dialog_add_message(kfd->gd, nullptr, _("Search for keywords"), nullptr, FALSE);
3158 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0);
3159 pref_spacer(hbox, PREF_PAD_INDENT);
3160 kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
3162 hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3163 pref_label_new(hbox, _("Folder:"));
3165 label = tab_completion_new(&kfd->entry, path, nullptr, nullptr, nullptr, nullptr);
3166 tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE);
3167 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3168 gtk_widget_show(label);
3170 pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse);
3172 pref_line(kfd->gd->vbox, PREF_PAD_SPACE);
3173 hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3175 kfd->progress = gtk_entry_new();
3176 gtk_widget_set_can_focus(kfd->progress, FALSE);
3177 gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE);
3178 gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin"));
3179 gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0);
3180 gtk_widget_show(kfd->progress);
3182 kfd->spinner = spinner_new(nullptr, -1);
3183 gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0);
3184 gtk_widget_show(kfd->spinner);
3186 kfd->list = nullptr;
3188 gtk_widget_show(kfd->gd->dialog);
3191 static void keywords_find_cb(GtkWidget *widget, gpointer UNUSED(data))
3193 const gchar *path = layout_get_path(nullptr);
3195 if (!path || !*path) path = homedir();
3196 keywords_find_dialog(widget, path);
3199 static void config_tab_keywords_save()
3201 GtkTextIter start, end;
3202 GtkTextBuffer *buffer;
3203 GList *kw_list = nullptr;
3209 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3210 gtk_text_buffer_get_bounds(buffer, &start, &end);
3212 buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3214 kw_split = strtok(buffer_text, "\n");
3215 while (kw_split != nullptr)
3221 if (g_strcmp0(static_cast<const gchar *>(work->data), kw_split) == 0)
3230 kw_list = g_list_append(kw_list, g_strdup(kw_split));
3232 kw_split = strtok(nullptr, "\n");
3235 keyword_list_set(kw_list);
3237 g_list_free_full(kw_list, g_free);
3238 g_free(buffer_text);
3241 static void config_tab_keywords(GtkWidget *notebook)
3247 GtkWidget *scrolled;
3249 GtkTextBuffer *buffer;
3252 GspellTextView *gspell_view;
3255 vbox = scrolled_notebook_page(notebook, _("Keywords"));
3257 group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL);
3259 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3261 button = pref_button_new(hbox, GTK_STOCK_EXECUTE, _("Search"), FALSE,
3262 G_CALLBACK(keywords_find_cb), keyword_text);
3263 gtk_widget_set_tooltip_text(button, "Search for existing keywords");
3266 keyword_text = gtk_text_view_new();
3267 gtk_widget_set_size_request(keyword_text, 20, 20);
3268 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
3269 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3270 gtk_widget_show(scrolled);
3273 if (options->metadata.check_spelling)
3275 gspell_view = gspell_text_view_get_from_gtk_text_view(GTK_TEXT_VIEW(keyword_text));
3276 gspell_text_view_basic_setup(gspell_view);
3280 gtk_container_add(GTK_CONTAINER(scrolled), keyword_text);
3281 gtk_widget_show(keyword_text);
3283 gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE);
3285 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text));
3286 gtk_text_buffer_create_tag(buffer, "monospace",
3287 "family", "monospace", NULL);
3289 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD);
3290 gtk_text_buffer_get_start_iter(buffer, &iter);
3291 gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
3294 path = g_build_filename(get_rc_dir(), "keywords", NULL);
3296 GList *kwl = keyword_list_get();
3297 kwl = g_list_first(kwl);
3300 gtk_text_buffer_get_end_iter (buffer, &iter);
3301 tmp = g_strconcat(static_cast<const gchar *>(kwl->data), "\n", NULL);
3302 gtk_text_buffer_insert(buffer, &iter, tmp, -1);
3307 gtk_text_buffer_set_modified(buffer, FALSE);
3314 static void intent_menu_cb(GtkWidget *combo, gpointer data)
3316 auto option = static_cast<gint *>(data);
3318 switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
3322 *option = INTENT_PERCEPTUAL;
3325 *option = INTENT_RELATIVE_COLORIMETRIC;
3328 *option = INTENT_SATURATION;
3331 *option = INTENT_ABSOLUTE_COLORIMETRIC;
3336 static void add_intent_menu(GtkWidget *table, gint column, gint row, const gchar *text,
3337 gint option, gint *option_c)
3344 pref_table_label(table, column, row, text, 0.0);
3346 combo = gtk_combo_box_text_new();
3348 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Perceptual"));
3349 if (option == INTENT_PERCEPTUAL) current = 0;
3350 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Relative Colorimetric"));
3351 if (option == INTENT_RELATIVE_COLORIMETRIC) current = 1;
3352 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Saturation"));
3353 if (option == INTENT_SATURATION) current = 2;
3354 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _("Absolute Colorimetric"));
3355 if (option == INTENT_ABSOLUTE_COLORIMETRIC) current = 3;
3357 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current);
3359 gtk_widget_set_tooltip_text(combo,"Refer to the lcms documentation for the defaults used when the selected Intent is not available");
3361 g_signal_connect(G_OBJECT(combo), "changed",
3362 G_CALLBACK(intent_menu_cb), option_c);
3364 gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, GTK_SHRINK, static_cast<GtkAttachOptions>(0), 0, 0);
3365 gtk_widget_show(combo);
3369 static void config_tab_color(GtkWidget *notebook)
3378 vbox = scrolled_notebook_page(notebook, _("Color management"));
3380 group = pref_group_new(vbox, FALSE, _("Input profiles"), GTK_ORIENTATION_VERTICAL);
3382 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3385 table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 1, FALSE, FALSE);
3386 gtk_table_set_col_spacings(GTK_TABLE(table), PREF_PAD_GAP);
3388 label = pref_table_label(table, 0, 0, _("Type"), 0.0);
3389 pref_label_bold(label, TRUE, FALSE);
3391 label = pref_table_label(table, 1, 0, _("Menu name"), 0.0);
3392 pref_label_bold(label, TRUE, FALSE);
3394 label = pref_table_label(table, 2, 0, _("File"), 0.0);
3395 pref_label_bold(label, TRUE, FALSE);
3397 for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
3402 buf = g_strdup_printf(_("Input %d:"), i + COLOR_PROFILE_FILE);
3403 pref_table_label(table, 0, i + 1, buf, 1.0);
3406 entry = gtk_entry_new();
3407 gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
3408 if (options->color_profile.input_name[i])
3410 gtk_entry_set_text(GTK_ENTRY(entry), options->color_profile.input_name[i]);
3412 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, i + 1, i + 2,
3413 static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3414 gtk_widget_show(entry);
3415 color_profile_input_name_entry[i] = entry;
3417 tabcomp = tab_completion_new(&entry, options->color_profile.input_file[i], nullptr, ".icc", "ICC Files", nullptr);
3418 tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
3419 gtk_widget_set_size_request(entry, 160, -1);
3420 gtk_table_attach(GTK_TABLE(table), tabcomp, 2, 3, i + 1, i + 2,
3421 static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3422 gtk_widget_show(tabcomp);
3423 color_profile_input_file_entry[i] = entry;
3426 group = pref_group_new(vbox, FALSE, _("Screen profile"), GTK_ORIENTATION_VERTICAL);
3428 gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
3430 pref_checkbox_new_int(group, _("Use system screen profile if available"),
3431 options->color_profile.use_x11_screen_profile, &c_options->color_profile.use_x11_screen_profile);
3433 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3435 pref_table_label(table, 0, 0, _("Screen:"), 1.0);
3436 tabcomp = tab_completion_new(&color_profile_screen_file_entry,
3437 options->color_profile.screen_file, nullptr, ".icc", "ICC Files", nullptr);
3438 tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
3439 gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
3441 add_intent_menu(table, 0, 1, _("Render Intent:"), options->color_profile.render_intent, &c_options->color_profile.render_intent);
3443 gtk_table_attach(GTK_TABLE(table), tabcomp, 1, 2,
3445 static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND), static_cast<GtkAttachOptions>(0), 0, 0);
3447 gtk_widget_show(tabcomp);
3450 /* advanced entry tab */
3451 static void use_geeqie_trash_cb(GtkWidget *widget, gpointer UNUSED(data))
3453 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3455 c_options->file_ops.use_system_trash = FALSE;
3456 c_options->file_ops.no_trash = FALSE;
3460 static void use_system_trash_cb(GtkWidget *widget, gpointer UNUSED(data))
3462 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3464 c_options->file_ops.use_system_trash = TRUE;
3465 c_options->file_ops.no_trash = FALSE;
3469 static void use_no_cache_cb(GtkWidget *widget, gpointer UNUSED(data))
3471 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
3473 c_options->file_ops.no_trash = TRUE;
3477 static void config_tab_behavior(GtkWidget *notebook)
3484 GtkWidget *ct_button;
3488 GtkWidget *with_rename;
3489 GtkWidget *collections_on_top;
3490 GtkWidget *hide_window_in_fullscreen;
3491 GtkWidget *checkbox;
3494 vbox = scrolled_notebook_page(notebook, _("Behavior"));
3496 group = pref_group_new(vbox, FALSE, _("Delete"), GTK_ORIENTATION_VERTICAL);
3498 pref_checkbox_new_int(group, _("Confirm permanent file delete"),
3499 options->file_ops.confirm_delete, &c_options->file_ops.confirm_delete);
3500 pref_checkbox_new_int(group, _("Confirm move file to Trash"),
3501 options->file_ops.confirm_move_to_trash, &c_options->file_ops.confirm_move_to_trash);
3502 pref_checkbox_new_int(group, _("Enable Delete key"),
3503 options->file_ops.enable_delete_key, &c_options->file_ops.enable_delete_key);
3505 ct_button = pref_radiobutton_new(group, nullptr, _("Use Geeqie trash location"),
3506 !options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_geeqie_trash_cb),nullptr);
3508 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
3509 pref_checkbox_link_sensitivity(ct_button, hbox);
3511 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_SPACE);
3512 pref_label_new(hbox, _("Folder:"));
3514 tabcomp = tab_completion_new(&safe_delete_path_entry, options->file_ops.safe_delete_path, nullptr, nullptr, nullptr, nullptr);
3515 tab_completion_add_select_button(safe_delete_path_entry, nullptr, TRUE);
3516 gtk_box_pack_start(GTK_BOX(hbox), tabcomp, TRUE, TRUE, 0);
3517 gtk_widget_show(tabcomp);
3519 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3520 pref_checkbox_link_sensitivity(ct_button, hbox);
3522 pref_spacer(hbox, PREF_PAD_INDENT - PREF_PAD_GAP);
3523 spin = pref_spin_new_int(hbox, _("Maximum size:"), _("MiB"),
3524 0, 2048, 1, options->file_ops.safe_delete_folder_maxsize, &c_options->file_ops.safe_delete_folder_maxsize);
3525 gtk_widget_set_tooltip_markup(spin, _("Set to 0 for unlimited size"));
3526 button = pref_button_new(nullptr, nullptr, _("View"), FALSE,
3527 G_CALLBACK(safe_delete_view_cb), nullptr);
3528 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3529 gtk_widget_show(button);
3531 button = pref_button_new(nullptr, GTK_STOCK_CLEAR, nullptr, FALSE,
3532 G_CALLBACK(safe_delete_clear_cb), nullptr);
3533 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3535 c_options->file_ops.no_trash = options->file_ops.no_trash;
3536 c_options->file_ops.use_system_trash = options->file_ops.use_system_trash;
3538 pref_radiobutton_new(group, ct_button, _("Use system Trash bin"),
3539 options->file_ops.use_system_trash && !options->file_ops.no_trash, G_CALLBACK(use_system_trash_cb), nullptr);
3541 pref_radiobutton_new(group, ct_button, _("Use no trash at all"),
3542 options->file_ops.no_trash, G_CALLBACK(use_no_cache_cb), nullptr);
3544 gtk_widget_show(button);
3546 pref_spacer(group, PREF_PAD_GROUP);
3549 group = pref_group_new(vbox, FALSE, _("Behavior"), GTK_ORIENTATION_VERTICAL);
3551 pref_checkbox_new_int(group, _("Descend folders in tree view"),
3552 options->tree_descend_subdirs, &c_options->tree_descend_subdirs);
3554 pref_checkbox_new_int(group, _("In place renaming"),
3555 options->file_ops.enable_in_place_rename, &c_options->file_ops.enable_in_place_rename);
3557 pref_checkbox_new_int(group, _("List directory view uses single click to enter"),
3558 options->view_dir_list_single_click_enter, &c_options->view_dir_list_single_click_enter);
3560 tmp = pref_checkbox_new_int(group, _("Circular selection lists"),
3561 options->circular_selection_lists, &c_options->circular_selection_lists);
3562 gtk_widget_set_tooltip_text(tmp, _("Traverse selection lists in a circular manner"));
3564 marks = pref_checkbox_new_int(group, _("Save marks on exit"),
3565 options->marks_save, &c_options->marks_save);
3566 gtk_widget_set_tooltip_text(marks,"Note that marks linked to a keyword will be saved irrespective of this setting");
3568 with_rename = pref_checkbox_new_int(group, _("Use \"With Rename\" as default for Copy/Move dialogs"),
3569 options->with_rename, &c_options->with_rename);
3570 gtk_widget_set_tooltip_text(with_rename,"Change the default button for Copy/Move dialogs");
3572 collections_on_top = pref_checkbox_new_int(group, _("Open collections on top"),
3573 options->collections_on_top, &c_options->collections_on_top);
3574 gtk_widget_set_tooltip_text(collections_on_top,"Open collections window on top");
3576 hide_window_in_fullscreen = pref_checkbox_new_int(group, _("Hide window in fullscreen"),
3577 options->hide_window_in_fullscreen, &c_options->hide_window_in_fullscreen);
3578 gtk_widget_set_tooltip_text(hide_window_in_fullscreen,"When alt-tabbing, prevent Geeqie window showing twice");
3580 pref_spin_new_int(group, _("Recent folder list maximum size"), nullptr,
3581 1, 50, 1, options->open_recent_list_maxsize, &c_options->open_recent_list_maxsize);
3583 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);
3584 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."));
3586 pref_spin_new_int(group, _("Drag'n drop icon size"), nullptr,
3587 16, 256, 16, options->dnd_icon_size, &c_options->dnd_icon_size);
3589 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3590 add_dnd_default_action_selection_menu(table, 0, 0, _("Drag`n drop default action:"), options->dnd_default_action, &c_options->dnd_default_action);
3592 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3593 add_clipboard_selection_menu(table, 0, 0, _("Copy path clipboard selection:"), options->clipboard_selection, &c_options->clipboard_selection);
3595 pref_spacer(group, PREF_PAD_GROUP);
3597 group = pref_group_new(vbox, FALSE, _("Navigation"), GTK_ORIENTATION_VERTICAL);
3599 pref_checkbox_new_int(group, _("Progressive keyboard scrolling"),
3600 options->progressive_key_scrolling, &c_options->progressive_key_scrolling);
3601 pref_spin_new_int(group, _("Keyboard scrolling step multiplier:"), nullptr,
3602 1, 32, 1, options->keyboard_scroll_step, reinterpret_cast<int *>(&c_options->keyboard_scroll_step));
3603 pref_checkbox_new_int(group, _("Mouse wheel scrolls image"),
3604 options->mousewheel_scrolls, &c_options->mousewheel_scrolls);
3605 pref_checkbox_new_int(group, _("Navigation by left or middle click on image"),
3606 options->image_lm_click_nav, &c_options->image_lm_click_nav);
3607 pref_checkbox_new_int(group, _("Open archive by left click on image"),
3608 options->image_l_click_archive, &c_options->image_l_click_archive);
3609 pref_checkbox_new_int(group, _("Play video by left click on image"),
3610 options->image_l_click_video, &c_options->image_l_click_video);
3611 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3612 add_video_menu(table, 0, 0, _("Play with:"), options->image_l_click_video_editor, &c_options->image_l_click_video_editor);
3614 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3615 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3616 add_mouse_selection_menu(table, 0, 0, _("Mouse button Back:"), options->mouse_button_8, &c_options->mouse_button_8);
3617 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3618 add_mouse_selection_menu(table, 0, 0, _("Mouse button Forward:"), options->mouse_button_9, &c_options->mouse_button_9);
3620 pref_spacer(group, PREF_PAD_GROUP);
3622 group = pref_group_new(vbox, FALSE, _("GPU"), GTK_ORIENTATION_VERTICAL);
3624 checkbox = pref_checkbox_new_int(group, _("Override disable GPU"),
3625 options->override_disable_gpu, &c_options->override_disable_gpu);
3626 gtk_widget_set_tooltip_text(checkbox, "Contact the developers for usage");
3629 pref_spacer(group, PREF_PAD_GROUP);
3631 group = pref_group_new(vbox, FALSE, _("Debugging"), GTK_ORIENTATION_VERTICAL);
3633 pref_spin_new_int(group, _("Debug level:"), nullptr,
3634 DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
3636 pref_checkbox_new_int(group, _("Timer data"),
3637 options->log_window.timer_data, &c_options->log_window.timer_data);
3639 pref_spin_new_int(group, _("Log Window max. lines:"), nullptr,
3640 1, 99999, 1, options->log_window_lines, &options->log_window_lines);
3644 /* accelerators tab */
3646 static gboolean accel_search_function_cb(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer UNUSED(data))
3648 gboolean ret = TRUE;
3653 gtk_tree_model_get(model, iter, column, &text, -1);
3654 text_nocase = g_utf8_casefold(text, -1);
3655 key_nocase = g_utf8_casefold(key, -1);
3657 if (g_strstr_len(text_nocase, -1, key_nocase))
3664 g_free(text_nocase);
3669 static void accel_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *UNUSED(path), GtkTreeViewColumn *column, gpointer UNUSED(user_data))
3674 list = gtk_tree_view_get_columns(tree_view);
3675 col_num = g_list_index(list, column);
3679 gtk_tree_view_set_search_column(tree_view, col_num);
3682 static void config_tab_accelerators(GtkWidget *notebook)
3688 GtkWidget *scrolled;
3689 GtkWidget *accel_view;
3690 GtkCellRenderer *renderer;
3691 GtkTreeSelection *selection;
3692 GtkTreeViewColumn *column;
3694 vbox = scrolled_notebook_page(notebook, _("Keyboard"));
3696 group = pref_group_new(vbox, TRUE, _("Accelerators"), GTK_ORIENTATION_VERTICAL);
3698 scrolled = gtk_scrolled_window_new(nullptr, nullptr);
3699 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
3700 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3701 gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0);
3702 gtk_widget_show(scrolled);
3704 accel_store = gtk_tree_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
3706 accel_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(accel_store));
3707 g_object_unref(accel_store);
3708 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(accel_view));
3709 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
3711 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), FALSE);
3713 renderer = gtk_cell_renderer_text_new();
3715 column = gtk_tree_view_column_new_with_attributes(_("Action"),
3720 gtk_tree_view_column_set_sort_column_id(column, AE_ACTION);
3721 gtk_tree_view_column_set_resizable(column, TRUE);
3722 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3725 renderer = gtk_cell_renderer_accel_new();
3726 g_signal_connect(G_OBJECT(renderer), "accel-cleared",
3727 G_CALLBACK(accel_store_cleared_cb), accel_store);
3728 g_signal_connect(G_OBJECT(renderer), "accel-edited",
3729 G_CALLBACK(accel_store_edited_cb), accel_store);
3732 g_object_set (renderer,
3734 "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
3737 column = gtk_tree_view_column_new_with_attributes(_("KEY"),
3742 gtk_tree_view_column_set_sort_column_id(column, AE_KEY);
3743 gtk_tree_view_column_set_resizable(column, TRUE);
3744 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3746 renderer = gtk_cell_renderer_text_new();
3748 column = gtk_tree_view_column_new_with_attributes(_("Tooltip"),
3753 gtk_tree_view_column_set_sort_column_id(column, AE_TOOLTIP);
3754 gtk_tree_view_column_set_resizable(column, TRUE);
3755 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3757 renderer = gtk_cell_renderer_text_new();
3759 column = gtk_tree_view_column_new_with_attributes("Accel",
3764 gtk_tree_view_column_set_sort_column_id(column, AE_ACCEL);
3765 gtk_tree_view_column_set_resizable(column, TRUE);
3766 gtk_tree_view_append_column(GTK_TREE_VIEW(accel_view), column);
3768 /* Search on text in column */
3769 gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(accel_view), TRUE);
3770 g_signal_connect(accel_view, "row_activated", G_CALLBACK(accel_row_activated_cb), accel_store);
3771 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(accel_view), TRUE);
3772 gtk_tree_view_set_search_column(GTK_TREE_VIEW(accel_view), AE_TOOLTIP);
3773 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(accel_view), accel_search_function_cb, nullptr, nullptr);
3775 accel_store_populate();
3776 gtk_container_add(GTK_CONTAINER(scrolled), accel_view);
3777 gtk_widget_show(accel_view);
3779 hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
3781 button = pref_button_new(nullptr, nullptr, _("Defaults"), FALSE,
3782 G_CALLBACK(accel_default_cb), accel_view);
3783 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3784 gtk_widget_show(button);
3786 button = pref_button_new(nullptr, nullptr, _("Reset selected"), FALSE,
3787 G_CALLBACK(accel_reset_cb), accel_view);
3788 gtk_widget_set_tooltip_text(button, _("Will only reset changes made before the settings are saved"));
3789 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3790 gtk_widget_show(button);
3792 button = pref_button_new(nullptr, nullptr, _("Clear selected"), FALSE,
3793 G_CALLBACK(accel_clear_cb), accel_view);
3794 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3795 gtk_widget_show(button);
3798 /* toolbar main tab */
3799 static void config_tab_toolbar_main(GtkWidget *notebook)
3802 GtkWidget *toolbardata;
3805 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3807 vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
3809 toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
3810 gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3811 gtk_widget_show(vbox);
3814 /* toolbar status tab */
3815 static void config_tab_toolbar_status(GtkWidget *notebook)
3818 GtkWidget *toolbardata;
3821 lw = static_cast<LayoutWindow *>(layout_window_list->data);
3823 vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
3825 toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
3826 gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
3827 gtk_widget_show(vbox);
3831 static gint extension_sort_cb(gconstpointer a, gconstpointer b)
3833 return g_strcmp0(static_cast<const gchar *>(a), static_cast<const gchar *>(b));
3836 static void config_tab_advanced(GtkWidget *notebook)
3840 GSList *formats_list;
3841 GList *extensions_list = nullptr;
3844 GdkPixbufFormat *fm;
3846 GString *types_string = g_string_new(nullptr);
3847 GtkWidget *types_string_label;
3848 GtkWidget *threads_string_label;
3849 GtkWidget *dupes_threads_spin;
3851 vbox = scrolled_notebook_page(notebook, _("Advanced"));
3852 group = pref_group_new(vbox, FALSE, _("External preview extraction"), GTK_ORIENTATION_VERTICAL);
3854 pref_checkbox_new_int(group, _("Use external preview extraction - Requires restart"), options->external_preview.enable, &c_options->external_preview.enable);
3856 pref_spacer(group, PREF_PAD_GROUP);
3858 formats_list = gdk_pixbuf_get_formats();
3860 while (formats_list)
3862 fm = static_cast<GdkPixbufFormat *>(formats_list->data);
3863 extensions = gdk_pixbuf_format_get_extensions(fm);
3866 while (extensions[i])
3868 extensions_list = g_list_insert_sorted(extensions_list, g_strdup(extensions[i]), extension_sort_cb);
3872 g_strfreev(extensions);
3873 formats_list = formats_list->next;
3876 while (extensions_list)
3878 if (types_string->len == 0)
3880 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3884 types_string = g_string_append(types_string, ", ");
3885 types_string = g_string_append(types_string, static_cast<const gchar *>(extensions_list->data));
3888 extensions_list = extensions_list->next;
3891 types_string = g_string_prepend(types_string, _("Usable file types:\n"));
3892 types_string_label = pref_label_new(group, types_string->str);
3893 gtk_label_set_line_wrap(GTK_LABEL(types_string_label), TRUE);
3895 pref_spacer(group, PREF_PAD_GROUP);
3897 group = pref_group_new(vbox, FALSE, _("File identification tool"), GTK_ORIENTATION_VERTICAL);
3898 external_preview_select_entry = gtk_entry_new();
3899 tabcomp = tab_completion_new(&external_preview_select_entry, options->external_preview.select, nullptr, nullptr, nullptr, nullptr);
3900 tab_completion_add_select_button(external_preview_select_entry, _("Select file identification tool"), FALSE);
3901 gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3902 gtk_widget_show(tabcomp);
3904 group = pref_group_new(vbox, FALSE, _("Preview extraction tool"), GTK_ORIENTATION_VERTICAL);
3905 external_preview_extract_entry = gtk_entry_new();
3906 tabcomp = tab_completion_new(&external_preview_extract_entry, options->external_preview.extract, nullptr, nullptr, nullptr, nullptr);
3907 tab_completion_add_select_button(external_preview_extract_entry, _("Select preview extraction tool"), FALSE);
3908 gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
3909 gtk_widget_show(tabcomp);
3911 gtk_widget_show(vbox);
3913 g_slist_free(formats_list);
3914 g_list_free_full(extensions_list, g_free);
3915 g_string_free(types_string, TRUE);
3917 pref_spacer(group, PREF_PAD_GROUP);
3919 pref_line(vbox, PREF_PAD_SPACE);
3920 group = pref_group_new(vbox, FALSE, _("Thread pool limits"), GTK_ORIENTATION_VERTICAL);
3922 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.");
3923 gtk_label_set_line_wrap(GTK_LABEL(threads_string_label), TRUE);
3925 pref_spacer(vbox, PREF_PAD_GROUP);
3927 dupes_threads_spin = pref_spin_new_int(vbox, _("Duplicate check:"), _("max. threads"), 0, get_cpu_cores(), 1, options->threads.duplicates, &c_options->threads.duplicates);
3928 gtk_widget_set_tooltip_markup(dupes_threads_spin, _("Set to 0 for unlimited"));
3932 static void config_tab_stereo(GtkWidget *notebook)
3940 GtkWidget *fs_button;
3941 vbox = scrolled_notebook_page(notebook, _("Stereo"));
3943 group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL);
3945 table = pref_table_new(group, 2, 1, FALSE, FALSE);
3946 add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE);
3948 table = pref_table_new(group, 2, 2, TRUE, FALSE);
3949 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3950 pref_checkbox_new_int(box, _("Mirror left image"),
3951 options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left);
3952 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3953 pref_checkbox_new_int(box, _("Flip left image"),
3954 options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left);
3955 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3956 pref_checkbox_new_int(box, _("Mirror right image"),
3957 options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right);
3958 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3959 pref_checkbox_new_int(box, _("Flip right image"),
3960 options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right);
3961 pref_checkbox_new_int(group, _("Swap left and right images"),
3962 options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap);
3963 pref_checkbox_new_int(group, _("Disable stereo mode on single image source"),
3964 options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable);
3966 group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL);
3967 fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"),
3968 options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode);
3969 box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
3970 pref_checkbox_link_sensitivity(fs_button, box2);
3971 table = pref_table_new(box2, 2, 1, FALSE, FALSE);
3972 add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE);
3973 table = pref_table_new(box2, 2, 2, TRUE, FALSE);
3974 box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3975 pref_checkbox_new_int(box, _("Mirror left image"),
3976 options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left);
3977 box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, nullptr);
3978 pref_checkbox_new_int(box, _("Flip left image"),
3979 options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left);
3980 box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3981 pref_checkbox_new_int(box, _("Mirror right image"),
3982 options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right);
3983 box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, nullptr);
3984 pref_checkbox_new_int(box, _("Flip right image"),
3985 options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right);
3986 pref_checkbox_new_int(box2, _("Swap left and right images"),
3987 options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap);
3988 pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"),
3989 options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable);
3991 group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL);
3992 table = pref_table_new(group2, 5, 3, FALSE, FALSE);
3993 pref_table_spin_new_int(table, 0, 0, _("Width"), nullptr,
3994 1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w);
3995 pref_table_spin_new_int(table, 3, 0, _("Height"), nullptr,
3996 1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h);
3997 pref_table_spin_new_int(table, 0, 1, _("Left X"), nullptr,
3998 0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1);
3999 pref_table_spin_new_int(table, 3, 1, _("Left Y"), nullptr,
4000 0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1);
4001 pref_table_spin_new_int(table, 0, 2, _("Right X"), nullptr,
4002 0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2);
4003 pref_table_spin_new_int(table, 3, 2, _("Right Y"), nullptr,
4004 0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2);
4008 /* Main preferences window */
4009 static void config_window_create(LayoutWindow *lw)
4011 GtkWidget *win_vbox;
4013 GtkWidget *notebook;
4015 GtkWidget *ct_button;
4017 if (!c_options) c_options = init_options(nullptr);
4019 configwindow = window_new(GTK_WINDOW_TOPLEVEL, "preferences", PIXBUF_INLINE_ICON_CONFIG, nullptr, _("Preferences"));
4020 DEBUG_NAME(configwindow);
4021 gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
4022 g_signal_connect(G_OBJECT(configwindow), "delete_event",
4023 G_CALLBACK(config_window_delete), NULL);
4024 if (options->save_dialog_window_positions)
4026 gtk_window_resize(GTK_WINDOW(configwindow), lw->options.preferences_window.w, lw->options.preferences_window.h);
4027 gtk_window_move(GTK_WINDOW(configwindow), lw->options.preferences_window.x, lw->options.preferences_window.y);
4031 gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
4033 gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
4034 gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
4036 win_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
4037 gtk_container_add(GTK_CONTAINER(configwindow), win_vbox);
4038 gtk_widget_show(win_vbox);
4040 notebook = gtk_notebook_new();
4041 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
4042 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
4043 gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0);
4045 config_tab_general(notebook);
4046 config_tab_image(notebook);
4047 config_tab_osd(notebook);
4048 config_tab_windows(notebook);
4049 config_tab_accelerators(notebook);
4050 config_tab_files(notebook);
4051 config_tab_metadata(notebook);
4052 config_tab_keywords(notebook);
4053 config_tab_color(notebook);
4054 config_tab_stereo(notebook);
4055 config_tab_behavior(notebook);
4056 config_tab_toolbar_main(notebook);
4057 config_tab_toolbar_status(notebook);
4058 config_tab_advanced(notebook);
4060 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), lw->options.preferences_window.page_number);
4062 hbox = gtk_hbutton_box_new();
4063 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
4064 gtk_box_set_spacing(GTK_BOX(hbox), PREF_PAD_BUTTON_GAP);
4065 gtk_box_pack_end(GTK_BOX(win_vbox), hbox, FALSE, FALSE, 0);
4066 gtk_widget_show(hbox);
4068 button = pref_button_new(nullptr, GTK_STOCK_HELP, nullptr, FALSE,
4069 G_CALLBACK(config_window_help_cb), notebook);
4070 gtk_container_add(GTK_CONTAINER(hbox), button);
4071 gtk_widget_set_can_default(button, TRUE);
4072 gtk_widget_show(button);
4074 button = pref_button_new(nullptr, GTK_STOCK_OK, nullptr, FALSE,
4075 G_CALLBACK(config_window_ok_cb), notebook);
4076 gtk_container_add(GTK_CONTAINER(hbox), button);
4077 gtk_widget_set_can_default(button, TRUE);
4078 gtk_widget_grab_default(button);
4079 gtk_widget_show(button);
4083 button = pref_button_new(nullptr, GTK_STOCK_CANCEL, nullptr, FALSE,
4084 G_CALLBACK(config_window_close_cb), nullptr);
4085 gtk_container_add(GTK_CONTAINER(hbox), button);
4086 gtk_widget_set_can_default(button, TRUE);
4087 gtk_widget_show(button);
4089 if (!generic_dialog_get_alternative_button_order(configwindow))
4091 gtk_box_reorder_child(GTK_BOX(hbox), ct_button, -1);
4094 gtk_widget_show(notebook);
4096 gtk_widget_show(configwindow);
4100 *-----------------------------------------------------------------------------
4101 * config window show (public)
4102 *-----------------------------------------------------------------------------
4105 void show_config_window(LayoutWindow *lw)
4109 gtk_window_present(GTK_WINDOW(configwindow));
4113 config_window_create(lw);
4122 void show_about_window(LayoutWindow *lw)
4124 GDataInputStream *data_stream;
4125 GInputStream *in_stream_authors;
4126 GInputStream *in_stream_translators;
4128 GdkPixbuf *pixbuf_icon;
4129 GdkPixbuf *pixbuf_logo;
4133 gchar *authors[1000];
4134 gchar *authors_path;
4136 gchar *timezone_path;
4138 gchar *translators_path;
4146 copyright = g_string_new(nullptr);
4147 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");
4149 timezone_path = g_build_filename(get_rc_dir(), TIMEZONE_DATABASE_FILE, NULL);
4150 if (g_file_test(timezone_path, G_FILE_TEST_EXISTS))
4152 cd = ZDOpenDatabase(timezone_path);
4155 copyright = g_string_append(copyright, ZDGetNotice(cd));
4159 log_printf("Error: Init of timezone database %s failed\n", timezone_path);
4161 ZDCloseDatabase(cd);
4163 g_free(timezone_path);
4165 authors_path = g_build_filename(GQ_RESOURCE_PATH_CREDITS, "authors", NULL);
4167 in_stream_authors = g_resources_open_stream(authors_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4169 data_stream = g_data_input_stream_new(in_stream_authors);
4171 authors[0] = nullptr;
4172 while ((author_line = g_data_input_stream_read_line(G_DATA_INPUT_STREAM(data_stream), &length, nullptr, nullptr)))
4174 authors[i_authors] = g_strdup(author_line);
4176 g_free(author_line);
4178 authors[i_authors] = nullptr;
4180 g_input_stream_close(in_stream_authors, nullptr, nullptr);
4182 translators_path = g_build_filename(GQ_RESOURCE_PATH_CREDITS, "translators", NULL);
4184 g_resources_get_info(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &size, &flags, nullptr);
4186 in_stream_translators = g_resources_open_stream(translators_path, G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
4187 translators = static_cast<gchar *>(g_malloc0(size));
4188 g_input_stream_read_all(in_stream_translators, translators, size, &bytes_read, nullptr, nullptr);
4189 g_input_stream_close(in_stream_translators, nullptr, nullptr);
4191 comment = g_strconcat("Project created by John Ellis\nGQview 1998\nGeeqie 2007\n\n\nDevelopment and bug reports:\n", GQ_EMAIL_ADDRESS, "\nhttps://github.com/BestImageViewer/geeqie/issues",NULL);
4193 artists[0] = g_strdup("Néstor Díaz Valencia <nestor@estudionexos.com>");
4194 artists[1] = nullptr;
4196 pixbuf_logo = pixbuf_inline(PIXBUF_INLINE_LOGO);
4197 pixbuf_icon = pixbuf_inline(PIXBUF_INLINE_ICON);
4198 gtk_show_about_dialog(GTK_WINDOW(lw->window),
4199 "title", _("About Geeqie"),
4201 "program-name", GQ_APPNAME,
4203 "logo", pixbuf_logo,
4204 "icon", pixbuf_icon,
4205 "website", GQ_WEBSITE,
4206 "website-label", "Website",
4207 "comments", comment,
4210 "translator-credits", translators,
4211 "wrap-license", TRUE,
4212 "license", copyright->str,
4215 g_string_free(copyright, TRUE);
4217 while(n < i_authors)
4224 g_free(authors_path);
4226 g_free(translators);
4227 g_free(translators_path);
4228 g_object_unref(data_stream);
4229 g_object_unref(in_stream_authors);
4230 g_object_unref(in_stream_translators);
4233 static void image_overlay_set_text_colours()
4235 c_options->image_overlay.text_red = options->image_overlay.text_red;
4236 c_options->image_overlay.text_green = options->image_overlay.text_green;
4237 c_options->image_overlay.text_blue = options->image_overlay.text_blue;
4238 c_options->image_overlay.text_alpha = options->image_overlay.text_alpha;
4239 c_options->image_overlay.background_red = options->image_overlay.background_red;
4240 c_options->image_overlay.background_green = options->image_overlay.background_green;
4241 c_options->image_overlay.background_blue = options->image_overlay.background_blue;
4242 c_options->image_overlay.background_alpha = options->image_overlay.background_alpha;
4246 *-----------------------------------------------------------------------------
4247 * timezone database routines
4248 *-----------------------------------------------------------------------------
4251 static void timezone_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
4253 GError *error = nullptr;
4254 auto tz = static_cast<TZData *>(data);
4255 gchar *tmp_filename;
4256 gchar *timezone_bin;
4257 gchar *tmp_dir = nullptr;
4260 if (!g_cancellable_is_cancelled(tz->cancellable))
4262 generic_dialog_close(tz->gd);
4266 if (g_file_copy_finish(G_FILE(source_object), res, &error))
4268 tmp_filename = g_file_get_path(tz->tmp_g_file);
4269 fd = file_data_new_simple(tmp_filename);
4270 tmp_dir = open_archive(fd);
4274 timezone_bin = g_build_filename(tmp_dir, TIMEZONE_DATABASE_VERSION, TIMEZONE_DATABASE_FILE, NULL);
4275 if (isfile(timezone_bin))
4277 move_file(timezone_bin, tz->timezone_database_user);
4281 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GTK_STOCK_DIALOG_WARNING, nullptr);
4284 g_free(timezone_bin);
4285 g_free(tmp_dir); // The folder in /tmp is deleted in exit_program_final()
4289 warning_dialog(_("Warning: Cannot open timezone database file"), _("See the Log Window"), GTK_STOCK_DIALOG_WARNING, nullptr);
4291 g_free(tmp_filename);
4292 file_data_unref(fd);
4296 file_util_warning_dialog(_("Error: Timezone database download failed"), error->message, GTK_STOCK_DIALOG_ERROR, nullptr);
4299 g_file_delete(tz->tmp_g_file, nullptr, &error);
4300 g_object_unref(tz->tmp_g_file);
4301 tz->tmp_g_file = nullptr;
4302 g_object_unref(tz->cancellable);
4303 g_object_unref(tz->timezone_database_gq);
4306 static void timezone_progress_cb(goffset current_num_bytes, goffset total_num_bytes, gpointer data)
4308 auto tz = static_cast<TZData *>(data);
4310 if (!g_cancellable_is_cancelled(tz->cancellable))
4312 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(tz->progress), static_cast<gdouble>(current_num_bytes) / total_num_bytes);
4316 static void timezone_cancel_button_cb(GenericDialog *UNUSED(gd), gpointer data)
4318 auto tz = static_cast<TZData *>(data);
4320 g_cancellable_cancel(tz->cancellable);
4323 static void timezone_database_install_cb(GtkWidget *widget, gpointer data)
4325 auto tz = static_cast<TZData *>(data);
4326 GError *error = nullptr;
4327 GFileIOStream *io_stream;
4334 tz->tmp_g_file = g_file_new_tmp("geeqie_timezone_XXXXXX", &io_stream, &error);
4338 file_util_warning_dialog(_("Timezone database download failed"), error->message, GTK_STOCK_DIALOG_ERROR, nullptr);
4339 log_printf("Error: Download timezone database failed:\n%s", error->message);
4340 g_error_free(error);
4341 g_object_unref(tz->tmp_g_file);
4345 tz->timezone_database_gq = g_file_new_for_uri(TIMEZONE_DATABASE_WEB);
4347 tz->gd = generic_dialog_new(_("Timezone database"), "download_timezone_database", nullptr, TRUE, timezone_cancel_button_cb, tz);
4349 generic_dialog_add_message(tz->gd, GTK_STOCK_DIALOG_INFO, _("Downloading timezone database"), nullptr, FALSE);
4351 tz->progress = gtk_progress_bar_new();
4352 gtk_box_pack_start(GTK_BOX(tz->gd->vbox), tz->progress, FALSE, FALSE, 0);
4353 gtk_widget_show(tz->progress);
4355 gtk_widget_show(tz->gd->dialog);
4356 tz->cancellable = g_cancellable_new();
4357 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);
4359 gtk_button_set_label(GTK_BUTTON(widget), _("Update"));
4362 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */