Salome HOME
Updated copyright comment
[samples/light.git] / src / LIGHTGUI / LIGHTGUI_DataModel.cxx
1 // Copyright (C) 2005-2024  OPEN CASCADE
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
18 //
19 // File   : LIGHTGUI_DataModel.cxx
20 // Author : Julia DOROVSKIKH
21
22 #include "LIGHTGUI_DataModel.h"
23 #include "LIGHTGUI_DataObject.h"
24
25 #include <CAM_Application.h>
26 #include <CAM_Module.h>
27 #include <LightApp_Study.h>
28 #include <SUIT_DataObjectIterator.h>
29 #include <SUIT_Tools.h>
30
31 #include <QFile>
32 #include <QTextStream>
33
34 /*!
35   \class LIGHTGUI_DataModel
36   \brief LIGHT module data model.
37
38   This class defines an internal organization of the module's data and the way
39   how these data are presented in the study (e.g. in the Object Browser).
40
41   In addition, this class includes methods which modify the data tree:
42   load/save text file, insert/remove text line, etc.
43 */
44
45 /*!
46   \brief Constructor
47   \param module parent module
48 */
49 LIGHTGUI_DataModel::LIGHTGUI_DataModel( CAM_Module* module )
50 : LightApp_DataModel( module ),
51   myFileName( "" ),
52   myStudyURL( "" ),
53   myMaxId( 0 )
54 {
55 }
56
57 /*!
58   \brief Destructor.
59 */
60 LIGHTGUI_DataModel::~LIGHTGUI_DataModel()
61 {
62 }
63
64 /*!
65   \brief Open data model (read data from the files).
66   \param url study file path
67   \param study study pointer
68   \param listOfFiles list of the (temporary) files with data
69   \return operation status (\c true on success and \c false on error)
70 */
71 bool LIGHTGUI_DataModel::open( const QString& url, CAM_Study* study, QStringList listOfFiles )
72 {
73   LightApp_Study* aDoc = dynamic_cast<LightApp_Study*>( study );
74   if ( !aDoc )
75     return false;
76
77   LightApp_DataModel::open( url, aDoc, listOfFiles );
78
79   // The first list item contains path to a temporary
80   // directory, where the persistent files was placed
81   if ( listOfFiles.count() > 0 ) {
82     QString aTmpDir ( listOfFiles[0] );
83
84     // This module operates with a single persistent file
85     if ( listOfFiles.size() == 2 ) {
86       myStudyURL = url;
87       QString aFullPath = SUIT_Tools::addSlash( aTmpDir ) + listOfFiles[1];
88       loadFile( aFullPath, aDoc );
89     }
90   }
91
92   return true;
93 }
94
95 /*!
96   \brief Save data model (write data to the files).
97   \param listOfFiles returning list of the (temporary) files with saved data
98   \return operation status (\c true on success and \c false on error)
99 */
100 bool LIGHTGUI_DataModel::save( QStringList& theListOfFiles )
101 {
102   bool isMultiFile = false; // TODO: decide, how to access this parameter
103
104   LightApp_DataModel::save( theListOfFiles );
105
106   LightApp_Study* study = dynamic_cast<LightApp_Study*>( module()->application()->activeStudy() );
107
108   QString aTmpDir = study->GetTmpDir( myStudyURL.toLatin1(), isMultiFile ).c_str();
109
110   QString aFileName = SUIT_Tools::file( myStudyURL, false ) + "_LIGHTGUI.txt";
111   QString aFullPath = aTmpDir + aFileName;
112   dumpFile( aFullPath );
113
114   theListOfFiles.append( aTmpDir );
115   theListOfFiles.append( aFileName );
116
117   return true;
118 }
119
120 /*!
121   \brief Save data model (write data to the files).
122   \param url study file path
123   \param study study pointer
124   \param listOfFiles returning list of the (temporary) files with saved data
125   \return operation status (\c true on success and \c false on error)
126 */
127 bool LIGHTGUI_DataModel::saveAs( const QString& url, CAM_Study* study, QStringList& theListOfFiles )
128 {
129   myStudyURL = url;
130   return save( theListOfFiles );
131 }
132
133 /*!
134   \brief Close data model (remove all relevant data).
135   \return operation status (\c true on success and \c false on error)
136 */
137 bool LIGHTGUI_DataModel::close()
138 {
139   return LightApp_DataModel::close();
140 }
141
142 /*!
143   \brief Create empty data model.
144   \param study study pointer
145   \return operation status (\c true on success and \c false on error)
146 */
147 bool LIGHTGUI_DataModel::create( CAM_Study* study )
148 {
149   return true;
150 }
151
152 /*!
153   \brief Get 'modified' flag.
154   \return \c true if data is changed from the last saving
155 */
156 bool LIGHTGUI_DataModel::isModified() const
157 {
158   // return false to avoid masking study's isModified()
159   return false;
160 }
161
162 /*!
163   \brief Get 'saved' flag.
164   \return \c true if data has been saved
165 */
166 bool LIGHTGUI_DataModel::isSaved() const
167 {
168   // return true to avoid masking study's isSaved()
169   return true;
170 }
171
172 /*!
173   \brief Update data model.
174   \param obj data object (starting for the update)
175   \param study study pointer
176 */
177 void LIGHTGUI_DataModel::update( LightApp_DataObject* /*obj*/, LightApp_Study* /*study*/ )
178 {
179   // Nothing to do here: we always keep the data tree in the up-to-date state
180   // The only goal of this method is to hide default behavior from LightApp_DataModel
181   return;
182 }
183
184 /*
185   \brief Load text data from the file.
186   \param filename file path
187   \param study study pointer
188   \return \c true if data is loaded successfully
189 */  
190 bool LIGHTGUI_DataModel::loadFile( const QString& filename, CAM_Study* study )
191 {
192   if ( filename.isEmpty() ) return false;
193
194   myFileName = filename;
195   
196   QStringList lines;
197   QFile file ( myFileName );
198   if ( file.open( QIODevice::ReadOnly ) ) {
199     QTextStream stream ( &file );
200     QString line;
201     while ( !stream.atEnd() ) {
202       line = stream.readLine(); // line of text excluding '\n'
203       lines += line;
204     }
205     file.close();
206
207     if  ( !study )
208       study = (CAM_Study*)module()->application()->activeStudy();
209     buildTree( study->root(), lines );
210
211     return true;
212   }
213
214   return false;
215 }
216
217 /*!
218   \brief Save text data to the file
219   \param filename file path
220   \return \c true if data is loaded successfully
221 */
222 bool LIGHTGUI_DataModel::dumpFile( const QString& filename )
223 {
224   QString aFileName = filename;
225   if ( aFileName.isEmpty() )
226     aFileName = myFileName;
227
228   if ( aFileName.isEmpty() ) return false;
229
230   QStringList lines;
231   for ( SUIT_DataObjectIterator it( root(), SUIT_DataObjectIterator::DepthLeft ); it.current(); ++it ) {
232     LIGHTGUI_DataObject* obj = dynamic_cast<LIGHTGUI_DataObject*>( it.current() );
233     if ( obj && obj->lineNb() > 0 )
234       lines.insert( obj->lineNb()-1, obj->lineText() );
235   }
236
237   QFile file ( aFileName );
238   if ( file.open( QIODevice::WriteOnly ) ) {
239     QTextStream stream ( &file );
240     QStringList::Iterator it = lines.begin();
241     for ( ; it != lines.end(); ++it ) {
242       stream << *it << "\n";
243     }
244     file.close();
245
246     myFileName = aFileName;
247     return true;
248   }
249
250   return false;
251 }
252
253 /*!
254   \brief Get latest loaded or saved text file's name.
255   \return file name (empty string if file has not been loaded or saved)
256 */
257 QString LIGHTGUI_DataModel::fileName() const
258 {
259   return myFileName;
260 }
261
262 /*!
263   \brief Get text for the specified line.
264   \param id line identifier
265   \return string text for the line with identifier \a id
266           (empty string is \a id is invalid)
267 */
268 QString LIGHTGUI_DataModel::getLineText( const int id ) const
269 {
270   QString aText;
271
272   LIGHTGUI_DataObject* obj = findObject( id );
273   if ( obj )
274     aText = obj->lineText();
275
276   return aText;
277 }
278
279 /*!
280   \brief Set text for the specified line.
281   \param id line identifier
282   \param txt new string text for the line
283   \return \c true on success or \c false if \a id is invalid
284 */
285 bool LIGHTGUI_DataModel::setLineText( const int id, const QString& txt )
286 {
287   LIGHTGUI_DataObject* obj = findObject( id );
288   if ( obj ) {
289     if ( (  txt.trimmed().isEmpty() && !obj->lineText().trimmed().isEmpty() ) ||
290          ( !txt.trimmed().isEmpty() &&  obj->lineText().trimmed().isEmpty() ) ) {
291       if ( obj->lineText().trimmed().isEmpty() ) {
292         // paragraph becomes a text line
293         SUIT_DataObject* newParent = obj->prevBrother();
294         if ( newParent ) {
295           obj->setParent( newParent );
296           while ( SUIT_DataObject* first = obj->firstChild() )
297             first->setParent( newParent );
298         }
299       }
300       else {
301         // text line becomes a paragraph
302         SUIT_DataObject* parent = obj->parent();
303         SUIT_DataObject* modelRoot = parent ? parent->parent() : 0;
304         if ( modelRoot && parent ) {
305           int pos = parent->childPos( obj );
306           modelRoot->insertChild( obj, modelRoot->childPos( parent ) + 1 );
307           while ( SUIT_DataObject* next = parent->childObject( pos ) )
308             obj->appendChild( next );
309         }
310       }
311     }
312     obj->setLineText( txt );
313     return true;
314   }
315   return false;
316 }
317
318 /*!
319   \brief Insert new text line.
320   \param id identifier of the line after which new one should be inserted
321          (if <=0, new line is added to the end)
322   \param txt string text
323   \return \c true on success or \c false if \a id is invalid
324 */
325 bool LIGHTGUI_DataModel::insertLineBefore( const int id, const QString& txt )
326 {
327   // Insert new line before the item at position in the list,
328   // or at the end() if position is out of range
329   LightApp_DataObject* modelRoot = dynamic_cast<LightApp_DataObject*>( root() );
330   LightApp_Study* study = dynamic_cast<LightApp_Study*>( module()->application()->activeStudy() );
331   if ( id > 0 ) {
332     if ( !modelRoot )
333       return false;
334     LIGHTGUI_DataObject* obj = findObject( id );
335     if ( !obj || !obj->parent() )
336       return false;
337     SUIT_DataObject* parent = obj->parent();
338     if ( txt.trimmed().isEmpty() ) {
339       // insert new paragraph
340       if ( obj->lineText().trimmed().isEmpty() ) {
341         int pos = modelRoot->childPos( obj );
342         modelRoot->insertChild( new LIGHTGUI_DataObject( generateId(), txt, 0 ), pos );
343       }
344       else {
345         int pos = parent->childPos( obj );
346         LIGHTGUI_DataObject* newObj = new LIGHTGUI_DataObject( generateId(), txt, 0 );
347         modelRoot->insertChild( newObj, modelRoot->childPos( parent ) + 1 );
348         while ( SUIT_DataObject* next = parent->childObject( pos ) )
349           newObj->appendChild( next );
350       }
351     }
352     else {
353       // insert new text line
354       if ( obj->lineText().trimmed().isEmpty() ) {
355         SUIT_DataObject* prevParent = obj->prevBrother();
356         if ( prevParent )
357           prevParent->appendChild( new LIGHTGUI_DataObject( generateId(), txt, prevParent ) );
358       }
359       else {
360         int pos = parent->childPos( obj );
361         parent->insertChild( new LIGHTGUI_DataObject( generateId(), txt, 0 ), pos );
362       }
363     }
364   }
365   else {
366     // append new paragraph/line
367     if ( modelRoot )
368       txt.trimmed().isEmpty() ? new LIGHTGUI_DataObject( generateId(), txt, modelRoot ) : 
369                                 new LIGHTGUI_DataObject( generateId(), txt, modelRoot->lastChild() );
370     else {
371       QStringList lines;
372       if ( !txt.trimmed().isEmpty() ) 
373         lines.append( txt );
374       buildTree( study->root(), lines );
375     }
376       
377   }
378   return true;
379 }
380
381 /*!
382   \brief Remove text line.
383   \param id line identifier
384   \return \c true on success or \c false if \a id is invalid
385 */
386 bool LIGHTGUI_DataModel::deleteTextLine( const int id )
387 {
388   LightApp_DataObject* modelRoot = dynamic_cast<LightApp_DataObject*>( root() );
389
390   if ( !modelRoot )
391     return false;
392
393   LIGHTGUI_DataObject* obj = findObject( id );
394   if ( !obj || !obj->parent() )
395     return false;
396   
397   if ( obj->lineText().trimmed().isEmpty() ) {
398     // if paragraph : put all child lines to the previous paragraph
399     SUIT_DataObject* newParent = obj->prevBrother();
400     if ( newParent ) {
401       while ( SUIT_DataObject* first = obj->firstChild() )
402         first->setParent( newParent );
403     }
404   }
405   obj->parent()->removeChild( obj );
406
407   return true;
408 }
409
410 /*!
411   \brief Remove all lines.
412 */
413 void LIGHTGUI_DataModel::clearAll()
414 {
415   CAM_Study* study = (CAM_Study*)module()->application()->activeStudy();
416   buildTree( study->root(), QStringList() );
417 }
418
419 /*!
420   \brief Getnerate unique line identifier.
421 */
422 int LIGHTGUI_DataModel::generateId()
423 {
424   return ++myMaxId;
425 }
426
427 /*!
428   \brief Get line identifier from the object entry.
429   \param entry object entry
430   \return object ID or -1 if \a entry is invalid
431 */
432 int LIGHTGUI_DataModel::id( const QString& entry )
433 {
434   int id = -1;
435   QStringList ids = entry.split( ":", QString::SkipEmptyParts );
436   if ( ids.count() == 2 && ids[0] == "LIGHTGUI" ) {
437     bool bOk;
438     int p = ids[1].toInt( &bOk );
439     if ( bOk ) 
440       id = p;
441   }
442   return id;
443 }
444
445 /*!
446   \brief Get object entry for the line with sepcified identifier.
447   \param id object ID
448   \return object entry corresponding to the given \a id
449 */
450 QString LIGHTGUI_DataModel::entry( const int id )
451 {
452   QString entry;
453   if ( id > 0 )
454     entry = QString( "LIGHTGUI:%1" ).arg( id );
455   return entry;
456 }
457
458 /*!
459   \brief Get line number for the object with the specified entry.
460   \param entry object entry
461   \return line number or 0 if \a entry is invalid
462 */
463 int LIGHTGUI_DataModel::lineNb( const QString& entry )
464 {
465   return lineNb( LIGHTGUI_DataModel::id( entry ) );
466 }
467
468 /*!
469   \brief Get line number for the specified identifier.
470   \param id line identifier
471   \return line number or 0 if \a id is invalid
472 */
473 int LIGHTGUI_DataModel::lineNb( const int id )
474 {
475   LIGHTGUI_DataObject* obj = findObject( id );
476   return obj ? obj->lineNb() : 0;
477 }
478
479 /*!
480   \brief Get all existing lines identifiers.
481   \return list of lines identifiers
482 */
483 QList<int> LIGHTGUI_DataModel::getIds() const
484 {
485   QList<int> l;
486   for ( SUIT_DataObjectIterator it( root(), SUIT_DataObjectIterator::DepthLeft ); it.current(); ++it ) {
487     LIGHTGUI_DataObject* obj = dynamic_cast<LIGHTGUI_DataObject*>( it.current() );
488     if ( obj )
489       l.append( obj->id() );
490   }
491   return l;
492 }
493
494 /*!
495   \brief Rebuild data tree.
496   \param studyRoot study root data object
497   \param lines string data
498 */
499 void LIGHTGUI_DataModel::buildTree( SUIT_DataObject* studyRoot, const QStringList& lines )
500 {
501   if ( !studyRoot )
502     return;
503
504   CAM_ModuleObject* modelRoot = createModuleObject( studyRoot );
505   LIGHTGUI_DataObject* aParaObject;
506   LIGHTGUI_DataObject* aLineObject;
507
508   aParaObject = new LIGHTGUI_DataObject ( generateId(), "", modelRoot );
509
510   QStringList::ConstIterator it1 = lines.begin(),
511                              it2 = lines.end();
512   for ( ; it1 != it2; ++it1 ) {
513     if ( (*it1).trimmed().isEmpty() ) {
514       aParaObject = new LIGHTGUI_DataObject ( generateId(), *it1, modelRoot );
515     }
516     else {
517       aLineObject = new LIGHTGUI_DataObject ( generateId(), *it1, aParaObject );
518     }
519   }
520
521   modelRoot->setDataModel( this );
522   setRoot( modelRoot );
523 }
524
525 /*!
526   \brief Search data object corresponding to the specified line number.
527   \param id line identifier
528   \return data object or 0 if \a id is invalid
529 */
530 LIGHTGUI_DataObject* LIGHTGUI_DataModel::findObject( const int id ) const
531 {
532   for ( SUIT_DataObjectIterator it( root(), SUIT_DataObjectIterator::DepthLeft ); it.current(); ++it ) {
533     LIGHTGUI_DataObject* obj = dynamic_cast<LIGHTGUI_DataObject*>( it.current() );
534     if ( obj && obj->id() == id )
535       return obj;
536   }
537   return 0;
538 }