/*************************************************************************** * 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 #include #include #include 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(m_selectList->activeItem()) ); } CNItem* ICNDocument::cnItemWithID( const TQString &id ) { return dynamic_cast(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(*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(qcanvasItem1); Node *endNode = dynamic_cast(qcanvasItem2); if ( (startNode && startNode->numCon( true, false ) > 2) || (endNode && endNode->numCon( true, false ) > 2) ) return false; Connector *startConnector = dynamic_cast(qcanvasItem1); Connector *endConnector = dynamic_cast(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="<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((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((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(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((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(qcanvasItem); node->removeNode(); } else if ( qcanvasItem->rtti() == ItemDocument::RTTI::CNItem || qcanvasItem->rtti() == ItemDocument::RTTI::DrawPart ) { Item *item = dynamic_cast(qcanvasItem); item->removeItem(); } else if ( qcanvasItem->rtti() == ItemDocument::RTTI::Connector || qcanvasItem->rtti() == ItemDocument::RTTI::ConnectorLine ) { Connector *connector = dynamic_cast(qcanvasItem); if (!connector) connector = (dynamic_cast(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(qcanvasItem) ) m_itemList.remove(item); else if ( qcanvasItem->rtti() == ItemDocument::RTTI::Node ) m_nodeList.remove( dynamic_cast(qcanvasItem) ); else if ( qcanvasItem->rtti() == ItemDocument::RTTI::Connector ) m_connectorList.remove( dynamic_cast(qcanvasItem) ); else kdError() << k_funcinfo << "Unknown 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(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"<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::tqclipboard()->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((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((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(*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(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((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=("<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(*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((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"