|
|
|
/* This file is part of the KDE project
|
|
|
|
Copyright (C) 2004 Peter Simonsson <psn@linux.se>,
|
|
|
|
|
|
|
|
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 "kiviopolylineconnector.h"
|
|
|
|
|
|
|
|
#include <kdebug.h>
|
|
|
|
|
|
|
|
#include <KoZoomHandler.h>
|
|
|
|
#include <KoSize.h>
|
|
|
|
|
|
|
|
#include "kivio_line_style.h"
|
|
|
|
#include "kivio_painter.h"
|
|
|
|
#include "kivio_intra_stencil_data.h"
|
|
|
|
#include "kivio_connector_point.h"
|
|
|
|
#include "kivio_custom_drag_data.h"
|
|
|
|
#include "tkmath.h"
|
|
|
|
#include "kivio_page.h"
|
|
|
|
#include "kivio_layer.h"
|
|
|
|
#include "kivio_common.h"
|
|
|
|
|
|
|
|
|
|
|
|
namespace Kivio {
|
|
|
|
PolyLineConnector::PolyLineConnector()
|
|
|
|
: Kivio1DStencil()
|
|
|
|
{
|
|
|
|
m_startArrow = new KivioArrowHead();
|
|
|
|
m_endArrow = new KivioArrowHead();
|
|
|
|
|
|
|
|
m_needsWidth = false;
|
|
|
|
m_needsText = false; // FIXME add text support
|
|
|
|
|
|
|
|
m_pCanProtect->clearBit(kpAspect);
|
|
|
|
m_pCanProtect->clearBit(kpWidth);
|
|
|
|
m_pCanProtect->clearBit(kpHeight);
|
|
|
|
m_pCanProtect->clearBit(kpX);
|
|
|
|
m_pCanProtect->clearBit(kpY);
|
|
|
|
|
|
|
|
// This is a stencil of type connector
|
|
|
|
setType(kstConnector);
|
|
|
|
}
|
|
|
|
|
|
|
|
PolyLineConnector::~PolyLineConnector()
|
|
|
|
{
|
|
|
|
delete m_startArrow;
|
|
|
|
delete m_endArrow;
|
|
|
|
}
|
|
|
|
|
|
|
|
KivioStencil* PolyLineConnector::duplicate()
|
|
|
|
{
|
|
|
|
PolyLineConnector* connector = new PolyLineConnector();
|
|
|
|
copyBasicInto(connector);
|
|
|
|
connector->m_points = m_points;
|
|
|
|
|
|
|
|
// Copy the arrow head information
|
|
|
|
connector->setStartAHType( m_startArrow->type() );
|
|
|
|
connector->setStartAHWidth( m_startArrow->width() );
|
|
|
|
connector->setStartAHLength( m_startArrow->length() );
|
|
|
|
|
|
|
|
connector->setEndAHType( m_endArrow->type() );
|
|
|
|
connector->setEndAHWidth( m_endArrow->width() );
|
|
|
|
connector->setEndAHLength( m_endArrow->length() );
|
|
|
|
|
|
|
|
return connector;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PolyLineConnector::loadCustom(const TQDomElement& e)
|
|
|
|
{
|
|
|
|
TQDomNode node = e.firstChild();
|
|
|
|
TQString nodeName;
|
|
|
|
m_points.clear();
|
|
|
|
|
|
|
|
while(!node.isNull()) {
|
|
|
|
nodeName = node.nodeName();
|
|
|
|
|
|
|
|
if(nodeName == "KivioArrowHeads") {
|
|
|
|
loadArrowHeads(node.toElement());
|
|
|
|
} else if(nodeName == "KivioGeometryPoints") {
|
|
|
|
TQDomNode pointsNode = node.firstChild();
|
|
|
|
TQDomElement el;
|
|
|
|
|
|
|
|
while(!pointsNode.isNull()) {
|
|
|
|
if(pointsNode.nodeName() == "KivioPoint") {
|
|
|
|
el = pointsNode.toElement();
|
|
|
|
KoPoint p;
|
|
|
|
p.setX(XmlReadFloat(el, "x", 1.0f));
|
|
|
|
p.setY(XmlReadFloat(el, "y", 1.0f));
|
|
|
|
addPoint(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
pointsNode = pointsNode.nextSibling();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
node = node.nextSibling();
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PolyLineConnector::saveCustom(TQDomElement& e, TQDomDocument& doc)
|
|
|
|
{
|
|
|
|
e.appendChild(saveArrowHeads(doc));
|
|
|
|
TQDomElement pointsElement = doc.createElement("KivioGeometryPoints");
|
|
|
|
|
|
|
|
for(TQValueList<KoPoint>::iterator it = m_points.begin(); it != m_points.end(); ++it) {
|
|
|
|
KoPoint p = (*it);
|
|
|
|
TQDomElement el = doc.createElement("KivioPoint");
|
|
|
|
XmlWriteFloat(el, "x", p.x());
|
|
|
|
XmlWriteFloat(el, "y", p.y());
|
|
|
|
pointsElement.appendChild(el);
|
|
|
|
}
|
|
|
|
|
|
|
|
e.appendChild(pointsElement);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
KivioCollisionType PolyLineConnector::checkForCollision(KoPoint* p, double threshold)
|
|
|
|
{
|
|
|
|
unsigned int i = 0;
|
|
|
|
KoPoint point;
|
|
|
|
double px = p->x();
|
|
|
|
double py = p->y();
|
|
|
|
uint count = m_points.count();
|
|
|
|
|
|
|
|
while(i < count) {
|
|
|
|
point = m_points[i];
|
|
|
|
|
|
|
|
if(px >= point.x() - threshold && px <= point.x() + threshold &&
|
|
|
|
py >= point.y() - threshold && py <= point.y() + threshold)
|
|
|
|
{
|
|
|
|
return static_cast<KivioCollisionType>(i + kctCustom + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
count--; // As we need current + 1;
|
|
|
|
|
|
|
|
while(i < count) {
|
|
|
|
point = m_points[i];
|
|
|
|
|
|
|
|
if(collisionLine(point.x(), point.y(),
|
|
|
|
m_points[i + 1].x(), m_points[i + 1].y(), px, py, threshold))
|
|
|
|
{
|
|
|
|
return kctBody;
|
|
|
|
}
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return kctNone;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PolyLineConnector::paint(KivioIntraStencilData* data)
|
|
|
|
{
|
|
|
|
if(m_points.count() < 2) {
|
|
|
|
kdDebug(43000) << "ARGH! We're missing points in this connector!" << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
KoZoomHandler* zoom = data->zoomHandler;
|
|
|
|
KivioPainter* painter = data->painter;
|
|
|
|
|
|
|
|
painter->setLineStyle(m_pLineStyle);
|
|
|
|
painter->setLineWidth(zoom->zoomItY(m_pLineStyle->width()));
|
|
|
|
|
|
|
|
TQPointArray pa(m_points.count());
|
|
|
|
TQValueList<KoPoint>::iterator it;
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
for(it = m_points.begin(); it != m_points.end(); ++it) {
|
|
|
|
pa.setPoint(i, zoom->zoomPoint(*it));
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
KoPoint startVec = m_points[1] - m_points[0];
|
|
|
|
KoPoint endVec = m_points.last() - m_points[m_points.count() - 2];
|
|
|
|
double startLen = startVec.manhattanLength();
|
|
|
|
double endLen = endVec.manhattanLength();
|
|
|
|
|
|
|
|
if(startLen) {
|
|
|
|
startVec.setX(startVec.x() / startLen);
|
|
|
|
startVec.setY(startVec.y() / startLen);
|
|
|
|
TQPoint p = pa[0];
|
|
|
|
p.setX(p.x() + tqRound(startVec.x() * zoom->zoomItX(m_startArrow->cut())));
|
|
|
|
p.setY(p.y() + tqRound(startVec.y() * zoom->zoomItY(m_startArrow->cut())));
|
|
|
|
}
|
|
|
|
|
|
|
|
if(endLen) {
|
|
|
|
endVec.setX(endVec.x() / endLen);
|
|
|
|
endVec.setY(endVec.y() / endLen);
|
|
|
|
TQPoint p = pa[m_points.count() - 1];
|
|
|
|
p.setX(p.x() + tqRound(endVec.x() * zoom->zoomItX(m_endArrow->cut())));
|
|
|
|
p.setY(p.y() + tqRound(endVec.y() * zoom->zoomItY(m_endArrow->cut())));
|
|
|
|
}
|
|
|
|
|
|
|
|
painter->drawPolyline(pa);
|
|
|
|
painter->setBGColor(m_pFillStyle->color());
|
|
|
|
|
|
|
|
if(startLen) {
|
|
|
|
m_startArrow->paint(painter, m_points[0].x(), m_points[0].y(), -startVec.x(), -startVec.y(), zoom);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(endLen) {
|
|
|
|
m_endArrow->paint(painter, m_points.last().x(), m_points.last().y(), endVec.x(), endVec.y(), zoom);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PolyLineConnector::paintOutline(KivioIntraStencilData* data)
|
|
|
|
{
|
|
|
|
paint(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PolyLineConnector::paintSelectionHandles( KivioIntraStencilData* data )
|
|
|
|
{
|
|
|
|
KivioPainter* painter = data->painter;
|
|
|
|
KoZoomHandler* zoomHandler = data->zoomHandler;
|
|
|
|
TQValueList<KoPoint>::Iterator it;
|
|
|
|
int x, y, flag;
|
|
|
|
x = y = flag = 0;
|
|
|
|
|
|
|
|
for(it = m_points.begin(); it != m_points.end(); ++it) {
|
|
|
|
x = zoomHandler->zoomItX((*it).x());
|
|
|
|
y = zoomHandler->zoomItY((*it).y());
|
|
|
|
|
|
|
|
if((*it) == m_pEnd->position()) {
|
|
|
|
flag = ((m_pEnd->target()) ? KivioPainter::cpfConnected : 0) | KivioPainter::cpfEnd;
|
|
|
|
} else if((*it) == m_pStart->position()) {
|
|
|
|
flag = ((m_pStart->target()) ? KivioPainter::cpfConnected : 0) | KivioPainter::cpfStart;
|
|
|
|
} else {
|
|
|
|
flag = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
painter->drawHandle(x, y, flag);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PolyLineConnector::addPoint(const KoPoint& p)
|
|
|
|
{
|
|
|
|
if(m_points.count() == 0) {
|
|
|
|
m_pStart->setPosition(p.x(), p.y(), false);
|
|
|
|
m_pStart->disconnect();
|
|
|
|
} else {
|
|
|
|
m_pEnd->setPosition(p.x(), p.y(), false);
|
|
|
|
m_pEnd->disconnect();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_points.append(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PolyLineConnector::movePoint(unsigned int index, double xOffset, double yOffset)
|
|
|
|
{
|
|
|
|
if(index >= m_points.count()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
KoPoint p(xOffset, yOffset);
|
|
|
|
m_points[index] += p;
|
|
|
|
|
|
|
|
if(index == (m_points.count() - 1)) {
|
|
|
|
m_pEnd->setPosition(m_points[index].x(), m_points[index].y(), false);
|
|
|
|
m_pEnd->disconnect();
|
|
|
|
} else if(index == 0) {
|
|
|
|
m_pStart->setPosition(m_points[index].x(), m_points[index].y(), false);
|
|
|
|
m_pStart->disconnect();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PolyLineConnector::movePointTo(unsigned int index, const KoPoint& p)
|
|
|
|
{
|
|
|
|
m_points[index] = p;
|
|
|
|
|
|
|
|
if(index == (m_points.count() - 1)) {
|
|
|
|
m_pEnd->setPosition(p.x(), p.y(), false);
|
|
|
|
m_pEnd->disconnect();
|
|
|
|
} else if(index == 0) {
|
|
|
|
m_pStart->setPosition(p.x(), p.y(), false);
|
|
|
|
m_pStart->disconnect();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PolyLineConnector::moveLastPointTo(const KoPoint& p)
|
|
|
|
{
|
|
|
|
movePointTo(m_points.count() - 1, p);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PolyLineConnector::customDrag(KivioCustomDragData* data)
|
|
|
|
{
|
|
|
|
KoPoint pos(data->x, data->y);
|
|
|
|
setCustomIDPoint(data->id, pos, data->page);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PolyLineConnector::move(double xOffset, double yOffset)
|
|
|
|
{
|
|
|
|
for(unsigned int i = 0; i < m_points.count(); i++) {
|
|
|
|
movePoint(i, xOffset, yOffset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
double PolyLineConnector::x()
|
|
|
|
{
|
|
|
|
if(m_points.count() == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rect().x();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PolyLineConnector::setX(double newX)
|
|
|
|
{
|
|
|
|
double dx = newX - x();
|
|
|
|
move(dx, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
double PolyLineConnector::y()
|
|
|
|
{
|
|
|
|
if(m_points.count() == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rect().y();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PolyLineConnector::setY(double newY)
|
|
|
|
{
|
|
|
|
double dy = newY - y();
|
|
|
|
move(0, dy);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PolyLineConnector::checkForConnection(KivioConnectorPoint* cp, KivioPage* page)
|
|
|
|
{
|
|
|
|
if(cp->connectable()) {
|
|
|
|
KivioLayer* currentLayer = page->curLayer();
|
|
|
|
KivioLayer* layer = page->firstLayer();
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
while(layer && !found) {
|
|
|
|
if((layer != currentLayer) && (!layer->connectable() || !layer->visible())) {
|
|
|
|
layer = page->nextLayer();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(layer->connectPointToTarget(cp, 8.0f)) {
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
layer = page->nextLayer();
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!found) {
|
|
|
|
cp->disconnect();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PolyLineConnector::updateConnectorPoints(KivioConnectorPoint* cp, double /*oldX*/, double /*oldY*/)
|
|
|
|
{
|
|
|
|
if(cp == m_pStart) {
|
|
|
|
m_points[0] = m_pStart->position();
|
|
|
|
} else if(cp == m_pEnd) {
|
|
|
|
m_points[m_points.count() - 1] = m_pEnd->position();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
KoRect PolyLineConnector::rect()
|
|
|
|
{
|
|
|
|
KoPoint p = m_points.first();
|
|
|
|
KoPoint topLeft(p.x(), p.y()), bottomRight;
|
|
|
|
TQValueList<KoPoint>::iterator it;
|
|
|
|
TQValueList<KoPoint>::iterator itEnd = m_points.end();
|
|
|
|
|
|
|
|
for(it = m_points.begin(); it != itEnd; ++it) {
|
|
|
|
p = (*it);
|
|
|
|
topLeft.setX(TQMIN(p.x(), topLeft.x()));
|
|
|
|
topLeft.setY(TQMIN(p.y(), topLeft.y()));
|
|
|
|
bottomRight.setX(TQMAX(p.x(), bottomRight.x()));
|
|
|
|
bottomRight.setY(TQMAX(p.y(), bottomRight.y()));
|
|
|
|
}
|
|
|
|
|
|
|
|
KoRect rect;
|
|
|
|
rect.moveTopLeft(topLeft);
|
|
|
|
rect.setWidth(bottomRight.x() - topLeft.x());
|
|
|
|
rect.setHeight(bottomRight.y() - topLeft.y());
|
|
|
|
return rect;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PolyLineConnector::isInRect(const KoRect& rect)
|
|
|
|
{
|
|
|
|
TQValueList<KoPoint>::Iterator it;
|
|
|
|
bool retVal = true;
|
|
|
|
|
|
|
|
for(it = m_points.begin(); it != m_points.end(); ++it) {
|
|
|
|
retVal = retVal && rect.contains((*it));
|
|
|
|
}
|
|
|
|
|
|
|
|
return retVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PolyLineConnector::loadArrowHeads(const TQDomElement& e)
|
|
|
|
{
|
|
|
|
bool first = true;
|
|
|
|
TQDomNode node = e.firstChild();
|
|
|
|
|
|
|
|
while(!node.isNull()) {
|
|
|
|
if(node.nodeName() == "KivioArrowHead") {
|
|
|
|
if(first) {
|
|
|
|
m_startArrow->loadXML(node.toElement());
|
|
|
|
first = false;
|
|
|
|
} else {
|
|
|
|
m_endArrow->loadXML(node.toElement());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
node = node.nextSibling();
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQDomElement PolyLineConnector::saveArrowHeads(TQDomDocument& doc)
|
|
|
|
{
|
|
|
|
TQDomElement e = doc.createElement("KivioArrowHeads");
|
|
|
|
|
|
|
|
e.appendChild( m_startArrow->saveXML(doc) );
|
|
|
|
e.appendChild( m_endArrow->saveXML(doc) );
|
|
|
|
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PolyLineConnector::removePoint(unsigned int index)
|
|
|
|
{
|
|
|
|
if(index >= m_points.count()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_points.remove(m_points.at(index));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PolyLineConnector::removeLastPoint()
|
|
|
|
{
|
|
|
|
removePoint(m_points.count() - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PolyLineConnector::setCustomIDPoint(int customID, const KoPoint& point, KivioPage* page)
|
|
|
|
{
|
|
|
|
int index = customID - (kctCustom + 1);
|
|
|
|
|
|
|
|
if((index < 0) || index >= (int)m_points.count()) {
|
|
|
|
kdDebug(43000) << "PolyLineConnector::setCustomIDPoint: Index out of range! Index = " << index << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
movePointTo(index, point);
|
|
|
|
KivioConnectorPoint* cp;
|
|
|
|
|
|
|
|
if(index == 0) {
|
|
|
|
cp = m_pStart;
|
|
|
|
} else if(index == ((int)m_points.count() - 1)) {
|
|
|
|
cp = m_pEnd;
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
checkForConnection(cp, page);
|
|
|
|
}
|
|
|
|
|
|
|
|
KoPoint PolyLineConnector::customIDPoint(int customID)
|
|
|
|
{
|
|
|
|
int index = customID - (kctCustom + 1);
|
|
|
|
|
|
|
|
if((index < 0) || index >= (int)m_points.count()) {
|
|
|
|
kdDebug(43000) << "PolyLineConnector::customIDPoint: Index out of range! Index = " << index << endl;
|
|
|
|
return KoPoint();
|
|
|
|
}
|
|
|
|
|
|
|
|
return m_points[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|