Salome HOME
Merge branch 'master' of newgeom:newgeom.git
[modules/shaper.git] / src / Model / Model_Document.cpp
index cb1b6d3073a089da1cfbfa54e900888ff0bf6496..c51d32a74d523db9b02a0c186ceecab79f9421aa 100644 (file)
@@ -5,7 +5,7 @@
 #include <Model_Document.h>
 #include <Model_Data.h>
 #include <Model_Application.h>
-#include <Model_PluginManager.h>
+#include <Model_Session.h>
 #include <Model_Events.h>
 #include <Model_ResultPart.h>
 #include <Model_ResultConstruction.h>
@@ -45,8 +45,8 @@ static const int TAG_HISTORY = 3;  // tag of the history sub-tree (python dump)
 static const int TAG_FEATURE_ARGUMENTS = 1;  ///< where the arguments are located
 static const int TAG_FEATURE_RESULTS = 2;  ///< where the results are located
 
-Model_Document::Model_Document(const std::string theID)
-    : myID(theID),
+Model_Document::Model_Document(const std::string theID, const std::string theKind)
+    : myID(theID), myKind(theKind),
       myDoc(new TDocStd_Document("BinOcaf"))  // binary OCAF format
 {
   myDoc->SetUndoLimit(UNDO_LIMIT);
@@ -65,6 +65,9 @@ Model_Document::Model_Document(const std::string theID)
 static TCollection_ExtendedString DocFileName(const char* theFileName, const std::string& theID)
 {
   TCollection_ExtendedString aPath((const Standard_CString) theFileName);
+  // remove end-separators
+  while(aPath.Length() && (aPath.Value(aPath.Length()) == '\\' || aPath.Value(aPath.Length()) == '/'))
+    aPath.Remove(aPath.Length());
   aPath += _separator_;
   aPath += theID.c_str();
   aPath += ".cbf";  // standard binary file extension
@@ -74,7 +77,7 @@ static TCollection_ExtendedString DocFileName(const char* theFileName, const std
 bool Model_Document::load(const char* theFileName)
 {
   Handle(Model_Application) anApp = Model_Application::getApplication();
-  if (this == Model_PluginManager::get()->rootDocument().get()) {
+  if (this == Model_Session::get()->moduleDocument().get()) {
     anApp->setLoadPath(theFileName);
   }
   TCollection_ExtendedString aPath(DocFileName(theFileName, myID));
@@ -151,7 +154,7 @@ bool Model_Document::load(const char* theFileName)
 bool Model_Document::save(const char* theFileName, std::list<std::string>& theResults)
 {
   // create a directory in the root document if it is not yet exist
-  if (this == Model_PluginManager::get()->rootDocument().get()) {
+  if (this == Model_Session::get()->moduleDocument().get()) {
 #ifdef WIN32
     CreateDirectory(theFileName, NULL);
 #else
@@ -188,22 +191,23 @@ bool Model_Document::save(const char* theFileName, std::list<std::string>& theRe
   if (isDone) {  // save also sub-documents if any
     theResults.push_back(TCollection_AsciiString(aPath).ToCString());
     std::set<std::string>::iterator aSubIter = mySubs.begin();
-    for (; aSubIter != mySubs.end() && isDone; aSubIter++)
-      isDone = subDocument(*aSubIter)->save(theFileName, theResults);
+    for (; aSubIter != mySubs.end() && isDone; aSubIter++) {
+      isDone = subDoc(*aSubIter)->save(theFileName, theResults);
+    }
   }
   return isDone;
 }
 
 void Model_Document::close()
 {
-  boost::shared_ptr<ModelAPI_PluginManager> aPM = Model_PluginManager::get();
-  if (this != aPM->rootDocument().get() && this == aPM->currentDocument().get()) {
-    aPM->setCurrentDocument(aPM->rootDocument());
+  boost::shared_ptr<ModelAPI_Session> aPM = Model_Session::get();
+  if (this != aPM->moduleDocument().get() && this == aPM->activeDocument().get()) {
+    aPM->setActiveDocument(aPM->moduleDocument());
   }
   // close all subs
   std::set<std::string>::iterator aSubIter = mySubs.begin();
   for (; aSubIter != mySubs.end(); aSubIter++)
-    subDocument(*aSubIter)->close();
+    subDoc(*aSubIter)->close();
   mySubs.clear();
   // close this
   /* do not close because it can be undoed
@@ -220,19 +224,19 @@ void Model_Document::startOperation()
       myNestedNum = 0;
       myDoc->InitDeltaCompaction();
     }
-    myIsEmptyTr[myTransactionsAfterSave] = false;
+    myIsEmptyTr[myTransactionsAfterSave] = !myDoc->CommitCommand();
     myTransactionsAfterSave++;
-    myDoc->NewCommand();
+    myDoc->OpenCommand();
   } else {  // start the simple command
     myDoc->NewCommand();
   }
   // new command for all subs
   std::set<std::string>::iterator aSubIter = mySubs.begin();
   for (; aSubIter != mySubs.end(); aSubIter++)
-    subDocument(*aSubIter)->startOperation();
+    subDoc(*aSubIter)->startOperation();
 }
 
-void Model_Document::compactNested()
+bool Model_Document::compactNested()
 {
   bool allWasEmpty = true;
   while (myNestedNum != -1) {
@@ -246,14 +250,19 @@ void Model_Document::compactNested()
   myIsEmptyTr[myTransactionsAfterSave] = allWasEmpty;
   myTransactionsAfterSave++;
   myDoc->PerformDeltaCompaction();
+  return !allWasEmpty;
 }
 
 void Model_Document::finishOperation()
 {
-  // just to be sure that everybody knows that changes were performed
+  // finish for all subs first: to avoid nested finishing and "isOperation" calls problems inside
+  std::set<std::string>::iterator aSubIter = mySubs.begin();
+  for (; aSubIter != mySubs.end(); aSubIter++)
+    subDoc(*aSubIter)->finishOperation();
 
+  // just to be sure that everybody knows that changes were performed
   if (!myDoc->HasOpenCommand() && myNestedNum != -1)
-    boost::static_pointer_cast<Model_PluginManager>(Model_PluginManager::get())
+    boost::static_pointer_cast<Model_Session>(Model_Session::get())
         ->setCheckTransactions(false);  // for nested transaction commit
   Events_Loop* aLoop = Events_Loop::loop();
   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_CREATED));
@@ -261,7 +270,7 @@ void Model_Document::finishOperation()
   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED));
   if (!myDoc->HasOpenCommand() && myNestedNum != -1)
-    boost::static_pointer_cast<Model_PluginManager>(Model_PluginManager::get())
+    boost::static_pointer_cast<Model_Session>(Model_Session::get())
         ->setCheckTransactions(true);  // for nested transaction commit
 
   if (myNestedNum != -1)  // this nested transaction is owervritten
@@ -277,19 +286,16 @@ void Model_Document::finishOperation()
     myTransactionsAfterSave++;
   }
 
-  // finish for all subs
-  std::set<std::string>::iterator aSubIter = mySubs.begin();
-  for (; aSubIter != mySubs.end(); aSubIter++)
-    subDocument(*aSubIter)->finishOperation();
 }
 
 void Model_Document::abortOperation()
 {
   if (myNestedNum > 0 && !myDoc->HasOpenCommand()) {  // abort all what was done in nested
       // first compact all nested
-    compactNested();
-    // for nested it is undo and clear redos
-    myDoc->Undo();
+    if (compactNested()) {
+      // for nested it is undo and clear redos
+      myDoc->Undo();
+    }
     myDoc->ClearRedos();
     myTransactionsAfterSave--;
     myIsEmptyTr.erase(myTransactionsAfterSave);
@@ -302,7 +308,7 @@ void Model_Document::abortOperation()
   // abort for all subs
   std::set<std::string>::iterator aSubIter = mySubs.begin();
   for (; aSubIter != mySubs.end(); aSubIter++)
-    subDocument(*aSubIter)->abortOperation();
+    subDoc(*aSubIter)->abortOperation();
 }
 
 bool Model_Document::isOperation()
@@ -314,7 +320,7 @@ bool Model_Document::isOperation()
 bool Model_Document::isModified()
 {
   // is modified if at least one operation was commited and not undoed
-  return myTransactionsAfterSave > 0;
+  return myTransactionsAfterSave > 0 || isOperation();
 }
 
 bool Model_Document::canUndo()
@@ -325,7 +331,7 @@ bool Model_Document::canUndo()
   // check other subs contains operation that can be undoed
   std::set<std::string>::iterator aSubIter = mySubs.begin();
   for (; aSubIter != mySubs.end(); aSubIter++)
-    if (subDocument(*aSubIter)->canUndo())
+    if (subDoc(*aSubIter)->canUndo())
       return true;
   return false;
 }
@@ -341,7 +347,7 @@ void Model_Document::undo()
   // undo for all subs
   std::set<std::string>::iterator aSubIter = mySubs.begin();
   for (; aSubIter != mySubs.end(); aSubIter++)
-    subDocument(*aSubIter)->undo();
+    subDoc(*aSubIter)->undo();
 }
 
 bool Model_Document::canRedo()
@@ -351,7 +357,7 @@ bool Model_Document::canRedo()
   // check other subs contains operation that can be redoed
   std::set<std::string>::iterator aSubIter = mySubs.begin();
   for (; aSubIter != mySubs.end(); aSubIter++)
-    if (subDocument(*aSubIter)->canRedo())
+    if (subDoc(*aSubIter)->canRedo())
       return true;
   return false;
 }
@@ -367,7 +373,7 @@ void Model_Document::redo()
   // redo for all subs
   std::set<std::string>::iterator aSubIter = mySubs.begin();
   for (; aSubIter != mySubs.end(); aSubIter++)
-    subDocument(*aSubIter)->redo();
+    subDoc(*aSubIter)->redo();
 }
 
 /// Appenad to the array of references a new referenced label
@@ -392,7 +398,9 @@ FeaturePtr Model_Document::addFeature(std::string theID)
 {
   TDF_Label anEmptyLab;
   FeaturePtr anEmptyFeature;
-  FeaturePtr aFeature = ModelAPI_PluginManager::get()->createFeature(theID);
+  FeaturePtr aFeature = ModelAPI_Session::get()->createFeature(theID);
+  if (!aFeature)
+    return aFeature;
   boost::shared_ptr<Model_Document> aDocToAdd = boost::dynamic_pointer_cast<Model_Document>(
       aFeature->documentToAdd());
   if (aFeature) {
@@ -413,6 +421,9 @@ FeaturePtr Model_Document::addFeature(std::string theID)
       // event: feature is added
       static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED);
       ModelAPI_EventCreator::get()->sendUpdated(aFeature, anEvent);
+    } else { // feature must be executed
+       // no creation event => updater not working, problem with remove part
+      aFeature->execute();
     }
   }
   return aFeature;
@@ -450,15 +461,35 @@ static int RemoveFromRefArray(TDF_Label theArrayLab, TDF_Label theReferenced, co
   return aResult;
 }
 
-void Model_Document::removeFeature(FeaturePtr theFeature)
+void Model_Document::removeFeature(FeaturePtr theFeature, const bool theCheck)
 {
+  if (theCheck) {
+    // check the feature: it must have no depended objects on it
+    std::list<ResultPtr>::const_iterator aResIter = theFeature->results().cbegin();
+    for(; aResIter != theFeature->results().cend(); aResIter++) {
+      if (myConcealedResults.find(*aResIter) != myConcealedResults.end()) {
+        Events_Error::send("Feature '" + theFeature->data()->name() + "' is used and can not be deleted");
+        return;
+      }
+    }
+    NCollection_DataMap<TDF_Label, FeaturePtr>::Iterator anObjIter(myObjs);
+    for(; anObjIter.More(); anObjIter.Next()) {
+      DataPtr aData = anObjIter.Value()->data();
+      if (aData->referencesTo(theFeature)) {
+        Events_Error::send("Feature '" + theFeature->data()->name() + "' is used and can not be deleted");
+        return;
+      }
+    }
+  }
+
   boost::shared_ptr<Model_Data> aData = boost::static_pointer_cast<Model_Data>(theFeature->data());
   TDF_Label aFeatureLabel = aData->label().Father();
   if (myObjs.IsBound(aFeatureLabel))
     myObjs.UnBind(aFeatureLabel);
   else
     return;  // not found feature => do not remove
-
+  // erase fields
+  theFeature->erase();
   // erase all attributes under the label of feature
   aFeatureLabel.ForgetAllAttributes();
   // remove it from the references array
@@ -466,6 +497,7 @@ void Model_Document::removeFeature(FeaturePtr theFeature)
 
   // event: feature is deleted
   ModelAPI_EventCreator::get()->sendDeleted(theFeature->document(), ModelAPI_Feature::group());
+  /* this is in "erase"
   // results of this feature must be redisplayed
   static Events_ID EVENT_DISP = Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY);
   const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = theFeature->results();
@@ -476,6 +508,7 @@ void Model_Document::removeFeature(FeaturePtr theFeature)
     ModelAPI_EventCreator::get()->sendUpdated(aRes, EVENT_DISP);
     ModelAPI_EventCreator::get()->sendDeleted(theFeature->document(), aRes->groupName());
   }
+  */
 }
 
 FeaturePtr Model_Document::feature(TDF_Label& theLabel)
@@ -514,6 +547,15 @@ boost::shared_ptr<ModelAPI_Document> Model_Document::subDocument(std::string the
   return Model_Application::getApplication()->getDocument(theDocID);
 }
 
+boost::shared_ptr<Model_Document> Model_Document::subDoc(std::string theDocID)
+{
+  // just store sub-document identifier here to manage it later
+  if (mySubs.find(theDocID) == mySubs.end())
+    mySubs.insert(theDocID);
+  return boost::dynamic_pointer_cast<Model_Document>(
+    Model_Application::getApplication()->getDocument(theDocID));
+}
+
 ObjectPtr Model_Document::object(const std::string& theGroupID, const int theIndex,
                                  const bool theHidden)
 {
@@ -547,7 +589,12 @@ ObjectPtr Model_Document::object(const std::string& theGroupID, const int theInd
       const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
       std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
       for (; aRIter != aResults.cend(); aRIter++) {
-        if ((theHidden || (*aRIter)->isInHistory()) && (*aRIter)->groupName() == theGroupID) {
+        if ((*aRIter)->groupName() != theGroupID) continue;
+        bool isIn = theHidden;
+        if (!isIn && (*aRIter)->isInHistory()) { // check that there is nobody references this result
+          isIn = myConcealedResults.find(*aRIter) == myConcealedResults.end();
+        }
+        if (isIn) {
           if (anIndex == theIndex)
             return *aRIter;
           anIndex++;
@@ -579,9 +626,13 @@ int Model_Document::size(const std::string& theGroupID, const bool theHidden)
       const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
       std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
       for (; aRIter != aResults.cend(); aRIter++) {
-        if ((theHidden || (*aRIter)->isInHistory()) && (*aRIter)->groupName() == theGroupID) {
-          aResult++;
+        if ((*aRIter)->groupName() != theGroupID) continue;
+        bool isIn = theHidden;
+        if (!isIn && (*aRIter)->isInHistory()) { // check that there is nobody references this result
+          isIn = myConcealedResults.find(*aRIter) == myConcealedResults.end();
         }
+        if (isIn)
+          aResult++;
       }
     }
   }
@@ -655,7 +706,7 @@ void Model_Document::synchronizeFeatures(const bool theMarkUpdated)
   boost::shared_ptr<ModelAPI_Document> aThis = 
     Model_Application::getApplication()->getDocument(myID);
   // after all updates, sends a message that groups of features were created or updated
-  boost::static_pointer_cast<Model_PluginManager>(Model_PluginManager::get())
+  boost::static_pointer_cast<Model_Session>(Model_Session::get())
     ->setCheckTransactions(false);
   Events_Loop* aLoop = Events_Loop::loop();
   aLoop->activateFlushes(false);
@@ -667,7 +718,7 @@ void Model_Document::synchronizeFeatures(const bool theMarkUpdated)
     TDF_Label aFeatureLabel = aLabIter.Value()->Label();
     if (!myObjs.IsBound(aFeatureLabel)) {  // a new feature is inserted
       // create a feature
-      FeaturePtr aNewObj = ModelAPI_PluginManager::get()->createFeature(
+      FeaturePtr aNewObj = ModelAPI_Session::get()->createFeature(
           TCollection_AsciiString(Handle(TDataStd_Comment)::DownCast(aLabIter.Value())->Get())
               .ToCString());
       if (!aNewObj) {  // somethig is wrong, most probably, the opened document has invalid structure
@@ -707,25 +758,29 @@ void Model_Document::synchronizeFeatures(const bool theMarkUpdated)
     if (aKeptFeatures.find(aFIter.Value()) == aKeptFeatures.end()
         && aNewFeatures.find(aFIter.Value()) == aNewFeatures.end()) {
       FeaturePtr aFeature = aFIter.Value();
-      TDF_Label aLab = aFIter.Key();
-      aFIter.Next();
-      myObjs.UnBind(aLab);
       // event: model is updated
-      if (aFeature->isInHistory()) {
+      //if (aFeature->isInHistory()) {
         ModelAPI_EventCreator::get()->sendDeleted(aThis, ModelAPI_Feature::group());
-      }
+      //}
       // results of this feature must be redisplayed (hided)
       static Events_ID EVENT_DISP = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
       const std::list<boost::shared_ptr<ModelAPI_Result> >& aResults = aFeature->results();
       std::list<boost::shared_ptr<ModelAPI_Result> >::const_iterator aRIter = aResults.begin();
+      /*
       for (; aRIter != aResults.cend(); aRIter++) {
         boost::shared_ptr<ModelAPI_Result> aRes = *aRIter;
         //aRes->setData(boost::shared_ptr<ModelAPI_Data>()); // deleted flag
         ModelAPI_EventCreator::get()->sendUpdated(aRes, EVENT_DISP);
         ModelAPI_EventCreator::get()->sendDeleted(aThis, aRes->groupName());
       }
+      */
       // redisplay also removed feature (used for sketch and AISObject)
       ModelAPI_EventCreator::get()->sendUpdated(aFeature, EVENT_DISP);
+      aFeature->erase();
+      // unbind after the "erase" call: on abort sketch is removes sub-objects that corrupts aFIter
+      TDF_Label aLab = aFIter.Key();
+      aFIter.Next();
+      myObjs.UnBind(aLab);
     } else
       aFIter.Next();
   }
@@ -734,12 +789,12 @@ void Model_Document::synchronizeFeatures(const bool theMarkUpdated)
   aLoop->activateFlushes(true);
 
   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_CREATED));
+  aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED));
   if (theMarkUpdated) {
     aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
   }
-  aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED));
   aLoop->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
-  boost::static_pointer_cast<Model_PluginManager>(Model_PluginManager::get())
+  boost::static_pointer_cast<Model_Session>(Model_Session::get())
     ->setCheckTransactions(true);
   myExecuteFeatures = true;
 }
@@ -863,8 +918,8 @@ void Model_Document::updateResults(FeaturePtr theFeature)
         aNewBody = createBody(theFeature->data(), aResIndex);
       } else if (anArgLab.IsAttribute(ID_PART)) {
         aNewBody = createPart(theFeature->data(), aResIndex);
-      } else if (!anArgLab.IsAttribute(ID_CONSTRUCTION)) {
-        Events_Error::send("Unknown type of result if found in the document");
+      } else if (!anArgLab.IsAttribute(ID_CONSTRUCTION) && anArgLab.FindChild(1).HasAttribute()) {
+        Events_Error::send("Unknown type of result is found in the document");
       }
       if (aNewBody) {
         theFeature->setResult(aNewBody, aResIndex);
@@ -873,6 +928,48 @@ void Model_Document::updateResults(FeaturePtr theFeature)
   }
 }
 
+void Model_Document::objectIsReferenced(const ObjectPtr& theObject)
+{
+  // only bodies are concealed now
+  ResultBodyPtr aResult = boost::dynamic_pointer_cast<ModelAPI_ResultBody>(theObject);
+  if (aResult) {
+    if (myConcealedResults.find(aResult) != myConcealedResults.end()) {
+      Events_Error::send(std::string("The object '") + aResult->data()->name() +
+        "' is already referenced");
+    } else {
+      myConcealedResults.insert(aResult);
+      boost::shared_ptr<ModelAPI_Document> aThis = 
+        Model_Application::getApplication()->getDocument(myID);
+      ModelAPI_EventCreator::get()->sendDeleted(aThis, ModelAPI_ResultBody::group());
+
+      static Events_Loop* aLoop = Events_Loop::loop();
+      static Events_ID EVENT_DISP = aLoop->eventByName(EVENT_OBJECT_TO_REDISPLAY);
+      static const ModelAPI_EventCreator* aECreator = ModelAPI_EventCreator::get();
+      aECreator->sendUpdated(aResult, EVENT_DISP);
+    }
+  }
+}
+
+void Model_Document::objectIsNotReferenced(const ObjectPtr& theObject)
+{
+  // only bodies are concealed now
+  ResultBodyPtr aResult = boost::dynamic_pointer_cast<ModelAPI_ResultBody>(theObject);
+  if (aResult) {
+    std::set<ResultPtr>::iterator aFind = myConcealedResults.find(aResult);
+    if (aFind != myConcealedResults.end()) {
+      ResultPtr aFeature = *aFind;
+      myConcealedResults.erase(aFind);
+      boost::shared_ptr<ModelAPI_Document> aThis = 
+        Model_Application::getApplication()->getDocument(myID);
+      static Events_ID anEvent = Events_Loop::eventByName(EVENT_OBJECT_CREATED);
+      ModelAPI_EventCreator::get()->sendUpdated(aFeature, anEvent, false);
+    } else {
+      Events_Error::send(std::string("The object '") + aResult->data()->name() +
+        "' was not referenced '");
+    }
+  }
+}
+
 Standard_Integer HashCode(const TDF_Label& theLab, const Standard_Integer theUpper)
 {
   return TDF_LabelMapHasher::HashCode(theLab, theUpper);