File indexing completed on 2024-05-12 16:01:33

0001 /* This file is part of the KDE project
0002  * SPDX-FileCopyrightText: 2006 Boudewijn Rempt <boud@valdyas.org>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_mask_manager.h"
0008 
0009 #include <kactioncollection.h>
0010 
0011 #include <KoProperties.h>
0012 
0013 #include <kis_transaction.h>
0014 #include <filter/kis_filter_configuration.h>
0015 #include <commands/kis_node_commands.h>
0016 #include <kis_undo_adapter.h>
0017 #include <kis_paint_layer.h>
0018 #include "KisDocument.h"
0019 #include "KisViewManager.h"
0020 #include <kis_layer.h>
0021 #include <kis_clone_layer.h>
0022 #include <kis_group_layer.h>
0023 #include <kis_filter_mask.h>
0024 #include <lazybrush/kis_colorize_mask.h>
0025 #include <kis_transform_mask.h>
0026 #include <kis_transparency_mask.h>
0027 #include <kis_selection_mask.h>
0028 #include <kis_effect_mask.h>
0029 #include "dialogs/kis_dlg_adjustment_layer.h"
0030 #include "widgets/kis_mask_widgets.h"
0031 #include <kis_selection.h>
0032 #include <kis_selection_manager.h>
0033 #include <kis_pixel_selection.h>
0034 #include "dialogs/kis_dlg_adj_layer_props.h"
0035 #include <kis_image.h>
0036 #include <kis_transform_worker.h>
0037 #include <KoColorSpace.h>
0038 #include <KoColor.h>
0039 #include "kis_node_commands_adapter.h"
0040 #include "commands/kis_deselect_global_selection_command.h"
0041 #include "kis_iterator_ng.h"
0042 #include <KisGlobalResourcesInterface.h>
0043 #include "kis_node_manager.h"
0044 
0045 
0046 KisMaskManager::KisMaskManager(KisViewManager * view)
0047     : m_view(view)
0048     , m_imageView(0)
0049     , m_commandsAdapter(new KisNodeCommandsAdapter(m_view))
0050 {
0051 }
0052 
0053 void KisMaskManager::setView(QPointer<KisView>imageView)
0054 {
0055     m_imageView = imageView;
0056 }
0057 
0058 void KisMaskManager::setup(KisKActionCollection *actionCollection, KisActionManager *actionManager)
0059 {
0060     Q_UNUSED(actionCollection);
0061     Q_UNUSED(actionManager);
0062 }
0063 
0064 void KisMaskManager::updateGUI()
0065 {
0066     // XXX: enable/disable menu items according to whether there's a mask selected currently
0067     // XXX: disable the selection mask item if there's already a selection mask
0068     // YYY: doesn't KisAction do that already?
0069 }
0070 
0071 KisMaskSP KisMaskManager::activeMask()
0072 {
0073     if (m_imageView) {
0074         return m_imageView->currentMask();
0075     }
0076     return 0;
0077 }
0078 
0079 KisPaintDeviceSP KisMaskManager::activeDevice()
0080 {
0081     KisMaskSP mask = activeMask();
0082     return mask ? mask->paintDevice() : 0;
0083 }
0084 
0085 void KisMaskManager::activateMask(KisMaskSP mask)
0086 {
0087     Q_UNUSED(mask);
0088 }
0089 
0090 void KisMaskManager::masksUpdated()
0091 {
0092     m_view->updateGUI();
0093 }
0094 
0095 void KisMaskManager::adjustMaskPosition(KisNodeSP node, KisNodeSP activeNode, bool avoidActiveNode, KisNodeSP &parent, KisNodeSP &above)
0096 {
0097     Q_ASSERT(node);
0098     Q_ASSERT(activeNode);
0099 
0100     if (!avoidActiveNode && activeNode->allowAsChild(node)) {
0101         parent = activeNode;
0102         above = activeNode->lastChild();
0103     } else if (activeNode->parent() && activeNode->parent()->allowAsChild(node)
0104                && activeNode->parent()->parent() /* we don't want to add masks to root */) {
0105         parent = activeNode->parent();
0106         above = activeNode;
0107     } else {
0108         KisNodeSP t = activeNode;
0109         while ((t = t->nextSibling())) {
0110             if (t->allowAsChild(node)) {
0111                 parent = t;
0112                 above = t->lastChild();
0113                 break;
0114             }
0115         }
0116 
0117         if (!t) {
0118             t = activeNode;
0119             while ((t = t->prevSibling())) {
0120                 if (t->allowAsChild(node)) {
0121                     parent = t;
0122                     above = t->lastChild();
0123                     break;
0124                 }
0125             }
0126         }
0127 
0128         if (!t && activeNode->parent()) {
0129             adjustMaskPosition(node, activeNode->parent(), true, parent, above);
0130         } else if (!t) {
0131             KisImageWSP image = m_view->image();
0132             KisLayerSP layer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, image->colorSpace());
0133             m_commandsAdapter->addNode(layer, activeNode, 0);
0134 
0135             parent = layer;
0136             above = 0;
0137         }
0138     }
0139 }
0140 
0141 void KisMaskManager::createMaskCommon(KisMaskSP mask,
0142                                       KisNodeSP activeNode,
0143                                       KisPaintDeviceSP copyFrom,
0144                                       const KUndo2MagicString& macroName,
0145                                       const QString &nodeType,
0146                                       const QString &nodeName,
0147                                       bool suppressSelection,
0148                                       bool avoidActiveNode,
0149                                       bool updateImage)
0150 {
0151     m_commandsAdapter->beginMacro(macroName);
0152 
0153     KisNodeSP parent;
0154     KisNodeSP above;
0155     adjustMaskPosition(mask, activeNode, avoidActiveNode, parent, above);
0156 
0157     KisLayerSP parentLayer = qobject_cast<KisLayer*>(parent.data());
0158     Q_ASSERT(parentLayer);
0159 
0160     bool shouldDeselectGlobalSelection = false;
0161 
0162     if (!suppressSelection) {
0163         if (copyFrom) {
0164             mask->initSelection(copyFrom, parentLayer);
0165         } else {
0166             mask->initSelection(m_view->selection(), parentLayer);
0167             shouldDeselectGlobalSelection = m_view->selection();
0168         }
0169     }
0170 
0171     mask->setName( createMaskNameCommon(parentLayer, nodeType, nodeName) );
0172 
0173     m_commandsAdapter->addNode(mask, parentLayer, above, updateImage, updateImage);
0174 
0175     if (shouldDeselectGlobalSelection) {
0176         m_commandsAdapter->addExtraCommand(new KisDeselectGlobalSelectionCommand(m_imageView->image()));
0177     }
0178 
0179     m_commandsAdapter->endMacro();
0180 
0181     masksUpdated();
0182 }
0183 
0184 QString KisMaskManager::createMaskNameCommon(KisNodeSP targetNode, const QString &nodeType, const QString &desiredName)
0185 {
0186     QList<KisNodeSP> masks = targetNode->childNodes(QStringList(nodeType),KoProperties());
0187     int number = masks.count() + 1;
0188     return desiredName + QString(" ") + QString::number(number);
0189 }
0190 
0191 KisNodeSP KisMaskManager::createSelectionMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool convertActiveNode)
0192 {
0193     if (!m_view->nodeManager()->canModifyLayer(activeNode)) return 0;
0194 
0195     KisSelectionMaskSP mask = new KisSelectionMask(m_view->image());
0196 
0197     createMaskCommon(mask, activeNode, copyFrom, kundo2_i18n("Add Selection Mask"), "KisSelectionMask", i18n("Selection"), false, convertActiveNode, false);
0198     mask->setActive(true);
0199     if (convertActiveNode) {
0200         m_commandsAdapter->removeNode(activeNode);
0201     }
0202     return mask;
0203 }
0204 
0205 KisNodeSP KisMaskManager::createTransparencyMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool convertActiveNode)
0206 {
0207     if (!m_view->nodeManager()->canModifyLayer(activeNode)) return 0;
0208 
0209     KisMaskSP mask = new KisTransparencyMask(m_view->image(), "");
0210     createMaskCommon(mask, activeNode, copyFrom, kundo2_i18n("Add Transparency Mask"), "KisTransparencyMask", i18n("Transparency Mask"), false, convertActiveNode);
0211     if (convertActiveNode) {
0212         m_commandsAdapter->removeNode(activeNode);
0213     }
0214     return mask;
0215 }
0216 
0217 
0218 KisNodeSP KisMaskManager::createFilterMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool quiet, bool convertActiveNode)
0219 {
0220     if (!m_view->nodeManager()->canModifyLayer(activeNode)) return 0;
0221 
0222     KisFilterMaskSP mask = new KisFilterMask(m_view->image(), "");
0223     createMaskCommon(mask, activeNode, copyFrom, kundo2_i18n("Add Filter Mask"), "KisFilterMask", i18n("Filter Mask"), false, convertActiveNode);
0224 
0225     if (convertActiveNode) {
0226         m_commandsAdapter->removeNode(activeNode);
0227     }
0228 
0229     /**
0230      * FIXME: We'll use layer's original for creation of a thumbnail.
0231      * Actually, we can't use it's projection as newly created mask
0232      * may be going to be inserted in the middle of the masks stack
0233      */
0234     KisPaintDeviceSP originalDevice = mask->parent()->original();
0235 
0236 
0237     KisDlgAdjustmentLayer dialog(mask, mask.data(), originalDevice,
0238                                  mask->name(), i18n("New Filter Mask"),
0239                                  m_view, qApp->activeWindow());
0240 
0241     // If we are supposed to not disturb the user, don't start asking them about things.
0242     if(quiet) {
0243         KisFilterConfigurationSP filter = KisFilterRegistry::instance()->values().first()->defaultConfiguration(KisGlobalResourcesInterface::instance());
0244         if (filter) {
0245             mask->setFilter(filter->cloneWithResourcesSnapshot());
0246             mask->setName(mask->name());
0247         }
0248         return mask;
0249     }
0250 
0251     if (dialog.exec() == QDialog::Accepted) {
0252         KisFilterConfigurationSP filter = dialog.filterConfiguration();
0253         if (filter) {
0254             QString name = dialog.layerName();
0255             mask->setFilter(filter->cloneWithResourcesSnapshot());
0256             mask->setName(name);
0257         }
0258 
0259         return mask;
0260 
0261     } else {
0262         m_commandsAdapter->undoLastCommand();
0263     }
0264 
0265     return 0;
0266 }
0267 
0268 
0269 KisNodeSP KisMaskManager::createColorizeMask(KisNodeSP activeNode)
0270 {
0271     if (!m_view->nodeManager()->canModifyLayer(activeNode)) return 0;
0272 
0273     KisColorizeMaskSP mask = new KisColorizeMask(m_view->image(), "");
0274     createMaskCommon(mask, activeNode, 0, kundo2_i18n("Add Colorize Mask"), "KisColorizeMask", i18n("Colorize Mask"), true, false);
0275     mask->initializeCompositeOp();
0276     delete mask->setColorSpace(mask->parent()->colorSpace());
0277     return mask;
0278 }
0279 
0280 
0281 KisNodeSP KisMaskManager::createTransformMask(KisNodeSP activeNode)
0282 {
0283     if (!m_view->nodeManager()->canModifyLayer(activeNode)) return 0;
0284 
0285     KisTransformMaskSP mask = new KisTransformMask(m_view->image(), "");
0286     createMaskCommon(mask, activeNode, 0, kundo2_i18n("Add Transform Mask"), "KisTransformMask", i18n("Transform Mask"), true, false);
0287     return mask;
0288 }
0289 
0290 void KisMaskManager::maskProperties()
0291 {
0292     if (!activeMask()) return;
0293 
0294     if (!m_view->nodeManager()->canModifyLayer(activeMask())) return;
0295 
0296     if (activeMask()->inherits("KisFilterMask")) {
0297         KisFilterMask *mask = static_cast<KisFilterMask*>(activeMask().data());
0298 
0299         KisLayerSP layer = qobject_cast<KisLayer*>(mask->parent().data());
0300         if (! layer)
0301             return;
0302 
0303 
0304         KisPaintDeviceSP dev = layer->original();
0305         if (!dev) {
0306             return;
0307         }
0308 
0309         KisDlgAdjLayerProps dlg(layer, mask, dev, m_view, mask->filter().data(), mask->name(), i18n("Filter Mask Properties"), m_view->mainWindowAsQWidget(), "dlgeffectmaskprops");
0310 
0311         KisFilterConfigurationSP configBefore(mask->filter());
0312         Q_ASSERT(configBefore);
0313         QString xmlBefore = configBefore->toXML();
0314 
0315         if (dlg.exec() == QDialog::Accepted) {
0316 
0317             KisFilterConfigurationSP configAfter(dlg.filterConfiguration());
0318             Q_ASSERT(configAfter);
0319             QString xmlAfter = configAfter->toXML();
0320 
0321             mask->setName(dlg.layerName());
0322 
0323             if(xmlBefore != xmlAfter) {
0324                 KisChangeFilterCmd *cmd
0325                     = new KisChangeFilterCmd(mask,
0326                                              configBefore->cloneWithResourcesSnapshot(),
0327                                              configAfter->cloneWithResourcesSnapshot());
0328 
0329                 // FIXME: check whether is needed
0330                 cmd->redo();
0331                 m_view->undoAdapter()->addCommand(cmd);
0332                 m_view->document()->setModified(true);
0333             }
0334         }
0335         else {
0336             KisFilterConfigurationSP configAfter(dlg.filterConfiguration());
0337             Q_ASSERT(configAfter);
0338             QString xmlAfter = configAfter->toXML();
0339 
0340             if(xmlBefore != xmlAfter) {
0341                 mask->setFilter(configBefore->cloneWithResourcesSnapshot());
0342                 mask->setDirty();
0343             }
0344         }
0345 
0346     } else {
0347         // Not much to show for transparency or selection masks?
0348     }
0349 }