/* PIKA - Photo and Image Kooker Application * a rebranding of The GNU Image Manipulation Program (created with heckimp) * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio * * Original copyright, applying to most contents (license remains unchanged): * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include #include #include #include #include "libpikamath/pikamath.h" #include "libpikabase/pikabase.h" #include "libpikaconfig/pikaconfig.h" #include "libpikawidgets/pikawidgets.h" #include "dialogs-types.h" #include "pika-version.h" #include "config/pikarc.h" #include "core/pika.h" #include "core/pikatemplate.h" #include "core/pika-utils.h" #include "display/pikamodifiersmanager.h" #include "plug-in/pikapluginmanager.h" #include "widgets/pikaaction-history.h" #include "widgets/pikacolorpanel.h" #include "widgets/pikacontainercombobox.h" #include "widgets/pikacontainerview.h" #include "widgets/pikacontrollerlist.h" #include "widgets/pikadevices.h" #include "widgets/pikadialogfactory.h" #include "widgets/pikagrideditor.h" #include "widgets/pikahelp.h" #include "widgets/pikahelp-ids.h" #include "widgets/pikalanguagecombobox.h" #include "widgets/pikamessagebox.h" #include "widgets/pikamessagedialog.h" #include "widgets/pikapluginview.h" #include "widgets/pikaprefsbox.h" #include "widgets/pikapropwidgets.h" #include "widgets/pikamodifierseditor.h" #include "widgets/pikastrokeeditor.h" #include "widgets/pikatemplateeditor.h" #include "widgets/pikatooleditor.h" #include "widgets/pikawidgets-utils.h" #include "menus/menus.h" #include "tools/pika-tools.h" #include "gui/icon-themes.h" #include "gui/session.h" #include "gui/modifiers.h" #include "gui/themes.h" #include "preferences-dialog.h" #include "preferences-dialog-utils.h" #include "resolution-calibrate-dialog.h" #include "pika-intl.h" #define RESPONSE_RESET 1 /* preferences local functions */ static GtkWidget * prefs_dialog_new (Pika *pika, PikaConfig *config); static void prefs_config_notify (GObject *config, GParamSpec *param_spec, GObject *config_copy); static void prefs_config_copy_notify (GObject *config_copy, GParamSpec *param_spec, GObject *config); static void prefs_response (GtkWidget *widget, gint response_id, GtkWidget *dialog); static void prefs_message (GtkMessageType type, gboolean destroy, const gchar *message); static void prefs_color_management_reset (GtkWidget *widget, GObject *config); static void prefs_dialog_defaults_reset (GtkWidget *widget, GObject *config); static void prefs_folders_reset (GtkWidget *widget, GObject *config); static void prefs_path_reset (GtkWidget *widget, GObject *config); static void prefs_import_raw_procedure_callback (GtkWidget *widget, GObject *config); static void prefs_resolution_source_callback (GtkWidget *widget, GObject *config); static void prefs_resolution_calibrate_callback (GtkWidget *widget, GtkWidget *entry); static void prefs_input_devices_dialog (GtkWidget *widget, Pika *pika); static void prefs_keyboard_shortcuts_dialog (GtkWidget *widget, Pika *pika); static void prefs_menus_save_callback (GtkWidget *widget, Pika *pika); static void prefs_menus_clear_callback (GtkWidget *widget, Pika *pika); static void prefs_menus_remove_callback (GtkWidget *widget, Pika *pika); static void prefs_session_save_callback (GtkWidget *widget, Pika *pika); static void prefs_session_clear_callback (GtkWidget *widget, Pika *pika); static void prefs_devices_save_callback (GtkWidget *widget, Pika *pika); static void prefs_devices_clear_callback (GtkWidget *widget, Pika *pika); static void prefs_modifiers_clear_callback (GtkWidget *widget, PikaModifiersEditor *editor); static void prefs_search_clear_callback (GtkWidget *widget, Pika *pika); static void prefs_tool_options_save_callback (GtkWidget *widget, Pika *pika); static void prefs_tool_options_clear_callback (GtkWidget *widget, Pika *pika); static void prefs_help_language_change_callback (GtkComboBox *combo, Pika *pika); static void prefs_help_language_change_callback2 (GtkComboBox *combo, GtkContainer *box); static void prefs_check_style_callback (GObject *config, GParamSpec *pspec, GtkWidget *widget); static void prefs_gui_config_notify_icon_size (GObject *config, GParamSpec *pspec, GtkRange *range); static void prefs_icon_size_value_changed (GtkRange *range, PikaGuiConfig *config); static void prefs_gui_config_notify_font_size (GObject *config, GParamSpec *pspec, GtkRange *range); static void prefs_font_size_value_changed (GtkRange *range, PikaGuiConfig *config); /* private variables */ static GtkWidget *prefs_dialog = NULL; static GtkWidget *tool_editor = NULL; /* public function */ GtkWidget * preferences_dialog_create (Pika *pika) { PikaConfig *config; PikaConfig *config_copy; PikaConfig *config_orig; g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL); if (prefs_dialog) return prefs_dialog; /* turn off autosaving while the prefs dialog is open */ pika_rc_set_autosave (PIKA_RC (pika->edit_config), FALSE); config = PIKA_CONFIG (pika->edit_config); config_copy = pika_config_duplicate (config); config_orig = pika_config_duplicate (config); g_signal_connect_object (config, "notify", G_CALLBACK (prefs_config_notify), config_copy, 0); g_signal_connect_object (config_copy, "notify", G_CALLBACK (prefs_config_copy_notify), config, 0); g_set_weak_pointer (&prefs_dialog, prefs_dialog_new (pika, config_copy)); g_object_set_data (G_OBJECT (prefs_dialog), "pika", pika); g_object_set_data_full (G_OBJECT (prefs_dialog), "config-copy", config_copy, (GDestroyNotify) g_object_unref); g_object_set_data_full (G_OBJECT (prefs_dialog), "config-orig", config_orig, (GDestroyNotify) g_object_unref); return prefs_dialog; } /* private functions */ static void prefs_config_notify (GObject *config, GParamSpec *param_spec, GObject *config_copy) { GValue global_value = G_VALUE_INIT; GValue copy_value = G_VALUE_INIT; g_value_init (&global_value, param_spec->value_type); g_value_init (©_value, param_spec->value_type); g_object_get_property (config, param_spec->name, &global_value); g_object_get_property (config_copy, param_spec->name, ©_value); if (g_param_values_cmp (param_spec, &global_value, ©_value)) { g_signal_handlers_block_by_func (config_copy, prefs_config_copy_notify, config); g_object_set_property (config_copy, param_spec->name, &global_value); g_signal_handlers_unblock_by_func (config_copy, prefs_config_copy_notify, config); } g_value_unset (&global_value); g_value_unset (©_value); } static void prefs_config_copy_notify (GObject *config_copy, GParamSpec *param_spec, GObject *config) { GValue copy_value = G_VALUE_INIT; GValue global_value = G_VALUE_INIT; g_value_init (©_value, param_spec->value_type); g_value_init (&global_value, param_spec->value_type); g_object_get_property (config_copy, param_spec->name, ©_value); g_object_get_property (config, param_spec->name, &global_value); if (g_param_values_cmp (param_spec, ©_value, &global_value)) { if (param_spec->flags & PIKA_CONFIG_PARAM_CONFIRM) { #ifdef PIKA_CONFIG_DEBUG g_print ("NOT Applying prefs change of '%s' to edit_config " "because it needs confirmation\n", param_spec->name); #endif } else { #ifdef PIKA_CONFIG_DEBUG g_print ("Applying prefs change of '%s' to edit_config\n", param_spec->name); #endif g_signal_handlers_block_by_func (config, prefs_config_notify, config_copy); g_object_set_property (config, param_spec->name, ©_value); g_signal_handlers_unblock_by_func (config, prefs_config_notify, config_copy); } } g_value_unset (©_value); g_value_unset (&global_value); } static void prefs_response (GtkWidget *widget, gint response_id, GtkWidget *dialog) { Pika *pika = g_object_get_data (G_OBJECT (dialog), "pika"); switch (response_id) { case RESPONSE_RESET: { GtkWidget *confirm; confirm = pika_message_dialog_new (_("Reset All Preferences"), PIKA_ICON_DIALOG_QUESTION, dialog, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, pika_standard_help_func, NULL, _("_Cancel"), GTK_RESPONSE_CANCEL, _("_Reset"), GTK_RESPONSE_OK, NULL); pika_dialog_set_alternative_button_order (GTK_DIALOG (confirm), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1); pika_message_box_set_primary_text (PIKA_MESSAGE_DIALOG (confirm)->box, _("Do you really want to reset all " "preferences to default values?")); if (pika_dialog_run (PIKA_DIALOG (confirm)) == GTK_RESPONSE_OK) { PikaConfig *config_copy; config_copy = g_object_get_data (G_OBJECT (dialog), "config-copy"); pika_config_reset (config_copy); pika_rc_load_system (PIKA_RC (config_copy)); /* don't use the default value if there is no help browser */ if (! pika_help_browser_is_installed (pika)) { g_object_set (config_copy, "help-browser", PIKA_HELP_BROWSER_WEB_BROWSER, NULL); } } gtk_widget_destroy (confirm); return; } break; case GTK_RESPONSE_OK: { GObject *config_copy; GList *restart_diff; GList *confirm_diff; GList *list; config_copy = g_object_get_data (G_OBJECT (dialog), "config-copy"); /* destroy config_orig */ g_object_set_data (G_OBJECT (dialog), "config-orig", NULL); gtk_widget_set_sensitive (GTK_WIDGET (dialog), FALSE); confirm_diff = pika_config_diff (G_OBJECT (pika->edit_config), config_copy, PIKA_CONFIG_PARAM_CONFIRM); g_object_freeze_notify (G_OBJECT (pika->edit_config)); for (list = confirm_diff; list; list = g_list_next (list)) { GParamSpec *param_spec = list->data; GValue value = G_VALUE_INIT; g_value_init (&value, param_spec->value_type); g_object_get_property (config_copy, param_spec->name, &value); g_object_set_property (G_OBJECT (pika->edit_config), param_spec->name, &value); g_value_unset (&value); } g_object_thaw_notify (G_OBJECT (pika->edit_config)); g_list_free (confirm_diff); pika_rc_save (PIKA_RC (pika->edit_config)); /* spit out a solely informational warning about changed values * which need restart */ restart_diff = pika_config_diff (G_OBJECT (pika->edit_config), G_OBJECT (pika->config), PIKA_CONFIG_PARAM_RESTART); if (restart_diff) { GString *string; string = g_string_new (_("You will have to restart PIKA for " "the following changes to take effect:")); g_string_append (string, "\n\n"); for (list = restart_diff; list; list = g_list_next (list)) { GParamSpec *param_spec = list->data; /* The first 3 bytes are the bullet unicode character * for doing a list (U+2022). */ g_string_append_printf (string, "\xe2\x80\xa2 %s\n", g_param_spec_get_nick (param_spec)); } prefs_message (GTK_MESSAGE_INFO, FALSE, string->str); g_string_free (string, TRUE); } g_list_free (restart_diff); } break; default: { GObject *config_orig; GList *diff; GList *list; config_orig = g_object_get_data (G_OBJECT (dialog), "config-orig"); /* destroy config_copy */ g_object_set_data (G_OBJECT (dialog), "config-copy", NULL); gtk_widget_set_sensitive (GTK_WIDGET (dialog), FALSE); diff = pika_config_diff (G_OBJECT (pika->edit_config), config_orig, PIKA_CONFIG_PARAM_SERIALIZE); g_object_freeze_notify (G_OBJECT (pika->edit_config)); for (list = diff; list; list = g_list_next (list)) { GParamSpec *param_spec = list->data; GValue value = G_VALUE_INIT; g_value_init (&value, param_spec->value_type); g_object_get_property (config_orig, param_spec->name, &value); g_object_set_property (G_OBJECT (pika->edit_config), param_spec->name, &value); g_value_unset (&value); } pika_tool_editor_revert_changes (PIKA_TOOL_EDITOR (tool_editor)); g_object_thaw_notify (G_OBJECT (pika->edit_config)); g_list_free (diff); } tool_editor = NULL; } /* enable autosaving again */ pika_rc_set_autosave (PIKA_RC (pika->edit_config), TRUE); gtk_widget_destroy (dialog); } static void prefs_color_management_reset (GtkWidget *widget, GObject *config) { PikaCoreConfig *core_config = PIKA_CORE_CONFIG (config); pika_config_reset (PIKA_CONFIG (core_config->color_management)); pika_config_reset_property (config, "color-profile-policy"); pika_config_reset_property (config, "filter-tool-show-color-options"); } static void prefs_dialog_defaults_reset (GtkWidget *widget, GObject *config) { GParamSpec **pspecs; guint n_pspecs; guint i; pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (config), &n_pspecs); g_object_freeze_notify (config); for (i = 0; i < n_pspecs; i++) { GParamSpec *pspec = pspecs[i]; if (pspec->owner_type == PIKA_TYPE_DIALOG_CONFIG) pika_config_reset_property (config, pspec->name); } pika_config_reset_property (config, "filter-tool-max-recent"); pika_config_reset_property (config, "filter-tool-use-last-settings"); pika_config_reset_property (config, "filter-tool-show-color-options"); g_object_thaw_notify (config); g_free (pspecs); } static void prefs_folders_reset (GtkWidget *widget, GObject *config) { pika_config_reset_property (config, "temp-path"); pika_config_reset_property (config, "swap-path"); } static void prefs_path_reset (GtkWidget *widget, GObject *config) { const gchar *path_property; const gchar *writable_property; path_property = g_object_get_data (G_OBJECT (widget), "path"); writable_property = g_object_get_data (G_OBJECT (widget), "path-writable"); pika_config_reset_property (config, path_property); if (writable_property) pika_config_reset_property (config, writable_property); } static gboolean prefs_template_select_callback (PikaContainerView *view, GList *templates, GList *paths, PikaTemplate *edit_template) { g_return_val_if_fail (g_list_length (templates) < 2, FALSE); if (templates) { /* make sure the resolution values are copied first (see bug #546924) */ pika_config_sync (G_OBJECT (templates->data), G_OBJECT (edit_template), PIKA_TEMPLATE_PARAM_COPY_FIRST); pika_config_sync (G_OBJECT (templates->data), G_OBJECT (edit_template), 0); } return TRUE; } static void prefs_import_raw_procedure_callback (GtkWidget *widget, GObject *config) { gchar *raw_plug_in; raw_plug_in = pika_plug_in_view_get_plug_in (PIKA_PLUG_IN_VIEW (widget)); g_object_set (config, "import-raw-plug-in", raw_plug_in, NULL); g_free (raw_plug_in); } static void prefs_resolution_source_callback (GtkWidget *widget, GObject *config) { gdouble xres; gdouble yres; gboolean from_gdk; from_gdk = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); if (from_gdk) { pika_get_monitor_resolution (pika_widget_get_monitor (widget), &xres, &yres); } else { PikaSizeEntry *entry = g_object_get_data (G_OBJECT (widget), "monitor_resolution_sizeentry"); g_return_if_fail (PIKA_IS_SIZE_ENTRY (entry)); xres = pika_size_entry_get_refval (entry, 0); yres = pika_size_entry_get_refval (entry, 1); } g_object_set (config, "monitor-xresolution", xres, "monitor-yresolution", yres, "monitor-resolution-from-windowing-system", from_gdk, NULL); } static void prefs_resolution_calibrate_callback (GtkWidget *widget, GtkWidget *entry) { GtkWidget *dialog; GtkWidget *prefs_box; const gchar *icon_name; dialog = gtk_widget_get_toplevel (entry); prefs_box = g_object_get_data (G_OBJECT (dialog), "prefs-box"); icon_name = pika_prefs_box_get_current_icon_name (PIKA_PREFS_BOX (prefs_box)); resolution_calibrate_dialog (entry, icon_name); } static void prefs_input_devices_dialog (GtkWidget *widget, Pika *pika) { pika_dialog_factory_dialog_raise (pika_dialog_factory_get_singleton (), pika_widget_get_monitor (widget), widget, "pika-input-devices-dialog", 0); } static void prefs_keyboard_shortcuts_dialog (GtkWidget *widget, Pika *pika) { pika_dialog_factory_dialog_raise (pika_dialog_factory_get_singleton (), pika_widget_get_monitor (widget), widget, "pika-keyboard-shortcuts-dialog", 0); } static void prefs_menus_save_callback (GtkWidget *widget, Pika *pika) { GtkWidget *clear_button; menus_save (pika, TRUE); clear_button = g_object_get_data (G_OBJECT (widget), "clear-button"); if (clear_button) gtk_widget_set_sensitive (clear_button, TRUE); } static void prefs_menus_clear_callback (GtkWidget *widget, Pika *pika) { GError *error = NULL; if (! menus_clear (pika, &error)) { prefs_message (GTK_MESSAGE_ERROR, TRUE, error->message); g_clear_error (&error); } else { gtk_widget_set_sensitive (widget, FALSE); prefs_message (GTK_MESSAGE_INFO, TRUE, _("Your keyboard shortcuts will be reset to " "default values the next time you start PIKA.")); } } static void prefs_menus_remove_callback (GtkWidget *widget, Pika *pika) { GtkWidget *dialog; dialog = pika_message_dialog_new (_("Remove all Keyboard Shortcuts"), PIKA_ICON_DIALOG_QUESTION, gtk_widget_get_toplevel (widget), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, pika_standard_help_func, NULL, _("_Cancel"), GTK_RESPONSE_CANCEL, _("Cl_ear"), GTK_RESPONSE_OK, NULL); pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1); g_signal_connect_object (gtk_widget_get_toplevel (widget), "unmap", G_CALLBACK (gtk_widget_destroy), dialog, G_CONNECT_SWAPPED); pika_message_box_set_primary_text (PIKA_MESSAGE_DIALOG (dialog)->box, _("Do you really want to remove all " "keyboard shortcuts from all menus?")); if (pika_dialog_run (PIKA_DIALOG (dialog)) == GTK_RESPONSE_OK) { menus_remove (pika); } gtk_widget_destroy (dialog); } static void prefs_session_save_callback (GtkWidget *widget, Pika *pika) { GtkWidget *clear_button; session_save (pika, TRUE); clear_button = g_object_get_data (G_OBJECT (widget), "clear-button"); if (clear_button) gtk_widget_set_sensitive (clear_button, TRUE); } static void prefs_session_clear_callback (GtkWidget *widget, Pika *pika) { GError *error = NULL; if (! session_clear (pika, &error)) { prefs_message (GTK_MESSAGE_ERROR, TRUE, error->message); g_clear_error (&error); } else { gtk_widget_set_sensitive (widget, FALSE); prefs_message (GTK_MESSAGE_INFO, TRUE, _("Your window setup will be reset to " "default values the next time you start PIKA.")); } } static void prefs_devices_save_callback (GtkWidget *widget, Pika *pika) { GtkWidget *clear_button; pika_devices_save (pika, TRUE); clear_button = g_object_get_data (G_OBJECT (widget), "clear-button"); if (clear_button) gtk_widget_set_sensitive (clear_button, TRUE); } static void prefs_devices_clear_callback (GtkWidget *widget, Pika *pika) { GError *error = NULL; if (! pika_devices_clear (pika, &error)) { prefs_message (GTK_MESSAGE_ERROR, TRUE, error->message); g_clear_error (&error); } else { gtk_widget_set_sensitive (widget, FALSE); prefs_message (GTK_MESSAGE_INFO, TRUE, _("Your input device settings will be reset to " "default values the next time you start PIKA.")); } } static void prefs_modifiers_clear_callback (GtkWidget *widget, PikaModifiersEditor *editor) { pika_modifiers_editor_clear (editor); } #ifdef G_OS_WIN32 static gboolean prefs_devices_api_sensitivity_func (gint value, gpointer data) { static gboolean have_wintab = TRUE; static gboolean have_windows_ink = TRUE; static gboolean inited = FALSE; if (!inited) { have_wintab = pika_win32_have_wintab (); have_windows_ink = pika_win32_have_windows_ink (); inited = TRUE; } switch (value) { case PIKA_WIN32_POINTER_INPUT_API_WINTAB: return have_wintab; case PIKA_WIN32_POINTER_INPUT_API_WINDOWS_INK: return have_windows_ink; default: return TRUE; } } #endif static void prefs_search_clear_callback (GtkWidget *widget, Pika *pika) { pika_action_history_clear (pika); } static void prefs_tool_options_save_callback (GtkWidget *widget, Pika *pika) { GtkWidget *clear_button; pika_tools_save (pika, TRUE, TRUE); clear_button = g_object_get_data (G_OBJECT (widget), "clear-button"); if (clear_button) gtk_widget_set_sensitive (clear_button, TRUE); } static void prefs_tool_options_clear_callback (GtkWidget *widget, Pika *pika) { GError *error = NULL; if (! pika_tools_clear (pika, &error)) { prefs_message (GTK_MESSAGE_ERROR, TRUE, error->message); g_clear_error (&error); } else { gtk_widget_set_sensitive (widget, FALSE); prefs_message (GTK_MESSAGE_INFO, TRUE, _("Your tool options will be reset to " "default values the next time you start PIKA.")); } } static void prefs_help_language_change_callback (GtkComboBox *combo, Pika *pika) { gchar *help_locales = NULL; gchar *code; code = pika_language_combo_box_get_code (PIKA_LANGUAGE_COMBO_BOX (combo)); if (code && g_strcmp0 ("", code) != 0) { help_locales = g_strdup_printf ("%s:", code); } g_object_set (pika->config, "help-locales", help_locales? help_locales : "", NULL); g_free (code); if (help_locales) g_free (help_locales); } static void prefs_help_language_change_callback2 (GtkComboBox *combo, GtkContainer *box) { Pika *pika; GtkLabel *label = NULL; GtkImage *icon = NULL; GList *children; GList *iter; const gchar *text; const gchar *icon_name; pika = g_object_get_data (G_OBJECT (box), "pika"); children = gtk_container_get_children (box); for (iter = children; iter; iter = iter->next) { if (GTK_IS_LABEL (iter->data)) { label = iter->data; } else if (GTK_IS_IMAGE (iter->data)) { icon = iter->data; } } if (pika_help_user_manual_is_installed (pika)) { text = _("There's a local installation of the user manual."); icon_name = PIKA_ICON_DIALOG_INFORMATION; } else { text = _("The user manual is not installed locally."); icon_name = PIKA_ICON_DIALOG_WARNING; } if (label) { gtk_label_set_text (label, text); } if (icon) { gtk_image_set_from_icon_name (icon, icon_name, GTK_ICON_SIZE_BUTTON); } g_list_free (children); } static void prefs_check_style_callback (GObject *config, GParamSpec *pspec, GtkWidget *widget) { PikaDisplayConfig *display_config = PIKA_DISPLAY_CONFIG (config); gtk_widget_set_sensitive (widget, display_config->transparency_type == PIKA_CHECK_TYPE_CUSTOM_CHECKS); } static void prefs_icon_size_value_changed (GtkRange *range, PikaGuiConfig *config) { gint value = (gint) gtk_range_get_value (range); g_signal_handlers_block_by_func (config, G_CALLBACK (prefs_gui_config_notify_icon_size), range); g_object_set (G_OBJECT (config), "custom-icon-size", (PikaIconSize) value, NULL); g_signal_handlers_unblock_by_func (config, G_CALLBACK (prefs_gui_config_notify_icon_size), range); } static void prefs_gui_config_notify_icon_size (GObject *config, GParamSpec *pspec, GtkRange *range) { PikaIconSize size = PIKA_GUI_CONFIG (config)->custom_icon_size; g_signal_handlers_block_by_func (range, G_CALLBACK (prefs_icon_size_value_changed), config); gtk_range_set_value (range, (gdouble) size); g_signal_handlers_unblock_by_func (range, G_CALLBACK (prefs_icon_size_value_changed), config); } static void prefs_font_size_value_changed (GtkRange *range, PikaGuiConfig *config) { gdouble value = gtk_range_get_value (range); g_signal_handlers_block_by_func (config, G_CALLBACK (prefs_gui_config_notify_font_size), range); g_object_set (G_OBJECT (config), "font-relative-size", value / 100.0, NULL); g_signal_handlers_unblock_by_func (config, G_CALLBACK (prefs_gui_config_notify_font_size), range); } static void prefs_gui_config_notify_font_size (GObject *config, GParamSpec *pspec, GtkRange *range) { g_signal_handlers_block_by_func (range, G_CALLBACK (prefs_font_size_value_changed), config); gtk_range_set_value (range, PIKA_GUI_CONFIG (config)->font_relative_size * 100.0); g_signal_handlers_unblock_by_func (range, G_CALLBACK (prefs_font_size_value_changed), config); } static void prefs_format_string_select_callback (GtkListBox *listbox, GtkListBoxRow *row, gpointer user_data) { GtkEntry *entry = GTK_ENTRY (user_data); gtk_entry_set_text (entry, g_object_get_data (G_OBJECT (row), "format")); } static void prefs_theme_select_callback (GtkListBox *listbox, GtkListBoxRow *row, Pika *pika) { const char *theme; g_return_if_fail (row != NULL); theme = g_object_get_data (G_OBJECT (row), "theme"); g_object_set (pika->config, "theme", theme, NULL); } static void prefs_theme_reload_callback (GtkWidget *button, Pika *pika) { g_object_notify (G_OBJECT (pika->config), "theme"); } static void prefs_icon_theme_select_callback (GtkListBox *listbox, GtkListBoxRow *row, Pika *pika) { const char *icon_theme; icon_theme = g_object_get_data (G_OBJECT (row), "icon-theme"); g_object_set (pika->config, "icon-theme", icon_theme, NULL); } static void prefs_canvas_padding_color_changed (GtkWidget *button, GtkWidget *combo) { pika_int_combo_box_set_active (PIKA_INT_COMBO_BOX (combo), PIKA_CANVAS_PADDING_MODE_CUSTOM); } static void prefs_display_options_frame_add (Pika *pika, GObject *object, const gchar *label, GtkContainer *parent) { GtkWidget *vbox; GtkWidget *hbox; GtkWidget *checks_vbox; GtkWidget *grid; GtkWidget *combo; GtkWidget *button; vbox = prefs_frame_new (label, parent, FALSE); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); checks_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); gtk_box_pack_start (GTK_BOX (hbox), checks_vbox, TRUE, TRUE, 0); gtk_widget_show (checks_vbox); prefs_check_button_add (object, "show-selection", _("Show s_election"), GTK_BOX (checks_vbox)); prefs_check_button_add (object, "show-layer-boundary", _("Show _layer boundary"), GTK_BOX (checks_vbox)); prefs_check_button_add (object, "show-canvas-boundary", _("Show can_vas boundary"), GTK_BOX (checks_vbox)); prefs_check_button_add (object, "show-guides", _("Show _guides"), GTK_BOX (checks_vbox)); prefs_check_button_add (object, "show-grid", _("Show gri_d"), GTK_BOX (checks_vbox)); prefs_check_button_add (object, "show-sample-points", _("Show _sample points"), GTK_BOX (checks_vbox)); checks_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); gtk_box_pack_start (GTK_BOX (hbox), checks_vbox, TRUE, TRUE, 0); gtk_widget_show (checks_vbox); #ifndef GDK_WINDOWING_QUARTZ prefs_check_button_add (object, "show-menubar", _("Show _menubar"), GTK_BOX (checks_vbox)); #endif /* !GDK_WINDOWING_QUARTZ */ prefs_check_button_add (object, "show-rulers", _("Show _rulers"), GTK_BOX (checks_vbox)); prefs_check_button_add (object, "show-scrollbars", _("Show scroll_bars"), GTK_BOX (checks_vbox)); prefs_check_button_add (object, "show-statusbar", _("Show s_tatusbar"), GTK_BOX (checks_vbox)); grid = prefs_grid_new (GTK_CONTAINER (vbox)); combo = prefs_enum_combo_box_add (object, "padding-mode", 0, 0, _("Canvas _padding mode:"), GTK_GRID (grid), 0, NULL); button = prefs_color_button_add (object, "padding-color", _("Custom p_adding color:"), _("Select Custom Canvas Padding Color"), GTK_GRID (grid), 1, NULL, pika_get_user_context (pika)); g_signal_connect (button, "color-changed", G_CALLBACK (prefs_canvas_padding_color_changed), combo); prefs_check_button_add (object, "padding-in-show-all", _("_Keep canvas padding in \"Show All\" mode"), GTK_BOX (vbox)); } static void prefs_behavior_options_frame_add (Pika *pika, GObject *object, const gchar *label, GtkContainer *parent) { GtkWidget *vbox; GtkWidget *hbox; GtkWidget *checks_vbox; vbox = prefs_frame_new (label, parent, FALSE); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); checks_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); gtk_box_pack_start (GTK_BOX (hbox), checks_vbox, TRUE, TRUE, 0); gtk_widget_show (checks_vbox); prefs_check_button_add (object, "snap-to-guides", _("Snap to _Guides"), GTK_BOX (checks_vbox)); prefs_check_button_add (object, "snap-to-grid", _("S_nap to Grid"), GTK_BOX (checks_vbox)); prefs_check_button_add (object, "snap-to-canvas", _("Snap to Canvas _Edges"), GTK_BOX (checks_vbox)); checks_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); gtk_box_pack_start (GTK_BOX (hbox), checks_vbox, TRUE, TRUE, 0); gtk_widget_show (checks_vbox); prefs_check_button_add (object, "snap-to-path", _("Snap to _Active Path"), GTK_BOX (checks_vbox)); prefs_check_button_add (object, "snap-to-bbox", _("Snap to _Bounding Box"), GTK_BOX (checks_vbox)); prefs_check_button_add (object, "snap-to-equidistance", _("Snap to _Equidistance"), GTK_BOX (checks_vbox)); } static void prefs_help_func (const gchar *help_id, gpointer help_data) { GtkWidget *prefs_box; prefs_box = g_object_get_data (G_OBJECT (help_data), "prefs-box"); help_id = pika_prefs_box_get_current_help_id (PIKA_PREFS_BOX (prefs_box)); pika_standard_help_func (help_id, NULL); } static void prefs_message (GtkMessageType type, gboolean destroy_with_parent, const gchar *message) { GtkWidget *dialog; dialog = gtk_message_dialog_new (GTK_WINDOW (prefs_dialog), destroy_with_parent ? GTK_DIALOG_DESTROY_WITH_PARENT : 0, type, GTK_BUTTONS_OK, "%s", message); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } static GtkWidget * prefs_dialog_new (Pika *pika, PikaConfig *config) { GtkWidget *dialog; GtkTreeIter top_iter; GtkTreeIter child_iter; GtkWidget *prefs_box; GtkSizeGroup *size_group = NULL; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *vbox2; GtkWidget *vbox3; GtkWidget *button; GtkWidget *button2; GtkWidget *grid; GtkWidget *label; GtkWidget *entry; GtkWidget *calibrate_button; GSList *group; GtkWidget *separator; GtkWidget *editor; gint i; GObject *object; PikaCoreConfig *core_config; PikaDisplayConfig *display_config; GList *manuals; g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL); g_return_val_if_fail (PIKA_IS_CONFIG (config), NULL); object = G_OBJECT (config); core_config = PIKA_CORE_CONFIG (config); display_config = PIKA_DISPLAY_CONFIG (config); dialog = pika_dialog_new (_("Preferences"), "pika-preferences", NULL, 0, prefs_help_func, PIKA_HELP_PREFS_DIALOG, _("_Reset"), RESPONSE_RESET, _("_Cancel"), GTK_RESPONSE_CANCEL, _("_OK"), GTK_RESPONSE_OK, NULL); pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog), RESPONSE_RESET, GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1); g_signal_connect (dialog, "response", G_CALLBACK (prefs_response), dialog); /* The prefs box */ prefs_box = pika_prefs_box_new (); gtk_container_set_border_width (GTK_CONTAINER (prefs_box), 12); gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), prefs_box, TRUE, TRUE, 0); gtk_widget_show (prefs_box); g_object_set_data (G_OBJECT (dialog), "prefs-box", prefs_box); /**********************/ /* System Resources */ /**********************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-prefs-system-resources", _("System Resources"), _("System Resources"), PIKA_HELP_PREFS_SYSTEM_RESOURCES, NULL, &top_iter); pika_prefs_box_set_page_scrollable (PIKA_PREFS_BOX (prefs_box), vbox, TRUE); size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); vbox2 = prefs_frame_new (_("Resource Consumption"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_spin_button_add (object, "undo-levels", 1.0, 5.0, 0, _("Minimal number of _undo levels:"), GTK_GRID (grid), 0, size_group); prefs_memsize_entry_add (object, "undo-size", _("Maximum undo _memory:"), GTK_GRID (grid), 1, size_group); prefs_memsize_entry_add (object, "tile-cache-size", _("Tile cache _size:"), GTK_GRID (grid), 2, size_group); prefs_memsize_entry_add (object, "max-new-image-size", _("Maximum _new image size:"), GTK_GRID (grid), 3, size_group); prefs_compression_combo_box_add (object, "swap-compression", _("S_wap compression:"), GTK_GRID (grid), 4, size_group); #ifdef ENABLE_MP prefs_spin_button_add (object, "num-processors", 1.0, 4.0, 0, _("Number of _threads to use:"), GTK_GRID (grid), 5, size_group); #endif /* ENABLE_MP */ /* Internet access */ #ifdef CHECK_UPDATE if (pika_version_check_update ()) { vbox2 = prefs_frame_new (_("Network access"), GTK_CONTAINER (vbox), FALSE); prefs_switch_add (object, "check-updates", _("Check for updates (requires internet)"), GTK_BOX (vbox2), size_group); } #endif /* Image Thumbnails */ vbox2 = prefs_frame_new (_("Image Thumbnails"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_enum_combo_box_add (object, "thumbnail-size", 0, 0, _("Size of _thumbnails:"), GTK_GRID (grid), 0, size_group); prefs_memsize_entry_add (object, "thumbnail-filesize-limit", _("Maximum _filesize for thumbnailing:"), GTK_GRID (grid), 1, size_group); /* Document History */ vbox2 = prefs_frame_new (_("Document History"), GTK_CONTAINER (vbox), FALSE); prefs_switch_add (object, "save-document-history", _("_Keep record of used files in the Recent Documents list"), GTK_BOX (vbox2), size_group); g_clear_object (&size_group); /***************/ /* Debugging */ /***************/ /* No debugging preferences are needed on win32. Either PIKA has been * built with DrMinGW support (HAVE_EXCHNDL) or not. If it has, then * the backtracing is enabled and can't be disabled. It assume it will * work only upon a crash. */ #ifndef G_OS_WIN32 vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-mascot-eek", /* TODO: icon needed. */ _("Debugging"), _("Debugging"), PIKA_HELP_PREFS_DEBUGGING, NULL, &top_iter); hbox = g_object_new (PIKA_TYPE_HINT_BOX, "icon-name", PIKA_ICON_DIALOG_WARNING, "hint", _("We hope you will never need these " "settings, but as all software, PIKA " "has bugs, and crashes can occur. If it " "happens, you can help us by reporting " "bugs."), NULL); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); vbox2 = prefs_frame_new (_("Bug Reporting"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); button = prefs_enum_combo_box_add (object, "debug-policy", 0, 0, _("Debug _policy:"), GTK_GRID (grid), 0, NULL); /* Check existence of gdb or lldb to activate the preference, as a * good hint of its prerequisite, unless backtrace() API exists, in * which case the feature is always available. */ hbox = NULL; if (! pika_stack_trace_available (TRUE)) { #ifndef HAVE_EXECINFO_H hbox = prefs_hint_box_new (PIKA_ICON_DIALOG_WARNING, _("This feature requires \"gdb\" or \"lldb\" installed on your system.")); gtk_widget_set_sensitive (button, FALSE); #else hbox = prefs_hint_box_new (PIKA_ICON_DIALOG_WARNING, _("This feature is more efficient with \"gdb\" or \"lldb\" installed on your system.")); #endif /* ! HAVE_EXECINFO_H */ } if (hbox) gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); #endif /* ! G_OS_WIN32 */ /**********************/ /* Color Management */ /**********************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-prefs-color-management", _("Color Management"), _("Color Management"), PIKA_HELP_PREFS_COLOR_MANAGEMENT, NULL, &top_iter); pika_prefs_box_set_page_scrollable (PIKA_PREFS_BOX (prefs_box), vbox, TRUE); button = pika_prefs_box_set_page_resettable (PIKA_PREFS_BOX (prefs_box), vbox, _("R_eset Color Management")); g_signal_connect (button, "clicked", G_CALLBACK (prefs_color_management_reset), config); { GObject *color_config = G_OBJECT (core_config->color_management); GtkListStore *store; GFile *file; gint row = 0; file = pika_directory_file ("profilerc", NULL); store = pika_color_profile_store_new (file); g_object_unref (file); pika_color_profile_store_add_file (PIKA_COLOR_PROFILE_STORE (store), NULL, NULL); size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); grid = prefs_grid_new (GTK_CONTAINER (vbox)); prefs_enum_combo_box_add (color_config, "mode", 0, 0, _("Image display _mode:"), GTK_GRID (grid), row++, NULL); /* Color Managed Display */ vbox2 = prefs_frame_new (_("Color Managed Display"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); row = 0; prefs_profile_combo_box_add (color_config, "display-profile", store, _("Select Monitor Color Profile"), _("_Monitor profile:"), GTK_GRID (grid), row++, size_group, object, "color-profile-path"); button = pika_prop_check_button_new (color_config, "display-profile-from-gdk", _("_Try to use the system monitor " "profile")); gtk_grid_attach (GTK_GRID (grid), button, 1, row, 1, 1); row++; prefs_enum_combo_box_add (color_config, "display-rendering-intent", 0, 0, _("_Rendering intent:"), GTK_GRID (grid), row++, size_group); button = pika_prop_check_button_new (color_config, "display-use-black-point-compensation", _("Use _black point compensation")); gtk_grid_attach (GTK_GRID (grid), button, 1, row, 1, 1); row++; prefs_boolean_combo_box_add (color_config, "display-optimize", _("Speed"), _("Precision / Color Fidelity"), _("_Optimize image display for:"), GTK_GRID (grid), row++, size_group); /* Print Simulation (Soft-proofing) */ vbox2 = prefs_frame_new (_("Soft-Proofing"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); row = 0; prefs_boolean_combo_box_add (color_config, "simulation-optimize", _("Speed"), _("Precision / Color Fidelity"), _("O_ptimize soft-proofing for:"), GTK_GRID (grid), row++, size_group); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_grid_attach (GTK_GRID (grid), hbox, 1, row, 1, 1); gtk_widget_show (hbox); row++; button = pika_prop_check_button_new (color_config, "simulation-gamut-check", _("Mar_k out of gamut colors")); gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); button = pika_prop_color_button_new (color_config, "out-of-gamut-color", _("Select Warning Color"), PREFS_COLOR_BUTTON_WIDTH, PREFS_COLOR_BUTTON_HEIGHT, PIKA_COLOR_AREA_FLAT); gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); pika_color_panel_set_context (PIKA_COLOR_PANEL (button), pika_get_user_context (pika)); /* Preferred profiles */ vbox2 = prefs_frame_new (_("Preferred Profiles"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); row = 0; prefs_profile_combo_box_add (color_config, "rgb-profile", store, _("Select Preferred RGB Color Profile"), _("_RGB profile:"), GTK_GRID (grid), row++, size_group, object, "color-profile-path"); prefs_profile_combo_box_add (color_config, "gray-profile", store, _("Select Preferred Grayscale Color Profile"), _("_Grayscale profile:"), GTK_GRID (grid), row++, size_group, object, "color-profile-path"); prefs_profile_combo_box_add (color_config, "cmyk-profile", store, _("Select CMYK Color Profile"), _("_CMYK profile:"), GTK_GRID (grid), row++, size_group, object, "color-profile-path"); /* Policies */ vbox2 = prefs_frame_new (_("Policies"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); button = prefs_enum_combo_box_add (object, "color-profile-policy", 0, 0, _("_File Open behavior:"), GTK_GRID (grid), 0, size_group); /* Filter Dialogs */ vbox2 = prefs_frame_new (_("Filter Dialogs"), GTK_CONTAINER (vbox), FALSE); button = prefs_check_button_add (object, "filter-tool-show-color-options", _("Show _advanced color options"), GTK_BOX (vbox2)); g_clear_object (&size_group); g_object_unref (store); } /***************************/ /* Image Import / Export */ /***************************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-prefs-import-export", _("Image Import & Export"), _("Image Import & Export"), PIKA_HELP_PREFS_IMPORT_EXPORT, NULL, &top_iter); pika_prefs_box_set_page_scrollable (PIKA_PREFS_BOX (prefs_box), vbox, TRUE); size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); /* Import Policies */ vbox2 = prefs_frame_new (_("Import Policies"), GTK_CONTAINER (vbox), FALSE); button = prefs_check_button_add (object, "import-promote-float", _("Promote imported images to " "_floating point precision"), GTK_BOX (vbox2)); vbox3 = prefs_frame_new (NULL, GTK_CONTAINER (vbox2), FALSE); g_object_bind_property (button, "active", vbox3, "sensitive", G_BINDING_SYNC_CREATE); button = prefs_check_button_add (object, "import-promote-dither", _("_Dither images when promoting to " "floating point"), GTK_BOX (vbox3)); button = prefs_check_button_add (object, "import-add-alpha", _("_Add an alpha channel to imported images"), GTK_BOX (vbox2)); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); button = prefs_enum_combo_box_add (object, "color-profile-policy", 0, 0, _("Color _profile policy:"), GTK_GRID (grid), 0, size_group); button = prefs_enum_combo_box_add (object, "metadata-rotation-policy", 0, 0, _("Metadata _rotation policy:"), GTK_GRID (grid), 1, size_group); /* Export Policies */ vbox2 = prefs_frame_new (_("Export Policies"), GTK_CONTAINER (vbox), FALSE); button = prefs_check_button_add (object, "export-color-profile", _("Export the i_mage's color profile by default"), GTK_BOX (vbox2)); button = prefs_check_button_add (object, "export-comment", _("Export the image's comment by default"), GTK_BOX (vbox2)); button = prefs_check_button_add (object, "export-thumbnail", _("Export the image's thumbnail by default"), GTK_BOX (vbox2)); button = prefs_check_button_add (object, "export-metadata-exif", /* Translators: label for * configuration option (checkbox). * It determines how file export * plug-ins handle Exif by default. */ _("Export _Exif metadata by default when available"), GTK_BOX (vbox2)); button = prefs_check_button_add (object, "export-metadata-xmp", /* Translators: label for * configuration option (checkbox). * It determines how file export * plug-ins handle XMP by default. */ _("Export _XMP metadata by default when available"), GTK_BOX (vbox2)); button = prefs_check_button_add (object, "export-metadata-iptc", /* Translators: label for * configuration option (checkbox). * It determines how file export * plug-ins handle IPTC by default. */ _("Export _IPTC metadata by default when available"), GTK_BOX (vbox2)); hbox = prefs_hint_box_new (PIKA_ICON_DIALOG_WARNING, _("Metadata can contain sensitive information.")); gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); /* Export File Type */ vbox2 = prefs_frame_new (_("Export File Type"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_enum_combo_box_add (object, "export-file-type", 0, 0, _("Default export file t_ype:"), GTK_GRID (grid), 0, size_group); /* Raw Image Importer */ vbox2 = prefs_frame_new (_("Raw Image Importer"), GTK_CONTAINER (vbox), TRUE); { GtkWidget *scrolled_window; GtkWidget *view; scrolled_window = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_IN); gtk_box_pack_start (GTK_BOX (vbox2), scrolled_window, TRUE, TRUE, 0); gtk_widget_show (scrolled_window); view = pika_plug_in_view_new (pika->plug_in_manager->display_raw_load_procs); pika_plug_in_view_set_plug_in (PIKA_PLUG_IN_VIEW (view), core_config->import_raw_plug_in); gtk_container_add (GTK_CONTAINER (scrolled_window), view); gtk_widget_show (view); g_signal_connect (view, "changed", G_CALLBACK (prefs_import_raw_procedure_callback), config); } g_clear_object (&size_group); /****************/ /* Playground */ /****************/ if (pika->show_playground) { vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-prefs-playground", _("Experimental Playground"), _("Playground"), PIKA_HELP_PREFS_PLAYGROUND, NULL, &top_iter); hbox = g_object_new (PIKA_TYPE_HINT_BOX, "icon-name", PIKA_ICON_DIALOG_WARNING, "hint", _("These features are unfinished, buggy " "and may crash PIKA. It is unadvised to " "use them unless you really know what " "you are doing or you intend to contribute " "patches."), NULL); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); /* Hardware Acceleration */ vbox2 = prefs_frame_new (_("Hardware Acceleration"), GTK_CONTAINER (vbox), FALSE); hbox = prefs_hint_box_new (PIKA_ICON_DIALOG_WARNING, _("OpenCL drivers and support are experimental, " "expect slowdowns and possible crashes " "(please report).")); gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); prefs_switch_add (object, "use-opencl", _("Use O_penCL"), GTK_BOX (vbox2), NULL); /* Very unstable tools */ vbox2 = prefs_frame_new (_("Insane Options"), GTK_CONTAINER (vbox), FALSE); button = prefs_check_button_add (object, "playground-npd-tool", _("_N-Point Deformation tool"), GTK_BOX (vbox2)); button = prefs_check_button_add (object, "playground-seamless-clone-tool", _("_Seamless Clone tool"), GTK_BOX (vbox2)); button = prefs_check_button_add (object, "playground-paint-select-tool", _("_Paint Select tool"), GTK_BOX (vbox2)); if (! gegl_has_operation ("gegl:paint-select")) { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), FALSE); gtk_widget_set_sensitive (button, FALSE); /* The tooltip is not translated on purpose. By the time it * hits stable release, I sure hope this won't be considered * an optional operation anymore. The info is still useful for * dev-release testers, but no need to bother translators with * a temporary string otherwise. */ pika_help_set_help_data (button, "Missing GEGL operation 'gegl:paint-select'.", NULL); } } /******************/ /* Tool Options */ /******************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-prefs-tool-options", C_("preferences", "Tool Options"), C_("preferences", "Tool Options"), PIKA_HELP_PREFS_TOOL_OPTIONS, NULL, &top_iter); pika_prefs_box_set_page_scrollable (PIKA_PREFS_BOX (prefs_box), vbox, TRUE); size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); /* General */ vbox2 = prefs_frame_new (_("General"), GTK_CONTAINER (vbox), FALSE); prefs_check_button_add (object, "edit-non-visible", _("Allow _editing on non-visible layers"), GTK_BOX (vbox2)); prefs_check_button_add (object, "save-tool-options", _("_Save tool options on exit"), GTK_BOX (vbox2)); button = prefs_button_add (PIKA_ICON_DOCUMENT_SAVE, _("Save Tool Options _Now"), GTK_BOX (vbox2)); g_signal_connect (button, "clicked", G_CALLBACK (prefs_tool_options_save_callback), pika); button2 = prefs_button_add (PIKA_ICON_RESET, _("_Reset Saved Tool Options to " "Default Values"), GTK_BOX (vbox2)); g_signal_connect (button2, "clicked", G_CALLBACK (prefs_tool_options_clear_callback), pika); g_object_set_data (G_OBJECT (button), "clear-button", button2); /* Scaling */ vbox2 = prefs_frame_new (_("Scaling"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_enum_combo_box_add (object, "interpolation-type", 0, 0, _("Default _interpolation:"), GTK_GRID (grid), 0, size_group); g_object_unref (size_group); size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); /* Global Brush, Pattern, ... */ vbox2 = prefs_frame_new (_("Paint Options Shared Between Tools"), GTK_CONTAINER (vbox), FALSE); prefs_check_button_add_with_icon (object, "global-brush", _("_Brush"), PIKA_ICON_BRUSH, GTK_BOX (vbox2), size_group); prefs_check_button_add_with_icon (object, "global-dynamics", _("_Dynamics"), PIKA_ICON_DYNAMICS, GTK_BOX (vbox2), size_group); prefs_check_button_add_with_icon (object, "global-pattern", _("_Pattern"), PIKA_ICON_PATTERN, GTK_BOX (vbox2), size_group); prefs_check_button_add_with_icon (object, "global-gradient", _("_Gradient"), PIKA_ICON_GRADIENT, GTK_BOX (vbox2), size_group); /* Move Tool */ vbox2 = prefs_frame_new (_("Move Tool"), GTK_CONTAINER (vbox), FALSE); prefs_check_button_add_with_icon (object, "move-tool-changes-active", _("Set _layer or path as active"), PIKA_ICON_TOOL_MOVE, GTK_BOX (vbox2), size_group); g_clear_object (&size_group); /*******************/ /* Default Image */ /*******************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-prefs-new-image", _("Default New Image"), _("Default Image"), PIKA_HELP_PREFS_NEW_IMAGE, NULL, &top_iter); pika_prefs_box_set_page_scrollable (PIKA_PREFS_BOX (prefs_box), vbox, TRUE); grid = prefs_grid_new (GTK_CONTAINER (vbox)); { GtkWidget *combo; combo = pika_container_combo_box_new (pika->templates, pika_get_user_context (pika), 16, 0); pika_grid_attach_aligned (GTK_GRID (grid), 0, 0, _("_Template:"), 0.0, 0.5, combo, 1); pika_container_view_select_items (PIKA_CONTAINER_VIEW (combo), NULL); g_signal_connect (combo, "select-items", G_CALLBACK (prefs_template_select_callback), core_config->default_image); } editor = pika_template_editor_new (core_config->default_image, pika, FALSE); gtk_widget_set_vexpand (editor, FALSE); pika_template_editor_show_advanced (PIKA_TEMPLATE_EDITOR (editor), TRUE); gtk_box_pack_start (GTK_BOX (vbox), editor, FALSE, FALSE, 0); gtk_widget_show (editor); /* Quick Mask Color */ vbox2 = prefs_frame_new (_("Quick Mask"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_color_button_add (object, "quick-mask-color", _("Quick Mask color:"), _("Set the default Quick Mask color"), GTK_GRID (grid), 0, NULL, pika_get_user_context (pika)); /**********************************/ /* Default Image / Default Grid */ /**********************************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-prefs-default-grid", _("Default Image Grid"), _("Default Grid"), PIKA_HELP_PREFS_DEFAULT_GRID, &top_iter, &child_iter); pika_prefs_box_set_page_scrollable (PIKA_PREFS_BOX (prefs_box), vbox, TRUE); /* Grid */ editor = pika_grid_editor_new (core_config->default_grid, pika_get_user_context (pika), pika_template_get_resolution_x (core_config->default_image), pika_template_get_resolution_y (core_config->default_image)); gtk_box_pack_start (GTK_BOX (vbox), editor, TRUE, TRUE, 0); gtk_widget_show (editor); /***************/ /* Interface */ /***************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-prefs-interface", _("User Interface"), _("Interface"), PIKA_HELP_PREFS_INTERFACE, NULL, &top_iter); pika_prefs_box_set_page_scrollable (PIKA_PREFS_BOX (prefs_box), vbox, TRUE); /* Language */ /* Only add the language entry if the iso-codes package is available. */ #ifdef HAVE_ISO_CODES vbox2 = prefs_frame_new (_("Language"), GTK_CONTAINER (vbox), FALSE); prefs_language_combo_box_add (object, "language", GTK_BOX (vbox2)); #endif /* Previews */ vbox2 = prefs_frame_new (_("Previews"), GTK_CONTAINER (vbox), FALSE); button = prefs_check_button_add (object, "layer-previews", _("_Enable layer & channel previews"), GTK_BOX (vbox2)); vbox3 = prefs_frame_new (NULL, GTK_CONTAINER (vbox2), FALSE); g_object_bind_property (button, "active", vbox3, "sensitive", G_BINDING_SYNC_CREATE); button = prefs_check_button_add (object, "group-layer-previews", _("Enable layer _group previews"), GTK_BOX (vbox3)); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_enum_combo_box_add (object, "layer-preview-size", 0, 0, _("_Default layer & channel preview size:"), GTK_GRID (grid), 0, NULL); prefs_enum_combo_box_add (object, "undo-preview-size", 0, 0, _("_Undo preview size:"), GTK_GRID (grid), 1, NULL); prefs_enum_combo_box_add (object, "navigation-preview-size", 0, 0, _("Na_vigation preview size:"), GTK_GRID (grid), 2, NULL); /* Item */ vbox2 = prefs_frame_new (_("Item search"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_enum_combo_box_add (object, "items-select-method", 0, 0, _("Pattern syntax for searching and selecting items:"), GTK_GRID (grid), 0, NULL); /* Keyboard Shortcuts */ vbox2 = prefs_frame_new (_("Keyboard Shortcuts"), GTK_CONTAINER (vbox), FALSE); button = prefs_button_add (PIKA_ICON_PREFERENCES_SYSTEM, _("Configure _Keyboard Shortcuts..."), GTK_BOX (vbox2)); g_signal_connect (button, "clicked", G_CALLBACK (prefs_keyboard_shortcuts_dialog), pika); prefs_check_button_add (object, "save-accels", _("_Save keyboard shortcuts on exit"), GTK_BOX (vbox2)); button = prefs_button_add (PIKA_ICON_DOCUMENT_SAVE, _("Save Keyboard Shortcuts _Now"), GTK_BOX (vbox2)); g_signal_connect (button, "clicked", G_CALLBACK (prefs_menus_save_callback), pika); button2 = prefs_button_add (PIKA_ICON_RESET, _("_Reset Keyboard Shortcuts to Default Values"), GTK_BOX (vbox2)); g_signal_connect (button2, "clicked", G_CALLBACK (prefs_menus_clear_callback), pika); g_object_set_data (G_OBJECT (button), "clear-button", button2); button = prefs_button_add (PIKA_ICON_EDIT_CLEAR, _("Remove _All Keyboard Shortcuts"), GTK_BOX (vbox2)); g_signal_connect (button, "clicked", G_CALLBACK (prefs_menus_remove_callback), pika); /***********************/ /* Interface / Theme */ /***********************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-prefs-theme", _("Theme"), _("Theme"), PIKA_HELP_PREFS_THEME, &top_iter, &child_iter); vbox2 = prefs_frame_new (_("Select Theme"), GTK_CONTAINER (vbox), TRUE); { GtkWidget *scrolled_win; GtkWidget *listbox; GtkWidget *scale; gchar **themes; gint n_themes; gint i; scrolled_win = gtk_scrolled_window_new (NULL, NULL); gtk_widget_set_size_request (scrolled_win, -1, 80); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win), GTK_SHADOW_IN); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start (GTK_BOX (vbox2), scrolled_win, TRUE, TRUE, 0); gtk_widget_show (scrolled_win); listbox = gtk_list_box_new (); gtk_list_box_set_selection_mode (GTK_LIST_BOX (listbox), GTK_SELECTION_BROWSE); gtk_container_add (GTK_CONTAINER (scrolled_win), listbox); gtk_widget_show (listbox); themes = themes_list_themes (pika, &n_themes); for (i = 0; i < n_themes; i++) { GtkWidget *row; GtkWidget *grid; GtkWidget *name_label, *folder_label; GFile *theme_dir = themes_get_theme_dir (pika, themes[i]); row = gtk_list_box_row_new (); g_object_set_data_full (G_OBJECT (row), "theme", g_strdup (themes[i]), g_free); grid = gtk_grid_new (); gtk_grid_set_column_spacing (GTK_GRID (grid), 12); gtk_container_add (GTK_CONTAINER (row), grid); name_label = gtk_label_new (themes[i]); g_object_set (name_label, "xalign", 0.0, NULL); gtk_grid_attach (GTK_GRID (grid), name_label, 1, 0, 1, 1); folder_label = gtk_label_new (pika_file_get_utf8_name (theme_dir)); g_object_set (folder_label, "xalign", 0.0, NULL); gtk_style_context_add_class (gtk_widget_get_style_context (folder_label), "dim-label"); gtk_grid_attach (GTK_GRID (grid), folder_label, 1, 1, 1, 1); gtk_widget_show_all (row); gtk_list_box_insert (GTK_LIST_BOX (listbox), row, -1); if (PIKA_GUI_CONFIG (object)->theme && ! strcmp (PIKA_GUI_CONFIG (object)->theme, themes[i])) { gtk_list_box_select_row (GTK_LIST_BOX (listbox), GTK_LIST_BOX_ROW (row)); } } g_strfreev (themes); g_signal_connect (listbox, "row-selected", G_CALLBACK (prefs_theme_select_callback), pika); prefs_check_button_add (object, "prefer-dark-theme", _("Use dark theme variant if available"), GTK_BOX (vbox2)); /* Override icon sizes. */ button = prefs_check_button_add (object, "override-theme-icon-size", _("_Override icon sizes set by the theme"), GTK_BOX (vbox2)); vbox3 = prefs_frame_new (NULL, GTK_CONTAINER (vbox2), FALSE); g_object_bind_property (button, "active", vbox3, "sensitive", G_BINDING_SYNC_CREATE); scale = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 0.0, 3.0, 1.0); /* 'draw_value' updates round_digits. So set it first. */ gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE); gtk_range_set_round_digits (GTK_RANGE (scale), 0.0); gtk_scale_add_mark (GTK_SCALE (scale), 0.0, GTK_POS_BOTTOM, _("Small")); gtk_scale_add_mark (GTK_SCALE (scale), 1.0, GTK_POS_BOTTOM, _("Medium")); gtk_scale_add_mark (GTK_SCALE (scale), 2.0, GTK_POS_BOTTOM, _("Large")); gtk_scale_add_mark (GTK_SCALE (scale), 3.0, GTK_POS_BOTTOM, _("Huge")); gtk_range_set_value (GTK_RANGE (scale), (gdouble) PIKA_GUI_CONFIG (object)->custom_icon_size); g_signal_connect (G_OBJECT (scale), "value-changed", G_CALLBACK (prefs_icon_size_value_changed), PIKA_GUI_CONFIG (object)); g_signal_connect (G_OBJECT (object), "notify::custom-icon-size", G_CALLBACK (prefs_gui_config_notify_icon_size), scale); gtk_box_pack_start (GTK_BOX (vbox3), scale, FALSE, FALSE, 0); gtk_widget_show (scale); /* Font sizes. */ vbox3 = prefs_frame_new (_("Font Scaling"), GTK_CONTAINER (vbox2), FALSE); pika_help_set_help_data (vbox3, _("Font scaling will not work with themes using absolute sizes."), NULL); scale = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 50, 200, 10); gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_BOTTOM); gtk_scale_add_mark (GTK_SCALE (scale), 50.0, GTK_POS_BOTTOM, _("50%")); gtk_scale_add_mark (GTK_SCALE (scale), 100.0, GTK_POS_BOTTOM, _("100%")); gtk_scale_add_mark (GTK_SCALE (scale), 200.0, GTK_POS_BOTTOM, _("200%")); gtk_range_set_value (GTK_RANGE (scale), (gdouble) PIKA_GUI_CONFIG (object)->font_relative_size * 100.0); g_signal_connect (G_OBJECT (scale), "value-changed", G_CALLBACK (prefs_font_size_value_changed), PIKA_GUI_CONFIG (object)); g_signal_connect (G_OBJECT (object), "notify::font-relative-size", G_CALLBACK (prefs_gui_config_notify_font_size), scale); gtk_box_pack_start (GTK_BOX (vbox3), scale, FALSE, FALSE, 0); gtk_widget_show (scale); /* Reload Current Theme button */ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); button = prefs_button_add (PIKA_ICON_VIEW_REFRESH, _("Reload C_urrent Theme"), GTK_BOX (hbox)); g_signal_connect (button, "clicked", G_CALLBACK (prefs_theme_reload_callback), pika); } /****************************/ /* Interface / Icon Theme */ /****************************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-prefs-icon-theme", _("Icon Theme"), _("Icon Theme"), PIKA_HELP_PREFS_ICON_THEME, &top_iter, &child_iter); vbox2 = prefs_frame_new (_("Select an Icon Theme"), GTK_CONTAINER (vbox), TRUE); { GtkWidget *scrolled_win; GtkWidget *listbox; gchar **icon_themes; gint scale_factor; gint n_icon_themes; gint i; scrolled_win = gtk_scrolled_window_new (NULL, NULL); gtk_widget_set_size_request (scrolled_win, -1, 80); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win), GTK_SHADOW_IN); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start (GTK_BOX (vbox2), scrolled_win, TRUE, TRUE, 0); gtk_widget_show (scrolled_win); listbox = gtk_list_box_new (); gtk_list_box_set_selection_mode (GTK_LIST_BOX (listbox), GTK_SELECTION_BROWSE); gtk_container_add (GTK_CONTAINER (scrolled_win), listbox); gtk_widget_show (listbox); /* _("Icon Theme"), */ /* _("Folder"), */ scale_factor = gtk_widget_get_scale_factor (scrolled_win); icon_themes = icon_themes_list_themes (pika, &n_icon_themes); for (i = 0; i < n_icon_themes; i++) { GtkWidget *row; GtkWidget *grid; GtkWidget *image; GtkWidget *name_label, *folder_label; GFile *icon_theme_dir = icon_themes_get_theme_dir (pika, icon_themes[i]); GFile *icon_theme_search_path = g_file_get_parent (icon_theme_dir); GtkIconTheme *theme; gchar *example; cairo_surface_t *surface; theme = gtk_icon_theme_new (); gtk_icon_theme_prepend_search_path (theme, pika_file_get_utf8_name (icon_theme_search_path)); g_object_unref (icon_theme_search_path); gtk_icon_theme_set_custom_theme (theme, icon_themes[i]); example = gtk_icon_theme_get_example_icon_name (theme); if (! example) { /* If the icon theme didn't explicitly specify an example * icon, try "pika-mascot". */ example = g_strdup ("pika-mascot-symbolic"); } surface = gtk_icon_theme_load_surface (theme, example, 30, scale_factor, NULL, GTK_ICON_LOOKUP_GENERIC_FALLBACK, NULL); row = gtk_list_box_row_new (); g_object_set_data_full (G_OBJECT (row), "icon-theme", g_strdup (icon_themes[i]), g_free); grid = gtk_grid_new (); gtk_grid_set_column_spacing (GTK_GRID (grid), 12); gtk_container_add (GTK_CONTAINER (row), grid); image = gtk_image_new_from_surface (surface); gtk_grid_attach (GTK_GRID (grid), image, 0, 0, 1, 2); name_label = gtk_label_new (icon_themes[i]); g_object_set (name_label, "xalign", 0.0, NULL); gtk_grid_attach (GTK_GRID (grid), name_label, 1, 0, 1, 1); folder_label = gtk_label_new (pika_file_get_utf8_name (icon_theme_dir)); g_object_set (folder_label, "xalign", 0.0, NULL); gtk_style_context_add_class (gtk_widget_get_style_context (folder_label), "dim-label"); gtk_grid_attach (GTK_GRID (grid), folder_label, 1, 1, 1, 1); gtk_widget_show_all (row); gtk_list_box_insert (GTK_LIST_BOX (listbox), row, -1); g_object_unref (theme); cairo_surface_destroy (surface); g_free (example); if (PIKA_GUI_CONFIG (object)->icon_theme && ! strcmp (PIKA_GUI_CONFIG (object)->icon_theme, icon_themes[i])) { gtk_list_box_select_row (GTK_LIST_BOX (listbox), GTK_LIST_BOX_ROW (row)); } } g_strfreev (icon_themes); g_signal_connect (listbox, "row-selected", G_CALLBACK (prefs_icon_theme_select_callback), pika); prefs_check_button_add (object, "prefer-symbolic-icons", _("Use symbolic icons if available"), GTK_BOX (vbox2)); } /*************************/ /* Interface / Toolbox */ /*************************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-prefs-toolbox", _("Toolbox"), _("Toolbox"), PIKA_HELP_PREFS_TOOLBOX, &top_iter, &child_iter); size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); /* Appearance */ vbox2 = prefs_frame_new (_("Appearance"), GTK_CONTAINER (vbox), FALSE); prefs_check_button_add_with_icon (object, "toolbox-mascot", _("Show PIKA _logo (drag-and-drop target)"), PIKA_ICON_MASCOT, GTK_BOX (vbox2), size_group); prefs_check_button_add_with_icon (object, "toolbox-color-area", _("Show _foreground & background color"), PIKA_ICON_COLORS_DEFAULT, GTK_BOX (vbox2), size_group); prefs_check_button_add_with_icon (object, "toolbox-foo-area", _("Show active _brush, pattern & gradient"), PIKA_ICON_BRUSH, GTK_BOX (vbox2), size_group); prefs_check_button_add_with_icon (object, "toolbox-image-area", _("Show active _image"), PIKA_ICON_IMAGE, GTK_BOX (vbox2), size_group); separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); gtk_box_pack_start (GTK_BOX (vbox2), separator, FALSE, FALSE, 0); gtk_widget_show (separator); prefs_check_button_add_with_icon (object, "toolbox-groups", _("Use tool _groups"), NULL, GTK_BOX (vbox2), size_group); g_clear_object (&size_group); /* Tool Editor */ vbox2 = prefs_frame_new (_("Tools Configuration"), GTK_CONTAINER (vbox), TRUE); tool_editor = pika_tool_editor_new (pika->tool_item_list, pika->user_context, PIKA_VIEW_SIZE_SMALL, 1); gtk_box_pack_start (GTK_BOX (vbox2), tool_editor, TRUE, TRUE, 0); gtk_widget_show (tool_editor); /*********************************/ /* Interface / Dialog Defaults */ /*********************************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), /* FIXME need an icon */ "pika-prefs-controllers", _("Dialog Defaults"), _("Dialog Defaults"), PIKA_HELP_PREFS_DIALOG_DEFAULTS, &top_iter, &child_iter); pika_prefs_box_set_page_scrollable (PIKA_PREFS_BOX (prefs_box), vbox, TRUE); button = pika_prefs_box_set_page_resettable (PIKA_PREFS_BOX (prefs_box), vbox, _("Reset Dialog _Defaults")); g_signal_connect (button, "clicked", G_CALLBACK (prefs_dialog_defaults_reset), config); size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); /* Color profile import dialog */ vbox2 = prefs_frame_new (_("Color Profile Import Dialog"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); button = prefs_enum_combo_box_add (object, "color-profile-policy", 0, 0, _("Color profile policy:"), GTK_GRID (grid), 0, size_group); /* All color profile chooser dialogs */ vbox2 = prefs_frame_new (_("Color Profile File Dialogs"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_file_chooser_button_add (object, "color-profile-path", _("Profile folder:"), _("Select Default Folder for Color Profiles"), GTK_GRID (grid), 0, size_group); /* Convert to Color Profile Dialog */ vbox2 = prefs_frame_new (_("Convert to Color Profile Dialog"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_enum_combo_box_add (object, "image-convert-profile-intent", 0, 0, _("Rendering intent:"), GTK_GRID (grid), 0, size_group); prefs_check_button_add (object, "image-convert-profile-black-point-compensation", _("Black point compensation"), GTK_BOX (vbox2)); /* Convert Precision Dialog */ vbox2 = prefs_frame_new (_("Precision Conversion Dialog"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_enum_combo_box_add (object, "image-convert-precision-layer-dither-method", 0, 0, _("Dither layers:"), GTK_GRID (grid), 0, size_group); prefs_enum_combo_box_add (object, "image-convert-precision-text-layer-dither-method", 0, 0, _("Dither text layers:"), GTK_GRID (grid), 1, size_group); prefs_enum_combo_box_add (object, "image-convert-precision-channel-dither-method", 0, 0, _("Dither channels/masks:"), GTK_GRID (grid), 2, size_group); /* Convert Indexed Dialog */ vbox2 = prefs_frame_new (_("Indexed Conversion Dialog"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_enum_combo_box_add (object, "image-convert-indexed-palette-type", 0, 0, _("Colormap:"), GTK_GRID (grid), 0, size_group); prefs_spin_button_add (object, "image-convert-indexed-max-colors", 1.0, 8.0, 0, _("Maximum number of colors:"), GTK_GRID (grid), 1, size_group); prefs_check_button_add (object, "image-convert-indexed-remove-duplicates", _("Remove unused and duplicate colors " "from colormap"), GTK_BOX (vbox2)); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_enum_combo_box_add (object, "image-convert-indexed-dither-type", 0, 0, _("Color dithering:"), GTK_GRID (grid), 0, size_group); prefs_check_button_add (object, "image-convert-indexed-dither-alpha", _("Enable dithering of transparency"), GTK_BOX (vbox2)); prefs_check_button_add (object, "image-convert-indexed-dither-text-layers", _("Enable dithering of text layers"), GTK_BOX (vbox2)); /* Filter Dialogs */ vbox2 = prefs_frame_new (_("Filter Dialogs"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_spin_button_add (object, "filter-tool-max-recent", 1.0, 8.0, 0, _("Keep recent settings:"), GTK_GRID (grid), 1, size_group); button = prefs_check_button_add (object, "filter-tool-use-last-settings", _("Default to the last used settings"), GTK_BOX (vbox2)); button = prefs_check_button_add (object, "filter-tool-show-color-options", _("Show advanced color options"), GTK_BOX (vbox2)); /* Canvas Size Dialog */ vbox2 = prefs_frame_new (_("Canvas Size Dialog"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_enum_combo_box_add (object, "image-resize-fill-type", 0, 0, _("Fill with:"), GTK_GRID (grid), 0, size_group); prefs_enum_combo_box_add (object, "image-resize-layer-set", 0, 0, _("Resize layers:"), GTK_GRID (grid), 1, size_group); prefs_check_button_add (object, "image-resize-resize-text-layers", _("Resize text layers"), GTK_BOX (vbox2)); /* New Layer Dialog */ vbox2 = prefs_frame_new (_("New Layer Dialog"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_entry_add (object, "layer-new-name", _("Layer name:"), GTK_GRID (grid), 0, size_group); prefs_enum_combo_box_add (object, "layer-new-fill-type", 0, 0, _("Fill type:"), GTK_GRID (grid), 1, size_group); /* Layer Boundary Size Dialog */ vbox2 = prefs_frame_new (_("Layer Boundary Size Dialog"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_enum_combo_box_add (object, "layer-resize-fill-type", 0, 0, _("Fill with:"), GTK_GRID (grid), 0, size_group); /* Add Layer Mask Dialog */ vbox2 = prefs_frame_new (_("Add Layer Mask Dialog"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_enum_combo_box_add (object, "layer-add-mask-type", 0, 0, _("Layer mask type:"), GTK_GRID (grid), 0, size_group); prefs_check_button_add (object, "layer-add-mask-invert", _("Invert mask"), GTK_BOX (vbox2)); /* Merge Layers Dialog */ vbox2 = prefs_frame_new (_("Merge Layers Dialog"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_enum_combo_box_add (object, "layer-merge-type", PIKA_EXPAND_AS_NECESSARY, PIKA_CLIP_TO_BOTTOM_LAYER, _("Merged layer size:"), GTK_GRID (grid), 0, size_group); prefs_check_button_add (object, "layer-merge-active-group-only", _("Merge within active groups only"), GTK_BOX (vbox2)); prefs_check_button_add (object, "layer-merge-discard-invisible", _("Discard invisible layers"), GTK_BOX (vbox2)); /* New Channel Dialog */ vbox2 = prefs_frame_new (_("New Channel Dialog"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_entry_add (object, "channel-new-name", _("Channel name:"), GTK_GRID (grid), 0, size_group); prefs_color_button_add (object, "channel-new-color", _("Color and opacity:"), _("Default New Channel Color and Opacity"), GTK_GRID (grid), 1, size_group, pika_get_user_context (pika)); /* New Path Dialog */ vbox2 = prefs_frame_new (_("New Path Dialog"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_entry_add (object, "path-new-name", _("Path name:"), GTK_GRID (grid), 0, size_group); /* Export Path Dialog */ vbox2 = prefs_frame_new (_("Export Paths Dialog"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_file_chooser_button_add (object, "path-export-path", _("Export folder:"), _("Select Default Folder for Exporting Paths"), GTK_GRID (grid), 0, size_group); prefs_check_button_add (object, "path-export-active-only", _("Export the selected paths only"), GTK_BOX (vbox2)); /* Import Path Dialog */ vbox2 = prefs_frame_new (_("Import Paths Dialog"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_file_chooser_button_add (object, "path-import-path", _("Import folder:"), _("Select Default Folder for Importing Paths"), GTK_GRID (grid), 0, size_group); prefs_check_button_add (object, "path-import-merge", _("Merge imported paths"), GTK_BOX (vbox2)); prefs_check_button_add (object, "path-import-scale", _("Scale imported paths"), GTK_BOX (vbox2)); /* Feather Selection Dialog */ vbox2 = prefs_frame_new (_("Feather Selection Dialog"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_spin_button_add (object, "selection-feather-radius", 1.0, 10.0, 2, _("Feather radius:"), GTK_GRID (grid), 0, size_group); prefs_check_button_add (object, "selection-feather-edge-lock", _("Selected areas continue outside the image"), GTK_BOX (vbox2)); /* Grow Selection Dialog */ vbox2 = prefs_frame_new (_("Grow Selection Dialog"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_spin_button_add (object, "selection-grow-radius", 1.0, 10.0, 0, _("Grow radius:"), GTK_GRID (grid), 0, size_group); /* Shrink Selection Dialog */ vbox2 = prefs_frame_new (_("Shrink Selection Dialog"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_spin_button_add (object, "selection-shrink-radius", 1.0, 10.0, 0, _("Shrink radius:"), GTK_GRID (grid), 0, size_group); prefs_check_button_add (object, "selection-shrink-edge-lock", _("Selected areas continue outside the image"), GTK_BOX (vbox2)); /* Border Selection Dialog */ vbox2 = prefs_frame_new (_("Border Selection Dialog"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_spin_button_add (object, "selection-border-radius", 1.0, 10.0, 0, _("Border radius:"), GTK_GRID (grid), 0, size_group); prefs_enum_combo_box_add (object, "selection-border-style", 0, 0, _("Border style:"), GTK_GRID (grid), 1, size_group); prefs_check_button_add (object, "selection-border-edge-lock", _("Selected areas continue outside the image"), GTK_BOX (vbox2)); /* Fill Options Dialog */ vbox2 = prefs_frame_new (_("Fill Selection Outline & Fill Path Dialogs"), GTK_CONTAINER (vbox), FALSE); editor = pika_fill_editor_new (PIKA_DIALOG_CONFIG (object)->fill_options, FALSE, FALSE); gtk_box_pack_start (GTK_BOX (vbox2), editor, FALSE, FALSE, 0); gtk_widget_show (editor); /* Stroke Options Dialog */ vbox2 = prefs_frame_new (_("Stroke Selection & Stroke Path Dialogs"), GTK_CONTAINER (vbox), FALSE); /* The stroke line width physical values could be based on either the * x or y resolution, some average, or whatever which makes a bit of * sense. There is no perfect answer. The actual stroke dialog though * uses the y resolution on the opened image. So using the y resolution * of the default image seems like the best compromise in the preferences. */ editor = pika_stroke_editor_new (PIKA_DIALOG_CONFIG (object)->stroke_options, pika_template_get_resolution_y (core_config->default_image), FALSE, FALSE); gtk_box_pack_start (GTK_BOX (vbox2), editor, FALSE, FALSE, 0); gtk_widget_show (editor); g_clear_object (&size_group); /*****************************/ /* Interface / Help System */ /*****************************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-prefs-help-system", _("Help System"), _("Help System"), PIKA_HELP_PREFS_HELP, &top_iter, &child_iter); size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); /* General */ vbox2 = prefs_frame_new (_("General"), GTK_CONTAINER (vbox), FALSE); prefs_check_button_add (object, "show-help-button", _("Show help _buttons"), GTK_BOX (vbox2)); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); button = prefs_boolean_combo_box_add (object, "user-manual-online", _("Use the online version"), _("Use a locally installed copy"), _("U_ser manual:"), GTK_GRID (grid), 0, size_group); pika_help_set_help_data (button, NULL, NULL); manuals = pika_help_get_installed_languages (); entry = NULL; if (manuals != NULL) { gchar *help_locales = NULL; entry = pika_language_combo_box_new (TRUE, _("User interface language")); g_object_get (config, "help-locales", &help_locales, NULL); if (help_locales && strlen (help_locales)) { gchar *sep; sep = strchr (help_locales, ':'); if (sep) *sep = '\0'; } if (help_locales) { pika_language_combo_box_set_code (PIKA_LANGUAGE_COMBO_BOX (entry), help_locales); g_free (help_locales); } else { pika_language_combo_box_set_code (PIKA_LANGUAGE_COMBO_BOX (entry), ""); } g_signal_connect (entry, "changed", G_CALLBACK (prefs_help_language_change_callback), pika); gtk_grid_attach (GTK_GRID (grid), entry, 1, 1, 1, 1); gtk_widget_show (entry); } if (pika_help_user_manual_is_installed (pika)) { hbox = prefs_hint_box_new (PIKA_ICON_DIALOG_INFORMATION, _("There's a local installation " "of the user manual.")); } else { hbox = prefs_hint_box_new (PIKA_ICON_DIALOG_WARNING, _("The user manual is not installed " "locally.")); } if (manuals) { g_object_set_data (G_OBJECT (hbox), "pika", pika); g_signal_connect (entry, "changed", G_CALLBACK (prefs_help_language_change_callback2), hbox); g_list_free_full (manuals, g_free); } gtk_grid_attach (GTK_GRID (grid), hbox, 1, 2, 1, 1); gtk_widget_show (hbox); /* Help Browser */ #ifdef HAVE_WEBKIT /* If there is no webkit available, assume we are on a platform * that doesn't use the help browser, so don't bother showing * the combo. */ vbox2 = prefs_frame_new (_("Help Browser"), GTK_CONTAINER (vbox), FALSE); if (pika_help_browser_is_installed (pika)) { grid = prefs_grid_new (GTK_CONTAINER (vbox2)); button = prefs_enum_combo_box_add (object, "help-browser", 0, 0, _("H_elp browser to use:"), GTK_GRID (grid), 0, size_group); } else { hbox = prefs_hint_box_new (PIKA_ICON_DIALOG_WARNING, _("The PIKA help browser doesn't seem to " "be installed. Using the web browser " "instead.")); gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); g_object_set (config, "help-browser", PIKA_HELP_BROWSER_WEB_BROWSER, NULL); } #else g_object_set (config, "help-browser", PIKA_HELP_BROWSER_WEB_BROWSER, NULL); #endif /* HAVE_WEBKIT */ /* Action Search */ vbox2 = prefs_frame_new (_("Action Search"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_spin_button_add (object, "action-history-size", 1.0, 10.0, 0, _("_Maximum History Size:"), GTK_GRID (grid), 0, size_group); button = prefs_button_add (PIKA_ICON_SHRED, _("C_lear Action History"), GTK_BOX (vbox2)); g_signal_connect (button, "clicked", G_CALLBACK (prefs_search_clear_callback), pika); g_clear_object (&size_group); /*************************/ /* Interface / Display */ /*************************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-prefs-display", _("Display"), _("Display"), PIKA_HELP_PREFS_DISPLAY, &top_iter, &child_iter); pika_prefs_box_set_page_scrollable (PIKA_PREFS_BOX (prefs_box), vbox, TRUE); size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); /* Transparency */ vbox2 = prefs_frame_new (_("Transparency"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_enum_combo_box_add (object, "transparency-type", 0, 0, _("_Check style:"), GTK_GRID (grid), 0, size_group); button = pika_prop_label_color_new (object, "transparency-custom-color1", TRUE); pika_grid_attach_aligned (GTK_GRID (grid), 0, 1, NULL, 0.0, 0.5, button, 1); gtk_widget_set_hexpand (button, FALSE); pika_color_button_set_color_config (PIKA_COLOR_BUTTON (pika_label_color_get_color_widget (PIKA_LABEL_COLOR (button))), pika->config->color_management); gtk_widget_set_sensitive (button, display_config->transparency_type == PIKA_CHECK_TYPE_CUSTOM_CHECKS); g_signal_connect (object, "notify::transparency-type", G_CALLBACK (prefs_check_style_callback), button); button = pika_prop_label_color_new (object, "transparency-custom-color2", TRUE); pika_grid_attach_aligned (GTK_GRID (grid), 0, 2, NULL, 0.0, 0.5, button, 1); gtk_widget_set_hexpand (button, FALSE); pika_color_button_set_color_config (PIKA_COLOR_BUTTON (pika_label_color_get_color_widget (PIKA_LABEL_COLOR (button))), pika->config->color_management); gtk_widget_set_sensitive (button, display_config->transparency_type == PIKA_CHECK_TYPE_CUSTOM_CHECKS); g_signal_connect (object, "notify::transparency-type", G_CALLBACK (prefs_check_style_callback), button); prefs_enum_combo_box_add (object, "transparency-size", 0, 0, _("Check _size:"), GTK_GRID (grid), 3, size_group); /* Zoom Quality */ vbox2 = prefs_frame_new (_("Zoom Quality"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_enum_combo_box_add (object, "zoom-quality", 0, 0, _("_Zoom quality:"), GTK_GRID (grid), 0, size_group); /* Monitor Resolution */ vbox2 = prefs_frame_new (_("Monitor Resolution"), GTK_CONTAINER (vbox), FALSE); { gchar *pixels_per_unit = g_strconcat (_("Pixels"), "/%s", NULL); entry = pika_prop_coordinates_new (object, "monitor-xresolution", "monitor-yresolution", NULL, pixels_per_unit, PIKA_SIZE_ENTRY_UPDATE_RESOLUTION, 0.0, 0.0, TRUE); g_free (pixels_per_unit); } gtk_grid_set_column_spacing (GTK_GRID (entry), 2); gtk_grid_set_row_spacing (GTK_GRID (entry), 2); pika_size_entry_attach_label (PIKA_SIZE_ENTRY (entry), _("Horizontal"), 0, 1, 0.0); pika_size_entry_attach_label (PIKA_SIZE_ENTRY (entry), _("Vertical"), 0, 2, 0.0); pika_size_entry_attach_label (PIKA_SIZE_ENTRY (entry), _("ppi"), 1, 4, 0.0); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_widget_set_halign (hbox, GTK_ALIGN_START); gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 24); gtk_widget_set_sensitive (entry, ! display_config->monitor_res_from_gdk); group = NULL; { gdouble xres; gdouble yres; gchar *str; pika_get_monitor_resolution (gdk_display_get_monitor (gdk_display_get_default (), 0), &xres, &yres); str = g_strdup_printf (_("_Detect automatically (currently %d × %d ppi)"), ROUND (xres), ROUND (yres)); button = gtk_radio_button_new_with_mnemonic (group, str); g_free (str); } group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button)); gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0); gtk_widget_show (button); g_object_set_data (G_OBJECT (button), "monitor_resolution_sizeentry", entry); g_signal_connect (button, "toggled", G_CALLBACK (prefs_resolution_source_callback), config); button = gtk_radio_button_new_with_mnemonic (group, _("_Enter manually")); group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button)); gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0); gtk_widget_show (button); gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); if (! display_config->monitor_res_from_gdk) gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); calibrate_button = gtk_button_new_with_mnemonic (_("C_alibrate...")); label = gtk_bin_get_child (GTK_BIN (calibrate_button)); g_object_set (label, "margin-start", 4, "margin-end", 4, NULL); gtk_box_pack_start (GTK_BOX (hbox), calibrate_button, FALSE, FALSE, 0); gtk_widget_show (calibrate_button); gtk_widget_set_sensitive (calibrate_button, ! display_config->monitor_res_from_gdk); g_object_bind_property (button, "active", entry, "sensitive", G_BINDING_SYNC_CREATE); g_object_bind_property (button, "active", calibrate_button, "sensitive", G_BINDING_SYNC_CREATE); g_signal_connect (calibrate_button, "clicked", G_CALLBACK (prefs_resolution_calibrate_callback), entry); g_clear_object (&size_group); /***********************************/ /* Interface / Window Management */ /***********************************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-prefs-window-management", _("Window Management"), _("Window Management"), PIKA_HELP_PREFS_WINDOW_MANAGEMENT, &top_iter, &child_iter); vbox2 = prefs_frame_new (_("Window Manager Hints"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_enum_combo_box_add (object, "dock-window-hint", 0, 0, _("Hint for _docks and toolbox:"), GTK_GRID (grid), 1, NULL); vbox2 = prefs_frame_new (_("Focus"), GTK_CONTAINER (vbox), FALSE); prefs_check_button_add (object, "activate-on-focus", _("Activate the _focused image"), GTK_BOX (vbox2)); /* Window Positions */ vbox2 = prefs_frame_new (_("Window Positions"), GTK_CONTAINER (vbox), FALSE); prefs_check_button_add (object, "save-session-info", _("_Save window positions on exit"), GTK_BOX (vbox2)); prefs_check_button_add (object, "restore-monitor", _("Open windows on the same _monitor they were open before"), GTK_BOX (vbox2)); button = prefs_button_add (PIKA_ICON_DOCUMENT_SAVE, _("Save Window Positions _Now"), GTK_BOX (vbox2)); g_signal_connect (button, "clicked", G_CALLBACK (prefs_session_save_callback), pika); button2 = prefs_button_add (PIKA_ICON_RESET, _("_Reset Saved Window Positions to " "Default Values"), GTK_BOX (vbox2)); g_signal_connect (button2, "clicked", G_CALLBACK (prefs_session_clear_callback), pika); g_object_set_data (G_OBJECT (button), "clear-button", button2); /************************/ /* Canvas Interaction */ /************************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-prefs-image-windows", _("Canvas Interaction"), _("Canvas Interaction"), PIKA_HELP_PREFS_CANVAS_INTERACTION, NULL, &top_iter); pika_prefs_box_set_page_scrollable (PIKA_PREFS_BOX (prefs_box), vbox, TRUE); size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); /* Space Bar */ vbox2 = prefs_frame_new (_("Space Bar"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_enum_combo_box_add (object, "space-bar-action", 0, 0, _("_While space bar is pressed:"), GTK_GRID (grid), 0, size_group); /* Zoom by drag Behavior */ vbox2 = prefs_frame_new (_("Zoom"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_enum_combo_box_add (object, "drag-zoom-mode", 0, 0, _("Dra_g-to-zoom behavior:"), GTK_GRID (grid), 0, size_group); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_spin_button_add (object, "drag-zoom-speed", 5.0, 25.0, 0, _("Drag-to-zoom spe_ed:"), GTK_GRID (grid), 0, size_group); /************************************/ /* Canvas Interaction / Modifiers */ /************************************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), /* TODO: custom icon. */ "pika-prefs-image-windows", _("Modifiers"), _("Modifiers"), PIKA_HELP_PREFS_CANVAS_MODIFIERS, &top_iter, &child_iter); vbox2 = pika_modifiers_editor_new (PIKA_MODIFIERS_MANAGER (display_config->modifiers_manager), pika); gtk_widget_show (vbox2); gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0); button2 = prefs_button_add (PIKA_ICON_RESET, _("_Reset Saved Modifiers Settings to " "Default Values"), GTK_BOX (vbox)); g_signal_connect (button2, "clicked", G_CALLBACK (prefs_modifiers_clear_callback), vbox2); g_object_set_data (G_OBJECT (button), "clear-button", button2); /***********************************/ /* Canvas Interaction / Snapping */ /***********************************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-prefs-image-windows-snapping", _("Snapping Behavior"), _("Snapping"), PIKA_HELP_PREFS_IMAGE_WINDOW_SNAPPING, &top_iter, &child_iter); prefs_behavior_options_frame_add (pika, G_OBJECT (display_config->default_view), _("Default Behavior in Normal Mode"), GTK_CONTAINER (vbox)); prefs_behavior_options_frame_add (pika, G_OBJECT (display_config->default_fullscreen_view), _("Default Behavior in Fullscreen Mode"), GTK_CONTAINER (vbox)); /* Snapping Distance */ vbox2 = prefs_frame_new (_("General"), GTK_CONTAINER (vbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_spin_button_add (object, "snap-distance", 1.0, 5.0, 0, _("_Snapping distance:"), GTK_GRID (grid), 0, NULL); /*******************/ /* Image Windows */ /*******************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-prefs-image-windows", _("Image Windows"), _("Image Windows"), PIKA_HELP_PREFS_IMAGE_WINDOW, NULL, &top_iter); pika_prefs_box_set_page_scrollable (PIKA_PREFS_BOX (prefs_box), vbox, TRUE); size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); /* General */ vbox2 = prefs_frame_new (_("General"), GTK_CONTAINER (vbox), FALSE); /* See app/display/pikaimagewindow.c: the code path where "custom-title-bar" * is verified never happens for macOS which always uses * gtk_application_set_menubar() instead. */ #ifndef GDK_WINDOWING_QUARTZ prefs_check_button_add (object, "custom-title-bar", _("Merge menu and title bar"), GTK_BOX (vbox2)); hbox = prefs_hint_box_new (PIKA_ICON_DIALOG_WARNING, _("PIKA will try to convince your system not to decorate image windows. " "If it doesn't work properly on your system " "(i.e. you get 2 title bars), please report.")); gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); g_object_bind_property (object, "custom-title-bar", hbox, "visible", G_BINDING_SYNC_CREATE); #endif prefs_check_button_add (object, "default-show-all", _("Use \"Show _all\" by default"), GTK_BOX (vbox2)); prefs_check_button_add (object, "default-dot-for-dot", _("Use \"_Dot for dot\" by default"), GTK_BOX (vbox2)); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_spin_button_add (object, "marching-ants-speed", 1.0, 10.0, 0, _("Marching ants s_peed:"), GTK_GRID (grid), 0, size_group); /* Zoom & Resize Behavior */ vbox2 = prefs_frame_new (_("Zoom & Resize Behavior"), GTK_CONTAINER (vbox), FALSE); prefs_check_button_add (object, "resize-windows-on-zoom", _("Resize window on _zoom"), GTK_BOX (vbox2)); prefs_check_button_add (object, "resize-windows-on-resize", _("Resize window on image _size change"), GTK_BOX (vbox2)); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); prefs_boolean_combo_box_add (object, "initial-zoom-to-fit", _("Show entire image"), "1:1", _("Initial zoom _ratio:"), GTK_GRID (grid), 0, size_group); /********************************/ /* Image Windows / Appearance */ /********************************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-prefs-image-windows-appearance", _("Image Window Appearance"), _("Appearance"), PIKA_HELP_PREFS_IMAGE_WINDOW_APPEARANCE, &top_iter, &child_iter); pika_prefs_box_set_page_scrollable (PIKA_PREFS_BOX (prefs_box), vbox, TRUE); prefs_display_options_frame_add (pika, G_OBJECT (display_config->default_view), _("Default Appearance in Normal Mode"), GTK_CONTAINER (vbox)); prefs_display_options_frame_add (pika, G_OBJECT (display_config->default_fullscreen_view), _("Default Appearance in Fullscreen Mode"), GTK_CONTAINER (vbox)); /****************************************************/ /* Image Windows / Image Title & Statusbar Format */ /****************************************************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-prefs-image-title", _("Image Title & Statusbar Format"), _("Title & Status"), PIKA_HELP_PREFS_IMAGE_WINDOW_TITLE, &top_iter, &child_iter); { const gchar *format_strings[] = { NULL, NULL, "%f-%p.%i (%t) %z%%", "%f-%p.%i (%t) %d:%s", "%f-%p.%i (%t) %wx%h", "%f-%p-%i (%t) %wx%h (%xx%y)" }; const gchar *format_names[] = { N_("Current format"), N_("Default format"), N_("Show zoom percentage"), N_("Show zoom ratio"), N_("Show image size"), N_("Show drawable size") }; struct { gchar *current_setting; const gchar *default_setting; const gchar *title; const gchar *property_name; } formats[] = { { NULL, PIKA_CONFIG_DEFAULT_IMAGE_TITLE_FORMAT, N_("Image Title Format"), "image-title-format" }, { NULL, PIKA_CONFIG_DEFAULT_IMAGE_STATUS_FORMAT, N_("Image Statusbar Format"), "image-status-format" } }; gint format; pika_assert (G_N_ELEMENTS (format_strings) == G_N_ELEMENTS (format_names)); formats[0].current_setting = display_config->image_title_format; formats[1].current_setting = display_config->image_status_format; for (format = 0; format < G_N_ELEMENTS (formats); format++) { GtkWidget *scrolled_win; GtkWidget *listbox; GtkSizeGroup *name_group, *format_group; gint i; format_strings[0] = formats[format].current_setting; format_strings[1] = formats[format].default_setting; vbox2 = prefs_frame_new (gettext (formats[format].title), GTK_CONTAINER (vbox), TRUE); entry = pika_prop_entry_new (object, formats[format].property_name, 0); gtk_box_pack_start (GTK_BOX (vbox2), entry, FALSE, FALSE, 0); scrolled_win = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win), GTK_SHADOW_IN); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start (GTK_BOX (vbox2), scrolled_win, TRUE, TRUE, 0); gtk_widget_show (scrolled_win); listbox = gtk_list_box_new (); gtk_list_box_set_selection_mode (GTK_LIST_BOX (listbox), GTK_SELECTION_BROWSE); gtk_container_add (GTK_CONTAINER (scrolled_win), listbox); gtk_widget_show (listbox); name_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); format_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); for (i = 0; i < G_N_ELEMENTS (format_strings); i++) { GtkWidget *row; GtkWidget *grid; GtkWidget *name_label, *format_label; row = gtk_list_box_row_new (); g_object_set_data_full (G_OBJECT (row), "format", g_strdup (format_strings[i]), g_free); grid = gtk_grid_new (); gtk_grid_set_column_spacing (GTK_GRID (grid), 6); gtk_container_add (GTK_CONTAINER (row), grid); name_label = gtk_label_new (gettext (format_names[i])); g_object_set (name_label, "xalign", 0.0, "margin", 3, NULL); gtk_size_group_add_widget (name_group, name_label); gtk_grid_attach (GTK_GRID (grid), name_label, 0, 0, 1, 1); format_label = gtk_label_new (format_strings[i]); g_object_set (format_label, "xalign", 0.0, "margin", 3, NULL); gtk_size_group_add_widget (format_group, format_label); gtk_grid_attach (GTK_GRID (grid), format_label, 1, 0, 1, 1); gtk_widget_show_all (row); gtk_list_box_insert (GTK_LIST_BOX (listbox), row, -1); if (i == 0) { gtk_list_box_select_row (GTK_LIST_BOX (listbox), GTK_LIST_BOX_ROW (row)); } } g_object_unref (name_group); g_object_unref (format_group); g_signal_connect (listbox, "row-selected", G_CALLBACK (prefs_format_string_select_callback), entry); } } /*******************/ /* Input Devices */ /*******************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-prefs-input-devices", _("Input Devices"), _("Input Devices"), PIKA_HELP_PREFS_INPUT_DEVICES, NULL, &top_iter); /* Mouse Pointers */ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10); gtk_widget_set_halign (hbox, GTK_ALIGN_START); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); vbox2 = prefs_frame_new (_("Pointers"), GTK_CONTAINER (hbox), FALSE); grid = prefs_grid_new (GTK_CONTAINER (vbox2)); size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); prefs_enum_combo_box_add (object, "cursor-mode", 0, 0, _("Pointer _mode:"), GTK_GRID (grid), 0, size_group); prefs_enum_combo_box_add (object, "cursor-handedness", 0, 0, _("Pointer _handedness:"), GTK_GRID (grid), 1, size_group); g_clear_object (&size_group); vbox2 = prefs_frame_new (_("Paint Tools"), GTK_CONTAINER (hbox), FALSE); button = prefs_check_button_add (object, "show-brush-outline", _("Show _brush outline"), GTK_BOX (vbox2)); vbox3 = prefs_frame_new (NULL, GTK_CONTAINER (vbox2), FALSE); g_object_bind_property (button, "active", vbox3, "sensitive", G_BINDING_SYNC_CREATE); prefs_check_button_add (object, "snap-brush-outline", _("S_nap brush outline to stroke"), GTK_BOX (vbox3)); prefs_check_button_add (object, "show-paint-tool-cursor", _("Show pointer for paint _tools"), GTK_BOX (vbox2)); /* Extended Input Devices */ vbox2 = prefs_frame_new (_("Extended Input Devices"), GTK_CONTAINER (vbox), FALSE); #ifdef G_OS_WIN32 if ((gtk_get_major_version () == 3 && gtk_get_minor_version () > 24) || (gtk_get_major_version () == 3 && gtk_get_minor_version () == 24 && gtk_get_micro_version () >= 30)) { GtkWidget *combo; grid = prefs_grid_new (GTK_CONTAINER (vbox2)); combo = prefs_enum_combo_box_add (object, "win32-pointer-input-api", 0, 0, _("Pointer Input API:"), GTK_GRID (grid), 0, NULL); pika_int_combo_box_set_sensitivity (PIKA_INT_COMBO_BOX (combo), prefs_devices_api_sensitivity_func, NULL, NULL); } #endif prefs_check_button_add (object, "devices-share-tool", _("S_hare tool and tool options between input devices"), GTK_BOX (vbox2)); button = prefs_button_add (PIKA_ICON_PREFERENCES_SYSTEM, _("Configure E_xtended Input Devices..."), GTK_BOX (vbox2)); g_signal_connect (button, "clicked", G_CALLBACK (prefs_input_devices_dialog), pika); prefs_check_button_add (object, "save-device-status", _("_Save input device settings on exit"), GTK_BOX (vbox2)); button = prefs_button_add (PIKA_ICON_DOCUMENT_SAVE, _("Save Input Device Settings _Now"), GTK_BOX (vbox2)); g_signal_connect (button, "clicked", G_CALLBACK (prefs_devices_save_callback), pika); button2 = prefs_button_add (PIKA_ICON_RESET, _("_Reset Saved Input Device Settings to " "Default Values"), GTK_BOX (vbox2)); g_signal_connect (button2, "clicked", G_CALLBACK (prefs_devices_clear_callback), pika); g_object_set_data (G_OBJECT (button), "clear-button", button2); /****************************/ /* Additional Controllers */ /****************************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-prefs-controllers", _("Additional Input Controllers"), _("Input Controllers"), PIKA_HELP_PREFS_INPUT_CONTROLLERS, &top_iter, &child_iter); vbox2 = pika_controller_list_new (pika); gtk_box_pack_start (GTK_BOX (vbox), vbox2, TRUE, TRUE, 0); gtk_widget_show (vbox2); /*************/ /* Folders */ /*************/ vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), "pika-prefs-folders", _("Folders"), _("Folders"), PIKA_HELP_PREFS_FOLDERS, NULL, &top_iter); button = pika_prefs_box_set_page_resettable (PIKA_PREFS_BOX (prefs_box), vbox, _("Reset _Folders")); g_signal_connect (button, "clicked", G_CALLBACK (prefs_folders_reset), config); { static const struct { const gchar *property_name; const gchar *label; const gchar *dialog_title; } dirs[] = { { "temp-path", N_("_Temporary folder:"), N_("Select Folder for Temporary Files") }, { "swap-path", N_("_Swap folder:"), N_("Select Swap Folder") } }; grid = prefs_grid_new (GTK_CONTAINER (vbox)); for (i = 0; i < G_N_ELEMENTS (dirs); i++) { prefs_file_chooser_button_add (object, dirs[i].property_name, gettext (dirs[i].label), gettext (dirs[i].dialog_title), GTK_GRID (grid), i, NULL); } } /*********************/ /* Folders / */ /*********************/ { static const struct { const gchar *tree_label; const gchar *label; const gchar *icon; const gchar *help_data; const gchar *reset_label; const gchar *fs_label; const gchar *path_property_name; const gchar *writable_property_name; } paths[] = { { N_("Brushes"), N_("Brush Folders"), "folders-brushes", PIKA_HELP_PREFS_FOLDERS_BRUSHES, N_("Reset Brush _Folders"), N_("Select Brush Folders"), "brush-path", "brush-path-writable" }, { N_("Dynamics"), N_("Dynamics Folders"), "folders-dynamics", PIKA_HELP_PREFS_FOLDERS_DYNAMICS, N_("Reset Dynamics _Folders"), N_("Select Dynamics Folders"), "dynamics-path", "dynamics-path-writable" }, { N_("Patterns"), N_("Pattern Folders"), "folders-patterns", PIKA_HELP_PREFS_FOLDERS_PATTERNS, N_("Reset Pattern _Folders"), N_("Select Pattern Folders"), "pattern-path", "pattern-path-writable" }, { N_("Palettes"), N_("Palette Folders"), "folders-palettes", PIKA_HELP_PREFS_FOLDERS_PALETTES, N_("Reset Palette _Folders"), N_("Select Palette Folders"), "palette-path", "palette-path-writable" }, { N_("Gradients"), N_("Gradient Folders"), "folders-gradients", PIKA_HELP_PREFS_FOLDERS_GRADIENTS, N_("Reset Gradient _Folders"), N_("Select Gradient Folders"), "gradient-path", "gradient-path-writable" }, { N_("Fonts"), N_("Font Folders"), "folders-fonts", PIKA_HELP_PREFS_FOLDERS_FONTS, N_("Reset Font _Folders"), N_("Select Font Folders"), "font-path", NULL }, { N_("Tool Presets"), N_("Tool Preset Folders"), "folders-tool-presets", PIKA_HELP_PREFS_FOLDERS_TOOL_PRESETS, N_("Reset Tool Preset _Folders"), N_("Select Tool Preset Folders"), "tool-preset-path", "tool-preset-path-writable" }, { N_("MyPaint Brushes"), N_("MyPaint Brush Folders"), "folders-mypaint-brushes", PIKA_HELP_PREFS_FOLDERS_MYPAINT_BRUSHES, N_("Reset MyPaint Brush _Folders"), N_("Select MyPaint Brush Folders"), "mypaint-brush-path", "mypaint-brush-path-writable" }, { N_("Plug-ins"), N_("Plug-in Folders"), "folders-plug-ins", PIKA_HELP_PREFS_FOLDERS_PLUG_INS, N_("Reset plug-in _Folders"), N_("Select plug-in Folders"), "plug-in-path", NULL }, { N_("Scripts"), N_("Script-Fu Folders"), "folders-scripts", PIKA_HELP_PREFS_FOLDERS_SCRIPTS, N_("Reset Script-Fu _Folders"), N_("Select Script-Fu Folders"), "script-fu-path", NULL }, { N_("Modules"), N_("Module Folders"), "folders-modules", PIKA_HELP_PREFS_FOLDERS_MODULES, N_("Reset Module _Folders"), N_("Select Module Folders"), "module-path", NULL }, { N_("Interpreters"), N_("Interpreter Folders"), "folders-interp", PIKA_HELP_PREFS_FOLDERS_INTERPRETERS, N_("Reset Interpreter _Folders"), N_("Select Interpreter Folders"), "interpreter-path", NULL }, { N_("Environment"), N_("Environment Folders"), "folders-environ", PIKA_HELP_PREFS_FOLDERS_ENVIRONMENT, N_("Reset Environment _Folders"), N_("Select Environment Folders"), "environ-path", NULL }, { N_("Themes"), N_("Theme Folders"), "folders-themes", PIKA_HELP_PREFS_FOLDERS_THEMES, N_("Reset Theme _Folders"), N_("Select Theme Folders"), "theme-path", NULL }, { N_("Icon Themes"), N_("Icon Theme Folders"), "folders-icon-themes", PIKA_HELP_PREFS_FOLDERS_ICON_THEMES, N_("Reset Icon Theme _Folders"), N_("Select Icon Theme Folders"), "icon-theme-path", NULL } }; for (i = 0; i < G_N_ELEMENTS (paths); i++) { GtkWidget *editor; gchar *icon_name; icon_name = g_strconcat ("pika-prefs-", paths[i].icon, NULL); vbox = pika_prefs_box_add_page (PIKA_PREFS_BOX (prefs_box), icon_name, gettext (paths[i].label), gettext (paths[i].tree_label), paths[i].help_data, &top_iter, &child_iter); g_free (icon_name); button = pika_prefs_box_set_page_resettable (PIKA_PREFS_BOX (prefs_box), vbox, gettext (paths[i].reset_label)); g_object_set_data (G_OBJECT (button), "path", (gpointer) paths[i].path_property_name); g_object_set_data (G_OBJECT (button), "path-writable", (gpointer) paths[i].writable_property_name); g_signal_connect (button, "clicked", G_CALLBACK (prefs_path_reset), config); editor = pika_prop_path_editor_new (object, paths[i].path_property_name, paths[i].writable_property_name, gettext (paths[i].fs_label)); gtk_box_pack_start (GTK_BOX (vbox), editor, TRUE, TRUE, 0); } } { GtkWidget *tv; GtkTreeModel *model; GtkTreePath *path; tv = pika_prefs_box_get_tree_view (PIKA_PREFS_BOX (prefs_box)); gtk_tree_view_expand_all (GTK_TREE_VIEW (tv)); /* collapse the Folders subtree */ model = gtk_tree_view_get_model (GTK_TREE_VIEW (tv)); path = gtk_tree_model_get_path (model, &top_iter); gtk_tree_view_collapse_row (GTK_TREE_VIEW (tv), path); gtk_tree_path_free (path); } return dialog; }