Salome HOME
bos #32738 [CEA] Scaled Jacobian quality mesh measure for volumetric elements.
[modules/smesh.git] / src / SMESHGUI / SMESHGUI_MeshInfo.cxx
1 // Copyright (C) 2007-2023  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
23 #include "SMESHGUI_MeshInfo.h"
24
25 #include "SMDS_Mesh.hxx"
26 #include "SMESH_Actor.h"
27 #include "SMESHGUI.h"
28 #include "SMESHGUI_FilterUtils.h"
29 #include "SMESHGUI_IdPreview.h"
30 #include "SMESHGUI_IdValidator.h"
31 #include "SMESHGUI_SpinBox.h"
32 #include "SMESHGUI_Utils.h"
33 #include "SMESHGUI_VTKUtils.h"
34
35 #include <SUIT_FileDlg.h>
36 #include <SUIT_OverrideCursor.h>
37 #include <SUIT_ResourceMgr.h>
38 #include <SUIT_Session.h>
39 #include <SVTK_ViewWindow.h>
40 #include <LightApp_SelectionMgr.h>
41
42 #include <SALOMEDSClient_Study.hxx>
43
44 #include <QApplication>
45 #include <QButtonGroup>
46 #include <QCheckBox>
47 #include <QComboBox>
48 #include <QContextMenuEvent>
49 #include <QGridLayout>
50 #include <QHBoxLayout>
51 #include <QHeaderView>
52 #include <QItemDelegate>
53 #include <QKeyEvent>
54 #include <QLabel>
55 #include <QLineEdit>
56 #include <QMenu>
57 #include <QPushButton>
58 #include <QRadioButton>
59 #include <QStackedWidget>
60 #include <QStandardItemModel>
61 #include <QTabWidget>
62 #include <QTextBrowser>
63 #include <QTextStream>
64 #include <QToolButton>
65 #include <QTreeWidget>
66 #include <QVBoxLayout>
67
68 ////////////////////////////////////////////////////////////////////////////////
69 /// \class FieldInfo
70 /// \brief FieldInfo widget.
71 /// \internal
72 ////////////////////////////////////////////////////////////////////////////////
73
74 class FieldInfo : public QLabel
75 {
76 public:
77   FieldInfo( QWidget*, const QString& = QString() );
78   bool event( QEvent* );
79 };
80
81 /*!
82   \brief Constructor.
83   \param parent Parent widget.
84   \param name FieldInfo name. Defaults to null string.
85 */
86 FieldInfo::FieldInfo( QWidget* parent, const QString& name ): QLabel( parent )
87 {
88   setFrameStyle( QLabel::StyledPanel | QLabel::Sunken );
89   setAlignment( Qt::AlignCenter );
90   setAutoFillBackground( true );
91   QPalette pal = palette();
92   QColor base = QApplication::palette().color( QPalette::Active, QPalette::Base );
93   pal.setColor( QPalette::Window, base );
94   setPalette( pal );
95   setMinimumWidth( 60 );
96   if ( !name.isEmpty() )
97     setObjectName( name );
98 }
99
100 /*!
101   \brief Event handler. Redefined from QLabel.
102 */
103 bool FieldInfo::event( QEvent* e )
104 {
105   if ( e->type() == QEvent::DynamicPropertyChange )
106   {
107     QDynamicPropertyChangeEvent* ce = (QDynamicPropertyChangeEvent*)e;
108     if ( ce->propertyName() == "value" && property( "value" ).isValid() )
109     {
110       setText( QString::number( property( "value" ).toInt() ) );
111       setProperty( "value", QVariant() );
112       return true;
113     }
114   }
115   return QLabel::event( e );
116 }
117
118 ////////////////////////////////////////////////////////////////////////////////
119 /// \class TreeItemCreator
120 /// \brief Generic tree item creator.
121 /// \internal
122 ////////////////////////////////////////////////////////////////////////////////
123
124 class TreeItemCreator
125 {
126 public:
127   TreeItemCreator() {}
128   virtual ~TreeItemCreator() {}
129   virtual QTreeWidgetItem* createItem( QTreeWidgetItem*, int ) = 0;
130 };
131
132 ////////////////////////////////////////////////////////////////////////////////
133 // General purpose services.
134 ////////////////////////////////////////////////////////////////////////////////
135
136 namespace
137 {
138   const int SPACING = 6;
139   const int MARGIN = 9;
140
141   enum
142   {
143     Bold = 0x01,
144     Italic = 0x02,
145     AllColumns = 0x04,
146     Expanded = 0x08,
147     Editable = 0x10
148   };
149
150   enum
151   {
152     GroupsId = 100,
153     SubMeshesId
154   };
155
156   enum
157   {
158     TypeRole = Qt::UserRole + 10,
159     IdRole,
160   };
161
162   enum
163   {
164     NodeConnectivity = 100,
165     ElemConnectivity,
166   };
167
168   /*!
169     \brief Get item's depth in the tree.
170     \param item Tree widget item.
171     \return Item's depth in the tree widget (top-level item has zero depth).
172     \internal
173   */
174   int itemDepth( QTreeWidgetItem* item )
175   {
176     QList<QTreeWidgetItem*> parents;
177     parents << item;
178     while ( parents.last()->parent() )
179       parents << parents.last()->parent();
180     return parents.size()-1;
181   }
182
183   /*!
184     \brief Get chunk size.
185     \return Chunk size.
186     \internal
187   */
188   int blockSize()
189   {
190     return 10;
191   }
192
193   /*!
194     \brief Get spacer.
195     \return Spacer string.
196     \internal
197   */
198   QString spacing()
199   {
200     static int size = 1;
201     static QChar spacer = ' ';
202     return QString( size, spacer );
203   }
204
205   /*!
206     \brief Get indent.
207     \param length Indent size. Defaults to 1.
208     \return Indentation string.
209     \internal
210   */
211   QString indent( int length = 1 )
212   {
213     static int size = 4;
214     static QChar spacer = ' ';
215     return QString( size * length, spacer );
216   }
217
218   /*!
219     \brief Get indent.
220     \param spacer Spacer.
221     \param length Indent size. Defaults to 1.
222     \return Indentation string.
223     \internal
224   */
225   QString indent( const QString& spacer, uint length = 1 )
226   {
227     QString result;
228     while( length-- > 0 )
229       result += spacer;
230     return result;
231   }
232
233   /*!
234     \brief Get group separator.
235     \param length Length of ruler (number of symbols). Defaults to 80.
236     \return Ruler string.
237     \internal
238   */
239   QString ruler( int length = 80 )
240   {
241     static QChar ruler = '-';
242     return QString( length, ruler );
243   }
244
245   /*!
246     \brief Get text value from label.
247     \param w Widget (QLabel).
248     \return Value string.
249     \internal
250   */
251   QString widgetValue( QWidget* w )
252   {
253     QString v;
254     if ( qobject_cast<QLabel*>( w ) )
255       v = qobject_cast<QLabel*>( w )->text();
256     return v;
257   }
258
259   /*!
260     \brief Get font for given options.
261     \param font Initial font.
262     \param options Font attributes.
263     \return Font.
264   */
265   QFont fontFromOptions( const QFont& font, int options )
266   {
267     QFont f = font;
268     f.setBold( options & Bold );
269     f.setItalic( options & Italic );
270     return f;
271   }
272
273   /*!
274     \brief Set font attributes to given widget.
275     \param w Widget.
276     \param options Font attributes.
277   */
278   void setFontAttributes( QWidget* w, int options )
279   {
280     if ( w )
281       w->setFont( fontFromOptions( w->font(), options ) );
282   }
283
284   /*!
285     \brief Set attributes to given tree item.
286     \param item Tree widget item.
287     \param options Item attributes.
288   */
289   void setTreeItemAttributes( QTreeWidgetItem* item, int options )
290   {
291     if ( item && item->treeWidget() )
292     {
293       for ( int i = 0; i < item->treeWidget()->columnCount(); i++ )
294       {
295         if ( i == 0 || options & AllColumns )
296           item->setFont( i, fontFromOptions( item->font( 0 ), options) );
297       }
298     }
299     if ( options & Expanded )
300       item->setExpanded( true );
301     if ( options & Editable )
302       item->setFlags( item->flags() | Qt::ItemIsEditable );
303   }
304
305   /*!
306     \brief Create label.
307     \param parent Parent widget.
308     \param options Label options. Defaults to 0 (none).
309     \return New label.
310   */
311   QLabel* createLabel( QWidget* parent, int options = 0 )
312   {
313     QLabel* lab = new QLabel( parent );
314     setFontAttributes( lab, options );
315     return lab;
316   }
317
318   /*!
319     \brief Create label.
320     \param text Label text.
321     \param parent Parent widget.
322     \param options Label options. Defaults to 0 (none).
323     \return New label.
324   */
325   QLabel* createLabel( const QString& text, QWidget* parent, int options = 0 )
326   {
327     QLabel* lab = createLabel( parent, options );
328     lab->setText( text );
329     return lab;
330   }
331
332   /*!
333     \brief Create information field.
334     \param parent Parent widget.
335     \param name FieldInfo's object. Default to null string.
336     \return New FieldInfo.
337   */
338   QLabel* createField( QWidget* parent, const QString& name = QString() )
339   {
340     return new FieldInfo( parent, name );
341   }
342
343   /*!
344     \brief Create information field.
345     \param parent Parent widget.
346     \param options Label options.
347     \param name Field's object. Default to null string.
348     \return New field.
349   */
350   QLabel* createField( QWidget* parent, int options, const QString& name = QString() )
351   {
352     QLabel* field = createField( parent, name );
353     setFontAttributes( field, options );
354     return field;
355   }
356
357   /*!
358     \brief Create ruler.
359     \param parent Parent widget.
360     \param orientation Ruler orientation. Defaults to horizontal.
361     \return New ruler.
362   */
363   QWidget* createSeparator( QWidget* parent, Qt::Orientation orientation = Qt::Horizontal )
364   {
365     QFrame* line = new QFrame( parent );
366     line->setFrameShape( orientation == Qt::Horizontal ? QFrame::HLine : QFrame::HLine );
367     line->setFrameShadow( QFrame::Sunken );
368     return line;
369   }
370
371   /*!
372     \brief Decorate text as bold.
373     \param text Initial text.
374     \return Decorated text.
375   */
376   QString bold( const QString& text )
377   {
378     return QString("<b>%1</b>").arg( text );
379   }
380
381   /*!
382     \brief Format connectivity data to string representation.
383     \param connectivity Connectivity map.
384     \param type Element type or face index if negative
385     \return Stringified representation of the connectivity.
386   */
387   QString formatConnectivity( SMESH::Connectivity connectivity, int type )
388   {
389     QStringList str;
390     QString     result;
391     bool isNodal = ( type == SMDSAbs_Node || type < 0 );
392     type = qAbs( type );
393     if ( connectivity.contains( type ))
394     {
395       QList<int> elements = connectivity[ type ];
396       if ( !isNodal ) // order of nodes is important
397         qSort( elements );
398       foreach( int id, elements )
399         str << QString::number( id );
400
401       // wrap IDs into an html link, to be treated by QTextBrowser used by SMESHGUI_SimpleElemInfo
402       QString s = str.join( " " );
403       result = ( "<a href = \"" +                                 // URL:
404                  QString( isNodal ? "nodes" : "elems" ) + "://" + // protocol
405                  QString( "host.com/") + // QUrl return nothing if host missing
406                  s + "\">" +                                      // path
407                  s + "</a>" );                                    // anchor text
408     }
409     return result;
410   }
411 } // end of anonymous namespace
412
413 ////////////////////////////////////////////////////////////////////////////////
414 /// \class SMESHGUI_Info
415 /// \brief Base widget for all information panes.
416 ////////////////////////////////////////////////////////////////////////////////
417
418 /*!
419   \brief Constructor.
420   \param parent Parent widget. Defaults to 0.
421 */
422 SMESHGUI_Info::SMESHGUI_Info( QWidget* parent ): QWidget( parent )
423 {
424 }
425
426 ////////////////////////////////////////////////////////////////////////////////
427 /// \class ExtraWidget
428 /// \brief Auxiliary widget to browse between chunks of information.
429 /// \internal
430 ////////////////////////////////////////////////////////////////////////////////
431
432 class ExtraWidget : public QWidget
433 {
434 public:
435   ExtraWidget( QWidget*, bool = false );
436   void updateControls( int, int );
437
438 public:
439   QLabel* current;
440   QPushButton* prev;
441   QPushButton* next;
442   bool brief;
443 };
444
445 /*
446   \brief Constructor.
447   \param parent Parent widget.
448   \param briefSummary Show summary in brief format. Defaults to \c false.
449 */
450 ExtraWidget::ExtraWidget( QWidget* parent, bool briefSummary ): QWidget( parent ), brief( briefSummary )
451 {
452   current = new QLabel( this );
453   current->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
454   prev = new QPushButton( "<<", this );
455   next = new QPushButton( ">>", this );
456   QHBoxLayout* hbl = new QHBoxLayout( this );
457   hbl->setMargin( 0 );
458   hbl->setSpacing( SPACING );
459   hbl->addStretch();
460   hbl->addWidget( current );
461   hbl->addWidget( prev );
462   hbl->addWidget( next );
463 }
464
465 /*
466   \brief Update controls.
467   \param total Total number of items.
468   \param index Current index.
469 */
470 void ExtraWidget::updateControls( int total, int index )
471 {
472   setVisible( total > blockSize() );
473   QString format = brief ? QString( "%1-%2 / %3" ) : SMESHGUI_MeshInfoDlg::tr( "X_FROM_Y_ITEMS_SHOWN" );
474   current->setText( format.arg( index*blockSize()+1 ).arg( qMin( index*blockSize()+blockSize(), total ) ).arg( total ) );
475   prev->setEnabled( index > 0 );
476   next->setEnabled( (index+1)*blockSize() < total );
477 }
478
479 ////////////////////////////////////////////////////////////////////////////////
480 /// \class DumpFileDlg
481 /// \brief Standard Save File dialog box, customized for dump info operation.
482 /// \internal
483 ////////////////////////////////////////////////////////////////////////////////
484
485 class DumpFileDlg : public SUIT_FileDlg
486 {
487   QMap<int, QCheckBox*> myControls;
488 public:
489   DumpFileDlg( QWidget*, bool = true );
490   bool isChecked( int ) const;
491   void setChecked( int, bool );
492 };
493
494 /*!
495   \brief Constructor.
496   \param parent Parent widget.
497   \param showControls Show additional controls. Defaults to \c true.
498   \internal
499 */
500 DumpFileDlg::DumpFileDlg( QWidget* parent, bool showControls ): SUIT_FileDlg( parent, false, true, true )
501 {
502   if ( showControls )
503   {
504     QWidget* hB = new QWidget( this );
505     myControls[SMESHGUI_MeshInfoDlg::BaseInfo] = new QCheckBox( SMESHGUI::tr( "PREF_DUMP_BASE_INFO" ), hB );
506     myControls[SMESHGUI_MeshInfoDlg::ElemInfo] = new QCheckBox( SMESHGUI::tr( "PREF_DUMP_ELEM_INFO" ), hB );
507     myControls[SMESHGUI_MeshInfoDlg::AddInfo] = new QCheckBox( SMESHGUI::tr( "PREF_DUMP_ADD_INFO" ),  hB );
508     myControls[SMESHGUI_MeshInfoDlg::CtrlInfo] = new QCheckBox( SMESHGUI::tr( "PREF_DUMP_CTRL_INFO" ), hB );
509
510     QGridLayout* layout = new QGridLayout( hB );
511     layout->setMargin( 0 );
512     layout->addWidget( myControls[SMESHGUI_MeshInfoDlg::BaseInfo], 0, 0 );
513     layout->addWidget( myControls[SMESHGUI_MeshInfoDlg::ElemInfo], 0, 1 );
514     layout->addWidget( myControls[SMESHGUI_MeshInfoDlg::AddInfo], 1, 0 );
515     layout->addWidget( myControls[SMESHGUI_MeshInfoDlg::CtrlInfo], 1, 1 );
516
517     addWidgets( 0, hB, 0 );
518   }
519 }
520
521 /*!
522   \brief Get control's value.
523   \param option Control identifier.
524   \return Control value.
525   \internal
526 */
527 bool DumpFileDlg::isChecked( int option ) const
528 {
529   return myControls.contains( option ) ? myControls[option]->isChecked() : false;
530 }
531
532 /*!
533   \brief Set control's initial value.
534   \param option Control identifier.
535   \param value Control value.
536   \internal
537 */
538 void DumpFileDlg::setChecked( int option, bool value )
539 {
540   if ( myControls.contains( option ) ) 
541     myControls[option]->setChecked( value );
542 }
543
544 ////////////////////////////////////////////////////////////////////////////////
545 /// \class SMESHGUI_BaseInfo
546 /// \brief Show basic information on selected object.
547 ///
548 /// Displays the base information about selected object: mesh, sub-mesh, group
549 /// or arbitrary ID source.
550 /// \todo Hide unnecessary widgets (e.g. for mesh group).
551 ////////////////////////////////////////////////////////////////////////////////
552
553 /*!
554   \brief Constructor.
555   \param parent Parent widget. Defaults to 0.
556 */
557 SMESHGUI_BaseInfo::SMESHGUI_BaseInfo( QWidget* parent ): SMESHGUI_Info( parent )
558 {
559   QGridLayout* l = new QGridLayout( this );
560   l->setMargin( MARGIN );
561   l->setSpacing( SPACING );
562
563   // object info
564   // - name
565   addWidget( createLabel( tr( "NAME_LAB" ), this, Bold ), iName, iLabel );
566   addWidget( createField( this, "meshName" ), iName, iSingle, 4 )->setMinimumWidth( 150 );
567   // - type
568   addWidget( createLabel( tr( "OBJECT_LAB" ), this, Bold ), iObject, iLabel );
569   addWidget( createField( this, "meshType" ), iObject, iSingle, 4 )->setMinimumWidth( 150 );
570   // - --- (separator)
571   addWidget( createSeparator( this ), iObjectEnd, iLabel, 5 );
572
573   // node info
574   // - info
575   addWidget( createLabel( tr( "NODES_LAB" ), this, Bold ), iNodes, iLabel );
576   addWidget( createField( this, "nbNodes" ), iNodes, iTotal );
577   // - --- (separator)
578   addWidget( createSeparator( this ), iNodesEnd, iLabel, 5 );
579
580   // element info
581   // - title
582   addWidget( createLabel( tr( "ELEMENTS_LAB" ), this, Bold ), iElementsTitle, iLabel );
583   addWidget( createLabel( tr( "TOTAL_LAB" ), this, Italic ), iElementsTitle, iTotal );
584   addWidget( createLabel( tr( "LINEAR_LAB" ), this, Italic ), iElementsTitle, iLinear );
585   addWidget( createLabel( tr( "QUADRATIC_LAB" ), this, Italic ), iElementsTitle, iQuadratic );
586   addWidget( createLabel( tr( "BI_QUADRATIC_LAB" ), this, Italic ), iElementsTitle, iBiQuadratic );
587   // - --- (separator)
588   addWidget( createSeparator( this ), iElementsTitleEnd, iTotal, 4 );
589   // - summary
590   addWidget( createField( this, "totalNbElems" ), iElementsTotal, iTotal );
591   addWidget( createField( this, "totalNbLinearElems" ), iElementsTotal, iLinear );
592   addWidget( createField( this, "totalNbQuadraticElems" ), iElementsTotal, iQuadratic );
593   addWidget( createField( this, "totalNbBiQuadraticElems" ), iElementsTotal, iBiQuadratic );
594   // - --- (separator)
595   addWidget( createSeparator( this ), iElementsTotalEnd, iTotal, 4 );
596   // - 0D elements info
597   addWidget( createLabel( tr( "0D_LAB" ), this, Bold | Italic ), i0D, iLabel );
598   addWidget( createField( this, "nb0D" ), i0D, iTotal );
599   // - --- (separator)
600   addWidget( createSeparator( this ), i0DEnd, iTotal, 4 );
601   // - balls info
602   addWidget( createLabel( tr( "BALL_LAB" ), this, Bold | Italic ), iBalls, iLabel );
603   addWidget( createField( this, "nbBall" ), iBalls, iTotal );
604   // - --- (separator)
605   addWidget( createSeparator( this ), iBallsEnd, iTotal, 4 );
606   // - 1D elements info
607   addWidget( createLabel( tr( "1D_LAB" ), this, Bold | Italic ), i1D, iLabel );
608   addWidget( createField( this, "nb1D" ), i1D, iTotal );
609   addWidget( createField( this, "nbLinear1D" ), i1D, iLinear );
610   addWidget( createField( this, "nbQuadratic1D" ), i1D, iQuadratic );
611   // - --- (separator)
612   addWidget( createSeparator( this ), i1DEnd, iTotal, 4 );
613   // - 2D elements info
614   // --+ summary
615   addWidget( createLabel( tr( "2D_LAB" ), this, Bold | Italic ), i2D, iLabel );
616   addWidget( createField( this, "nb2D" ), i2D, iTotal );
617   addWidget( createField( this, "nbLinear2D" ), i2D, iLinear );
618   addWidget( createField( this, "nbQuadratic2D" ), i2D, iQuadratic );
619   addWidget( createField( this, "nbBiQuadratic2D" ), i2D, iBiQuadratic );
620   // --+ triangles
621   addWidget( createLabel( tr( "TRIANGLES_LAB" ), this, Italic ), i2DTriangles, iLabel );
622   addWidget( createField( this, Italic, "nbTriangle" ), i2DTriangles, iTotal );
623   addWidget( createField( this, Italic, "nbLinearTriangle" ), i2DTriangles, iLinear );
624   addWidget( createField( this, Italic, "nbQuadraticTriangle" ), i2DTriangles, iQuadratic );
625   addWidget( createField( this, Italic, "nbBiQuadraticTriangle" ), i2DTriangles, iBiQuadratic );
626   // --+ quadrangles
627   addWidget( createLabel( tr( "QUADRANGLES_LAB" ), this, Italic ), i2DQuadrangles, iLabel );
628   addWidget( createField( this, Italic, "nbQuadrangle" ), i2DQuadrangles, iTotal );
629   addWidget( createField( this, Italic, "nbLinearQuadrangle" ), i2DQuadrangles, iLinear );
630   addWidget( createField( this, Italic, "nbQuadraticQuadrangle" ), i2DQuadrangles, iQuadratic );
631   addWidget( createField( this, Italic, "nbBiQuadraticQuadrangle" ), i2DQuadrangles, iBiQuadratic );
632   // --+ polygons
633   addWidget( createLabel( tr( "POLYGONS_LAB" ), this, Italic ), i2DPolygons, iLabel );
634   addWidget( createField( this, Italic, "nbPolygon" ), i2DPolygons, iTotal );
635   addWidget( createField( this, Italic, "nbLinearPolygon" ), i2DPolygons, iLinear );
636   addWidget( createField( this, Italic, "nbQuadraticPolygon" ), i2DPolygons, iQuadratic );
637   // - --- (separator)
638   addWidget( createSeparator( this ), i2DEnd, iTotal, 4 );
639   // - 3D elements info
640   // --+ summary
641   addWidget( createLabel( tr( "3D_LAB" ), this, Bold | Italic ), i3D, iLabel );
642   addWidget( createField( this, "nb3D" ), i3D, iTotal );
643   addWidget( createField( this, "nbLinear3D" ), i3D, iLinear );
644   addWidget( createField( this, "nbQuadratic3D" ), i3D, iQuadratic );
645   addWidget( createField( this, "nbBiQuadratic3D" ), i3D, iBiQuadratic );
646   // --+ tetras
647   addWidget( createLabel( tr( "TETRAHEDRONS_LAB" ), this, Italic ), i3DTetrahedrons, iLabel );
648   addWidget( createField( this, Italic, "nbTetrahedron" ), i3DTetrahedrons, iTotal );
649   addWidget( createField( this, Italic, "nbLinearTetrahedron" ), i3DTetrahedrons, iLinear );
650   addWidget( createField( this, Italic, "nbQudraticTetrahedron" ), i3DTetrahedrons, iQuadratic );
651   // --+ hexas
652   addWidget( createLabel( tr( "HEXAHEDONRS_LAB" ), this, Italic ), i3DHexahedrons, iLabel );
653   addWidget( createField( this, Italic, "nbHexahedron" ), i3DHexahedrons, iTotal );
654   addWidget( createField( this, Italic, "nbLinearHexahedron" ), i3DHexahedrons, iLinear );
655   addWidget( createField( this, Italic, "nbQuadraticHexahedron" ), i3DHexahedrons, iQuadratic );
656   addWidget( createField( this, Italic, "nbBiQuadraticHexahedron" ), i3DHexahedrons, iBiQuadratic );
657   // --+ pyramids
658   addWidget( createLabel( tr( "PYRAMIDS_LAB" ), this, Italic ), i3DPyramids, iLabel );
659   addWidget( createField( this, Italic, "nbPyramid" ), i3DPyramids, iTotal );
660   addWidget( createField( this, Italic, "nbLinearPyramid" ), i3DPyramids, iLinear );
661   addWidget( createField( this, Italic, "nbQuadraticPyramid" ), i3DPyramids, iQuadratic );
662   // --+ prisms
663   addWidget( createLabel( tr( "PRISMS_LAB" ), this, Italic ), i3DPrisms, iLabel );
664   addWidget( createField( this, Italic, "nbPrism" ), i3DPrisms, iTotal );
665   addWidget( createField( this, Italic, "nbLinearPrism" ), i3DPrisms, iLinear );
666   addWidget( createField( this, Italic, "nbQuadraticPrism" ), i3DPrisms, iQuadratic );
667   addWidget( createField( this, Italic, "nbBiQuadraticPrism" ), i3DPrisms, iBiQuadratic );
668   // --+ hexagonal prisms
669   addWidget( createLabel( tr( "HEX_PRISMS_LAB" ), this, Italic ), i3DHexaPrisms, iLabel );
670   addWidget( createField( this, Italic, "nbHexagonalPrism" ), i3DHexaPrisms, iTotal );
671   // --+ polyhedrons
672   addWidget( createLabel( tr( "POLYHEDRONS_LAB" ), this, Italic ), i3DPolyhedrons, iLabel );
673   addWidget( createField( this, Italic, "nbPolyhedron" ), i3DPolyhedrons, iTotal );
674
675   // load button
676   QPushButton* loadBtn = new QPushButton( tr( "BUT_LOAD_MESH" ), this );
677   loadBtn->setAutoDefault( true );
678   connect( loadBtn, SIGNAL( clicked() ), this, SLOT( loadMesh() ) );
679   addWidget( loadBtn, iEnd, iSingle, 4 );
680
681   // additional layout set-up
682   l->setColumnStretch( iLabel, 0 );
683   l->setColumnStretch( iTotal, 5 );
684   l->setColumnStretch( iLinear, 5 );
685   l->setColumnStretch( iQuadratic, 5 );
686   l->setColumnStretch( iBiQuadratic, 5 );
687   l->setRowStretch( iElementsEnd, 5 );
688
689   // set initial values
690   clear();
691 }
692
693 /*!
694   \brief Destructor.
695 */
696 SMESHGUI_BaseInfo::~SMESHGUI_BaseInfo()
697 {
698 }
699
700 /*!
701   \brief Show information on given object.
702   \param proxy Object to show information on (mesh, sub-mesh, group, ID source).
703 */
704 void SMESHGUI_BaseInfo::showInfo( const SMESH::SelectionProxy& proxy )
705 {
706   // reset panel
707   clear();
708
709   // then fill panel with data if object is not null
710   if ( proxy )
711   {
712     myProxy = proxy;
713
714     SMESH::MeshInfo info = proxy.meshInfo();
715
716     // object info
717     // - name
718     widget( iName, iSingle )->setProperty( "text", proxy.name() );
719     // - type
720     QString typeName;
721     SMESH::SelectionProxy::Type type = proxy.type();
722     if ( type == SMESH::SelectionProxy::Mesh )
723     {
724       typeName = tr( "OBJECT_MESH" );
725     }
726     else if ( type == SMESH::SelectionProxy::Submesh )
727     {
728       typeName = tr( "OBJECT_SUBMESH" );
729     }
730     else if ( type >= SMESH::SelectionProxy::Group )
731     {
732       switch( proxy.groupElementType() )
733       {
734       case SMESH::NODE:   typeName = tr( "OBJECT_GROUP_NODES"   ); break;
735       case SMESH::EDGE:   typeName = tr( "OBJECT_GROUP_EDGES"   ); break;
736       case SMESH::FACE:   typeName = tr( "OBJECT_GROUP_FACES"   ); break;
737       case SMESH::VOLUME: typeName = tr( "OBJECT_GROUP_VOLUMES" ); break;
738       case SMESH::ELEM0D: typeName = tr( "OBJECT_GROUP_0DELEMS" ); break;
739       case SMESH::BALL:   typeName = tr( "OBJECT_GROUP_BALLS"   ); break;
740       default:            typeName = tr( "OBJECT_GROUP"         ); break;
741       }
742       QString subType;
743       if ( type == SMESH::SelectionProxy::GroupStd )
744         subType = tr( "OBJECT_GROUP_STANDALONE" );
745       else if ( type == SMESH::SelectionProxy::GroupGeom )
746         subType = tr( "OBJECT_GROUP_GEOM" );
747       else if ( type == SMESH::SelectionProxy::GroupFilter )
748         subType = tr( "OBJECT_GROUP_FILTER" );
749       if ( !subType.isEmpty() )
750         typeName = QString( "%1 %2" ).arg( typeName, subType );
751     }
752     widget( iObject, iSingle )->setProperty( "text", typeName );
753
754     // node info
755     uint nbNodes = info[SMDSEntity_Node];
756     widget( iNodes, iTotal )->setProperty( "value", nbNodes );
757
758     // element info
759     // - 0D elements info
760     uint nb0d = info[SMDSEntity_0D];
761     widget( i0D, iTotal )->setProperty( "value", nb0d );
762     // - balls info
763     uint nbBalls = info[SMDSEntity_Ball];
764     widget( iBalls, iTotal )->setProperty( "value", nbBalls );
765     // - 1D elements info
766     uint nb1dLin = info[SMDSEntity_Edge];
767     uint nb1dQua = info[SMDSEntity_Quad_Edge];
768     uint nb1d = nb1dLin + nb1dQua;
769     widget( i1D, iLinear )->setProperty( "value", nb1dLin );
770     widget( i1D, iQuadratic )->setProperty( "value", nb1dQua );
771     widget( i1D, iTotal )->setProperty( "value", nb1d );
772     // - 2D elements info
773     // --+ triangles
774     uint nbTriLin = info[SMDSEntity_Triangle];
775     uint nbTriQua = info[SMDSEntity_Quad_Triangle];
776     uint nbTriBiq = info[SMDSEntity_BiQuad_Triangle];
777     uint nbTri = nbTriLin + nbTriQua + nbTriBiq;
778     widget( i2DTriangles, iLinear )->setProperty( "value", nbTriLin );
779     widget( i2DTriangles, iQuadratic )->setProperty( "value", nbTriQua );
780     widget( i2DTriangles, iBiQuadratic )->setProperty( "value", nbTriBiq );
781     widget( i2DTriangles, iTotal )->setProperty( "value", nbTri );
782     // --+ quadrangles
783     uint nbQuadLin = info[SMDSEntity_Quadrangle];
784     uint nbQuadQua = info[SMDSEntity_Quad_Quadrangle];
785     uint nbQuadBiq = info[SMDSEntity_BiQuad_Quadrangle];
786     uint nbQuad = nbQuadLin + nbQuadQua + nbQuadBiq;
787     widget( i2DQuadrangles, iLinear )->setProperty( "value", nbQuadLin );
788     widget( i2DQuadrangles, iQuadratic )->setProperty( "value", nbQuadQua );
789     widget( i2DQuadrangles, iBiQuadratic )->setProperty( "value", nbQuadBiq );
790     widget( i2DQuadrangles, iTotal )->setProperty( "value", nbQuad );
791     // --+ polygons
792     uint nbPolyLin = info[SMDSEntity_Polygon];
793     uint nbPolyQua = info[SMDSEntity_Quad_Polygon];
794     uint nbPoly = nbPolyLin + nbPolyQua;
795     widget( i2DPolygons, iLinear )->setProperty( "value", nbPolyLin );
796     widget( i2DPolygons, iQuadratic )->setProperty( "value", nbPolyQua );
797     widget( i2DPolygons, iTotal )->setProperty( "value", nbPoly );
798     // --+ summary
799     uint nb2dLin = nbTriLin + nbQuadLin + nbPolyLin;
800     uint nb2dQua = nbTriQua + nbQuadQua + nbPolyQua;
801     uint nb2dBiq = nbTriBiq + nbQuadBiq;
802     uint nb2d = nb2dLin + nb2dQua + nb2dBiq;
803     widget( i2D, iLinear )->setProperty( "value", nb2dLin );
804     widget( i2D, iQuadratic )->setProperty( "value", nb2dQua );
805     widget( i2D, iBiQuadratic )->setProperty( "value", nb2dBiq );
806     widget( i2D, iTotal )->setProperty( "value", nb2d );
807     // - 3D elements info
808     // --+ tetras
809     uint nbTetraLin = info[SMDSEntity_Tetra];
810     uint nbTetraQua = info[SMDSEntity_Quad_Tetra];
811     uint nbTetra = nbTetraLin + nbTetraQua;
812     widget( i3DTetrahedrons, iLinear )->setProperty( "value", nbTetraLin );
813     widget( i3DTetrahedrons, iQuadratic )->setProperty( "value", nbTetraQua );
814     widget( i3DTetrahedrons, iTotal )->setProperty( "value", nbTetra );
815     // --+ hexas
816     uint nbHexaLin = info[SMDSEntity_Hexa];
817     uint nbHexaQua = info[SMDSEntity_Quad_Hexa];
818     uint nbHexaBiq = info[SMDSEntity_TriQuad_Hexa];
819     uint nbHexa = nbHexaLin + nbHexaQua + nbHexaBiq;
820     widget( i3DHexahedrons, iLinear )->setProperty( "value", nbHexaLin );
821     widget( i3DHexahedrons, iQuadratic )->setProperty( "value", nbHexaQua );
822     widget( i3DHexahedrons, iBiQuadratic )->setProperty( "value", nbHexaBiq );
823     widget( i3DHexahedrons, iTotal )->setProperty( "value", nbHexa );
824     // --+ pyramids
825     uint nbPyraLin = info[SMDSEntity_Pyramid];
826     uint nbPyraQua = info[SMDSEntity_Quad_Pyramid];
827     uint nbPyra = nbPyraLin + nbPyraQua;
828     widget( i3DPyramids, iLinear )->setProperty( "value", nbPyraLin );
829     widget( i3DPyramids, iQuadratic )->setProperty( "value", nbPyraQua );
830     widget( i3DPyramids, iTotal )->setProperty( "value", nbPyra );
831     // --+ prisms
832     uint nbPentaLin = info[SMDSEntity_Penta];
833     uint nbPentaQua = info[SMDSEntity_Quad_Penta];
834     uint nbPentaBiq = info[SMDSEntity_BiQuad_Penta];
835     uint nbPenta = nbPentaLin + nbPentaQua + nbPentaBiq;
836     widget( i3DPrisms, iLinear )->setProperty( "value", nbPentaLin );
837     widget( i3DPrisms, iQuadratic )->setProperty( "value", nbPentaQua );
838     widget( i3DPrisms, iBiQuadratic )->setProperty( "value", nbPentaBiq );
839     widget( i3DPrisms, iTotal )->setProperty( "value", nbPenta );
840     // --+ hexagonal prisms
841     uint nbHexaPri = info[SMDSEntity_Hexagonal_Prism];
842     widget( i3DHexaPrisms, iTotal )->setProperty( "value", nbHexaPri );
843     // --+ polyhedrons
844     uint nbPolyhedra = info[SMDSEntity_Polyhedra];
845     widget( i3DPolyhedrons, iTotal )->setProperty( "value", nbPolyhedra );
846     // --+ summary
847     uint nb3dLin = nbTetraLin + nbHexaLin + nbPyraLin + nbPentaLin + nbHexaPri + nbPolyhedra;
848     uint nb3dQua = nbTetraQua + nbHexaQua + nbPyraQua + nbPentaQua;
849     uint nb3dBiq = nbHexaBiq + nbPentaBiq;
850     uint nb3d = nb3dLin + nb3dQua + nb3dBiq;
851     widget( i3D, iLinear )->setProperty( "value", nb3dLin );
852     widget( i3D, iQuadratic )->setProperty( "value", nb3dQua );
853     widget( i3D, iBiQuadratic )->setProperty( "value", nb3dBiq );
854     widget( i3D, iTotal )->setProperty( "value", nb3d );
855     // - summary
856     uint nbElemLin = nb1dLin + nb2dLin + nb3dLin;
857     uint nbElemQua = nb1dQua + nb2dQua + nb3dQua;
858     uint nbElemBiq = nb2dBiq + nb3dBiq;
859     uint nbElem = nb0d + nbBalls + nb1d + nb2d + nb3d;
860     widget( iElementsTotal, iLinear )->setProperty( "value", nbElemLin );
861     widget( iElementsTotal, iQuadratic )->setProperty( "value", nbElemQua );
862     widget( iElementsTotal, iBiQuadratic )->setProperty( "value", nbElemBiq );
863     widget( iElementsTotal, iTotal )->setProperty( "value", nbElem );
864
865     // show 'Load' button if data was not loaded yet
866     widget( iEnd, iSingle )->setVisible( !proxy.isValid() );
867
868     // until data is loaded from study file, type of elements in a sub-mesh or group
869     // can be undefined in some cases
870     if ( !proxy.isValid() )
871     {
872       // two cases are possible:
873       // 1. type of 2D or 3D elements is unknown but their nb is OK (for a sub-mesh)
874       // 2. there is no info at all (for a group on geom or on filter)
875       if ( info.count( SMDSEntity_Node, SMDSEntity_Last ) > 0 ) // believe it is a sub-mesh
876       {
877         if ( nb2dLin + nb2dQua + nb2dBiq > 0 )
878         {
879           // we know nothing about triangles, quadranges and polygons
880           for ( int i = i2DTriangles; i < i2DEnd; i++ )
881           {
882             for ( int j = iTotal; j < iNbColumns; j++ )
883             {
884               if ( widget( i, j ) )
885                 widget( i, j )->setProperty( "text", "?" );
886             }
887           }
888           // we don't know if elements are linear, quadratic or bi-quadratic
889           for ( int j = iLinear; j < iNbColumns; j++ )
890           {
891             if ( widget( i2D, j ) )
892               widget( i2D, j )->setProperty( "text", "?" );
893             if ( widget( iElementsTotal, j ) )
894               widget( iElementsTotal, j )->setProperty( "text", "?" );
895           }
896         }
897         else if ( nb3dLin + nb3dQua + nb3dBiq > 0 )
898         {
899           // we know nothing about tetras, hexas, etc.
900           for ( int i = i3DTetrahedrons; i < i3DEnd; i++ )
901           {
902             for ( int j = iTotal; j < iNbColumns; j++ )
903             {
904               if ( widget( i, j ) )
905                 widget( i, j )->setProperty( "text", "?" );
906             }
907           }
908           // we don't know if elements are linear, quadratic or bi-quadratic
909           for ( int j = iLinear; j < iNbColumns; j++ )
910           {
911             if ( widget( i3D, j ) )
912               widget( i3D, j )->setProperty( "text", "?" );
913             if ( widget( iElementsTotal, j ) )
914               widget( iElementsTotal, j )->setProperty( "text", "?" );
915           }
916         }
917       }
918       else
919       {
920         // we know nothing about nodes :(
921         widget( iNodes, iTotal )->setProperty( "text", "?" );
922         // we know nothing about elements :(
923         for ( int i = iElementsTotal; i < iElementsEnd; i++ )
924         {
925           for ( int j = iTotal; j < iNbColumns; j++ )
926           {
927             if ( widget( i, j ) )
928               widget( i, j )->setProperty( "text", "?" );
929           }
930         }
931       }
932     }
933   }
934 }
935
936 /*!
937   \brief Update information in panel.
938 */
939 void SMESHGUI_BaseInfo::updateInfo()
940 {
941   showInfo( myProxy );
942 }
943
944 /*!
945   \brief Load mesh from a study file.
946 */
947 void SMESHGUI_BaseInfo::loadMesh()
948 {
949   SUIT_OverrideCursor wc;
950   if ( myProxy )
951   {
952     myProxy.load();
953     updateInfo();
954   }
955 }
956
957 /*!
958   \brief Reset panel (clear all data).
959 */
960 void SMESHGUI_BaseInfo::clear()
961 {
962   // - name
963   widget( iName, iSingle )->setProperty( "text", QString() );
964   // - type
965   widget( iObject, iSingle )->setProperty( "text", QString() );
966   // - nodes
967   widget( iNodes, iTotal )->setProperty( "value", 0 );
968   // - elements
969   for ( int i = iElementsTotal; i < iElementsEnd; i++ )
970   {
971     for ( int j = iTotal; j < iNbColumns; j++ )
972     {
973       if ( widget( i, j ) )
974         widget( i, j )->setProperty( "value", 0 );
975     }
976   }
977   // hide 'Load' button
978   widget( iEnd, iSingle )->setVisible( false );
979 }
980
981 /*!
982   \brief Register widget in a grid.
983   \param w Widget being added.
984   \param row Row index in a grid.
985   \param column Column index in a grid.
986   \param colspan Number of columns to span in a grid. Defaults to 1.
987   \return Just added widget.
988 */
989 QWidget* SMESHGUI_BaseInfo::addWidget( QWidget* w, int row, int column, int colspan )
990 {
991   if ( !myWidgets.contains( row ) )
992     myWidgets[row] = wlist();
993   myWidgets[row][column] = w;
994   dynamic_cast<QGridLayout*>( layout() )->addWidget( w, row, column, 1, colspan );
995   return w;
996 }
997
998 /*!
999   \brief Get registered widget.
1000   \param row Row index in a grid.
1001   \param column Column index in a grid.
1002   \return Widget stored in a given grid cell (0 if there's no widget).
1003 */
1004 QWidget* SMESHGUI_BaseInfo::widget( int row, int column ) const
1005 {
1006   return myWidgets.contains( row ) && myWidgets[row].contains( column ) ? myWidgets[row][column] : 0;
1007 }
1008
1009 /*!
1010   \brief Get text value from registered widget.
1011   \param row Row index in a grid.
1012   \param column Column index in a grid.
1013   \return Value string (empty string if no label in given cell).
1014 */
1015 QString SMESHGUI_BaseInfo::value( int row, int column ) const
1016 {
1017   return widgetValue( widget( row, column ) );
1018 }
1019
1020 /*!
1021   \brief Show/hide group(s) of widgets.
1022   \param startRow Starting grid row.
1023   \param lastRow Last grid row.
1024   \param on Visibility flag.
1025 */
1026 void SMESHGUI_BaseInfo::setFieldsVisible( int startRow, int lastRow, bool on )
1027 {
1028   startRow = qMax( 0, startRow );
1029   lastRow = qMin( lastRow, (int)iEnd );
1030   for ( int i = startRow; i <= lastRow; i++ )
1031   {
1032     wlist widgets = myWidgets[i];
1033     foreach ( QWidget* w, widgets )
1034       w->setVisible( on );
1035   }
1036 }
1037
1038 /*!
1039   \brief Write information from panel to output stream.
1040   \param out Text stream output.
1041 */
1042 void SMESHGUI_BaseInfo::saveInfo( QTextStream& out )
1043 {
1044   // title
1045   QString title = tr( "BASE_INFO" );
1046   out << ruler( title.size() ) << endl;
1047   out << title << endl;
1048   out << ruler( title.size() ) << endl;
1049   out << endl;
1050
1051   // object info
1052   // - name
1053   out << tr( "NAME_LAB" ) << spacing() << value( iName, iSingle ) << endl;
1054   // - type
1055   out << tr( "OBJECT_LAB" ) << spacing() << value( iObject, iSingle ) << endl;
1056   // - --- (separator)
1057   out << endl;
1058
1059   // node info
1060   out << tr( "NODES_LAB" ) << spacing() << value( iNodes, iTotal ) << endl;
1061   // - --- (separator)
1062   out << endl;
1063
1064   // element info
1065   QString lin = tr( "LINEAR_LAB" ) + ":" + spacing();
1066   QString qua = tr( "QUADRATIC_LAB" ) + ":" + spacing();
1067   QString biq = tr( "BI_QUADRATIC_LAB" ) + ":" + spacing();
1068   // - summary
1069   out << tr( "ELEMENTS_LAB" ) << spacing() << value( iElementsTotal, iTotal ) << endl;
1070   out << indent(1) << lin << value( iElementsTotal, iLinear ) << endl;
1071   out << indent(1) << qua << value( iElementsTotal, iQuadratic ) << endl;
1072   out << indent(1) << biq << value( iElementsTotal, iBiQuadratic ) << endl;
1073   // - --- (separator)
1074   out << endl;
1075   // - 0D elements info
1076   out << indent(1) << tr( "0D_LAB" ) << spacing() << value( i0D, iTotal ) << endl;
1077   // - --- (separator)
1078   out << endl;
1079   // - balls info
1080   out << indent(1) << tr( "BALL_LAB" ) << spacing() << value( iBalls, iTotal ) << endl;
1081   // - --- (separator)
1082   out << endl;
1083   // - 1D elements info
1084   out << indent(1) << tr( "1D_LAB" ) << spacing() << value( i1D, iTotal ) << endl;
1085   out << indent(2) << lin << value( i1D, iLinear ) << endl;
1086   out << indent(2) << qua << value( i1D, iQuadratic ) << endl;
1087   // - --- (separator)
1088   out << endl;
1089   // - 2D elements info
1090   // - summary
1091   out << indent(1) << tr( "2D_LAB" ) << spacing() << value( i2D, iTotal ) << endl;
1092   out << indent(2) << lin << value( i2D, iLinear ) << endl;
1093   out << indent(2) << qua << value( i2D, iQuadratic ) << endl;
1094   out << indent(2) << biq << value( i2D, iBiQuadratic ) << endl;
1095   // - --- (separator)
1096   out << endl;
1097   // --+ triangles
1098   out << indent(2) << tr( "TRIANGLES_LAB" ) << spacing() << value( i2DTriangles, iTotal ) << endl;
1099   out << indent(3) << lin << value( i2DTriangles, iLinear ) << endl;
1100   out << indent(3) << qua << value( i2DTriangles, iQuadratic ) << endl;
1101   out << indent(3) << biq << value( i2DTriangles, iBiQuadratic ) << endl;
1102   // --+ quadrangles
1103   out << indent(2) << tr( "QUADRANGLES_LAB" ) << spacing() << value( i2DQuadrangles, iTotal ) << endl;
1104   out << indent(3) << lin << value( i2DQuadrangles, iLinear ) << endl;
1105   out << indent(3) << qua << value( i2DQuadrangles, iQuadratic ) << endl;
1106   out << indent(3) << biq << value( i2DQuadrangles, iBiQuadratic ) << endl;
1107   // --+ polygons
1108   out << indent(2) << tr( "POLYGONS_LAB" ) << spacing() << value( i2DPolygons, iTotal ) << endl;
1109   out << indent(3) << lin << value( i2DPolygons, iLinear ) << endl;
1110   out << indent(3) << qua << value( i2DPolygons, iQuadratic ) << endl;
1111   // - --- (separator)
1112   out << endl;
1113   // - 3D elements info
1114   // --+ summary
1115   out << indent(1) << tr( "3D_LAB" ) << spacing() << value( i3D, iTotal ) << endl;
1116   out << indent(2) << lin << value( i3D, iLinear ) << endl;
1117   out << indent(2) << qua << value( i3D, iQuadratic ) << endl;
1118   out << indent(2) << biq << value( i3D, iBiQuadratic ) << endl;
1119   // - --- (separator)
1120   out << endl;
1121   // --+ tetras
1122   out << indent(2) << tr( "TETRAHEDRONS_LAB" ) << spacing() << value( i3DTetrahedrons, iTotal ) << endl;
1123   out << indent(3) << lin << value( i3DTetrahedrons, iLinear ) << endl;
1124   out << indent(3) << qua << value( i3DTetrahedrons, iQuadratic ) << endl;
1125   // --+ hexas
1126   out << indent(2) << tr( "HEXAHEDONRS_LAB" ) << spacing() << value( i3DHexahedrons, iTotal ) << endl;
1127   out << indent(3) << lin << value( i3DHexahedrons, iLinear ) << endl;
1128   out << indent(3) << qua << value( i3DHexahedrons, iQuadratic ) << endl;
1129   out << indent(3) << biq << value( i3DHexahedrons, iBiQuadratic ) << endl;
1130   // --+ pyramids
1131   out << indent(2) << tr( "PYRAMIDS_LAB" ) << spacing() << value( i3DPyramids, iTotal ) << endl;
1132   out << indent(3) << lin << value( i3DPyramids, iLinear ) << endl;
1133   out << indent(3) << qua << value( i3DPyramids, iQuadratic ) << endl;
1134   // --+ prisms
1135   out << indent(2) << tr( "PRISMS_LAB" ) << spacing() << value( i3DPrisms, iTotal ) << endl;
1136   out << indent(3) << lin << value( i3DPrisms, iLinear ) << endl;
1137   out << indent(3) << qua << value( i3DPrisms, iQuadratic ) << endl;
1138   out << indent(3) << biq << value( i3DPrisms, iBiQuadratic ) << endl;
1139   // --+ hexagonal prisms
1140   out << indent(2) << tr( "HEX_PRISMS_LAB" ) << spacing() << value( i3DHexaPrisms, iTotal ) << endl;
1141   // --+ polyhedrons
1142   out << indent(2) << tr( "POLYHEDRONS_LAB" ) << spacing() << value( i3DPolyhedrons, iTotal ) << endl;
1143   // - --- (separator)
1144   out << endl;
1145 }
1146
1147 ////////////////////////////////////////////////////////////////////////////////
1148 /// \class InfoWriter
1149 /// \brief Base info writer class.
1150 /// \internal
1151 ////////////////////////////////////////////////////////////////////////////////
1152
1153 class InfoWriter
1154 {
1155 protected:
1156   int myPrecision;
1157   bool myRecursive;
1158 public:
1159   InfoWriter( bool = false );
1160   void write( const QString&, bool = false );
1161   void write( const QString&, const QString&, bool = false );
1162   void write( const QString&, int, bool = false );
1163   void write( const QString&, double, bool = false );
1164   void write( const QString&, const SMESH::XYZ&, bool = false );
1165   virtual void indent() {}
1166   virtual void unindent() {}
1167   virtual void separator() {}
1168 protected:
1169   virtual void put( const QString&, const QString&, bool = false ) = 0;
1170 };
1171
1172 InfoWriter::InfoWriter( bool r ): myRecursive(r)
1173 {
1174   myPrecision = SMESHGUI::resourceMgr()->integerValue( "SMESH", "length_precision", 6 );
1175 }
1176
1177 void InfoWriter::write( const QString& key, bool emphasize )
1178 {
1179   put( key, QString(), emphasize );
1180 }
1181
1182 void InfoWriter::write( const QString& key, const QString& value, bool emphasize )
1183 {
1184   put( key, value, emphasize );
1185 }
1186
1187 void InfoWriter::write( const QString& key, int value, bool emphasize )
1188 {
1189   put( key, QString::number( value ), emphasize );
1190 }
1191
1192 void InfoWriter::write( const QString& key, double value, bool emphasize )
1193 {
1194   put( key, QString::number( value, myPrecision > 0 ? 'f' : 'g', qAbs( myPrecision ) ), emphasize );
1195 }
1196
1197 void InfoWriter::write( const QString& key, const SMESH::XYZ& value, bool emphasize )
1198 {
1199   if ( myRecursive )
1200   {
1201     write( key, emphasize );
1202     indent();
1203     write( "X", value.x() );
1204     write( "Y", value.y() );
1205     write( "Z", value.z() );
1206     unindent(); 
1207   }
1208   else
1209   {
1210     QStringList vl;
1211     vl << QString::number( value.x(), myPrecision > 0 ? 'f' : 'g', qAbs( myPrecision ) );
1212     vl << QString::number( value.y(), myPrecision > 0 ? 'f' : 'g', qAbs( myPrecision ) );
1213     vl << QString::number( value.z(), myPrecision > 0 ? 'f' : 'g', qAbs( myPrecision ) );
1214     put( key, vl.join( ", " ), emphasize );
1215   }
1216 }
1217
1218 ////////////////////////////////////////////////////////////////////////////////
1219 /// \class SimpleWriter
1220 /// \brief Base text writer.
1221 /// \internal
1222 ////////////////////////////////////////////////////////////////////////////////
1223
1224 class SimpleWriter: public InfoWriter
1225 {
1226 protected:
1227   int myIndent;
1228 public:
1229   SimpleWriter();
1230   void indent();
1231   void unindent();
1232   void separator();
1233 protected:
1234   void put( const QString&, const QString&, bool );
1235   virtual QString spacer() const;
1236   virtual QString decorate( const QString& ) const;
1237   virtual void dumpLine( const QString& ) = 0;
1238 };
1239
1240 SimpleWriter::SimpleWriter(): InfoWriter(false), myIndent(0)
1241 {
1242 }
1243
1244 void SimpleWriter::indent()
1245 {
1246   myIndent += 1;
1247 }
1248
1249 void SimpleWriter::unindent()
1250 {
1251   myIndent = qMax( myIndent-1, 0 );
1252 }
1253
1254 void SimpleWriter::separator()
1255 {
1256   write( "" );
1257 }
1258
1259 QString SimpleWriter::spacer() const
1260 {
1261   return " ";
1262 }
1263
1264 QString SimpleWriter::decorate( const QString& s ) const
1265 {
1266   return s;
1267 }
1268
1269 void SimpleWriter::put( const QString& key, const QString& value, bool emphasize )
1270 {
1271   QString line;
1272   line += ::indent( spacer(), myIndent*4 );
1273   line += decorate( key );
1274   if ( !value.isEmpty() )
1275   {
1276     line += ":";
1277     line += emphasize ? decorate( value ) : value;
1278   }
1279   dumpLine( line );
1280 }
1281
1282 ////////////////////////////////////////////////////////////////////////////////
1283 /// \class StreamWriter
1284 /// \brief Writer for QTextStream.
1285 /// \internal
1286 ////////////////////////////////////////////////////////////////////////////////
1287
1288 class StreamWriter: public SimpleWriter
1289 {
1290   QTextStream& myOut;
1291 public:
1292   StreamWriter( QTextStream& );
1293 protected:
1294   void dumpLine( const QString& );
1295 };
1296
1297 StreamWriter::StreamWriter( QTextStream& out ): SimpleWriter(), myOut(out)
1298 {
1299 }
1300
1301 void StreamWriter::dumpLine( const QString& line )
1302 {
1303   myOut << line;
1304   myOut << endl;
1305 }
1306
1307 ////////////////////////////////////////////////////////////////////////////////
1308 /// \class TextWriter
1309 /// \brief Writer for QTextBrowser.
1310 /// \internal
1311 ////////////////////////////////////////////////////////////////////////////////
1312
1313 class TextWriter: public SimpleWriter
1314 {
1315   QTextBrowser* myInfo;
1316 public:
1317   TextWriter( QTextBrowser* );
1318 protected:
1319   QString spacer() const;
1320   QString decorate( const QString& ) const;
1321   void dumpLine( const QString& );
1322 };
1323
1324 TextWriter::TextWriter( QTextBrowser* w ): SimpleWriter(), myInfo(w)
1325 {
1326 }
1327
1328 QString TextWriter::spacer() const
1329 {
1330   return "&nbsp;";
1331 }
1332
1333 QString TextWriter::decorate( const QString& s ) const
1334 {
1335   return bold( s );
1336 }
1337
1338 void TextWriter::dumpLine( const QString& line )
1339 {
1340   myInfo->append( line );
1341 }
1342
1343 ////////////////////////////////////////////////////////////////////////////////
1344 /// \class TreeWriter
1345 /// \brief Writer for QTreeWidget.
1346 /// \internal
1347 ////////////////////////////////////////////////////////////////////////////////
1348
1349 class TreeWriter: public InfoWriter
1350 {
1351   QTreeWidget* myInfo;
1352   QTreeWidgetItem* myCurrentItem;
1353   TreeItemCreator* myCreator;
1354 public:
1355   TreeWriter( QTreeWidget*, TreeItemCreator* );
1356   ~TreeWriter();
1357   void indent();
1358   void unindent();
1359 protected:
1360   void put( const QString&, const QString&, bool = false );
1361 };
1362
1363 TreeWriter::TreeWriter( QTreeWidget* w, TreeItemCreator* c ):
1364   InfoWriter(true), myInfo(w), myCurrentItem(0), myCreator(c)
1365 {
1366 }
1367
1368 TreeWriter::~TreeWriter()
1369 {
1370   delete myCreator;
1371 }
1372
1373 void TreeWriter::put( const QString& key, const QString& value, bool emphasize )
1374 {
1375   //std::string sss = myCurrentItem ? myCurrentItem->text(0).toStdString() : "";
1376   int options = Bold;
1377   if ( emphasize ) options |= AllColumns;
1378   QTreeWidgetItem* item = myCreator->createItem( myCurrentItem, options );
1379   item->setText( 0, key );
1380   if ( !value.isEmpty() )
1381   {
1382     QString val = value;
1383     if ( value.startsWith( "<a href" )) // connectivity encoded as: <a href = "nodes://host/1 2">1 2</a>
1384     {
1385       int role = ( value[11] == 'n' ) ? NodeConnectivity : ElemConnectivity;
1386       val = value.mid( value.lastIndexOf( '>', -5 ) + 1 ); // ==>   1 2</a>
1387       val.chop( 4 );
1388       item->setData( 1, TypeRole, role );
1389     }
1390     item->setText( 1, val );
1391   }
1392 }
1393
1394 void TreeWriter::indent()
1395 {
1396   QTreeWidgetItem* item = myCurrentItem ? myCurrentItem : myInfo->invisibleRootItem();
1397   if ( item->childCount() > 0 )
1398     myCurrentItem = item->child( item->childCount()-1 );
1399 }
1400
1401 void TreeWriter::unindent()
1402 {
1403   if ( myCurrentItem )
1404     myCurrentItem = myCurrentItem->parent();
1405 }
1406
1407 ////////////////////////////////////////////////////////////////////////////////
1408 /// \class SMESHGUI_ElemInfo
1409 /// \brief Base class for the mesh element information widget.
1410 ///
1411 /// Displays the detail information about given mesh node(s) or element(s).
1412 /// Default class does not provide working implementation but onle general
1413 /// functionalities; main work is done in sub-classes.
1414 ////////////////////////////////////////////////////////////////////////////////
1415
1416 /*!
1417   \brief Constructor.
1418   \param parent Parent widget. Defaults to 0.
1419 */
1420 SMESHGUI_ElemInfo::SMESHGUI_ElemInfo( QWidget* parent ): SMESHGUI_Info( parent ), myWhat( ShowNone )
1421 {
1422   myFrame = new QWidget( this );
1423   myExtra = new ExtraWidget( this );
1424
1425   QVBoxLayout* vbl = new QVBoxLayout( this );
1426   vbl->setMargin( 0 );
1427   vbl->setSpacing( SPACING );
1428   vbl->addWidget( myFrame );
1429   vbl->addWidget( myExtra );
1430
1431   connect( myExtra->prev, SIGNAL( clicked() ), this, SLOT( showPrevious() ) );
1432   connect( myExtra->next, SIGNAL( clicked() ), this, SLOT( showNext() ) );
1433
1434   clear();
1435 }
1436
1437 /*!
1438   \brief Destructor.
1439 */
1440 SMESHGUI_ElemInfo::~SMESHGUI_ElemInfo()
1441 {
1442 }
1443
1444 /*!
1445   \brief Show information on given node / element.
1446   \param proxy Object to compute information on (mesh, sub-mesh, group, ID source).
1447   \param id Mesh node / element ID.
1448   \param isElement If \c true, show element info; otherwise show node info.
1449 */
1450 void SMESHGUI_ElemInfo::showInfo( const SMESH::SelectionProxy& proxy, uint id, bool isElement )
1451 {
1452   QSet<uint> ids;
1453   ids << id;
1454   showInfo( proxy, ids, isElement );
1455 }
1456
1457 /*!
1458   \brief Show information on given nodes / elements.
1459   \param proxy Object to compute information on (mesh, sub-mesh, group, ID source).
1460   \param ids Mesh nodes / elements IDs.
1461   \param isElement If \c true, show element info; otherwise show node info.
1462 */
1463 void SMESHGUI_ElemInfo::showInfo( const SMESH::SelectionProxy& proxy, QSet<uint> ids, bool isElement )
1464 {
1465   if ( !proxy )
1466   {
1467     clear();
1468     return;
1469   }
1470
1471   QList<uint> newIds = ids.toList();
1472   qSort( newIds );
1473   int what = isElement ? ShowElements : ShowNodes;
1474   
1475   if ( myProxy == proxy && myIDs == newIds && myWhat == what )
1476     return;
1477   
1478   myProxy = proxy;
1479   myProxy.refresh(); // try to re-initialize actor
1480
1481   clear();
1482
1483   myIDs = newIds;
1484   myWhat = what;
1485   myIndex = 0;
1486   
1487   updateControls();
1488   information( myIDs.mid( myIndex*blockSize(), blockSize() ) );
1489 }
1490
1491 /*!
1492   \brief Show information on given group.
1493   \param proxy Object to compute information on (group).
1494 */
1495 void SMESHGUI_ElemInfo::showInfo( const SMESH::SelectionProxy& proxy )
1496 {
1497   if ( !proxy || proxy.groupElementType() == SMESH::ALL ) // null proxy or not a group
1498   {
1499     clear();
1500     return;
1501   }
1502
1503   showInfo( proxy, proxy.ids(), proxy.groupElementType() != SMESH::NODE );
1504 }
1505
1506 /*!
1507   \brief Reset panel (clear all data).
1508 */
1509 void SMESHGUI_ElemInfo::clear()
1510 {
1511   myIDs.clear();
1512   myIndex = 0;
1513   clearInternal();
1514   updateControls();
1515 }
1516
1517 /*!
1518   \brief Get central area widget.
1519   \return Central widget.
1520 */
1521 QWidget* SMESHGUI_ElemInfo::centralWidget() const
1522 {
1523   return myFrame;
1524 }
1525
1526 /*!
1527   \brief Get current mesh proxy object information is shown on.
1528   \return Current proxy.
1529 */
1530 SMESH::SelectionProxy SMESHGUI_ElemInfo::proxy() const
1531 {
1532   return myProxy;
1533 }
1534
1535 /*!
1536   \brief Get current info mode.
1537   \return Current panel mode.
1538 */
1539 int SMESHGUI_ElemInfo::what() const
1540 {
1541   return myWhat;
1542 }
1543
1544 /*!
1545   \brief Get title for given element type.
1546   \param type Mesh element type.
1547   \param multiple Use plural form. Defaults to \c false.
1548   \return Element type's title.
1549 */
1550 QString SMESHGUI_ElemInfo::type2str( int type, bool multiple )
1551 {
1552   QString title;
1553   switch ( type )
1554   {
1555   case SMDSAbs_Edge:
1556     title = multiple ? tr( "EDGES" ) : tr( "EDGE" ) ; break;
1557   case SMDSAbs_Face:
1558     title = multiple ? tr( "FACES" ) : tr( "FACE" ); break;
1559   case SMDSAbs_Volume:
1560     title = multiple ? tr( "VOLUMES" ) : tr( "VOLUME" ); break;
1561   case SMDSAbs_0DElement:
1562     title = multiple ? tr( "0D_ELEMENTS" ) : tr( "0D_ELEMENT" ); break;
1563   case SMDSAbs_Ball:
1564     title = multiple ? tr( "BALL_ELEMENTS" ) : tr( "BALL" ); break;
1565   default:
1566     break;
1567   }
1568   return title;
1569 }
1570
1571 /*!
1572   \brief Get title for given shape type.
1573   \param type Shape type.
1574   \return Shape type's title.
1575 */
1576 QString SMESHGUI_ElemInfo::stype2str( int type )
1577 {
1578   QString title;
1579   switch ( type )
1580   {
1581   case GEOM::VERTEX:
1582     title = tr( "GEOM_VERTEX" ); break;
1583   case GEOM::EDGE:
1584     title = tr( "GEOM_EDGE" ); break;
1585   case GEOM::FACE:
1586     title = tr( "GEOM_FACE" ); break;
1587   case GEOM::SOLID:
1588   default:
1589     title = tr( "GEOM_SOLID" ); break;
1590     break;
1591   }
1592   return title;
1593 }
1594
1595 /*!
1596   \brief Get title for given element type.
1597   \param type Mesh element type.
1598   \return Element type's title.
1599 */
1600 QString SMESHGUI_ElemInfo::etype2str( int type )
1601 {
1602   QString title;
1603   switch ( type )
1604   {
1605   case SMESH::Entity_0D:
1606     title = tr( "SMESH_ELEM0D" ); break;
1607   case SMESH::Entity_Edge:
1608     title = tr( "SMESH_EDGE" ); break;
1609   case SMESH::Entity_Quad_Edge:
1610     title = tr( "SMESH_QUADRATIC_EDGE" ); break;
1611   case SMESH::Entity_Triangle:
1612     title = tr( "SMESH_TRIANGLE" ); break;
1613   case SMESH::Entity_Quad_Triangle:
1614     title = tr( "SMESH_QUADRATIC_TRIANGLE" ); break;
1615   case SMESH::Entity_BiQuad_Triangle:
1616     title = tr( "SMESH_BIQUADRATIC_TRIANGLE" ); break;
1617   case SMESH::Entity_Quadrangle:
1618     title = tr( "SMESH_QUADRANGLE" ); break;
1619   case SMESH::Entity_Quad_Quadrangle:
1620     title = tr( "SMESH_QUADRATIC_QUADRANGLE" ); break;
1621   case SMESH::Entity_BiQuad_Quadrangle:
1622     title = tr( "SMESH_BIQUADRATIC_QUADRANGLE" ); break;
1623   case SMESH::Entity_Polygon:
1624     title = tr( "SMESH_POLYGON" ); break;
1625   case SMESH::Entity_Quad_Polygon:
1626     title = tr( "SMESH_QUADRATIC_POLYGON" ); break;
1627   case SMESH::Entity_Tetra:
1628     title = tr( "SMESH_TETRAHEDRON" ); break;
1629   case SMESH::Entity_Quad_Tetra:
1630     title = tr( "SMESH_QUADRATIC_TETRAHEDRON" ); break;
1631   case SMESH::Entity_Pyramid:
1632     title = tr( "SMESH_PYRAMID" ); break;
1633   case SMESH::Entity_Quad_Pyramid:
1634     title = tr( "SMESH_QUADRATIC_PYRAMID" ); break;
1635   case SMESH::Entity_Hexa:
1636     title = tr( "SMESH_HEXAHEDRON" ); break;
1637   case SMESH::Entity_Quad_Hexa:
1638     title = tr( "SMESH_QUADRATIC_HEXAHEDRON" ); break;
1639   case SMESH::Entity_TriQuad_Hexa:
1640     title = tr( "SMESH_TRIQUADRATIC_HEXAHEDRON" ); break;
1641   case SMESH::Entity_Penta:
1642     title = tr( "SMESH_PENTA" ); break;
1643   case SMESH::Entity_Quad_Penta:
1644     title = tr( "SMESH_QUADRATIC_PENTAHEDRON" ); break;
1645   case SMESH::Entity_BiQuad_Penta:
1646     title = tr( "SMESH_BIQUADRATIC_PENTAHEDRON" ); break;
1647   case SMESH::Entity_Hexagonal_Prism:
1648     title = tr( "SMESH_HEX_PRISM" ); break;
1649   case SMESH::Entity_Polyhedra:
1650     title = tr( "SMESH_POLYEDRON" ); break;
1651   case SMESH::Entity_Quad_Polyhedra:
1652     title = tr( "SMESH_QUADRATIC_POLYEDRON" ); break;
1653   case SMESH::Entity_Ball:
1654     title = tr( "SMESH_BALL" ); break;
1655   default:
1656     break;
1657   }
1658   return title;
1659 }
1660
1661 /*!
1662   \brief Get title for given quality control.
1663   \param type Mesh control type.
1664   \return Quality control's title.
1665 */
1666 QString SMESHGUI_ElemInfo::ctrl2str( int control )
1667 {
1668   QString title;
1669   switch ( control )
1670   {
1671   case SMESH::FT_AspectRatio:
1672     title = tr( "ASPECTRATIO_ELEMENTS" ); break;
1673   case SMESH::FT_AspectRatio3D:
1674     title = tr( "ASPECTRATIO_3D_ELEMENTS" ); break;
1675   case SMESH::FT_Warping:
1676     title = tr( "WARP_ELEMENTS" ); break;
1677   case SMESH::FT_MinimumAngle:
1678     title = tr( "MINIMUMANGLE_ELEMENTS" ); break;
1679   case SMESH::FT_Taper:
1680     title = tr( "TAPER_ELEMENTS" ); break;
1681   case SMESH::FT_Skew:
1682     title = tr( "SKEW_ELEMENTS" ); break;
1683   case SMESH::FT_Area:
1684     title = tr( "AREA_ELEMENTS" ); break;
1685   case SMESH::FT_Volume3D:
1686     title = tr( "VOLUME_3D_ELEMENTS" ); break;
1687   case SMESH::FT_ScaledJacobian:
1688     title = tr( "SCALED_JACOBIAN" ); break;  
1689   case SMESH::FT_MaxElementLength2D:
1690     title = tr( "MAX_ELEMENT_LENGTH_2D" ); break;
1691   case SMESH::FT_MaxElementLength3D:
1692     title = tr( "MAX_ELEMENT_LENGTH_3D" ); break;
1693   case SMESH::FT_Length:
1694     title = tr( "LENGTH_EDGES" ); break;
1695   case SMESH::FT_Length2D:
1696   case SMESH::FT_Length3D:
1697     title = tr( "MIN_ELEM_EDGE" ); break;
1698   case SMESH::FT_BallDiameter:
1699     title = tr( "BALL_DIAMETER" ); break;
1700   default:
1701     break;
1702   }
1703   return title;
1704 }
1705
1706 /*!
1707   \brief Write information on given mesh nodes / elements.
1708   \param writer Information writer.
1709   \param ids Nodes / elements IDs.
1710 */
1711 void SMESHGUI_ElemInfo::writeInfo( InfoWriter* writer, const QList<uint>& ids )
1712 {
1713   if ( !proxy() )
1714     return;
1715
1716   bool grp_details = SMESHGUI::resourceMgr()->booleanValue( "SMESH", "elem_info_grp_details", false );
1717   int cprecision = SMESHGUI::resourceMgr()->booleanValue( "SMESH", "use_precision", false ) ? 
1718     SMESHGUI::resourceMgr()->integerValue( "SMESH", "controls_precision", -1 ) : -1;
1719
1720   SMESH::XYZ xyz;
1721   SMESH::Connectivity connectivity;
1722   SMESH::Position position;
1723   bool ok;
1724
1725   foreach ( uint id, ids )
1726   {
1727     writer->separator();
1728
1729     if ( what() == ShowNodes )
1730     {
1731       // show node info
1732       // - check that node exists
1733       if ( !proxy().hasNode( id ) )
1734         continue;
1735       // - id
1736       writer->write( tr( "NODE" ), (int)id, true );
1737       writer->indent();
1738       // - coordinates
1739       ok = proxy().nodeCoordinates( id, xyz );
1740       if ( ok )
1741       {
1742         writer->write( tr( "COORDINATES" ), xyz );
1743       }
1744       // - connectivity
1745       ok = proxy().nodeConnectivity( id, connectivity );
1746       if ( ok )
1747       {
1748         if ( !connectivity.isEmpty() )
1749         {
1750           writer->write( tr( "CONNECTIVITY" ) );
1751           writer->indent();
1752           for ( int i = SMDSAbs_Edge; i <= SMDSAbs_Ball; i++ )
1753           {
1754             QString formatted = formatConnectivity( connectivity, i );
1755             if ( !formatted.isEmpty() )
1756               writer->write( type2str( i, true ), formatted );
1757           }
1758           writer->unindent();
1759         }
1760         else
1761         {
1762           writer->write( tr( "CONNECTIVITY" ), tr( "FREE_NODE" ) );
1763         }
1764       }
1765       // - position
1766       ok = proxy().nodePosition( id, position );
1767       if ( ok && position.isValid() )
1768       {
1769         writer->write( tr( "POSITION" ), (stype2str( position.shapeType() ) + " #%1").arg( position.shapeId() ) );
1770         writer->indent();
1771         if ( position.hasU() )
1772           writer->write( tr("U_POSITION"), position.u() );
1773         if ( position.hasV() )
1774           writer->write( tr("V_POSITION"), position.v() );
1775         writer->unindent();
1776       }
1777       // - groups node belongs to
1778       QList<SMESH::SelectionProxy> groups = proxy().nodeGroups( id );
1779       bool topCreated = false;
1780       foreach( SMESH::SelectionProxy group, groups )
1781       {
1782         if ( group && !group.name().isEmpty() )
1783         {
1784           if ( !topCreated )
1785           {
1786             writer->write( SMESHGUI_AddInfo::tr( "GROUPS" ) );
1787             writer->indent();
1788             topCreated = true;
1789           }
1790           writer->write( group.name().trimmed() ); // trim name
1791           if ( grp_details )
1792           {
1793             writer->indent();
1794             int type = group.type();
1795             if ( type == SMESH::SelectionProxy::GroupStd )
1796             {
1797               writer->write( SMESHGUI_AddInfo::tr( "TYPE" ), SMESHGUI_AddInfo::tr( "STANDALONE_GROUP" ) );
1798             }
1799             else if ( type == SMESH::SelectionProxy::GroupGeom )
1800             {
1801               writer->write( SMESHGUI_AddInfo::tr( "TYPE" ), SMESHGUI_AddInfo::tr( "GROUP_ON_GEOMETRY" ) );
1802               writer->write( SMESHGUI_AddInfo::tr( "GEOM_OBJECT" ), group.shapeName() );
1803             }
1804             else if ( type == SMESH::SelectionProxy::GroupFilter )
1805             {
1806               writer->write( SMESHGUI_AddInfo::tr( "TYPE" ), SMESHGUI_AddInfo::tr( "GROUP_ON_FILTER" ) );
1807             }
1808             int size = group.size();
1809             if ( size != -1 )
1810               writer->write( SMESHGUI_AddInfo::tr( "SIZE" ), size );
1811             QColor color = group.color();
1812             if ( color.isValid() )
1813               writer->write( SMESHGUI_AddInfo::tr( "COLOR" ), color.name() );
1814             writer->unindent();
1815           }
1816         }
1817       }
1818       if ( topCreated )
1819         writer->unindent();
1820       writer->unindent();
1821     }
1822     else if ( what() == ShowElements )
1823     {
1824       // show element info
1825       // - check that element exists
1826       if ( !proxy().hasElement( id ) )
1827         continue;
1828       // - id & type
1829       int type = proxy().elementType( id );
1830       if  ( type == SMESH::ALL )
1831         continue;
1832       writer->write( type2str( type ), (int)id, true );
1833       writer->indent();
1834       // - geometry type
1835       type = proxy().elementEntityType( id );
1836       writer->write( tr( "TYPE" ), etype2str( type ) );
1837       // - connectivity
1838       if ( type == SMESH::Entity_Polyhedra ||
1839            type == SMESH::Entity_Quad_Polyhedra )
1840       {
1841         int nbNodes;
1842         ok = proxy().perFaceConnectivity( id, connectivity, nbNodes );
1843         if ( ok && !connectivity.isEmpty() )
1844         {
1845           writer->write( tr( "NB_NODES" ), nbNodes );
1846           writer->write( tr( "CONNECTIVITY" ) );
1847           writer->indent();
1848           int nbFaces = connectivity.size();
1849           for ( int iF = 1; iF <= nbFaces; ++iF )
1850           {
1851             QString formatted = formatConnectivity( connectivity, -iF );
1852             writer->write(( type2str( SMDSAbs_Face, 0 ) + " %1 / %2" ).arg( iF ).arg( nbFaces ),
1853                           formatted );
1854           }
1855           writer->unindent();
1856         }
1857       }
1858       else
1859       {
1860         ok = proxy().elementConnectivity( id, connectivity );
1861         if ( ok && !connectivity.isEmpty() )
1862         {
1863           QString formatted = formatConnectivity( connectivity, SMDSAbs_Node );
1864           if ( !formatted.isEmpty() )
1865           {
1866             writer->write( tr( "NB_NODES" ), connectivity[ SMDSAbs_Node ].size() );
1867             writer->write( tr( "CONNECTIVITY" ), formatted ); //todo: TypeRole: ElemConnectivity
1868           }
1869         }
1870       }
1871       // - position
1872       ok = proxy().elementPosition( id, position );
1873       if ( ok && position.isValid() )
1874       {
1875         writer->write( tr( "POSITION" ), (stype2str( position.shapeType() ) + " #%1").arg( position.shapeId() ) );
1876       }
1877       // - gravity center
1878       ok = proxy().elementGravityCenter( id, xyz );
1879       if ( ok )
1880       {
1881         writer->write( tr( "GRAVITY_CENTER" ), xyz );
1882       }
1883       // - normal vector
1884       ok = proxy().elementNormal( id, xyz );
1885       if ( ok )
1886       {
1887         writer->write( tr( "NORMAL_VECTOR" ), xyz );
1888       }
1889       // - controls
1890       bool topCreated = false;
1891       for ( int i = SMESH::FT_AspectRatio; i < SMESH::FT_Undefined; i++ )
1892       {
1893         QString ctrlTitle = ctrl2str( i );
1894         if ( ctrlTitle.isEmpty() )
1895           continue;
1896         if ( !topCreated )
1897         {
1898           writer->write( tr( "CONTROLS" ) );
1899           writer->indent();
1900           topCreated = true;
1901         }
1902         double value;
1903         if ( proxy().elementControl( id, i, cprecision, value ) )
1904           writer->write( ctrlTitle, value );
1905       }
1906       if ( topCreated )
1907         writer->unindent();
1908       // - groups element belongs to
1909       QList<SMESH::SelectionProxy> groups = proxy().elementGroups( id );
1910       topCreated = false;
1911       foreach( SMESH::SelectionProxy group, groups )
1912       {
1913         if ( group && !group.name().isEmpty() )
1914         {
1915           if ( !topCreated )
1916           {
1917             writer->write( SMESHGUI_AddInfo::tr( "GROUPS" ) );
1918             writer->indent();
1919             topCreated = true;
1920           }
1921           writer->write( group.name().trimmed() ); // trim name
1922           if ( grp_details )
1923           {
1924             writer->indent();
1925             int type = group.type();
1926             if ( type == SMESH::SelectionProxy::GroupStd )
1927             {
1928               writer->write( SMESHGUI_AddInfo::tr( "TYPE" ), SMESHGUI_AddInfo::tr( "STANDALONE_GROUP" ) );
1929             }
1930             else if ( type == SMESH::SelectionProxy::GroupGeom )
1931             {
1932               writer->write( SMESHGUI_AddInfo::tr( "TYPE" ), SMESHGUI_AddInfo::tr( "GROUP_ON_GEOMETRY" ) );
1933               writer->write( SMESHGUI_AddInfo::tr( "GEOM_OBJECT" ), group.shapeName() );
1934             }
1935             else if ( type == SMESH::SelectionProxy::GroupFilter )
1936             {
1937               writer->write( SMESHGUI_AddInfo::tr( "TYPE" ), SMESHGUI_AddInfo::tr( "GROUP_ON_FILTER" ) );
1938             }
1939             int size = group.size();
1940             if ( size != -1 )
1941               writer->write( SMESHGUI_AddInfo::tr( "SIZE" ), size );
1942             QColor color = group.color();
1943             if ( color.isValid() )
1944               writer->write( SMESHGUI_AddInfo::tr( "COLOR" ), color.name() );
1945             writer->unindent();
1946           }
1947         }
1948       }
1949       if ( topCreated )
1950         writer->unindent();
1951       writer->unindent();
1952     }
1953   }  
1954 }
1955
1956 /*!
1957   \fn void SMESHGUI_ElemInfo::information( const QList<uint>& ids )
1958   \brief Show information on given mesh nodes / elements.
1959
1960   This function has to be redefined in sub-classes.
1961
1962   \param ids Nodes / elements IDs.
1963 */
1964
1965 /*!
1966   \brief Internal clean-up (reset panel).
1967
1968   Default implementation does nothing; the method has to be redefined
1969   in sub-classes to perform internal clean-up.
1970 */
1971 void SMESHGUI_ElemInfo::clearInternal()
1972 {
1973 }
1974
1975 /*!
1976   \brief Show previous chunk of information.
1977 */
1978 void SMESHGUI_ElemInfo::showPrevious()
1979 {
1980   myIndex = qMax( 0, myIndex-1 );
1981   updateControls();
1982   information( myIDs.mid( myIndex*blockSize(), blockSize() ) );
1983 }
1984
1985 /*!
1986   \brief Show next chunk of information.
1987 */
1988 void SMESHGUI_ElemInfo::showNext()
1989 {
1990   myIndex = qMin( myIndex+1, myIDs.count() / blockSize() );
1991   updateControls();
1992   information( myIDs.mid( myIndex*blockSize(), blockSize() ) );
1993 }
1994
1995 /*!
1996   \brief Update control widget state.
1997 */
1998 void SMESHGUI_ElemInfo::updateControls()
1999 {
2000   myExtra->updateControls( myIDs.count(), myIndex );
2001 }
2002
2003 /*!
2004   \brief Write information from panel to output stream.
2005   \param out Text stream output.
2006 */
2007 void SMESHGUI_ElemInfo::saveInfo( QTextStream &out )
2008 {
2009   // title
2010   QString title = tr( "ELEM_INFO" );
2011   out << ruler( title.size() ) << endl;
2012   out << title << endl;
2013   out << ruler( title.size() ) << endl;
2014   //  out << endl;
2015
2016   // info
2017   StreamWriter writer( out );
2018   writeInfo( &writer, myIDs );
2019   out << endl;
2020 }
2021
2022 ////////////////////////////////////////////////////////////////////////////////
2023 /// \class SMESHGUI_SimpleElemInfo
2024 /// \brief Show mesh element information in the simple text area.
2025 ////////////////////////////////////////////////////////////////////////////////
2026
2027 /*!
2028   \brief Constructor.
2029   \param parent Parent widget. Defaults to 0.
2030 */
2031 SMESHGUI_SimpleElemInfo::SMESHGUI_SimpleElemInfo( QWidget* parent )
2032   : SMESHGUI_ElemInfo( parent )
2033 {
2034   myInfo = new QTextBrowser( centralWidget() );
2035   QVBoxLayout* l = new QVBoxLayout( centralWidget() );
2036   l->setMargin( 0 );
2037   l->addWidget( myInfo );
2038
2039   connect( myInfo, SIGNAL( anchorClicked(QUrl)), this, SLOT( connectivityClicked( QUrl )));
2040 }
2041
2042 /*!
2043   \brief Show mesh element information.
2044   \param ids Nodes / elements IDs.
2045 */
2046 void SMESHGUI_SimpleElemInfo::information( const QList<uint>& ids )
2047 {
2048   clearInternal();
2049   TextWriter writer( myInfo );
2050   writeInfo( &writer, ids );
2051 }
2052
2053 /*!
2054   \brief Internal clean-up (reset widget)
2055 */
2056 void SMESHGUI_SimpleElemInfo::clearInternal()
2057 {
2058   myInfo->clear();
2059 }
2060
2061 void SMESHGUI_SimpleElemInfo::connectivityClicked(const QUrl & url)
2062 {
2063   int type = ( url.scheme()[0] == 'n' ) ? NodeConnectivity : ElemConnectivity;
2064   QString ids = url.path(); // excess chars will be filtered off by SMESHGUI_IdValidator
2065   emit( itemInfo( type, ids ));
2066 }
2067
2068 ////////////////////////////////////////////////////////////////////////////////
2069 /// \class SMESHGUI_TreeElemInfo::ItemDelegate
2070 /// \brief Item delegate for tree mesh info widget.
2071 /// \internal
2072 ////////////////////////////////////////////////////////////////////////////////
2073
2074 class SMESHGUI_TreeElemInfo::ItemDelegate : public QItemDelegate
2075 {
2076 public:
2077   ItemDelegate( QObject* );
2078   QWidget* createEditor( QWidget*, const QStyleOptionViewItem&, const QModelIndex& ) const;
2079 };
2080
2081 /*!
2082   \brief Constructor.
2083   \internal
2084 */
2085 SMESHGUI_TreeElemInfo::ItemDelegate::ItemDelegate( QObject* parent ): QItemDelegate( parent )
2086 {
2087 }
2088
2089 /*!
2090   \brief Redefined from QItemDelegate.
2091   \internal
2092 */
2093 QWidget* SMESHGUI_TreeElemInfo::ItemDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const
2094 {
2095   QWidget* w = index.column() == 0 ? 0: QItemDelegate::createEditor( parent, option, index );
2096   if ( qobject_cast<QLineEdit*>( w ) )
2097     qobject_cast<QLineEdit*>( w )->setReadOnly( true );
2098   return w;
2099 }
2100
2101 ////////////////////////////////////////////////////////////////////////////////
2102 /// \class SMESHGUI_TreeElemInfo::ItemCreator
2103 /// \brief Item creator for tree mesh info widget.
2104 /// \internal
2105 ////////////////////////////////////////////////////////////////////////////////
2106
2107 class SMESHGUI_TreeElemInfo::ItemCreator : public TreeItemCreator
2108 {
2109   SMESHGUI_TreeElemInfo* myView;
2110 public:
2111   ItemCreator( SMESHGUI_TreeElemInfo* );
2112   QTreeWidgetItem* createItem( QTreeWidgetItem*, int );
2113 };
2114
2115 /*!
2116   \brief Constructor.
2117   \param view Parent view.
2118   \internal
2119 */
2120 SMESHGUI_TreeElemInfo::ItemCreator::ItemCreator( SMESHGUI_TreeElemInfo* view ): TreeItemCreator(), myView( view )
2121 {
2122 }
2123
2124 /*!
2125   \brief Create new tree item.
2126   \param parent Parent tree item.
2127   \param options Item options.
2128   \return New tree widget item.
2129   \internal
2130 */
2131 QTreeWidgetItem* SMESHGUI_TreeElemInfo::ItemCreator::createItem( QTreeWidgetItem* parent, int options )
2132 {
2133   return myView->createItem( parent, options );
2134 }
2135
2136 ////////////////////////////////////////////////////////////////////////////////
2137 /// \class SMESHGUI_TreeElemInfo
2138 /// \brief Show mesh element information as the tree.
2139 ////////////////////////////////////////////////////////////////////////////////
2140
2141 /*!
2142   \brief Constructor.
2143   \param parent Parent widget. Defaults to 0.
2144 */
2145 SMESHGUI_TreeElemInfo::SMESHGUI_TreeElemInfo( QWidget* parent )
2146   : SMESHGUI_ElemInfo( parent )
2147 {
2148   myInfo = new QTreeWidget( centralWidget() );
2149   myInfo->setColumnCount( 2 );
2150   myInfo->setHeaderLabels( QStringList() << tr( "PROPERTY" ) << tr( "VALUE" ) );
2151   myInfo->header()->setStretchLastSection( true );
2152   myInfo->header()->setSectionResizeMode( 0, QHeaderView::ResizeToContents );
2153   myInfo->setItemDelegate( new ItemDelegate( myInfo ) );
2154   QVBoxLayout* l = new QVBoxLayout( centralWidget() );
2155   l->setMargin( 0 );
2156   l->addWidget( myInfo );
2157   connect( myInfo, SIGNAL( itemDoubleClicked( QTreeWidgetItem*, int ) ), this, SLOT( itemDoubleClicked( QTreeWidgetItem*, int ) ) );
2158   connect( myInfo, SIGNAL( itemCollapsed( QTreeWidgetItem* )), this, SLOT( saveExpanded( QTreeWidgetItem* )) );
2159   connect( myInfo, SIGNAL( itemExpanded( QTreeWidgetItem* )), this, SLOT( saveExpanded( QTreeWidgetItem* )) );
2160 }
2161
2162 /*!
2163   \brief Show mesh element information.
2164   \param ids Nodes / elements IDs.
2165 */
2166 void SMESHGUI_TreeElemInfo::information( const QList<uint>& ids )
2167 {
2168   clearInternal();
2169   TreeWriter writer( myInfo, new ItemCreator( this ) );
2170   writeInfo( &writer, ids );
2171 }
2172
2173 /*!
2174   \brief Show node information
2175   \param node mesh node for showing
2176   \param index index of current node
2177   \param nbNodes number of unique nodes in element
2178   \param parentItem parent item of tree
2179 */
2180 void SMESHGUI_TreeElemInfo::nodeInfo( const SMDS_MeshNode* /*node*/, int /*index*/,
2181                                       int /*nbNodes*/, QTreeWidgetItem* /*parentItem*/ )
2182 {
2183   // int precision   = SMESHGUI::resourceMgr()->integerValue( "SMESH", "length_precision", 6 );
2184   // // node number and ID
2185   // QTreeWidgetItem* nodeItem = createItem( parentItem, Bold );
2186   // nodeItem->setText( 0, QString( "%1 %2 / %3" ).arg( SMESHGUI_ElemInfo::tr( "NODE" )).arg( index ).arg( nbNodes ));
2187   // nodeItem->setText( 1, QString( "#%1" ).arg( node->GetID() ));
2188   // nodeItem->setData( 1, TypeRole, ElemConnectivity );
2189   // nodeItem->setData( 1, IdRole, node->GetID() );
2190   // nodeItem->setExpanded( false );
2191   // // node coordinates
2192   // QTreeWidgetItem* coordItem = createItem( nodeItem );
2193   // coordItem->setText( 0, SMESHGUI_ElemInfo::tr( "COORDINATES" ));
2194   // QTreeWidgetItem* xItem = createItem( coordItem );
2195   // xItem->setText( 0, "X" );
2196   // xItem->setText( 1, QString::number( node->X(), precision > 0 ? 'f' : 'g', qAbs( precision )) );
2197   // QTreeWidgetItem* yItem = createItem( coordItem );
2198   // yItem->setText( 0, "Y" );
2199   // yItem->setText( 1, QString::number( node->Y(), precision > 0 ? 'f' : 'g', qAbs( precision )) );
2200   // QTreeWidgetItem* zItem = createItem( coordItem );
2201   // zItem->setText( 0, "Z" );
2202   // zItem->setText( 1, QString::number( node->Z(), precision > 0 ? 'f' : 'g', qAbs( precision )) );
2203   // // node connectivity
2204   // QTreeWidgetItem* nconItem = createItem( nodeItem );
2205   // nconItem->setText( 0, SMESHGUI_ElemInfo::tr( "CONNECTIVITY" ));
2206   // Connectivity connectivity = nodeConnectivity( node );
2207   // if ( !connectivity.isEmpty() ) {
2208   //   QString con = formatConnectivity( connectivity, SMDSAbs_0DElement );
2209   //   if ( !con.isEmpty() ) {
2210   //     QTreeWidgetItem* i = createItem( nconItem );
2211   //     i->setText( 0, SMESHGUI_ElemInfo::tr( "0D_ELEMENTS" ));
2212   //     i->setText( 1, con );
2213   //   }
2214   //   con = formatConnectivity( connectivity, SMDSAbs_Edge );
2215   //   if ( !con.isEmpty() ) {
2216   //     QTreeWidgetItem* i = createItem( nconItem );
2217   //     i->setText( 0, SMESHGUI_ElemInfo::tr( "EDGES" ));
2218   //     i->setText( 1, con );
2219   //     i->setData( 1, TypeRole, NodeConnectivity );
2220   //   }
2221   //   con = formatConnectivity( connectivity, SMDSAbs_Ball );
2222   //   if ( !con.isEmpty() ) {
2223   //     QTreeWidgetItem* i = createItem( nconItem );
2224   //     i->setText( 0, SMESHGUI_ElemInfo::tr( "BALL_ELEMENTS" ));
2225   //     i->setText( 1, con );
2226   //     i->setData( 1, TypeRole, NodeConnectivity );
2227   //   }
2228   //   con = formatConnectivity( connectivity, SMDSAbs_Face );
2229   //   if ( !con.isEmpty() ) {
2230   //     QTreeWidgetItem* i = createItem( nconItem );
2231   //     i->setText( 0, SMESHGUI_ElemInfo::tr( "FACES" ));
2232   //     i->setText( 1, con );
2233   //     i->setData( 1, TypeRole, NodeConnectivity );
2234   //   }
2235   //   con = formatConnectivity( connectivity, SMDSAbs_Volume );
2236   //   if ( !con.isEmpty() ) {
2237   //     QTreeWidgetItem* i = createItem( nconItem );
2238   //     i->setText( 0, SMESHGUI_ElemInfo::tr( "VOLUMES" ));
2239   //     i->setText( 1, con );
2240   //     i->setData( 1, TypeRole, NodeConnectivity );
2241   //   }
2242   // }
2243 }
2244 /*!
2245   \brief Internal clean-up (reset widget)
2246 */
2247 void SMESHGUI_TreeElemInfo::clearInternal()
2248 {
2249   myInfo->clear();
2250   myInfo->repaint();
2251 }
2252
2253 /*!
2254   \brief Create new item and add it to the tree.
2255   \param parent Parent tree widget item. Defaults to 0 (top-level item).
2256   \param options Item flags. Defaults to 0 (none).
2257   \return New tree widget item.
2258 */
2259 QTreeWidgetItem* SMESHGUI_TreeElemInfo::createItem( QTreeWidgetItem* parent, int options )
2260 {
2261   QTreeWidgetItem* item = new QTreeWidgetItem( parent ? parent : myInfo->invisibleRootItem() );
2262   setTreeItemAttributes( item, options | Expanded | Editable );
2263
2264   if ( parent && parent->childCount() == 1 && itemDepth( parent ) == 1 )
2265   {
2266     QString resName = expandedResource( parent );
2267     parent->setExpanded( SMESHGUI::resourceMgr()->booleanValue("SMESH", resName, true ));
2268   }
2269   
2270   return item;
2271 }
2272
2273 void SMESHGUI_TreeElemInfo::contextMenuEvent( QContextMenuEvent* e )
2274 {
2275   QList< QTreeWidgetItem* > widgets = myInfo->selectedItems();
2276   if ( widgets.isEmpty() ) return;
2277   QTreeWidgetItem* aTreeItem = widgets.first();
2278   int type = aTreeItem->data( 1, TypeRole ).toInt();
2279   if (( type == ElemConnectivity || type == NodeConnectivity ) &&
2280       ( !aTreeItem->text( 1 ).isEmpty() ))
2281   {
2282     QMenu menu;
2283     QAction* a = menu.addAction( tr( "SHOW_ITEM_INFO" ));
2284     if ( menu.exec( e->globalPos() ) == a )
2285       emit( itemInfo( type, aTreeItem->text( 1 )) );
2286   }
2287 }
2288
2289 void SMESHGUI_TreeElemInfo::itemDoubleClicked( QTreeWidgetItem* theItem, int /*theColumn*/ )
2290 {
2291   if ( theItem ) {
2292     int type = theItem->data( 1, TypeRole ).toInt();
2293     emit( itemInfo( type, theItem->text( 1 )) );
2294   }
2295 }
2296
2297 void SMESHGUI_TreeElemInfo::saveExpanded( QTreeWidgetItem* theItem )
2298 {
2299   if ( theItem )
2300     SMESHGUI::resourceMgr()->setValue("SMESH", expandedResource( theItem ), theItem->isExpanded() );
2301 }
2302
2303 QString SMESHGUI_TreeElemInfo::expandedResource( QTreeWidgetItem* theItem )
2304 {
2305   return QString("Expanded_") + ( what()==ShowElements ? "E_" : "N_" ) + theItem->text(0);
2306 }
2307
2308 ////////////////////////////////////////////////////////////////////////////////
2309 /// \class InfoComputor
2310 /// \brief Mesh information computor.
2311 /// \internal
2312 ///
2313 /// The class is created for different computation operations. Currently it is
2314 /// used to compute size and number of underlying nodes for given group.
2315 ////////////////////////////////////////////////////////////////////////////////
2316
2317 /*!
2318   \brief Constructor.
2319   \param parent Parent object.
2320   \param proxy Object to compute information on (group).
2321   \param item Tree widget item, referenced by this computer.
2322   \param operation Value to compute.
2323   \internal
2324 */
2325 InfoComputor::InfoComputor( QObject* parent, const SMESH::SelectionProxy& proxy, int operation )
2326   : QObject( parent ), myProxy( proxy ), myOperation( operation )
2327 {
2328 }
2329
2330 /*!
2331   \brief Compute requested information.
2332   \internal
2333 */
2334 void InfoComputor::compute()
2335 {
2336   if ( myProxy )
2337   {
2338     SUIT_OverrideCursor wc;
2339     myProxy.load();
2340     switch ( myOperation )
2341     {
2342     case GrpSize:
2343       myProxy.size( true ); // force size computation
2344       emit computed();
2345       break;
2346     case GrpNbNodes:
2347       myProxy.nbNodes( true ); // force size computation
2348       emit computed();
2349       break;
2350     default:
2351       break;
2352     }
2353   }
2354 }
2355
2356 ////////////////////////////////////////////////////////////////////////////////
2357 /// \class SMESHGUI_AddInfo
2358 /// \brief Show additional information on selected object.
2359 ///
2360 /// Displays an additional information about selected object: mesh, sub-mesh
2361 /// or group.
2362 ///
2363 /// \todo Rewrite saveInfo() method to print all data, not currently shown only.
2364 ////////////////////////////////////////////////////////////////////////////////
2365
2366 /*!
2367   \brief Constructor.
2368   \param parent Parent widget. Defaults to 0.
2369 */
2370 SMESHGUI_AddInfo::SMESHGUI_AddInfo( QWidget* parent ): SMESHGUI_Info( parent )
2371 {
2372   QVBoxLayout* l = new QVBoxLayout( this );
2373   l->setMargin( 0 );
2374   l->setSpacing( SPACING );
2375
2376   myTree = new QTreeWidget( this );
2377
2378   myTree->setColumnCount( 2 );
2379   myTree->header()->setStretchLastSection( true );
2380   myTree->header()->setSectionResizeMode( 0, QHeaderView::ResizeToContents );
2381   myTree->header()->hide();
2382
2383   l->addWidget( myTree );
2384 }
2385
2386 /*!
2387   \brief Destructor.
2388 */
2389 SMESHGUI_AddInfo::~SMESHGUI_AddInfo()
2390 {
2391 }
2392
2393 /*!
2394   \brief Show information on given object.
2395   \param proxy Object to show information on (mesh, sub-mesh, group).
2396 */
2397 void SMESHGUI_AddInfo::showInfo( const SMESH::SelectionProxy& proxy )
2398 {
2399   // reset panel
2400   setProperty( "group_index", 0 );
2401   setProperty( "submesh_index",  0 );
2402   myComputors.clear();
2403   myTree->clear();
2404
2405   // then fill panel with data if object is not null
2406   if ( proxy )
2407   {
2408     myProxy = proxy;
2409
2410     // name
2411     QTreeWidgetItem* nameItem = createItem( 0, Bold | AllColumns );
2412     nameItem->setText( 0, tr( "NAME" ) );
2413     nameItem->setText( 1, proxy.name() );
2414
2415     // object info
2416     if ( proxy.type() == SMESH::SelectionProxy::Mesh )
2417       meshInfo( proxy, nameItem );
2418     else if ( proxy.type() == SMESH::SelectionProxy::Submesh )
2419       subMeshInfo( proxy, nameItem );
2420     else if ( proxy.type() >= SMESH::SelectionProxy::Group )
2421       groupInfo( proxy, nameItem );
2422   }
2423 }
2424
2425 /*!
2426   \brief Update information in panel.
2427 */
2428 void SMESHGUI_AddInfo::updateInfo()
2429 {
2430   showInfo( myProxy );
2431 }
2432
2433 /*!
2434   \brief Reset panel (clear all data).
2435 */
2436 void SMESHGUI_AddInfo::clear()
2437 {
2438   myTree->clear();
2439 }
2440
2441 /*!
2442   \brief Create new item and add it to the tree.
2443   \param parent Parent tree widget item. Defaults to 0 (top-level item).
2444   \param options Item flags. Defaults to 0 (none).
2445   \return New tree widget item.
2446 */
2447 QTreeWidgetItem* SMESHGUI_AddInfo::createItem( QTreeWidgetItem* parent, int options )
2448 {
2449   QTreeWidgetItem* item = parent ? new QTreeWidgetItem( parent ) : 
2450     new QTreeWidgetItem( myTree->invisibleRootItem() );
2451   setTreeItemAttributes( item, options | Expanded );
2452   return item;
2453 }
2454
2455 /*!
2456   \brief Show information on mesh.
2457   \param proxy Proxy object (mesh).
2458   \param parent Parent tree item.
2459 */
2460 void SMESHGUI_AddInfo::meshInfo( const SMESH::SelectionProxy& proxy, QTreeWidgetItem* parent )
2461 {
2462   if ( !proxy )
2463     return;
2464
2465   QString shapeName = proxy.shapeName();
2466   SMESH::MedInfo inf = proxy.medFileInfo();
2467
2468   // type
2469   QTreeWidgetItem* typeItem = createItem( parent, Bold );
2470   typeItem->setText( 0, tr( "TYPE" ) );
2471   if ( !shapeName.isEmpty() )
2472   {
2473     typeItem->setText( 1, tr( "MESH_ON_GEOMETRY" ) );
2474     // shape
2475     QTreeWidgetItem* gobjItem = createItem( parent, Bold );
2476     gobjItem->setText( 0, tr( "GEOM_OBJECT" ) );
2477     gobjItem->setText( 1, shapeName );
2478   }
2479   else if ( inf.isValid() )
2480   {
2481     typeItem->setText( 1, tr( "MESH_FROM_FILE" ) );
2482     // med file information
2483     QTreeWidgetItem* fileItem = createItem( parent, Bold );
2484     fileItem->setText( 0, tr( "FILE_NAME" ) );
2485     fileItem->setText( 1, inf.fileName() );
2486     QTreeWidgetItem* sizeItem = createItem( parent, Bold );
2487     sizeItem->setText( 0, tr( "FILE_SIZE" ) );
2488     sizeItem->setText( 1, QString::number( inf.size() ) );
2489     QTreeWidgetItem* versionItem = createItem( parent, Bold );
2490     versionItem->setText( 0, tr( "FILE_VERSION" ) );
2491     versionItem->setText( 1, inf.version() != "0" ? inf.version() : tr( "VERSION_UNKNOWN" ) );
2492   }
2493   else
2494   {
2495     typeItem->setText( 1, tr( "STANDALONE_MESH" ) );
2496   }
2497   
2498   // groups
2499   myGroups = proxy.groups();
2500   showGroups();
2501
2502   // sub-meshes
2503   mySubMeshes = proxy.submeshes();
2504   showSubMeshes();
2505 }
2506
2507 /*!
2508   \brief Show information on sub-mesh.
2509   \param proxy Proxy object (sub-mesh).
2510   \param parent Parent tree item.
2511 */
2512 void SMESHGUI_AddInfo::subMeshInfo( const SMESH::SelectionProxy& proxy, QTreeWidgetItem* parent )
2513 {
2514   if ( !proxy )
2515     return;
2516
2517   bool isShort = parent->parent() != 0;
2518
2519   if ( !isShort )
2520   {
2521     // parent mesh
2522     SMESH::SelectionProxy meshProxy = proxy.mesh();
2523     if ( meshProxy )
2524     {
2525       QTreeWidgetItem* nameItem = createItem( parent, Bold );
2526       nameItem->setText( 0, tr( "PARENT_MESH" ) );
2527       nameItem->setText( 1, meshProxy.name() );
2528     }
2529   }
2530   
2531   // shape
2532   QString shapeName = proxy.shapeName();
2533   if ( !shapeName.isEmpty() )
2534   {
2535     QTreeWidgetItem* gobjItem = createItem( parent, Bold );
2536     gobjItem->setText( 1, shapeName );
2537   }
2538 }
2539
2540 /*!
2541   \brief Show information on group.
2542   \param proxy Proxy object (group).
2543   \param parent Parent tree item.
2544 */
2545 void SMESHGUI_AddInfo::groupInfo( const SMESH::SelectionProxy& proxy, QTreeWidgetItem* parent )
2546 {
2547   if ( !proxy )
2548     return;
2549
2550   bool isShort = parent->parent() != 0;
2551
2552   if ( !isShort )
2553   {
2554     // parent mesh
2555     SMESH::SelectionProxy meshProxy = proxy.mesh();
2556     if ( meshProxy )
2557     {
2558       QTreeWidgetItem* nameItem = createItem( parent, Bold );
2559       nameItem->setText( 0, tr( "PARENT_MESH" ) );
2560       nameItem->setText( 1, meshProxy.name() );
2561     }
2562   }
2563
2564   // type
2565   SMESH::SelectionProxy::Type type = proxy.type();
2566   QTreeWidgetItem* typeItem = createItem( parent, Bold );
2567   typeItem->setText( 0, tr( "TYPE" ) );
2568   if ( type == SMESH::SelectionProxy::GroupStd )
2569   {
2570     typeItem->setText( 1, tr( "STANDALONE_GROUP" ) );
2571   }
2572   else if ( type == SMESH::SelectionProxy::GroupGeom )
2573   {
2574     typeItem->setText( 1, tr( "GROUP_ON_GEOMETRY" ) );
2575     // shape
2576     QTreeWidgetItem* gobjItem = createItem( parent, Bold );
2577     gobjItem->setText( 0, tr( "GEOM_OBJECT" ) );
2578     gobjItem->setText( 1, proxy.shapeName() );
2579   }
2580   else if ( type == SMESH::SelectionProxy::GroupFilter )
2581   {
2582     typeItem->setText( 1, tr( "GROUP_ON_FILTER" ) );
2583   }
2584
2585   // element type
2586   int etype = proxy.groupElementType();
2587   if ( !isShort )
2588   {
2589     QString typeName = tr( "UNKNOWN" );
2590     switch( etype )
2591     {
2592     case SMESH::NODE:
2593       typeName = tr( "NODE" );
2594       break;
2595     case SMESH::EDGE:
2596       typeName = tr( "EDGE" );
2597       break;
2598     case SMESH::FACE:
2599       typeName = tr( "FACE" );
2600       break;
2601     case SMESH::VOLUME:
2602       typeName = tr( "VOLUME" );
2603       break;
2604     case SMESH::ELEM0D:
2605       typeName = tr( "0DELEM" );
2606       break;
2607     case SMESH::BALL:
2608       typeName = tr( "BALL" );
2609       break;
2610     default:
2611       break;
2612     }
2613     QTreeWidgetItem* etypeItem = createItem( parent, Bold );
2614     etypeItem->setText( 0, tr( "ENTITY_TYPE" ) );
2615     etypeItem->setText( 1, typeName );
2616   }
2617
2618   // size
2619   // note: size is not computed for group on filter for performance reasons, see IPAL52831
2620   bool meshLoaded = proxy.isMeshLoaded();
2621   int size = proxy.size();
2622
2623   QTreeWidgetItem* sizeItem = createItem( parent, Bold );
2624   sizeItem->setText( 0, tr( "SIZE" ) );
2625   if ( size >= 0 )
2626   {
2627     sizeItem->setText( 1, QString::number( size ) );
2628   }
2629   else
2630   {
2631     QPushButton* btn = new QPushButton( meshLoaded ? tr( "COMPUTE" ) : tr( "LOAD" ), this );
2632     myTree->setItemWidget( sizeItem, 1, btn );
2633     InfoComputor* comp = new InfoComputor( this, proxy, InfoComputor::GrpSize );
2634     connect( btn, SIGNAL( clicked() ), comp, SLOT( compute() ) );
2635     connect( comp, SIGNAL( computed() ), this, SLOT( updateInfo() ) );
2636     myComputors.append( comp );
2637   }
2638
2639   // color
2640   QTreeWidgetItem* colorItem = createItem( parent, Bold );
2641   colorItem->setText( 0, tr( "COLOR" ) );
2642   colorItem->setBackground( 1, proxy.color() );
2643
2644   // nb of underlying nodes
2645   if ( etype != SMESH::NODE )
2646   {
2647     QTreeWidgetItem* nodesItem = createItem( parent, Bold );
2648     nodesItem->setText( 0, tr( "NB_NODES" ) );
2649
2650     int nbNodes = proxy.nbNodes();
2651     if ( nbNodes >= 0 )
2652     {
2653       nodesItem->setText( 1, QString::number( nbNodes ) );
2654     }
2655     else
2656     {
2657       QPushButton* btn = new QPushButton( meshLoaded ? tr( "COMPUTE" ) : tr( "LOAD" ), this );
2658       myTree->setItemWidget( nodesItem, 1, btn );
2659       InfoComputor* comp = new InfoComputor( this, proxy, InfoComputor::GrpNbNodes ); 
2660       connect( btn, SIGNAL( clicked() ), comp, SLOT( compute() ) );
2661       connect( comp, SIGNAL( computed() ), this, SLOT( updateInfo() ) );
2662       myComputors.append( comp );
2663     }
2664   }
2665 }
2666
2667 /*!
2668   \brief Update information on child groups.
2669 */
2670 void SMESHGUI_AddInfo::showGroups()
2671 {
2672   // remove all computors
2673   myComputors.clear();
2674
2675   // tree root should be the first top level item
2676   QTreeWidgetItem* parent = myTree->topLevelItemCount() > 0 ? myTree->topLevelItem( 0 ) : 0;
2677   if ( !parent )
2678     return;
2679
2680   int idx = property( "group_index" ).toInt();
2681
2682   // find sub-meshes top-level container item
2683   QTreeWidgetItem* itemGroups = 0;
2684   for ( int i = 0; i < parent->childCount() && !itemGroups; i++ )
2685   {
2686     if ( parent->child( i )->data( 0, Qt::UserRole ).toInt() == GroupsId )
2687     {
2688       itemGroups = parent->child( i );
2689       // update controls
2690       ExtraWidget* extra = dynamic_cast<ExtraWidget*>( myTree->itemWidget( itemGroups, 1 ) );
2691       if ( extra )
2692         extra->updateControls( myGroups.count(), idx );
2693       // clear: remove all group items
2694       while ( itemGroups->childCount() )
2695         delete itemGroups->child( 0 );
2696     }
2697   }
2698
2699   QMap<int, QTreeWidgetItem*> grpItems;
2700   for ( int i = idx*blockSize() ; i < qMin( (idx+1)*blockSize(), (int)myGroups.count() ); i++ )
2701   {
2702     SMESH::SelectionProxy grp = myGroups[i];
2703     if ( !grp )
2704       continue;
2705
2706     int grpType = grp.groupElementType();
2707
2708     // create top-level groups container item if it does not exist
2709     if ( !itemGroups )
2710     {
2711       itemGroups = createItem( parent, Bold | AllColumns );
2712       itemGroups->setText( 0, tr( "GROUPS" ) );
2713       itemGroups->setData( 0, Qt::UserRole, GroupsId );
2714
2715       // if necessary, create extra widget to show information by chunks
2716       if ( myGroups.count() > blockSize() )
2717       {
2718         ExtraWidget* extra = new ExtraWidget( this, true );
2719         connect( extra->prev, SIGNAL( clicked() ), this, SLOT( showPreviousGroups() ) );
2720         connect( extra->next, SIGNAL( clicked() ), this, SLOT( showNextGroups() ) );
2721         myTree->setItemWidget( itemGroups, 1, extra );
2722         extra->updateControls( myGroups.count(), idx );
2723       }
2724     }
2725
2726     // create container item corresponding to particular element type
2727     if ( !grpItems.contains( grpType ) )
2728     {
2729       grpItems[ grpType ] = createItem( itemGroups, Bold | AllColumns );
2730       grpItems[ grpType ]->setText( 0, tr( QString( "GROUPS_%1" ).arg( grpType ).toLatin1().constData() ) );
2731       itemGroups->insertChild( grpType-1, grpItems[ grpType ] ); // -1 needed since 0 corresponds to SMESH::ALL
2732     }
2733   
2734     // name
2735     QTreeWidgetItem* nameItem = createItem( grpItems[ grpType ] );
2736     nameItem->setText( 0, grp.name().trimmed() ); // trim name
2737
2738     // group info
2739     groupInfo( grp, nameItem );
2740   }
2741 }
2742
2743 /*!
2744   \brief Update information on child sub-meshes.
2745 */
2746 void SMESHGUI_AddInfo::showSubMeshes()
2747 {
2748   // tree root should be the first top level item
2749   QTreeWidgetItem* parent = myTree->topLevelItemCount() > 0 ? myTree->topLevelItem( 0 ) : 0;
2750   if ( !parent )
2751     return;
2752
2753   int idx = property( "submesh_index" ).toInt();
2754
2755   // find sub-meshes top-level container item
2756   QTreeWidgetItem* itemSubMeshes = 0;
2757   for ( int i = 0; i < parent->childCount() && !itemSubMeshes; i++ )
2758   {
2759     if ( parent->child( i )->data( 0, Qt::UserRole ).toInt() == SubMeshesId )
2760     {
2761       itemSubMeshes = parent->child( i );
2762       // update controls
2763       ExtraWidget* extra = dynamic_cast<ExtraWidget*>( myTree->itemWidget( itemSubMeshes, 1 ) );
2764       if ( extra )
2765         extra->updateControls( mySubMeshes.count(), idx );
2766       // clear: remove all sub-mesh items
2767       while ( itemSubMeshes->childCount() )
2768         delete itemSubMeshes->child( 0 );
2769     }
2770   }
2771
2772   QMap<int, QTreeWidgetItem*> smItems;
2773   for ( int i = idx*blockSize() ; i < qMin( (idx+1)*blockSize(), mySubMeshes.count() ); i++ )
2774   {
2775     SMESH::SelectionProxy sm = mySubMeshes[i];
2776     if ( !sm )
2777       continue;
2778     
2779     int smType = sm.shapeType();
2780     if ( smType < 0 )
2781       continue;
2782     else if ( smType == GEOM::COMPSOLID )
2783       smType = GEOM::COMPOUND;
2784
2785     // create top-level sub-meshes container item if it does not exist
2786     if ( !itemSubMeshes )
2787     {
2788       itemSubMeshes = createItem( parent, Bold | AllColumns );
2789       itemSubMeshes->setText( 0, tr( "SUBMESHES" ) );
2790       itemSubMeshes->setData( 0, Qt::UserRole, SubMeshesId );
2791
2792       // if necessary, create extra widget to show information by chunks
2793       if ( mySubMeshes.count() > blockSize() )
2794       {
2795         ExtraWidget* extra = new ExtraWidget( this, true );
2796         connect( extra->prev, SIGNAL( clicked() ), this, SLOT( showPreviousSubMeshes() ) );
2797         connect( extra->next, SIGNAL( clicked() ), this, SLOT( showNextSubMeshes() ) );
2798         myTree->setItemWidget( itemSubMeshes, 1, extra );
2799         extra->updateControls( mySubMeshes.count(), idx );
2800       }
2801     }
2802
2803     // create container item corresponding to particular shape type
2804     if ( !smItems.contains( smType ) )
2805     {
2806       smItems[ smType ] = createItem( itemSubMeshes, Bold | AllColumns );
2807       smItems[ smType ]->setText( 0, tr( QString( "SUBMESHES_%1" ).arg( smType ).toLatin1().constData() ) );
2808       itemSubMeshes->insertChild( smType, smItems[ smType ] );
2809     }
2810     
2811     // name
2812     QTreeWidgetItem* nameItem = createItem( smItems[ smType ] );
2813     nameItem->setText( 0, sm.name().trimmed() ); // trim name
2814     
2815     // submesh info
2816     subMeshInfo( sm, nameItem );
2817   }
2818 }
2819
2820 /*!
2821   \brief Show previous chunk of information on child groups.
2822 */
2823 void SMESHGUI_AddInfo::showPreviousGroups()
2824 {
2825   int idx = property( "group_index" ).toInt();
2826   setProperty( "group_index", idx-1 );
2827   showGroups();
2828 }
2829
2830 /*!
2831   \brief Show next chunk of information on child groups.
2832 */
2833 void SMESHGUI_AddInfo::showNextGroups()
2834 {
2835   int idx = property( "group_index" ).toInt();
2836   setProperty( "group_index", idx+1 );
2837   showGroups();
2838 }
2839
2840 /*!
2841   \brief Show previous chunk of information on child sub-meshes.
2842 */
2843 void SMESHGUI_AddInfo::showPreviousSubMeshes()
2844 {
2845   int idx = property( "submesh_index" ).toInt();
2846   setProperty( "submesh_index", idx-1 );
2847   showSubMeshes();
2848 }
2849
2850 /*!
2851   \brief Show next chunk of information on child sub-meshes.
2852 */
2853 void SMESHGUI_AddInfo::showNextSubMeshes()
2854 {
2855   int idx = property( "submesh_index" ).toInt();
2856   setProperty( "submesh_index", idx+1 );
2857   showSubMeshes();
2858 }
2859
2860 /*!
2861   \brief Write information from panel to output stream.
2862   \param out Text stream output.
2863 */
2864 void SMESHGUI_AddInfo::saveInfo( QTextStream &out )
2865 {
2866   // title
2867   QString title = tr( "ADDITIONAL_INFO" );
2868   out << ruler( title.size() ) << endl;
2869   out << title << endl;
2870   out << ruler( title.size() ) << endl;
2871   out << endl;
2872
2873   // info
2874   QTreeWidgetItemIterator it( myTree );
2875   while ( *it )
2876   {
2877     if ( !( ( *it )->text(0) ).isEmpty() )
2878     {
2879       out << indent( itemDepth( *it ) ) << ( *it )->text(0);
2880       if ( ( *it )->text(0)  == tr( "COLOR" ) )
2881         out << ":" << spacing() << ( ( ( *it )->background(1) ).color() ).name();
2882       else if ( !( ( *it )->text(1) ).isEmpty() )
2883         out << ":" << spacing() << ( *it )->text(1);
2884       out << endl;
2885     }
2886     ++it;
2887   }
2888   out << endl;
2889 }
2890
2891 ////////////////////////////////////////////////////////////////////////////////
2892 /// \class GroupCombo
2893 /// \brief Customized combo box to manage list of mesh groups.
2894 /// \internal
2895 ////////////////////////////////////////////////////////////////////////////////
2896
2897 class GroupCombo: public QComboBox
2898 {
2899   class Item: public QStandardItem
2900   {
2901   public:
2902     SMESH::SelectionProxy myGroup;
2903     Item( const SMESH::SelectionProxy& group )
2904     {
2905       myGroup = group;
2906       setText( myGroup.name() );
2907     }
2908     SMESH::SelectionProxy group()
2909     {
2910       return myGroup;
2911     }
2912   };
2913
2914   SMESH::SelectionProxy myProxy;
2915
2916 public:
2917   GroupCombo( QWidget* );
2918   void setSource( const SMESH::SelectionProxy& );
2919   SMESH::SelectionProxy currentGroup() const;
2920 };
2921
2922 /*!
2923   \brief Constructor.
2924   \param parent Parent widget.
2925   \internal
2926 */
2927 GroupCombo::GroupCombo( QWidget* parent ): QComboBox( parent )
2928 {
2929   setModel( new QStandardItemModel( this ) );
2930 }
2931
2932 /*!
2933   \brief Set mesh source.
2934   \param obj Mesh source.
2935   \internal
2936 */
2937 void GroupCombo::setSource( const SMESH::SelectionProxy& proxy )
2938 {
2939   if ( myProxy == proxy )
2940     return;
2941
2942   myProxy = proxy;
2943
2944   bool blocked = blockSignals( true );
2945   QStandardItemModel* m = dynamic_cast<QStandardItemModel*>( model() );
2946   m->clear();
2947
2948   if ( myProxy )
2949   {
2950     if ( myProxy.type() == SMESH::SelectionProxy::Mesh )
2951     {
2952       QList<SMESH::SelectionProxy> groups = myProxy.groups();
2953       for ( int i = 0; i < groups.count(); ++i )
2954       {
2955         if ( groups[i] )
2956         {
2957           QString name = groups[i].name();
2958           if ( !name.isEmpty() )
2959             m->appendRow( new Item( groups[i] ) );
2960         }
2961       }
2962       setCurrentIndex( -1 ); // for performance reasons
2963     }
2964     else if ( myProxy.type() >= SMESH::SelectionProxy::Group )
2965     {
2966       m->appendRow( new Item( myProxy ) );
2967       setCurrentIndex( 0 );
2968     }
2969   }
2970
2971   blockSignals( blocked );
2972 }
2973
2974 /*!
2975   \brief Get currently selected group.
2976   \return Selected group.
2977   \internal
2978 */
2979 SMESH::SelectionProxy GroupCombo::currentGroup() const
2980 {
2981   SMESH::SelectionProxy group;
2982   QStandardItemModel* m = dynamic_cast<QStandardItemModel*>( model() );
2983   if ( currentIndex() >= 0 )
2984     group = dynamic_cast<Item*>( m->item( currentIndex() ) )->group();
2985   return group;
2986 }
2987
2988 ////////////////////////////////////////////////////////////////////////////////
2989 /// \class SMESHGUI_MeshInfoDlg
2990 /// \brief Mesh information dialog box
2991 ///
2992 /// \todo Move all business logic for element info to SMESHGUI_ElemInfo class.
2993 /// \todo Add selection button to reactivate selection on move from other dlg.
2994 ////////////////////////////////////////////////////////////////////////////////
2995
2996 /*!
2997   \brief Constructor
2998   \param parent Parent widget.
2999   \param page Dialog page to show at start-up. Defaults to \c BaseInfo.
3000 */
3001 SMESHGUI_MeshInfoDlg::SMESHGUI_MeshInfoDlg( QWidget* parent, int page )
3002   : QDialog( parent )
3003 {
3004   setModal( false );
3005   setAttribute( Qt::WA_DeleteOnClose, true );
3006   setWindowTitle( tr( "MESH_INFO" ) );
3007   setSizeGripEnabled( true );
3008
3009   myTabWidget = new QTabWidget( this );
3010
3011   // base info
3012
3013   myBaseInfo = new SMESHGUI_BaseInfo( myTabWidget );
3014   myTabWidget->addTab( myBaseInfo, tr( "BASE_INFO" ) );
3015
3016   // elem info 
3017
3018   QWidget* w = new QWidget( myTabWidget );
3019
3020   myMode = new QButtonGroup( this );
3021   myMode->addButton( new QRadioButton( tr( "NODE_MODE" ), w ), NodeMode );
3022   myMode->addButton( new QRadioButton( tr( "ELEM_MODE" ), w ), ElemMode );
3023   myMode->addButton( new QRadioButton( tr( "GROUP_MODE" ), w ), GroupMode );
3024   myMode->button( NodeMode )->setChecked( true );
3025   myID = new QLineEdit( w );
3026   myID->setValidator( new SMESHGUI_IdValidator( this ) );
3027   myGroups = new GroupCombo( w );
3028   QStackedWidget* stack = new QStackedWidget( w );
3029   stack->addWidget( myID );
3030   stack->addWidget( myGroups );
3031   myIDPreviewCheck = new QCheckBox( tr( "SHOW_IDS" ), w );
3032   myIDPreview = new SMESHGUI_IdPreview( SMESH::GetViewWindow( SMESHGUI::GetSMESHGUI() ) );
3033
3034   int mode = SMESHGUI::resourceMgr()->integerValue( "SMESH", "mesh_elem_info", 1 );
3035   mode = qMin( 1, qMax( 0, mode ) );
3036
3037   if ( mode == 0 )
3038     myElemInfo = new SMESHGUI_SimpleElemInfo( w );
3039   else
3040     myElemInfo = new SMESHGUI_TreeElemInfo( w );
3041   stack->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
3042
3043   QGridLayout* elemLayout = new QGridLayout( w );
3044   elemLayout->setMargin( MARGIN );
3045   elemLayout->setSpacing( SPACING );
3046   elemLayout->addWidget( myMode->button( NodeMode ), 0, 0 );
3047   elemLayout->addWidget( myMode->button( ElemMode ), 0, 1 );
3048   elemLayout->addWidget( myMode->button( GroupMode ), 0, 2 );
3049   elemLayout->addWidget( stack, 0, 3 );
3050   elemLayout->addWidget( myIDPreviewCheck, 1, 0, 1, 4 );
3051   elemLayout->addWidget( myElemInfo, 2, 0, 1, 4 );
3052
3053   myTabWidget->addTab( w, tr( "ELEM_INFO" ) );
3054
3055   // additional info
3056
3057   myAddInfo = new SMESHGUI_AddInfo( myTabWidget );
3058   myTabWidget->addTab( myAddInfo, tr( "ADDITIONAL_INFO" ) );
3059
3060   // controls info
3061
3062   myCtrlInfo = new SMESHGUI_CtrlInfo( myTabWidget );
3063   myTabWidget->addTab( myCtrlInfo, tr( "CTRL_INFO" ) );
3064
3065   // buttons
3066
3067   QPushButton* okBtn = new QPushButton( tr( "SMESH_BUT_OK" ), this );
3068   okBtn->setAutoDefault( true );
3069   okBtn->setDefault( true );
3070   okBtn->setFocus();
3071   QPushButton* dumpBtn = new QPushButton( tr( "BUT_DUMP_MESH" ), this );
3072   dumpBtn->setAutoDefault( true );
3073   QPushButton* helpBtn = new QPushButton( tr( "SMESH_BUT_HELP" ), this );
3074   helpBtn->setAutoDefault( true );
3075
3076   QHBoxLayout* btnLayout = new QHBoxLayout;
3077   btnLayout->setSpacing( SPACING );
3078   btnLayout->setMargin( 0 );
3079
3080   btnLayout->addWidget( okBtn );
3081   btnLayout->addWidget( dumpBtn );
3082   btnLayout->addStretch( 10 );
3083   btnLayout->addWidget( helpBtn );
3084
3085   // arrange widgets
3086
3087   QVBoxLayout* l = new QVBoxLayout ( this );
3088   l->setMargin( MARGIN );
3089   l->setSpacing( SPACING );
3090   l->addWidget( myTabWidget );
3091   l->addLayout( btnLayout );
3092
3093   // set initial page
3094
3095   myTabWidget->setCurrentIndex( qMax( (int)BaseInfo, qMin( (int)ElemInfo, page ) ) );
3096
3097   // set-up connections
3098
3099   connect( okBtn, SIGNAL( clicked() ), this, SLOT( reject() ) );
3100   connect( dumpBtn, SIGNAL( clicked() ), this, SLOT( dump() ) );
3101   connect( helpBtn, SIGNAL( clicked() ), this, SLOT( help() ) );
3102   connect( myTabWidget, SIGNAL( currentChanged( int ) ), this, SLOT( updateSelection() ) );
3103   connect( myMode, SIGNAL( buttonClicked( int ) ), this, SLOT( modeChanged() ) );
3104   connect( myGroups, SIGNAL( currentIndexChanged( int ) ), this, SLOT( modeChanged() ) );
3105   connect( myID, SIGNAL( textChanged( QString ) ), this, SLOT( idChanged() ) );
3106   connect( myIDPreviewCheck, SIGNAL( toggled( bool ) ), this, SLOT( idPreviewChange( bool ) ) );
3107   connect( SMESHGUI::GetSMESHGUI(), SIGNAL( SignalDeactivateActiveDialog() ), this, SLOT( deactivate() ) );
3108   connect( SMESHGUI::GetSMESHGUI(), SIGNAL( SignalCloseAllDialogs() ), this, SLOT( reject() ) );
3109   connect( myElemInfo, SIGNAL( itemInfo( int, QString ) ), this, SLOT( showItemInfo( int, QString ) ) );
3110   connect( this, SIGNAL( switchMode( int ) ), stack, SLOT( setCurrentIndex( int ) ) );
3111
3112   // initialize
3113
3114   myIDPreviewCheck->setChecked( SMESHGUI::resourceMgr()->booleanValue( "SMESH", "id_preview_resource", false ) );
3115   updateSelection();
3116 }
3117
3118 /*!
3119   \brief Destructor.
3120 */
3121 SMESHGUI_MeshInfoDlg::~SMESHGUI_MeshInfoDlg()
3122 {
3123   delete myIDPreview;
3124 }
3125
3126 /*!
3127   \brief Show mesh information on given object.
3128   \param io Interactive object.
3129 */
3130 void SMESHGUI_MeshInfoDlg::showInfo( const Handle(SALOME_InteractiveObject)& io )
3131 {
3132   if ( !io.IsNull() )
3133     showInfo( SMESH::SelectionProxy( io ) );
3134 }
3135
3136 /*!
3137   \brief Show mesh information on given object.
3138   \param proxy Selection proxy.
3139 */
3140 void SMESHGUI_MeshInfoDlg::showInfo( const SMESH::SelectionProxy& proxy )
3141 {
3142   SUIT_OverrideCursor wc;
3143
3144   if ( !proxy )
3145     return;
3146
3147   myProxy = proxy;
3148
3149   SMESH::SMESH_IDSource_var obj = myProxy.object();
3150
3151   // "Base info" tab
3152   myBaseInfo->showInfo( proxy );
3153
3154   // "Additional info" tab
3155   myAddInfo->showInfo( proxy );
3156
3157   // "Quality info" tab
3158   // Note: for performance reasons we update it only if it is currently active
3159   if ( myTabWidget->currentIndex() == CtrlInfo )
3160     myCtrlInfo->showInfo( proxy );
3161
3162   // "Element info" tab
3163   myGroups->setSource( proxy );
3164   if ( myMode->checkedId() == GroupMode ) {
3165     SMESH::SelectionProxy group = myGroups->currentGroup();
3166     if ( group )
3167       myElemInfo->showInfo( group );
3168     else
3169       myElemInfo->clear();
3170   }
3171   else {
3172     SVTK_Selector* selector = SMESH::GetSelector();
3173     QString ID;
3174     int nb = 0;
3175     if ( myProxy.actor() && selector ) { //todo: actor()?
3176       nb = myMode->checkedId() == NodeMode ?
3177         SMESH::GetNameOfSelectedElements( selector, myProxy.io(), ID ) :
3178         SMESH::GetNameOfSelectedNodes( selector, myProxy.io(), ID );
3179     }
3180     if ( nb > 0 ) {
3181       myID->setText( ID.trimmed() );
3182       QSet<uint> ids;
3183       QStringList idTxt = ID.split( " ", QString::SkipEmptyParts );
3184       foreach ( ID, idTxt )
3185         ids << ID.trimmed().toUInt();
3186       myElemInfo->showInfo( proxy, ids, myMode->checkedId() == ElemMode );
3187     }
3188     else {
3189       myID->clear();
3190       myElemInfo->clear();
3191     }
3192   }
3193 }
3194
3195 /*!
3196   \brief Update information.
3197 */
3198 void SMESHGUI_MeshInfoDlg::updateInfo()
3199 {
3200   SALOME_ListIO selected;
3201   SMESHGUI::selectionMgr()->selectedObjects( selected );
3202
3203   if ( selected.Extent() == 1 )
3204     showInfo( selected.First() );
3205   else
3206     showInfo( myProxy );
3207 }
3208
3209 /*!
3210   \brief Clean-up on dialog closing.
3211 */
3212 void SMESHGUI_MeshInfoDlg::reject()
3213 {
3214   LightApp_SelectionMgr* selMgr = SMESHGUI::selectionMgr();
3215   selMgr->clearFilters();
3216   SMESH::SetPointRepresentation( false );
3217   if ( SVTK_ViewWindow* viewWindow = SMESH::GetViewWindow() )
3218     viewWindow->SetSelectionMode( ActorSelection );
3219   QDialog::reject();
3220   myIDPreview->SetPointsLabeled( false );
3221 }
3222
3223 /*!
3224   \brief Process keyboard event.
3225   \param e Key press event.
3226 */
3227 void SMESHGUI_MeshInfoDlg::keyPressEvent( QKeyEvent* e )
3228 {
3229   QDialog::keyPressEvent( e );
3230   if ( !e->isAccepted() && e->key() == Qt::Key_F1 ) {
3231     e->accept();
3232     help();
3233   }
3234 }
3235
3236 /*!
3237   \brief Set-up selection mode for currently selected page.
3238 */
3239 void SMESHGUI_MeshInfoDlg::updateSelection()
3240 {
3241   LightApp_SelectionMgr* selMgr = SMESHGUI::selectionMgr();
3242
3243   disconnect( selMgr, 0, this, 0 );
3244   selMgr->clearFilters();
3245
3246   int selMode = ActorSelection;
3247   if ( myTabWidget->currentIndex() == ElemInfo && myMode->checkedId() == NodeMode )
3248     selMode = NodeSelection;
3249   else if ( myTabWidget->currentIndex() == ElemInfo && myMode->checkedId() == ElemMode )
3250     selMode = CellSelection;
3251   SMESH::SetPointRepresentation( selMode == NodeSelection );
3252   if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() )
3253     aViewWindow->SetSelectionMode( selMode );
3254
3255   SMESH::SelectionProxy previous = myProxy;
3256   QString ids = myID->text().trimmed();
3257   myID->clear();
3258   
3259   connect( selMgr, SIGNAL( currentSelectionChanged() ), this, SLOT( updateInfo() ) );
3260   updateInfo();
3261   
3262   if ( myProxy && myProxy == previous && !ids.isEmpty() ) {
3263     myID->setText( ids );
3264     idChanged();
3265   }
3266 }
3267
3268 /*!
3269   \brief Show documentation on selected dialog page.
3270 */
3271 void SMESHGUI_MeshInfoDlg::help()
3272 {
3273   QString helpPage = "mesh_infos.html";
3274   switch ( myTabWidget->currentIndex() )
3275   {
3276   case BaseInfo:
3277     helpPage += "#advanced-mesh-infos-anchor";
3278     break;
3279   case ElemInfo:
3280     helpPage += "#mesh-element-info-anchor";
3281     break;
3282   case AddInfo:
3283     helpPage += "#mesh-addition-info-anchor";
3284     break;
3285   case CtrlInfo:
3286     helpPage += "#mesh-quality-info-anchor";
3287     break;
3288   default:
3289     break;
3290   }
3291   SMESH::ShowHelpFile( helpPage );
3292 }
3293
3294 /*!
3295   \brief Deactivate dialog box.
3296 */
3297 void SMESHGUI_MeshInfoDlg::deactivate()
3298 {
3299   disconnect( SMESHGUI::selectionMgr(), SIGNAL( currentSelectionChanged() ), this, SLOT( updateInfo() ) );
3300 }
3301
3302 /*!
3303   \brief Called when users switches between node / element modes.
3304 */
3305 void SMESHGUI_MeshInfoDlg::modeChanged()
3306 {
3307   emit( switchMode( myMode->checkedId() == GroupMode ? 1 : 0 ) );
3308   myID->clear();
3309   updateSelection();
3310 }
3311
3312 /*!
3313   \brief Called when users prints mesh element ID in the corresponding field.
3314 */
3315 void SMESHGUI_MeshInfoDlg::idChanged()
3316 {
3317   myIDPreview->SetPointsLabeled( false );
3318
3319   if ( myProxy ) {
3320     SVTK_TVtkIDsMap      ID;
3321     QSet<uint>           ids;
3322     std::vector<int>     idVec;
3323     std::list< gp_XYZ >  aGrCentersXYZ;
3324     SMESH::XYZ           xyz;
3325     const bool           isElem = ( myMode->checkedId() == ElemMode );
3326     QStringList idTxt = myID->text().split( " ", QString::SkipEmptyParts );
3327     foreach ( QString tid, idTxt ) {
3328       long id = tid.toUInt();
3329       if ( isElem ? myProxy.hasElement( id ) : myProxy.hasNode( id ))
3330       {
3331         ID.Add( id );
3332         ids << id;
3333         if ( isElem && myProxy.actor() && myProxy.elementGravityCenter( id, xyz ))
3334         {
3335           idVec.push_back( id );
3336           aGrCentersXYZ.push_back( xyz );
3337         }
3338       }
3339     }
3340     SVTK_Selector* selector = SMESH::GetSelector();
3341     if ( myProxy.actor() && selector ) {
3342       Handle(SALOME_InteractiveObject) IO = myProxy.actor()->getIO();
3343       selector->AddOrRemoveIndex( IO, ID, false );
3344       if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() )
3345       {
3346         if ( myMode->checkedId() == NodeMode )
3347           myIDPreview->SetPointsData( myProxy.actor()->GetObject()->GetMesh(), ID );
3348         else
3349           myIDPreview->SetElemsData( idVec, aGrCentersXYZ );
3350
3351         bool showIDs = ( !ID.IsEmpty() &&
3352                          myIDPreviewCheck->isChecked() &&
3353                          myTabWidget->currentIndex() == ElemInfo );
3354         myIDPreview->SetPointsLabeled( showIDs, myProxy.actor()->GetVisibility() );
3355
3356         aViewWindow->highlight( IO, true, true );
3357         aViewWindow->Repaint();
3358       }
3359     }
3360     myElemInfo->showInfo( myProxy, ids, isElem );
3361   }
3362 }
3363
3364 /*!
3365  * \brief Show IDs clicked
3366  */
3367 void SMESHGUI_MeshInfoDlg::idPreviewChange( bool isOn )
3368 {
3369   myIDPreview->SetPointsLabeled( isOn && !myID->text().simplified().isEmpty() );
3370   SMESHGUI::resourceMgr()->setValue("SMESH", "id_preview_resource", isOn );
3371   if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() )
3372     aViewWindow->Repaint();
3373 }
3374
3375 void SMESHGUI_MeshInfoDlg::showItemInfo( int type, const QString& ids )
3376 {
3377   if ( !ids.isEmpty() && ( type == NodeConnectivity || type == ElemConnectivity )) {
3378     myMode->button( type - NodeConnectivity )->click();
3379     myID->setText( ids );
3380   }
3381 }
3382
3383 /*!
3384   \brief Dump information to file.
3385 */
3386 void SMESHGUI_MeshInfoDlg::dump()
3387 {
3388   DumpFileDlg fd( this );
3389   fd.setWindowTitle( tr( "SAVE_INFO" ) );
3390   fd.setNameFilters( QStringList() << tr( "TEXT_FILES" ) );
3391   fd.setChecked( BaseInfo, SMESHGUI::resourceMgr()->booleanValue( "SMESH", "info_dump_base", true ) );
3392   fd.setChecked( ElemInfo, SMESHGUI::resourceMgr()->booleanValue( "SMESH", "info_dump_elem", true ) );
3393   fd.setChecked( AddInfo, SMESHGUI::resourceMgr()->booleanValue( "SMESH", "info_dump_add", true ) );
3394   fd.setChecked( CtrlInfo, SMESHGUI::resourceMgr()->booleanValue( "SMESH", "info_dump_ctrl", true ) );
3395   if ( fd.exec() == QDialog::Accepted )
3396   {
3397     QString fileName = fd.selectedFile();
3398     if ( !fileName.isEmpty() ) {
3399       QFile file( fileName );
3400       if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) )
3401         return;
3402
3403       QTextStream out( &file );
3404       if ( fd.isChecked( BaseInfo ) ) myBaseInfo->saveInfo( out );
3405       if ( fd.isChecked( ElemInfo ) ) myElemInfo->saveInfo( out );
3406       if ( fd.isChecked( AddInfo ) )  myAddInfo->saveInfo( out );
3407       if ( fd.isChecked( CtrlInfo ) )
3408       {
3409         myCtrlInfo->showInfo( myProxy ); // it saves what is shown only
3410         myCtrlInfo->saveInfo( out );
3411       }
3412     }
3413   }
3414 }
3415
3416 ////////////////////////////////////////////////////////////////////////////////
3417 /// \class SMESHGUI_CtrlInfo
3418 /// \brief Show quality statistics information on selected object.
3419 ///
3420 /// Displays quality controls statistics about selected object: mesh, sub-mesh,
3421 /// group or arbitrary ID source.
3422 ////////////////////////////////////////////////////////////////////////////////
3423
3424 /*!
3425   \brief Constructor.
3426   \param parent Parent widget. Defaults to 0.
3427 */
3428 SMESHGUI_CtrlInfo::SMESHGUI_CtrlInfo( QWidget* parent ): SMESHGUI_Info( parent )
3429 {
3430   QGridLayout* l = new QGridLayout( this );
3431   l->setMargin( MARGIN );
3432   l->setSpacing( SPACING );
3433
3434   QIcon aComputeIcon( SUIT_Session::session()->resourceMgr()->loadPixmap( "SMESH", tr( "ICON_COMPUTE" ) ) );
3435   SMESH::FilterManager_var aFilterMgr = SMESH::GetFilterManager();
3436
3437   // QToolBox with MeshInfo
3438   myMeshTB = new QToolBox(this);
3439
3440   // name
3441   QLabel* aNameLab = createLabel( tr( "NAME_LAB" ), this, Bold );
3442   QLabel* aName = createField( this, "ctrlName" );
3443   aName->setMinimumWidth( 150 );
3444   myWidgets << aName;
3445
3446   // nodes info
3447   QLabel* aNodesLab = createLabel( tr( "NODES_INFO" ), this, Bold );
3448   QLabel* aNodesFreeLab = new QLabel( tr( "NUMBER_OF_THE_FREE_NODES" ), this );
3449   QLabel* aNodesFree = createField( this, "ctrlNodesFree" );
3450   myWidgets << aNodesFree;
3451   myPredicates << aFilterMgr->CreateFreeNodes();
3452   //
3453   QLabel* aNodesNbConnLab = new QLabel( tr( "MAX_NODE_CONNECTIVITY" ), this );
3454   QLabel* aNodesNbConn = createField( this, "ctrlNodesCnty" );
3455   myWidgets << aNodesNbConn;
3456   myNodeConnFunctor = aFilterMgr->CreateNodeConnectivityNumber();
3457   //
3458   QLabel* aNodesDoubleLab = new QLabel( tr( "NUMBER_OF_THE_DOUBLE_NODES" ), this );
3459   QLabel* aNodesDouble = createField( this, "ctrlNodesDouble" );
3460   myWidgets << aNodesDouble;
3461   myPredicates << aFilterMgr->CreateEqualNodes();
3462   QLabel* aToleranceLab = new QLabel( tr( "DOUBLE_NODES_TOLERANCE" ), this );
3463   myToleranceWidget = new SMESHGUI_SpinBox( this );
3464   myToleranceWidget->RangeStepAndValidator(0.0000000001, 1000000.0, 0.0000001, "length_precision" );
3465   myToleranceWidget->setAcceptNames( false );
3466   myToleranceWidget->SetValue( SMESHGUI::resourceMgr()->doubleValue( "SMESH", "equal_nodes_tolerance", 1e-7 ) );
3467
3468   // edges info
3469   QLabel* anEdgesLab = createLabel( tr( "EDGES_INFO" ), this, Bold );
3470   QLabel* anEdgesDoubleLab = new QLabel( tr( "NUMBER_OF_THE_DOUBLE_EDGES" ),     this );
3471   QLabel* anEdgesDouble = createField( this, "ctrlEdgesDouble" );
3472   myWidgets << anEdgesDouble;
3473   myPredicates << aFilterMgr->CreateEqualEdges();
3474
3475   // faces info
3476   QLabel* aFacesLab = createLabel( tr( "FACES_INFO" ), this, Bold );
3477   QLabel* aFacesDoubleLab = new QLabel( tr( "NUMBER_OF_THE_DOUBLE_FACES" ), this );
3478   QLabel* aFacesDouble = createField( this, "ctrlFacesDouble" );
3479   myWidgets << aFacesDouble;
3480   myPredicates << aFilterMgr->CreateEqualFaces();
3481   QLabel* aFacesOverLab = new QLabel( tr( "NUMBER_OF_THE_OVER_CONSTRAINED" ), this );
3482   QLabel* aFacesOver = createField( this, "ctrlFacesOver" );
3483   myWidgets << aFacesOver;
3484   myPredicates << aFilterMgr->CreateOverConstrainedFace();
3485   QLabel* anAspectRatioLab = new QLabel( tr( "ASPECT_RATIO_HISTOGRAM" ), this );
3486   myPlot = createPlot( this );
3487   myAspectRatio = aFilterMgr->CreateAspectRatio();
3488  
3489   // volumes info
3490   QLabel* aVolumesLab = createLabel( tr( "VOLUMES_INFO" ), this, Bold );
3491   QLabel* aVolumesDoubleLab = new QLabel( tr( "NUMBER_OF_THE_DOUBLE_VOLUMES" ), this );
3492   QLabel* aVolumesDouble = createField( this, "ctrlVolumesDouble" );
3493   myWidgets << aVolumesDouble;
3494   myPredicates << aFilterMgr->CreateEqualVolumes();
3495   QLabel* aVolumesOverLab = new QLabel( tr( "NUMBER_OF_THE_OVER_CONSTRAINED" ), this );
3496   QLabel* aVolumesOver = createField( this, "ctrlVolumesOver" );
3497   myWidgets << aVolumesOver;
3498   myPredicates << aFilterMgr->CreateOverConstrainedVolume();
3499   QLabel* anAspectRatio3DLab = new QLabel( tr( "ASPECT_RATIO_3D_HISTOGRAM" ), this );
3500   myPlot3D = createPlot( this );
3501   myAspectRatio3D = aFilterMgr->CreateAspectRatio3D();
3502
3503   QToolButton* aFreeNodesBtn = new QToolButton( this );
3504   aFreeNodesBtn->setIcon(aComputeIcon);
3505   myButtons << aFreeNodesBtn;       //0
3506
3507   QToolButton* aNodesNbConnBtn = new QToolButton( this );
3508   aNodesNbConnBtn->setIcon(aComputeIcon);
3509   myButtons << aNodesNbConnBtn;     //1
3510
3511   QToolButton* aDoubleNodesBtn = new QToolButton( this );
3512   aDoubleNodesBtn->setIcon(aComputeIcon);
3513   myButtons << aDoubleNodesBtn;     //2
3514
3515   QToolButton* aDoubleEdgesBtn = new QToolButton( this );
3516   aDoubleEdgesBtn->setIcon(aComputeIcon);
3517   myButtons << aDoubleEdgesBtn;     //3
3518
3519   QToolButton* aDoubleFacesBtn = new QToolButton( this );
3520   aDoubleFacesBtn->setIcon(aComputeIcon);
3521   myButtons << aDoubleFacesBtn;     //4
3522
3523   QToolButton* aOverContFacesBtn = new QToolButton( this );
3524   aOverContFacesBtn->setIcon(aComputeIcon);
3525   myButtons << aOverContFacesBtn;   //5
3526
3527   QToolButton* aComputeFaceBtn = new QToolButton( this );
3528   aComputeFaceBtn->setIcon(aComputeIcon);
3529   myButtons << aComputeFaceBtn;     //6
3530
3531   QToolButton* aDoubleVolumesBtn = new QToolButton( this );
3532   aDoubleVolumesBtn->setIcon(aComputeIcon);
3533   myButtons << aDoubleVolumesBtn;   //7
3534
3535   QToolButton* aOverContVolumesBtn = new QToolButton( this );
3536   aOverContVolumesBtn->setIcon(aComputeIcon);
3537   myButtons << aOverContVolumesBtn; //8
3538
3539   QToolButton* aComputeVolumeBtn = new QToolButton( this );
3540   aComputeVolumeBtn->setIcon(aComputeIcon);
3541   myButtons << aComputeVolumeBtn;   //9
3542
3543   connect( aComputeFaceBtn,   SIGNAL( clicked() ), this, SLOT( computeAspectRatio() ) );
3544   connect( aComputeVolumeBtn, SIGNAL( clicked() ), this, SLOT( computeAspectRatio3D() ) );
3545   connect( aFreeNodesBtn,     SIGNAL( clicked() ), this, SLOT( computeFreeNodesInfo() ) );
3546   connect( aNodesNbConnBtn,   SIGNAL( clicked() ), this, SLOT( computeNodesNbConnInfo() ) );
3547   connect( aDoubleNodesBtn,   SIGNAL( clicked() ), this, SLOT( computeDoubleNodesInfo() ) );
3548   connect( aDoubleEdgesBtn,   SIGNAL( clicked() ), this, SLOT( computeDoubleEdgesInfo() ) );
3549   connect( aDoubleFacesBtn,   SIGNAL( clicked() ), this, SLOT( computeDoubleFacesInfo() ) );
3550   connect( aOverContFacesBtn, SIGNAL( clicked() ), this, SLOT( computeOverConstrainedFacesInfo() ) );
3551   connect( aDoubleVolumesBtn, SIGNAL( clicked() ), this, SLOT( computeDoubleVolumesInfo() ) );
3552   connect( aOverContVolumesBtn,SIGNAL( clicked() ), this, SLOT( computeOverConstrainedVolumesInfo() ) );
3553   connect( myToleranceWidget, SIGNAL( valueChanged( double ) ), this, SLOT( setTolerance( double ) ) );
3554   this->setTolerance(myToleranceWidget->GetValue());
3555
3556   l->addWidget( aNameLab,           0, 0 ); //0
3557   l->addWidget( aName,              0, 1 ); //1
3558   
3559   // Node group
3560   QWidget* NodeGrp = new QWidget();
3561   QGridLayout* NodeLayout = new QGridLayout(NodeGrp);
3562   NodeLayout->setSpacing(SPACING); NodeLayout->setMargin(MARGIN);
3563
3564   NodeLayout->addWidget( aNodesFreeLab,      0, 0 );
3565   NodeLayout->addWidget( aNodesFree,         0, 1 );
3566   NodeLayout->addWidget( aFreeNodesBtn,      0, 2 );
3567   NodeLayout->addWidget( aNodesNbConnLab,    1, 0 );
3568   NodeLayout->addWidget( aNodesNbConn,       1, 1 );
3569   NodeLayout->addWidget( aNodesNbConnBtn,    1, 2 );
3570   NodeLayout->addWidget( aNodesDoubleLab,    2, 0 );
3571   NodeLayout->addWidget( aNodesDouble,       2, 1 );
3572   NodeLayout->addWidget( aDoubleNodesBtn,    2, 2 );
3573   NodeLayout->addWidget( aToleranceLab,      3, 0 );
3574   NodeLayout->addWidget( myToleranceWidget,  3, 1 );
3575   NodeLayout->addWidget( myToleranceWidget,  3, 1 );
3576   NodeLayout->setRowStretch(4, 5);
3577
3578   myMeshTB->addItem(NodeGrp, aNodesLab->text());
3579   aNodesLab->setVisible(false);
3580
3581   // Edge group
3582   QWidget* EdgeGrp = new QWidget();
3583   QGridLayout* EdgeLayout = new QGridLayout(EdgeGrp);
3584   EdgeLayout->setSpacing(SPACING); EdgeLayout->setMargin(MARGIN);
3585
3586   EdgeLayout->addWidget( anEdgesDoubleLab,   0, 0 );
3587   EdgeLayout->addWidget( anEdgesDouble,      0, 1 );
3588   EdgeLayout->addWidget( aDoubleEdgesBtn,    0, 2 );
3589   EdgeLayout->setRowStretch(1, 5);
3590
3591   myMeshTB->addItem(EdgeGrp, anEdgesLab->text());
3592   anEdgesLab->setVisible(false);
3593
3594   // Face group
3595   QWidget* FaceGrp = new QWidget();
3596   QGridLayout* FaceLayout = new QGridLayout(FaceGrp);
3597   FaceLayout->setSpacing(SPACING); FaceLayout->setMargin(MARGIN);
3598
3599   FaceLayout->addWidget( aFacesDoubleLab,    0, 0 );
3600   FaceLayout->addWidget( aFacesDouble,       0, 1 );
3601   FaceLayout->addWidget( aDoubleFacesBtn,    0, 2 );
3602   FaceLayout->addWidget( aFacesOverLab,      1, 0 );
3603   FaceLayout->addWidget( aFacesOver,         1, 1 );
3604   FaceLayout->addWidget( aOverContFacesBtn,  1, 2 );
3605   FaceLayout->addWidget( anAspectRatioLab,   2, 0 );
3606   FaceLayout->addWidget( aComputeFaceBtn,    2, 2 );
3607   FaceLayout->addWidget( myPlot,             3, 0, 1, 3 );
3608
3609   myMeshTB->addItem(FaceGrp, aFacesLab->text());
3610   aFacesLab->setVisible(false);
3611
3612   // Volume group
3613   QWidget* VolumeGrp = new QWidget();
3614   QGridLayout* VolumeLayout = new QGridLayout(VolumeGrp);
3615   VolumeLayout->setSpacing(SPACING); VolumeLayout->setMargin(MARGIN);
3616
3617   VolumeLayout->addWidget( aVolumesDoubleLab,  0, 0 );
3618   VolumeLayout->addWidget( aVolumesDouble,     0, 1 );
3619   VolumeLayout->addWidget( aDoubleVolumesBtn,  0, 2 );
3620   VolumeLayout->addWidget( aVolumesOverLab,    1, 0 );
3621   VolumeLayout->addWidget( aVolumesOver,       1, 1 );
3622   VolumeLayout->addWidget( aOverContVolumesBtn,1, 2 );
3623   VolumeLayout->addWidget( anAspectRatio3DLab, 2, 0 );
3624   VolumeLayout->addWidget( aComputeVolumeBtn,  2, 2 );
3625   VolumeLayout->addWidget( myPlot3D,           3, 0, 1, 3 );
3626
3627   myMeshTB->addItem(VolumeGrp, aVolumesLab->text());
3628   aVolumesLab->setVisible(false);
3629
3630   l->addWidget( myMeshTB,                1, 0, 1, 2 ); //2
3631   l->setRowStretch( 2,  5 );
3632
3633   clearInternal();
3634 }
3635
3636 /*!
3637   \brief Destructor.
3638 */
3639 SMESHGUI_CtrlInfo::~SMESHGUI_CtrlInfo()
3640 {
3641 }
3642
3643 /*!
3644   \brief Create plot widget.
3645   \param parent Parent widget.
3646   \return New plot widget.
3647 */
3648 QwtPlot* SMESHGUI_CtrlInfo::createPlot( QWidget* parent )
3649 {
3650   QwtPlot* aPlot = new QwtPlot( parent );
3651   aPlot->setMinimumSize( 100, 100 );
3652   QFont xFont = aPlot->axisFont( QwtPlot::xBottom );
3653   xFont.setPointSize( 5 );
3654   QFont yFont = aPlot->axisFont( QwtPlot::yLeft );
3655   yFont.setPointSize( 5 );
3656   aPlot->setAxisFont( QwtPlot::xBottom, xFont );
3657   aPlot->setAxisFont( QwtPlot::yLeft, yFont );
3658   aPlot->replot();
3659   return aPlot;
3660 }
3661
3662 /*!
3663   \brief Show information on given object.
3664   \param proxy Object to show information on (mesh, sub-mesh, group, ID source).
3665 */
3666 void SMESHGUI_CtrlInfo::showInfo( const SMESH::SelectionProxy& proxy )
3667 {
3668   clearInternal();
3669
3670   if ( !proxy )
3671     return;
3672
3673   myProxy = proxy;
3674   SMESH::SMESH_IDSource_var obj = proxy.object();
3675
3676   myWidgets[0]->setText( proxy.name() );
3677
3678   SMESH::SMESH_Mesh_var mesh = obj->GetMesh();
3679   if ( mesh->_is_nil() ) return;
3680
3681   const bool meshLoaded = mesh->IsLoaded();
3682   if ( !meshLoaded ) // mesh not yet loaded from the hdf file
3683     // enable Compute buttons, just in case obj->GetNbElementsByType() fails
3684     for ( int i = 0; i < myButtons.count(); ++i )
3685       myButtons[i]->setEnabled( true );
3686
3687   SMESH::smIdType_array_var nbElemsByType = obj->GetNbElementsByType();
3688   if ( ! &nbElemsByType.in() ) return;
3689
3690   const CORBA::Long ctrlLimit =
3691     meshLoaded ? SMESHGUI::resourceMgr()->integerValue( "SMESH", "info_controls_limit", 3000 ) : -1;
3692
3693   // nodes info
3694   const SMESH::smIdType nbNodes =  nbElemsByType[ SMESH::NODE ];
3695   // const CORBA::Long nbElems = ( nbElemsByType[ SMESH::EDGE ] +
3696   //                               nbElemsByType[ SMESH::FACE ] +
3697   //                               nbElemsByType[ SMESH::VOLUME ] );
3698   if ( nbNodes > 0 ) {
3699     if ( nbNodes <= ctrlLimit ) {
3700       // free nodes
3701       computeFreeNodesInfo();
3702       // node connectivity number
3703       computeNodesNbConnInfo();
3704       // double nodes
3705       computeDoubleNodesInfo();
3706     }
3707     else {
3708       myButtons[0]->setEnabled( true );
3709       myButtons[1]->setEnabled( true );
3710       myButtons[2]->setEnabled( true );
3711     }
3712   }
3713   else {
3714     myMeshTB->setItemEnabled(0, false );
3715     myMeshTB->widget(0)->setVisible( false );
3716   }
3717
3718   // edges info
3719   if ( nbElemsByType[ SMESH::EDGE ] > 0 ) {
3720     // double edges
3721     if( nbElemsByType[ SMESH::EDGE ] <= ctrlLimit )
3722       computeDoubleEdgesInfo();
3723     else
3724       myButtons[3]->setEnabled( true );
3725   }
3726   else {
3727     myMeshTB->setItemEnabled(1, false );
3728     myMeshTB->widget(1)->setVisible( false );
3729   }
3730
3731   // faces info
3732   if ( nbElemsByType[ SMESH::FACE ] > 0 ) {
3733     if ( nbElemsByType[ SMESH::FACE ] <= ctrlLimit ) {
3734       // double faces
3735       computeDoubleFacesInfo();
3736       // over constrained faces
3737       computeOverConstrainedFacesInfo();
3738       // aspect Ratio histogram
3739       computeAspectRatio();
3740     }
3741     else {
3742       myButtons[4]->setEnabled( true );
3743       myButtons[5]->setEnabled( true );
3744       myButtons[6]->setEnabled( true );
3745     }
3746   }
3747   else {
3748     myMeshTB->setItemEnabled(2, false );
3749     myMeshTB->widget(2)->setVisible( false );
3750   }
3751
3752   // volumes info
3753   if ( nbElemsByType[ SMESH::VOLUME ] > 0 ) {
3754     if ( nbElemsByType[ SMESH::VOLUME ] <= ctrlLimit ) {
3755       // double volumes
3756       computeDoubleVolumesInfo();
3757       // over constrained volumes
3758       computeOverConstrainedVolumesInfo();
3759       // aspect Ratio 3D histogram
3760       computeAspectRatio3D();
3761     }
3762     else {
3763       myButtons[7]->setEnabled( true );
3764       myButtons[8]->setEnabled( true );
3765       myButtons[9]->setEnabled( true );
3766     }
3767   }
3768   else {
3769     myMeshTB->setItemEnabled(3, false );
3770     myMeshTB->widget(3)->setVisible( false );
3771   }
3772   myMeshTB->setCurrentIndex(0);
3773   myMeshTB->setVisible( (nbNodes + nbElemsByType[ SMESH::EDGE ] + 
3774                                    nbElemsByType[ SMESH::FACE ] + 
3775                                    nbElemsByType[ SMESH::VOLUME ]) > 0 );
3776 }
3777
3778 //================================================================================
3779 /*!
3780  * \brief Computes and shows nb of elements satisfying a given predicate
3781  *  \param [in] ft - a predicate type (SMESH::FunctorType)
3782  *  \param [in] iBut - index of one of myButtons to disable
3783  *  \param [in] iWdg - index of one of myWidgets to show the computed number
3784  */
3785 //================================================================================
3786
3787 void SMESHGUI_CtrlInfo::computeNb( int ft, int iBut, int iWdg )
3788 {
3789   myButtons[ iBut ]->setEnabled( false );
3790   myWidgets[ iWdg ]->setText( "" );
3791
3792   if ( !myProxy )
3793     return;
3794
3795   SUIT_OverrideCursor wc;
3796
3797   SMESH::SMESH_IDSource_var obj = myProxy.object();
3798   SMESH::SMESH_Mesh_var mesh = obj->GetMesh();
3799
3800   if ( !mesh->_is_nil() && !mesh->IsLoaded() )
3801   {
3802     mesh->Load();
3803     showInfo( myProxy ); // try to show all values
3804     if ( !myWidgets[ iWdg ]->text().isEmpty() )
3805       return; // <ft> predicate already computed
3806   }
3807   // look for a predicate of type <ft>
3808   for ( int i = 0; i < myPredicates.count(); ++i )
3809     if ( myPredicates[i]->GetFunctorType() == ft )
3810     {
3811       CORBA::Long nb = myPredicates[i]->NbSatisfying( obj );
3812       myWidgets[ iWdg ]->setText( QString::number( nb ) );
3813     }
3814 }
3815
3816 void SMESHGUI_CtrlInfo::computeFreeNodesInfo()
3817 {
3818   computeNb( SMESH::FT_FreeNodes, 0, 1 );
3819 }
3820
3821 void SMESHGUI_CtrlInfo::computeDoubleNodesInfo()
3822 {
3823   computeNb( SMESH::FT_EqualNodes, 2, 3 );
3824 }
3825
3826 void SMESHGUI_CtrlInfo::computeDoubleEdgesInfo()
3827 {
3828   computeNb( SMESH::FT_EqualEdges, 3, 4 );
3829 }
3830
3831 void SMESHGUI_CtrlInfo::computeDoubleFacesInfo()
3832 {
3833   computeNb( SMESH::FT_EqualFaces, 4, 5 );
3834 }
3835
3836 void SMESHGUI_CtrlInfo::computeOverConstrainedFacesInfo()
3837 {
3838   computeNb( SMESH::FT_OverConstrainedFace, 5, 6 );
3839 }
3840
3841 void SMESHGUI_CtrlInfo::computeDoubleVolumesInfo()
3842 {
3843   computeNb( SMESH::FT_EqualVolumes, 7, 7 );
3844 }
3845
3846 void SMESHGUI_CtrlInfo::computeOverConstrainedVolumesInfo()
3847 {
3848   computeNb( SMESH::FT_OverConstrainedVolume, 8, 8 );
3849 }
3850
3851 void SMESHGUI_CtrlInfo::computeNodesNbConnInfo()
3852 {
3853   myButtons[ 1 ]->setEnabled( false );
3854   myWidgets[ 2 ]->setText( "" );
3855
3856   if ( !myProxy )
3857     return;
3858
3859   SUIT_OverrideCursor wc;
3860
3861   SMESH::SMESH_IDSource_var obj = myProxy.object();
3862   SMESH::SMESH_Mesh_var mesh = obj->GetMesh();
3863
3864   if ( !mesh->IsLoaded() )
3865   {
3866     mesh->Load();
3867     showInfo( myProxy ); // try to show all values
3868     if ( !myWidgets[ 2 ]->text().isEmpty() )
3869       return; // already computed
3870   }
3871   myNodeConnFunctor->SetMesh( mesh );
3872   SMESH::Histogram_var histogram =
3873     myNodeConnFunctor->GetLocalHistogram( 1, /*isLogarithmic=*/false, obj );
3874
3875   myWidgets[ 2 ]->setText( QString::number( histogram[0].max ) );
3876 }
3877
3878 void SMESHGUI_CtrlInfo::computeAspectRatio()
3879 {
3880 #ifndef DISABLE_PLOT2DVIEWER
3881   myButtons[6]->setEnabled( false );
3882
3883   if ( !myProxy )
3884     return;
3885
3886   SUIT_OverrideCursor wc;
3887
3888   SMESH::SMESH_IDSource_var obj = myProxy.object();
3889   SMESH::SMESH_Mesh_var mesh = obj->GetMesh();
3890
3891   Plot2d_Histogram* aHistogram = getHistogram( myAspectRatio );
3892   if ( aHistogram && !aHistogram->isEmpty() ) {
3893     QwtPlotItem* anItem = aHistogram->createPlotItem();
3894     anItem->attach( myPlot );
3895     myPlot->replot();
3896   }
3897   delete aHistogram;
3898 #endif
3899 }
3900
3901 void SMESHGUI_CtrlInfo::computeAspectRatio3D()
3902 {
3903 #ifndef DISABLE_PLOT2DVIEWER
3904   myButtons[9]->setEnabled( false );
3905
3906   if ( !myProxy )
3907     return;
3908
3909   SUIT_OverrideCursor wc;
3910
3911   SMESH::SMESH_IDSource_var obj = myProxy.object();
3912   SMESH::SMESH_Mesh_var mesh = obj->GetMesh();
3913
3914   Plot2d_Histogram* aHistogram = getHistogram( myAspectRatio3D );
3915   if ( aHistogram && !aHistogram->isEmpty() ) {
3916     QwtPlotItem* anItem = aHistogram->createPlotItem();
3917     anItem->attach( myPlot3D );
3918     myPlot3D->replot();
3919   }
3920   delete aHistogram;
3921 #endif
3922 }
3923
3924 /*!
3925   \brief Internal clean-up (reset widget)
3926 */
3927 void SMESHGUI_CtrlInfo::clearInternal()
3928 {
3929   for (int i=0; i<=3;i++) {
3930     myMeshTB->setItemEnabled(i, true );
3931     myMeshTB->widget(i)->setVisible( true );
3932   }
3933   for( int i=0; i<=9; i++)
3934     myButtons[i]->setEnabled( false );
3935   myPlot->detachItems();
3936   myPlot3D->detachItems();
3937   myPlot->replot();
3938   myPlot3D->replot();
3939   myWidgets[0]->setText( QString() );
3940   for ( int i = 1; i < myWidgets.count(); i++ )
3941     myWidgets[i]->setText( "" );
3942 }
3943
3944 void SMESHGUI_CtrlInfo::setTolerance( double theTolerance )
3945 {
3946   myButtons[2]->setEnabled( true );
3947   myWidgets[3]->setText("");
3948   for ( int i = 0; i < myPredicates.count(); ++i )
3949     if ( myPredicates[i]->GetFunctorType() == SMESH::FT_EqualNodes )
3950     {
3951       SMESH::EqualNodes_var functor = SMESH::EqualNodes::_narrow( myPredicates[i] );
3952       if ( !functor->_is_nil() )
3953         functor->SetTolerance( theTolerance );
3954     }
3955 }
3956
3957 #ifndef DISABLE_PLOT2DVIEWER
3958 Plot2d_Histogram* SMESHGUI_CtrlInfo::getHistogram( SMESH::NumericalFunctor_ptr aNumFun )
3959 {
3960   SUIT_OverrideCursor wc;
3961
3962   SMESH::SMESH_IDSource_var obj = myProxy.object();
3963   SMESH::SMESH_Mesh_var mesh = obj->GetMesh();
3964
3965   if ( !mesh->IsLoaded() )
3966     mesh->Load();
3967   aNumFun->SetMesh( mesh );
3968
3969   CORBA::Long cprecision = 6;
3970   if ( SMESHGUI::resourceMgr()->booleanValue( "SMESH", "use_precision", false ) )
3971     cprecision = SMESHGUI::resourceMgr()->integerValue( "SMESH", "controls_precision", -1 );
3972   aNumFun->SetPrecision( cprecision );
3973
3974   int nbIntervals = SMESHGUI::resourceMgr()->integerValue( "SMESH", "scalar_bar_num_colors", false );
3975
3976   SMESH::Histogram_var histogramVar = aNumFun->GetLocalHistogram( nbIntervals,
3977                                                                   /*isLogarithmic=*/false,
3978                                                                   obj );
3979   Plot2d_Histogram* aHistogram = new Plot2d_Histogram();
3980   aHistogram->setColor( palette().color( QPalette::Highlight ) );
3981   if ( &histogramVar.in() )
3982   {
3983     for ( size_t i = 0, nb = histogramVar->length(); i < nb; i++ )
3984       aHistogram->addPoint( 0.5 * ( histogramVar[i].min + histogramVar[i].max ), histogramVar[i].nbEvents );
3985     if ( histogramVar->length() >= 2 )
3986       aHistogram->setWidth( ( histogramVar[0].max - histogramVar[0].min ) * 0.8 );
3987   }
3988   return aHistogram;
3989 }
3990 #endif
3991
3992 void SMESHGUI_CtrlInfo::saveInfo( QTextStream &out )
3993 {
3994   // title
3995   QString title = tr( "CTRL_INFO" );
3996   out << ruler( title.size() ) << endl;
3997   out << title << endl;
3998   out << ruler( title.size() ) << endl;
3999   out << endl;
4000
4001   // info
4002   out << tr( "NAME_LAB" ) << "  " << myWidgets[0]->text() << endl;
4003   out << tr( "NODES_INFO" ) << endl;
4004   out << indent() << tr( "NUMBER_OF_THE_FREE_NODES" ) << ": " << myWidgets[1]->text() << endl;
4005   out << indent() << tr( "NUMBER_OF_THE_DOUBLE_NODES" ) << ": " << myWidgets[3]->text() << endl;
4006   out << tr( "EDGES_INFO" ) << endl;
4007   out << indent() << tr( "NUMBER_OF_THE_DOUBLE_EDGES" ) << ": " << myWidgets[4]->text() << endl;
4008   out << tr( "FACES_INFO" ) << endl;
4009   out << indent() << tr( "NUMBER_OF_THE_DOUBLE_FACES" ) << ": " << myWidgets[5]->text() << endl;
4010   out << indent() << tr( "NUMBER_OF_THE_OVER_CONSTRAINED" ) << ": " << myWidgets[6]->text() << endl;
4011   out << tr( "VOLUMES_INFO" ) << endl;
4012   out << indent() << tr( "NUMBER_OF_THE_DOUBLE_VOLUMES" ) << ": " << myWidgets[7]->text() << endl;
4013   out << indent() << tr( "NUMBER_OF_THE_OVER_CONSTRAINED" ) << ": " << myWidgets[8]->text() << endl;
4014 }
4015
4016 ////////////////////////////////////////////////////////////////////////////////
4017 /// \class SMESHGUI_CtrlInfoDlg
4018 /// \brief Overall Mesh Quality dialog.
4019 /// \todo Add selection button to reactivate selection on move from other dlg.
4020 ////////////////////////////////////////////////////////////////////////////////
4021
4022 /*!
4023   \brief Constructor
4024   \param parent parent widget
4025 */
4026 SMESHGUI_CtrlInfoDlg::SMESHGUI_CtrlInfoDlg( QWidget* parent )
4027   : QDialog( parent )
4028 {
4029   setAttribute( Qt::WA_DeleteOnClose, true );
4030   setWindowTitle( tr( "CTRL_INFO" ) );
4031   setMinimumSize( 400, 600 );
4032
4033   myCtrlInfo = new SMESHGUI_CtrlInfo( this );
4034   
4035   // buttons
4036   QPushButton* okBtn = new QPushButton( tr( "SMESH_BUT_OK" ), this );
4037   okBtn->setAutoDefault( true );
4038   okBtn->setDefault( true );
4039   okBtn->setFocus();
4040   QPushButton* dumpBtn = new QPushButton( tr( "BUT_DUMP_MESH" ), this );
4041   dumpBtn->setAutoDefault( true );
4042   QPushButton* helpBtn = new QPushButton( tr( "SMESH_BUT_HELP" ), this );
4043   helpBtn->setAutoDefault( true );
4044
4045   QHBoxLayout* btnLayout = new QHBoxLayout;
4046   btnLayout->setSpacing( SPACING );
4047   btnLayout->setMargin( 0 );
4048
4049   btnLayout->addWidget( okBtn );
4050   btnLayout->addWidget( dumpBtn );
4051   btnLayout->addStretch( 10 );
4052   btnLayout->addWidget( helpBtn );
4053
4054   QVBoxLayout* l = new QVBoxLayout ( this );
4055   l->setMargin( 0 );
4056   l->setSpacing( SPACING );
4057   l->addWidget( myCtrlInfo );
4058   l->addLayout( btnLayout );
4059
4060   connect( okBtn,   SIGNAL( clicked() ), this, SLOT( reject() ) );
4061   connect( dumpBtn, SIGNAL( clicked() ), this, SLOT( dump() ) );
4062   connect( helpBtn, SIGNAL( clicked() ), this, SLOT( help() ) );
4063   connect( SMESHGUI::GetSMESHGUI(), SIGNAL( SignalDeactivateActiveDialog() ), this, SLOT( deactivate() ) );
4064   connect( SMESHGUI::GetSMESHGUI(), SIGNAL( SignalCloseAllDialogs() ),        this, SLOT( reject() ) );
4065
4066   updateSelection();
4067 }
4068
4069 /*!
4070   \brief Destructor
4071 */
4072 SMESHGUI_CtrlInfoDlg::~SMESHGUI_CtrlInfoDlg()
4073 {
4074 }
4075
4076 /*!
4077   \brief Show mesh quality information on given object.
4078   \param io Interactive object.
4079 */
4080 void SMESHGUI_CtrlInfoDlg::showInfo( const Handle(SALOME_InteractiveObject)& io )
4081 {  
4082   if ( !io.IsNull() )
4083     showInfo( SMESH::SelectionProxy( io ) );
4084 }
4085
4086 /*!
4087   \brief Show mesh quality information on given object.
4088   \param proxy Selection proxy.
4089 */
4090 void SMESHGUI_CtrlInfoDlg::showInfo( const SMESH::SelectionProxy& proxy )
4091 {
4092   SUIT_OverrideCursor wc;
4093
4094   if ( !proxy )
4095     return;
4096
4097   myProxy = proxy;
4098   myCtrlInfo->showInfo( proxy );
4099 }
4100
4101 /*!
4102   \brief Show mesh information
4103 */
4104 void SMESHGUI_CtrlInfoDlg::updateInfo()
4105 {
4106   SALOME_ListIO selected;
4107   SMESHGUI::selectionMgr()->selectedObjects( selected );
4108
4109   if ( selected.Extent() == 1 )
4110     showInfo( selected.First() );
4111   else
4112     showInfo( myProxy );
4113 }
4114
4115 /*!
4116   \brief Perform clean-up actions on the dialog box closing.
4117 */
4118 void SMESHGUI_CtrlInfoDlg::reject()
4119 {
4120   SMESH::SetPointRepresentation( false );
4121   QDialog::reject();
4122 }
4123
4124 /*!
4125   \brief Setup selection mode depending on the current dialog box state.
4126 */
4127 void SMESHGUI_CtrlInfoDlg::updateSelection()
4128 {
4129   LightApp_SelectionMgr* selMgr = SMESHGUI::selectionMgr();
4130   disconnect( selMgr, 0, this, 0 );
4131   SMESH::SetPointRepresentation( false );  
4132   connect( selMgr, SIGNAL( currentSelectionChanged() ), this, SLOT( updateInfo() ) );
4133   updateInfo();  
4134 }
4135
4136 /*!
4137   \brief Deactivate dialog box.
4138 */
4139 void SMESHGUI_CtrlInfoDlg::deactivate()
4140 {
4141   disconnect( SMESHGUI::selectionMgr(), SIGNAL( currentSelectionChanged() ), this, SLOT( updateInfo() ) );
4142 }
4143
4144 /*!
4145   \brief Dump information to file.
4146 */
4147 void SMESHGUI_CtrlInfoDlg::dump()
4148 {
4149   DumpFileDlg fd( this, false );
4150   fd.setWindowTitle( tr( "SAVE_INFO" ) );
4151   fd.setNameFilters( QStringList() << tr( "TEXT_FILES" ) );
4152   if ( fd.exec() == QDialog::Accepted )
4153   {
4154     QString fileName = fd.selectedFile();
4155     if ( !fileName.isEmpty() ) {
4156       QFile file( fileName );
4157       if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) )
4158         return;
4159
4160       QTextStream out( &file );
4161       myCtrlInfo->saveInfo( out );
4162     }
4163   }
4164 }
4165
4166 /*!
4167   \brief Show documentation on dialog.
4168 */
4169 void SMESHGUI_CtrlInfoDlg::help()
4170 {
4171   SMESH::ShowHelpFile( "mesh_infos.html#mesh-quality-info-anchor" );
4172 }