diff --git a/libs/flake/KoShape.cpp b/libs/flake/KoShape.cpp index 542a331eb4..ff95e7284e 100644 --- a/libs/flake/KoShape.cpp +++ b/libs/flake/KoShape.cpp @@ -1,1357 +1,1356 @@ /* This file is part of the KDE project Copyright (C) 2006 C. Boemann Rasmussen Copyright (C) 2006-2010 Thomas Zander Copyright (C) 2006-2010 Thorsten Zachmann Copyright (C) 2007-2009,2011 Jan Hambrecht CopyRight (C) 2010 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. */ #include #include "KoShape.h" #include "KoShape_p.h" #include "KoShapeContainer.h" #include "KoShapeLayer.h" #include "KoShapeContainerModel.h" #include "KoSelection.h" #include "KoPointerEvent.h" #include "KoInsets.h" #include "KoShapeStrokeModel.h" #include "KoShapeBackground.h" #include "KoColorBackground.h" #include "KoHatchBackground.h" #include "KoGradientBackground.h" #include "KoPatternBackground.h" #include "KoShapeManager.h" #include "KoShapeUserData.h" #include "KoShapeApplicationData.h" #include "KoShapeSavingContext.h" #include "KoShapeLoadingContext.h" #include "KoViewConverter.h" #include "KoShapeStroke.h" #include "KoShapeShadow.h" #include "KoClipPath.h" #include "KoPathShape.h" #include "KoFilterEffectStack.h" #include -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_assert.h" #include // KoShape::Private KoShape::SharedData::SharedData() : QSharedData() , size(50, 50) , shadow(0) , filterEffectStack(0) , transparency(0.0) , zIndex(0) , runThrough(0) , visible(true) , printable(true) , geometryProtected(false) , keepAspect(false) , selectable(true) , protectContent(false) , textRunAroundSide(KoShape::BiggestRunAroundSide) , textRunAroundDistanceLeft(0.0) , textRunAroundDistanceTop(0.0) , textRunAroundDistanceRight(0.0) , textRunAroundDistanceBottom(0.0) , textRunAroundThreshold(0.0) , textRunAroundContour(KoShape::ContourFull) { } KoShape::SharedData::SharedData(const SharedData &rhs) : QSharedData() , size(rhs.size) , shapeId(rhs.shapeId) , name(rhs.name) , localMatrix(rhs.localMatrix) , userData(rhs.userData ? rhs.userData->clone() : 0) , stroke(rhs.stroke) , fill(rhs.fill) , inheritBackground(rhs.inheritBackground) , inheritStroke(rhs.inheritStroke) , shadow(0) // WARNING: not implemented in Krita , clipPath(rhs.clipPath ? rhs.clipPath->clone() : 0) , clipMask(rhs.clipMask ? rhs.clipMask->clone() : 0) , additionalAttributes(rhs.additionalAttributes) , additionalStyleAttributes(rhs.additionalStyleAttributes) , filterEffectStack(0) // WARNING: not implemented in Krita , transparency(rhs.transparency) , hyperLink(rhs.hyperLink) , zIndex(rhs.zIndex) , runThrough(rhs.runThrough) , visible(rhs.visible) , printable(rhs.visible) , geometryProtected(rhs.geometryProtected) , keepAspect(rhs.keepAspect) , selectable(rhs.selectable) , protectContent(rhs.protectContent) , textRunAroundSide(rhs.textRunAroundSide) , textRunAroundDistanceLeft(rhs.textRunAroundDistanceLeft) , textRunAroundDistanceTop(rhs.textRunAroundDistanceTop) , textRunAroundDistanceRight(rhs.textRunAroundDistanceRight) , textRunAroundDistanceBottom(rhs.textRunAroundDistanceBottom) , textRunAroundThreshold(rhs.textRunAroundThreshold) , textRunAroundContour(rhs.textRunAroundContour) { } KoShape::SharedData::~SharedData() { if (shadow && !shadow->deref()) delete shadow; if (filterEffectStack && !filterEffectStack->deref()) delete filterEffectStack; } void KoShape::shapeChangedPriv(KoShape::ChangeType type) { if (d->parent) d->parent->model()->childChanged(this, type); this->shapeChanged(type); Q_FOREACH (KoShape * shape, d->dependees) { shape->shapeChanged(type, this); } Q_FOREACH (KoShape::ShapeChangeListener *listener, d->listeners) { listener->notifyShapeChangedImpl(type, this); } } void KoShape::addShapeManager(KoShapeManager *manager) { d->shapeManagers.insert(manager); } void KoShape::removeShapeManager(KoShapeManager *manager) { d->shapeManagers.remove(manager); } // ======== KoShape const qint16 KoShape::maxZIndex = std::numeric_limits::max(); const qint16 KoShape::minZIndex = std::numeric_limits::min(); KoShape::KoShape() : d(new Private()), s(new SharedData) { notifyChanged(); } KoShape::KoShape(const KoShape &rhs) : d(new Private()), s(rhs.s) { } KoShape::~KoShape() { shapeChangedPriv(Deleted); d->listeners.clear(); /** * The shape must have already been detached from all the parents and * shape managers. Otherwise we migh accidentally request some RTTI * information, which is not available anymore (we are in d-tor). * * TL;DR: fix the code that caused this destruction without unparenting * instead of trying to remove these assert! */ KIS_SAFE_ASSERT_RECOVER (!d->parent) { d->parent->removeShape(this); } KIS_SAFE_ASSERT_RECOVER (d->shapeManagers.isEmpty()) { Q_FOREACH (KoShapeManager *manager, d->shapeManagers) { manager->shapeInterface()->notifyShapeDestructed(this); } d->shapeManagers.clear(); } } KoShape *KoShape::cloneShape() const { KIS_SAFE_ASSERT_RECOVER_NOOP(0 && "not implemented!"); qWarning() << shapeId() << "cannot be cloned"; return 0; } void KoShape::paintStroke(QPainter &painter, KoShapePaintingContext &paintcontext) const { Q_UNUSED(paintcontext); if (stroke()) { stroke()->paint(this, painter); } } void KoShape::scale(qreal sx, qreal sy) { QPointF pos = position(); QTransform scaleMatrix; scaleMatrix.translate(pos.x(), pos.y()); scaleMatrix.scale(sx, sy); scaleMatrix.translate(-pos.x(), -pos.y()); s->localMatrix = s->localMatrix * scaleMatrix; notifyChanged(); shapeChangedPriv(ScaleChanged); } void KoShape::rotate(qreal angle) { QPointF center = s->localMatrix.map(QPointF(0.5 * size().width(), 0.5 * size().height())); QTransform rotateMatrix; rotateMatrix.translate(center.x(), center.y()); rotateMatrix.rotate(angle); rotateMatrix.translate(-center.x(), -center.y()); s->localMatrix = s->localMatrix * rotateMatrix; notifyChanged(); shapeChangedPriv(RotationChanged); } void KoShape::shear(qreal sx, qreal sy) { QPointF pos = position(); QTransform shearMatrix; shearMatrix.translate(pos.x(), pos.y()); shearMatrix.shear(sx, sy); shearMatrix.translate(-pos.x(), -pos.y()); s->localMatrix = s->localMatrix * shearMatrix; notifyChanged(); shapeChangedPriv(ShearChanged); } void KoShape::setSize(const QSizeF &newSize) { QSizeF oldSize(size()); // always set size, as d->size and size() may vary setSizeImpl(newSize); if (oldSize == newSize) return; notifyChanged(); shapeChangedPriv(SizeChanged); } void KoShape::setSizeImpl(const QSizeF &size) const { s->size = size; } void KoShape::setPosition(const QPointF &newPosition) { QPointF currentPos = position(); if (newPosition == currentPos) return; QTransform translateMatrix; translateMatrix.translate(newPosition.x() - currentPos.x(), newPosition.y() - currentPos.y()); s->localMatrix = s->localMatrix * translateMatrix; notifyChanged(); shapeChangedPriv(PositionChanged); } bool KoShape::hitTest(const QPointF &position) const { if (d->parent && d->parent->isClipped(this) && !d->parent->hitTest(position)) return false; QPointF point = absoluteTransformation().inverted().map(position); QRectF bb = outlineRect(); if (s->stroke) { KoInsets insets; s->stroke->strokeInsets(this, insets); bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom); } if (bb.contains(point)) return true; // if there is no shadow we can as well just leave if (! s->shadow) return false; // the shadow has an offset to the shape, so we simply // check if the position minus the shadow offset hits the shape point = absoluteTransformation().inverted().map(position - s->shadow->offset()); return bb.contains(point); } QRectF KoShape::boundingRect() const { QTransform transform = absoluteTransformation(); QRectF bb = outlineRect(); if (s->stroke) { KoInsets insets; s->stroke->strokeInsets(this, insets); bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom); } bb = transform.mapRect(bb); if (s->shadow) { KoInsets insets; s->shadow->insets(insets); bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom); } if (s->filterEffectStack) { QRectF clipRect = s->filterEffectStack->clipRectForBoundingRect(outlineRect()); bb |= transform.mapRect(clipRect); } return bb; } QRectF KoShape::boundingRect(const QList &shapes) { QRectF boundingRect; Q_FOREACH (KoShape *shape, shapes) { boundingRect |= shape->boundingRect(); } return boundingRect; } QRectF KoShape::absoluteOutlineRect() const { return absoluteTransformation().map(outline()).boundingRect(); } QRectF KoShape::absoluteOutlineRect(const QList &shapes) { QRectF absoluteOutlineRect; Q_FOREACH (KoShape *shape, shapes) { absoluteOutlineRect |= shape->absoluteOutlineRect(); } return absoluteOutlineRect; } QTransform KoShape::absoluteTransformation() const { QTransform matrix; // apply parents matrix to inherit any transformations done there. KoShapeContainer * container = d->parent; if (container) { if (container->inheritsTransform(this)) { matrix = container->absoluteTransformation(); } else { QSizeF containerSize = container->size(); QPointF containerPos = container->absolutePosition() - QPointF(0.5 * containerSize.width(), 0.5 * containerSize.height()); matrix.translate(containerPos.x(), containerPos.y()); } } return s->localMatrix * matrix; } void KoShape::applyAbsoluteTransformation(const QTransform &matrix) { QTransform globalMatrix = absoluteTransformation(); // the transformation is relative to the global coordinate system // but we want to change the local matrix, so convert the matrix // to be relative to the local coordinate system QTransform transformMatrix = globalMatrix * matrix * globalMatrix.inverted(); applyTransformation(transformMatrix); } void KoShape::applyTransformation(const QTransform &matrix) { s->localMatrix = matrix * s->localMatrix; notifyChanged(); shapeChangedPriv(GenericMatrixChange); } void KoShape::setTransformation(const QTransform &matrix) { s->localMatrix = matrix; notifyChanged(); shapeChangedPriv(GenericMatrixChange); } QTransform KoShape::transformation() const { return s->localMatrix; } KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy() { return ChildZDefault; } bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2) { /** * WARNING: Our definition of zIndex is not yet compatible with SVG2's * definition. In SVG stacking context of groups with the same * zIndex are **merged**, while in Krita the contents of groups * is never merged. One group will always below than the other. * Therefore, when zIndex of two groups inside the same parent * coincide, the resulting painting order in Krita is * **UNDEFINED**. * * To avoid this trouble we use KoShapeReorderCommand::mergeInShape() * inside KoShapeCreateCommand. */ /** * The algorithm below doesn't correctly handle the case when the two pointers actually * point to the same shape. So just check it in advance to guarantee strict weak ordering * relation requirement */ if (s1 == s2) return false; // First sort according to runThrough which is sort of a master level KoShape *parentShapeS1 = s1->parent(); KoShape *parentShapeS2 = s2->parent(); int runThrough1 = s1->runThrough(); int runThrough2 = s2->runThrough(); while (parentShapeS1) { if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) { runThrough1 = parentShapeS1->runThrough(); } else { runThrough1 = runThrough1 + parentShapeS1->runThrough(); } parentShapeS1 = parentShapeS1->parent(); } while (parentShapeS2) { if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) { runThrough2 = parentShapeS2->runThrough(); } else { runThrough2 = runThrough2 + parentShapeS2->runThrough(); } parentShapeS2 = parentShapeS2->parent(); } if (runThrough1 > runThrough2) { return false; } if (runThrough1 < runThrough2) { return true; } // If on the same runThrough level then the zIndex is all that matters. // // We basically walk up through the parents until we find a common base parent // To do that we need two loops where the inner loop walks up through the parents // of s2 every time we step up one parent level on s1 // // We don't update the index value until after we have seen that it's not a common base // That way we ensure that two children of a common base are sorted according to their respective // z value bool foundCommonParent = false; int index1 = s1->zIndex(); int index2 = s2->zIndex(); parentShapeS1 = s1; parentShapeS2 = s2; while (parentShapeS1 && !foundCommonParent) { parentShapeS2 = s2; index2 = parentShapeS2->zIndex(); while (parentShapeS2) { if (parentShapeS2 == parentShapeS1) { foundCommonParent = true; break; } if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) { index2 = parentShapeS2->zIndex(); } parentShapeS2 = parentShapeS2->parent(); } if (!foundCommonParent) { if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) { index1 = parentShapeS1->zIndex(); } parentShapeS1 = parentShapeS1->parent(); } } // If the one shape is a parent/child of the other then sort so. if (s1 == parentShapeS2) { return true; } if (s2 == parentShapeS1) { return false; } // If we went that far then the z-Index is used for sorting. return index1 < index2; } void KoShape::setParent(KoShapeContainer *parent) { if (d->parent == parent) { return; } KoShapeContainer *oldParent = d->parent; d->parent = 0; // avoids recursive removing if (oldParent) { oldParent->shapeInterface()->removeShape(this); } KIS_SAFE_ASSERT_RECOVER_NOOP(parent != this); if (parent && parent != this) { d->parent = parent; parent->shapeInterface()->addShape(this); } notifyChanged(); shapeChangedPriv(ParentChanged); } bool KoShape::inheritsTransformFromAny(const QList ancestorsInQuestion) const { bool result = false; KoShape *shape = const_cast(this); while (shape) { KoShapeContainer *parent = shape->parent(); if (parent && !parent->inheritsTransform(shape)) { break; } if (ancestorsInQuestion.contains(shape)) { result = true; break; } shape = parent; } return result; } bool KoShape::hasCommonParent(const KoShape *shape) const { const KoShape *thisShape = this; while (thisShape) { const KoShape *otherShape = shape; while (otherShape) { if (thisShape == otherShape) { return true; } otherShape = otherShape->parent(); } thisShape = thisShape->parent(); } return false; } qint16 KoShape::zIndex() const { return s->zIndex; } void KoShape::update() const { if (!d->shapeManagers.empty()) { QRectF rect(boundingRect()); Q_FOREACH (KoShapeManager * manager, d->shapeManagers) { manager->update(rect, this, true); } } } void KoShape::updateAbsolute(const QRectF &rect) const { if (rect.isEmpty() && !rect.isNull()) { return; } if (!d->shapeManagers.empty() && isVisible()) { Q_FOREACH (KoShapeManager *manager, d->shapeManagers) { manager->update(rect); } } } QPainterPath KoShape::outline() const { QPainterPath path; path.addRect(outlineRect()); return path; } QRectF KoShape::outlineRect() const { const QSizeF s = size(); return QRectF(QPointF(0, 0), QSizeF(qMax(s.width(), qreal(0.0001)), qMax(s.height(), qreal(0.0001)))); } QPainterPath KoShape::shadowOutline() const { if (background()) { return outline(); } return QPainterPath(); } QPointF KoShape::absolutePosition(KoFlake::AnchorPosition anchor) const { const QRectF rc = outlineRect(); QPointF point = rc.topLeft(); bool valid = false; QPointF anchoredPoint = KoFlake::anchorToPoint(anchor, rc, &valid); if (valid) { point = anchoredPoint; } return absoluteTransformation().map(point); } void KoShape::setAbsolutePosition(const QPointF &newPosition, KoFlake::AnchorPosition anchor) { QPointF currentAbsPosition = absolutePosition(anchor); QPointF translate = newPosition - currentAbsPosition; QTransform translateMatrix; translateMatrix.translate(translate.x(), translate.y()); applyAbsoluteTransformation(translateMatrix); notifyChanged(); shapeChangedPriv(PositionChanged); } void KoShape::copySettings(const KoShape *shape) { s->size = shape->size(); s->zIndex = shape->zIndex(); s->visible = shape->isVisible(false); // Ensure printable is true by default if (!s->visible) s->printable = true; else s->printable = shape->isPrintable(); s->geometryProtected = shape->isGeometryProtected(); s->protectContent = shape->isContentProtected(); s->selectable = shape->isSelectable(); s->keepAspect = shape->keepAspectRatio(); s->localMatrix = shape->s->localMatrix; } void KoShape::notifyChanged() { Q_FOREACH (KoShapeManager * manager, d->shapeManagers) { manager->notifyShapeChanged(this); } } void KoShape::setUserData(KoShapeUserData *userData) { s->userData.reset(userData); } KoShapeUserData *KoShape::userData() const { return s->userData.data(); } bool KoShape::hasTransparency() const { QSharedPointer bg = background(); return !bg || bg->hasTransparency() || s->transparency > 0.0; } void KoShape::setTransparency(qreal transparency) { s->transparency = qBound(0.0, transparency, 1.0); shapeChangedPriv(TransparencyChanged); notifyChanged(); } qreal KoShape::transparency(bool recursive) const { if (!recursive || !parent()) { return s->transparency; } else { const qreal parentOpacity = 1.0-parent()->transparency(recursive); const qreal childOpacity = 1.0-s->transparency; return 1.0-(parentOpacity*childOpacity); } } KoInsets KoShape::strokeInsets() const { KoInsets answer; if (s->stroke) s->stroke->strokeInsets(this, answer); return answer; } qreal KoShape::rotation() const { // try to extract the rotation angle out of the local matrix // if it is a pure rotation matrix // check if the matrix has shearing mixed in if (fabs(fabs(s->localMatrix.m12()) - fabs(s->localMatrix.m21())) > 1e-10) return std::numeric_limits::quiet_NaN(); // check if the matrix has scaling mixed in if (fabs(s->localMatrix.m11() - s->localMatrix.m22()) > 1e-10) return std::numeric_limits::quiet_NaN(); // calculate the angle from the matrix elements qreal angle = atan2(-s->localMatrix.m21(), s->localMatrix.m11()) * 180.0 / M_PI; if (angle < 0.0) angle += 360.0; return angle; } QSizeF KoShape::size() const { return s->size; } QPointF KoShape::position() const { QPointF center = outlineRect().center(); return s->localMatrix.map(center) - center; } KoShape::TextRunAroundSide KoShape::textRunAroundSide() const { return s->textRunAroundSide; } void KoShape::setTextRunAroundSide(TextRunAroundSide side, RunThroughLevel runThrought) { if (side == RunThrough) { if (runThrought == Background) { setRunThrough(-1); } else { setRunThrough(1); } } else { setRunThrough(0); } if ( s->textRunAroundSide == side) { return; } s->textRunAroundSide = side; notifyChanged(); shapeChangedPriv(TextRunAroundChanged); } qreal KoShape::textRunAroundDistanceTop() const { return s->textRunAroundDistanceTop; } void KoShape::setTextRunAroundDistanceTop(qreal distance) { s->textRunAroundDistanceTop = distance; } qreal KoShape::textRunAroundDistanceLeft() const { return s->textRunAroundDistanceLeft; } void KoShape::setTextRunAroundDistanceLeft(qreal distance) { s->textRunAroundDistanceLeft = distance; } qreal KoShape::textRunAroundDistanceRight() const { return s->textRunAroundDistanceRight; } void KoShape::setTextRunAroundDistanceRight(qreal distance) { s->textRunAroundDistanceRight = distance; } qreal KoShape::textRunAroundDistanceBottom() const { return s->textRunAroundDistanceBottom; } void KoShape::setTextRunAroundDistanceBottom(qreal distance) { s->textRunAroundDistanceBottom = distance; } qreal KoShape::textRunAroundThreshold() const { return s->textRunAroundThreshold; } void KoShape::setTextRunAroundThreshold(qreal threshold) { s->textRunAroundThreshold = threshold; } KoShape::TextRunAroundContour KoShape::textRunAroundContour() const { return s->textRunAroundContour; } void KoShape::setTextRunAroundContour(KoShape::TextRunAroundContour contour) { s->textRunAroundContour = contour; } void KoShape::setBackground(QSharedPointer fill) { s->inheritBackground = false; s->fill = fill; shapeChangedPriv(BackgroundChanged); notifyChanged(); } QSharedPointer KoShape::background() const { QSharedPointer bg; if (!s->inheritBackground) { bg = s->fill; } else if (parent()) { bg = parent()->background(); } return bg; } void KoShape::setInheritBackground(bool value) { s->inheritBackground = value; if (s->inheritBackground) { s->fill.clear(); } } bool KoShape::inheritBackground() const { return s->inheritBackground; } void KoShape::setZIndex(qint16 zIndex) { if (s->zIndex == zIndex) return; s->zIndex = zIndex; notifyChanged(); } int KoShape::runThrough() const { return s->runThrough; } void KoShape::setRunThrough(short int runThrough) { s->runThrough = runThrough; } void KoShape::setVisible(bool on) { int _on = (on ? 1 : 0); if (s->visible == _on) return; s->visible = _on; } bool KoShape::isVisible(bool recursive) const { if (!recursive) return s->visible; if (!s->visible) return false; KoShapeContainer * parentShape = parent(); if (parentShape) { return parentShape->isVisible(true); } return true; } void KoShape::setPrintable(bool on) { s->printable = on; } bool KoShape::isPrintable() const { if (s->visible) return s->printable; else return false; } void KoShape::setSelectable(bool selectable) { s->selectable = selectable; } bool KoShape::isSelectable() const { return s->selectable; } void KoShape::setGeometryProtected(bool on) { s->geometryProtected = on; } bool KoShape::isGeometryProtected() const { return s->geometryProtected; } void KoShape::setContentProtected(bool protect) { s->protectContent = protect; } bool KoShape::isContentProtected() const { return s->protectContent; } KoShapeContainer *KoShape::parent() const { return d->parent; } void KoShape::setKeepAspectRatio(bool keepAspect) { s->keepAspect = keepAspect; shapeChangedPriv(KeepAspectRatioChange); notifyChanged(); } bool KoShape::keepAspectRatio() const { return s->keepAspect; } QString KoShape::shapeId() const { return s->shapeId; } void KoShape::setShapeId(const QString &id) { s->shapeId = id; } KoShapeStrokeModelSP KoShape::stroke() const { KoShapeStrokeModelSP stroke; if (!s->inheritStroke) { stroke = s->stroke; } else if (parent()) { stroke = parent()->stroke(); } return stroke; } void KoShape::setStroke(KoShapeStrokeModelSP stroke) { s->inheritStroke = false; s->stroke = stroke; shapeChangedPriv(StrokeChanged); notifyChanged(); } void KoShape::setInheritStroke(bool value) { s->inheritStroke = value; if (s->inheritStroke) { s->stroke.clear(); } } bool KoShape::inheritStroke() const { return s->inheritStroke; } void KoShape::setShadow(KoShapeShadow *shadow) { if (s->shadow) s->shadow->deref(); s->shadow = shadow; if (s->shadow) { s->shadow->ref(); // TODO update changed area } shapeChangedPriv(ShadowChanged); notifyChanged(); } KoShapeShadow *KoShape::shadow() const { return s->shadow; } void KoShape::setClipPath(KoClipPath *clipPath) { s->clipPath.reset(clipPath); shapeChangedPriv(ClipPathChanged); notifyChanged(); } KoClipPath * KoShape::clipPath() const { return s->clipPath.data(); } void KoShape::setClipMask(KoClipMask *clipMask) { s->clipMask.reset(clipMask); shapeChangedPriv(ClipMaskChanged); notifyChanged(); } KoClipMask* KoShape::clipMask() const { return s->clipMask.data(); } QTransform KoShape::transform() const { return s->localMatrix; } QString KoShape::name() const { return s->name; } void KoShape::setName(const QString &name) { s->name = name; } void KoShape::waitUntilReady(bool asynchronous) const { Q_UNUSED(asynchronous); } bool KoShape::isShapeEditable(bool recursive) const { if (!s->visible || s->geometryProtected) return false; if (recursive && d->parent) { return d->parent->isShapeEditable(true); } return true; } KisHandlePainterHelper KoShape::createHandlePainterHelperView(QPainter *painter, KoShape *shape, const KoViewConverter &converter, qreal handleRadius) { const QTransform originalPainterTransform = painter->transform(); painter->setTransform(shape->absoluteTransformation() * converter.documentToView() * painter->transform()); // move c-tor return KisHandlePainterHelper(painter, originalPainterTransform, handleRadius); } KisHandlePainterHelper KoShape::createHandlePainterHelperDocument(QPainter *painter, KoShape *shape, qreal handleRadius) { const QTransform originalPainterTransform = painter->transform(); painter->setTransform(shape->absoluteTransformation() * painter->transform()); // move c-tor return KisHandlePainterHelper(painter, originalPainterTransform, handleRadius); } QPointF KoShape::shapeToDocument(const QPointF &point) const { return absoluteTransformation().map(point); } QRectF KoShape::shapeToDocument(const QRectF &rect) const { return absoluteTransformation().mapRect(rect); } QPointF KoShape::documentToShape(const QPointF &point) const { return absoluteTransformation().inverted().map(point); } QRectF KoShape::documentToShape(const QRectF &rect) const { return absoluteTransformation().inverted().mapRect(rect); } bool KoShape::addDependee(KoShape *shape) { if (! shape) return false; // refuse to establish a circular dependency if (shape->hasDependee(this)) return false; if (! d->dependees.contains(shape)) d->dependees.append(shape); return true; } void KoShape::removeDependee(KoShape *shape) { int index = d->dependees.indexOf(shape); if (index >= 0) d->dependees.removeAt(index); } bool KoShape::hasDependee(KoShape *shape) const { return d->dependees.contains(shape); } QList KoShape::dependees() const { return d->dependees; } void KoShape::shapeChanged(ChangeType type, KoShape *shape) { Q_UNUSED(type); Q_UNUSED(shape); } KoSnapData KoShape::snapData() const { return KoSnapData(); } void KoShape::setAdditionalAttribute(const QString &name, const QString &value) { s->additionalAttributes.insert(name, value); } void KoShape::removeAdditionalAttribute(const QString &name) { s->additionalAttributes.remove(name); } bool KoShape::hasAdditionalAttribute(const QString &name) const { return s->additionalAttributes.contains(name); } QString KoShape::additionalAttribute(const QString &name) const { return s->additionalAttributes.value(name); } void KoShape::setAdditionalStyleAttribute(const char *name, const QString &value) { s->additionalStyleAttributes.insert(name, value); } void KoShape::removeAdditionalStyleAttribute(const char *name) { s->additionalStyleAttributes.remove(name); } KoFilterEffectStack *KoShape::filterEffectStack() const { return s->filterEffectStack; } void KoShape::setFilterEffectStack(KoFilterEffectStack *filterEffectStack) { if (s->filterEffectStack) s->filterEffectStack->deref(); s->filterEffectStack = filterEffectStack; if (s->filterEffectStack) { s->filterEffectStack->ref(); } notifyChanged(); } QSet KoShape::toolDelegates() const { return d->toolDelegates; } void KoShape::setToolDelegates(const QSet &delegates) { d->toolDelegates = delegates; } QString KoShape::hyperLink () const { return s->hyperLink; } void KoShape::setHyperLink(const QString &hyperLink) { s->hyperLink = hyperLink; } KoShape::ShapeChangeListener::~ShapeChangeListener() { Q_FOREACH(KoShape *shape, m_registeredShapes) { shape->removeShapeChangeListener(this); } } void KoShape::ShapeChangeListener::registerShape(KoShape *shape) { KIS_SAFE_ASSERT_RECOVER_RETURN(!m_registeredShapes.contains(shape)); m_registeredShapes.append(shape); } void KoShape::ShapeChangeListener::unregisterShape(KoShape *shape) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_registeredShapes.contains(shape)); m_registeredShapes.removeAll(shape); } void KoShape::ShapeChangeListener::notifyShapeChangedImpl(KoShape::ChangeType type, KoShape *shape) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_registeredShapes.contains(shape)); notifyShapeChanged(type, shape); if (type == KoShape::Deleted) { unregisterShape(shape); } } void KoShape::addShapeChangeListener(KoShape::ShapeChangeListener *listener) { KIS_SAFE_ASSERT_RECOVER_RETURN(!d->listeners.contains(listener)); listener->registerShape(this); d->listeners.append(listener); } void KoShape::removeShapeChangeListener(KoShape::ShapeChangeListener *listener) { KIS_SAFE_ASSERT_RECOVER_RETURN(d->listeners.contains(listener)); d->listeners.removeAll(listener); listener->unregisterShape(this); } QList KoShape::listeners() const { return d->listeners; } QList KoShape::linearizeSubtree(const QList &shapes) { QList result; Q_FOREACH (KoShape *shape, shapes) { result << shape; KoShapeContainer *container = dynamic_cast(shape); if (container) { result << linearizeSubtree(container->shapes()); } } return result; } QList KoShape::linearizeSubtreeSorted(const QList &shapes) { QList sortedShapes = shapes; std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex); QList result; Q_FOREACH (KoShape *shape, sortedShapes) { result << shape; KoShapeContainer *container = dynamic_cast(shape); if (container) { result << linearizeSubtreeSorted(container->shapes()); } } return result; } diff --git a/libs/flake/KoShapeLoadingContext.h b/libs/flake/KoShapeLoadingContext.h index de1e3ad863..96c09f14cc 100644 --- a/libs/flake/KoShapeLoadingContext.h +++ b/libs/flake/KoShapeLoadingContext.h @@ -1,199 +1,200 @@ /* This file is part of the KDE project Copyright (C) 2007 Thorsten Zachmann Copyright (C) 2007 Jan Hambrecht Copyright (C) 2014-2015 Denis Kuplyakov 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 KOSHAPELOADINGCONTEXT_H #define KOSHAPELOADINGCONTEXT_H #include #include #include #include "kritaflake_export.h" class KoShapeLayer; class KoShape; class KoShapeControllerBase; class KoLoadingShapeUpdater; class KoImageCollection; class KoSharedLoadingData; class KoDocumentResourceManager; class KoSectionModel; class QVariant; class QObject; class KoStore; /** * Context passed to shapes during loading. * This class holds various variables as well as a context full of variables which all together * form the context of a loading operation. */ class KRITAFLAKE_EXPORT KoShapeLoadingContext { public: /** * Struct to store data about additional attributes that should be loaded during * the shape loading. * * Make sure all parameters point to const char * that stay around. e.g. The a KoXmlNS or * a "tag" defined string e.g. * AdditionalAttributeData( KoXmlNS::presentation, "placeholder", presentation:placeholder" ) */ struct AdditionalAttributeData { AdditionalAttributeData(const QString &ns, const QString &tag, const QString &name) : ns(ns) , tag(tag) , name(name) { } const QString ns; const QString tag; const QString name; bool operator==(const AdditionalAttributeData &other) const { return name == other.name; } }; /** * constructor * @param context the context created for generic ODF loading. * @param documentResources the data of the shape controller. */ KoShapeLoadingContext(KoStore *store, KoDocumentResourceManager *documentResources); /// destructor ~KoShapeLoadingContext(); KoStore *store() const; QString mimeTypeForPath(const QString &href, bool b = true); /// Returns layer referenced by given name KoShapeLayer *layer(const QString &layerName); /// Adds a new layer to be referenced by the given name later void addLayer(KoShapeLayer *layer, const QString &layerName); /** * remove all layers * * This can be used for loading different layer sets per page. */ void clearLayers(); /// register the id for a specific shape void addShapeId(KoShape *shape, const QString &id); + /// return the shape formerly registered using addShapeId() KoShape *shapeById(const QString &id); /// register the id for a specific shape sub item void addShapeSubItemId(KoShape *shape, const QVariant &subItem, const QString &id); /// return the shape and subitem formerly registered using addShapeSubItemId() QPair shapeSubItemById(const QString &id); /** * call function on the shapeUpdater when the shape with the id shapeid is inserted * After that destroy the updater. */ void updateShape(const QString &id, KoLoadingShapeUpdater *shapeUpdater); /** * this checks if there is an updater for this shape if yes it calls it * this needs to be done via the shape id and */ void shapeLoaded(KoShape *shape); /// Returns the image collection for loading images KoImageCollection *imageCollection(); /// Get current z-index int zIndex(); /// Set z-index void setZIndex(int index); /** * Add shared data * * This can be use to pass data between shapes on loading. E.g. The decoded text styles * of the TextShape. With that the styles only have to be read once and can be used in * all shapes that also need them. * * The ownership of the added data is passed to the context. The KoShapeLoadingContext will * delete the added data when it is destroyed. * * Data inserted for a specific id will not be overwritten by calling addSharedData with * the same id again. * * You get an assertion when the id is already existing. * * @see KoSharedLoadingData */ void addSharedData(const QString &id, KoSharedLoadingData *data); /** * Get the shared data. * * @see KoSharedLoadingData * * @param id The id used to identify the shared data. * @return The shared data for the id or 0 if there is no shared data for the id. */ KoSharedLoadingData *sharedData(const QString &id) const; /** * @brief Add an additional attribute that should be loaded during shape loading * * An application can use that to set the data for additional attributes that should be * loaded during shape loading. * If attribute is set it will not change if set again. The tag is used to differentiate * the attributes * * @param attributeData The data describing the additional attribute data */ static void addAdditionalAttributeData(const AdditionalAttributeData &attributeData); /** * @brief Get the additional attribute data for loading of a shape * * This is used by KoShape::loadOdfAttributes to load all additional attributes defined * in the returned set. */ static QSet additionalAttributeData(); KoDocumentResourceManager *documentResourceManager() const; /** * @brief returns the current section model * @return the pointer to KoSectionModel */ KoSectionModel *sectionModel(); /** * @brief sets the section model for the loading context * @param sectionModel the section model to set */ void setSectionModel(KoSectionModel *sectionModel); private: // to allow only the KoShapeRegistry access to the KoShapeControllerBase class Private; Private * const d; }; #endif /* KOSHAPELOADINGCONTEXT_H */ diff --git a/libs/flake/KoShapeSavingContext.cpp b/libs/flake/KoShapeSavingContext.cpp index 8b9696bab0..c729cccbf3 100644 --- a/libs/flake/KoShapeSavingContext.cpp +++ b/libs/flake/KoShapeSavingContext.cpp @@ -1,332 +1,269 @@ /* This file is part of the KDE project Copyright (C) 2004-2006 David Faure Copyright (C) 2007-2009, 2011 Thorsten Zachmann Copyright (C) 2007 Jan Hambrecht Copyright (C) 2010 Benjamin Port 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 "KoShapeSavingContext.h" #include "KoDataCenterBase.h" #include "KoShapeLayer.h" #include "KoImageData.h" #include "KoMarker.h" #include #include #include #include -#include - #include #include #include #include class KoShapeSavingContextPrivate { public: KoShapeSavingContextPrivate(KoXmlWriter&, KoGenStyles&); ~KoShapeSavingContextPrivate(); KoXmlWriter *xmlWriter; KoShapeSavingContext::ShapeSavingOptions savingOptions; QList layers; QSet dataCenters; QMap sharedData; QMap imageNames; int imageId; QMap images; QHash shapeOffsets; QMap markerRefs; KoGenStyles& mainStyles; - QMap references; QMap referenceCounters; QMap > prefixedReferences; }; KoShapeSavingContextPrivate::KoShapeSavingContextPrivate(KoXmlWriter &w, KoGenStyles &s) : xmlWriter(&w), savingOptions(0), imageId(0), mainStyles(s) { } KoShapeSavingContextPrivate::~KoShapeSavingContextPrivate() { Q_FOREACH (KoSharedSavingData * data, sharedData) { delete data; } } KoShapeSavingContext::KoShapeSavingContext(KoXmlWriter &xmlWriter, KoGenStyles &mainStyles) : d(new KoShapeSavingContextPrivate(xmlWriter, mainStyles)) { // by default allow saving of draw:id + xml:id addOption(KoShapeSavingContext::DrawId); } KoShapeSavingContext::~KoShapeSavingContext() { delete d; } KoXmlWriter & KoShapeSavingContext::xmlWriter() { return *d->xmlWriter; } void KoShapeSavingContext::setXmlWriter(KoXmlWriter &xmlWriter) { d->xmlWriter = &xmlWriter; } KoGenStyles & KoShapeSavingContext::mainStyles() { return d->mainStyles; } bool KoShapeSavingContext::isSet(ShapeSavingOption option) const { return d->savingOptions & option; } void KoShapeSavingContext::setOptions(ShapeSavingOptions options) { d->savingOptions = options; } KoShapeSavingContext::ShapeSavingOptions KoShapeSavingContext::options() const { return d->savingOptions; } void KoShapeSavingContext::addOption(ShapeSavingOption option) { d->savingOptions = d->savingOptions | option; } void KoShapeSavingContext::removeOption(ShapeSavingOption option) { if (isSet(option)) d->savingOptions = d->savingOptions ^ option; // xor to remove it. } -KoElementReference KoShapeSavingContext::xmlid(const void *referent, const QString& prefix, KoElementReference::GenerationOption counter) -{ - Q_ASSERT(counter == KoElementReference::UUID || (counter == KoElementReference::Counter && !prefix.isEmpty())); - - if (d->references.contains(referent)) { - return d->references[referent]; - } - - KoElementReference ref; - - if (counter == KoElementReference::Counter) { - int referenceCounter = d->referenceCounters[prefix]; - referenceCounter++; - ref = KoElementReference(prefix, referenceCounter); - d->references.insert(referent, ref); - d->referenceCounters[prefix] = referenceCounter; - } - else { - if (!prefix.isEmpty()) { - ref = KoElementReference(prefix); - d->references.insert(referent, ref); - } - else { - d->references.insert(referent, ref); - } - } - - if (!prefix.isNull()) { - d->prefixedReferences[prefix].append(referent); - } - return ref; -} - -KoElementReference KoShapeSavingContext::existingXmlid(const void *referent) -{ - if (d->references.contains(referent)) { - return d->references[referent]; - } - else { - KoElementReference ref; - ref.invalidate(); - return ref; - } -} - -void KoShapeSavingContext::clearXmlIds(const QString &prefix) -{ - - if (d->prefixedReferences.contains(prefix)) { - Q_FOREACH (const void* ptr, d->prefixedReferences[prefix]) { - d->references.remove(ptr); - } - d->prefixedReferences.remove(prefix); - } - - if (d->referenceCounters.contains(prefix)) { - d->referenceCounters[prefix] = 0; - } -} - void KoShapeSavingContext::addLayerForSaving(const KoShapeLayer *layer) { if (layer && ! d->layers.contains(layer)) d->layers.append(layer); } void KoShapeSavingContext::saveLayerSet(KoXmlWriter &xmlWriter) const { xmlWriter.startElement("draw:layer-set"); Q_FOREACH (const KoShapeLayer * layer, d->layers) { xmlWriter.startElement("draw:layer"); xmlWriter.addAttribute("draw:name", layer->name()); if (layer->isGeometryProtected()) xmlWriter.addAttribute("draw:protected", "true"); if (! layer->isVisible(false)) xmlWriter.addAttribute("draw:display", "none"); xmlWriter.endElement(); // draw:layer } xmlWriter.endElement(); // draw:layer-set } void KoShapeSavingContext::clearLayers() { d->layers.clear(); } QString KoShapeSavingContext::imageHref(const KoImageData *image) { QMap::iterator it(d->imageNames.find(image->key())); if (it == d->imageNames.end()) { QString suffix = image->suffix(); if (suffix.isEmpty()) { it = d->imageNames.insert(image->key(), QString("Pictures/image%1").arg(++d->imageId)); } else { it = d->imageNames.insert(image->key(), QString("Pictures/image%1.%2").arg(++d->imageId).arg(suffix)); } } return it.value(); } QString KoShapeSavingContext::imageHref(const QImage &image) { // TODO this can be optimized to recognize images which have the same content // Also this can use quite a lot of memory as the qimage are all kept until // they are saved to the store in memory QString href = QString("Pictures/image%1.png").arg(++d->imageId); d->images.insert(href, image); return href; } QMap KoShapeSavingContext::imagesToSave() { return d->imageNames; } QString KoShapeSavingContext::markerRef(const KoMarker */*marker*/) { return QString(); } void KoShapeSavingContext::addDataCenter(KoDataCenterBase * dataCenter) { if (dataCenter) { d->dataCenters.insert(dataCenter); } } bool KoShapeSavingContext::saveDataCenter(KoStore *store, KoXmlWriter* manifestWriter) { bool ok = true; Q_FOREACH (KoDataCenterBase *dataCenter, d->dataCenters) { ok = ok && dataCenter->completeSaving(store, manifestWriter, this); //debugFlake << "ok" << ok; } // Save images for (QMap::iterator it(d->images.begin()); it != d->images.end(); ++it) { if (store->open(it.key())) { KoStoreDevice device(store); ok = ok && it.value().save(&device, "PNG"); store->close(); // TODO error handling if (ok) { const QString mimetype = KisMimeDatabase::mimeTypeForFile(it.key(), false); manifestWriter->addManifestEntry(it.key(), mimetype); } else { warnFlake << "saving image failed"; } } else { ok = false; warnFlake << "saving image failed: open store failed"; } } return ok; } void KoShapeSavingContext::addSharedData(const QString &id, KoSharedSavingData * data) { QMap::iterator it(d->sharedData.find(id)); // data will not be overwritten if (it == d->sharedData.end()) { d->sharedData.insert(id, data); } else { warnFlake << "The id" << id << "is already registered. Data not inserted"; Q_ASSERT(it == d->sharedData.end()); } } KoSharedSavingData * KoShapeSavingContext::sharedData(const QString &id) const { KoSharedSavingData * data = 0; QMap::const_iterator it(d->sharedData.constFind(id)); if (it != d->sharedData.constEnd()) { data = it.value(); } return data; } void KoShapeSavingContext::addShapeOffset(const KoShape *shape, const QTransform &m) { d->shapeOffsets.insert(shape, m); } void KoShapeSavingContext::removeShapeOffset(const KoShape *shape) { d->shapeOffsets.remove(shape); } QTransform KoShapeSavingContext::shapeOffset(const KoShape *shape) const { return d->shapeOffsets.value(shape, QTransform()); } diff --git a/libs/flake/KoShapeSavingContext.h b/libs/flake/KoShapeSavingContext.h index b520795043..103e1e869e 100644 --- a/libs/flake/KoShapeSavingContext.h +++ b/libs/flake/KoShapeSavingContext.h @@ -1,282 +1,254 @@ /* This file is part of the KDE project Copyright (C) 2004-2006 David Faure Copyright (C) 2007-2008 Thorsten Zachmann Copyright (C) 2007 Jan Hambrecht Copyright (C) 2010 Benjamin Port 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 KOSHAPESAVINGCONTEXT_H #define KOSHAPESAVINGCONTEXT_H #include "kritaflake_export.h" -#include #include class KoShape; class KoXmlWriter; class KoGenStyles; class KoDataCenterBase; class KoImageData; class KoMarker; class KoShapeLayer; class KoStore; class KoSharedSavingData; class KoShapeSavingContextPrivate; class QImage; class QTransform; /** * The set of data for the ODF file format used during saving of a shape. */ class KRITAFLAKE_EXPORT KoShapeSavingContext { public: /// The Style used for saving the shape enum ShapeSavingOption { /** * If set the style of family presentation is used, when not set the * family graphic is used. * See OpenDocument 9.2.15 Common Drawing Shape Attributes / Style */ PresentationShape = 1, /** * Save the draw:id used for referencing the shape. If draw:id is saved, xml:id is also * saved. * See OpenDocument 9.2.15 Common Drawing Shape Attributes / ID */ DrawId = 2, /** * If set the automatic style will be marked as being needed in styles.xml */ AutoStyleInStyleXml = 4, /** * If set duplicate master pages will be merged to one */ UniqueMasterPages = 8, /** * If set the z-index is saved in the shape */ ZIndex = 16 }; Q_DECLARE_FLAGS(ShapeSavingOptions, ShapeSavingOption) /** * @brief Constructor * @param xmlWriter used for writing the xml * @param mainStyles for saving the styles * @param embeddedSaver for saving embedded documents */ KoShapeSavingContext(KoXmlWriter &xmlWriter, KoGenStyles &mainStyles); virtual ~KoShapeSavingContext(); /** * @brief Get the xml writer * * @return xmlWriter */ KoXmlWriter &xmlWriter(); /** * @brief Set the xml writer * * Change the xmlWriter that is used in the Context e.g. for saving to styles.xml * instead of content.xml * * @param xmlWriter to use */ void setXmlWriter(KoXmlWriter &xmlWriter); /** * @brief Get the main styles * * @return main styles */ KoGenStyles &mainStyles(); /** * @brief Check if an option is set * * @return ture if the option is set, false otherwise */ bool isSet(ShapeSavingOption option) const; /** * @brief Set the options to use * * @param options to use */ void setOptions(ShapeSavingOptions options); /// add an option to the set of options stored on this context, will leave the other options intact. void addOption(ShapeSavingOption option); /// remove an option, will leave the other options intact. void removeOption(ShapeSavingOption option); /** * @brief Get the options used * * @return options used */ ShapeSavingOptions options() const; - - /** - * @brief xmlid returns an element reference that can be related to the given referent. If there is a - * prefix given, this prefix will be used in addition to either the counter or the uuid. - * @param referent the object we are referring to - * @param prefix a prefix for the xml:id string - * @param counter if counter is true, shapesavingcontext will use a counter to create the xml:id - * @return a KoElementReference; if insert is false and referent doesn't exist yet in the list, the elementreference will be invalid. - */ - KoElementReference xmlid(const void *referent, const QString& prefix = QString(), KoElementReference::GenerationOption counter = KoElementReference::UUID); - - /** - * @brief existingXmlid retrieve an existing xml id or invalid xml id if the referent object doesn't exist - */ - KoElementReference existingXmlid(const void *referent); - - /** - * @brief Clear out all given draw ids - * @param prefix: removes all xml:id's that have the given prefix. - * - * This is needed for checking if master pages are the same. In normal saving - * this should not be called. - * - * @see KoPAPastePage::process - */ - void clearXmlIds(const QString &prefix); - /** * Adds a layer to save into a layer-set in styles.xml according to 9.1.2/9.1.3 odf spec * @param layer the layer to save */ void addLayerForSaving(const KoShapeLayer *layer); /** * Saves the layers added with addLayerForSaving to the xml writer */ void saveLayerSet(KoXmlWriter &xmlWriter) const; /** * remove all layers * * This can be used for saving different layer sets per page. */ void clearLayers(); /** * Get the image href under which the image will be saved in the store */ QString imageHref(const KoImageData *image); /** * Get the image href under which the image will be save in the store * * This should only be used for temporary images that are onle there during * saving, e.g. a pixmap representation of a draw:frame */ QString imageHref(const QImage &image); /** * Get the images that needs to be saved to the store */ QMap imagesToSave(); /** * Get the reference to use for the marker lookup */ QString markerRef(const KoMarker *marker); /** * Add data center */ void addDataCenter(KoDataCenterBase *dataCenter); /** * Save the data centers * * This calls KoDataCenterBase::completeSaving() * @returns false if an error occurred, which typically cancels the save. */ bool saveDataCenter(KoStore *store, KoXmlWriter *manifestWriter); /** * Add shared data * * This can be use to pass data between shapes on saving. E.g. The presentation page layout * styles. With that e.g. the styles only need to be saved once and can be used everywhere * without creating them again. * * The ownership of the added data is passed to the context. The KoShapeSavingContext will * delete the added data when it is destroyed. * * Data inserted for a specific id will not be overwritten by calling addSharedData with * the same id again. * * You get an assertion when the id is already existing. * * @see KoSharedSavingData */ void addSharedData(const QString &id, KoSharedSavingData *data); /** * Get the shared data. * * @see KoSharedLoadingData * * @param id The id used to identify the shared data. * @return The shared data for the id or 0 if there is no shared data for the id. */ KoSharedSavingData *sharedData(const QString &id) const; /** * Add an offset that will be applied to the shape position when saved * * This is needed e.g. for shapes anchored to a text shape as the position is * saved as offset to the anchor. * * @param shape The shape for which the offset should be added. * @param matrix The offset which should be applied on saving the position. */ void addShapeOffset(const KoShape *shape, const QTransform &matrix); /** * Remove an offset from the saved offset list * * @param shape The shape for which the offset should be removed. */ void removeShapeOffset(const KoShape *shape); /** * Get the offest that will be applied to the shape position when saved. * * @param shape The shape for which the offset should be get. * @return the saved offset or QTransform() when offset is not set. */ QTransform shapeOffset(const KoShape *shape) const; private: KoShapeSavingContextPrivate * const d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(KoShapeSavingContext::ShapeSavingOptions) #endif // KOSHAPESAVINGCONTEXT_H diff --git a/libs/odf/CMakeLists.txt b/libs/odf/CMakeLists.txt index 67b459629b..8cc3d972de 100644 --- a/libs/odf/CMakeLists.txt +++ b/libs/odf/CMakeLists.txt @@ -1,26 +1,25 @@ add_subdirectory( tests ) set(kritaodf_LIB_SRCS KoGenStyle.cpp KoGenStyles.cpp KoFontFace.cpp KoOdfStylesReader.cpp KoOdfReadStore.cpp KoOdfWriteStore.cpp KoStyleStack.cpp KoOdfGraphicStyles.cpp KoShadowStyle.cpp - KoElementReference.cpp OdfDebug.cpp ) add_library(kritaodf SHARED ${kritaodf_LIB_SRCS}) generate_export_header(kritaodf BASE_NAME kritaodf) target_link_libraries(kritaodf kritaglobal kritaversion kritaplugin kritastore KF5::CoreAddons KF5::ConfigCore KF5::I18n Qt5::PrintSupport Qt5::Gui Qt5::Xml) set_target_properties(kritaodf PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritaodf ${INSTALL_TARGETS_DEFAULT_ARGS} ) diff --git a/libs/odf/KoElementReference.cpp b/libs/odf/KoElementReference.cpp deleted file mode 100644 index d58c2bbfd8..0000000000 --- a/libs/odf/KoElementReference.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2011 Boudewijn Rempt - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser 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 "KoElementReference.h" - -#include "KoXmlReader.h" -#include "KoXmlWriter.h" -#include - -KoElementReference::KoElementReference() - : d(new KoElementReferenceData()) -{ - d->xmlid = "id-" + d->xmlid; -} - -KoElementReference::KoElementReference(const QString &prefix) - : d(new KoElementReferenceData) -{ - d->xmlid = prefix + "-" + d->xmlid; -} - -KoElementReference::KoElementReference(const QString &prefix, int counter) - : d(new KoElementReferenceData) -{ - d->xmlid = QString("%1-%2").arg(prefix).arg(counter); -} - -KoElementReference::KoElementReference(const KoElementReference &other) - : d(other.d) -{ -} - -KoElementReference &KoElementReference::operator=(const KoElementReference &rhs) -{ - if (this == &rhs) return *this; - d = rhs.d; - - return *this; -} - -bool KoElementReference::operator==(const KoElementReference &other) const -{ - return d->xmlid == other.d->xmlid; -} - -bool KoElementReference::operator!=(const KoElementReference &other) const -{ - return !(*this == other); -} - -bool KoElementReference::isValid() const -{ - return (!d->xmlid.isEmpty()); -} - -QString KoElementReference::toString() const -{ - return d->xmlid; -} - - -void KoElementReference::invalidate() -{ - d->xmlid.clear(); -} diff --git a/libs/odf/KoElementReference.h b/libs/odf/KoElementReference.h deleted file mode 100644 index 40b8584a4b..0000000000 --- a/libs/odf/KoElementReference.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2011-2012 Boudewijn Rempt - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser 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 KOELEMENTREFERENCE_H -#define KOELEMENTREFERENCE_H - -#include -#include -#include - -#include "KoXmlReaderForward.h" - -#include "kritaodf_export.h" - -class KoXmlWriter; - -class KoElementReferenceData : public QSharedData -{ -public: - - KoElementReferenceData() - { - xmlid = QUuid::createUuid().toString(); - xmlid.remove('{'); - xmlid.remove('}'); - } - - KoElementReferenceData(const KoElementReferenceData &other) - : QSharedData(other) - , xmlid(other.xmlid) - { - } - - ~KoElementReferenceData() {} - - QString xmlid; -}; - -/** - * KoElementReference is used to store unique identifiers for elements in an odf document. - * Element references are saved as xml:id and optionally for compatibility also as draw:id - * and text:id. - * - * You can use element references wherever you would have used a QString to refer to the id - * of an object. - * - * Element references are implicitly shared, so you can and should pass them along by value. - */ -class KRITAODF_EXPORT KoElementReference -{ -public: - - enum GenerationOption { - UUID = 0, - Counter = 1 - }; - - enum SaveOption { - XmlId = 0x0, - DrawId = 0x1, - TextId = 0x2 - }; - Q_DECLARE_FLAGS(SaveOptions, SaveOption) - - KoElementReference(); - explicit KoElementReference(const QString &prefix); - KoElementReference(const QString &prefix, int counter); - KoElementReference(const KoElementReference &other); - KoElementReference &operator=(const KoElementReference &rhs); - bool operator==(const KoElementReference &other) const; - bool operator!=(const KoElementReference &other) const; - - /** - * @return true if the xmlid is valid, i.e., not null - */ - bool isValid() const; - - /** - * @brief toString creates a QString from the element reference - * @return a string that represents the element. Can be used in maps etc. - */ - QString toString() const; - - /** - * Invalidate the reference - */ - void invalidate(); - - -private: - - QSharedDataPointer d; -}; - - -Q_DECLARE_OPERATORS_FOR_FLAGS(KoElementReference::SaveOptions) - -#endif // KOELEMENTREFERENCE_H diff --git a/libs/odf/tests/CMakeLists.txt b/libs/odf/tests/CMakeLists.txt index a4c6cd22c0..2db29fb8a7 100644 --- a/libs/odf/tests/CMakeLists.txt +++ b/libs/odf/tests/CMakeLists.txt @@ -1,19 +1,18 @@ set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) include(ECMAddTests) include(KritaAddBrokenUnitTest) ecm_add_tests( TestKoGenStyles.cpp TestStorage.cpp NAME_PREFIX "libs-odf-" LINK_LIBRARIES kritaodf KF5::I18n Qt5::Test) ecm_add_tests( TestXmlWriter.cpp kodomtest.cpp TestKoUnit.cpp - TestKoElementReference.cpp NAME_PREFIX "libs-odf-" LINK_LIBRARIES kritaodf Qt5::Test) diff --git a/libs/odf/tests/TestKoElementReference.cpp b/libs/odf/tests/TestKoElementReference.cpp deleted file mode 100644 index 211ecf3ddd..0000000000 --- a/libs/odf/tests/TestKoElementReference.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2011 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. - */ -#include "TestKoElementReference.h" - -#include - -#include - -void TestKoElementReference::testElementReference() -{ - KoElementReference ref1; - KoElementReference ref2; - - QVERIFY(ref1 != ref2); - - KoElementReference ref3(ref1); - QVERIFY(ref1 == ref3); - - { - KoElementReference ref4; - ref3 = ref4; - - QVERIFY(ref3 == ref4); - } - - QVERIFY(ref3 != ref1); -} - -QTEST_MAIN(TestKoElementReference) diff --git a/libs/odf/tests/TestKoElementReference.h b/libs/odf/tests/TestKoElementReference.h deleted file mode 100644 index ac7f0c3084..0000000000 --- a/libs/odf/tests/TestKoElementReference.h +++ /dev/null @@ -1,33 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2011 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 TESTKOELEMENTREFERENCE_H -#define TESTKOELEMENTREFERENCE_H - -#include - -class TestKoElementReference : public QObject -{ - Q_OBJECT - -private Q_SLOTS: - - void testElementReference(); -}; - -#endif diff --git a/libs/ui/flake/kis_shape_selection.cpp b/libs/ui/flake/kis_shape_selection.cpp index c82489462f..c5912433da 100644 --- a/libs/ui/flake/kis_shape_selection.cpp +++ b/libs/ui/flake/kis_shape_selection.cpp @@ -1,339 +1,338 @@ /* * Copyright (c) 2010 Sven Langkamp * Copyright (c) 2011 Jan Hambrecht * * 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_shape_selection.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 "kis_shape_selection_model.h" #include "kis_shape_selection_canvas.h" #include "kis_take_all_shapes_command.h" #include "kis_image_view_converter.h" #include "kis_shape_layer.h" #include KisShapeSelection::KisShapeSelection(KoShapeControllerBase *shapeControllerBase, KisImageWSP image, KisSelectionWSP selection) : KoShapeLayer(m_model = new KisShapeSelectionModel(image, selection, this)) , m_image(image) , m_shapeControllerBase(shapeControllerBase) { Q_ASSERT(m_image); setShapeId("KisShapeSelection"); setSelectable(false); m_converter = new KisImageViewConverter(image); m_canvas = new KisShapeSelectionCanvas(shapeControllerBase); m_canvas->shapeManager()->addShape(this); m_model->setObjectName("KisShapeSelectionModel"); m_model->moveToThread(image->thread()); m_canvas->setObjectName("KisShapeSelectionCanvas"); m_canvas->moveToThread(image->thread()); connect(this, SIGNAL(sigMoveShapes(QPointF)), SLOT(slotMoveShapes(QPointF))); } KisShapeSelection::~KisShapeSelection() { m_model->setShapeSelection(0); delete m_canvas; delete m_converter; } KisShapeSelection::KisShapeSelection(const KisShapeSelection& rhs, KisSelection* selection) : KoShapeLayer(m_model = new KisShapeSelectionModel(rhs.m_image, selection, this)) { m_image = rhs.m_image; m_shapeControllerBase = rhs.m_shapeControllerBase; m_converter = new KisImageViewConverter(m_image); m_canvas = new KisShapeSelectionCanvas(m_shapeControllerBase); // TODO: refactor shape selection to pass signals // via KoShapeManager, not via the model m_canvas->shapeManager()->setUpdatesBlocked(true); m_model->setUpdatesEnabled(false); m_canvas->shapeManager()->addShape(this); Q_FOREACH (KoShape *shape, rhs.shapes()) { KoShape *clonedShape = shape->cloneShape(); KIS_SAFE_ASSERT_RECOVER(clonedShape) { continue; } this->addShape(clonedShape); } m_canvas->shapeManager()->setUpdatesBlocked(false); m_model->setUpdatesEnabled(true); } KisSelectionComponent* KisShapeSelection::clone(KisSelection* selection) { /** * TODO: make cloning of vector selections safe! Right now it crashes * on Windows because of manipulations with timers from non-gui thread. */ KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == qApp->thread()); return new KisShapeSelection(*this, selection); } bool KisShapeSelection::saveSelection(KoStore * store) const { const QSizeF sizeInPx = m_image->bounds().size(); const QSizeF sizeInPt(sizeInPx.width() / m_image->xRes(), sizeInPx.height() / m_image->yRes()); return KisShapeLayer::saveShapesToStore(store, this->shapes(), sizeInPt); } bool KisShapeSelection::loadSelection(KoStore* store) { QSizeF fragmentSize; // unused! // FIXME: we handle xRes() only! KIS_SAFE_ASSERT_RECOVER_NOOP(qFuzzyCompare(m_image->xRes(), m_image->yRes())); const qreal resolutionPPI = 72.0 * m_image->xRes(); QList shapes; if (store->open("content.svg")) { KoStoreDevice storeDev(store); storeDev.open(QIODevice::ReadOnly); shapes = KisShapeLayer::createShapesFromSvg(&storeDev, "", m_image->bounds(), resolutionPPI, m_canvas->shapeController()->resourceManager(), &fragmentSize); store->close(); Q_FOREACH (KoShape *shape, shapes) { addShape(shape); } return true; } return false; } void KisShapeSelection::setUpdatesEnabled(bool enabled) { m_model->setUpdatesEnabled(enabled); } bool KisShapeSelection::updatesEnabled() const { return m_model->updatesEnabled(); } KUndo2Command* KisShapeSelection::resetToEmpty() { return new KisTakeAllShapesCommand(this, true, false); } bool KisShapeSelection::isEmpty() const { return !m_model->count(); } QPainterPath KisShapeSelection::outlineCache() const { return m_outline; } bool KisShapeSelection::outlineCacheValid() const { return true; } void KisShapeSelection::recalculateOutlineCache() { QTransform resolutionMatrix; resolutionMatrix.scale(m_image->xRes(), m_image->yRes()); QList shapesList = shapes(); QPainterPath outline; Q_FOREACH (KoShape * shape, shapesList) { /** * WARNING: we should unite all the shapes in image coordinates, * not in points. Boolean operations inside the QPainterPath * linearize the curves into lines and they use absolute values * for thresholds. * * See KritaUtils::pathShapeBooleanSpaceWorkaround() for more info */ QTransform shapeMatrix = shape->absoluteTransformation(); outline = outline.united(resolutionMatrix.map(shapeMatrix.map(shape->outline()))); } m_outline = outline; } void KisShapeSelection::paintComponent(QPainter& painter, KoShapePaintingContext &) const { Q_UNUSED(painter); } void KisShapeSelection::renderToProjection(KisPaintDeviceSP projection) { Q_ASSERT(projection); Q_ASSERT(m_image); QRectF boundingRect = outlineCache().boundingRect(); renderSelection(projection, boundingRect.toAlignedRect()); } void KisShapeSelection::renderToProjection(KisPaintDeviceSP projection, const QRect& r) { Q_ASSERT(projection); renderSelection(projection, r); } void KisShapeSelection::renderSelection(KisPaintDeviceSP projection, const QRect& requestedRect) { KIS_SAFE_ASSERT_RECOVER_RETURN(projection); KIS_SAFE_ASSERT_RECOVER_RETURN(m_image); const qint32 MASK_IMAGE_WIDTH = 256; const qint32 MASK_IMAGE_HEIGHT = 256; const QPainterPath selectionOutline = outlineCache(); if (*projection->defaultPixel().data() == OPACITY_TRANSPARENT_U8) { projection->clear(requestedRect); } else { KoColor transparentColor(Qt::transparent, projection->colorSpace()); projection->fill(requestedRect, transparentColor); } const QRect r = requestedRect & selectionOutline.boundingRect().toAlignedRect(); QImage polygonMaskImage(MASK_IMAGE_WIDTH, MASK_IMAGE_HEIGHT, QImage::Format_ARGB32); QPainter maskPainter(&polygonMaskImage); maskPainter.setRenderHint(QPainter::Antialiasing, true); // Break the mask up into chunks so we don't have to allocate a potentially very large QImage. for (qint32 x = r.x(); x < r.x() + r.width(); x += MASK_IMAGE_WIDTH) { for (qint32 y = r.y(); y < r.y() + r.height(); y += MASK_IMAGE_HEIGHT) { maskPainter.fillRect(polygonMaskImage.rect(), Qt::black); maskPainter.translate(-x, -y); maskPainter.fillPath(selectionOutline, Qt::white); maskPainter.translate(x, y); qint32 rectWidth = qMin(r.x() + r.width() - x, MASK_IMAGE_WIDTH); qint32 rectHeight = qMin(r.y() + r.height() - y, MASK_IMAGE_HEIGHT); KisSequentialIterator it(projection, QRect(x, y, rectWidth, rectHeight)); while (it.nextPixel()) { (*it.rawData()) = qRed(polygonMaskImage.pixel(it.x() - x, it.y() - y)); } } } } KoShapeManager* KisShapeSelection::shapeManager() const { return m_canvas->shapeManager(); } KisShapeSelectionFactory::KisShapeSelectionFactory() : KoShapeFactoryBase("KisShapeSelection", "selection shape container") { setHidden(true); } void KisShapeSelection::moveX(qint32 x) { const QPointF diff(x / m_image->xRes(), 0); emit sigMoveShapes(diff); } void KisShapeSelection::moveY(qint32 y) { const QPointF diff(0, y / m_image->yRes()); emit sigMoveShapes(diff); } void KisShapeSelection::slotMoveShapes(const QPointF &diff) { Q_FOREACH (KoShape* shape, shapeManager()->shapes()) { if (shape != this) { QPointF pos = shape->position(); shape->setPosition(pos + diff); } } } // TODO same code as in vector layer, refactor! KUndo2Command* KisShapeSelection::transform(const QTransform &transform) { QList shapes = m_canvas->shapeManager()->shapes(); if(shapes.isEmpty()) return 0; QTransform realTransform = m_converter->documentToView() * transform * m_converter->viewToDocument(); QList oldTransformations; QList newTransformations; // this code won't work if there are shapes, that inherit the transformation from the parent container. // the chart and tree shapes are examples for that, but they aren't used in krita and there are no other shapes like that. Q_FOREACH (const KoShape* shape, shapes) { QTransform oldTransform = shape->transformation(); oldTransformations.append(oldTransform); if (dynamic_cast(shape)) { newTransformations.append(oldTransform); } else { QTransform globalTransform = shape->absoluteTransformation(); QTransform localTransform = globalTransform * realTransform * globalTransform.inverted(); newTransformations.append(localTransform*oldTransform); } } return new KoShapeTransformCommand(shapes, oldTransformations, newTransformations); }