Salome HOME
Issue #2052: Modification of parameters don't work (sketch, extrusion)
[modules/shaper.git] / src / Model / Model_Session.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:        Model_Session.cxx
4 // Created:     20 Mar 2014
5 // Author:      Mikhail PONIKAROV
6
7 #include <Model_Session.h>
8 #include <ModelAPI_Feature.h>
9 #include <ModelAPI_Plugin.h>
10 #include <Model_Data.h>
11 #include <Model_Document.h>
12 #include <Model_Objects.h>
13 #include <Model_Application.h>
14 #include <Model_Events.h>
15 #include <Model_Validator.h>
16 #include <ModelAPI_Events.h>
17 #include <Events_Loop.h>
18 #include <Events_InfoMessage.h>
19 #include <Config_FeatureMessage.h>
20 #include <Config_AttributeMessage.h>
21 #include <Config_ValidatorMessage.h>
22 #include <Config_ModuleReader.h>
23 #include <Config_ValidatorReader.h>
24
25 #include <ModelAPI_CompositeFeature.h>
26 #include <ModelAPI_ResultPart.h>
27 #include <ModelAPI_Tools.h>
28
29 #include <TDF_ChildIDIterator.hxx>
30 #include <TDF_CopyTool.hxx>
31 #include <TDF_DataSet.hxx>
32 #include <TDF_RelocationTable.hxx>
33 #include <TDF_ClosureTool.hxx>
34
35 #include <TNaming_Builder.hxx>
36 #include <TNaming_Iterator.hxx>
37 #include <TNaming_NamedShape.hxx>
38
39 #include <TopoDS_Shape.hxx>
40
41 static Model_Session* myImpl = new Model_Session();
42
43 // t oredirect all calls to the root document
44 #define ROOT_DOC std::dynamic_pointer_cast<Model_Document>(moduleDocument())
45
46 bool Model_Session::load(const char* theFileName)
47 {
48   bool aRes = ROOT_DOC->load(theFileName, "root", ROOT_DOC);
49   return aRes;
50 }
51
52 bool Model_Session::save(const char* theFileName, std::list<std::string>& theResults)
53 {
54   return ROOT_DOC->save(theFileName, "root", theResults);
55 }
56
57 void Model_Session::closeAll()
58 {
59   Model_Application::getApplication()->deleteAllDocuments();
60 }
61
62 void Model_Session::startOperation(const std::string& theId, const bool theAttachedToNested)
63 {
64   myOperationAttachedToNext = theAttachedToNested;
65   ROOT_DOC->startOperation();
66   ROOT_DOC->operationId(theId);
67   static std::shared_ptr<Events_Message> aStartedMsg
68     (new Events_Message(Events_Loop::eventByName("StartOperation")));
69   Events_Loop::loop()->send(aStartedMsg);
70   // remove all useless documents that has been closed: on start of operation undo/redo is cleared
71   // MPV: this code is dangerous now because it may close the document that is activated right now
72   // but not in the list of the opened documents yet (create, delete, undo, activate Part)
73   // later this must be updated by correct usage of uniques IDs of documents, not names of results
74   //std::list<std::shared_ptr<ModelAPI_Document> > aUsedDocs = allOpenedDocuments();
75   //Model_Application::getApplication()->removeUselessDocuments(aUsedDocs);
76 }
77
78 void Model_Session::finishOperation()
79 {
80   setCheckTransactions(false);
81   ROOT_DOC->finishOperation();
82   if (myOperationAttachedToNext) { // twice, with nested
83     ROOT_DOC->finishOperation();
84     myOperationAttachedToNext = false;
85   }
86   setCheckTransactions(true);
87 }
88
89 void Model_Session::abortOperation()
90 {
91   setCheckTransactions(false);
92   ROOT_DOC->abortOperation();
93   if (myOperationAttachedToNext) { // twice, with nested
94     ROOT_DOC->abortOperation();
95     myOperationAttachedToNext = false;
96   }
97   setCheckTransactions(true);
98   // here the update mechanism may work after abort, so, supress the warnings about
99   // modifications outside of the transactions
100   bool aWasCheck = myCheckTransactions;
101   myCheckTransactions = false;
102   static std::shared_ptr<Events_Message> anAbortMsg
103     (new Events_Message(Events_Loop::eventByName("AbortOperation")));
104   Events_Loop::loop()->send(anAbortMsg);
105   myCheckTransactions = true;
106   myCheckTransactions = aWasCheck;
107 }
108
109 bool Model_Session::isOperation()
110 {
111   return ROOT_DOC->isOperation();
112 }
113
114 bool Model_Session::isModified()
115 {
116   return ROOT_DOC->isModified();
117 }
118
119 bool Model_Session::canUndo()
120 {
121   return ROOT_DOC->canUndo();
122 }
123
124 void Model_Session::undo()
125 {
126   setCheckTransactions(false);
127   ROOT_DOC->undo();
128   setCheckTransactions(true);
129 }
130
131 bool Model_Session::canRedo()
132 {
133   return ROOT_DOC->canRedo();
134 }
135
136 void Model_Session::redo()
137 {
138   setCheckTransactions(false);
139   ROOT_DOC->redo();
140   setCheckTransactions(true);
141 }
142
143 //! Returns stack of performed operations
144 std::list<std::string> Model_Session::undoList()
145 {
146   return ROOT_DOC->undoList();
147 }
148 //! Returns stack of rolled back operations
149 std::list<std::string> Model_Session::redoList()
150 {
151   return ROOT_DOC->redoList();
152 }
153
154 FeaturePtr Model_Session::createFeature(std::string theFeatureID, Model_Document* theDocOwner)
155 {
156   if (this != myImpl) {
157     return myImpl->createFeature(theFeatureID, theDocOwner);
158   }
159
160   // load all information about plugins, features and attributes
161   LoadPluginsInfo();
162
163   if (myPlugins.find(theFeatureID) != myPlugins.end()) {
164     std::pair<std::string, std::string>& aPlugin = myPlugins[theFeatureID]; // plugin and doc kind
165     if (!aPlugin.second.empty() && aPlugin.second != theDocOwner->kind()) {
166       Events_InfoMessage("Model_Session",
167           "Feature '%1' can be created only in document '%2' by the XML definition")
168           .arg(theFeatureID).arg(aPlugin.second).send();
169       return FeaturePtr();
170     }
171     myCurrentPluginName = aPlugin.first;
172     if (myPluginObjs.find(myCurrentPluginName) == myPluginObjs.end()) {
173       // load plugin library if not yet done
174       Config_ModuleReader::loadPlugin(myCurrentPluginName);
175     }
176     if (myPluginObjs.find(myCurrentPluginName) != myPluginObjs.end()) {
177       FeaturePtr aCreated = myPluginObjs[myCurrentPluginName]->createFeature(theFeatureID);
178       if (!aCreated) {
179         Events_InfoMessage("Model_Session",
180             "Can not initialize feature '%1' in plugin '%2'")
181             .arg(theFeatureID).arg(myCurrentPluginName).send();
182       }
183       return aCreated;
184     } else {
185       Events_InfoMessage("Model_Session",
186         "Can not load plugin '%1'").arg(myCurrentPluginName).send();
187     }
188   } else {
189     Events_InfoMessage("Model_Session",
190       "Feature '%1' not found in any plugin").arg(theFeatureID).send();
191   }
192
193   return FeaturePtr();  // return nothing
194 }
195
196 std::shared_ptr<ModelAPI_Document> Model_Session::moduleDocument()
197 {
198   Handle(Model_Application) anApp = Model_Application::getApplication();
199   bool aFirstCall = !anApp->hasRoot();
200   if (aFirstCall) {
201     // to be sure that plugins are loaded,
202     // even before the first "createFeature" call (in unit tests)
203
204     LoadPluginsInfo();
205     // creation of the root document is always outside of the transaction, so, avoid checking it
206     setCheckTransactions(false);
207     anApp->createDocument(0); // 0 is a root ID
208     // creation of the root document is always outside of the transaction, so, avoid checking it
209     setCheckTransactions(true);
210   }
211   return anApp->rootDocument();
212 }
213
214 std::shared_ptr<ModelAPI_Document> Model_Session::document(int theDocID)
215 {
216   return std::shared_ptr<ModelAPI_Document>(
217       Model_Application::getApplication()->document(theDocID));
218 }
219
220 bool Model_Session::hasModuleDocument()
221 {
222   return Model_Application::getApplication()->hasRoot();
223 }
224
225 std::shared_ptr<ModelAPI_Document> Model_Session::activeDocument()
226 {
227   if (!myCurrentDoc || !Model_Application::getApplication()->hasDocument(myCurrentDoc->id()))
228     myCurrentDoc = moduleDocument();
229   return myCurrentDoc;
230 }
231
232 /// makes the last feature in the document as the current
233 static void makeCurrentLast(std::shared_ptr<ModelAPI_Document> theDoc) {
234   if (theDoc.get()) {
235     FeaturePtr aLast = std::dynamic_pointer_cast<Model_Document>(theDoc)->lastFeature();
236     // if last is nested into something else, make this something else as last:
237     // otherwise it will look like edition of sub-element, so, the main will be disabled
238     if (aLast.get()) {
239       CompositeFeaturePtr aMain = ModelAPI_Tools::compositeOwner(aLast);
240       while(aMain.get()) {
241         aLast = aMain;
242         aMain = ModelAPI_Tools::compositeOwner(aLast);
243       }
244     }
245     theDoc->setCurrentFeature(aLast, false);
246   }
247 }
248
249 void Model_Session::setActiveDocument(
250   std::shared_ptr<ModelAPI_Document> theDoc, bool theSendSignal)
251 {
252   if (myCurrentDoc != theDoc) {
253     if (myCurrentDoc.get())
254       myCurrentDoc->setActive(false);
255     if (theDoc.get())
256       theDoc->setActive(true);
257
258     std::shared_ptr<ModelAPI_Document> aPrevious = myCurrentDoc;
259     myCurrentDoc = theDoc;
260     if (theDoc.get() && theSendSignal) {
261       // this must be before the synchronisation call because features in PartSet lower than this
262       // part feature must be disabled and don't recomputed anymore (issue 1156,
263       // translation feature is failed on activation of Part 2)
264       if (isOperation()) { // do it only in transaction, not on opening of document
265         DocumentPtr aRoot = moduleDocument();
266         if (myCurrentDoc != aRoot) {
267           FeaturePtr aPartFeat = ModelAPI_Tools::findPartFeature(aRoot, myCurrentDoc);
268           if (aPartFeat.get()) {
269             aRoot->setCurrentFeature(aPartFeat, false);
270           }
271         }
272       }
273       // syncronize the document: it may be just opened or opened but removed before
274       std::shared_ptr<Model_Document> aDoc = std::dynamic_pointer_cast<Model_Document>(theDoc);
275       if (aDoc.get()) {
276         bool aWasChecked = myCheckTransactions;
277         setCheckTransactions(false);
278         TDF_LabelList anEmptyUpdated;
279         aDoc->objects()->synchronizeFeatures(anEmptyUpdated, true, true, false, true);
280         if (aWasChecked)
281             setCheckTransactions(true);
282       }
283       static std::shared_ptr<Events_Message> aMsg(
284           new Events_Message(Events_Loop::eventByName(EVENT_DOCUMENT_CHANGED)));
285       Events_Loop::loop()->send(aMsg);
286     }
287     // make the current state correct and synchronised in the module and sub-documents
288     if (isOperation()) { // do it only in transaction, not on opening of document
289       if (myCurrentDoc == moduleDocument()) {
290         // make the current feature the latest in root, in previous root current become also last
291         makeCurrentLast(aPrevious);
292         makeCurrentLast(myCurrentDoc);
293       } else {
294         // make the current feature the latest in sub, root current feature becomes this sub
295         makeCurrentLast(myCurrentDoc);
296       }
297     }
298   }
299 }
300
301 std::list<std::shared_ptr<ModelAPI_Document> > Model_Session::allOpenedDocuments()
302 {
303   std::list<std::shared_ptr<ModelAPI_Document> > aResult;
304   aResult.push_back(moduleDocument());
305   // add subs recursively
306   std::list<std::shared_ptr<ModelAPI_Document> >::iterator aDoc = aResult.begin();
307   for(; aDoc != aResult.end(); aDoc++) {
308     DocumentPtr anAPIDoc = *aDoc;
309     std::shared_ptr<Model_Document> aDoc = std::dynamic_pointer_cast<Model_Document>(anAPIDoc);
310     if (aDoc) {
311       const std::set<int> aSubs = aDoc->subDocuments();
312       std::set<int>::const_iterator aSubIter = aSubs.cbegin();
313       for(; aSubIter != aSubs.cend(); aSubIter++) {
314         aResult.push_back(Model_Application::getApplication()->document(*aSubIter));
315       }
316     }
317   }
318   return aResult;
319 }
320
321 bool Model_Session::isLoadByDemand(const std::string theDocID)
322 {
323   return Model_Application::getApplication()->isLoadByDemand(theDocID);
324 }
325
326 std::shared_ptr<ModelAPI_Document> Model_Session::copy(
327     std::shared_ptr<ModelAPI_Document> theSource, const int theDestID)
328 {
329   std::shared_ptr<Model_Document> aNew = Model_Application::getApplication()->document(theDestID);
330   // make a copy of all labels
331   TDF_Label aSourceRoot = std::dynamic_pointer_cast<Model_Document>(theSource)->document()->Main()
332       .Father();
333   TDF_Label aTargetRoot = aNew->document()->Main().Father();
334   Handle(TDF_DataSet) aDS = new TDF_DataSet;
335   aDS->AddLabel(aSourceRoot);
336   TDF_ClosureTool::Closure(aDS);
337   Handle(TDF_RelocationTable) aRT = new TDF_RelocationTable(Standard_True);
338   aRT->SetRelocation(aSourceRoot, aTargetRoot);
339   TDF_CopyTool::Copy(aDS, aRT);
340
341   // TODO: remove after fix in OCCT.
342   // All named shapes are stored in reversed order, so to fix this we reverse them back.
343   for(TDF_ChildIDIterator aChildIter(aTargetRoot, TNaming_NamedShape::GetID(), true);
344       aChildIter.More();
345       aChildIter.Next()) {
346     Handle(TNaming_NamedShape) aNamedShape =
347       Handle(TNaming_NamedShape)::DownCast(aChildIter.Value());
348     if (aNamedShape.IsNull()) {
349       continue;
350     }
351
352     TopoDS_Shape aShape = aNamedShape->Get();
353     if(aShape.IsNull() || aShape.ShapeType() != TopAbs_COMPOUND) {
354       continue;
355     }
356
357     TNaming_Evolution anEvol = aNamedShape->Evolution();
358     std::list<std::pair<TopoDS_Shape, TopoDS_Shape> > aShapePairs; // to store old and new shapes
359     for(TNaming_Iterator anIter(aNamedShape); anIter.More(); anIter.Next()) {
360       aShapePairs.push_back(
361         std::pair<TopoDS_Shape, TopoDS_Shape>(anIter.OldShape(), anIter.NewShape()));
362     }
363
364     // Add in reverse order.
365     TDF_Label aLabel = aNamedShape->Label();
366     TNaming_Builder aBuilder(aLabel);
367     for(std::list<std::pair<TopoDS_Shape, TopoDS_Shape> >::iterator aPairsIter =
368           aShapePairs.begin();
369         aPairsIter != aShapePairs.end();
370         aPairsIter++) {
371       if (anEvol == TNaming_GENERATED) {
372         aBuilder.Generated(aPairsIter->first, aPairsIter->second);
373       } else if (anEvol == TNaming_MODIFY) {
374         aBuilder.Modify(aPairsIter->first, aPairsIter->second);
375       } else if (anEvol == TNaming_DELETE) {
376         aBuilder.Delete(aPairsIter->first);
377       } else if (anEvol == TNaming_PRIMITIVE) {
378         aBuilder.Generated(aPairsIter->second);
379       } else if (anEvol == TNaming_SELECTED) {
380         aBuilder.Select(aPairsIter->second, aPairsIter->first);
381       }
382     }
383   }
384
385   TDF_LabelList anEmptyUpdated;
386   aNew->objects()->synchronizeFeatures(anEmptyUpdated, true, true, true, true);
387   return aNew;
388 }
389
390 Model_Session::Model_Session()
391 {
392   myPluginsInfoLoaded = false;
393   myCheckTransactions = true;
394   myOperationAttachedToNext = false;
395   ModelAPI_Session::setSession(std::shared_ptr<ModelAPI_Session>(this));
396   // register the configuration reading listener
397   Events_Loop* aLoop = Events_Loop::loop();
398   static const Events_ID kFeatureEvent =
399     Events_Loop::eventByName(Config_FeatureMessage::MODEL_EVENT());
400   aLoop->registerListener(this, kFeatureEvent);
401   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED), 0, true);
402   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED), 0, true);
403   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED), 0, true);
404   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_VALIDATOR_LOADED));
405 }
406
407 void Model_Session::processEvent(const std::shared_ptr<Events_Message>& theMessage)
408 {
409   static const Events_ID kFeatureEvent =
410     Events_Loop::eventByName(Config_FeatureMessage::MODEL_EVENT());
411   static const Events_ID kValidatorEvent = Events_Loop::eventByName(EVENT_VALIDATOR_LOADED);
412   if (theMessage->eventID() == kFeatureEvent) {
413     const std::shared_ptr<Config_FeatureMessage> aMsg =
414       std::dynamic_pointer_cast<Config_FeatureMessage>(theMessage);
415     if (aMsg) {
416
417       // process the plugin info, load plugin
418       if (myPlugins.find(aMsg->id()) == myPlugins.end()) {
419         myPlugins[aMsg->id()] = std::pair<std::string, std::string>(
420           aMsg->pluginLibrary(), aMsg->documentKind());
421       }
422     } else {
423       const std::shared_ptr<Config_AttributeMessage> aMsgAttr =
424         std::dynamic_pointer_cast<Config_AttributeMessage>(theMessage);
425       if (aMsgAttr) {
426
427         if (!aMsgAttr->isObligatory()) {
428           validators()->registerNotObligatory(aMsgAttr->featureId(), aMsgAttr->attributeId());
429         }
430         if(aMsgAttr->isConcealment()) {
431           validators()->registerConcealment(aMsgAttr->featureId(), aMsgAttr->attributeId());
432         }
433         const std::list<std::pair<std::string, std::string> >& aCases = aMsgAttr->getCases();
434         if (!aCases.empty()) {
435           validators()->registerCase(aMsgAttr->featureId(), aMsgAttr->attributeId(), aCases);
436         }
437       }
438     }
439     // plugins information was started to load, so, it will be loaded
440     myPluginsInfoLoaded = true;
441   } else if (theMessage->eventID() == kValidatorEvent) {
442     std::shared_ptr<Config_ValidatorMessage> aMsg =
443       std::dynamic_pointer_cast<Config_ValidatorMessage>(theMessage);
444     if (aMsg) {
445       if (aMsg->attributeId().empty()) {  // feature validator
446         validators()->assignValidator(aMsg->validatorId(), aMsg->featureId(), aMsg->parameters());
447       } else {  // attribute validator
448         validators()->assignValidator(aMsg->validatorId(), aMsg->featureId(), aMsg->attributeId(),
449                                       aMsg->parameters());
450       }
451     }
452   } else {  // create/update/delete
453     if (myCheckTransactions && !isOperation())
454       Events_InfoMessage("Model_Session",
455         "Modification of data structure outside of the transaction").send();
456     // if part is deleted, make the root as the current document (on undo of Parts creations)
457     static const Events_ID kDeletedEvent = Events_Loop::eventByName(EVENT_OBJECT_DELETED);
458     if (theMessage->eventID() == kDeletedEvent) {
459       std::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleted =
460         std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
461       if (aDeleted &&
462           aDeleted->groups().find(ModelAPI_ResultPart::group()) != aDeleted->groups().end())
463       {
464          // check that the current feature of the session is still the active Part (even disabled)
465         bool aFound = false;
466         FeaturePtr aCurrentPart = moduleDocument()->currentFeature(true);
467         if (aCurrentPart.get()) {
468           const std::list<std::shared_ptr<ModelAPI_Result> >& aResList = aCurrentPart->results();
469           std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRes = aResList.begin();
470           for(; !aFound && aRes != aResList.end(); aRes++) {
471             ResultPartPtr aPRes = std::dynamic_pointer_cast<ModelAPI_ResultPart>(*aRes);
472             if (aPRes.get() && aPRes->isActivated() && aPRes->partDoc() == activeDocument()) {
473               aFound = true;
474
475             }
476           }
477         }
478         if (!aFound) { // if not, the part was removed, so activate the module document
479           if (myCurrentDoc.get())
480             setActiveDocument(moduleDocument());
481         }
482       }
483     }
484   }
485 }
486
487 void Model_Session::LoadPluginsInfo()
488 {
489   if (myPluginsInfoLoaded)  // nothing to do
490     return;
491   // Read plugins information from XML files
492   Config_ModuleReader aModuleReader(Config_FeatureMessage::MODEL_EVENT());
493   aModuleReader.readAll();
494   std::set<std::string> aFiles = aModuleReader.modulePluginFiles();
495   std::set<std::string>::iterator it = aFiles.begin();
496   for ( ; it != aFiles.end(); it++ ) {
497     Config_ValidatorReader aValidatorReader (*it);
498     aValidatorReader.readAll();
499   };
500
501 }
502
503 void Model_Session::registerPlugin(ModelAPI_Plugin* thePlugin)
504 {
505   myPluginObjs[myCurrentPluginName] = thePlugin;
506   static Events_ID EVENT_LOAD = Events_Loop::loop()->eventByName(EVENT_PLUGIN_LOADED);
507   ModelAPI_EventCreator::get()->sendUpdated(ObjectPtr(), EVENT_LOAD, false);
508   // If the plugin has an ability to process GUI events, register it
509   Events_Listener* aListener = dynamic_cast<Events_Listener*>(thePlugin);
510   if (aListener) {
511     Events_Loop* aLoop = Events_Loop::loop();
512     static Events_ID aStateRequestEventId =
513         Events_Loop::loop()->eventByName(EVENT_FEATURE_STATE_REQUEST);
514     aLoop->registerListener(aListener, aStateRequestEventId);
515   }
516 }
517
518 ModelAPI_ValidatorsFactory* Model_Session::validators()
519 {
520   static Model_ValidatorsFactory* aFactory = new Model_ValidatorsFactory;
521   return aFactory;
522 }
523
524 int Model_Session::transactionID()
525 {
526   return ROOT_DOC->transactionID();
527 }