App/Document.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   Copyright (c) Jürgen Riegel          (juergen.riegel@web.de) 2002     *
00003  *                                                                         *
00004  *   This file is part of the FreeCAD CAx development system.              *
00005  *                                                                         *
00006  *   This library is free software; you can redistribute it and/or         *
00007  *   modify it under the terms of the GNU Library General Public           *
00008  *   License as published by the Free Software Foundation; either          *
00009  *   version 2 of the License, or (at your option) any later version.      *
00010  *                                                                         *
00011  *   This library  is distributed in the hope that it will be useful,      *
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00014  *   GNU Library General Public License for more details.                  *
00015  *                                                                         *
00016  *   You should have received a copy of the GNU Library General Public     *
00017  *   License along with this library; see the file COPYING.LIB. If not,    *
00018  *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
00019  *   Suite 330, Boston, MA  02111-1307, USA                                *
00020  *                                                                         *
00021  ***************************************************************************/
00022 
00023 
00052 #include "PreCompiled.h"
00053 
00054 #ifndef _PreComp_
00055 # include <sstream>
00056 # include <climits>
00057 #endif
00058 
00059 #include <boost/graph/topological_sort.hpp>
00060 #include <boost/graph/depth_first_search.hpp>
00061 #include <boost/graph/dijkstra_shortest_paths.hpp>
00062 #include <boost/graph/visitors.hpp>
00063 #include <boost/graph/graphviz.hpp>
00064 #include <boost/bind.hpp>
00065 
00066 
00067 #include "Document.h"
00068 #include "DocumentPy.h"
00069 #include "Application.h"
00070 #include "DocumentObject.h"
00071 #include "PropertyLinks.h"
00072 
00073 #include <Base/Console.h>
00074 #include <Base/Exception.h>
00075 #include <Base/FileInfo.h>
00076 #include <Base/TimeInfo.h>
00077 #include <Base/Interpreter.h>
00078 #include <Base/Reader.h>
00079 #include <Base/Writer.h>
00080 #include <Base/Stream.h>
00081 #include <Base/FileInfo.h>
00082 #include <Base/Tools.h>
00083 #include <Base/Uuid.h>
00084 
00085 #include <zipios++/zipios-config.h>
00086 #include <zipios++/zipfile.h>
00087 #include <zipios++/zipinputstream.h>
00088 #include <zipios++/zipoutputstream.h>
00089 #include <zipios++/meta-iostreams.h>
00090 
00091 #include "Application.h"
00092 #include "Transactions.h"
00093 
00094 using Base::Console;
00095 using Base::streq;
00096 using Base::Writer;
00097 using namespace App;
00098 using namespace std;
00099 using namespace boost;
00100 using namespace zipios;
00101 
00102 #ifdef MemDebugOn
00103 # define new DEBUG_CLIENTBLOCK
00104 #endif
00105 
00106 #if FC_DEBUG
00107 #  define FC_LOGFEATUREUPDATE
00108 #endif 
00109 
00110 // typedef boost::property<boost::vertex_root_t, DocumentObject* > VertexProperty;
00111 typedef boost::adjacency_list <
00112 boost::vecS,           // class OutEdgeListS  : a Sequence or an AssociativeContainer
00113 boost::vecS,           // class VertexListS   : a Sequence or a RandomAccessContainer
00114 boost::directedS,      // class DirectedS     : This is a directed graph
00115 boost::no_property,    // class VertexProperty:
00116 boost::no_property,    // class EdgeProperty:
00117 boost::no_property,    // class GraphProperty:
00118 boost::listS           // class EdgeListS:
00119 > DependencyList;
00120 typedef boost::graph_traits<DependencyList> Traits;
00121 typedef Traits::vertex_descriptor Vertex;
00122 typedef Traits::edge_descriptor Edge;
00123 
00124 namespace App {
00125 
00126 // Pimpl class
00127 struct DocumentP
00128 {
00129     // Array to preserve the creation order of created objects
00130     std::vector<DocumentObject*> objectArray;
00131     std::map<std::string,DocumentObject*> objectMap;
00132     DocumentObject* activeObject;
00133     Transaction *activeUndoTransaction;
00134     Transaction *activeTransaction;
00135     int iTransactionMode;
00136     int iTransactionCount;
00137     std::map<int,Transaction*> mTransactions;
00138     std::map<Vertex,DocumentObject*> vertexMap;
00139     bool rollback;
00140     bool closable;
00141     int iUndoMode;
00142     unsigned int UndoMemSize;
00143     unsigned int UndoMaxStackSize;
00144 
00145     DocumentP() {
00146         activeObject = 0;
00147         activeUndoTransaction = 0;
00148         activeTransaction = 0;
00149         iTransactionMode = 0;
00150         iTransactionCount = 0;
00151         rollback = false;
00152         closable = true;
00153         iUndoMode = 0;
00154         UndoMemSize = 0;
00155         UndoMaxStackSize = 20;
00156     }
00157 };
00158 
00159 } // namespace App
00160 
00161 PROPERTY_SOURCE(App::Document, App::PropertyContainer)
00162 
00163 void Document::writeDependencyGraphViz(std::ostream &out)
00164 {
00165     //  // caching vertex to DocObject
00166     //std::map<Vertex,DocumentObject*> VertexMap;
00167     //for(std::map<DocumentObject*,Vertex>::const_iterator It1= _DepConMap.begin();It1 != _DepConMap.end(); ++It1)
00168     //  VertexMap[It1->second] = It1->first;
00169 
00170     out << "digraph G {" << endl;
00171     out << "\tordering=out;" << endl;
00172     out << "\tnode [shape = box];" << endl;
00173 
00174     for (std::map<std::string,DocumentObject*>::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It) {
00175         out << "\t" << It->first << ";" <<endl;
00176         std::vector<DocumentObject*> OutList = It->second->getOutList();
00177         for (std::vector<DocumentObject*>::const_iterator It2=OutList.begin();It2!=OutList.end();++It2)
00178             if (*It2)
00179                 out << "\t" << It->first << "->" << (*It2)->getNameInDocument() << ";" <<endl;
00180     }
00181 
00182     /*
00183     graph_traits<DependencyList>::edge_iterator ei, ei_end;
00184     for (tie(ei,ei_end) = edges(_DepList); ei != ei_end; ++ei)
00185       out << "\t"
00186           << VertexMap[source(*ei, _DepList)]->getNameInDocument()
00187           << " -> "
00188           << VertexMap[target(*ei, _DepList)]->getNameInDocument()
00189           << ";" << endl;
00190     */
00191     out << "}" << endl;
00192 }
00193 
00194 //bool _has_cycle_dfs(const DependencyList & g, vertex_t u, default_color_type * color)
00195 //{
00196 //  color[u] = gray_color;
00197 //  graph_traits < DependencyList >::adjacency_iterator vi, vi_end;
00198 //  for (tie(vi, vi_end) = adjacent_vertices(u, g); vi != vi_end; ++vi)
00199 //    if (color[*vi] == white_color)
00200 //      if (has_cycle_dfs(g, *vi, color))
00201 //        return true;            // cycle detected, return immediately
00202 //      else if (color[*vi] == gray_color)        // *vi is an ancestor!
00203 //        return true;
00204 //  color[u] = black_color;
00205 //  return false;
00206 //}
00207 
00208 bool Document::checkOnCycle(void)
00209 {/*
00210   std::vector < default_color_type > color(num_vertices(_DepList), white_color);
00211   graph_traits < DependencyList >::vertex_iterator vi, vi_end;
00212   for (tie(vi, vi_end) = vertices(_DepList); vi != vi_end; ++vi)
00213     if (color[*vi] == white_color)
00214       if (_has_cycle_dfs(_DepList, *vi, &color[0]))
00215         return true; */
00216     return false;
00217 }
00218 
00219 bool Document::undo(void)
00220 {
00221     if (d->iUndoMode) {
00222         if (d->activeUndoTransaction)
00223             commitTransaction();
00224         else
00225             assert(mUndoTransactions.size()!=0);
00226 
00227         // redo
00228         d->activeUndoTransaction = new Transaction();
00229         d->activeUndoTransaction->Name = mUndoTransactions.back()->Name;
00230 
00231         // applying the undo
00232         mUndoTransactions.back()->apply(*this,false);
00233 
00234         // save the redo
00235         mRedoTransactions.push_back(d->activeUndoTransaction);
00236         d->activeUndoTransaction = 0;
00237 
00238         delete mUndoTransactions.back();
00239         mUndoTransactions.pop_back();
00240     }
00241 
00242     return false;
00243 }
00244 
00245 bool Document::redo(void)
00246 {
00247     if (d->iUndoMode) {
00248         if (d->activeUndoTransaction)
00249             commitTransaction();
00250 
00251         assert(mRedoTransactions.size()!=0);
00252 
00253         // undo
00254         d->activeUndoTransaction = new Transaction();
00255         d->activeUndoTransaction->Name = mRedoTransactions.back()->Name;
00256 
00257         // do the redo
00258         mRedoTransactions.back()->apply(*this,true);
00259         mUndoTransactions.push_back(d->activeUndoTransaction);
00260         d->activeUndoTransaction = 0;
00261 
00262         delete mRedoTransactions.back();
00263         mRedoTransactions.pop_back();
00264     }
00265 
00266     return false;
00267 }
00268 
00269 std::vector<std::string> Document::getAvailableUndoNames() const
00270 {
00271     std::vector<std::string> vList;
00272     if (d->activeUndoTransaction)
00273         vList.push_back(d->activeUndoTransaction->Name);
00274     for (std::list<Transaction*>::const_reverse_iterator It=mUndoTransactions.rbegin();It!=mUndoTransactions.rend();++It)
00275         vList.push_back((**It).Name);
00276     return vList;
00277 }
00278 
00279 std::vector<std::string> Document::getAvailableRedoNames() const
00280 {
00281     std::vector<std::string> vList;
00282     for (std::list<Transaction*>::const_reverse_iterator It=mRedoTransactions.rbegin();It!=mRedoTransactions.rend();++It)
00283         vList.push_back((**It).Name);
00284     return vList;
00285 }
00286 
00287 void Document::openTransaction(const char* name)
00288 {
00289     if (d->iUndoMode) {
00290         if (d->activeUndoTransaction)
00291             commitTransaction();
00292         _clearRedos();
00293 
00294         d->activeUndoTransaction = new Transaction();
00295         if (name)
00296             d->activeUndoTransaction->Name = name;
00297     }
00298 }
00299 
00300 void Document::_clearRedos()
00301 {
00302     while (!mRedoTransactions.empty()) {
00303         delete mRedoTransactions.back();
00304         mRedoTransactions.pop_back();
00305     }
00306 }
00307 
00308 void Document::commitTransaction()
00309 {
00310     if (d->activeUndoTransaction) {
00311         mUndoTransactions.push_back(d->activeUndoTransaction);
00312         d->activeUndoTransaction = 0;
00313         // check the stack for the limits
00314         if(mUndoTransactions.size() > d->UndoMaxStackSize){
00315             delete mUndoTransactions.front();
00316             mUndoTransactions.pop_front();
00317         }
00318     }
00319 }
00320 
00321 void Document::abortTransaction()
00322 {
00323     if (d->activeUndoTransaction) {
00324         d->rollback = true;
00325         // applying the so far made changes
00326         d->activeUndoTransaction->apply(*this,false);
00327         d->rollback = false;
00328 
00329         // destroy the undo
00330         delete d->activeUndoTransaction;
00331         d->activeUndoTransaction = 0;
00332     }
00333 }
00334 
00335 void Document::clearUndos()
00336 {
00337     if (d->activeUndoTransaction)
00338         commitTransaction();
00339 
00340     // When cleaning up the undo stack we must delete the transactions from front
00341     // to back because a document object can appear in several transactions but
00342     // once removed from the document the object can never ever appear in any later
00343     // transaction. Since the document object may be also deleted when the transaction
00344     // is deleted we must make sure not access an object once it's destroyed. Thus, we
00345     // go from front to back and not the other way round.
00346     while (!mUndoTransactions.empty()) {
00347         delete mUndoTransactions.front();
00348         mUndoTransactions.pop_front();
00349     }
00350     //while (!mUndoTransactions.empty()) {
00351     //    delete mUndoTransactions.back();
00352     //    mUndoTransactions.pop_back();
00353     //}
00354 
00355     _clearRedos();
00356 }
00357 
00358 int Document::getAvailableUndos() const
00359 {
00360     if (d->activeUndoTransaction)
00361         return static_cast<int>(mUndoTransactions.size() + 1);
00362     else
00363         return static_cast<int>(mUndoTransactions.size());
00364 }
00365 
00366 int Document::getAvailableRedos() const
00367 {
00368     return static_cast<int>(mRedoTransactions.size());
00369 }
00370 
00371 void Document::setUndoMode(int iMode)
00372 {
00373     if (d->iUndoMode && !iMode)
00374         clearUndos();
00375 
00376     d->iUndoMode = iMode;
00377 }
00378 
00379 int Document::getUndoMode(void) const
00380 {
00381     return d->iUndoMode;
00382 }
00383 
00384 unsigned int Document::getUndoMemSize (void) const
00385 {
00386     return d->UndoMemSize;
00387 }
00388 
00389 void Document::setUndoLimit(unsigned int UndoMemSize)
00390 {
00391     d->UndoMemSize = UndoMemSize;
00392 }
00393 
00394 void Document::setMaxUndoStackSize(unsigned int UndoMaxStackSize)
00395 {
00396      d->UndoMaxStackSize = UndoMaxStackSize;
00397 }
00398 
00399 unsigned int Document::getMaxUndoStackSize(void)const
00400 {
00401     return d->UndoMaxStackSize;
00402 }
00403 
00404 void Document::onChanged(const Property* prop)
00405 {
00406     // the Name property is a label for display purposes
00407     if (prop == &Label)
00408         App::GetApplication().signalRelabelDocument(*this);
00409 }
00410 
00411 void Document::onBeforeChangeProperty(const DocumentObject *Who, const Property *What)
00412 {
00413     if (d->activeUndoTransaction && !d->rollback)
00414         d->activeUndoTransaction->addObjectChange(Who,What);
00415 }
00416 
00417 void Document::onChangedProperty(const DocumentObject *Who, const Property *What)
00418 {
00419     if (d->activeTransaction && !d->rollback)
00420         d->activeTransaction->addObjectChange(Who,What);
00421     signalChangedObject(*Who, *What);
00422 }
00423 
00424 void Document::setTransactionMode(int iMode)
00425 {
00426     /*  if(_iTransactionMode == 0 && iMode == 1)
00427         beginTransaction();
00428 
00429       if(activTransaction && iMode == 0)
00430         endTransaction();
00431       */
00432     d->iTransactionMode = iMode;
00433 }
00434 
00435 #if 0
00437 int  Document::beginTransaction(void)
00438 {
00439     if (activTransaction)
00440         endTransaction();
00441 
00442     iTransactionCount++;
00443 
00444     activTransaction = new Transaction(iTransactionCount);
00445     d->mTransactions[iTransactionCount] = activTransaction;
00446 
00447     return iTransactionCount;
00448 }
00449 
00451 void Document::rollbackTransaction(void)
00452 {
00453     // ToDo
00454     assert(0);
00455     endTransaction();
00456 }
00457 
00459 int  Document::endTransaction(void)
00460 {
00461     activTransaction = 0;
00462     return iTransactionCount;
00463 }
00464 
00466 const Transaction *Document::getTransaction(int pos) const
00467 {
00468     if (pos == -1)
00469         return activTransaction;
00470     else {
00471         std::map<int,Transaction*>::const_iterator Pos(d->mTransactions.find(pos));
00472         if (Pos != d->mTransactions.end())
00473             return Pos->second;
00474         else
00475             return 0;
00476     }
00477 }
00478 #endif
00479 
00480 //--------------------------------------------------------------------------
00481 // constructor
00482 //--------------------------------------------------------------------------
00483 Document::Document(void)
00484 {
00485     // Remark: In a constructor we should never increment a Python object as we cannot be sure
00486     // if the Python interpreter gets a reference of it. E.g. if we increment but Python don't
00487     // get a reference then the object wouldn't get deleted in the destructor.
00488     // So, we must increment only if the interpreter gets a reference.
00489     // Remark: We force the document Python object to own the DocumentPy instance, thus we don't
00490     // have to care about ref counting any more.
00491     DocumentPythonObject = Py::Object(new DocumentPy(this), true);
00492     d = new DocumentP;
00493 
00494 #ifdef FC_LOGUPDATECHAIN
00495     Console().Log("+App::Document: %p\n",this);
00496 #endif
00497 
00498     ADD_PROPERTY_TYPE(Label,("Unnamed"),0,Prop_None,"The name of the document");
00499     ADD_PROPERTY_TYPE(FileName,(""),0,Prop_None,"The path to the file where the document is saved to");
00500     ADD_PROPERTY_TYPE(CreatedBy,(""),0,Prop_None,"The creator of the document");
00501     ADD_PROPERTY_TYPE(CreationDate,(Base::TimeInfo::currentDateTimeString()),0,Prop_ReadOnly,"Date of creation");
00502     ADD_PROPERTY_TYPE(LastModifiedBy,(""),0,Prop_None,0);
00503     ADD_PROPERTY_TYPE(LastModifiedDate,("Unknown"),0,Prop_ReadOnly,"Date of last modification");
00504     ADD_PROPERTY_TYPE(Company,(""),0,Prop_None,"Additional tag to save the the name of the company");
00505     ADD_PROPERTY_TYPE(Comment,(""),0,Prop_None,"Additional tag to save a comment");
00506     // create the uuid for the document
00507     Base::Uuid id;
00508     ADD_PROPERTY_TYPE(Id,(id.UuidStr),0,Prop_None,"UUID of the document");
00509 
00510     // create transient directory
00511     std::string basePath = Base::FileInfo::getTempPath() + GetApplication().getExecutableName();
00512     Base::FileInfo TransDir(basePath + "_Doc_" + id.UuidStr);
00513     if (!TransDir.exists())
00514         TransDir.createDirectory();
00515     ADD_PROPERTY_TYPE(TransientDir,(TransDir.filePath().c_str()),0,Prop_Transient,
00516         "Transient directory, where the files live while the document is open");
00517 }
00518 
00519 Document::~Document()
00520 {
00521 #ifdef FC_LOGUPDATECHAIN
00522     Console().Log("-App::Document: %s %p\n",getName(), this);
00523 #endif
00524 
00525     clearUndos();
00526 
00527     std::map<std::string,DocumentObject*>::iterator it;
00528 
00529 #ifdef FC_LOGUPDATECHAIN
00530     Console().Log("-Delete Features of %s \n",getName());
00531 #endif
00532 
00533     d->objectArray.clear();
00534     for (it = d->objectMap.begin(); it != d->objectMap.end(); ++it) {
00535         delete(it->second);
00536     }
00537 
00538     // Remark: The API of Py::Object has been changed to set whether the wrapper owns the passed
00539     // Python object or not. In the constructor we forced the wrapper to own the object so we need
00540     // not to dec'ref the Python object any more.
00541     // But we must still invalidate the Python object because it need not to be
00542     // destructed right now because the interpreter can own several references to it.
00543     Base::PyObjectBase* doc = (Base::PyObjectBase*)DocumentPythonObject.ptr();
00544     // Call before decrementing the reference counter, otherwise a heap error can occur
00545     doc->setInvalid();
00546 
00547     // remove Transient directory
00548     Base::FileInfo TransDir(TransientDir.getValue());
00549     TransDir.deleteDirectoryRecursive();
00550     delete d;
00551 }
00552 
00553 //--------------------------------------------------------------------------
00554 // Exported functions
00555 //--------------------------------------------------------------------------
00556 
00557 void Document::Save (Base::Writer &writer) const
00558 {
00559     writer.Stream() << "<?xml version='1.0' encoding='utf-8'?>" << endl
00560     << "<!--" << endl
00561     << " FreeCAD Document, see http://free-cad.sourceforge.net for more information..." << endl
00562     << "-->" << endl;
00563 
00564     writer.Stream() << "<Document SchemaVersion=\"4\">" << endl;
00565 
00566     PropertyContainer::Save(writer);
00567 
00568     // writing the features types
00569     writer.incInd(); // indention for 'Objects count'
00570     writer.Stream() << writer.ind() << "<Objects Count=\"" << d->objectArray.size() <<"\">" << endl;
00571 
00572     writer.incInd(); // indention for 'Object type'
00573     std::vector<DocumentObject*>::const_iterator it;
00574     for (it = d->objectArray.begin(); it != d->objectArray.end(); ++it) {
00575         writer.Stream() << writer.ind() << "<Object "
00576         << "type=\"" << (*it)->getTypeId().getName() << "\" "
00577         << "name=\"" << (*it)->getNameInDocument()       << "\" "
00578         << "/>" << endl;
00579     }
00580 
00581     writer.decInd();  // indention for 'Object type'
00582     writer.Stream() << writer.ind() << "</Objects>" << endl;
00583 
00584     // writing the features itself
00585     writer.Stream() << writer.ind() << "<ObjectData Count=\"" << d->objectArray.size() <<"\">" << endl;
00586 
00587     writer.incInd(); // indention for 'Object name'
00588     for (it = d->objectArray.begin(); it != d->objectArray.end(); ++it) {
00589         writer.Stream() << writer.ind() << "<Object name=\"" << (*it)->getNameInDocument() << "\">" << endl;
00590         (*it)->Save(writer);
00591         writer.Stream() << writer.ind() << "</Object>" << endl;
00592     }
00593 
00594     writer.decInd(); // indention for 'Object name'
00595     writer.Stream() << writer.ind() << "</ObjectData>" << endl;
00596     writer.decInd();  // indention for 'Objects count'
00597     writer.Stream() << "</Document>" << endl;
00598 }
00599 
00600 void Document::Restore(Base::XMLReader &reader)
00601 {
00602     int i,Cnt;
00603     reader.readElement("Document");
00604     long scheme = reader.getAttributeAsInteger("SchemaVersion");
00605     reader.DocumentSchema = scheme;
00606 
00607     // When this document was created the FileName and Label properties
00608     // were set to the absolute path or file name, respectively. To save
00609     // the document to the file it was loaded from or to show the file name
00610     // in the tree view we must restore them after loading the file because
00611     // they will be overridden.
00612     // Note: This does not affect the internal name of the document in any way
00613     // that is kept in Application.
00614     std::string FilePath = FileName.getValue();
00615     std::string DocLabel = Label.getValue();
00616 
00617     // remove previous Transient directory
00618     Base::FileInfo TransDir(TransientDir.getValue());
00619     TransDir.deleteDirectoryRecursive();
00620 
00621 
00622     // read the Document Properties
00623     PropertyContainer::Restore(reader);
00624 
00625     // We must restore the correct 'FileName' property again because the stored
00626     // value could be invalid.
00627     FileName.setValue(FilePath.c_str());
00628     Label.setValue(DocLabel.c_str());
00629 
00630     // create new transient directory
00631     std::string basePath = Base::FileInfo::getTempPath() + GetApplication().getExecutableName();
00632     Base::FileInfo TransDirNew(basePath + "_Doc_"  + Id.getValue());
00633     if(!TransDirNew.exists())
00634         TransDirNew.createDirectory();
00635     TransientDir.setValue(TransDirNew.filePath());
00636 
00637 
00638     // SchemeVersion "2"
00639     if ( scheme == 2 ) {
00640         // read the feature types
00641         reader.readElement("Features");
00642         Cnt = reader.getAttributeAsInteger("Count");
00643         for (i=0 ;i<Cnt ;i++) {
00644             reader.readElement("Feature");
00645             string type = reader.getAttribute("type");
00646             string name = reader.getAttribute("name");
00647 
00648             try {
00649                 addObject(type.c_str(),name.c_str());
00650             }
00651             catch ( Base::Exception& ) {
00652                 Base::Console().Message("Cannot create object '%s'\n", name.c_str());
00653             }
00654         }
00655         reader.readEndElement("Features");
00656 
00657         // read the features itself
00658         reader.readElement("FeatureData");
00659         Cnt = reader.getAttributeAsInteger("Count");
00660         for (i=0 ;i<Cnt ;i++) {
00661             reader.readElement("Feature");
00662             string name = reader.getAttribute("name");
00663             DocumentObject* pObj = getObject(name.c_str());
00664             if (pObj) { // check if this feature has been registered
00665                 pObj->StatusBits.set(4);
00666                 pObj->Restore(reader);
00667                 pObj->StatusBits.reset(4);
00668             }
00669             reader.readEndElement("Feature");
00670         }
00671         reader.readEndElement("FeatureData");
00672     } // SchemeVersion "3" or higher
00673     else if ( scheme >= 3 ) {
00674         // read the feature types
00675         reader.readElement("Objects");
00676         Cnt = reader.getAttributeAsInteger("Count");
00677         for (i=0 ;i<Cnt ;i++) {
00678             reader.readElement("Object");
00679             string type = reader.getAttribute("type");
00680             string name = reader.getAttribute("name");
00681 
00682             try {
00683                 addObject(type.c_str(),name.c_str());
00684             }
00685             catch ( Base::Exception& ) {
00686                 Base::Console().Message("Cannot create object '%s'\n", name.c_str());
00687             }
00688         }
00689         reader.readEndElement("Objects");
00690 
00691         // read the features itself
00692         reader.readElement("ObjectData");
00693         Cnt = reader.getAttributeAsInteger("Count");
00694         for (i=0 ;i<Cnt ;i++) {
00695             reader.readElement("Object");
00696             string name = reader.getAttribute("name");
00697             DocumentObject* pObj = getObject(name.c_str());
00698             if (pObj) { // check if this feature has been registered
00699                 pObj->StatusBits.set(4);
00700                 pObj->Restore(reader);
00701                 pObj->StatusBits.reset(4);
00702             }
00703             reader.readEndElement("Object");
00704         }
00705         reader.readEndElement("ObjectData");
00706     }
00707 
00708     reader.readEndElement("Document");
00709 }
00710 
00711 void Document::exportObjects(const std::vector<App::DocumentObject*>& obj,
00712                              std::ostream& out)
00713 {
00714     Base::ZipWriter writer(out);
00715     writer.putNextEntry("Document.xml");
00716     writer.Stream() << "<?xml version='1.0' encoding='utf-8'?>" << endl;
00717     writer.Stream() << "<Document SchemaVersion=\"4\">" << endl;
00718     // Add this block to have the same layout as for normal documents
00719     writer.Stream() << "<Properties Count=\"0\">" << endl;
00720     writer.Stream() << "</Properties>" << endl;
00721 
00722     // writing the features types
00723     writer.incInd(); // indention for 'Objects count'
00724     writer.Stream() << writer.ind() << "<Objects Count=\"" << obj.size() <<"\">" << endl;
00725 
00726     writer.incInd(); // indention for 'Object type'
00727     std::vector<DocumentObject*>::const_iterator it;
00728     for (it = obj.begin(); it != obj.end(); ++it) {
00729         writer.Stream() << writer.ind() << "<Object "
00730         << "type=\"" << (*it)->getTypeId().getName() << "\" "
00731         << "name=\"" << (*it)->getNameInDocument() << "\" "
00732         << "/>" << endl;
00733     }
00734 
00735     writer.decInd();  // indention for 'Object type'
00736     writer.Stream() << writer.ind() << "</Objects>" << endl;
00737 
00738     // writing the features itself
00739     writer.Stream() << writer.ind() << "<ObjectData Count=\"" << obj.size() <<"\">" << endl;
00740 
00741     writer.incInd(); // indention for 'Object name'
00742     for (it = obj.begin(); it != obj.end(); ++it) {
00743         writer.Stream() << writer.ind() << "<Object name=\"" << (*it)->getNameInDocument() << "\">" << endl;
00744         (*it)->Save(writer);
00745         writer.Stream() << writer.ind() << "</Object>" << endl;
00746     }
00747 
00748     writer.decInd(); // indention for 'Object name'
00749     writer.Stream() << writer.ind() << "</ObjectData>" << endl;
00750     writer.decInd();  // indention for 'Objects count'
00751     writer.Stream() << "</Document>" << endl;
00752 
00753     // Hook for others to add further data.
00754     signalExportObjects(obj, writer);
00755 
00756     // write additional files
00757     writer.writeFiles();
00758 }
00759 
00760 std::vector<App::DocumentObject*>
00761 Document::importObjects(std::istream& input)
00762 {
00763     std::vector<App::DocumentObject*> objs;
00764     zipios::ZipInputStream zipstream(input);
00765     Base::XMLReader reader("<memory>", zipstream);
00766 
00767     int i,Cnt;
00768     reader.readElement("Document");
00769     long scheme = reader.getAttributeAsInteger("SchemaVersion");
00770     reader.DocumentSchema = scheme;
00771 
00772     // read the object types
00773     std::map<std::string, std::string> nameMap;
00774     reader.readElement("Objects");
00775     Cnt = reader.getAttributeAsInteger("Count");
00776     for (i=0 ;i<Cnt ;i++) {
00777         reader.readElement("Object");
00778         string type = reader.getAttribute("type");
00779         string name = reader.getAttribute("name");
00780 
00781         try {
00782             App::DocumentObject* o = addObject(type.c_str(),name.c_str());
00783             objs.push_back(o);
00784             // use this name for the later access because an object with
00785             // the given name may already exist
00786             nameMap[name] = o->getNameInDocument();
00787         }
00788         catch (Base::Exception&) {
00789             Base::Console().Message("Cannot create object '%s'\n", name.c_str());
00790         }
00791     }
00792     reader.readEndElement("Objects");
00793 
00794     // read the features itself
00795     reader.readElement("ObjectData");
00796     Cnt = reader.getAttributeAsInteger("Count");
00797     for (i=0 ;i<Cnt ;i++) {
00798         reader.readElement("Object");
00799         std::string name = nameMap[reader.getAttribute("name")];
00800         DocumentObject* pObj = getObject(name.c_str());
00801         if (pObj) { // check if this feature has been registered
00802             pObj->StatusBits.set(4);
00803             pObj->Restore(reader);
00804             pObj->StatusBits.reset(4);
00805         }
00806         reader.readEndElement("Object");
00807     }
00808     reader.readEndElement("ObjectData");
00809 
00810     reader.readEndElement("Document");
00811     signalImportObjects(objs, reader);
00812     reader.readFiles(zipstream);
00813     // reset all touched
00814     for (std::vector<DocumentObject*>::iterator it= objs.begin();it!=objs.end();++it)
00815         (*it)->purgeTouched();
00816     return objs;
00817 }
00818 
00819 unsigned int Document::getMemSize (void) const
00820 {
00821     unsigned int size = 0;
00822 
00823     // size of the DocObjects in the document
00824     std::vector<DocumentObject*>::const_iterator it;
00825     for (it = d->objectArray.begin(); it != d->objectArray.end(); ++it)
00826         size += (*it)->getMemSize();
00827 
00828     // size of the document properties...
00829     size += PropertyContainer::getMemSize();
00830 
00831     // Undo Redo size
00832     size += getUndoMemSize();
00833 
00834     return size;
00835 }
00836 
00837 // Save the document under the name it has been opened
00838 bool Document::save (void)
00839 {
00840     int compression = App::GetApplication().GetParameterGroupByPath
00841         ("User parameter:BaseApp/Preferences/Document")->GetInt("CompressionLevel",3);
00842 
00843     if (*(FileName.getValue()) != '\0') {
00844         LastModifiedDate.setValue(Base::TimeInfo::currentDateTimeString());
00845         // make a tmp. file where to save the project data first and then rename to
00846         // the actual file name. This may be useful if overwriting an existing file
00847         // fails so that the data of the work up to now isn't lost.
00848         std::string uuid = Base::Uuid::CreateUuid();
00849         std::string fn = FileName.getValue();
00850         fn += "."; fn += uuid;
00851         Base::FileInfo tmp(fn);
00852 
00853         // open extra scope to close ZipWriter properly
00854         {
00855             Base::ofstream file(tmp, std::ios::out | std::ios::binary);
00856             Base::ZipWriter writer(file);
00857 
00858             writer.setComment("FreeCAD Document");
00859             writer.setLevel(compression);
00860             writer.putNextEntry("Document.xml");
00861 
00862             Document::Save(writer);
00863 
00864             // Special handling for Gui document.
00865             signalSaveDocument(writer);
00866 
00867             // write additional files
00868             writer.writeFiles();
00869 
00870             GetApplication().signalSaveDocument(*this);
00871         }
00872 
00873         // if saving the project data succeeded rename to the actual file name
00874         Base::FileInfo fi(FileName.getValue());
00875         if (fi.exists()) {
00876             bool backup = App::GetApplication().GetParameterGroupByPath
00877                 ("User parameter:BaseApp/Preferences/Document")->GetBool("CreateBackupFiles",true);
00878             int count_bak = App::GetApplication().GetParameterGroupByPath
00879                 ("User parameter:BaseApp/Preferences/Document")->GetInt("CountBackupFiles",1);
00880             if (backup) {
00881                 int nSuff = 0;
00882                 std::string fn = fi.fileName();
00883                 Base::FileInfo di(fi.dirPath());
00884                 std::vector<Base::FileInfo> backup;
00885                 std::vector<Base::FileInfo> files = di.getDirectoryContent();
00886                 for (std::vector<Base::FileInfo>::iterator it = files.begin(); it != files.end(); ++it) {
00887                     std::string file = it->fileName();
00888                     if (file.substr(0,fn.length()) == fn) {
00889                         // starts with the same file name
00890                         std::string suf(file.substr(fn.length()));
00891                         if (suf.size() > 0) {
00892                             std::string::size_type nPos = suf.find_first_not_of("0123456789");
00893                             if (nPos==std::string::npos) {
00894                                 // store all backup files
00895                                 backup.push_back(*it);
00896                                 nSuff = std::max<int>(nSuff, std::atol(suf.c_str()));
00897                             }
00898                         }
00899                     }
00900                 }
00901 
00902                 if (!backup.empty() && (int)backup.size() >= count_bak) {
00903                     // delete the oldest backup file we found
00904                     Base::FileInfo del = backup.front();
00905                     for (std::vector<Base::FileInfo>::iterator it = backup.begin(); it != backup.end(); ++it) {
00906                         if (it->lastModified() < del.lastModified())
00907                             del = *it;
00908                     }
00909 
00910                     del.deleteFile();
00911                     fn = del.filePath();
00912                 }
00913                 else {
00914                     // create a new backup file
00915                     std::stringstream str;
00916                     str << fi.filePath() << (nSuff + 1);
00917                     fn = str.str();
00918                 }
00919 
00920                 fi.renameFile(fn.c_str());
00921             }
00922             else {
00923                 fi.deleteFile();
00924             }
00925         }
00926         if (tmp.renameFile(FileName.getValue()) == false)
00927             Base::Console().Warning("Cannot rename file from '%s' to '%s'\n",
00928             fn.c_str(), FileName.getValue());
00929 
00930         return true;
00931     }
00932 
00933     return false;
00934 }
00935 
00936 // Open the document
00937 void Document::restore (void)
00938 {
00939     // clean up if the document is not empty
00940     // !TODO mind exeptions while restoring!
00941     clearUndos();
00942     for (std::vector<DocumentObject*>::iterator obj = d->objectArray.begin(); obj != d->objectArray.end(); ++obj) {
00943         signalDeletedObject(*(*obj));
00944         delete *obj;
00945     }
00946     d->objectArray.clear();
00947     d->objectMap.clear();
00948     d->activeObject = 0;
00949 
00950     Base::FileInfo fi(FileName.getValue());
00951     Base::ifstream file(fi, std::ios::in | std::ios::binary);
00952     std::streambuf* buf = file.rdbuf();
00953     std::streamoff size = buf->pubseekoff(0, std::ios::end, std::ios::in);
00954     buf->pubseekoff(0, std::ios::beg, std::ios::in);
00955     if (size < 22) // an empty zip archive has 22 bytes
00956         throw Base::FileException("Invalid project file",FileName.getValue());
00957 
00958     zipios::ZipInputStream zipstream(file);
00959     Base::XMLReader reader(FileName.getValue(), zipstream);
00960 
00961     if (!reader.isValid())
00962         throw Base::FileException("Error reading compression file",FileName.getValue());
00963 
00964     try {
00965         Document::Restore(reader);
00966     }
00967     catch (const Base::Exception& e) {
00968         Base::Console().Error("Invalid Document.xml: %s\n", e.what());
00969     }
00970 
00971     // Special handling for Gui document, the view representations must already
00972     // exist, what is done in Restore().
00973     // Note: This file doesn't need to be available if the document has been created
00974     // without GUI. But if available then follow after all data files of the App document.
00975     signalRestoreDocument(reader);
00976     reader.readFiles(zipstream);
00977     
00978     // reset all touched
00979     for (std::map<std::string,DocumentObject*>::iterator It= d->objectMap.begin();It!=d->objectMap.end();++It)
00980         It->second->purgeTouched();
00981 
00982     GetApplication().signalRestoreDocument(*this);
00983 }
00984 
00985 bool Document::isSaved() const
00986 {
00987     std::string name = FileName.getValue();
00988     return !name.empty();
00989 }
00990 
01004 const char* Document::getName() const
01005 {
01006     return GetApplication().getDocumentName(this);
01007 }
01008 
01010 void Document::purgeTouched()
01011 {
01012     for (std::vector<DocumentObject*>::iterator It = d->objectArray.begin();It != d->objectArray.end();++It)
01013         (*It)->purgeTouched();
01014 }
01015 
01016 bool Document::isTouched() const
01017 {
01018     for (std::vector<DocumentObject*>::const_iterator It = d->objectArray.begin();It != d->objectArray.end();++It)
01019         if ((*It)->isTouched())
01020             return true;
01021     return false;
01022 }
01023 
01024 vector<DocumentObject*> Document::getTouched(void) const
01025 {
01026     vector<DocumentObject*> result;
01027 
01028     for (std::vector<DocumentObject*>::const_iterator It = d->objectArray.begin();It != d->objectArray.end();++It)
01029         if ((*It)->isTouched())
01030             result.push_back(*It);
01031 
01032     return result;
01033 }
01034 
01035 void Document::setClosable(bool c)
01036 {
01037     d->closable = c;
01038 }
01039 
01040 bool Document::isClosable() const
01041 {
01042     return d->closable;
01043 }
01044 
01045 int Document::countObjects(void) const
01046 {
01047    return static_cast<int>(d->objectArray.size());
01048 }
01049 
01050 std::vector<App::DocumentObject*> Document::getInList(const DocumentObject* me) const
01051 {
01052     // result list
01053     std::vector<App::DocumentObject*> result;
01054     // go through all objects
01055     for (std::map<std::string,DocumentObject*>::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It) {
01056         // get the outList and search if me is in that list
01057         std::vector<DocumentObject*> OutList = It->second->getOutList();
01058         for (std::vector<DocumentObject*>::const_iterator It2=OutList.begin();It2!=OutList.end();++It2)
01059             if (*It2 && *It2 == me)
01060                 // add the parent object
01061                 result.push_back(It->second);
01062     }
01063     return result;
01064 }
01065 
01066 void Document::recompute()
01067 {
01068     // delete recompute log
01069     for( std::vector<App::DocumentObjectExecReturn*>::iterator it=_RecomputeLog.begin();it!=_RecomputeLog.end();++it)
01070         delete *it;
01071     _RecomputeLog.clear();
01072 
01073     DependencyList DepList;
01074     std::map<DocumentObject*,Vertex> VertexObjectList;
01075 
01076     // Filling up the adjacency List
01077     for (std::map<std::string,DocumentObject*>::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It)
01078         // add the object as Vertex and remember the index
01079         VertexObjectList[It->second] = add_vertex(DepList);
01080     // add the edges
01081     for (std::map<std::string,DocumentObject*>::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It) {
01082         std::vector<DocumentObject*> OutList = It->second->getOutList();
01083         for (std::vector<DocumentObject*>::const_iterator It2=OutList.begin();It2!=OutList.end();++It2)
01084             if (*It2)
01085                 add_edge(VertexObjectList[It->second],VertexObjectList[*It2],DepList);
01086     }
01087 
01088     std::list<Vertex> make_order;
01089     DependencyList::out_edge_iterator j, jend;
01090 
01091     try {
01092         // this sort gives the execute
01093         boost::topological_sort(DepList, std::front_inserter(make_order));
01094     }
01095     catch (const std::exception& e) {
01096         std::cerr << "Document::recompute: " << e.what() << std::endl;
01097         return;
01098     }
01099 
01100     // caching vertex to DocObject
01101     for (std::map<DocumentObject*,Vertex>::const_iterator It1= VertexObjectList.begin();It1 != VertexObjectList.end(); ++It1)
01102         d->vertexMap[It1->second] = It1->first;
01103 
01104 #ifdef FC_LOGFEATUREUPDATE
01105     std::clog << "make ordering: " << std::endl;
01106 #endif
01107 
01108     for (std::list<Vertex>::reverse_iterator i = make_order.rbegin();i != make_order.rend(); ++i) {
01109         DocumentObject* Cur = d->vertexMap[*i];
01110         if (!Cur) continue;
01111 #ifdef FC_LOGFEATUREUPDATE
01112         std::clog << Cur->getNameInDocument() << " dep on: " ;
01113 #endif
01114         bool NeedUpdate = false;
01115 
01116         // ask the object if it should be recomputed
01117         if (Cur->mustExecute() == 1)
01118             NeedUpdate = true;
01119         else {// if (Cur->mustExecute() == -1)
01120             // update if one of the dependencies is touched
01121             for (boost::tie(j, jend) = out_edges(*i, DepList); j != jend; ++j) {
01122                 DocumentObject* Test = d->vertexMap[target(*j, DepList)];
01123                 if (!Test) continue;
01124 #ifdef FC_LOGFEATUREUPDATE
01125                 std::clog << Test->getNameInDocument() << ", " ;
01126 #endif
01127                 if (Test->isTouched()) {
01128                     NeedUpdate = true;
01129                     break;
01130                 }
01131             }
01132 #ifdef FC_LOGFEATUREUPDATE
01133             std::clog << std::endl;
01134 #endif
01135         }
01136         // if one touched recompute
01137         if (NeedUpdate) {
01138 #ifdef FC_LOGFEATUREUPDATE
01139             std::clog << "Recompute" << std::endl;
01140 #endif
01141             if (_recomputeFeature(Cur)) {
01142                 // if somthing happen break execution of recompute
01143                 d->vertexMap.clear();
01144                 return;
01145             }
01146         }
01147     }
01148 
01149     // reset all touched
01150     for (std::map<Vertex,DocumentObject*>::iterator it = d->vertexMap.begin(); it != d->vertexMap.end(); ++it) {
01151         if (it->second)
01152             it->second->purgeTouched();
01153     }
01154     d->vertexMap.clear();
01155 }
01156 
01157 const char * Document::getErrorDescription(const App::DocumentObject*Obj) const
01158 {
01159     for (std::vector<App::DocumentObjectExecReturn*>::const_iterator it=_RecomputeLog.begin();it!=_RecomputeLog.end();++it)
01160         if ((*it)->Which == Obj)
01161             return (*it)->Why.c_str();
01162     return 0;
01163 }
01164 
01165 // call the recompute of the Feature and handle the exceptions and errors.
01166 bool Document::_recomputeFeature(DocumentObject* Feat)
01167 {
01168 #ifdef FC_LOGFEATUREUPDATE
01169     std::clog << "Solv: Executing Feature: " << Feat->getNameInDocument() << std::endl;;
01170 #endif
01171 
01172     DocumentObjectExecReturn  *returnCode = 0;
01173     try {
01174         returnCode = Feat->recompute();
01175     }
01176     catch(Base::AbortException &e){
01177         e.ReportException();
01178         _RecomputeLog.push_back(new DocumentObjectExecReturn("User abort",Feat));
01179         Feat->setError();
01180         return true;
01181     }
01182     catch (const Base::MemoryException& e) {
01183         Base::Console().Error("Memory exception in feature '%s' thrown: %s\n",Feat->getNameInDocument(),e.what());
01184         _RecomputeLog.push_back(new DocumentObjectExecReturn("Out of memory exception",Feat));
01185         Feat->setError();
01186         return true;
01187     }
01188     catch (Base::Exception &e) {
01189         e.ReportException();
01190         _RecomputeLog.push_back(new DocumentObjectExecReturn(e.what(),Feat));
01191         Feat->setError();
01192         return false;
01193     }
01194     catch (std::exception &e) {
01195         Base::Console().Warning("exception in Feature \"%s\" thrown: %s\n",Feat->getNameInDocument(),e.what());
01196         _RecomputeLog.push_back(new DocumentObjectExecReturn(e.what(),Feat));
01197         Feat->setError();
01198         return false;
01199     }
01200 #ifndef FC_DEBUG
01201     catch (...) {
01202         Base::Console().Error("App::Document::_RecomputeFeature(): Unknown exception in Feature \"%s\" thrown\n",Feat->getNameInDocument());
01203         _RecomputeLog.push_back(new DocumentObjectExecReturn("Unknown exeption!"));
01204         Feat->setError();
01205         return true;
01206     }
01207 #endif
01208 
01209     // error code
01210     if (returnCode == DocumentObject::StdReturn) {
01211         Feat->resetError();
01212     }
01213     else {
01214         returnCode->Which = Feat;
01215         _RecomputeLog.push_back(returnCode);
01216         Base::Console().Error("%s\n",returnCode->Why.c_str());
01217         Feat->setError();
01218     }
01219     return false;
01220 }
01221 
01222 void Document::recomputeFeature(DocumentObject* Feat)
01223 {
01224      // delete recompute log
01225     for( std::vector<App::DocumentObjectExecReturn*>::iterator it=_RecomputeLog.begin();it!=_RecomputeLog.end();++it)
01226         delete *it;
01227     _RecomputeLog.clear();
01228 
01229     _recomputeFeature(Feat);
01230 }
01231 
01232 DocumentObject * Document::addObject(const char* sType, const char* pObjectName)
01233 {
01234     Base::BaseClass* base = static_cast<Base::BaseClass*>(Base::Type::createInstanceByName(sType,true));
01235 
01236     string ObjectName;
01237     if (!base)
01238         return 0;
01239     if (!base->getTypeId().isDerivedFrom(App::DocumentObject::getClassTypeId())) {
01240         delete base;
01241         std::stringstream str;
01242         str << "'" << sType << "' is not a document object type";
01243         throw Base::Exception(str.str());
01244     }
01245 
01246     App::DocumentObject* pcObject = static_cast<App::DocumentObject*>(base);
01247     pcObject->setDocument(this);
01248 
01249     // do no transactions if we do a rollback!
01250     if(!d->rollback){
01251         // Transaction stuff
01252         if (d->activeTransaction)
01253             d->activeTransaction->addObjectNew(pcObject);
01254         // Undo stuff
01255         if (d->activeUndoTransaction)
01256             d->activeUndoTransaction->addObjectDel(pcObject);
01257     }
01258 
01259     // get Unique name
01260     if (pObjectName && pObjectName[0] != '\0')
01261         ObjectName = getUniqueObjectName(pObjectName);
01262     else
01263         ObjectName = getUniqueObjectName(sType);
01264 
01265 
01266     d->activeObject = pcObject;
01267 
01268     // insert in the name map
01269     d->objectMap[ObjectName] = pcObject;
01270     // cache the pointer to the name string in the Object (for performance of DocumentObject::getNameInDocument())
01271     pcObject->pcNameInDocument = &(d->objectMap.find(ObjectName)->first);
01272     // insert in the vector
01273     d->objectArray.push_back(pcObject);
01274     // insert in the adjacence list and referenc through the ConectionMap
01275     //_DepConMap[pcObject] = add_vertex(_DepList);
01276 
01277     pcObject->Label.setValue( ObjectName );
01278 
01279     // mark the object as new (i.e. set status bit 2) and send the signal
01280     pcObject->StatusBits.set(2);
01281     signalNewObject(*pcObject);
01282     signalActivatedObject(*pcObject);
01283 
01284     // return the Object
01285     return pcObject;
01286 }
01287 
01288 void Document::_addObject(DocumentObject* pcObject, const char* pObjectName)
01289 {
01290     d->objectMap[pObjectName] = pcObject;
01291     d->objectArray.push_back(pcObject);
01292     // cache the pointer to the name string in the Object (for performance of DocumentObject::getNameInDocument())
01293     pcObject->pcNameInDocument = &(d->objectMap.find(pObjectName)->first);
01294 
01295     // do no transactions if we do a rollback!
01296     if(!d->rollback){
01297         // Transaction stuff
01298         if (d->activeTransaction)
01299             d->activeTransaction->addObjectNew(pcObject);
01300         // Undo stuff
01301         if (d->activeUndoTransaction)
01302             d->activeUndoTransaction->addObjectDel(pcObject);
01303     }
01304     // send the signal
01305     signalNewObject(*pcObject);
01306 }
01307 
01309 void Document::remObject(const char* sName)
01310 {
01311     std::map<std::string,DocumentObject*>::iterator pos = d->objectMap.find(sName);
01312 
01313     // name not found?
01314     if (pos == d->objectMap.end())
01315         return;
01316 
01317     if (d->activeObject == pos->second)
01318         d->activeObject = 0;
01319 
01320     signalDeletedObject(*(pos->second));
01321     if (!d->vertexMap.empty()) {
01322         // recompute of document is running
01323         for (std::map<Vertex,DocumentObject*>::iterator it = d->vertexMap.begin(); it != d->vertexMap.end(); ++it) {
01324             if (it->second == pos->second) {
01325                 it->second = 0; // just nullify the pointer
01326                 break;
01327             }
01328         }
01329     }
01330 
01331     // Before deleting we must nullify all dependant objects
01332     breakDependency(pos->second, true);
01333 
01334     // do no transactions if we do a rollback!
01335     if(!d->rollback){
01336 
01337         // Transaction stuff
01338         if (d->activeTransaction)
01339             d->activeTransaction->addObjectDel(pos->second);
01340 
01341         // Undo stuff
01342         if (d->activeUndoTransaction) {
01343             // in this case transaction delete or save the object
01344             d->activeUndoTransaction->addObjectNew(pos->second);
01345             // set name cache false
01346             //pos->second->pcNameInDocument = 0;
01347         }
01348         else
01349             // if not saved in undo -> delete object
01350             delete pos->second;
01351     }
01352 
01353     for (std::vector<DocumentObject*>::iterator obj = d->objectArray.begin(); obj != d->objectArray.end(); ++obj) {
01354         if (*obj == pos->second) {
01355             d->objectArray.erase(obj);
01356             break;
01357         }
01358     }
01359     // remove from adjancy list
01360     //remove_vertex(_DepConMap[pos->second],_DepList);
01361     //_DepConMap.erase(pos->second);
01362     d->objectMap.erase(pos);
01363 }
01364 
01366 void Document::_remObject(DocumentObject* pcObject)
01367 {
01368     std::map<std::string,DocumentObject*>::iterator pos = d->objectMap.find(pcObject->getNameInDocument());
01369 
01370     if (d->activeObject == pcObject)
01371         d->activeObject = 0;
01372 
01373     signalDeletedObject(*pcObject);
01374 
01375     // do no transactions if we do a rollback!
01376     if(!d->rollback){
01377         // Transaction stuff
01378         if (d->activeTransaction)
01379             d->activeTransaction->addObjectDel(pcObject);
01380 
01381         // Undo stuff
01382         if (d->activeUndoTransaction)
01383             d->activeUndoTransaction->addObjectNew(pcObject);
01384     }
01385     // remove from map
01386     d->objectMap.erase(pos);
01388     //pcObject->pcNameInDocument = 0;
01389 
01390     for (std::vector<DocumentObject*>::iterator it = d->objectArray.begin(); it != d->objectArray.end(); ++it) {
01391         if (*it == pcObject) {
01392             d->objectArray.erase(it);
01393             break;
01394         }
01395     }
01396 }
01397 
01398 void Document::breakDependency(DocumentObject* pcObject, bool clear)
01399 {
01400     // Nullify all dependant objects
01401     for (std::map<std::string,DocumentObject*>::iterator it = d->objectMap.begin(); it != d->objectMap.end(); ++it) {
01402         std::map<std::string,App::Property*> Map;
01403         it->second->getPropertyMap(Map);
01404         // search for all properties that could have a link to the object
01405         for (std::map<std::string,App::Property*>::iterator pt = Map.begin(); pt != Map.end(); ++pt) {
01406             if (pt->second->getTypeId().isDerivedFrom(PropertyLink::getClassTypeId())) {
01407                 PropertyLink* link = static_cast<PropertyLink*>(pt->second);
01408                 if (link->getValue() == pcObject)
01409                     link->setValue(0);
01410                 else if (link->getContainer() == pcObject && clear)
01411                     link->setValue(0);
01412             }
01413             else if (pt->second->getTypeId().isDerivedFrom(PropertyLinkSub::getClassTypeId())) {
01414                 PropertyLinkSub* link = static_cast<PropertyLinkSub*>(pt->second);
01415                 if (link->getValue() == pcObject)
01416                     link->setValue(0);
01417                 else if (link->getContainer() == pcObject && clear)
01418                     link->setValue(0);
01419             }
01420             else if (pt->second->getTypeId().isDerivedFrom(PropertyLinkList::getClassTypeId())) {
01421                 PropertyLinkList* link = static_cast<PropertyLinkList*>(pt->second);
01422                 if (link->getContainer() == pcObject && clear) {
01423                     link->setValues(std::vector<DocumentObject*>());
01424                 }
01425                 else {
01426                     // copy the list (not the objects)
01427                     std::vector<DocumentObject*> linked = link->getValues();
01428                     for (std::vector<DocumentObject*>::iterator fIt = linked.begin(); fIt != linked.end(); ++fIt) {
01429                         if ((*fIt) == pcObject) {
01430                             // reassign the the list without the object to be deleted
01431                             linked.erase(fIt);
01432                             link->setValues(linked);
01433                             break;
01434                         }
01435                     }
01436                 }
01437             }
01438         }
01439     }
01440 }
01441 
01442 DocumentObject* Document::_copyObject(DocumentObject* obj, std::map<DocumentObject*, 
01443                                       DocumentObject*>& copy_map, bool recursive)
01444 {
01445     if (!obj) return 0;
01446     // remove number from end to avoid lengthy names
01447     std::string objname = obj->getNameInDocument();
01448     size_t lastpos = objname.length()-1;
01449     while (objname[lastpos] >= 48 && objname[lastpos] <= 57)
01450         lastpos--;
01451     objname = objname.substr(0, lastpos+1);
01452     DocumentObject* copy = addObject(obj->getTypeId().getName(),objname.c_str());
01453     if (!copy) return 0;
01454     copy->addDynamicProperties(obj);
01455 
01456     copy_map[obj] = copy;
01457 
01458     std::map<std::string,App::Property*> props;
01459     copy->getPropertyMap(props);
01460     for (std::map<std::string,App::Property*>::iterator it = props.begin(); it != props.end(); ++it) {
01461         App::Property* prop = obj->getPropertyByName(it->first.c_str());
01462         if (prop && prop->getTypeId() == it->second->getTypeId()) {
01463             if (prop->getTypeId() == PropertyLink::getClassTypeId()) {
01464                 DocumentObject* link = static_cast<PropertyLink*>(prop)->getValue();
01465                 std::map<DocumentObject*, DocumentObject*>::iterator pt = copy_map.find(link);
01466                 if (pt != copy_map.end()) {
01467                     // the object has already been copied
01468                     static_cast<PropertyLink*>(it->second)->setValue(pt->second);
01469                 }
01470                 else if (recursive) {
01471                     DocumentObject* link_copy = _copyObject(link, copy_map, recursive);
01472                     copy_map[link] = link_copy;
01473                     static_cast<PropertyLink*>(it->second)->setValue(link_copy);
01474                 }
01475                 else if (link && link->getDocument() == this) {
01476                     //static_cast<PropertyLink*>(it->second)->setValue(link);
01477                 }
01478             }
01479             else if (prop->getTypeId() == PropertyLinkList::getClassTypeId()) {
01480                 std::vector<DocumentObject*> links = static_cast<PropertyLinkList*>(prop)->getValues();
01481                 if (recursive) {
01482                     std::vector<DocumentObject*> links_copy;
01483                     for (std::vector<DocumentObject*>::iterator jt = links.begin(); jt != links.end(); ++jt) {
01484                         std::map<DocumentObject*, DocumentObject*>::iterator pt = copy_map.find(*jt);
01485                         if (pt != copy_map.end()) {
01486                             // the object has already been copied
01487                             links_copy.push_back(pt->second);
01488                         }
01489                         else {
01490                             links_copy.push_back(_copyObject(*jt, copy_map, recursive));
01491                             copy_map[*jt] = links_copy.back();
01492                         }
01493                     }
01494                     static_cast<PropertyLinkList*>(it->second)->setValues(links_copy);
01495                 }
01496                 else {
01497                     std::vector<DocumentObject*> links_ref;
01498                     //for (std::vector<DocumentObject*>::iterator jt = links.begin(); jt != links.end(); ++jt) {
01499                     //    if ((*jt)->getDocument() == this)
01500                     //        links_ref.push_back(*jt);
01501                     //}
01502                     static_cast<PropertyLinkList*>(it->second)->setValues(links_ref);
01503                 }
01504             }
01505             else {
01506                 std::auto_ptr<Property> data(prop->Copy());
01507                 if (data.get()) {
01508                     it->second->Paste(*data);
01509                 }
01510             }
01511         }
01512     }
01513 
01514      // unmark to be not re-computed later
01515     copy->purgeTouched();
01516     return copy;
01517 }
01518 
01519 DocumentObject* Document::copyObject(DocumentObject* obj, bool recursive)
01520 {
01521     std::map<DocumentObject*, DocumentObject*> copy_map;
01522     DocumentObject* copy = _copyObject(obj, copy_map, recursive);
01523     return copy;
01524 }
01525 
01526 DocumentObject* Document::moveObject(DocumentObject* obj, bool recursive)
01527 {
01528     Document* that = obj->getDocument();
01529     if (that == this)
01530         return 0; // nothing todo
01531 
01532     // all object of the other document that refer to this object must be nullified
01533     that->breakDependency(obj, false);
01534     std::string objname = getUniqueObjectName(obj->getNameInDocument());
01535     that->_remObject(obj);
01536     this->_addObject(obj, objname.c_str());
01537     obj->setDocument(this);
01538 
01539     std::map<std::string,App::Property*> props;
01540     obj->getPropertyMap(props);
01541     for (std::map<std::string,App::Property*>::iterator it = props.begin(); it != props.end(); ++it) {
01542         if (it->second->getTypeId() == PropertyLink::getClassTypeId()) {
01543             DocumentObject* link = static_cast<PropertyLink*>(it->second)->getValue();
01544             if (recursive) {
01545                 moveObject(link, recursive);
01546                 static_cast<PropertyLink*>(it->second)->setValue(link);
01547             }
01548             else {
01549                 static_cast<PropertyLink*>(it->second)->setValue(0);
01550             }
01551         }
01552         else if (it->second->getTypeId() == PropertyLinkList::getClassTypeId()) {
01553             std::vector<DocumentObject*> links = static_cast<PropertyLinkList*>(it->second)->getValues();
01554             if (recursive) {
01555                 for (std::vector<DocumentObject*>::iterator jt = links.begin(); jt != links.end(); ++jt)
01556                     moveObject(*jt, recursive);
01557                 static_cast<PropertyLinkList*>(it->second)->setValues(links);
01558             }
01559             else {
01560                 static_cast<PropertyLinkList*>(it->second)->setValues(std::vector<DocumentObject*>());
01561             }
01562         }
01563     }
01564 
01565     return obj;
01566 }
01567 
01568 DocumentObject * Document::getActiveObject(void) const
01569 {
01570     return d->activeObject;
01571 }
01572 
01573 DocumentObject * Document::getObject(const char *Name) const
01574 {
01575     std::map<std::string,DocumentObject*>::const_iterator pos;
01576 
01577     pos = d->objectMap.find(Name);
01578 
01579     if (pos != d->objectMap.end())
01580         return pos->second;
01581     else
01582         return 0;
01583 }
01584 
01585 const char * Document::getObjectName(DocumentObject *pFeat) const
01586 {
01587     std::map<std::string,DocumentObject*>::const_iterator pos;
01588 
01589     for (pos = d->objectMap.begin();pos != d->objectMap.end();++pos)
01590         if (pos->second == pFeat)
01591             return pos->first.c_str();
01592 
01593     return 0;
01594 }
01595 
01596 std::string Document::getUniqueObjectName(const char *Name) const
01597 {
01598     if (!Name || *Name == '\0')
01599         return std::string();
01600     std::string CleanName = Base::Tools::getIdentifier(Name);
01601 
01602     // name in use?
01603     std::map<std::string,DocumentObject*>::const_iterator pos;
01604     pos = d->objectMap.find(CleanName);
01605 
01606     if (pos == d->objectMap.end()) {
01607         // if not, name is OK
01608         return CleanName;
01609     }
01610     else {
01611         std::vector<std::string> names;
01612         names.reserve(d->objectMap.size());
01613         for (pos = d->objectMap.begin();pos != d->objectMap.end();++pos) {
01614             names.push_back(pos->first);
01615         }
01616         return Base::Tools::getUniqueName(CleanName, names, 3);
01617     }
01618 }
01619 
01620 std::string Document::getStandardObjectName(const char *Name, int d) const
01621 {
01622     std::vector<App::DocumentObject*> mm = getObjects();
01623     std::vector<std::string> labels;
01624     labels.reserve(mm.size());
01625 
01626     for (std::vector<App::DocumentObject*>::const_iterator it = mm.begin(); it != mm.end(); ++it) {
01627         std::string label = (*it)->Label.getValue();
01628         labels.push_back(label);
01629     }
01630     return Base::Tools::getUniqueName(Name, labels, d);
01631 }
01632 
01633 std::vector<DocumentObject*> Document::getObjects() const
01634 {
01635     return d->objectArray;
01636 }
01637 
01638 std::vector<DocumentObject*> Document::getObjectsOfType(const Base::Type& typeId) const
01639 {
01640     std::vector<DocumentObject*> Objects;
01641     for (std::vector<DocumentObject*>::const_iterator it = d->objectArray.begin(); it != d->objectArray.end(); ++it) {
01642         if ((*it)->getTypeId().isDerivedFrom(typeId))
01643             Objects.push_back(*it);
01644     }
01645     return Objects;
01646 }
01647 
01648 int Document::countObjectsOfType(const Base::Type& typeId) const
01649 {
01650     int ct=0;
01651     for (std::map<std::string,DocumentObject*>::const_iterator it = d->objectMap.begin(); it != d->objectMap.end(); ++it) {
01652         if (it->second->getTypeId().isDerivedFrom(typeId))
01653             ct++;
01654     }
01655 
01656     return ct;
01657 }
01658 
01659 PyObject * Document::getPyObject(void)
01660 {
01661     return Py::new_reference_to(DocumentPythonObject);
01662 }

Generated on Wed Nov 23 19:00:07 2011 for FreeCAD by  doxygen 1.6.1