/* 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 Spencer Kimball and Peter Mattis * * pika-tool-options-manager.c * Copyright (C) 2018 Michael Natterer * * 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 "libpikaconfig/pikaconfig.h" #include "tools-types.h" #include "config/pikacoreconfig.h" #include "core/pika.h" #include "core/pikatoolinfo.h" #include "paint/pikapaintoptions.h" #include "widgets/pikawidgets-utils.h" #include "pika-tool-options-manager.h" typedef struct _PikaToolOptionsManager PikaToolOptionsManager; struct _PikaToolOptionsManager { Pika *pika; PikaPaintOptions *global_paint_options; PikaContextPropMask global_props; PikaToolInfo *active_tool; }; /* local function prototypes */ static PikaContextPropMask tool_options_manager_get_global_props (PikaCoreConfig *config); static void tool_options_manager_global_notify (PikaCoreConfig *config, const GParamSpec *pspec, PikaToolOptionsManager *manager); static void tool_options_manager_paint_options_notify (PikaPaintOptions *src, const GParamSpec *pspec, PikaPaintOptions *dest); static void tool_options_manager_copy_paint_props (PikaPaintOptions *src, PikaPaintOptions *dest, PikaContextPropMask prop_mask); static void tool_options_manager_tool_changed (PikaContext *user_context, PikaToolInfo *tool_info, PikaToolOptionsManager *manager); static GQuark manager_quark = 0; /* public functions */ void pika_tool_options_manager_init (Pika *pika) { PikaToolOptionsManager *manager; PikaContext *user_context; GList *list; g_return_if_fail (PIKA_IS_PIKA (pika)); g_return_if_fail (manager_quark == 0); manager_quark = g_quark_from_static_string ("pika-tool-options-manager"); manager = g_slice_new0 (PikaToolOptionsManager); g_object_set_qdata (G_OBJECT (pika), manager_quark, manager); manager->pika = pika; manager->global_paint_options = g_object_new (PIKA_TYPE_PAINT_OPTIONS, "pika", pika, "name", "tool-options-manager-global-paint-options", NULL); manager->global_props = tool_options_manager_get_global_props (pika->config); user_context = pika_get_user_context (pika); for (list = pika_get_tool_info_iter (pika); list; list = g_list_next (list)) { PikaToolInfo *tool_info = list->data; /* the global props that are actually used by the tool are * always shared with the user context by undefining them... */ pika_context_define_properties (PIKA_CONTEXT (tool_info->tool_options), manager->global_props & tool_info->context_props, FALSE); /* ...and setting the user context as parent */ pika_context_set_parent (PIKA_CONTEXT (tool_info->tool_options), user_context); /* make sure paint tools also share their brush, dynamics, * gradient properties if the resp. context properties are * global */ if (PIKA_IS_PAINT_OPTIONS (tool_info->tool_options)) { g_signal_connect (tool_info->tool_options, "notify", G_CALLBACK (tool_options_manager_paint_options_notify), manager->global_paint_options); g_signal_connect (manager->global_paint_options, "notify", G_CALLBACK (tool_options_manager_paint_options_notify), tool_info->tool_options); tool_options_manager_copy_paint_props (manager->global_paint_options, PIKA_PAINT_OPTIONS (tool_info->tool_options), tool_info->context_props & manager->global_props); } } g_signal_connect (pika->config, "notify::global-brush", G_CALLBACK (tool_options_manager_global_notify), manager); g_signal_connect (pika->config, "notify::global-dynamics", G_CALLBACK (tool_options_manager_global_notify), manager); g_signal_connect (pika->config, "notify::global-pattern", G_CALLBACK (tool_options_manager_global_notify), manager); g_signal_connect (pika->config, "notify::global-palette", G_CALLBACK (tool_options_manager_global_notify), manager); g_signal_connect (pika->config, "notify::global-gradient", G_CALLBACK (tool_options_manager_global_notify), manager); g_signal_connect (pika->config, "notify::global-font", G_CALLBACK (tool_options_manager_global_notify), manager); g_signal_connect (pika->config, "notify::global-expand", G_CALLBACK (tool_options_manager_global_notify), manager); g_signal_connect (user_context, "tool-changed", G_CALLBACK (tool_options_manager_tool_changed), manager); tool_options_manager_tool_changed (user_context, pika_context_get_tool (user_context), manager); } void pika_tool_options_manager_exit (Pika *pika) { PikaToolOptionsManager *manager; PikaContext *user_context; GList *list; g_return_if_fail (PIKA_IS_PIKA (pika)); manager = g_object_get_qdata (G_OBJECT (pika), manager_quark); g_return_if_fail (manager != NULL); user_context = pika_get_user_context (pika); g_signal_handlers_disconnect_by_func (user_context, tool_options_manager_tool_changed, manager); g_signal_handlers_disconnect_by_func (pika->config, tool_options_manager_global_notify, manager); for (list = pika_get_tool_info_iter (pika); list; list = g_list_next (list)) { PikaToolInfo *tool_info = list->data; pika_context_set_parent (PIKA_CONTEXT (tool_info->tool_options), NULL); if (PIKA_IS_PAINT_OPTIONS (tool_info->tool_options)) { g_signal_handlers_disconnect_by_func (tool_info->tool_options, tool_options_manager_paint_options_notify, manager->global_paint_options); g_signal_handlers_disconnect_by_func (manager->global_paint_options, tool_options_manager_paint_options_notify, tool_info->tool_options); } } g_clear_object (&manager->global_paint_options); g_slice_free (PikaToolOptionsManager, manager); g_object_set_qdata (G_OBJECT (pika), manager_quark, NULL); } /* private functions */ static PikaContextPropMask tool_options_manager_get_global_props (PikaCoreConfig *config) { PikaContextPropMask global_props = 0; /* FG and BG are always shared between all tools */ global_props |= PIKA_CONTEXT_PROP_MASK_FOREGROUND; global_props |= PIKA_CONTEXT_PROP_MASK_BACKGROUND; if (config->global_brush) global_props |= PIKA_CONTEXT_PROP_MASK_BRUSH; if (config->global_dynamics) global_props |= PIKA_CONTEXT_PROP_MASK_DYNAMICS; if (config->global_pattern) global_props |= PIKA_CONTEXT_PROP_MASK_PATTERN; if (config->global_palette) global_props |= PIKA_CONTEXT_PROP_MASK_PALETTE; if (config->global_gradient) global_props |= PIKA_CONTEXT_PROP_MASK_GRADIENT; if (config->global_font) global_props |= PIKA_CONTEXT_PROP_MASK_FONT; if (config->global_expand) global_props |= PIKA_CONTEXT_PROP_MASK_EXPAND; return global_props; } static void tool_options_manager_global_notify (PikaCoreConfig *config, const GParamSpec *pspec, PikaToolOptionsManager *manager) { PikaContextPropMask global_props; PikaContextPropMask enabled_global_props; PikaContextPropMask disabled_global_props; GList *list; global_props = tool_options_manager_get_global_props (config); enabled_global_props = global_props & ~manager->global_props; disabled_global_props = manager->global_props & ~global_props; /* copy the newly enabled global props to all tool options, and * disconnect the newly disabled ones from the user context */ for (list = pika_get_tool_info_iter (manager->pika); list; list = g_list_next (list)) { PikaToolInfo *tool_info = list->data; /* don't change the active tool, it is always fully connected * to the user_context anyway because we set its * defined/undefined context props in tool_changed() */ if (tool_info == manager->active_tool) continue; /* defining the newly disabled ones disconnects them from the * parent user context */ pika_context_define_properties (PIKA_CONTEXT (tool_info->tool_options), tool_info->context_props & disabled_global_props, TRUE); /* undefining the newly enabled ones copies the value from the * parent user context */ pika_context_define_properties (PIKA_CONTEXT (tool_info->tool_options), tool_info->context_props & enabled_global_props, FALSE); if (PIKA_IS_PAINT_OPTIONS (tool_info->tool_options)) tool_options_manager_copy_paint_props (manager->global_paint_options, PIKA_PAINT_OPTIONS (tool_info->tool_options), tool_info->context_props & enabled_global_props); } manager->global_props = global_props; } static void tool_options_manager_paint_options_notify (PikaPaintOptions *src, const GParamSpec *pspec, PikaPaintOptions *dest) { Pika *pika = PIKA_CONTEXT (src)->pika; PikaCoreConfig *config = pika->config; PikaToolOptionsManager *manager; PikaToolInfo *tool_info; PikaContextPropMask prop_mask = 0; gboolean active = FALSE; manager = g_object_get_qdata (G_OBJECT (pika), manager_quark); /* one of the options is the global one, the other is the tool's, * get the tool_info from the tool's options */ if (manager->global_paint_options == src) tool_info = pika_context_get_tool (PIKA_CONTEXT (dest)); else tool_info = pika_context_get_tool (PIKA_CONTEXT (src)); if (tool_info == manager->active_tool) active = TRUE; if ((active || config->global_brush) && tool_info->context_props & PIKA_CONTEXT_PROP_MASK_BRUSH) { prop_mask |= PIKA_CONTEXT_PROP_MASK_BRUSH; } if ((active || config->global_dynamics) && tool_info->context_props & PIKA_CONTEXT_PROP_MASK_DYNAMICS) { prop_mask |= PIKA_CONTEXT_PROP_MASK_DYNAMICS; } if ((active || config->global_gradient) && tool_info->context_props & PIKA_CONTEXT_PROP_MASK_GRADIENT) { prop_mask |= PIKA_CONTEXT_PROP_MASK_GRADIENT; } if ((active || config->global_expand) && tool_info->context_props & PIKA_CONTEXT_PROP_MASK_EXPAND) { prop_mask |= PIKA_CONTEXT_PROP_MASK_EXPAND; } if (pika_paint_options_is_prop (pspec->name, prop_mask)) { GValue value = G_VALUE_INIT; g_value_init (&value, pspec->value_type); g_object_get_property (G_OBJECT (src), pspec->name, &value); g_signal_handlers_block_by_func (dest, tool_options_manager_paint_options_notify, src); g_object_set_property (G_OBJECT (dest), pspec->name, &value); g_signal_handlers_unblock_by_func (dest, tool_options_manager_paint_options_notify, src); g_value_unset (&value); } } static void tool_options_manager_copy_paint_props (PikaPaintOptions *src, PikaPaintOptions *dest, PikaContextPropMask prop_mask) { g_signal_handlers_block_by_func (dest, tool_options_manager_paint_options_notify, src); pika_paint_options_copy_props (src, dest, prop_mask); g_signal_handlers_unblock_by_func (dest, tool_options_manager_paint_options_notify, src); } static void tool_options_manager_tool_changed (PikaContext *user_context, PikaToolInfo *tool_info, PikaToolOptionsManager *manager) { if (tool_info == manager->active_tool) return; /* FIXME: pika_busy HACK * the tool manager will stop the emission, so simply return */ if (manager->pika->busy) return; if (manager->active_tool) { PikaToolInfo *active = manager->active_tool; /* disconnect the old active tool from all context properties * it uses, but are not currently global */ pika_context_define_properties (PIKA_CONTEXT (active->tool_options), active->context_props & ~manager->global_props, TRUE); } manager->active_tool = tool_info; if (manager->active_tool) { PikaToolInfo *active = manager->active_tool; /* make sure the tool options GUI always exists, this call * creates it if needed, so tools always have their option GUI * available even if the tool options dockable is not open, see * for example issue #3435 */ pika_tools_get_tool_options_gui (active->tool_options); /* copy the new tool's context properties that are not * currently global to the user context, so they get used by * everything */ pika_context_copy_properties (PIKA_CONTEXT (active->tool_options), pika_get_user_context (manager->pika), active->context_props & ~manager->global_props); if (PIKA_IS_PAINT_OPTIONS (active->tool_options)) tool_options_manager_copy_paint_props (PIKA_PAINT_OPTIONS (active->tool_options), manager->global_paint_options, active->context_props & ~manager->global_props); /* then, undefine these properties so the tool syncs with the * user context automatically */ pika_context_define_properties (PIKA_CONTEXT (active->tool_options), active->context_props & ~manager->global_props, FALSE); } }