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