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