00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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;
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
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
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--;
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 , double )
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 }