|
|
|
/***************************************************************************
|
|
|
|
* Copyright (C) 2003-2005 by David Saxton *
|
|
|
|
* david@bluehaze.org *
|
|
|
|
* *
|
|
|
|
* 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. *
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
#include "cnitem.h"
|
|
|
|
#include "icndocument.h"
|
|
|
|
#include "connector.h"
|
|
|
|
#include "itemdocumentdata.h"
|
|
|
|
#include "node.h"
|
|
|
|
|
|
|
|
#include <kdebug.h>
|
|
|
|
|
|
|
|
#include <tqpainter.h>
|
|
|
|
|
|
|
|
Node::Node( ICNDocument *icnDocument, Node::node_type type, node_dir dir, const TQPoint &pos, TQString *id )
|
|
|
|
: TQObject(), TQCanvasPolygon( icnDocument->canvas() )
|
|
|
|
{
|
|
|
|
p_nodeGroup = 0l;
|
|
|
|
p_parentItem = 0L;
|
|
|
|
b_deleted = false;
|
|
|
|
m_dir = dir;
|
|
|
|
m_type = type;
|
|
|
|
p_icnDocument = icnDocument;
|
|
|
|
m_level = 0;
|
|
|
|
m_selectedColor = TQColor( 101, 134, 192 );
|
|
|
|
|
|
|
|
if (id)
|
|
|
|
{
|
|
|
|
m_id = *id;
|
|
|
|
if ( !p_icnDocument->registerUID(*id) )
|
|
|
|
kdError() << k_funcinfo << "Could not register id " << *id << endl;
|
|
|
|
}
|
|
|
|
else m_id = p_icnDocument->generateUID("node"+TQString::number(type));
|
|
|
|
|
|
|
|
initPoints();
|
|
|
|
move( pos.x(), pos.y() );
|
|
|
|
setBrush( TQt::black );
|
|
|
|
setPen( TQt::black );
|
|
|
|
show();
|
|
|
|
|
|
|
|
emit (moved(this));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node::~Node()
|
|
|
|
{
|
|
|
|
p_icnDocument->unregisterUID( id() );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int Node::rtti() const
|
|
|
|
{
|
|
|
|
return ICNDocument::RTTI::Node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Node::setLevel( const int level )
|
|
|
|
{
|
|
|
|
m_level = level;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Node::acceptInput() const
|
|
|
|
{
|
|
|
|
return type() != fp_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Node::acceptOutput() const
|
|
|
|
{
|
|
|
|
return type() != fp_in;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Node::setVisible( bool yes )
|
|
|
|
{
|
|
|
|
if ( isVisible() == yes )
|
|
|
|
return;
|
|
|
|
|
|
|
|
TQCanvasPolygon::setVisible(yes);
|
|
|
|
|
|
|
|
const ConnectorList::iterator inputEnd = m_inputConnectorList.end();
|
|
|
|
for ( ConnectorList::iterator it = m_inputConnectorList.begin(); it != inputEnd; ++it )
|
|
|
|
{
|
|
|
|
Connector *connector = *it;
|
|
|
|
if (connector)
|
|
|
|
{
|
|
|
|
if ( isVisible() )
|
|
|
|
connector->setVisible(true);
|
|
|
|
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Node *node = connector->startNode();
|
|
|
|
connector->setVisible( node && node->isVisible() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const ConnectorList::iterator outputEnd = m_outputConnectorList.end();
|
|
|
|
for ( ConnectorList::iterator it = m_outputConnectorList.begin(); it != outputEnd; ++it )
|
|
|
|
{
|
|
|
|
Connector *connector = *it;
|
|
|
|
if (connector)
|
|
|
|
{
|
|
|
|
if ( isVisible() )
|
|
|
|
connector->setVisible(true);
|
|
|
|
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Node *node = connector->endNode();
|
|
|
|
connector->setVisible( node && node->isVisible() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Node::isConnected( Node *node, NodeList *checkedNodes )
|
|
|
|
{
|
|
|
|
if ( this == node )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
bool firstNode = !checkedNodes;
|
|
|
|
if (firstNode)
|
|
|
|
checkedNodes = new NodeList();
|
|
|
|
|
|
|
|
else if ( checkedNodes->contains(this) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
checkedNodes->append(this);
|
|
|
|
|
|
|
|
const ConnectorList::const_iterator inputEnd = m_inputConnectorList.end();
|
|
|
|
for ( ConnectorList::const_iterator it = m_inputConnectorList.begin(); it != inputEnd; ++it )
|
|
|
|
{
|
|
|
|
Connector *connector = *it;
|
|
|
|
if (connector)
|
|
|
|
{
|
|
|
|
Node *startNode = connector->startNode();
|
|
|
|
if ( startNode && startNode->isConnected( node, checkedNodes ) ) {
|
|
|
|
if (firstNode) {
|
|
|
|
delete checkedNodes;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const ConnectorList::const_iterator outputEnd = m_outputConnectorList.end();
|
|
|
|
for ( ConnectorList::const_iterator it = m_outputConnectorList.begin(); it != outputEnd; ++it )
|
|
|
|
{
|
|
|
|
Connector *connector = *it;
|
|
|
|
if (connector)
|
|
|
|
{
|
|
|
|
Node *endNode = connector->endNode();
|
|
|
|
if ( endNode && endNode->isConnected( node, checkedNodes ) ) {
|
|
|
|
if (firstNode) {
|
|
|
|
delete checkedNodes;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (firstNode) {
|
|
|
|
delete checkedNodes;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Node::setOrientation( node_dir dir )
|
|
|
|
{
|
|
|
|
if ( m_dir == dir )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( dir != Node::dir_up &&
|
|
|
|
dir != Node::dir_right &&
|
|
|
|
dir != Node::dir_down &&
|
|
|
|
dir != Node::dir_left )
|
|
|
|
{
|
|
|
|
kdDebug() << "Node::setOrientation: Unknown node direction "<<dir<<endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_dir = dir;
|
|
|
|
initPoints();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Node::initPoints()
|
|
|
|
{
|
|
|
|
if ( type() == ec_junction )
|
|
|
|
{
|
|
|
|
setPoints( TQPointArray( TQRect( -4, -4, 8, 8 ) ) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( type() == fp_junction )
|
|
|
|
{
|
|
|
|
setPoints( TQPointArray( TQRect( -4, -4, 9, 9 ) ) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const int length = ( type() == ec_pin ) ? 8 : -8;
|
|
|
|
|
|
|
|
// Bounding rectangle, facing right
|
|
|
|
TQPointArray pa( TQRect( 0, -8, length, 16 ) );
|
|
|
|
|
|
|
|
double angle;
|
|
|
|
if ( m_dir == Node::dir_up ) angle = -90.;
|
|
|
|
else if ( m_dir == Node::dir_right ) angle = 0.;
|
|
|
|
else if ( m_dir == Node::dir_down ) angle = 90.;
|
|
|
|
else if ( m_dir == Node::dir_left ) angle = 180.;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
kdError() << "Node::initPoints: unknown m_dir = "<<m_dir<<endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQWMatrix m;
|
|
|
|
m.rotate(angle);
|
|
|
|
pa = m.map(pa);
|
|
|
|
setPoints(pa);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TQPoint Node::findConnectorDivergePoint( bool * found )
|
|
|
|
{
|
|
|
|
bool temp;
|
|
|
|
if (!found)
|
|
|
|
found = &temp;
|
|
|
|
*found = false;
|
|
|
|
|
|
|
|
if ( numCon( false, false ) != 2 )
|
|
|
|
return TQPoint(0,0);
|
|
|
|
|
|
|
|
TQPointList p1;
|
|
|
|
TQPointList p2;
|
|
|
|
|
|
|
|
int inSize = m_inputConnectorList.count();
|
|
|
|
|
|
|
|
const ConnectorList connectors = m_inputConnectorList + m_outputConnectorList;
|
|
|
|
const ConnectorList::const_iterator end = connectors.end();
|
|
|
|
bool gotP1 = false;
|
|
|
|
bool gotP2 = false;
|
|
|
|
int at = -1;
|
|
|
|
for ( ConnectorList::const_iterator it = connectors.begin(); it != end && !gotP2; ++it )
|
|
|
|
{
|
|
|
|
at++;
|
|
|
|
if ( !(*it) || !(*it)->canvas() )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (gotP1)
|
|
|
|
{
|
|
|
|
p2 = (*it)->connectorPoints( at < inSize );
|
|
|
|
gotP2 = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
p1 = (*it)->connectorPoints( at < inSize );
|
|
|
|
gotP1 = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( !gotP1 || !gotP2 )
|
|
|
|
return TQPoint(0,0);
|
|
|
|
|
|
|
|
unsigned maxLength = p1.size() > p2.size() ? p1.size() : p2.size();
|
|
|
|
|
|
|
|
for ( unsigned i = 1; i < maxLength; ++i )
|
|
|
|
{
|
|
|
|
if ( p1[i] != p2[i] )
|
|
|
|
{
|
|
|
|
*found = true;
|
|
|
|
return p1[i-1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return TQPoint(0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Node::setParentItem( CNItem *parentItem )
|
|
|
|
{
|
|
|
|
if (!parentItem)
|
|
|
|
{
|
|
|
|
kdError() << k_funcinfo << "no parent item" << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
p_parentItem = parentItem;
|
|
|
|
|
|
|
|
setLevel(p_parentItem->level());
|
|
|
|
|
|
|
|
connect( p_parentItem, TQT_SIGNAL(movedBy(double, double )), this, TQT_SLOT(moveBy(double, double)) );
|
|
|
|
connect( p_parentItem, TQT_SIGNAL(removed(Item*)), this, TQT_SLOT(removeNode(Item*)) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Node::removeNode()
|
|
|
|
{
|
|
|
|
if (b_deleted)
|
|
|
|
return;
|
|
|
|
b_deleted = true;
|
|
|
|
|
|
|
|
emit removed(this);
|
|
|
|
p_icnDocument->appendDeleteList(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Node::moveBy( double dx, double dy )
|
|
|
|
{
|
|
|
|
if ( dx == 0 && dy == 0 ) return;
|
|
|
|
TQCanvasPolygon::moveBy( dx, dy );
|
|
|
|
emit moved(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int Node::numCon( bool includeParentItem, bool includeHiddenConnectors ) const
|
|
|
|
{
|
|
|
|
unsigned count = 0;
|
|
|
|
|
|
|
|
const ConnectorList connectors[2] = { m_inputConnectorList, m_outputConnectorList };
|
|
|
|
|
|
|
|
for ( unsigned i = 0; i < 2; i++ )
|
|
|
|
{
|
|
|
|
ConnectorList::const_iterator end = connectors[i].end();
|
|
|
|
for ( ConnectorList::const_iterator it = connectors[i].begin(); it != end; ++it )
|
|
|
|
{
|
|
|
|
if ( *it && (includeHiddenConnectors || (*it)->canvas()) )
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( isChildNode() && includeParentItem )
|
|
|
|
count++;
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Node::addOutputConnector( Connector * const connector )
|
|
|
|
{
|
|
|
|
if ( type() == fp_in || !handleNewConnector(connector) )
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_outputConnectorList.append(connector);
|
|
|
|
|
|
|
|
if ( type() == fp_out || type() == fp_junction )
|
|
|
|
{
|
|
|
|
// We can only have one output connector, so remove the others. Note
|
|
|
|
// that this code has to come *after* adding the new output connector,
|
|
|
|
// as this node will delete itself if it's an fp_junction and there are
|
|
|
|
// no output connectors.
|
|
|
|
|
|
|
|
const ConnectorList connectors = m_outputConnectorList;
|
|
|
|
const ConnectorList::const_iterator end = connectors.end();
|
|
|
|
for ( ConnectorList::const_iterator it = connectors.begin(); it != end; ++it )
|
|
|
|
{
|
|
|
|
Connector * con = *it;
|
|
|
|
if ( con && con != connector )
|
|
|
|
con->removeConnector();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_outputConnectorList.remove((Connector*)0l);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Node::addInputConnector( Connector * const connector )
|
|
|
|
{
|
|
|
|
if ( type() == fp_out || !handleNewConnector(connector) )
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_inputConnectorList.append(connector);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Node::handleNewConnector( Connector * connector )
|
|
|
|
{
|
|
|
|
if (!connector)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if ( m_inputConnectorList.contains(connector) || m_outputConnectorList.contains(connector) )
|
|
|
|
{
|
|
|
|
kdWarning() << k_funcinfo << " Already have connector = " << connector << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
connect( this, TQT_SIGNAL(removed(Node*)), connector, TQT_SLOT(removeConnector(Node*)) );
|
|
|
|
connect( connector, TQT_SIGNAL(removed(Connector*)), this, TQT_SLOT(checkForRemoval(Connector*)) );
|
|
|
|
connect( connector, TQT_SIGNAL(selected(bool)), this, TQT_SLOT(setNodeSelected(bool)) );
|
|
|
|
|
|
|
|
if ( !isChildNode() )
|
|
|
|
p_icnDocument->slotRequestAssignNG();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Connector* Node::createInputConnector( Node * startNode )
|
|
|
|
{
|
|
|
|
if ( type() == fp_out || !startNode )
|
|
|
|
return 0l;
|
|
|
|
|
|
|
|
Connector *connector = new Connector( startNode, this, p_icnDocument );
|
|
|
|
addInputConnector(connector);
|
|
|
|
|
|
|
|
return connector;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Node::removeConnector( Connector *connector )
|
|
|
|
{
|
|
|
|
if (!connector)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ConnectorList::iterator it;
|
|
|
|
|
|
|
|
it = m_inputConnectorList.find(connector);
|
|
|
|
if ( it != m_inputConnectorList.end() )
|
|
|
|
{
|
|
|
|
(*it)->removeConnector();
|
|
|
|
(*it) = 0L;
|
|
|
|
}
|
|
|
|
|
|
|
|
it = m_outputConnectorList.find(connector);
|
|
|
|
if ( it != m_outputConnectorList.end() )
|
|
|
|
{
|
|
|
|
(*it)->removeConnector();
|
|
|
|
(*it) = 0L;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Node::checkForRemoval( Connector *connector )
|
|
|
|
{
|
|
|
|
removeConnector(connector);
|
|
|
|
setNodeSelected(false);
|
|
|
|
|
|
|
|
removeNullConnectors();
|
|
|
|
|
|
|
|
if (!p_parentItem)
|
|
|
|
{
|
|
|
|
int conCount = m_inputConnectorList.count() + m_outputConnectorList.count();
|
|
|
|
if ( conCount < 2 )
|
|
|
|
removeNode();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( type() == Node::fp_junction && m_outputConnectorList.isEmpty() )
|
|
|
|
removeNode();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Node::removeNullConnectors()
|
|
|
|
{
|
|
|
|
m_inputConnectorList.remove((Connector*)0L);
|
|
|
|
m_outputConnectorList.remove((Connector*)0L);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NodeData Node::nodeData() const
|
|
|
|
{
|
|
|
|
NodeData data;
|
|
|
|
data.x = x();
|
|
|
|
data.y = y();
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Node::setNodeSelected( bool yes )
|
|
|
|
{
|
|
|
|
if ( isSelected() == yes )
|
|
|
|
return;
|
|
|
|
|
|
|
|
TQCanvasItem::setSelected(yes);
|
|
|
|
|
|
|
|
setPen( yes ? m_selectedColor : TQt::black );
|
|
|
|
setBrush( yes ? m_selectedColor : TQt::black );
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "node.moc"
|