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 if (!node) {
153 selection->setActiveLayer(0);
154 imageView->setCurrentNode(0);
158 } else {
160
161 KoShape * shape = view->document()->shapeForNode(node);
162
164
165 selection->select(shape);
166 KoShapeLayer * shapeLayer = dynamic_cast<KoShapeLayer*>(shape);
167
168 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(shapeLayer, false);
169
170 // shapeLayer->setGeometryProtected(node->userLocked());
171 // shapeLayer->setVisible(node->visible());
172 selection->setActiveLayer(shapeLayer);
173
174 imageView->setCurrentNode(node);
175 if (KisLayerSP layer = qobject_cast<KisLayer*>(node.data())) {
178 } else if (KisMaskSP mask = dynamic_cast<KisMask*>(node.data())) {
180 // XXX_NODE: for now, masks cannot be nested.
181 layerManager.activateLayer(static_cast<KisLayer*>(node->parent().data()));
182 }
183 }
184 return true;
185}
186
187//=====================================================================================
188
194
196{
197 delete m_d;
198}
199
201{
202 m_d->maskManager.setView(imageView);
203 m_d->layerManager.setView(imageView);
204
205 if (m_d->imageView) {
206 KisShapeController *shapeController = dynamic_cast<KisShapeController*>(m_d->imageView->document()->shapeController());
207 Q_ASSERT(shapeController);
208 shapeController->disconnect(SIGNAL(sigActivateNode(KisNodeSP)), this);
209 m_d->imageView->image()->disconnect(this);
210 m_d->imageView->image()->disconnect(&m_d->activateNodeConnection);
211 }
212
213 m_d->imageView = imageView;
214
215 if (m_d->imageView) {
216 KisShapeController *shapeController = dynamic_cast<KisShapeController*>(m_d->imageView->document()->shapeController());
217 Q_ASSERT(shapeController);
218 connect(shapeController, SIGNAL(sigActivateNode(KisNodeSP)), SLOT(slotNonUiActivatedNode(KisNodeSP)));
219
220 if (!m_d->imageView->currentNode()) {
225 if (shapeController->lastActivatedNode() && !m_d->imageView->currentNode()) {
226 slotNonUiActivatedNode(shapeController->lastActivatedNode());
227 } else {
228 // if last activated node is null, most probably, it means that the shape controller
229 // is going to Q_EMIT the activation signal very soon
230 }
231 } else {
238 if (!m_d->imageView->currentNode()->graphListener()) {
239 slotNonUiActivatedNode(m_d->imageView->image()->root()->lastChild());
240 }
241 }
242
244 m_d->imageView->resourceProvider()->slotNodeActivated(m_d->imageView->currentNode());
245 connect(m_d->imageView->image(), SIGNAL(sigIsolatedModeChanged()), this, SLOT(handleExternalIsolationChange()));
246 }
247
248}
249
250#define NEW_LAYER_ACTION(id, layerType) \
251{ \
252 action = actionManager->createAction(id); \
253 m_d->nodeCreationSignalMapper.setMapping(action, layerType); \
254 connect(action, SIGNAL(triggered()), \
255 &m_d->nodeCreationSignalMapper, SLOT(map())); \
256 }
257
258#define CONVERT_NODE_ACTION_2(id, layerType, exclude) \
259{ \
260 action = actionManager->createAction(id); \
261 action->setExcludedNodeTypes(QStringList(exclude)); \
262 actionManager->addAction(id, action); \
263 m_d->nodeConversionSignalMapper.setMapping(action, layerType); \
264 connect(action, SIGNAL(triggered()), \
265 &m_d->nodeConversionSignalMapper, SLOT(map())); \
266 }
267
268#define CONVERT_NODE_ACTION(id, layerType) \
269 CONVERT_NODE_ACTION_2(id, layerType, layerType)
270
271void KisNodeManager::setup(KisKActionCollection * actionCollection, KisActionManager* actionManager)
272{
273 m_d->layerManager.setup(actionManager);
274 m_d->maskManager.setup(actionCollection, actionManager);
275
276 KisAction * action = 0;
277
278 action = actionManager->createAction("mirrorNodeX");
279 connect(action, SIGNAL(triggered()), this, SLOT(mirrorNodeX()));
280
281 action = actionManager->createAction("mirrorNodeY");
282 connect(action, SIGNAL(triggered()), this, SLOT(mirrorNodeY()));
283
284 action = actionManager->createAction("mirrorAllNodesX");
285 connect(action, SIGNAL(triggered()), this, SLOT(mirrorAllNodesX()));
286
287 action = actionManager->createAction("mirrorAllNodesY");
288 connect(action, SIGNAL(triggered()), this, SLOT(mirrorAllNodesY()));
289
290 action = actionManager->createAction("activateNextLayer");
291 connect(action, SIGNAL(triggered()), this, SLOT(activateNextNode()));
292
293 action = actionManager->createAction("activateNextSiblingLayer");
294 connect(action, SIGNAL(triggered()), this, SLOT(activateNextSiblingNode()));
295
296 action = actionManager->createAction("activatePreviousLayer");
297 connect(action, SIGNAL(triggered()), this, SLOT(activatePreviousNode()));
298
299 action = actionManager->createAction("activatePreviousSiblingLayer");
300 connect(action, SIGNAL(triggered()), this, SLOT(activatePreviousSiblingNode()));
301
302 action = actionManager->createAction("switchToPreviouslyActiveNode");
303 connect(action, SIGNAL(triggered()), this, SLOT(switchToPreviouslyActiveNode()));
304
305 action = actionManager->createAction("save_node_as_image");
306 connect(action, SIGNAL(triggered()), this, SLOT(saveNodeAsImage()));
307
308 action = actionManager->createAction("save_vector_node_to_svg");
309 connect(action, SIGNAL(triggered()), this, SLOT(saveVectorLayerAsImage()));
311
312 action = actionManager->createAction("duplicatelayer");
313 connect(action, SIGNAL(triggered()), this, SLOT(duplicateActiveNode()));
314
315 action = actionManager->createAction("copy_layer_clipboard");
316 connect(action, SIGNAL(triggered()), this, SLOT(copyLayersToClipboard()));
317
318 action = actionManager->createAction("cut_layer_clipboard");
319 connect(action, SIGNAL(triggered()), this, SLOT(cutLayersToClipboard()));
320
321 action = actionManager->createAction("paste_layer_from_clipboard");
322 connect(action, SIGNAL(triggered()), this, SLOT(pasteLayersFromClipboard()));
323
324 action = actionManager->createAction("create_quick_group");
325 connect(action, SIGNAL(triggered()), this, SLOT(createQuickGroup()));
326
327 action = actionManager->createAction("create_quick_clipping_group");
328 connect(action, SIGNAL(triggered()), this, SLOT(createQuickClippingGroup()));
329
330 action = actionManager->createAction("quick_ungroup");
331 connect(action, SIGNAL(triggered()), this, SLOT(quickUngroup()));
332
333 action = actionManager->createAction("select_all_layers");
334 connect(action, SIGNAL(triggered()), this, SLOT(selectAllNodes()));
335
336 action = actionManager->createAction("select_visible_layers");
337 connect(action, SIGNAL(triggered()), this, SLOT(selectVisibleNodes()));
338
339 action = actionManager->createAction("select_locked_layers");
340 connect(action, SIGNAL(triggered()), this, SLOT(selectLockedNodes()));
341
342 action = actionManager->createAction("select_invisible_layers");
343 connect(action, SIGNAL(triggered()), this, SLOT(selectInvisibleNodes()));
344
345 action = actionManager->createAction("select_unlocked_layers");
346 connect(action, SIGNAL(triggered()), this, SLOT(selectUnlockedNodes()));
347
348 action = actionManager->createAction("new_from_visible");
349 connect(action, SIGNAL(triggered()), this, SLOT(createFromVisible()));
350
351 action = actionManager->createAction("create_reference_image_from_active_layer");
352 connect(action, SIGNAL(triggered()), this, SLOT(createReferenceImageFromLayer()));
353
354 action = actionManager->createAction("create_reference_image_from_visible_canvas");
355 connect(action, SIGNAL(triggered()), this, SLOT(createReferenceImageFromVisible()));
356
357 action = actionManager->createAction("pin_to_timeline");
358 action->setCheckable(true);
359 connect(action, SIGNAL(toggled(bool)), this, SLOT(slotPinToTimeline(bool)));
360 m_d->pinToTimeline = action;
361
362 NEW_LAYER_ACTION("add_new_paint_layer", "KisPaintLayer");
363
364 NEW_LAYER_ACTION("add_new_group_layer", "KisGroupLayer");
365
366 NEW_LAYER_ACTION("add_new_clone_layer", "KisCloneLayer");
367
368 NEW_LAYER_ACTION("add_new_shape_layer", "KisShapeLayer");
369
370 NEW_LAYER_ACTION("add_new_adjustment_layer", "KisAdjustmentLayer");
371
372 NEW_LAYER_ACTION("add_new_fill_layer", "KisGeneratorLayer");
373
374 NEW_LAYER_ACTION("add_new_file_layer", "KisFileLayer");
375
376 NEW_LAYER_ACTION("add_new_transparency_mask", "KisTransparencyMask");
377
378 NEW_LAYER_ACTION("add_new_filter_mask", "KisFilterMask");
379
380 // NOTE: FastColorOverlayFilterMask is just an identifier, not an actual class name
381 NEW_LAYER_ACTION("add_new_fast_color_overlay_mask", "FastColorOverlayFilterMask");
382
383 NEW_LAYER_ACTION("add_new_colorize_mask", "KisColorizeMask");
384
385 NEW_LAYER_ACTION("add_new_transform_mask", "KisTransformMask");
386
387 NEW_LAYER_ACTION("add_new_selection_mask", "KisSelectionMask");
388
389 connect(&m_d->nodeCreationSignalMapper, SIGNAL(mapped(QString)),
390 this, SLOT(createNode(QString)));
391
392 CONVERT_NODE_ACTION("convert_to_paint_layer", "KisPaintLayer");
393
394 CONVERT_NODE_ACTION_2("convert_to_selection_mask", "KisSelectionMask", QStringList() << "KisSelectionMask" << "KisColorizeMask");
395
396 CONVERT_NODE_ACTION_2("convert_to_filter_mask", "KisFilterMask", QStringList() << "KisFilterMask" << "KisColorizeMask");
397
398 CONVERT_NODE_ACTION_2("convert_to_transparency_mask", "KisTransparencyMask", QStringList() << "KisTransparencyMask" << "KisColorizeMask");
399
400 CONVERT_NODE_ACTION("convert_to_animated", "animated");
401
402 CONVERT_NODE_ACTION_2("convert_to_file_layer", "KisFileLayer", QStringList() << "KisFileLayer" << "KisCloneLayer");
403
404 connect(&m_d->nodeConversionSignalMapper, SIGNAL(mapped(QString)),
405 this, SLOT(convertNode(QString)));
406
407 // Isolation Modes...
408 // Post Qt5.14 this can be replaced with QActionGroup + ExclusionPolicy::ExclusiveOptional.
409 action = actionManager->createAction("isolate_active_layer");
410 connect(action, SIGNAL(toggled(bool)), this, SLOT(setIsolateActiveLayerMode(bool)));
411 action = actionManager->createAction("isolate_active_group");
412 connect(action, SIGNAL(triggered(bool)), this, SLOT(setIsolateActiveGroupMode(bool)));
414
415 action = actionManager->createAction("toggle_layer_visibility");
416 connect(action, SIGNAL(triggered()), this, SLOT(toggleVisibility()));
417
418 action = actionManager->createAction("toggle_layer_lock");
419 connect(action, SIGNAL(triggered()), this, SLOT(toggleLock()));
420
421 action = actionManager->createAction("toggle_layer_inherit_alpha");
422 connect(action, SIGNAL(triggered()), this, SLOT(toggleInheritAlpha()));
423
424 action = actionManager->createAction("toggle_layer_alpha_lock");
425 connect(action, SIGNAL(triggered()), this, SLOT(toggleAlphaLock()));
426
427 action = actionManager->createAction("split_alpha_into_mask");
428 connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaIntoMask()));
429
430 action = actionManager->createAction("split_alpha_write");
431 connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaWrite()));
432
433 // HINT: we can save even when the nodes are not editable
434 action = actionManager->createAction("split_alpha_save_merged");
435 connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaSaveMerged()));
436}
437
439{
440 // enable/disable all relevant actions
443}
444
446{
447 if (m_d->imageView) {
448 return m_d->imageView->currentNode();
449 }
450 return 0;
451}
452
457
459{
462 } else {
463 Q_ASSERT(m_d->layerManager.activeLayer());
466 else
467 return m_d->view->image()->colorSpace();
468 }
469}
470
471bool KisNodeManager::canModifyLayers(KisNodeList nodes, bool showWarning)
472{
473 KisNodeSP lockedNode;
474 Q_FOREACH (KisNodeSP node, nodes) {
475 if (!node->isEditable(false)) {
476 lockedNode = node;
477 break;
478 }
479 }
480
481 if (lockedNode && showWarning) {
482 QString errorMessage;
483
484 if (nodes.size() <= 1) {
485 errorMessage = i18n("Layer is locked");
486 } else {
487 errorMessage = i18n("Layer \"%1\" is locked", lockedNode->name());
488 }
489
490 m_d->view->showFloatingMessage(errorMessage, QIcon());
491 }
492
493 return !lockedNode;
494}
495
496bool KisNodeManager::canModifyLayer(KisNodeSP node, bool showWarning)
497{
498 return canModifyLayers({node}, showWarning);
499}
500
501bool KisNodeManager::canMoveLayers(KisNodeList nodes, bool showWarning)
502{
503 KisNodeSP lockedNode;
504 Q_FOREACH (KisNodeSP node, nodes) {
505 if (node->parent() && !node->parent()->isEditable(false)) {
506 lockedNode = node->parent();
507 break;
508 }
509 }
510
511 if (lockedNode && showWarning) {
512 QString errorMessage = i18n("Layer \"%1\" is locked", lockedNode->name());
513 m_d->view->showFloatingMessage(errorMessage, QIcon());
514 }
515
516 return !lockedNode;
517}
518
519bool KisNodeManager::canMoveLayer(KisNodeSP node, bool showWarning)
520{
521 return canMoveLayers({node}, showWarning);
522}
523
524void KisNodeManager::moveNodeAt(KisNodeSP node, KisNodeSP parent, int index)
525{
526 if (parent->allowAsChild(node)) {
527 if (node->inherits("KisSelectionMask") && parent->inherits("KisLayer")) {
528 KisSelectionMask *m = dynamic_cast<KisSelectionMask*>(node.data());
529 KisLayer *l = qobject_cast<KisLayer*>(parent.data());
530 if (m && m->active() && l && l->selectionMask()) {
531 l->selectionMask()->setActive(false);
532 }
533 }
534 m_d->commandsAdapter.moveNode(node, parent, index);
535 }
536}
537
539{
540 KUndo2MagicString actionName = kundo2_i18n("Move Nodes");
541 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
542 juggler->moveNode(nodes, parent, aboveThis);
543}
544
546{
547 KUndo2MagicString actionName = kundo2_i18n("Copy Nodes");
548 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
549 juggler->copyNode(nodes, parent, aboveThis);
550}
551
553{
554 KUndo2MagicString actionName = kundo2_i18n("Add Nodes");
555 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
556 juggler->addNode(nodes, parent, aboveThis);
557}
558
560{
561 QAction* action = m_d->view->actionManager()->actionByName("isolate_active_layer");
562 action->toggle();
563}
564
566{
567 KisImageWSP image = m_d->view->image();
569
570 const bool groupIsolationState = image->isIsolatingGroup();
571 changeIsolationMode(checked, groupIsolationState);
572}
573
575{
576 KisImageWSP image = m_d->view->image();
578
579 const bool layerIsolationState = image->isIsolatingLayer();
580 changeIsolationMode(layerIsolationState, checked);
581}
582
583void KisNodeManager::changeIsolationMode(bool isolateActiveLayer, bool isolateActiveGroup)
584{
585 KisImageWSP image = m_d->view->image();
588
589 if (isolateActiveLayer || isolateActiveGroup) {
590 if (image->startIsolatedMode(activeNode, isolateActiveLayer, isolateActiveGroup) == false) {
592 }
593 } else {
594 image->stopIsolatedMode();
595 }
596}
597
599{
600 KisImageWSP image = m_d->view->image();
601 if (!image || !isolationRoot) return;
602
603 const bool isIsolatingLayer = image->isIsolatingLayer();
604 const bool isIsolatingGroup = image->isIsolatingGroup();
605
606 // Restart isolation with a new root node and the same settings.
607 if (image->startIsolatedMode(isolationRoot, isIsolatingLayer, isIsolatingGroup) == false) {
609 }
610}
611
613{
614 // It might be that we have multiple Krita windows open. In such a case
615 // only the currently active one should restart isolated mode
616 if (!m_d->view->mainWindowAsQWidget()->isActiveWindow()) return;
617
618 KisImageWSP image = m_d->view->image();
620
621 const bool isIsolatingLayer = image->isIsolatingLayer();
622 const bool isIsolatingGroup = image->isIsolatingGroup();
623
624 m_d->view->actionManager()->actionByName("isolate_active_layer")->setChecked(isIsolatingLayer);
625 m_d->view->actionManager()->actionByName("isolate_active_group")->setChecked(isIsolatingGroup);
626}
627
629{
630 m_d->view->actionManager()->actionByName("isolate_active_layer")->setChecked(false);
631 m_d->view->actionManager()->actionByName("isolate_active_group")->setChecked(false);
632}
633
634KisNodeSP KisNodeManager::createNode(const QString & nodeType, bool quiet, KisPaintDeviceSP copyFrom)
635{
637 return 0;
638 }
639
641 if (!activeNode) {
642 activeNode = m_d->view->image()->root();
643 }
644
646
650
651 // XXX: make factories for this kind of stuff,
652 // with a registry
653
654 if (nodeType == "KisPaintLayer") {
656 } else if (nodeType == "KisGroupLayer") {
658 } else if (nodeType == "KisAdjustmentLayer") {
660 } else if (nodeType == "KisGeneratorLayer") {
662 } else if (nodeType == "KisShapeLayer") {
664 } else if (nodeType == "KisCloneLayer") {
665 KisNodeList nodes = selectedNodes();
666 if (nodes.isEmpty()) {
667 nodes.append(activeNode);
668 }
669 return m_d->layerManager.addCloneLayer(nodes);
670 } else if (nodeType == "KisTransparencyMask") {
671 return m_d->maskManager.createTransparencyMask(activeNode, copyFrom, false);
672 } else if (nodeType == "KisFilterMask") {
673 return m_d->maskManager.createFilterMask(activeNode, copyFrom, quiet, false);
674 } else if (nodeType == "FastColorOverlayFilterMask") {
676 } else if (nodeType == "KisColorizeMask") {
678 } else if (nodeType == "KisTransformMask") {
680 } else if (nodeType == "KisSelectionMask") {
681 return m_d->maskManager.createSelectionMask(activeNode, copyFrom, false);
682 } else if (nodeType == "KisFileLayer") {
684 }
685 return 0;
686}
687
692
694{
695 Q_FOREACH (KisNodeSP node, selectedNodes()) {
697 }
698}
699
701{
702 KisNodeSP node = createNode("KisPaintLayer");
703 return dynamic_cast<KisLayer*>(node.data());
704}
705
707{
709 return;
710 }
711
713 if (!activeNode) return;
714
715 if (!canModifyLayer(activeNode)) return;
716
717 if (nodeType == "KisPaintLayer") {
719 } else if (nodeType == "KisSelectionMask" ||
720 nodeType == "KisFilterMask" ||
721 nodeType == "KisTransparencyMask") {
722
725
726 m_d->commandsAdapter.beginMacro(kundo2_i18n("Convert to a Selection Mask"));
727
728 bool result = false;
729
730 if (nodeType == "KisSelectionMask") {
731 result = !m_d->maskManager.createSelectionMask(activeNode, copyFrom, true).isNull();
732 } else if (nodeType == "KisFilterMask") {
733 result = !m_d->maskManager.createFilterMask(activeNode, copyFrom, false, true).isNull();
734 } else if (nodeType == "KisTransparencyMask") {
735 result = !m_d->maskManager.createTransparencyMask(activeNode, copyFrom, true).isNull();
736 }
737
739
740 if (!result) {
743 }
744 } else if (nodeType == "KisFileLayer") {
746 } else {
747 warnKrita << "Unsupported node conversion type:" << nodeType;
748 }
749}
750
752 KisViewManager* m_view = m_d->view;
753 KisDocument *document = m_view->document();
754 KisCanvas2 *canvas = m_view->canvasBase();
755
756 const KisPaintDeviceSP paintDevice = fromLayer ? m_view->activeLayer()->projection()
757 : canvas->currentImage()->projection();
760 QScopedPointer<KisReferenceImage> reference(KisReferenceImage::fromQImage(*canvas->coordinatesConverter(), image));
762 if (reference) {
763 if (document->referenceImagesLayer()) {
764 reference->setZIndex(document->referenceImagesLayer()->shapes().size());
765 }
766 canvas->addCommand(KisReferenceImagesLayer::addReferenceImages(document, {reference.take()}));
767
768 KoToolManager::instance()->switchToolRequested("ToolReferenceImages");
769
770 } else {
771 if (canvas->canvasWidget()) {
772 QString strMessage = fromLayer ? i18nc("error dialog from the reference tool", "Could not create a reference image from the active layer.")
773 : i18nc("error dialog from the reference tool", "Could not create a reference image from the visible canvas.");
774
775 m_d->view->showFloatingMessage(strMessage, QIcon(), 5000, KisFloatingMessage::High, Qt::TextSingleLine);
776 }
777 }
778}
779
783
787
789{
790 KisDummiesFacadeBase *dummiesFacade = dynamic_cast<KisDummiesFacadeBase*>(m_d->imageView->document()->shapeController());
791 KIS_SAFE_ASSERT_RECOVER_RETURN(dummiesFacade);
792
793 const bool nodeVisible = !isNodeHidden(node, !m_d->nodeDisplayModeAdapter->showGlobalSelectionMask());
794 if (!nodeVisible) {
795 return;
796 }
797
799 if (m_d->activateNodeImpl(node)) {
800 Q_EMIT sigUiNeedChangeActiveNode(node);
801 Q_EMIT sigNodeActivated(node);
802 nodesUpdated();
803 if (node) {
804 bool toggled = m_d->view->actionCollection()->action("view_show_canvas_only")->isChecked();
805 if (toggled) {
806 m_d->view->showFloatingMessage( activeLayer()->name(), QIcon(), 1600, KisFloatingMessage::Medium, Qt::TextSingleLine);
807 }
808 }
809 }
810}
811
813{
814 // the node must still be in the graph, some asynchronous
815 // signals may easily break this requirement
816 if (node && !node->graphListener()) {
817 node = 0;
818 }
819
820 if (node == activeNode()) return;
821
823
824 if (node) {
825 bool toggled = m_d->view->actionCollection()->action("view_show_canvas_only")->isChecked();
826 if (toggled) {
827 m_d->view->showFloatingMessage( activeLayer()->name(), QIcon(), 1600, KisFloatingMessage::Medium, Qt::TextSingleLine);
828 }
829 }
830}
831
833{
834 // the node must still be in the graph, some asynchronous
835 // signals may easily break this requirement
836 if (node && !node->graphListener()) {
837 node = 0;
838 }
839
840 if (node) {
841 QStringList vectorTools = QStringList()
842 << "InteractionTool"
843 << "KarbonGradientTool"
844 << "KarbonCalligraphyTool"
845 << "PathTool";
846
847 QStringList pixelTools = QStringList()
848 << "KritaShape/KisToolBrush"
849 << "KritaShape/KisToolDyna"
850 << "KritaShape/KisToolMultiBrush"
851 << "KritaFill/KisToolFill"
852 << "KritaFill/KisToolGradient";
853
854 KisSelectionMask *selectionMask = dynamic_cast<KisSelectionMask*>(node.data());
855 const bool nodeHasVectorAbilities = node->inherits("KisShapeLayer") ||
856 (selectionMask && selectionMask->selection()->hasShapeSelection());
857
858 if (nodeHasVectorAbilities) {
859 if (pixelTools.contains(KoToolManager::instance()->activeToolId())) {
860 KoToolManager::instance()->switchToolRequested("InteractionTool");
861 }
862 }
863 else {
864 if (vectorTools.contains(KoToolManager::instance()->activeToolId())) {
865 KoToolManager::instance()->switchToolRequested("KritaShape/KisToolBrush");
866 }
867 }
868 }
869
870 if (node == activeNode()) return;
871
873}
874
876{
877 KisNodeSP node = activeNode();
878 if (!node) return;
879
882
883 m_d->view->updateGUI();
885
886 {
888 m_d->pinToTimeline->setChecked(node->isPinnedToTimeline());
889 }
890}
891
898
900{
901 if ((selectedNodes().size() > 1 && node->inherits("KisLayer")) || node->inherits("KisLayer")) {
903 }
904 else if (node->inherits("KisMask")) {
906 }
907}
908
910{
911 Q_ASSERT(node);
912
913 // Change the current node temporarily
914 KisNodeSP originalNode = m_d->imageView->currentNode();
915 m_d->imageView->setCurrentNode(node);
916
917 if (node->inherits("KisLayer")) {
919 }
920 else if (node->inherits("KisMask")) {
922 }
923
924 m_d->imageView->setCurrentNode(originalNode);
925}
926
931
933{
939 return qMin(255, int(opacity * 2.55 + 0.5));
940}
941
942void KisNodeManager::setNodeName(KisNodeSP node, const QString &name)
943{
944 if (!node) return;
945 if (node->name() == name) return;
946
947 m_d->commandsAdapter.setNodeName(node, name);
948
949}
950
952{
953 if (!node) return;
954 if (node->opacity() == opacity) return;
955
956 m_d->commandsAdapter.setOpacity(node, opacity);
957}
958
960 const KoCompositeOp* compositeOp)
961{
962 if (!node) return;
963 if (node->compositeOp() == compositeOp) return;
964
965 m_d->commandsAdapter.setCompositeOp(node, compositeOp);
966}
967
969{
970 if (activeNode) {
972 }
973 if (!selectedNodes.isEmpty()) {
975 }
976}
977
979{
980 m_d->selectedNodes = nodes;
981 Q_EMIT sigUiNeedChangeSelectedNodes(nodes);
982}
983
988
993
998
1003
1004bool KisNodeManager::isNodeHidden(KisNodeSP node, bool isGlobalSelectionHidden)
1005{
1006 if (node && node->isFakeNode()) {
1007 return true;
1008 }
1009
1010 if (isGlobalSelectionHidden && dynamic_cast<KisSelectionMask *>(node.data()) &&
1011 (!node->parent() || !node->parent()->parent())) {
1012 return true;
1013 }
1014
1015 return false;
1016}
1017
1019{
1020 const KisPaintLayer *paintLayer = dynamic_cast<KisPaintLayer*>(node.data());
1021 if (paintLayer) {
1023
1024 if (properties.contains(onionSkinOn)) {
1025 const KisPaintDeviceSP &paintDevice = paintLayer->paintDevice();
1026 if (paintDevice && paintDevice->defaultPixel().opacityU8() == 255) {
1027 m_d->view->showFloatingMessage(i18n("Onion skins require a layer with transparent background."), QIcon());
1028 return false;
1029 }
1030 }
1031 }
1032
1034
1035 return true;
1036}
1037
1039{
1040 KisNodeSP node = activeNode();
1041
1042 setNodeOpacity(node, convertOpacityToInt(opacity));
1043}
1044
1046{
1047 KisNodeSP node = activeNode();
1048
1049 setNodeCompositeOp(node, op);
1050}
1051
1053{
1054 KUndo2MagicString actionName = kundo2_i18n("Duplicate Nodes");
1055 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
1056 juggler->duplicateNode(selectedNodes());
1057}
1058
1060{
1061 KisImageWSP image = view->image();
1062
1063 if (!nodeJuggler ||
1064 (nodeJuggler &&
1065 (nodeJuggler->isEnded() ||
1066 !nodeJuggler->canMergeAction(actionName)))) {
1067
1068 nodeJuggler = new KisNodeJugglerCompressed(actionName, image, q, 750);
1069 nodeJuggler->setAutoDelete(true);
1070 }
1071
1072 return nodeJuggler;
1073}
1074
1076{
1077 if (!canMoveLayers(selectedNodes())) return;
1078
1079 KUndo2MagicString actionName = kundo2_i18n("Raise Nodes");
1080 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
1081 juggler->raiseNode(selectedNodes());
1082}
1083
1085{
1086 if (!canMoveLayers(selectedNodes())) return;
1087
1088 KUndo2MagicString actionName = kundo2_i18n("Lower Nodes");
1089 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
1090 juggler->lowerNode(selectedNodes());
1091}
1092
1094{
1095 if (!node || !node->parent()) {
1096 return;
1097 }
1098
1099 KisNodeList nodes;
1100 nodes << node;
1101 removeSelectedNodes(nodes);
1102}
1103
1105{
1106 if (!canModifyLayers(nodes)) return;
1107
1108 KUndo2MagicString actionName = kundo2_i18n("Remove Nodes");
1109 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
1110 juggler->removeNode(nodes);
1111}
1112
1117
1119{
1120 KisNodeList nodes = selectedNodes();
1121
1122 KUndo2MagicString commandName;
1123 if (nodes.size() == 1 && nodes[0]->inherits("KisMask")) {
1124 commandName = kundo2_i18n("Mirror Mask Horizontally");
1125 }
1126 else {
1127 commandName = kundo2_i18np("Mirror Layer Horizontally", "Mirror %1 Layers Horizontally", nodes.size());
1128 }
1129 mirrorNodes(nodes, commandName, Qt::Horizontal, m_d->view->selection());
1130}
1131
1133{
1134 KisNodeList nodes = selectedNodes();
1135
1136 KUndo2MagicString commandName;
1137 if (nodes.size() == 1 && nodes[0]->inherits("KisMask")) {
1138 commandName = kundo2_i18n("Mirror Mask Vertically");
1139 }
1140 else {
1141 commandName = kundo2_i18np("Mirror Layer Vertically", "Mirror %1 Layers Vertically", nodes.size());
1142 }
1143 mirrorNodes(nodes, commandName, Qt::Vertical, m_d->view->selection());
1144}
1145
1147{
1148 KisNodeSP node = m_d->view->image()->root();
1149 mirrorNode(node, kundo2_i18n("Mirror All Layers Horizontally"),
1150 Qt::Horizontal, m_d->view->selection());
1151}
1152
1154{
1155 KisNodeSP node = m_d->view->image()->root();
1156 mirrorNode(node, kundo2_i18n("Mirror All Layers Vertically"),
1157 Qt::Vertical, m_d->view->selection());
1158}
1159
1161{
1163 if (!activeNode) return;
1164
1165 KisNodeSP nextNode = activeNode->nextSibling();
1166
1167 if (!siblingsOnly) {
1168 // Recurse groups...
1169 while (nextNode && nextNode->childCount() > 0) {
1170 nextNode = nextNode->firstChild();
1171 }
1172
1173 // Out of nodes? Back out of group...
1174 if (!nextNode && activeNode->parent()) {
1175 nextNode = activeNode->parent();
1176 }
1177 }
1178
1179 // Skip nodes hidden from tree view..
1180 while (nextNode && isNodeHidden(nextNode, m_d->nodeDisplayModeAdapter->showGlobalSelectionMask())) {
1181 nextNode = nextNode->nextSibling();
1182 }
1183
1184 // Select node, unless root..
1185 if (nextNode && nextNode->parent()) {
1186 slotNonUiActivatedNode(nextNode);
1187 }
1188}
1189
1194
1196{
1198 if (!activeNode) return;
1199
1200 KisNodeSP nextNode = activeNode->prevSibling();
1201
1202 if (!siblingsOnly) {
1203 // Enter groups..
1204 if (activeNode->childCount() > 0) {
1205 nextNode = activeNode->lastChild();
1206 }
1207
1208 // Out of nodes? Back out of group...
1209 if (!nextNode && activeNode->parent()) {
1210 nextNode = activeNode->parent()->prevSibling();
1211 }
1212 }
1213
1214 // Skip nodes hidden from tree view..
1215 while (nextNode && isNodeHidden(nextNode, m_d->nodeDisplayModeAdapter->showGlobalSelectionMask())) {
1216 nextNode = nextNode->prevSibling();
1217 }
1218
1219 // Select node, unless root..
1220 if (nextNode && nextNode->parent()) {
1221 slotNonUiActivatedNode(nextNode);
1222 }
1223}
1224
1229
1236
1238 const KUndo2MagicString& actionName,
1239 Qt::Orientation orientation,
1240 KisSelectionSP selection)
1241{
1242 KisNodeList nodes = {node};
1243 mirrorNodes(nodes, actionName, orientation, selection);
1244}
1245
1247 const KUndo2MagicString& actionName,
1248 Qt::Orientation orientation,
1249 KisSelectionSP selection)
1250{
1251 Q_FOREACH(KisNodeSP node, nodes) {
1252 if (!canModifyLayer(node)) return;
1253 }
1254
1255 KisImageSignalVector emitSignals;
1256
1257 KisProcessingApplicator applicator(m_d->view->image(), nodes,
1259 emitSignals, actionName);
1260
1261 KisProcessingVisitorSP visitor;
1262
1263 if (selection) {
1264 visitor = new KisMirrorProcessingVisitor(selection, orientation);
1265 } else {
1266 visitor = new KisMirrorProcessingVisitor(m_d->view->image()->bounds(), orientation);
1267 }
1268
1269 if (!selection) {
1271 } else {
1272 applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
1273 }
1274
1275 applicator.end();
1276
1277 nodesUpdated();
1278}
1279
1281 const QString &defaultName,
1282 const QRect &bounds,
1283 qreal xRes,
1284 qreal yRes,
1285 quint8 opacity)
1286{
1287 KoFileDialog dialog(view->mainWindowAsQWidget(), KoFileDialog::SaveFile, "savenodeasimage");
1288 dialog.setCaption(i18n("Export \"%1\"", defaultName));
1289 dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
1291 QString filename = dialog.filename();
1292
1293 if (filename.isEmpty()) return;
1294
1295 QString mimefilter = KisMimeDatabase::mimeTypeForFile(filename, false);
1296
1297 QScopedPointer<KisDocument> doc(KisPart::instance()->createDocument());
1298
1299 KisImageSP dst = new KisImage(doc->createUndoStore(),
1300 bounds.width(),
1301 bounds.height(),
1303 defaultName);
1304 dst->setResolution(xRes, yRes);
1305 doc->setCurrentImage(dst);
1306 KisPaintLayer* paintLayer = new KisPaintLayer(dst, "paint device", opacity);
1307 paintLayer->paintDevice()->makeCloneFrom(device, bounds);
1308 dst->addNode(paintLayer, dst->rootLayer(), KisLayerSP(0));
1309
1310 dst->initialRefreshGraph();
1311
1312 if (!doc->exportDocumentSync(filename, mimefilter.toLatin1())) {
1313 QMessageBox::warning(qApp->activeWindow(),
1314 i18nc("@title:window", "Krita"),
1315 i18n("Could not save the layer. %1", doc->errorMessage().toUtf8().data()),
1316 QMessageBox::Ok);
1317
1318 }
1319}
1320
1322{
1323 KisNodeSP node = activeNode();
1324
1325 if (!node) {
1326 warnKrita << "BUG: Save Node As Image was called without any node selected";
1327 return;
1328 }
1329
1330 KisPaintDeviceSP saveDevice = node->projection();
1331
1332 if (!saveDevice) {
1333 m_d->view->showFloatingMessage(i18nc("warning message when trying to export a transform mask", "Layer has no pixel data"), QIcon());
1334 return;
1335 }
1336
1337 KisImageSP image = m_d->view->image();
1338 QRect saveRect = image->bounds() | node->exactBounds();
1339
1340 m_d->saveDeviceAsImage(saveDevice,
1341 node->name(),
1342 saveRect,
1343 image->xRes(), image->yRes(),
1344 node->opacity());
1345}
1346
1347#include "SvgWriter.h"
1348
1350{
1351 KisShapeLayerSP shapeLayer = qobject_cast<KisShapeLayer*>(activeNode().data());
1352 if (!shapeLayer) {
1353 return;
1354 }
1355
1356 KoFileDialog dialog(m_d->view->mainWindowAsQWidget(), KoFileDialog::SaveFile, "savenodeasimage");
1357 dialog.setCaption(i18nc("@title:window", "Export to SVG"));
1358 dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
1359 dialog.setMimeTypeFilters(QStringList() << "image/svg+xml", "image/svg+xml");
1360 QString filename = dialog.filename();
1361
1362 if (filename.isEmpty()) return;
1363
1364 QUrl url = QUrl::fromLocalFile(filename);
1365
1366 if (url.isEmpty()) return;
1367
1368 const QSizeF sizeInPx = m_d->view->image()->bounds().size();
1369 const QSizeF sizeInPt(sizeInPx.width() / m_d->view->image()->xRes(),
1370 sizeInPx.height() / m_d->view->image()->yRes());
1371
1372 QList<KoShape*> shapes = shapeLayer->shapes();
1373 std::sort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex);
1374
1375 SvgWriter writer(shapes);
1376 if (!writer.save(filename, sizeInPt, true)) {
1377 QMessageBox::warning(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("Could not save to svg: %1", filename));
1378 }
1379}
1380
1382{
1383 KisNodeSP node = activeNode();
1384 if (!canModifyLayer(node)) return;
1385
1386 // guaranteed by KisActionManager
1388
1389 KisLayerUtils::splitAlphaToMask(node->image(), node, m_d->maskManager.createMaskNameCommon(node, "KisTransparencyMask", i18n("Transparency Mask")));
1390}
1391
1393{
1394 KisNodeSP node = q->activeNode();
1395 KisNodeSP parentNode = node->parent();
1396
1397 // guaranteed by KisActionManager
1398 KIS_ASSERT_RECOVER_RETURN(node->inherits("KisTransparencyMask"));
1399
1400 if (writeToLayers && (!parentNode->hasEditablePaintDevice() || !node->isEditable(false))) {
1401 QMessageBox::information(view->mainWindowAsQWidget(),
1402 i18nc("@title:window", "Layer %1 is not editable", parentNode->name()),
1403 i18n("Cannot write alpha channel of "
1404 "the parent layer \"%1\".\n"
1405 "The operation will be cancelled.", parentNode->name()));
1406 return;
1407 }
1408
1409 KisPaintDeviceSP dstDevice;
1410 if (writeToLayers) {
1412 dstDevice = parentNode->paintDevice();
1413 } else {
1414 KisPaintDeviceSP copyDevice = parentNode->paintDevice();
1415 if (!copyDevice) {
1416 copyDevice = parentNode->original();
1417 }
1418 dstDevice = new KisPaintDevice(*copyDevice);
1419 }
1420
1421 const KoColorSpace *dstCS = dstDevice->colorSpace();
1422
1423 KisPaintDeviceSP selectionDevice = node->paintDevice();
1424 KIS_ASSERT_RECOVER_RETURN(selectionDevice->colorSpace()->pixelSize() == 1);
1425
1426 const QRect processRect =
1427 selectionDevice->exactBounds() |
1428 dstDevice->exactBounds() |
1429 selectionDevice->defaultBounds()->bounds();
1430
1431 QScopedPointer<KisTransaction> transaction;
1432
1433 if (writeToLayers) {
1434 commandsAdapter.beginMacro(kundo2_i18n("Write Alpha into a Layer"));
1435 transaction.reset(new KisTransaction(kundo2_noi18n("__write_alpha_channel__"), dstDevice));
1436 }
1437
1438 KisSequentialIterator srcIt(selectionDevice, processRect);
1439 KisSequentialIterator dstIt(dstDevice, processRect);
1440
1441 while (srcIt.nextPixel() && dstIt.nextPixel()) {
1442 quint8 *alpha8Ptr = srcIt.rawData();
1443 quint8 *dstPtr = dstIt.rawData();
1444
1445 dstCS->setOpacity(dstPtr, *alpha8Ptr, 1);
1446 }
1447
1448 if (writeToLayers) {
1449 commandsAdapter.addExtraCommand(transaction->endAndTake());
1450 commandsAdapter.removeNode(node);
1451 commandsAdapter.endMacro();
1452 } else {
1453 KisImageWSP image = view->image();
1454 QRect saveRect = image->bounds();
1455
1456 saveDeviceAsImage(dstDevice, parentNode->name(),
1457 saveRect,
1458 image->xRes(), image->yRes(),
1460 }
1461}
1462
1463
1468
1473
1475{
1476 KisNodeList nodes = this->selectedNodes();
1477 KisNodeSP active = activeNode();
1478 if (nodes.isEmpty() || !active) return;
1479
1480 bool isLocked = active->userLocked();
1481
1482 for (auto &node : nodes) {
1484 }
1485}
1486
1488{
1489 KisNodeList nodes = this->selectedNodes();
1490 KisNodeSP active = activeNode();
1491 if (nodes.isEmpty() || !active) return;
1492
1493 bool isVisible = active->visible();
1494
1495 for (auto &node : nodes) {
1497 }
1498}
1499
1501{
1502 KisNodeList nodes = this->selectedNodes();
1503 KisNodeSP active = activeNode();
1504 if (nodes.isEmpty() || !active) return;
1505
1506 auto layer = qobject_cast<KisPaintLayer*>(active.data());
1507 if (!layer) {
1508 return;
1509 }
1510
1511 bool isAlphaLocked = layer->alphaLocked();
1512 for (auto &node : nodes) {
1513 auto layer = qobject_cast<KisPaintLayer*>(node.data());
1514 if (layer) {
1516 }
1517 }
1518}
1519
1521{
1522 KisNodeList nodes = this->selectedNodes();
1523 KisNodeSP active = activeNode();
1524 if (nodes.isEmpty() || !active) return;
1525
1526 auto layer = qobject_cast<KisLayer*>(active.data());
1527 if (!layer) {
1528 return;
1529 }
1530
1531 bool isAlphaDisabled = layer->alphaChannelDisabled();
1532 for (auto &node : nodes) {
1533 auto layer = qobject_cast<KisLayer*>(node.data());
1534 if (layer) {
1536 }
1537 }
1538}
1539
1541{
1542 Q_ASSERT(node);
1543 KisLayerSP layer = qobject_cast<KisLayer*>(node.data());
1544 if (!layer) {
1545 return;
1546 }
1547
1548 KisFilterMaskSP mask = layer->colorOverlayMask();
1549 if (!mask) {
1550 // This layer does not use fast color overlay mask.
1551 return;
1552 }
1553
1555}
1556
1558{
1559 KisNodeList nodes = this->selectedNodes();
1560 if (nodes.isEmpty()) return;
1561
1562 KisNodeList::Iterator it = nodes.begin();
1563 while (it != nodes.end()) {
1564 // make sure the deleted nodes aren't referenced here again
1565 if (!it->data()->parent()) {
1566 nodes.erase(it);
1567 }
1568 it++;
1569 }
1570
1571 KisClipboard::instance()->setLayers(nodes, m_d->view->image(), false);
1572
1573 if (canModifyLayers(nodes)) {
1574 KUndo2MagicString actionName = kundo2_i18n("Cut Nodes");
1575 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
1576 juggler->removeNode(nodes);
1577 }
1578}
1579
1581{
1582 KisNodeList nodes = this->selectedNodes();
1583 KisClipboard::instance()->setLayers(nodes, m_d->view->image(), true);
1584}
1585
1586void KisNodeManager::pasteLayersFromClipboard(bool changeOffset, QPointF offset, KisProcessingApplicator *applicator)
1587{
1588 const QMimeData *data = KisClipboard::instance()->layersMimeData();
1589 if (!data) return;
1590
1592
1593 KisShapeController *shapeController = dynamic_cast<KisShapeController*>(m_d->imageView->document()->shapeController());
1594 Q_ASSERT(shapeController);
1595
1596 KisDummiesFacadeBase *dummiesFacade = dynamic_cast<KisDummiesFacadeBase*>(m_d->imageView->document()->shapeController());
1597 Q_ASSERT(dummiesFacade);
1598
1599 const bool copyNode = false;
1600 KisImageSP image = m_d->view->image();
1601 KisNodeDummy *parentDummy = dummiesFacade->dummyForNode(activeNode ? activeNode : image->root());
1602 KisNodeDummy *aboveThisDummy = parentDummy ? parentDummy->lastChild() : 0;
1603
1605 image,
1606 shapeController,
1607 parentDummy,
1608 aboveThisDummy,
1609 copyNode,
1611 changeOffset,
1612 offset,
1613 applicator);
1614}
1615
1617 const QString &overrideGroupName,
1618 KisNodeSP *newGroup,
1619 KisNodeSP *newLastChild)
1620{
1621 KisNodeSP active = activeNode();
1622 if (!active) return false;
1623
1624 if (!canMoveLayer(active)) return false;
1625
1626 KisImageSP image = m_d->view->image();
1627 QString groupName = !overrideGroupName.isEmpty() ? overrideGroupName : image->nextLayerName(i18nc("A group of layers", "Group"));
1628 KisGroupLayerSP group = new KisGroupLayer(image.data(), groupName, OPACITY_OPAQUE_U8);
1629
1630 KisNodeList nodes1;
1631 nodes1 << group;
1632
1633 KisNodeList nodes2;
1636
1637 if (nodes2.size() == 0) return false;
1638
1639 if (KisLayerUtils::checkIsChildOf(active, nodes2)) {
1640 active = nodes2.first();
1641 }
1642
1643 KisNodeSP parent = active->parent();
1644 KisNodeSP aboveThis = active;
1645
1646 juggler->addNode(nodes1, parent, aboveThis);
1647 juggler->moveNode(nodes2, group, 0);
1648
1649 *newGroup = group;
1650 *newLastChild = nodes2.last();
1651
1652 return true;
1653}
1654
1656{
1657 KUndo2MagicString actionName = kundo2_i18n("Quick Group");
1658 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
1659
1660 KisNodeSP parent;
1661 KisNodeSP above;
1662
1663 createQuickGroupImpl(juggler, "", &parent, &above);
1664}
1665
1667{
1668 KUndo2MagicString actionName = kundo2_i18n("Quick Clipping Group");
1669 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
1670
1671 KisNodeSP parent;
1672 KisNodeSP above;
1673
1674 KisImageSP image = m_d->view->image();
1675 if (createQuickGroupImpl(juggler, image->nextLayerName(i18nc("default name for a clipping group layer", "Clipping Group")), &parent, &above)) {
1676 KisPaintLayerSP maskLayer = new KisPaintLayer(image.data(), i18nc("default name for quick clip group mask layer", "Mask Layer"), OPACITY_OPAQUE_U8, image->colorSpace());
1677 maskLayer->disableAlphaChannel(true);
1678
1679 juggler->addNode(KisNodeList() << maskLayer, parent, above);
1680 }
1681}
1682
1684{
1685 KisNodeSP active = activeNode();
1686 if (!active) return;
1687
1688 if (!canModifyLayer(active)) return;
1689
1690 KisNodeSP parent = active->parent();
1691 KisNodeSP aboveThis = active;
1692
1693 auto checkCanMoveLayers = [this] (KisNodeList nodes, KisNodeSP newParent) -> bool {
1694 auto incompatibleNode =
1695 std::find_if(nodes.begin(), nodes.end(),
1696 [newParent] (KisNodeSP node) {
1697 return !newParent->allowAsChild(node);
1698 });
1699
1700 if (incompatibleNode != nodes.end()) {
1701 const QString message =
1702 newParent->parent() ?
1703 i18n("Cannot move layer \"%1\" into new parent \"%2\"",
1704 (*incompatibleNode)->name(),
1705 newParent->name()) :
1706 i18n("Cannot move layer \"%1\" into the root layer",
1707 (*incompatibleNode)->name());
1708 m_d->view->showFloatingMessage(message, QIcon());
1709 return false;
1710 }
1711 return true;
1712 };
1713
1714 KUndo2MagicString actionName = kundo2_i18n("Quick Ungroup");
1715
1716 if (parent && dynamic_cast<KisGroupLayer*>(active.data())) {
1717 KisNodeList nodes = active->childNodes(QStringList(), KoProperties());
1718
1719 if (checkCanMoveLayers(nodes, parent)) {
1720 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
1721 juggler->moveNode(nodes, parent, active);
1722 juggler->removeNode(KisNodeList() << active);
1723 }
1724 } else if (parent && parent->parent()) {
1725 KisNodeSP grandParent = parent->parent();
1726
1727 KisNodeList allChildNodes = parent->childNodes(QStringList(), KoProperties());
1728 KisNodeList allSelectedNodes = selectedNodes();
1729
1730 const bool removeParent = KritaUtils::compareListsUnordered(allChildNodes, allSelectedNodes);
1731
1732 if (checkCanMoveLayers(allSelectedNodes, parent)) {
1733 KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
1734 juggler->moveNode(allSelectedNodes, grandParent, parent);
1735 if (removeParent) {
1736 juggler->removeNode(KisNodeList() << parent);
1737 }
1738 }
1739 }
1740}
1741
1742void KisNodeManager::selectLayersImpl(const KoProperties &props, const KoProperties &invertedProps)
1743{
1744 KisImageSP image = m_d->view->image();
1745 KisNodeList nodes = KisLayerUtils::findNodesWithProps(image->root(), props, true);
1746
1748
1750 nodes = KisLayerUtils::findNodesWithProps(image->root(), invertedProps, true);
1751 }
1752
1753 if (!nodes.isEmpty()) {
1754 slotImageRequestNodeReselection(nodes.last(), nodes);
1755 }
1756}
1757
1759{
1760 KoProperties props;
1761 selectLayersImpl(props, props);
1762}
1763
1765{
1766 KoProperties props;
1767 props.setProperty("visible", true);
1768
1769 KoProperties invertedProps;
1770 invertedProps.setProperty("visible", false);
1771
1772 selectLayersImpl(props, invertedProps);
1773}
1774
1776{
1777 KoProperties props;
1778 props.setProperty("locked", true);
1779
1780 KoProperties invertedProps;
1781 invertedProps.setProperty("locked", false);
1782
1783 selectLayersImpl(props, invertedProps);
1784}
1785
1787{
1788 KoProperties props;
1789 props.setProperty("visible", false);
1790
1791 KoProperties invertedProps;
1792 invertedProps.setProperty("visible", true);
1793
1794 selectLayersImpl(props, invertedProps);
1795}
1796
1798{
1799 KoProperties props;
1800 props.setProperty("locked", false);
1801
1802 KoProperties invertedProps;
1803 invertedProps.setProperty("locked", true);
1804
1805 selectLayersImpl(props, invertedProps);
1806}
1807
1809{
1810 if (!sender()->property("node").isNull()) {
1811 QString name = sender()->property("node").toString();
1812 KisNodeSP node = KisLayerUtils::findNodeByName(m_d->imageView->image()->rootLayer(),name);
1813 if (node) {
1814 slotUiActivatedNode(node);
1815 }
1816 }
1817}
float value(const T *src, size_t ch)
qreal v
QList< QString > QStringList
const quint8 OPACITY_OPAQUE_U8
char nodeType(const KoPathPoint *point)
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
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 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
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:715
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:434
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:820
void disableAlphaChannel(bool disable)
Definition kis_layer.cc:319
virtual KisSelectionMaskSP selectionMask() const
Definition kis_layer.cc:498
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:567
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