|
|
|
/***************************************************************************
|
|
|
|
* Copyright (C) 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 "canvasmanipulator.h"
|
|
|
|
#include "component.h"
|
|
|
|
#include "connector.h"
|
|
|
|
#include "conrouter.h"
|
|
|
|
#include "cnitemgroup.h"
|
|
|
|
#include "ecnode.h"
|
|
|
|
#include "flowcontainer.h"
|
|
|
|
#include "fpnode.h"
|
|
|
|
#include "icndocument.h"
|
|
|
|
#include "icnview.h"
|
|
|
|
#include "itemdocumentdata.h"
|
|
|
|
#include "itemlibrary.h"
|
|
|
|
#include "ktechlab.h"
|
|
|
|
#include "nodegroup.h"
|
|
|
|
|
|
|
|
#include <kapplication.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <clipboard.h>
|
|
|
|
#include <tqtimer.h>
|
|
|
|
|
|
|
|
ICNDocument::ICNDocument( const TQString &caption, KTechlab *ktechlab, const char *name )
|
|
|
|
: ItemDocument( caption, ktechlab, name ),
|
|
|
|
m_cells(0l)
|
|
|
|
{
|
|
|
|
m_canvas->retune(48);
|
|
|
|
m_selectList = new CNItemGroup(this);
|
|
|
|
|
|
|
|
createCellMap();
|
|
|
|
|
|
|
|
m_cmManager->addManipulatorInfo( CMItemMove::manipulatorInfo() );
|
|
|
|
m_cmManager->addManipulatorInfo( CMAutoConnector::manipulatorInfo() );
|
|
|
|
m_cmManager->addManipulatorInfo( CMManualConnector::manipulatorInfo() );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ICNDocument::~ICNDocument()
|
|
|
|
{
|
|
|
|
m_bDeleted = true;
|
|
|
|
|
|
|
|
// Go to hell, TQCanvas. I'm in charge of what gets deleted.
|
|
|
|
TQCanvasItemList all = m_canvas->allItems();
|
|
|
|
const TQCanvasItemList::Iterator end = all.end();
|
|
|
|
for ( TQCanvasItemList::Iterator it= all.begin(); it != end; ++it )
|
|
|
|
(*it)->setCanvas(0l);
|
|
|
|
|
|
|
|
// Remove all items from the canvas
|
|
|
|
selectAll();
|
|
|
|
deleteSelection();
|
|
|
|
|
|
|
|
// Delete anything that got through the above couple of lines
|
|
|
|
ConnectorList connectorsToDelete = m_connectorList;
|
|
|
|
const ConnectorList::iterator connectorListEnd = connectorsToDelete.end();
|
|
|
|
for ( ConnectorList::iterator it = connectorsToDelete.begin(); it != connectorListEnd; ++it )
|
|
|
|
delete *it;
|
|
|
|
|
|
|
|
NodeList nodesToDelete = m_nodeList;
|
|
|
|
const NodeList::iterator nodeListEnd = nodesToDelete.end();
|
|
|
|
for ( NodeList::iterator it = nodesToDelete.begin(); it != nodeListEnd; ++it )
|
|
|
|
delete *it;
|
|
|
|
|
|
|
|
GuardedNodeGroupList ngToDelete = m_nodeGroupList;
|
|
|
|
const GuardedNodeGroupList::iterator nglEnd = ngToDelete.end();
|
|
|
|
for ( GuardedNodeGroupList::iterator it = ngToDelete.begin(); it != nglEnd; ++it )
|
|
|
|
delete *it;
|
|
|
|
|
|
|
|
delete m_cells;
|
|
|
|
m_cells = 0l;
|
|
|
|
delete m_selectList;
|
|
|
|
m_selectList = 0l;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
View *ICNDocument::createView( ViewContainer *viewContainer, uint viewAreaId, const char *name )
|
|
|
|
{
|
|
|
|
ICNView *icnView = new ICNView( this, viewContainer, viewAreaId, name );
|
|
|
|
handleNewView(icnView);
|
|
|
|
return icnView;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ItemGroup* ICNDocument::selectList() const
|
|
|
|
{
|
|
|
|
return m_selectList;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ICNDocument::fillContextMenu( const TQPoint &pos )
|
|
|
|
{
|
|
|
|
ItemDocument::fillContextMenu(pos);
|
|
|
|
slotInitItemActions( dynamic_cast<CNItem*>(m_selectList->activeItem()) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CNItem* ICNDocument::cnItemWithID( const TQString &id )
|
|
|
|
{
|
|
|
|
return dynamic_cast<CNItem*>(itemWithID(id));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Node *ICNDocument::nodeWithID( const TQString &id )
|
|
|
|
{
|
|
|
|
const NodeList::iterator end = m_nodeList.end();
|
|
|
|
for ( NodeList::iterator it = m_nodeList.begin(); it != end; ++it )
|
|
|
|
{
|
|
|
|
if ( (*it)->id() == id )
|
|
|
|
return *it;
|
|
|
|
}
|
|
|
|
return 0L;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Connector *ICNDocument::connectorWithID( const TQString &id )
|
|
|
|
{
|
|
|
|
const ConnectorList::iterator end = m_connectorList.end();
|
|
|
|
for ( ConnectorList::iterator it = m_connectorList.begin(); it != end; ++it )
|
|
|
|
{
|
|
|
|
if ( (*it)->id() == id )
|
|
|
|
return *it;
|
|
|
|
}
|
|
|
|
return 0L;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FlowContainer *ICNDocument::flowContainer( const TQPoint &pos )
|
|
|
|
{
|
|
|
|
TQCanvasItemList collisions = m_canvas->collisions(pos);
|
|
|
|
FlowContainer *flowContainer = 0l;
|
|
|
|
int currentLevel = -1;
|
|
|
|
const TQCanvasItemList::iterator end = collisions.end();
|
|
|
|
for ( TQCanvasItemList::iterator it = collisions.begin(); it != end; ++it )
|
|
|
|
{
|
|
|
|
if ( FlowContainer *container = dynamic_cast<FlowContainer*>(*it) )
|
|
|
|
{
|
|
|
|
if ( container->level() > currentLevel && !m_selectList->contains(container) )
|
|
|
|
{
|
|
|
|
currentLevel = container->level();
|
|
|
|
flowContainer = container;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return flowContainer;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ICNDocument::canConnect( TQCanvasItem *qcanvasItem1, TQCanvasItem *qcanvasItem2 ) const
|
|
|
|
{
|
|
|
|
// Rough outline of what can and can't connect:
|
|
|
|
// * At most three connectors to a node
|
|
|
|
// * Can't have connectors going between different levels (e.g. can't have
|
|
|
|
// a connector coming outside a FlowContainer from inside).
|
|
|
|
// * Can't have more than one route between any two nodes
|
|
|
|
// * In all connections between nodes, must have at least one input and one
|
|
|
|
// output node at the ends.
|
|
|
|
|
|
|
|
Node *startNode = dynamic_cast<Node*>(qcanvasItem1);
|
|
|
|
Node *endNode = dynamic_cast<Node*>(qcanvasItem2);
|
|
|
|
|
|
|
|
if ( (startNode && startNode->numCon( true, false ) > 2) || (endNode && endNode->numCon( true, false ) > 2) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
Connector *startConnector = dynamic_cast<Connector*>(qcanvasItem1);
|
|
|
|
Connector *endConnector = dynamic_cast<Connector*>(qcanvasItem2);
|
|
|
|
|
|
|
|
// Can't have T- or I- junction in PinMapEditor document
|
|
|
|
if ( type() == Document::dt_pinMapEditor && (startConnector || endConnector) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Can't have I-junction in flowcode document
|
|
|
|
if ( type() == Document::dt_flowcode && startConnector && endConnector )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
//BEGIN Change connectors to nodes
|
|
|
|
Node * startNode1 = 0l;
|
|
|
|
Node * startNode2 = 0l;
|
|
|
|
if (startConnector)
|
|
|
|
{
|
|
|
|
startNode1 = startConnector->startNode();
|
|
|
|
startNode2 = startConnector->endNode();
|
|
|
|
|
|
|
|
if ( !startNode1 || !startNode2 )
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if (!startNode)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Node * endNode1 = 0l;
|
|
|
|
Node * endNode2 = 0l;
|
|
|
|
if (endConnector)
|
|
|
|
{
|
|
|
|
endNode1 = endConnector->startNode();
|
|
|
|
endNode2 = endConnector->endNode();
|
|
|
|
|
|
|
|
if ( !endNode1 || !endNode2 )
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if ( !endNode )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Node * start[3];
|
|
|
|
start[0] = startNode;
|
|
|
|
start[1] = startNode1;
|
|
|
|
start[2] = startNode2;
|
|
|
|
|
|
|
|
Node * end[3];
|
|
|
|
end[0] = endNode;
|
|
|
|
end[1] = endNode1;
|
|
|
|
end[2] = endNode2;
|
|
|
|
//END Change connectors to nodes
|
|
|
|
|
|
|
|
|
|
|
|
//BEGIN Check nodes aren't already connected
|
|
|
|
for ( unsigned i = 0; i < 3; i++ )
|
|
|
|
{
|
|
|
|
for ( unsigned j = 0; j < 3; j++ )
|
|
|
|
{
|
|
|
|
if ( start[i] && end[j] && start[i]->isConnected(end[j]) )
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//END Check nodes aren't already connected together
|
|
|
|
|
|
|
|
|
|
|
|
//BEGIN Check we have appropriate input and output allowance
|
|
|
|
if ( type() == Document::dt_flowcode )
|
|
|
|
{
|
|
|
|
if ( startNode1 && startNode2 && endNode1 && endNode2 )
|
|
|
|
{
|
|
|
|
// Can't have I-configuration
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( startNode && endNode )
|
|
|
|
{
|
|
|
|
// Nice and easy straight line to check
|
|
|
|
|
|
|
|
if ( !startNode->acceptInput() && !endNode->acceptInput() )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if ( !startNode->acceptOutput() && !endNode->acceptOutput() )
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// We're in a T-configuration, we can only make this if the base of
|
|
|
|
// the T is an output
|
|
|
|
Node * base = startNode ? startNode : endNode;
|
|
|
|
if ( !base->acceptOutput() )
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//END Check we have appropriate input and output allowance
|
|
|
|
|
|
|
|
|
|
|
|
//BEGIN Simple level check
|
|
|
|
for ( unsigned i = 0; i < 3; i++ )
|
|
|
|
{
|
|
|
|
for ( unsigned j = 0; j < 3; j++ )
|
|
|
|
{
|
|
|
|
if ( start[i] && end[j] && start[i]->level() != end[j]->level() )
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//END Simple level check
|
|
|
|
|
|
|
|
|
|
|
|
//BEGIN Advanced level check
|
|
|
|
CNItem * startParentItem[3];
|
|
|
|
for ( unsigned i = 0; i < 3; i++ )
|
|
|
|
startParentItem[i] = start[i] ? start[i]->parentItem() : 0l;
|
|
|
|
|
|
|
|
CNItem * endParentItem[3];
|
|
|
|
for ( unsigned i = 0; i < 3; i++ )
|
|
|
|
endParentItem[i] = end[i] ? end[i]->parentItem() : 0l;
|
|
|
|
|
|
|
|
Item * container[6] = {0l};
|
|
|
|
|
|
|
|
for ( unsigned i = 0; i < 3; i++ )
|
|
|
|
{
|
|
|
|
if (startParentItem[i])
|
|
|
|
{
|
|
|
|
int dl = start[i]->level() - startParentItem[i]->level();
|
|
|
|
if ( dl == 0 )
|
|
|
|
container[i] = startParentItem[i]->parentItem();
|
|
|
|
else if ( dl == 1 )
|
|
|
|
container[i] = startParentItem[i];
|
|
|
|
else
|
|
|
|
kdError() << k_funcinfo << " start, i="<<i<<" dl="<<dl<<endl;
|
|
|
|
}
|
|
|
|
if (endParentItem[i])
|
|
|
|
{
|
|
|
|
int dl = end[i]->level() - endParentItem[i]->level();
|
|
|
|
if ( dl == 0 )
|
|
|
|
container[i+3] = endParentItem[i]->parentItem();
|
|
|
|
else if ( dl == 1 )
|
|
|
|
container[i+3] = endParentItem[i];
|
|
|
|
else
|
|
|
|
kdError() << k_funcinfo << " end, i="<<i<<" dl="<<dl<<endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Everything better well have the same container...
|
|
|
|
for ( unsigned i = 0; i < 6; ++i )
|
|
|
|
{
|
|
|
|
for ( unsigned j = 0; j < i; ++j )
|
|
|
|
{
|
|
|
|
Node * n1 = i < 3 ? start[i] : end[i-3];
|
|
|
|
Node * n2 = j < 3 ? start[j] : end[j-3];
|
|
|
|
if ( n1 && n2 && (container[i] != container[j]) )
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//END Advanced level check
|
|
|
|
|
|
|
|
|
|
|
|
// Well, it looks like we can, afterall, connect them...
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Connector * ICNDocument::createConnector( Node *startNode, Node *endNode, TQPointList *pointList )
|
|
|
|
{
|
|
|
|
if ( !canConnect( startNode, endNode ) )
|
|
|
|
return 0l;
|
|
|
|
|
|
|
|
TQPointList autoPoints;
|
|
|
|
if (!pointList)
|
|
|
|
{
|
|
|
|
addAllItemConnectorPoints();
|
|
|
|
ConRouter cr(this);
|
|
|
|
cr.mapRoute( int(startNode->x()), int(startNode->y()), int(endNode->x()), int(endNode->y()) );
|
|
|
|
autoPoints = cr.pointList(false);
|
|
|
|
pointList = &autoPoints;
|
|
|
|
}
|
|
|
|
|
|
|
|
Connector * con = 0l;
|
|
|
|
|
|
|
|
// Check if we need to swap the ends around, and create the connector
|
|
|
|
if ( endNode->type() == Node::fp_out )
|
|
|
|
con = createConnector( endNode->id(), startNode->id(), pointList );
|
|
|
|
else
|
|
|
|
con = createConnector( startNode->id(), endNode->id(), pointList );
|
|
|
|
|
|
|
|
bool startInGroup = deleteNodeGroup(startNode);
|
|
|
|
bool endInGroup = deleteNodeGroup(endNode);
|
|
|
|
if ( startInGroup || endInGroup )
|
|
|
|
{
|
|
|
|
NodeGroup *ng = createNodeGroup(startNode);
|
|
|
|
ng->addNode( endNode, true );
|
|
|
|
ng->init();
|
|
|
|
}
|
|
|
|
|
|
|
|
flushDeleteList();
|
|
|
|
return con;
|
|
|
|
}
|
|
|
|
|
|
|
|
Connector * ICNDocument::createConnector( Node *node, Connector *con, const TQPoint &pos2, TQPointList *pointList )
|
|
|
|
{
|
|
|
|
if ( !canConnect( node, con ) )
|
|
|
|
return 0l;
|
|
|
|
|
|
|
|
Node *conStartNode = con->startNode();
|
|
|
|
Node *conEndNode = con->endNode();
|
|
|
|
|
|
|
|
const bool usedManual = con->usesManualPoints();
|
|
|
|
|
|
|
|
Node *newNode = 0l;
|
|
|
|
if ( type() == Document::dt_circuit )
|
|
|
|
newNode = new ECNode( this, Node::ec_junction, Node::dir_right, pos2 );
|
|
|
|
|
|
|
|
else if ( type() == Document::dt_flowcode )
|
|
|
|
newNode = new FPNode( this, Node::fp_junction, Node::dir_right, pos2 );
|
|
|
|
|
|
|
|
else
|
|
|
|
return 0l;
|
|
|
|
|
|
|
|
TQPointList autoPoints;
|
|
|
|
if (!pointList)
|
|
|
|
{
|
|
|
|
addAllItemConnectorPoints();
|
|
|
|
ConRouter cr(this);
|
|
|
|
cr.mapRoute( int(node->x()), int(node->y()), pos2.x(), pos2.y() );
|
|
|
|
autoPoints = cr.pointList(false);
|
|
|
|
pointList = &autoPoints;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQValueList<TQPointList> oldConPoints = con->splitConnectorPoints(pos2);
|
|
|
|
con->hide();
|
|
|
|
|
|
|
|
// The actual new connector
|
|
|
|
Connector *new1 = newNode->createInputConnector(node);
|
|
|
|
node->addOutputConnector(new1);
|
|
|
|
new1->setRoutePoints(*pointList,usedManual);
|
|
|
|
|
|
|
|
// The two connectors formed from the original one when split
|
|
|
|
Connector *new2 = newNode->createInputConnector(conStartNode);
|
|
|
|
conStartNode->addOutputConnector(new2);
|
|
|
|
new2->setRoutePoints( *oldConPoints.at(0), usedManual );
|
|
|
|
|
|
|
|
Connector *new3 = conEndNode->createInputConnector(newNode);
|
|
|
|
newNode->addOutputConnector(new3);
|
|
|
|
new3->setRoutePoints( *oldConPoints.at(1), usedManual );
|
|
|
|
|
|
|
|
// Avoid flicker: tell them to update their draw lists now
|
|
|
|
con->updateConnectorPoints(false);
|
|
|
|
new1->updateDrawList();
|
|
|
|
new2->updateDrawList();
|
|
|
|
new3->updateDrawList();
|
|
|
|
|
|
|
|
// Now it's safe to remove the connector...
|
|
|
|
con->removeConnector();
|
|
|
|
flushDeleteList();
|
|
|
|
|
|
|
|
deleteNodeGroup(conStartNode);
|
|
|
|
deleteNodeGroup(conEndNode);
|
|
|
|
createNodeGroup(newNode)->init();
|
|
|
|
|
|
|
|
return new1;
|
|
|
|
}
|
|
|
|
|
|
|
|
Connector * ICNDocument::createConnector( Connector *con1, Connector *con2, const TQPoint &pos1, const TQPoint &pos2, TQPointList *pointList )
|
|
|
|
{
|
|
|
|
if ( !canConnect( con1, con2 ) )
|
|
|
|
return 0l;
|
|
|
|
|
|
|
|
const bool con1UsedManual = con1->usesManualPoints();
|
|
|
|
const bool con2UsedManual = con2->usesManualPoints();
|
|
|
|
|
|
|
|
TQValueList<TQPointList> oldCon1Points = con1->splitConnectorPoints(pos1);
|
|
|
|
TQValueList<TQPointList> oldCon2Points = con2->splitConnectorPoints(pos2);
|
|
|
|
|
|
|
|
Node *node1a = con1->startNode();
|
|
|
|
Node *node1b = con1->endNode();
|
|
|
|
|
|
|
|
Node *node2a = con2->startNode();
|
|
|
|
Node *node2b = con2->endNode();
|
|
|
|
|
|
|
|
if ( !node1a || !node1b || !node2a || !node2b )
|
|
|
|
return 0l;
|
|
|
|
|
|
|
|
con1->hide();
|
|
|
|
con2->hide();
|
|
|
|
|
|
|
|
if ( type() != Document::dt_circuit )
|
|
|
|
return 0l;
|
|
|
|
|
|
|
|
ECNode * newNode1 = new ECNode( this, Node::ec_junction, Node::dir_right, pos1 );
|
|
|
|
ECNode * newNode2 = new ECNode( this, Node::ec_junction, Node::dir_right, pos2 );
|
|
|
|
|
|
|
|
Connector *con1a = newNode1->createInputConnector(node1a);
|
|
|
|
node1a->addOutputConnector(con1a);
|
|
|
|
Connector *con1b = newNode1->createInputConnector(node1b);
|
|
|
|
node1b->addOutputConnector(con1b);
|
|
|
|
|
|
|
|
Connector *newCon = newNode1->createInputConnector(newNode2);
|
|
|
|
newNode2->addOutputConnector(newCon);
|
|
|
|
|
|
|
|
Connector *con2a = node2a->createInputConnector(newNode2);
|
|
|
|
newNode2->addOutputConnector(con2a);
|
|
|
|
Connector *con2b = node2b->createInputConnector(newNode2);
|
|
|
|
newNode2->addOutputConnector(con2b);
|
|
|
|
|
|
|
|
if ( !con1a || !con1b || !con2a || !con2b )
|
|
|
|
{
|
|
|
|
// This should never happen, as the canConnect function should strictly
|
|
|
|
// determine whether the connectors could be created before hand.
|
|
|
|
kdWarning() << k_funcinfo << "Not all the connectors were created, this should never happen" << endl;
|
|
|
|
|
|
|
|
if (con1a)
|
|
|
|
con1a->removeConnector();
|
|
|
|
|
|
|
|
if (con1b)
|
|
|
|
con1b->removeConnector();
|
|
|
|
|
|
|
|
if (con2a)
|
|
|
|
con2a->removeConnector();
|
|
|
|
|
|
|
|
if (con2b)
|
|
|
|
con2b->removeConnector();
|
|
|
|
|
|
|
|
newNode1->removeNode();
|
|
|
|
newNode2->removeNode();
|
|
|
|
|
|
|
|
flushDeleteList();
|
|
|
|
return 0l;
|
|
|
|
}
|
|
|
|
|
|
|
|
con1a->setRoutePoints( *oldCon1Points.at(0), con1UsedManual );
|
|
|
|
con1b->setRoutePoints( *oldCon1Points.at(1), con1UsedManual );
|
|
|
|
|
|
|
|
con2a->setRoutePoints( *oldCon2Points.at(0), con2UsedManual );
|
|
|
|
con2b->setRoutePoints( *oldCon2Points.at(1), con2UsedManual );
|
|
|
|
|
|
|
|
TQPointList autoPoints;
|
|
|
|
if (!pointList)
|
|
|
|
{
|
|
|
|
addAllItemConnectorPoints();
|
|
|
|
ConRouter cr(this);
|
|
|
|
cr.mapRoute( pos1.x(), pos1.y(), pos2.x(), pos2.y() );
|
|
|
|
autoPoints = cr.pointList(false);
|
|
|
|
pointList = &autoPoints;
|
|
|
|
}
|
|
|
|
newCon->setRoutePoints(*pointList,true);
|
|
|
|
|
|
|
|
|
|
|
|
// Avoid flicker: tell them to update their draw lists now
|
|
|
|
con1->updateConnectorPoints(false);
|
|
|
|
con2->updateConnectorPoints(false);
|
|
|
|
newCon->updateDrawList();
|
|
|
|
con1a->updateDrawList();
|
|
|
|
con1b->updateDrawList();
|
|
|
|
con2a->updateDrawList();
|
|
|
|
con2b->updateDrawList();
|
|
|
|
|
|
|
|
|
|
|
|
// Now it's safe to remove the connectors
|
|
|
|
con1->removeConnector();
|
|
|
|
con2->removeConnector();
|
|
|
|
|
|
|
|
flushDeleteList();
|
|
|
|
|
|
|
|
deleteNodeGroup(node1a);
|
|
|
|
deleteNodeGroup(node1b);
|
|
|
|
deleteNodeGroup(node2a);
|
|
|
|
deleteNodeGroup(node2b);
|
|
|
|
NodeGroup *ng = createNodeGroup(newNode1);
|
|
|
|
ng->addNode( newNode2, true );
|
|
|
|
ng->init();
|
|
|
|
|
|
|
|
return newCon;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NodeGroup* ICNDocument::createNodeGroup( Node *node )
|
|
|
|
{
|
|
|
|
if ( !node || node->isChildNode() ) {
|
|
|
|
return 0l;
|
|
|
|
}
|
|
|
|
|
|
|
|
const GuardedNodeGroupList::iterator end = m_nodeGroupList.end();
|
|
|
|
for ( GuardedNodeGroupList::iterator it = m_nodeGroupList.begin(); it != end; ++it )
|
|
|
|
{
|
|
|
|
if ( *it && (*it)->contains(node) ) {
|
|
|
|
return *it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NodeGroup *group = new NodeGroup(this);
|
|
|
|
m_nodeGroupList += group;
|
|
|
|
group->addNode( node, true );
|
|
|
|
|
|
|
|
return group;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ICNDocument::deleteNodeGroup( Node *node )
|
|
|
|
{
|
|
|
|
if ( !node || node->isChildNode() ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const GuardedNodeGroupList::iterator end = m_nodeGroupList.end();
|
|
|
|
for ( GuardedNodeGroupList::iterator it = m_nodeGroupList.begin(); it != end; ++it )
|
|
|
|
{
|
|
|
|
if ( *it && (*it)->contains(node) )
|
|
|
|
{
|
|
|
|
delete *it;
|
|
|
|
m_nodeGroupList.remove(it);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ICNDocument::slotRequestAssignNG()
|
|
|
|
{
|
|
|
|
requestEvent( ItemDocumentEvent::UpdateNodeGroups );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ICNDocument::slotAssignNodeGroups()
|
|
|
|
{
|
|
|
|
const GuardedNodeGroupList::iterator nglEnd = m_nodeGroupList.end();
|
|
|
|
for ( GuardedNodeGroupList::iterator it = m_nodeGroupList.begin(); it != nglEnd; ++it )
|
|
|
|
delete *it;
|
|
|
|
m_nodeGroupList.clear();
|
|
|
|
|
|
|
|
const NodeList::iterator end = m_nodeList.end();
|
|
|
|
for ( NodeList::iterator it = m_nodeList.begin(); it != end; ++it )
|
|
|
|
{
|
|
|
|
NodeGroup *ng = createNodeGroup(*it);
|
|
|
|
if (ng)
|
|
|
|
ng->init();
|
|
|
|
}
|
|
|
|
|
|
|
|
// We've destroyed the old node groups, so any collapsed flowcontainers
|
|
|
|
// containing new node groups need to update them to make them invisible.
|
|
|
|
const ItemList::const_iterator itemListEnd = m_itemList.end();
|
|
|
|
for ( ItemList::const_iterator it = m_itemList.begin(); it != itemListEnd; ++it )
|
|
|
|
{
|
|
|
|
if ( FlowContainer * fc = dynamic_cast<FlowContainer*>((Item*)*it) )
|
|
|
|
fc->updateContainedVisibility();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ICNDocument::getTranslatable( const ItemList & itemList, ConnectorList * fixedConnectors, ConnectorList * translatableConnectors, NodeGroupList * translatableNodeGroups )
|
|
|
|
{
|
|
|
|
ConnectorList tempCL1;
|
|
|
|
if ( !fixedConnectors )
|
|
|
|
fixedConnectors = &tempCL1;
|
|
|
|
|
|
|
|
ConnectorList tempCL2;
|
|
|
|
if ( !translatableConnectors )
|
|
|
|
translatableConnectors = &tempCL2;
|
|
|
|
|
|
|
|
NodeGroupList tempNGL;
|
|
|
|
if ( !translatableNodeGroups )
|
|
|
|
translatableNodeGroups = &tempNGL;
|
|
|
|
|
|
|
|
// We record the connectors attached to the items, and
|
|
|
|
// the number of times an item in the list is connected to
|
|
|
|
// it - i.e. 1 or 2. For those with 2, it is safe to update their
|
|
|
|
// route as it simply involves shifting the route
|
|
|
|
typedef TQMap< Connector*, int > ConnectorMap;
|
|
|
|
ConnectorMap fixedConnectorMap;
|
|
|
|
|
|
|
|
// This list of nodes is built up, used for later in determining fixed NodeGroups
|
|
|
|
NodeList itemNodeList;
|
|
|
|
{
|
|
|
|
const ItemList::const_iterator itemListEnd = itemList.end();
|
|
|
|
for ( ItemList::const_iterator it = itemList.begin(); it != itemListEnd; ++it )
|
|
|
|
{
|
|
|
|
CNItem *cnItem = dynamic_cast<CNItem*>((Item*)*it);
|
|
|
|
if ( !cnItem || !cnItem->canvas() )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
NodeMap nodeMap = cnItem->nodeMap();
|
|
|
|
const NodeMap::iterator nlEnd = nodeMap.end();
|
|
|
|
for ( NodeMap::iterator nlIt = nodeMap.begin(); nlIt != nlEnd; ++nlIt )
|
|
|
|
{
|
|
|
|
itemNodeList.append(nlIt.data().node);
|
|
|
|
}
|
|
|
|
|
|
|
|
ConnectorList conList = cnItem->connectorList();
|
|
|
|
conList.remove((Connector*)0l);
|
|
|
|
const ConnectorList::iterator clEnd = conList.end();
|
|
|
|
for ( ConnectorList::iterator clit = conList.begin(); clit != clEnd; ++clit )
|
|
|
|
{
|
|
|
|
ConnectorMap::iterator cit = fixedConnectorMap.find(*clit);
|
|
|
|
if ( cit != fixedConnectorMap.end() ) {
|
|
|
|
cit.data()++;
|
|
|
|
} else {
|
|
|
|
fixedConnectorMap[*clit] = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We now look through the NodeGroups to see if we have all the external
|
|
|
|
// nodes for a given nodeGroup - if so, then the connectors in the fixed
|
|
|
|
// connectors are ok to be moved
|
|
|
|
ConnectorList fixedNGConnectors;
|
|
|
|
{
|
|
|
|
translatableNodeGroups->clear();
|
|
|
|
const GuardedNodeGroupList::const_iterator end = m_nodeGroupList.end();
|
|
|
|
for ( GuardedNodeGroupList::const_iterator it = m_nodeGroupList.begin(); it != end; ++it )
|
|
|
|
{
|
|
|
|
NodeGroup *ng = *it;
|
|
|
|
if (!ng)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
NodeList externalNodeList = ng->externalNodeList();
|
|
|
|
const NodeList::iterator itemNodeListEnd = itemNodeList.end();
|
|
|
|
for ( NodeList::iterator inlIt = itemNodeList.begin(); inlIt != itemNodeListEnd; ++inlIt )
|
|
|
|
externalNodeList.remove(*inlIt);
|
|
|
|
|
|
|
|
if ( externalNodeList.isEmpty() )
|
|
|
|
{
|
|
|
|
translatableNodeGroups->append(ng);
|
|
|
|
|
|
|
|
const ConnectorList ngConnectorList = ng->connectorList();
|
|
|
|
const ConnectorList::const_iterator ngConnectorListEnd = ngConnectorList.end();
|
|
|
|
for ( ConnectorList::const_iterator ngclIt = ngConnectorList.begin(); ngclIt != ngConnectorListEnd; ++ngclIt )
|
|
|
|
{
|
|
|
|
if (*ngclIt)
|
|
|
|
fixedNGConnectors += *ngclIt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
translatableConnectors->clear();
|
|
|
|
const ConnectorMap::iterator fcEnd = fixedConnectorMap.end();
|
|
|
|
for ( ConnectorMap::iterator it = fixedConnectorMap.begin(); it != fcEnd; ++it )
|
|
|
|
{
|
|
|
|
// We allow it to be fixed if it is connected to two of the CNItems in the
|
|
|
|
// select list, or is connected to itself (hence only appears to be connected to one,
|
|
|
|
// but is fixed anyway
|
|
|
|
Node *startNode = it.key()->endNode();
|
|
|
|
Node *endNode = it.key()->startNode();
|
|
|
|
if ( (it.data() > 1) ||
|
|
|
|
(startNode && endNode && startNode->parentItem() == endNode->parentItem()) )
|
|
|
|
{
|
|
|
|
translatableConnectors->append( const_cast<Connector*>(it.key()) );
|
|
|
|
}
|
|
|
|
else if ( !fixedNGConnectors.contains(it.key()) && !fixedConnectors->contains(it.key()) )
|
|
|
|
{
|
|
|
|
fixedConnectors->append(it.key());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ICNDocument::addCPenalty( int x, int y, int score )
|
|
|
|
{
|
|
|
|
if ( isValidCellReference(x,y) )
|
|
|
|
{
|
|
|
|
(*m_cells)[x][y].Cpenalty += score;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ICNDocument::createCellMap()
|
|
|
|
{
|
|
|
|
unsigned newCellsX = TQMAX( canvas()->width()/cellSize, 1 );
|
|
|
|
unsigned newCellsY = TQMAX( canvas()->height()/cellSize, 1 );
|
|
|
|
|
|
|
|
if ( m_cells && newCellsX == m_cellsX && newCellsY == m_cellsY )
|
|
|
|
return;
|
|
|
|
|
|
|
|
const ItemList::iterator ciEnd = m_itemList.end();
|
|
|
|
for ( ItemList::iterator it = m_itemList.begin(); it != ciEnd; ++it )
|
|
|
|
{
|
|
|
|
CNItem *cnItem = dynamic_cast<CNItem*>((Item*)(*it));
|
|
|
|
if (cnItem)
|
|
|
|
cnItem->updateConnectorPoints(false);
|
|
|
|
}
|
|
|
|
const ConnectorList::iterator conEnd = m_connectorList.end();
|
|
|
|
for ( ConnectorList::iterator it = m_connectorList.begin(); it != conEnd; ++it )
|
|
|
|
{
|
|
|
|
(*it)->updateConnectorPoints(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
delete m_cells;
|
|
|
|
m_cellsX = newCellsX;
|
|
|
|
m_cellsY = newCellsY;
|
|
|
|
m_cells = new Cells( m_cellsX, m_cellsY );
|
|
|
|
|
|
|
|
for ( ConnectorList::iterator it = m_connectorList.begin(); it != conEnd; ++it )
|
|
|
|
(*it)->updateConnectorPoints(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int ICNDocument::gridSnap( int pos )
|
|
|
|
{
|
|
|
|
return pos-(pos%8)+4;
|
|
|
|
// return int((floor(pos/8))*8)+4;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQPoint ICNDocument::gridSnap( const TQPoint &pos )
|
|
|
|
{
|
|
|
|
return TQPoint( gridSnap( pos.x() ), gridSnap( pos.y() ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ICNDocument::appendDeleteList( TQCanvasItem *qcanvasItem )
|
|
|
|
{
|
|
|
|
if ( !qcanvasItem || m_itemDeleteList.findIndex(qcanvasItem) != -1 ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_itemDeleteList.append(qcanvasItem);
|
|
|
|
|
|
|
|
if ( qcanvasItem->rtti() == ItemDocument::RTTI::Node )
|
|
|
|
{
|
|
|
|
Node *node = dynamic_cast<Node*>(qcanvasItem);
|
|
|
|
node->removeNode();
|
|
|
|
}
|
|
|
|
else if ( qcanvasItem->rtti() == ItemDocument::RTTI::CNItem ||
|
|
|
|
qcanvasItem->rtti() == ItemDocument::RTTI::DrawPart )
|
|
|
|
{
|
|
|
|
Item *item = dynamic_cast<Item*>(qcanvasItem);
|
|
|
|
item->removeItem();
|
|
|
|
}
|
|
|
|
else if ( qcanvasItem->rtti() == ItemDocument::RTTI::Connector ||
|
|
|
|
qcanvasItem->rtti() == ItemDocument::RTTI::ConnectorLine )
|
|
|
|
{
|
|
|
|
Connector *connector = dynamic_cast<Connector*>(qcanvasItem);
|
|
|
|
if (!connector)
|
|
|
|
connector = (dynamic_cast<ConnectorLine*>(qcanvasItem))->parent();
|
|
|
|
connector->removeConnector();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
kdDebug() << k_funcinfo << "unrecognised TQCanvasItem rtti " << TQString::number(qcanvasItem->rtti()) << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ICNDocument::flushDeleteList()
|
|
|
|
{
|
|
|
|
// Remove duplicate items in the delete list
|
|
|
|
TQCanvasItemList::iterator end = m_itemDeleteList.end();
|
|
|
|
for ( TQCanvasItemList::iterator it = m_itemDeleteList.begin(); it != end; ++it )
|
|
|
|
{
|
|
|
|
if ( *it && m_itemDeleteList.contains(*it) > 1 ) {
|
|
|
|
*it = 0l;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_itemDeleteList.remove(0l);
|
|
|
|
|
|
|
|
end = m_itemDeleteList.end();
|
|
|
|
for ( TQCanvasItemList::iterator it = m_itemDeleteList.begin(); it != end; ++it )
|
|
|
|
{
|
|
|
|
TQCanvasItem *qcanvasItem = *it;
|
|
|
|
m_selectList->removeTQCanvasItem(*it);
|
|
|
|
|
|
|
|
if ( Item *item = dynamic_cast<Item*>(qcanvasItem) )
|
|
|
|
m_itemList.remove(item);
|
|
|
|
|
|
|
|
else if ( qcanvasItem->rtti() == ItemDocument::RTTI::Node )
|
|
|
|
m_nodeList.remove( dynamic_cast<Node*>(qcanvasItem) );
|
|
|
|
|
|
|
|
else if ( qcanvasItem->rtti() == ItemDocument::RTTI::Connector )
|
|
|
|
m_connectorList.remove( dynamic_cast<Connector*>(qcanvasItem) );
|
|
|
|
|
|
|
|
else
|
|
|
|
kdError() << k_funcinfo << "Unknown qcanvasItem! "<<qcanvasItem << endl;
|
|
|
|
|
|
|
|
qcanvasItem->setCanvas(0l);
|
|
|
|
|
|
|
|
delete qcanvasItem;
|
|
|
|
*it = 0l;
|
|
|
|
}
|
|
|
|
|
|
|
|
// // Check connectors for merging
|
|
|
|
bool doneJoin = false;
|
|
|
|
const NodeList::iterator nlEnd = m_nodeList.end();
|
|
|
|
for ( NodeList::iterator it = m_nodeList.begin(); it != nlEnd; ++it )
|
|
|
|
{
|
|
|
|
(*it)->removeNullConnectors();
|
|
|
|
int conCount = (*it)->inputConnectorList().count() + (*it)->outputConnectorList().count();
|
|
|
|
if ( conCount == 2 && !(*it)->parentItem() )
|
|
|
|
{
|
|
|
|
if ( joinConnectors(*it) )
|
|
|
|
doneJoin = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (doneJoin)
|
|
|
|
flushDeleteList();
|
|
|
|
|
|
|
|
requestRerouteInvalidatedConnectors();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool ICNDocument::joinConnectors( Node *node )
|
|
|
|
{
|
|
|
|
// We don't want to destroy the node if it has a parent
|
|
|
|
if ( node->parentItem() )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
node->removeNullConnectors();
|
|
|
|
|
|
|
|
int conCount = node->inputConnectorList().count() + node->outputConnectorList().count();
|
|
|
|
if ( conCount != 2 )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Connector *con1, *con2;
|
|
|
|
Node *startNode, *endNode;
|
|
|
|
TQPointList conPoints;
|
|
|
|
|
|
|
|
|
|
|
|
if ( node->inputConnectorList().count() == 0 )
|
|
|
|
{
|
|
|
|
// Both connectors emerge from node - output - i.e. node is pure start node
|
|
|
|
con1 = *node->outputConnectorList().at(0);
|
|
|
|
con2 = *node->outputConnectorList().at(1);
|
|
|
|
if ( con1 == con2 ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
startNode = con1->endNode();
|
|
|
|
endNode = con2->endNode();
|
|
|
|
conPoints = con1->connectorPoints(true) + con2->connectorPoints(false);
|
|
|
|
}
|
|
|
|
else if ( node->inputConnectorList().count() == 1 )
|
|
|
|
{
|
|
|
|
// Ont input, one output
|
|
|
|
con1 = *node->inputConnectorList().at(0);
|
|
|
|
con2 = *node->outputConnectorList().at(0);
|
|
|
|
if ( con1 == con2 ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
startNode = con1->startNode();
|
|
|
|
endNode = con2->endNode();
|
|
|
|
conPoints = con1->connectorPoints(false) + con2->connectorPoints(false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Both input - i.e. node is pure end node
|
|
|
|
con1 = *node->inputConnectorList().at(0);
|
|
|
|
con2 = *node->inputConnectorList().at(1);
|
|
|
|
if ( con1 == con2 ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
startNode = con1->startNode();
|
|
|
|
endNode = con2->startNode();
|
|
|
|
conPoints = con1->connectorPoints(false) + con2->connectorPoints(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !startNode || !endNode )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Connector *newCon = endNode->createInputConnector(startNode);
|
|
|
|
if (!newCon)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
startNode->addOutputConnector(newCon);
|
|
|
|
newCon->setRoutePoints( conPoints, con1->usesManualPoints() || con2->usesManualPoints() );
|
|
|
|
|
|
|
|
// Avoid flicker: update draw lists now
|
|
|
|
con1->updateConnectorPoints(false);
|
|
|
|
con2->updateConnectorPoints(false);
|
|
|
|
newCon->updateDrawList();
|
|
|
|
|
|
|
|
node->removeNode();
|
|
|
|
con1->removeConnector();
|
|
|
|
con2->removeConnector();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ICNDocument::registerItem( TQCanvasItem *qcanvasItem )
|
|
|
|
{
|
|
|
|
if (!qcanvasItem)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if ( !ItemDocument::registerItem(qcanvasItem) )
|
|
|
|
{
|
|
|
|
switch (qcanvasItem->rtti())
|
|
|
|
{
|
|
|
|
case ItemDocument::RTTI::Node:
|
|
|
|
{
|
|
|
|
Node *node = (Node*)qcanvasItem;
|
|
|
|
m_nodeList.append(node);
|
|
|
|
connect( node, TQT_SIGNAL(removed(Node*)), this, TQT_SLOT(requestRerouteInvalidatedConnectors()) );
|
|
|
|
emit nodeAdded(node);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ItemDocument::RTTI::Connector:
|
|
|
|
{
|
|
|
|
Connector *connector = dynamic_cast<Connector*>(qcanvasItem);
|
|
|
|
m_connectorList.append(connector);
|
|
|
|
connect( connector, TQT_SIGNAL(removed(Connector*)), this, TQT_SLOT(requestRerouteInvalidatedConnectors()) );
|
|
|
|
emit connectorAdded(connector);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
kdError() << k_funcinfo << "Unrecognised item rtti"<<endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
requestRerouteInvalidatedConnectors();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ICNDocument::copy()
|
|
|
|
{
|
|
|
|
if ( m_selectList->isEmpty() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
ItemDocumentData data( type() );
|
|
|
|
|
|
|
|
// We only want to copy the connectors who have all ends attached to something in the selection
|
|
|
|
ConnectorList connectorList = m_selectList->connectors(false);
|
|
|
|
|
|
|
|
typedef TQMap< Node*, ConnectorList > NCLMap;
|
|
|
|
NCLMap nclMap;
|
|
|
|
|
|
|
|
ConnectorList::iterator end = connectorList.end();
|
|
|
|
for ( ConnectorList::iterator it = connectorList.begin(); it != end; ++it )
|
|
|
|
{
|
|
|
|
Node *startNode = (*it)->startNode();
|
|
|
|
if ( startNode && !startNode->isChildNode() )
|
|
|
|
nclMap[startNode].append(*it);
|
|
|
|
|
|
|
|
Node *endNode = (*it)->endNode();
|
|
|
|
if ( endNode && !endNode->isChildNode() )
|
|
|
|
nclMap[endNode].append(*it);
|
|
|
|
}
|
|
|
|
|
|
|
|
NodeList nodeList;
|
|
|
|
// Remove those connectors (and nodes) which are dangling on an orphan node
|
|
|
|
NCLMap::iterator nclEnd = nclMap.end();
|
|
|
|
for ( NCLMap::iterator it = nclMap.begin(); it != nclEnd; ++it )
|
|
|
|
{
|
|
|
|
if ( it.data().size() > 1 )
|
|
|
|
nodeList.append(it.key());
|
|
|
|
|
|
|
|
else if ( it.data().size() > 0 )
|
|
|
|
connectorList.remove( it.data().at(0) );
|
|
|
|
}
|
|
|
|
|
|
|
|
data.addItems( m_selectList->items(false) );
|
|
|
|
data.addNodes( nodeList );
|
|
|
|
data.addConnectors( connectorList );
|
|
|
|
|
|
|
|
KApplication::clipboard()->setText( data.toXML(), TQClipboard::Clipboard );
|
|
|
|
}
|
|
|
|
|
|
|
|
void ICNDocument::selectAll()
|
|
|
|
{
|
|
|
|
const NodeList::iterator nodeEnd = m_nodeList.end();
|
|
|
|
for ( NodeList::iterator nodeIt = m_nodeList.begin(); nodeIt != nodeEnd; ++nodeIt )
|
|
|
|
{
|
|
|
|
if (*nodeIt)
|
|
|
|
select(*nodeIt);
|
|
|
|
}
|
|
|
|
const ItemList::iterator itemEnd = m_itemList.end();
|
|
|
|
for ( ItemList::iterator itemIt = m_itemList.begin(); itemIt != itemEnd; ++itemIt )
|
|
|
|
{
|
|
|
|
if (*itemIt)
|
|
|
|
select(*itemIt);
|
|
|
|
}
|
|
|
|
const ConnectorList::iterator conEnd = m_connectorList.end();
|
|
|
|
for ( ConnectorList::iterator connectorIt = m_connectorList.begin(); connectorIt != conEnd; ++connectorIt )
|
|
|
|
{
|
|
|
|
if (*connectorIt)
|
|
|
|
select(*connectorIt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Item* ICNDocument::addItem( const TQString &id, const TQPoint &p, bool newItem )
|
|
|
|
{
|
|
|
|
if ( !isValidItem(id) ) {
|
|
|
|
return 0l;
|
|
|
|
}
|
|
|
|
|
|
|
|
// First, we need to tell all containers to go to full bounding so that
|
|
|
|
// we can detect a "collision" with them
|
|
|
|
const ItemList::iterator end = m_itemList.end();
|
|
|
|
for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it )
|
|
|
|
{
|
|
|
|
if ( FlowContainer *flowContainer = dynamic_cast<FlowContainer*>((Item*)(*it)) )
|
|
|
|
flowContainer->setFullBounds(true);
|
|
|
|
}
|
|
|
|
TQCanvasItemList preCollisions = canvas()->collisions(p);
|
|
|
|
for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it )
|
|
|
|
{
|
|
|
|
if ( FlowContainer *flowContainer = dynamic_cast<FlowContainer*>((Item*)(*it)) )
|
|
|
|
flowContainer->setFullBounds(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Item *item = itemLibrary()->createItem( id, this, newItem );
|
|
|
|
if (!item) return 0L;
|
|
|
|
|
|
|
|
// Look through the CNItems at the given point (sorted by z-coordinate) for
|
|
|
|
// a container item.
|
|
|
|
FlowContainer *container = 0l;
|
|
|
|
const TQCanvasItemList::iterator pcEnd = preCollisions.end();
|
|
|
|
for ( TQCanvasItemList::iterator it = preCollisions.begin(); it != pcEnd && !container; ++it )
|
|
|
|
{
|
|
|
|
if ( FlowContainer *flowContainer = dynamic_cast<FlowContainer*>(*it) )
|
|
|
|
container = flowContainer;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We want to check it is not a special item first as
|
|
|
|
// isValidItem may prompt the user about his bad choice
|
|
|
|
if ( !isValidItem(item) )
|
|
|
|
{
|
|
|
|
item->removeItem();
|
|
|
|
flushDeleteList();
|
|
|
|
return 0L;
|
|
|
|
}
|
|
|
|
|
|
|
|
int x = int(p.x());
|
|
|
|
int y = int(p.y());
|
|
|
|
|
|
|
|
if ( x < 16 || x > canvas()->width() )
|
|
|
|
x = 16;
|
|
|
|
if ( y < 16 || y > canvas()->height() )
|
|
|
|
y = 16;
|
|
|
|
|
|
|
|
if ( CNItem *cnItem = dynamic_cast<CNItem*>(item) )
|
|
|
|
{
|
|
|
|
cnItem->snap( x, y );
|
|
|
|
|
|
|
|
if (container)
|
|
|
|
container->addChild(cnItem);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
item->move( x, y );
|
|
|
|
|
|
|
|
item->show();
|
|
|
|
requestStateSave();
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ICNDocument::addAllItemConnectorPoints()
|
|
|
|
{
|
|
|
|
// FIXME The next line crashes sometimes??!
|
|
|
|
const ItemList::iterator ciEnd = m_itemList.end();
|
|
|
|
for ( ItemList::iterator it = m_itemList.begin(); it != ciEnd; ++it )
|
|
|
|
{
|
|
|
|
if ( CNItem *cnItem = dynamic_cast<CNItem*>((Item*)(*it)) )
|
|
|
|
cnItem->updateConnectorPoints(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ICNDocument::requestRerouteInvalidatedConnectors()
|
|
|
|
{
|
|
|
|
requestEvent( ItemDocumentEvent::RerouteInvalidatedConnectors );
|
|
|
|
}
|
|
|
|
void ICNDocument::rerouteInvalidatedConnectors()
|
|
|
|
{
|
|
|
|
tqApp->processEvents(300);
|
|
|
|
|
|
|
|
// We only ever need to add the connector points for CNItem's when we're about to reroute...
|
|
|
|
addAllItemConnectorPoints();
|
|
|
|
|
|
|
|
// List of connectors which are to be determined to need rerouting (and whose routes aren't controlled by NodeGroups)
|
|
|
|
ConnectorList connectorRerouteList;
|
|
|
|
|
|
|
|
// For those connectors that are controlled by node groups
|
|
|
|
NodeGroupList nodeGroupRerouteList;
|
|
|
|
|
|
|
|
const ConnectorList::iterator connectorListEnd = m_connectorList.end();
|
|
|
|
for ( ConnectorList::iterator it = m_connectorList.begin(); it != connectorListEnd; ++it )
|
|
|
|
{
|
|
|
|
Connector *connector = *it;
|
|
|
|
if ( connector && connector->isVisible() && connector->startNode() && connector->endNode() )
|
|
|
|
{
|
|
|
|
// Perform a series of tests to see if the connector needs rerouting
|
|
|
|
bool needsRerouting = false;
|
|
|
|
|
|
|
|
// Test to see if we actually have any points
|
|
|
|
const TQPointList pointList = connector->connectorPoints();
|
|
|
|
if ( pointList.isEmpty() )
|
|
|
|
needsRerouting = true;
|
|
|
|
|
|
|
|
// Test to see if the route doesn't match up with the node positions at either end
|
|
|
|
if (!needsRerouting)
|
|
|
|
{
|
|
|
|
const TQPoint listStart = pointList.first();
|
|
|
|
const TQPoint listEnd = pointList.last();
|
|
|
|
const TQPoint nodeStart = TQPoint( int(connector->startNode()->x()), int(connector->startNode()->y()) );
|
|
|
|
const TQPoint nodeEnd = TQPoint( int(connector->endNode()->x()), int(connector->endNode()->y()) );
|
|
|
|
|
|
|
|
if ( ((listStart != nodeStart) || (listEnd != nodeEnd)) &&
|
|
|
|
((listStart != nodeEnd) || (listEnd != nodeStart)) )
|
|
|
|
{
|
|
|
|
needsRerouting = true;
|
|
|
|
// kdDebug() << "listStart=("<<listStart.x()<<","<<listStart.y()<<") nodeStart=("<<nodeStart.x()<<","<<nodeStart.y()<<") listEnd=("<<listEnd.x()<<","<<listEnd.y()<<") nodeEnd=("<<nodeEnd.x()<<","<<nodeEnd.y()<<")"<<endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test to see if the route intersects any Items (we ignore if it is a manual route)
|
|
|
|
if ( !needsRerouting && !connector->usesManualPoints() )
|
|
|
|
{
|
|
|
|
const TQCanvasItemList collisions = connector->collisions(true);
|
|
|
|
const TQCanvasItemList::const_iterator collisionsEnd = collisions.end();
|
|
|
|
for ( TQCanvasItemList::const_iterator collisionsIt = collisions.begin(); (collisionsIt != collisionsEnd) && !needsRerouting; ++collisionsIt )
|
|
|
|
{
|
|
|
|
if ( dynamic_cast<Item*>(*collisionsIt) )
|
|
|
|
needsRerouting = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (needsRerouting)
|
|
|
|
{
|
|
|
|
NodeGroup *nodeGroup = connector->nodeGroup();
|
|
|
|
|
|
|
|
if ( !nodeGroup && !connectorRerouteList.contains(connector) )
|
|
|
|
connectorRerouteList.append(connector);
|
|
|
|
|
|
|
|
else if ( nodeGroup && !nodeGroupRerouteList.contains(nodeGroup) )
|
|
|
|
nodeGroupRerouteList.append(nodeGroup);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// To allow proper rerouting, we want to start with clean routes for all of the invalidated connectors
|
|
|
|
const NodeGroupList::iterator nodeGroupRerouteEnd = nodeGroupRerouteList.end();
|
|
|
|
for ( NodeGroupList::iterator it = nodeGroupRerouteList.begin(); it != nodeGroupRerouteEnd; ++it )
|
|
|
|
{
|
|
|
|
const ConnectorList contained = (*it)->connectorList();
|
|
|
|
const ConnectorList::const_iterator end = contained.end();
|
|
|
|
for ( ConnectorList::const_iterator it = contained.begin(); it != end; ++it )
|
|
|
|
(*it)->updateConnectorPoints(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
const ConnectorList::iterator connectorRerouteEnd = connectorRerouteList.end();
|
|
|
|
for ( ConnectorList::iterator it = connectorRerouteList.begin(); it != connectorRerouteEnd; ++it )
|
|
|
|
(*it)->updateConnectorPoints(false);
|
|
|
|
|
|
|
|
// And finally, reroute the connectors
|
|
|
|
for ( NodeGroupList::iterator it = nodeGroupRerouteList.begin(); it != nodeGroupRerouteEnd; ++it )
|
|
|
|
(*it)->updateRoutes();
|
|
|
|
|
|
|
|
for ( ConnectorList::iterator it = connectorRerouteList.begin(); it != connectorRerouteEnd; ++it )
|
|
|
|
(*it)->rerouteConnector();
|
|
|
|
|
|
|
|
for ( ConnectorList::iterator it = m_connectorList.begin(); it != connectorListEnd; ++it )
|
|
|
|
{
|
|
|
|
if (*it)
|
|
|
|
(*it)->updateDrawList();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Connector* ICNDocument::createConnector( const TQString &startNodeId, const TQString &endNodeId, TQPointList *pointList )
|
|
|
|
{
|
|
|
|
Node *startNode = nodeWithID(startNodeId);
|
|
|
|
Node *endNode = nodeWithID(endNodeId);
|
|
|
|
|
|
|
|
if ( !startNode || !endNode )
|
|
|
|
{
|
|
|
|
kdDebug() << "Either/both the connector start node and end node could not be found" << endl;
|
|
|
|
return 0L;
|
|
|
|
}
|
|
|
|
|
|
|
|
Connector *connector = endNode->createInputConnector(startNode);
|
|
|
|
if (!connector)
|
|
|
|
{
|
|
|
|
kdError() << k_funcinfo << "End node did not create the connector" << endl;
|
|
|
|
return 0l;
|
|
|
|
}
|
|
|
|
startNode->addOutputConnector(connector);
|
|
|
|
flushDeleteList(); // Delete any connectors that might have been removed by the nodes
|
|
|
|
|
|
|
|
// Set the route to the manual created one if the user created such a route
|
|
|
|
if (pointList)
|
|
|
|
connector->setRoutePoints(*pointList,true);
|
|
|
|
|
|
|
|
ConnectorList connectorList;
|
|
|
|
connectorList.append(connector);
|
|
|
|
|
|
|
|
setModified(true);
|
|
|
|
|
|
|
|
requestRerouteInvalidatedConnectors();
|
|
|
|
return connector;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ICNDocument::deleteSelection()
|
|
|
|
{
|
|
|
|
// End whatever editing mode we are in, as we don't want to start editing
|
|
|
|
// something that is about to no longer exist...
|
|
|
|
m_cmManager->cancelCurrentManipulation();
|
|
|
|
|
|
|
|
if ( m_selectList->isEmpty() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_selectList->deleteAllItems();
|
|
|
|
flushDeleteList();
|
|
|
|
setModified(true);
|
|
|
|
|
|
|
|
// We need to emit this so that property widgets etc...
|
|
|
|
// can clear themselves.
|
|
|
|
emit itemUnselected(0L);
|
|
|
|
|
|
|
|
requestRerouteInvalidatedConnectors();
|
|
|
|
requestStateSave();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ConnectorList ICNDocument::getCommonConnectors( const ItemList &list )
|
|
|
|
{
|
|
|
|
NodeList nodeList = getCommonNodes(list);
|
|
|
|
|
|
|
|
// Now, get all the connectors, and remove the ones that don't have both end
|
|
|
|
// nodes in the above generated list
|
|
|
|
ConnectorList connectorList = m_connectorList;
|
|
|
|
const ConnectorList::iterator connectorListEnd = connectorList.end();
|
|
|
|
for ( ConnectorList::iterator it = connectorList.begin(); it != connectorListEnd; ++it )
|
|
|
|
{
|
|
|
|
Connector *con = *it;
|
|
|
|
if ( !con || !nodeList.contains(con->startNode()) || !nodeList.contains(con->endNode()) ) {
|
|
|
|
*it = 0l;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
connectorList.remove((Connector*)0l);
|
|
|
|
return connectorList;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NodeList ICNDocument::getCommonNodes( const ItemList &list )
|
|
|
|
{
|
|
|
|
NodeList nodeList;
|
|
|
|
|
|
|
|
const ItemList::const_iterator listEnd = list.end();
|
|
|
|
for ( ItemList::const_iterator it = list.begin(); it != listEnd; ++it )
|
|
|
|
{
|
|
|
|
NodeMap nodeMap;
|
|
|
|
CNItem *cnItem = dynamic_cast<CNItem*>((Item*)*it);
|
|
|
|
if (cnItem)
|
|
|
|
nodeMap = cnItem->nodeMap();
|
|
|
|
const NodeMap::iterator nodeMapEnd = nodeMap.end();
|
|
|
|
for ( NodeMap::iterator it = nodeMap.begin(); it != nodeMapEnd; ++it )
|
|
|
|
{
|
|
|
|
Node *node = it.data().node;
|
|
|
|
|
|
|
|
if ( !nodeList.contains(node) ) {
|
|
|
|
nodeList += node;
|
|
|
|
}
|
|
|
|
|
|
|
|
NodeGroup *ng = node->nodeGroup();
|
|
|
|
if (ng)
|
|
|
|
{
|
|
|
|
NodeList intNodeList = ng->internalNodeList();
|
|
|
|
const NodeList::iterator intNodeListEnd = intNodeList.end();
|
|
|
|
for ( NodeList::iterator it = intNodeList.begin(); it != intNodeListEnd; ++it )
|
|
|
|
{
|
|
|
|
Node *intNode = *it;
|
|
|
|
if ( !nodeList.contains(intNode) ) {
|
|
|
|
nodeList += intNode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nodeList;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DirCursor *DirCursor::m_self = 0l;
|
|
|
|
|
|
|
|
DirCursor::DirCursor()
|
|
|
|
{
|
|
|
|
initCursors();
|
|
|
|
}
|
|
|
|
|
|
|
|
DirCursor::~DirCursor()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
DirCursor* DirCursor::self()
|
|
|
|
{
|
|
|
|
if (!m_self) m_self = new DirCursor;
|
|
|
|
return m_self;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DirCursor::initCursors()
|
|
|
|
{
|
|
|
|
// TQCursor c(TQt::ArrowCursor);
|
|
|
|
// TQBitmap bitmap = *c.bitmap();
|
|
|
|
// TQBitmap mask = *c.mask();
|
|
|
|
// TQPixmap pm( bitmap->width(), bitmap->height() );
|
|
|
|
// pm.setMask(mask);
|
|
|
|
// pm = c.pi
|
|
|
|
// @todo finish
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#include "icndocument.moc"
|