diff --git a/krita/image/kis_group_layer.cc b/krita/image/kis_group_layer.cc index e9eb77ec01..296a2b2c82 100644 --- a/krita/image/kis_group_layer.cc +++ b/krita/image/kis_group_layer.cc @@ -1,309 +1,341 @@ /* * Copyright (c) 2005 C. Boemann * Copyright (c) 2007 Boudewijn Rempt * Copyright (c) 2009 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_group_layer.h" #include #include #include #include #include "kis_types.h" #include "kis_node_visitor.h" #include "kis_processing_visitor.h" #include "kis_debug.h" #include "kis_image.h" #include "kis_paint_device.h" #include "kis_default_bounds.h" #include "kis_clone_layer.h" #include "kis_selection_mask.h" #include "kis_psd_layer_style.h" struct KisGroupLayer::Private { public: Private() : paintDevice(0) , x(0) - , y(0) { + , y(0) + , passThroughMode(false) + { } KisPaintDeviceSP paintDevice; qint32 x; qint32 y; + bool passThroughMode; }; KisGroupLayer::KisGroupLayer(KisImageWSP image, const QString &name, quint8 opacity) : KisLayer(image, name, opacity), m_d(new Private()) { resetCache(); } KisGroupLayer::KisGroupLayer(const KisGroupLayer &rhs) : KisLayer(rhs), m_d(new Private()) { m_d->paintDevice = new KisPaintDevice(*rhs.m_d->paintDevice.data()); m_d->x = rhs.m_d->x; m_d->y = rhs.m_d->y; m_d->paintDevice->setDefaultPixel(const_cast(&rhs)->m_d->paintDevice->defaultPixel()); } KisGroupLayer::~KisGroupLayer() { delete m_d; } bool KisGroupLayer::checkCloneLayer(KisCloneLayerSP clone) const { KisNodeSP source = clone->copyFrom(); if (source) { if(!allowAsChild(source)) return false; if (source->inherits("KisGroupLayer")) { KisNodeSP newParent = const_cast(this); while (newParent) { if (newParent == source) { return false; } newParent = newParent->parent(); } } } return true; } bool KisGroupLayer::checkNodeRecursively(KisNodeSP node) const { KisCloneLayerSP cloneLayer = dynamic_cast(node.data()); if(cloneLayer) { return checkCloneLayer(cloneLayer); } else if (node->inherits("KisGroupLayer")) { KisNodeSP child = node->firstChild(); while (child) { if (!checkNodeRecursively(child)) { return false; } child = child->nextSibling(); } } return true; } bool KisGroupLayer::allowAsChild(KisNodeSP node) const { return checkNodeRecursively(node) && (parent() || (node->inherits("KisSelectionMask") && !selectionMask()) || !node->inherits("KisMask")); } const KoColorSpace * KisGroupLayer::colorSpace() const { return m_d->paintDevice->colorSpace(); } QIcon KisGroupLayer::icon() const { return koIcon("folder"); } void KisGroupLayer::setImage(KisImageWSP image) { m_d->paintDevice->setDefaultBounds(new KisDefaultBounds(image)); KisLayer::setImage(image); } KisLayerSP KisGroupLayer::createMergedLayer(KisLayerSP prevLayer) { KisGroupLayer *prevGroup = dynamic_cast(prevLayer.data()); if (prevGroup) { KisSharedPtr merged(new KisGroupLayer(*prevGroup)); KisNodeSP child, cloned; for (child = firstChild(); child; child = child->nextSibling()) { cloned = child->clone(); image()->addNode(cloned, merged); } image()->refreshGraphAsync(merged); return merged; } else return KisLayer::createMergedLayer(prevLayer); } void KisGroupLayer::resetCache(const KoColorSpace *colorSpace) { if (!colorSpace) colorSpace = image()->colorSpace(); Q_ASSERT(colorSpace); if (!m_d->paintDevice) { m_d->paintDevice = new KisPaintDevice(this, colorSpace, new KisDefaultBounds(image())); m_d->paintDevice->setX(m_d->x); m_d->paintDevice->setY(m_d->y); } else if(!(*m_d->paintDevice->colorSpace() == *colorSpace)) { KisPaintDeviceSP dev = new KisPaintDevice(this, colorSpace, new KisDefaultBounds(image())); dev->setX(m_d->x); dev->setY(m_d->y); quint8* defaultPixel = new quint8[colorSpace->pixelSize()]; m_d->paintDevice->colorSpace()-> convertPixelsTo(m_d->paintDevice->defaultPixel(), defaultPixel, colorSpace, 1, KoColorConversionTransformation::InternalRenderingIntent, KoColorConversionTransformation::InternalConversionFlags); dev->setDefaultPixel(defaultPixel); delete[] defaultPixel; m_d->paintDevice = dev; } else { m_d->paintDevice->clear(); } } KisLayer* KisGroupLayer::onlyMeaningfulChild() const { KisNode *child = firstChild().data(); KisLayer *onlyLayer = 0; while (child) { KisLayer *layer = dynamic_cast(child); if (layer) { if (onlyLayer) return 0; onlyLayer = layer; } child = child->nextSibling().data(); } return onlyLayer; } KisPaintDeviceSP KisGroupLayer::tryObligeChild() const { const KisLayer *child = onlyMeaningfulChild(); if (child && child->channelFlags().isEmpty() && child->projection() && child->visible() && (child->compositeOpId() == COMPOSITE_OVER || child->compositeOpId() == COMPOSITE_ALPHA_DARKEN || child->compositeOpId() == COMPOSITE_COPY) && child->opacity() == OPACITY_OPAQUE_U8 && *child->projection()->colorSpace() == *colorSpace() && !child->layerStyle()) { quint8 defaultOpacity = m_d->paintDevice->colorSpace()->opacityU8( m_d->paintDevice->defaultPixel()); if(defaultOpacity == OPACITY_TRANSPARENT_U8) { return child->projection(); } } return 0; } KisPaintDeviceSP KisGroupLayer::original() const { /** * We are too lazy! Let's our children work for us. * Try to use children's paintDevice if it's the only * one in stack and meets some conditions */ KisPaintDeviceSP realOriginal = tryObligeChild(); if (!realOriginal) { if (!childCount() && !m_d->paintDevice->extent().isEmpty()) { m_d->paintDevice->clear(); } realOriginal = m_d->paintDevice; } return realOriginal; } void KisGroupLayer::setDefaultProjectionColor(KoColor color) { color.convertTo(m_d->paintDevice->colorSpace()); m_d->paintDevice->setDefaultPixel(color.data()); } KoColor KisGroupLayer::defaultProjectionColor() const { KoColor color(m_d->paintDevice->defaultPixel(), m_d->paintDevice->colorSpace()); return color; } +bool KisGroupLayer::passThroughMode() const +{ + return m_d->passThroughMode; +} + +void KisGroupLayer::setPassThroughMode(bool value) +{ + m_d->passThroughMode = value; +} + +KisDocumentSectionModel::PropertyList KisGroupLayer::sectionModelProperties() const +{ + KisDocumentSectionModel::PropertyList l = KisLayer::sectionModelProperties(); + // XXX: get right icons + l << KisDocumentSectionModel::Property(i18n("Pass Through"), koIcon("transparency-locked"), koIcon("transparency-unlocked"), passThroughMode()); + return l; +} + +void KisGroupLayer::setSectionModelProperties(const KisDocumentSectionModel::PropertyList &properties) +{ + foreach (const KisDocumentSectionModel::Property &property, properties) { + if (property.name == i18n("Pass Through")) { + setPassThroughMode(property.state.toBool()); + } + } + + KisLayer::setSectionModelProperties(properties); +} + bool KisGroupLayer::accept(KisNodeVisitor &v) { return v.visit(this); } void KisGroupLayer::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) { return visitor.visit(this, undoAdapter); } qint32 KisGroupLayer::x() const { return m_d->x; } qint32 KisGroupLayer::y() const { return m_d->y; } void KisGroupLayer::setX(qint32 x) { qint32 delta = x - m_d->x; m_d->x = x; if(m_d->paintDevice) { m_d->paintDevice->setX(m_d->paintDevice->x() + delta); Q_ASSERT(m_d->paintDevice->x() == m_d->x); } } void KisGroupLayer::setY(qint32 y) { qint32 delta = y - m_d->y; m_d->y = y; if(m_d->paintDevice) { m_d->paintDevice->setY(m_d->paintDevice->y() + delta); Q_ASSERT(m_d->paintDevice->y() == m_d->y); } } #include "kis_group_layer.moc" diff --git a/krita/image/kis_group_layer.h b/krita/image/kis_group_layer.h index 73bdcadb1f..e08c19829c 100644 --- a/krita/image/kis_group_layer.h +++ b/krita/image/kis_group_layer.h @@ -1,109 +1,115 @@ /* * Copyright (c) 2005 C. Boemann * Copyright (c) 2007 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_GROUP_LAYER_H_ #define KIS_GROUP_LAYER_H_ #include "kis_layer.h" #include "kis_types.h" class KoColorSpace; /** * A KisLayer that bundles child layers into a single layer. * The top layer is firstChild(), with index 0; the bottommost lastChild() with index childCount() - 1. * KisLayer::nextSibling() moves towards higher indices, from the top to the bottom layer; prevSibling() the reverse. * (Implementation detail: internally, the indices are reversed, for speed.) **/ class KRITAIMAGE_EXPORT KisGroupLayer : public KisLayer { Q_OBJECT public: KisGroupLayer(KisImageWSP image, const QString &name, quint8 opacity); KisGroupLayer(const KisGroupLayer& rhs); virtual ~KisGroupLayer(); KisNodeSP clone() const { return KisNodeSP(new KisGroupLayer(*this)); } bool allowAsChild(KisNodeSP) const; QIcon icon() const; + KisDocumentSectionModel::PropertyList sectionModelProperties() const; + void setSectionModelProperties(const KisDocumentSectionModel::PropertyList &properties); + virtual void setImage(KisImageWSP image); virtual KisLayerSP createMergedLayer(KisLayerSP prevLayer); /** * Clear the projection */ void resetCache(const KoColorSpace *colorSpace = 0); /** * XXX: make the colorspace of a layergroup user-settable: we want * to be able to have, for instance, a group of grayscale layers * resulting in a grayscale projection that is then merged with an * rgb image stack. */ const KoColorSpace * colorSpace() const; /// @return the projection of the layers in the group before the masks are applied. KisPaintDeviceSP original() const; qint32 x() const; qint32 y() const; void setX(qint32 x); void setY(qint32 y); /** Accect the specified visitor. @return true if the operation succeeded, false if it failed. */ bool accept(KisNodeVisitor &v); void accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter); /** * A special method that changes the default color of the * projection merged onto this group layer. Please note, that you * cannot use original()->setDefaultPixel(), because original() * device can be switched by tryOblidgeChild() mechanism randomly. */ void setDefaultProjectionColor(KoColor color); /** * \see setDefaultProjectionColor() */ KoColor defaultProjectionColor() const; + bool passThroughMode() const; + void setPassThroughMode(bool value); + protected: KisLayer* onlyMeaningfulChild() const; KisPaintDeviceSP tryObligeChild() const; private: bool checkCloneLayer(KisCloneLayerSP clone) const; bool checkNodeRecursively(KisNodeSP node) const; private: struct Private; Private * const m_d; }; #endif // KIS_GROUP_LAYER_H_ diff --git a/krita/image/kis_layer_projection_plane.cpp b/krita/image/kis_layer_projection_plane.cpp index 231a9eee9c..dafa5b0f09 100644 --- a/krita/image/kis_layer_projection_plane.cpp +++ b/krita/image/kis_layer_projection_plane.cpp @@ -1,113 +1,114 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_layer_projection_plane.h" #include #include #include #include #include "kis_painter.h" +#include "kis_projection_leaf.h" struct KisLayerProjectionPlane::Private { KisLayer *layer; }; KisLayerProjectionPlane::KisLayerProjectionPlane(KisLayer *layer) : m_d(new Private) { m_d->layer = layer; } KisLayerProjectionPlane::~KisLayerProjectionPlane() { } QRect KisLayerProjectionPlane::recalculate(const QRect& rect, KisNodeSP filthyNode) { return m_d->layer->updateProjection(rect, filthyNode); } void KisLayerProjectionPlane::apply(KisPainter *painter, const QRect &rect) { KisPaintDeviceSP device = m_d->layer->projection(); if (!device) return; QRect needRect = rect; if (m_d->layer->compositeOpId() != COMPOSITE_COPY) { needRect &= device->extent(); } if(needRect.isEmpty()) return; QBitArray channelFlags = m_d->layer->channelFlags(); // if the color spaces don't match we will have a problem with the channel flags // because the channel flags from the source layer doesn't match with the colorspace of the projection device // this leads to the situation that the wrong channels will be enabled/disabled const KoColorSpace* srcCS = device->colorSpace(); const KoColorSpace* dstCS = painter->device()->colorSpace(); if (!channelFlags.isEmpty() && srcCS != dstCS) { bool alphaFlagIsSet = (srcCS->channelFlags(false,true) & channelFlags) == srcCS->channelFlags(false,true); bool allColorFlagsAreSet = (srcCS->channelFlags(true,false) & channelFlags) == srcCS->channelFlags(true,false); bool allColorFlagsAreUnset = (srcCS->channelFlags(true,false) & channelFlags).count(true) == 0; if(allColorFlagsAreSet) { channelFlags = dstCS->channelFlags(true, alphaFlagIsSet); } else if(allColorFlagsAreUnset) { channelFlags = dstCS->channelFlags(false, alphaFlagIsSet); } else { //TODO: convert the cannel flags properly // for now just the alpha channel bit is copied and the other channels are left alone for (quint32 i=0; i < dstCS->channelCount(); ++i) { if (dstCS->channels()[i]->channelType() == KoChannelInfo::ALPHA) { channelFlags.setBit(i, alphaFlagIsSet); break; } } } } painter->setChannelFlags(channelFlags); painter->setCompositeOp(m_d->layer->compositeOp()); - painter->setOpacity(m_d->layer->opacity()); + painter->setOpacity(m_d->layer->projectionLeaf()->opacity()); painter->bitBlt(needRect.topLeft(), device, needRect); } QRect KisLayerProjectionPlane::needRect(const QRect &rect, KisLayer::PositionToFilthy pos) const { return m_d->layer->needRect(rect, pos); } QRect KisLayerProjectionPlane::changeRect(const QRect &rect, KisLayer::PositionToFilthy pos) const { return m_d->layer->changeRect(rect, pos); } QRect KisLayerProjectionPlane::accessRect(const QRect &rect, KisLayer::PositionToFilthy pos) const { return m_d->layer->accessRect(rect, pos); } diff --git a/krita/image/kis_projection_leaf.cpp b/krita/image/kis_projection_leaf.cpp index 4ca00752ef..71602ecb4b 100644 --- a/krita/image/kis_projection_leaf.cpp +++ b/krita/image/kis_projection_leaf.cpp @@ -1,140 +1,208 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_projection_leaf.h" +#include + #include "kis_layer.h" #include "kis_mask.h" #include "kis_group_layer.h" #include "kis_adjustment_layer.h" struct KisProjectionLeaf::Private { Private(KisNode *_node) : node(_node) {} KisNode* node; + + static bool checkPassThrough(const KisNode *node) { + const KisGroupLayer *group = qobject_cast(node); + return group && group->passThroughMode(); + } + + bool checkParentPassThrough() { + return node->parent() && checkPassThrough(node->parent()); + } + + bool checkThisPassThrough() { + return checkPassThrough(node); + } }; KisProjectionLeaf::KisProjectionLeaf(KisNode *node) : m_d(new Private(node)) { } KisProjectionLeaf::~KisProjectionLeaf() { } KisProjectionLeafSP KisProjectionLeaf::parent() const { KisNodeSP node = m_d->node->parent(); + + if (node && Private::checkPassThrough(node)) { + node = node->parent(); + } + return node ? node->projectionLeaf() : KisProjectionLeafSP(); } KisProjectionLeafSP KisProjectionLeaf::firstChild() const { - KisNodeSP node = m_d->node->firstChild(); + KisNodeSP node; + + if (!m_d->checkThisPassThrough()) { + node = m_d->node->firstChild(); + } + return node ? node->projectionLeaf() : KisProjectionLeafSP(); } KisProjectionLeafSP KisProjectionLeaf::lastChild() const { - KisNodeSP node = m_d->node->lastChild(); + KisNodeSP node; + + if (!m_d->checkThisPassThrough()) { + node = m_d->node->lastChild(); + } + return node ? node->projectionLeaf() : KisProjectionLeafSP(); } KisProjectionLeafSP KisProjectionLeaf::prevSibling() const { - KisNodeSP node = m_d->node->prevSibling(); + KisNodeSP node; + + if (m_d->checkThisPassThrough()) { + node = m_d->node->lastChild(); + } + + if (!node) { + node = m_d->node->prevSibling(); + } + + if (!node && m_d->checkParentPassThrough()) { + node = m_d->node->parent()->prevSibling(); + } + return node ? node->projectionLeaf() : KisProjectionLeafSP(); } KisProjectionLeafSP KisProjectionLeaf::nextSibling() const { KisNodeSP node = m_d->node->nextSibling(); + + if (node && Private::checkPassThrough(node) && node->firstChild()) { + node = node->firstChild(); + } + + if (!node && m_d->checkParentPassThrough()) { + node = m_d->node->parent(); + } + return node ? node->projectionLeaf() : KisProjectionLeafSP(); } int KisProjectionLeaf::childCount() const { return m_d->node->childCount(); } KisNodeSP KisProjectionLeaf::node() const { return m_d->node; } KisAbstractProjectionPlaneSP KisProjectionLeaf::projectionPlane() const { return m_d->node->projectionPlane(); } bool KisProjectionLeaf::accept(KisNodeVisitor &visitor) { return m_d->node->accept(visitor); } KisPaintDeviceSP KisProjectionLeaf::original() { return m_d->node->original(); } KisPaintDeviceSP KisProjectionLeaf::projection() { return m_d->node->projection(); } bool KisProjectionLeaf::isRoot() const { return (bool)!m_d->node->parent(); } bool KisProjectionLeaf::isLayer() const { return (bool)qobject_cast(m_d->node); } bool KisProjectionLeaf::isMask() const { return (bool)qobject_cast(m_d->node); } bool KisProjectionLeaf::canHaveChildLayers() const { return (bool)qobject_cast(m_d->node); } bool KisProjectionLeaf::dependsOnLowerNodes() const { return (bool)qobject_cast(m_d->node); } bool KisProjectionLeaf::visible() const { // check opacity as well! - return m_d->node->visible(); + return m_d->node->visible(true); +} + +quint8 KisProjectionLeaf::opacity() const +{ + quint8 resultOpacity = m_d->node->opacity(); + quint8 parentOpacity = 255; + + if (m_d->checkParentPassThrough()) { + quint8 parentOpacity = m_d->node->parent()->projectionLeaf()->opacity(); + + if (parentOpacity != OPACITY_OPAQUE_U8) { + resultOpacity = (int(resultOpacity) * parentOpacity) / OPACITY_OPAQUE_U8; + } + } + + return resultOpacity; } bool KisProjectionLeaf::isStillInGraph() const { return (bool)m_d->node->graphListener(); } diff --git a/krita/image/kis_projection_leaf.h b/krita/image/kis_projection_leaf.h index 07b6fec178..1a2c46faaa 100644 --- a/krita/image/kis_projection_leaf.h +++ b/krita/image/kis_projection_leaf.h @@ -1,66 +1,67 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_PROJECTION_LEAF_H #define __KIS_PROJECTION_LEAF_H #include #include "kis_types.h" #include "krita_export.h" class KisNodeVisitor; class KRITAIMAGE_EXPORT KisProjectionLeaf { public: KisProjectionLeaf(KisNode *node); virtual ~KisProjectionLeaf(); KisProjectionLeafSP parent() const; KisProjectionLeafSP firstChild() const; KisProjectionLeafSP lastChild() const; KisProjectionLeafSP prevSibling() const; KisProjectionLeafSP nextSibling() const; int childCount() const; KisNodeSP node() const; KisAbstractProjectionPlaneSP projectionPlane() const; bool accept(KisNodeVisitor &visitor); KisPaintDeviceSP original(); KisPaintDeviceSP projection(); bool isRoot() const; bool isLayer() const; bool isMask() const; bool canHaveChildLayers() const; bool dependsOnLowerNodes() const; bool visible() const; + quint8 opacity() const; bool isStillInGraph() const; private: struct Private; const QScopedPointer m_d; }; #endif /* __KIS_PROJECTION_LEAF_H */ diff --git a/krita/image/tests/kis_projection_leaf_test.cpp b/krita/image/tests/kis_projection_leaf_test.cpp index 4cb101c89d..592a703ea2 100644 --- a/krita/image/tests/kis_projection_leaf_test.cpp +++ b/krita/image/tests/kis_projection_leaf_test.cpp @@ -1,72 +1,164 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_projection_leaf_test.h" #include #include "qimage_based_test.h" #include "kis_projection_leaf.h" +#include "kis_group_layer.h" struct TestImage : TestUtil::QImageBasedTest { TestImage() : TestUtil::QImageBasedTest("") { undoStore = new KisSurrogateUndoStore(); image = createImage(undoStore); addGlobalSelection(image); } KisSurrogateUndoStore *undoStore; KisImageSP image; + + KisNodeSP findBlur1() { + return findNode(image->root(), "blur1"); + } + + KisNodeSP findClone1() { + return findNode(image->root(), "clone1"); + } + + KisNodeSP findPaint1() { + return findNode(image->root(), "paint1"); + } }; bool safeCompare(KisProjectionLeafSP leaf, KisNodeSP node) { return (!leaf && !node) || (leaf->node() == node); } void checkNode(KisNodeSP node, const QString &prefix) { qDebug() << prefix << node->name(); safeCompare(node->projectionLeaf()->parent(), node->parent()); safeCompare(node->projectionLeaf()->firstChild(), node->firstChild()); safeCompare(node->projectionLeaf()->lastChild(), node->lastChild()); safeCompare(node->projectionLeaf()->prevSibling(), node->prevSibling()); safeCompare(node->projectionLeaf()->nextSibling(), node->nextSibling()); QCOMPARE(node->projectionLeaf()->childCount(), (int)node->childCount()); QCOMPARE(node->projectionLeaf()->node(), node); KisNodeSP prevNode = node->lastChild(); while(prevNode) { checkNode(prevNode, QString("\"\"%1").arg(prefix)); prevNode = prevNode->prevSibling(); } } +void printNodes(KisNodeSP node, const QString &prefix = "") +{ + qDebug() << prefix << node->name(); + + KisNodeSP prevNode = node->lastChild(); + while(prevNode) { + printNodes(prevNode, QString("\"\"%1").arg(prefix)); + prevNode = prevNode->prevSibling(); + } +} + +void printLeafsBackward(KisProjectionLeafSP leaf, const QString &prefix = "") +{ + qDebug() << prefix << leaf->node()->name(); + + KisProjectionLeafSP prevLeaf = leaf->lastChild(); + while(prevLeaf) { + printLeafsBackward(prevLeaf, QString("\"\"%1").arg(prefix)); + prevLeaf = prevLeaf->prevSibling(); + } +} + +void printLeafsForward(KisProjectionLeafSP leaf, const QString &prefix = "") +{ + qDebug() << prefix << leaf->node()->name(); + + KisProjectionLeafSP prevLeaf = leaf->firstChild(); + while(prevLeaf) { + printLeafsForward(prevLeaf, QString("\"\"%1").arg(prefix)); + prevLeaf = prevLeaf->nextSibling(); + } +} + +void printParents(KisProjectionLeafSP leaf, const QString &prefix = "") +{ + qDebug() << prefix << leaf->node()->name(); + + + leaf = leaf->parent(); + if (leaf) { + printParents(leaf, QString("\"\"%1").arg(prefix)); + } +} + void KisProjectionLeafTest::test() { TestImage t; checkNode(t.image->root(), ""); } +void KisProjectionLeafTest::testPassThrough() +{ + TestImage t; + + KisGroupLayerSP group1 = new KisGroupLayer(t.image, "group1", OPACITY_OPAQUE_U8); + KisPaintLayerSP paint2 = new KisPaintLayer(t.image, "paint2", OPACITY_OPAQUE_U8); + KisPaintLayerSP paint3 = new KisPaintLayer(t.image, "paint3", OPACITY_OPAQUE_U8); + KisPaintLayerSP paint4 = new KisPaintLayer(t.image, "paint4", OPACITY_OPAQUE_U8); + + group1->setPassThroughMode(true); + + t.image->addNode(group1, t.image->root(), t.findBlur1()); + t.image->addNode(paint2, group1); + t.image->addNode(paint3, group1); + t.image->addNode(paint4, group1); + + //checkNode(t.image->root(), ""); + + qDebug() << "== Nodes"; + printNodes(t.image->root()); + qDebug() << "== Leafs backward"; + printLeafsBackward(t.image->root()->projectionLeaf()); + qDebug() << "== Leafs forward"; + printLeafsForward(t.image->root()->projectionLeaf()); + + qDebug() << "== Parents for paint4"; + printParents(paint4->projectionLeaf()); + + qDebug() << "== Parents for paint3"; + printParents(paint3->projectionLeaf()); + + qDebug() << "== Parents for group1"; + printParents(group1->projectionLeaf()); +} + QTEST_KDEMAIN(KisProjectionLeafTest, GUI) diff --git a/krita/image/tests/kis_projection_leaf_test.h b/krita/image/tests/kis_projection_leaf_test.h index 4e9eab4f32..2244fd412a 100644 --- a/krita/image/tests/kis_projection_leaf_test.h +++ b/krita/image/tests/kis_projection_leaf_test.h @@ -1,31 +1,32 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_PROJECTION_LEAF_TEST_H #define __KIS_PROJECTION_LEAF_TEST_H #include class KisProjectionLeafTest : public QObject { Q_OBJECT private slots: void test(); + void testPassThrough(); }; #endif /* __KIS_PROJECTION_LEAF_TEST_H */ diff --git a/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp b/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp index 61b94c39f4..08d7c4d1b2 100644 --- a/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp +++ b/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp @@ -1,790 +1,792 @@ /* * kis_layer_box.cc - part of Krita aka Krayon aka KimageShop * * Copyright (c) 2002 Patrick Julien * Copyright (C) 2006 Gábor Lehel * Copyright (C) 2007 Thomas Zander * Copyright (C) 2007 Boudewijn Rempt * Copyright (c) 2011 José Luis Vergara * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_layer_box.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_action.h" #include "kis_action_manager.h" #include "widgets/kis_cmb_composite.h" #include "widgets/kis_slider_spin_box.h" #include "KisViewManager.h" #include "kis_node_manager.h" #include "kis_node_model.h" #include "canvas/kis_canvas2.h" #include "KisDocument.h" #include "kis_dummies_facade_base.h" #include "kis_shape_controller.h" #include "kis_selection_mask.h" #include "kis_config.h" #include "KisView.h" #include "ui_wdglayerbox.h" class ButtonAction : public KisAction { public: ButtonAction(QAbstractButton* button, const KIcon& icon, const QString& text, QObject* parent) : KisAction(icon, text, parent) , m_button(button) { connect(m_button, SIGNAL(clicked()), this, SLOT(trigger())); } ButtonAction(QAbstractButton* button, QObject* parent) : KisAction(parent) , m_button(button) { connect(m_button, SIGNAL(clicked()), this, SLOT(trigger())); } virtual void setActionEnabled(bool enabled) { KisAction::setActionEnabled(enabled); m_button->setEnabled(enabled); } private: QAbstractButton* m_button; }; inline void KisLayerBox::connectActionToButton(KisViewManager* view, QAbstractButton *button, const QString &id) { Q_ASSERT(view); KisAction *action = view->actionManager()->actionByName(id); connect(button, SIGNAL(clicked()), action, SLOT(trigger())); connect(action, SIGNAL(sigEnableSlaves(bool)), button, SLOT(setEnabled(bool))); } inline void KisLayerBox::addActionToMenu(QMenu *menu, const QString &id) { Q_ASSERT(m_canvas); menu->addAction(m_canvas->viewManager()->actionManager()->actionByName(id)); } KisLayerBox::KisLayerBox() : QDockWidget(i18n("Layers")) , m_canvas(0) , m_wdgLayerBox(new Ui_WdgLayerBox) { KisConfig cfg; setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); QWidget* mainWidget = new QWidget(this); setWidget(mainWidget); m_opacityDelayTimer.setSingleShot(true); m_wdgLayerBox->setupUi(mainWidget); m_wdgLayerBox->listLayers->setDefaultDropAction(Qt::MoveAction); m_wdgLayerBox->listLayers->setSelectionMode(QAbstractItemView::ExtendedSelection); m_wdgLayerBox->listLayers->setVerticalScrollMode(QAbstractItemView::ScrollPerItem); m_wdgLayerBox->listLayers->setSelectionBehavior(QAbstractItemView::SelectRows); connect(m_wdgLayerBox->listLayers, SIGNAL(contextMenuRequested(const QPoint&, const QModelIndex&)), this, SLOT(slotContextMenuRequested(const QPoint&, const QModelIndex&))); connect(m_wdgLayerBox->listLayers, SIGNAL(collapsed(const QModelIndex&)), SLOT(slotCollapsed(const QModelIndex &))); connect(m_wdgLayerBox->listLayers, SIGNAL(expanded(const QModelIndex&)), SLOT(slotExpanded(const QModelIndex &))); connect(m_wdgLayerBox->listLayers, SIGNAL(selectionChanged(const QModelIndexList&)), SLOT(selectionChanged(const QModelIndexList&))); m_viewModeMenu = new KMenu(this); QActionGroup *group = new QActionGroup(this); QList actions; actions << m_viewModeMenu->addAction(koIcon("view-list-text"), i18n("Minimal View"), this, SLOT(slotMinimalView())); actions << m_viewModeMenu->addAction(koIcon("view-list-details"), i18n("Detailed View"), this, SLOT(slotDetailedView())); actions << m_viewModeMenu->addAction(koIcon("view-preview"), i18n("Thumbnail View"), this, SLOT(slotThumbnailView())); for (int i = 0, n = actions.count(); i < n; ++i) { actions[i]->setCheckable(true); actions[i]->setActionGroup(group); } m_wdgLayerBox->bnAdd->setIcon(themedIcon("addlayer")); m_wdgLayerBox->bnViewMode->setMenu(m_viewModeMenu); m_wdgLayerBox->bnViewMode->setPopupMode(QToolButton::InstantPopup); m_wdgLayerBox->bnViewMode->setIcon(koIcon("view-choose")); m_wdgLayerBox->bnViewMode->setText(i18n("View mode")); m_wdgLayerBox->bnDelete->setIcon(themedIcon("deletelayer")); m_wdgLayerBox->bnDelete->setIconSize(QSize(22, 22)); m_wdgLayerBox->bnRaise->setEnabled(false); m_wdgLayerBox->bnRaise->setIcon(themedIcon("arrowupblr")); m_wdgLayerBox->bnRaise->setIconSize(QSize(22, 22)); m_wdgLayerBox->bnLower->setEnabled(false); m_wdgLayerBox->bnLower->setIcon(themedIcon("arrowdown")); m_wdgLayerBox->bnLower->setIconSize(QSize(22, 22)); m_wdgLayerBox->bnLeft->setEnabled(true); m_wdgLayerBox->bnLeft->setIcon(themedIcon("removefromfolder")); m_wdgLayerBox->bnLeft->setIconSize(QSize(22, 22)); m_wdgLayerBox->bnRight->setEnabled(true); m_wdgLayerBox->bnRight->setIcon(themedIcon("addtofolder")); m_wdgLayerBox->bnRight->setIconSize(QSize(22, 22)); m_wdgLayerBox->bnProperties->setIcon(themedIcon("properties")); m_wdgLayerBox->bnProperties->setIconSize(QSize(22, 22)); m_wdgLayerBox->bnDuplicate->setIcon(themedIcon("duplicatelayer")); m_wdgLayerBox->bnDuplicate->setIconSize(QSize(22, 22)); m_removeAction = new ButtonAction(m_wdgLayerBox->bnDelete, themedIcon("deletelayer"), i18n("&Remove Layer"), this); m_removeAction->setActivationFlags(KisAction::ACTIVE_NODE); m_removeAction->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE); m_removeAction->setObjectName("remove_layer"); connect(m_removeAction, SIGNAL(triggered()), this, SLOT(slotRmClicked())); m_actions.append(m_removeAction); KisAction* action = new ButtonAction(m_wdgLayerBox->bnLeft, this); action->setText(i18n("Move Layer Left")); action->setActivationFlags(KisAction::ACTIVE_NODE); action->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE); action->setObjectName("move_layer_left"); connect(action, SIGNAL(triggered()), this, SLOT(slotLeftClicked())); m_actions.append(action); action = new ButtonAction(m_wdgLayerBox->bnRight, this); action->setText(i18n("Move Layer Right")); action->setActivationFlags(KisAction::ACTIVE_NODE); action->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE); action->setObjectName("move_layer_right"); connect(action, SIGNAL(triggered()), this, SLOT(slotRightClicked())); m_actions.append(action); m_propertiesAction = new ButtonAction(m_wdgLayerBox->bnProperties, themedIcon("properties"), i18n("&Properties..."),this); m_propertiesAction->setActivationFlags(KisAction::ACTIVE_NODE); m_propertiesAction->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE); m_propertiesAction->setObjectName("layer_properties"); connect(m_propertiesAction, SIGNAL(triggered()), this, SLOT(slotPropertiesClicked())); m_actions.append(m_propertiesAction); // NOTE: this is _not_ a mistake. The layerbox shows the layers in the reverse order connect(m_wdgLayerBox->bnRaise, SIGNAL(clicked()), SLOT(slotLowerClicked())); connect(m_wdgLayerBox->bnLower, SIGNAL(clicked()), SLOT(slotRaiseClicked())); // END NOTE if (cfg.sliderLabels()) { m_wdgLayerBox->opacityLabel->hide(); m_wdgLayerBox->doubleOpacity->setPrefix(QString("%1: ").arg(i18n("Opacity"))); } m_wdgLayerBox->doubleOpacity->setRange(0, 100, 0); m_wdgLayerBox->doubleOpacity->setSuffix("%"); connect(m_wdgLayerBox->doubleOpacity, SIGNAL(valueChanged(qreal)), SLOT(slotOpacitySliderMoved(qreal))); connect(&m_opacityDelayTimer, SIGNAL(timeout()), SLOT(slotOpacityChanged())); connect(m_wdgLayerBox->cmbComposite, SIGNAL(activated(int)), SLOT(slotCompositeOpChanged(int))); m_selectOpaque = new KisAction(i18n("&Select Opaque"), this); m_selectOpaque->setActivationFlags(KisAction::ACTIVE_DEVICE); m_selectOpaque->setActivationConditions(KisAction::SELECTION_EDITABLE); m_selectOpaque->setObjectName("select_opaque"); connect(m_selectOpaque, SIGNAL(triggered(bool)), this, SLOT(slotSelectOpaque())); m_actions.append(m_selectOpaque); m_newLayerMenu = new KMenu(this); m_wdgLayerBox->bnAdd->setMenu(m_newLayerMenu); m_wdgLayerBox->bnAdd->setPopupMode(QToolButton::MenuButtonPopup); m_nodeModel = new KisNodeModel(this); /** * Connect model updateUI() to enable/disable controls. * Note: nodeActivated() is connected separately in setImage(), because * it needs particular order of calls: first the connection to the * node manager should be called, then updateUI() */ connect(m_nodeModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), SLOT(updateUI())); connect(m_nodeModel, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), SLOT(updateUI())); connect(m_nodeModel, SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)), SLOT(updateUI())); connect(m_nodeModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), SLOT(updateUI())); connect(m_nodeModel, SIGNAL(modelReset()), SLOT(updateUI())); KisAction *showGlobalSelectionMask = new KisAction(i18n("&Show Global Selection Mask"), this); showGlobalSelectionMask->setObjectName("show-global-selection-mask"); showGlobalSelectionMask->setToolTip(i18nc("@info:tooltip", "Shows global selection as a usual selection mask in Layers docker")); showGlobalSelectionMask->setCheckable(true); connect(showGlobalSelectionMask, SIGNAL(triggered(bool)), SLOT(slotEditGlobalSelection(bool))); m_actions.append(showGlobalSelectionMask); showGlobalSelectionMask->setChecked(cfg.showGlobalSelection()); m_wdgLayerBox->listLayers->setModel(m_nodeModel); } KisLayerBox::~KisLayerBox() { delete m_wdgLayerBox; } void expandNodesRecursively(KisNodeSP root, QPointer nodeModel, KisDocumentSectionView *sectionView) { if (!root) return; if (nodeModel.isNull()) return; if (!sectionView) return; sectionView->blockSignals(true); KisNodeSP node = root->firstChild(); while (node) { QModelIndex idx = nodeModel->indexFromNode(node); if (idx.isValid()) { if (node->collapsed()) { sectionView->collapse(idx); } } if (node->childCount() > 0) { expandNodesRecursively(node, nodeModel, sectionView); } node = node->nextSibling(); } sectionView->blockSignals(false); } void KisLayerBox::setMainWindow(KisViewManager* kisview) { m_nodeManager = kisview->nodeManager(); foreach(KisAction *action, m_actions) { kisview->actionManager()-> addAction(action->objectName(), action); } connectActionToButton(kisview, m_wdgLayerBox->bnAdd, "add_new_paint_layer"); connectActionToButton(kisview, m_wdgLayerBox->bnDuplicate, "duplicatelayer"); } void KisLayerBox::setCanvas(KoCanvasBase *canvas) { setEnabled(canvas != 0); if (m_canvas) { m_canvas->disconnectCanvasObserver(this); m_nodeModel->setDummiesFacade(0, 0, 0); disconnect(m_image, 0, this, 0); disconnect(m_nodeManager, 0, this, 0); disconnect(m_nodeModel, 0, m_nodeManager, 0); disconnect(m_nodeModel, SIGNAL(nodeActivated(KisNodeSP)), this, SLOT(updateUI())); } m_canvas = dynamic_cast(canvas); if (m_canvas) { m_image = m_canvas->image(); connect(m_image, SIGNAL(sigImageUpdated(QRect)), SLOT(updateThumbnail())); KisDocument* doc = static_cast(m_canvas->imageView()->document()); KisShapeController *kritaShapeController = dynamic_cast(doc->shapeController()); KisDummiesFacadeBase *kritaDummiesFacade = static_cast(kritaShapeController); m_nodeModel->setDummiesFacade(kritaDummiesFacade, m_image, kritaShapeController); connect(m_image, SIGNAL(sigAboutToBeDeleted()), SLOT(notifyImageDeleted())); connect(m_image, SIGNAL(sigNodeCollapsedChanged()), SLOT(slotNodeCollapsedChanged())); // cold start if (m_nodeManager) { setCurrentNode(m_nodeManager->activeNode()); } else { setCurrentNode(m_canvas->imageView()->currentNode()); } // Connection KisNodeManager -> KisLayerBox connect(m_nodeManager, SIGNAL(sigUiNeedChangeActiveNode(KisNodeSP)), this, SLOT(setCurrentNode(KisNodeSP))); // Connection KisLayerBox -> KisNodeManager // The order of these connections is important! See comment in the ctor connect(m_nodeModel, SIGNAL(nodeActivated(KisNodeSP)), m_nodeManager, SLOT(slotUiActivatedNode(KisNodeSP))); connect(m_nodeModel, SIGNAL(nodeActivated(KisNodeSP)), SLOT(updateUI())); // Connection KisLayerBox -> KisNodeManager (isolate layer) connect(m_nodeModel, SIGNAL(toggleIsolateActiveNode()), m_nodeManager, SLOT(toggleIsolateActiveNode())); // Node manipulation methods are forwarded to the node manager connect(m_nodeModel, SIGNAL(requestAddNode(KisNodeSP, KisNodeSP, KisNodeSP)), m_nodeManager, SLOT(addNodeDirect(KisNodeSP, KisNodeSP, KisNodeSP))); connect(m_nodeModel, SIGNAL(requestMoveNode(KisNodeSP, KisNodeSP, KisNodeSP)), m_nodeManager, SLOT(moveNodeDirect(KisNodeSP, KisNodeSP, KisNodeSP))); m_wdgLayerBox->listLayers->expandAll(); expandNodesRecursively(m_image->rootLayer(), m_nodeModel, m_wdgLayerBox->listLayers); m_wdgLayerBox->listLayers->scrollTo(m_wdgLayerBox->listLayers->currentIndex()); addActionToMenu(m_newLayerMenu, "add_new_paint_layer"); addActionToMenu(m_newLayerMenu, "add_new_group_layer"); addActionToMenu(m_newLayerMenu, "add_new_clone_layer"); addActionToMenu(m_newLayerMenu, "add_new_shape_layer"); addActionToMenu(m_newLayerMenu, "add_new_adjustment_layer"); addActionToMenu(m_newLayerMenu, "add_new_fill_layer"); addActionToMenu(m_newLayerMenu, "add_new_file_layer"); m_newLayerMenu->addSeparator(); addActionToMenu(m_newLayerMenu, "add_new_transparency_mask"); addActionToMenu(m_newLayerMenu, "add_new_filter_mask"); addActionToMenu(m_newLayerMenu, "add_new_transform_mask"); addActionToMenu(m_newLayerMenu, "add_new_selection_mask"); } } void KisLayerBox::unsetCanvas() { setEnabled(false); if (m_canvas) { m_newLayerMenu->clear(); } setCanvas(0); m_nodeManager->setSelectedNodes(QList()); } void KisLayerBox::notifyImageDeleted() { setCanvas(0); m_nodeManager->setSelectedNodes(QList()); } void KisLayerBox::updateUI() { if (!m_canvas) return; if (!m_nodeManager) return; KisNodeSP activeNode = m_nodeManager->activeNode(); m_wdgLayerBox->bnRaise->setEnabled(activeNode && activeNode->isEditable(false) && (activeNode->nextSibling() || (activeNode->parent() && activeNode->parent() != m_image->root()))); m_wdgLayerBox->bnLower->setEnabled(activeNode && activeNode->isEditable(false) && (activeNode->prevSibling() || (activeNode->parent() && activeNode->parent() != m_image->root()))); m_wdgLayerBox->doubleOpacity->setEnabled(activeNode && activeNode->isEditable(false)); m_wdgLayerBox->cmbComposite->setEnabled(activeNode && activeNode->isEditable(false)); if (activeNode) { if (m_nodeManager->activePaintDevice()) { slotFillCompositeOps(m_nodeManager->activeColorSpace()); } else { slotFillCompositeOps(m_image->colorSpace()); } if (activeNode->inherits("KisMask")) { m_wdgLayerBox->cmbComposite->setEnabled(false); m_wdgLayerBox->doubleOpacity->setEnabled(false); - } - - if (activeNode->inherits("KisLayer")) { - m_wdgLayerBox->cmbComposite->setEnabled(true); + } else if (activeNode->inherits("KisLayer")) { m_wdgLayerBox->doubleOpacity->setEnabled(true); KisLayerSP l = qobject_cast(activeNode.data()); slotSetOpacity(l->opacity() * 100.0 / 255); const KoCompositeOp* compositeOp = l->compositeOp(); if (compositeOp) { slotSetCompositeOp(compositeOp); } else { m_wdgLayerBox->cmbComposite->setEnabled(false); } + + const KisGroupLayer *group = qobject_cast(activeNode.data()); + bool compositeSelectionActive = !(group && group->passThroughMode()); + + m_wdgLayerBox->cmbComposite->setEnabled(compositeSelectionActive); } } } /** * This method is callen *only* when non-GUI code requested the * change of the current node */ void KisLayerBox::setCurrentNode(KisNodeSP node) { QModelIndex index = node ? m_nodeModel->indexFromNode(node) : QModelIndex(); m_wdgLayerBox->listLayers->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); updateUI(); } void KisLayerBox::slotSetCompositeOp(const KoCompositeOp* compositeOp) { KoID opId = KoCompositeOpRegistry::instance().getKoID(compositeOp->id()); m_wdgLayerBox->cmbComposite->blockSignals(true); m_wdgLayerBox->cmbComposite->selectCompositeOp(opId); m_wdgLayerBox->cmbComposite->blockSignals(false); } void KisLayerBox::slotFillCompositeOps(const KoColorSpace* colorSpace) { m_wdgLayerBox->cmbComposite->validate(colorSpace); } // range: 0-100 void KisLayerBox::slotSetOpacity(double opacity) { Q_ASSERT(opacity >= 0 && opacity <= 100); m_wdgLayerBox->doubleOpacity->blockSignals(true); m_wdgLayerBox->doubleOpacity->setValue(opacity); m_wdgLayerBox->doubleOpacity->blockSignals(false); } void KisLayerBox::slotContextMenuRequested(const QPoint &pos, const QModelIndex &index) { QMenu menu; if (index.isValid()) { menu.addAction(m_propertiesAction); menu.addSeparator(); menu.addAction(m_removeAction); addActionToMenu(&menu, "duplicatelayer"); addActionToMenu(&menu, "flatten_image"); addActionToMenu(&menu, "flatten_layer"); // TODO: missing icon "edit-merge" QAction* mergeLayerDown = menu.addAction(i18n("&Merge with Layer Below"), this, SLOT(slotMergeLayer())); if (!index.sibling(index.row() + 1, 0).isValid()) mergeLayerDown->setEnabled(false); menu.addSeparator(); QMenu *convertToMenu = menu.addMenu(i18n("&Convert")); addActionToMenu(convertToMenu, "convert_to_paint_layer"); addActionToMenu(convertToMenu, "convert_to_transparency_mask"); addActionToMenu(convertToMenu, "convert_to_filter_mask"); addActionToMenu(convertToMenu, "convert_to_transform_mask"); addActionToMenu(convertToMenu, "convert_to_selection_mask"); QMenu *splitAlphaMenu = menu.addMenu(i18n("S&plit Alpha")); addActionToMenu(splitAlphaMenu, "split_alpha_into_mask"); addActionToMenu(splitAlphaMenu, "split_alpha_write"); addActionToMenu(splitAlphaMenu, "split_alpha_save_merged"); addActionToMenu(&menu, "isolate_layer"); } menu.addSeparator(); addActionToMenu(&menu, "add_new_transparency_mask"); addActionToMenu(&menu, "add_new_filter_mask"); addActionToMenu(&menu, "add_new_transform_mask"); addActionToMenu(&menu, "add_new_selection_mask"); menu.addSeparator(); menu.addAction(m_selectOpaque); menu.exec(pos); } void KisLayerBox::slotMergeLayer() { if (!m_canvas) return; m_nodeManager->mergeLayerDown(); } void KisLayerBox::slotMinimalView() { m_wdgLayerBox->listLayers->setDisplayMode(KisDocumentSectionView::MinimalMode); } void KisLayerBox::slotDetailedView() { m_wdgLayerBox->listLayers->setDisplayMode(KisDocumentSectionView::DetailedMode); } void KisLayerBox::slotThumbnailView() { m_wdgLayerBox->listLayers->setDisplayMode(KisDocumentSectionView::ThumbnailMode); } void KisLayerBox::slotRmClicked() { if (!m_canvas) return; m_nodeManager->removeNode(); } void KisLayerBox::slotRaiseClicked() { if (!m_canvas) return; KisNodeSP node = m_nodeManager->activeNode(); KisNodeSP parent = node->parent(); KisNodeSP grandParent = parent->parent(); if (!m_nodeManager->activeNode()->prevSibling()) { if (!grandParent) return; if (!grandParent->parent() && node->inherits("KisMask")) return; m_nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent)); } else { m_nodeManager->raiseNode(); } } void KisLayerBox::slotLowerClicked() { if (!m_canvas) return; KisNodeSP node = m_nodeManager->activeNode(); KisNodeSP parent = node->parent(); KisNodeSP grandParent = parent->parent(); if (!m_nodeManager->activeNode()->nextSibling()) { if (!grandParent) return; if (!grandParent->parent() && node->inherits("KisMask")) return; m_nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent) + 1); } else { m_nodeManager->lowerNode(); } } void KisLayerBox::slotLeftClicked() { if (!m_canvas) return; foreach(KisNodeSP node, m_nodeManager->selectedNodes()) { KisNodeSP parent = node->parent(); KisNodeSP grandParent = parent->parent(); quint16 nodeIndex = parent->index(node); if (!grandParent) continue; if (!grandParent->parent() && node->inherits("KisMask")) continue; if (nodeIndex <= parent->childCount() / 2) { m_nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent)); } else { m_nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent) + 1); } } } void KisLayerBox::slotRightClicked() { if (!m_canvas) return; foreach(KisNodeSP node, m_nodeManager->selectedNodes()) { KisNodeSP parent = m_nodeManager->activeNode()->parent(); KisNodeSP newParent; int nodeIndex = parent->index(node); int indexAbove = nodeIndex + 1; int indexBelow = nodeIndex - 1; if (parent->at(indexBelow) && parent->at(indexBelow)->allowAsChild(node)) { newParent = parent->at(indexBelow); m_nodeManager->moveNodeAt(node, newParent, newParent->childCount()); } else if (parent->at(indexAbove) && parent->at(indexAbove)->allowAsChild(node)) { newParent = parent->at(indexAbove); m_nodeManager->moveNodeAt(node, newParent, 0); } } } void KisLayerBox::slotPropertiesClicked() { if (!m_canvas) return; if (KisNodeSP active = m_nodeManager->activeNode()) { m_nodeManager->nodeProperties(active); } } void KisLayerBox::slotCompositeOpChanged(int index) { Q_UNUSED(index); if (!m_canvas) return; QString compositeOp = m_wdgLayerBox->cmbComposite->selectedCompositeOp().id(); m_nodeManager->nodeCompositeOpChanged(m_nodeManager->activeColorSpace()->compositeOp(compositeOp)); } void KisLayerBox::slotOpacityChanged() { if (!m_canvas) return; m_nodeManager->nodeOpacityChanged(m_newOpacity, true); } void KisLayerBox::slotOpacitySliderMoved(qreal opacity) { m_newOpacity = opacity; m_opacityDelayTimer.start(200); } void KisLayerBox::slotCollapsed(const QModelIndex &index) { KisNodeSP node = m_nodeModel->nodeFromIndex(index); if (node) { node->setCollapsed(true); } } void KisLayerBox::slotExpanded(const QModelIndex &index) { KisNodeSP node = m_nodeModel->nodeFromIndex(index); if (node) { node->setCollapsed(false); } } void KisLayerBox::slotSelectOpaque() { if (!m_canvas) return; QAction *action = m_canvas->viewManager()->actionManager()->actionByName("selectopaque"); if (action) { action->trigger(); } } void KisLayerBox::slotNodeCollapsedChanged() { m_wdgLayerBox->listLayers->expandAll(); expandNodesRecursively(m_image->rootLayer(), m_nodeModel, m_wdgLayerBox->listLayers); } inline bool isSelectionMask(KisNodeSP node) { return dynamic_cast(node.data()); } KisNodeSP KisLayerBox::findNonHidableNode(KisNodeSP startNode) { if (isSelectionMask(startNode) && startNode->parent() && !startNode->parent()->parent()) { KisNodeSP node = startNode->prevSibling(); while (node && isSelectionMask(node)) { node = node->prevSibling(); } if (!node) { node = startNode->nextSibling(); while (node && isSelectionMask(node)) { node = node->nextSibling(); } } if (!node) { node = m_image->root()->lastChild(); while (node && isSelectionMask(node)) { node = node->prevSibling(); } } KIS_ASSERT_RECOVER_NOOP(node && "cannot activate any node!"); startNode = node; } return startNode; } void KisLayerBox::slotEditGlobalSelection(bool showSelections) { KisNodeSP lastActiveNode = m_nodeManager->activeNode(); KisNodeSP activateNode = lastActiveNode; if (!showSelections) { activateNode = findNonHidableNode(activateNode); } m_nodeModel->setShowGlobalSelection(showSelections); if (showSelections) { KisNodeSP newMask = m_image->rootLayer()->selectionMask(); if (newMask) { activateNode = newMask; } } if (activateNode) { if (lastActiveNode != activateNode) { m_nodeManager->slotNonUiActivatedNode(activateNode); } else { setCurrentNode(lastActiveNode); } } } void KisLayerBox::selectionChanged(const QModelIndexList selection) { if (!m_nodeManager) return; if (selection.isEmpty() && m_nodeManager->activeNode()) { m_wdgLayerBox->listLayers->selectionModel()-> setCurrentIndex( m_nodeModel->indexFromNode(m_nodeManager->activeNode()), QItemSelectionModel::ClearAndSelect); return; } QList selectedNodes; foreach(const QModelIndex &idx, selection) { selectedNodes << m_nodeModel->nodeFromIndex(idx); } m_nodeManager->setSelectedNodes(selectedNodes); updateUI(); } void KisLayerBox::updateThumbnail() { m_wdgLayerBox->listLayers->updateNode(m_wdgLayerBox->listLayers->currentIndex()); } #include "kis_layer_box.moc" diff --git a/krita/ui/kra/kis_kra_loader.cpp b/krita/ui/kra/kis_kra_loader.cpp index b0122cac4a..35bd6211dd 100644 --- a/krita/ui/kra/kis_kra_loader.cpp +++ b/krita/ui/kra/kis_kra_loader.cpp @@ -1,923 +1,932 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2007 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kra/kis_kra_loader.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_resource_server_provider.h" #include "KisDocument.h" #include "kis_config.h" #include "kis_kra_tags.h" #include "kis_kra_utils.h" #include "kis_kra_load_visitor.h" /* Color model id comparison through the ages: 2.4 2.5 2.6 ideal ALPHA ALPHA ALPHA ALPHAU8 CMYK CMYK CMYK CMYKAU8 CMYKAF32 CMYKAF32 CMYKA16 CMYKAU16 CMYKAU16 GRAYA GRAYA GRAYA GRAYAU8 GrayF32 GRAYAF32 GRAYAF32 GRAYA16 GRAYAU16 GRAYAU16 LABA LABA LABA LABAU16 LABAF32 LABAF32 LABAU8 LABAU8 RGBA RGBA RGBA RGBAU8 RGBA16 RGBA16 RGBA16 RGBAU16 RgbAF32 RGBAF32 RGBAF32 RgbAF16 RgbAF16 RGBAF16 XYZA16 XYZA16 XYZA16 XYZAU16 XYZA8 XYZA8 XYZAU8 XyzAF16 XyzAF16 XYZAF16 XyzAF32 XYZAF32 XYZAF32 YCbCrA YCBCRA8 YCBCRA8 YCBCRAU8 YCbCrAU16 YCBCRAU16 YCBCRAU16 YCBCRF32 YCBCRF32 */ using namespace KRA; struct KisKraLoader::Private { public: KisDocument* document; QString imageName; // used to be stored in the image, is now in the documentInfo block QString imageComment; // used to be stored in the image, is now in the documentInfo block QMap layerFilenames; // temp storage during loading int syntaxVersion; // version of the fileformat we are loading vKisNodeSP selectedNodes; // the nodes that were active when saving the document. QMap assistantsFilenames; QList assistants; QStringList errorMessages; }; void convertColorSpaceNames(QString &colorspacename, QString &profileProductName) { if (colorspacename == "Grayscale + Alpha") { colorspacename = "GRAYA"; profileProductName.clear(); } else if (colorspacename == "RgbAF32") { colorspacename = "RGBAF32"; profileProductName.clear(); } else if (colorspacename == "RgbAF16") { colorspacename = "RGBAF16"; profileProductName.clear(); } else if (colorspacename == "CMYKA16") { colorspacename = "CMYKAU16"; } else if (colorspacename == "GrayF32") { colorspacename = "GRAYAF32"; profileProductName.clear(); } else if (colorspacename == "GRAYA16") { colorspacename = "GRAYAU16"; } else if (colorspacename == "XyzAF16") { colorspacename = "XYZAF16"; profileProductName.clear(); } else if (colorspacename == "XyzAF32") { colorspacename = "XYZAF32"; profileProductName.clear(); } else if (colorspacename == "YCbCrA") { colorspacename = "YCBCRA8"; } else if (colorspacename == "YCbCrAU16") { colorspacename = "YCBCRAU16"; } } KisKraLoader::KisKraLoader(KisDocument * document, int syntaxVersion) : m_d(new Private()) { m_d->document = document; m_d->syntaxVersion = syntaxVersion; } KisKraLoader::~KisKraLoader() { delete m_d; } KisImageWSP KisKraLoader::loadXML(const KoXmlElement& element) { QString attr; KisImageWSP image = 0; QString name; qint32 width; qint32 height; QString profileProductName; double xres; double yres; QString colorspacename; const KoColorSpace * cs; if ((attr = element.attribute(MIME)) == NATIVE_MIMETYPE) { if ((m_d->imageName = element.attribute(NAME)).isNull()) { m_d->errorMessages << i18n("Image does not have a name."); return KisImageWSP(0); } if ((attr = element.attribute(WIDTH)).isNull()) { m_d->errorMessages << i18n("Image does not specify a width."); return KisImageWSP(0); } width = attr.toInt(); if ((attr = element.attribute(HEIGHT)).isNull()) { m_d->errorMessages << i18n("Image does not specify a height."); return KisImageWSP(0); } height = attr.toInt(); m_d->imageComment = element.attribute(DESCRIPTION); xres = 100.0 / 72.0; if (!(attr = element.attribute(X_RESOLUTION)).isNull()) { if (attr.toDouble() > 1.0) { xres = attr.toDouble() / 72.0; } } yres = 100.0; if (!(attr = element.attribute(Y_RESOLUTION)).isNull()) { if (attr.toDouble() > 1.0) { yres = attr.toDouble() / 72.0; } } if ((colorspacename = element.attribute(COLORSPACE_NAME)).isNull()) { // An old file: take a reasonable default. // Krita didn't support anything else in those // days anyway. colorspacename = "RGBA"; } profileProductName = element.attribute(PROFILE); // A hack for an old colorspacename convertColorSpaceNames(colorspacename, profileProductName); QString colorspaceModel = KoColorSpaceRegistry::instance()->colorSpaceColorModelId(colorspacename).id(); QString colorspaceDepth = KoColorSpaceRegistry::instance()->colorSpaceColorDepthId(colorspacename).id(); if (profileProductName.isNull()) { // no mention of profile so get default profile"; cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, ""); } else { cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, profileProductName); } if (cs == 0) { // try once more without the profile cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, ""); if (cs == 0) { m_d->errorMessages << i18n("Image specifies an unsupported color model: %1.", colorspacename); return KisImageWSP(0); } } if (m_d->document) { image = new KisImage(m_d->document->createUndoStore(), width, height, cs, name); } else { image = new KisImage(0, width, height, cs, name); } image->setResolution(xres, yres); loadNodes(element, image, const_cast(image->rootLayer().data())); KoXmlNode child; for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) { KoXmlElement e = child.toElement(); if(e.tagName() == "ProjectionBackgroundColor") { if (e.hasAttribute("ColorData")) { QByteArray colorData = QByteArray::fromBase64(e.attribute("ColorData").toLatin1()); KoColor color((const quint8*)colorData.data(), image->colorSpace()); image->setDefaultProjectionColor(color); } } } for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) { KoXmlElement e = child.toElement(); if(e.tagName() == "compositions") { loadCompositions(e, image); } } } KoXmlNode child; for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) { KoXmlElement e = child.toElement(); if (e.tagName() == "assistants") { loadAssistantsList(e); } } return image; } void KisKraLoader::loadBinaryData(KoStore * store, KisImageWSP image, const QString & uri, bool external) { // icc profile: if present, this overrides the profile product name loaded in loadXML. QString location = external ? QString() : uri; location += m_d->imageName + ICC_PATH; if (store->hasFile(location)) { if (store->open(location)) { QByteArray data; data.resize(store->size()); bool res = (store->read(data.data(), store->size()) > -1); store->close(); if (res) { const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(image->colorSpace()->colorModelId().id(), image->colorSpace()->colorDepthId().id(), data); if (profile && profile->valid()) { res = image->assignImageProfile(profile); } if (!res) { profile = KoColorSpaceRegistry::instance()->profileByName(KoColorSpaceRegistry::instance()->colorSpaceFactory(image->colorSpace()->id())->defaultProfile()); Q_ASSERT(profile && profile->valid()); image->assignImageProfile(profile); } } } } // Load the layers data: if there is a profile associated with a layer it will be set now. KisKraLoadVisitor visitor(image, store, m_d->layerFilenames, m_d->imageName, m_d->syntaxVersion); if (external) { visitor.setExternalUri(uri); } image->rootLayer()->accept(visitor); if (!visitor.errorMessages().isEmpty()) { m_d->errorMessages.append(visitor.errorMessages()); } // annotations // exif location = external ? QString() : uri; location += m_d->imageName + EXIF_PATH; if (store->hasFile(location)) { QByteArray data; store->open(location); data = store->read(store->size()); store->close(); image->addAnnotation(KisAnnotationSP(new KisAnnotation("exif", "", data))); } // layer styles location = external ? QString() : uri; location += m_d->imageName + LAYER_STYLES_PATH; if (store->hasFile(location)) { KisPSDLayerStyleCollectionResource *collection = new KisPSDLayerStyleCollectionResource("Embedded Styles.asl"); collection->setName(i18nc("Auto-generated layer style collection name for embedded styles (collection)", "<%1> (embedded)", m_d->imageName)); KIS_ASSERT_RECOVER_NOOP(!collection->valid()); store->open(location); { KoStoreDevice device(store); device.open(QIODevice::ReadOnly); /** * ASL loading code cannot work with non-sequential IO devices, * so convert the device beforehand! */ QByteArray buf = device.readAll(); QBuffer raDevice(&buf); raDevice.open(QIODevice::ReadOnly); collection->loadFromDevice(&raDevice); } store->close(); if (collection->valid()) { KoResourceServer *server = KisResourceServerProvider::instance()->layerStyleCollectionServer(); server->addResource(collection, false); collection->assignAllLayerStyles(image->root()); } else { qWarning() << "WARNING: Couldn't load layer styles library from .kra!"; delete collection; } } if (m_d->document && m_d->document->documentInfo()->aboutInfo("title").isNull()) m_d->document->documentInfo()->setAboutInfo("title", m_d->imageName); if (m_d->document && m_d->document->documentInfo()->aboutInfo("comment").isNull()) m_d->document->documentInfo()->setAboutInfo("comment", m_d->imageComment); loadAssistants(store, uri, external); } vKisNodeSP KisKraLoader::selectedNodes() const { return m_d->selectedNodes; } QList KisKraLoader::assistants() const { return m_d->assistants; } QStringList KisKraLoader::errorMessages() const { return m_d->errorMessages; } void KisKraLoader::loadAssistants(KoStore *store, const QString &uri, bool external) { QString file_path; QString location; QMap handleMap; KisPaintingAssistant* assistant = 0; QMap::const_iterator loadedAssistant = m_d->assistantsFilenames.constBegin(); while (loadedAssistant != m_d->assistantsFilenames.constEnd()){ const KisPaintingAssistantFactory* factory = KisPaintingAssistantFactoryRegistry::instance()->get(loadedAssistant.value()); if (factory) { assistant = factory->createPaintingAssistant(); location = external ? QString() : uri; location += m_d->imageName + ASSISTANTS_PATH; file_path = location + loadedAssistant.key(); assistant->loadXml(store, handleMap, file_path); m_d->assistants.append(assistant); } loadedAssistant++; } } KisNodeSP KisKraLoader::loadNodes(const KoXmlElement& element, KisImageWSP image, KisNodeSP parent) { KoXmlNode node = element.firstChild(); KoXmlNode child; if (!node.isNull()) { if (node.isElement()) { if (node.nodeName().toUpper() == LAYERS.toUpper() || node.nodeName().toUpper() == MASKS.toUpper()) { for (child = node.lastChild(); !child.isNull(); child = child.previousSibling()) { KisNodeSP node = loadNode(child.toElement(), image, parent); if (node) { image->nextLayerName(); // Make sure the nameserver is current with the number of nodes. image->addNode(node, parent); if (node->inherits("KisLayer") && child.childNodesCount() > 0) { loadNodes(child.toElement(), image, node); } } } } } } return parent; } KisNodeSP KisKraLoader::loadNode(const KoXmlElement& element, KisImageWSP image, KisNodeSP parent) { // Nota bene: If you add new properties to layers, you should // ALWAYS define a default value in case the property is not // present in the layer definition: this helps a LOT with backward // compatibility. QString name = element.attribute(NAME, "No Name"); QUuid id = QUuid(element.attribute(UUID, QUuid().toString())); qint32 x = element.attribute(X, "0").toInt(); qint32 y = element.attribute(Y, "0").toInt(); qint32 opacity = element.attribute(OPACITY, QString::number(OPACITY_OPAQUE_U8)).toInt(); if (opacity < OPACITY_TRANSPARENT_U8) opacity = OPACITY_TRANSPARENT_U8; if (opacity > OPACITY_OPAQUE_U8) opacity = OPACITY_OPAQUE_U8; const KoColorSpace* colorSpace = 0; if ((element.attribute(COLORSPACE_NAME)).isNull()) { dbgFile << "No attribute color space for layer: " << name; colorSpace = image->colorSpace(); } else { QString colorspacename = element.attribute(COLORSPACE_NAME); QString profileProductName; convertColorSpaceNames(colorspacename, profileProductName); QString colorspaceModel = KoColorSpaceRegistry::instance()->colorSpaceColorModelId(colorspacename).id(); QString colorspaceDepth = KoColorSpaceRegistry::instance()->colorSpaceColorDepthId(colorspacename).id(); dbgFile << "Searching color space: " << colorspacename << colorspaceModel << colorspaceDepth << " for layer: " << name; // use default profile - it will be replaced later in completeLoading colorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, ""); dbgFile << "found colorspace" << colorSpace; if (!colorSpace) { m_d->errorMessages << i18n("Layer %1 specifies an unsupported color model: %2.", name, colorspacename); return 0; } } bool visible = element.attribute(VISIBLE, "1") == "0" ? false : true; bool locked = element.attribute(LOCKED, "0") == "0" ? false : true; bool collapsed = element.attribute(COLLAPSED, "0") == "0" ? false : true; // Now find out the layer type and do specific handling QString nodeType; if (m_d->syntaxVersion == 1) { nodeType = element.attribute("layertype"); if (nodeType.isEmpty()) { nodeType = PAINT_LAYER; } } else { nodeType = element.attribute(NODE_TYPE); } if (nodeType.isEmpty()) { m_d->errorMessages << i18n("Layer %1 has an unsupported type.", name); return 0; } KisNodeSP node = 0; if (nodeType == PAINT_LAYER) node = loadPaintLayer(element, image, name, colorSpace, opacity); else if (nodeType == GROUP_LAYER) node = loadGroupLayer(element, image, name, colorSpace, opacity); else if (nodeType == ADJUSTMENT_LAYER) node = loadAdjustmentLayer(element, image, name, colorSpace, opacity); else if (nodeType == SHAPE_LAYER) node = loadShapeLayer(element, image, name, colorSpace, opacity); else if (nodeType == GENERATOR_LAYER) node = loadGeneratorLayer(element, image, name, colorSpace, opacity); else if (nodeType == CLONE_LAYER) node = loadCloneLayer(element, image, name, colorSpace, opacity); else if (nodeType == FILTER_MASK) node = loadFilterMask(element, parent); else if (nodeType == TRANSFORM_MASK) node = loadTransformMask(element, parent); else if (nodeType == TRANSPARENCY_MASK) node = loadTransparencyMask(element, parent); else if (nodeType == SELECTION_MASK) node = loadSelectionMask(image, element, parent); else if (nodeType == FILE_LAYER) { node = loadFileLayer(element, image, name, opacity); } else { m_d->errorMessages << i18n("Layer %1 has an unsupported type: %2.", name, nodeType); return 0; } // Loading the node went wrong. Return empty node and leave to // upstream to complain to the user if (!node) { m_d->errorMessages << i18n("Failure loading layer %1 of type: %2.", name, nodeType); return 0; } node->setVisible(visible, true); node->setUserLocked(locked); node->setCollapsed(collapsed); node->setX(x); node->setY(y); node->setName(name); if (! id.isNull()) // if no uuid in file, new one has been generated already node->setUuid(id); if (node->inherits("KisLayer")) { KisLayer* layer = qobject_cast(node.data()); QBitArray channelFlags = stringToFlags(element.attribute(CHANNEL_FLAGS, ""), colorSpace->channelCount()); QString compositeOpName = element.attribute(COMPOSITE_OP, "normal"); layer->setChannelFlags(channelFlags); layer->setCompositeOp(compositeOpName); if (element.hasAttribute(LAYER_STYLE_UUID)) { QString uuidString = element.attribute(LAYER_STYLE_UUID); QUuid uuid(uuidString); if (!uuid.isNull()) { KisPSDLayerStyleSP dumbLayerStyle(new KisPSDLayerStyle()); dumbLayerStyle->setUuid(uuid); layer->setLayerStyle(dumbLayerStyle); } else { qWarning() << "WARNING: Layer style for layer" << layer->name() << "contains invalid UUID" << uuidString; } } } + if (node->inherits("KisGroupLayer")) { + if (element.hasAttribute(PASS_THROUGH_MODE)) { + bool value = element.attribute(PASS_THROUGH_MODE, "0") != "0"; + + KisGroupLayer *group = qobject_cast(node.data()); + group->setPassThroughMode(value); + } + } + if (node->inherits("KisPaintLayer")) { KisPaintLayer* layer = qobject_cast(node.data()); QBitArray channelLockFlags = stringToFlags(element.attribute(CHANNEL_LOCK_FLAGS, ""), colorSpace->channelCount()); layer->setChannelLockFlags(channelLockFlags); } if (element.attribute(FILE_NAME).isNull()) { m_d->layerFilenames[node.data()] = name; } else { m_d->layerFilenames[node.data()] = element.attribute(FILE_NAME); } if (element.hasAttribute("selected") && element.attribute("selected") == "true") { m_d->selectedNodes.append(node); } return node; } KisNodeSP KisKraLoader::loadPaintLayer(const KoXmlElement& element, KisImageWSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { Q_UNUSED(element); KisPaintLayer* layer; layer = new KisPaintLayer(image, name, opacity, cs); Q_CHECK_PTR(layer); // Load exif info /*TODO: write and use the legacy stuff to load that exif tag for( KoXmlNode node = element.firstChild(); !node.isNull(); node = node.nextSibling() ) { KoXmlElement e = node.toElement(); if ( !e.isNull() && e.tagName() == "ExifInfo" ) { layer->paintDevice()->exifInfo()->load(e); } }*/ // TODO load metadata return layer; } KisNodeSP KisKraLoader::loadFileLayer(const KoXmlElement& element, KisImageWSP image, const QString& name, quint32 opacity) { QString filename = element.attribute("source", QString()); if (filename.isNull()) return 0; bool scale = (element.attribute("scale", "true") == "true"); int scalingMethod = element.attribute("scalingmethod", "-1").toInt(); if (scalingMethod < 0) { if (scale) { scalingMethod = KisFileLayer::ToImagePPI; } else { scalingMethod = KisFileLayer::None; } } QString documentPath; if (m_d->document) { documentPath = m_d->document->url().toLocalFile(); } QFileInfo info(documentPath); QString basePath = info.absolutePath(); QString fullPath = basePath + QDir::separator() + filename; if (!QFileInfo(fullPath).exists()) { QString msg = i18nc( "@info", "The file associated to a file layer with the name \"%1\" is not found." "Expected path:" "%2" "Do you want to locate it manually?", name, fullPath); int result = QMessageBox::warning(0, i18nc("@title:window", "File not found"), msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); if (result == QMessageBox::Yes) { KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument"); dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Import)); dialog.setDefaultDir(basePath); QString url = dialog.url(); if (!QFileInfo(basePath).exists()) { filename = url; } else { QDir d(basePath); filename = d.relativeFilePath(url); } } } KisLayer *layer = new KisFileLayer(image, basePath, filename, (KisFileLayer::ScalingMethod)scalingMethod, name, opacity); Q_CHECK_PTR(layer); return layer; } KisNodeSP KisKraLoader::loadGroupLayer(const KoXmlElement& element, KisImageWSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { Q_UNUSED(element); Q_UNUSED(cs); QString attr; KisGroupLayer* layer; layer = new KisGroupLayer(image, name, opacity); Q_CHECK_PTR(layer); return layer; } KisNodeSP KisKraLoader::loadAdjustmentLayer(const KoXmlElement& element, KisImageWSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { // XXX: do something with filterversion? Q_UNUSED(cs); QString attr; KisAdjustmentLayer* layer; QString filtername; if ((filtername = element.attribute(FILTER_NAME)).isNull()) { // XXX: Invalid adjustmentlayer! We should warn about it! warnFile << "No filter in adjustment layer"; return 0; } KisFilterSP f = KisFilterRegistry::instance()->value(filtername); if (!f) { warnFile << "No filter for filtername" << filtername << ""; return 0; // XXX: We don't have this filter. We should warn about it! } KisFilterConfiguration * kfc = f->defaultConfiguration(0); // We'll load the configuration and the selection later. layer = new KisAdjustmentLayer(image, name, kfc, 0); Q_CHECK_PTR(layer); layer->setOpacity(opacity); return layer; } KisNodeSP KisKraLoader::loadShapeLayer(const KoXmlElement& element, KisImageWSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { Q_UNUSED(element); Q_UNUSED(cs); QString attr; KoShapeBasedDocumentBase * shapeController = 0; if (m_d->document) { shapeController = m_d->document->shapeController(); } KisShapeLayer* layer = new KisShapeLayer(shapeController, image, name, opacity); Q_CHECK_PTR(layer); return layer; } KisNodeSP KisKraLoader::loadGeneratorLayer(const KoXmlElement& element, KisImageWSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { Q_UNUSED(cs); // XXX: do something with generator version? KisGeneratorLayer* layer; QString generatorname = element.attribute(GENERATOR_NAME); if (generatorname.isNull()) { // XXX: Invalid generator layer! We should warn about it! warnFile << "No generator in generator layer"; return 0; } KisGeneratorSP generator = KisGeneratorRegistry::instance()->value(generatorname); if (!generator) { warnFile << "No generator for generatorname" << generatorname << ""; return 0; // XXX: We don't have this generator. We should warn about it! } KisFilterConfiguration * kgc = generator->defaultConfiguration(0); // We'll load the configuration and the selection later. layer = new KisGeneratorLayer(image, name, kgc, 0); Q_CHECK_PTR(layer); layer->setOpacity(opacity); return layer; } KisNodeSP KisKraLoader::loadCloneLayer(const KoXmlElement& element, KisImageWSP image, const QString& name, const KoColorSpace* cs, quint32 opacity) { Q_UNUSED(cs); KisCloneLayerSP layer = new KisCloneLayer(0, image, name, opacity); KisCloneInfo info; if (! (element.attribute(CLONE_FROM_UUID)).isNull()) { info = KisCloneInfo(QUuid(element.attribute(CLONE_FROM_UUID))); } else { if ((element.attribute(CLONE_FROM)).isNull()) { return 0; } else { info = KisCloneInfo(element.attribute(CLONE_FROM)); } } layer->setCopyFromInfo(info); if ((element.attribute(CLONE_TYPE)).isNull()) { return 0; } else { layer->setCopyType((CopyLayerType) element.attribute(CLONE_TYPE).toInt()); } return layer; } KisNodeSP KisKraLoader::loadFilterMask(const KoXmlElement& element, KisNodeSP parent) { Q_UNUSED(parent); QString attr; KisFilterMask* mask; QString filtername; // XXX: should we check the version? if ((filtername = element.attribute(FILTER_NAME)).isNull()) { // XXX: Invalid filter layer! We should warn about it! warnFile << "No filter in filter layer"; return 0; } KisFilterSP f = KisFilterRegistry::instance()->value(filtername); if (!f) { warnFile << "No filter for filtername" << filtername << ""; return 0; // XXX: We don't have this filter. We should warn about it! } KisFilterConfiguration * kfc = f->defaultConfiguration(0); // We'll load the configuration and the selection later. mask = new KisFilterMask(); mask->setFilter(kfc); Q_CHECK_PTR(mask); return mask; } KisNodeSP KisKraLoader::loadTransformMask(const KoXmlElement& element, KisNodeSP parent) { Q_UNUSED(element); Q_UNUSED(parent); KisTransformMask* mask; /** * We'll load the transform configuration later on a stage * of binary data loading */ mask = new KisTransformMask(); Q_CHECK_PTR(mask); return mask; } KisNodeSP KisKraLoader::loadTransparencyMask(const KoXmlElement& element, KisNodeSP parent) { Q_UNUSED(element); Q_UNUSED(parent); KisTransparencyMask* mask = new KisTransparencyMask(); Q_CHECK_PTR(mask); return mask; } KisNodeSP KisKraLoader::loadSelectionMask(KisImageWSP image, const KoXmlElement& element, KisNodeSP parent) { Q_UNUSED(parent); KisSelectionMaskSP mask = new KisSelectionMask(image); bool active = element.attribute(ACTIVE, "1") == "0" ? false : true; mask->setActive(active); Q_CHECK_PTR(mask); return mask; } void KisKraLoader::loadCompositions(const KoXmlElement& elem, KisImageWSP image) { KoXmlNode child; for (child = elem.firstChild(); !child.isNull(); child = child.nextSibling()) { KoXmlElement e = child.toElement(); QString name = e.attribute("name"); bool exportEnabled = e.attribute("exportEnabled", "1") == "0" ? false : true; KisLayerComposition* composition = new KisLayerComposition(image, name); composition->setExportEnabled(exportEnabled); KoXmlNode value; for (value = child.lastChild(); !value.isNull(); value = value.previousSibling()) { KoXmlElement e = value.toElement(); QUuid uuid(e.attribute("uuid")); bool visible = e.attribute("visible", "1") == "0" ? false : true; composition->setVisible(uuid, visible); bool collapsed = e.attribute("collapsed", "1") == "0" ? false : true; composition->setCollapsed(uuid, collapsed); } image->addComposition(composition); } } void KisKraLoader::loadAssistantsList(const KoXmlElement &elem) { KoXmlNode child; int count = 0; for (child = elem.firstChild(); !child.isNull(); child = child.nextSibling()) { KoXmlElement e = child.toElement(); QString type = e.attribute("type"); QString file_name = e.attribute("filename"); m_d->assistantsFilenames.insert(file_name,type); count++; } } diff --git a/krita/ui/kra/kis_kra_savexml_visitor.cpp b/krita/ui/kra/kis_kra_savexml_visitor.cpp index 9a942b8635..8980bcc596 100644 --- a/krita/ui/kra/kis_kra_savexml_visitor.cpp +++ b/krita/ui/kra/kis_kra_savexml_visitor.cpp @@ -1,406 +1,407 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2005 C. Boemann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kra/kis_kra_savexml_visitor.h" #include "kis_kra_tags.h" #include "kis_kra_utils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KRA; KisSaveXmlVisitor::KisSaveXmlVisitor(QDomDocument doc, const QDomElement & element, quint32 &count, const QString &url, bool root) : KisNodeVisitor() , m_doc(doc) , m_count(count) , m_url(url) , m_root(root) { Q_ASSERT(!element.isNull()); m_elem = element; } void KisSaveXmlVisitor::setSelectedNodes(vKisNodeSP selectedNodes) { m_selectedNodes = selectedNodes; } QStringList KisSaveXmlVisitor::errorMessages() const { return m_errorMessages; } bool KisSaveXmlVisitor::visit(KisExternalLayer * layer) { if (layer->inherits("KisShapeLayer")) { QDomElement layerElement = m_doc.createElement(LAYER); saveLayer(layerElement, SHAPE_LAYER, layer); m_elem.appendChild(layerElement); m_count++; return saveMasks(layer, layerElement); } else if (layer->inherits("KisFileLayer")) { QDomElement layerElement = m_doc.createElement(LAYER); saveLayer(layerElement, FILE_LAYER, layer); KisFileLayer *fileLayer = dynamic_cast(layer); QString path = fileLayer->path(); QDir d(QFileInfo(m_url).absolutePath()); layerElement.setAttribute("source", d.relativeFilePath(path)); if (fileLayer->scalingMethod() == KisFileLayer::ToImagePPI) { layerElement.setAttribute("scale", "true"); } else { layerElement.setAttribute("scale", "false"); } layerElement.setAttribute("scalingmethod", (int)fileLayer->scalingMethod()); layerElement.setAttribute(COLORSPACE_NAME, layer->original()->colorSpace()->id()); m_elem.appendChild(layerElement); m_count++; return saveMasks(layer, layerElement); } return false; } QDomElement KisSaveXmlVisitor::savePaintLayerAttributes(KisPaintLayer *layer, QDomDocument &doc) { QDomElement element = doc.createElement(LAYER); saveLayer(element, PAINT_LAYER, layer); element.setAttribute(CHANNEL_LOCK_FLAGS, flagsToString(layer->channelLockFlags())); element.setAttribute(COLORSPACE_NAME, layer->paintDevice()->colorSpace()->id()); return element; } void KisSaveXmlVisitor::loadPaintLayerAttributes(const QDomElement &el, KisPaintLayer *layer) { loadLayerAttributes(el, layer); if (el.hasAttribute(CHANNEL_LOCK_FLAGS)) { layer->setChannelLockFlags(stringToFlags(el.attribute(CHANNEL_LOCK_FLAGS))); } } bool KisSaveXmlVisitor::visit(KisPaintLayer *layer) { QDomElement layerElement = savePaintLayerAttributes(layer, m_doc); m_elem.appendChild(layerElement); /* if(layer->paintDevice()->hasExifInfo()) { QDomElement exifElmt = layer->paintDevice()->exifInfo()->save(m_doc); layerElement.appendChild(exifElmt); } TODO: save the metadata */ m_count++; return saveMasks(layer, layerElement); } bool KisSaveXmlVisitor::visit(KisGroupLayer *layer) { QDomElement layerElement; if (m_root) // if this is the root we fake so not to save it layerElement = m_elem; else { layerElement = m_doc.createElement(LAYER); saveLayer(layerElement, GROUP_LAYER, layer); + layerElement.setAttribute(PASS_THROUGH_MODE, layer->passThroughMode()); m_elem.appendChild(layerElement); } QDomElement elem = m_doc.createElement(LAYERS); Q_ASSERT(!layerElement.isNull()); layerElement.appendChild(elem); KisSaveXmlVisitor visitor(m_doc, elem, m_count, m_url, false); visitor.setSelectedNodes(m_selectedNodes); m_count++; bool success = visitor.visitAllInverse(layer); m_errorMessages.append(visitor.errorMessages()); if (!m_errorMessages.isEmpty()) { return false; } QMapIterator i(visitor.nodeFileNames()); while (i.hasNext()) { i.next(); m_nodeFileNames[i.key()] = i.value(); } return success; } bool KisSaveXmlVisitor::visit(KisAdjustmentLayer* layer) { if (!layer->filter()) { return false; } QDomElement layerElement = m_doc.createElement(LAYER); saveLayer(layerElement, ADJUSTMENT_LAYER, layer); layerElement.setAttribute(FILTER_NAME, layer->filter()->name()); layerElement.setAttribute(FILTER_VERSION, layer->filter()->version()); m_elem.appendChild(layerElement); m_count++; return saveMasks(layer, layerElement); } bool KisSaveXmlVisitor::visit(KisGeneratorLayer *layer) { QDomElement layerElement = m_doc.createElement(LAYER); saveLayer(layerElement, GENERATOR_LAYER, layer); layerElement.setAttribute(GENERATOR_NAME, layer->filter()->name()); layerElement.setAttribute(GENERATOR_VERSION, layer->filter()->version()); m_elem.appendChild(layerElement); m_count++; return saveMasks(layer, layerElement); } bool KisSaveXmlVisitor::visit(KisCloneLayer *layer) { QDomElement layerElement = m_doc.createElement(LAYER); saveLayer(layerElement, CLONE_LAYER, layer); layerElement.setAttribute(CLONE_FROM, layer->copyFromInfo().name()); layerElement.setAttribute(CLONE_FROM_UUID, layer->copyFromInfo().uuid().toString()); layerElement.setAttribute(CLONE_TYPE, layer->copyType()); m_elem.appendChild(layerElement); m_count++; return saveMasks(layer, layerElement); } bool KisSaveXmlVisitor::visit(KisFilterMask *mask) { Q_ASSERT(mask); if (!mask->filter()) { return false; } QDomElement el = m_doc.createElement(MASK); saveMask(el, FILTER_MASK, mask); el.setAttribute(FILTER_NAME, mask->filter()->name()); el.setAttribute(FILTER_VERSION, mask->filter()->version()); m_elem.appendChild(el); m_count++; return true; } bool KisSaveXmlVisitor::visit(KisTransformMask *mask) { Q_ASSERT(mask); QDomElement el = m_doc.createElement(MASK); saveMask(el, TRANSFORM_MASK, mask); m_elem.appendChild(el); m_count++; return true; } bool KisSaveXmlVisitor::visit(KisTransparencyMask *mask) { Q_ASSERT(mask); QDomElement el = m_doc.createElement(MASK); saveMask(el, TRANSPARENCY_MASK, mask); m_elem.appendChild(el); m_count++; return true; } bool KisSaveXmlVisitor::visit(KisSelectionMask *mask) { Q_ASSERT(mask); QDomElement el = m_doc.createElement(MASK); saveMask(el, SELECTION_MASK, mask); m_elem.appendChild(el); m_count++; return true; } void KisSaveXmlVisitor::loadLayerAttributes(const QDomElement &el, KisLayer *layer) { if (el.hasAttribute(NAME)) { QString layerName = el.attribute(NAME); KIS_ASSERT_RECOVER_RETURN(layerName == layer->name()); } if (el.hasAttribute(CHANNEL_FLAGS)) { layer->setChannelFlags(stringToFlags(el.attribute(CHANNEL_FLAGS))); } if (el.hasAttribute(OPACITY)) { layer->setOpacity(el.attribute(OPACITY).toInt()); } if (el.hasAttribute(COMPOSITE_OP)) { layer->setCompositeOp(el.attribute(COMPOSITE_OP)); } if (el.hasAttribute(VISIBLE)) { layer->setVisible(el.attribute(VISIBLE).toInt()); } if (el.hasAttribute(LOCKED)) { layer->setUserLocked(el.attribute(LOCKED).toInt()); } if (el.hasAttribute(X)) { layer->setX(el.attribute(X).toInt()); } if (el.hasAttribute(Y)) { layer->setY(el.attribute(Y).toInt()); } if (el.hasAttribute(UUID)) { layer->setUuid(el.attribute(UUID)); } if (el.hasAttribute(COLLAPSED)) { layer->setCollapsed(el.attribute(COLLAPSED).toInt()); } if (el.hasAttribute(LAYER_STYLE_UUID)) { QString uuidString = el.attribute(LAYER_STYLE_UUID); QUuid uuid(uuidString); if (!uuid.isNull()) { KisPSDLayerStyleSP dumbLayerStyle(new KisPSDLayerStyle()); dumbLayerStyle->setUuid(uuid); layer->setLayerStyle(dumbLayerStyle); } else { qWarning() << "WARNING: Layer style for layer" << layer->name() << "contains invalid UUID" << uuidString; } } } void KisSaveXmlVisitor::saveLayer(QDomElement & el, const QString & layerType, const KisLayer * layer) { el.setAttribute(CHANNEL_FLAGS, flagsToString(layer->channelFlags())); el.setAttribute(NAME, layer->name()); el.setAttribute(OPACITY, layer->opacity()); el.setAttribute(COMPOSITE_OP, layer->compositeOp()->id()); el.setAttribute(VISIBLE, layer->visible()); el.setAttribute(LOCKED, layer->userLocked()); el.setAttribute(NODE_TYPE, layerType); el.setAttribute(FILE_NAME, LAYER + QString::number(m_count)); el.setAttribute(X, layer->x()); el.setAttribute(Y, layer->y()); el.setAttribute(UUID, layer->uuid().toString()); el.setAttribute(COLLAPSED, layer->collapsed()); if (layer->layerStyle()) { el.setAttribute(LAYER_STYLE_UUID, layer->layerStyle()->uuid().toString()); } foreach (KisNodeSP node, m_selectedNodes) { if (node.data() == layer) { el.setAttribute("selected", "true"); break; } } m_nodeFileNames[layer] = LAYER + QString::number(m_count); dbgFile << "Saved layer " << layer->name() << " of type " << layerType << " with filename " << LAYER + QString::number(m_count); } void KisSaveXmlVisitor::saveMask(QDomElement & el, const QString & maskType, const KisMask * mask) { el.setAttribute(NAME, mask->name()); el.setAttribute(VISIBLE, mask->visible()); el.setAttribute(LOCKED, mask->userLocked()); el.setAttribute(NODE_TYPE, maskType); el.setAttribute(FILE_NAME, MASK + QString::number(m_count)); el.setAttribute(X, mask->x()); el.setAttribute(Y, mask->y()); el.setAttribute(UUID, mask->uuid().toString()); if (maskType == SELECTION_MASK) { el.setAttribute(ACTIVE, mask->nodeProperties().boolProperty("visible")); } m_nodeFileNames[mask] = MASK + QString::number(m_count); dbgFile << "Saved mask " << mask->name() << " of type " << maskType << " with filename " << MASK + QString::number(m_count); } bool KisSaveXmlVisitor::saveMasks(KisNode * node, QDomElement & layerElement) { if (node->childCount() > 0) { QDomElement elem = m_doc.createElement(MASKS); Q_ASSERT(!layerElement.isNull()); layerElement.appendChild(elem); KisSaveXmlVisitor visitor(m_doc, elem, m_count, m_url, false); visitor.setSelectedNodes(m_selectedNodes); bool success = visitor.visitAllInverse(node); m_errorMessages.append(visitor.errorMessages()); if (!m_errorMessages.isEmpty()) { return false; } QMapIterator i(visitor.nodeFileNames()); while (i.hasNext()) { i.next(); m_nodeFileNames[i.key()] = i.value(); } return success; } return true; } diff --git a/krita/ui/kra/kis_kra_tags.h b/krita/ui/kra/kis_kra_tags.h index 7eaad6b182..1a9294a2e2 100644 --- a/krita/ui/kra/kis_kra_tags.h +++ b/krita/ui/kra/kis_kra_tags.h @@ -1,111 +1,112 @@ /* This file is part of the KDE project * Copyright 2008 (C) Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_KRA_TAGS #define KIS_KRA_TAGS #include /** * Tag definitions for our xml file format */ namespace KRA { // mimetype const QString NATIVE_MIMETYPE = "application/x-kra"; // xml tags const QString SEPARATOR = "/"; const QString SHAPE_LAYER_PATH = "/shapelayers/"; const QString EXIF_PATH = "/annotations/exif"; const QString ICC_PATH = "/annotations/icc"; const QString LAYER_STYLES_PATH = "/annotations/layerstyles.asl"; const QString ASSISTANTS_PATH = "/assistants/"; const QString LAYER_PATH = "/layers/"; const QString ADJUSTMENT_LAYER = "adjustmentlayer"; const QString CHANNEL_FLAGS = "channelflags"; const QString CHANNEL_LOCK_FLAGS = "channellockflags"; const QString CLONE_FROM = "clonefrom"; const QString CLONE_FROM_UUID = "clonefromuuid"; const QString CLONE_LAYER = "clonelayer"; const QString CLONE_TYPE = "clonetype"; const QString COLORSPACE_NAME = "colorspacename"; const QString COMPOSITE_OP = "compositeop"; const QString DESCRIPTION = "description"; const QString DOT_FILTERCONFIG = ".filterconfig"; const QString DOT_TRANSFORMCONFIG = ".transformconfig"; const QString DOT_ICC = ".icc"; const QString DOT_PIXEL_SELECTION = ".pixelselection"; const QString DOT_SHAPE_SELECTION = ".shapeselection"; const QString DOT_SHAPE_LAYER = ".shapelayer"; const QString DOT_METADATA = ".metadata"; const QString FILE_NAME = "filename"; const QString FILTER_MASK = "filtermask"; const QString FILTER_NAME = "filtername"; const QString FILTER_STATEGY = "filter_strategy"; const QString FILTER_VERSION = "filterversion"; const QString GENERATOR_LAYER = "generatorlayer"; const QString GENERATOR_NAME = "generatorname"; const QString GENERATOR_VERSION = "generatorversion"; const QString GROUP_LAYER = "grouplayer"; const QString HEIGHT = "height"; const QString ICC = "icc"; const QString LAYER = "layer"; const QString LAYERS = "layers"; const QString NODE_TYPE = "nodetype"; const QString LOCKED = "locked"; const QString MASK = "mask"; const QString MASKS = "masks"; const QString MIME = "mime"; const QString NAME = "name"; const QString OPACITY = "opacity"; const QString COLLAPSED = "collapsed"; const QString PAINT_LAYER = "paintlayer"; const QString PROFILE = "profile"; const QString ROTATION = "rotation"; const QString SELECTION_MASK = "selectionmask"; const QString SHAPE_LAYER = "shapelayer"; const QString FILE_LAYER = "filelayer"; const QString TRANSPARENCY_MASK = "transparencymask"; const QString TRANSFORM_MASK = "transformmask"; const QString UUID = "uuid"; const QString VISIBLE = "visible"; const QString WIDTH = "width"; const QString X = "x"; const QString X_RESOLUTION = "x-res"; const QString X_SCALE = "x_scale"; const QString X_SHEAR = "x_shear"; const QString X_TRANSLATION = "x_translation"; const QString Y = "y"; const QString Y_RESOLUTION = "y-res"; const QString Y_SCALE = "y_scale"; const QString Y_SHEAR = "y_shear"; const QString Y_TRANSLATION = "y_translation"; const QString ACTIVE = "active"; const QString LAYER_STYLE_UUID = "layerstyle"; +const QString PASS_THROUGH_MODE = "passthrough"; } #endif