Salome HOME
Merge from V5_1_main 14/05/2010
[modules/visu.git] / src / VISU_I / VISU_Table_i.cc
1 //  Copyright (C) 2007-2010  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 //  Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 //  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 //  This library is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU Lesser General Public
8 //  License as published by the Free Software Foundation; either
9 //  version 2.1 of the License.
10 //
11 //  This library is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 //  Lesser General Public License for more details.
15 //
16 //  You should have received a copy of the GNU Lesser General Public
17 //  License along with this library; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 //  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 //  VISU OBJECT : interactive object for VISU entities implementation
24 //  File   : VISU_Table_i.cc
25 //  Author : Vadim SANDLER
26 //  Module : VISU
27 //
28 #include "VISU_Table_i.hh"
29
30 #include "VISU_CutLinesBase_i.hh"
31 #include "VISU_CutSegment_i.hh"
32 #include "VISU_Result_i.hh"
33 #include "VISU_ViewManager_i.hh"
34
35 #include "SALOME_Event.h"
36 #include "SPlot2d_Curve.h"
37
38 #include "VISU_TableReader.hxx"
39 #include "VISU_ConvertorUtils.hxx"
40
41 #include "utilities.h"
42
43 #include <Basics_Utils.hxx>
44
45 using namespace std;
46
47 #ifdef _DEBUG_
48 static int MYDEBUG = 0;
49 #else
50 static int MYDEBUG = 0;
51 #endif
52 //----------------------------------------------------------------
53 //                      Table Object
54 //----------------------------------------------------------------
55 int VISU::Table_i::myNbPresent = 0;
56 const string VISU::Table_i::myComment  = "TABLE";
57 /*!
58   Generate unique name
59 */
60 QString VISU::Table_i::GenerateName()
61 {
62   return VISU::GenerateName( "Table", ++myNbPresent );
63 }
64 /*!
65   Gets comment string
66 */
67 const char* VISU::Table_i::GetComment() const
68 {
69   return myComment.c_str();
70 }
71 /*!
72   Constructor
73 */
74 VISU::Table_i::Table_i( SALOMEDS::Study_ptr theStudy, const char* theObjectEntry )
75      : PrsObject_i(theStudy)
76 {
77   MESSAGE("Table_i::Table_i - "<<this);
78   mySObj = theStudy->FindObjectID(theObjectEntry);
79   myOrientation = VISU::Table::HORIZONTAL;
80 }
81 /*!
82   Destructor
83 */
84 VISU::Table_i::~Table_i()
85 {
86   MESSAGE("Table_i::~Table_i - "<<this);
87 }
88
89 //----------------------------------------------------------------------------
90 void
91 VISU::Table_i
92 ::SetTitle( const char* theTitle )
93 {
94   SetName( theTitle, true );
95 }
96
97 //----------------------------------------------------------------------------
98 char*
99 VISU::Table_i
100 ::GetTitle()
101 {
102   return CORBA::string_dup( GetName().c_str() );
103 }
104
105 //----------------------------------------------------------------------------
106 void
107 VISU::Table_i
108 ::SetOrientation( VISU::Table::Orientation theOrientation )
109 {
110   myOrientation = theOrientation;
111 }
112
113 //----------------------------------------------------------------------------
114 VISU::Table::Orientation
115 VISU::Table_i
116 ::GetOrientation()
117 {
118   return myOrientation;
119 }
120
121
122 //----------------------------------------------------------------------------
123 void
124 VISU::Table_i
125 ::SortRow(CORBA::Long theRow, VISU::SortOrder theSortOrder, VISU::SortPolicy theSortPolicy)
126 {
127   SALOMEDS::SObject_var SO = mySObj;
128   SALOMEDS::StudyBuilder_var Builder = GetStudyDocument()->NewBuilder();
129   if ( !SO->_is_nil() ) {
130     SALOMEDS::GenericAttribute_var anAttr;
131     if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfInteger" ) ) {
132       SALOMEDS::AttributeTableOfInteger_var anInt = SALOMEDS::AttributeTableOfInteger::_narrow( anAttr );
133       anInt->SortRow( theRow, (SALOMEDS::AttributeTable::SortOrder)theSortOrder,
134                       (SALOMEDS::AttributeTable::SortPolicy)theSortPolicy );
135     }
136     else if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfReal" ) ) {
137       SALOMEDS::AttributeTableOfReal_var aReal = SALOMEDS::AttributeTableOfReal::_narrow( anAttr );
138       aReal->SortRow( theRow, (SALOMEDS::AttributeTable::SortOrder)theSortOrder,
139                       (SALOMEDS::AttributeTable::SortPolicy)theSortPolicy );
140     }
141     UpdateCurves( std::map<int, int>() );
142   }
143 }
144
145 //----------------------------------------------------------------------------
146 void
147 VISU::Table_i
148 ::SortColumn(CORBA::Long theColumn, VISU::SortOrder theSortOrder, VISU::SortPolicy theSortPolicy)
149 {
150   SALOMEDS::SObject_var SO = mySObj;
151   SALOMEDS::StudyBuilder_var Builder = GetStudyDocument()->NewBuilder();
152   if ( !SO->_is_nil() ) {
153     SALOMEDS::GenericAttribute_var anAttr;
154     if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfInteger" ) ) {
155       SALOMEDS::AttributeTableOfInteger_var anInt = SALOMEDS::AttributeTableOfInteger::_narrow( anAttr );
156       anInt->SortColumn( theColumn, (SALOMEDS::AttributeTable::SortOrder)theSortOrder,
157                          (SALOMEDS::AttributeTable::SortPolicy)theSortPolicy );
158     }
159     else if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfReal" ) ) {
160       SALOMEDS::AttributeTableOfReal_var aReal = SALOMEDS::AttributeTableOfReal::_narrow( anAttr );
161       aReal->SortColumn( theColumn, (SALOMEDS::AttributeTable::SortOrder)theSortOrder,
162                          (SALOMEDS::AttributeTable::SortPolicy)theSortPolicy );
163     }
164     UpdateCurves( std::map<int, int>() );
165   }
166 }
167
168 //----------------------------------------------------------------------------
169 void
170 VISU::Table_i
171 ::SortByRow(CORBA::Long theRow, VISU::SortOrder theSortOrder, VISU::SortPolicy theSortPolicy)
172 {
173   SALOMEDS::SObject_var SO = mySObj;
174   SALOMEDS::StudyBuilder_var Builder = GetStudyDocument()->NewBuilder();
175   if ( !SO->_is_nil() ) {
176     SALOMEDS::GenericAttribute_var anAttr;
177     if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfInteger" ) ) {
178       SALOMEDS::AttributeTableOfInteger_var anInt = SALOMEDS::AttributeTableOfInteger::_narrow( anAttr );
179       anInt->SortByRow( theRow, (SALOMEDS::AttributeTable::SortOrder)theSortOrder,
180                         (SALOMEDS::AttributeTable::SortPolicy)theSortPolicy );
181     }
182     else if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfReal" ) ) {
183       SALOMEDS::AttributeTableOfReal_var aReal = SALOMEDS::AttributeTableOfReal::_narrow( anAttr );
184       aReal->SortByRow( theRow, (SALOMEDS::AttributeTable::SortOrder)theSortOrder,
185                         (SALOMEDS::AttributeTable::SortPolicy)theSortPolicy );
186     }
187     UpdateCurves( std::map<int, int>() );
188   }
189 }
190
191 //----------------------------------------------------------------------------
192 void
193 VISU::Table_i
194 ::SortByColumn(CORBA::Long theColumn, VISU::SortOrder theSortOrder, VISU::SortPolicy theSortPolicy)
195 {
196   SALOMEDS::SObject_var SO = mySObj;
197   SALOMEDS::StudyBuilder_var Builder = GetStudyDocument()->NewBuilder();
198   if ( !SO->_is_nil() ) {
199     SALOMEDS::LongSeq_var aRowIndices;
200     SALOMEDS::GenericAttribute_var anAttr;
201     if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfInteger" ) ) {
202       SALOMEDS::AttributeTableOfInteger_var anInt = SALOMEDS::AttributeTableOfInteger::_narrow( anAttr );
203       aRowIndices = anInt->SortByColumn( theColumn, (SALOMEDS::AttributeTable::SortOrder)theSortOrder,
204                                          (SALOMEDS::AttributeTable::SortPolicy)theSortPolicy );
205     }
206     else if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfReal" ) ) {
207       SALOMEDS::AttributeTableOfReal_var aReal = SALOMEDS::AttributeTableOfReal::_narrow( anAttr );
208       aRowIndices = aReal->SortByColumn( theColumn, (SALOMEDS::AttributeTable::SortOrder)theSortOrder,
209                                          (SALOMEDS::AttributeTable::SortPolicy)theSortPolicy );
210     }
211     std::map<int, int> aMixData;
212     for ( int i = 0, n = aRowIndices->length(); i < n; i++ )
213       aMixData[ aRowIndices[i] ] = i+1;
214     UpdateCurves( aMixData );
215   }
216 }
217
218 //----------------------------------------------------------------------------
219 void
220 VISU::Table_i
221 ::UpdateCurves(std::map<int,int> theMixData)
222 {
223   SALOMEDS::SObject_var SO = mySObj;
224   SALOMEDS::StudyBuilder_var Builder = GetStudyDocument()->NewBuilder();
225   SALOMEDS::ChildIterator_var CI = GetStudyDocument()->NewChildIterator( SO );
226   for ( CI->InitEx( true ); CI->More(); CI->Next() ) {
227     CORBA::Object_var anObj = SObjectToObject( CI->Value() );
228     VISU::Curve_var aCurve = VISU::Curve::_narrow( anObj );
229     if ( !aCurve->_is_nil() ) {
230       if ( VISU::Curve_i* pCurve = dynamic_cast<VISU::Curve_i*>( GetServant( aCurve ).in() ) ) {
231         int aHRow = pCurve->GetHRow(), aVRow = pCurve->GetVRow();
232         if ( theMixData.find( aHRow ) != theMixData.end() )
233           pCurve->SetHRow( theMixData[ aHRow ] );
234         if ( theMixData.find( aVRow ) != theMixData.end() )
235           pCurve->SetVRow( theMixData[ aVRow ] );
236         UpdatePlot2d( pCurve, eUpdateData );
237       }
238     }
239   }
240 }
241
242 //----------------------------------------------------------------------------
243 SALOMEDS::SObject_var
244 VISU::Table_i
245 ::GetSObject() const
246 {
247   return mySObj;
248 }
249
250 //----------------------------------------------------------------------------
251 std::string
252 VISU::Table_i
253 ::GetObjectEntry() 
254 {
255   CORBA::String_var anEntry = mySObj->GetID();
256   return anEntry.in(); 
257 }
258
259 //----------------------------------------------------------------------------
260 /*!
261   Gets number of rows in table
262 */
263 CORBA::Long VISU::Table_i::GetNbRows()
264 {
265   SALOMEDS::SObject_var SO = mySObj;
266   SALOMEDS::StudyBuilder_var Builder = GetStudyDocument()->NewBuilder();
267   if ( !SO->_is_nil() ) {
268     SALOMEDS::GenericAttribute_var        anAttr;
269     if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfInteger" ) ) {
270       SALOMEDS::AttributeTableOfInteger_var anInt =  SALOMEDS::AttributeTableOfInteger::_narrow( anAttr );
271         return anInt->GetNbRows();
272     }
273     else if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfReal" ) ) {
274       SALOMEDS::AttributeTableOfReal_var aReal =  SALOMEDS::AttributeTableOfReal::_narrow( anAttr );
275       return aReal->GetNbRows();
276     }
277   }
278   return 0;
279 }
280 /*!
281   Gets number of columns in table
282 */
283 CORBA::Long VISU::Table_i::GetNbColumns()
284 {
285   SALOMEDS::SObject_var SO = mySObj;
286   SALOMEDS::StudyBuilder_var Builder = GetStudyDocument()->NewBuilder();
287   if ( !SO->_is_nil() ) {
288     SALOMEDS::GenericAttribute_var        anAttr;
289     if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfInteger" ) ) {
290       SALOMEDS::AttributeTableOfInteger_var anInt =  SALOMEDS::AttributeTableOfInteger::_narrow( anAttr );
291       return anInt->GetNbColumns();
292     }
293     else if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfReal" ) ) {
294       SALOMEDS::AttributeTableOfReal_var aReal =  SALOMEDS::AttributeTableOfReal::_narrow( anAttr );
295       return aReal->GetNbColumns();
296     }
297   }
298   return 0;
299 }
300 /*!
301   Creates table object
302 */
303 VISU::Storable* VISU::Table_i::Create()
304 {
305   // generate name ...
306   SetName(GetTableTitle().toLatin1().data(), false);
307
308   // mpv (PAL 5357): if name attribute already exist at this label, use it as name of table
309   if ( GetName() == "" )
310     if ( !mySObj->_is_nil() ) {
311       CutLinesBase_i* pCutLines = NULL;
312       CORBA::Object_var anObj = SObjectToObject(mySObj);
313       if(!CORBA::is_nil(anObj)){
314         VISU::CutLinesBase_var aCutLines = VISU::CutLinesBase::_narrow(anObj);
315           if(!aCutLines->_is_nil())
316             pCutLines = dynamic_cast<CutLinesBase_i*>(GetServant(aCutLines).in());
317         }
318       if (!pCutLines)
319         if (mySObj->GetName()) SetName(mySObj->GetName(), false);
320     }
321
322   if ( GetName() == "" )
323     SetName(GenerateName().toLatin1().data(), false);
324   // ... and build the object
325   return Build( false );
326 }
327 /*!
328   Builds presentation of table
329 */
330 VISU::Storable* VISU::Table_i::Build( int theRestoring )
331 {
332
333   // look for reference SObject with table attribute
334   SALOMEDS::SObject_var SO = mySObj;
335
336   if ( !SO->_is_nil() ) {
337     CutLinesBase_i* pCutLines = NULL;
338     CORBA::Object_var anObj = SObjectToObject(SO);
339     if(!CORBA::is_nil(anObj)){
340       VISU::CutLinesBase_var aCutLines = VISU::CutLinesBase::_narrow(anObj);
341       if(!aCutLines->_is_nil())
342         pCutLines = dynamic_cast<CutLinesBase_i*>(GetServant(aCutLines).in());
343     }
344     SALOMEDS::StudyBuilder_var Builder = GetStudyDocument()->NewBuilder();
345     SALOMEDS::GenericAttribute_var anAttr;
346     // look for component
347     if ( !theRestoring ) {
348       SALOMEDS::SComponent_var SComponent = VISU::FindOrCreateVisuComponent( GetStudyDocument() );
349       // create SObject and set attributes
350       QString aComment;
351       if(pCutLines)
352         aComment.sprintf("myComment=%s;mySourceId=CutLines",GetComment());
353       else{
354         aComment.sprintf("myComment=%s;mySourceId=TableAttr",GetComment());
355         SALOMEDS::SObject_var aFatherSObject = SO->GetFather();
356         if(aFatherSObject->FindAttribute(anAttr,"AttributeString")){
357           SALOMEDS::AttributeString_var aCommentAttr =
358             SALOMEDS::AttributeString::_narrow(anAttr);
359           CORBA::String_var aValue = aCommentAttr->Value();
360           Storable::TRestoringMap aMap;
361           Storable::StringToMap(aValue.in(),aMap);
362           bool anIsExist;
363           QString aMethodName = VISU::Storable::FindValue(aMap,"myComment",&anIsExist);
364           if(anIsExist){
365             if( aMethodName == "ImportTables" ){
366               aComment.sprintf("myComment=%s;mySourceId=TableFile",GetComment());
367             }
368           }
369         }
370       }
371
372       string anEntry = CreateAttributes( GetStudyDocument(),
373                                          SO->GetID(),//SComponent->GetID(),
374                                          "ICON_TREE_TABLE",
375                                          GetID(),
376                                          GetName(),
377                                          "",
378                                          aComment.toLatin1().data(),
379                                          pCutLines );
380       // create SObject referenced to real table object
381       mySObj = SALOMEDS::SObject::_duplicate(GetStudyDocument()->FindObjectID( anEntry.c_str() ));
382       if(pCutLines) {
383         bool isCutSegment = dynamic_cast<CutSegment_i*>(pCutLines);
384         pCutLines->BuildTableOfReal(mySObj, isCutSegment);
385       }
386       // mpv (PAL5357): reference attributes are unnecessary now
387       //SALOMEDS::SObject_var refSO = Builder->NewObject( mySObj );
388       //Builder->Addreference( refSO, SO );
389     }
390
391     return this;
392   }
393   return NULL;
394 }
395 /*!
396   Restores table object from stream
397 */
398 VISU::Storable* VISU::Table_i::Restore( const Storable::TRestoringMap& theMap, SALOMEDS::SObject_ptr SO)
399 {
400   if(MYDEBUG) MESSAGE(GetComment());
401   SetName(VISU::Storable::FindValue(theMap,"myName").toLatin1().data(), false);
402   myTitle = VISU::Storable::FindValue(theMap,"myTitle").toLatin1().data();
403   myOrientation = ( VISU::Table::Orientation )( VISU::Storable::FindValue(theMap,"myOrientation").toInt() );
404   mySObj = SALOMEDS::SObject::_duplicate(SO);
405   return Build( true );
406 }
407 /*!
408   Flushes table data into stream
409 */
410 void VISU::Table_i::ToStream( std::ostringstream& theStr )
411 {
412   Storable::DataToStream( theStr, "myName",        GetName().c_str() );
413   Storable::DataToStream( theStr, "myTitle",       myTitle.c_str() );
414   Storable::DataToStream( theStr, "myOrientation", myOrientation );
415 }
416 /*!
417   Called from engine to restore table from the file
418 */
419 VISU::Storable* VISU::Table_i::StorableEngine(SALOMEDS::SObject_ptr theSObject,
420                                               const Storable::TRestoringMap& theMap,
421                                               const std::string& thePrefix,
422                                               CORBA::Boolean theIsMultiFile)
423 {
424   SALOMEDS::Study_var aStudy = theSObject->GetStudy();
425   VISU::Table_i* pResent = new VISU::Table_i( aStudy, "" );
426   return pResent->Restore( theMap, theSObject);
427 }
428 /*!
429   Gets title for the original table object
430 */
431 QString VISU::Table_i::GetTableTitle()
432 {
433   SALOMEDS::SObject_var SO = mySObj;
434   SALOMEDS::StudyBuilder_var Builder = GetStudyDocument()->NewBuilder();
435   SALOMEDS::GenericAttribute_var        anAttr;
436   SALOMEDS::AttributeTableOfInteger_var anInt;
437   SALOMEDS::AttributeTableOfReal_var    aReal;
438   if ( !SO->_is_nil() ) {
439     if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfInteger" ) ) {
440       anInt = SALOMEDS::AttributeTableOfInteger::_narrow( anAttr );
441       CORBA::String_var aString = anInt->GetTitle();
442       return aString.in();
443     }
444     else if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfReal" ) ) {
445       aReal = SALOMEDS::AttributeTableOfReal::_narrow( anAttr );
446       CORBA::String_var aString = aReal->GetTitle();
447       return aString.in();
448     }
449   }
450   return "";
451 }
452
453 //---------------------------------------------------------------
454 void VISU::Table_i::RemoveFromStudy()
455 {
456   struct TRemoveFromStudy: public SALOME_Event
457   {
458     VISU::Table_i* myRemovable;
459     TRemoveFromStudy(VISU::Table_i* theRemovable):
460       myRemovable(theRemovable)
461     {}
462     
463     virtual
464     void
465     Execute()
466     {
467       VISU::RemoveFromStudy(myRemovable->GetSObject(),false);
468     }
469   };
470
471   // Remove the table with all curves
472   ProcessVoidEvent(new TRemoveFromStudy(this));
473 }
474
475 //----------------------------------------------------------------
476 //                      Curve Object
477 //----------------------------------------------------------------
478 /*!
479   Restores table object from the stream [ static ]
480 */
481 static VISU::Table_i* GetTable( SALOMEDS::Study_ptr theStudy, SALOMEDS::SObject_ptr theSO ) {
482   CORBA::Object_var anObject = VISU::SObjectToObject( theSO );
483   if( !CORBA::is_nil( anObject ) ) {
484     CORBA::Object_ptr aTable = VISU::Table::_narrow( anObject );
485     if( !CORBA::is_nil( aTable ) )
486       return dynamic_cast<VISU::Table_i*>(VISU::GetServant(aTable).in());
487   }
488   return NULL;
489 }
490
491 int VISU::Curve_i::myNbPresent = 0;
492 const string VISU::Curve_i::myComment  = "CURVE";
493 /*!
494   Generate unique name
495 */
496 QString VISU::Curve_i::GenerateName()
497 {
498   return VISU::GenerateName( "Curve", ++myNbPresent ).toLatin1().data();
499 }
500 /*!
501   Gets comment string
502 */
503 const char* VISU::Curve_i::GetComment() const
504 {
505   return myComment.c_str();
506 }
507 /*!
508   Constructor
509   NB : theHRow, theVRow are the indexes of rows in the Table object and numbered from the 1 to GetNbRows()
510 */
511 VISU::Curve_i::Curve_i( SALOMEDS::Study_ptr theStudy, Table_i* theTable,
512                         CORBA::Long theHRow, CORBA::Long theVRow,
513                         CORBA::Long theZRow, CORBA::Boolean theIsV2 )
514 : PrsObject_i(theStudy), myTable( theTable ), myHRow( theHRow ),
515   myVRow( theVRow ), myZRow( theZRow ), myIsV2( theIsV2 )
516 {
517   myAuto = true;
518   myLine = VISU::Curve::SOLIDLINE;
519   myLineWidth = 0;
520   myMarker = VISU::Curve::CIRCLE;
521   myColor.R = 0.0; myColor.G = 0.0; myColor.B = 0.0;
522 }
523 /*!
524   Destructor
525 */
526 VISU::Curve_i::~Curve_i()
527 {
528   MESSAGE("Curve_i::~Curve_i");
529 }
530
531 //----------------------------------------------------------------------------
532 void
533 VISU::Curve_i
534 ::SetTitle( const char* theTitle )
535 {
536   SetName( theTitle, true );
537 }
538
539 //----------------------------------------------------------------------------
540 char*
541 VISU::Curve_i
542 ::GetTitle()
543 {
544   return CORBA::string_dup( GetName().c_str() );
545 }
546
547 //----------------------------------------------------------------------------
548 void
549 VISU::Curve_i
550 ::SetColor( const SALOMEDS::Color& theColor )
551 {
552   myColor = theColor; 
553   myAuto = false;
554 }
555
556 //----------------------------------------------------------------------------
557 SALOMEDS::Color
558 VISU::Curve_i
559 ::GetColor()
560 {
561   return myColor;
562 }
563
564 //----------------------------------------------------------------------------
565 void
566 VISU::Curve_i
567 ::SetMarker( VISU::Curve::MarkerType theType )
568 {
569   myMarker = theType; 
570   myAuto = false;
571 }
572
573 //----------------------------------------------------------------------------
574 VISU::Curve::MarkerType
575 VISU::Curve_i
576 ::GetMarker()
577 {
578   return myMarker;
579 }
580
581 //----------------------------------------------------------------------------
582 void
583 VISU::Curve_i
584 ::SetLine( VISU::Curve::LineType theType, CORBA::Long theWidth )
585 {
586   myLine = theType; 
587   myLineWidth = theWidth; 
588   myAuto = false;
589 }
590
591 //----------------------------------------------------------------------------
592 VISU::Curve::LineType
593 VISU::Curve_i
594 ::GetLine()
595 {
596   return myLine;
597 }
598
599 //----------------------------------------------------------------------------
600 CORBA::Long
601 VISU::Curve_i
602 ::GetLineWidth()
603 {
604   return myLineWidth;
605 }
606
607 //----------------------------------------------------------------------------
608 /*!
609   Creates curve object
610 */
611 VISU::Storable* VISU::Curve_i::Create()
612 {
613   // generate name ...
614   SetName(GetVerTitle(), false);
615   if ( GetName() == "" )
616     SetName(GenerateName().toLatin1().data(), false);
617   // ... and build the object
618   return Build( false );
619 }
620 /*!
621   Builds presentation of curve
622 */
623 VISU::Storable* VISU::Curve_i::Build(int theRestoring )
624 {
625   if ( myTable != NULL ) {
626     // getting table SObject by it's entry
627     int nbRows = myTable->GetNbRows();
628     if ( myHRow > 0 && myHRow <= nbRows && myVRow > 0 && myVRow <= nbRows ) {
629       if ( !theRestoring ) {
630         // look for component
631         SALOMEDS::SComponent_var SComponent = VISU::FindOrCreateVisuComponent( GetStudyDocument() );
632         // create SObject and set attributes
633         QString aComment;
634         aComment.sprintf("myComment=%s",GetComment());
635         string anEntry = CreateAttributes( GetStudyDocument(),
636                                           myTable->GetObjectEntry(),
637                                           "",
638                                           GetID(),
639                                           GetName(),
640                                           "",
641                                           aComment.toLatin1().data(),
642                                           true );
643         // create SObject referenced to real table object
644         mySObj = SALOMEDS::SObject::_duplicate(GetStudyDocument()->FindObjectID(anEntry.c_str()));
645
646         // Set icon
647         SALOMEDS::StudyBuilder_var aStudyBuilder = GetStudyDocument()->NewBuilder();
648         SALOMEDS::GenericAttribute_var anAttr;
649         SALOMEDS::AttributePixMap_var  aPixmap;
650         anAttr  = aStudyBuilder->FindOrCreateAttribute( mySObj, "AttributePixMap" );
651         aPixmap = SALOMEDS::AttributePixMap::_narrow( anAttr );
652         aPixmap ->SetPixMap("ICON_TREE_CURVE");
653       }
654       return this;
655     }
656   }
657   return NULL;
658 }
659
660 /*!
661   Returns CORBA::True if curve refers to valid table data
662 */
663 CORBA::Boolean VISU::Curve_i::IsValid()
664 {
665   // getting table SObject by it's entry
666   SALOMEDS::SObject_var SO = GetStudyDocument()->FindObjectID(myTable->GetObjectEntry().c_str());
667   SALOMEDS::StudyBuilder_var Builder = GetStudyDocument()->NewBuilder();
668   SALOMEDS::GenericAttribute_var        anAttr;
669   SALOMEDS::AttributeTableOfInteger_var anInt;
670   SALOMEDS::AttributeTableOfReal_var    aReal;
671   if ( !SO->_is_nil() ) {
672     if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfInteger" ) ) {
673       anInt = SALOMEDS::AttributeTableOfInteger::_narrow( anAttr );
674       if ( myHRow > 0 && myHRow <= anInt->GetNbRows() && myVRow > 0 && myVRow <= anInt->GetNbRows() ) {
675         return true;
676       }
677     }
678     else if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfReal" ) ) {
679       aReal = SALOMEDS::AttributeTableOfReal::_narrow( anAttr );
680       if ( myHRow > 0 && myHRow <= aReal->GetNbRows() && myVRow > 0 && myVRow <= aReal->GetNbRows() ) {
681         return true;
682       }
683     }
684   }
685   return false;
686 }
687 /*!
688   Returns hor.axis title
689 */
690 string VISU::Curve_i::GetHorTitle()
691 {
692   string title;
693   // getting table SObject by it's entry
694   SALOMEDS::SObject_var SO = GetStudyDocument()->FindObjectID(myTable->GetObjectEntry().c_str());
695   SALOMEDS::StudyBuilder_var Builder = GetStudyDocument()->NewBuilder();
696   SALOMEDS::GenericAttribute_var        anAttr;
697   SALOMEDS::AttributeTableOfInteger_var anInt;
698   SALOMEDS::AttributeTableOfReal_var    aReal;
699   if ( !SO->_is_nil() ) {
700     if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfInteger" ) ) {
701       anInt = SALOMEDS::AttributeTableOfInteger::_narrow( anAttr );
702       SALOMEDS::StringSeq_var rowTitles = anInt->GetRowTitles();
703       if ( rowTitles->length() > 0 && myHRow > 0 && myHRow <= anInt->GetNbRows() ) {
704         title = rowTitles[ myHRow-1 ];
705       }
706     }
707     else if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfReal" ) ) {
708       aReal = SALOMEDS::AttributeTableOfReal::_narrow( anAttr );
709       SALOMEDS::StringSeq_var rowTitles = aReal->GetRowTitles();
710       if ( rowTitles->length() > 0 && myHRow > 0 && myHRow <= aReal->GetNbRows() ) {
711         title = rowTitles[ myHRow-1 ];
712       }
713     }
714   }
715   return title;
716 }
717 /*!
718   Returns ver.axis title
719 */
720 string VISU::Curve_i::GetVerTitle()
721 {
722   string title;
723   // getting table SObject by it's entry
724   SALOMEDS::SObject_var SO = GetStudyDocument()->FindObjectID(myTable->GetObjectEntry().c_str());
725   SALOMEDS::StudyBuilder_var Builder = GetStudyDocument()->NewBuilder();
726   SALOMEDS::GenericAttribute_var        anAttr;
727   SALOMEDS::AttributeTableOfInteger_var anInt;
728   SALOMEDS::AttributeTableOfReal_var    aReal;
729   if ( !SO->_is_nil() ) {
730     if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfInteger" ) ) {
731       anInt = SALOMEDS::AttributeTableOfInteger::_narrow( anAttr );
732       SALOMEDS::StringSeq_var rowTitles = anInt->GetRowTitles();
733       if ( rowTitles->length() > 0 && myVRow > 0 && myVRow <= anInt->GetNbRows() )
734         title = rowTitles[ myVRow-1 ];
735     }
736     else if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfReal" ) ) {
737       aReal = SALOMEDS::AttributeTableOfReal::_narrow( anAttr );
738       SALOMEDS::StringSeq_var rowTitles = aReal->GetRowTitles();
739       if ( rowTitles->length() > 0 && myVRow > 0 && myVRow <= aReal->GetNbRows() )
740         title = rowTitles[ myVRow-1 ];
741     }
742   }
743   return title;
744 }
745 /*!
746   Returns hor.axis units
747 */
748 string VISU::Curve_i::GetHorUnits()
749 {
750   string units;
751   // getting table SObject by it's entry
752   SALOMEDS::SObject_var SO = GetStudyDocument()->FindObjectID(myTable->GetObjectEntry().c_str());
753   SALOMEDS::StudyBuilder_var Builder = GetStudyDocument()->NewBuilder();
754   SALOMEDS::GenericAttribute_var        anAttr;
755   SALOMEDS::AttributeTableOfInteger_var anInt;
756   SALOMEDS::AttributeTableOfReal_var    aReal;
757   if ( !SO->_is_nil()  ) {
758     if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfInteger" ) ) {
759       anInt = SALOMEDS::AttributeTableOfInteger::_narrow( anAttr );
760       SALOMEDS::StringSeq_var rowUnits = anInt->GetRowUnits();
761       if ( rowUnits->length() > 0 && myHRow > 0 && myHRow <= anInt->GetNbRows() )
762         units = rowUnits[ myHRow-1 ];
763     }
764     else if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfReal" ) ) {
765       aReal = SALOMEDS::AttributeTableOfReal::_narrow( anAttr );
766       SALOMEDS::StringSeq_var rowUnits = aReal->GetRowUnits();
767       if ( rowUnits->length() > 0 && myHRow > 0 && myHRow <= aReal->GetNbRows() )
768         units = rowUnits[ myHRow-1 ];
769     }
770   }
771   return units;
772 }
773 /*!
774   Returns ver.axis units
775 */
776 string VISU::Curve_i::GetVerUnits()
777 {
778   string units;
779   // getting table SObject by it's entry
780   SALOMEDS::SObject_var SO = GetStudyDocument()->FindObjectID(myTable->GetObjectEntry().c_str());
781   SALOMEDS::StudyBuilder_var Builder = GetStudyDocument()->NewBuilder();
782   SALOMEDS::GenericAttribute_var        anAttr;
783   SALOMEDS::AttributeTableOfInteger_var anInt;
784   SALOMEDS::AttributeTableOfReal_var    aReal;
785   if ( !SO->_is_nil() ) {
786     if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfInteger" ) ) {
787       anInt = SALOMEDS::AttributeTableOfInteger::_narrow( anAttr );
788       SALOMEDS::StringSeq_var rowUnits = anInt->GetRowUnits();
789       if ( rowUnits->length() > 0 && myVRow > 0 && myVRow <= anInt->GetNbRows() )
790         units = rowUnits[ myVRow-1];
791     }
792     else if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfReal" ) ) {
793       aReal = SALOMEDS::AttributeTableOfReal::_narrow( anAttr );
794       SALOMEDS::StringSeq_var rowUnits = aReal->GetRowUnits();
795       if ( rowUnits->length() > 0 && myVRow > 0 && myVRow <= aReal->GetNbRows() )
796         units = rowUnits[ myVRow-1 ];
797     }
798   }
799   return units;
800 }
801 /*!
802   Gets curve data
803 */
804 int VISU::Curve_i::GetData( double*& theHorList, double*& theVerList, QStringList& zList )
805 {
806   theHorList = 0; theVerList = 0;
807   // getting table SObject by it's entry
808   SALOMEDS::SObject_var SO = GetStudyDocument()->FindObjectID(myTable->GetObjectEntry().c_str());
809   SALOMEDS::StudyBuilder_var Builder = GetStudyDocument()->NewBuilder();
810   SALOMEDS::GenericAttribute_var        anAttr;
811   SALOMEDS::AttributeTableOfInteger_var anInt;
812   SALOMEDS::AttributeTableOfReal_var    aReal;
813
814   QString tip = "%1: %2", z_data;
815
816   if ( !SO->_is_nil() ) {
817     if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfInteger" ) ) {
818       anInt = SALOMEDS::AttributeTableOfInteger::_narrow( anAttr );
819       int nbCols = anInt->GetNbColumns(), nbRows = anInt->GetNbRows();
820       if ( nbCols > 0 && myHRow > 0 && myHRow <= anInt->GetNbRows() && myVRow > 0 && myVRow <= anInt->GetNbRows() ) {
821         int nbPoints = 0;
822         for ( int j = 1; j <= nbCols; j++ ) {
823           if ( anInt->HasValue( myHRow, j ) && anInt->HasValue( myVRow, j ) )
824             nbPoints++;
825         }
826         if ( nbPoints > 0 ) {
827           theHorList = new double[ nbPoints ];
828           theVerList = new double[ nbPoints ];
829           int k = 0;
830
831           SALOMEDS::StringSeq_var rowTitles = anInt->GetRowTitles();
832
833           for ( int j = 1; j <= nbCols; j++ ) {
834             if ( anInt->HasValue( myHRow, j ) && anInt->HasValue( myVRow, j ) ) {
835               theHorList[k] = anInt->GetValue( myHRow, j );
836               theVerList[k] = anInt->GetValue( myVRow, j );
837
838               z_data = tip.arg( GetHorTitle().c_str() ).arg( theHorList[k] ) + "\n";
839               z_data += tip.arg( GetVerTitle().c_str() ).arg( theVerList[k] );
840
841               if( myZRow>0 && myZRow<=nbRows && anInt->HasValue( myZRow, j ) )
842               {
843                 string title;
844                 title = rowTitles[ myZRow-1 ];
845                 z_data += "\n" + tip.arg( title.c_str() ).arg( anInt->GetValue( myZRow, j ) );
846               }
847               zList.append( z_data );
848               k++;
849             }
850           }
851         }
852         return nbPoints;
853       }
854     }
855     else if ( Builder->FindAttribute( SO, anAttr, "AttributeTableOfReal" ) ) {
856       aReal = SALOMEDS::AttributeTableOfReal::_narrow( anAttr );
857       int nbCols = aReal->GetNbColumns(), nbRows = aReal->GetNbRows();
858       if ( nbCols > 0 && myHRow > 0 && myHRow <= aReal->GetNbRows() && myVRow > 0 && myVRow <= aReal->GetNbRows() ) {
859         int nbPoints = 0;
860         for ( int j = 1; j <= nbCols; j++ ) {
861           if ( aReal->HasValue( myHRow, j ) && aReal->HasValue( myVRow, j ) )
862             nbPoints++;
863         }
864         if ( nbPoints > 0 ) {
865           theHorList = new double[ nbPoints ];
866           theVerList = new double[ nbPoints ];
867           int k = 0;
868
869           SALOMEDS::StringSeq_var rowTitles = aReal->GetRowTitles();
870
871           for ( int j = 1; j <= nbCols; j++ ) {
872             if ( aReal->HasValue( myHRow, j ) && aReal->HasValue( myVRow, j ) ) {
873               theHorList[k] = aReal->GetValue( myHRow, j );
874               theVerList[k] = aReal->GetValue( myVRow, j );
875
876               z_data = tip.arg( GetHorTitle().c_str() ).arg( theHorList[k] ) + "\n";
877               z_data += tip.arg( GetVerTitle().c_str() ).arg( theVerList[k] );
878
879               if( myZRow>0 && myZRow<=nbRows && aReal->HasValue( myZRow, j ) )
880               {
881                 string title;
882                 title = rowTitles[ myZRow-1 ];
883                 z_data += "\n" + tip.arg( title.c_str() ).arg( aReal->GetValue( myZRow, j ) );
884               }
885               zList.append( z_data );
886               k++;
887             }
888           }
889         }
890         return nbPoints;
891       }
892     }
893   }
894   return 0;
895 }
896 /*!
897   Creates curve Plot2d presentation object
898 */
899 SPlot2d_Curve* VISU::Curve_i::CreatePresentation()
900 {
901   SPlot2d_Curve* crv = new SPlot2d_Curve();
902   crv->setYAxis( myIsV2 ? QwtPlot::yRight : QwtPlot::yLeft );
903   crv->setHorTitle( GetHorTitle().c_str() );
904   string tlt = GetTitle();
905   if ( tlt.length() <= 0 )
906     tlt = GetVerTitle();
907   //crv->setVerTitle( strdup( GetVerTitle().c_str() ) );
908   //crv->setVerTitle( strdup( GetName() ) );
909   crv->setVerTitle( tlt.c_str() );
910   crv->setHorUnits( GetHorUnits().c_str() );
911   crv->setVerUnits( GetVerUnits().c_str() );
912   double* xList = 0;
913   double* yList = 0;
914   QStringList zList;
915   int     nbPoints = GetData( xList, yList, zList );
916   if ( nbPoints > 0 && xList && yList ) {
917     crv->setData( xList, yList, nbPoints, zList );
918   }
919   //cout << "********** Number of points: " << nbPoints <<endl;
920   //for ( int i =0 ; i < nbPoints; i++ ) {
921   //  cout << i<<"\t"<<xList[i] << "\t"<< yList[i] << endl;
922   //}
923   crv->setLine( (Plot2d::LineType)GetLine(), GetLineWidth() );
924   crv->setMarker( (Plot2d::MarkerType)GetMarker() );
925   SALOMEDS::Color color = GetColor();
926   crv->setColor( QColor( (int)(color.R*255.), (int)(color.G*255.), (int)(color.B*255.) ) );
927   crv->setAutoAssign( IsAuto() );
928   CORBA::String_var aString = mySObj->GetID();
929   crv->setIO(new SALOME_InteractiveObject(aString.in(), "VISU", GetName().c_str()));
930   if ( myTable )
931     crv->setTableIO(new SALOME_InteractiveObject(myTable->GetObjectEntry().c_str(), "VISU", myTable->GetName().c_str()));
932   return crv;
933 }
934 /*!
935   Restores curve object from stream
936 */
937 VISU::Storable* VISU::Curve_i::Restore( const Storable::TRestoringMap& theMap, SALOMEDS::SObject_ptr theSO)
938 {
939   if(MYDEBUG) MESSAGE(GetComment());
940   mySObj = SALOMEDS::SObject::_duplicate(theSO);
941   SetName(VISU::Storable::FindValue(theMap,"myName").toLatin1().data(), false);
942   myHRow = VISU::Storable::FindValue(theMap,"myHRow").toInt();
943   myVRow = VISU::Storable::FindValue(theMap,"myVRow").toInt();
944   bool ok = false;
945   QString z_str = VISU::Storable::FindValue(theMap,"myZRow", &ok);
946   myZRow = ok ? z_str.toInt() : 0;
947   ok = false;
948   QString v2_str = VISU::Storable::FindValue(theMap,"myIsV2", &ok);
949   myIsV2 = ok ? v2_str.toInt() : false;
950
951   myColor.R = VISU::Storable::FindValue(theMap,"myColor.R").toDouble();
952   myColor.G = VISU::Storable::FindValue(theMap,"myColor.G").toDouble();
953   myColor.B = VISU::Storable::FindValue(theMap,"myColor.B").toDouble();
954   myMarker = ( VISU::Curve::MarkerType )( VISU::Storable::FindValue(theMap,"myMarker").toInt() );
955   myLine = ( VISU::Curve::LineType )( VISU::Storable::FindValue(theMap,"myLine").toInt() );
956   myLineWidth = VISU::Storable::FindValue(theMap,"myLineWidth").toInt();
957   myAuto = VISU::Storable::FindValue(theMap,"myAuto").toInt();
958   return Build( true );
959 }
960 /*!
961   Flushes curve data into stream
962 */
963 void VISU::Curve_i::ToStream( std::ostringstream& theStr )
964 {
965   Storable::DataToStream( theStr, "myName",      GetName().c_str() );
966   Storable::DataToStream( theStr, "myHRow",      myHRow );
967   Storable::DataToStream( theStr, "myVRow",      myVRow );
968   Storable::DataToStream( theStr, "myZRow",      myZRow );
969   Storable::DataToStream( theStr, "myIsV2",      myIsV2 );
970   Storable::DataToStream( theStr, "myColor.R",   myColor.R );
971   Storable::DataToStream( theStr, "myColor.G",   myColor.G );
972   Storable::DataToStream( theStr, "myColor.B",   myColor.B );
973   Storable::DataToStream( theStr, "myMarker",    myMarker );
974   Storable::DataToStream( theStr, "myLine",      myLine );
975   Storable::DataToStream( theStr, "myLineWidth", myLineWidth );
976   Storable::DataToStream( theStr, "myAuto",      myAuto );
977 }
978 /*!
979   Gets reference table's entry
980 */
981 std::string VISU::Curve_i::GetTableID() {
982   return myTable->GetObjectEntry();
983 }
984 /*!
985   Called from engine to restore curve from the file
986 */
987 VISU::Storable* VISU::Curve_i::StorableEngine(SALOMEDS::SObject_ptr theSObject,
988                                               const Storable::TRestoringMap& theMap,
989                                               const std::string& thePrefix,
990                                               CORBA::Boolean theIsMultiFile)
991 {
992   SALOMEDS::Study_var aStudy = theSObject->GetStudy();
993   VISU::Table_i* pTable = GetTable(aStudy, theSObject->GetFather());
994   if( pTable != NULL ) {
995     VISU::Curve_i* pResent = new VISU::Curve_i( aStudy, pTable, 0, 0, 0, false );
996     return pResent->Restore( theMap, theSObject);
997   }
998   return NULL;
999 }
1000
1001 void VISU::Curve_i::RemoveFromStudy()
1002 {
1003   struct TRemoveFromStudy: public SALOME_Event
1004   {
1005     VISU::Curve_i* myRemovable;
1006     TRemoveFromStudy(VISU::Curve_i* theRemovable):
1007       myRemovable(theRemovable)
1008     {}
1009     
1010     virtual
1011     void
1012     Execute()
1013     {
1014       VISU::DeleteActors(myRemovable);
1015       VISU::RemoveFromStudy(myRemovable->GetSObject(),false);
1016     }
1017   };
1018
1019   ProcessVoidEvent(new TRemoveFromStudy(this));
1020 }
1021
1022 SALOMEDS::SObject_var VISU::Curve_i::GetSObject()
1023 {
1024   return mySObj;
1025 }
1026
1027 //----------------------------------------------------------------
1028 //                      Container Object
1029 //----------------------------------------------------------------
1030 int VISU::Container_i::myNbPresent = 0;
1031 const string VISU::Container_i::myComment  = "CONTAINER";
1032 /*!
1033   Generate unique name
1034 */
1035 QString VISU::Container_i::GenerateName()
1036 {
1037   return VISU::GenerateName( "Plot2DView", ++myNbPresent ).toLatin1().data();
1038 }
1039 /*!
1040   Gets comment string
1041 */
1042 const char* VISU::Container_i::GetComment() const
1043 {
1044   return myComment.c_str();
1045 }
1046 /*!
1047   Constructor
1048 */
1049 VISU::Container_i::Container_i( SALOMEDS::Study_ptr theStudy )
1050      : PrsObject_i( theStudy )
1051 {
1052 }
1053 /*!
1054   Destructor
1055 */
1056 VISU::Container_i::~Container_i()
1057 {
1058   MESSAGE("Container_i::~Container_i");
1059   myCurves.clear();
1060 }
1061 /*!
1062   Inserts curve into the container
1063 */
1064 void VISU::Container_i::AddCurve( Curve_ptr theCurve )
1065 {
1066   if ( GetStudyDocument()->_is_nil() )
1067     return;
1068   SALOMEDS::SObject_var mySO = GetStudyDocument()->FindObjectID( GetEntry().c_str() );
1069   if ( mySO->_is_nil() )
1070     return;
1071   PortableServer::POA_ptr aPOA = GetPOA();
1072   Curve_i* pCurve = dynamic_cast<Curve_i*>( aPOA->reference_to_servant( theCurve ) );
1073   if( pCurve ) {
1074     QString entry( pCurve->GetEntry().c_str() );
1075     SALOMEDS::SObject_var SO = GetStudyDocument()->FindObjectID( entry.toLatin1().data() );
1076     if ( !SO->_is_nil() && myCurves.indexOf( entry ) == -1 ) {
1077       myCurves.append( entry );
1078       SALOMEDS::StudyBuilder_var Builder = GetStudyDocument()->NewBuilder();
1079       SALOMEDS::SObject_var newSO = Builder->NewObject( mySO );
1080       Builder->Addreference( newSO, SO );
1081     }
1082   }
1083 }
1084 /*!
1085   Removes curve from the container
1086 */
1087 void VISU::Container_i::RemoveCurve( Curve_ptr theCurve )
1088 {
1089   if ( GetStudyDocument()->_is_nil() )
1090     return;
1091   SALOMEDS::SObject_var mySO = GetStudyDocument()->FindObjectID( GetEntry().c_str() );
1092   if ( mySO->_is_nil() )
1093     return;
1094   PortableServer::POA_ptr aPOA = GetPOA();
1095   Curve_i* pCurve = dynamic_cast<Curve_i*>( aPOA->reference_to_servant( theCurve ) );
1096   if( pCurve ) {
1097     QString entry( pCurve->GetEntry().c_str() );
1098     if ( myCurves.indexOf( entry ) != -1 ) {
1099       // found !!!
1100       myCurves.removeAll( entry );
1101       SALOMEDS::StudyBuilder_var Builder = GetStudyDocument()->NewBuilder();
1102       SALOMEDS::ChildIterator_var CI = GetStudyDocument()->NewChildIterator( mySO );
1103       for ( ; CI->More(); CI->Next() ) {
1104         SALOMEDS::SObject_var childSO = CI->Value();
1105         SALOMEDS::SObject_var refSO;
1106         if ( childSO->ReferencedObject( refSO ) && !refSO->_is_nil() && entry == QString( refSO->GetID() ) ) {
1107           Builder->RemoveObject( childSO );
1108         }
1109       }
1110     }
1111   }
1112 }
1113 /*!
1114   Gets number of curves in the container
1115 */
1116 CORBA::Long VISU::Container_i::GetNbCurves()
1117 {
1118   Update();
1119   return myCurves.count();
1120 }
1121 /*!
1122   Clears container
1123 */
1124 void VISU::Container_i::Clear()
1125 {
1126   if ( GetStudyDocument()->_is_nil() )
1127     return;
1128   SALOMEDS::SObject_var mySO = GetStudyDocument()->FindObjectID( GetEntry().c_str() );
1129   if ( mySO->_is_nil() )
1130     return;
1131   QStringList toDelete;
1132   SALOMEDS::ChildIterator_var CI = GetStudyDocument()->NewChildIterator( mySO );
1133   for ( ; CI->More(); CI->Next() ) {
1134     toDelete.append( CI->Value()->GetID() );
1135   }
1136   SALOMEDS::StudyBuilder_var Builder = GetStudyDocument()->NewBuilder();
1137   for ( int i = 0; i < toDelete.count(); i++ ) {
1138     SALOMEDS::SObject_var SO = GetStudyDocument()->FindObjectID( toDelete[i].toLatin1().data() );
1139     Builder->RemoveObject( SO );
1140   }
1141   myCurves.clear();
1142 }
1143 /*!
1144   Creates container object
1145 */
1146 VISU::Storable* VISU::Container_i::Create()
1147 {
1148   // generate name ...
1149   SetName(GenerateName().toLatin1().data(), false);
1150   // ... and build the object
1151   return Build( false );
1152 }
1153 /*!
1154   Builds presentation of container
1155 */
1156 VISU::Storable* VISU::Container_i::Build( int theRestoring )
1157 {
1158   if ( !theRestoring ) {
1159     // looking for component
1160     SALOMEDS::SComponent_var SComponent = VISU::FindOrCreateVisuComponent( GetStudyDocument() );
1161     // create SObject and set attributes
1162     QString aComment;
1163     aComment.sprintf("myComment=%s",GetComment());
1164     string anEntry = CreateAttributes( GetStudyDocument(),
1165                                        SComponent->GetID(),
1166                                        "",
1167                                        GetID(),
1168                                        GetName(),
1169                                        "",
1170                                        aComment.toLatin1().data(),
1171                                        true );
1172     mySObj = SALOMEDS::SObject::_duplicate(GetStudyDocument()->FindObjectID(anEntry.c_str()));
1173
1174     // Set icon
1175     SALOMEDS::StudyBuilder_var aStudyBuilder = GetStudyDocument()->NewBuilder();
1176     SALOMEDS::GenericAttribute_var anAttr;
1177     SALOMEDS::AttributePixMap_var  aPixmap;
1178     anAttr  = aStudyBuilder->FindOrCreateAttribute( mySObj, "AttributePixMap" );
1179     aPixmap = SALOMEDS::AttributePixMap::_narrow( anAttr );
1180     aPixmap ->SetPixMap("ICON_TREE_CONTAINER");
1181   }
1182   return this;
1183 }
1184 /*!
1185   Updates presentation of container
1186 */
1187 void VISU::Container_i::Update()
1188 {
1189   if ( GetStudyDocument()->_is_nil() )
1190     return;
1191   SALOMEDS::StudyBuilder_var Builder = GetStudyDocument()->NewBuilder();
1192   SALOMEDS::SObject_var mySO = GetStudyDocument()->FindObjectID( GetEntry().c_str() );
1193   SALOMEDS::GenericAttribute_var anAttr;
1194   if ( !mySO->_is_nil() ) {
1195     QStringList toDelete;
1196     int i;
1197     for ( i = 0; i < myCurves.count(); i++ ) {
1198       SALOMEDS::SObject_var SO = GetStudyDocument()->FindObjectID( myCurves[i].toLatin1().data() );
1199       if ( !SO->_is_nil() && Builder->FindAttribute( SO, anAttr, "AttributeIOR" ) ) {
1200         // if real Curve Object still exists
1201         SALOMEDS::ChildIterator_var CI = GetStudyDocument()->NewChildIterator( mySO );
1202         bool bFound = false;
1203         for ( ; CI->More(); CI->Next() ) {
1204           SALOMEDS::SObject_var childSO = CI->Value();
1205           SALOMEDS::SObject_var refSO;
1206           if ( childSO->ReferencedObject( refSO ) && !refSO->_is_nil() && myCurves[i] == QString( refSO->GetID() ) ) {
1207             bFound = true; break;
1208           }
1209         }
1210         if (! bFound ) {
1211           // create SObject referenced to real curve object if is not yet added
1212           SALOMEDS::SObject_var newSO = Builder->NewObject( mySO );
1213           Builder->Addreference( newSO, SO );
1214         }
1215       }
1216       else {
1217         // real Curve Object doesn't exist (might be removed)
1218         toDelete.append( myCurves[i] );
1219       }
1220     }
1221     for ( i = 0; i < toDelete.count(); i++ ) {
1222       myCurves.removeAll( toDelete[i] );
1223     }
1224     toDelete.clear();
1225     SALOMEDS::ChildIterator_var CI = GetStudyDocument()->NewChildIterator( mySO );
1226     for ( ; CI->More(); CI->Next() ) {
1227       SALOMEDS::SObject_var childSO = CI->Value();
1228       SALOMEDS::SObject_var refSO;
1229       if ( childSO->ReferencedObject( refSO ) && ( refSO->_is_nil() || !Builder->FindAttribute( refSO, anAttr, "AttributeIOR" ) ||
1230                                                    myCurves.indexOf( refSO->GetID() ) == -1 ) ) {
1231         toDelete.append( childSO->GetID() );
1232       }
1233     }
1234     for ( i = 0; i < toDelete.count(); i++ ) {
1235       SALOMEDS::ChildIterator_var CI = GetStudyDocument()->NewChildIterator( mySO );
1236       for ( ; CI->More(); CI->Next() ) {
1237         SALOMEDS::SObject_var childSO = CI->Value();
1238         if ( toDelete[i] == CI->Value()->GetID() ) {
1239           Builder->RemoveObject( childSO );
1240         }
1241       }
1242     }
1243   }
1244 }
1245 /*!
1246   Gets curve from container by index
1247   NB : curves are numbered from 1
1248 */
1249 VISU::Curve_i* VISU::Container_i::GetCurve( CORBA::Long theIndex )
1250 {
1251   if ( theIndex > 0 && theIndex <= myCurves.count()  ) {
1252     SALOMEDS::StudyBuilder_var Builder = GetStudyDocument()->NewBuilder();
1253     SALOMEDS::GenericAttribute_var anAttr;
1254     SALOMEDS::SObject_var SO = GetStudyDocument()->FindObjectID(myCurves[ theIndex-1 ].toLatin1().data() );
1255     CORBA::Object_var anObject = VISU::SObjectToObject( SO );
1256     if( !CORBA::is_nil( anObject ) ) {
1257       // if real Curve Object exists
1258       CORBA::Object_ptr aCurve = VISU::Curve::_narrow( anObject );
1259       if( !CORBA::is_nil( aCurve ) )
1260       return dynamic_cast<VISU::Curve_i*>(VISU::GetServant(aCurve).in());
1261     }
1262   }
1263   return NULL;
1264 }
1265 /*!
1266   Restores container data from the stream
1267 */
1268 VISU::Storable* VISU::Container_i::Restore( const Storable::TRestoringMap& theMap, SALOMEDS::SObject_ptr SO )
1269 {
1270   if(MYDEBUG) MESSAGE(GetComment());
1271   mySObj = SALOMEDS::SObject::_duplicate(SO);
1272   SetName(VISU::Storable::FindValue( theMap, "myName" ).toLatin1().data(), false);
1273   QString val = VISU::Storable::FindValue( theMap, "myCurves" );
1274   myCurves = val.split( "*", QString::SkipEmptyParts );
1275   return Build( true );
1276 }
1277 /*!
1278   Flushes container data into the stream
1279 */
1280 void VISU::Container_i::ToStream( std::ostringstream& theStr )
1281 {
1282   Storable::DataToStream( theStr, "myName",   GetName().c_str() );
1283   Storable::DataToStream( theStr, "myCurves", myCurves.join( QString( "*" ) ) );
1284 //  theStr<<" myName "<<myName;
1285 //  theStr<<" myCurves "<<myCurves.join( QString( "*" ) ).latin1()<<"* ";
1286 }
1287 /*!
1288   Called from engine to restore container from the file
1289 */
1290 VISU::Storable* VISU::Container_i::StorableEngine(SALOMEDS::SObject_ptr theSObject,
1291                                                   const Storable::TRestoringMap& theMap,
1292                                                   const std::string& thePrefix,
1293                                                   CORBA::Boolean theIsMultiFile)
1294 {
1295   SALOMEDS::Study_var aStudy = theSObject->GetStudy();
1296   VISU::Container_i* pResent = new VISU::Container_i( aStudy );
1297   return pResent->Restore( theMap, theSObject );
1298 }
1299
1300 void VISU::Container_i::RemoveFromStudy()
1301 {
1302   struct TRemoveFromStudy: public SALOME_Event
1303   {
1304     VISU::Container_i* myRemovable;
1305     TRemoveFromStudy(VISU::Container_i* theRemovable):
1306       myRemovable(theRemovable)
1307     {}
1308     
1309     virtual
1310     void
1311     Execute()
1312     {
1313       VISU::RemoveFromStudy(myRemovable->GetSObject(),false);
1314     }
1315   };
1316
1317   ProcessVoidEvent(new TRemoveFromStudy(this));
1318 }
1319
1320 SALOMEDS::SObject_var VISU::Container_i::GetSObject()
1321 {
1322   return mySObj;
1323 }
1324
1325 SALOMEDS::SObject_var
1326 VISU::ImportTables(const char* theFileName, SALOMEDS::Study_ptr theStudy,
1327                    bool theFirstStrAsTitle)
1328 {
1329   // Set "C" numeric locale to import numbers correctly
1330   Kernel_Utils::Localizer loc;
1331
1332   TTableContainer aContainer;
1333   ImportTables( theFileName, aContainer, theFirstStrAsTitle );
1334   if ( aContainer.empty() ) 
1335     return SALOMEDS::SObject::_nil();
1336
1337   SALOMEDS::StudyBuilder_var aStudyBuilder = theStudy->NewBuilder();
1338   SALOMEDS::SComponent_var theSComponent = VISU::FindOrCreateVisuComponent(theStudy);
1339   SALOMEDS::SObject_var aFileObject = aStudyBuilder->NewObject(theSComponent);
1340   SALOMEDS::GenericAttribute_var anAttr =
1341     aStudyBuilder->FindOrCreateAttribute(aFileObject, "AttributeName");
1342   SALOMEDS::AttributeName_var aName = SALOMEDS::AttributeName::_narrow(anAttr);
1343   QFileInfo aFileInfo(theFileName);
1344   aName->SetValue( aFileInfo.fileName().toLatin1().data());
1345   anAttr = aStudyBuilder->FindOrCreateAttribute(aFileObject, "AttributeString");
1346   SALOMEDS::AttributeString_var aComment = SALOMEDS::AttributeString::_narrow(anAttr);
1347   QString aString;
1348   aString.sprintf("myComment=ImportTables;myFileName=%s;myFirstStrAsTitle=%d",
1349                   aFileInfo.absoluteFilePath().toLatin1().data(),theFirstStrAsTitle);
1350   aComment->SetValue(aString.toLatin1().data());
1351   for(int i = 0, iEnd = aContainer.size(); i < iEnd; i++) {
1352     PTableIDMapper aTableIDMapper = aContainer[i];
1353     const TTable2D& aTable2D = *aTableIDMapper;
1354     SALOMEDS::SObject_var aRealObject = aStudyBuilder->NewObject(aFileObject);
1355     anAttr = aStudyBuilder->FindOrCreateAttribute(aRealObject, "AttributeName");
1356     aName = SALOMEDS::AttributeName::_narrow(anAttr);
1357     if(MYDEBUG) MESSAGE("aTable2D.myTitle = "<<aTable2D.myTitle);
1358     if ( aTable2D.myTitle != "" ) {
1359       aName->SetValue(aTable2D.myTitle.c_str());
1360     }
1361     else {
1362       QString aNewName;
1363       aNewName.sprintf("Table:%d",i);
1364       aName->SetValue(aNewName.toLatin1().data());
1365     }
1366
1367     anAttr = aStudyBuilder->FindOrCreateAttribute(aRealObject, "AttributeTableOfReal");
1368     SALOMEDS::AttributeTableOfReal_var aTableOfReal = SALOMEDS::AttributeTableOfReal::_narrow(anAttr);
1369     aTableOfReal->SetTitle(aTable2D.myTitle.c_str());
1370     TTable2D aNewTable2D;
1371     aTable2D.getColumns(aNewTable2D);
1372     int kEnd = aNewTable2D.myRows[0].myValues.size();
1373     // check empty columns
1374     TColStd_MapOfInteger EmptyColumns;
1375     for(int j = 0, jEnd = aNewTable2D.myRows.size(); j < jEnd; j++) {
1376       bool hasVal = false;
1377       for(int k = 0; k < kEnd; k++) {
1378         QString aVal = aNewTable2D.myRows[j].myValues[k].c_str();
1379         bool anIsOk = false;
1380         double aValue = aVal.toDouble(&anIsOk);
1381         if(anIsOk) {
1382           hasVal = true;
1383           break;
1384         }
1385       }
1386       if(!hasVal) {
1387         EmptyColumns.Add(j);
1388       }
1389     }
1390     // create table of real
1391     aTableOfReal->SetNbColumns( kEnd - EmptyColumns.Extent() );
1392     int currNum = -1;
1393     for(int j = 0, jEnd = aNewTable2D.myRows.size(); j < jEnd; j++) {
1394       if( EmptyColumns.Contains(j) ) continue;
1395       currNum++;
1396       if(MYDEBUG) MESSAGE("j = "<<j<<"; kEnd = "<<kEnd);
1397       for(int k = 0; k < kEnd; k++) {
1398         QString aVal = aNewTable2D.myRows[j].myValues[k].c_str();
1399         bool anIsOk = false;
1400         double aValue = aVal.toDouble(&anIsOk);
1401         if( anIsOk && !aVal.contains("NAN",Qt::CaseInsensitive) &&
1402             !aVal.contains("INF",Qt::CaseInsensitive) ) {
1403           aTableOfReal->PutValue(aValue,currNum+1,k+1);
1404         }
1405       }
1406       aTableOfReal->SetRowTitle(currNum+1,aNewTable2D.myRows[j].myTitle.c_str());
1407       aTableOfReal->SetRowUnit(currNum+1,aNewTable2D.myRows[j].myUnit.c_str());
1408     }
1409     for(int k = 0; k < kEnd; k++)
1410       aTableOfReal->SetColumnTitle(k+1,aNewTable2D.myColumnTitles[k].c_str());
1411   }
1412   return aFileObject;
1413 }
1414
1415
1416 //=======================================================================
1417 //function : updateStrForCSV
1418 //purpose  : auxilary for ExportTableToFile
1419 //=======================================================================
1420 void updateStrForCSV(QString& aStr, const char aSep)
1421 {
1422   int index = aStr.indexOf('"');
1423   while(index>=0) {
1424     aStr.insert(index,'"');
1425     if( index+2 >= aStr.size() ) break;
1426     index = aStr.indexOf('"',index+2);
1427   }
1428   index = aStr.indexOf(aSep);
1429   if(index>=0) {
1430     // current string contains separator => need to use "..."
1431     aStr.insert(0,'"');
1432     aStr.push_back('"');
1433   }
1434 }
1435
1436
1437 //=======================================================================
1438 //function : ExportTableToFile
1439 //purpose  : 
1440 //=======================================================================
1441 template<class TTableAttr> bool ExportTableToFile(const TTableAttr& aTabAttr,
1442                                                   const char* theFileName)
1443 {
1444   if (CORBA::is_nil(aTabAttr))
1445     return false;
1446
1447   // Set "C" numeric locale to save numbers correctly
1448   Kernel_Utils::Localizer loc;
1449
1450   QFile aFile(theFileName);
1451   aFile.open(QIODevice::WriteOnly);
1452
1453   /* extract the table info and write it into file */
1454
1455   QString aTitle(aTabAttr->GetTitle()); /*Table title*/
1456   int aRowsNb = aTabAttr->GetNbRows();
1457   int aColNb  = aTabAttr->GetNbColumns();
1458
1459   SALOMEDS::StringSeq_var aRowTitles = aTabAttr->GetRowTitles();
1460   SALOMEDS::StringSeq_var aRowUnits = aTabAttr->GetRowUnits();
1461   SALOMEDS::StringSeq_var aColumnTitles = aTabAttr->GetColumnTitles();
1462
1463   //--------------------------------------------------
1464   //    write as *.csv file if it is needed
1465   //--------------------------------------------------
1466   QString tmp(theFileName);
1467   tmp = tmp.trimmed();
1468   tmp = tmp.right(3).trimmed();
1469   if( tmp == QString("csv") ) {
1470     const char aSep = ',';
1471     // write column titles
1472     QString aLine(aRowTitles[0]);
1473     updateStrForCSV(aLine,aSep);
1474     for(int i=1; i<aRowsNb; i++) {
1475       aLine += aSep;
1476       QString aTmp(aRowTitles[i]);
1477       updateStrForCSV(aTmp,aSep);
1478       aLine += aTmp;
1479     }
1480     aLine += "\n";
1481     aFile.write(aLine.toLatin1() );
1482     // write table data
1483     QString aValue;
1484     for (int j = 1; j <= aColNb; j++) {
1485       QString aLine = "";
1486       if(aTabAttr->HasValue(j,1)) {
1487         aLine = aValue.sprintf("%.16g",(double)aTabAttr->GetValue(1,j));
1488       }
1489       for (int i = 2; i <= aRowsNb; i++) {
1490         if(aTabAttr->HasValue(i,j)) {
1491           aLine += aSep + aValue.sprintf("%.16g",(double)aTabAttr->GetValue(i,j));
1492         }
1493         else aLine += aSep;
1494       }
1495       aLine += "\n";
1496       aFile.write(aLine.toLatin1() );
1497     }
1498
1499     aFile.close();
1500     return true;
1501   }
1502   //--------------------------------------------------
1503   //       end of writing as *.csv file
1504   //--------------------------------------------------
1505
1506   /* The given table is rare (some cells is empty) or not? */
1507   bool isRareTable = false;
1508   for (int i = 1; i <= aRowsNb; i++)
1509     for (int j = 1; j <= aColNb && !isRareTable; j++)
1510       isRareTable = !aTabAttr->HasValue(i,j);
1511
1512   QString aLine;
1513   if (isRareTable) {
1514     /* Separate the given table to 2D tables and write these ones to the file */
1515     QString anAbscissTitle(aRowTitles[0]); /*Absciss row title (X coord)*/
1516     anAbscissTitle.trimmed();
1517     QString anAbscissUnit(aRowUnits[0]);
1518     anAbscissUnit.trimmed();
1519     if (aRowsNb > 2 && aTitle.length() )  aTitle = aTitle + " - ";
1520
1521     for (int i = 2; i <= aRowsNb; i++ )
1522       {
1523         /* TITLE */
1524         QString anOrdinate(aRowTitles[i-1]), aTail;
1525         anOrdinate.trimmed();
1526
1527         aLine = "#TITLE: " + aTitle +
1528           ((anOrdinate.length())?  anOrdinate :
1529                                   (aRowsNb>2)? aTail.sprintf("%d",i-1) : aTail.sprintf("") ) + "\n";
1530         aFile.write(aLine.toLatin1() );
1531
1532         /* COLUMN_TITLES */
1533         if ( anAbscissTitle.length() || anOrdinate.length() ) {
1534           aLine = "#COLUMN_TITLES: " + anAbscissTitle + " | " + anOrdinate;
1535           int tmpind = aLine.indexOf("\n");
1536           while(tmpind>=0) {
1537             aLine.remove(tmpind,1);
1538             tmpind = aLine.indexOf("\n");
1539           }
1540           aLine += "\n";
1541           aFile.write(aLine.toLatin1() );
1542         }
1543
1544         /* COLUMN_UNITS */
1545         aLine = anAbscissUnit + " " +aRowUnits[i-1];
1546         if (!aLine.trimmed().isEmpty()) {
1547           aLine = "#COLUMN_UNITS: " + aLine  + "\n";
1548           aFile.write(aLine.toLatin1() );
1549         }
1550
1551         /* CURVE COORDINATES */
1552         for (int j = 1; j <= aColNb; j++)
1553           {
1554             if ( aTabAttr -> HasValue(i,j) &&  aTabAttr -> HasValue(1, j)) {
1555               aLine = aLine.sprintf("%.16g %.16g",
1556                                     (double)(aTabAttr->GetValue(1,j)),
1557                                     (double)(aTabAttr->GetValue(i,j)));  /* aTabAttr->GetValue(1,j) - X coord */
1558               if ( !aLine.trimmed().isEmpty() ) {
1559                 QString aColTitle(aColumnTitles[j-1]);
1560                 if ( !aColTitle.trimmed().isEmpty() )
1561                   aLine = aLine + "  #TITLE: " + aColTitle ;
1562                 aFile.write(QString(aLine + "\n").toLatin1() );
1563               }
1564             }
1565           }
1566         aFile.write("\n", 1);
1567       }
1568   }//end of if (isRareTable)
1569   else {
1570     /* Write the table in the file without separating */
1571     /* TITLE */
1572     aLine = "#TITLE: " + aTitle + "\n";
1573     aFile.write(aLine.toLatin1());
1574
1575     /* COLUMN_TITLES  and COLUMN_UNITS */
1576     QString aTitlesSep = "";
1577     QString aUnitsSep  = "";
1578     QString aTitlesStr = "#COLUMN_TITLES: ";
1579     QString aUnitsStr  = "#COLUMN_UNITS: ";
1580     for (int i = 1; i <= aRowsNb; i++) {
1581       if (!QString(aRowTitles[i-1]).trimmed().isEmpty()) {
1582         aTitlesStr += (aTitlesSep + aRowTitles[i-1]);
1583         if (aTitlesSep.isEmpty()) aTitlesSep = " | ";
1584       }
1585       if (!QString(aRowUnits[i-1]).trimmed().isEmpty()) {
1586         aUnitsStr += (aUnitsSep + aRowUnits[i-1]);
1587         if (aUnitsSep.isEmpty()) aUnitsSep = " ";
1588       }
1589     }
1590     int tmpind = aTitlesStr.indexOf("\n");
1591     while(tmpind>=0) {
1592       aTitlesStr.remove(tmpind,1);
1593       tmpind = aTitlesStr.indexOf("\n");
1594     }
1595     aTitlesStr += "\n";
1596     aUnitsStr  += "\n";
1597     aFile.write(aTitlesStr.toLatin1());
1598     aFile.write(aUnitsStr.toLatin1());
1599
1600     /* CURVE COORDINATES */
1601     QString aSep, aValue, aColTitle;
1602     for (int j = 1; j <= aColNb; j++) {
1603       aLine = ""; aSep  = "";
1604       for (int i = 1; i <= aRowsNb; i++) {
1605         aLine += (aSep + aValue.sprintf("%.16g", (double)(aTabAttr->GetValue(i,j))));
1606         if (aSep.isEmpty()) aSep = " ";
1607       }
1608       if (!aLine.trimmed().isEmpty()) {
1609         aColTitle = aColumnTitles[j-1];
1610         if (!aColTitle.trimmed().isEmpty())
1611           aLine = aLine + "  #TITLE: " + aColTitle;
1612         aLine += "\n";
1613         aFile.write(aLine.toLatin1());
1614       }
1615     }
1616   } //end of else
1617
1618   aFile.close();
1619   return true;
1620 }
1621
1622 bool VISU::ExportTableToFile(SALOMEDS::SObject_ptr theTable, const char* theFileName)
1623 {
1624   //Find table
1625   SALOMEDS::GenericAttribute_var anAttr ;
1626   if (theTable->FindAttribute(anAttr, "AttributeTableOfReal")) {
1627     SALOMEDS::AttributeTableOfReal_var aTabAttr =
1628       SALOMEDS::AttributeTableOfReal ::_narrow(anAttr);
1629     return ExportTableToFile ( aTabAttr , theFileName);
1630   }
1631   else if (theTable->FindAttribute(anAttr, "AttributeTableOfInteger")) {
1632     SALOMEDS::AttributeTableOfInteger_var aTabAttr =
1633       SALOMEDS::AttributeTableOfInteger ::_narrow(anAttr);
1634     return ExportTableToFile ( aTabAttr , theFileName);
1635   }
1636   return false;
1637 }