1 /* This file is part of the KDE project
2  * Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2006
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 #include "kis_mask_manager.h"
20 
21 #include <kactioncollection.h>
22 
23 #include <KoProperties.h>
24 
25 #include <kis_transaction.h>
26 #include <filter/kis_filter_configuration.h>
27 #include <commands/kis_node_commands.h>
28 #include <kis_undo_adapter.h>
29 #include <kis_paint_layer.h>
30 #include "KisDocument.h"
31 #include "KisViewManager.h"
32 #include <kis_layer.h>
33 #include <kis_clone_layer.h>
34 #include <kis_group_layer.h>
35 #include <kis_filter_mask.h>
36 #include <lazybrush/kis_colorize_mask.h>
37 #include <kis_transform_mask.h>
38 #include <kis_transparency_mask.h>
39 #include <kis_selection_mask.h>
40 #include <kis_effect_mask.h>
41 #include "dialogs/kis_dlg_adjustment_layer.h"
42 #include "widgets/kis_mask_widgets.h"
43 #include <kis_selection.h>
44 #include <kis_selection_manager.h>
45 #include <kis_pixel_selection.h>
46 #include "dialogs/kis_dlg_adj_layer_props.h"
47 #include <kis_image.h>
48 #include <kis_transform_worker.h>
49 #include <KoColorSpace.h>
50 #include <KoColor.h>
51 #include "kis_node_commands_adapter.h"
52 #include "commands/kis_deselect_global_selection_command.h"
53 #include "kis_iterator_ng.h"
54 #include "kis_node_manager.h"
55 
56 
KisMaskManager(KisViewManager * view)57 KisMaskManager::KisMaskManager(KisViewManager * view)
58     : m_view(view)
59     , m_imageView(0)
60     , m_commandsAdapter(new KisNodeCommandsAdapter(m_view))
61 {
62 }
63 
setView(QPointer<KisView> imageView)64 void KisMaskManager::setView(QPointer<KisView>imageView)
65 {
66     m_imageView = imageView;
67 }
68 
setup(KActionCollection * actionCollection,KisActionManager * actionManager)69 void KisMaskManager::setup(KActionCollection *actionCollection, KisActionManager *actionManager)
70 {
71     Q_UNUSED(actionCollection);
72     Q_UNUSED(actionManager);
73 }
74 
updateGUI()75 void KisMaskManager::updateGUI()
76 {
77     // XXX: enable/disable menu items according to whether there's a mask selected currently
78     // XXX: disable the selection mask item if there's already a selection mask
79     // YYY: doesn't KisAction do that already?
80 }
81 
activeMask()82 KisMaskSP KisMaskManager::activeMask()
83 {
84     if (m_imageView) {
85         return m_imageView->currentMask();
86     }
87     return 0;
88 }
89 
activeDevice()90 KisPaintDeviceSP KisMaskManager::activeDevice()
91 {
92     KisMaskSP mask = activeMask();
93     return mask ? mask->paintDevice() : 0;
94 }
95 
activateMask(KisMaskSP mask)96 void KisMaskManager::activateMask(KisMaskSP mask)
97 {
98     Q_UNUSED(mask);
99 }
100 
masksUpdated()101 void KisMaskManager::masksUpdated()
102 {
103     m_view->updateGUI();
104 }
105 
adjustMaskPosition(KisNodeSP node,KisNodeSP activeNode,bool avoidActiveNode,KisNodeSP & parent,KisNodeSP & above)106 void KisMaskManager::adjustMaskPosition(KisNodeSP node, KisNodeSP activeNode, bool avoidActiveNode, KisNodeSP &parent, KisNodeSP &above)
107 {
108     Q_ASSERT(node);
109     Q_ASSERT(activeNode);
110 
111     if (!avoidActiveNode && activeNode->allowAsChild(node)) {
112         parent = activeNode;
113         above = activeNode->lastChild();
114     } else if (activeNode->parent() && activeNode->parent()->allowAsChild(node)
115                && activeNode->parent()->parent() /* we don't want to add masks to root */) {
116         parent = activeNode->parent();
117         above = activeNode;
118     } else {
119         KisNodeSP t = activeNode;
120         while ((t = t->nextSibling())) {
121             if (t->allowAsChild(node)) {
122                 parent = t;
123                 above = t->lastChild();
124                 break;
125             }
126         }
127 
128         if (!t) {
129             t = activeNode;
130             while ((t = t->prevSibling())) {
131                 if (t->allowAsChild(node)) {
132                     parent = t;
133                     above = t->lastChild();
134                     break;
135                 }
136             }
137         }
138 
139         if (!t && activeNode->parent()) {
140             adjustMaskPosition(node, activeNode->parent(), true, parent, above);
141         } else if (!t) {
142             KisImageWSP image = m_view->image();
143             KisLayerSP layer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, image->colorSpace());
144             m_commandsAdapter->addNode(layer, activeNode, 0);
145 
146             parent = layer;
147             above = 0;
148         }
149     }
150 }
151 
createMaskCommon(KisMaskSP mask,KisNodeSP activeNode,KisPaintDeviceSP copyFrom,const KUndo2MagicString & macroName,const QString & nodeType,const QString & nodeName,bool suppressSelection,bool avoidActiveNode,bool updateImage)152 void KisMaskManager::createMaskCommon(KisMaskSP mask,
153                                       KisNodeSP activeNode,
154                                       KisPaintDeviceSP copyFrom,
155                                       const KUndo2MagicString& macroName,
156                                       const QString &nodeType,
157                                       const QString &nodeName,
158                                       bool suppressSelection,
159                                       bool avoidActiveNode,
160                                       bool updateImage)
161 {
162     m_commandsAdapter->beginMacro(macroName);
163 
164     KisNodeSP parent;
165     KisNodeSP above;
166     adjustMaskPosition(mask, activeNode, avoidActiveNode, parent, above);
167 
168     KisLayerSP parentLayer = qobject_cast<KisLayer*>(parent.data());
169     Q_ASSERT(parentLayer);
170 
171     bool shouldDeselectGlobalSelection = false;
172 
173     if (!suppressSelection) {
174         if (copyFrom) {
175             mask->initSelection(copyFrom, parentLayer);
176         } else {
177             mask->initSelection(m_view->selection(), parentLayer);
178             shouldDeselectGlobalSelection = m_view->selection();
179         }
180     }
181 
182     //counting number of KisSelectionMask
183     QList<KisNodeSP> masks = parentLayer->childNodes(QStringList(nodeType),KoProperties());
184     int number = masks.count() + 1;
185     mask->setName(nodeName + QString(" ") + QString::number(number));
186 
187     m_commandsAdapter->addNode(mask, parentLayer, above, updateImage, updateImage);
188 
189     if (shouldDeselectGlobalSelection) {
190         m_commandsAdapter->addExtraCommand(new KisDeselectGlobalSelectionCommand(m_imageView->image()));
191     }
192 
193     m_commandsAdapter->endMacro();
194 
195     masksUpdated();
196 }
197 
createSelectionMask(KisNodeSP activeNode,KisPaintDeviceSP copyFrom,bool convertActiveNode)198 KisNodeSP KisMaskManager::createSelectionMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool convertActiveNode)
199 {
200     if (!m_view->nodeManager()->canModifyLayer(activeNode)) return 0;
201 
202     KisSelectionMaskSP mask = new KisSelectionMask(m_view->image());
203 
204     createMaskCommon(mask, activeNode, copyFrom, kundo2_i18n("Add Selection Mask"), "KisSelectionMask", i18n("Selection"), false, convertActiveNode, false);
205     mask->setActive(true);
206     if (convertActiveNode) {
207         m_commandsAdapter->removeNode(activeNode);
208     }
209     return mask;
210 }
211 
createTransparencyMask(KisNodeSP activeNode,KisPaintDeviceSP copyFrom,bool convertActiveNode)212 KisNodeSP KisMaskManager::createTransparencyMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool convertActiveNode)
213 {
214     if (!m_view->nodeManager()->canModifyLayer(activeNode)) return 0;
215 
216     KisMaskSP mask = new KisTransparencyMask(m_view->image(), "");
217     createMaskCommon(mask, activeNode, copyFrom, kundo2_i18n("Add Transparency Mask"), "KisTransparencyMask", i18n("Transparency Mask"), false, convertActiveNode);
218     if (convertActiveNode) {
219         m_commandsAdapter->removeNode(activeNode);
220     }
221     return mask;
222 }
223 
224 
createFilterMask(KisNodeSP activeNode,KisPaintDeviceSP copyFrom,bool quiet,bool convertActiveNode)225 KisNodeSP KisMaskManager::createFilterMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool quiet, bool convertActiveNode)
226 {
227     if (!m_view->nodeManager()->canModifyLayer(activeNode)) return 0;
228 
229     KisFilterMaskSP mask = new KisFilterMask(m_view->image(), "");
230     createMaskCommon(mask, activeNode, copyFrom, kundo2_i18n("Add Filter Mask"), "KisFilterMask", i18n("Filter Mask"), false, convertActiveNode);
231 
232     if (convertActiveNode) {
233         m_commandsAdapter->removeNode(activeNode);
234     }
235 
236     /**
237      * FIXME: We'll use layer's original for creation of a thumbnail.
238      * Actually, we can't use it's projection as newly created mask
239      * may be going to be inserted in the middle of the masks stack
240      */
241     KisPaintDeviceSP originalDevice = mask->parent()->original();
242 
243 
244     KisDlgAdjustmentLayer dialog(mask, mask.data(), originalDevice,
245                                  mask->name(), i18n("New Filter Mask"),
246                                  m_view, qApp->activeWindow());
247 
248     // If we are supposed to not disturb the user, don't start asking them about things.
249     if(quiet) {
250         KisFilterConfigurationSP filter = KisFilterRegistry::instance()->values().first()->defaultConfiguration();
251         if (filter) {
252             mask->setFilter(filter);
253             mask->setName(mask->name());
254         }
255         return mask;
256     }
257 
258     if (dialog.exec() == QDialog::Accepted) {
259         KisFilterConfigurationSP filter = dialog.filterConfiguration();
260         if (filter) {
261             QString name = dialog.layerName();
262             mask->setFilter(filter);
263             mask->setName(name);
264         }
265 
266         return mask;
267 
268     } else {
269         m_commandsAdapter->undoLastCommand();
270     }
271 
272     return 0;
273 }
274 
275 
createColorizeMask(KisNodeSP activeNode)276 KisNodeSP KisMaskManager::createColorizeMask(KisNodeSP activeNode)
277 {
278     if (!m_view->nodeManager()->canModifyLayer(activeNode)) return 0;
279 
280     KisColorizeMaskSP mask = new KisColorizeMask(m_view->image(), "");
281     createMaskCommon(mask, activeNode, 0, kundo2_i18n("Add Colorize Mask"), "KisColorizeMask", i18n("Colorize Mask"), true, false);
282     mask->initializeCompositeOp();
283     delete mask->setColorSpace(mask->parent()->colorSpace());
284     return mask;
285 }
286 
287 
createTransformMask(KisNodeSP activeNode)288 KisNodeSP KisMaskManager::createTransformMask(KisNodeSP activeNode)
289 {
290     if (!m_view->nodeManager()->canModifyLayer(activeNode)) return 0;
291 
292     KisTransformMaskSP mask = new KisTransformMask(m_view->image(), "");
293     createMaskCommon(mask, activeNode, 0, kundo2_i18n("Add Transform Mask"), "KisTransformMask", i18n("Transform Mask"), true, false);
294     return mask;
295 }
296 
maskProperties()297 void KisMaskManager::maskProperties()
298 {
299     if (!activeMask()) return;
300 
301     if (!m_view->nodeManager()->canModifyLayer(activeMask())) return;
302 
303     if (activeMask()->inherits("KisFilterMask")) {
304         KisFilterMask *mask = static_cast<KisFilterMask*>(activeMask().data());
305 
306         KisLayerSP layer = qobject_cast<KisLayer*>(mask->parent().data());
307         if (! layer)
308             return;
309 
310 
311         KisPaintDeviceSP dev = layer->original();
312         if (!dev) {
313             return;
314         }
315 
316         KisDlgAdjLayerProps dlg(layer, mask, dev, m_view, mask->filter().data(), mask->name(), i18n("Filter Mask Properties"), m_view->mainWindow(), "dlgeffectmaskprops");
317 
318         KisFilterConfigurationSP configBefore(mask->filter());
319         Q_ASSERT(configBefore);
320         QString xmlBefore = configBefore->toXML();
321 
322         if (dlg.exec() == QDialog::Accepted) {
323 
324             KisFilterConfigurationSP configAfter(dlg.filterConfiguration());
325             Q_ASSERT(configAfter);
326             QString xmlAfter = configAfter->toXML();
327 
328             mask->setName(dlg.layerName());
329 
330             if(xmlBefore != xmlAfter) {
331                 KisChangeFilterCmd *cmd
332                     = new KisChangeFilterCmd(mask,
333                                              configBefore->name(),
334                                              xmlBefore,
335                                              configAfter->name(),
336                                              xmlAfter,
337                                              false);
338 
339                 // FIXME: check whether is needed
340                 cmd->redo();
341                 m_view->undoAdapter()->addCommand(cmd);
342                 m_view->document()->setModified(true);
343             }
344         }
345         else {
346             KisFilterConfigurationSP configAfter(dlg.filterConfiguration());
347             Q_ASSERT(configAfter);
348             QString xmlAfter = configAfter->toXML();
349 
350             if(xmlBefore != xmlAfter) {
351                 mask->setFilter(KisFilterRegistry::instance()->cloneConfiguration(configBefore.data()));
352                 mask->setDirty();
353             }
354         }
355 
356     } else {
357         // Not much to show for transparency or selection masks?
358     }
359 }
360