From kde-kimageshop Fri Aug 14 20:02:18 2020 From: Dmitry Kazakov Date: Fri, 14 Aug 2020 20:02:18 +0000 To: kde-kimageshop Subject: [graphics/krita] libs: Fix flatten/merge down/merge multiple action handle locked groups correctly Message-Id: <20200814200218.25890124151B () leptone ! kde ! org> X-MARC-Message: https://marc.info/?l=kde-kimageshop&m=159743538313730 Git commit fe9741d27cb87a59b3add558489ce5b407a35d9a by Dmitry Kazakov. Committed on 14/08/2020 at 20:02. Pushed by dkazakov into branch 'master'. Fix flatten/merge down/merge multiple action handle locked groups correctly This patch extends the already existing strategy, which was previously used for merge down operation only: 1) If there is at least one unlocked layer in the selection, then the merge will happen. 2) The merge result will be written into nearest unlocked parent 3) All unlocked layers of the selection will be removed 4) All locked layers will be kept in the layers stack, but switched into *invisible* state. CCBUG:406697 CC:kimageshop@kde.org M +85 -20 libs/image/kis_layer_utils.cpp M +18 -0 libs/ui/kis_layer_manager.cc https://invent.kde.org/graphics/krita/commit/fe9741d27cb87a59b3add558489ce5= b407a35d9a diff --git a/libs/image/kis_layer_utils.cpp b/libs/image/kis_layer_utils.cpp index dd700d7562..25c59b43ca 100644 --- a/libs/image/kis_layer_utils.cpp +++ b/libs/image/kis_layer_utils.cpp @@ -692,6 +692,52 @@ namespace KisLayerUtils { }; = = + void splitNonRemovableNodes(KisNodeList &nodesToRemove, KisNodeList &_= nodesToHide) + { + QSet nodesToHide; + QSet extraNodesToRemove; + + for (auto it =3D nodesToRemove.begin(); it !=3D nodesToRemove.end(= ); ++it) { + KisNodeSP root =3D *it; + KIS_SAFE_ASSERT_RECOVER_NOOP(root->visible()); + + if (!root->isEditable(false)) { + nodesToHide.insert(root); + } else { + bool rootNeedsCarefulRemoval =3D false; + + recursiveApplyNodes(root, + [root, &nodesToHide, &rootNeedsCareful= Removal] (KisNodeSP node) { + if (!node->isEditable(false)) { + while (node !=3D root) { + nodesToHide.insert(node); + node =3D node->parent(); + KIS_SAFE_ASSERT_RECOVER_BR= EAK(node); + } + nodesToHide.insert(root); + rootNeedsCarefulRemoval =3D tr= ue; + } + }); + + if (rootNeedsCarefulRemoval) { + recursiveApplyNodes(root, + [&extraNodesToRemove] (KisNodeSP n= ode) { + extraNodesToRemove.insert(node= ); + }); + } + } + } + + nodesToRemove +=3D extraNodesToRemove.toList(); + + KritaUtils::filterContainer(nodesToRemove, + [nodesToHide](KisNodeSP n= ode) { + return !nodesToHide.c= ontains(node); + }); + + _nodesToHide =3D nodesToHide.toList(); + } + struct CleanUpNodes : private RemoveNodeHelper, public KisCommandUtils= ::AggregateCommand { CleanUpNodes(MergeDownInfoBaseSP info, KisNodeSP putAfter) : m_info(info), m_putAfter(putAfter) {} @@ -792,16 +838,14 @@ namespace KisLayerUtils { } = KisNodeList safeNodesToDelete =3D m_info->allSrcNodes(); - for (KisNodeList::iterator it =3D safeNodesToDelete.begin(= ); it !=3D safeNodesToDelete.end(); ++it) { - KisNodeSP node =3D *it; - if (node->userLocked() && node->visible()) { - addCommand(new KisImageChangeVisibilityCommand(fal= se, node)); - } + KisNodeList safeNodesToHide; + + splitNonRemovableNodes(safeNodesToDelete, safeNodesToHide); + + Q_FOREACH(KisNodeSP node, safeNodesToHide) { + addCommand(new KisImageChangeVisibilityCommand(false, = node)); } = - KritaUtils::filterContainer(safeNodesToDelete= , [](KisNodeSP node) { - return !node->userLocked(); - }); safeRemoveMultipleNodes(safeNodesToDelete, m_info->image); } = @@ -1312,25 +1356,46 @@ namespace KisLayerUtils { emitSignals << ModifiedSignal; emitSignals << ComplexNodeReselectionSignal(KisNodeSP(), KisNodeLi= st(), KisNodeSP(), mergedNodes); = - KisProcessingApplicator applicator(image, 0, - KisProcessingApplicator::NONE, - emitSignals, - actionName); = = KisNodeList originalNodes =3D mergedNodes; KisNodeList invisibleNodes; mergedNodes =3D filterInvisibleNodes(originalNodes, &invisibleNode= s, &putAfter); = - if (!invisibleNodes.isEmpty() && !mergedNodes.isEmpty()) { - /* If the putAfter node is invisible, - * we should instead pick one of the nodes - * to be merged to avoid a null putAfter. - */ - if (!putAfter->visible()){ - putAfter =3D mergedNodes.first(); - } + if (mergedNodes.isEmpty()) return; + + /* If the putAfter node is invisible, + * we should instead pick one of the nodes + * to be merged to avoid a null putAfter. + */ + if (!putAfter->visible()){ + putAfter =3D mergedNodes.first(); + } + + // make sure we don't add the new layer into a locked group + KIS_SAFE_ASSERT_RECOVER_RETURN(putAfter->parent()); + while (putAfter->parent() && !putAfter->parent()->isEditable()) { + putAfter =3D putAfter->parent(); + } + + /** + * We have reached the root of the layer hierarchy and didn't mana= ge + * to find a node that was editable enough for putting our merged + * result into it. That whouldn't happen in normal circumstances, + * unless the user chose to make the root layer visible and lock + * it manually. + */ + if (!putAfter->parent()) { + return; + } + + KisProcessingApplicator applicator(image, 0, + KisProcessingApplicator::NONE, + emitSignals, + actionName); + = + if (!invisibleNodes.isEmpty()) { applicator.applyCommand( new SimpleRemoveLayers(invisibleNodes, image), diff --git a/libs/ui/kis_layer_manager.cc b/libs/ui/kis_layer_manager.cc index 19d804dae7..dfa0b8c8c6 100644 --- a/libs/ui/kis_layer_manager.cc +++ b/libs/ui/kis_layer_manager.cc @@ -804,6 +804,24 @@ void KisLayerManager::mergeLayer() if (!m_view->blockUntilOperationsFinished(image)) return; = QList selectedNodes =3D m_view->nodeManager()->selectedNode= s(); + + // check if all the layers are a part of a locked group + bool hasEditableLayer =3D false; + Q_FOREACH (KisNodeSP node, selectedNodes) { + if (node->isEditable()) { + hasEditableLayer =3D true; + break; + } + } + + if (!hasEditableLayer) { + m_view->showFloatingMessage( + i18nc("floating message in layer manager", + "Layer is locked "), + QIcon(), 2000, KisFloatingMessage::Low); + return; + } + if (selectedNodes.size() > 1) { image->mergeMultipleLayers(selectedNodes, m_view->activeNode()); }