Commit 1893c0a7 authored by Dmitry Kazakov's avatar Dmitry Kazakov
Browse files

Fix actions not to modify locked layers

1) When trying to D&D **from** a locked layer Krita will
   force a "copy" mode

2) When trying to D&D **into** a locked group the operation will
   be forbidden. If the user still managed to do that, the layer
   will be put into a new position.

3) Layer->Properties should not be allowed for locked layers or
   masks.

4) Layer->Convert should not be allowed for locked layers

5) Lower/Raise node should not move nodes in a locked group

6) Lower/Raise node should not try to enter a locked group

BUG:425412
BUG:406697
parent 36d91be0
......@@ -236,6 +236,8 @@ void KisLayerManager::layerProperties()
QList<KisNodeSP> selectedNodes = m_view->nodeManager()->selectedNodes();
const bool multipleLayersSelected = selectedNodes.size() > 1;
if (!m_view->nodeManager()->canModifyLayers(selectedNodes)) return;
KisAdjustmentLayerSP adjustmentLayer = KisAdjustmentLayerSP(dynamic_cast<KisAdjustmentLayer*>(layer.data()));
KisGeneratorLayerSP generatorLayer = KisGeneratorLayerSP(dynamic_cast<KisGeneratorLayer*>(layer.data()));
KisFileLayerSP fileLayer = KisFileLayerSP(dynamic_cast<KisFileLayer*>(layer.data()));
......@@ -367,6 +369,8 @@ void KisLayerManager::changeCloneSource()
return;
}
if (!m_view->nodeManager()->canModifyLayers(implicitCastList<KisNodeSP>(cloneLayers))) return;
KisDlgChangeCloneSource *dialog = new KisDlgChangeCloneSource(cloneLayers, m_view);
dialog->setCaption(i18n("Change Clone Layer"));
dialog->resize(dialog->minimumSizeHint());
......@@ -381,6 +385,8 @@ void KisLayerManager::convertNodeToPaintLayer(KisNodeSP source)
KisImageWSP image = m_view->image();
if (!image) return;
// this precondition must be checked at higher level
KIS_SAFE_ASSERT_RECOVER_RETURN(source->isEditable(false));
KisLayer *srcLayer = qobject_cast<KisLayer*>(source.data());
if (srcLayer && (srcLayer->inherits("KisGroupLayer") || srcLayer->layerStyle() || srcLayer->childCount() > 0)) {
......@@ -388,7 +394,6 @@ void KisLayerManager::convertNodeToPaintLayer(KisNodeSP source)
return;
}
KisPaintDeviceSP srcDevice =
source->paintDevice() ? source->projection() : source->original();
......@@ -456,6 +461,8 @@ void KisLayerManager::convertGroupToAnimated()
KisGroupLayerSP group = dynamic_cast<KisGroupLayer*>(activeLayer().data());
if (group.isNull()) return;
if (!m_view->nodeManager()->canModifyLayer(group)) return;
KisPaintLayerSP animatedLayer = new KisPaintLayer(m_view->image(), group->name(), OPACITY_OPAQUE_U8);
animatedLayer->enableAnimation();
KisRasterKeyframeChannel *contentChannel = dynamic_cast<KisRasterKeyframeChannel*>(
......@@ -482,6 +489,9 @@ void KisLayerManager::convertLayerToFileLayer(KisNodeSP source)
KisImageSP image = m_view->image();
if (!image) return;
// this precondition must be checked at higher level
KIS_SAFE_ASSERT_RECOVER_RETURN(source->isEditable(false));
QStringList listMimeFilter = KisImportExportManager::supportedMimeTypes(KisImportExportManager::Export);
KoDialog dlg;
......@@ -574,7 +584,7 @@ void KisLayerManager::adjustLayerPosition(KisNodeSP node, KisNodeSP activeNode,
}
while (parent &&
(!parent->allowAsChild(node) || parent->userLocked())) {
(!parent->allowAsChild(node) || !parent->isEditable(false))) {
above = parent;
parent = parent->parent();
......@@ -813,7 +823,7 @@ void KisLayerManager::mergeLayer()
// check if all the layers are a part of a locked group
bool hasEditableLayer = false;
Q_FOREACH (KisNodeSP node, selectedNodes) {
if (node->isEditable()) {
if (node->isEditable(false)) {
hasEditableLayer = true;
break;
}
......@@ -821,8 +831,8 @@ void KisLayerManager::mergeLayer()
if (!hasEditableLayer) {
m_view->showFloatingMessage(
i18nc("floating message in layer manager",
"Layer is locked "),
i18ncp("floating message in layer manager",
"Layer is locked ", "Layers are locked", selectedNodes.size()),
QIcon(), 2000, KisFloatingMessage::Low);
return;
}
......@@ -869,6 +879,7 @@ void KisLayerManager::flattenLayer()
if (!layer) return;
if (!m_view->blockUntilOperationsFinished(image)) return;
if (!m_view->nodeManager()->canModifyLayer(layer)) return;
convertNodeToPaintLayer(layer);
m_view->updateGUI();
......@@ -883,6 +894,7 @@ void KisLayerManager::rasterizeLayer()
if (!layer) return;
if (!m_view->blockUntilOperationsFinished(image)) return;
if (!m_view->nodeManager()->canModifyLayer(layer)) return;
KisPaintLayerSP paintLayer = new KisPaintLayer(image, layer->name(), layer->opacity());
KisPainter gc(paintLayer->paintDevice());
......@@ -1006,6 +1018,7 @@ void KisLayerManager::layerStyle()
if (!layer) return;
if (!m_view->blockUntilOperationsFinished(image)) return;
if (!m_view->nodeManager()->canModifyLayer(layer)) return;
KisPSDLayerStyleSP oldStyle;
if (layer->layerStyle()) {
......
......@@ -51,6 +51,8 @@
#include "kis_node_commands_adapter.h"
#include "commands/kis_deselect_global_selection_command.h"
#include "kis_iterator_ng.h"
#include "kis_node_manager.h"
KisMaskManager::KisMaskManager(KisViewManager * view)
: m_view(view)
......@@ -195,9 +197,7 @@ void KisMaskManager::createMaskCommon(KisMaskSP mask,
KisNodeSP KisMaskManager::createSelectionMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool convertActiveNode)
{
if (!activeNode->isEditable()) {
return 0;
}
if (!m_view->nodeManager()->canModifyLayer(activeNode)) return 0;
KisSelectionMaskSP mask = new KisSelectionMask(m_view->image());
......@@ -211,9 +211,7 @@ KisNodeSP KisMaskManager::createSelectionMask(KisNodeSP activeNode, KisPaintDevi
KisNodeSP KisMaskManager::createTransparencyMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool convertActiveNode)
{
if (!activeNode->isEditable()) {
return 0;
}
if (!m_view->nodeManager()->canModifyLayer(activeNode)) return 0;
KisMaskSP mask = new KisTransparencyMask(m_view->image(), "");
createMaskCommon(mask, activeNode, copyFrom, kundo2_i18n("Add Transparency Mask"), "KisTransparencyMask", i18n("Transparency Mask"), false, convertActiveNode);
......@@ -226,9 +224,7 @@ KisNodeSP KisMaskManager::createTransparencyMask(KisNodeSP activeNode, KisPaintD
KisNodeSP KisMaskManager::createFilterMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool quiet, bool convertActiveNode)
{
if (!activeNode->isEditable()) {
return 0;
}
if (!m_view->nodeManager()->canModifyLayer(activeNode)) return 0;
KisFilterMaskSP mask = new KisFilterMask(m_view->image(), "");
createMaskCommon(mask, activeNode, copyFrom, kundo2_i18n("Add Filter Mask"), "KisFilterMask", i18n("Filter Mask"), false, convertActiveNode);
......@@ -279,9 +275,7 @@ KisNodeSP KisMaskManager::createFilterMask(KisNodeSP activeNode, KisPaintDeviceS
KisNodeSP KisMaskManager::createColorizeMask(KisNodeSP activeNode)
{
if (!activeNode->isEditable()) {
return 0;
}
if (!m_view->nodeManager()->canModifyLayer(activeNode)) return 0;
KisColorizeMaskSP mask = new KisColorizeMask(m_view->image(), "");
createMaskCommon(mask, activeNode, 0, kundo2_i18n("Add Colorize Mask"), "KisColorizeMask", i18n("Colorize Mask"), true, false);
......@@ -293,9 +287,7 @@ KisNodeSP KisMaskManager::createColorizeMask(KisNodeSP activeNode)
KisNodeSP KisMaskManager::createTransformMask(KisNodeSP activeNode)
{
if (!activeNode->isEditable()) {
return 0;
}
if (!m_view->nodeManager()->canModifyLayer(activeNode)) return 0;
KisTransformMaskSP mask = new KisTransformMask(m_view->image(), "");
createMaskCommon(mask, activeNode, 0, kundo2_i18n("Add Transform Mask"), "KisTransformMask", i18n("Transform Mask"), true, false);
......@@ -306,6 +298,8 @@ void KisMaskManager::maskProperties()
{
if (!activeMask()) return;
if (!m_view->nodeManager()->canModifyLayer(activeMask())) return;
if (activeMask()->inherits("KisFilterMask")) {
KisFilterMask *mask = static_cast<KisFilterMask*>(activeMask().data());
......
......@@ -346,6 +346,16 @@ struct LowerRaiseLayer : public KisCommandUtils::AggregateCommand {
AllMasks;
}
bool allowsAsChildren(KisNodeSP parent, KisNodeList nodes) {
if (!parent->isEditable(false)) return false;
Q_FOREACH (KisNodeSP node, nodes) {
if (!parent->allowAsChild(node)) return false;
}
return true;
}
void populateChildCommands() override {
KisNodeList sortedNodes = KisLayerUtils::sortAndFilterAnyMergableNodesSafe(m_nodes, m_image);
KisNodeSP headNode = m_lower ? sortedNodes.first() : sortedNodes.last();
......@@ -354,6 +364,8 @@ struct LowerRaiseLayer : public KisCommandUtils::AggregateCommand {
KisNodeSP parent = headNode->parent();
KisNodeSP grandParent = parent ? parent->parent() : 0;
if (!parent->isEditable(false)) return;
KisNodeSP newAbove;
KisNodeSP newParent;
......@@ -361,11 +373,8 @@ struct LowerRaiseLayer : public KisCommandUtils::AggregateCommand {
KisNodeSP prevNode = headNode->prevSibling();
if (prevNode) {
if ((prevNode->inherits("KisGroupLayer") &&
!prevNode->collapsed())
||
(nodesType == AllMasks &&
prevNode->inherits("KisLayer"))) {
if (allowsAsChildren(prevNode, sortedNodes) &&
!prevNode->collapsed()) {
newAbove = prevNode->lastChild();
newParent = prevNode;
......@@ -390,12 +399,8 @@ struct LowerRaiseLayer : public KisCommandUtils::AggregateCommand {
KisNodeSP nextNode = headNode->nextSibling();
if (nextNode) {
if ((nextNode->inherits("KisGroupLayer") &&
!nextNode->collapsed())
||
(nodesType == AllMasks &&
nextNode->inherits("KisLayer"))) {
if (allowsAsChildren(nextNode, sortedNodes) &&
!nextNode->collapsed()) {
newAbove = 0;
newParent = nextNode;
} else {
......@@ -478,8 +483,7 @@ struct DuplicateLayers : public KisCommandUtils::AggregateCommand {
KisNodeSP newAbove = filteredNodes.last();
// make sure we don't add the new layer into a locked group
KIS_SAFE_ASSERT_RECOVER_RETURN(newAbove->parent());
while (newAbove->parent() && !newAbove->parent()->isEditable()) {
while (newAbove->parent() && !newAbove->parent()->isEditable(false)) {
newAbove = newAbove->parent();
}
......
......@@ -438,6 +438,59 @@ const KoColorSpace* KisNodeManager::activeColorSpace()
}
}
bool KisNodeManager::canModifyLayers(KisNodeList nodes, bool showWarning)
{
KisNodeSP lockedNode;
Q_FOREACH (KisNodeSP node, nodes) {
if (!node->isEditable(false)) {
lockedNode = node;
break;
}
}
if (lockedNode && showWarning) {
QString errorMessage;
if (nodes.size() <= 1) {
errorMessage = i18n("Layer is locked");
} else {
errorMessage = i18n("Layer \"%1\" is locked", lockedNode->name());
}
m_d->view->showFloatingMessage(errorMessage, QIcon());
}
return !lockedNode;
}
bool KisNodeManager::canModifyLayer(KisNodeSP node, bool showWarning)
{
return canModifyLayers({node}, showWarning);
}
bool KisNodeManager::canMoveLayers(KisNodeList nodes, bool showWarning)
{
KisNodeSP lockedNode;
Q_FOREACH (KisNodeSP node, nodes) {
if (node->parent() && !node->parent()->isEditable(false)) {
lockedNode = node->parent();
break;
}
}
if (lockedNode && showWarning) {
QString errorMessage = i18n("Layer \"%1\" is locked", lockedNode->name());
m_d->view->showFloatingMessage(errorMessage, QIcon());
}
return !lockedNode;
}
bool KisNodeManager::canMoveLayer(KisNodeSP node, bool showWarning)
{
return canMoveLayers({node}, showWarning);
}
void KisNodeManager::moveNodeAt(KisNodeSP node, KisNodeSP parent, int index)
{
if (parent->allowAsChild(node)) {
......@@ -558,6 +611,10 @@ KisNodeSP KisNodeManager::createNode(const QString & nodeType, bool quiet, KisP
KIS_ASSERT_RECOVER_RETURN_VALUE(activeNode, 0);
/// the check for editability happens inside the functions
/// themselves, because layers can be created anyway (in a
/// different position), but masks cannot.
// XXX: make factories for this kind of stuff,
// with a registry
......@@ -621,6 +678,8 @@ void KisNodeManager::convertNode(const QString &nodeType)
KisNodeSP activeNode = this->activeNode();
if (!activeNode) return;
if (!canModifyLayer(activeNode)) return;
if (nodeType == "KisPaintLayer") {
m_d->layerManager.convertNodeToPaintLayer(activeNode);
} else if (nodeType == "KisSelectionMask" ||
......@@ -930,6 +989,8 @@ KisNodeJugglerCompressed* KisNodeManager::Private::lazyGetJuggler(const KUndo2Ma
void KisNodeManager::raiseNode()
{
if (!canMoveLayers(selectedNodes())) return;
KUndo2MagicString actionName = kundo2_i18n("Raise Nodes");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->raiseNode(selectedNodes());
......@@ -937,6 +998,8 @@ void KisNodeManager::raiseNode()
void KisNodeManager::lowerNode()
{
if (!canMoveLayers(selectedNodes())) return;
KUndo2MagicString actionName = kundo2_i18n("Lower Nodes");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->lowerNode(selectedNodes());
......@@ -955,6 +1018,8 @@ void KisNodeManager::removeSingleNode(KisNodeSP node)
void KisNodeManager::removeSelectedNodes(KisNodeList nodes)
{
if (!canModifyLayers(nodes)) return;
KUndo2MagicString actionName = kundo2_i18n("Remove Nodes");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->removeNode(nodes);
......@@ -1087,6 +1152,8 @@ void KisNodeManager::mirrorNode(KisNodeSP node,
Qt::Orientation orientation,
KisSelectionSP selection)
{
if (!canModifyLayer(node)) return;
KisImageSignalVector emitSignals;
emitSignals << ModifiedSignal;
......@@ -1214,6 +1281,7 @@ void KisNodeManager::saveVectorLayerAsImage()
void KisNodeManager::slotSplitAlphaIntoMask()
{
KisNodeSP node = activeNode();
if (!canModifyLayer(node)) return;
// guaranteed by KisActionManager
KIS_ASSERT_RECOVER_RETURN(node->hasEditablePaintDevice());
......@@ -1255,7 +1323,7 @@ void KisNodeManager::Private::mergeTransparencyMaskAsAlpha(bool writeToLayers)
// guaranteed by KisActionManager
KIS_ASSERT_RECOVER_RETURN(node->inherits("KisTransparencyMask"));
if (writeToLayers && !parentNode->hasEditablePaintDevice()) {
if (writeToLayers && (!parentNode->hasEditablePaintDevice() || !node->isEditable(false))) {
QMessageBox::information(view->mainWindow(),
i18nc("@title:window", "Layer %1 is not editable", parentNode->name()),
i18n("Cannot write alpha channel of "
......@@ -1404,29 +1472,10 @@ void KisNodeManager::cutLayersToClipboard()
KisClipboard::instance()->setLayers(nodes, m_d->view->image(), false);
KisNodeSP lockedNode;
Q_FOREACH (KisNodeSP node, nodes) {
if (!node->isEditable(false)) {
lockedNode = node;
break;
}
}
if (!lockedNode) {
if (canModifyLayers(nodes)) {
KUndo2MagicString actionName = kundo2_i18n("Cut Nodes");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->removeNode(nodes);
} else {
QString errorMessage;
if (nodes.size() <= 1) {
errorMessage = i18n("Layer is locked");
} else {
errorMessage = i18n("Layer \"%1\" is locked", lockedNode->name());
}
m_d->view->showFloatingMessage(errorMessage, QIcon());
}
}
......@@ -1463,13 +1512,15 @@ void KisNodeManager::pasteLayersFromClipboard()
nodeInsertionAdapter());
}
void KisNodeManager::createQuickGroupImpl(KisNodeJugglerCompressed *juggler,
bool KisNodeManager::createQuickGroupImpl(KisNodeJugglerCompressed *juggler,
const QString &overrideGroupName,
KisNodeSP *newGroup,
KisNodeSP *newLastChild)
{
KisNodeSP active = activeNode();
if (!active) return;
if (!active) return false;
if (!canMoveLayer(active)) return false;
KisImageSP image = m_d->view->image();
QString groupName = !overrideGroupName.isEmpty() ? overrideGroupName : image->nextLayerName(i18n("Group"));
......@@ -1482,7 +1533,7 @@ void KisNodeManager::createQuickGroupImpl(KisNodeJugglerCompressed *juggler,
nodes2 = KisLayerUtils::sortMergableNodes(image->root(), selectedNodes());
KisLayerUtils::filterMergableNodes(nodes2);
if (nodes2.size() == 0) return;
if (nodes2.size() == 0) return false;
if (KisLayerUtils::checkIsChildOf(active, nodes2)) {
active = nodes2.first();
......@@ -1496,6 +1547,8 @@ void KisNodeManager::createQuickGroupImpl(KisNodeJugglerCompressed *juggler,
*newGroup = group;
*newLastChild = nodes2.last();
return true;
}
void KisNodeManager::createQuickGroup()
......@@ -1518,12 +1571,12 @@ void KisNodeManager::createQuickClippingGroup()
KisNodeSP above;
KisImageSP image = m_d->view->image();
createQuickGroupImpl(juggler, image->nextLayerName(i18nc("default name for a clipping group layer", "Clipping Group")), &parent, &above);
KisPaintLayerSP maskLayer = new KisPaintLayer(image.data(), i18nc("default name for quick clip group mask layer", "Mask Layer"), OPACITY_OPAQUE_U8, image->colorSpace());
maskLayer->disableAlphaChannel(true);
if (createQuickGroupImpl(juggler, image->nextLayerName(i18nc("default name for a clipping group layer", "Clipping Group")), &parent, &above)) {
KisPaintLayerSP maskLayer = new KisPaintLayer(image.data(), i18nc("default name for quick clip group mask layer", "Mask Layer"), OPACITY_OPAQUE_U8, image->colorSpace());
maskLayer->disableAlphaChannel(true);
juggler->addNode(KisNodeList() << maskLayer, parent, above);
juggler->addNode(KisNodeList() << maskLayer, parent, above);
}
}
void KisNodeManager::quickUngroup()
......@@ -1531,6 +1584,8 @@ void KisNodeManager::quickUngroup()
KisNodeSP active = activeNode();
if (!active) return;
if (!canModifyLayer(active)) return;
KisNodeSP parent = active->parent();
KisNodeSP aboveThis = active;
......
......@@ -115,6 +115,13 @@ public:
bool trySetNodeProperties(KisNodeSP node, KisImageSP image, KisBaseNode::PropertyList properties) const;
bool canModifyLayers(KisNodeList nodes, bool showWarning = true);
bool canModifyLayer(KisNodeSP node, bool showWarning = true);
bool canMoveLayers(KisNodeList nodes, bool showWarning = true);
bool canMoveLayer(KisNodeSP node, bool showWarning = true);
public Q_SLOTS:
/**
......@@ -257,7 +264,7 @@ private:
qint32 convertOpacityToInt(qreal opacity);
void removeSelectedNodes(KisNodeList selectedNodes);
void slotSomethingActivatedNodeImpl(KisNodeSP node);
void createQuickGroupImpl(KisNodeJugglerCompressed *juggler,
bool createQuickGroupImpl(KisNodeJugglerCompressed *juggler,
const QString &overrideGroupName,
KisNodeSP *newGroup,
KisNodeSP *newLastChild);
......
......@@ -658,12 +658,16 @@ QStringList KisNodeModel::mimeTypes() const
QMimeData * KisNodeModel::mimeData(const QModelIndexList &indexes) const
{
bool hasLockedLayer = false;
KisNodeList nodes;
Q_FOREACH (const QModelIndex &idx, indexes) {
nodes << nodeFromIndex(idx);
KisNodeSP node = nodeFromIndex(idx);
nodes << node;
hasLockedLayer |= !node->isEditable(false);
}
return KisMimeData::mimeForLayers(nodes, m_d->image);
return KisMimeData::mimeForLayers(nodes, m_d->image, hasLockedLayer);
}
bool KisNodeModel::dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent)
......@@ -728,7 +732,7 @@ void KisNodeModel::updateDropEnabled(const QList<KisNodeSP> &nodes, QModelIndex
bool dropEnabled = true;
Q_FOREACH (const KisNodeSP &node, nodes) {
if (!target->allowAsChild(node)) {
if (!target->allowAsChild(node) || !target->isEditable(false)) {
dropEnabled = false;
break;
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment