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 }