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