Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_node_manager.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2007 Boudewijn Rempt <boud@valdyas.org>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#include "kis_node_manager.h"
8
9#include <QStandardPaths>
10#include <QMessageBox>
11#include <KisSignalMapper.h>
12#include <QApplication>
13
14#include <kactioncollection.h>
15
16#include <QKeySequence>
17
18#include <kis_icon.h>
19#include <KoSelection.h>
20#include <KoShapeManager.h>
21#include <KoShape.h>
22#include <KoShapeLayer.h>
24#include <KoFileDialog.h>
25#include <KoToolManager.h>
26#include <KoProperties.h>
27
28#include <KoColorSpace.h>
31
32#include <kis_types.h>
33#include <kis_node.h>
34#include <kis_selection.h>
35#include <kis_selection_mask.h>
36#include <kis_layer.h>
37#include <kis_mask.h>
38#include <kis_image.h>
39#include <kis_painter.h>
40#include <kis_paint_layer.h>
41#include <KisMimeDatabase.h>
43
44#include "KisPart.h"
45#include "canvas/kis_canvas2.h"
48#include "KisViewManager.h"
49#include "KisDocument.h"
50#include "kis_mask_manager.h"
51#include "kis_group_layer.h"
52#include "kis_layer_manager.h"
55#include "kis_action.h"
56#include "kis_action_manager.h"
59#include "kis_transaction.h"
64#include "kis_clipboard.h"
66#include "kis_mimedata.h"
67#include "kis_layer_utils.h"
68#include "krita_utils.h"
69#include "kis_shape_layer.h"
73#include "kis_layer_utils.h"
74#include "kis_filter_mask.h"
75
77#include "KisView.h"
78
79#include <kis_signals_blocker.h>
83
98
99 KisNodeManager * q {nullptr};
100 KisViewManager * view {nullptr};
105 QScopedPointer<KisNodeSelectionAdapter> nodeSelectionAdapter;
106 QScopedPointer<KisNodeInsertionAdapter> nodeInsertionAdapter;
107 QScopedPointer<KisNodeDisplayModeAdapter> nodeDisplayModeAdapter;
108
110
113
115
116 bool activateNodeImpl(KisNodeSP node);
117
120
123
125 const QString &defaultName,
126 const QRect &bounds,
127 qreal xRes,
128 qreal yRes,
129 quint8 opacity);
130
131 void mergeTransparencyMaskAsAlpha(bool writeToLayers);
133};
134
136{
137 Q_ASSERT(view);
138 Q_ASSERT(view->canvasBase());
139 Q_ASSERT(view->canvasBase()->globalShapeManager());
140 Q_ASSERT(imageView);
141 if (node && node == q->activeNode()) {
142 return false;
143 }
144
145 // Set the selection on the shape manager to the active layer
146 // and set call KoSelection::setActiveLayer( KoShapeLayer* layer )
147 // with the parent of the active layer.
149 Q_ASSERT(selection);
150 selection->deselectAll();
151
152 // Disable all enter-group modes that were active
154
155 if (!node) {
156 selection->setActiveLayer(0);
157 imageView->setCurrentNode(0);
161 } else {
163
164 KoShape * shape = view->document()->shapeForNode(node);
165
167
168 selection->select(shape);
169 KoShapeLayer * shapeLayer = dynamic_cast<KoShapeLayer*>(shape);
170
171 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(shapeLayer, false);
172
173 // shapeLayer->setGeometryProtected(node->userLocked());
174 // shapeLayer->setVisible(node->visible());
175 selection->setActiveLayer(shapeLayer);
176
177 imageView->setCurrentNode(node);
178 if (KisLayerSP layer = qobject_cast<KisLayer*>(node.data())) {
181 } else if (KisMaskSP mask = dynamic_cast<KisMask*>(node.data())) {
183 // XXX_NODE: for now, masks cannot be nested.
184 layerManager.activateLayer(static_cast<KisLayer*>(node->parent().data()));
185 }
186 }
187 return true;
188}
189
190//=====================================================================================
191
197
199{
200 delete m_d;
201}
202
204{
205 m_d->maskManager.setView(imageView);
206 m_d->layerManager.setView(imageView);
207
208 if (m_d->imageView) {
209 KisShapeController *shapeController = dynamic_cast<KisShapeController*>(m_d->imageView->document()->shapeController());
210 Q_ASSERT(shapeController);
211 shapeController->disconnect(SIGNAL(sigActivateNode(KisNodeSP)), this);
212 m_d->imageView->image()->disconnect(this);
213 m_d->imageView->image()->disconnect(&m_d->activateNodeConnection);
214 }
215
216 m_d->imageView = imageView;
217
218 if (m_d->imageView) {
219 KisShapeController *shapeController = dynamic_cast<KisShapeController*>(m_d->imageView->document()->shapeController());
220 Q_ASSERT(shapeController);
221 connect(shapeController, SIGNAL(sigActivateNode(KisNodeSP)), SLOT(slotNonUiActivatedNode(KisNodeSP)));
222
223 if (!m_d->imageView->currentNode()) {
228 if (shapeController->lastActivatedNode() && !m_d->imageView->currentNode()) {
229 slotNonUiActivatedNode(shapeController->lastActivatedNode());
230 } else {
231 // if last activated node is null, most probably, it means that the shape controller
232 // is going to Q_EMIT the activation signal very soon
233 }
234 } else {
241 if (!m_d->imageView->currentNode()->graphListener()) {
242 slotNonUiActivatedNode(m_d->imageView->image()->root()->lastChild());
243 }
244 }
245
247 m_d->imageView->resourceProvider()->slotNodeActivated(m_d->imageView->currentNode());
248 connect(m_d->imageView->image(), SIGNAL(sigIsolatedModeChanged()), this, SLOT(handleExternalIsolationChange()));
249 }
250
251}
252
253#define NEW_LAYER_ACTION(id, layerType) \
254{ \
255 action = actionManager->createAction(id); \
256 m_d->nodeCreationSignalMapper.setMapping(action, layerType); \
257 connect(action, SIGNAL(triggered()), \
258 &m_d->nodeCreationSignalMapper, SLOT(map())); \
259 }
260
261#define CONVERT_NODE_ACTION_2(id, layerType, exclude) \
262{ \
263 action = actionManager->createAction(id); \
264 action->setExcludedNodeTypes(QStringList(exclude)); \
265 actionManager->addAction(id, action); \
266 m_d->nodeConversionSignalMapper.setMapping(action, layerType); \
267 connect(action, SIGNAL(triggered()), \
268 &m_d->nodeConversionSignalMapper, SLOT(map())); \
269 }
270
271#define CONVERT_NODE_ACTION(id, layerType) \
272 CONVERT_NODE_ACTION_2(id, layerType, layerType)
273
274void KisNodeManager::setup(KisKActionCollection * actionCollection, KisActionManager* actionManager)
275{
276 m_d->layerManager.setup(actionManager);
277 m_d->maskManager.setup(actionCollection, actionManager);
278
279 KisAction * action = 0;
280
281 action = actionManager->createAction("mirrorNodeX");
282 connect(action, SIGNAL(triggered()), this, SLOT(mirrorNodeX()));
283
284 action = actionManager->createAction("mirrorNodeY");
285 connect(action, SIGNAL(triggered()), this, SLOT(mirrorNodeY()));
286
287 action = actionManager->createAction("mirrorAllNodesX");
288 connect(action, SIGNAL(triggered()), this, SLOT(mirrorAllNodesX()));
289
290 action = actionManager->createAction("mirrorAllNodesY");
291 connect(action, SIGNAL(triggered()), this, SLOT(mirrorAllNodesY()));
292
293 action = actionManager->createAction("activateNextLayer");
294 connect(action, SIGNAL(triggered()), this, SLOT(activateNextNode()));
295
296 action = actionManager->createAction("activateNextSiblingLayer");
297 connect(action, SIGNAL(triggered()), this, SLOT(activateNextSiblingNode()));
298
299 action = actionManager->createAction("activatePreviousLayer");
300 connect(action, SIGNAL(triggered()), this, SLOT(activatePreviousNode()));
301
302 action = actionManager->createAction("activatePreviousSiblingLayer");
303 connect(action, SIGNAL(triggered()), this, SLOT(activatePreviousSiblingNode()));
304
305 action = actionManager->createAction("switchToPreviouslyActiveNode");
306 connect(action, SIGNAL(triggered()), this, SLOT(switchToPreviouslyActiveNode()));
307
308 action = actionManager->createAction("save_node_as_image");
309 connect(action, SIGNAL(triggered()), this, SLOT(saveNodeAsImage()));
310
311 action = actionManager->createAction("save_vector_node_to_svg");
312 connect(action, SIGNAL(triggered()), this, SLOT(saveVectorLayerAsImage()));
314
315 action = actionManager->createAction("duplicatelayer");
316 connect(action, SIGNAL(triggered()), this, SLOT(duplicateActiveNode()));
317
318 action = actionManager->createAction("copy_layer_clipboard");
319 connect(action, SIGNAL(triggered()), this, SLOT(copyLayersToClipboard()));
320
321 action = actionManager->createAction("cut_layer_clipboard");
322 connect(action, SIGNAL(triggered()), this, SLOT(cutLayersToClipboard()));
323
324 action = actionManager->createAction("paste_layer_from_clipboard");
325 connect(action, SIGNAL(triggered()), this, SLOT(pasteLayersFromClipboard()));
326
327 action = actionManager->createAction("create_quick_group");
328 connect(action, SIGNAL(triggered()), this, SLOT(createQuickGroup()));
329
330 action = actionManager->createAction("create_quick_clipping_group");
331 connect(action, SIGNAL(triggered()), this, SLOT(createQuickClippingGroup()));
332
333 action = actionManager->createAction("quick_ungroup");
334 connect(action, SIGNAL(triggered()), this, SLOT(quickUngroup()));
335
336 action = actionManager->createAction("select_all_layers");
337 connect(action, SIGNAL(triggered()), this, SLOT(selectAllNodes()));
338
339 action = actionManager->createAction("select_visible_layers");
340 connect(action, SIGNAL(triggered()), this, SLOT(selectVisibleNodes()));
341
342 action = actionManager->createAction("select_locked_layers");
343 connect(action, SIGNAL(triggered()), this, SLOT(selectLockedNodes()));
344
345 action = actionManager->createAction("select_invisible_layers");
346 connect(action, SIGNAL(triggered()), this, SLOT(selectInvisibleNodes()));
347
348 action = actionManager->createAction("select_unlocked_layers");
349 connect(action, SIGNAL(triggered()), this, SLOT(selectUnlockedNodes()));
350
351 action = actionManager->createAction("new_from_visible");
352 connect(action, SIGNAL(triggered()), this, SLOT(createFromVisible()));
353
354 action = actionManager->createAction("create_reference_image_from_active_layer");
355 connect(action, SIGNAL(triggered()), this, SLOT(createReferenceImageFromLayer()));
356
357 action = actionManager->createAction("create_reference_image_from_visible_canvas");
358 connect(action, SIGNAL(triggered()), this, SLOT(createReferenceImageFromVisible()));
359
360 action = actionManager->createAction("pin_to_timeline");
361 action->setCheckable(true);
362 connect(action, SIGNAL(toggled(bool)), this, SLOT(slotPinToTimeline(bool)));
363 m_d->pinToTimeline = action;
364
365 NEW_LAYER_ACTION("add_new_paint_layer", "KisPaintLayer");
366
367 NEW_LAYER_ACTION("add_new_group_layer", "KisGroupLayer");
368
369 NEW_LAYER_ACTION("add_new_clone_layer", "KisCloneLayer");
370
371 NEW_LAYER_ACTION("add_new_shape_layer", "KisShapeLayer");
372
373 NEW_LAYER_ACTION("add_new_adjustment_layer", "KisAdjustmentLayer");
374
375 NEW_LAYER_ACTION("add_new_fill_layer", "KisGeneratorLayer");
376
377 NEW_LAYER_ACTION("add_new_file_layer", "KisFileLayer");
378
379 NEW_LAYER_ACTION("add_new_transparency_mask", "KisTransparencyMask");
380
381 NEW_LAYER_ACTION("add_new_filter_mask", "KisFilterMask");
382
383 // NOTE: FastColorOverlayFilterMask is just an identifier, not an actual class name
384 NEW_LAYER_ACTION("add_new_fast_color_overlay_mask", "FastColorOverlayFilterMask");
385
386 NEW_LAYER_ACTION("add_new_colorize_mask", "KisColorizeMask");
387
388 NEW_LAYER_ACTION("add_new_transform_mask", "KisTransformMask");
389
390 NEW_LAYER_ACTION("add_new_selection_mask", "KisSelectionMask");
391
392 connect(&m_d->nodeCreationSignalMapper, SIGNAL(mapped(QString)),
393 this, SLOT(createNode(QString)));
394
395 CONVERT_NODE_ACTION("convert_to_paint_layer", "KisPaintLayer");
396
397 CONVERT_NODE_ACTION_2("convert_to_selection_mask", "KisSelectionMask", QStringList() << "KisSelectionMask" << "KisColorizeMask");
398
399 CONVERT_NODE_ACTION_2("convert_to_filter_mask", "KisFilterMask", QStringList() << "KisFilterMask" << "KisColorizeMask");
400
401 CONVERT_NODE_ACTION_2("convert_to_transparency_mask", "KisTransparencyMask", QStringList() << "KisTransparencyMask" << "KisColorizeMask");
402
403 CONVERT_NODE_ACTION("convert_to_animated", "animated");
404
405 CONVERT_NODE_ACTION_2("convert_to_file_layer", "KisFileLayer", QStringList() << "KisFileLayer" << "KisCloneLayer");
406
407 connect(&m_d->nodeConversionSignalMapper, SIGNAL(mapped(QString)),
408 this, SLOT(convertNode(QString)));
409
410 // Isolation Modes...
411 // Post Qt5.14 this can be replaced with QActionGroup + ExclusionPolicy::ExclusiveOptional.
412 action = actionManager->createAction("isolate_active_layer");
413 connect(action, SIGNAL(toggled(bool)), this, SLOT(setIsolateActiveLayerMode(bool)));
414 action = actionManager->createAction("isolate_active_group");
415 connect(action, SIGNAL(triggered(bool)), this, SLOT(setIsolateActiveGroupMode(bool)));
416 connect(this, SIGNAL(sigNodeActivated(KisNodeSP)), SLOT(changeIsolationRoot(KisNodeSP)));
417
418 action = actionManager->createAction("toggle_layer_visibility");
419 connect(action, SIGNAL(triggered()), this, SLOT(toggleVisibility()));
420
421 action = actionManager->createAction("toggle_layer_lock");
422 connect(action, SIGNAL(triggered()), this, SLOT(toggleLock()));
423
424 action = actionManager->createAction("toggle_layer_inherit_alpha");
425 connect(action, SIGNAL(triggered()), this, SLOT(toggleInheritAlpha()));
426
427 action = actionManager->createAction("toggle_layer_alpha_lock");
428 connect(action, SIGNAL(triggered()), this, SLOT(toggleAlphaLock()));
429
430 action = actionManager->createAction("split_alpha_into_mask");
431 connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaIntoMask()));
432
433 action = actionManager->createAction("split_alpha_write");
434 connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaWrite()));
435
436 // HINT: we can save even when the nodes are not editable
437 action = actionManager->createAction("split_alpha_save_merged");
438 connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaSaveMerged()));
439}
440
442{
443 // enable/disable all relevant actions
446}
447
449{
450 if (m_d->imageView) {
451 return m_d->imageView->currentNode();
452 }
453 return 0;
454}
455
460
462{
465 } else {
466 Q_ASSERT(m_d->layerManager.activeLayer());
469 else
470 return m_d->view->image()->colorSpace();
471 }
472}
473
474bool KisNodeManager::canModifyLayers(KisNodeList nodes, bool showWarning)
475{
476 KisNodeSP lockedNode;
477 Q_FOREACH (KisNodeSP node, nodes) {
478 if (!node->isEditable(false)) {
479 lockedNode = node;
480 break;
481 }
482 }
483
484 if (lockedNode && showWarning) {
485 QString errorMessage;
486
487 if (nodes.size() <= 1) {
488 errorMessage = i18n("Layer is locked");
489 } else {
490 errorMessage = i18n("Layer \"%1\" is locked", lockedNode->name());
491 }
492
493 m_d->view->showFloatingMessage(errorMessage, QIcon());
494 }
495
496 return !lockedNode;
497}
498
499bool KisNodeManager::canModifyLayer(KisNodeSP node, bool showWarning)
500{
501 return canModifyLayers({node}, showWarning);
502}
503
504bool KisNodeManager::canMoveLayers(KisNodeList nodes, bool showWarning)
505{
506 KisNodeSP lockedNode;
507 Q_FOREACH (KisNodeSP node, nodes) {
508 if (node->parent() && !node->parent()->isEditable(false)) {
509 lockedNode = node->parent();
510 break;
511 }
512 }
513
514 if (lockedNode && showWarning) {
515 QString errorMessage = i18n("Layer \"%1\" is locked", lockedNode->name());
516 m_d->view->showFloatingMessage(errorMessage, QIcon());
517 }
518
519 return !lockedNode;
520}
521
522bool KisNodeManager::canMoveLayer(KisNodeSP node, bool showWarning)
523{
524 return canMoveLayers({node}, showWarning);
525}
526
527void KisNodeManager::moveNodeAt(KisNodeSP node, KisNodeSP parent, int index)
528{
529 if (parent->allowAsChild(node)) {
530 if (node->inherits("KisSelectionMask") && parent->inherits("KisLayer")) {
531 KisSelectionMask *m = dynamic_cast<KisSelectionMask*>(node.data());
532 KisLayer *l = qobject_cast<KisLayer*>(parent.data());
533 if (m && m->active() && l && l->selectionMask()) {
534 l->selectionMask()->setActive(false);
535 }
536 }
537 m_d->commandsAdapter.moveNode(node, parent, index);
538 }
539}
540
542{
543 KUndo2MagicString actionName = kundo2_i18n("Move Nodes");
544 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
545 juggler->moveNode(nodes, parent, aboveThis);
546}
547
549{
550 KUndo2MagicString actionName = kundo2_i18n("Copy Nodes");
551 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
552 juggler->copyNode(nodes, parent, aboveThis);
553}
554
556{
557 KUndo2MagicString actionName = kundo2_i18n("Add Nodes");
558 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
559 juggler->addNode(nodes, parent, aboveThis);
560}
561
563{
564 QAction* action = m_d->view->actionManager()->actionByName("isolate_active_layer");
565 action->toggle();
566}
567
569{
570 KisImageWSP image = m_d->view->image();
572
573 const bool groupIsolationState = image->isIsolatingGroup();
574 changeIsolationMode(checked, groupIsolationState);
575}
576
578{
579 KisImageWSP image = m_d->view->image();
581
582 const bool layerIsolationState = image->isIsolatingLayer();
583 changeIsolationMode(layerIsolationState, checked);
584}
585
586void KisNodeManager::changeIsolationMode(bool isolateActiveLayer, bool isolateActiveGroup)
587{
588 KisImageWSP image = m_d->view->image();
591
592 if (isolateActiveLayer || isolateActiveGroup) {
593 if (image->startIsolatedMode(activeNode, isolateActiveLayer, isolateActiveGroup) == false) {
595 }
596 } else {
597 image->stopIsolatedMode();
598 }
599}
600
602{
603 KisImageWSP image = m_d->view->image();
604 if (!image || !isolationRoot) return;
605
606 const bool isIsolatingLayer = image->isIsolatingLayer();
607 const bool isIsolatingGroup = image->isIsolatingGroup();
608
609 // Restart isolation with a new root node and the same settings.
610 if (image->startIsolatedMode(isolationRoot, isIsolatingLayer, isIsolatingGroup) == false) {
612 }
613}
614
616{
617 // It might be that we have multiple Krita windows open. In such a case
618 // only the currently active one should restart isolated mode
619 if (!m_d->view->mainWindowAsQWidget()->isActiveWindow()) return;
620
621 KisImageWSP image = m_d->view->image();
623
624 const bool isIsolatingLayer = image->isIsolatingLayer();
625 const bool isIsolatingGroup = image->isIsolatingGroup();
626
627 m_d->view->actionManager()->actionByName("isolate_active_layer")->setChecked(isIsolatingLayer);
628 m_d->view->actionManager()->actionByName("isolate_active_group")->setChecked(isIsolatingGroup);
629}
630
632{
633 m_d->view->actionManager()->actionByName("isolate_active_layer")->setChecked(false);
634 m_d->view->actionManager()->actionByName("isolate_active_group")->setChecked(false);
635}
636
637KisNodeSP KisNodeManager::createNode(const QString & nodeType, bool quiet, KisPaintDeviceSP copyFrom)
638{
640 return 0;
641 }
642
644 if (!activeNode) {
645 activeNode = m_d->view->image()->root();
646 }
647
649
653
654 // XXX: make factories for this kind of stuff,
655 // with a registry
656
657 if (nodeType == "KisPaintLayer") {
659 } else if (nodeType == "KisGroupLayer") {
661 } else if (nodeType == "KisAdjustmentLayer") {
663 } else if (nodeType == "KisGeneratorLayer") {
665 } else if (nodeType == "KisShapeLayer") {
667 } else if (nodeType == "KisCloneLayer") {
668 KisNodeList nodes = selectedNodes();
669 if (nodes.isEmpty()) {
670 nodes.append(activeNode);
671 }
672 return m_d->layerManager.addCloneLayer(nodes);
673 } else if (nodeType == "KisTransparencyMask") {
674 return m_d->maskManager.createTransparencyMask(activeNode, copyFrom, false);
675 } else if (nodeType == "KisFilterMask") {
676 return m_d->maskManager.createFilterMask(activeNode, copyFrom, quiet, false);
677 } else if (nodeType == "FastColorOverlayFilterMask") {
679 } else if (nodeType == "KisColorizeMask") {
681 } else if (nodeType == "KisTransformMask") {
683 } else if (nodeType == "KisSelectionMask") {
684 return m_d->maskManager.createSelectionMask(activeNode, copyFrom, false);
685 } else if (nodeType == "KisFileLayer") {
687 }
688 return 0;
689}
690
695
697{
698 Q_FOREACH (KisNodeSP node, selectedNodes()) {
700 }
701}
702
704{
705 KisNodeSP node = createNode("KisPaintLayer");
706 return dynamic_cast<KisLayer*>(node.data());
707}
708
710{
712 return;
713 }
714
716 if (!activeNode) return;
717
718 if (!canModifyLayer(activeNode)) return;
719
720 if (nodeType == "KisPaintLayer") {
722 } else if (nodeType == "KisSelectionMask" ||
723 nodeType == "KisFilterMask" ||
724 nodeType == "KisTransparencyMask") {
725
728
729 m_d->commandsAdapter.beginMacro(kundo2_i18n("Convert to a Selection Mask"));
730
731 bool result = false;
732
733 if (nodeType == "KisSelectionMask") {
734 result = !m_d->maskManager.createSelectionMask(activeNode, copyFrom, true).isNull();
735 } else if (nodeType == "KisFilterMask") {
736 result = !m_d->maskManager.createFilterMask(activeNode, copyFrom, false, true).isNull();
737 } else if (nodeType == "KisTransparencyMask") {
738 result = !m_d->maskManager.createTransparencyMask(activeNode, copyFrom, true).isNull();
739 }
740
742
743 if (!result) {
746 }
747 } else if (nodeType == "KisFileLayer") {
749 } else {
750 warnKrita << "Unsupported node conversion type:" << nodeType;
751 }
752}
753
755 KisViewManager* m_view = m_d->view;
756 KisDocument *document = m_view->document();
757 KisCanvas2 *canvas = m_view->canvasBase();
758
759 const KisPaintDeviceSP paintDevice = fromLayer ? m_view->activeLayer()->projection()
760 : canvas->currentImage()->projection();
763 std::unique_ptr<KisReferenceImage> reference(KisReferenceImage::fromQImage(*canvas->coordinatesConverter(), image));
765 if (reference) {
766 if (document->referenceImagesLayer()) {
767 reference->setZIndex(document->referenceImagesLayer()->shapes().size());
768 }
769 canvas->addCommand(KisReferenceImagesLayer::addReferenceImages(document, {reference.release()}));
770
771 KoToolManager::instance()->switchToolRequested("ToolReferenceImages");
772
773 } else {
774 if (canvas->canvasWidget()) {
775 QString strMessage = fromLayer ? i18nc("error dialog from the reference tool", "Could not create a reference image from the active layer.")
776 : i18nc("error dialog from the reference tool", "Could not create a reference image from the visible canvas.");
777
778 m_d->view->showFloatingMessage(strMessage, QIcon(), 5000, KisFloatingMessage::High, Qt::TextSingleLine);
779 }
780 }
781}
782
786
790
792{
793 KisDummiesFacadeBase *dummiesFacade = dynamic_cast<KisDummiesFacadeBase*>(m_d->imageView->document()->shapeController());
794 KIS_SAFE_ASSERT_RECOVER_RETURN(dummiesFacade);
795
796 const bool nodeVisible = !isNodeHidden(node, !m_d->nodeDisplayModeAdapter->showGlobalSelectionMask());
797 if (!nodeVisible) {
798 return;
799 }
800
802 if (m_d->activateNodeImpl(node)) {
803 if (node) {
809 dummiesFacade->setLastActivatedNode(node);
810 }
811 Q_EMIT sigUiNeedChangeActiveNode(node);
812 Q_EMIT sigNodeActivated(node);
813 nodesUpdated();
814 if (node) {
815 bool toggled = m_d->view->actionCollection()->action("view_show_canvas_only")->isChecked();
816 if (toggled) {
817 m_d->view->showFloatingMessage( node->name(), QIcon(), 1600, KisFloatingMessage::Medium, Qt::TextSingleLine);
818 }
819 }
820 }
821}
822
824{
825 // the node must still be in the graph, some asynchronous
826 // signals may easily break this requirement
827 if (node && !node->graphListener()) {
828 node = 0;
829 }
830
831 if (node == activeNode()) return;
832
834}
835
837{
838 // the node must still be in the graph, some asynchronous
839 // signals may easily break this requirement
840 if (node && !node->graphListener()) {
841 node = 0;
842 }
843
844 if (node) {
845 QStringList vectorTools = QStringList()
846 << "InteractionTool"
847 << "KarbonGradientTool"
848 << "KarbonCalligraphyTool"
849 << "PathTool";
850
851 QStringList pixelTools = QStringList()
852 << "KritaShape/KisToolBrush"
853 << "KritaShape/KisToolDyna"
854 << "KritaShape/KisToolMultiBrush"
855 << "KritaFill/KisToolFill"
856 << "KritaFill/KisToolGradient";
857
858 KisSelectionMask *selectionMask = dynamic_cast<KisSelectionMask*>(node.data());
859 const bool nodeHasVectorAbilities = node->inherits("KisShapeLayer") ||
860 (selectionMask && selectionMask->selection()->hasShapeSelection());
861
862 if (nodeHasVectorAbilities) {
863 if (pixelTools.contains(KoToolManager::instance()->activeToolId())) {
864 KoToolManager::instance()->switchToolRequested("InteractionTool");
865 }
866 }
867 else {
868 if (vectorTools.contains(KoToolManager::instance()->activeToolId())) {
869 KoToolManager::instance()->switchToolRequested("KritaShape/KisToolBrush");
870 }
871 }
872 }
873
874 if (node == activeNode()) return;
875
877}
878
880{
881 KisNodeSP node = activeNode();
882 if (!node) return;
883
886
887 m_d->view->updateGUI();
889
890 {
892 m_d->pinToTimeline->setChecked(node->isPinnedToTimeline());
893 }
894}
895
902
904{
905 if ((selectedNodes().size() > 1 && node->inherits("KisLayer")) || node->inherits("KisLayer")) {
907 }
908 else if (node->inherits("KisMask")) {
910 }
911}
912
914{
915 Q_ASSERT(node);
916
917 // Change the current node temporarily
918 KisNodeSP originalNode = m_d->imageView->currentNode();
919 m_d->imageView->setCurrentNode(node);
920
921 if (node->inherits("KisLayer")) {
923 }
924 else if (node->inherits("KisMask")) {
926 }
927
928 m_d->imageView->setCurrentNode(originalNode);
929}
930
935
937{
943 return qMin(255, int(opacity * 2.55 + 0.5));
944}
945
946void KisNodeManager::setNodeName(KisNodeSP node, const QString &name)
947{
948 if (!node) return;
949 if (node->name() == name) return;
950
951 m_d->commandsAdapter.setNodeName(node, name);
952
953}
954
956{
957 if (!node) return;
958 if (node->opacity() == opacity) return;
959
960 m_d->commandsAdapter.setOpacity(node, opacity);
961}
962
964 const KoCompositeOp* compositeOp)
965{
966 if (!node) return;
967 if (node->compositeOp() == compositeOp) return;
968
969 m_d->commandsAdapter.setCompositeOp(node, compositeOp);
970}
971
973{
974 if (activeNode) {
976 }
977 if (!selectedNodes.isEmpty()) {
979 }
980}
981
983{
984 m_d->selectedNodes = nodes;
985 Q_EMIT sigUiNeedChangeSelectedNodes(nodes);
986}
987
992
997
1002
1007
1008bool KisNodeManager::isNodeHidden(KisNodeSP node, bool isGlobalSelectionHidden)
1009{
1010 if (node && node->isFakeNode()) {
1011 return true;
1012 }
1013
1014 if (isGlobalSelectionHidden && dynamic_cast<KisSelectionMask *>(node.data()) &&
1015 (!node->parent() || !node->parent()->parent())) {
1016 return true;
1017 }
1018
1019 return false;
1020}
1021
1023{
1024 const KisPaintLayer *paintLayer = dynamic_cast<KisPaintLayer*>(node.data());
1025 if (paintLayer) {
1027
1028 if (properties.contains(onionSkinOn)) {
1029 const KisPaintDeviceSP &paintDevice = paintLayer->paintDevice();
1030 if (paintDevice && paintDevice->defaultPixel().opacityU8() == 255) {
1031 m_d->view->showFloatingMessage(i18n("Onion skins require a layer with transparent background."), QIcon());
1032 return false;
1033 }
1034 }
1035 }
1036
1038
1039 return true;
1040}
1041
1043{
1044 KisNodeSP node = activeNode();
1045
1046 setNodeOpacity(node, convertOpacityToInt(opacity));
1047}
1048
1050{
1051 KisNodeSP node = activeNode();
1052
1053 setNodeCompositeOp(node, op);
1054}
1055
1057{
1058 KUndo2MagicString actionName = kundo2_i18n("Duplicate Nodes");
1059 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
1060 juggler->duplicateNode(selectedNodes());
1061}
1062
1064{
1065 KisImageWSP image = view->image();
1066
1067 if (!nodeJuggler ||
1068 (nodeJuggler &&
1069 (nodeJuggler->isEnded() ||
1070 !nodeJuggler->canMergeAction(actionName)))) {
1071
1072 nodeJuggler = new KisNodeJugglerCompressed(actionName, image, q, 750);
1073 nodeJuggler->setAutoDelete(true);
1074 }
1075
1076 return nodeJuggler;
1077}
1078
1080{
1081 if (!canMoveLayers(selectedNodes())) return;
1082
1083 KUndo2MagicString actionName = kundo2_i18n("Raise Nodes");
1084 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
1085 juggler->raiseNode(selectedNodes());
1086}
1087
1089{
1090 if (!canMoveLayers(selectedNodes())) return;
1091
1092 KUndo2MagicString actionName = kundo2_i18n("Lower Nodes");
1093 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
1094 juggler->lowerNode(selectedNodes());
1095}
1096
1098{
1099 if (!node || !node->parent()) {
1100 return;
1101 }
1102
1103 KisNodeList nodes;
1104 nodes << node;
1105 removeSelectedNodes(nodes);
1106}
1107
1109{
1110 if (!canModifyLayers(nodes)) return;
1111
1112 KUndo2MagicString actionName = kundo2_i18n("Remove Nodes");
1113 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
1114 juggler->removeNode(nodes);
1115}
1116
1121
1123{
1124 KisNodeList nodes = selectedNodes();
1125
1126 KUndo2MagicString commandName;
1127 if (nodes.size() == 1 && nodes[0]->inherits("KisMask")) {
1128 commandName = kundo2_i18n("Mirror Mask Horizontally");
1129 }
1130 else {
1131 commandName = kundo2_i18np("Mirror Layer Horizontally", "Mirror %1 Layers Horizontally", nodes.size());
1132 }
1133 mirrorNodes(nodes, commandName, Qt::Horizontal, m_d->view->selection());
1134}
1135
1137{
1138 KisNodeList nodes = selectedNodes();
1139
1140 KUndo2MagicString commandName;
1141 if (nodes.size() == 1 && nodes[0]->inherits("KisMask")) {
1142 commandName = kundo2_i18n("Mirror Mask Vertically");
1143 }
1144 else {
1145 commandName = kundo2_i18np("Mirror Layer Vertically", "Mirror %1 Layers Vertically", nodes.size());
1146 }
1147 mirrorNodes(nodes, commandName, Qt::Vertical, m_d->view->selection());
1148}
1149
1151{
1152 KisNodeSP node = m_d->view->image()->root();
1153 mirrorNode(node, kundo2_i18n("Mirror All Layers Horizontally"),
1154 Qt::Horizontal, m_d->view->selection());
1155}
1156
1158{
1159 KisNodeSP node = m_d->view->image()->root();
1160 mirrorNode(node, kundo2_i18n("Mirror All Layers Vertically"),
1161 Qt::Vertical, m_d->view->selection());
1162}
1163
1165{
1167 if (!activeNode) return;
1168
1169 KisNodeSP nextNode = activeNode->nextSibling();
1170
1171 if (!siblingsOnly) {
1172 // Recurse groups...
1173 while (nextNode && nextNode->childCount() > 0) {
1174 nextNode = nextNode->firstChild();
1175 }
1176
1177 // Out of nodes? Back out of group...
1178 if (!nextNode && activeNode->parent()) {
1179 nextNode = activeNode->parent();
1180 }
1181 }
1182
1183 // Skip nodes hidden from tree view..
1184 while (nextNode && isNodeHidden(nextNode, m_d->nodeDisplayModeAdapter->showGlobalSelectionMask())) {
1185 nextNode = nextNode->nextSibling();
1186 }
1187
1188 // Select node, unless root..
1189 if (nextNode && nextNode->parent()) {
1190 slotNonUiActivatedNode(nextNode);
1191 }
1192}
1193
1198
1200{
1202 if (!activeNode) return;
1203
1204 KisNodeSP nextNode = activeNode->prevSibling();
1205
1206 if (!siblingsOnly) {
1207 // Enter groups..
1208 if (activeNode->childCount() > 0) {
1209 nextNode = activeNode->lastChild();
1210 }
1211
1212 // Out of nodes? Back out of group...
1213 if (!nextNode && activeNode->parent()) {
1214 nextNode = activeNode->parent()->prevSibling();
1215 }
1216 }
1217
1218 // Skip nodes hidden from tree view..
1219 while (nextNode && isNodeHidden(nextNode, m_d->nodeDisplayModeAdapter->showGlobalSelectionMask())) {
1220 nextNode = nextNode->prevSibling();
1221 }
1222
1223 // Select node, unless root..
1224 if (nextNode && nextNode->parent()) {
1225 slotNonUiActivatedNode(nextNode);
1226 }
1227}
1228
1233
1240
1242 const KUndo2MagicString& actionName,
1243 Qt::Orientation orientation,
1244 KisSelectionSP selection)
1245{
1246 KisNodeList nodes = {node};
1247 mirrorNodes(nodes, actionName, orientation, selection);
1248}
1249
1251 const KUndo2MagicString& actionName,
1252 Qt::Orientation orientation,
1253 KisSelectionSP selection)
1254{
1255 Q_FOREACH(KisNodeSP node, nodes) {
1256 if (!canModifyLayer(node)) return;
1257 }
1258
1259 KisImageSignalVector emitSignals;
1260
1261 KisProcessingApplicator applicator(m_d->view->image(), nodes,
1263 emitSignals, actionName);
1264
1265 KisProcessingVisitorSP visitor;
1266
1267 if (selection) {
1268 visitor = new KisMirrorProcessingVisitor(selection, orientation);
1269 } else {
1270 visitor = new KisMirrorProcessingVisitor(m_d->view->image()->bounds(), orientation);
1271 }
1272
1273 if (!selection) {
1275 } else {
1276 applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
1277 }
1278
1279 applicator.end();
1280
1281 nodesUpdated();
1282}
1283
1285 const QString &defaultName,
1286 const QRect &bounds,
1287 qreal xRes,
1288 qreal yRes,
1289 quint8 opacity)
1290{
1291 KoFileDialog dialog(view->mainWindowAsQWidget(), KoFileDialog::SaveFile, "savenodeasimage");
1292 dialog.setCaption(i18n("Export \"%1\"", defaultName));
1293 dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
1295 QString filename = dialog.filename();
1296
1297 if (filename.isEmpty()) return;
1298
1299 QString mimefilter = KisMimeDatabase::mimeTypeForFile(filename, false);
1300
1301 QScopedPointer<KisDocument> doc(KisPart::instance()->createDocument());
1302
1303 KisImageSP dst = new KisImage(doc->createUndoStore(),
1304 bounds.width(),
1305 bounds.height(),
1307 defaultName);
1308 dst->setResolution(xRes, yRes);
1309 doc->setCurrentImage(dst);
1310 KisPaintLayer* paintLayer = new KisPaintLayer(dst, "paint device", opacity);
1311 paintLayer->paintDevice()->makeCloneFrom(device, bounds);
1312 dst->addNode(paintLayer, dst->rootLayer(), KisLayerSP(0));
1313
1314 dst->initialRefreshGraph();
1315
1316 if (!doc->exportDocumentSync(filename, mimefilter.toLatin1())) {
1317 QMessageBox::warning(qApp->activeWindow(),
1318 i18nc("@title:window", "Krita"),
1319 i18n("Could not save the layer. %1", doc->errorMessage().toUtf8().data()),
1320 QMessageBox::Ok);
1321
1322 }
1323}
1324
1326{
1327 KisNodeSP node = activeNode();
1328
1329 if (!node) {
1330 warnKrita << "BUG: Save Node As Image was called without any node selected";
1331 return;
1332 }
1333
1334 KisPaintDeviceSP saveDevice = node->projection();
1335
1336 if (!saveDevice) {
1337 m_d->view->showFloatingMessage(i18nc("warning message when trying to export a transform mask", "Layer has no pixel data"), QIcon());
1338 return;
1339 }
1340
1341 KisImageSP image = m_d->view->image();
1342 QRect saveRect = image->bounds() | node->exactBounds();
1343
1344 m_d->saveDeviceAsImage(saveDevice,
1345 node->name(),
1346 saveRect,
1347 image->xRes(), image->yRes(),
1348 node->opacity());
1349}
1350
1351#include "SvgWriter.h"
1352
1354{
1355 KisShapeLayerSP shapeLayer = qobject_cast<KisShapeLayer*>(activeNode().data());
1356 if (!shapeLayer) {
1357 return;
1358 }
1359
1360 KoFileDialog dialog(m_d->view->mainWindowAsQWidget(), KoFileDialog::SaveFile, "savenodeasimage");
1361 dialog.setCaption(i18nc("@title:window", "Export to SVG"));
1362 dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
1363 dialog.setMimeTypeFilters(QStringList() << "image/svg+xml", "image/svg+xml");
1364 QString filename = dialog.filename();
1365
1366 if (filename.isEmpty()) return;
1367
1368 QUrl url = QUrl::fromLocalFile(filename);
1369
1370 if (url.isEmpty()) return;
1371
1372 const QSizeF sizeInPx = m_d->view->image()->bounds().size();
1373 const QSizeF sizeInPt(sizeInPx.width() / m_d->view->image()->xRes(),
1374 sizeInPx.height() / m_d->view->image()->yRes());
1375
1376 QList<KoShape*> shapes = shapeLayer->shapes();
1377 std::sort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex);
1378
1379 SvgWriter writer(shapes);
1380 if (!writer.save(filename, sizeInPt, true)) {
1381 QMessageBox::warning(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("Could not save to svg: %1", filename));
1382 }
1383}
1384
1386{
1387 KisNodeSP node = activeNode();
1388 if (!canModifyLayer(node)) return;
1389
1390 // guaranteed by KisActionManager
1392
1393 KisLayerUtils::splitAlphaToMask(node->image(), node, m_d->maskManager.createMaskNameCommon(node, "KisTransparencyMask", i18n("Transparency Mask")));
1394}
1395
1397{
1398 KisNodeSP node = q->activeNode();
1399 KisNodeSP parentNode = node->parent();
1400
1401 // guaranteed by KisActionManager
1402 KIS_ASSERT_RECOVER_RETURN(node->inherits("KisTransparencyMask"));
1403
1404 if (writeToLayers && (!parentNode->hasEditablePaintDevice() || !node->isEditable(false))) {
1405 QMessageBox::information(view->mainWindowAsQWidget(),
1406 i18nc("@title:window", "Layer %1 is not editable", parentNode->name()),
1407 i18n("Cannot write alpha channel of "
1408 "the parent layer \"%1\".\n"
1409 "The operation will be cancelled.", parentNode->name()));
1410 return;
1411 }
1412
1413 KisPaintDeviceSP dstDevice;
1414 if (writeToLayers) {
1416 dstDevice = parentNode->paintDevice();
1417 } else {
1418 KisPaintDeviceSP copyDevice = parentNode->paintDevice();
1419 if (!copyDevice) {
1420 copyDevice = parentNode->original();
1421 }
1422 dstDevice = new KisPaintDevice(*copyDevice);
1423 }
1424
1425 const KoColorSpace *dstCS = dstDevice->colorSpace();
1426
1427 KisPaintDeviceSP selectionDevice = node->paintDevice();
1428 KIS_ASSERT_RECOVER_RETURN(selectionDevice->colorSpace()->pixelSize() == 1);
1429
1430 const QRect processRect =
1431 selectionDevice->exactBounds() |
1432 dstDevice->exactBounds() |
1433 selectionDevice->defaultBounds()->bounds();
1434
1435 QScopedPointer<KisTransaction> transaction;
1436
1437 if (writeToLayers) {
1438 commandsAdapter.beginMacro(kundo2_i18n("Write Alpha into a Layer"));
1439 transaction.reset(new KisTransaction(kundo2_noi18n("__write_alpha_channel__"), dstDevice));
1440 }
1441
1442 KisSequentialIterator srcIt(selectionDevice, processRect);
1443 KisSequentialIterator dstIt(dstDevice, processRect);
1444
1445 while (srcIt.nextPixel() && dstIt.nextPixel()) {
1446 quint8 *alpha8Ptr = srcIt.rawData();
1447 quint8 *dstPtr = dstIt.rawData();
1448
1449 dstCS->setOpacity(dstPtr, *alpha8Ptr, 1);
1450 }
1451
1452 if (writeToLayers) {
1453 commandsAdapter.addExtraCommand(transaction->endAndTake());
1454 commandsAdapter.removeNode(node);
1455 commandsAdapter.endMacro();
1456 } else {
1457 KisImageWSP image = view->image();
1458 QRect saveRect = image->bounds();
1459
1460 saveDeviceAsImage(dstDevice, parentNode->name(),
1461 saveRect,
1462 image->xRes(), image->yRes(),
1464 }
1465}
1466
1467
1472
1477
1479{
1480 KisNodeList nodes = this->selectedNodes();
1481 KisNodeSP active = activeNode();
1482 if (nodes.isEmpty() || !active) return;
1483
1484 bool isLocked = active->userLocked();
1485
1486 for (auto &node : nodes) {
1488 }
1489}
1490
1492{
1493 KisNodeList nodes = this->selectedNodes();
1494 KisNodeSP active = activeNode();
1495 if (nodes.isEmpty() || !active) return;
1496
1497 bool isVisible = active->visible();
1498
1499 for (auto &node : nodes) {
1501 }
1502}
1503
1505{
1506 KisNodeList nodes = this->selectedNodes();
1507 KisNodeSP active = activeNode();
1508 if (nodes.isEmpty() || !active) return;
1509
1510 auto layer = qobject_cast<KisPaintLayer*>(active.data());
1511 if (!layer) {
1512 return;
1513 }
1514
1515 bool isAlphaLocked = layer->alphaLocked();
1516 for (auto &node : nodes) {
1517 auto layer = qobject_cast<KisPaintLayer*>(node.data());
1518 if (layer) {
1520 }
1521 }
1522}
1523
1525{
1526 KisNodeList nodes = this->selectedNodes();
1527 KisNodeSP active = activeNode();
1528 if (nodes.isEmpty() || !active) return;
1529
1530 auto layer = qobject_cast<KisLayer*>(active.data());
1531 if (!layer) {
1532 return;
1533 }
1534
1535 bool isAlphaDisabled = layer->alphaChannelDisabled();
1536 for (auto &node : nodes) {
1537 auto layer = qobject_cast<KisLayer*>(node.data());
1538 if (layer) {
1540 }
1541 }
1542}
1543
1545{
1546 Q_ASSERT(node);
1547 KisLayerSP layer = qobject_cast<KisLayer*>(node.data());
1548 if (!layer) {
1549 return;
1550 }
1551
1552 KisFilterMaskSP mask = layer->colorOverlayMask();
1553 if (!mask) {
1554 // This layer does not use fast color overlay mask.
1555 return;
1556 }
1557
1559}
1560
1562{
1563 KisNodeList nodes = this->selectedNodes();
1564 if (nodes.isEmpty()) return;
1565
1566 KisNodeList::Iterator it = nodes.begin();
1567 while (it != nodes.end()) {
1568 // make sure the deleted nodes aren't referenced here again
1569 if (!it->data()->parent()) {
1570 nodes.erase(it);
1571 }
1572 it++;
1573 }
1574
1575 KisClipboard::instance()->setLayers(nodes, m_d->view->image(), false);
1576
1577 if (canModifyLayers(nodes)) {
1578 KUndo2MagicString actionName = kundo2_i18n("Cut Nodes");
1579 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
1580 juggler->removeNode(nodes);
1581 }
1582}
1583
1585{
1586 KisNodeList nodes = this->selectedNodes();
1587 KisClipboard::instance()->setLayers(nodes, m_d->view->image(), true);
1588}
1589
1590void KisNodeManager::pasteLayersFromClipboard(bool changeOffset, QPointF offset, KisProcessingApplicator *applicator)
1591{
1592 const QMimeData *data = KisClipboard::instance()->layersMimeData();
1593 if (!data) return;
1594
1596
1597 KisShapeController *shapeController = dynamic_cast<KisShapeController*>(m_d->imageView->document()->shapeController());
1598 Q_ASSERT(shapeController);
1599
1600 KisDummiesFacadeBase *dummiesFacade = dynamic_cast<KisDummiesFacadeBase*>(m_d->imageView->document()->shapeController());
1601 Q_ASSERT(dummiesFacade);
1602
1603 const bool copyNode = false;
1604 KisImageSP image = m_d->view->image();
1605 KisNodeDummy *parentDummy = dummiesFacade->dummyForNode(activeNode ? activeNode : image->root());
1606 KisNodeDummy *aboveThisDummy = parentDummy ? parentDummy->lastChild() : 0;
1607
1609 image,
1610 shapeController,
1611 parentDummy,
1612 aboveThisDummy,
1613 copyNode,
1615 changeOffset,
1616 offset,
1617 applicator);
1618}
1619
1621 const QString &overrideGroupName,
1622 KisNodeSP *newGroup,
1623 KisNodeSP *newLastChild)
1624{
1625 KisNodeSP active = activeNode();
1626 if (!active) return false;
1627
1628 if (!canMoveLayer(active)) return false;
1629
1630 KisImageSP image = m_d->view->image();
1631 QString groupName = !overrideGroupName.isEmpty() ? overrideGroupName : image->nextLayerName(i18nc("A group of layers", "Group"));
1632 KisGroupLayerSP group = new KisGroupLayer(image.data(), groupName, OPACITY_OPAQUE_U8);
1633
1634 KisNodeList nodes1;
1635 nodes1 << group;
1636
1637 KisNodeList nodes2;
1640
1641 if (nodes2.size() == 0) return false;
1642
1643 if (KisLayerUtils::checkIsChildOf(active, nodes2)) {
1644 active = nodes2.first();
1645 }
1646
1647 KisNodeSP parent = active->parent();
1648 KisNodeSP aboveThis = active;
1649
1650 juggler->addNode(nodes1, parent, aboveThis);
1651 juggler->moveNode(nodes2, group, 0);
1652
1653 *newGroup = group;
1654 *newLastChild = nodes2.last();
1655
1656 return true;
1657}
1658
1660{
1661 KUndo2MagicString actionName = kundo2_i18n("Quick Group");
1662 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
1663
1664 KisNodeSP parent;
1665 KisNodeSP above;
1666
1667 createQuickGroupImpl(juggler, "", &parent, &above);
1668}
1669
1671{
1672 KUndo2MagicString actionName = kundo2_i18n("Quick Clipping Group");
1673 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
1674
1675 KisNodeSP parent;
1676 KisNodeSP above;
1677
1678 KisImageSP image = m_d->view->image();
1679 if (createQuickGroupImpl(juggler, image->nextLayerName(i18nc("default name for a clipping group layer", "Clipping Group")), &parent, &above)) {
1680 KisPaintLayerSP maskLayer = new KisPaintLayer(image.data(), i18nc("default name for quick clip group mask layer", "Mask Layer"), OPACITY_OPAQUE_U8, image->colorSpace());
1681 maskLayer->disableAlphaChannel(true);
1682
1683 juggler->addNode(KisNodeList() << maskLayer, parent, above);
1684 }
1685}
1686
1688{
1689 KisNodeSP active = activeNode();
1690 if (!active) return;
1691
1692 if (!canModifyLayer(active)) return;
1693
1694 KisNodeSP parent = active->parent();
1695 KisNodeSP aboveThis = active;
1696
1697 auto checkCanMoveLayers = [this] (KisNodeList nodes, KisNodeSP newParent) -> bool {
1698 auto incompatibleNode =
1699 std::find_if(nodes.begin(), nodes.end(),
1700 [newParent] (KisNodeSP node) {
1701 return !newParent->allowAsChild(node);
1702 });
1703
1704 if (incompatibleNode != nodes.end()) {
1705 const QString message =
1706 newParent->parent() ?
1707 i18n("Cannot move layer \"%1\" into new parent \"%2\"",
1708 (*incompatibleNode)->name(),
1709 newParent->name()) :
1710 i18n("Cannot move layer \"%1\" into the root layer",
1711 (*incompatibleNode)->name());
1712 m_d->view->showFloatingMessage(message, QIcon());
1713 return false;
1714 }
1715 return true;
1716 };
1717
1718 KUndo2MagicString actionName = kundo2_i18n("Quick Ungroup");
1719
1720 if (parent && dynamic_cast<KisGroupLayer*>(active.data())) {
1721 KisNodeList nodes = active->childNodes(QStringList(), KoProperties());
1722
1723 if (checkCanMoveLayers(nodes, parent)) {
1724 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
1725 juggler->moveNode(nodes, parent, active);
1726 juggler->removeNode(KisNodeList() << active);
1727 }
1728 } else if (parent && parent->parent()) {
1729 KisNodeSP grandParent = parent->parent();
1730
1731 KisNodeList allChildNodes = parent->childNodes(QStringList(), KoProperties());
1732 KisNodeList allSelectedNodes = selectedNodes();
1733
1734 const bool removeParent = KritaUtils::compareListsUnordered(allChildNodes, allSelectedNodes);
1735
1736 if (checkCanMoveLayers(allSelectedNodes, parent)) {
1737 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
1738 juggler->moveNode(allSelectedNodes, grandParent, parent);
1739 if (removeParent) {
1740 juggler->removeNode(KisNodeList() << parent);
1741 }
1742 }
1743 }
1744}
1745
1746void KisNodeManager::selectLayersImpl(const KoProperties &props, const KoProperties &invertedProps)
1747{
1748 KisImageSP image = m_d->view->image();
1749 KisNodeList nodes = KisLayerUtils::findNodesWithProps(image->root(), props, true);
1750
1752
1754 nodes = KisLayerUtils::findNodesWithProps(image->root(), invertedProps, true);
1755 }
1756
1757 if (!nodes.isEmpty()) {
1758 slotImageRequestNodeReselection(nodes.last(), nodes);
1759 }
1760}
1761
1763{
1764 KoProperties props;
1765 selectLayersImpl(props, props);
1766}
1767
1769{
1770 KoProperties props;
1771 props.setProperty("visible", true);
1772
1773 KoProperties invertedProps;
1774 invertedProps.setProperty("visible", false);
1775
1776 selectLayersImpl(props, invertedProps);
1777}
1778
1780{
1781 KoProperties props;
1782 props.setProperty("locked", true);
1783
1784 KoProperties invertedProps;
1785 invertedProps.setProperty("locked", false);
1786
1787 selectLayersImpl(props, invertedProps);
1788}
1789
1791{
1792 KoProperties props;
1793 props.setProperty("visible", false);
1794
1795 KoProperties invertedProps;
1796 invertedProps.setProperty("visible", true);
1797
1798 selectLayersImpl(props, invertedProps);
1799}
1800
1802{
1803 KoProperties props;
1804 props.setProperty("locked", false);
1805
1806 KoProperties invertedProps;
1807 invertedProps.setProperty("locked", true);
1808
1809 selectLayersImpl(props, invertedProps);
1810}
1811
1813{
1814 if (!sender()->property("node").isNull()) {
1815 QString name = sender()->property("node").toString();
1816 KisNodeSP node = KisLayerUtils::findNodeByName(m_d->imageView->image()->rootLayer(),name);
1817 if (node) {
1818 slotUiActivatedNode(node);
1819 }
1820 }
1821}
float value(const T *src, size_t ch)
qreal v
QList< QString > QStringList
const quint8 OPACITY_OPAQUE_U8
char nodeType(const KoPathPoint *point)
A KisActionManager class keeps track of KisActions. These actions are always associated with the GUI....
KisAction * createAction(const QString &name)
KisAction * actionByName(const QString &name) const
void setActivationFlags(ActivationFlags flags)
@ ACTIVE_SHAPE_LAYER
Activate if the current node is a vector layer.
Definition kis_action.h:50
KisImageWSP currentImage() const
KisCoordinatesConverter * coordinatesConverter
void setCurrentShapeManagerOwnerShape(KoShape *source) override
sets the group shape that is supposed to be "entered"
void addCommand(KUndo2Command *command) override
KisAbstractCanvasWidget * canvasWidget
KoShapeManager * globalShapeManager() const
void setLayers(KisNodeList nodes, KisImageSP image, bool forceCopy=false)
static KisClipboard * instance()
const QMimeData * layersMimeData() const
virtual QRect bounds() const =0
KoShapeLayer * shapeForNode(KisNodeSP layer) const
virtual KisNodeDummy * dummyForNode(KisNodeSP node) const =0
void setLastActivatedNode(KisNodeSP node)
bool isIsolatingLayer() const
bool startIsolatedMode(KisNodeSP node, bool isolateLayer, bool isolateGroup)
KisGroupLayerSP rootLayer() const
void sigRequestNodeReselection(KisNodeSP activeNode, const KisNodeList &selectedNodes)
const KoColorSpace * colorSpace() const
QString nextLayerName(const QString &baseName="") const
Definition kis_image.cc:716
KisPaintDeviceSP projection() const
void initialRefreshGraph()
void stopIsolatedMode()
bool isIsolatingGroup() const
double xRes() const
double yRes() const
QRect bounds() const override
void setResolution(double xres, double yres)
static QStringList supportedMimeTypes(Direction direction)
A container for a set of QAction objects.
QAction * action(int index) const
KisNodeSP addShapeLayer(KisNodeSP activeNode)
KisNodeSP addGeneratorLayer(KisNodeSP activeNode)
void setView(QPointer< KisView >view)
KisNodeSP addFileLayer(KisNodeSP activeNode)
void activateLayer(KisLayerSP layer)
KisPaintDeviceSP activeDevice()
KisLayerSP activeLayer()
void setup(KisActionManager *actionManager)
void convertLayerToFileLayer(KisNodeSP source)
KisLayerSP addPaintLayer(KisNodeSP activeNode)
KisNodeSP addGroupLayer(KisNodeSP activeNode)
KisNodeSP addCloneLayer(KisNodeList nodes)
void convertNodeToPaintLayer(KisNodeSP source)
KisNodeSP addAdjustmentLayer(KisNodeSP activeNode)
static void setNodePropertyAutoUndo(KisNodeSP node, const KoID &id, const QVariant &value, KisImageSP image)
static KisBaseNode::Property getProperty(const KoID &id, bool state)
KisNodeSP createTransformMask(KisNodeSP activeNode)
void setup(KisKActionCollection *actionCollection, KisActionManager *actionManager)
void activateMask(KisMaskSP mask)
KisNodeSP createSelectionMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool convertActiveNode)
KisPaintDeviceSP activeDevice()
KisNodeSP createColorizeMask(KisNodeSP activeNode)
KisMaskSP activeMask()
void setView(QPointer< KisView >view)
KisNodeSP createFastColorOverlayMask(KisNodeSP activeNode)
KisNodeSP createTransparencyMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool convertActiveNode)
KisNodeSP createFilterMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool quiet, bool convertActiveNode)
QString createMaskNameCommon(KisNodeSP targetNode, const QString &nodeType, const QString &desiredName)
static bool insertMimeLayers(const QMimeData *data, KisImageSP image, KisShapeController *shapeController, KisNodeDummy *parentDummy, KisNodeDummy *aboveThisDummy, bool copyNode, KisNodeInsertionAdapter *nodeInsertionAdapter, bool changeOffset=false, QPointF offset=QPointF(), KisProcessingApplicator *applicator=nullptr)
static QString mimeTypeForFile(const QString &file, bool checkExistingFiles=true)
Find the mimetype for the given filename. The filename must include a suffix.
void moveNode(KisNodeSP node, KisNodeSP parent, KisNodeSP aboveThis)
void setNodeName(KisNodeSP node, const QString &name)
void setOpacity(KisNodeSP node, qint32 opacity)
void setCompositeOp(KisNodeSP node, const KoCompositeOp *compositeOp)
void beginMacro(const KUndo2MagicString &macroName)
KisNodeDummy * lastChild() const
void removeNode(const KisNodeList &nodes)
void raiseNode(const KisNodeList &nodes)
void addNode(const KisNodeList &nodes, KisNodeSP dstParent, KisNodeSP dstAbove)
void duplicateNode(const KisNodeList &nodes)
void copyNode(const KisNodeList &nodes, KisNodeSP dstParent, KisNodeSP dstAbove)
void lowerNode(const KisNodeList &nodes)
void moveNode(KisNodeSP node, KisNodeSP parent, KisNodeSP above)
void activateNextNode(bool siblingsOnly=false)
static bool isNodeHidden(KisNodeSP node, bool isGlobalSelectionHidden)
void selectLayersImpl(const KoProperties &props, const KoProperties &invertedProps)
KisLayerSP activeLayer()
void setIsolateActiveGroupMode(bool checked)
KisNodeDisplayModeAdapter * nodeDisplayModeAdapter() const
void createReferenceImageFromVisible()
KisNodeInsertionAdapter * nodeInsertionAdapter() const
void setView(QPointer< KisView >imageView)
bool canMoveLayer(KisNodeSP node, bool showWarning=true)
void copyNodesDirect(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis)
void colorOverlayMaskProperties(KisNodeSP node)
void nodeOpacityChanged(qreal opacity)
void sigUiNeedChangeActiveNode(KisNodeSP node)
void slotPinToTimeline(bool value)
void setNodeOpacity(KisNodeSP node, qint32 opacity)
void slotUiActivateNode()
slotUiActivateNode inspects the sender to see which node needs to be activated.
void changeCloneSource()
pop up a window for changing the source of the selected Clone Layers
void moveNodesDirect(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis)
void switchToPreviouslyActiveNode()
void setIsolateActiveLayerMode(bool checked)
void setup(KisKActionCollection *collection, KisActionManager *actionManager)
void handleExternalIsolationChange()
KisNodeSP activeNode()
Convenience function to get the active layer or mask.
KisNodeList selectedNodes()
void nodePropertiesIgnoreSelection(KisNodeSP node)
void slotImageRequestNodeReselection(KisNodeSP activeNode, const KisNodeList &selectedNodes)
void mirrorNodes(KisNodeList nodes, const KUndo2MagicString &commandName, Qt::Orientation orientation, KisSelectionSP selection)
void sigUiNeedChangeSelectedNodes(const QList< KisNodeSP > &nodes)
void activatePreviousNode(bool siblingsOnly=false)
Private *const m_d
const KoColorSpace * activeColorSpace()
void setNodeCompositeOp(KisNodeSP node, const KoCompositeOp *compositeOp)
void changeIsolationRoot(KisNodeSP isolationRoot)
void addNodesDirect(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis)
KisPaintDeviceSP activePaintDevice()
Get the paint device the user wants to paint on now.
void setNodeName(KisNodeSP node, const QString &name)
qint32 convertOpacityToInt(qreal opacity)
void mirrorNode(KisNodeSP node, const KUndo2MagicString &commandName, Qt::Orientation orientation, KisSelectionSP selection)
bool createQuickGroupImpl(KisNodeJugglerCompressed *juggler, const QString &overrideGroupName, KisNodeSP *newGroup, KisNodeSP *newLastChild)
void slotSetSelectedNodes(const KisNodeList &nodes)
slotSetSelectedNodes set the list of nodes selected in the layerbox. Selected nodes are not necessari...
void pasteLayersFromClipboard(bool changeOffset=false, QPointF offset=QPointF(), KisProcessingApplicator *applicator=nullptr)
bool canModifyLayers(KisNodeList nodes, bool showWarning=true)
KisNodeSelectionAdapter * nodeSelectionAdapter() const
bool trySetNodeProperties(KisNodeSP node, KisImageSP image, KisBaseNode::PropertyList properties) const
KisNodeSP createNode(const QString &nodeType, bool quiet=false, KisPaintDeviceSP copyFrom=0)
bool canModifyLayer(KisNodeSP node, bool showWarning=true)
void slotNonUiActivatedNode(KisNodeSP node)
void nodeProperties(KisNodeSP node)
void sigNodeActivated(KisNodeSP node)
emitted whenever a node is selected.
void createReferenceImage(bool fromLayer)
KisNodeManager(KisViewManager *view)
void moveNodeAt(KisNodeSP node, KisNodeSP parent, int index)
~KisNodeManager() override
void removeSingleNode(KisNodeSP node)
void createReferenceImageFromLayer()
void reinitializeIsolationActionGroup()
void slotSomethingActivatedNodeImpl(KisNodeSP node)
KisLayerSP createPaintLayer()
bool canMoveLayers(KisNodeList nodes, bool showWarning=true)
void nodeCompositeOpChanged(const KoCompositeOp *op)
void removeSelectedNodes(KisNodeList selectedNodes)
void changeIsolationMode(bool isolateActiveLayer, bool isolateActiveGroup)
void convertNode(const QString &nodeType)
void slotUiActivatedNode(KisNodeSP node)
static void setNodePropertiesAutoUndo(KisNodeSP node, KisImageSP image, PropertyList proplist)
virtual const KoColorSpace * compositionSourceColorSpace() const
QRect exactBounds() const
const KoColorSpace * colorSpace() const
KoColor defaultPixel() const
QImage convertToQImage(const KoColorProfile *dstProfile, qint32 x, qint32 y, qint32 w, qint32 h, KoColorConversionTransformation::Intent renderingIntent=KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::ConversionFlags conversionFlags=KoColorConversionTransformation::internalConversionFlags()) const
KisDefaultBoundsBaseSP defaultBounds() const
void makeCloneFrom(KisPaintDeviceSP src, const QRect &rect)
static KisPart * instance()
Definition KisPart.cpp:131
void applyVisitor(KisProcessingVisitorSP visitor, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
void applyVisitorAllFrames(KisProcessingVisitorSP visitor, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
static KisReferenceImage * fromQImage(const KisCoordinatesConverter &converter, const QImage &img)
static KUndo2Command * addReferenceImages(KisDocument *document, QList< KoShape * > referenceImages)
ALWAYS_INLINE quint8 * rawData()
bool isNull() const
The KisSignalMapper class bundles signals from identifiable senders.
bool blockUntilOperationsFinished(KisImageSP image)
blockUntilOperationsFinished blocks the GUI of the application until execution of actions on image is...
KisDocument * document() const
KisActionManager * actionManager() const
KisCanvas2 * canvasBase() const
Return the canvas base class.
KisSelectionSP selection()
KisLayerSP activeLayer()
Convenience method to get at the active layer.
void blockUntilOperationsFinishedForced(KisImageSP image)
blockUntilOperationsFinished blocks the GUI of the application until execution of actions on image is...
virtual KisKActionCollection * actionCollection() const
KisSelectionManager * selectionManager()
QWidget * mainWindowAsQWidget() const
KisImageWSP image() const
Return the image this view is displaying.
void showFloatingMessage(const QString &message, const QIcon &icon, int timeout=4500, KisFloatingMessage::Priority priority=KisFloatingMessage::Medium, int alignment=Qt::AlignCenter|Qt::TextWordWrap)
shows a floating message in the top right corner of the canvas
virtual quint32 pixelSize() const =0
virtual void setOpacity(quint8 *pixels, quint8 alpha, qint32 nPixels) const =0
quint8 opacityU8() const
Definition KoColor.cpp:341
void setProperty(const QString &name, const QVariant &value)
void deselectAll()
clear the selections list
void setActiveLayer(KoShapeLayer *layer)
void select(KoShape *shape)
QList< KoShape * > shapes() const
KoSelection * selection
static bool compareShapeZIndex(KoShape *s1, KoShape *s2)
Definition KoShape.cpp:388
void switchToolRequested(const QString &id)
QString activeToolId() const
Returns the toolId of the currently active tool.
static KoToolManager * instance()
Return the toolmanager singleton.
Implements exporting shapes to SVG.
Definition SvgWriter.h:33
bool save(QIODevice &outputDevice, const QSizeF &pageSize)
Writes svg to specified output device.
Definition SvgWriter.cpp:82
#define KIS_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:85
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define bounds(x, a, b)
#define warnKrita
Definition kis_debug.h:87
KisDocument * createDocument(QList< KisNodeSP > nodes, KisImageSP srcImage, const QRect &copiedBounds)
#define CONVERT_NODE_ACTION_2(id, layerType, exclude)
#define CONVERT_NODE_ACTION(id, layerType)
#define NEW_LAYER_ACTION(id, layerType)
QList< KisNodeSP > KisNodeList
Definition kis_types.h:264
KUndo2MagicString kundo2_i18n(const char *text)
KUndo2MagicString kundo2_noi18n(const QString &text)
KUndo2MagicString kundo2_i18np(const char *sing, const char *plur, const A1 &a1)
void splitAlphaToMask(KisImageSP image, KisNodeSP node, const QString &maskName)
void sortMergeableNodes(KisNodeSP root, KisNodeList &inputNodes, KisNodeList &outputNodes)
KisNodeSP findNodeByName(KisNodeSP root, const QString &name)
KisNodeList findNodesWithProps(KisNodeSP root, const KoProperties &props, bool excludeRoot)
void newLayerFromVisible(KisImageSP image, KisNodeSP putAfter, MergeFlags flags)
void filterMergeableNodes(KisNodeList &nodes, bool allowMasks)
bool checkIsChildOf(KisNodeSP node, const KisNodeList &parents)
bool compareListsUnordered(const QList< T > &a, const QList< T > &b)
void setPinnedToTimeline(bool pinned)
virtual KisPaintDeviceSP projection() const =0
bool isEditable(bool checkVisibility=true) const
bool isPinnedToTimeline() const
virtual QRect exactBounds() const
virtual KisPaintDeviceSP original() const =0
QString compositeOp
KisImageWSP image
bool userLocked() const
virtual KisPaintDeviceSP paintDevice() const =0
QString name() const
virtual bool isFakeNode() const
quint8 opacity() const
virtual bool visible(bool recursive=false) const
bool hasEditablePaintDevice() const
KisPaintDeviceSP projection() const override
Definition kis_layer.cc:826
void disableAlphaChannel(bool disable)
Definition kis_layer.cc:319
virtual KisSelectionMaskSP selectionMask() const
Definition kis_layer.cc:504
const KoColorSpace * colorSpace() const override
returns the image's colorSpace or null, if there is no image
Definition kis_layer.cc:225
KisFilterMaskSP colorOverlayMask() const
Definition kis_layer.cc:573
KisLayerSP parentLayer() const
KisSelectionSP selection
Definition kis_mask.cc:44
bool addNode(KisNodeSP node, KisNodeSP parent=KisNodeSP(), KisNodeAdditionFlags flags=KisNodeAdditionFlag::None)
void mergeTransparencyMaskAsAlpha(bool writeToLayers)
QScopedPointer< KisNodeSelectionAdapter > nodeSelectionAdapter
bool activateNodeImpl(KisNodeSP node)
void saveDeviceAsImage(KisPaintDeviceSP device, const QString &defaultName, const QRect &bounds, qreal xRes, qreal yRes, quint8 opacity)
KisNodeCommandsAdapter commandsAdapter
KisSynchronizedConnection< KisNodeSP, KisNodeList > activateNodeConnection
QScopedPointer< KisNodeInsertionAdapter > nodeInsertionAdapter
KisSignalMapper nodeCreationSignalMapper
QPointer< KisNodeJugglerCompressed > nodeJuggler
KisNodeJugglerCompressed * lazyGetJuggler(const KUndo2MagicString &actionName)
Private(KisNodeManager *_q, KisViewManager *v)
QPointer< KisView > imageView
KisSignalMapper nodeConversionSignalMapper
QScopedPointer< KisNodeDisplayModeAdapter > nodeDisplayModeAdapter
KisNodeSP prevSibling() const
Definition kis_node.cpp:402
KisNodeSP firstChild() const
Definition kis_node.cpp:361
QList< KisNodeSP > childNodes(const QStringList &nodeTypes, const KoProperties &properties) const
Definition kis_node.cpp:439
quint32 childCount() const
Definition kis_node.cpp:414
KisNodeWSP parent
Definition kis_node.cpp:86
KisNodeSP lastChild() const
Definition kis_node.cpp:367
KisNodeSP nextSibling() const
Definition kis_node.cpp:408
KisNodeGraphListener * graphListener
Definition kis_node.cpp:87
KisPaintDeviceSP paintDevice
void setActive(bool active)
bool hasShapeSelection() const