Salome HOME
Ordering of model objects implementation.
[modules/hydro.git] / src / HYDROData / HYDROData_Document.cxx
1
2 #include <HYDROData_Document.h>
3 #include <HYDROData_Application.h>
4 #include <HYDROData_Iterator.h>
5 #include <HYDROData_Tool.h>
6
7 #include <TDataStd_Integer.hxx>
8
9 #include <TDF_Delta.hxx>
10
11 #include <QFile>
12 #include <QStringList>
13 #include <QTextStream>
14
15 IMPLEMENT_STANDARD_HANDLE(HYDROData_Document,MMgt_TShared)
16 IMPLEMENT_STANDARD_RTTIEXT(HYDROData_Document,MMgt_TShared)
17
18 #define PYTHON_DOC_NAME "hydro_doc"
19
20 static const int UNDO_LIMIT = 10; // number of possible undo operations in the module
21
22 static const int TAG_PROPS = 1; // general properties tag
23 static const int TAG_PROPS_NEW_ID = 1; // general properties: tag for storage of the new object ID
24 static const int TAG_OBJECTS = 2; // tag of the objects sub-tree
25 static const int TAG_HISTORY = 3; // tag of the history sub-tree (Root for History)
26
27 using namespace std;
28
29 typedef QMap<Standard_Integer,Handle_HYDROData_Entity> MapOfOrdered;
30 typedef QMap<QString,Handle_HYDROData_Entity> MapOfUnordered;
31
32 Handle(HYDROData_Document) HYDROData_Document::Document(const int theStudyID)
33 {
34   Handle(HYDROData_Document) aResult = 
35     HYDROData_Application::GetApplication()->GetDocument(theStudyID);
36   if (aResult.IsNull()) {
37     aResult = new HYDROData_Document();
38     HYDROData_Application::GetApplication()->AddDocument(theStudyID, aResult);
39   }
40   return aResult;
41 }
42
43 Handle(HYDROData_Document) HYDROData_Document::Document(
44   const TDF_Label& theObjectLabel )
45 {
46   Handle(HYDROData_Document) aResDoc;
47   if ( theObjectLabel.IsNull() )
48     return aResDoc;
49
50   Handle(TDocStd_Document) anObjDoc;
51   try
52   {
53     anObjDoc = TDocStd_Document::Get( theObjectLabel );
54   }
55   catch( ... )
56   {
57   }
58
59   if ( anObjDoc.IsNull() )
60     return aResDoc;
61
62   HYDROData_Application* anApp = HYDROData_Application::GetApplication();
63
64   DataMapOfStudyIDDocument::Iterator aMapIt( anApp->myDocuments );
65   for ( ; aMapIt.More(); aMapIt.Next() )
66   {
67     Handle(HYDROData_Document) anAppDoc = aMapIt.Value();
68     if ( anAppDoc.IsNull() || anAppDoc->myDoc != anObjDoc )
69       continue;
70
71     aResDoc = anAppDoc;
72     break;
73   }
74
75   return aResDoc;
76 }
77
78 bool HYDROData_Document::HasDocument(const int theStudyID)
79 {
80   Handle(HYDROData_Document) aResult = 
81     HYDROData_Application::GetApplication()->GetDocument(theStudyID);
82   return !aResult.IsNull();
83 }
84
85 bool HYDROData_Document::DocumentId(const Handle(HYDROData_Document)& theDocument,
86                                     int&                              theDocId )
87 {
88   return HYDROData_Application::GetApplication()->GetDocumentId(theDocument, theDocId);
89 }
90
91 Data_DocError HYDROData_Document::Load(const char* theFileName, const int theStudyID)
92 {
93   Handle(TDocStd_Document) aResult;
94   TCollection_ExtendedString aPath ((const Standard_CString)theFileName);
95   PCDM_ReaderStatus aStatus = (PCDM_ReaderStatus) -1;
96   try
97   {
98     aStatus = HYDROData_Application::GetApplication()->Open (aPath, aResult);
99   }
100   catch (Standard_Failure)
101   {}
102   if (!aResult.IsNull()) {
103     aResult->SetUndoLimit(UNDO_LIMIT);
104     HYDROData_Application::GetApplication()->AddDocument(theStudyID, new HYDROData_Document(aResult));
105   }
106   // recognize error
107   Data_DocError anError;
108   switch(aStatus) {
109   case PCDM_RS_OK:
110     anError = DocError_OK;
111     break;
112   case PCDM_RS_NoDriver:
113   case PCDM_RS_UnknownFileDriver:
114   case PCDM_RS_NoSchema:
115   case PCDM_RS_DriverFailure:
116   case PCDM_RS_WrongResource:
117     anError = DocError_ResourcesProblem;
118     break;
119   case PCDM_RS_OpenError:
120   case PCDM_RS_NoDocument:
121   case PCDM_RS_WrongStreamMode:
122   case PCDM_RS_PermissionDenied:
123     anError = DocError_CanNotOpen;
124     break;
125   case PCDM_RS_NoVersion:
126     anError = DocError_InvalidVersion;
127     break;
128   case PCDM_RS_ExtensionFailure:
129   case PCDM_RS_FormatFailure:
130   case PCDM_RS_TypeFailure:
131   case PCDM_RS_TypeNotFoundInSchema:
132   case PCDM_RS_UnrecognizedFileFormat:
133     anError = DocError_InvalidFormat;
134     break;
135   case PCDM_RS_MakeFailure:
136   default:
137     anError = DocError_UnknownProblem;
138     break;
139   }
140   return anError;
141 }
142
143 Data_DocError HYDROData_Document::Save(const char* theFileName)
144 {
145   TCollection_ExtendedString aPath ((const Standard_CString)theFileName);
146   PCDM_StoreStatus aStatus;
147   try {
148     aStatus = HYDROData_Application::GetApplication()->SaveAs (myDoc, aPath);
149   }
150   catch (Standard_Failure) {}
151   myTransactionsAfterSave = 0;
152   Standard::Purge(); // Release free memory
153
154   // recognize error
155   Data_DocError anError;
156   switch(aStatus) {
157   case PCDM_SS_OK:
158     anError = DocError_OK;
159     break;
160   case PCDM_SS_DriverFailure:
161     anError = DocError_ResourcesProblem;
162     break;
163   case PCDM_SS_WriteFailure:
164   //case PCDM_SS_DiskWritingFailure:
165   //case PCDM_SS_UserRightsFailure:
166     anError = DocError_CanNotOpen;
167     break;
168   default:
169     anError = DocError_UnknownProblem;
170     break;
171   }
172   return anError;
173 }
174
175 void HYDROData_Document::Close()
176 {
177   myDoc->Close();
178   HYDROData_Application::GetApplication()->RemoveDocument(this);
179 }
180
181 bool HYDROData_Document::DumpToPython( const QString& theFileName,
182                                        const bool     theIsMultiFile ) const
183 {
184   // Try to open the file
185   QFile aFile( theFileName );
186   if ( !aFile.open( QIODevice::WriteOnly ) )
187     return false;
188
189   MapOfTreatedObjects aTreatedObjects;
190
191   // Dump header for python script
192   QStringList aHeaderDump = DumpToPython( aTreatedObjects, theIsMultiFile );
193   if ( aHeaderDump.isEmpty() )
194     return false;
195
196   HYDROData_Tool::WriteStringsToFile( aFile, aHeaderDump );
197
198   bool aRes = true;
199
200   // Dump all model objects to Python script
201   aRes = aRes && dumpPartitionToPython( aFile, theIsMultiFile, aTreatedObjects, KIND_IMAGE );
202   aRes = aRes && dumpPartitionToPython( aFile, theIsMultiFile, aTreatedObjects, KIND_POLYLINEXY );
203   aRes = aRes && dumpPartitionToPython( aFile, theIsMultiFile, aTreatedObjects, KIND_BATHYMETRY );
204   aRes = aRes && dumpPartitionToPython( aFile, theIsMultiFile, aTreatedObjects, KIND_PROFILE );
205   aRes = aRes && dumpPartitionToPython( aFile, theIsMultiFile, aTreatedObjects, KIND_POLYLINE );
206   aRes = aRes && dumpPartitionToPython( aFile, theIsMultiFile, aTreatedObjects, KIND_IMMERSIBLE_ZONE );
207   aRes = aRes && dumpPartitionToPython( aFile, theIsMultiFile, aTreatedObjects, KIND_STREAM );
208   aRes = aRes && dumpPartitionToPython( aFile, theIsMultiFile, aTreatedObjects, KIND_CHANNEL );
209   aRes = aRes && dumpPartitionToPython( aFile, theIsMultiFile, aTreatedObjects, KIND_DIGUE );
210   aRes = aRes && dumpPartitionToPython( aFile, theIsMultiFile, aTreatedObjects, KIND_OBSTACLE );
211   aRes = aRes && dumpPartitionToPython( aFile, theIsMultiFile, aTreatedObjects, KIND_CALCULATION );
212
213   // Dump code to close python fuction
214   if ( aRes && theIsMultiFile )
215   {
216     QStringList aFooterScript;
217     aFooterScript << QString( "" );
218     aFooterScript << QString( "  pass" );
219     HYDROData_Tool::WriteStringsToFile( aFile, aFooterScript );
220   }
221
222   return aRes;
223 }
224
225 QString HYDROData_Document::GetDocPyName() const
226 {
227   QString aDocName = PYTHON_DOC_NAME;
228   
229   /*
230   int aDocId = 1;
231   if ( DocumentId( this, aDocId ) )
232     aDocName += "_" + QString::number( aDocId );
233   */
234   
235   return aDocName;
236 }
237
238 QStringList HYDROData_Document::DumpToPython( MapOfTreatedObjects& theTreatedObjects,
239                                               const bool           theIsMultiFile ) const
240 {
241   QString aDocName = GetDocPyName();
242
243   // Append document in to the map of treated objects to prevent names overlaping
244   theTreatedObjects.insert( aDocName, this );
245
246   int aDocId = 1;
247   if ( !DocumentId( this, aDocId ) )
248     aDocId = 1;
249
250   QStringList aResScript;
251
252   aResScript << QString( "from HYDROPy import *" );
253   aResScript << QString( "from PyQt4.QtCore import *" );
254   aResScript << QString( "from PyQt4.QtGui import *" );
255
256   if ( theIsMultiFile )
257   {
258     aResScript << QString( "" );
259     aResScript << QString( "def RebuildData( theStudy ):" );
260     aResScript << QString( "  %1 = HYDROData_Document.Document( theStudy._get_StudyId() );" ).arg( aDocName );
261   }
262   else
263   {
264     aResScript << QString( "" );
265     aResScript << QString( "%1 = HYDROData_Document.Document( theStudy._get_StudyId() );" ).arg( aDocName );
266   }
267
268   return aResScript;
269 }
270
271 bool HYDROData_Document::dumpPartitionToPython( QFile&               theFile,
272                                                 const bool           theIsMultiFile,
273                                                 MapOfTreatedObjects& theTreatedObjects,
274                                                 const ObjectKind&    theObjectKind ) const
275 {
276   if ( !theFile.isOpen() )
277     return false;
278
279   QTextStream anOutStream( &theFile );
280
281   bool aRes = true;
282
283   HYDROData_Iterator anIterator( this, theObjectKind );
284   for( ; anIterator.More(); anIterator.Next() )
285   {
286     Handle(HYDROData_Entity) anObject = anIterator.Current();
287     if ( anObject.IsNull() )
288       continue;
289
290     QString anObjName = anObject->GetName();
291     if ( theTreatedObjects.contains( anObjName ) )
292       continue;
293
294     theTreatedObjects.insert( anObjName, anObject );
295
296     QStringList anObjDump = anObject->DumpToPython( theTreatedObjects );
297
298     if ( theIsMultiFile )
299     {
300       // For multifile dump we use the function, see the document dump header
301       QStringList::iterator anIt = anObjDump.begin();
302       for ( ; anIt != anObjDump.end(); ++anIt )
303         anIt->prepend( "  " );
304     }
305     
306     HYDROData_Tool::WriteStringsToFile( theFile, anObjDump );
307   }
308   
309   return aRes;
310 }
311
312 bool takeLastDigits( QString& theStr, int& aRes )
313 {
314   aRes = -1;
315
316   QString anStrNum;
317   for ( int i = theStr.length() - 1; i >= 0; --i )
318   {
319     const QChar& aChar = theStr.at( i );
320     if ( !aChar.isDigit() )
321       break;
322
323     anStrNum.prepend( aChar );
324   }
325
326   if ( anStrNum.isEmpty() )
327     return false;
328
329   theStr.remove( theStr.length() - anStrNum.length(), anStrNum.length() );
330   aRes = anStrNum.toInt();
331
332   return true;
333 }
334
335 bool isObjectNameLessThan( const QString& theStr1, const QString& theStr2 )
336 {
337   QString aStr1 = theStr1, aStr2 = theStr2;
338
339   int aNum1 = -1, aNum2 = -1;
340   if ( takeLastDigits( aStr1, aNum1 ) && takeLastDigits( aStr2, aNum2 ) )
341   {
342     if ( aStr1 == aStr2 )
343       return aNum1 < aNum2;
344   }
345
346   return theStr1 < theStr2;
347 }
348
349 HYDROData_SequenceOfObjects HYDROData_Document::GetObjectsLayerOrder(
350   const Standard_Boolean theIsAll ) const
351 {
352   HYDROData_SequenceOfObjects anOrder;
353
354   MapOfOrdered   aMapOfOrdered;
355   MapOfUnordered aMapOfUnordered;
356
357   HYDROData_Iterator anIter( this );
358   for ( ; anIter.More(); anIter.Next() )
359   {
360     Handle(HYDROData_Entity) anObject = anIter.Current();
361     if ( anObject.IsNull() || !anObject->IsHas2dPrs() )
362       continue;
363
364     Standard_Integer anObjZLevel = -1;
365     if ( anObject->GetZLevel( anObjZLevel ) )
366     {
367       aMapOfOrdered.insert( anObjZLevel, anObject );
368     }
369     else
370     {
371       QString anObjName = anObject->GetName();
372       if ( anObjName.isEmpty() )
373         continue;
374
375       aMapOfUnordered.insert( anObjName, anObject );
376     }
377   }
378
379   MapOfOrdered::const_iterator anOrderedMapIt = aMapOfOrdered.constBegin();
380   for ( ; anOrderedMapIt != aMapOfOrdered.constEnd(); anOrderedMapIt++ )
381   {
382     const Handle(HYDROData_Entity)& anObject = anOrderedMapIt.value();
383     anOrder.Prepend( anObject );
384   }
385
386   if ( theIsAll )
387   {
388     QStringList aSortedNames = aMapOfUnordered.keys();
389     qSort( aSortedNames.begin(), aSortedNames.end(), isObjectNameLessThan );
390
391     for ( int i = 0; i < aSortedNames.length(); ++i )
392     {
393       QString anObjName = aSortedNames.value( i );
394
395       const Handle(HYDROData_Entity)& anObject = aMapOfUnordered.value( anObjName );
396       anOrder.Append( anObject );
397     }
398   }
399
400   return anOrder;
401 }
402
403 void HYDROData_Document::SetObjectsLayerOrder( const HYDROData_SequenceOfObjects& theOrder )
404 {
405   // At first we remove previous model order
406   RemoveObjectsLayerOrder();
407
408   // Make new objects order
409   Standard_Integer aLevel = 0;
410   for ( int i = theOrder.Length(), n = 1; i >= n; --i )
411   {
412     const Handle(HYDROData_Entity)& anObject = theOrder.Value( i );
413     if ( anObject.IsNull() || !anObject->IsHas2dPrs() )
414       continue;
415
416     anObject->SetZLevel( aLevel++ );
417   }
418 }
419
420 void HYDROData_Document::Show( const Handle_HYDROData_Entity& theObject )
421 {
422   HYDROData_SequenceOfObjects anOrder;
423   anOrder.Append( theObject );
424   Show( anOrder );
425 }
426
427 void HYDROData_Document::Show( const HYDROData_SequenceOfObjects& theObjects )
428 {
429   MapOfUnordered aMapOfUnordered;
430
431   for ( int i = 1, n = theObjects.Length(); i <= n; ++i )
432   {
433     const Handle(HYDROData_Entity)& anObject = theObjects.Value( i );
434     if ( anObject.IsNull() || !anObject->IsHas2dPrs() )
435       continue;
436
437     Standard_Integer anObjZLevel = -1;
438     if ( anObject->GetZLevel( anObjZLevel ) )
439     {
440       continue; // Skip objects that already have the z-level
441     }
442     else
443     {
444       QString anObjName = anObject->GetName();
445       if ( anObjName.isEmpty() )
446         continue;
447
448       aMapOfUnordered.insert( anObjName, anObject );
449     }
450   }
451
452   if ( aMapOfUnordered.isEmpty() )
453     return; // Nothing to show
454
455   Standard_Integer aTopId = 0;
456
457   HYDROData_SequenceOfObjects aModelOrder = GetObjectsLayerOrder( Standard_False );
458   if ( !aModelOrder.IsEmpty() )
459   {
460     const Handle(HYDROData_Entity)& anObject = aModelOrder.First();
461     anObject->GetZLevel( aTopId );
462     aTopId++;
463   }
464
465   aTopId += aMapOfUnordered.size() - 1;
466
467   QStringList aSortedNames = aMapOfUnordered.keys();
468   qSort( aSortedNames.begin(), aSortedNames.end(), isObjectNameLessThan );
469
470   for ( int i = 0; i < aSortedNames.length(); ++i )
471   {
472     QString anObjName = aSortedNames.value( i );
473
474     const Handle(HYDROData_Entity)& anObject = aMapOfUnordered.value( anObjName );
475     anObject->SetZLevel( aTopId-- );
476   }
477 }
478
479 void HYDROData_Document::RemoveObjectsLayerOrder()
480 {
481   HYDROData_Iterator anIter( this );
482   for ( ; anIter.More(); anIter.Next() )
483   {
484     Handle(HYDROData_Entity) anObject = anIter.Current();
485     if ( anObject.IsNull() || !anObject->IsHas2dPrs() )
486       continue;
487
488     anObject->RemoveZLevel();
489   }
490 }
491
492 void HYDROData_Document::StartOperation()
493 {
494   myDoc->NewCommand();
495 }
496
497 void HYDROData_Document::CommitOperation(const TCollection_ExtendedString& theName)
498 {
499   if( !myDoc->CommitCommand() ) // it means that there were no modifications done
500   {
501     myDoc->NewCommand();
502     NewID(); // workaround: do something just to modify the document
503     myDoc->CommitCommand();
504   }
505   myTransactionsAfterSave++;
506
507   if( theName.Length() != 0 )
508   {
509     const TDF_DeltaList& aList = GetUndos();
510     if( !aList.IsEmpty() )
511     {
512       Handle(TDF_Delta) aDelta = aList.Last();
513       if( !aDelta.IsNull() )
514         aDelta->SetName( theName );
515     }
516   }
517 }
518
519 void HYDROData_Document::AbortOperation()
520 {
521   myDoc->AbortCommand();
522 }
523
524 bool HYDROData_Document::IsOperation()
525 {
526   return myDoc->HasOpenCommand() != 0;
527 }
528
529 bool HYDROData_Document::IsModified()
530 {
531   return myTransactionsAfterSave != 0;
532 }
533
534 bool HYDROData_Document::CanUndo()
535 {
536   return myDoc->GetAvailableUndos() > 0;
537 }
538
539 const TDF_DeltaList& HYDROData_Document::GetUndos()
540 {
541   return myDoc->GetUndos();
542 }
543
544 void HYDROData_Document::ClearUndos()
545 {
546   return myDoc->ClearUndos();
547 }
548
549 void HYDROData_Document::Undo()
550 {
551   myDoc->Undo();
552   myTransactionsAfterSave--;
553 }
554
555 bool HYDROData_Document::CanRedo()
556 {
557   return myDoc->GetAvailableRedos() > 0;
558 }
559
560 const TDF_DeltaList& HYDROData_Document::GetRedos()
561 {
562   return myDoc->GetRedos();
563 }
564
565 void HYDROData_Document::ClearRedos()
566 {
567   return myDoc->ClearRedos();
568 }
569
570 void HYDROData_Document::Redo()
571 {
572   myDoc->Redo();
573   myTransactionsAfterSave++;
574 }
575
576 Handle(HYDROData_Entity) HYDROData_Document::CreateObject( const ObjectKind theKind )
577 {
578   return HYDROData_Iterator::CreateObject( this, theKind );
579 }
580
581 Handle(HYDROData_Entity) HYDROData_Document::FindObjectByName( 
582   const QString&   theName,
583   const ObjectKind theObjectKind ) const
584 {
585   Handle(HYDROData_Entity) anObject;
586   if ( theName.isEmpty() )
587     return anObject;
588
589   QStringList aNamesList;
590   aNamesList << theName;
591
592   HYDROData_SequenceOfObjects aSeqOfObjs = FindObjectsByNames( aNamesList, theObjectKind );
593   if( aSeqOfObjs.IsEmpty() )
594     return anObject;
595   
596   anObject = aSeqOfObjs.First();
597   return anObject;
598 }
599
600 HYDROData_SequenceOfObjects HYDROData_Document::FindObjectsByNames(
601   const QStringList& theNames, 
602   const ObjectKind   theObjectKind ) const
603 {
604   HYDROData_SequenceOfObjects aResSeq;
605
606   QStringList aNamesList = theNames;
607
608   HYDROData_Iterator anIter( this, theObjectKind );
609   for( ; anIter.More(); anIter.Next() )
610   {
611     Handle(HYDROData_Entity) anObject = anIter.Current();
612     if( anObject.IsNull() )
613       continue;
614
615     QString anObjName = anObject->GetName();
616     if ( anObjName.isEmpty() || !aNamesList.contains( anObjName ) )
617       continue;
618
619     aResSeq.Append( anObject );
620
621     aNamesList.removeAll( anObjName );
622     if ( aNamesList.isEmpty() )
623       break;
624   }
625
626   return aResSeq;
627 }
628
629 HYDROData_Document::HYDROData_Document()
630 {
631   HYDROData_Application::GetApplication()->NewDocument("BinOcaf", myDoc);
632   myDoc->SetUndoLimit(UNDO_LIMIT);
633   NewID(); // needed to have at least one attribute in initial document to avoid errors
634   myTransactionsAfterSave = 0;
635 }
636
637 HYDROData_Document::HYDROData_Document(const Handle(TDocStd_Document)& theDoc)
638 {
639   myDoc = theDoc;
640   myTransactionsAfterSave = 0;
641 }
642
643 HYDROData_Document::~HYDROData_Document()
644 {
645 }
646
647 int HYDROData_Document::NewID()
648 {
649   TDF_Label anIDLab = myDoc->Main().FindChild(TAG_PROPS).
650     FindChild(TAG_PROPS_NEW_ID);
651   Handle(TDataStd_Integer) anInt;
652   if (!anIDLab.FindAttribute(TDataStd_Integer::GetID(), anInt)) {
653     anInt = TDataStd_Integer::Set(anIDLab, 0);
654   }
655   // just increment value and return
656   anInt->Set(anInt->Get() + 1);
657   return anInt->Get();
658 }
659
660 TDF_Label HYDROData_Document::LabelOfObjects()
661 {
662   return myDoc->Main().FindChild(TAG_OBJECTS);
663 }