You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/kivio/kiviopart/kiviosdk/kivio_1d_stencil.cpp

1012 lines
22 KiB

/*
* Kivio - Visual Modelling and Flowcharting
* Copyright (C) 2000-2001 theKompany.com & Dave Marotti
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/*
* Implementing a connector involves deriving from this class.
*
* You need to implement the following routines:
* setStartPoint
* setEndPoint
* checkForCollision
* duplicate
* paint
* paintOutline
* saveXML
* loadXML
*
* Save and Load should call saveConnectors, loadConnectors, saveProperties, and
* loadProperties. These are helper functions which take care of the common
* acts of saving/loading connectors and colors/line-styles, etc...
*
*/
#include "kivio_1d_stencil.h"
#include "kivio_arrowhead.h"
#include "kivio_common.h"
#include "kivio_connector_point.h"
#include "kivio_custom_drag_data.h"
#include "kivio_fill_style.h"
#include "kivio_intra_stencil_data.h"
#include "kivio_layer.h"
#include "kivio_line_style.h"
#include "kivio_page.h"
#include "kivio_painter.h"
#include "kivio_point.h"
#include "kivio_stencil.h"
#include "kivio_stencil_spawner.h"
#include "kivio_stencil_spawner_info.h"
#include "kivio_stencil_spawner_set.h"
#include "kivio_text_style.h"
#include "kivio_connector_target.h"
#include <kdebug.h>
#include <math.h>
#include <KoZoomHandler.h>
#include <tqbitmap.h>
#include <tqpainter.h>
/**
* Default constructor.
*
* This will allocate a new fill style object, a list for
* connection points, and set some default values.
*/
Kivio1DStencil::Kivio1DStencil()
: KivioStencil()
{
m_pFillStyle = new KivioFillStyle();
m_pLineStyle = new KivioLineStyle();
m_pTextStyle = new KivioTextStyle();
m_pConnectorPoints = new TQPtrList<KivioConnectorPoint>;
m_pConnectorPoints->setAutoDelete(true);
m_pStart = new KivioConnectorPoint(this, true);
m_pStart->setPosition(72.0f, 18.0f, false);
m_pEnd = new KivioConnectorPoint(this, true);
m_pEnd->setPosition(0.0f, 18.0f, false);
m_pLeft = new KivioConnectorPoint(this, false);
m_pLeft->setPosition(36.0f, 36.0f, false);
m_pRight = new KivioConnectorPoint(this, false);
m_pRight->setPosition(36.0f, 0.0f, false);
m_pTextConn = new KivioConnectorPoint(this, false);
m_pTextConn->setPosition(36.0f, 18.0f, false);
m_connectorWidth = 36.0f;
m_needsWidth = true;
m_needsText = false;
m_pConnectorPoints->append( m_pStart );
m_pConnectorPoints->append( m_pEnd );
m_pConnectorPoints->append( m_pLeft );
m_pConnectorPoints->append( m_pRight );
m_pConnectorPoints->append( m_pTextConn );
}
/**
* Destructor
*/
Kivio1DStencil::~Kivio1DStencil()
{
delete m_pFillStyle;
delete m_pLineStyle;
delete m_pTextStyle;
delete m_pConnectorPoints;
}
void Kivio1DStencil::setFGColor( TQColor c )
{
m_pLineStyle->setColor(c);
}
TQColor Kivio1DStencil::fgColor()
{
return m_pLineStyle->color();
}
void Kivio1DStencil::setLineWidth( double f )
{
m_pLineStyle->setWidth(f);
}
double Kivio1DStencil::lineWidth()
{
return m_pLineStyle->width();
}
void Kivio1DStencil::setLinePattern(int p)
{
m_pLineStyle->setStyle(p);
}
int Kivio1DStencil::linePattern()
{
return m_pLineStyle->style();
}
void Kivio1DStencil::setFillPattern(int p)
{
m_pFillStyle->setBrushStyle(static_cast<Qt::BrushStyle>(p));
}
int Kivio1DStencil::fillPattern()
{
return m_pFillStyle->brushStyle();
}
void Kivio1DStencil::setBGColor( TQColor c )
{
m_pFillStyle->setColor(c);
}
TQColor Kivio1DStencil::bgColor()
{
return m_pFillStyle->color();
}
/////////////////////////////////
// Position functions
/////////////////////////////////
void Kivio1DStencil::setX( double x )
{
double dx = x - m_x;
KivioConnectorPoint *p = m_pConnectorPoints->first();
while( p )
{
p->disconnect();
p->setX( p->x() + dx, false );
p = m_pConnectorPoints->next();
}
m_x = x;
}
void Kivio1DStencil::setY( double y )
{
double dy = y - m_y;
KivioConnectorPoint *p = m_pConnectorPoints->first();
while( p )
{
p->disconnect();
p->setY( p->y() + dy, false );
p = m_pConnectorPoints->next();
}
m_y = y;
}
void Kivio1DStencil::setPosition( double x, double y )
{
double dx = x - m_x;
double dy = y - m_y;
m_x += dx;
m_y += dy;
KivioConnectorPoint *p = m_pConnectorPoints->first();
while( p )
{
p->setPosition( p->x()+dx, p->y()+dy, false );
p->disconnect();
p = m_pConnectorPoints->next();
}
m_x = x;
m_y = y;
}
/////////////////////////////
// Connection tool functions
/////////////////////////////
void Kivio1DStencil::setStartPoint( double x, double y )
{
double oldX = m_pStart->x();
double oldY = m_pStart->y();
m_pStart->setPosition(x, y, false);
m_pStart->disconnect();
updateConnectorPoints(m_pStart, oldX, oldY);
}
void Kivio1DStencil::setEndPoint( double x, double y )
{
double oldX = m_pEnd->x();
double oldY = m_pEnd->y();
m_pEnd->setPosition(x, y, false);
m_pEnd->disconnect();
updateConnectorPoints(m_pEnd, oldX, oldY);
}
void Kivio1DStencil::updateConnectorPoints( KivioConnectorPoint *p, double /*oldX*/, double /*oldY*/ )
{
// If p is the start or end, we need to adjust the width connectors
if( p == m_pStart || p == m_pEnd )
{
double vx = m_pStart->x() - m_pEnd->x();
double vy = m_pStart->y() - m_pEnd->y();
double len = sqrt( vx*vx + vy*vy );
double midX = (m_pStart->x() + m_pEnd->x())/2.0f;
double midY = (m_pStart->y() + m_pEnd->y())/2.0f;
vx /= len;
vy /= len;
double d = m_connectorWidth/2.0f;
m_pLeft->setPosition( midX + d*vy, midY + d*(-vx), false );
m_pRight->setPosition( midX + d*(-vy), midY + d*vx, false );
}
updateGeometry();
}
void Kivio1DStencil::paint( KivioIntraStencilData * )
{
/* Derived class must implement this */
}
void Kivio1DStencil::paintOutline( KivioIntraStencilData *pData )
{
/* Derived class should implement this */
paint( pData );
}
void Kivio1DStencil::paintConnectorTargets( KivioIntraStencilData * )
{
}
void Kivio1DStencil::paintSelectionHandles( KivioIntraStencilData *pData )
{
KivioPainter *painter = pData->painter;
double x1, y1;
int flag;
KoZoomHandler* zoomHandler = pData->zoomHandler;
KivioConnectorPoint *p = m_pConnectorPoints->first();
while( p )
{
// If we don't need width connectors and we are on a width connector,
// ignore it.
x1 = zoomHandler->zoomItX(p->x());
y1 = zoomHandler->zoomItY(p->y());
flag = (p->target()) ? KivioPainter::cpfConnected : 0;
if( p==m_pTextConn )
{
if( m_needsText==true )
{
painter->drawHandle( x1, y1, 0 );
}
}
else if( p==m_pLeft || p==m_pRight )
{
if( m_needsWidth==true )
{
painter->drawHandle( x1, y1, 0 );
}
}
else if( p==m_pStart )
{
painter->drawHandle( x1, y1, KivioPainter::cpfStart | flag );
}
else if( p==m_pEnd )
{
painter->drawHandle( x1, y1, KivioPainter::cpfEnd | flag );
}
else
{
if( p->connectable() ) {
painter->drawHandle( x1, y1, KivioPainter::cpfConnectable | flag );
} else {
painter->drawHandle( x1, y1, flag );
}
}
p = m_pConnectorPoints->next();
}
}
///////////////////////////////
// Collision detection
///////////////////////////////
KivioCollisionType Kivio1DStencil::checkForCollision( KoPoint *, double )
{
/* Derived class must implement this */
return kctNone;
}
/////////////////////////////////
// Custom dragging
/////////////////////////////////
/**
* Custom drag the connector points.
*
* The default action of this function is to locate the point
* in the connector list by the id and then drag it around.
* Then attempt to snap it to another stencil. Otherwise
* disconnect it.
*/
void Kivio1DStencil::customDrag( KivioCustomDragData *pData )
{
setCustomIDPoint(pData->id, KoPoint(pData->x, pData->y), pData->page);
}
/**
* Sets the position and dimensions of this stencil based on its connection points.
*/
void Kivio1DStencil::updateGeometry()
{
double minX, minY, maxX, maxY;
minX = 1000000000000.0f;
minY = minX;
maxX = -100000000000.0f;
maxY = maxX;
KivioConnectorPoint *p;
p = m_pConnectorPoints->first();
while( p )
{
if( p->x() < minX )
minX = p->x();
if( p->x() > maxX )
maxX = p->x();
if( p->y() < minY )
minY = p->y();
if( p->y() > maxY )
maxY = p->y();
p = m_pConnectorPoints->next();
}
m_x = minX;
m_y = minY;
m_w = maxX - minX + 1.0f;
m_h = maxY - minY + 1.0f;
}
// file i/o routines
bool Kivio1DStencil::loadXML( const TQDomElement &e )
{
TQDomNode node;
TQString name;
node = e.firstChild();
while( !node.isNull() )
{
name = node.nodeName();
if( name == "KivioStencilProperties" )
{
loadProperties(node.toElement() );
}
node = node.nextSibling();
}
updateGeometry();
return true;
}
TQDomElement Kivio1DStencil::createRootElement( TQDomDocument &doc )
{
TQDomElement e = doc.createElement("KivioPluginStencil");
XmlWriteString( e, "id", m_pSpawner->info()->id() );
XmlWriteString( e, "setId", m_pSpawner->set()->id() );
return e;
}
TQDomElement Kivio1DStencil::saveXML( TQDomDocument &doc )
{
TQDomElement e = createRootElement(doc);
e.appendChild( saveProperties(doc) );
return e;
}
KivioStencil *Kivio1DStencil::duplicate()
{
/* Derived class must implement this function */
return NULL;
}
bool Kivio1DStencil::boolAllTrue( bool *boolArray, int count )
{
int i;
for( i=0; i<count; i++ )
{
if( boolArray[i]==false )
return false;
}
return true;
}
bool Kivio1DStencil::boolContainsFalse( bool *boolArray, int count )
{
int i;
for( i=0; i<count; i++ )
{
if( boolArray[i]==false )
return true;
}
return false;
}
void Kivio1DStencil::searchForConnections( KivioPage *pPage )
{
bool *done = new bool[ m_pConnectorPoints->count()];
unsigned int i;
for(i = 0; i < m_pConnectorPoints->count(); i++) {
done[i] = false;
}
KivioConnectorPoint *p;
i = 0;
p = m_pConnectorPoints->first();
while( p )
{
if( p->targetId() == -1 ) {
done[i] = true;
}
i++;
p = m_pConnectorPoints->next();
}
// No connections? BaiL!
if( boolAllTrue( done, m_pConnectorPoints->count() ) )
{
delete [] done;
return;
}
KivioLayer *pLayer = pPage->firstLayer();
while( pLayer && ( boolContainsFalse(done, m_pConnectorPoints->count()) ) )
{
KivioStencil *pStencil = pLayer->firstStencil();
while( pStencil && ( boolContainsFalse(done, m_pConnectorPoints->count()) ) )
{
// No connecting to ourself!
if((pStencil != this))
{
// Iterate through all connectors attempting to connect it to the stencil.
// If it connects, mark it as done
i=0;
p = m_pConnectorPoints->first();
while( p )
{
if( !done[i] && p->targetId() != -1 )
{
if(pStencil->connectToTarget( p, p->targetId()))
{
done[i] = true;
}
}
i++;
p = m_pConnectorPoints->next();
}
}
pStencil = pLayer->nextStencil();
}
pLayer = pPage->nextLayer();
}
delete [] done;
}
void Kivio1DStencil::searchForConnections( KivioPage *pPage, double threshold )
{
bool *done = new bool[ m_pConnectorPoints->count()];
int i;
for( i=0; i<(int)m_pConnectorPoints->count(); i++ ) {
done[i] = false;
}
KivioConnectorPoint *p;
i = 0;
p = m_pConnectorPoints->first();
while( p )
{
if(p->target() != 0L) {
done[i] = true;
}
i++;
p = m_pConnectorPoints->next();
}
// No connections? BaiL!
if( boolAllTrue( done, m_pConnectorPoints->count() ) )
{
delete [] done;
return;
}
KivioLayer *pLayer = pPage->firstLayer();
while( pLayer && ( boolContainsFalse(done, m_pConnectorPoints->count()) ) )
{
KivioStencil *pStencil = pLayer->firstStencil();
while( pStencil && ( boolContainsFalse(done, m_pConnectorPoints->count()) ) )
{
// No connecting to ourself!
if( pStencil != this )
{
// Iterate through all connectors attempting to connect it to the stencil.
// If it connects, mark it as done
i = 0;
p = m_pConnectorPoints->first();
while( p )
{
if( !done[i] && p->target() == 0 )
{
if( pStencil->connectToTarget( p, threshold ) )
{
done[i] = true;
}
}
i++;
p = m_pConnectorPoints->next();
}
}
pStencil = pLayer->nextStencil();
}
pLayer = pPage->nextLayer();
}
delete [] done;
}
//////////////////////
// resize handles
//////////////////////
int Kivio1DStencil::resizeHandlePositions()
{
return (int)krhpNone;
}
TQDomElement Kivio1DStencil::saveConnectors( TQDomDocument &doc )
{
TQDomElement eConns = doc.createElement("KivioConnectorList");
KivioConnectorPoint *p;
p = m_pConnectorPoints->first();
while( p )
{
eConns.appendChild( p->saveXML(doc) );
p = m_pConnectorPoints->next();
}
return eConns;
}
bool Kivio1DStencil::loadConnectors( const TQDomElement &e )
{
m_pConnectorPoints->clear();
KivioConnectorPoint *p;
TQDomNode node = e.firstChild();
TQDomElement e2;
TQString name;
while( !node.isNull() )
{
e2 = node.toElement();
name = e2.nodeName();
if( name == "KivioConnectorPoint" )
{
p = new KivioConnectorPoint();
p->setStencil(this);
p->loadXML( e2 );
m_pConnectorPoints->append( p );
p = NULL;
}
node = node.nextSibling();
}
// Set the pointers to the start,end,left,right points
m_pStart = m_pConnectorPoints->first();
m_pEnd = m_pConnectorPoints->next();
m_pLeft = m_pConnectorPoints->next();
m_pRight = m_pConnectorPoints->next();
m_pTextConn = m_pConnectorPoints->next();
// Hopefully this will help with backwards compatibility
if( m_pStart == NULL ) {
m_pStart = new KivioConnectorPoint(this, true);
}
if( m_pEnd == NULL ) {
m_pEnd = new KivioConnectorPoint(this, true);
}
if( m_pLeft == NULL ) {
m_pLeft = new KivioConnectorPoint(this, false);
}
if( m_pRight == NULL ) {
m_pRight = new KivioConnectorPoint(this, false);
}
if( m_pTextConn == NULL ) {
m_pTextConn = new KivioConnectorPoint(this, false);
}
return true;
}
TQDomElement Kivio1DStencil::saveProperties( TQDomDocument &doc )
{
TQDomElement propE = doc.createElement("KivioStencilProperties");
TQDomElement connE = doc.createElement("Kivio1DProperties");
XmlWriteFloat( connE, "connectorWidth", m_connectorWidth );
XmlWriteInt( connE, "needsWidth", m_needsWidth );
propE.appendChild( connE );
propE.appendChild( m_pLineStyle->saveXML( doc ) );
propE.appendChild( m_pFillStyle->saveXML( doc ) );
propE.appendChild( m_pTextStyle->saveXML( doc ) );
propE.appendChild( saveConnectors(doc) );
TQDomElement customE = doc.createElement("CustomData");
if( saveCustom( customE, doc )==true )
{
propE.appendChild( customE );
}
return propE;
}
bool Kivio1DStencil::loadProperties( const TQDomElement &e )
{
TQDomNode node;
TQDomElement nodeE;
TQString nodeName;
node = e.firstChild();
while( !node.isNull() )
{
nodeE = node.toElement();
nodeName = node.nodeName();
if( nodeName == "KivioFillStyle" )
{
m_pFillStyle->loadXML( nodeE );
}
else if( nodeName == "KivioLineStyle" )
{
m_pLineStyle->loadXML( nodeE );
}
else if( nodeName == "KivioTextStyle" )
{
m_pTextStyle->loadXML( nodeE );
}
else if( nodeName == "KivioConnectorList" )
{
loadConnectors( nodeE );
}
else if( nodeName == "Kivio1DProperties" )
{
m_needsWidth = (bool)XmlReadInt( nodeE, "needsWidth", (int)true );
m_connectorWidth = XmlReadFloat( nodeE, "connectorWidth", 36.0f );
}
else if( nodeName == "CustomData" )
{
loadCustom( nodeE );
}
node = node.nextSibling();
}
return true;
}
bool Kivio1DStencil::loadCustom( const TQDomElement & )
{
return true;
}
bool Kivio1DStencil::saveCustom( TQDomElement &, TQDomDocument & )
{
return false;
}
void Kivio1DStencil::copyBasicInto( Kivio1DStencil *pStencil )
{
KivioConnectorPoint *pSrc, *pTg;
// Copy the spawner
pStencil->setSpawner( m_pSpawner );
// Copy the connector points
pSrc = m_pConnectorPoints->first();
pTg = pStencil->m_pConnectorPoints->first();
while( pSrc && pTg )
{
pTg->setPosition( pSrc->x(), pSrc->y(), false );
pSrc = m_pConnectorPoints->next();
pTg = pStencil->m_pConnectorPoints->next();
}
// Copy the dimensions
pStencil->m_x = m_x;
pStencil->m_y = m_y;
pStencil->m_w = m_w;
pStencil->m_h = m_h;
// Copy over the width info
pStencil->m_connectorWidth = m_connectorWidth;
pStencil->m_needsWidth = m_needsWidth;
// Copy over the styles
m_pFillStyle->copyInto( pStencil->m_pFillStyle );
m_pLineStyle->copyInto( pStencil->m_pLineStyle );
m_pTextStyle->copyInto( pStencil->m_pTextStyle );
// Copy over the protection
*(pStencil->m_pProtection) = *m_pProtection;
*(pStencil->m_pCanProtect) = *m_pCanProtect;
}
void Kivio1DStencil::drawText( KivioIntraStencilData *pData )
{
if(m_pTextStyle->text().isEmpty()) {
return;
}
KoZoomHandler* zoomHandler = pData->zoomHandler;
KivioPainter *painter = pData->painter;
int _x, _y, _w, _h;
_x = zoomHandler->zoomItX(m_pTextConn->x());
_y = zoomHandler->zoomItY(m_pTextConn->y());
_w = 10000000;
_h = 10000000;
TQFont f = m_pTextStyle->font();
int tf = m_pTextStyle->hTextAlign() | m_pTextStyle->vTextAlign();
f.setPointSizeFloat(f.pointSizeFloat() * (((float)zoomHandler->zoom()) / 100.0));
painter->setFont(f);
TQRect boundRect = painter->boundingRect( _x, _y, _w, _h, tf, m_pTextStyle->text() );
TQPixmap pix(boundRect.width(), boundRect.height());
pix.fill();
TQPainter p(&pix);
p.setPen(m_pTextStyle->color());
p.setFont(f);
p.drawText( 0, 0, boundRect.width(), boundRect.height(), tf, m_pTextStyle->text() );
TQBitmap mask;
mask = pix;
pix.setMask(mask);
painter->drawPixmap(_x, _y, pix);
}
bool Kivio1DStencil::connected()
{
KivioConnectorPoint *p;
p = m_pConnectorPoints->first();
while( p )
{
if(p->target() != 0) {
return true;
}
p = m_pConnectorPoints->next();
}
return false;
}
void Kivio1DStencil::disconnectFromTargets()
{
KivioConnectorPoint *p;
p = m_pConnectorPoints->first();
while( p )
{
p->disconnect(true);
p = m_pConnectorPoints->next();
}
}
KivioLineStyle Kivio1DStencil::lineStyle()
{
return *m_pLineStyle;
}
void Kivio1DStencil::setLineStyle(KivioLineStyle ls)
{
ls.copyInto(m_pLineStyle);
}
void Kivio1DStencil::setCustomIDPoint(int customID, const KoPoint& point, KivioPage* page)
{
double oldX, oldY;
KivioConnectorPoint *p;
// Locate the point specified by customID
p = m_pConnectorPoints->at( customID - (kctCustom+1));
if( !p )
{
kdDebug(43000) << "Kivio1DStencil::customDrag() - KivioConnectorPoint customID: " << (customID - (kctCustom+1)) << " not found\n" << endl;
return;
}
oldX = p->x();
oldY = p->y();
p->setPosition(point.x(),point.y(),true);
if( p->connectable()==true )
{
// Attempt a snap....
KivioLayer *pCurLayer = page->curLayer();
KivioLayer *pLayer = page->firstLayer(); //page->curLayer();
bool foundConnection = false;
while( pLayer && !foundConnection )
{
// To be connected to, a layer must be visible and connectable
if( pLayer!=pCurLayer )
{
if( pLayer->connectable()==false || pLayer->visible()==false )
{
pLayer = page->nextLayer();
continue;
}
}
// Tell the layer to search for a target
if( pLayer->connectPointToTarget( p, 8.0f ) )
{
foundConnection = true;
}
pLayer = page->nextLayer();
}
if( foundConnection == false )
{
p->disconnect();
}
}
// If it is a start/end point, then make a request to update the connectors (must be implemented by stencil developer)
if( customID == kctCustom+1 || customID == kctCustom+2 )
{
// If it's the end connector, then update the text point
if( p==m_pEnd && m_needsText==true )
{
m_pTextConn->setPosition( m_pTextConn->x() + (m_pEnd->x() - oldX),
m_pTextConn->y() + (m_pEnd->y() - oldY), false );
}
updateConnectorPoints(p, oldX, oldY);
}
// If it is one of the width handles, then fix the width and update the opposite point
// only if the stencils 'needs width' connectors
else if( (customID == kctCustom+3 || customID == kctCustom+4) && (m_needsWidth==true) )
{
double vx = m_pStart->x() - m_pEnd->x();
double vy = m_pStart->y() - m_pEnd->y();
double len = sqrt( vx*vx + vy*vy );
double midX = (m_pStart->x() + m_pEnd->x())/2.0f;
double midY = (m_pStart->y() + m_pEnd->y())/2.0f;
vx /= len;
vy /= len;
double d = shortestDistance( m_pStart, m_pEnd, (customID==kctCustom+3) ? m_pLeft : m_pRight );
m_pLeft->setPosition( midX + d*vy, midY + d*(-vx), false );
m_pRight->setPosition( midX + d*(-vy), midY + d*vx, false );
m_connectorWidth = d*2.0f;
updateConnectorPoints(p, oldX, oldY);
return;
}
// Text handle
else if( customID == kctCustom+5 )
{
updateConnectorPoints(p, oldX, oldY);
}
}
KoPoint Kivio1DStencil::customIDPoint(int customID)
{
KivioConnectorPoint *p;
// Locate the point specified by customID
p = m_pConnectorPoints->at( customID - (kctCustom+1));
return p->position();
}