kivio

kiviopolylineconnector.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2004 Peter Simonsson <psn@linux.se>,
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017    Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "kiviopolylineconnector.h"
00021 
00022 #include <kdebug.h>
00023 
00024 #include <KoZoomHandler.h>
00025 #include <KoSize.h>
00026 
00027 #include "kivio_line_style.h"
00028 #include "kivio_painter.h"
00029 #include "kivio_intra_stencil_data.h"
00030 #include "kivio_connector_point.h"
00031 #include "kivio_custom_drag_data.h"
00032 #include "tkmath.h"
00033 #include "kivio_page.h"
00034 #include "kivio_layer.h"
00035 #include "kivio_common.h"
00036 
00037 
00038 namespace Kivio {
00039   PolyLineConnector::PolyLineConnector()
00040     : Kivio1DStencil()
00041   {
00042     m_startArrow = new KivioArrowHead();
00043     m_endArrow = new KivioArrowHead();
00044 
00045     m_needsWidth = false;
00046     m_needsText = false; // FIXME add text support
00047 
00048     m_pCanProtect->clearBit(kpAspect);
00049     m_pCanProtect->clearBit(kpWidth);
00050     m_pCanProtect->clearBit(kpHeight);
00051     m_pCanProtect->clearBit(kpX);
00052     m_pCanProtect->clearBit(kpY);
00053 
00054     // This is a stencil of type connector
00055     setType(kstConnector);
00056   }
00057 
00058   PolyLineConnector::~PolyLineConnector()
00059   {
00060     delete m_startArrow;
00061     delete m_endArrow;
00062   }
00063 
00064   KivioStencil* PolyLineConnector::duplicate()
00065   {
00066     PolyLineConnector* connector = new PolyLineConnector();
00067     copyBasicInto(connector);
00068     connector->m_points = m_points;
00069 
00070     // Copy the arrow head information
00071     connector->setStartAHType( m_startArrow->type() );
00072     connector->setStartAHWidth( m_startArrow->width() );
00073     connector->setStartAHLength( m_startArrow->length() );
00074 
00075     connector->setEndAHType( m_endArrow->type() );
00076     connector->setEndAHWidth( m_endArrow->width() );
00077     connector->setEndAHLength( m_endArrow->length() );
00078 
00079     return connector;
00080   }
00081 
00082   bool PolyLineConnector::loadCustom(const QDomElement& e)
00083   {
00084     QDomNode node = e.firstChild();
00085     QString nodeName;
00086     m_points.clear();
00087 
00088     while(!node.isNull()) {
00089       nodeName = node.nodeName();
00090 
00091       if(nodeName == "KivioArrowHeads") {
00092         loadArrowHeads(node.toElement());
00093       } else if(nodeName == "KivioGeometryPoints") {
00094         QDomNode pointsNode = node.firstChild();
00095         QDomElement el;
00096 
00097         while(!pointsNode.isNull()) {
00098           if(pointsNode.nodeName() == "KivioPoint") {
00099             el = pointsNode.toElement();
00100             KoPoint p;
00101             p.setX(XmlReadFloat(el, "x", 1.0f));
00102             p.setY(XmlReadFloat(el, "y", 1.0f));
00103             addPoint(p);
00104           }
00105 
00106           pointsNode = pointsNode.nextSibling();
00107         }
00108       }
00109 
00110       node = node.nextSibling();
00111     }
00112 
00113     return true;
00114   }
00115 
00116   bool PolyLineConnector::saveCustom(QDomElement& e, QDomDocument& doc)
00117   {
00118     e.appendChild(saveArrowHeads(doc));
00119     QDomElement pointsElement = doc.createElement("KivioGeometryPoints");
00120 
00121     for(QValueList<KoPoint>::iterator it = m_points.begin(); it != m_points.end(); ++it) {
00122       KoPoint p = (*it);
00123       QDomElement el = doc.createElement("KivioPoint");
00124       XmlWriteFloat(el, "x", p.x());
00125       XmlWriteFloat(el, "y", p.y());
00126       pointsElement.appendChild(el);
00127     }
00128 
00129     e.appendChild(pointsElement);
00130 
00131     return true;
00132   }
00133 
00134   KivioCollisionType PolyLineConnector::checkForCollision(KoPoint* p, double threshold)
00135   {
00136     unsigned int i = 0;
00137     KoPoint point;
00138     double px = p->x();
00139     double py = p->y();
00140     uint count = m_points.count();
00141 
00142     while(i < count) {
00143       point = m_points[i];
00144 
00145       if(px >= point.x() - threshold && px <= point.x() + threshold &&
00146         py >= point.y() - threshold && py <= point.y() + threshold)
00147       {
00148         return static_cast<KivioCollisionType>(i + kctCustom + 1);
00149       }
00150 
00151       i++;
00152     }
00153 
00154     i = 0;
00155     count--; // As we need current + 1;
00156 
00157     while(i < count) {
00158       point = m_points[i];
00159 
00160       if(collisionLine(point.x(), point.y(),
00161         m_points[i + 1].x(), m_points[i + 1].y(), px, py, threshold))
00162       {
00163         return kctBody;
00164       }
00165 
00166       i++;
00167     }
00168 
00169     return kctNone;
00170   }
00171 
00172   void PolyLineConnector::paint(KivioIntraStencilData* data)
00173   {
00174     if(m_points.count() < 2) {
00175       kdDebug(43000) << "ARGH! We're missing points in this connector!" << endl;
00176       return;
00177     }
00178 
00179     KoZoomHandler* zoom = data->zoomHandler;
00180     KivioPainter* painter = data->painter;
00181 
00182     painter->setLineStyle(m_pLineStyle);
00183     painter->setLineWidth(zoom->zoomItY(m_pLineStyle->width()));
00184 
00185     QPointArray pa(m_points.count());
00186     QValueList<KoPoint>::iterator it;
00187     int i = 0;
00188 
00189     for(it = m_points.begin(); it != m_points.end(); ++it) {
00190       pa.setPoint(i, zoom->zoomPoint(*it));
00191       i++;
00192     }
00193 
00194     KoPoint startVec = m_points[1] - m_points[0];
00195     KoPoint endVec = m_points.last() - m_points[m_points.count() - 2];
00196     double startLen = startVec.manhattanLength();
00197     double endLen = endVec.manhattanLength();
00198 
00199     if(startLen) {
00200       startVec.setX(startVec.x() / startLen);
00201       startVec.setY(startVec.y() / startLen);
00202       QPoint p = pa[0];
00203       p.setX(p.x() + qRound(startVec.x() * zoom->zoomItX(m_startArrow->cut())));
00204       p.setY(p.y() + qRound(startVec.y() * zoom->zoomItY(m_startArrow->cut())));
00205     }
00206 
00207     if(endLen) {
00208       endVec.setX(endVec.x() / endLen);
00209       endVec.setY(endVec.y() / endLen);
00210       QPoint p = pa[m_points.count() - 1];
00211       p.setX(p.x() + qRound(endVec.x() * zoom->zoomItX(m_endArrow->cut())));
00212       p.setY(p.y() + qRound(endVec.y() * zoom->zoomItY(m_endArrow->cut())));
00213     }
00214 
00215     painter->drawPolyline(pa);
00216     painter->setBGColor(m_pFillStyle->color());
00217 
00218     if(startLen) {
00219       m_startArrow->paint(painter, m_points[0].x(), m_points[0].y(), -startVec.x(), -startVec.y(), zoom);
00220     }
00221 
00222     if(endLen) {
00223       m_endArrow->paint(painter, m_points.last().x(), m_points.last().y(), endVec.x(), endVec.y(), zoom);
00224     }
00225   }
00226 
00227   void PolyLineConnector::paintOutline(KivioIntraStencilData* data)
00228   {
00229     paint(data);
00230   }
00231 
00232   void PolyLineConnector::paintSelectionHandles( KivioIntraStencilData* data )
00233   {
00234     KivioPainter* painter = data->painter;
00235     KoZoomHandler* zoomHandler = data->zoomHandler;
00236     QValueList<KoPoint>::Iterator it;
00237     int x, y, flag;
00238     x = y = flag = 0;
00239 
00240     for(it = m_points.begin(); it != m_points.end(); ++it) {
00241       x = zoomHandler->zoomItX((*it).x());
00242       y = zoomHandler->zoomItY((*it).y());    
00243 
00244       if((*it) == m_pEnd->position()) {
00245         flag = ((m_pEnd->target()) ? KivioPainter::cpfConnected : 0) | KivioPainter::cpfEnd;
00246       } else if((*it) == m_pStart->position()) {
00247         flag = ((m_pStart->target()) ? KivioPainter::cpfConnected : 0) | KivioPainter::cpfStart;
00248       } else {
00249         flag = 0;
00250       }
00251 
00252       painter->drawHandle(x, y, flag);
00253     }
00254   }
00255 
00256   void PolyLineConnector::addPoint(const KoPoint& p)
00257   {
00258     if(m_points.count() == 0) {
00259       m_pStart->setPosition(p.x(), p.y(), false);
00260       m_pStart->disconnect();
00261     } else {
00262       m_pEnd->setPosition(p.x(), p.y(), false);
00263       m_pEnd->disconnect();
00264     }
00265 
00266     m_points.append(p);
00267   }
00268 
00269   void PolyLineConnector::movePoint(unsigned int index, double xOffset, double yOffset)
00270   {
00271     if(index >= m_points.count()) {
00272       return;
00273     }
00274 
00275     KoPoint p(xOffset, yOffset);
00276     m_points[index] += p;
00277 
00278     if(index == (m_points.count() - 1)) {
00279       m_pEnd->setPosition(m_points[index].x(), m_points[index].y(), false);
00280       m_pEnd->disconnect();
00281     } else if(index == 0) {
00282       m_pStart->setPosition(m_points[index].x(), m_points[index].y(), false);
00283       m_pStart->disconnect();
00284     }
00285   }
00286 
00287   void PolyLineConnector::movePointTo(unsigned int index, const KoPoint& p)
00288   {
00289     m_points[index] = p;
00290 
00291     if(index == (m_points.count() - 1)) {
00292       m_pEnd->setPosition(p.x(), p.y(), false);
00293       m_pEnd->disconnect();
00294     } else if(index == 0) {
00295       m_pStart->setPosition(p.x(), p.y(), false);
00296       m_pStart->disconnect();
00297     }
00298   }
00299 
00300   void PolyLineConnector::moveLastPointTo(const KoPoint& p)
00301   {
00302     movePointTo(m_points.count() - 1, p);
00303   }
00304 
00305   void PolyLineConnector::customDrag(KivioCustomDragData* data)
00306   {
00307     KoPoint pos(data->x, data->y);
00308     setCustomIDPoint(data->id, pos, data->page);
00309   }
00310 
00311   void PolyLineConnector::move(double xOffset, double yOffset)
00312   {
00313     for(unsigned int i = 0; i < m_points.count(); i++) {
00314       movePoint(i, xOffset, yOffset);
00315     }
00316   }
00317 
00318   double PolyLineConnector::x()
00319   {
00320     if(m_points.count() == 0) {
00321       return 0;
00322     }
00323 
00324     return rect().x();
00325   }
00326 
00327   void PolyLineConnector::setX(double newX)
00328   {
00329     double dx = newX - x();
00330     move(dx, 0);
00331   }
00332 
00333   double PolyLineConnector::y()
00334   {
00335     if(m_points.count() == 0) {
00336       return 0;
00337     }
00338 
00339     return rect().y();
00340   }
00341 
00342   void PolyLineConnector::setY(double newY)
00343   {
00344     double dy = newY - y();
00345     move(0, dy);
00346   }
00347 
00348   void PolyLineConnector::checkForConnection(KivioConnectorPoint* cp, KivioPage* page)
00349   {
00350     if(cp->connectable()) {
00351       KivioLayer* currentLayer = page->curLayer();
00352       KivioLayer* layer = page->firstLayer();
00353       bool found = false;
00354 
00355       while(layer && !found) {
00356         if((layer != currentLayer) && (!layer->connectable() || !layer->visible())) {
00357           layer = page->nextLayer();
00358           continue;
00359         }
00360 
00361         if(layer->connectPointToTarget(cp, 8.0f)) {
00362           found = true;
00363         }
00364 
00365         layer = page->nextLayer();
00366       }
00367 
00368       if(!found) {
00369         cp->disconnect();
00370       }
00371     }
00372   }
00373 
00374   void PolyLineConnector::updateConnectorPoints(KivioConnectorPoint* cp, double /*oldX*/, double /*oldY*/)
00375   {
00376     if(cp == m_pStart) {
00377       m_points[0] = m_pStart->position();
00378     } else if(cp == m_pEnd) {
00379       m_points[m_points.count() - 1] = m_pEnd->position();
00380     }
00381   }
00382 
00383   KoRect PolyLineConnector::rect()
00384   {
00385     KoPoint p = m_points.first();
00386     KoPoint topLeft(p.x(), p.y()), bottomRight;
00387     QValueList<KoPoint>::iterator it;
00388     QValueList<KoPoint>::iterator itEnd = m_points.end();
00389 
00390     for(it = m_points.begin(); it != itEnd; ++it) {
00391       p = (*it);
00392       topLeft.setX(QMIN(p.x(), topLeft.x()));
00393       topLeft.setY(QMIN(p.y(), topLeft.y()));
00394       bottomRight.setX(QMAX(p.x(), bottomRight.x()));
00395       bottomRight.setY(QMAX(p.y(), bottomRight.y()));
00396     }
00397 
00398     KoRect rect;
00399     rect.moveTopLeft(topLeft);
00400     rect.setWidth(bottomRight.x() - topLeft.x());
00401     rect.setHeight(bottomRight.y() - topLeft.y());
00402     return rect;
00403   }
00404 
00405   bool PolyLineConnector::isInRect(const KoRect& rect)
00406   {
00407     QValueList<KoPoint>::Iterator it;
00408     bool retVal = true;
00409 
00410     for(it = m_points.begin(); it != m_points.end(); ++it) {
00411       retVal = retVal && rect.contains((*it));
00412     }
00413 
00414     return retVal;
00415   }
00416 
00417   bool PolyLineConnector::loadArrowHeads(const QDomElement& e)
00418   {
00419     bool first = true;
00420     QDomNode node = e.firstChild();
00421 
00422     while(!node.isNull()) {
00423       if(node.nodeName() == "KivioArrowHead") {
00424         if(first) {
00425           m_startArrow->loadXML(node.toElement());
00426           first = false;
00427         } else {
00428           m_endArrow->loadXML(node.toElement());
00429         }
00430       }
00431 
00432       node = node.nextSibling();
00433     }
00434 
00435     return true;
00436   }
00437 
00438   QDomElement PolyLineConnector::saveArrowHeads(QDomDocument& doc)
00439   {
00440     QDomElement e = doc.createElement("KivioArrowHeads");
00441 
00442     e.appendChild( m_startArrow->saveXML(doc) );
00443     e.appendChild( m_endArrow->saveXML(doc) );
00444 
00445     return e;
00446   }
00447 
00448   void PolyLineConnector::removePoint(unsigned int index)
00449   {
00450     if(index >= m_points.count()) {
00451       return;
00452     }
00453 
00454     m_points.remove(m_points.at(index));
00455   }
00456 
00457   void PolyLineConnector::removeLastPoint()
00458   {
00459     removePoint(m_points.count() - 1);
00460   }
00461 
00462   void PolyLineConnector::setCustomIDPoint(int customID, const KoPoint& point, KivioPage* page)
00463   {
00464     int index = customID - (kctCustom + 1);
00465 
00466     if((index < 0) || index >= (int)m_points.count()) {
00467       kdDebug(43000) << "PolyLineConnector::setCustomIDPoint: Index out of range! Index = " << index << endl;
00468       return;
00469     }
00470 
00471     movePointTo(index, point);
00472     KivioConnectorPoint* cp;
00473 
00474     if(index == 0) {
00475       cp = m_pStart;
00476     } else if(index == ((int)m_points.count() - 1)) {
00477       cp = m_pEnd;
00478     } else {
00479       return;
00480     }
00481 
00482     checkForConnection(cp, page);
00483   }
00484 
00485   KoPoint PolyLineConnector::customIDPoint(int customID)
00486   {
00487     int index = customID - (kctCustom + 1);
00488 
00489     if((index < 0) || index >= (int)m_points.count()) {
00490       kdDebug(43000) << "PolyLineConnector::customIDPoint: Index out of range! Index = " << index << endl;
00491       return KoPoint();
00492     }
00493 
00494     return m_points[index];
00495   }
00496 
00497 }
KDE Home | KDE Accessibility Home | Description of Access Keys