Salome HOME
Update of CheckDone
[modules/smesh.git] / src / SMESHGUI / SMESHGUI_Measurements.cxx
1 // Copyright (C) 2007-2024  CEA, EDF, 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, or (at your option) any later version.
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 //  File   : SMESHGUI_Measurements.cxx
23 //  Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
24
25 #include "SMESHGUI_Measurements.h"
26
27 #include "SMESH_Actor.h"
28 #include "SMESHGUI.h"
29 #include "SMESHGUI_IdValidator.h"
30 #include "SMESHGUI_Utils.h"
31 #include "SMESHGUI_MeshEditPreview.h"
32 #include "SMESHGUI_VTKUtils.h"
33 #include <SMESH_TypeFilter.hxx>
34 #include <SMESH_MeshAlgos.hxx>
35 #include <SMESH_LogicalFilter.hxx>
36 #include <SMDS_Mesh.hxx>
37 #include <SMDS_MeshNode.hxx>
38
39 #include <LightApp_SelectionMgr.h>
40 #include <SUIT_OverrideCursor.h>
41 #include <SUIT_ResourceMgr.h>
42 #include <SVTK_ViewWindow.h>
43 #include <SALOME_ListIO.hxx>
44
45 #include <QButtonGroup>
46 #include <QGridLayout>
47 #include <QGroupBox>
48 #include <QHBoxLayout>
49 #include <QKeyEvent>
50 #include <QLabel>
51 #include <QLineEdit>
52 #include <QPushButton>
53 #include <QRadioButton>
54 #include <QTabWidget>
55 #include <QVBoxLayout>
56
57 #include <vtkPoints.h>
58 #include <vtkUnstructuredGrid.h>
59 #include <vtkIdList.h>
60 #include <vtkCellArray.h>
61 #include <vtkUnsignedCharArray.h>
62 #include <vtkDataSetMapper.h>
63 //#include <VTKViewer_CellLocationsArray.h>
64 #include <vtkProperty.h>
65
66 #include <ElCLib.hxx>
67
68 #include <SALOMEconfig.h>
69 #include CORBA_SERVER_HEADER(SMESH_MeshEditor)
70 #include CORBA_SERVER_HEADER(SMESH_Measurements)
71
72 const int SPACING = 6;            // layout spacing
73 const int MARGIN  = 9;            // layout margin
74 const int MAX_NB_FOR_EDITOR = 40; // max nb of items in the ID list editor field
75
76 // Uncomment as soon as elements are supported by Min Distance operation
77 //#define MINDIST_ENABLE_ELEMENT
78
79 // Uncomment as soon as objects are supported by Min Distance operation
80 //#define MINDIST_ENABLE_OBJECT
81
82 /*!
83   \class SMESHGUI_MinDistance
84   \brief Minimum distance measurement widget.
85   
86   Widget to calculate minimum distance between two objects.
87 */
88
89 /*!
90   \brief Constructor.
91   \param parent parent widget
92 */
93 SMESHGUI_MinDistance::SMESHGUI_MinDistance( QWidget* parent )
94 : QWidget( parent ), myCurrentTgt( FirstTgt ), myFirstActor( 0 ), mySecondActor( 0 ), myPreview( 0 )
95 {
96   QGroupBox*    aFirstTgtGrp = new QGroupBox( tr( "FIRST_TARGET" ), this );
97   QRadioButton* aFNode       = new QRadioButton( tr( "NODE" ),    aFirstTgtGrp );
98   QRadioButton* aFElem       = new QRadioButton( tr( "ELEMENT" ), aFirstTgtGrp );
99   QRadioButton* aFObject     = new QRadioButton( tr( "OBJECT" ),  aFirstTgtGrp );
100   myFirstTgt                 = new QLineEdit( aFirstTgtGrp );
101
102   QGridLayout* fl = new QGridLayout( aFirstTgtGrp );
103   fl->setMargin( MARGIN );
104   fl->setSpacing( SPACING );
105   fl->addWidget( aFNode,     0, 0 );
106   fl->addWidget( aFElem,     0, 1 );
107   fl->addWidget( aFObject,   0, 2 );
108   fl->addWidget( myFirstTgt, 1, 0, 1, 3 );
109
110   myFirst = new QButtonGroup( this );
111   myFirst->addButton( aFNode,   NodeTgt );
112   myFirst->addButton( aFElem,   ElementTgt );
113   myFirst->addButton( aFObject, ObjectTgt );
114
115   QGroupBox*    aSecondTgtGrp = new QGroupBox( tr( "SECOND_TARGET" ), this );
116   QRadioButton* aSOrigin      = new QRadioButton( tr( "ORIGIN" ),  aSecondTgtGrp );
117   QRadioButton* aSNode        = new QRadioButton( tr( "NODE" ),    aSecondTgtGrp );
118   QRadioButton* aSElem        = new QRadioButton( tr( "ELEMENT" ), aSecondTgtGrp );
119   QRadioButton* aSObject      = new QRadioButton( tr( "OBJECT" ),  aSecondTgtGrp );
120   mySecondTgt                 = new QLineEdit( aSecondTgtGrp );
121
122   QGridLayout* sl = new QGridLayout( aSecondTgtGrp );
123   sl->setMargin( MARGIN );
124   sl->setSpacing( SPACING );
125   sl->addWidget( aSOrigin,   0, 0 );
126   sl->addWidget( aSNode,     0, 1 );
127   sl->addWidget( aSElem,     0, 2 );
128   sl->addWidget( aSObject,   0, 3 );
129   sl->addWidget( mySecondTgt, 1, 0, 1, 4 );
130
131   mySecond = new QButtonGroup( this );
132   mySecond->addButton( aSOrigin, OriginTgt );
133   mySecond->addButton( aSNode,   NodeTgt );
134   mySecond->addButton( aSElem,   ElementTgt );
135   mySecond->addButton( aSObject, ObjectTgt );
136
137   QPushButton* aCompute = new QPushButton( tr( "COMPUTE" ), this );
138   
139   QGroupBox* aResults = new QGroupBox( tr( "RESULT" ), this );
140   QLabel* aDxLab   = new QLabel( "dX", aResults );
141   myDX             = new QLineEdit( aResults );
142   QLabel* aDyLab   = new QLabel( "dY", aResults );
143   myDY             = new QLineEdit( aResults );
144   QLabel* aDzLab   = new QLabel( "dZ", aResults );
145   myDZ             = new QLineEdit( aResults );
146   QLabel* aDistLab = new QLabel( tr( "DISTANCE" ), aResults );
147   myDistance       = new QLineEdit( aResults );
148
149   QGridLayout* rl = new QGridLayout( aResults );
150   rl->setMargin( MARGIN );
151   rl->setSpacing( SPACING );
152   rl->addWidget( aDxLab,     0, 0 );
153   rl->addWidget( myDX,       0, 1 );
154   rl->addWidget( aDyLab,     1, 0 );
155   rl->addWidget( myDY,       1, 1 );
156   rl->addWidget( aDzLab,     2, 0 );
157   rl->addWidget( myDZ,       2, 1 );
158   rl->addWidget( aDistLab,   0, 2 );
159   rl->addWidget( myDistance, 0, 3 );
160
161   QGridLayout* l = new QGridLayout( this );
162   l->setMargin( MARGIN );
163   l->setSpacing( SPACING );
164
165   l->addWidget( aFirstTgtGrp,  0, 0, 1, 2 );
166   l->addWidget( aSecondTgtGrp, 1, 0, 1, 2 );
167   l->addWidget( aCompute,      2, 0 );
168   l->addWidget( aResults,      3, 0, 1, 2 );
169   l->setColumnStretch( 1, 5 );
170   l->setRowStretch( 4, 5 );
171
172   aFNode->setChecked( true );
173   aSOrigin->setChecked( true );
174 #ifndef MINDIST_ENABLE_ELEMENT
175   aFElem->setEnabled( false );   // NOT AVAILABLE YET
176   //aSElem->setEnabled( false );   // NOT AVAILABLE YET
177 #endif
178 #ifndef MINDIST_ENABLE_OBJECT
179   aFObject->setEnabled( false ); // NOT AVAILABLE YET
180   //aSObject->setEnabled( false ); // NOT AVAILABLE YET
181 #endif
182   myDX->setReadOnly( true );
183   myDY->setReadOnly( true );
184   myDZ->setReadOnly( true );
185   myDistance->setReadOnly( true );
186
187   myValidator = new SMESHGUI_IdValidator( this, 1 );
188
189   myFirstTgt->installEventFilter( this );
190   mySecondTgt->installEventFilter( this );
191
192   connect( myFirst,     SIGNAL( buttonClicked( int ) ),  this, SLOT( firstChanged() ) );
193   connect( mySecond,    SIGNAL( buttonClicked( int ) ),  this, SLOT( secondChanged() ) );
194   connect( aCompute,    SIGNAL( clicked() ),             this, SLOT( compute() ) );
195   connect( myFirstTgt,  SIGNAL( textEdited( QString ) ), this, SLOT( firstEdited() ) );
196   connect( mySecondTgt, SIGNAL( textEdited( QString ) ), this, SLOT( secondEdited() ) );
197
198   QList<SUIT_SelectionFilter*> filters;
199   filters.append( new SMESH_TypeFilter( SMESH::MESHorSUBMESH ) );
200   filters.append( new SMESH_TypeFilter( SMESH::GROUP ) );
201   myFilter = new SMESH_LogicalFilter( filters, SMESH_LogicalFilter::LO_OR );
202
203   mySecondTgt->setEnabled( mySecond->checkedId() != OriginTgt );
204   clear();
205
206   //setTarget( FirstTgt );
207   selectionChanged();
208 }
209
210 /*!
211   \brief Destructor
212 */
213 SMESHGUI_MinDistance::~SMESHGUI_MinDistance()
214 {
215   erasePreview();
216   if ( myPreview )
217     myPreview->Delete();
218 }
219
220 /*!
221   \brief Event filter
222   \param o object
223   \param o event
224   \return \c true if event is filtered or \c false otherwise
225 */
226 bool SMESHGUI_MinDistance::eventFilter( QObject* o, QEvent* e )
227 {
228   if ( e->type() == QEvent::FocusIn ) {
229     if ( o == myFirstTgt )
230       setTarget( FirstTgt );
231     else if ( o == mySecondTgt )
232       setTarget( SecondTgt );
233   }
234   return QWidget::eventFilter( o, e );
235 }
236
237 /*!
238   \brief Setup selection mode depending on the current widget state
239 */
240 void SMESHGUI_MinDistance::updateSelection()
241 {
242   LightApp_SelectionMgr* selMgr = SMESHGUI::selectionMgr();
243
244   disconnect( selMgr, 0, this, 0 );
245   selMgr->clearFilters();
246   
247   bool nodeMode = ( myCurrentTgt == FirstTgt  && myFirst->checkedId() == NodeTgt ) ||
248                   ( myCurrentTgt == SecondTgt && mySecond->checkedId() == NodeTgt );
249   bool elemMode = ( myCurrentTgt == FirstTgt  && myFirst->checkedId() == ElementTgt ) ||
250                   ( myCurrentTgt == SecondTgt && mySecond->checkedId() == ElementTgt );
251   bool objMode  = ( myCurrentTgt == FirstTgt  && myFirst->checkedId() == ObjectTgt ) ||
252                   ( myCurrentTgt == SecondTgt && mySecond->checkedId() == ObjectTgt ) ||
253                   ( myCurrentTgt == NoTgt );
254
255   if ( nodeMode ) {
256     SMESH::SetPointRepresentation( true );
257     if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() )
258       aViewWindow->SetSelectionMode( NodeSelection );
259   }
260   else if ( elemMode ) {
261     SMESH::SetPointRepresentation( false );
262     if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() )
263       aViewWindow->SetSelectionMode( CellSelection );
264   }
265   else if ( objMode ) {
266     SMESH::SetPointRepresentation( false );
267     if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() )
268       aViewWindow->SetSelectionMode( ActorSelection );
269     selMgr->installFilter( myFilter );
270   }
271
272   connect( selMgr, SIGNAL( currentSelectionChanged() ), this, SLOT( selectionChanged() ) );
273
274   if ( myCurrentTgt == FirstTgt )
275     firstEdited();
276   else if ( myCurrentTgt == SecondTgt )
277     secondEdited();
278
279   //selectionChanged();
280 }
281
282 /*!
283   \brief Deactivate widget
284 */
285 void SMESHGUI_MinDistance::deactivate()
286 {
287   disconnect( SMESHGUI::selectionMgr(), 0, this, 0 );
288 }
289
290 /*!
291   \brief Set current target for selection
292   \param target new target ID
293 */
294 void SMESHGUI_MinDistance::setTarget( int target )
295 {
296   if ( myCurrentTgt != target ) {
297     myCurrentTgt = target;
298     updateSelection();
299   }
300 }
301
302 /*!
303   \brief Erase preview actor
304 */
305 void SMESHGUI_MinDistance::erasePreview()
306 {
307   SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow();
308   if ( aViewWindow && myPreview ) {
309     aViewWindow->RemoveActor( myPreview );
310     aViewWindow->Repaint();
311   }
312 }
313
314 /*!
315   \brief Display preview actor
316 */
317 void SMESHGUI_MinDistance::displayPreview()
318 {
319   SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow();
320   if ( aViewWindow && myPreview ) {
321     aViewWindow->AddActor( myPreview );
322     aViewWindow->Repaint();
323   }
324 }
325
326 /*!
327   \brief Create preview actor
328   \param x1 X coordinate of first point
329   \param y1 X coordinate of first point
330   \param z1 Y coordinate of first point
331   \param x2 Y coordinate of second point
332   \param y2 Z coordinate of second point
333   \param z2 Z coordinate of second point
334 */
335 void SMESHGUI_MinDistance::createPreview( double x1, double y1, double z1, double x2, double y2, double z2 )
336 {
337   if ( myPreview )
338     myPreview->Delete();
339
340   vtkUnstructuredGrid* aGrid = vtkUnstructuredGrid::New();
341   // create points
342   vtkPoints* aPoints = vtkPoints::New();
343   aPoints->SetNumberOfPoints( 2 );
344   aPoints->SetPoint( 0, x1, y1, z1 );
345   aPoints->SetPoint( 1, x2, y2, z2 );
346   aGrid->SetPoints( aPoints );
347   aPoints->Delete();
348   // create cells
349   vtkIdList* anIdList = vtkIdList::New();
350   anIdList->SetNumberOfIds( 2 );
351   vtkCellArray* aCells = vtkCellArray::New();
352   aCells->Allocate( 2, 0);
353   vtkUnsignedCharArray* aCellTypesArray = vtkUnsignedCharArray::New();
354   aCellTypesArray->SetNumberOfComponents( 1 );
355   aCellTypesArray->Allocate( 1 );
356   anIdList->SetId( 0, 0 ); anIdList->SetId( 1, 1 );
357   aCells->InsertNextCell( anIdList );
358   aCellTypesArray->InsertNextValue( VTK_LINE );
359   anIdList->Delete();
360   vtkIdTypeArray* aCellLocationsArray = vtkIdTypeArray::New();
361   aCellLocationsArray->SetNumberOfComponents( 1 );
362   aCellLocationsArray->SetNumberOfTuples( 1 );
363   aCells->InitTraversal();
364   vtkIdType const *pts(nullptr);
365   for( vtkIdType idType = 0, npts; aCells->GetNextCell( npts, pts ); idType++ )
366     aCellLocationsArray->SetValue( idType, aCells->GetTraversalLocation( npts ) );
367   aGrid->SetCells( aCellTypesArray, aCellLocationsArray, aCells );
368   aCellLocationsArray->Delete();
369   aCellTypesArray->Delete();
370   aCells->Delete();
371   // create actor
372   vtkDataSetMapper* aMapper = vtkDataSetMapper::New();
373   aMapper->SetInputData( aGrid );
374   aGrid->Delete();
375   myPreview = SALOME_Actor::New();
376   myPreview->PickableOff();
377   myPreview->SetMapper( aMapper );
378   myPreview->SetResolveCoincidentTopology(true);
379   aMapper->Delete();
380   vtkProperty* aProp = vtkProperty::New();
381   aProp->SetRepresentationToWireframe();
382   aProp->SetColor( 250, 0, 250 );
383   aProp->SetPointSize( 5 );
384   aProp->SetLineWidth( 3 );
385   myPreview->SetProperty( aProp );
386   aProp->Delete();
387 }
388
389 /*!
390   \brief Called when selection is changed
391 */
392 void SMESHGUI_MinDistance::selectionChanged()
393 {
394   SUIT_OverrideCursor wc;
395
396   SALOME_ListIO selected;
397   SMESHGUI::selectionMgr()->selectedObjects( selected );
398
399   if ( selected.Extent() == 1 ) {
400     Handle(SALOME_InteractiveObject) IO = selected.First();
401     SMESH::SMESH_IDSource_var obj = SMESH::IObjectToInterface<SMESH::SMESH_IDSource>( IO );
402     if ( !CORBA::is_nil( obj ) ) {
403       if ( myCurrentTgt == FirstTgt ) {
404         myFirstSrc = obj;
405         myFirstActor = SMESH::FindActorByEntry( IO->getEntry() );
406         if ( myFirst->checkedId() == ObjectTgt ) {
407           QString aName;
408           SMESH::GetNameOfSelectedIObjects( SMESHGUI::selectionMgr(), aName );
409           myFirstTgt->setText( aName );
410         }
411         else {
412           SVTK_Selector* selector = SMESH::GetViewWindow()->GetSelector();
413           QString ID;
414           int nb = 0;
415           if ( myFirstActor && selector ) {
416             nb = myFirst->checkedId() == NodeTgt ? 
417               SMESH::GetNameOfSelectedElements( selector, IO, ID ) :
418               SMESH::GetNameOfSelectedNodes( selector, IO, ID );
419           }
420           if ( nb == 1 )
421             myFirstTgt->setText( ID.trimmed() );
422           else
423             myFirstTgt->clear();
424         }
425       }
426       else if ( myCurrentTgt == SecondTgt ) {
427         mySecondSrc = obj;
428         mySecondActor = SMESH::FindActorByEntry( IO->getEntry() );
429         if ( mySecond->checkedId() == ObjectTgt ) {
430           QString aName;
431           SMESH::GetNameOfSelectedIObjects( SMESHGUI::selectionMgr(), aName );
432           mySecondTgt->setText( aName );
433         }
434         else {
435           SVTK_Selector* selector = SMESH::GetViewWindow()->GetSelector();
436           QString ID;
437           int nb = 0;
438           if ( mySecondActor && selector ) {
439             nb = mySecond->checkedId() == NodeTgt ? 
440               SMESH::GetNameOfSelectedElements( selector, IO, ID ) :
441               SMESH::GetNameOfSelectedNodes( selector, IO, ID );
442           }
443           if ( nb == 1 )
444             mySecondTgt->setText( ID.trimmed() );
445           else
446             mySecondTgt->clear();
447         }
448       }
449     }
450   }
451   clear();
452 }
453
454 /*!
455   \brief Called when first target mode is changed by the user
456 */
457 void SMESHGUI_MinDistance::firstChanged()
458 {
459   myFirstSrc = SMESH::SMESH_IDSource::_nil();
460   myFirstTgt->clear();
461   myFirstTgt->setReadOnly( myFirst->checkedId() == ObjectTgt );
462   myFirstTgt->setValidator( myFirst->checkedId() == ObjectTgt ? 0 : myValidator );
463   setTarget( FirstTgt );
464   updateSelection();
465   clear();
466 }
467
468 /*!
469   \brief Called when second target mode is changed by the user
470 */
471 void SMESHGUI_MinDistance::secondChanged()
472 {
473   mySecondSrc = SMESH::SMESH_IDSource::_nil();
474   mySecondTgt->setEnabled( mySecond->checkedId() != OriginTgt );
475   mySecondTgt->setReadOnly( mySecond->checkedId() == ObjectTgt );
476   mySecondTgt->setValidator( mySecond->checkedId() == ObjectTgt ? 0 : myValidator );
477   mySecondTgt->clear();
478   setTarget( mySecond->checkedId() != OriginTgt ? SecondTgt : NoTgt );
479   updateSelection();
480   clear();
481 }
482
483 /*!
484   \brief Called when first target is edited by the user
485 */
486 void SMESHGUI_MinDistance::firstEdited()
487 {
488   setTarget( FirstTgt );
489   if ( sender() == myFirstTgt )
490     clear();
491   SVTK_Selector* selector = SMESH::GetViewWindow()->GetSelector();
492   if ( myFirstActor && selector ) {
493     Handle(SALOME_InteractiveObject) IO = myFirstActor->getIO();
494     if ( myFirst->checkedId() == NodeTgt || myFirst->checkedId() == ElementTgt ) {
495       SVTK_TVtkIDsMap ID;
496       ID.Add( myFirstTgt->text().toLong() );
497       selector->AddOrRemoveIndex( IO, ID, false );
498     }
499     if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() )
500       aViewWindow->highlight( IO, true, true );
501   }
502 }
503
504 /*!
505   \brief Called when second target is edited by the user
506 */
507 void SMESHGUI_MinDistance::secondEdited()
508 {
509   setTarget( SecondTgt );
510   if ( sender() == mySecondTgt )
511     clear();
512   QString text = mySecondTgt->text();
513   if ( !mySecondActor )
514   {
515     selectionChanged();
516     mySecondTgt->setText( text );
517   }
518   SVTK_Selector* selector = SMESH::GetViewWindow()->GetSelector();
519   if ( mySecondActor && selector ) {
520     Handle(SALOME_InteractiveObject) IO = mySecondActor->getIO();
521     if ( mySecond->checkedId() == NodeTgt || mySecond->checkedId() == ElementTgt ) {
522       if ( !text.isEmpty() ) {
523         SVTK_TVtkIDsMap ID;
524         ID.Add( text.toLong() );
525         selector->AddOrRemoveIndex( IO, ID, false );
526       }
527     }
528     if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() )
529       aViewWindow->highlight( IO, true, true );
530   }
531 }
532
533 /*!
534   \brief Compute the minimum distance between targets
535 */
536 void SMESHGUI_MinDistance::compute()
537 {
538   SUIT_OverrideCursor wc;
539   SMESH::IDSource_wrap s1;
540   SMESH::IDSource_wrap s2;
541   bool isOrigin = mySecond->checkedId() == OriginTgt;
542
543   // process first target
544   if ( !CORBA::is_nil( myFirstSrc ) ) {
545     if ( myFirst->checkedId() == NodeTgt || myFirst->checkedId() == ElementTgt ) {
546       SMESH::SMESH_Mesh_var m = myFirstSrc->GetMesh();
547       long id = myFirstTgt->text().toLong();
548       if ( !CORBA::is_nil( m ) && id ) {
549         SMESH::smIdType_array_var ids = new SMESH::smIdType_array();
550         ids->length( 1 );
551         ids[0] = id;
552         SMESH::SMESH_MeshEditor_var me = m->GetMeshEditor();
553         s1 = me->MakeIDSource( ids.in(), myFirst->checkedId() == NodeTgt ? SMESH::NODE : SMESH::FACE );
554       }
555     }
556     else {
557       s1 = myFirstSrc;
558       s1->Register();
559     }
560   }
561
562   // process second target
563   if ( !CORBA::is_nil( mySecondSrc ) ) {
564     if ( mySecond->checkedId() == NodeTgt || mySecond->checkedId() == ElementTgt ) {
565       SMESH::SMESH_Mesh_var m = mySecondSrc->GetMesh();
566       long id = mySecondTgt->text().toLong();
567       if ( !CORBA::is_nil( m ) && id ) {
568         SMESH::smIdType_array_var ids = new SMESH::smIdType_array();
569         ids->length( 1 );
570         ids[0] = id;
571         SMESH::SMESH_MeshEditor_var me = m->GetMeshEditor();
572         s2 = me->MakeIDSource( ids.in(), mySecond->checkedId() == NodeTgt ? SMESH::NODE : SMESH::FACE );
573       }
574     }
575     else {
576       s2 = mySecondSrc;
577       s2->Register();
578     }
579   }
580
581   if ( !CORBA::is_nil( s1 ) && ( !CORBA::is_nil( s2 ) || isOrigin ) ) {
582     // compute min distance
583     int precision = SMESHGUI::resourceMgr()->integerValue( "SMESH", "length_precision", 6 );
584     SMESH::Measurements_var measure = SMESHGUI::GetSMESHGen()->CreateMeasurements();
585     SMESH::Measure result = measure->MinDistance( s1.in(), s2.in() );
586     measure->UnRegister();
587     myDX->setText( QString::number( result.minX, precision > 0 ? 'f' : 'g', qAbs( precision ) ) );
588     myDY->setText( QString::number( result.minY, precision > 0 ? 'f' : 'g', qAbs( precision ) ) );
589     myDZ->setText( QString::number( result.minZ, precision > 0 ? 'f' : 'g', qAbs( precision ) ) );
590     myDistance->setText( QString::number( result.value, precision > 0 ? 'f' : 'g', qAbs( precision ) ) );
591     // update preview actor
592     erasePreview();
593     double x1, y1, z1, x2, y2, z2;
594     SMESH::double_array_var coord = s1->GetMesh()->GetNodeXYZ( result.node1 );
595     x1 = coord[0]; y1 = coord[1]; z1 = coord[2];
596     if ( isOrigin ) {
597       x2 = y2 = z2 = 0.;
598     }
599     else if ( mySecond->checkedId() == NodeTgt ) {
600       coord = s2->GetMesh()->GetNodeXYZ( result.node2 );
601       x2 = coord[0]; y2 = coord[1]; z2 = coord[2];
602     }
603     else
604     {
605       x2 = result.maxX; y2 = result.maxY; z2 = result.maxZ; 
606     }
607     createPreview( x1, y1, z1, x2, y2, z2 );
608     displayPreview();
609   }
610   else {
611     clear();
612   }
613 }
614
615 /*!
616   \brief Reset the widget to the initial state (nullify result fields)
617 */
618 void SMESHGUI_MinDistance::clear()
619 {
620   myDX->clear();
621   myDY->clear();
622   myDZ->clear();
623   myDistance->clear();
624   erasePreview();
625 }
626
627 /*!
628   \class SMESHGUI_BoundingBox
629   \brief Bounding box measurement widget.
630   
631   Widget to calculate bounding box of the selected object(s).
632 */
633
634 /*!
635   \brief Constructor.
636   \param parent parent widget
637 */
638 SMESHGUI_BoundingBox::SMESHGUI_BoundingBox( QWidget* parent )
639 : QWidget( parent ), myActor( 0 ), myPreview( 0 )
640 {
641   QGroupBox* aSourceGrp = new QGroupBox( tr( "SOURCE" ), this );
642   QRadioButton* aObjects  = new QRadioButton( tr( "OBJECTS" ),  aSourceGrp );
643   QRadioButton* aNodes    = new QRadioButton( tr( "NODES" ),    aSourceGrp );
644   QRadioButton* aElements = new QRadioButton( tr( "ELEMENTS" ), aSourceGrp );
645   mySource = new QLineEdit( aSourceGrp );
646   
647   QGridLayout* fl = new QGridLayout( aSourceGrp );
648   fl->setMargin( MARGIN );
649   fl->setSpacing( SPACING );
650   fl->addWidget( aObjects,   0, 0 );
651   fl->addWidget( aNodes,     0, 1 );
652   fl->addWidget( aElements,  0, 2 );
653   fl->addWidget( mySource,   1, 0, 1, 3 );
654   
655   mySourceMode = new QButtonGroup( this );
656   mySourceMode->addButton( aObjects,  ObjectsSrc );
657   mySourceMode->addButton( aNodes,    NodesSrc );
658   mySourceMode->addButton( aElements, ElementsSrc );
659
660   QPushButton* aCompute = new QPushButton( tr( "COMPUTE" ), this );
661
662   QGroupBox* aResults = new QGroupBox( tr( "RESULT" ), this );
663   QLabel* aXminLab = new QLabel( "Xmin", aResults );
664   myXmin           = new QLineEdit( aResults );
665   QLabel* aXmaxLab = new QLabel( "Xmax", aResults );
666   myXmax           = new QLineEdit( aResults );
667   QLabel* aDxLab   = new QLabel( "dX", aResults );
668   myDX             = new QLineEdit( aResults );
669   QLabel* aYminLab = new QLabel( "Ymin", aResults );
670   myYmin           = new QLineEdit( aResults );
671   QLabel* aYmaxLab = new QLabel( "Ymax", aResults );
672   myYmax           = new QLineEdit( aResults );
673   QLabel* aDyLab   = new QLabel( "dY", aResults );
674   myDY             = new QLineEdit( aResults );
675   QLabel* aZminLab = new QLabel( "Zmin", aResults );
676   myZmin           = new QLineEdit( aResults );
677   QLabel* aZmaxLab = new QLabel( "Zmax", aResults );
678   myZmax           = new QLineEdit( aResults );
679   QLabel* aDzLab   = new QLabel( "dZ", aResults );
680   myDZ             = new QLineEdit( aResults );
681
682   QGridLayout* rl  = new QGridLayout( aResults );
683   rl->setMargin( MARGIN );
684   rl->setSpacing( SPACING );
685   rl->addWidget( aXminLab,   0, 0 );
686   rl->addWidget( myXmin,     0, 1 );
687   rl->addWidget( aXmaxLab,   0, 2 );
688   rl->addWidget( myXmax,     0, 3 );
689   rl->addWidget( aDxLab,     0, 4 );
690   rl->addWidget( myDX,       0, 5 );
691   rl->addWidget( aYminLab,   1, 0 );
692   rl->addWidget( myYmin,     1, 1 );
693   rl->addWidget( aYmaxLab,   1, 2 );
694   rl->addWidget( myYmax,     1, 3 );
695   rl->addWidget( aDyLab,     1, 4 );
696   rl->addWidget( myDY,       1, 5 );
697   rl->addWidget( aZminLab,   2, 0 );
698   rl->addWidget( myZmin,     2, 1 );
699   rl->addWidget( aZmaxLab,   2, 2 );
700   rl->addWidget( myZmax,     2, 3 );
701   rl->addWidget( aDzLab,     2, 4 );
702   rl->addWidget( myDZ,       2, 5 );
703
704   QGridLayout* l = new QGridLayout( this );
705   l->setMargin( MARGIN );
706   l->setSpacing( SPACING );
707
708   l->addWidget( aSourceGrp, 0, 0, 1, 2 );
709   l->addWidget( aCompute,   1, 0 );
710   l->addWidget( aResults,   2, 0, 1, 2 );
711   l->setColumnStretch( 1, 5 );
712   l->setRowStretch( 3, 5 );
713
714   aObjects->setChecked( true );
715   myXmin->setReadOnly( true );
716   myXmax->setReadOnly( true );
717   myDX->setReadOnly( true );
718   myYmin->setReadOnly( true );
719   myYmax->setReadOnly( true );
720   myDY->setReadOnly( true );
721   myZmin->setReadOnly( true );
722   myZmax->setReadOnly( true );
723   myDZ->setReadOnly( true );
724
725   myValidator = new SMESHGUI_IdValidator( this );
726
727   connect( mySourceMode, SIGNAL( buttonClicked( int ) ),  this, SLOT( sourceChanged() ) );
728   connect( aCompute,     SIGNAL( clicked() ),             this, SLOT( compute() ) );
729   connect( mySource,     SIGNAL( textEdited( QString ) ), this, SLOT( sourceEdited() ) );
730
731   QList<SUIT_SelectionFilter*> filters;
732   filters.append( new SMESH_TypeFilter( SMESH::MESHorSUBMESH ) );
733   filters.append( new SMESH_TypeFilter( SMESH::GROUP ) );
734   myFilter = new SMESH_LogicalFilter( filters, SMESH_LogicalFilter::LO_OR );
735
736   clear();
737 }
738
739 /*!
740   \brief Destructor
741 */
742 SMESHGUI_BoundingBox::~SMESHGUI_BoundingBox()
743 {
744   erasePreview();
745   if ( myPreview )
746     myPreview->Delete();
747 }
748
749 /*!
750   \brief Setup selection mode depending on the current widget state
751 */
752 void SMESHGUI_BoundingBox::updateSelection()
753 {
754   LightApp_SelectionMgr* selMgr = SMESHGUI::selectionMgr();
755
756   disconnect( selMgr, 0, this, 0 );
757   selMgr->clearFilters();
758   
759   bool nodeMode = mySourceMode->checkedId() == NodesSrc;
760   bool elemMode = mySourceMode->checkedId() == ElementsSrc;
761   bool objMode  = mySourceMode->checkedId() == ObjectsSrc;
762
763   if ( nodeMode ) {
764     SMESH::SetPointRepresentation( true );
765     if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() )
766       aViewWindow->SetSelectionMode( NodeSelection );
767   }
768   else if ( elemMode ) {
769     SMESH::SetPointRepresentation( false );
770     if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() )
771       aViewWindow->SetSelectionMode( CellSelection );
772   }
773   else if ( objMode ) {
774     SMESH::SetPointRepresentation( false );
775     if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() )
776       aViewWindow->SetSelectionMode( ActorSelection );
777     selMgr->installFilter( myFilter );
778   }
779
780   connect( selMgr, SIGNAL( currentSelectionChanged() ), this, SLOT( selectionChanged() ) );
781
782   sourceEdited();
783
784   if ( mySource->text().isEmpty() )
785     selectionChanged();
786 }
787
788 /*!
789   \brief Deactivate widget
790 */
791 void SMESHGUI_BoundingBox::deactivate()
792 {
793   disconnect( SMESHGUI::selectionMgr(), 0, this, 0 );
794 }
795
796 /*!
797   \brief Erase preview actor
798 */
799 void SMESHGUI_BoundingBox::erasePreview()
800 {
801   SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow();
802   if ( aViewWindow && myPreview ) {
803     aViewWindow->RemoveActor( myPreview );
804     aViewWindow->Repaint();
805   }
806 }
807
808 /*!
809   \brief Display preview actor
810 */
811 void SMESHGUI_BoundingBox::displayPreview()
812 {
813   SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow();
814   if ( aViewWindow && myPreview ) {
815     aViewWindow->AddActor( myPreview );
816     aViewWindow->Repaint();
817   }
818 }
819
820 /*!
821   \brief Create preview actor
822   \param minX min X coordinate of bounding box
823   \param maxX max X coordinate of bounding box
824   \param minY min Y coordinate of bounding box
825   \param maxY max Y coordinate of bounding box
826   \param minZ min Z coordinate of bounding box
827   \param maxZ max Z coordinate of bounding box
828 */
829 void SMESHGUI_BoundingBox::createPreview( double minX, double maxX, double minY, double maxY, double minZ, double maxZ )
830 {
831   if ( myPreview )
832     myPreview->Delete();
833
834   vtkUnstructuredGrid* aGrid = vtkUnstructuredGrid::New();
835   // create points
836   vtkPoints* aPoints = vtkPoints::New();
837   aPoints->SetNumberOfPoints( 8 );
838   aPoints->SetPoint( 0, minX, minY, minZ );
839   aPoints->SetPoint( 1, maxX, minY, minZ );
840   aPoints->SetPoint( 2, minX, maxY, minZ );
841   aPoints->SetPoint( 3, maxX, maxY, minZ );
842   aPoints->SetPoint( 4, minX, minY, maxZ );
843   aPoints->SetPoint( 5, maxX, minY, maxZ );
844   aPoints->SetPoint( 6, minX, maxY, maxZ );
845   aPoints->SetPoint( 7, maxX, maxY, maxZ );
846   aGrid->SetPoints( aPoints );
847   aPoints->Delete();
848   // create cells
849   // connectivity: 0-1 0-4 0-2 1-5 1-3 2-6 2-3 3-7 4-6 4-5 5-7 6-7
850   vtkIdList* anIdList = vtkIdList::New();
851   anIdList->SetNumberOfIds( 2 );
852   vtkCellArray* aCells = vtkCellArray::New();
853   aCells->Allocate( 2*12, 0);
854   vtkUnsignedCharArray* aCellTypesArray = vtkUnsignedCharArray::New();
855   aCellTypesArray->SetNumberOfComponents( 1 );
856   aCellTypesArray->Allocate( 12 );
857   anIdList->SetId( 0, 0 ); anIdList->SetId( 1, 1 );
858   aCells->InsertNextCell( anIdList );
859   aCellTypesArray->InsertNextValue( VTK_LINE );
860   anIdList->SetId( 0, 0 ); anIdList->SetId( 1, 4 );
861   aCells->InsertNextCell( anIdList );
862   aCellTypesArray->InsertNextValue( VTK_LINE );
863   anIdList->SetId( 0, 0 ); anIdList->SetId( 1, 2 );
864   aCells->InsertNextCell( anIdList );
865   aCellTypesArray->InsertNextValue( VTK_LINE );
866   anIdList->SetId( 0, 1 ); anIdList->SetId( 1, 5 );
867   aCells->InsertNextCell( anIdList );
868   aCellTypesArray->InsertNextValue( VTK_LINE );
869   anIdList->SetId( 0, 1 ); anIdList->SetId( 1, 3 );
870   aCells->InsertNextCell( anIdList );
871   aCellTypesArray->InsertNextValue( VTK_LINE );
872   anIdList->SetId( 0, 2 ); anIdList->SetId( 1, 6 );
873   aCells->InsertNextCell( anIdList );
874   aCellTypesArray->InsertNextValue( VTK_LINE );
875   anIdList->SetId( 0, 2 ); anIdList->SetId( 1, 3 );
876   aCells->InsertNextCell( anIdList );
877   aCellTypesArray->InsertNextValue( VTK_LINE );
878   anIdList->SetId( 0, 3 ); anIdList->SetId( 1, 7 );
879   aCells->InsertNextCell( anIdList );
880   aCellTypesArray->InsertNextValue( VTK_LINE );
881   anIdList->SetId( 0, 4 ); anIdList->SetId( 1, 6 );
882   aCells->InsertNextCell( anIdList );
883   aCellTypesArray->InsertNextValue( VTK_LINE );
884   anIdList->SetId( 0, 4 ); anIdList->SetId( 1, 5 );
885   aCells->InsertNextCell( anIdList );
886   aCellTypesArray->InsertNextValue( VTK_LINE );
887   anIdList->SetId( 0, 5 ); anIdList->SetId( 1, 7 );
888   aCells->InsertNextCell( anIdList );
889   aCellTypesArray->InsertNextValue( VTK_LINE );
890   anIdList->SetId( 0, 6 ); anIdList->SetId( 1, 7 );
891   aCells->InsertNextCell( anIdList );
892   aCellTypesArray->InsertNextValue( VTK_LINE );
893   anIdList->Delete();
894   vtkIdTypeArray* aCellLocationsArray = vtkIdTypeArray::New();
895   aCellLocationsArray->SetNumberOfComponents( 1 );
896   aCellLocationsArray->SetNumberOfTuples( 12 );
897   aCells->InitTraversal();
898   vtkIdType const *pts(nullptr);
899   for( vtkIdType idType = 0, npts; aCells->GetNextCell( npts, pts ); idType++ )
900     aCellLocationsArray->SetValue( idType, aCells->GetTraversalLocation( npts ) );
901   aGrid->SetCells( aCellTypesArray, aCellLocationsArray, aCells );
902   aCellLocationsArray->Delete();
903   aCellTypesArray->Delete();
904   aCells->Delete();
905   // create actor
906   vtkDataSetMapper* aMapper = vtkDataSetMapper::New();
907   aMapper->SetInputData( aGrid );
908   aGrid->Delete();
909   myPreview = SALOME_Actor::New();
910   myPreview->PickableOff();
911   myPreview->SetMapper( aMapper );
912   aMapper->Delete();
913   vtkProperty* aProp = vtkProperty::New();
914   aProp->SetRepresentationToWireframe();
915   aProp->SetColor( 250, 0, 250 );
916   aProp->SetPointSize( 5 );
917   aProp->SetLineWidth( 3 );
918   myPreview->SetProperty( aProp );
919   aProp->Delete();
920 }
921
922 /*!
923   \brief Called when selection is changed
924 */
925 void SMESHGUI_BoundingBox::selectionChanged()
926 {
927   SUIT_OverrideCursor wc;
928
929   SALOME_ListIO selected;
930   SMESHGUI::selectionMgr()->selectedObjects( selected );
931
932   if ( selected.Extent() == 1 ) {
933     Handle(SALOME_InteractiveObject) IO = selected.First();
934     SMESH::SMESH_IDSource_var obj = SMESH::IObjectToInterface<SMESH::SMESH_IDSource>( IO );
935     if ( !CORBA::is_nil( obj ) ) {
936       mySrc.clear();
937       mySrc.append( obj );
938       myActor = SMESH::FindActorByEntry( IO->getEntry() );
939       if ( mySourceMode->checkedId() == ObjectsSrc ) {
940         QString aName;
941         SMESH::GetNameOfSelectedIObjects( SMESHGUI::selectionMgr(), aName );
942         mySource->setText( aName );
943       }
944       else {
945         SVTK_Selector* selector = SMESH::GetViewWindow()->GetSelector();
946         QString ID;
947         int nb = 0;
948         if ( myActor && selector ) {
949           nb = mySourceMode->checkedId() == NodesSrc ? 
950             SMESH::GetNameOfSelectedElements( selector, IO, ID ) :
951             SMESH::GetNameOfSelectedNodes( selector, IO, ID );
952         }
953         if ( nb > 0 ) {
954           myIDs = ID.trimmed();
955           if ( nb < MAX_NB_FOR_EDITOR ) {
956             mySource->setReadOnly( false );
957             if ( mySource->validator() != myValidator )
958               mySource->setValidator( myValidator );
959             mySource->setText( ID.trimmed() );
960           }
961           else {
962             mySource->setReadOnly( true );
963             mySource->setValidator( 0 );
964             mySource->setText( tr( "SELECTED_NB_OBJ" ).arg( nb )
965                                .arg( mySourceMode->checkedId() == NodesSrc ? tr( "NB_NODES" ) : tr( "NB_ELEMENTS") ) );
966           }
967         }
968         else {
969           myIDs = "";
970           mySource->clear();
971           mySource->setReadOnly( false );
972           mySource->setValidator( myValidator );
973         }
974       }
975     }
976   }
977   else if ( selected.Extent() > 1 ) {
978     myIDs = "";
979     SALOME_ListIteratorOfListIO It( selected );
980     mySrc.clear();
981     myActor = 0;
982     if ( mySourceMode->checkedId() == ObjectsSrc ) {
983       for( ; It.More(); It.Next()){
984         Handle(SALOME_InteractiveObject) IO = It.Value();
985         SMESH::SMESH_IDSource_var obj = SMESH::IObjectToInterface<SMESH::SMESH_IDSource>( IO );
986         if ( !CORBA::is_nil( obj ) ) {
987           mySrc.append( obj );
988         }
989       }
990       QString aName;
991       SMESH::GetNameOfSelectedIObjects( SMESHGUI::selectionMgr(), aName );
992       mySource->setText( aName );
993     }
994     else {
995       mySource->clear();
996     }
997   }
998   clear();
999 }
1000
1001 /*!
1002   \brief Called when source mode is changed by the user
1003 */
1004 void SMESHGUI_BoundingBox::sourceChanged()
1005 {
1006   myIDs = "";
1007   mySource->clear();
1008   mySource->setReadOnly( mySourceMode->checkedId() == ObjectsSrc );
1009   mySource->setValidator( mySourceMode->checkedId() == ObjectsSrc ? 0 : myValidator );
1010   updateSelection();
1011   clear();
1012 }
1013
1014 /*!
1015   \brief Called when source mode is edited by the user
1016 */
1017 void SMESHGUI_BoundingBox::sourceEdited()
1018 {
1019   if ( sender() == mySource )
1020     clear();
1021   SVTK_Selector* selector = SMESH::GetViewWindow()->GetSelector();
1022   if ( myActor && selector ) {
1023     Handle(SALOME_InteractiveObject) IO = myActor->getIO();
1024     if ( mySourceMode->checkedId() == NodesSrc || mySourceMode->checkedId() == ElementsSrc ) {
1025       SVTK_TVtkIDsMap ID;
1026       if ( !mySource->isReadOnly() )
1027         myIDs = mySource->text();
1028       QStringList ids = myIDs.split( " ", QString::SkipEmptyParts );
1029       foreach ( QString id, ids )
1030         ID.Add( id.trimmed().toLong() );
1031       selector->AddOrRemoveIndex( IO, ID, false );
1032     }
1033     if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() )
1034       aViewWindow->highlight( IO, true, true );
1035   }
1036 }
1037
1038 /*!
1039   \brief Calculate bounding box of the selected object(s)
1040 */
1041 void SMESHGUI_BoundingBox::compute()
1042 {
1043   SUIT_OverrideCursor wc;
1044   SMESH::ListOfIDSources_var srcList = new SMESH::ListOfIDSources();
1045   if ( mySourceMode->checkedId() == NodesSrc || mySourceMode->checkedId() == ElementsSrc ) {
1046     if ( mySrc.count() > 0 && !CORBA::is_nil( mySrc[0] ) ) {
1047       SMESH::SMESH_Mesh_var m = mySrc[0]->GetMesh();
1048       QStringList ids = myIDs.split( " ", QString::SkipEmptyParts );
1049       if ( !CORBA::is_nil( m ) && ids.count() > 0 ) {
1050         SMESH::smIdType_array_var ids_in = new SMESH::smIdType_array();
1051         ids_in->length( ids.count() );
1052         for( int i = 0; i < ids.count(); i++ )
1053           ids_in[i] = ids[i].trimmed().toLong();
1054         SMESH::SMESH_MeshEditor_var me = m->GetMeshEditor();
1055         SMESH::SMESH_IDSource_var s = me->MakeIDSource( ids_in.in(), mySourceMode->checkedId() == NodesSrc ? SMESH::NODE : SMESH::FACE ); 
1056         srcList->length( 1 );
1057         srcList[0] = s;
1058       }
1059     }
1060   }
1061   else {
1062     srcList->length( mySrc.count() );
1063     for( int i = 0; i < mySrc.count(); i++ ) {
1064       srcList[i] = mySrc[i];
1065       mySrc[i]->Register();
1066     }
1067   }
1068   if ( srcList->length() > 0 ) {
1069     // compute bounding box
1070     int precision = SMESHGUI::resourceMgr()->integerValue( "SMESH", "length_precision", 6 );
1071     SMESH::Measurements_var measure = SMESHGUI::GetSMESHGen()->CreateMeasurements();
1072     SMESH::Measure result = measure->BoundingBox( srcList.in() );
1073     SALOME::UnRegister( srcList );
1074     measure->UnRegister();
1075     myXmin->setText( QString::number( result.minX, precision > 0 ? 'f' : 'g', qAbs( precision ) ) );
1076     myXmax->setText( QString::number( result.maxX, precision > 0 ? 'f' : 'g', qAbs( precision ) ) );
1077     myDX->setText( QString::number( result.maxX-result.minX, precision > 0 ? 'f' : 'g', qAbs( precision ) ) );
1078     myYmin->setText( QString::number( result.minY, precision > 0 ? 'f' : 'g', qAbs( precision ) ) );
1079     myYmax->setText( QString::number( result.maxY, precision > 0 ? 'f' : 'g', qAbs( precision ) ) );
1080     myDY->setText( QString::number( result.maxY-result.minY, precision > 0 ? 'f' : 'g', qAbs( precision ) ) );
1081     myZmin->setText( QString::number( result.minZ, precision > 0 ? 'f' : 'g', qAbs( precision ) ) );
1082     myZmax->setText( QString::number( result.maxZ, precision > 0 ? 'f' : 'g', qAbs( precision ) ) );
1083     myDZ->setText( QString::number( result.maxZ-result.minZ, precision > 0 ? 'f' : 'g', qAbs( precision ) ) );
1084     // update preview actor
1085     erasePreview();
1086     createPreview( result.minX, result.maxX, result.minY, result.maxY, result.minZ, result.maxZ );
1087     displayPreview();
1088   }
1089   else {
1090     clear();
1091   }
1092 }
1093
1094 /*!
1095   \brief Reset the widget to the initial state (nullify result fields)
1096 */
1097 void SMESHGUI_BoundingBox::clear()
1098 {
1099   myXmin->clear();
1100   myXmax->clear();
1101   myDX->clear();
1102   myYmin->clear();
1103   myYmax->clear();
1104   myDY->clear();
1105   myZmin->clear();
1106   myZmax->clear();
1107   myDZ->clear();
1108   erasePreview();
1109 }
1110
1111 /*!
1112   \class SMESHGUI_BasicProperties
1113   \brief basic properties measurement widget.
1114   
1115   Widget to calculate length, area or volume for the selected object(s).
1116 */
1117
1118 /*!
1119   \brief Constructor.
1120   \param parent parent widget
1121 */
1122 SMESHGUI_BasicProperties::SMESHGUI_BasicProperties( QWidget* parent )
1123 : QWidget( parent )
1124 {
1125   // Property (length, area or volume)
1126   QGroupBox* aPropertyGrp = new QGroupBox( tr( "PROPERTY" ), this );
1127
1128   QRadioButton* aLength = new QRadioButton( tr( "LENGTH" ), aPropertyGrp );
1129   QRadioButton* anArea = new QRadioButton( tr( "AREA" ), aPropertyGrp );
1130   QRadioButton* aVolume = new QRadioButton( tr( "VOLUME" ), aPropertyGrp );
1131
1132   myMode = new QButtonGroup( this );
1133   myMode->addButton( aLength, Length );
1134   myMode->addButton( anArea, Area );
1135   myMode->addButton( aVolume, Volume );
1136
1137   QHBoxLayout* aPropertyLayout = new QHBoxLayout;
1138   aPropertyLayout->addWidget( aLength );
1139   aPropertyLayout->addWidget( anArea );
1140   aPropertyLayout->addWidget( aVolume );
1141
1142   aPropertyGrp->setLayout( aPropertyLayout );
1143
1144   // Source object
1145   QGroupBox* aSourceGrp = new QGroupBox( tr( "SOURCE_MESH_SUBMESH_GROUP" ), this );
1146
1147   mySource = new QLineEdit( aSourceGrp );
1148   mySource->setReadOnly( true );
1149     
1150   QHBoxLayout* aSourceLayout = new QHBoxLayout;
1151   aSourceLayout->addWidget( mySource );
1152   
1153   aSourceGrp->setLayout( aSourceLayout );
1154
1155   // Compute button
1156   QPushButton* aCompute = new QPushButton( tr( "COMPUTE" ), this );
1157
1158   // Result of computation (length, area or volume)
1159   myResultGrp = new QGroupBox( this );
1160
1161   myResult = new QLineEdit;
1162   myResult->setReadOnly( true );
1163
1164   QHBoxLayout* aResultLayout = new QHBoxLayout;
1165   aResultLayout->addWidget( myResult );
1166   
1167   myResultGrp->setLayout( aResultLayout );
1168
1169   // Layout
1170   QGridLayout* aMainLayout = new QGridLayout( this );
1171   aMainLayout->setMargin( MARGIN );
1172   aMainLayout->setSpacing( SPACING );
1173
1174   aMainLayout->addWidget( aPropertyGrp, 0, 0, 1, 2 );
1175   aMainLayout->addWidget( aSourceGrp, 1, 0, 1, 2 );
1176   aMainLayout->addWidget( aCompute,   2, 0 );
1177   aMainLayout->addWidget( myResultGrp, 3, 0, 1, 2 );
1178   aMainLayout->setColumnStretch( 1, 5 );
1179   aMainLayout->setRowStretch( 4, 5 );
1180
1181   // Initial state
1182   setMode( Length );
1183   
1184   // Connections
1185   connect( myMode, SIGNAL( buttonClicked( int ) ),  this, SLOT( modeChanged( int ) ) );
1186   connect( aCompute, SIGNAL( clicked() ), this, SLOT( compute() ) );
1187   
1188   // Selection filter
1189   QList<SUIT_SelectionFilter*> filters;
1190   filters.append( new SMESH_TypeFilter( SMESH::MESHorSUBMESH ) );
1191   filters.append( new SMESH_TypeFilter( SMESH::GROUP ) );
1192   myFilter = new SMESH_LogicalFilter( filters, SMESH_LogicalFilter::LO_OR );
1193 }
1194
1195 /*!
1196   \brief Destructor
1197 */
1198 SMESHGUI_BasicProperties::~SMESHGUI_BasicProperties()
1199 {
1200 }
1201
1202 /*!
1203   \brief Sets the measurement mode.
1204   \param theMode the mode to set (length, area or volume meausurement)
1205 */
1206 void SMESHGUI_BasicProperties::setMode( const Mode theMode )
1207 {
1208   QRadioButton* aButton = qobject_cast<QRadioButton*>( myMode->button( theMode ) );
1209   if ( aButton ) {
1210     aButton->setChecked( true );
1211     modeChanged( theMode );
1212   }
1213 }
1214
1215 /*!
1216   \brief Setup the selection mode.
1217 */
1218 void SMESHGUI_BasicProperties::updateSelection()
1219 {
1220   LightApp_SelectionMgr* selMgr = SMESHGUI::selectionMgr();
1221
1222   disconnect( selMgr, 0, this, 0 );
1223   selMgr->clearFilters();
1224   
1225   SMESH::SetPointRepresentation( false );
1226   if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() ) {
1227     aViewWindow->SetSelectionMode( ActorSelection );
1228   }
1229   selMgr->installFilter( myFilter );
1230   
1231   connect( selMgr, SIGNAL( currentSelectionChanged() ), this, SLOT( selectionChanged() ) );
1232
1233   if ( mySource->text().isEmpty() )
1234     selectionChanged();
1235 }
1236
1237 /*!
1238   \brief Deactivate widget
1239 */
1240 void SMESHGUI_BasicProperties::deactivate()
1241 {
1242   disconnect( SMESHGUI::selectionMgr(), 0, this, 0 );
1243 }
1244
1245 /*!
1246   \brief Called when selection is changed
1247 */
1248 void SMESHGUI_BasicProperties::selectionChanged()
1249 {
1250   SUIT_OverrideCursor wc;
1251
1252   SALOME_ListIO selected;
1253   SMESHGUI::selectionMgr()->selectedObjects( selected );
1254
1255   if ( selected.Extent() == 1 ) {
1256     Handle(SALOME_InteractiveObject) IO = selected.First();
1257     SMESH::SMESH_IDSource_var obj = SMESH::IObjectToInterface<SMESH::SMESH_IDSource>( IO );
1258     if ( !CORBA::is_nil( obj ) ) {
1259       mySrc = obj;
1260
1261       QString aName;
1262       SMESH::GetNameOfSelectedIObjects( SMESHGUI::selectionMgr(), aName );
1263       mySource->setText( aName );
1264     }
1265   }
1266
1267   clear();
1268 }
1269
1270 /*!
1271   \brief Called when the measurement mode selection is changed.
1272   \param theMode the selected mode
1273 */
1274 void SMESHGUI_BasicProperties::modeChanged( int theMode )
1275 {
1276   clear();
1277
1278   if ( theMode == Length ) {
1279     myResultGrp->setTitle( tr("LENGTH") );
1280   } else if ( theMode == Area ) {
1281     myResultGrp->setTitle( tr("AREA") );
1282   } else if ( theMode == Volume ) {
1283     myResultGrp->setTitle( tr("VOLUME") );
1284   }
1285 }
1286
1287 /*!
1288   \brief Calculate length, area or volume for the selected object(s)
1289 */
1290 void SMESHGUI_BasicProperties::compute()
1291 {
1292   SUIT_OverrideCursor wc;
1293
1294   SMESH::SMESH_IDSource_var source;
1295
1296   if ( !CORBA::is_nil( mySrc ) ) {
1297     // compute
1298     int precision = SMESHGUI::resourceMgr()->integerValue( "SMESH", "length_precision", 6 );
1299     SMESH::Measurements_var measure = SMESHGUI::GetSMESHGen()->CreateMeasurements();
1300
1301     double result = 0;
1302
1303     if ( myMode->checkedId() == Length ) {
1304       result = measure->Length( mySrc.in() );
1305     } else if ( myMode->checkedId() == Area ) {
1306       result = measure->Area( mySrc.in() );
1307     } else if ( myMode->checkedId() == Volume ) {
1308       result = measure->Volume( mySrc.in() );
1309     }
1310     
1311     measure->UnRegister();
1312
1313     myResult->setText( QString::number( result, precision > 0 ? 'f' : 'g', qAbs( precision ) ) );
1314   } else {
1315     clear();
1316   }
1317 }
1318
1319 /*!
1320   \brief Reset the widget to the initial state (nullify the result field)
1321 */
1322 void SMESHGUI_BasicProperties::clear()
1323 {
1324   myResult->clear();
1325 }
1326
1327 /*!
1328   \class SMESHGUI_Angle
1329   \brief Angle measurement widget.
1330   
1331   Widget to calculate angle between 3 nodes.
1332 */
1333
1334 /*!
1335   \brief Constructor.
1336   \param parent parent widget
1337 */
1338 SMESHGUI_Angle::SMESHGUI_Angle( QWidget* parent )
1339   : QWidget( parent )
1340 {
1341   // 3 nodes
1342
1343   QGroupBox* aNodesGrp = new QGroupBox( tr( "NODES_GROUP" ), this );
1344   myNodes = new QLineEdit( aNodesGrp );
1345   myNodes->setValidator( new SMESHGUI_IdValidator( this, 3 ));
1346   QHBoxLayout* aNodesLayout = new QHBoxLayout( aNodesGrp );
1347   aNodesLayout->addWidget( myNodes );
1348   
1349   // Compute button
1350   QPushButton* aCompute = new QPushButton( tr( "COMPUTE" ), this );
1351
1352   // Angle
1353
1354   QGroupBox* aResultGrp = new QGroupBox( tr( "RESULT" ), this );
1355
1356   myResult = new QLineEdit;
1357   myResult->setReadOnly( true );
1358
1359   QHBoxLayout* aResultLayout = new QHBoxLayout( aResultGrp );
1360   aResultLayout->addWidget( myResult );
1361   
1362   // Layout
1363
1364   QGridLayout* aMainLayout = new QGridLayout( this );
1365   aMainLayout->setMargin( MARGIN );
1366   aMainLayout->setSpacing( SPACING );
1367
1368   aMainLayout->addWidget( aNodesGrp,   0, 0, 1, 2 );
1369   aMainLayout->addWidget( aCompute,    1, 0 );
1370   aMainLayout->addWidget( aResultGrp, 2, 0, 1, 2 );
1371   aMainLayout->setColumnStretch( 1, 5 );
1372   aMainLayout->setRowStretch( 3, 5 );
1373
1374   // Connections
1375   connect( aCompute, SIGNAL( clicked() ), this, SLOT( compute() ));
1376
1377   // preview
1378   myPreview = 0;
1379   if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() )
1380   {
1381     myPreview = new SMESHGUI_MeshEditPreview( aViewWindow );
1382     if ( myPreview && myPreview->GetActor() )
1383       myPreview->GetActor()->GetProperty()->SetLineWidth( 5 );
1384   }
1385   myActor = 0;
1386 }
1387
1388 SMESHGUI_Angle::~SMESHGUI_Angle()
1389 {
1390   if ( myPreview )
1391     delete myPreview;
1392 }
1393
1394 /*!
1395   \brief Setup selection mode
1396 */
1397 void SMESHGUI_Angle::updateSelection()
1398 {
1399   LightApp_SelectionMgr* selMgr = SMESHGUI::selectionMgr();
1400
1401   disconnect( selMgr, 0, this, 0 );
1402   selMgr->clearFilters();
1403
1404   SMESH::SetPointRepresentation( true );
1405   if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() )
1406     aViewWindow->SetSelectionMode( NodeSelection );
1407
1408   connect( selMgr,  SIGNAL( currentSelectionChanged() ), this, SLOT( selectionChanged() ));
1409   connect( myNodes, SIGNAL( textEdited( QString ) ),     this, SLOT( nodesEdited() ));
1410
1411   if ( myPoints.empty() )
1412     selectionChanged();
1413 }
1414
1415 /*!
1416   \brief Called when selection is changed
1417 */
1418 void SMESHGUI_Angle::selectionChanged()
1419 {
1420   clear();
1421   QString nodesString;
1422
1423   SVTK_TIndexedMapOfVtkId idsMap;
1424   SALOME_ListIO selected;
1425   SMESHGUI::selectionMgr()->selectedObjects( selected );
1426   selected.Reverse(); // to keep order of selection
1427
1428   SALOME_ListIteratorOfListIO ioIterator( selected );
1429   for ( ; ioIterator.More(); ioIterator.Next() )
1430   {
1431     Handle(SALOME_InteractiveObject) IO = ioIterator.Value();
1432
1433     idsMap.Clear();
1434     if ( SVTK_Selector* selector = SMESH::GetViewWindow()->GetSelector() )
1435       selector->GetIndex( IO, idsMap );
1436
1437     if ( SMESH_Actor* actor = SMESH::FindActorByEntry( IO->getEntry() ))
1438     {
1439       myActor = actor;
1440       for ( int i = 1; i <= idsMap.Extent() && myPoints.size() < 3; ++i )
1441         if ( addPointByActor( idsMap(i) ))
1442           nodesString += QString(" %1").arg( idsMap(i) );
1443       idsMap.Clear();
1444     }
1445     SMESH::SMESH_IDSource_var obj = SMESH::IObjectToInterface<SMESH::SMESH_IDSource>( IO );
1446     if ( !CORBA::is_nil( obj ) )
1447     {
1448       myIDSrc = obj;
1449       for ( int i = 1; i <= idsMap.Extent() && myPoints.size() < 3; ++i )
1450         if ( addPointByIDSource( idsMap(i) ))
1451           nodesString += QString(" %1").arg( idsMap(i) );
1452     }
1453   }
1454
1455   myNodes->setText( nodesString );
1456 }
1457
1458 //=======================================================================
1459 //function : clear
1460 //purpose  : Erase preview and result
1461 //=======================================================================
1462
1463 void SMESHGUI_Angle::clear()
1464 {
1465   myPoints.clear();
1466   myResult->clear();
1467   if ( myPreview && myPreview->GetActor())
1468   {
1469     myPreview->GetActor()->SetVisibility( false );
1470     if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() )
1471       aViewWindow->Repaint();
1472   }
1473 }
1474
1475 //=======================================================================
1476 //function : addPointByActor
1477 //purpose  : append to myPoints XYZ got from myActor
1478 //=======================================================================
1479
1480 bool SMESHGUI_Angle::addPointByActor( int id )
1481 {
1482   size_t nbP = myPoints.size();
1483
1484   if ( myActor )
1485   {
1486     TVisualObjPtr obj = myActor->GetObject();
1487     if ( SMDS_Mesh* mesh = obj->GetMesh() )
1488       if ( const SMDS_MeshNode* node = mesh->FindNode( id ))
1489       {
1490         SMESH::PointStruct p = { node->X(), node->Y(), node->Z() };
1491         myPoints.push_back( p );
1492       }
1493   }
1494   return nbP < myPoints.size();
1495 }
1496
1497 //=======================================================================
1498 //function : addPointByIDSource
1499 //purpose  : append to myPoints XYZ got from myIDSrc
1500 //=======================================================================
1501
1502 bool SMESHGUI_Angle::addPointByIDSource( int id )
1503 {
1504   size_t nbP = myPoints.size();
1505
1506   if ( !myIDSrc->_is_nil() )
1507   {
1508     SMESH::SMESH_Mesh_var mesh = myIDSrc->GetMesh();
1509     if ( !mesh->_is_nil() )
1510     {
1511       SMESH::double_array_var xyz = mesh->GetNodeXYZ( id );
1512       if ( xyz->length() == 3 )
1513       {
1514         SMESH::PointStruct p = { xyz[0], xyz[1], xyz[2] };
1515         myPoints.push_back( p );
1516       }
1517     }
1518   }
1519   return nbP < myPoints.size();
1520 }
1521
1522 //=======================================================================
1523 //function : nodesEdited
1524 //purpose  : SLOT called when the user types node IDs
1525 //=======================================================================
1526
1527 void SMESHGUI_Angle::nodesEdited()
1528 {
1529   clear();
1530
1531   SVTK_TVtkIDsMap ID;
1532   QStringList ids = myNodes->text().split( " ", QString::SkipEmptyParts );
1533   foreach ( QString idStr, ids )
1534   {
1535     int id = idStr.trimmed().toLong();
1536     if (( !ID.Contains( id )) &&
1537         ( addPointByActor( id ) || addPointByIDSource( id )))
1538       ID.Add( id );
1539   }
1540
1541   SVTK_Selector* selector = SMESH::GetViewWindow()->GetSelector();
1542   if ( myActor && selector )
1543   {
1544     Handle(SALOME_InteractiveObject) IO = myActor->getIO();
1545     selector->AddOrRemoveIndex( IO, ID, false );
1546     if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() )
1547       aViewWindow->highlight( IO, true, true );
1548   }
1549 }
1550
1551 //=======================================================================
1552 //function : compute
1553 //purpose  : SLOT. Compute angle and show preview
1554 //=======================================================================
1555
1556 void SMESHGUI_Angle::compute()
1557 {
1558   if ( myPoints.size() != 3 )
1559     return;
1560
1561   // --------------
1562   // compute angle
1563   // --------------
1564
1565   SMESH::Measurements_var measure = SMESHGUI::GetSMESHGen()->CreateMeasurements();
1566   double radians = measure->Angle( myPoints[0], myPoints[1], myPoints[2] );
1567   measure->UnRegister();
1568   if ( radians < 0 )
1569     return;
1570
1571   int precision = SMESHGUI::resourceMgr()->integerValue( "SMESH", "length_precision", 6 );
1572   myResult->setText( QString::number( radians * 180 / M_PI,
1573                                       precision > 0 ? 'f' : 'g', qAbs( precision )));
1574
1575   // -------------
1576   // show preview
1577   // -------------
1578
1579   if ( !myPreview || !myPreview->GetActor() )
1580     return;
1581
1582   SMESH::MeshPreviewStruct preveiwData;
1583
1584   const double     anglePerSeg = 5 * M_PI/180; // angle per an arc segment
1585   const double arcRadiusFactor = 0.5;          // arc position, from p1
1586
1587   gp_Pnt p0 ( myPoints[0].x, myPoints[0].y, myPoints[0].z );
1588   gp_Pnt p1 ( myPoints[1].x, myPoints[1].y, myPoints[1].z );
1589   gp_Pnt p2 ( myPoints[2].x, myPoints[2].y, myPoints[2].z );
1590   gp_Vec vec10( p1, p0 ), vec12( p1, p2 ), norm( vec10 ^ vec12 );
1591
1592   if ( norm.Magnitude() <= gp::Resolution() ) // 180 degrees
1593     norm = getNormal( vec10 );
1594
1595   double     len10 = vec10.Magnitude();
1596   double     len12 = vec12.Magnitude();
1597   double    lenMax = Max( len10, len12 );
1598   double arcRadius = arcRadiusFactor * lenMax;
1599
1600   p0 = p1.Translated( lenMax * vec10.Normalized() );
1601   p2 = p1.Translated( lenMax * vec12.Normalized() );
1602
1603   gp_Circ arc( gp_Ax2( p1, norm, vec10 ), arcRadius );
1604
1605   int nbRadialSegmensts = ceil( radians / anglePerSeg ) + 1;
1606   int           nbNodes = 3 + ( nbRadialSegmensts + 1 );
1607
1608   // coordinates
1609   preveiwData.nodesXYZ.length( nbNodes );
1610   int iP = 0;
1611   for ( ; iP < nbRadialSegmensts + 1; ++iP )
1612   {
1613     double u = double( iP ) / nbRadialSegmensts * radians;
1614     gp_Pnt p = ElCLib::Value( u, arc );
1615     preveiwData.nodesXYZ[ iP ].x = p.X();
1616     preveiwData.nodesXYZ[ iP ].y = p.Y();
1617     preveiwData.nodesXYZ[ iP ].z = p.Z();
1618   }
1619   int iP0 = iP;
1620   preveiwData.nodesXYZ[ iP ].x = p0.X();
1621   preveiwData.nodesXYZ[ iP ].y = p0.Y();
1622   preveiwData.nodesXYZ[ iP ].z = p0.Z();
1623   int iP1 = ++iP;
1624   preveiwData.nodesXYZ[ iP ].x = p1.X();
1625   preveiwData.nodesXYZ[ iP ].y = p1.Y();
1626   preveiwData.nodesXYZ[ iP ].z = p1.Z();
1627   int iP2 = ++iP;
1628   preveiwData.nodesXYZ[ iP ].x = p2.X();
1629   preveiwData.nodesXYZ[ iP ].y = p2.Y();
1630   preveiwData.nodesXYZ[ iP ].z = p2.Z();
1631
1632   // connectivity
1633   preveiwData.elementConnectivities.length( 2 * ( 2 + nbRadialSegmensts ));
1634   for ( int iSeg = 0; iSeg < nbRadialSegmensts; ++iSeg )
1635   {
1636     preveiwData.elementConnectivities[ iSeg * 2 + 0 ] = iSeg;
1637     preveiwData.elementConnectivities[ iSeg * 2 + 1 ] = iSeg + 1;
1638   }
1639   int iSeg = nbRadialSegmensts;
1640   preveiwData.elementConnectivities[ iSeg * 2 + 0 ] = iP0;
1641   preveiwData.elementConnectivities[ iSeg * 2 + 1 ] = iP1;
1642   ++iSeg;
1643   preveiwData.elementConnectivities[ iSeg * 2 + 0 ] = iP1;
1644   preveiwData.elementConnectivities[ iSeg * 2 + 1 ] = iP2;
1645
1646   // types
1647   preveiwData.elementTypes.length( 2 + nbRadialSegmensts );
1648   SMESH::ElementSubType type = { SMESH::EDGE, /*isPoly=*/false, /*nbNodesInElement=*/2 };
1649   for ( CORBA::ULong i = 0; i < preveiwData.elementTypes.length(); ++i )
1650     preveiwData.elementTypes[ i ] = type;
1651
1652   myPreview->SetData( preveiwData );
1653 }
1654
1655 //================================================================================
1656 /*!
1657  * \brief Return normal to a plane of drawing in the case of 180 degrees angle
1658  */
1659 //================================================================================
1660
1661 gp_Vec SMESHGUI_Angle::getNormal(const gp_Vec& vec10 )
1662 {
1663   gp_XYZ norm;
1664
1665   // try to get normal by a face at the 2nd node
1666   if ( myActor && myActor->GetObject()->GetMesh() )
1667   {
1668     QStringList ids = myNodes->text().split( " ", QString::SkipEmptyParts );
1669     SMDS_Mesh* mesh = myActor->GetObject()->GetMesh();
1670     if ( const SMDS_MeshNode* n = mesh->FindNode( ids[1].trimmed().toLong() ))
1671     {
1672       SMDS_ElemIteratorPtr faceIt = n->GetInverseElementIterator( SMDSAbs_Face );
1673       while ( faceIt->more() )
1674         if ( SMESH_MeshAlgos::FaceNormal( faceIt->next(), norm ))
1675           return norm;
1676     }
1677   }
1678   int iMinCoord = 1;
1679   if ( vec10.Coord( iMinCoord ) > vec10.Y() ) iMinCoord = 2;
1680   if ( vec10.Coord( iMinCoord ) > vec10.Z() ) iMinCoord = 3;
1681
1682   gp_Vec vec = vec10;
1683   vec.SetCoord( iMinCoord, vec10.Coord( iMinCoord ) + 1. );
1684
1685   return vec ^ vec10;
1686 }
1687
1688 /*!
1689   \class SMESHGUI_MeshInfoDlg
1690   \brief Centralized dialog box for the measurements
1691 */
1692
1693 /*!
1694   \brief Constructor
1695   \param parent parent widget
1696   \param page specifies the dialog page to be shown at the start-up
1697 */
1698 SMESHGUI_MeasureDlg::SMESHGUI_MeasureDlg( QWidget* parent, int page )
1699   : QDialog( parent )
1700 {
1701   setModal( false );
1702   setAttribute( Qt::WA_DeleteOnClose, true );
1703   setWindowTitle( tr( "MEASUREMENTS" ) );
1704   setSizeGripEnabled( true );
1705
1706   SUIT_ResourceMgr* resMgr = SMESHGUI::resourceMgr();
1707
1708   myTabWidget = new QTabWidget( this );
1709
1710   // min distance
1711
1712   myMinDist = new SMESHGUI_MinDistance( myTabWidget );
1713   int aMinDistInd = myTabWidget->addTab( myMinDist, resMgr->loadPixmap( "SMESH", tr( "ICON_MEASURE_MIN_DIST" ) ), tr( "MIN_DIST" ) );
1714
1715   // bounding box
1716   
1717   myBndBox = new SMESHGUI_BoundingBox( myTabWidget );
1718   int aBndBoxInd = myTabWidget->addTab( myBndBox, resMgr->loadPixmap( "SMESH", tr( "ICON_MEASURE_BND_BOX" ) ), tr( "BND_BOX" ) );
1719
1720   // basic properties
1721   
1722   myBasicProps = new SMESHGUI_BasicProperties( myTabWidget );
1723   int aBasicPropInd = myTabWidget->addTab( myBasicProps, resMgr->loadPixmap( "SMESH", tr( "ICON_MEASURE_BASIC_PROPS" ) ), tr( "BASIC_PROPERTIES" ) );
1724
1725   myAngle = new SMESHGUI_Angle( myTabWidget );
1726   int aAngleInd = myTabWidget->addTab( myAngle, resMgr->loadPixmap( "SMESH", tr( "ICON_MEASURE_ANGLE" ) ), tr( "ANGLE" ) );
1727
1728   // buttons
1729   QPushButton* okBtn = new QPushButton( tr( "SMESH_BUT_OK" ), this );
1730   okBtn->setAutoDefault( true );
1731   okBtn->setDefault( true );
1732   okBtn->setFocus();
1733   QPushButton* helpBtn = new QPushButton( tr( "SMESH_BUT_HELP" ), this );
1734   helpBtn->setAutoDefault( true );
1735
1736   QHBoxLayout* btnLayout = new QHBoxLayout;
1737   btnLayout->setSpacing( SPACING );
1738   btnLayout->setMargin( 0 );
1739   btnLayout->addWidget( okBtn );
1740   btnLayout->addStretch( 10 );
1741   btnLayout->addWidget( helpBtn );
1742
1743   QVBoxLayout* l = new QVBoxLayout ( this );
1744   l->setMargin( MARGIN );
1745   l->setSpacing( SPACING );
1746   l->addWidget( myTabWidget );
1747   l->addStretch();
1748   l->addLayout( btnLayout );
1749
1750   int anInd = -1;
1751   if ( page == MinDistance ) {
1752     anInd = aMinDistInd;
1753   } else if ( page == BoundingBox ) {
1754     anInd = aBndBoxInd;
1755   } else if ( page == Angle ) {
1756     anInd = aAngleInd;
1757   } else if ( page == Length || page == Area || page == Volume ) {
1758     myBasicProps->setMode( (SMESHGUI_BasicProperties::Mode)(page - Length) );
1759     anInd = aBasicPropInd;
1760   }
1761
1762   if ( anInd >= 0 ) {
1763     myTabWidget->setCurrentIndex( anInd );
1764   }
1765
1766   connect( okBtn,       SIGNAL( clicked() ),              this, SLOT( reject() ) );
1767   connect( helpBtn,     SIGNAL( clicked() ),              this, SLOT( help() ) );
1768   connect( myTabWidget, SIGNAL( currentChanged( int  ) ), this, SLOT( updateSelection() ) );
1769   connect( SMESHGUI::GetSMESHGUI(),  SIGNAL( SignalDeactivateActiveDialog() ), this, SLOT( deactivate() ) );
1770   connect( SMESHGUI::GetSMESHGUI(),  SIGNAL( SignalCloseAllDialogs() ),        this, SLOT( reject() ) );
1771
1772   updateSelection();
1773 }
1774
1775 /*!
1776   \brief Destructor
1777 */
1778 SMESHGUI_MeasureDlg::~SMESHGUI_MeasureDlg()
1779 {
1780 }
1781
1782 /*!
1783   \brief Perform clean-up actions on the dialog box closing.
1784 */
1785 void SMESHGUI_MeasureDlg::reject()
1786 {
1787   LightApp_SelectionMgr* selMgr = SMESHGUI::selectionMgr();
1788   selMgr->clearFilters();
1789   SMESH::SetPointRepresentation( false );
1790   if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() )
1791     aViewWindow->SetSelectionMode( ActorSelection );
1792   QDialog::reject();
1793 }
1794
1795 /*!
1796   \brief Process keyboard event
1797   \param e key press event
1798 */
1799 void SMESHGUI_MeasureDlg::keyPressEvent( QKeyEvent* e )
1800 {
1801   QDialog::keyPressEvent( e );
1802   if ( !e->isAccepted() && e->key() == Qt::Key_F1 ) {
1803     e->accept();
1804     help();
1805   }
1806 }
1807
1808 /*!
1809   \brief Reactivate dialog box, when mouse pointer goes into it.
1810 */
1811 void SMESHGUI_MeasureDlg::enterEvent( QEvent* )
1812 {
1813   activate();
1814 }
1815
1816 /*!
1817   \brief Setup selection mode depending on the current dialog box state.
1818 */
1819 void SMESHGUI_MeasureDlg::updateSelection()
1820 {
1821   if ( myTabWidget->currentIndex() == MinDistance )
1822     myMinDist->updateSelection();
1823   else if ( myTabWidget->currentIndex() == BoundingBox )
1824     myBndBox->updateSelection();
1825   else if ( myTabWidget->currentWidget() == myAngle )
1826     myAngle->updateSelection();
1827   else {
1828     myBndBox->erasePreview();
1829     myBasicProps->updateSelection();
1830   }
1831 }
1832
1833 /*!
1834   \brief Show help page
1835 */
1836 void SMESHGUI_MeasureDlg::help()
1837 {
1838   QString aHelpFile;
1839   if ( myTabWidget->currentIndex() == MinDistance ) {
1840     aHelpFile = "measurements.html#min-distance-anchor";
1841   } else if ( myTabWidget->currentIndex() == BoundingBox ) {
1842     aHelpFile = "measurements.html#bounding-box-anchor";
1843   } else if ( myTabWidget->currentWidget() == myAngle ) {
1844     aHelpFile = "measurements.html#angle-anchor";
1845   } else {
1846     aHelpFile = "measurements.html#basic-properties-anchor";
1847   }
1848
1849   SMESH::ShowHelpFile( aHelpFile );
1850 }
1851
1852 /*!
1853   \brief Activate dialog box
1854 */
1855 void SMESHGUI_MeasureDlg::activate()
1856 {
1857   SMESHGUI::GetSMESHGUI()->EmitSignalDeactivateDialog();
1858   SMESHGUI::GetSMESHGUI()->SetActiveDialogBox( this );
1859   myTabWidget->setEnabled( true );
1860   updateSelection();
1861 }
1862
1863 /*!
1864   \brief Deactivate dialog box
1865 */
1866 void SMESHGUI_MeasureDlg::deactivate()
1867 {
1868   myBasicProps->deactivate();
1869   myMinDist->deactivate();
1870   myBndBox->deactivate();
1871   myTabWidget->setEnabled( false );
1872   disconnect( SMESHGUI::selectionMgr(), SIGNAL( currentSelectionChanged() ), this, SLOT( updateInfo() ) );
1873 }