From 5de3dd4762ca33a0f92e79ffa4fe2ff67069d531 Mon Sep 17 00:00:00 2001 From: tpearson Date: Wed, 24 Feb 2010 01:49:02 +0000 Subject: Added KDE3 version of ktechlab git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/ktechlab@1095338 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- src/itemdocumentdata.cpp | 1340 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1340 insertions(+) create mode 100644 src/itemdocumentdata.cpp (limited to 'src/itemdocumentdata.cpp') diff --git a/src/itemdocumentdata.cpp b/src/itemdocumentdata.cpp new file mode 100644 index 0000000..a582231 --- /dev/null +++ b/src/itemdocumentdata.cpp @@ -0,0 +1,1340 @@ +/*************************************************************************** + * 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 "connector.h" +#include "ecnode.h" +#include "ecsubcircuit.h" +#include "flowcodedocument.h" +#include "flowcontainer.h" +#include "fpnode.h" +#include "itemdocumentdata.h" +#include "itemlibrary.h" +#include "picitem.h" +#include "pinmapping.h" + +#include +#include +#include +#include +#include +#include +#include + + +// Converts the QBitArray into a string (e.g. "F289A9E") that can be stored in an xml file +static QString toAsciiHex( QBitArray _data ) +{ + QBitArray data = _data; +// data = qCompress(data); + + // Pad out the data to a nice size + if ( (data.size() % 4) != 0 ) + { + data.detach(); + data.resize( data.size() + 4 - (data.size()%4) ); + } + + QString text; + for ( unsigned i = 0; i < data.size()/4; ++i ) + { + unsigned val = 0; + for ( unsigned j = 0; j < 4; ++j ) + val += (data[4*i+j] ? 1:0) << j; + + text += QString::number( val, 16 ); + } + return text; +} + +// Converts a string (e.g. "F289A9E") into a QBitArray, the opposite of the above function +static QBitArray toQBitArray( QString text ) +{ + unsigned size = text.length(); + QBitArray data(size*4); + + for ( unsigned i = 0; i < size; ++i ) + { + unsigned val = QString(text[i]).toInt( 0l, 16 ); + for ( unsigned j = 0; j < 4; ++j ) + data[4*i+j] = val & (1 << j); + } + +// data = qUncompress(data); + + return data; +} + + +//BEGIN class ItemDocumentData +ItemDocumentData::ItemDocumentData( uint documentType ) +{ + reset(); + m_documentType = documentType; +} + + +ItemDocumentData::~ItemDocumentData() +{ +} + + +void ItemDocumentData::reset() +{ + m_itemDataMap.clear(); + m_connectorDataMap.clear(); + m_nodeDataMap.clear(); + m_microData.reset(); + m_documentType = Document::dt_none; +} + + +bool ItemDocumentData::loadData( const KURL &url ) +{ + QString target; + if ( !KIO::NetAccess::download( url, target, 0l ) ) + { + // If the file could not be downloaded, for example does not + // exist on disk, NetAccess will tell us what error to use + KMessageBox::error( 0l, KIO::NetAccess::lastErrorString() ); + + return false; + } + + QFile file(target); + if ( !file.open( IO_ReadOnly ) ) + { + KMessageBox::sorry( 0l, i18n("Could not open %1 for reading").arg(target) ); + return false; + } + + QString xml; + QTextStream textStream( &file ); + while ( !textStream.eof() ) + xml += textStream.readLine() + '\n'; + + file.close(); + return fromXML(xml); +} + + +bool ItemDocumentData::fromXML( const QString &xml ) +{ + reset(); + + QDomDocument doc( "KTechlab" ); + QString errorMessage; + if ( !doc.setContent( xml, &errorMessage ) ) + { + KMessageBox::sorry( 0l, i18n("Couldn't parse xml:\n%1").arg(errorMessage) ); + return false; + } + + QDomElement root = doc.documentElement(); + + QDomNode node = root.firstChild(); + while ( !node.isNull() ) + { + QDomElement element = node.toElement(); + if ( !element.isNull() ) + { + const QString tagName = element.tagName(); + + if ( tagName == "item" ) + elementToItemData(element); + + else if ( tagName == "node" ) + elementToNodeData(element); + + else if ( tagName == "connector" ) + elementToConnectorData(element); + + else if ( tagName == "pic-settings" || tagName == "micro" ) + elementToMicroData(element); + + else if ( tagName == "code" ) + ; // do nothing - we no longer use this tag + + else + kdWarning() << k_funcinfo << "Unrecognised element tag name: "<= 0, then set by a FlowPart, so we don't need to worry about the angle / flip + if ( itemData.orientation >= 0 ) + { + node.setAttribute( "orientation", itemData.orientation ); + } + else + { + node.setAttribute( "angle", itemData.angleDegrees ); + node.setAttribute( "flip", itemData.flipped ); + } + + if ( !itemData.parentId.isEmpty() ) + node.setAttribute( "parent", itemData.parentId ); + + const QStringMap::const_iterator stringEnd = itemData.dataString.end(); + for ( QStringMap::const_iterator it = itemData.dataString.begin(); it != stringEnd; ++it ) + { + QDomElement e = doc.createElement("data"); + node.appendChild(e); + e.setAttribute( "id", it.key() ); + e.setAttribute( "type", "string" ); + e.setAttribute( "value", it.data() ); + } + + const DoubleMap::const_iterator numberEnd = itemData.dataNumber.end(); + for ( DoubleMap::const_iterator it = itemData.dataNumber.begin(); it != numberEnd; ++it ) + { + QDomElement e = doc.createElement("data"); + node.appendChild(e); + e.setAttribute( "id", it.key() ); + e.setAttribute( "type", "number" ); + e.setAttribute( "value", QString::number(it.data()) ); + } + + const QColorMap::const_iterator colorEnd = itemData.dataColor.end(); + for ( QColorMap::const_iterator it = itemData.dataColor.begin(); it != colorEnd; ++it ) + { + QDomElement e = doc.createElement("data"); + node.appendChild(e); + e.setAttribute( "id", it.key() ); + e.setAttribute( "type", "color" ); + e.setAttribute( "value", it.data().name() ); + } + + const QBitArrayMap::const_iterator rawEnd = itemData.dataRaw.end(); + for ( QBitArrayMap::const_iterator it = itemData.dataRaw.begin(); it != rawEnd; ++it ) + { + QDomElement e = doc.createElement("data"); + node.appendChild(e); + e.setAttribute( "id", it.key() ); + e.setAttribute( "type", "raw" ); + e.setAttribute( "value", toAsciiHex(it.data()) ); + } + + const BoolMap::const_iterator boolEnd = itemData.dataBool.end(); + for ( BoolMap::const_iterator it = itemData.dataBool.begin(); it != boolEnd; ++it ) + { + QDomElement e = doc.createElement("data"); + node.appendChild(e); + e.setAttribute( "id", it.key() ); + e.setAttribute( "type", "bool" ); + e.setAttribute( "value", QString::number(it.data()) ); + } + + const BoolMap::const_iterator buttonEnd = itemData.buttonMap.end(); + for ( BoolMap::const_iterator it = itemData.buttonMap.begin(); it != buttonEnd; ++it ) + { + QDomElement e = doc.createElement("button"); + node.appendChild(e); + e.setAttribute( "id", it.key() ); + e.setAttribute( "state", QString::number(it.data()) ); + } + + const IntMap::const_iterator sliderEnd = itemData.sliderMap.end(); + for ( IntMap::const_iterator it = itemData.sliderMap.begin(); it != sliderEnd; ++it ) + { + QDomElement e = doc.createElement("slider"); + node.appendChild(e); + e.setAttribute( "id", it.key() ); + e.setAttribute( "value", QString::number(it.data()) ); + } + + return node; +} + + +void ItemDocumentData::elementToItemData( QDomElement element ) +{ + QString id = element.attribute( "id", QString::null ); + if ( id.isNull() ) + { + kdError() << k_funcinfo << "Could not find id in element" << endl; + return; + } + + ItemData itemData; + itemData.type = element.attribute( "type", QString::null ); + itemData.x = element.attribute( "x", "120" ).toInt(); + itemData.y = element.attribute( "y", "120" ).toInt(); + itemData.z = element.attribute( "z", "-1" ).toInt(); + + if ( element.hasAttribute("width") && + element.hasAttribute("height") ) + { + itemData.setSize = true; + itemData.size = QRect( element.attribute( "offset-x", "0" ).toInt(), + element.attribute( "offset-y", "0" ).toInt(), + element.attribute( "width", "120" ).toInt(), + element.attribute( "height", "120" ).toInt() ); + } + else + itemData.setSize = false; + + itemData.angleDegrees = element.attribute( "angle", "0" ).toInt(); + itemData.flipped = element.attribute( "flip", "0" ).toInt(); + itemData.orientation = element.attribute( "orientation", "-1" ).toInt(); + itemData.parentId = element.attribute( "parent", QString::null ); + + m_itemDataMap[id] = itemData; + + QDomNode node = element.firstChild(); + while ( !node.isNull() ) + { + QDomElement childElement = node.toElement(); + if ( !childElement.isNull() ) + { + const QString tagName = childElement.tagName(); + + if ( tagName == "item" ) + { + // We're reading in a file saved in the older format, with + // child items nestled, so we must specify that the new item + // has the currently parsed item as its parent. + elementToItemData(childElement); + QString childId = childElement.attribute( "id", QString::null ); + if ( !childId.isNull() ) + m_itemDataMap[childId].parentId = id; + } + + else if ( tagName == "data" ) + { + QString dataId = childElement.attribute( "id", QString::null ); + if ( !dataId.isNull() ) + { + QString dataType = childElement.attribute( "type", QString::null ); + QString value = childElement.attribute( "value", QString::null ); + + if ( dataType == "string" || dataType == "multiline" ) + m_itemDataMap[id].dataString[dataId] = value; + else if ( dataType == "number" ) + m_itemDataMap[id].dataNumber[dataId] = value.toDouble(); + else if ( dataType == "color" ) + m_itemDataMap[id].dataColor[dataId] = QColor(value); + else if ( dataType == "raw" ) + m_itemDataMap[id].dataRaw[dataId] = toQBitArray(value); + else if ( dataType == "bool" ) + m_itemDataMap[id].dataBool[dataId] = bool(value.toInt()); + else + kdError() << k_funcinfo << "Unknown data type of \""<itemList() ); + + if ( ICNDocument *icnd = dynamic_cast(itemDocument) ) + { + addConnectors( icnd->connectorList() ); + addNodes( icnd->nodeList() ); + + if ( FlowCodeDocument *fcd = dynamic_cast(itemDocument) ) + { + if ( fcd->microSettings() ) + setMicroData( fcd->microSettings()->microData() ); + } + } + + m_documentType = itemDocument->type(); +} + + +void ItemDocumentData::generateUniqueIDs( ItemDocument *itemDocument ) +{ + if (!itemDocument) + return; + + QStringMap replaced; + replaced[""] = QString::null; + replaced[QString::null] = QString::null; + + ItemDataMap newItemDataMap; + ConnectorDataMap newConnectorDataMap; + NodeDataMap newNodeDataMap; + + //BEGIN Go through and replace the old ids + { + const ItemDataMap::iterator end = m_itemDataMap.end(); + for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != end; ++it ) + { + if ( !replaced.contains( it.key() ) ) + replaced[it.key()] = itemDocument->generateUID(it.key()); + + newItemDataMap[replaced[it.key()]] = it.data(); + } + } + { + const NodeDataMap::iterator end = m_nodeDataMap.end(); + for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != end; ++it ) + { + if ( !replaced.contains( it.key() ) ) + replaced[it.key()] = itemDocument->generateUID(it.key()); + + newNodeDataMap[replaced[it.key()]] = it.data(); + } + } + { + const ConnectorDataMap::iterator end = m_connectorDataMap.end(); + for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != end; ++it ) + { + if ( !replaced.contains( it.key() ) ) + replaced[it.key()] = itemDocument->generateUID(it.key()); + + newConnectorDataMap[replaced[it.key()]] = it.data(); + } + } + //END Go through and replace the old ids + + //BEGIN Go through and replace the internal references to the ids + { + const ItemDataMap::iterator end = newItemDataMap.end(); + for ( ItemDataMap::iterator it = newItemDataMap.begin(); it != end; ++it ) + { + it.data().parentId = replaced[it.data().parentId]; + } + } + { + const ConnectorDataMap::iterator end = newConnectorDataMap.end(); + for ( ConnectorDataMap::iterator it = newConnectorDataMap.begin(); it != end; ++it ) + { + it.data().startNodeParent = replaced[it.data().startNodeParent]; + it.data().endNodeParent = replaced[it.data().endNodeParent]; + + it.data().startNodeId = replaced[it.data().startNodeId]; + it.data().endNodeId = replaced[it.data().endNodeId]; + } + } + //END Go through and replace the internal references to the ids + + + m_itemDataMap = newItemDataMap; + m_connectorDataMap = newConnectorDataMap; + m_nodeDataMap = newNodeDataMap; +} + + +void ItemDocumentData::translateContents( int dx, int dy ) +{ + //BEGIN Go through and replace the old ids + { + const ItemDataMap::iterator end = m_itemDataMap.end(); + for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != end; ++it ) + { + it.data().x += dx; + it.data().y += dx; + } + } + { + const NodeDataMap::iterator end = m_nodeDataMap.end(); + for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != end; ++it ) + { + it.data().x += dx; + it.data().y += dy; + } + } + { + const ConnectorDataMap::iterator end = m_connectorDataMap.end(); + for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != end; ++it ) + { + const QPointList::iterator routeEnd = it.data().route.end(); + for ( QPointList::iterator routeIt = it.data().route.begin(); routeIt != routeEnd; ++routeIt ) + { + *routeIt += QPoint( dx/8, dy/8 ); + } + } + } +} + + +void ItemDocumentData::restoreDocument( ItemDocument *itemDocument ) +{ + if ( !itemDocument ) + return; + + ICNDocument *icnd = dynamic_cast(itemDocument); + FlowCodeDocument *fcd = dynamic_cast(icnd); + if ( fcd && !m_microData.id.isEmpty() ) + { + fcd->setPicType(m_microData.id); + fcd->microSettings()->restoreFromMicroData(m_microData); + } + + mergeWithDocument(itemDocument,false); + + { + ItemList removeItems = itemDocument->itemList(); + removeItems.remove((Item*)0l); + + const ItemDataMap::iterator end = m_itemDataMap.end(); + for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != end; ++it ) + removeItems.remove( itemDocument->itemWithID(it.key()) ); + + const ItemList::iterator removeEnd = removeItems.end(); + for ( ItemList::iterator it = removeItems.begin(); it != removeEnd; ++it ) + { + if ( (*it)->canvas() && (*it)->type() != PicItem::typeString() ) + (*it)->removeItem(); + } + } + + if (icnd) + { + { + NodeList removeNodes = icnd->nodeList(); + removeNodes.remove((Node*)0l); + + const NodeDataMap::iterator end = m_nodeDataMap.end(); + for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != end; ++it ) + removeNodes.remove( icnd->nodeWithID( it.key() ) ); + + const NodeList::iterator removeEnd = removeNodes.end(); + for ( NodeList::iterator it = removeNodes.begin(); it != removeEnd; ++it ) + { + if ( (*it)->canvas() && !(*it)->isChildNode() ) + (*it)->removeNode(); + } + } + { + ConnectorList removeConnectors = icnd->connectorList(); + removeConnectors.remove((Connector*)0l); + + const ConnectorDataMap::iterator end = m_connectorDataMap.end(); + for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != end; ++it ) + removeConnectors.remove( icnd->connectorWithID(it.key()) ); + + const ConnectorList::iterator removeEnd = removeConnectors.end(); + for ( ConnectorList::iterator it = removeConnectors.begin(); it != removeEnd; ++it ) + { + if ( (*it)->canvas() ) + (*it)->removeConnector(); + } + } + } + + itemDocument->flushDeleteList(); +} + + +void ItemDocumentData::mergeWithDocument( ItemDocument *itemDocument, bool selectNew ) +{ + if ( !itemDocument ) + return; + + ICNDocument *icnd = dynamic_cast(itemDocument); + + //BEGIN Restore Nodes + if (icnd) + { + const NodeDataMap::iterator nodeEnd = m_nodeDataMap.end(); + for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != nodeEnd; ++it ) + { + if ( !icnd->nodeWithID( it.key() ) ) + { + QString id = it.key(); + if ( itemDocument->type() == Document::dt_circuit ) + new ECNode( icnd, Node::ec_junction, Node::dir_up, QPoint( int(it.data().x), int(it.data().y) ), &id ); + + else if ( itemDocument->type() == Document::dt_flowcode ) + new FPNode( icnd, Node::fp_junction, Node::dir_up, QPoint( int(it.data().x), int(it.data().y) ), &id ); + } + } + for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != nodeEnd; ++it ) + { + Node *node = icnd->nodeWithID( it.key() ); + if (node) + node->move( it.data().x, it.data().y ); + } + } + //END Restore Nodes + + + //BEGIN Restore items + const ItemDataMap::iterator itemEnd = m_itemDataMap.end(); + for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != itemEnd; ++it ) + { + if ( !it.data().type.isEmpty() && !itemDocument->itemWithID( it.key() ) ) + { + Item *item = itemLibrary()->createItem( it.data().type, itemDocument, false, it.key(), false ); + if ( item && !itemDocument->isValidItem(item) ) + { + kdWarning() << "Attempted to create invalid item with id: " << it.key() << endl; + item->removeItem(); + itemDocument->flushDeleteList(); + item = 0l; + } + if (item) + { + //HACK We move the item now before restoreFromItemData is called later, in case it is to be parented + //(as we don't want to move children)... + item->move( it.data().x, it.data().y ); + } + } + } + for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != itemEnd; ++it ) + { + Item *item = itemDocument->itemWithID(it.key()); + if (!item) + continue; + + item->restoreFromItemData( it.data() ); + item->finishedCreation(); + if (selectNew) + itemDocument->select(item); + item->show(); + } + //END Restore Items + + //BEGIN Restore Connectors + if (icnd) + { + const ConnectorDataMap::iterator connectorEnd = m_connectorDataMap.end(); + for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != connectorEnd; ++it ) + { + if ( icnd->connectorWithID( it.key() ) ) + continue; + + QString id = it.key(); + Node *startNode = 0l; + Node *endNode = 0l; + + if ( it.data().startNodeIsChild ) + { + CNItem *item = icnd->cnItemWithID( it.data().startNodeParent ); + if (!item) + kdError() << k_funcinfo << "Unable to find node parent with id: "<childNode( it.data().startNodeCId ); + } + else + startNode = icnd->nodeWithID( it.data().startNodeId ); + + if ( it.data().endNodeIsChild ) + { + CNItem *item = icnd->cnItemWithID( it.data().endNodeParent ); + if (!item) + kdError() << k_funcinfo << "Unable to find node parent with id: "<childNode( it.data().endNodeCId ); + } + else + endNode = icnd->nodeWithID( it.data().endNodeId ); + + if ( !startNode || !endNode ) + { + kdError() << k_funcinfo << "End and start nodes for the connector do not both exist" << endl; + } + else + { + Connector *connector = new Connector( startNode, endNode, icnd, &id ); + + startNode->addOutputConnector(connector); + endNode->addInputConnector(connector); + } + } + for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != connectorEnd; ++it ) + { + Connector *connector = icnd->connectorWithID( it.key() ); + if (connector) + { + connector->restoreFromConnectorData( it.data() ); + if (selectNew) + icnd->select(connector); + } + } + } + //END Restore Connectors + + // This is kind of hackish, but never mind + if ( FlowCodeDocument *fcd = dynamic_cast(itemDocument) ) + { + const ItemList fcdItems = fcd->itemList(); + const ItemList::const_iterator fcdItemsEnd = fcdItems.constEnd(); + for ( ItemList::const_iterator it = fcdItems.constBegin(); it != fcdItemsEnd; ++it ) + { + if ( FlowContainer * fc = dynamic_cast((Item*)*it) ) + fc->updateContainedVisibility(); + } + } +} + + +void ItemDocumentData::setMicroData( const MicroData &data ) +{ + m_microData = data; +} + + +void ItemDocumentData::addItems( const ItemList &itemList ) +{ + const ItemList::const_iterator end = itemList.constEnd(); + for ( ItemList::const_iterator it = itemList.constBegin(); it != end; ++it ) + { + if ( *it && (*it)->canvas() && (*it)->type() != PicItem::typeString() ) + addItemData( (*it)->itemData(), (*it)->id() ); + } +} + + +void ItemDocumentData::addConnectors( const ConnectorList &connectorList ) +{ + const ConnectorList::const_iterator end = connectorList.constEnd(); + for ( ConnectorList::const_iterator it = connectorList.constBegin(); it != end; ++it ) + { + if ( *it && (*it)->canvas() ) + { + if ( (*it)->startNode() && (*it)->endNode() ) + addConnectorData( (*it)->connectorData(), (*it)->id() ); + + else + kdDebug() << k_funcinfo << " *it="<<*it<<" (*it)->startNode()="<<(*it)->startNode()<<" (*it)->endNode()="<<(*it)->endNode()<canvas() && !(*it)->isChildNode() ) + addNodeData( (*it)->nodeData(), (*it)->id() ); + } +} + + +void ItemDocumentData::addItemData( ItemData itemData, QString id ) +{ + m_itemDataMap[id] = itemData; +} + + +void ItemDocumentData::addConnectorData( ConnectorData connectorData, QString id ) +{ + m_connectorDataMap[id] = connectorData; +} + + +void ItemDocumentData::addNodeData( NodeData nodeData, QString id ) +{ + m_nodeDataMap[id] = nodeData; +} +//END class ItemDocumentData + + +//BEGIN class ItemData +ItemData::ItemData() +{ + x = 0; + y = 0; + z = -1; + angleDegrees = 0; + flipped = false; + orientation = -1; + setSize = false; +} +//END class ItemData + + +//BEGIN class ConnectorData +ConnectorData::ConnectorData() +{ + manualRoute = false; + startNodeIsChild = false; + endNodeIsChild = false; +} +//END class ConnectorData + + +//BEGIN class NodeData +NodeData::NodeData() +{ + x = 0; + y = 0; +} +//END class NodeDaata + + +//BEGIN class PinData +PinData::PinData() +{ + type = PinSettings::pt_input; + state = PinSettings::ps_off; +} +//END class PinData + + +//BEGIN class MicroData +MicroData::MicroData() +{ +} + + +void MicroData::reset() +{ + id = QString::null; + pinMap.clear(); +} +//END class MicroData + + +//BEGIN class SubcircuitData +SubcircuitData::SubcircuitData() + : ItemDocumentData( Document::dt_circuit ) +{ +} + + +void SubcircuitData::initECSubcircuit( ECSubcircuit * ecSubcircuit ) +{ + if (!ecSubcircuit) + return; + + generateUniqueIDs( ecSubcircuit->itemDocument() ); + + // Generate a list of the External Connections, sorting by x coordinate + std::multimap< double, QString > extCon; + ItemDataMap::iterator itemEnd = m_itemDataMap.end(); + for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != itemEnd; ++it ) + { + if ( it.data().type == "ec/external_connection" ) + extCon.insert( std::make_pair( it.data().x, it.key() ) ); + } + + // How many external connections do we have? + ecSubcircuit->setNumExtCon(extCon.size()); + + // Sort the connections into the pins of the subcircuit by y coordinate + std::multimap< double, QString > leftPins; + std::multimap< double, QString > rightPins; + int at = 0; + int size = (extCon.size()/2) + (extCon.size()%2); + const std::multimap< double, QString >::iterator extConEnd = extCon.end(); + for ( std::multimap< double, QString >::iterator it = extCon.begin(); it != extConEnd; ++it ) + { + if ( at < size ) + leftPins.insert( std::make_pair( m_itemDataMap[it->second].y, it->second ) ); + else + rightPins.insert( std::make_pair( m_itemDataMap[it->second].y, it->second ) ); + at++; + } + + // Remove the external connections (recording their names and associated numerical position) + int nodeId = 0; + typedef QMap IntMap; + IntMap nodeMap; + const std::multimap< double, QString >::iterator leftPinsEnd = leftPins.end(); + for ( std::multimap< double, QString >::iterator it = leftPins.begin(); it != leftPinsEnd; ++it ) + { + nodeMap[ it->second ] = nodeId; + ecSubcircuit->setExtConName( nodeId, m_itemDataMap[ it->second ].dataString["name"].data() ); + nodeId++; + m_itemDataMap.remove( it->second ); + } + nodeId = extCon.size()-1; + const std::multimap< double, QString >::iterator rightPinsEnd = rightPins.end(); + for ( std::multimap< double, QString >::iterator it = rightPins.begin(); it != rightPinsEnd; ++it ) + { + nodeMap[ it->second ] = nodeId; + ecSubcircuit->setExtConName( nodeId, m_itemDataMap[ it->second ].dataString["name"].data() ); + nodeId--; + m_itemDataMap.remove( it->second ); + } + + // Replace connector references to the old External Connectors to the nodes + const ConnectorDataMap::iterator connectorEnd = m_connectorDataMap.end(); + for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != connectorEnd; ++it ) + { + if ( it.data().startNodeIsChild && nodeMap.contains(it.data().startNodeParent ) ) + { + it.data().startNodeCId = QString::number( nodeMap[it.data().startNodeParent] ); + it.data().startNodeParent = ecSubcircuit->id(); + + } + if ( it.data().endNodeIsChild && nodeMap.contains(it.data().endNodeParent ) ) + { + it.data().endNodeCId = QString::number( nodeMap[it.data().endNodeParent] ); + it.data().endNodeParent = ecSubcircuit->id(); + } + } + + // Create all the new stuff + mergeWithDocument( ecSubcircuit->itemDocument(), false ); + + // Parent and hide the new stuff + itemEnd = m_itemDataMap.end(); + for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != itemEnd; ++it) + { + Component * component = static_cast(ecSubcircuit->itemDocument()->itemWithID( it.key() )); + if (component) + { + component->setParentItem(ecSubcircuit); + component->updateConnectorPoints(false); + component->setVisible(false); + component->setCanvas(0l); + ecSubcircuit->connect( ecSubcircuit, SIGNAL(subcircuitDeleted()), component, SLOT(removeItem()) ); + } + } + for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != connectorEnd; ++it ) + { + Connector * connector = (static_cast(ecSubcircuit->itemDocument()))->connectorWithID( it.key() ); + if (connector) + { + connector->updateConnectorPoints(false); + connector->setVisible(false); + connector->setCanvas(0l); + ecSubcircuit->connect( ecSubcircuit, SIGNAL(subcircuitDeleted()), connector, SLOT(removeConnector()) ); + } + } + const NodeDataMap::iterator nodeEnd = m_nodeDataMap.end(); + for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != nodeEnd; ++it ) + { + Node * node = (static_cast(ecSubcircuit->itemDocument()))->nodeWithID( it.key() ); + if (node) + { + node->setVisible(false); + node->setCanvas(0l); + ecSubcircuit->connect( ecSubcircuit, SIGNAL(subcircuitDeleted()), node, SLOT(removeNode()) ); + } + } + + ecSubcircuit->doneSCInit(); +} +//END class SubcircuitData + -- cgit v1.2.3