itemdocumentdata.cpp

00001 /***************************************************************************
00002  *   Copyright (C) 2005 by David Saxton                                    *
00003  *   david@bluehaze.org                                                    *
00004  *                                                                         *
00005  *   This program is free software; you can redistribute it and/or modify  *
00006  *   it under the terms of the GNU General Public License as published by  *
00007  *   the Free Software Foundation; either version 2 of the License, or     *
00008  *   (at your option) any later version.                                   *
00009  ***************************************************************************/
00010 
00011 #include "connector.h"
00012 #include "ecnode.h"
00013 #include "ecsubcircuit.h"
00014 #include "flowcodedocument.h"
00015 #include "flowcontainer.h"
00016 #include "fpnode.h"
00017 #include "itemdocumentdata.h"
00018 #include "itemlibrary.h"
00019 #include "picitem.h"
00020 #include "pinmapping.h"
00021 
00022 #include <kdebug.h>
00023 #include <kio/netaccess.h>
00024 #include <klocale.h>
00025 #include <kmessagebox.h> 
00026 #include <ktempfile.h>
00027 #include <qbitarray.h>
00028 #include <qfile.h>
00029 
00030 
00031 // Converts the QBitArray into a string (e.g. "F289A9E") that can be stored in an xml file
00032 static QString toAsciiHex( QBitArray _data )
00033 {
00034         QBitArray data = _data;
00035 //      data = qCompress(data);
00036         
00037         // Pad out the data to a nice size
00038         if ( (data.size() % 4) != 0 )
00039         {
00040                 data.detach();
00041                 data.resize( data.size() + 4 - (data.size()%4) );
00042         }
00043         
00044         QString text;
00045         for ( unsigned i = 0; i < data.size()/4; ++i )
00046         {
00047                 unsigned val = 0;
00048                 for ( unsigned j = 0; j < 4; ++j )
00049                         val += (data[4*i+j] ? 1:0) << j;
00050                 
00051                 text += QString::number( val, 16 );
00052         }
00053         return text;
00054 }
00055 
00056 // Converts a string (e.g. "F289A9E") into a QBitArray, the opposite of the above function
00057 static QBitArray toQBitArray( QString text )
00058 {
00059         unsigned size = text.length();
00060         QBitArray data(size*4);
00061         
00062         for ( unsigned i = 0; i < size; ++i )
00063         {
00064                 unsigned val = QString(text[i]).toInt( 0, 16 );
00065                 for ( unsigned j = 0; j < 4; ++j )
00066                         data[4*i+j] = val & (1 << j);
00067         }
00068         
00069 //      data = qUncompress(data);
00070         
00071         return data;
00072 }
00073 
00074 
00075 //BEGIN class ItemDocumentData
00076 ItemDocumentData::ItemDocumentData( uint documentType )
00077 {
00078         reset();
00079         m_documentType = documentType;
00080 }
00081 
00082 
00083 ItemDocumentData::~ItemDocumentData()
00084 {
00085 }
00086 
00087 
00088 void ItemDocumentData::reset()
00089 {
00090         m_itemDataMap.clear();
00091         m_connectorDataMap.clear();
00092         m_nodeDataMap.clear();
00093         m_microData.reset();
00094         m_documentType = Document::dt_none;
00095 }
00096 
00097 
00098 bool ItemDocumentData::loadData( const KURL &url )
00099 {
00100         QString target;
00101         if ( !KIO::NetAccess::download( url, target, 0 ) )
00102         {
00103                 // If the file could not be downloaded, for example does not
00104                 // exist on disk, NetAccess will tell us what error to use
00105                 KMessageBox::error( 0, KIO::NetAccess::lastErrorString() );
00106                 
00107                 return false;
00108         }
00109         
00110         QFile file(target);
00111         if ( !file.open( IO_ReadOnly ) )
00112         {
00113                 KMessageBox::sorry( 0, i18n("Could not open %1 for reading").arg(target) );
00114                 return false;
00115         }
00116         
00117         QString xml;
00118         QTextStream textStream( &file );
00119         while ( !textStream.eof() )
00120                 xml += textStream.readLine() + '\n';
00121         
00122         file.close();
00123         return fromXML(xml);
00124 }
00125 
00126 
00127 bool ItemDocumentData::fromXML( const QString &xml )
00128 {
00129         reset();
00130         
00131         QDomDocument doc( "KTechlab" );
00132         QString errorMessage;
00133         if ( !doc.setContent( xml, &errorMessage ) )
00134         {
00135                 KMessageBox::sorry( 0, i18n("Couldn't parse xml:\n%1").arg(errorMessage) );
00136                 return false;
00137         }
00138         
00139         QDomElement root = doc.documentElement();
00140         
00141         QDomNode node = root.firstChild();
00142         while ( !node.isNull() )
00143         {
00144                 QDomElement element = node.toElement();
00145                 if ( !element.isNull() )
00146                 {
00147                         const QString tagName = element.tagName();
00148                         
00149                         if ( tagName == "item" )
00150                                 elementToItemData(element);
00151                         
00152                         else if ( tagName == "node" )
00153                                 elementToNodeData(element);
00154                         
00155                         else if ( tagName == "connector" )
00156                                 elementToConnectorData(element);
00157                         
00158                         else if ( tagName == "pic-settings" || tagName == "micro" )
00159                                 elementToMicroData(element);
00160                         
00161                         else if ( tagName == "code" )
00162                                 ; // do nothing - we no longer use this tag
00163                         
00164                         else
00165                                 kdWarning() << k_funcinfo << "Unrecognised element tag name: "<<tagName<<endl;
00166                 }
00167                 
00168                 node = node.nextSibling();
00169         }
00170         
00171         return true;
00172 }
00173 
00174 
00175 bool ItemDocumentData::saveData( const KURL &url )
00176 {
00177         
00178         if ( url.isLocalFile() )
00179         {
00180                 QFile file( url.path() );
00181                 if ( !file.open(IO_WriteOnly) )
00182                 {
00183                         KMessageBox::sorry( 0, i18n("Could not open '%1' for writing. Check that you have write permissions").arg(url.path()), i18n("Saving File") );
00184                         return false;
00185                 }
00186                 
00187                 QTextStream stream(&file);
00188                 stream << toXML();
00189                 file.close();
00190         }
00191         else
00192         {
00193                 KTempFile file;
00194                 *file.textStream() << toXML();
00195                 file.close();
00196                 
00197                 if ( !KIO::NetAccess::upload( file.name(), url, 0 ) )
00198                 {
00199                         KMessageBox::error( 0, KIO::NetAccess::lastErrorString() );
00200                         return false;
00201                 }
00202         }
00203                 
00204         return true;
00205 }
00206 
00207 
00208 QString ItemDocumentData::toXML()
00209 {
00210         QDomDocument doc("KTechlab");
00211         //TODO Add revision information to save file
00212         
00213         QDomElement root = doc.createElement("document");
00214         root.setAttribute( "type", documentTypeString() );
00215         doc.appendChild(root);
00216         
00217         {
00218                 const ItemDataMap::iterator end = m_itemDataMap.end();
00219                 for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != end; ++it )
00220                 {
00221                         QDomElement node = itemDataToElement( doc, it.data() );
00222                         node.setAttribute( "id", it.key() );
00223                         root.appendChild(node);
00224                 }
00225         }
00226         {
00227                 const ConnectorDataMap::iterator end = m_connectorDataMap.end();
00228                 for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != end; ++it )
00229                 {
00230                         QDomElement node = connectorDataToElement( doc, it.data() );
00231                         node.setAttribute( "id", it.key() );
00232                         root.appendChild(node);
00233                 }
00234         }
00235         {
00236                 const NodeDataMap::iterator end = m_nodeDataMap.end();
00237                 for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != end; ++it )
00238                 {
00239                         QDomElement node = nodeDataToElement( doc, it.data() );
00240                         node.setAttribute( "id", it.key() );
00241                         root.appendChild(node);
00242                 }
00243         }
00244         if ( m_documentType == Document::dt_flowcode )
00245         {
00246                 QDomElement node = microDataToElement(doc);
00247                 root.appendChild(node);
00248         }
00249         
00250         return doc.toString();
00251 }
00252 
00253 
00254 
00255 //BEGIN functions for generating / reading QDomElements
00256 QDomElement ItemDocumentData::microDataToElement( QDomDocument &doc )
00257 {
00258         QDomElement node = doc.createElement("micro");
00259         node.setAttribute( "id", m_microData.id );
00260         
00261         {
00262                 const PinMappingMap::iterator end = m_microData.pinMappings.end();
00263                 for ( PinMappingMap::iterator it = m_microData.pinMappings.begin(); it != end; ++it )
00264                 {
00265                         QDomElement pinMapNode = doc.createElement("pinmap");
00266                         
00267                         QString type;
00268                         switch ( it.data().type() )
00269                         {
00270                                 case PinMapping::SevenSegment:
00271                                         type = "sevensegment";
00272                                         break;
00273                                         
00274                                 case PinMapping::Keypad_4x3:
00275                                         type = "keypad_4x3";
00276                                         break;
00277                                         
00278                                 case PinMapping::Keypad_4x4:
00279                                         type = "keypad_4x4";
00280                                         break;
00281                                         
00282                                 case PinMapping::Invalid:
00283                                         break;
00284                         }
00285                         
00286                         pinMapNode.setAttribute( "id", it.key() );
00287                         pinMapNode.setAttribute( "type", type );
00288                         pinMapNode.setAttribute( "map", it.data().pins().join(" ") );
00289                         
00290                         node.appendChild(pinMapNode);
00291                 }
00292         }
00293         
00294         {
00295                 const PinDataMap::iterator end = m_microData.pinMap.end();
00296                 for ( PinDataMap::iterator it = m_microData.pinMap.begin(); it != end; ++it )
00297                 {
00298                         QDomElement pinNode = doc.createElement("pin");
00299                         
00300                         pinNode.setAttribute( "id", it.key() );
00301                         pinNode.setAttribute( "type", (it.data().type == PinSettings::pt_input) ? "input" : "output" );
00302                         pinNode.setAttribute( "state", (it.data().state == PinSettings::ps_off) ? "off" : "on" );
00303                         
00304                         node.appendChild(pinNode);
00305                 }
00306         }
00307         
00308         {
00309                 const QStringMap::iterator end = m_microData.variableMap.end();
00310                 for ( QStringMap::iterator it = m_microData.variableMap.begin(); it != end; ++it )
00311                 {
00312                         QDomElement variableNode = doc.createElement("variable");
00313                         
00314                         variableNode.setAttribute( "name", it.key() );
00315                         variableNode.setAttribute( "value", it.data() );
00316                         
00317                         node.appendChild(variableNode);
00318                 }
00319         }
00320         
00321         return node;
00322 }
00323 
00324 
00325 void ItemDocumentData::elementToMicroData( QDomElement element )
00326 {
00327         QString id = element.attribute( "id", QString::null );
00328         
00329         if ( id.isNull() )
00330                 id = element.attribute( "pic", QString::null );
00331         
00332         if ( id.isNull() )
00333         {
00334                 kdError() << k_funcinfo << "Could not find id in element" << endl;
00335                 return;
00336         }
00337         
00338         m_microData.reset();
00339         m_microData.id = id;
00340         
00341         QDomNode node = element.firstChild();
00342         while ( !node.isNull() )
00343         {
00344                 QDomElement childElement = node.toElement();
00345                 if ( !childElement.isNull() )
00346                 {
00347                         const QString tagName = childElement.tagName();
00348                         
00349                         if ( tagName == "pinmap" )
00350                         {
00351                                 QString id = childElement.attribute( "id", QString::null );
00352                                 QString typeString = childElement.attribute( "type", QString::null );
00353                                 
00354                                 if ( !id.isEmpty() && !typeString.isEmpty() )
00355                                 {
00356                                         PinMapping::Type type = PinMapping::Invalid;
00357                                         
00358                                         if ( typeString == "sevensegment" )
00359                                                 type = PinMapping::SevenSegment;
00360                                         
00361                                         else if ( typeString == "keypad_4x3" )
00362                                                 type = PinMapping::Keypad_4x3;
00363                                         
00364                                         else if ( typeString == "keypad_4x4" )
00365                                                 type = PinMapping::Keypad_4x4;
00366                                         
00367                                         PinMapping pinMapping( type );
00368                                         pinMapping.setPins( QStringList::split( " ", childElement.attribute( "map", 0 ) ) );
00369                                         
00370                                         m_microData.pinMappings[id] = pinMapping;
00371                                 }
00372                         }
00373                         
00374                         else if ( tagName == "pin" )
00375                         {
00376                                 QString pinID = childElement.attribute( "id", QString::null );
00377                                 if ( !pinID.isEmpty() )
00378                                 {
00379                                         m_microData.pinMap[pinID].type = (childElement.attribute( "type", "input" ) == "input" ) ? PinSettings::pt_input : PinSettings::pt_output;
00380                                         m_microData.pinMap[pinID].state = (childElement.attribute( "state", "off" ) == "off" ) ? PinSettings::ps_off : PinSettings::ps_on;
00381                                 }
00382                         }
00383                         
00384                         else if ( tagName == "variable" )
00385                         {
00386                                 QString variableId = childElement.attribute( "name", QString::null );
00387                                 m_microData.variableMap[variableId] = childElement.attribute( "value", QString::null );
00388                         }
00389                         
00390                         else
00391                                 kdError() << k_funcinfo << "Unrecognised element tag name: "<<tagName<<endl;
00392                 }
00393                 
00394                 node = node.nextSibling();
00395         }
00396 }
00397 
00398 
00399 QDomElement ItemDocumentData::itemDataToElement( QDomDocument &doc, const ItemData &itemData )
00400 {
00401         QDomElement node = doc.createElement("item");
00402         node.setAttribute( "type", itemData.type );
00403         node.setAttribute( "x", itemData.x );
00404         node.setAttribute( "y", itemData.y );
00405         if ( itemData.z != -1 )
00406                 node.setAttribute( "z", itemData.z );
00407         if ( itemData.setSize )
00408         {
00409                 node.setAttribute( "offset-x", itemData.size.x() );
00410                 node.setAttribute( "offset-y", itemData.size.y() );
00411                 node.setAttribute( "width", itemData.size.width() );
00412                 node.setAttribute( "height", itemData.size.height() );
00413         }
00414         
00415         // If the "orientation" is >= 0, then set by a FlowPart, so we don't need to worry about the angle / flip
00416         if ( itemData.orientation >= 0 )
00417         {
00418                 node.setAttribute( "orientation", itemData.orientation );
00419         }
00420         else
00421         {
00422                 node.setAttribute( "angle", itemData.angleDegrees );
00423                 node.setAttribute( "flip", itemData.flipped );
00424         }
00425         
00426         if ( !itemData.parentId.isEmpty() )
00427                 node.setAttribute( "parent", itemData.parentId );
00428         
00429         const QStringMap::const_iterator stringEnd = itemData.dataString.end();
00430         for ( QStringMap::const_iterator it = itemData.dataString.begin(); it != stringEnd; ++it )
00431         {
00432                 QDomElement e = doc.createElement("data");
00433                 node.appendChild(e);
00434                 e.setAttribute( "id", it.key() );
00435                 e.setAttribute( "type", "string" );
00436                 e.setAttribute( "value", it.data() );
00437         }
00438         
00439         const DoubleMap::const_iterator numberEnd = itemData.dataNumber.end();
00440         for ( DoubleMap::const_iterator it = itemData.dataNumber.begin(); it != numberEnd; ++it )
00441         {
00442                 QDomElement e = doc.createElement("data");
00443                 node.appendChild(e);
00444                 e.setAttribute( "id", it.key() );
00445                 e.setAttribute( "type", "number" );
00446                 e.setAttribute( "value", QString::number(it.data()) );
00447         }
00448         
00449         const QColorMap::const_iterator colorEnd = itemData.dataColor.end();
00450         for ( QColorMap::const_iterator it = itemData.dataColor.begin(); it != colorEnd; ++it )
00451         {
00452                 QDomElement e = doc.createElement("data");
00453                 node.appendChild(e);
00454                 e.setAttribute( "id", it.key() );
00455                 e.setAttribute( "type", "color" );
00456                 e.setAttribute( "value", it.data().name() );
00457         }
00458         
00459         const QBitArrayMap::const_iterator rawEnd = itemData.dataRaw.end();
00460         for ( QBitArrayMap::const_iterator it = itemData.dataRaw.begin(); it != rawEnd; ++it )
00461         {
00462                 QDomElement e = doc.createElement("data");
00463                 node.appendChild(e);
00464                 e.setAttribute( "id", it.key() );
00465                 e.setAttribute( "type", "raw" );
00466                 e.setAttribute( "value", toAsciiHex(it.data()) );
00467         }
00468         
00469         const BoolMap::const_iterator boolEnd = itemData.dataBool.end();
00470         for ( BoolMap::const_iterator it = itemData.dataBool.begin(); it != boolEnd; ++it )
00471         {
00472                 QDomElement e = doc.createElement("data");
00473                 node.appendChild(e);
00474                 e.setAttribute( "id", it.key() );
00475                 e.setAttribute( "type", "bool" );
00476                 e.setAttribute( "value", QString::number(it.data()) );
00477         }
00478         
00479         const BoolMap::const_iterator buttonEnd = itemData.buttonMap.end();
00480         for ( BoolMap::const_iterator it = itemData.buttonMap.begin(); it != buttonEnd; ++it )
00481         {
00482                 QDomElement e = doc.createElement("button");
00483                 node.appendChild(e);
00484                 e.setAttribute( "id", it.key() );
00485                 e.setAttribute( "state", QString::number(it.data()) );
00486         }
00487         
00488         const IntMap::const_iterator sliderEnd = itemData.sliderMap.end();
00489         for ( IntMap::const_iterator it = itemData.sliderMap.begin(); it != sliderEnd; ++it )
00490         {
00491                 QDomElement e = doc.createElement("slider");
00492                 node.appendChild(e);
00493                 e.setAttribute( "id", it.key() );
00494                 e.setAttribute( "value", QString::number(it.data()) );
00495         }
00496         
00497         return node;
00498 }
00499 
00500 
00501 void ItemDocumentData::elementToItemData( QDomElement element )
00502 {
00503         QString id = element.attribute( "id", QString::null );
00504         if ( id.isNull() )
00505         {
00506                 kdError() << k_funcinfo << "Could not find id in element" << endl;
00507                 return;
00508         }
00509         
00510         ItemData itemData;
00511         itemData.type = element.attribute( "type", QString::null );
00512         itemData.x = element.attribute( "x", "120" ).toInt();
00513         itemData.y = element.attribute( "y", "120" ).toInt();
00514         itemData.z = element.attribute( "z", "-1" ).toInt();
00515         
00516         if ( element.hasAttribute("width") &&
00517                         element.hasAttribute("height") )
00518         {
00519                 itemData.setSize = true;
00520                 itemData.size = QRect( element.attribute( "offset-x", "0" ).toInt(),
00521                                                            element.attribute( "offset-y", "0" ).toInt(),
00522                                                            element.attribute( "width", "120" ).toInt(),
00523                                                            element.attribute( "height", "120" ).toInt() );
00524         }
00525         else
00526                 itemData.setSize = false;
00527         
00528         itemData.angleDegrees = element.attribute( "angle", "0" ).toInt();
00529         itemData.flipped = element.attribute( "flip", "0" ).toInt();
00530         itemData.orientation = element.attribute( "orientation", "-1" ).toInt();
00531         itemData.parentId = element.attribute( "parent", QString::null );
00532         
00533         m_itemDataMap[id] = itemData;
00534         
00535         QDomNode node = element.firstChild();
00536         while ( !node.isNull() )
00537         {
00538                 QDomElement childElement = node.toElement();
00539                 if ( !childElement.isNull() )
00540                 {
00541                         const QString tagName = childElement.tagName();
00542                         
00543                         if ( tagName == "item" )
00544                         {
00545                                 // We're reading in a file saved in the older format, with
00546                                 // child items nestled, so we must specify that the new item
00547                                 // has the currently parsed item as its parent.
00548                                 elementToItemData(childElement);
00549                                 QString childId = childElement.attribute( "id", QString::null );
00550                                 if ( !childId.isNull() )
00551                                         m_itemDataMap[childId].parentId = id;
00552                         }
00553                         
00554                         else if ( tagName == "data" )
00555                         {
00556                                 QString dataId = childElement.attribute( "id", QString::null );
00557                                 if ( !dataId.isNull() )
00558                                 {
00559                                         QString dataType = childElement.attribute( "type", QString::null );
00560                                         QString value = childElement.attribute( "value", QString::null );
00561                                         
00562                                         if ( dataType == "string" || dataType == "multiline" )
00563                                                 m_itemDataMap[id].dataString[dataId] = value;
00564                                         else if ( dataType == "number" )
00565                                                 m_itemDataMap[id].dataNumber[dataId] = value.toDouble();
00566                                         else if ( dataType == "color" )
00567                                                 m_itemDataMap[id].dataColor[dataId] = QColor(value);
00568                                         else if ( dataType == "raw" )
00569                                                 m_itemDataMap[id].dataRaw[dataId] = toQBitArray(value);
00570                                         else if ( dataType == "bool" )
00571                                                 m_itemDataMap[id].dataBool[dataId] = bool(value.toInt());
00572                                         else
00573                                                 kdError() << k_funcinfo << "Unknown data type of \""<<dataType<<"\" with id \""<<dataId<<"\""<<endl;
00574                                 }
00575                         }
00576                         
00577                         else if ( tagName == "button" )
00578                         {
00579                                 QString buttonId = childElement.attribute( "id", QString::null );
00580                                 if ( !buttonId.isNull() )
00581                                         m_itemDataMap[id].buttonMap[buttonId] = childElement.attribute( "state", "0" ).toInt();
00582                         }
00583                         
00584                         else if ( tagName == "slider" )
00585                         {
00586                                 QString sliderId = childElement.attribute( "id", QString::null );
00587                                 if ( !sliderId.isNull() )
00588                                         m_itemDataMap[id].sliderMap[sliderId] = childElement.attribute( "value", "0" ).toInt();
00589                         }
00590                         
00591                         else if ( tagName == "child-node" )
00592                                 ; // Tag name was used in 0.1 file save format
00593                         
00594                         else
00595                                 kdError() << k_funcinfo << "Unrecognised element tag name: "<<tagName<<endl;
00596                 }
00597                 
00598                 node = node.nextSibling();
00599         }
00600 }
00601 
00602 
00603 QDomElement ItemDocumentData::nodeDataToElement( QDomDocument &doc, const NodeData &nodeData )
00604 {
00605         QDomElement node = doc.createElement("node");
00606         node.setAttribute( "x", nodeData.x );
00607         node.setAttribute( "y", nodeData.y );
00608         return node;
00609 }
00610 
00611 
00612 void ItemDocumentData::elementToNodeData( QDomElement element )
00613 {
00614         QString id = element.attribute( "id", QString::null );
00615         if ( id.isNull() )
00616         {
00617                 kdError() << k_funcinfo << "Could not find id in element" << endl;
00618                 return;
00619         }
00620         
00621         NodeData nodeData;
00622         nodeData.x = element.attribute( "x", "120" ).toInt();
00623         nodeData.y = element.attribute( "y", "120" ).toInt();
00624         
00625         m_nodeDataMap[id] = nodeData;
00626 }
00627 
00628 
00629 QDomElement ItemDocumentData::connectorDataToElement( QDomDocument &doc, const ConnectorData &connectorData )
00630 {
00631         QDomElement node = doc.createElement("connector");
00632         
00633         node.setAttribute( "manual-route", connectorData.manualRoute );
00634         
00635         QString route;
00636         const QPointList::const_iterator end = connectorData.route.end();
00637         for ( QPointList::const_iterator it = connectorData.route.begin(); it != end; ++it )
00638         {
00639                 route.append( QString::number((*it).x())+"," );
00640                 route.append( QString::number((*it).y())+"," );
00641         }
00642         node.setAttribute( "route", route );
00643         
00644         if ( connectorData.startNodeIsChild )
00645         {
00646                 node.setAttribute( "start-node-is-child", 1 );
00647                 node.setAttribute( "start-node-cid", connectorData.startNodeCId );
00648                 node.setAttribute( "start-node-parent", connectorData.startNodeParent );
00649         }
00650         else
00651         {
00652                 node.setAttribute( "start-node-is-child", 0 );
00653                 node.setAttribute( "start-node-id", connectorData.startNodeId );
00654         }
00655         
00656         
00657         if ( connectorData.endNodeIsChild )
00658         {
00659                 node.setAttribute( "end-node-is-child", 1 );
00660                 node.setAttribute( "end-node-cid", connectorData.endNodeCId );
00661                 node.setAttribute( "end-node-parent", connectorData.endNodeParent );
00662         }
00663         else
00664         {
00665                 node.setAttribute( "end-node-is-child", 0 );
00666                 node.setAttribute( "end-node-id", connectorData.endNodeId );
00667         }
00668         
00669         return node;
00670 }
00671 
00672 
00673 void ItemDocumentData::elementToConnectorData( QDomElement element )
00674 {
00675         QString id = element.attribute( "id", QString::null );
00676         if ( id.isNull() )
00677         {
00678                 kdError() << k_funcinfo << "Could not find id in element" << endl;
00679                 return;
00680         }
00681         
00682         ConnectorData connectorData;
00683         
00684         connectorData.manualRoute = element.attribute( "manual-route", "0" );
00685         QString route = element.attribute( "route", "" );
00686         
00687         QStringList points = QStringList::split( ",", route );
00688         const QStringList::iterator end = points.end();
00689         for ( QStringList::iterator it = points.begin(); it != end; ++it )
00690         {
00691                 int x = (*it).toInt();
00692                 it++;
00693                 if ( it != end )
00694                 {
00695                         int y = (*it).toInt();
00696                         connectorData.route.append( QPoint(x,y) );
00697                 }
00698         }
00699         
00700         connectorData.startNodeIsChild = element.attribute( "start-node-is-child", "0" ).toInt();
00701         if ( connectorData.startNodeIsChild )
00702         {
00703                 connectorData.startNodeCId = element.attribute( "start-node-cid", QString::null );
00704                 connectorData.startNodeParent = element.attribute( "start-node-parent", QString::null );
00705         }
00706         else
00707                 connectorData.startNodeId = element.attribute( "start-node-id", QString::null );
00708         
00709         
00710         connectorData.endNodeIsChild = element.attribute( "end-node-is-child", "0" ).toInt();
00711         if ( connectorData.endNodeIsChild )
00712         {
00713                 connectorData.endNodeCId = element.attribute( "end-node-cid", QString::null );
00714                 connectorData.endNodeParent = element.attribute( "end-node-parent", QString::null );
00715         }
00716         else
00717                 connectorData.endNodeId = element.attribute( "end-node-id", QString::null );
00718         
00719         m_connectorDataMap[id] = connectorData;
00720 }
00721 //END functions for generating / reading QDomElements
00722 
00723 
00724 
00725 QString ItemDocumentData::documentTypeString() const
00726 {
00727         switch (m_documentType)
00728         {
00729                 case Document::dt_circuit:
00730                         return "circuit";
00731                         break;
00732                 case Document::dt_flowcode:
00733                         return "flowcode";
00734                         break;
00735                 case Document::dt_mechanics:
00736                         return "mechanics";
00737                         break;
00738                 case Document::dt_text:
00739                 case Document::dt_none:
00740                 default:
00741                         return "none";
00742                         break;
00743         }
00744 }
00745 
00746 
00747 QString ItemDocumentData::revisionString() const
00748 {
00749         return "1";
00750 }
00751 
00752 
00753 void ItemDocumentData::saveDocumentState( ItemDocument *itemDocument )
00754 {
00755         if (!itemDocument)
00756                 return;
00757         
00758         reset();
00759         
00760         addItems( itemDocument->itemList() );
00761         
00762         if ( ICNDocument *icnd = dynamic_cast<ICNDocument*>(itemDocument) )
00763         {
00764                 addConnectors( icnd->connectorList() );
00765                 addNodes( icnd->nodeList() );
00766         
00767                 if ( FlowCodeDocument *fcd = dynamic_cast<FlowCodeDocument*>(itemDocument) )
00768                 {
00769                         if ( fcd->microSettings() )
00770                                 setMicroData( fcd->microSettings()->microData() );
00771                 }
00772         }
00773         
00774         m_documentType = itemDocument->type();
00775 }
00776 
00777 
00778 void ItemDocumentData::generateUniqueIDs( ItemDocument *itemDocument )
00779 {
00780         if (!itemDocument)
00781                 return;
00782         
00783         QStringMap replaced;
00784         replaced[""] = QString::null;
00785         replaced[QString::null] = QString::null;
00786         
00787         ItemDataMap newItemDataMap;
00788         ConnectorDataMap newConnectorDataMap;
00789         NodeDataMap newNodeDataMap;
00790         
00791         //BEGIN Go through and replace the old ids
00792         {
00793                 const ItemDataMap::iterator end = m_itemDataMap.end();
00794                 for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != end; ++it )
00795                 {
00796                         if ( !replaced.contains( it.key() ) )
00797                                 replaced[it.key()] = itemDocument->generateUID(it.key());
00798                         
00799                         newItemDataMap[replaced[it.key()]] = it.data();
00800                 }
00801         }
00802         {
00803                 const NodeDataMap::iterator end = m_nodeDataMap.end();
00804                 for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != end; ++it )
00805                 {
00806                         if ( !replaced.contains( it.key() ) )
00807                                 replaced[it.key()] = itemDocument->generateUID(it.key());
00808                         
00809                         newNodeDataMap[replaced[it.key()]] = it.data();
00810                 }
00811         }
00812         {
00813                 const ConnectorDataMap::iterator end = m_connectorDataMap.end();
00814                 for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != end; ++it )
00815                 {
00816                         if ( !replaced.contains( it.key() ) )
00817                                 replaced[it.key()] = itemDocument->generateUID(it.key());
00818                         
00819                         newConnectorDataMap[replaced[it.key()]] = it.data();
00820                 }
00821         }
00822         //END Go through and replace the old ids
00823         
00824         //BEGIN Go through and replace the internal references to the ids
00825         {
00826                 const ItemDataMap::iterator end = newItemDataMap.end();
00827                 for ( ItemDataMap::iterator it = newItemDataMap.begin(); it != end; ++it )
00828                 {
00829                         it.data().parentId = replaced[it.data().parentId];
00830                 }
00831         }
00832         {
00833                 const ConnectorDataMap::iterator end = newConnectorDataMap.end();
00834                 for ( ConnectorDataMap::iterator it = newConnectorDataMap.begin(); it != end; ++it )
00835                 {
00836                         it.data().startNodeParent = replaced[it.data().startNodeParent];
00837                         it.data().endNodeParent = replaced[it.data().endNodeParent];
00838                         
00839                         it.data().startNodeId = replaced[it.data().startNodeId];
00840                         it.data().endNodeId = replaced[it.data().endNodeId];
00841                 }
00842         }
00843         //END Go through and replace the internal references to the ids
00844         
00845         
00846         m_itemDataMap = newItemDataMap;
00847         m_connectorDataMap = newConnectorDataMap;
00848         m_nodeDataMap = newNodeDataMap;
00849 }
00850 
00851 
00852 void ItemDocumentData::translateContents( int dx, int dy )
00853 {
00854         //BEGIN Go through and replace the old ids
00855         {
00856                 const ItemDataMap::iterator end = m_itemDataMap.end();
00857                 for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != end; ++it )
00858                 {
00859                         it.data().x += dx;
00860                         it.data().y += dx;
00861                 }
00862         }
00863         {
00864                 const NodeDataMap::iterator end = m_nodeDataMap.end();
00865                 for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != end; ++it )
00866                 {
00867                         it.data().x += dx;
00868                         it.data().y += dy;
00869                 }
00870         }
00871         {
00872                 const ConnectorDataMap::iterator end = m_connectorDataMap.end();
00873                 for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != end; ++it )
00874                 {
00875                         const QPointList::iterator routeEnd = it.data().route.end();
00876                         for ( QPointList::iterator routeIt = it.data().route.begin(); routeIt != routeEnd; ++routeIt )
00877                         {
00878                                 *routeIt += QPoint( dx/8, dy/8 );
00879                         }
00880                 }
00881         }
00882 }
00883 
00884 
00885 void ItemDocumentData::restoreDocument( ItemDocument *itemDocument )
00886 {
00887         if ( !itemDocument )
00888                 return;
00889         
00890         ICNDocument *icnd = dynamic_cast<ICNDocument*>(itemDocument);
00891         FlowCodeDocument *fcd = dynamic_cast<FlowCodeDocument*>(icnd);
00892         if ( fcd && !m_microData.id.isEmpty() )
00893         {
00894                 fcd->setPicType(m_microData.id);
00895                 fcd->microSettings()->restoreFromMicroData(m_microData);
00896         }
00897         
00898         mergeWithDocument(itemDocument,false);
00899         
00900         {
00901                 ItemList removeItems = itemDocument->itemList();
00902                 removeItems.remove((Item*)0);
00903                 
00904                 const ItemDataMap::iterator end = m_itemDataMap.end();
00905                 for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != end; ++it )
00906                         removeItems.remove( itemDocument->itemWithID(it.key()) );
00907                 
00908                 const ItemList::iterator removeEnd = removeItems.end();
00909                 for ( ItemList::iterator it = removeItems.begin(); it != removeEnd; ++it )
00910                 {
00911                         if ( (*it)->canvas() && (*it)->type() != PicItem::typeString() )
00912                                 (*it)->removeItem();
00913                 }
00914         }
00915         
00916         if (icnd)
00917         {
00918                 {
00919                         NodeList removeNodes = icnd->nodeList();
00920                         removeNodes.remove((Node*)0);
00921                         
00922                         const NodeDataMap::iterator end = m_nodeDataMap.end();
00923                         for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != end; ++it )
00924                                 removeNodes.remove( icnd->nodeWithID( it.key() ) );
00925                         
00926                         const NodeList::iterator removeEnd = removeNodes.end();
00927                         for ( NodeList::iterator it = removeNodes.begin(); it != removeEnd; ++it )
00928                         {
00929                                 if ( (*it)->canvas() && !(*it)->isChildNode() )
00930                                         (*it)->removeNode();
00931                         }
00932                 }
00933                 {
00934                         ConnectorList removeConnectors = icnd->connectorList();
00935                         removeConnectors.remove((Connector*)0);
00936                         
00937                         const ConnectorDataMap::iterator end = m_connectorDataMap.end();
00938                         for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != end; ++it )
00939                                 removeConnectors.remove( icnd->connectorWithID(it.key()) );
00940                         
00941                         const ConnectorList::iterator removeEnd = removeConnectors.end();
00942                         for ( ConnectorList::iterator it = removeConnectors.begin(); it != removeEnd; ++it )
00943                         {
00944                                 if ( (*it)->canvas() )
00945                                         (*it)->removeConnector();
00946                         }
00947                 }
00948         }
00949         
00950         itemDocument->flushDeleteList();
00951 }
00952 
00953 
00954 void ItemDocumentData::mergeWithDocument( ItemDocument *itemDocument, bool selectNew )
00955 {
00956         if ( !itemDocument )
00957                 return;
00958         
00959         ICNDocument *icnd = dynamic_cast<ICNDocument*>(itemDocument);
00960         
00961         //BEGIN Restore Nodes
00962         if (icnd)
00963         {
00964                 const NodeDataMap::iterator nodeEnd = m_nodeDataMap.end();
00965                 for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != nodeEnd; ++it )
00966                 {
00967                         if ( !icnd->nodeWithID( it.key() ) )
00968                         {
00969                                 QString id = it.key();
00970                                 if ( itemDocument->type() == Document::dt_circuit )
00971                                         new ECNode( icnd, Node::ec_junction, Node::dir_up, QPoint( int(it.data().x), int(it.data().y) ), &id );
00972                         
00973                                 else if ( itemDocument->type() == Document::dt_flowcode )
00974                                         new FPNode( icnd, Node::fp_junction, Node::dir_up, QPoint( int(it.data().x), int(it.data().y) ), &id );
00975                         }
00976                 }
00977                 for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != nodeEnd; ++it )
00978                 {
00979                         Node *node = icnd->nodeWithID( it.key() );
00980                         if (node)
00981                                 node->move( it.data().x, it.data().y );
00982                 }
00983         }
00984         //END Restore Nodes
00985         
00986         
00987         //BEGIN Restore items
00988         const ItemDataMap::iterator itemEnd = m_itemDataMap.end();
00989         for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != itemEnd; ++it )
00990         {
00991                 if ( !it.data().type.isEmpty() && !itemDocument->itemWithID( it.key() ) )
00992                 {
00993                         Item *item = itemLibrary()->createItem( it.data().type, itemDocument, false, it.key(), false );
00994                         if ( item && !itemDocument->isValidItem(item) )
00995                         {
00996                                 kdWarning() << "Attempted to create invalid item with id: " << it.key() << endl;
00997                                 item->removeItem();
00998                                 itemDocument->flushDeleteList();
00999                                 item = 0;
01000                         }
01001                         if (item)
01002                         {
01003                                 //HACK We move the item now before restoreFromItemData is called later, in case it is to be parented
01004                                 //(as we don't want to move children)...
01005                                 item->move( it.data().x, it.data().y );
01006                         }
01007                 }
01008         }
01009         for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != itemEnd; ++it )
01010         {
01011                 Item *item = itemDocument->itemWithID(it.key());
01012                 if (!item)
01013                         continue;
01014                 
01015                 item->restoreFromItemData( it.data() );
01016                 item->finishedCreation();
01017                 if (selectNew)
01018                         itemDocument->select(item);
01019                 item->show();
01020         }
01021         //END Restore Items
01022         
01023         //BEGIN Restore Connectors
01024         if (icnd)
01025         {
01026                 const ConnectorDataMap::iterator connectorEnd = m_connectorDataMap.end();
01027                 for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != connectorEnd; ++it )
01028                 {
01029                         if ( icnd->connectorWithID( it.key() ) )
01030                                 continue;
01031                         
01032                         QString id = it.key();
01033                         Node *startNode = 0;
01034                         Node *endNode = 0;
01035                         
01036                         if ( it.data().startNodeIsChild )
01037                         {
01038                                 CNItem *item = icnd->cnItemWithID( it.data().startNodeParent );
01039                                 if (!item)
01040                                         kdError() << k_funcinfo << "Unable to find node parent with id: "<<it.data().startNodeParent<<endl;
01041                                 else
01042                                         startNode = item->childNode( it.data().startNodeCId );
01043                         }
01044                         else
01045                                 startNode = icnd->nodeWithID( it.data().startNodeId );
01046                         
01047                         if ( it.data().endNodeIsChild )
01048                         {
01049                                 CNItem *item = icnd->cnItemWithID( it.data().endNodeParent );
01050                                 if (!item)
01051                                         kdError() << k_funcinfo << "Unable to find node parent with id: "<<it.data().endNodeParent<<endl;
01052                                 else
01053                                         endNode = item->childNode( it.data().endNodeCId );
01054                         }
01055                         else
01056                                 endNode = icnd->nodeWithID( it.data().endNodeId );
01057                         
01058                         if ( !startNode || !endNode )
01059                         {
01060                                 kdError() << k_funcinfo << "End and start nodes for the connector do not both exist" << endl;
01061                         }
01062                         else
01063                         {
01064                                 Connector *connector = new Connector( startNode, endNode, icnd, &id );
01065                                         
01066                                 startNode->addOutputConnector(connector);
01067                                 endNode->addInputConnector(connector);
01068                         }
01069                 }
01070                 for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != connectorEnd; ++it )
01071                 {
01072                         Connector *connector = icnd->connectorWithID( it.key() );
01073                         if (connector)
01074                         {
01075                                 connector->restoreFromConnectorData( it.data() );
01076                                 if (selectNew)
01077                                         icnd->select(connector);
01078                         }
01079                 }
01080         }
01081         //END Restore Connectors
01082         
01083         // This is kind of hackish, but never mind
01084         if ( FlowCodeDocument *fcd = dynamic_cast<FlowCodeDocument*>(itemDocument) )
01085         {
01086                 const ItemList fcdItems = fcd->itemList();
01087                 const ItemList::const_iterator fcdItemsEnd = fcdItems.constEnd();
01088                 for ( ItemList::const_iterator it = fcdItems.constBegin(); it != fcdItemsEnd; ++it )
01089                 {
01090                         if ( FlowContainer * fc = dynamic_cast<FlowContainer*>((Item*)*it) )
01091                                 fc->updateContainedVisibility();
01092                 }
01093         }
01094 }
01095 
01096 
01097 void ItemDocumentData::setMicroData( const MicroData &data )
01098 {
01099         m_microData = data;
01100 }
01101 
01102 
01103 void ItemDocumentData::addItems( const ItemList &itemList )
01104 {
01105         const ItemList::const_iterator end = itemList.constEnd();
01106         for ( ItemList::const_iterator it = itemList.constBegin(); it != end; ++it )
01107         {
01108                 if ( *it && (*it)->canvas() && (*it)->type() != PicItem::typeString() )
01109                         addItemData( (*it)->itemData(), (*it)->id() );
01110         }
01111 }
01112 
01113 
01114 void ItemDocumentData::addConnectors( const ConnectorList &connectorList )
01115 {
01116         const ConnectorList::const_iterator end = connectorList.constEnd();
01117         for ( ConnectorList::const_iterator it = connectorList.constBegin(); it != end; ++it )
01118         {
01119                 if ( *it && (*it)->canvas() )
01120                 {
01121                         if ( (*it)->startNode() && (*it)->endNode() )
01122                                 addConnectorData( (*it)->connectorData(), (*it)->id() );
01123                         
01124                         else
01125                                 kdDebug() << k_funcinfo << " *it="<<*it<<" (*it)->startNode()="<<(*it)->startNode()<<" (*it)->endNode()="<<(*it)->endNode()<<endl;
01126                 }
01127         }
01128 }
01129 
01130 
01131 void ItemDocumentData::addNodes( const NodeList &nodeList )
01132 {
01133         const NodeList::const_iterator end = nodeList.constEnd();
01134         for ( NodeList::const_iterator it = nodeList.constBegin(); it != end; ++it )
01135         {
01136                 if ( *it && (*it)->canvas() && !(*it)->isChildNode() )
01137                         addNodeData( (*it)->nodeData(), (*it)->id() );
01138         }
01139 }
01140 
01141 
01142 void ItemDocumentData::addItemData( ItemData itemData, QString id )
01143 {
01144         m_itemDataMap[id] = itemData;
01145 }
01146 
01147 
01148 void ItemDocumentData::addConnectorData( ConnectorData connectorData, QString id )
01149 {
01150         m_connectorDataMap[id] = connectorData;
01151 }
01152 
01153 
01154 void ItemDocumentData::addNodeData( NodeData nodeData, QString id )
01155 {
01156         m_nodeDataMap[id] = nodeData;
01157 }
01158 //END class ItemDocumentData
01159 
01160 
01161 //BEGIN class ItemData
01162 ItemData::ItemData()
01163 {
01164         x = 0;
01165         y = 0;
01166         z = -1;
01167         angleDegrees = 0;
01168         flipped = false;
01169         orientation = -1;
01170         setSize = false;
01171 }
01172 //END class ItemData
01173 
01174 
01175 //BEGIN class ConnectorData
01176 ConnectorData::ConnectorData()
01177 {
01178         manualRoute = false;
01179         startNodeIsChild = false;
01180         endNodeIsChild = false;
01181 }
01182 //END class ConnectorData
01183 
01184 
01185 //BEGIN class NodeData
01186 NodeData::NodeData()
01187 {
01188         x = 0;
01189         y = 0;
01190 }
01191 //END class NodeDaata
01192 
01193 
01194 //BEGIN class PinData
01195 PinData::PinData()
01196 {
01197         type = PinSettings::pt_input;
01198         state = PinSettings::ps_off;
01199 }
01200 //END class PinData
01201 
01202 
01203 //BEGIN class MicroData
01204 MicroData::MicroData()
</