libs/flake

KoPathShape.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002    Copyright (C) 2006-2008 Thorsten Zachmann <zachmann@kde.org>
00003    Copyright (C) 2006-2008 Jan Hambrecht <jaham@gmx.net>
00004    Copyright (C) 2007-2009 Thomas Zander <zander@kde.org>
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License as published by the Free Software Foundation; either
00009    version 2 of the License, or (at your option) any later version.
00010 
00011    This library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Library General Public License for more details.
00015 
00016    You should have received a copy of the GNU Library General Public License
00017    along with this library; see the file COPYING.LIB.  If not, write to
00018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019  * Boston, MA 02110-1301, USA.
00020 */
00021 
00022 #include "KoPathShape.h"
00023 #include "KoPathShape_p.h"
00024 #include "KoPathPoint.h"
00025 #include "KoPointGroup.h"
00026 #include "KoShapeBorderModel.h"
00027 #include "KoViewConverter.h"
00028 #include "KoPathShapeLoader.h"
00029 #include "KoShapeSavingContext.h"
00030 #include "KoShapeLoadingContext.h"
00031 #include "KoShapeShadow.h"
00032 #include "KoShapeBackground.h"
00033 #include "KoShapeContainer.h"
00034 #include "KoFilterEffectStack.h"
00035 
00036 #include <KoXmlReader.h>
00037 #include <KoXmlWriter.h>
00038 #include <KoXmlNS.h>
00039 #include <KoUnit.h>
00040 #include <KoGenStyle.h>
00041 #include <KoStyleStack.h>
00042 #include <KoOdfLoadingContext.h>
00043 
00044 #include <KDebug>
00045 #include <QtGui/QPainter>
00046 
00047 KoPathShapePrivate::KoPathShapePrivate(KoPathShape *q)
00048     : KoShapePrivate(q),
00049     fillRule(Qt::OddEvenFill)
00050 {
00051 }
00052 
00053 KoPathShape::KoPathShape()
00054     :KoShape(*(new KoPathShapePrivate(this)))
00055 {
00056 }
00057 
00058 KoPathShape::KoPathShape(KoPathShapePrivate &dd)
00059     : KoShape(dd)
00060 {
00061 }
00062 
00063 KoPathShape::~KoPathShape()
00064 {
00065     clear();
00066 }
00067 
00068 void KoPathShape::saveOdf(KoShapeSavingContext & context) const
00069 {
00070     context.xmlWriter().startElement("draw:path");
00071     saveOdfAttributes(context, OdfAllAttributes | OdfViewbox);
00072 
00073     context.xmlWriter().addAttribute("svg:d", toString(QMatrix()));
00074     context.xmlWriter().addAttribute("koffice:nodeTypes", nodeTypes());
00075 
00076     saveOdfCommonChildElements(context);
00077     context.xmlWriter().endElement();
00078 }
00079 
00080 bool KoPathShape::loadOdf(const KoXmlElement & element, KoShapeLoadingContext &context)
00081 {
00082     loadOdfAttributes(element, context, OdfMandatories | OdfAdditionalAttributes | OdfCommonChildElements);
00083 
00084     // first clear the path data from the default path
00085     clear();
00086 
00087     if (element.localName() == "line") {
00088         QPointF start;
00089         start.setX(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "x1", "")));
00090         start.setY(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "y1", "")));
00091         QPointF end;
00092         end.setX(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "x2", "")));
00093         end.setY(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "y2", "")));
00094         moveTo(start);
00095         lineTo(end);
00096     } else if (element.localName() == "polyline" || element.localName() == "polygon") {
00097         QString points = element.attributeNS(KoXmlNS::draw, "points").simplified();
00098         points.replace(',', ' ');
00099         points.remove('\r');
00100         points.remove('\n');
00101         bool firstPoint = true;
00102         const QStringList coordinateList = points.split(' ');
00103         for (QStringList::ConstIterator it = coordinateList.constBegin(); it != coordinateList.constEnd(); ++it) {
00104             QPointF point;
00105             point.setX((*it).toDouble());
00106             ++it;
00107             point.setY((*it).toDouble());
00108             if (firstPoint) {
00109                 moveTo(point);
00110                 firstPoint = false;
00111             } else
00112                 lineTo(point);
00113         }
00114         if (element.localName() == "polygon")
00115             close();
00116     } else { // path loading
00117         KoPathShapeLoader loader(this);
00118         loader.parseSvg(element.attributeNS(KoXmlNS::svg, "d"), true);
00119         loadNodeTypes(element);
00120     }
00121 
00122     applyViewboxTransformation(element);
00123     QPointF pos = normalize();
00124     setTransformation(QMatrix());
00125 
00126     if (element.hasAttributeNS(KoXmlNS::svg, "x") || element.hasAttributeNS(KoXmlNS::svg, "y")) {
00127         pos.setX(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "x", QString())));
00128         pos.setY(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "y", QString())));
00129     }
00130 
00131     setPosition(pos);
00132 
00133     loadOdfAttributes(element, context, OdfTransformation);
00134 
00135     return true;
00136 }
00137 
00138 QString KoPathShape::saveStyle(KoGenStyle &style, KoShapeSavingContext &context) const
00139 {
00140     Q_D(const KoPathShape);
00141     style.addProperty("svg:fill-rule", d->fillRule == Qt::OddEvenFill ? "evenodd" : "nonzero");
00142 
00143     return KoShape::saveStyle(style, context);
00144 }
00145 
00146 void KoPathShape::loadStyle(const KoXmlElement & element, KoShapeLoadingContext &context)
00147 {
00148     Q_D(KoPathShape);
00149     KoShape::loadStyle(element, context);
00150 
00151     KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
00152     styleStack.save();
00153 
00154     // fill the style stack with the shapes style
00155     if (element.hasAttributeNS(KoXmlNS::draw, "style-name")) {
00156         context.odfLoadingContext().fillStyleStack(element, KoXmlNS::draw, "style-name", "graphic");
00157     } else if (element.hasAttributeNS(KoXmlNS::presentation, "style-name")) {
00158         context.odfLoadingContext().fillStyleStack(element, KoXmlNS::presentation, "style-name", "presentation");
00159     }
00160     styleStack.setTypeProperties("graphic");
00161 
00162     if (styleStack.hasProperty(KoXmlNS::svg, "fill-rule")) {
00163         QString rule = styleStack.property(KoXmlNS::svg, "fill-rule");
00164         d->fillRule = rule == "nonzero" ?  Qt::WindingFill : Qt::OddEvenFill;
00165     }
00166     styleStack.restore();
00167 }
00168 
00169 QRectF KoPathShape::loadOdfViewbox(const KoXmlElement & element) const
00170 {
00171     QRectF viewbox;
00172 
00173     QString data = element.attributeNS(KoXmlNS::svg, "viewBox");
00174     if (! data.isEmpty()) {
00175         data.replace(',', ' ');
00176         QStringList coordinates = data.simplified().split(' ', QString::SkipEmptyParts);
00177         if (coordinates.count() == 4) {
00178             viewbox.setRect(coordinates[0].toDouble(), coordinates[1].toDouble(),
00179                             coordinates[2].toDouble(), coordinates[3].toDouble());
00180         }
00181     }
00182 
00183     return viewbox;
00184 }
00185 
00186 void KoPathShape::applyViewboxTransformation(const KoXmlElement & element)
00187 {
00188     // apply viewbox transformation
00189     QRectF viewBox = loadOdfViewbox(element);
00190     if (! viewBox.isEmpty()) {
00191         // load the desired size
00192         QSizeF size;
00193         size.setWidth(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "width", QString())));
00194         size.setHeight(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "height", QString())));
00195 
00196         // load the desired position
00197         QPointF pos;
00198         pos.setX(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "x", QString())));
00199         pos.setY(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "y", QString())));
00200 
00201         // create matrix to transform original path data into desired size and position
00202         QMatrix viewMatrix;
00203         viewMatrix.translate(-viewBox.left(), -viewBox.top());
00204         viewMatrix.scale(size.width() / viewBox.width(), size.height() / viewBox.height());
00205         viewMatrix.translate(pos.x(), pos.y());
00206 
00207         // transform the path data
00208         map(viewMatrix);
00209     }
00210 }
00211 
00212 void KoPathShape::clear()
00213 {
00214     foreach(KoSubpath *subpath, m_subpaths) {
00215         foreach(KoPathPoint *point, *subpath)
00216             delete point;
00217         delete subpath;
00218     }
00219     m_subpaths.clear();
00220 }
00221 
00222 void KoPathShape::paint(QPainter &painter, const KoViewConverter &converter)
00223 {
00224     Q_D(KoPathShape);
00225     applyConversion(painter, converter);
00226     QPainterPath path(outline());
00227     path.setFillRule(d->fillRule);
00228 
00229     if (background())
00230         background()->paint(painter, path);
00231     //paintDebug( painter );
00232 }
00233 
00234 #ifndef NDEBUG
00235 void KoPathShape::paintDebug(QPainter &painter)
00236 {
00237     KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
00238     int i = 0;
00239 
00240     QPen pen(Qt::black);
00241     painter.save();
00242     painter.setPen(pen);
00243     for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
00244         KoSubpath::const_iterator it((*pathIt)->constBegin());
00245         for (; it != (*pathIt)->constEnd(); ++it) {
00246             ++i;
00247             KoPathPoint *point = (*it);
00248             QRectF r(point->point(), QSizeF(5, 5));
00249             r.translate(-2.5, -2.5);
00250             QPen pen(Qt::black);
00251             painter.setPen(pen);
00252             if (point->group()) {
00253                 QBrush b(Qt::blue);
00254                 painter.setBrush(b);
00255             } else if (point->activeControlPoint1() && point->activeControlPoint2()) {
00256                 QBrush b(Qt::red);
00257                 painter.setBrush(b);
00258             } else if (point->activeControlPoint1()) {
00259                 QBrush b(Qt::yellow);
00260                 painter.setBrush(b);
00261             } else if (point->activeControlPoint2()) {
00262                 QBrush b(Qt::darkYellow);
00263                 painter.setBrush(b);
00264             }
00265             painter.drawEllipse(r);
00266         }
00267     }
00268     painter.restore();
00269     kDebug(30006) << "nop =" << i;
00270 }
00271 
00272 void KoPathShape::debugPath()
00273 {
00274     KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
00275     for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
00276         KoSubpath::const_iterator it((*pathIt)->constBegin());
00277         for (; it != (*pathIt)->constEnd(); ++it) {
00278             kDebug(30006) << "p:" << (*pathIt) << "," << *it << "," << (*it)->point() << "," << (*it)->properties() << "," << (*it)->group();
00279         }
00280     }
00281 }
00282 #endif
00283 
00284 void KoPathShape::paintPoints(QPainter &painter, const KoViewConverter &converter, int handleRadius)
00285 {
00286     applyConversion(painter, converter);
00287 
00288     KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
00289 
00290     QRectF handle = converter.viewToDocument(handleRect(QPoint(0, 0), handleRadius));
00291 
00292     for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
00293         KoSubpath::const_iterator it((*pathIt)->constBegin());
00294         for (; it != (*pathIt)->constEnd(); ++it)
00295             (*it)->paint(painter, handleRadius, KoPathPoint::Node);
00296     }
00297 }
00298 
00299 QRectF KoPathShape::handleRect(const QPointF &p, qreal radius) const
00300 {
00301     return QRectF(p.x() - radius, p.y() - radius, 2*radius, 2*radius);
00302 }
00303 
00304 QPainterPath KoPathShape::outline() const
00305 {
00306     QPainterPath path;
00307     foreach(KoSubpath * subpath, m_subpaths) {
00308         KoPathPoint * lastPoint = subpath->first();
00309         bool activeCP = false;
00310         foreach(KoPathPoint * currPoint, *subpath) {
00311             KoPathPoint::KoPointProperties currProperties = currPoint->properties();
00312             if (currPoint == subpath->first()) {
00313                 if (currProperties & KoPathPoint::StartSubpath) {
00314                     path.moveTo(currPoint->point());
00315                 }
00316             } else if (activeCP && currPoint->activeControlPoint1()) {
00317                 path.cubicTo(
00318                     lastPoint->controlPoint2(),
00319                     currPoint->controlPoint1(),
00320                     currPoint->point());
00321             } else if( activeCP || currPoint->activeControlPoint1()) {
00322                 path.quadTo(
00323                     activeCP ? lastPoint->controlPoint2() : currPoint->controlPoint1(),
00324                     currPoint->point());
00325             } else {
00326                 path.lineTo(currPoint->point());
00327             }
00328             if (currProperties & KoPathPoint::CloseSubpath && currProperties & KoPathPoint::StopSubpath) {
00329                 // add curve when there is a curve on the way to the first point
00330                 KoPathPoint * firstPoint = subpath->first();
00331                 if (currPoint->activeControlPoint2() && firstPoint->activeControlPoint1()) {
00332                     path.cubicTo(
00333                         currPoint->controlPoint2(),
00334                         firstPoint->controlPoint1(),
00335                         firstPoint->point());
00336                 }
00337                 else if(currPoint->activeControlPoint2() || firstPoint->activeControlPoint1()) {
00338                     path.quadTo(
00339                         currPoint->activeControlPoint2() ? currPoint->controlPoint2() : firstPoint->controlPoint1(),
00340                         firstPoint->point());
00341                 }
00342                 path.closeSubpath();
00343             }
00344 
00345             if (currPoint->activeControlPoint2()) {
00346                 activeCP = true;
00347             } else {
00348                 activeCP = false;
00349             }
00350             lastPoint = currPoint;
00351         }
00352     }
00353     return path;
00354 }
00355 
00356 QRectF KoPathShape::boundingRect() const
00357 {
00358     QMatrix transform = absoluteTransformation(0);
00359     // calculate the bounding rect of the transformed outline
00360     QRectF bb(transform.map(outline()).boundingRect());
00361     if (border()) {
00362         KoInsets inset;
00363         border()->borderInsets(this, inset);
00364 
00365         // calculate transformed border insets
00366         QPointF center = transform.map(QPointF());
00367         QPointF tl = transform.map(QPointF(-inset.left,-inset.top)) - center;
00368         QPointF br = transform.map(QPointF(inset.right,inset.bottom)) -center;
00369         qreal left = qMin(tl.x(),br.x());
00370         qreal right = qMax(tl.x(),br.x());
00371         qreal top = qMin(tl.y(),br.y());
00372         qreal bottom = qMax(tl.y(),br.y());
00373         bb.adjust(left, top, right, bottom);
00374     }
00375     if (shadow()) {
00376         KoInsets insets;
00377         shadow()->insets(this, insets);
00378         bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
00379     }
00380     if (filterEffectStack()) {
00381         QRectF clipRect = filterEffectStack()->clipRectForBoundingRect(QRectF(QPointF(), size()));
00382         bb |= transform.mapRect(clipRect);
00383     }
00384     return bb;
00385 }
00386 
00387 QSizeF KoPathShape::size() const
00388 {
00389     // don't call boundingRect here as it uses absoluteTransformation
00390     // which itself uses size() -> leads to infinite reccursion
00391     return outline().boundingRect().size();
00392 }
00393 
00394 void KoPathShape::setSize(const QSizeF &newSize)
00395 {
00396     QMatrix matrix(resizeMatrix(newSize));
00397 
00398     KoShape::setSize(newSize);
00399     map(matrix);
00400 }
00401 
00402 QMatrix KoPathShape::resizeMatrix( const QSizeF & newSize ) const
00403 {
00404     QSizeF oldSize = size();
00405     if (oldSize.width() == 0.0) {
00406         oldSize.setWidth(0.000001);
00407     }
00408     if (oldSize.height() == 0.0) {
00409         oldSize.setHeight(0.000001);
00410     }
00411 
00412     QSizeF sizeNew(newSize);
00413     if (sizeNew.width() == 0.0) {
00414         sizeNew.setWidth(0.000001);
00415     }
00416     if (sizeNew.height() == 0.0) {
00417         sizeNew.setHeight(0.000001);
00418     }
00419 
00420     return QMatrix(sizeNew.width() / oldSize.width(), 0, 0, sizeNew.height() / oldSize.height(), 0, 0);
00421 }
00422 
00423 KoPathPoint * KoPathShape::moveTo(const QPointF &p)
00424 {
00425     KoPathPoint * point = new KoPathPoint(this, p, KoPathPoint::StartSubpath | KoPathPoint::StopSubpath);
00426     KoSubpath * path = new KoSubpath;
00427     path->push_back(point);
00428     m_subpaths.push_back(path);
00429     return point;
00430 }
00431 
00432 KoPathPoint * KoPathShape::lineTo(const QPointF &p)
00433 {
00434     if (m_subpaths.empty()) {
00435         moveTo(QPointF(0, 0));
00436     }
00437     KoPathPoint * point = new KoPathPoint(this, p, KoPathPoint::StopSubpath);
00438     KoPathPoint * lastPoint = m_subpaths.last()->last();
00439     updateLast(&lastPoint);
00440     m_subpaths.last()->push_back(point);
00441     return point;
00442 }
00443 
00444 KoPathPoint * KoPathShape::curveTo(const QPointF &c1, const QPointF &c2, const QPointF &p)
00445 {
00446     if (m_subpaths.empty()) {
00447         moveTo(QPointF(0, 0));
00448     }
00449     KoPathPoint * lastPoint = m_subpaths.last()->last();
00450     updateLast(&lastPoint);
00451     lastPoint->setControlPoint2(c1);
00452     KoPathPoint * point = new KoPathPoint(this, p, KoPathPoint::StopSubpath);
00453     point->setControlPoint1(c2);
00454     m_subpaths.last()->push_back(point);
00455     return point;
00456 }
00457 
00458 KoPathPoint * KoPathShape::curveTo(const QPointF &c, const QPointF &p)
00459 {
00460     if (m_subpaths.empty())
00461         moveTo(QPointF(0, 0));
00462 
00463     KoPathPoint * lastPoint = m_subpaths.last()->last();
00464     updateLast(&lastPoint);
00465     lastPoint->setControlPoint2(c);
00466     KoPathPoint * point = new KoPathPoint(this, p, KoPathPoint::StopSubpath);
00467     m_subpaths.last()->push_back(point);
00468 
00469     return point;
00470 }
00471 
00472 KoPathPoint * KoPathShape::arcTo(qreal rx, qreal ry, qreal startAngle, qreal sweepAngle)
00473 {
00474     if (m_subpaths.empty()) {
00475         moveTo(QPointF(0, 0));
00476     }
00477 
00478     KoPathPoint * lastPoint = m_subpaths.last()->last();
00479     if (lastPoint->properties() & KoPathPoint::CloseSubpath) {
00480         lastPoint = m_subpaths.last()->first();
00481     }
00482     QPointF startpoint(lastPoint->point());
00483 
00484     KoPathPoint * newEndPoint = lastPoint;
00485 
00486     QPointF curvePoints[12];
00487     int pointCnt = arcToCurve(rx, ry, startAngle, sweepAngle, startpoint, curvePoints);
00488     for (int i = 0; i < pointCnt; i += 3) {
00489         newEndPoint = curveTo(curvePoints[i], curvePoints[i+1], curvePoints[i+2]);
00490     }
00491     return newEndPoint;
00492 }
00493 
00494 int KoPathShape::arcToCurve(qreal rx, qreal ry, qreal startAngle, qreal sweepAngle, const QPointF & offset, QPointF * curvePoints) const
00495 {
00496     int pointCnt = 0;
00497 
00498     // check Parameters
00499     if (sweepAngle == 0)
00500         return pointCnt;
00501     if (sweepAngle > 360)
00502         sweepAngle = 360;
00503     else if (sweepAngle < -360)
00504         sweepAngle = - 360;
00505 
00506     if (rx == 0 || ry == 0) {
00507         //TODO
00508     }
00509 
00510     // split angles bigger than 90° so that it gives a good aproximation to the circle
00511     qreal parts = ceil(qAbs(sweepAngle / 90.0));
00512 
00513     qreal sa_rad = startAngle * M_PI / 180.0;
00514     qreal partangle = sweepAngle / parts;
00515     qreal endangle = startAngle + partangle;
00516     qreal se_rad = endangle * M_PI / 180.0;
00517     qreal sinsa = sin(sa_rad);
00518     qreal cossa = cos(sa_rad);
00519     qreal kappa = 4.0 / 3.0 * tan((se_rad - sa_rad) / 4);
00520 
00521     // startpoint is at the last point is the path but when it is closed
00522     // it is at the first point
00523     QPointF startpoint(offset);
00524 
00525     //center berechnen
00526     QPointF center(startpoint - QPointF(cossa * rx, -sinsa * ry));
00527 
00528     //kDebug(30006) <<"kappa" << kappa <<"parts" << parts;;
00529 
00530     for (int part = 0; part < parts; ++part) {
00531         // start tangent
00532         curvePoints[pointCnt++] = QPointF(startpoint - QPointF(sinsa * rx * kappa, cossa * ry * kappa));
00533 
00534         qreal sinse = sin(se_rad);
00535         qreal cosse = cos(se_rad);
00536 
00537         // end point
00538         QPointF endpoint(center + QPointF(cosse * rx, -sinse * ry));
00539         // end tangent
00540         curvePoints[pointCnt++] = QPointF(endpoint - QPointF(-sinse * rx * kappa, -cosse * ry * kappa));
00541         curvePoints[pointCnt++] = endpoint;
00542 
00543         // set the endpoint as next start point
00544         startpoint = endpoint;
00545         sinsa = sinse;
00546         cossa = cosse;
00547         endangle += partangle;
00548         se_rad = endangle * M_PI / 180.0;
00549     }
00550 
00551     return pointCnt;
00552 }
00553 
00554 void KoPathShape::close()
00555 {
00556     if (m_subpaths.empty()) {
00557         return;
00558     }
00559     closeSubpath(m_subpaths.last());
00560 }
00561 
00562 void KoPathShape::closeMerge()
00563 {
00564     if (m_subpaths.empty()) {
00565         return;
00566     }
00567     closeMergeSubpath(m_subpaths.last());
00568 }
00569 
00570 QPointF KoPathShape::normalize()
00571 {
00572     QPointF tl(outline().boundingRect().topLeft());
00573     QMatrix matrix;
00574     matrix.translate(-tl.x(), -tl.y());
00575     map(matrix);
00576 
00577     // keep the top left point of the object
00578     applyTransformation(matrix.inverted());
00579 
00580     return tl;
00581 }
00582 
00583 void KoPathShape::map(const QMatrix &matrix)
00584 {
00585     KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
00586     for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
00587         KoSubpath::const_iterator it((*pathIt)->constBegin());
00588         for (; it != (*pathIt)->constEnd(); ++it) {
00589             (*it)->map(matrix);
00590         }
00591     }
00592 }
00593 
00594 void KoPathShape::updateLast(KoPathPoint ** lastPoint)
00595 {
00596     // check if we are about to add a new point to a closed subpath
00597     if ((*lastPoint)->properties() & KoPathPoint::StopSubpath
00598             && (*lastPoint)->properties() & KoPathPoint::CloseSubpath) {
00599         // get the first point of the subpath
00600         KoPathPoint * subpathStart = m_subpaths.last()->first();
00601         // clone the first point of the subpath...
00602         KoPathPoint * newLastPoint = new KoPathPoint(*subpathStart);
00603         // ... and make it a normal point
00604         newLastPoint->setProperties(KoPathPoint::Normal);
00605         // make a point group of the first point and its clone
00606         KoPointGroup * group = subpathStart->group();
00607         if (group == 0) {
00608             group = new KoPointGroup();
00609             group->add(subpathStart);
00610         }
00611         group->add(newLastPoint);
00612 
00613         // now start a new subpath with the cloned start point
00614         KoSubpath *path = new KoSubpath;
00615         path->push_back(newLastPoint);
00616         m_subpaths.push_back(path);
00617         *lastPoint = newLastPoint;
00618     } else {
00619         // the subpath was not closed so the formerly last point
00620         // of the subpath is no end point anymore
00621         (*lastPoint)->unsetProperty(KoPathPoint::StopSubpath);
00622     }
00623     (*lastPoint)->unsetProperty(KoPathPoint::CloseSubpath);
00624 }
00625 
00626 QList<KoPathPoint*> KoPathShape::pointsAt(const QRectF &r)
00627 {
00628     QList<KoPathPoint*> result;
00629 
00630     KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
00631     for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
00632         KoSubpath::const_iterator it((*pathIt)->constBegin());
00633         for (; it != (*pathIt)->constEnd(); ++it) {
00634             if (r.contains((*it)->point()))
00635                 result.append(*it);
00636             else if ((*it)->activeControlPoint1() && r.contains((*it)->controlPoint1()))
00637                 result.append(*it);
00638             else if ((*it)->activeControlPoint2() && r.contains((*it)->controlPoint2()))
00639                 result.append(*it);
00640         }
00641     }
00642     return result;
00643 }
00644 
00645 QList<KoPathSegment> KoPathShape::segmentsAt(const QRectF &r)
00646 {
00647     QList<KoPathSegment> segments;
00648     int subpathCount = m_subpaths.count();
00649     for (int subpathIndex = 0; subpathIndex < subpathCount; ++subpathIndex) {
00650         KoSubpath * subpath = m_subpaths[subpathIndex];
00651         int pointCount = subpath->count();
00652         bool subpathClosed = isClosedSubpath(subpathIndex);
00653         for (int pointIndex = 0; pointIndex < pointCount; ++pointIndex) {
00654             if (pointIndex == (pointCount - 1) && ! subpathClosed)
00655                 break;
00656             KoPathSegment s(subpath->at(pointIndex), subpath->at((pointIndex + 1) % pointCount));
00657             QRectF controlRect = s.controlPointRect();
00658             if (! r.intersects(controlRect) && ! controlRect.contains(r))
00659                 continue;
00660             QRectF bound = s.boundingRect();
00661             if (! r.intersects(bound) && ! bound.contains(r))
00662                 continue;
00663 
00664             segments.append(s);
00665         }
00666     }
00667     return segments;
00668 }
00669 
00670 KoPathPointIndex KoPathShape::pathPointIndex(const KoPathPoint *point) const
00671 {
00672     for (int subpathIndex = 0; subpathIndex < m_subpaths.size(); ++subpathIndex) {
00673         KoSubpath * subpath = m_subpaths.at(subpathIndex);
00674         for (int pointPos = 0; pointPos < subpath->size(); ++pointPos) {
00675             if (subpath->at(pointPos) == point) {
00676                 return KoPathPointIndex(subpathIndex, pointPos);
00677             }
00678         }
00679     }
00680     return KoPathPointIndex(-1, -1);
00681 }
00682 
00683 KoPathPoint * KoPathShape::pointByIndex(const KoPathPointIndex &pointIndex) const
00684 {
00685     KoSubpath * subpath = subPath(pointIndex.first);
00686 
00687     if (subpath == 0 || pointIndex.second < 0 || pointIndex.second >= subpath->size())
00688         return 0;
00689 
00690     return subpath->at(pointIndex.second);
00691 }
00692 
00693 KoPathSegment KoPathShape::segmentByIndex(const KoPathPointIndex &pointIndex) const
00694 {
00695     KoPathSegment segment(0, 0);
00696 
00697     KoSubpath * subpath = subPath(pointIndex.first);
00698 
00699     if (subpath != 0 && pointIndex.second >= 0 && pointIndex.second < subpath->size()) {
00700         KoPathPoint * point = subpath->at(pointIndex.second);
00701         int index = pointIndex.second;
00702         // check if we have a (closing) segment starting from the last point
00703         if ((index == subpath->size() - 1) && point->properties() & KoPathPoint::CloseSubpath)
00704             index = 0;
00705         else
00706             ++index;
00707 
00708         if (index < subpath->size()) {
00709             segment = KoPathSegment(point, subpath->at(index));
00710         }
00711     }
00712     return segment;
00713 }
00714 
00715 int KoPathShape::pointCount() const
00716 {
00717     int i = 0;
00718     KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
00719     for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
00720         i += (*pathIt)->size();
00721     }
00722 
00723     return i;
00724 }
00725 
00726 int KoPathShape::subpathCount() const
00727 {
00728     return m_subpaths.count();
00729 }
00730 
00731 int KoPathShape::pointCountSubpath(int subpathIndex) const
00732 {
00733     KoSubpath * subpath = subPath(subpathIndex);
00734 
00735     if (subpath == 0)
00736         return -1;
00737 
00738     return subpath->size();
00739 }
00740 
00741 bool KoPathShape::isClosedSubpath(int subpathIndex)
00742 {
00743     KoSubpath * subpath = subPath(subpathIndex);
00744 
00745     if (subpath == 0)
00746         return false;
00747 
00748     const bool firstClosed = subpath->first()->properties() & KoPathPoint::CloseSubpath;
00749     const bool lastClosed = subpath->last()->properties() & KoPathPoint::CloseSubpath;
00750 
00751     return firstClosed && lastClosed;
00752 }
00753 
00754 bool KoPathShape::insertPoint(KoPathPoint* point, const KoPathPointIndex &pointIndex)
00755 {
00756     KoSubpath * subpath = subPath(pointIndex.first);
00757 
00758     if (subpath == 0 || pointIndex.second < 0 || pointIndex.second > subpath->size())
00759         return false;
00760 
00761     KoPathPoint::KoPointProperties properties = point->properties();
00762     properties &= ~KoPathPoint::StartSubpath;
00763     properties &= ~KoPathPoint::StopSubpath;
00764     properties &= ~KoPathPoint::CloseSubpath;
00765     // check if new point starts subpath
00766     if (pointIndex.second == 0) {
00767         properties |= KoPathPoint::StartSubpath;
00768         // subpath was closed
00769         if (subpath->last()->properties() & KoPathPoint::CloseSubpath) {
00770             // keep the path closed
00771             properties |= KoPathPoint::CloseSubpath;
00772         }
00773         // old first point does not start the subpath anymore
00774         subpath->first()->unsetProperty(KoPathPoint::StartSubpath);
00775     }
00776     // check if new point stops subpath
00777     else if (pointIndex.second == subpath->size()) {
00778         properties |= KoPathPoint::StopSubpath;
00779         // subpath was closed
00780         if (subpath->last()->properties() & KoPathPoint::CloseSubpath) {
00781             // keep the path closed
00782             properties = properties | KoPathPoint::CloseSubpath;
00783         }
00784         // old last point does not end subpath anymore
00785         subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
00786     }
00787 
00788     point->setProperties(properties);
00789     point->setParent(this);
00790     subpath->insert(pointIndex.second , point);
00791     return true;
00792 }
00793 
00794 KoPathPoint * KoPathShape::removePoint(const KoPathPointIndex &pointIndex)
00795 {
00796     KoSubpath * subpath = subPath(pointIndex.first);
00797 
00798     if (subpath == 0 || pointIndex.second < 0 || pointIndex.second >= subpath->size())
00799         return 0;
00800 
00801     KoPathPoint * point = subpath->takeAt(pointIndex.second);
00802 
00803     // check if we removed the first point
00804     if (pointIndex.second == 0) {
00805         // first point removed, set new StartSubpath
00806         subpath->first()->setProperty(KoPathPoint::StartSubpath);
00807         // check if path was closed
00808         if (subpath->last()->properties() & KoPathPoint::CloseSubpath) {
00809             // keep path closed
00810             subpath->first()->setProperty(KoPathPoint::CloseSubpath);
00811         }
00812     }
00813     // check if we removed the last point
00814     else if (pointIndex.second == subpath->size()) { // use size as point is already removed
00815         // last point removed, set new StopSubpath
00816         subpath->last()->setProperty(KoPathPoint::StopSubpath);
00817         // check if path was closed
00818         if (point->properties() & KoPathPoint::CloseSubpath) {
00819             // keep path closed
00820             subpath->last()->setProperty(KoPathPoint::CloseSubpath);
00821         }
00822     }
00823 
00824     return point;
00825 }
00826 
00827 bool KoPathShape::breakAfter(const KoPathPointIndex &pointIndex)
00828 {
00829     KoSubpath * subpath = subPath(pointIndex.first);
00830 
00831     if (!subpath || pointIndex.second < 0 || pointIndex.second > subpath->size() - 2
00832         || isClosedSubpath(pointIndex.first))
00833         return false;
00834 
00835     KoSubpath * newSubpath = new KoSubpath;
00836 
00837     int size = subpath->size();
00838     for (int i = pointIndex.second + 1; i < size; ++i) {
00839         newSubpath->append(subpath->takeAt(pointIndex.second + 1));
00840     }
00841     // now make the first point of the new subpath a starting node
00842     newSubpath->first()->setProperty(KoPathPoint::StartSubpath);
00843     // the last point of the old subpath is now an ending node
00844     subpath->last()->setProperty(KoPathPoint::StopSubpath);
00845 
00846     // insert the new subpath after the broken one
00847     m_subpaths.insert(pointIndex.first + 1, newSubpath);
00848 
00849     return true;
00850 }
00851 
00852 bool KoPathShape::join(int subpathIndex)
00853 {
00854     KoSubpath * subpath = subPath(subpathIndex);
00855     KoSubpath * nextSubpath = subPath(subpathIndex + 1);
00856 
00857     if (!subpath || !nextSubpath || isClosedSubpath(subpathIndex)
00858             || isClosedSubpath(subpathIndex+1))
00859         return false;
00860 
00861     // the last point of the subpath does not end the subpath anymore
00862     subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
00863     // the first point of the next subpath does not start a subpath anymore
00864     nextSubpath->first()->unsetProperty(KoPathPoint::StartSubpath);
00865 
00866     // append the second subpath to the first
00867     foreach(KoPathPoint * p, *nextSubpath)
00868         subpath->append(p);
00869 
00870     // remove the nextSubpath from path
00871     m_subpaths.removeAt(subpathIndex + 1);
00872 
00873     // delete it as it is no longer possible to use it
00874     delete nextSubpath;
00875 
00876     return true;
00877 }
00878 
00879 bool KoPathShape::moveSubpath(int oldSubpathIndex, int newSubpathIndex)
00880 {
00881     KoSubpath * subpath = subPath(oldSubpathIndex);
00882 
00883     if (subpath == 0 || newSubpathIndex >= m_subpaths.size())
00884         return false;
00885 
00886     if (oldSubpathIndex == newSubpathIndex)
00887         return true;
00888 
00889     m_subpaths.removeAt(oldSubpathIndex);
00890     m_subpaths.insert(newSubpathIndex, subpath);
00891 
00892     return true;
00893 }
00894 
00895 KoPathPointIndex KoPathShape::openSubpath(const KoPathPointIndex &pointIndex)
00896 {
00897     KoSubpath * subpath = subPath(pointIndex.first);
00898 
00899     if (!subpath || pointIndex.second < 0 || pointIndex.second >= subpath->size()
00900             || !isClosedSubpath(pointIndex.first))
00901         return KoPathPointIndex(-1, -1);
00902 
00903     KoPathPoint * oldStartPoint = subpath->first();
00904     // the old starting node no longer starts the subpath
00905     oldStartPoint->unsetProperty(KoPathPoint::StartSubpath);
00906     // the old end node no longer closes the subpath
00907     subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
00908 
00909     // reorder the subpath
00910     for (int i = 0; i < pointIndex.second; ++i) {
00911         subpath->append(subpath->takeFirst());
00912     }
00913     // make the first point a start node
00914     subpath->first()->setProperty(KoPathPoint::StartSubpath);
00915     // make the last point an end node
00916     subpath->last()->setProperty(KoPathPoint::StopSubpath);
00917 
00918     return pathPointIndex(oldStartPoint);
00919 }
00920 
00921 KoPathPointIndex KoPathShape::closeSubpath(const KoPathPointIndex &pointIndex)
00922 {
00923     KoSubpath * subpath = subPath(pointIndex.first);
00924 
00925     if (!subpath || pointIndex.second < 0 || pointIndex.second >= subpath->size()
00926         || isClosedSubpath(pointIndex.first))
00927         return KoPathPointIndex(-1, -1);
00928 
00929     KoPathPoint * oldStartPoint = subpath->first();
00930     // the old starting node no longer starts the subpath
00931     oldStartPoint->unsetProperty(KoPathPoint::StartSubpath);
00932     // the old end node no longer ends the subpath
00933     subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
00934 
00935     // reorder the subpath
00936     for (int i = 0; i < pointIndex.second; ++i) {
00937         subpath->append(subpath->takeFirst());
00938     }
00939     subpath->first()->setProperty(KoPathPoint::StartSubpath);
00940     subpath->last()->setProperty(KoPathPoint::StopSubpath);
00941 
00942     closeSubpath(subpath);
00943     return pathPointIndex(oldStartPoint);
00944 }
00945 
00946 bool KoPathShape::reverseSubpath(int subpathIndex)
00947 {
00948     KoSubpath * subpath = subPath(subpathIndex);
00949 
00950     if (subpath == 0)
00951         return false;
00952 
00953     int size = subpath->size();
00954     for (int i = 0; i < size; ++i) {
00955         KoPathPoint *p = subpath->takeAt(i);
00956         p->reverse();
00957         subpath->prepend(p);
00958     }
00959 
00960     // adjust the position dependent properties
00961     KoPathPoint *first = subpath->first();
00962     KoPathPoint *last = subpath->last();
00963 
00964     KoPathPoint::KoPointProperties firstProps = first->properties();
00965     KoPathPoint::KoPointProperties lastProps = last->properties();
00966 
00967     firstProps |= KoPathPoint::StartSubpath;
00968     firstProps &= ~KoPathPoint::StopSubpath;
00969     lastProps |= KoPathPoint::StopSubpath;
00970     lastProps &= ~KoPathPoint::StartSubpath;
00971     if (firstProps & KoPathPoint::CloseSubpath) {
00972         firstProps |= KoPathPoint::CloseSubpath;
00973         lastProps |= KoPathPoint::CloseSubpath;
00974     }
00975     first->setProperties(firstProps);
00976     last->setProperties(lastProps);
00977 
00978     return true;
00979 }
00980 
00981 KoSubpath * KoPathShape::removeSubpath(int subpathIndex)
00982 {
00983     KoSubpath * subpath = subPath(subpathIndex);
00984 
00985     if (subpath != 0)
00986         m_subpaths.removeAt(subpathIndex);
00987 
00988     return subpath;
00989 }
00990 
00991 bool KoPathShape::addSubpath(KoSubpath * subpath, int subpathIndex)
00992 {
00993     if (subpathIndex < 0 || subpathIndex > m_subpaths.size())
00994         return false;
00995 
00996     m_subpaths.insert(subpathIndex, subpath);
00997 
00998     return true;
00999 }
01000 
01001 bool KoPathShape::combine(KoPathShape *path)
01002 {
01003     if (! path)
01004         return false;
01005 
01006     QMatrix pathMatrix = path->absoluteTransformation(0);
01007     QMatrix myMatrix = absoluteTransformation(0).inverted();
01008 
01009     foreach(KoSubpath* subpath, path->m_subpaths) {
01010         KoSubpath *newSubpath = new KoSubpath();
01011 
01012         foreach(KoPathPoint* point, *subpath) {
01013             KoPathPoint *newPoint = new KoPathPoint(*point);
01014             newPoint->map(pathMatrix);
01015             newPoint->map(myMatrix);
01016             newPoint->setParent(this);
01017             newSubpath->append(newPoint);
01018         }
01019         m_subpaths.append(newSubpath);
01020     }
01021     normalize();
01022     return true;
01023 }
01024 
01025 bool KoPathShape::separate(QList<KoPathShape*> & separatedPaths)
01026 {
01027     if (! m_subpaths.size())
01028         return false;
01029 
01030     QMatrix myMatrix = absoluteTransformation(0);
01031 
01032     foreach(KoSubpath* subpath, m_subpaths) {
01033         KoPathShape *shape = new KoPathShape();
01034         if (! shape) continue;
01035 
01036         shape->setBorder(border());
01037         shape->setShapeId(shapeId());
01038 
01039         KoSubpath *newSubpath = new KoSubpath();
01040 
01041         foreach(KoPathPoint* point, *subpath) {
01042             KoPathPoint *newPoint = new KoPathPoint(*point);
01043             newPoint->map(myMatrix);
01044             newSubpath->append(newPoint);
01045         }
01046         shape->m_subpaths.append(newSubpath);
01047         shape->normalize();
01048         separatedPaths.append(shape);
01049     }
01050     return true;
01051 }
01052 
01053 void KoPathShape::closeSubpath(KoSubpath *subpath)
01054 {
01055     if (! subpath)
01056         return;
01057 
01058     subpath->last()->setProperty(KoPathPoint::CloseSubpath);
01059     subpath->first()->setProperty(KoPathPoint::CloseSubpath);
01060 }
01061 
01062 void KoPathShape::closeMergeSubpath(KoSubpath *subpath)
01063 {
01064     if (! subpath || subpath->size() < 2)
01065         return;
01066 
01067     KoPathPoint * lastPoint = subpath->last();
01068     KoPathPoint * firstPoint = subpath->first();
01069 
01070     // check if first and last points are coincident
01071     if (lastPoint->point() == firstPoint->point()) {
01072         // we are removing the current last point and
01073         // reuse its first control point if active
01074         firstPoint->setProperty(KoPathPoint::StartSubpath);
01075         firstPoint->setProperty(KoPathPoint::CloseSubpath);
01076         if (lastPoint->activeControlPoint1())
01077             firstPoint->setControlPoint1(lastPoint->controlPoint1());
01078         // remove last point
01079         delete subpath->takeLast();
01080         // the new last point closes the subpath now
01081         lastPoint = subpath->last();
01082         lastPoint->setProperty(KoPathPoint::StopSubpath);
01083         lastPoint->setProperty(KoPathPoint::CloseSubpath);
01084     } else {
01085         closeSubpath(subpath);
01086     }
01087 }
01088 
01089 KoSubpath * KoPathShape::subPath(int subpathIndex) const
01090 {
01091     if (subpathIndex < 0 || subpathIndex >= m_subpaths.size())
01092         return 0;
01093 
01094     return m_subpaths.at(subpathIndex);
01095 }
01096 
01097 QString KoPathShape::pathShapeId() const
01098 {
01099     return KoPathShapeId;
01100 }
01101 
01102 QString KoPathShape::toString(const QMatrix &matrix) const
01103 {
01104     QString d;
01105 
01106     KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
01107     for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
01108         KoSubpath::const_iterator it((*pathIt)->constBegin());
01109         KoPathPoint * lastPoint(*it);
01110         bool activeCP = false;
01111         for (; it != (*pathIt)->constEnd(); ++it) {
01112             // first point of subpath ?
01113             if (it == (*pathIt)->constBegin()) {
01114                 if ((*it)->properties() & KoPathPoint::StartSubpath) {
01115                     QPointF p = matrix.map((*it)->point());
01116                     d += QString("M%1 %2").arg(p.x()).arg(p.y());
01117                 }
01118             }
01119             // end point of curve ?
01120             else if (activeCP || (*it)->activeControlPoint1()) {
01121                 QPointF cp1 = matrix.map(activeCP ? lastPoint->controlPoint2() : lastPoint->point());
01122                 QPointF cp2 = matrix.map((*it)->activeControlPoint1() ? (*it)->controlPoint1() : (*it)->point());
01123                 QPointF p = matrix.map((*it)->point());
01124                 d += QString("C%1 %2 %3 %4 %5 %6")
01125                      .arg(cp1.x()).arg(cp1.y())
01126                      .arg(cp2.x()).arg(cp2.y())
01127                      .arg(p.x()).arg(p.y());
01128             }
01129             // end point of line
01130             else {
01131                 QPointF p = matrix.map((*it)->point());
01132                 d += QString("L%1 %2").arg(p.x()).arg(p.y());
01133             }
01134             // last point closes subpath ?
01135             if ((*it)->properties() & KoPathPoint::StopSubpath
01136                     && (*it)->properties() & KoPathPoint::CloseSubpath) {
01137                 // add curve when there is a curve on the way to the first point
01138                 KoPathPoint * firstPoint = (*pathIt)->first();
01139                 if ((*it)->activeControlPoint2() || firstPoint->activeControlPoint1()) {
01140                     QPointF cp1 = matrix.map((*it)->activeControlPoint2() ? (*it)->controlPoint2() : (*it)->point());
01141                     QPointF cp2 = matrix.map(firstPoint->activeControlPoint1() ? firstPoint->controlPoint1() : (firstPoint)->point());
01142                     QPointF p = matrix.map(firstPoint->point());
01143 
01144                     d += QString("C%1 %2 %3 %4 %5 %6")
01145                          .arg(cp1.x()).arg(cp1.y())
01146                          .arg(cp2.x()).arg(cp2.y())
01147                          .arg(p.x()).arg(p.y());
01148                 }
01149                 d += QString("Z");
01150             }
01151 
01152             activeCP = (*it)->activeControlPoint2();
01153             lastPoint = *it;
01154         }
01155     }
01156 
01157     return d;
01158 }
01159 
01160 char nodeType( const KoPathPoint * point )
01161 {
01162     if (point->properties() & KoPathPoint::IsSmooth) {
01163         return 's';
01164     }
01165     else if (point->properties() & KoPathPoint::IsSymmetric) {
01166         return 'z';
01167     }
01168     else {
01169         return 'c';
01170     }
01171 }
01172 
01173 QString KoPathShape::nodeTypes() const
01174 {
01175     QString types;
01176     KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
01177     for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
01178         KoSubpath::const_iterator it((*pathIt)->constBegin());
01179         for (; it != (*pathIt)->constEnd(); ++it) {
01180             if (it == (*pathIt)->constBegin()) {
01181                 types.append( 'c' );
01182             }
01183             else {
01184                 types.append( nodeType( *it ) );
01185             }
01186 
01187             if ((*it)->properties() & KoPathPoint::StopSubpath
01188                 && (*it)->properties() & KoPathPoint::CloseSubpath) {
01189                 KoPathPoint * firstPoint = (*pathIt)->first();
01190                 types.append( nodeType( firstPoint ) );
01191             }
01192         }
01193     }
01194     return types;
01195 }
01196 
01197 void updateNodeType( KoPathPoint * point, const QChar & nodeType )
01198 {
01199     if (nodeType == 's') {
01200         point->setProperty(KoPathPoint::IsSmooth);
01201     }
01202     else if (nodeType == 'z') {
01203         point->setProperty(KoPathPoint::IsSymmetric);
01204     }
01205 }
01206 
01207 void KoPathShape::loadNodeTypes(const KoXmlElement & element)
01208 {
01209     if (element.hasAttributeNS(KoXmlNS::koffice, "nodeTypes")) {
01210         QString nodeTypes = element.attributeNS(KoXmlNS::koffice, "nodeTypes");
01211         QString::const_iterator nIt(nodeTypes.constBegin());
01212         KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
01213         for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
01214             KoSubpath::const_iterator it((*pathIt)->constBegin());
01215             for (; it != (*pathIt)->constEnd(); ++it, nIt++) {
01216                 // be sure not to crash if there are not enough nodes in nodeTypes
01217                 if (nIt == nodeTypes.constEnd()) {
01218                     kWarning(30006) << "not enough nodes in koffice:nodeTypes";
01219                     return;
01220                 }
01221                 // the first node is always of type 'c'
01222                 if (it != (*pathIt)->constBegin()) {
01223                     updateNodeType(*it, *nIt);
01224                 }
01225 
01226                 if ((*it)->properties() & KoPathPoint::StopSubpath
01227                     && (*it)->properties() & KoPathPoint::CloseSubpath) {
01228                     ++nIt;
01229                     updateNodeType((*pathIt)->first(), *nIt);
01230                 }
01231             }
01232         }
01233     }
01234 }
01235 
01236 Qt::FillRule KoPathShape::fillRule() const
01237 {
01238     Q_D(const KoPathShape);
01239     return d->fillRule;
01240 }
01241 
01242 void KoPathShape::setFillRule(Qt::FillRule fillRule)
01243 {
01244     Q_D(KoPathShape);
01245     d->fillRule = fillRule;
01246 }
01247 
01248 KoPathShape * KoPathShape::fromQPainterPath(const QPainterPath &path)
01249 {
01250     KoPathShape * shape = new KoPathShape();
01251 
01252     int elementCount = path.elementCount();
01253     for (int i = 0; i < elementCount; i++) {
01254         QPainterPath::Element element = path.elementAt(i);
01255         switch (element.type) {
01256         case QPainterPath::MoveToElement:
01257             shape->moveTo(QPointF(element.x, element.y));
01258             break;
01259         case QPainterPath::LineToElement:
01260             shape->lineTo(QPointF(element.x, element.y));
01261             break;
01262         case QPainterPath::CurveToElement:
01263             shape->curveTo(QPointF(element.x, element.y),
01264                            QPointF(path.elementAt(i + 1).x, path.elementAt(i + 1).y),
01265                            QPointF(path.elementAt(i + 2).x, path.elementAt(i + 2).y));
01266             break;
01267         default:
01268             continue;
01269         }
01270     }
01271 
01272     shape->normalize();
01273     return shape;
01274 }
01275 
01276 bool KoPathShape::hitTest(const QPointF &position) const
01277 {
01278     if (parent() && parent()->childClipped(this) && ! parent()->hitTest(position))
01279         return false;
01280 
01281     QPointF point = absoluteTransformation(0).inverted().map(position);
01282     const QPainterPath outlinePath = outline();
01283     if (border()) {
01284         KoInsets insets;
01285         border()->borderInsets(this, insets);
01286         QRectF roi( QPointF(-insets.left, -insets.top), QPointF(insets.right, insets.bottom) );
01287         roi.moveCenter( point );
01288         if( outlinePath.intersects( roi ) || outlinePath.contains( roi ) )
01289             return true;
01290     } else {
01291         if( outlinePath.contains( point ) )
01292             return true;
01293     }
01294 
01295     // if there is no shadow we can as well just leave
01296     if (! shadow())
01297         return false;
01298 
01299     // the shadow has an offset to the shape, so we simply
01300     // check if the position minus the shadow offset hits the shape
01301     point = absoluteTransformation(0).inverted().map(position - shadow()->offset());
01302 
01303     return outlinePath.contains(point);
01304 }