diff --git a/libs/flake/KoColorBackground.cpp b/libs/flake/KoColorBackground.cpp index 7dd56f3f0a..b52fdef943 100644 --- a/libs/flake/KoColorBackground.cpp +++ b/libs/flake/KoColorBackground.cpp @@ -1,95 +1,93 @@ /* This file is part of the KDE project * Copyright (C) 2008 Jan Hambrecht * * 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 "KoColorBackground.h" #include "KoShapeSavingContext.h" -#include #include -#include #include #include class KoColorBackground::Private : public QSharedData { public: Private() : QSharedData() , color(Qt::black) , style(Qt::SolidPattern) {} QColor color; Qt::BrushStyle style; }; KoColorBackground::KoColorBackground() : KoShapeBackground() , d(new Private) { } KoColorBackground::KoColorBackground(const QColor &color, Qt::BrushStyle style) : KoShapeBackground() , d(new Private) { if (style < Qt::SolidPattern || style >= Qt::LinearGradientPattern) { style = Qt::SolidPattern; } d->style = style; d->color = color; } KoColorBackground::~KoColorBackground() { } bool KoColorBackground::compareTo(const KoShapeBackground *other) const { const KoColorBackground *bg = dynamic_cast(other); return bg && bg->color() == d->color; } QColor KoColorBackground::color() const { return d->color; } void KoColorBackground::setColor(const QColor &color) { d->color = color; } Qt::BrushStyle KoColorBackground::style() const { return d->style; } QBrush KoColorBackground::brush() const { return QBrush(d->color, d->style); } void KoColorBackground::paint(QPainter &painter, KoShapePaintingContext &/*context*/, const QPainterPath &fillPath) const { painter.setBrush(brush()); painter.drawPath(fillPath); } diff --git a/libs/flake/KoGradientBackground.cpp b/libs/flake/KoGradientBackground.cpp index adeebc713e..85a42231cc 100644 --- a/libs/flake/KoGradientBackground.cpp +++ b/libs/flake/KoGradientBackground.cpp @@ -1,140 +1,138 @@ /* This file is part of the KDE project * Copyright (C) 2008 Jan Hambrecht * * 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 "KoGradientBackground.h" #include "KoFlake.h" -#include #include -#include #include #include #include #include #include #include #include class KoGradientBackground::Private : public QSharedData { public: Private() : QSharedData() , gradient(0) {} QGradient *gradient; QTransform matrix; }; KoGradientBackground::KoGradientBackground(QGradient * gradient, const QTransform &matrix) : KoShapeBackground() , d(new Private) { d->gradient = gradient; d->matrix = matrix; Q_ASSERT(d->gradient); } KoGradientBackground::KoGradientBackground(const QGradient & gradient, const QTransform &matrix) : KoShapeBackground() , d(new Private) { d->gradient = KoFlake::cloneGradient(&gradient); d->matrix = matrix; Q_ASSERT(d->gradient); } KoGradientBackground::~KoGradientBackground() { delete d->gradient; } bool KoGradientBackground::compareTo(const KoShapeBackground *other) const { const KoGradientBackground *otherGradient = dynamic_cast(other); return otherGradient && d->matrix == otherGradient->d->matrix && *d->gradient == *otherGradient->d->gradient; } void KoGradientBackground::setTransform(const QTransform &matrix) { d->matrix = matrix; } QTransform KoGradientBackground::transform() const { return d->matrix; } void KoGradientBackground::setGradient(const QGradient &gradient) { delete d->gradient; d->gradient = KoFlake::cloneGradient(&gradient); Q_ASSERT(d->gradient); } const QGradient * KoGradientBackground::gradient() const { return d->gradient; } void KoGradientBackground::paint(QPainter &painter, KoShapePaintingContext &/*context*/, const QPainterPath &fillPath) const { if (!d->gradient) return; if (d->gradient->coordinateMode() == QGradient::ObjectBoundingMode) { /** * NOTE: important hack! * * Qt has different notation of QBrush::setTransform() in comparison * to what SVG defines. SVG defines gradientToUser matrix to be postmultiplied * by QBrush::transform(), but Qt does exactly reverse! * * That most probably has beed caused by the fact that Qt uses transposed * matrices and someone just mistyped the stuff long ago :( * * So here we basically emulate this feature by converting the gradient into * QGradient::LogicalMode and doing transformations manually. */ const QRectF boundingRect = fillPath.boundingRect(); QTransform gradientToUser(boundingRect.width(), 0, 0, boundingRect.height(), boundingRect.x(), boundingRect.y()); // TODO: how about slicing the object? QGradient g = *d->gradient; g.setCoordinateMode(QGradient::LogicalMode); QBrush b(g); b.setTransform(d->matrix * gradientToUser); painter.setBrush(b); } else { QBrush b(*d->gradient); b.setTransform(d->matrix); painter.setBrush(b); } painter.drawPath(fillPath); } diff --git a/libs/flake/KoHatchBackground.cpp b/libs/flake/KoHatchBackground.cpp index 80eff40587..9aad98fd71 100644 --- a/libs/flake/KoHatchBackground.cpp +++ b/libs/flake/KoHatchBackground.cpp @@ -1,130 +1,129 @@ /* This file is part of the KDE project * * Copyright (C) 2012 Thorsten Zachmann * * 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 "KoHatchBackground.h" -#include #include #include #include #include #include #include #include #include #include #include #include #include class KoHatchBackground::Private : public QSharedData { public: Private() : QSharedData() , angle(0.0) , distance(1.0) , style(KoHatchBackground::Single) {} QColor lineColor; int angle; qreal distance; KoHatchBackground::HatchStyle style; QString name; }; KoHatchBackground::KoHatchBackground() : KoColorBackground() , d(new Private) { } KoHatchBackground::~KoHatchBackground() { } void KoHatchBackground::paint(QPainter &painter, KoShapePaintingContext &context, const QPainterPath &fillPath) const { if (color().isValid()) { // paint background color if set by using the color background KoColorBackground::paint(painter, context, fillPath); } const QRectF targetRect = fillPath.boundingRect(); painter.save(); painter.setClipPath(fillPath); QPen pen(d->lineColor); // we set the pen width to 0.5 pt for the hatch. This is not defined in the spec. pen.setWidthF(0.5); painter.setPen(pen); QVector lines; // The different styles are handled by painting the lines multiple times with a different // angel offset as basically it just means we paint the lines also at a different angle. // This are the angle offsets we need to apply to the different lines of a style. // -90 is for single, 0 for the 2nd line in double and -45 for the 3th line in triple. const int angleOffset[] = {-90, 0, -45 }; // The number of loops is defined by the style. int loops = (d->style == Single) ? 1 : (d->style == Double) ? 2 : 3; for (int i = 0; i < loops; ++i) { int angle = d->angle - angleOffset[i]; qreal cosAngle = ::cos(angle/180.0*M_PI); // if cos is nearly 0 the lines are horizontal. Use a special case for that if (qAbs(cosAngle) > 0.00001) { qreal xDiff = tan(angle/180.0*M_PI) * targetRect.height(); // calculate the distance we need to increase x when creating the lines so that the // distance between the lines is also correct for rotated lines. qreal xOffset = qAbs(d->distance / cosAngle); // if the lines go to the right we need to start more to the left. Get the correct start. qreal xStart = 0; while (-xDiff < xStart) { xStart -= xOffset; } // if the lines go to the left we need to stop more at the right. Get the correct end offset qreal xEndOffset = 0; if (xDiff < 0) { while (xDiff < -xEndOffset) { xEndOffset += xOffset; } } // create line objects. lines.reserve(lines.size() + int((targetRect.width() + xEndOffset - xStart) / xOffset) + 1); for (qreal x = xStart; x < targetRect.width() + xEndOffset; x += xOffset) { lines.append(QLineF(x, 0, x + xDiff, targetRect.height())); } } else { // horizontal lines lines.reserve(lines.size() + int(targetRect.height()/d->distance) + 1); for (qreal y = 0; y < targetRect.height(); y += d->distance) { lines.append(QLineF(0, y, targetRect.width(), y)); } } } painter.drawLines(lines); painter.restore(); } diff --git a/libs/flake/KoPathShape.cpp b/libs/flake/KoPathShape.cpp index bf57464c7d..9a7e0edb0e 100644 --- a/libs/flake/KoPathShape.cpp +++ b/libs/flake/KoPathShape.cpp @@ -1,1403 +1,1402 @@ /* This file is part of the KDE project Copyright (C) 2006-2008, 2010-2011 Thorsten Zachmann Copyright (C) 2006-2011 Jan Hambrecht Copyright (C) 2007-2009 Thomas Zander Copyright (C) 2011 Jean-Nicolas Artaud 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 "KoPathShape.h" #include "KoPathShape_p.h" #include "KoPathSegment.h" #include "KoPathPoint.h" #include "KoShapeStrokeModel.h" #include "KoPathShapeLoader.h" #include "KoShapeSavingContext.h" #include "KoShapeLoadingContext.h" #include "KoShapeShadow.h" #include "KoShapeBackground.h" #include "KoShapeContainer.h" #include "KoFilterEffectStack.h" #include "KoMarker.h" #include "KoShapeStroke.h" #include "KoInsets.h" #include #include #include #include #include -#include #include "KisQPainterStateSaver.h" #include #include #include #include "kis_global.h" #include // for qIsNaN static bool qIsNaNPoint(const QPointF &p) { return qIsNaN(p.x()) || qIsNaN(p.y()); } KoPathShape::Private::Private() : fillRule(Qt::OddEvenFill) , autoFillMarkers(false) { } KoPathShape::Private::Private(const Private &rhs) : fillRule(rhs.fillRule) , markersNew(rhs.markersNew) , autoFillMarkers(rhs.autoFillMarkers) { } QRectF KoPathShape::Private::handleRect(const QPointF &p, qreal radius) const { return QRectF(p.x() - radius, p.y() - radius, 2*radius, 2*radius); } KoPathShape::KoPathShape() : KoTosContainer() , d(new Private) { } KoPathShape::KoPathShape(const KoPathShape &rhs) : KoTosContainer(rhs) , d(new Private(*rhs.d)) { // local data cannot be shared via QSharedData because // every path point holds a pointer to the parent shape KoSubpathList subpaths; Q_FOREACH (KoSubpath *subPath, rhs.d->subpaths) { KoSubpath *clonedSubPath = new KoSubpath(); Q_FOREACH (KoPathPoint *point, *subPath) { *clonedSubPath << new KoPathPoint(*point, this); } subpaths << clonedSubPath; } d->subpaths = subpaths; } KoPathShape::~KoPathShape() { clear(); } KoShape *KoPathShape::cloneShape() const { return new KoPathShape(*this); } void KoPathShape::clear() { Q_FOREACH (KoSubpath *subpath, d->subpaths) { Q_FOREACH (KoPathPoint *point, *subpath) delete point; delete subpath; } d->subpaths.clear(); notifyPointsChanged(); } void KoPathShape::paint(QPainter &painter, KoShapePaintingContext &paintContext) const { KisQPainterStateSaver saver(&painter); QPainterPath path(outline()); path.setFillRule(d->fillRule); if (background()) { background()->paint(painter, paintContext, path); } //d->paintDebug(painter); } #ifndef NDEBUG void KoPathShape::Private::paintDebug(QPainter &painter) { KoSubpathList::const_iterator pathIt(subpaths.constBegin()); int i = 0; QPen pen(Qt::black, 0); painter.save(); painter.setPen(pen); for (; pathIt != subpaths.constEnd(); ++pathIt) { KoSubpath::const_iterator it((*pathIt)->constBegin()); for (; it != (*pathIt)->constEnd(); ++it) { ++i; KoPathPoint *point = (*it); QRectF r(point->point(), QSizeF(5, 5)); r.translate(-2.5, -2.5); QPen pen(Qt::black, 0); painter.setPen(pen); if (point->activeControlPoint1() && point->activeControlPoint2()) { QBrush b(Qt::red); painter.setBrush(b); } else if (point->activeControlPoint1()) { QBrush b(Qt::yellow); painter.setBrush(b); } else if (point->activeControlPoint2()) { QBrush b(Qt::darkYellow); painter.setBrush(b); } painter.drawEllipse(r); } } painter.restore(); debugFlake << "nop =" << i; } void KoPathShape::Private::debugPath() const { KoSubpathList::const_iterator pathIt(subpaths.constBegin()); for (; pathIt != subpaths.constEnd(); ++pathIt) { KoSubpath::const_iterator it((*pathIt)->constBegin()); for (; it != (*pathIt)->constEnd(); ++it) { debugFlake << "p:" << (*pathIt) << "," << *it << "," << (*it)->point() << "," << (*it)->properties(); } } } #endif void KoPathShape::paintPoints(KisHandlePainterHelper &handlesHelper) { KoSubpathList::const_iterator pathIt(d->subpaths.constBegin()); for (; pathIt != d->subpaths.constEnd(); ++pathIt) { KoSubpath::const_iterator it((*pathIt)->constBegin()); for (; it != (*pathIt)->constEnd(); ++it) (*it)->paint(handlesHelper, KoPathPoint::Node); } } QRectF KoPathShape::outlineRect() const { return outline().boundingRect(); } QPainterPath KoPathShape::outline() const { QPainterPath path; for (auto subpathIt = d->subpaths.constBegin(); subpathIt != d->subpaths.constEnd(); ++subpathIt) { const KoSubpath * subpath = *subpathIt; const KoPathPoint * lastPoint = subpath->constFirst(); bool activeCP = false; for (auto pointIt = subpath->constBegin(); pointIt != subpath->constEnd(); ++pointIt) { const KoPathPoint * currPoint = *pointIt; KoPathPoint::PointProperties currProperties = currPoint->properties(); if (currPoint == subpath->constFirst()) { if (currProperties & KoPathPoint::StartSubpath) { Q_ASSERT(!qIsNaNPoint(currPoint->point())); path.moveTo(currPoint->point()); } } else if (activeCP && currPoint->activeControlPoint1()) { Q_ASSERT(!qIsNaNPoint(lastPoint->controlPoint2())); Q_ASSERT(!qIsNaNPoint(currPoint->controlPoint1())); Q_ASSERT(!qIsNaNPoint(currPoint->point())); path.cubicTo( lastPoint->controlPoint2(), currPoint->controlPoint1(), currPoint->point()); } else if (activeCP || currPoint->activeControlPoint1()) { Q_ASSERT(!qIsNaNPoint(lastPoint->controlPoint2())); Q_ASSERT(!qIsNaNPoint(currPoint->controlPoint1())); path.quadTo( activeCP ? lastPoint->controlPoint2() : currPoint->controlPoint1(), currPoint->point()); } else { Q_ASSERT(!qIsNaNPoint(currPoint->point())); path.lineTo(currPoint->point()); } if (currProperties & KoPathPoint::CloseSubpath && currProperties & KoPathPoint::StopSubpath) { // add curve when there is a curve on the way to the first point KoPathPoint * firstPoint = subpath->first(); Q_ASSERT(!qIsNaNPoint(firstPoint->point())); if (currPoint->activeControlPoint2() && firstPoint->activeControlPoint1()) { path.cubicTo( currPoint->controlPoint2(), firstPoint->controlPoint1(), firstPoint->point()); } else if (currPoint->activeControlPoint2() || firstPoint->activeControlPoint1()) { Q_ASSERT(!qIsNaNPoint(currPoint->point())); Q_ASSERT(!qIsNaNPoint(currPoint->controlPoint1())); path.quadTo( currPoint->activeControlPoint2() ? currPoint->controlPoint2() : firstPoint->controlPoint1(), firstPoint->point()); } path.closeSubpath(); } if (currPoint->activeControlPoint2()) { activeCP = true; } else { activeCP = false; } lastPoint = currPoint; } } return path; } QRectF KoPathShape::boundingRect() const { const QTransform transform = absoluteTransformation(); /** * First we approximate the insets of the stroke by rendering a fat bezier curve * with width set to the maximum inset of miters and markers. The are swept by this * curve will be a good approximation of the real curve bounding rect. */ qreal outlineSweepWidth = 0; const QSharedPointer lineBorder = qSharedPointerDynamicCast(stroke()); if (lineBorder) { outlineSweepWidth = lineBorder->lineWidth(); } if (stroke()) { KoInsets inset; stroke()->strokeInsets(this, inset); const qreal maxInset = std::max({inset.left, inset.top, inset.right, inset.bottom}); // insets extend outside the shape, but width extends both inside and outside, // so we should multiply insets by 2.0 outlineSweepWidth = std::max({outlineSweepWidth, 2.0 * maxInset, 2.0 * stroke()->strokeMaxMarkersInset(this)}); } /// NOTE: stroking the entire shape might be too expensive, so try to /// estimate the bounds using insets only... #if 0 QPen pen(Qt::black, outlineSweepWidth); // select round joins and caps to ensure it sweeps exactly // 'outlineSweepWidth' pixels in every possible pen.setJoinStyle(Qt::RoundJoin); pen.setCapStyle(Qt::RoundCap); QRectF bb = transform.map(pathStroke(pen)).boundingRect(); #endif QRectF bb = transform.mapRect(kisGrowRect(outline().boundingRect(), outlineSweepWidth)); if (shadow()) { KoInsets insets; shadow()->insets(insets); bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom); } if (filterEffectStack()) { QRectF clipRect = filterEffectStack()->clipRectForBoundingRect(QRectF(QPointF(), size())); bb |= transform.mapRect(clipRect); } return bb; } QSizeF KoPathShape::size() const { // don't call boundingRect here as it uses absoluteTransformation // which itself uses size() -> leads to infinite recursion return outlineRect().size(); } void KoPathShape::setSize(const QSizeF &newSize) { QTransform matrix(resizeMatrix(newSize)); KoShape::setSize(newSize); d->map(matrix); } QTransform KoPathShape::resizeMatrix(const QSizeF & newSize) const { QSizeF oldSize = size(); if (oldSize.width() == 0.0) { oldSize.setWidth(0.000001); } if (oldSize.height() == 0.0) { oldSize.setHeight(0.000001); } QSizeF sizeNew(newSize); if (sizeNew.width() == 0.0) { sizeNew.setWidth(0.000001); } if (sizeNew.height() == 0.0) { sizeNew.setHeight(0.000001); } return QTransform(sizeNew.width() / oldSize.width(), 0, 0, sizeNew.height() / oldSize.height(), 0, 0); } KoPathPoint * KoPathShape::moveTo(const QPointF &p) { KoPathPoint * point = new KoPathPoint(this, p, KoPathPoint::StartSubpath | KoPathPoint::StopSubpath); KoSubpath * path = new KoSubpath; path->push_back(point); d->subpaths.push_back(path); notifyPointsChanged(); return point; } KoPathPoint * KoPathShape::lineTo(const QPointF &p) { if (d->subpaths.empty()) { moveTo(QPointF(0, 0)); } KoPathPoint * point = new KoPathPoint(this, p, KoPathPoint::StopSubpath); KoPathPoint * lastPoint = d->subpaths.last()->last(); updateLastPriv(&lastPoint); d->subpaths.last()->push_back(point); notifyPointsChanged(); return point; } KoPathPoint * KoPathShape::curveTo(const QPointF &c1, const QPointF &c2, const QPointF &p) { if (d->subpaths.empty()) { moveTo(QPointF(0, 0)); } KoPathPoint * lastPoint = d->subpaths.last()->last(); updateLastPriv(&lastPoint); lastPoint->setControlPoint2(c1); KoPathPoint * point = new KoPathPoint(this, p, KoPathPoint::StopSubpath); point->setControlPoint1(c2); d->subpaths.last()->push_back(point); notifyPointsChanged(); return point; } KoPathPoint * KoPathShape::curveTo(const QPointF &c, const QPointF &p) { if (d->subpaths.empty()) moveTo(QPointF(0, 0)); KoPathPoint * lastPoint = d->subpaths.last()->last(); updateLastPriv(&lastPoint); lastPoint->setControlPoint2(c); KoPathPoint * point = new KoPathPoint(this, p, KoPathPoint::StopSubpath); d->subpaths.last()->push_back(point); notifyPointsChanged(); return point; } KoPathPoint * KoPathShape::arcTo(qreal rx, qreal ry, qreal startAngle, qreal sweepAngle) { if (d->subpaths.empty()) { moveTo(QPointF(0, 0)); } KoPathPoint * lastPoint = d->subpaths.last()->last(); if (lastPoint->properties() & KoPathPoint::CloseSubpath) { lastPoint = d->subpaths.last()->first(); } QPointF startpoint(lastPoint->point()); KoPathPoint * newEndPoint = lastPoint; QPointF curvePoints[12]; int pointCnt = arcToCurve(rx, ry, startAngle, sweepAngle, startpoint, curvePoints); for (int i = 0; i < pointCnt; i += 3) { newEndPoint = curveTo(curvePoints[i], curvePoints[i+1], curvePoints[i+2]); } return newEndPoint; } int KoPathShape::arcToCurve(qreal rx, qreal ry, qreal startAngle, qreal sweepAngle, const QPointF & offset, QPointF * curvePoints) const { int pointCnt = 0; // check Parameters if (sweepAngle == 0.0) return pointCnt; sweepAngle = qBound(-360.0, sweepAngle, 360.0); if (rx == 0 || ry == 0) { //TODO } // split angles bigger than 90° so that it gives a good approximation to the circle qreal parts = ceil(qAbs(sweepAngle / 90.0)); qreal sa_rad = startAngle * M_PI / 180.0; qreal partangle = sweepAngle / parts; qreal endangle = startAngle + partangle; qreal se_rad = endangle * M_PI / 180.0; qreal sinsa = sin(sa_rad); qreal cossa = cos(sa_rad); qreal kappa = 4.0 / 3.0 * tan((se_rad - sa_rad) / 4); // startpoint is at the last point is the path but when it is closed // it is at the first point QPointF startpoint(offset); //center berechnen QPointF center(startpoint - QPointF(cossa * rx, -sinsa * ry)); //debugFlake <<"kappa" << kappa <<"parts" << parts; for (int part = 0; part < parts; ++part) { // start tangent curvePoints[pointCnt++] = QPointF(startpoint - QPointF(sinsa * rx * kappa, cossa * ry * kappa)); qreal sinse = sin(se_rad); qreal cosse = cos(se_rad); // end point QPointF endpoint(center + QPointF(cosse * rx, -sinse * ry)); // end tangent curvePoints[pointCnt++] = QPointF(endpoint - QPointF(-sinse * rx * kappa, -cosse * ry * kappa)); curvePoints[pointCnt++] = endpoint; // set the endpoint as next start point startpoint = endpoint; sinsa = sinse; cossa = cosse; endangle += partangle; se_rad = endangle * M_PI / 180.0; } return pointCnt; } void KoPathShape::close() { if (d->subpaths.empty()) { return; } closeSubpathPriv(d->subpaths.last()); } void KoPathShape::closeMerge() { if (d->subpaths.empty()) { return; } closeMergeSubpathPriv(d->subpaths.last()); } QPointF KoPathShape::normalize() { QPointF tl(outline().boundingRect().topLeft()); QTransform matrix; matrix.translate(-tl.x(), -tl.y()); d->map(matrix); // keep the top left point of the object applyTransformation(matrix.inverted()); shapeChangedPriv(ContentChanged); return tl; } void KoPathShape::Private::map(const QTransform &matrix) { KoSubpathList::const_iterator pathIt(subpaths.constBegin()); for (; pathIt != subpaths.constEnd(); ++pathIt) { KoSubpath::const_iterator it((*pathIt)->constBegin()); for (; it != (*pathIt)->constEnd(); ++it) { // It's possible there are null points in the map... if (*it) { (*it)->map(matrix); } } } } void KoPathShape::updateLastPriv(KoPathPoint **lastPoint) { // check if we are about to add a new point to a closed subpath if ((*lastPoint)->properties() & KoPathPoint::StopSubpath && (*lastPoint)->properties() & KoPathPoint::CloseSubpath) { // get the first point of the subpath KoPathPoint *subpathStart = d->subpaths.last()->first(); // clone the first point of the subpath... KoPathPoint * newLastPoint = new KoPathPoint(*subpathStart, this); // ... and make it a normal point newLastPoint->setProperties(KoPathPoint::Normal); // now start a new subpath with the cloned start point KoSubpath *path = new KoSubpath; path->push_back(newLastPoint); d->subpaths.push_back(path); *lastPoint = newLastPoint; } else { // the subpath was not closed so the formerly last point // of the subpath is no end point anymore (*lastPoint)->unsetProperty(KoPathPoint::StopSubpath); } (*lastPoint)->unsetProperty(KoPathPoint::CloseSubpath); } QList KoPathShape::pointsAt(const QRectF &r) const { QList result; KoSubpathList::const_iterator pathIt(d->subpaths.constBegin()); for (; pathIt != d->subpaths.constEnd(); ++pathIt) { KoSubpath::const_iterator it((*pathIt)->constBegin()); for (; it != (*pathIt)->constEnd(); ++it) { if (r.contains((*it)->point())) result.append(*it); else if ((*it)->activeControlPoint1() && r.contains((*it)->controlPoint1())) result.append(*it); else if ((*it)->activeControlPoint2() && r.contains((*it)->controlPoint2())) result.append(*it); } } return result; } QList KoPathShape::segmentsAt(const QRectF &r) const { QList segments; int subpathCount = d->subpaths.count(); for (int subpathIndex = 0; subpathIndex < subpathCount; ++subpathIndex) { KoSubpath * subpath = d->subpaths[subpathIndex]; int pointCount = subpath->count(); bool subpathClosed = isClosedSubpath(subpathIndex); for (int pointIndex = 0; pointIndex < pointCount; ++pointIndex) { if (pointIndex == (pointCount - 1) && ! subpathClosed) break; KoPathSegment s(subpath->at(pointIndex), subpath->at((pointIndex + 1) % pointCount)); QRectF controlRect = s.controlPointRect(); if (! r.intersects(controlRect) && ! controlRect.contains(r)) continue; QRectF bound = s.boundingRect(); if (! r.intersects(bound) && ! bound.contains(r)) continue; segments.append(s); } } return segments; } KoPathPointIndex KoPathShape::pathPointIndex(const KoPathPoint *point) const { for (int subpathIndex = 0; subpathIndex < d->subpaths.size(); ++subpathIndex) { KoSubpath * subpath = d->subpaths.at(subpathIndex); for (int pointPos = 0; pointPos < subpath->size(); ++pointPos) { if (subpath->at(pointPos) == point) { return KoPathPointIndex(subpathIndex, pointPos); } } } return KoPathPointIndex(-1, -1); } KoPathPoint * KoPathShape::pointByIndex(const KoPathPointIndex &pointIndex) const { KoSubpath *subpath = d->subPath(pointIndex.first); if (subpath == 0 || pointIndex.second < 0 || pointIndex.second >= subpath->size()) return 0; return subpath->at(pointIndex.second); } KoPathSegment KoPathShape::segmentByIndex(const KoPathPointIndex &pointIndex) const { KoPathSegment segment(0, 0); KoSubpath *subpath = d->subPath(pointIndex.first); if (subpath != 0 && pointIndex.second >= 0 && pointIndex.second < subpath->size()) { KoPathPoint * point = subpath->at(pointIndex.second); int index = pointIndex.second; // check if we have a (closing) segment starting from the last point if ((index == subpath->size() - 1) && point->properties() & KoPathPoint::CloseSubpath) index = 0; else ++index; if (index < subpath->size()) { segment = KoPathSegment(point, subpath->at(index)); } } return segment; } int KoPathShape::pointCount() const { int i = 0; KoSubpathList::const_iterator pathIt(d->subpaths.constBegin()); for (; pathIt != d->subpaths.constEnd(); ++pathIt) { i += (*pathIt)->size(); } return i; } int KoPathShape::subpathCount() const { return d->subpaths.count(); } int KoPathShape::subpathPointCount(int subpathIndex) const { KoSubpath *subpath = d->subPath(subpathIndex); if (subpath == 0) return -1; return subpath->size(); } bool KoPathShape::isClosedSubpath(int subpathIndex) const { KoSubpath *subpath = d->subPath(subpathIndex); if (subpath == 0) return false; const bool firstClosed = subpath->first()->properties() & KoPathPoint::CloseSubpath; const bool lastClosed = subpath->last()->properties() & KoPathPoint::CloseSubpath; return firstClosed && lastClosed; } bool KoPathShape::insertPoint(KoPathPoint* point, const KoPathPointIndex &pointIndex) { KoSubpath *subpath = d->subPath(pointIndex.first); if (subpath == 0 || pointIndex.second < 0 || pointIndex.second > subpath->size()) return false; KoPathPoint::PointProperties properties = point->properties(); properties &= ~KoPathPoint::StartSubpath; properties &= ~KoPathPoint::StopSubpath; properties &= ~KoPathPoint::CloseSubpath; // check if new point starts subpath if (pointIndex.second == 0) { properties |= KoPathPoint::StartSubpath; // subpath was closed if (subpath->last()->properties() & KoPathPoint::CloseSubpath) { // keep the path closed properties |= KoPathPoint::CloseSubpath; } // old first point does not start the subpath anymore subpath->first()->unsetProperty(KoPathPoint::StartSubpath); } // check if new point stops subpath else if (pointIndex.second == subpath->size()) { properties |= KoPathPoint::StopSubpath; // subpath was closed if (subpath->last()->properties() & KoPathPoint::CloseSubpath) { // keep the path closed properties = properties | KoPathPoint::CloseSubpath; } // old last point does not end subpath anymore subpath->last()->unsetProperty(KoPathPoint::StopSubpath); } point->setProperties(properties); point->setParent(this); subpath->insert(pointIndex.second , point); notifyPointsChanged(); return true; } KoPathPoint * KoPathShape::removePoint(const KoPathPointIndex &pointIndex) { KoSubpath *subpath = d->subPath(pointIndex.first); if (subpath == 0 || pointIndex.second < 0 || pointIndex.second >= subpath->size()) return 0; KoPathPoint * point = subpath->takeAt(pointIndex.second); point->setParent(0); //don't do anything (not even crash), if there was only one point if (pointCount()==0) { return point; } // check if we removed the first point else if (pointIndex.second == 0) { // first point removed, set new StartSubpath subpath->first()->setProperty(KoPathPoint::StartSubpath); // check if path was closed if (subpath->last()->properties() & KoPathPoint::CloseSubpath) { // keep path closed subpath->first()->setProperty(KoPathPoint::CloseSubpath); } } // check if we removed the last point else if (pointIndex.second == subpath->size()) { // use size as point is already removed // last point removed, set new StopSubpath subpath->last()->setProperty(KoPathPoint::StopSubpath); // check if path was closed if (point->properties() & KoPathPoint::CloseSubpath) { // keep path closed subpath->last()->setProperty(KoPathPoint::CloseSubpath); } } notifyPointsChanged(); return point; } bool KoPathShape::breakAfter(const KoPathPointIndex &pointIndex) { KoSubpath *subpath = d->subPath(pointIndex.first); if (!subpath || pointIndex.second < 0 || pointIndex.second > subpath->size() - 2 || isClosedSubpath(pointIndex.first)) return false; KoSubpath * newSubpath = new KoSubpath; int size = subpath->size(); for (int i = pointIndex.second + 1; i < size; ++i) { newSubpath->append(subpath->takeAt(pointIndex.second + 1)); } // now make the first point of the new subpath a starting node newSubpath->first()->setProperty(KoPathPoint::StartSubpath); // the last point of the old subpath is now an ending node subpath->last()->setProperty(KoPathPoint::StopSubpath); // insert the new subpath after the broken one d->subpaths.insert(pointIndex.first + 1, newSubpath); notifyPointsChanged(); return true; } bool KoPathShape::join(int subpathIndex) { KoSubpath *subpath = d->subPath(subpathIndex); KoSubpath *nextSubpath = d->subPath(subpathIndex + 1); if (!subpath || !nextSubpath || isClosedSubpath(subpathIndex) || isClosedSubpath(subpathIndex+1)) return false; // the last point of the subpath does not end the subpath anymore subpath->last()->unsetProperty(KoPathPoint::StopSubpath); // the first point of the next subpath does not start a subpath anymore nextSubpath->first()->unsetProperty(KoPathPoint::StartSubpath); // append the second subpath to the first Q_FOREACH (KoPathPoint * p, *nextSubpath) subpath->append(p); // remove the nextSubpath from path d->subpaths.removeAt(subpathIndex + 1); // delete it as it is no longer possible to use it delete nextSubpath; notifyPointsChanged(); return true; } bool KoPathShape::moveSubpath(int oldSubpathIndex, int newSubpathIndex) { KoSubpath *subpath = d->subPath(oldSubpathIndex); if (subpath == 0 || newSubpathIndex >= d->subpaths.size()) return false; if (oldSubpathIndex == newSubpathIndex) return true; d->subpaths.removeAt(oldSubpathIndex); d->subpaths.insert(newSubpathIndex, subpath); notifyPointsChanged(); return true; } KoPathPointIndex KoPathShape::openSubpath(const KoPathPointIndex &pointIndex) { KoSubpath *subpath = d->subPath(pointIndex.first); if (!subpath || pointIndex.second < 0 || pointIndex.second >= subpath->size() || !isClosedSubpath(pointIndex.first)) return KoPathPointIndex(-1, -1); KoPathPoint * oldStartPoint = subpath->first(); // the old starting node no longer starts the subpath oldStartPoint->unsetProperty(KoPathPoint::StartSubpath); // the old end node no longer closes the subpath subpath->last()->unsetProperty(KoPathPoint::StopSubpath); // reorder the subpath for (int i = 0; i < pointIndex.second; ++i) { subpath->append(subpath->takeFirst()); } // make the first point a start node subpath->first()->setProperty(KoPathPoint::StartSubpath); // make the last point an end node subpath->last()->setProperty(KoPathPoint::StopSubpath); notifyPointsChanged(); return pathPointIndex(oldStartPoint); } KoPathPointIndex KoPathShape::closeSubpath(const KoPathPointIndex &pointIndex) { KoSubpath *subpath = d->subPath(pointIndex.first); if (!subpath || pointIndex.second < 0 || pointIndex.second >= subpath->size() || isClosedSubpath(pointIndex.first)) return KoPathPointIndex(-1, -1); KoPathPoint * oldStartPoint = subpath->first(); // the old starting node no longer starts the subpath oldStartPoint->unsetProperty(KoPathPoint::StartSubpath); // the old end node no longer ends the subpath subpath->last()->unsetProperty(KoPathPoint::StopSubpath); // reorder the subpath for (int i = 0; i < pointIndex.second; ++i) { subpath->append(subpath->takeFirst()); } subpath->first()->setProperty(KoPathPoint::StartSubpath); subpath->last()->setProperty(KoPathPoint::StopSubpath); closeSubpathPriv(subpath); notifyPointsChanged(); return pathPointIndex(oldStartPoint); } bool KoPathShape::reverseSubpath(int subpathIndex) { KoSubpath *subpath = d->subPath(subpathIndex); if (subpath == 0) return false; int size = subpath->size(); for (int i = 0; i < size; ++i) { KoPathPoint *p = subpath->takeAt(i); p->reverse(); subpath->prepend(p); } // adjust the position dependent properties KoPathPoint *first = subpath->first(); KoPathPoint *last = subpath->last(); KoPathPoint::PointProperties firstProps = first->properties(); KoPathPoint::PointProperties lastProps = last->properties(); firstProps |= KoPathPoint::StartSubpath; firstProps &= ~KoPathPoint::StopSubpath; lastProps |= KoPathPoint::StopSubpath; lastProps &= ~KoPathPoint::StartSubpath; if (firstProps & KoPathPoint::CloseSubpath) { firstProps |= KoPathPoint::CloseSubpath; lastProps |= KoPathPoint::CloseSubpath; } first->setProperties(firstProps); last->setProperties(lastProps); notifyPointsChanged(); return true; } KoSubpath * KoPathShape::removeSubpath(int subpathIndex) { KoSubpath *subpath = d->subPath(subpathIndex); if (subpath != 0) { Q_FOREACH (KoPathPoint* point, *subpath) { point->setParent(this); } d->subpaths.removeAt(subpathIndex); } notifyPointsChanged(); return subpath; } bool KoPathShape::addSubpath(KoSubpath * subpath, int subpathIndex) { if (subpathIndex < 0 || subpathIndex > d->subpaths.size()) return false; Q_FOREACH (KoPathPoint* point, *subpath) { point->setParent(this); } d->subpaths.insert(subpathIndex, subpath); notifyPointsChanged(); return true; } int KoPathShape::combine(KoPathShape *path) { int insertSegmentPosition = -1; if (!path) return insertSegmentPosition; QTransform pathMatrix = path->absoluteTransformation(); QTransform myMatrix = absoluteTransformation().inverted(); Q_FOREACH (KoSubpath* subpath, path->d->subpaths) { KoSubpath *newSubpath = new KoSubpath(); Q_FOREACH (KoPathPoint* point, *subpath) { KoPathPoint *newPoint = new KoPathPoint(*point, this); newPoint->map(pathMatrix); newPoint->map(myMatrix); newSubpath->append(newPoint); } d->subpaths.append(newSubpath); if (insertSegmentPosition < 0) { insertSegmentPosition = d->subpaths.size() - 1; } } normalize(); notifyPointsChanged(); return insertSegmentPosition; } bool KoPathShape::separate(QList & separatedPaths) { if (! d->subpaths.size()) return false; QTransform myMatrix = absoluteTransformation(); Q_FOREACH (KoSubpath* subpath, d->subpaths) { KoPathShape *shape = new KoPathShape(); shape->setStroke(stroke()); shape->setBackground(background()); shape->setShapeId(shapeId()); shape->setZIndex(zIndex()); KoSubpath *newSubpath = new KoSubpath(); Q_FOREACH (KoPathPoint* point, *subpath) { KoPathPoint *newPoint = new KoPathPoint(*point, shape); newPoint->map(myMatrix); newSubpath->append(newPoint); } shape->d->subpaths.append(newSubpath); shape->normalize(); // NOTE: shape cannot have any listeners yet, so no notification about // points modification is needed separatedPaths.append(shape); } return true; } void KoPathShape::closeSubpathPriv(KoSubpath *subpath) { if (! subpath) return; subpath->last()->setProperty(KoPathPoint::CloseSubpath); subpath->first()->setProperty(KoPathPoint::CloseSubpath); notifyPointsChanged(); } void KoPathShape::closeMergeSubpathPriv(KoSubpath *subpath) { if (! subpath || subpath->size() < 2) return; KoPathPoint * lastPoint = subpath->last(); KoPathPoint * firstPoint = subpath->first(); // check if first and last points are coincident if (lastPoint->point() == firstPoint->point()) { // we are removing the current last point and // reuse its first control point if active firstPoint->setProperty(KoPathPoint::StartSubpath); firstPoint->setProperty(KoPathPoint::CloseSubpath); if (lastPoint->activeControlPoint1()) firstPoint->setControlPoint1(lastPoint->controlPoint1()); // remove last point delete subpath->takeLast(); // the new last point closes the subpath now lastPoint = subpath->last(); lastPoint->setProperty(KoPathPoint::StopSubpath); lastPoint->setProperty(KoPathPoint::CloseSubpath); notifyPointsChanged(); } else { closeSubpathPriv(subpath); } } const KoSubpathList &KoPathShape::subpaths() const { return d->subpaths; } KoSubpathList &KoPathShape::subpaths() { return d->subpaths; } void KoPathShape::map(const QTransform &matrix) { return d->map(matrix); } KoSubpath *KoPathShape::Private::subPath(int subpathIndex) const { if (subpathIndex < 0 || subpathIndex >= subpaths.size()) return 0; return subpaths.at(subpathIndex); } QString KoPathShape::pathShapeId() const { return KoPathShapeId; } QString KoPathShape::toString(const QTransform &matrix) const { QString pathString; // iterate over all subpaths KoSubpathList::const_iterator pathIt(d->subpaths.constBegin()); for (; pathIt != d->subpaths.constEnd(); ++pathIt) { KoSubpath::const_iterator pointIt((*pathIt)->constBegin()); // keep a pointer to the first point of the subpath KoPathPoint *firstPoint(*pointIt); // keep a pointer to the previous point of the subpath KoPathPoint *lastPoint = firstPoint; // keep track if the previous point has an active control point 2 bool activeControlPoint2 = false; // iterate over all points of the current subpath for (; pointIt != (*pathIt)->constEnd(); ++pointIt) { KoPathPoint *currPoint(*pointIt); if (!currPoint) { qWarning() << "Found a zero point in the shape's path!"; continue; } // first point of subpath ? if (currPoint == firstPoint) { // are we starting a subpath ? if (currPoint->properties() & KoPathPoint::StartSubpath) { const QPointF p = matrix.map(currPoint->point()); pathString += QString("M%1 %2").arg(p.x()).arg(p.y()); } } // end point of curve segment ? else if (activeControlPoint2 || currPoint->activeControlPoint1()) { // check if we have a cubic or quadratic curve const bool isCubic = activeControlPoint2 && currPoint->activeControlPoint1(); KoPathSegment cubicSeg = isCubic ? KoPathSegment(lastPoint, currPoint) : KoPathSegment(lastPoint, currPoint).toCubic(); if (cubicSeg.first() && cubicSeg.second()) { const QPointF cp1 = matrix.map(cubicSeg.first()->controlPoint2()); const QPointF cp2 = matrix.map(cubicSeg.second()->controlPoint1()); const QPointF p = matrix.map(cubicSeg.second()->point()); pathString += QString("C%1 %2 %3 %4 %5 %6") .arg(cp1.x()).arg(cp1.y()) .arg(cp2.x()).arg(cp2.y()) .arg(p.x()).arg(p.y()); } } // end point of line segment! else { const QPointF p = matrix.map(currPoint->point()); pathString += QString("L%1 %2").arg(p.x()).arg(p.y()); } // last point closes subpath ? if (currPoint->properties() & KoPathPoint::StopSubpath && currPoint->properties() & KoPathPoint::CloseSubpath) { // add curve when there is a curve on the way to the first point if (currPoint->activeControlPoint2() || firstPoint->activeControlPoint1()) { // check if we have a cubic or quadratic curve const bool isCubic = currPoint->activeControlPoint2() && firstPoint->activeControlPoint1(); KoPathSegment cubicSeg = isCubic ? KoPathSegment(currPoint, firstPoint) : KoPathSegment(currPoint, firstPoint).toCubic(); if (cubicSeg.first() && cubicSeg.second()) { const QPointF cp1 = matrix.map(cubicSeg.first()->controlPoint2()); const QPointF cp2 = matrix.map(cubicSeg.second()->controlPoint1()); const QPointF p = matrix.map(cubicSeg.second()->point()); pathString += QString("C%1 %2 %3 %4 %5 %6") .arg(cp1.x()).arg(cp1.y()) .arg(cp2.x()).arg(cp2.y()) .arg(p.x()).arg(p.y()); } } pathString += QString("Z"); } activeControlPoint2 = currPoint->activeControlPoint2(); lastPoint = currPoint; } } return pathString; } char nodeType(const KoPathPoint * point) { if (point->properties() & KoPathPoint::IsSmooth) { return 's'; } else if (point->properties() & KoPathPoint::IsSymmetric) { return 'z'; } else { return 'c'; } } QString KoPathShape::Private::nodeTypes() const { QString types; KoSubpathList::const_iterator pathIt(subpaths.constBegin()); for (; pathIt != subpaths.constEnd(); ++pathIt) { KoSubpath::const_iterator it((*pathIt)->constBegin()); for (; it != (*pathIt)->constEnd(); ++it) { if (it == (*pathIt)->constBegin()) { types.append('c'); } else { types.append(nodeType(*it)); } if ((*it)->properties() & KoPathPoint::StopSubpath && (*it)->properties() & KoPathPoint::CloseSubpath) { KoPathPoint * firstPoint = (*pathIt)->first(); types.append(nodeType(firstPoint)); } } } return types; } void updateNodeType(KoPathPoint * point, const QChar & nodeType) { if (nodeType == 's') { point->setProperty(KoPathPoint::IsSmooth); } else if (nodeType == 'z') { point->setProperty(KoPathPoint::IsSymmetric); } } void KoPathShape::Private::loadNodeTypes(const KoXmlElement &element) { if (element.hasAttributeNS(KoXmlNS::calligra, "nodeTypes")) { QString nodeTypes = element.attributeNS(KoXmlNS::calligra, "nodeTypes"); QString::const_iterator nIt(nodeTypes.constBegin()); KoSubpathList::const_iterator pathIt(subpaths.constBegin()); for (; pathIt != subpaths.constEnd(); ++pathIt) { KoSubpath::const_iterator it((*pathIt)->constBegin()); for (; it != (*pathIt)->constEnd(); ++it, nIt++) { // be sure not to crash if there are not enough nodes in nodeTypes if (nIt == nodeTypes.constEnd()) { warnFlake << "not enough nodes in calligra:nodeTypes"; return; } // the first node is always of type 'c' if (it != (*pathIt)->constBegin()) { updateNodeType(*it, *nIt); } if ((*it)->properties() & KoPathPoint::StopSubpath && (*it)->properties() & KoPathPoint::CloseSubpath) { ++nIt; updateNodeType((*pathIt)->first(), *nIt); } } } } } Qt::FillRule KoPathShape::fillRule() const { return d->fillRule; } void KoPathShape::setFillRule(Qt::FillRule fillRule) { d->fillRule = fillRule; } KoPathShape * KoPathShape::createShapeFromPainterPath(const QPainterPath &path) { KoPathShape * shape = new KoPathShape(); int elementCount = path.elementCount(); for (int i = 0; i < elementCount; i++) { QPainterPath::Element element = path.elementAt(i); switch (element.type) { case QPainterPath::MoveToElement: shape->moveTo(QPointF(element.x, element.y)); break; case QPainterPath::LineToElement: shape->lineTo(QPointF(element.x, element.y)); break; case QPainterPath::CurveToElement: shape->curveTo(QPointF(element.x, element.y), QPointF(path.elementAt(i + 1).x, path.elementAt(i + 1).y), QPointF(path.elementAt(i + 2).x, path.elementAt(i + 2).y)); break; default: continue; } } shape->setShapeId(KoPathShapeId); //shape->normalize(); return shape; } bool KoPathShape::hitTest(const QPointF &position) const { if (parent() && parent()->isClipped(this) && ! parent()->hitTest(position)) return false; QPointF point = absoluteTransformation().inverted().map(position); const QPainterPath outlinePath = outline(); if (stroke()) { KoInsets insets; stroke()->strokeInsets(this, insets); QRectF roi(QPointF(-insets.left, -insets.top), QPointF(insets.right, insets.bottom)); roi.moveCenter(point); if (outlinePath.intersects(roi) || outlinePath.contains(roi)) return true; } else { if (outlinePath.contains(point)) return true; } // if there is no shadow we can as well just leave if (! 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 - shadow()->offset()); return outlinePath.contains(point); } void KoPathShape::setMarker(KoMarker *marker, KoFlake::MarkerPosition pos) { if (!marker && d->markersNew.contains(pos)) { d->markersNew.remove(pos); } else { d->markersNew[pos] = marker; } } KoMarker *KoPathShape::marker(KoFlake::MarkerPosition pos) const { return d->markersNew[pos].data(); } bool KoPathShape::hasMarkers() const { return !d->markersNew.isEmpty(); } bool KoPathShape::autoFillMarkers() const { return d->autoFillMarkers; } void KoPathShape::setAutoFillMarkers(bool value) { d->autoFillMarkers = value; } void KoPathShape::recommendPointSelectionChange(const QList &newSelection) { Q_FOREACH (KoShape::ShapeChangeListener *listener, listeners()) { PointSelectionChangeListener *pointListener = dynamic_cast(listener); if (pointListener) { pointListener->recommendPointSelectionChange(this, newSelection); } } } void KoPathShape::notifyPointsChanged() { Q_FOREACH (KoShape::ShapeChangeListener *listener, listeners()) { PointSelectionChangeListener *pointListener = dynamic_cast(listener); if (pointListener) { pointListener->notifyPathPointsChanged(this); } } } QPainterPath KoPathShape::pathStroke(const QPen &pen) const { if (d->subpaths.isEmpty()) { return QPainterPath(); } QPainterPath pathOutline; QPainterPathStroker stroker; stroker.setWidth(0); stroker.setJoinStyle(Qt::MiterJoin); stroker.setWidth(pen.widthF()); stroker.setJoinStyle(pen.joinStyle()); stroker.setMiterLimit(pen.miterLimit()); stroker.setCapStyle(pen.capStyle()); stroker.setDashOffset(pen.dashOffset()); stroker.setDashPattern(pen.dashPattern()); QPainterPath path = stroker.createStroke(outline()); pathOutline.addPath(path); pathOutline.setFillRule(Qt::WindingFill); return pathOutline; } void KoPathShape::PointSelectionChangeListener::notifyShapeChanged(KoShape::ChangeType type, KoShape *shape) { Q_UNUSED(type); Q_UNUSED(shape); } diff --git a/libs/flake/KoPatternBackground.cpp b/libs/flake/KoPatternBackground.cpp index 3e95ee006d..ada5eecc56 100644 --- a/libs/flake/KoPatternBackground.cpp +++ b/libs/flake/KoPatternBackground.cpp @@ -1,314 +1,312 @@ /* This file is part of the KDE project * Copyright (C) 2008 Jan Hambrecht * * 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 "KoPatternBackground.h" #include "KoShapeSavingContext.h" #include "KoImageData.h" #include "KoImageCollection.h" -#include #include #include #include -#include #include #include #include #include #include #include #include #include #include class KoPatternBackground::Private : public QSharedData { public: Private() : QSharedData() , repeat(KoPatternBackground::Tiled) , refPoint(KoPatternBackground::TopLeft) , imageCollection(0) , imageData(0) { } ~Private() { delete imageData; } QSizeF targetSize() const { QSizeF size = imageData->imageSize(); if (targetImageSizePercent.width() > 0.0) size.setWidth(0.01 * targetImageSizePercent.width() * size.width()); else if (targetImageSize.width() > 0.0) size.setWidth(targetImageSize.width()); if (targetImageSizePercent.height() > 0.0) size.setHeight(0.01 * targetImageSizePercent.height() * size.height()); else if (targetImageSize.height() > 0.0) size.setHeight(targetImageSize.height()); return size; } QPointF offsetFromRect(const QRectF &fillRect, const QSizeF &imageSize) const { QPointF offset; switch (refPoint) { case KoPatternBackground::TopLeft: offset = fillRect.topLeft(); break; case KoPatternBackground::Top: offset.setX(fillRect.center().x() - 0.5 * imageSize.width()); offset.setY(fillRect.top()); break; case KoPatternBackground::TopRight: offset.setX(fillRect.right() - imageSize.width()); offset.setY(fillRect.top()); break; case KoPatternBackground::Left: offset.setX(fillRect.left()); offset.setY(fillRect.center().y() - 0.5 * imageSize.height()); break; case KoPatternBackground::Center: offset.setX(fillRect.center().x() - 0.5 * imageSize.width()); offset.setY(fillRect.center().y() - 0.5 * imageSize.height()); break; case KoPatternBackground::Right: offset.setX(fillRect.right() - imageSize.width()); offset.setY(fillRect.center().y() - 0.5 * imageSize.height()); break; case KoPatternBackground::BottomLeft: offset.setX(fillRect.left()); offset.setY(fillRect.bottom() - imageSize.height()); break; case KoPatternBackground::Bottom: offset.setX(fillRect.center().x() - 0.5 * imageSize.width()); offset.setY(fillRect.bottom() - imageSize.height()); break; case KoPatternBackground::BottomRight: offset.setX(fillRect.right() - imageSize.width()); offset.setY(fillRect.bottom() - imageSize.height()); break; default: break; } if (refPointOffsetPercent.x() > 0.0) offset += QPointF(0.01 * refPointOffsetPercent.x() * imageSize.width(), 0); if (refPointOffsetPercent.y() > 0.0) offset += QPointF(0, 0.01 * refPointOffsetPercent.y() * imageSize.height()); return offset; } QTransform matrix; KoPatternBackground::PatternRepeat repeat; KoPatternBackground::ReferencePoint refPoint; QSizeF targetImageSize; QSizeF targetImageSizePercent; QPointF refPointOffsetPercent; QPointF tileRepeatOffsetPercent; QPointer imageCollection; KoImageData * imageData; }; // ---------------------------------------------------------------- KoPatternBackground::KoPatternBackground(KoImageCollection *imageCollection) : KoShapeBackground() , d(new Private) { d->imageCollection = imageCollection; Q_ASSERT(d->imageCollection); } KoPatternBackground::~KoPatternBackground() { } bool KoPatternBackground::compareTo(const KoShapeBackground *other) const { Q_UNUSED(other); return false; } void KoPatternBackground::setTransform(const QTransform &matrix) { d->matrix = matrix; } QTransform KoPatternBackground::transform() const { return d->matrix; } void KoPatternBackground::setPattern(const QImage &pattern) { delete d->imageData; if (d->imageCollection) { d->imageData = d->imageCollection->createImageData(pattern); } } void KoPatternBackground::setPattern(KoImageData *imageData) { delete d->imageData; d->imageData = imageData; } QImage KoPatternBackground::pattern() const { if (d->imageData) return d->imageData->image(); return QImage(); } void KoPatternBackground::setRepeat(PatternRepeat repeat) { d->repeat = repeat; } KoPatternBackground::PatternRepeat KoPatternBackground::repeat() const { return d->repeat; } KoPatternBackground::ReferencePoint KoPatternBackground::referencePoint() const { return d->refPoint; } void KoPatternBackground::setReferencePoint(ReferencePoint referencePoint) { d->refPoint = referencePoint; } QPointF KoPatternBackground::referencePointOffset() const { return d->refPointOffsetPercent; } void KoPatternBackground::setReferencePointOffset(const QPointF &offset) { qreal ox = qMax(qreal(0.0), qMin(qreal(100.0), offset.x())); qreal oy = qMax(qreal(0.0), qMin(qreal(100.0), offset.y())); d->refPointOffsetPercent = QPointF(ox, oy); } QPointF KoPatternBackground::tileRepeatOffset() const { return d->tileRepeatOffsetPercent; } void KoPatternBackground::setTileRepeatOffset(const QPointF &offset) { d->tileRepeatOffsetPercent = offset; } QSizeF KoPatternBackground::patternDisplaySize() const { return d->targetSize(); } void KoPatternBackground::setPatternDisplaySize(const QSizeF &size) { d->targetImageSizePercent = QSizeF(); d->targetImageSize = size; } QSizeF KoPatternBackground::patternOriginalSize() const { return d->imageData->imageSize(); } void KoPatternBackground::paint(QPainter &painter, KoShapePaintingContext &/*context*/, const QPainterPath &fillPath) const { if (! d->imageData) return; painter.save(); if (d->repeat == Tiled) { // calculate scaling of pixmap QSizeF targetSize = d->targetSize(); QSizeF imageSize = d->imageData->imageSize(); qreal scaleX = targetSize.width() / imageSize.width(); qreal scaleY = targetSize.height() / imageSize.height(); QRectF targetRect = fillPath.boundingRect(); // undo scaling on target rectangle targetRect.setWidth(targetRect.width() / scaleX); targetRect.setHeight(targetRect.height() / scaleY); // determine pattern offset QPointF offset = d->offsetFromRect(targetRect, imageSize); // create matrix for pixmap scaling QTransform matrix; matrix.scale(scaleX, scaleY); painter.setClipPath(fillPath); painter.setWorldTransform(matrix, true); painter.drawTiledPixmap(targetRect, d->imageData->pixmap(imageSize.toSize()), -offset); } else if (d->repeat == Original) { QRectF sourceRect(QPointF(0, 0), d->imageData->imageSize()); QRectF targetRect(QPoint(0, 0), d->targetSize()); targetRect.moveCenter(fillPath.boundingRect().center()); painter.setClipPath(fillPath); painter.drawPixmap(targetRect, d->imageData->pixmap(sourceRect.size().toSize()), sourceRect); } else if (d->repeat == Stretched) { painter.setClipPath(fillPath); // undo conversion of the scaling so that we can use a nicely scaled image of the correct size qWarning() << "WARNING: stretched KoPatternBackground painting code is abandoned. The result might be not correct"; const QRectF targetRect = fillPath.boundingRect(); painter.drawPixmap(targetRect.topLeft(), d->imageData->pixmap(targetRect.size().toSize())); } painter.restore(); } QRectF KoPatternBackground::patternRectFromFillSize(const QSizeF &size) { QRectF rect; switch (d->repeat) { case Tiled: rect.setTopLeft(d->offsetFromRect(QRectF(QPointF(), size), d->targetSize())); rect.setSize(d->targetSize()); break; case Original: rect.setLeft(0.5 * (size.width() - d->targetSize().width())); rect.setTop(0.5 * (size.height() - d->targetSize().height())); rect.setSize(d->targetSize()); break; case Stretched: rect.setTopLeft(QPointF(0.0, 0.0)); rect.setSize(size); break; } return rect; } diff --git a/libs/flake/KoShape.cpp b/libs/flake/KoShape.cpp index ff95e7284e..17f4f044d4 100644 --- a/libs/flake/KoShape.cpp +++ b/libs/flake/KoShape.cpp @@ -1,1356 +1,1355 @@ /* 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 "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/KoShapeAnchor.cpp b/libs/flake/KoShapeAnchor.cpp index 28989376ba..f4049a2b0d 100644 --- a/libs/flake/KoShapeAnchor.cpp +++ b/libs/flake/KoShapeAnchor.cpp @@ -1,198 +1,197 @@ /* This file is part of the KDE project * Copyright (C) 2007, 2009-2010 Thomas Zander * Copyright (C) 2010 Ko Gmbh * Copyright (C) 2011 Matus Hanzes * Copyright (C) 2013 C. Boemann * * 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 "KoShapeAnchor.h" -#include "KoStyleStack.h" #include #include #include #include #include #include #include #include #include class Q_DECL_HIDDEN KoShapeAnchor::Private { public: Private(KoShape *s) : shape(s) , verticalPos(KoShapeAnchor::VTop) , verticalRel(KoShapeAnchor::VLine) , horizontalPos(KoShapeAnchor::HLeft) , horizontalRel(KoShapeAnchor::HChar) , flowWithText(true) , anchorType(KoShapeAnchor::AnchorToCharacter) , placementStrategy(0) , pageNumber(-1) , textLocation(0) { } QDebug printDebug(QDebug dbg) const { #ifndef NDEBUG dbg.space() << "KoShapeAnchor" << this; dbg.space() << "offset:" << offset; dbg.space() << "shape:" << shape->name(); #endif return dbg.space(); } KoShape * const shape; QPointF offset; KoShapeAnchor::VerticalPos verticalPos; KoShapeAnchor::VerticalRel verticalRel; KoShapeAnchor::HorizontalPos horizontalPos; KoShapeAnchor::HorizontalRel horizontalRel; QString wrapInfluenceOnPosition; bool flowWithText; KoShapeAnchor::AnchorType anchorType; KoShapeAnchor::PlacementStrategy *placementStrategy; int pageNumber; KoShapeAnchor::TextLocation *textLocation; }; KoShapeAnchor::KoShapeAnchor(KoShape *shape) : d(new Private(shape)) { } KoShapeAnchor::~KoShapeAnchor() { if (d->placementStrategy != 0) { delete d->placementStrategy; } delete d; } KoShape *KoShapeAnchor::shape() const { return d->shape; } KoShapeAnchor::AnchorType KoShapeAnchor::anchorType() const { return d->anchorType; } void KoShapeAnchor::setHorizontalPos(HorizontalPos hp) { d->horizontalPos = hp; } KoShapeAnchor::HorizontalPos KoShapeAnchor::horizontalPos() const { return d->horizontalPos; } void KoShapeAnchor::setHorizontalRel(HorizontalRel hr) { d->horizontalRel = hr; } KoShapeAnchor::HorizontalRel KoShapeAnchor::horizontalRel() const { return d->horizontalRel; } void KoShapeAnchor::setVerticalPos(VerticalPos vp) { d->verticalPos = vp; } KoShapeAnchor::VerticalPos KoShapeAnchor::verticalPos() const { return d->verticalPos; } void KoShapeAnchor::setVerticalRel(VerticalRel vr) { d->verticalRel = vr; } KoShapeAnchor::VerticalRel KoShapeAnchor::verticalRel() const { return d->verticalRel; } QString KoShapeAnchor::wrapInfluenceOnPosition() const { return d->wrapInfluenceOnPosition; } bool KoShapeAnchor::flowWithText() const { return d->flowWithText; } int KoShapeAnchor::pageNumber() const { return d->pageNumber; } const QPointF &KoShapeAnchor::offset() const { return d->offset; } void KoShapeAnchor::setOffset(const QPointF &offset) { d->offset = offset; } void KoShapeAnchor::setAnchorType(KoShapeAnchor::AnchorType type) { d->anchorType = type; if (type == AnchorAsCharacter) { d->horizontalRel = HChar; d->horizontalPos = HLeft; } } KoShapeAnchor::TextLocation *KoShapeAnchor::textLocation() const { return d->textLocation; } void KoShapeAnchor::setTextLocation(TextLocation *textLocation) { d->textLocation = textLocation; } KoShapeAnchor::PlacementStrategy *KoShapeAnchor::placementStrategy() const { return d->placementStrategy; } void KoShapeAnchor::setPlacementStrategy(PlacementStrategy *placementStrategy) { if (placementStrategy != d->placementStrategy) { delete d->placementStrategy; d->placementStrategy = placementStrategy; } } diff --git a/libs/flake/KoShapeFactoryBase.cpp b/libs/flake/KoShapeFactoryBase.cpp index 12ef033e62..8244cd0859 100644 --- a/libs/flake/KoShapeFactoryBase.cpp +++ b/libs/flake/KoShapeFactoryBase.cpp @@ -1,249 +1,248 @@ /* This file is part of the KDE project * Copyright (c) 2006 Boudewijn Rempt (boud@valdyas.org) * Copyright (C) 2006-2007 Thomas Zander * Copyright (C) 2008 C. Boemann * Copyright (C) 2008 Thorsten Zachmann * * 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 "KoShapeFactoryBase.h" #include #include "KoDocumentResourceManager.h" #include "KoDeferredShapeFactoryBase.h" #include "KoShape.h" #include "KoShapeLoadingContext.h" #include -#include #include #include #include #include #include #include #include class Q_DECL_HIDDEN KoShapeFactoryBase::Private { public: Private(const QString &_id, const QString &_name, const QString &_deferredPluginName) : deferredFactory(0), deferredPluginName(_deferredPluginName), id(_id), name(_name), loadingPriority(0), hidden(false) { } ~Private() { Q_FOREACH (const KoShapeTemplate & t, templates) delete t.properties; templates.clear(); } KoDeferredShapeFactoryBase *deferredFactory; QMutex pluginLoadingMutex; QString deferredPluginName; QList templates; const QString id; const QString name; QString family; QString tooltip; QString iconName; int loadingPriority; QList > xmlElements; // xml name space -> xml element names bool hidden; QList > resourceManagers; }; KoShapeFactoryBase::KoShapeFactoryBase(const QString &id, const QString &name, const QString &deferredPluginName) : d(new Private(id, name, deferredPluginName)) { } KoShapeFactoryBase::~KoShapeFactoryBase() { delete d; } QString KoShapeFactoryBase::toolTip() const { return d->tooltip; } QString KoShapeFactoryBase::iconName() const { return d->iconName; } QString KoShapeFactoryBase::name() const { return d->name; } QString KoShapeFactoryBase::family() const { return d->family; } int KoShapeFactoryBase::loadingPriority() const { return d->loadingPriority; } QList > KoShapeFactoryBase::odfElements() const { return d->xmlElements; } void KoShapeFactoryBase::addTemplate(const KoShapeTemplate ¶ms) { KoShapeTemplate tmplate = params; tmplate.id = d->id; d->templates.append(tmplate); } void KoShapeFactoryBase::setToolTip(const QString & tooltip) { d->tooltip = tooltip; } void KoShapeFactoryBase::setIconName(const char *iconName) { d->iconName = QLatin1String(iconName); } void KoShapeFactoryBase::setFamily(const QString & family) { d->family = family; } QString KoShapeFactoryBase::id() const { return d->id; } QList KoShapeFactoryBase::templates() const { return d->templates; } void KoShapeFactoryBase::setLoadingPriority(int priority) { d->loadingPriority = priority; } void KoShapeFactoryBase::setXmlElementNames(const QString & nameSpace, const QStringList & names) { d->xmlElements.clear(); d->xmlElements.append(QPair(nameSpace, names)); } void KoShapeFactoryBase::setXmlElements(const QList > &elementNamesList) { d->xmlElements = elementNamesList; } bool KoShapeFactoryBase::hidden() const { return d->hidden; } void KoShapeFactoryBase::setHidden(bool hidden) { d->hidden = hidden; } void KoShapeFactoryBase::newDocumentResourceManager(KoDocumentResourceManager *manager) const { d->resourceManagers.append(manager); connect(manager, SIGNAL(destroyed(QObject*)), this, SLOT(pruneDocumentResourceManager(QObject*))); } KoShape *KoShapeFactoryBase::createDefaultShape(KoDocumentResourceManager *documentResources) const { if (!d->deferredPluginName.isEmpty()) { const_cast(this)->getDeferredPlugin(); Q_ASSERT(d->deferredFactory); if (d->deferredFactory) { return d->deferredFactory->createDefaultShape(documentResources); } } return 0; } KoShape *KoShapeFactoryBase::createShape(const KoProperties* properties, KoDocumentResourceManager *documentResources) const { if (!d->deferredPluginName.isEmpty()) { const_cast(this)->getDeferredPlugin(); Q_ASSERT(d->deferredFactory); if (d->deferredFactory) { return d->deferredFactory->createShape(properties, documentResources); } } return createDefaultShape(documentResources); } KoShape *KoShapeFactoryBase::createShapeFromXML(const KoXmlElement &element, KoShapeLoadingContext &context) { KoShape *shape = createDefaultShape(context.documentResourceManager()); if (!shape) return 0; if (shape->shapeId().isEmpty()) shape->setShapeId(id()); return shape; } void KoShapeFactoryBase::getDeferredPlugin() { QMutexLocker(&d->pluginLoadingMutex); if (d->deferredFactory) return; const QList offers = KoJsonTrader::instance()->query("Krita/Deferred", QString()); Q_ASSERT(offers.size() > 0); Q_FOREACH (QPluginLoader *pluginLoader, offers) { KPluginFactory *factory = qobject_cast(pluginLoader->instance()); KoDeferredShapeFactoryBase *plugin = factory->create(this, QVariantList()); if (plugin && plugin->deferredPluginName() == d->deferredPluginName) { d->deferredFactory = plugin; } } qDeleteAll(offers); } void KoShapeFactoryBase::pruneDocumentResourceManager(QObject *) { QList > rms; Q_FOREACH(QPointer rm, d->resourceManagers) { if (rm) { rms << rm; } } d->resourceManagers = rms; } diff --git a/libs/flake/KoShapeRegistry.cpp b/libs/flake/KoShapeRegistry.cpp index bf18acd030..c34f041f0a 100644 --- a/libs/flake/KoShapeRegistry.cpp +++ b/libs/flake/KoShapeRegistry.cpp @@ -1,396 +1,395 @@ /* This file is part of the KDE project * Copyright (c) 2006 Boudewijn Rempt (boud@valdyas.org) * Copyright (C) 2006-2007, 2010 Thomas Zander * Copyright (C) 2006,2008-2010 Thorsten Zachmann * Copyright (C) 2007 Jan Hambrecht * Copyright (C) 2010 Inge Wallin * * 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. */ // Own #include "KoShapeRegistry.h" #include "KoSvgTextShape.h" #include "KoPathShapeFactory.h" #include "KoShapeLoadingContext.h" #include "KoShapeSavingContext.h" #include "KoShapeGroup.h" #include "KoShapeLayer.h" #include "SvgShapeFactory.h" #include #include #include -#include #include #include #include #include #include #include Q_GLOBAL_STATIC(KoShapeRegistry, s_instance) class Q_DECL_HIDDEN KoShapeRegistry::Private { public: void insertFactory(KoShapeFactoryBase *factory); void init(KoShapeRegistry *q); KoShape *createShapeInternal(const KoXmlElement &fullElement, KoShapeLoadingContext &context, const KoXmlElement &element) const; // Map namespace,tagname to priority:factory QHash, QMultiMap > factoryMap; }; KoShapeRegistry::KoShapeRegistry() : d(new Private()) { } KoShapeRegistry::~KoShapeRegistry() { qDeleteAll(doubleEntries()); qDeleteAll(values()); delete d; } void KoShapeRegistry::Private::init(KoShapeRegistry *q) { KoPluginLoader::PluginsConfig config; config.whiteList = "FlakePlugins"; config.blacklist = "FlakePluginsDisabled"; config.group = "krita"; KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Flake"), QString::fromLatin1("[X-Flake-PluginVersion] == 28"), config); config.whiteList = "ShapePlugins"; config.blacklist = "ShapePluginsDisabled"; KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Shape"), QString::fromLatin1("[X-Flake-PluginVersion] == 28"), config); // Also add our hard-coded basic shapes q->add(new KoSvgTextShapeFactory()); q->add(new KoPathShapeFactory(QStringList())); // As long as there is no shape dealing with embedded svg images // we add the svg shape factory here by default q->add(new SvgShapeFactory); // Now all shape factories are registered with us, determine their // associated odf tagname & priority and prepare ourselves for // loading ODF. QList factories = q->values(); for (int i = 0; i < factories.size(); ++i) { insertFactory(factories[i]); } } KoShapeRegistry* KoShapeRegistry::instance() { if (!s_instance.exists()) { s_instance->d->init(s_instance); } return s_instance; } void KoShapeRegistry::addFactory(KoShapeFactoryBase * factory) { add(factory); d->insertFactory(factory); } void KoShapeRegistry::Private::insertFactory(KoShapeFactoryBase *factory) { const QList > odfElements(factory->odfElements()); if (odfElements.isEmpty()) { debugFlake << "Shape factory" << factory->id() << " does not have OdfNamespace defined, ignoring"; } else { int priority = factory->loadingPriority(); for (QList >::const_iterator it(odfElements.begin()); it != odfElements.end(); ++it) { foreach (const QString &elementName, (*it).second) { QPair p((*it).first, elementName); QMultiMap & priorityMap = factoryMap[p]; priorityMap.insert(priority, factory); debugFlake << "Inserting factory" << factory->id() << " for" << p << " with priority " << priority << " into factoryMap making " << priorityMap.size() << " entries. "; } } } } #include #include #include #include namespace { struct ObjectEntry { ObjectEntry() { } ObjectEntry(const ObjectEntry &rhs) : objectXmlContents(rhs.objectXmlContents), objectName(rhs.objectName), isDir(rhs.isDir) { } ~ObjectEntry() { } QByteArray objectXmlContents; // the XML tree in the object QString objectName; // object name in the frame without "./" // This is extracted from objectXmlContents. bool isDir = false; }; // A FileEntry is used to store information about embedded files // inside (i.e. referred to by) an object. struct FileEntry { FileEntry() {} FileEntry(const FileEntry &rhs) : path(rhs.path), mimeType(rhs.mimeType), isDir(rhs.isDir), contents(rhs.contents) { } QString path; // Normalized filename, i.e. without "./". QString mimeType; bool isDir; QByteArray contents; }; QByteArray loadFile(const QString &fileName, KoShapeLoadingContext &context) { // Can't load a file which is a directory, return an invalid QByteArray if (fileName.endsWith('/')) return QByteArray(); KoStore *store = context.store(); QByteArray fileContent; if (!store->open(fileName)) { store->close(); return QByteArray(); } int fileSize = store->size(); fileContent = store->read(fileSize); store->close(); //debugFlake << "File content: " << fileContent; return fileContent; } boost::optional storeFile(const QString &fileName, KoShapeLoadingContext &context) { debugFlake << "Saving file: " << fileName; boost::optional result; QByteArray fileContent = loadFile(fileName, context); if (!fileContent.isNull()) { // Actually store the file in the list. FileEntry entry; entry.path = fileName; if (entry.path.startsWith(QLatin1String("./"))) { entry.path.remove(0, 2); } entry.mimeType = context.mimeTypeForPath(entry.path); entry.isDir = false; entry.contents = fileContent; result = entry; } return result; } void storeXmlRecursive(const KoXmlElement &el, KoXmlWriter &writer, ObjectEntry *object, QHash &unknownNamespaces) { // Start the element; // keep the name in a QByteArray so that it stays valid until end element is called. const QByteArray name(el.nodeName().toLatin1()); writer.startElement(name.constData()); // Child elements // Loop through all the child elements of the draw:frame. KoXmlNode n = el.firstChild(); for (; !n.isNull(); n = n.nextSibling()) { if (n.isElement()) { storeXmlRecursive(n.toElement(), writer, object, unknownNamespaces); } else if (n.isText()) { writer.addTextNode(n.toText().data()/*.toUtf8()*/); } } // End the element writer.endElement(); } QVector storeObjects(const KoXmlElement &element) { QVector result; // Loop through all the child elements of the draw:frame and save them. KoXmlNode n = element.firstChild(); for (; !n.isNull(); n = n.nextSibling()) { debugFlake << "In draw:frame, node =" << n.nodeName(); // This disregards #text, but that's not in the spec anyway so // it doesn't need to be saved. if (!n.isElement()) continue; KoXmlElement el = n.toElement(); ObjectEntry object; QByteArray contentsTmp; QBuffer buffer(&contentsTmp); // the member KoXmlWriter writer(&buffer); // 1. Find out the objectName // Save the normalized filename, i.e. without a starting "./". // An empty string is saved if no name is found. QString name = el.attributeNS(KoXmlNS::xlink, "href", QString()); if (name.startsWith(QLatin1String("./"))) name.remove(0, 2); object.objectName = name; // 2. Copy the XML code. QHash unknownNamespaces; storeXmlRecursive(el, writer, &object, unknownNamespaces); object.objectXmlContents = contentsTmp; // 3, 4: the isDir and manifestEntry members are not set here, // but initialize them anyway. . object.isDir = false; // Has to be initialized to something. result.append(object); } return result; } } #include #include "kis_debug.h" #include #include #include #include #include KoShape * KoShapeRegistry::createShapeFromXML(const KoXmlElement & e, KoShapeLoadingContext & context) const { return 0; } KoShape *KoShapeRegistry::Private::createShapeInternal(const KoXmlElement &fullElement, KoShapeLoadingContext &context, const KoXmlElement &element) const { // Pair of namespace, tagname QPair p = QPair(element.namespaceURI(), element.tagName()); // Remove duplicate lookup. if (!factoryMap.contains(p)) return 0; QMultiMap priorityMap = factoryMap.value(p); QList factories = priorityMap.values(); #ifndef NDEBUG debugFlake << "Supported factories for=" << p; foreach (KoShapeFactoryBase *f, factories) debugFlake << f->id() << f->name(); #endif // Loop through all shape factories. If any of them supports this // element, then we let the factory create a shape from it. This // may fail because the element itself is too generic to draw any // real conclusions from it - we actually have to try to load it. // An example of this is the draw:image element which have // potentially hundreds of different image formats to support, // including vector formats. // // If it succeeds, then we use this shape, if it fails, then just // try the next. // // Higher numbers are more specific, map is sorted by keys. for (int i = factories.size() - 1; i >= 0; --i) { KoShapeFactoryBase * factory = factories[i]; if (factory->supports(element, context)) { KoShape *shape = factory->createShapeFromXML(fullElement, context); if (shape) { debugFlake << "Shape found for factory " << factory->id() << factory->name(); // we return the top-level most shape as that's the one that we'll have to // add to the KoShapeManager for painting later (and also to avoid memory leaks) // but don't go past a KoShapeLayer as KoShape adds those from the context // during loading and those are already added. while (shape->parent() && dynamic_cast(shape->parent()) == 0) shape = shape->parent(); return shape; } // Maybe a shape with a lower priority can load our // element, but this attempt has failed. } else { debugFlake << "No support for" << p << "by" << factory->id(); } } return 0; } QList KoShapeRegistry::factoriesForElement(const QString &nameSpace, const QString &elementName) { // Pair of namespace, tagname QPair p = QPair(nameSpace, elementName); QMultiMap priorityMap = d->factoryMap.value(p); QList shapeFactories; // sort list by priority Q_FOREACH (KoShapeFactoryBase *f, priorityMap.values()) { shapeFactories.prepend(f); } return shapeFactories; } diff --git a/libs/flake/KoShapeStroke.cpp b/libs/flake/KoShapeStroke.cpp index 3ce51cd1cb..a586ef6581 100644 --- a/libs/flake/KoShapeStroke.cpp +++ b/libs/flake/KoShapeStroke.cpp @@ -1,408 +1,407 @@ /* This file is part of the KDE project * * Copyright (C) 2006-2007 Thomas Zander * Copyright (C) 2006-2008 Jan Hambrecht * Copyright (C) 2007,2009 Thorsten Zachmann * Copyright (C) 2012 Inge Wallin * * 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. */ // Own #include "KoShapeStroke.h" // Posix #include // Qt #include #include // Calligra #include -#include // Flake #include "KoShape.h" #include "KoShapeSavingContext.h" #include "KoPathShape.h" #include "KoMarker.h" #include "KoInsets.h" #include #include #include #include "KisQPainterStateSaver.h" #include "kis_global.h" class Q_DECL_HIDDEN KoShapeStroke::Private { public: Private(KoShapeStroke *_q) : q(_q) {} KoShapeStroke *q; void paintBorder(const KoShape *shape, QPainter &painter, const QPen &pen) const; QColor color; QPen pen; QBrush brush; }; namespace { QPair anglesForSegment(KoPathSegment segment) { const qreal eps = 1e-6; if (segment.degree() < 3) { segment = segment.toCubic(); } QList points = segment.controlPoints(); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(points.size() == 4, qMakePair(0.0, 0.0)); QPointF vec1 = points[1] - points[0]; QPointF vec2 = points[3] - points[2]; if (vec1.manhattanLength() < eps) { points[1] = segment.pointAt(eps); vec1 = points[1] - points[0]; } if (vec2.manhattanLength() < eps) { points[2] = segment.pointAt(1.0 - eps); vec2 = points[3] - points[2]; } const qreal angle1 = std::atan2(vec1.y(), vec1.x()); const qreal angle2 = std::atan2(vec2.y(), vec2.x()); return qMakePair(angle1, angle2); } } void KoShapeStroke::Private::paintBorder(const KoShape *shape, QPainter &painter, const QPen &pen) const { if (!pen.isCosmetic() && pen.style() != Qt::NoPen) { const KoPathShape *pathShape = dynamic_cast(shape); if (pathShape) { QPainterPath path = pathShape->pathStroke(pen); painter.fillPath(path, pen.brush()); if (!pathShape->hasMarkers()) return; const bool autoFillMarkers = pathShape->autoFillMarkers(); KoMarker *startMarker = pathShape->marker(KoFlake::StartMarker); KoMarker *midMarker = pathShape->marker(KoFlake::MidMarker); KoMarker *endMarker = pathShape->marker(KoFlake::EndMarker); for (int i = 0; i < pathShape->subpathCount(); i++) { const int numSubPoints = pathShape->subpathPointCount(i); if (numSubPoints < 2) continue; const bool isClosedSubpath = pathShape->isClosedSubpath(i); qreal firstAngle = 0.0; { KoPathSegment segment = pathShape->segmentByIndex(KoPathPointIndex(i, 0)); firstAngle= anglesForSegment(segment).first; } const int numSegments = isClosedSubpath ? numSubPoints : numSubPoints - 1; qreal lastAngle = 0.0; { KoPathSegment segment = pathShape->segmentByIndex(KoPathPointIndex(i, numSegments - 1)); lastAngle = anglesForSegment(segment).second; } qreal previousAngle = 0.0; for (int j = 0; j < numSegments; j++) { KoPathSegment segment = pathShape->segmentByIndex(KoPathPointIndex(i, j)); QPair angles = anglesForSegment(segment); const qreal angle1 = angles.first; const qreal angle2 = angles.second; if (j == 0 && startMarker) { const qreal angle = isClosedSubpath ? bisectorAngle(firstAngle, lastAngle) : firstAngle; if (autoFillMarkers) { startMarker->applyShapeStroke(shape, q, segment.first()->point(), pen.widthF(), angle); } startMarker->paintAtPosition(&painter, segment.first()->point(), pen.widthF(), angle); } if (j > 0 && midMarker) { const qreal angle = bisectorAngle(previousAngle, angle1); if (autoFillMarkers) { midMarker->applyShapeStroke(shape, q, segment.first()->point(), pen.widthF(), angle); } midMarker->paintAtPosition(&painter, segment.first()->point(), pen.widthF(), angle); } if (j == numSegments - 1 && endMarker) { const qreal angle = isClosedSubpath ? bisectorAngle(firstAngle, lastAngle) : lastAngle; if (autoFillMarkers) { endMarker->applyShapeStroke(shape, q, segment.second()->point(), pen.widthF(), angle); } endMarker->paintAtPosition(&painter, segment.second()->point(), pen.widthF(), angle); } previousAngle = angle2; } } return; } painter.strokePath(shape->outline(), pen); } } KoShapeStroke::KoShapeStroke() : d(new Private(this)) { d->color = QColor(Qt::black); // we are not rendering stroke with zero width anymore // so lets use a default width of 1.0 d->pen.setWidthF(1.0); } KoShapeStroke::KoShapeStroke(const KoShapeStroke &other) : KoShapeStrokeModel(), d(new Private(this)) { d->color = other.d->color; d->pen = other.d->pen; d->brush = other.d->brush; } KoShapeStroke::KoShapeStroke(qreal lineWidth, const QColor &color) : d(new Private(this)) { d->pen.setWidthF(qMax(qreal(0.0), lineWidth)); d->pen.setJoinStyle(Qt::MiterJoin); d->color = color; } KoShapeStroke::~KoShapeStroke() { delete d; } KoShapeStroke &KoShapeStroke::operator = (const KoShapeStroke &rhs) { if (this == &rhs) return *this; d->pen = rhs.d->pen; d->color = rhs.d->color; d->brush = rhs.d->brush; return *this; } void KoShapeStroke::strokeInsets(const KoShape *shape, KoInsets &insets) const { Q_UNUSED(shape); // '0.5' --- since we draw a line half inside, and half outside the object. qreal extent = 0.5 * (d->pen.widthF() >= 0 ? d->pen.widthF() : 1.0); // if we have square cap, we need a little more space // -> sqrt((0.5*penWidth)^2 + (0.5*penWidth)^2) if (capStyle() == Qt::SquareCap) { extent *= M_SQRT2; } if (joinStyle() == Qt::MiterJoin) { // miter limit in Qt is normalized by the line width (and not half-width) extent = qMax(extent, d->pen.widthF() * miterLimit()); } insets.top = extent; insets.bottom = extent; insets.left = extent; insets.right = extent; } qreal KoShapeStroke::strokeMaxMarkersInset(const KoShape *shape) const { qreal result = 0.0; const KoPathShape *pathShape = dynamic_cast(shape); if (pathShape && pathShape->hasMarkers()) { const qreal lineWidth = d->pen.widthF(); QVector markers; markers << pathShape->marker(KoFlake::StartMarker); markers << pathShape->marker(KoFlake::MidMarker); markers << pathShape->marker(KoFlake::EndMarker); Q_FOREACH (const KoMarker *marker, markers) { if (marker) { result = qMax(result, marker->maxInset(lineWidth)); } } } return result; } bool KoShapeStroke::hasTransparency() const { return d->color.alpha() > 0; } QPen KoShapeStroke::resultLinePen() const { QPen pen = d->pen; if (d->brush.gradient()) { pen.setBrush(d->brush); } else { pen.setColor(d->color); } return pen; } void KoShapeStroke::paint(const KoShape *shape, QPainter &painter) const { KisQPainterStateSaver saver(&painter); d->paintBorder(shape, painter, resultLinePen()); } bool KoShapeStroke::compareFillTo(const KoShapeStrokeModel *other) { if (!other) return false; const KoShapeStroke *stroke = dynamic_cast(other); if (!stroke) return false; return (d->brush.gradient() && d->brush == stroke->d->brush) || (!d->brush.gradient() && d->color == stroke->d->color); } bool KoShapeStroke::compareStyleTo(const KoShapeStrokeModel *other) { if (!other) return false; const KoShapeStroke *stroke = dynamic_cast(other); if (!stroke) return false; QPen pen1 = d->pen; QPen pen2 = stroke->d->pen; // just a random color top avoid comparison of that property pen1.setColor(Qt::magenta); pen2.setColor(Qt::magenta); return pen1 == pen2; } bool KoShapeStroke::isVisible() const { return d->pen.widthF() > 0 && (d->brush.gradient() || d->color.alpha() > 0); } void KoShapeStroke::setCapStyle(Qt::PenCapStyle style) { d->pen.setCapStyle(style); } Qt::PenCapStyle KoShapeStroke::capStyle() const { return d->pen.capStyle(); } void KoShapeStroke::setJoinStyle(Qt::PenJoinStyle style) { d->pen.setJoinStyle(style); } Qt::PenJoinStyle KoShapeStroke::joinStyle() const { return d->pen.joinStyle(); } void KoShapeStroke::setLineWidth(qreal lineWidth) { d->pen.setWidthF(qMax(qreal(0.0), lineWidth)); } qreal KoShapeStroke::lineWidth() const { return d->pen.widthF(); } void KoShapeStroke::setMiterLimit(qreal miterLimit) { d->pen.setMiterLimit(miterLimit); } qreal KoShapeStroke::miterLimit() const { return d->pen.miterLimit(); } QColor KoShapeStroke::color() const { return d->color; } void KoShapeStroke::setColor(const QColor &color) { d->color = color; } void KoShapeStroke::setLineStyle(Qt::PenStyle style, const QVector &dashes) { if (style < Qt::CustomDashLine) { d->pen.setStyle(style); } else { d->pen.setDashPattern(dashes); } } Qt::PenStyle KoShapeStroke::lineStyle() const { return d->pen.style(); } QVector KoShapeStroke::lineDashes() const { return d->pen.dashPattern(); } void KoShapeStroke::setDashOffset(qreal dashOffset) { d->pen.setDashOffset(dashOffset); } qreal KoShapeStroke::dashOffset() const { return d->pen.dashOffset(); } void KoShapeStroke::setLineBrush(const QBrush &brush) { d->brush = brush; } QBrush KoShapeStroke::lineBrush() const { return d->brush; } diff --git a/libs/flake/KoTosContainer.cpp b/libs/flake/KoTosContainer.cpp index 97218f356b..0491925985 100644 --- a/libs/flake/KoTosContainer.cpp +++ b/libs/flake/KoTosContainer.cpp @@ -1,250 +1,249 @@ /* This file is part of the KDE project * Copyright (C) 2010 Thomas Zander * Copyright (C) 2010 KO GmbH * Copyright (C) 2010 Thorsten Zachmann * * 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 "KoTosContainer.h" #include "KoTosContainer_p.h" #include "KoShapeRegistry.h" #include "KoShapeFactoryBase.h" #include "KoShapeLoadingContext.h" #include "KoTextShapeDataBase.h" #include "KoTosContainerModel.h" -#include "KoStyleStack.h" #include "KoXmlNS.h" #include "KoGenStyle.h" #include #include #include KoTosContainer::Private::Private() : QSharedData() , resizeBehavior(KoTosContainer::IndependentSizes) { } KoTosContainer::Private::Private(const Private &rhs) : QSharedData() , resizeBehavior(rhs.resizeBehavior) , preferredTextRect(rhs.preferredTextRect) , alignment(rhs.alignment) { } KoTosContainer::Private::~Private() { } KoTosContainer::KoTosContainer() : KoShapeContainer() , d(new Private) { } KoTosContainer::KoTosContainer(const KoTosContainer &rhs) : KoShapeContainer(rhs) , d(rhs.d) { } KoTosContainer::~KoTosContainer() { delete textShape(); } void KoTosContainer::paintComponent(QPainter &, KoShapePaintingContext &) const { } bool KoTosContainer::loadText(const KoXmlElement &element, KoShapeLoadingContext &context) { return false; } void KoTosContainer::setPlainText(const QString &text) { KoShape *textShape = this->textShape(); if (textShape == 0) { warnFlake << "No text shape present in KoTosContainer"; return; } KoTextShapeDataBase *shapeData = qobject_cast(textShape->userData()); Q_ASSERT(shapeData->document()); shapeData->document()->setPlainText(text); } void KoTosContainer::setResizeBehavior(ResizeBehavior resizeBehavior) { if (d->resizeBehavior == resizeBehavior) { return; } d->resizeBehavior = resizeBehavior; if (model()) { model()->containerChanged(this, KoShape::SizeChanged); } } KoTosContainer::ResizeBehavior KoTosContainer::resizeBehavior() const { return d->resizeBehavior; } void KoTosContainer::setTextAlignment(Qt::Alignment alignment) { KoShape *textShape = this->textShape(); if (textShape == 0) { warnFlake << "No text shape present in KoTosContainer"; return; } // vertical KoTextShapeDataBase *shapeData = qobject_cast(textShape->userData()); shapeData->setVerticalAlignment(alignment); // horizontal Q_ASSERT(shapeData->document()); QTextBlockFormat bf; bf.setAlignment(alignment & Qt::AlignHorizontal_Mask); QTextCursor cursor(shapeData->document()); cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); cursor.mergeBlockFormat(bf); d->alignment = alignment; } Qt::Alignment KoTosContainer::textAlignment() const { KoShape *textShape = this->textShape(); if (textShape == 0) { warnFlake << "No text shape present in KoTosContainer"; return Qt::AlignTop; } // vertical KoTextShapeDataBase *shapeData = qobject_cast(textShape->userData()); // the model makes sure it contains a shape that has a KoTextShapeDataBase set so no need to check that Qt::Alignment answer = shapeData->verticalAlignment() & Qt::AlignVertical_Mask; // horizontal Q_ASSERT(shapeData->document()); QTextCursor cursor(shapeData->document()); answer = answer | (cursor.blockFormat().alignment() & Qt::AlignHorizontal_Mask); return answer; } void KoTosContainer::setPreferredTextRect(const QRectF &rect) { d->preferredTextRect = rect; KoShape *textShape = this->textShape(); //debugFlake << rect << textShape << d->resizeBehavior; if (d->resizeBehavior == TextFollowsPreferredTextRect && textShape) { //debugFlake << rect; textShape->setPosition(rect.topLeft()); textShape->setSize(rect.size()); } } QRectF KoTosContainer::preferredTextRect() const { return d->preferredTextRect; } KoShape *KoTosContainer::createTextShape(KoDocumentResourceManager *documentResources) { if (!documentResources) { warnFlake << "KoDocumentResourceManager not found"; return 0; } delete textShape(); delete model(); setModel(new KoTosContainerModel()); QSet delegates; delegates << this; KoShape *textShape = 0; KoShapeFactoryBase *factory = KoShapeRegistry::instance()->get("TextShapeID"); if (factory) { // not installed, that's too bad, but allowed textShape = factory->createDefaultShape(documentResources); Q_ASSERT(textShape); // would be a bug in the text shape; if (d->resizeBehavior == TextFollowsPreferredTextRect) { textShape->setSize(d->preferredTextRect.size()); } else { textShape->setSize(size()); } if (d->resizeBehavior == TextFollowsPreferredTextRect) { textShape->setPosition(d->preferredTextRect.topLeft()); } else { textShape->setPosition(QPointF(0, 0)); } textShape->setSelectable(false); textShape->setRunThrough(runThrough()); KoTextShapeDataBase *shapeData = qobject_cast(textShape->userData()); Q_ASSERT(shapeData); // would be a bug in kotext // TODO check if that is correct depending on the resize mode shapeData->setVerticalAlignment(Qt::AlignVCenter); addShape(textShape); // textShape->setZIndex(zIndex() + 1); // not needed as there as the text shape is the only sub shape delegates << textShape; } else { warnFlake << "Text shape factory not found"; } setToolDelegates(delegates); return textShape; } KoShape *KoTosContainer::textShape() const { const QList subShapes = shapes(); return subShapes.isEmpty() ? 0 : subShapes.at(0); } void KoTosContainer::shapeChanged(ChangeType type, KoShape *shape) { Q_UNUSED(shape); if (model() == 0) { return; } if (type == SizeChanged || type == ContentChanged) { model()->containerChanged(this, type); } // TODO is this needed? #if 0 Q_FOREACH (KoShape *shape, model()->shapes()) shape->notifyChanged(); #endif } void KoTosContainer::setRunThrough(short int runThrough) { KoShape::setRunThrough(runThrough); KoShape *textShape = this->textShape(); if (textShape) { textShape->setRunThrough(runThrough); } } diff --git a/libs/odf/CMakeLists.txt b/libs/odf/CMakeLists.txt index 8cc3d972de..73c55f71c4 100644 --- a/libs/odf/CMakeLists.txt +++ b/libs/odf/CMakeLists.txt @@ -1,25 +1,22 @@ 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 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/KoOdfGraphicStyles.cpp b/libs/odf/KoOdfGraphicStyles.cpp deleted file mode 100644 index 1b6ebb1179..0000000000 --- a/libs/odf/KoOdfGraphicStyles.cpp +++ /dev/null @@ -1,783 +0,0 @@ -/* This file is part of the KDE project - Copyright (C) 2004-2006 David Faure - Copyright (C) 2007 Jan Hambrecht - Copyright (C) 2007-2008,2010-2011 Thorsten Zachmann - Copyright (C) 2011 Lukáš Tvrdý - - 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 "KoOdfGraphicStyles.h" - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include "KoOdfStylesReader.h" - -void KoOdfGraphicStyles::saveOdfFillStyle(KoGenStyle &styleFill, KoGenStyles& mainStyles, const QBrush & brush) -{ - KoGenStyle::Type type = styleFill.type(); - KoGenStyle::PropertyType propertyType = (type == KoGenStyle::GraphicStyle || type == KoGenStyle::GraphicAutoStyle || - type == KoGenStyle::DrawingPageStyle || type == KoGenStyle::DrawingPageAutoStyle ) - ? KoGenStyle::DefaultType : KoGenStyle::GraphicType; - switch (brush.style()) { - case Qt::Dense1Pattern: - styleFill.addProperty("draw:opacity", "6%", propertyType); - styleFill.addProperty("draw:fill", "solid", propertyType); - styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType); - break; - case Qt::Dense2Pattern: - styleFill.addProperty("draw:opacity", "12%", propertyType); - styleFill.addProperty("draw:fill", "solid", propertyType); - styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType); - break; - case Qt::Dense3Pattern: - styleFill.addProperty("draw:opacity", "37%", propertyType); - styleFill.addProperty("draw:fill", "solid", propertyType); - styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType); - break; - case Qt::Dense4Pattern: - styleFill.addProperty("draw:opacity", "50%", propertyType); - styleFill.addProperty("draw:fill", "solid", propertyType); - styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType); - break; - case Qt::Dense5Pattern: - styleFill.addProperty("draw:opacity", "63%", propertyType); - styleFill.addProperty("draw:fill", "solid", propertyType); - styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType); - break; - case Qt::Dense6Pattern: - styleFill.addProperty("draw:opacity", "88%", propertyType); - styleFill.addProperty("draw:fill", "solid", propertyType); - styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType); - break; - case Qt::Dense7Pattern: - styleFill.addProperty("draw:opacity", "94%", propertyType); - styleFill.addProperty("draw:fill", "solid", propertyType); - styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType); - break; - case Qt::LinearGradientPattern: - case Qt::RadialGradientPattern: - case Qt::ConicalGradientPattern: - styleFill.addProperty("draw:fill", "gradient", propertyType); - styleFill.addProperty("draw:fill-gradient-name", saveOdfGradientStyle(mainStyles, brush), propertyType); - break; - case Qt::HorPattern: - case Qt::VerPattern: - case Qt::CrossPattern: - case Qt::BDiagPattern: - case Qt::FDiagPattern: - case Qt::DiagCrossPattern: - styleFill.addProperty("draw:fill", "hatch", propertyType); - styleFill.addProperty("draw:fill-hatch-name", saveOdfHatchStyle(mainStyles, brush), propertyType); - break; - case Qt::SolidPattern: - styleFill.addProperty("draw:fill", "solid", propertyType); - styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType); - if (! brush.isOpaque()) - styleFill.addProperty("draw:opacity", QString("%1%").arg(brush.color().alphaF() * 100.0), propertyType); - break; - case Qt::NoBrush: - default: - styleFill.addProperty("draw:fill", "none", propertyType); - break; - } -} - -void KoOdfGraphicStyles::saveOdfStrokeStyle(KoGenStyle &styleStroke, KoGenStyles &mainStyles, const QPen &pen) -{ - // TODO implement all possibilities - switch (pen.style()) { - case Qt::NoPen: - styleStroke.addProperty("draw:stroke", "none", KoGenStyle::GraphicType); - return; - case Qt::SolidLine: - styleStroke.addProperty("draw:stroke", "solid", KoGenStyle::GraphicType); - break; - default: { // must be a dashed line - styleStroke.addProperty("draw:stroke", "dash", KoGenStyle::GraphicType); - // save stroke dash (14.14.7) which is severely limited, but still - KoGenStyle dashStyle(KoGenStyle::StrokeDashStyle); - dashStyle.addAttribute("draw:style", "rect"); - QVector dashes = pen.dashPattern(); - dashStyle.addAttribute("draw:dots1", static_cast(1)); - dashStyle.addAttribute("draw:dots1-length", dashes[0]*pen.widthF()); - dashStyle.addAttribute("draw:distance", dashes[1]*pen.widthF()); - if (dashes.size() > 2) { - dashStyle.addAttribute("draw:dots2", static_cast(1)); - dashStyle.addAttribute("draw:dots2-length", dashes[2]*pen.widthF()); - } - QString dashStyleName = mainStyles.insert(dashStyle, "dash"); - styleStroke.addProperty("draw:stroke-dash", dashStyleName, KoGenStyle::GraphicType); - break; - } - } - - if (pen.brush().gradient()) { - styleStroke.addProperty("calligra:stroke-gradient", saveOdfGradientStyle(mainStyles, pen.brush()), KoGenStyle::GraphicType); - } - else { - styleStroke.addProperty("svg:stroke-color", pen.color().name(), KoGenStyle::GraphicType); - styleStroke.addProperty("svg:stroke-opacity", QString("%1").arg(pen.color().alphaF()), KoGenStyle::GraphicType); - } - styleStroke.addPropertyPt("svg:stroke-width", pen.widthF(), KoGenStyle::GraphicType); - - switch (pen.joinStyle()) { - case Qt::MiterJoin: - styleStroke.addProperty("draw:stroke-linejoin", "miter", KoGenStyle::GraphicType); - break; - case Qt::BevelJoin: - styleStroke.addProperty("draw:stroke-linejoin", "bevel", KoGenStyle::GraphicType); - break; - case Qt::RoundJoin: - styleStroke.addProperty("draw:stroke-linejoin", "round", KoGenStyle::GraphicType); - break; - default: - styleStroke.addProperty("draw:stroke-linejoin", "miter", KoGenStyle::GraphicType); - styleStroke.addProperty("calligra:stroke-miterlimit", QString("%1").arg(pen.miterLimit()), KoGenStyle::GraphicType); - break; - } - switch (pen.capStyle()) { - case Qt::RoundCap: - styleStroke.addProperty("svg:stroke-linecap", "round", KoGenStyle::GraphicType); - break; - case Qt::SquareCap: - styleStroke.addProperty("svg:stroke-linecap", "square", KoGenStyle::GraphicType); - break; - default: - styleStroke.addProperty("svg:stroke-linecap", "butt", KoGenStyle::GraphicType); - break; - } -} - -QString KoOdfGraphicStyles::saveOdfHatchStyle(KoGenStyles& mainStyles, const QBrush &brush) -{ - KoGenStyle hatchStyle(KoGenStyle::HatchStyle /*no family name*/); - hatchStyle.addAttribute("draw:color", brush.color().name()); - //hatchStyle.addAttribute( "draw:distance", m_distance ); not implemented into kpresenter - switch (brush.style()) { - case Qt::HorPattern: - hatchStyle.addAttribute("draw:style", "single"); - hatchStyle.addAttribute("draw:rotation", 0); - break; - case Qt::BDiagPattern: - hatchStyle.addAttribute("draw:style", "single"); - hatchStyle.addAttribute("draw:rotation", 450); - break; - case Qt::VerPattern: - hatchStyle.addAttribute("draw:style", "single"); - hatchStyle.addAttribute("draw:rotation", 900); - break; - case Qt::FDiagPattern: - hatchStyle.addAttribute("draw:style", "single"); - hatchStyle.addAttribute("draw:rotation", 1350); - break; - case Qt::CrossPattern: - hatchStyle.addAttribute("draw:style", "double"); - hatchStyle.addAttribute("draw:rotation", 0); - break; - case Qt::DiagCrossPattern: - hatchStyle.addAttribute("draw:style", "double"); - hatchStyle.addAttribute("draw:rotation", 450); - break; - default: - break; - } - - return mainStyles.insert(hatchStyle, "hatch"); -} - -QString KoOdfGraphicStyles::saveOdfGradientStyle(KoGenStyles &mainStyles, const QBrush &brush) -{ - KoGenStyle gradientStyle; - if (brush.style() == Qt::RadialGradientPattern) { - const QRadialGradient *gradient = static_cast(brush.gradient()); - gradientStyle = KoGenStyle(KoGenStyle::RadialGradientStyle /*no family name*/); - gradientStyle.addAttributePercent("svg:cx", gradient->center().x() * 100); - gradientStyle.addAttributePercent("svg:cy", gradient->center().y() * 100); - gradientStyle.addAttributePercent("svg:r", gradient->radius() * 100); - gradientStyle.addAttributePercent("svg:fx", gradient->focalPoint().x() * 100); - gradientStyle.addAttributePercent("svg:fy", gradient->focalPoint().y() * 100); - } else if (brush.style() == Qt::LinearGradientPattern) { - const QLinearGradient *gradient = static_cast(brush.gradient()); - gradientStyle = KoGenStyle(KoGenStyle::LinearGradientStyle /*no family name*/); - gradientStyle.addAttributePercent("svg:x1", gradient->start().x() * 100); - gradientStyle.addAttributePercent("svg:y1", gradient->start().y() * 100); - gradientStyle.addAttributePercent("svg:x2", gradient->finalStop().x() * 100); - gradientStyle.addAttributePercent("svg:y2", gradient->finalStop().y() * 100); - } else if (brush.style() == Qt::ConicalGradientPattern) { - const QConicalGradient * gradient = static_cast(brush.gradient()); - gradientStyle = KoGenStyle(KoGenStyle::ConicalGradientStyle /*no family name*/); - gradientStyle.addAttributePercent("svg:cx", gradient->center().x() * 100); - gradientStyle.addAttributePercent("svg:cy", gradient->center().y() * 100); - gradientStyle.addAttribute("draw:angle", QString("%1").arg(gradient->angle())); - } - const QGradient * gradient = brush.gradient(); - if (gradient->spread() == QGradient::RepeatSpread) - gradientStyle.addAttribute("svg:spreadMethod", "repeat"); - else if (gradient->spread() == QGradient::ReflectSpread) - gradientStyle.addAttribute("svg:spreadMethod", "reflect"); - else - gradientStyle.addAttribute("svg:spreadMethod", "pad"); - - if (! brush.transform().isIdentity()) { - gradientStyle.addAttribute("svg:gradientTransform", saveTransformation(brush.transform())); - } - - QBuffer buffer; - buffer.open(QIODevice::WriteOnly); - KoXmlWriter elementWriter(&buffer); // TODO pass indentation level - - // save stops - QGradientStops stops = gradient->stops(); - Q_FOREACH (const QGradientStop & stop, stops) { - elementWriter.startElement("svg:stop"); - elementWriter.addAttribute("svg:offset", QString("%1").arg(stop.first)); - elementWriter.addAttribute("svg:stop-color", stop.second.name()); - if (stop.second.alphaF() < 1.0) - elementWriter.addAttribute("svg:stop-opacity", QString("%1").arg(stop.second.alphaF())); - elementWriter.endElement(); - } - - QString elementContents = QString::fromUtf8(buffer.buffer(), buffer.buffer().size()); - gradientStyle.addChildElement("svg:stop", elementContents); - - return mainStyles.insert(gradientStyle, "gradient"); -} - -QBrush KoOdfGraphicStyles::loadOdfGradientStyle(const KoStyleStack &styleStack, const KoOdfStylesReader & stylesReader, const QSizeF &size) -{ - QString styleName = styleStack.property(KoXmlNS::draw, "fill-gradient-name"); - return loadOdfGradientStyleByName(stylesReader, styleName, size); -} - -qreal percent(const KoXmlElement &element, const QString &ns, const QString &type, const QString &defaultValue, qreal absolute) -{ - qreal tmp = 0.0; - QString value = element.attributeNS(ns, type, defaultValue); - if (value.indexOf('%') > -1) { // percent value - tmp = value.remove('%').toDouble() / 100.0; - } - else { // fixed value - tmp = KoUnit::parseValue(value) / absolute; - // The following is done so that we get the same data as when we save/load. - // This is needed that we get the same values due to rounding differences - // of absolute and relative values. - QString value = QString("%1").arg(tmp * 100.0); - tmp = value.toDouble() / 100; - } - - return tmp; -} - -QBrush KoOdfGraphicStyles::loadOdfGradientStyleByName(const KoOdfStylesReader &stylesReader, const QString &styleName, const QSizeF &size) -{ - KoXmlElement* e = stylesReader.drawStyles("gradient")[styleName]; - if (! e) - return QBrush(); - - QGradient * gradient = 0; - QTransform transform; - - if (e->namespaceURI() == KoXmlNS::draw && e->localName() == "gradient") { - // FIXME seems like oo renders the gradient start stop color at the center of the - // radial gradient, and the start color at the radius of the radial gradient - // whereas it is not mentioned in the spec how it should be rendered - // note that svg defines that exactly as the opposite as oo does - // so what should we do? - QString type = e->attributeNS(KoXmlNS::draw, "style", QString()); - if (type == "radial") { - // Zagge: at the moment the only objectBoundingBox is supported: - // 18.539 svg:gradientUnits - // See §13.2.2 and §13.2.3 of [SVG]. - // The default value for this attribute is objectBoundingBox. - // The only value of the svg:gradientUnits attribute is objectBoundingBox. - - qreal cx = KoUnit::parseValue(e->attributeNS(KoXmlNS::draw, "cx", QString()).remove('%')); - qreal cy = KoUnit::parseValue(e->attributeNS(KoXmlNS::draw, "cy", QString()).remove('%')); - gradient = new QRadialGradient(QPointF(cx * 0.01, cy * 0.01), sqrt(0.5)); - gradient->setCoordinateMode(QGradient::ObjectBoundingMode); - } else if (type == "linear" || type == "axial") { - QLinearGradient * lg = new QLinearGradient(); - lg->setCoordinateMode(QGradient::ObjectBoundingMode); - // Dividing by 10 here because OOo saves as degree * 10 - qreal angle = 90 + e->attributeNS(KoXmlNS::draw, "angle", "0").toDouble() / 10; - qreal radius = sqrt(0.5); - - qreal sx = cos(angle * M_PI / 180) * radius; - qreal sy = sin(angle * M_PI / 180) * radius; - lg->setStart(QPointF(0.5 + sx, 0.5 - sy)); - lg->setFinalStop(QPointF(0.5 - sx, 0.5 + sy)); - gradient = lg; - } else - return QBrush(); - - qreal border = 0.01 * e->attributeNS(KoXmlNS::draw, "border", "0").remove('%').toDouble(); - QGradientStops stops; - if (type != "axial") { - // In case of radial gradients the colors are reversed, because OOo saves them as the opposite of the SVG direction - // see bug 137639 - QGradientStop start; - start.first = (type != "radial") ? border : 1.0 - border; - start.second = QColor(e->attributeNS(KoXmlNS::draw, "start-color", QString())); - start.second.setAlphaF(0.01 * e->attributeNS(KoXmlNS::draw, "start-intensity", "100").remove('%').toDouble()); - - QGradientStop end; - end.first = (type != "radial") ? 1.0 : 0.0; - end.second = QColor(e->attributeNS(KoXmlNS::draw, "end-color", QString())); - end.second.setAlphaF(0.01 * e->attributeNS(KoXmlNS::draw, "end-intensity", "100").remove('%').toDouble()); - - stops << start << end; - } else { - QGradientStop start; - start.first = 0.5 * border; - start.second = QColor(e->attributeNS(KoXmlNS::draw, "end-color", QString())); - start.second.setAlphaF(0.01 * e->attributeNS(KoXmlNS::draw, "end-intensity", "100").remove('%').toDouble()); - - QGradientStop middle; - middle.first = 0.5; - middle.second = QColor(e->attributeNS(KoXmlNS::draw, "start-color", QString())); - middle.second.setAlphaF(0.01 * e->attributeNS(KoXmlNS::draw, "start-intensity", "100").remove('%').toDouble()); - - QGradientStop end; - end.first = 1.0 - 0.5 * border; - end.second = QColor(e->attributeNS(KoXmlNS::draw, "end-color", QString())); - end.second.setAlphaF(0.01 * e->attributeNS(KoXmlNS::draw, "end-intensity", "100").remove('%').toDouble()); - - stops << start << middle << end; - } - - gradient->setStops(stops); - } else if (e->namespaceURI() == KoXmlNS::svg) { - if (e->localName() == "linearGradient") { - QPointF start, stop; - start.setX(percent(*e, KoXmlNS::svg, "x1", "0%", size.width())); - start.setY(percent(*e, KoXmlNS::svg, "y1", "0%", size.height())); - stop.setX(percent(*e, KoXmlNS::svg, "x2", "100%", size.width())); - stop.setY(percent(*e, KoXmlNS::svg, "y2", "100%", size.height())); - gradient = new QLinearGradient(start, stop); - } else if (e->localName() == "radialGradient") { - QPointF center, focalPoint; - center.setX(percent(*e, KoXmlNS::svg, "cx", "50%", size.width())); - center.setY(percent(*e, KoXmlNS::svg, "cy", "50%", size.height())); - qreal r = percent(*e, KoXmlNS::svg, "r", "50%", sqrt(size.width() * size.width() + size.height() * size.height())); - focalPoint.setX(percent(*e, KoXmlNS::svg, "fx", QString(), size.width())); - focalPoint.setY(percent(*e, KoXmlNS::svg, "fy", QString(), size.height())); - gradient = new QRadialGradient(center, r, focalPoint ); - } - if (! gradient) - return QBrush(); - - gradient->setCoordinateMode(QGradient::ObjectBoundingMode); - - QString strSpread(e->attributeNS(KoXmlNS::svg, "spreadMethod", "pad")); - if (strSpread == "repeat") - gradient->setSpread(QGradient::RepeatSpread); - else if (strSpread == "reflect") - gradient->setSpread(QGradient::ReflectSpread); - else - gradient->setSpread(QGradient::PadSpread); - - if (e->hasAttributeNS(KoXmlNS::svg, "gradientTransform")) - transform = loadTransformation(e->attributeNS(KoXmlNS::svg, "gradientTransform", QString())); - - QGradientStops stops; - - // load stops - KoXmlElement colorstop; - forEachElement(colorstop, (*e)) { - if (colorstop.namespaceURI() == KoXmlNS::svg && colorstop.localName() == "stop") { - QGradientStop stop; - stop.second = QColor(colorstop.attributeNS(KoXmlNS::svg, "stop-color", QString())); - stop.second.setAlphaF(colorstop.attributeNS(KoXmlNS::svg, "stop-opacity", "1.0").toDouble()); - stop.first = colorstop.attributeNS(KoXmlNS::svg, "offset", "0.0").toDouble(); - stops.append(stop); - } - } - gradient->setStops(stops); - } else if (e->namespaceURI() == KoXmlNS::calligra) { - if (e->localName() == "conicalGradient") { - QPointF center; - center.setX(percent(*e, KoXmlNS::svg, "cx", "50%", size.width())); - center.setY(percent(*e, KoXmlNS::svg, "cy", "50%", size.height())); - qreal angle = KoUnit::parseValue(e->attributeNS(KoXmlNS::draw, "angle", QString())); - gradient = new QConicalGradient(center, angle); - gradient->setCoordinateMode(QGradient::ObjectBoundingMode); - - QString strSpread(e->attributeNS(KoXmlNS::svg, "spreadMethod", "pad")); - if (strSpread == "repeat") - gradient->setSpread(QGradient::RepeatSpread); - else if (strSpread == "reflect") - gradient->setSpread(QGradient::ReflectSpread); - else - gradient->setSpread(QGradient::PadSpread); - - if (e->hasAttributeNS(KoXmlNS::svg, "gradientTransform")) - transform = loadTransformation(e->attributeNS(KoXmlNS::svg, "gradientTransform", QString())); - - QGradientStops stops; - - // load stops - KoXmlElement colorstop; - forEachElement(colorstop, (*e)) { - if (colorstop.namespaceURI() == KoXmlNS::svg && colorstop.localName() == "stop") { - QGradientStop stop; - stop.second = QColor(colorstop.attributeNS(KoXmlNS::svg, "stop-color", QString())); - stop.second.setAlphaF(colorstop.attributeNS(KoXmlNS::svg, "stop-opacity", "1.0").toDouble()); - stop.first = colorstop.attributeNS(KoXmlNS::svg, "offset", "0.0").toDouble(); - stops.append(stop); - } - } - gradient->setStops(stops); - } - } - - if (! gradient) - return QBrush(); - - QBrush resultBrush(*gradient); - resultBrush.setTransform(transform); - - delete gradient; - return resultBrush; -} - -QBrush KoOdfGraphicStyles::loadOdfFillStyle(const KoStyleStack &styleStack, const QString & fill, const KoOdfStylesReader & stylesReader) -{ - QBrush tmpBrush; // default brush for "none" is a Qt::NoBrush - - if (fill == "solid") { - tmpBrush.setStyle(Qt::SolidPattern); - if (styleStack.hasProperty(KoXmlNS::draw, "fill-color")) - tmpBrush.setColor(styleStack.property(KoXmlNS::draw, "fill-color")); - if (styleStack.hasProperty(KoXmlNS::draw, "opacity")) { - QString opacity = styleStack.property(KoXmlNS::draw, "opacity"); - if (! opacity.isEmpty() && opacity.right(1) == "%") { - float percent = opacity.left(opacity.length() - 1).toFloat(); - QColor color = tmpBrush.color(); - color.setAlphaF(percent / 100.0); - tmpBrush.setColor(color); - } - } - //TODO - if (styleStack.hasProperty(KoXmlNS::draw, "transparency")) { - QString transparency = styleStack.property(KoXmlNS::draw, "transparency"); - if (transparency == "94%") { - tmpBrush.setStyle(Qt::Dense1Pattern); - } else if (transparency == "88%") { - tmpBrush.setStyle(Qt::Dense2Pattern); - } else if (transparency == "63%") { - tmpBrush.setStyle(Qt::Dense3Pattern); - - } else if (transparency == "50%") { - tmpBrush.setStyle(Qt::Dense4Pattern); - - } else if (transparency == "37%") { - tmpBrush.setStyle(Qt::Dense5Pattern); - - } else if (transparency == "12%") { - tmpBrush.setStyle(Qt::Dense6Pattern); - - } else if (transparency == "6%") { - tmpBrush.setStyle(Qt::Dense7Pattern); - - } else - debugOdf << " transparency is not defined into kpresenter :" << transparency; - } - } else if (fill == "hatch") { - QString style = styleStack.property(KoXmlNS::draw, "fill-hatch-name"); - debugOdf << " hatch style is :" << style; - - //type not defined by default - //try to use style. - KoXmlElement* draw = stylesReader.drawStyles("hatch")[style]; - if (draw) { - debugOdf << "We have a style"; - int angle = 0; - if (draw->hasAttributeNS(KoXmlNS::draw, "rotation")) { - angle = (draw->attributeNS(KoXmlNS::draw, "rotation", QString()).toInt()) / 10; - debugOdf << "angle :" << angle; - } - if (draw->hasAttributeNS(KoXmlNS::draw, "color")) { - //debugOdf<<" draw:color :"<attributeNS( KoXmlNS::draw,"color", QString() ); - tmpBrush.setColor(draw->attributeNS(KoXmlNS::draw, "color", QString())); - } - if (draw->hasAttributeNS(KoXmlNS::draw, "distance")) { - //todo implement it into kpresenter - } - if (draw->hasAttributeNS(KoXmlNS::draw, "display-name")) { - //todo implement it into kpresenter - } - if (draw->hasAttributeNS(KoXmlNS::draw, "style")) { - //todo implement it into kpresenter - QString styleHash = draw->attributeNS(KoXmlNS::draw, "style", QString()); - if (styleHash == "single") { - switch (angle) { - case 0: - case 180: - tmpBrush.setStyle(Qt::HorPattern); - break; - case 45: - case 225: - tmpBrush.setStyle(Qt::BDiagPattern); - break; - case 90: - case 270: - tmpBrush.setStyle(Qt::VerPattern); - break; - case 135: - case 315: - tmpBrush.setStyle(Qt::FDiagPattern); - break; - default: - //todo fixme when we will have a kopaint - debugOdf << " draw:rotation 'angle' :" << angle; - break; - } - } else if (styleHash == "double") { - switch (angle) { - case 0: - case 180: - case 90: - case 270: - tmpBrush.setStyle(Qt::CrossPattern); - break; - case 45: - case 135: - case 225: - case 315: - tmpBrush.setStyle(Qt::DiagCrossPattern); - break; - default: - //todo fixme when we will have a kopaint - debugOdf << " draw:rotation 'angle' :" << angle; - break; - } - - } else if (styleHash == "triple") { - debugOdf << " it is not implemented :("; - } - } - } - } - - return tmpBrush; -} - -static qreal parseDashEntrySize(QString& attr, qreal penWidth, qreal defaultValue = 0.0){ - qreal result = defaultValue; - if (attr.endsWith('%')) { - bool ok; - const int percent = attr.remove('%').toInt(&ok); - if (ok && percent >= 0) { - result = percent / 100.0; - } - } else { - result = KoUnit::parseValue(attr) / penWidth; - } - return result; -} - -QPen KoOdfGraphicStyles::loadOdfStrokeStyle(const KoStyleStack &styleStack, const QString & stroke, const KoOdfStylesReader & stylesReader) -{ - QPen tmpPen(Qt::NoPen); // default pen for "none" is a Qt::NoPen - - if (stroke == "solid" || stroke == "dash") { - // If solid or dash is set then we assume that the color is black and the penWidth - // is zero till defined otherwise with the following attributes. - tmpPen = QPen(); - - if (styleStack.hasProperty(KoXmlNS::svg, "stroke-color")) - tmpPen.setColor(styleStack.property(KoXmlNS::svg, "stroke-color")); - if (styleStack.hasProperty(KoXmlNS::svg, "stroke-opacity")) { - QColor color = tmpPen.color(); - QString opacity = styleStack.property(KoXmlNS::svg, "stroke-opacity"); - if (opacity.endsWith('%')) - color.setAlphaF(0.01 * opacity.remove('%').toDouble()); - else - color.setAlphaF(opacity.toDouble()); - tmpPen.setColor(color); - } - if (styleStack.hasProperty(KoXmlNS::svg, "stroke-width")) - tmpPen.setWidthF(KoUnit::parseValue(styleStack.property(KoXmlNS::svg, "stroke-width"))); - if (styleStack.hasProperty(KoXmlNS::draw, "stroke-linejoin")) { - QString join = styleStack.property(KoXmlNS::draw, "stroke-linejoin"); - if (join == "bevel") - tmpPen.setJoinStyle(Qt::BevelJoin); - else if (join == "round") - tmpPen.setJoinStyle(Qt::RoundJoin); - else { - tmpPen.setJoinStyle(Qt::MiterJoin); - if (styleStack.hasProperty(KoXmlNS::calligra, "stroke-miterlimit")) { - QString miterLimit = styleStack.property(KoXmlNS::calligra, "stroke-miterlimit"); - tmpPen.setMiterLimit(miterLimit.toDouble()); - } - } - } - if (styleStack.hasProperty(KoXmlNS::svg, "stroke-linecap")) { - const QString cap = styleStack.property(KoXmlNS::svg, "stroke-linecap"); - if (cap == "round") - tmpPen.setCapStyle(Qt::RoundCap); - else if (cap == "square") - tmpPen.setCapStyle(Qt::SquareCap); - else - tmpPen.setCapStyle(Qt::FlatCap); - } else { - // default as per svg specification - tmpPen.setCapStyle(Qt::FlatCap); - } - - if (stroke == "dash" && styleStack.hasProperty(KoXmlNS::draw, "stroke-dash")) { - QString dashStyleName = styleStack.property(KoXmlNS::draw, "stroke-dash"); - - // set width to 1 in case it is 0 as dividing by 0 gives infinity - qreal width = tmpPen.widthF(); - if ( width == 0 ) { - width = 1; - } - - KoXmlElement * dashElement = stylesReader.drawStyles("stroke-dash")[ dashStyleName ]; - if (dashElement) { - QVector dashes; - if (dashElement->hasAttributeNS(KoXmlNS::draw, "dots1")) { - QString distance( dashElement->attributeNS(KoXmlNS::draw, "distance", QString()) ); - qreal space = parseDashEntrySize(distance, width, 0.0); - - QString dots1Length(dashElement->attributeNS(KoXmlNS::draw, "dots1-length", QString())); - qreal dot1Length = parseDashEntrySize(dots1Length,width,1.0); - - bool ok; - int dots1 = dashElement->attributeNS(KoXmlNS::draw, "dots1").toInt(&ok); - if (!ok) { - dots1 = 1; - } - - for (int i = 0; i < dots1; i++) { - dashes.append(dot1Length); - dashes.append(space); - } - - if (dashElement->hasAttributeNS(KoXmlNS::draw, "dots2")) { - QString dots2Length(dashElement->attributeNS(KoXmlNS::draw, "dots2-length", QString())); - qreal dot2Length = parseDashEntrySize(dots2Length,width,1.0); - - int dots2 = dashElement->attributeNS(KoXmlNS::draw, "dots2").toInt(&ok); - if (!ok) { - dots2 = 1; - } - - for (int i = 0; i < dots2; i++) { - dashes.append(dot2Length); - dashes.append(space); - } - } - tmpPen.setDashPattern(dashes); - } - } - } - } - - return tmpPen; -} - -QTransform KoOdfGraphicStyles::loadTransformation(const QString &transformation) -{ - QTransform transform; - - // Split string for handling 1 transform statement at a time - QStringList subtransforms = transformation.split(')', QString::SkipEmptyParts); - QStringList::ConstIterator it = subtransforms.constBegin(); - QStringList::ConstIterator end = subtransforms.constEnd(); - for (; it != end; ++it) { - QStringList subtransform = (*it).split('(', QString::SkipEmptyParts); - - subtransform[0] = subtransform[0].trimmed().toLower(); - subtransform[1] = subtransform[1].simplified(); - QRegExp reg("[,( ]"); - QStringList params = subtransform[1].split(reg, QString::SkipEmptyParts); - - if (subtransform[0].startsWith(';') || subtransform[0].startsWith(',')) - subtransform[0] = subtransform[0].right(subtransform[0].length() - 1); - - if (subtransform[0] == "rotate") { - // TODO find out what oo2 really does when rotating, it seems severely broken - if (params.count() == 3) { - qreal x = KoUnit::parseValue(params[1]); - qreal y = KoUnit::parseValue(params[2]); - - transform.translate(x, y); - // oo2 rotates by radians - transform.rotate(params[0].toDouble()*180.0 / M_PI); - transform.translate(-x, -y); - } else { - // oo2 rotates by radians - transform.rotate(params[0].toDouble()*180.0 / M_PI); - } - } else if (subtransform[0] == "translate") { - if (params.count() == 2) { - qreal x = KoUnit::parseValue(params[0]); - qreal y = KoUnit::parseValue(params[1]); - transform.translate(x, y); - } else // Spec : if only one param given, assume 2nd param to be 0 - transform.translate(KoUnit::parseValue(params[0]) , 0); - } else if (subtransform[0] == "scale") { - if (params.count() == 2) - transform.scale(params[0].toDouble(), params[1].toDouble()); - else // Spec : if only one param given, assume uniform scaling - transform.scale(params[0].toDouble(), params[0].toDouble()); - } else if (subtransform[0] == "skewx") - transform.shear(tan(params[0].toDouble()), 0.0F); - else if (subtransform[0] == "skewy") - transform.shear(tan(params[0].toDouble()), 0.0F); - else if (subtransform[0] == "matrix") { - if (params.count() >= 6) { - transform.setMatrix(params[0].toDouble(), params[1].toDouble(), 0, - params[2].toDouble(), params[3].toDouble(), 0, - KoUnit::parseValue(params[4]), KoUnit::parseValue(params[5]), 1); - } - } - } - - return transform; -} - -QString KoOdfGraphicStyles::saveTransformation(const QTransform &transformation, bool appendTranslateUnit) -{ - QString transform; - if (appendTranslateUnit) - transform = QString("matrix(%1 %2 %3 %4 %5pt %6pt)") - .arg(transformation.m11()).arg(transformation.m12()) - .arg(transformation.m21()).arg(transformation.m22()) - .arg(transformation.dx()) .arg(transformation.dy()); - else - transform = QString("matrix(%1 %2 %3 %4 %5 %6)") - .arg(transformation.m11()).arg(transformation.m12()) - .arg(transformation.m21()).arg(transformation.m22()) - .arg(transformation.dx()) .arg(transformation.dy()); - - return transform; -} - diff --git a/libs/odf/KoOdfGraphicStyles.h b/libs/odf/KoOdfGraphicStyles.h deleted file mode 100644 index 5b6f8846cc..0000000000 --- a/libs/odf/KoOdfGraphicStyles.h +++ /dev/null @@ -1,68 +0,0 @@ -/* This file is part of the KDE project - Copyright (C) 2004-2006 David Faure - Copyright (C) 2007 Jan Hambrecht - Copyright (C) 2007 Thorsten Zachmann - - 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 KOODFGRAPHICSTYLES_H -#define KOODFGRAPHICSTYLES_H - -#include "kritaodf_export.h" - -#include - -class QBrush; -class QPen; -class QString; -class QSizeF; - -class KoGenStyle; -class KoGenStyles; -class KoStyleStack; - -class KoOdfStylesReader; - -namespace KoOdfGraphicStyles -{ - KRITAODF_EXPORT void saveOdfFillStyle(KoGenStyle &styleFill, KoGenStyles& mainStyles, const QBrush &brush); - - KRITAODF_EXPORT void saveOdfStrokeStyle(KoGenStyle &styleStroke, KoGenStyles &mainStyles, const QPen &pen); - - KRITAODF_EXPORT QString saveOdfHatchStyle(KoGenStyles &mainStyles, const QBrush &brush); - - /// Saves gradient style of brush into mainStyles and returns the styles name - KRITAODF_EXPORT QString saveOdfGradientStyle(KoGenStyles &mainStyles, const QBrush &brush); - - /// Loads gradient style from style stack and stylesReader adapted to the given size and returns a brush - KRITAODF_EXPORT QBrush loadOdfGradientStyle(const KoStyleStack &styleStack, const KoOdfStylesReader &stylesReader, const QSizeF &size); - - /// Loads gradient style with the given name from style stack and stylesReader adapted to the given size and returns a brush - KRITAODF_EXPORT QBrush loadOdfGradientStyleByName(const KoOdfStylesReader &stylesReader, const QString &styleName, const QSizeF &size); - - KRITAODF_EXPORT QBrush loadOdfFillStyle(const KoStyleStack &styleStack, const QString &fill, const KoOdfStylesReader &stylesReader); - - KRITAODF_EXPORT QPen loadOdfStrokeStyle(const KoStyleStack &styleStack, const QString &stroke, const KoOdfStylesReader &stylesReader); - - /// Helper function to parse a transformation attribute - KRITAODF_EXPORT QTransform loadTransformation(const QString &transformation); - - /// Helper function to create a transformation attribute - KRITAODF_EXPORT QString saveTransformation(const QTransform &transformation, bool appendTranslateUnit = true); -} - -#endif /* KOODFGRAPHICSTYLES_H */ diff --git a/libs/odf/KoShadowStyle.cpp b/libs/odf/KoShadowStyle.cpp deleted file mode 100644 index 12c07450b1..0000000000 --- a/libs/odf/KoShadowStyle.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* This file is part of the KDE project - * - * Copyright (C) 2011 Pierre Ducroquet - * - * 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 "KoShadowStyle.h" - -#include - - -// KoShadowStyle private class -class KoShadowStylePrivate: public QSharedData -{ -public: - KoShadowStylePrivate(); - ~KoShadowStylePrivate(); - - QVector shadows; -}; - -KoShadowStylePrivate::KoShadowStylePrivate() -{ -} - -KoShadowStylePrivate::~KoShadowStylePrivate() -{ -} - -// KoShadowStyle::ShadowData structure -KoShadowStyle::ShadowData::ShadowData() - : color(), offset(0, 0), radius(0.0) -{ -} - -bool KoShadowStyle::ShadowData::operator==(const KoShadowStyle::ShadowData &other) const -{ - return (color == other.color) && (offset == other.offset) && (radius == other.radius); -} - -// KoShadowStyle class -KoShadowStyle::KoShadowStyle() - : d(new KoShadowStylePrivate) -{ -} - -KoShadowStyle::KoShadowStyle(const KoShadowStyle &other) - : d(other.d) -{ -} - -KoShadowStyle::~KoShadowStyle() -{ -} - -bool KoShadowStyle::operator==(const KoShadowStyle &other) const -{ - if (d.data() == other.d.data()) - return true; - - if (shadowCount() != other.shadowCount()) - return false; - - foreach (const ShadowData &data, d->shadows) - { - if (!other.d->shadows.contains(data)) - return false; - } - return true; -} - -bool KoShadowStyle::operator!=(const KoShadowStyle &other) const -{ - return !operator==(other); -} - -int KoShadowStyle::shadowCount() const -{ - return d->shadows.size(); -} - diff --git a/libs/odf/KoShadowStyle.h b/libs/odf/KoShadowStyle.h deleted file mode 100644 index ac9a3a653a..0000000000 --- a/libs/odf/KoShadowStyle.h +++ /dev/null @@ -1,81 +0,0 @@ -/* This file is part of the KDE project - * - * Copyright (C) 2011 Pierre Ducroquet - * - * 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 KOSHADOWSTYLE_H -#define KOSHADOWSTYLE_H - -#include "kritaodf_export.h" - -#include -#include -#include -#include - -class KoShadowStylePrivate; - -/** - * A container and parser for shadows as defined in the - * OpenDocument specification. - * Applies to at least : - * - graphic elements, - * - headers-footers, - * - pages, - * - paragraphs, - * - tables and table cells. - */ -class KRITAODF_EXPORT KoShadowStyle -{ -public: - /// Default constructor, constructs an empty shadow - KoShadowStyle(); - /// Copy constructor - KoShadowStyle(const KoShadowStyle &other); - ~KoShadowStyle(); - - - // Holds data about one of the shadow this shadow contains - struct KRITAODF_EXPORT ShadowData { - ShadowData(); - bool operator==(const ShadowData &other) const; - QColor color; - QPointF offset; - qreal radius; - }; - - - bool operator==(const KoShadowStyle &other) const; - bool operator!=(const KoShadowStyle &other) const; - - /** - * Returns the number of shadows that are contained in this shadow - */ - int shadowCount() const; - - -private: - QSharedDataPointer d; -}; - -Q_DECLARE_TYPEINFO(KoShadowStyle::ShadowData, Q_MOVABLE_TYPE); - -Q_DECLARE_METATYPE(KoShadowStyle) - -#endif - diff --git a/libs/odf/KoStyleStack.cpp b/libs/odf/KoStyleStack.cpp deleted file mode 100644 index 7b09f5e5b1..0000000000 --- a/libs/odf/KoStyleStack.cpp +++ /dev/null @@ -1,279 +0,0 @@ -/* This file is part of the KDE project - Copyright (c) 2003 Lukas Tinkl - Copyright (c) 2003 David Faure - - 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 "KoStyleStack.h" -#include "KoUnit.h" -#include "KoXmlNS.h" -#include "KoXmlReader.h" - -#include - -//#define DEBUG_STYLESTACK - -class KoStyleStack::KoStyleStackPrivate -{ -}; - -KoStyleStack::KoStyleStack() - : m_styleNSURI(KoXmlNS::style), m_foNSURI(KoXmlNS::fo), d(0) -{ - clear(); -} - -KoStyleStack::KoStyleStack(const char* styleNSURI, const char* foNSURI) - : m_styleNSURI(styleNSURI), m_foNSURI(foNSURI), d(0) -{ - m_propertiesTagNames.append("properties"); - clear(); -} - -KoStyleStack::~KoStyleStack() -{ - delete d; -} - -void KoStyleStack::clear() -{ - m_stack.clear(); -#ifdef DEBUG_STYLESTACK - debugOdf << "clear!"; -#endif -} - -void KoStyleStack::save() -{ - m_marks.push(m_stack.count()); -#ifdef DEBUG_STYLESTACK - debugOdf << "save (level" << m_marks.count() << ") -> index" << m_stack.count(); -#endif -} - -void KoStyleStack::restore() -{ - Q_ASSERT(!m_marks.isEmpty()); - int toIndex = m_marks.pop(); -#ifdef DEBUG_STYLESTACK - debugOdf << "restore (level" << m_marks.count() + 1 << ") -> to index" << toIndex; -#endif - Q_ASSERT(toIndex > -1); - Q_ASSERT(toIndex <= (int)m_stack.count()); // If equal, nothing to remove. If greater, bug. - for (int index = (int)m_stack.count() - 1; index >= toIndex; --index) - m_stack.pop_back(); -} - -void KoStyleStack::pop() -{ - Q_ASSERT(!m_stack.isEmpty()); - m_stack.pop_back(); -#ifdef DEBUG_STYLESTACK - debugOdf << "pop -> count=" << m_stack.count(); -#endif -} - -void KoStyleStack::push(const KoXmlElement& style) -{ - m_stack.append(style); -#ifdef DEBUG_STYLESTACK - debugOdf << "pushed" << style.attributeNS(m_styleNSURI, "name", QString()) << " -> count=" << m_stack.count(); -#endif -} - -QString KoStyleStack::property(const QString &nsURI, const QString &name) const -{ - return property(nsURI, name, 0); -} -QString KoStyleStack::property(const QString &nsURI, const QString &name, const QString &detail) const -{ - return property(nsURI, name, &detail); -} - -inline QString KoStyleStack::property(const QString &nsURI, const QString &name, const QString *detail) const -{ - QString fullName(name); - if (detail) { - fullName += '-' + *detail; - } - QList::ConstIterator it = m_stack.end(); - while (it != m_stack.begin()) { - --it; - foreach (const QString &propertyTagName, m_propertiesTagNames) { - KoXmlElement properties = KoXml::namedItemNS(*it, m_styleNSURI, propertyTagName); - if (detail) { - QString attribute(properties.attributeNS(nsURI, fullName)); - if (!attribute.isEmpty()) { - return attribute; - } - } - QString attribute(properties.attributeNS(nsURI, name)); - if (!attribute.isEmpty()) { - return attribute; - } - } - } - return QString(); -} - -bool KoStyleStack::hasProperty(const QString &nsURI, const QString &name) const -{ - return hasProperty(nsURI, name, 0); -} - -bool KoStyleStack::hasProperty(const QString &nsURI, const QString &name, const QString &detail) const -{ - return hasProperty(nsURI, name, &detail); -} - -inline bool KoStyleStack::hasProperty(const QString &nsURI, const QString &name, const QString *detail) const -{ - QString fullName(name); - if (detail) { - fullName += '-' + *detail; - } - QList::ConstIterator it = m_stack.end(); - while (it != m_stack.begin()) { - --it; - foreach (const QString &propertiesTagName, m_propertiesTagNames) { - const KoXmlElement properties = KoXml::namedItemNS(*it, m_styleNSURI, propertiesTagName); - if (properties.hasAttributeNS(nsURI, name) || - (detail && properties.hasAttributeNS(nsURI, fullName))) - return true; - } - } - return false; -} - -// Font size is a bit special. "115%" applies to "the fontsize of the parent style". -// This can be generalized though (hasPropertyThatCanBePercentOfParent() ? :) -QPair KoStyleStack::fontSize(const qreal defaultFontPointSize) const -{ - const QString name = "font-size"; - qreal percent = 100; - QList::ConstIterator it = m_stack.end(); // reverse iterator - - while (it != m_stack.begin()) { - --it; - foreach (const QString &propertiesTagName, m_propertiesTagNames) { - KoXmlElement properties = KoXml::namedItemNS(*it, m_styleNSURI, propertiesTagName).toElement(); - if (properties.hasAttributeNS(m_foNSURI, name)) { - const QString value = properties.attributeNS(m_foNSURI, name, QString()); - if (value.endsWith('%')) { - //sebsauer, 20070609, the specs don't say that we have to calc them together but - //just that we are looking for a valid parent fontsize. So, let's only take the - //first percent definition into account and keep on to seek for a valid parent, - //percent *= value.left( value.length() - 1 ).toDouble() / 100.0; - if (percent == 100) - percent = value.left(value.length() - 1).toDouble(); - } else { - // e.g. 12pt and indicate that there was not percentage there - return QPair ((percent * KoUnit::parseValue(value))/100.0, 0.0); - } - break; - } - } - } - - //if there was no valid parent, we return the default fontsize together with an optional calculated percent-value. - return QPair ((percent * defaultFontPointSize)/100.0, percent); -} - -bool KoStyleStack::hasChildNode(const QString &nsURI, const QString &localName) const -{ - QList::ConstIterator it = m_stack.end(); - while (it != m_stack.begin()) { - --it; - foreach (const QString &propertiesTagName, m_propertiesTagNames) { - KoXmlElement properties = KoXml::namedItemNS(*it, m_styleNSURI, propertiesTagName); - if (!KoXml::namedItemNS(properties, nsURI, localName).isNull()) - return true; - } - } - - return false; -} - -KoXmlElement KoStyleStack::childNode(const QString &nsURI, const QString &localName) const -{ - QList::ConstIterator it = m_stack.end(); - - while (it != m_stack.begin()) { - --it; - foreach (const QString &propertiesTagName, m_propertiesTagNames) { - KoXmlElement properties = KoXml::namedItemNS(*it, m_styleNSURI, propertiesTagName); - KoXmlElement e = KoXml::namedItemNS(properties, nsURI, localName); - if (!e.isNull()) - return e; - } - } - - return KoXmlElement(); // a null element -} - -bool KoStyleStack::isUserStyle(const KoXmlElement& e, const QString& family) const -{ - if (e.attributeNS(m_styleNSURI, "family", QString()) != family) - return false; - const KoXmlElement parent = e.parentNode().toElement(); - //debugOdf <<"tagName=" << e.tagName() <<" parent-tagName=" << parent.tagName(); - return parent.localName() == "styles" /*&& parent.namespaceURI() == KoXmlNS::office*/; -} - -QString KoStyleStack::userStyleName(const QString& family) const -{ - QList::ConstIterator it = m_stack.end(); - while (it != m_stack.begin()) { - --it; - //debugOdf << (*it).attributeNS( m_styleNSURI,"name", QString()); - if (isUserStyle(*it, family)) - return (*it).attributeNS(m_styleNSURI, "name", QString()); - } - // Can this ever happen? - return "Standard"; -} - -QString KoStyleStack::userStyleDisplayName(const QString& family) const -{ - QList::ConstIterator it = m_stack.end(); - while (it != m_stack.begin()) { - --it; - //debugOdf << (*it).attributeNS( m_styleNSURI,"display-name"); - if (isUserStyle(*it, family)) - return (*it).attributeNS(m_styleNSURI, "display-name", QString()); - } - return QString(); // no display name, this can happen since it's optional -} - -void KoStyleStack::setTypeProperties(const char* typeProperties) -{ - m_propertiesTagNames.clear(); - m_propertiesTagNames.append(typeProperties == 0 || qstrlen(typeProperties) == 0 ? QString("properties") : (QString(typeProperties) + "-properties")); -} - -void KoStyleStack::setTypeProperties(const QList &typeProperties) -{ - m_propertiesTagNames.clear(); - foreach (const QString &typeProperty, typeProperties) { - if (!typeProperty.isEmpty()) { - m_propertiesTagNames.append(typeProperty + "-properties"); - } - } - if (m_propertiesTagNames.empty()) { - m_propertiesTagNames.append("properties"); - } -} diff --git a/libs/odf/KoStyleStack.h b/libs/odf/KoStyleStack.h deleted file mode 100644 index 73ed77fece..0000000000 --- a/libs/odf/KoStyleStack.h +++ /dev/null @@ -1,196 +0,0 @@ -/* This file is part of the KDE project - Copyright (c) 2003 Lukas Tinkl - Copyright (c) 2003 David Faure - - 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 KOSTYLESTACK_H -#define KOSTYLESTACK_H - - -#include -#include -#include - -#include "kritaodf_export.h" -#include - -/** - * @brief This class implements a stack for the different styles of an object. - * - * There can be several styles that are valid for one object. For example - * a textobject on a page has styles 'pr3' and 'P7' and a paragraph in - * that textobject has styles 'P1' and 'T3'. And some styles even have - * parent-styles... - * - * If you want to know if there is, for example, the attribute 'fo:font-family' - * for this paragraph, you have to look into style 'T3', 'P1', 'P7' and 'pr3'. - * When you find this attribute in one style you have to stop processing the list - * and take the found attribute for this object. - * - * This is what this class does. You can push styles on the stack while walking - * through the xml-tree to your object and then ask the stack if any of the styles - * provides a certain attribute. The stack will search from top to bottom, i.e. - * in our example from 'T3' to 'pr3' and return the first occurrence of the wanted - * attribute. - * - * So this is some sort of inheritance where the styles on top of the stack overwrite - * the same attribute of a lower style on the stack. - * - */ -class KRITAODF_EXPORT KoStyleStack -{ -public: - /** - * Create a OASIS style stack - */ - KoStyleStack(); - /** - * Create a style stack based on other namespaces than OASIS - used for OOo-1.1 import. - */ - explicit KoStyleStack(const char* styleNSURI, const char* foNSURI); - virtual ~KoStyleStack(); - - /** - * Clears the complete stack. - */ - void clear(); - - /** - * Save the current state of the stack. Any items added between - * this call and its corresponding restore() will be removed when calling restore(). - */ - void save(); - - /** - * Restore the stack to the state it was at the corresponding save() call. - */ - void restore(); - - /** - * Removes the style on top of the stack. - */ - void pop(); - - /** - * Pushes the new style onto the stack. - */ - void push(const KoXmlElement& style); - - /** - * Check if any of the styles on the stack has an attribute called 'localName' - */ - bool hasProperty(const QString &nsURI, const QString &localName) const; - - /** - * Check if any of the styles on the stack has an attribute called 'localname'-'detail' - * where detail is e.g. left, right, top or bottom. - * This allows to also find 'name' alone (e.g. padding implies padding-left, padding-right etc.) - */ - bool hasProperty(const QString &nsURI, const QString &localName, const QString &detail) const; - - /** - * Search for the attribute called 'localName', starting on top of the stack, - * and return it. - */ - QString property(const QString &nsURI, const QString &localName) const; - - /** - * Search for the attribute called 'localName'-'detail', starting on top of the stack, - * and return it, where detail is e.g. left, right, top or bottom. - * This allows to also find 'name' alone (e.g. padding implies padding-left, padding-right etc.) - */ - QString property(const QString &nsURI, const QString &localName, const QString &detail) const; - - /** - * Check if any of the styles on the stack has a child element called 'localName' in the namespace 'nsURI'. - */ - bool hasChildNode(const QString &nsURI, const QString &localName) const; - - /** - * Search for a child element which has a child element called 'localName' - * in the namespace 'nsURI' starting on top of the stack, - * and return it. - */ - KoXmlElement childNode(const QString &nsURI, const QString &localName) const; - - /** - * Special case for the current font size, due to special handling of fo:font-size="115%". - * First item in the returned value contains the font point size and the second the got percent. - */ - QPair fontSize(const qreal defaultFontPointSize = 12.0) const; - - /** - * Return the name of the style specified by the user, - * i.e. not an auto style. - * This is used to know e.g. which user-style is associated with the current paragraph. - * There could be none though. - */ - QString userStyleName(const QString& family) const; - - /** - * Return the display name of the style specified by the user, - * i.e. not an auto style - */ - QString userStyleDisplayName(const QString& family) const; - - /** - * Set the type of properties that will be looked for. - * For instance setTypeProperties("paragraph") will make hasAttribute() and attribute() - * look into "paragraph-properties". - * If @p typeProperties is 0, the stylestack is reset to look for "properties" - * as it does by default. - */ - void setTypeProperties(const char* typeProperties); - - /** - * Overloaded method to also set backup properties to search in - * - * If the list is graphic, paragraph it will search first in graphic-properties and then in paragraph-properties - */ - void setTypeProperties(const QList &typeProperties); - -private: - bool isUserStyle(const KoXmlElement& e, const QString& family) const; - - inline bool hasProperty(const QString &nsURI, const QString &localName, const QString *detail) const; - - inline QString property(const QString &nsURI, const QString &localName, const QString *detail) const; - - /// For save/restore: stack of "marks". Each mark is an index in m_stack. - QStack m_marks; - - /** - * We use QValueList instead of QValueStack because we need access to all styles - * not only the top one. - */ - QList m_stack; - - QList m_propertiesTagNames; - - QString m_styleNSURI; - QString m_foNSURI; - - class KoStyleStackPrivate; - KoStyleStackPrivate * const d; - - // forbidden - void operator=(const KoStyleStack&); - KoStyleStack(const KoStyleStack&); -}; - -#endif /* KOSTYLESTACK_H */