Salome HOME
e2ee202b32cc97ba6b8cd13f867b1a50165bd62f
[modules/smesh.git] / src / SMESHGUI / SMESHGUI_MultiEditDlg.cxx
1 //  SMESH SMESHGUI : GUI for SMESH component
2 //
3 //  Copyright (C) 2003  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 //  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS 
5 // 
6 //  This library is free software; you can redistribute it and/or 
7 //  modify it under the terms of the GNU Lesser General Public 
8 //  License as published by the Free Software Foundation; either 
9 //  version 2.1 of the License. 
10 // 
11 //  This library is distributed in the hope that it will be useful, 
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of 
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
14 //  Lesser General Public License for more details. 
15 // 
16 //  You should have received a copy of the GNU Lesser General Public 
17 //  License along with this library; if not, write to the Free Software 
18 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
19 // 
20 //  See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org 
21 //
22 //
23 //
24 //  File   : SMESHGUI_MultiEditDlg.cxx
25 //  Author : Sergey LITONIN
26 //  Module : SMESH
27
28 #include "SMESHGUI_MultiEditDlg.h"
29 #include "SMESHGUI_FilterDlg.h"
30 #include "SMESHGUI_Filter.h"
31
32 #include "SMESHGUI.h"
33 #include "SMESHGUI_Utils.h"
34 #include "SMESHGUI_VTKUtils.h"
35 #include "SMESHGUI_MeshUtils.h"
36
37 #include "QAD_Desktop.h"
38 #include "QAD_RightFrame.h"
39
40 #include "VTKViewer_ViewFrame.h"
41
42 #include "SMESH_Actor.h"
43 #include "SMDS_Mesh.hxx"
44 #include "SMDS_MeshElement.hxx"
45
46 #include "SALOME_Selection.h"
47 #include "SALOME_ListIteratorOfListIO.hxx"
48 #include "VTKViewer_InteractorStyleSALOME.h"
49
50 #include <vtkCell3D.h>
51 #include <vtkQuad.h>
52 #include <vtkTriangle.h>
53 #include <vtkPolygon.h>
54 #include <vtkConvexPointSet.h>
55 #include <vtkIdList.h>
56 #include <vtkIntArray.h>
57 #include <vtkCellArray.h>
58 #include <vtkUnsignedCharArray.h>
59 #include <vtkUnstructuredGrid.h>
60 #include <vtkDataSetMapper.h>
61
62 #include <TColStd_IndexedMapOfInteger.hxx>
63 #include <TColStd_MapIteratorOfMapOfInteger.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_DataMapOfIntegerInteger.hxx>
66
67 #include <qcheckbox.h>
68 #include <qframe.h>
69 #include <qgroupbox.h>
70 #include <qlabel.h>
71 #include <qlayout.h>
72 #include <qlineedit.h>
73 #include <qlistbox.h>
74 #include <qpushbutton.h>
75 #include <qapplication.h>
76 #include <qhbuttongroup.h>
77 #include <qradiobutton.h>
78
79 // IDL Headers
80 #include "SALOMEconfig.h"
81 #include CORBA_SERVER_HEADER(SMESH_Group)
82
83 #define SPACING 5
84 #define MARGIN  10
85
86 /*
87   Class       : SMESHGUI_MultiEditDlg
88   Description : Description : Inversion of the diagonal of a pseudo-quadrangle formed by 
89                 2 neighboring triangles with 1 common edge
90 */
91
92 //=======================================================================
93 // name    : SMESHGUI_MultiEditDlg::SMESHGUI_MultiEditDlg
94 // Purpose : Constructor
95 //=======================================================================
96 SMESHGUI_MultiEditDlg::SMESHGUI_MultiEditDlg( QWidget*              theParent, 
97                                               SALOME_Selection*     theSelection,
98                                               const int             theMode,
99                                               const bool            the3d2d,
100                                               const char*           theName )
101 : QDialog( theParent, theName, false, 
102            WStyle_Customize | WStyle_NormalBorder | WStyle_Title | WStyle_SysMenu | WDestructiveClose )
103 {
104   myFilterDlg = 0;
105   mySubmeshFilter = new SMESH_TypeFilter( SUBMESH );
106   myGroupFilter = new SMESH_TypeFilter( GROUP );
107
108   myEntityType = 0;
109
110   myFilterType = theMode;
111   QVBoxLayout* aDlgLay = new QVBoxLayout( this, MARGIN, SPACING );
112
113   QFrame* aMainFrame = createMainFrame  ( this, the3d2d );
114   QFrame* aBtnFrame  = createButtonFrame( this );
115
116   aDlgLay->addWidget( aMainFrame );
117   aDlgLay->addWidget( aBtnFrame );
118
119   aDlgLay->setStretchFactor( aMainFrame, 1 );
120   aDlgLay->setStretchFactor( aBtnFrame, 0 );
121   Init( theSelection ) ;
122 }
123
124 //=======================================================================
125 // name    : SMESHGUI_MultiEditDlg::createMainFrame
126 // Purpose : Create frame containing dialog's input fields
127 //=======================================================================
128 QFrame* SMESHGUI_MultiEditDlg::createMainFrame( QWidget* theParent, const bool the3d2d )
129 {
130   QGroupBox* aMainGrp = new QGroupBox( 1, Qt::Horizontal, theParent );
131   aMainGrp->setFrameStyle( QFrame::NoFrame );
132   aMainGrp->setInsideMargin( 0 );
133
134   QPixmap aPix( QAD_Desktop::getResourceManager()->loadPixmap( "SMESH",tr( "ICON_SELECT" ) ) );
135   
136   // "Selected cells" group
137   mySelGrp = new QGroupBox( 1, Qt::Horizontal,  aMainGrp );
138
139   myEntityTypeGrp = 0;
140   if ( the3d2d ) {
141     myEntityTypeGrp = new QHButtonGroup( tr("SMESH_ELEMENTS_TYPE"), mySelGrp );
142     (new QRadioButton( tr("SMESH_FACE"),   myEntityTypeGrp ))->setChecked( true );
143     (new QRadioButton( tr("SMESH_VOLUME"), myEntityTypeGrp ));
144     myEntityType = myEntityTypeGrp->id( myEntityTypeGrp->selected() );
145   }
146
147   QFrame* aFrame = new QFrame( mySelGrp );
148   
149   myListBox = new QListBox( aFrame );
150   myListBox->setSelectionMode( QListBox::Extended );
151   myListBox->setSizePolicy( QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding) );
152 //  myListBox->setColumnMode( QListBox::FitToHeight );
153   myListBox->installEventFilter( this );
154   
155   myFilterBtn = new QPushButton( tr( "FILTER" )   , aFrame );
156   myAddBtn    = new QPushButton( tr( "ADD" )      , aFrame );
157   myRemoveBtn = new QPushButton( tr( "REMOVE" )   , aFrame );
158   mySortBtn   = new QPushButton( tr( "SORT_LIST" ), aFrame );
159
160   QGridLayout* aLay = new QGridLayout( aFrame, 5, 2, 0, 5 );
161   aLay->addMultiCellWidget( myListBox, 0, 4, 0, 0 );
162   aLay->addWidget( myFilterBtn, 0, 1 );
163   aLay->addWidget( myAddBtn, 1, 1 );
164   aLay->addWidget( myRemoveBtn, 2, 1 );
165   aLay->addWidget( mySortBtn, 3, 1 );
166   
167   QSpacerItem* aSpacer = new QSpacerItem( 0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding );
168   aLay->addItem( aSpacer, 4, 1 );
169   
170   myToAllChk = new QCheckBox( tr( "TO_ALL" ), mySelGrp );
171
172   // "Select from" group
173   QGroupBox* aGrp = new QGroupBox( 3, Qt::Horizontal, tr( "SELECT_FROM" ), aMainGrp );
174   
175   mySubmeshChk = new QCheckBox( tr( "SMESH_SUBMESH" ), aGrp );
176   mySubmeshBtn = new QPushButton( aGrp );
177   mySubmesh = new QLineEdit( aGrp );
178   mySubmesh->setReadOnly( true );
179   mySubmeshBtn->setPixmap( aPix );
180   
181   myGroupChk = new QCheckBox( tr( "GROUP" ), aGrp );
182   myGroupBtn = new QPushButton( aGrp );
183   myGroup = new QLineEdit( aGrp );
184   myGroup->setReadOnly( true );
185   myGroupBtn->setPixmap( aPix );
186
187   return aMainGrp;
188 }
189
190 //=======================================================================
191 // name    : SMESHGUI_MultiEditDlg::createButtonFrame
192 // Purpose : Create frame containing buttons
193 //=======================================================================
194 QFrame* SMESHGUI_MultiEditDlg::createButtonFrame( QWidget* theParent )
195 {
196   QFrame* aFrame = new QFrame( theParent );
197   aFrame->setFrameStyle( QFrame::Box | QFrame::Sunken );
198
199   myOkBtn     = new QPushButton( tr( "SMESH_BUT_OK"    ), aFrame );
200   myApplyBtn  = new QPushButton( tr( "SMESH_BUT_APPLY" ), aFrame );
201   myCloseBtn  = new QPushButton( tr( "SMESH_BUT_CLOSE" ), aFrame );
202
203   QSpacerItem* aSpacer = new QSpacerItem( 0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum );
204
205   QHBoxLayout* aLay = new QHBoxLayout( aFrame, MARGIN, SPACING );
206
207   aLay->addWidget( myOkBtn );
208   aLay->addWidget( myApplyBtn );
209   aLay->addItem( aSpacer);
210   aLay->addWidget( myCloseBtn );
211
212   return aFrame;
213 }
214
215 //=======================================================================
216 // name    : SMESHGUI_MultiEditDlg::isValid
217 // Purpose : Verify validity of input data
218 //=======================================================================
219 bool SMESHGUI_MultiEditDlg::isValid( const bool /*theMess*/ ) const
220 {
221   return (!myMesh->_is_nil() &&
222           (myListBox->count() > 0 || (myToAllChk->isChecked() && myActor)));
223 }
224
225 //=======================================================================
226 // name    : SMESHGUI_MultiEditDlg::~SMESHGUI_MultiEditDlg
227 // Purpose : Destructor
228 //=======================================================================
229 SMESHGUI_MultiEditDlg::~SMESHGUI_MultiEditDlg()
230 {
231   if ( myFilterDlg != 0 )
232   {
233     myFilterDlg->reparent( 0, QPoint() );
234     delete myFilterDlg;
235   }
236 }
237
238 //=======================================================================
239 // name    : SMESHGUI_MultiEditDlg::eventFilter
240 // Purpose : event filter
241 //=======================================================================
242 bool SMESHGUI_MultiEditDlg::eventFilter( QObject* object, QEvent* event )
243 {
244   if ( object == myListBox && event->type() == QEvent::KeyPress ) {
245     QKeyEvent* ke = (QKeyEvent*)event;
246     if ( ke->key() == Key_Delete )
247       onRemoveBtn();
248   }
249   return QDialog::eventFilter( object, event );
250 }
251
252 //=======================================================================
253 // name    : SMESHGUI_MultiEditDlg::Init
254 // Purpose : Init dialog fields, connect signals and slots, show dialog
255 //=======================================================================
256 void SMESHGUI_MultiEditDlg::Init( SALOME_Selection* theSelection )
257 {
258   SMESHGUI* aSMESHGUI = SMESHGUI::GetSMESHGUI();
259   mySelection = theSelection;
260   aSMESHGUI->SetActiveDialogBox( ( QDialog* )this ) ;
261   myListBox->clear();
262   myIds.Clear();
263   myBusy = false;
264   myActor = 0;
265   emit ListContensChanged();
266
267   // main buttons
268   connect( myOkBtn,    SIGNAL( clicked() ), SLOT( onOk() ) );
269   connect( myCloseBtn, SIGNAL( clicked() ), SLOT( onClose() ) ) ;
270   connect( myApplyBtn, SIGNAL( clicked() ), SLOT( onApply() ) );
271
272   // selection and SMESHGUI
273   connect( mySelection, SIGNAL( currentSelectionChanged() ), SLOT( onSelectionDone() ) );
274   connect( aSMESHGUI, SIGNAL( SignalDeactivateActiveDialog() ), SLOT( onDeactivate() ) );
275   connect( aSMESHGUI, SIGNAL( SignalCloseAllDialogs() ), SLOT( onClose() ) );
276
277   // dialog controls
278   connect( myFilterBtn, SIGNAL( clicked() ), SLOT( onFilterBtn()   ) );
279   connect( myAddBtn   , SIGNAL( clicked() ), SLOT( onAddBtn()      ) );
280   connect( myRemoveBtn, SIGNAL( clicked() ), SLOT( onRemoveBtn()   ) );
281   connect( mySortBtn  , SIGNAL( clicked() ), SLOT( onSortListBtn() ) );
282   
283   connect( mySubmeshChk, SIGNAL( stateChanged( int ) ), SLOT( onSubmeshChk() ) );
284   connect( myGroupChk  , SIGNAL( stateChanged( int ) ), SLOT( onGroupChk()   ) );
285   connect( myToAllChk  , SIGNAL( stateChanged( int ) ), SLOT( onToAllChk()   ) );
286
287   if ( myEntityTypeGrp )
288     connect( myEntityTypeGrp, SIGNAL( clicked(int) ), SLOT( on3d2dChanged(int) ) );
289
290   connect( myListBox, SIGNAL( selectionChanged() ), SLOT( onListSelectionChanged() ) );
291
292   onSelectionDone();
293
294   // set selection mode
295   setSelectionMode();
296   updateButtons();
297 }
298
299 //=======================================================================
300 // name    : SMESHGUI_MultiEditDlg::onOk
301 // Purpose : SLOT called when "Ok" button pressed. 
302 //           Assign filters VTK viewer and close dialog
303 //=======================================================================
304 void SMESHGUI_MultiEditDlg::onOk()
305 {
306   if ( onApply() )
307     onClose();
308 }
309
310 //=======================================================================
311 // name    : SMESHGUI_MultiEditDlg::getIds
312 // Purpose : Retrive identifiers from list box
313 //=======================================================================
314 SMESH::long_array_var SMESHGUI_MultiEditDlg::getIds()
315 {
316   SMESH::long_array_var anIds = new SMESH::long_array;
317   
318   if ( myToAllChk->isChecked() )
319   {
320     myIds.Clear();
321     SMESH_Actor * anActor = SMESH::FindActorByObject( myMesh );
322     if ( !anActor )
323       anActor = myActor;
324     if ( anActor != 0 )
325     {
326       TVisualObjPtr aVisualObj = anActor->GetObject();
327       vtkUnstructuredGrid* aGrid = aVisualObj->GetUnstructuredGrid();
328       if ( aGrid != 0 )
329       {
330         for ( int i = 0, n = aGrid->GetNumberOfCells(); i < n; i++ )
331         {
332           vtkCell* aCell = aGrid->GetCell( i );
333           if ( aCell != 0 )
334           {
335             vtkTriangle* aTri = vtkTriangle::SafeDownCast(aCell);
336             vtkQuad*     aQua = vtkQuad::SafeDownCast(aCell);
337             vtkPolygon*  aPG  = vtkPolygon::SafeDownCast(aCell);
338
339             vtkCell3D*         a3d = vtkCell3D::SafeDownCast(aCell);
340             vtkConvexPointSet* aPH = vtkConvexPointSet::SafeDownCast(aCell);
341
342             if ( aTri && myFilterType == SMESHGUI_TriaFilter || 
343                  aQua && myFilterType == SMESHGUI_QuadFilter ||
344                  ( aTri || aQua || aPG ) && myFilterType == SMESHGUI_FaceFilter ||
345                  ( a3d || aPH ) && myFilterType == SMESHGUI_VolumeFilter )
346             {
347               int anObjId = aVisualObj->GetElemObjId( i );
348               myIds.Add( anObjId );
349             }
350           }
351         }
352       }
353     }
354   }
355
356   anIds->length( myIds.Extent() );
357   TColStd_MapIteratorOfMapOfInteger anIter( myIds );
358   for ( int i = 0; anIter.More(); anIter.Next()  )
359   {
360     anIds[ i++ ] = anIter.Key();
361   }
362   return anIds._retn();
363 }
364
365 //=======================================================================
366 // name    : SMESHGUI_MultiEditDlg::onClose
367 // Purpose : SLOT called when "Close" button pressed. Close dialog
368 //=======================================================================
369 void SMESHGUI_MultiEditDlg::onClose()
370 {
371   QAD_Application::getDesktop()->SetSelectionMode( ActorSelection );
372   disconnect( mySelection, 0, this, 0 );
373   disconnect( SMESHGUI::GetSMESHGUI(), 0, this, 0 );
374   SMESHGUI::GetSMESHGUI()->ResetState();
375   
376   SMESH::RemoveFilters();
377   SMESH::SetPickable();
378
379   mySelection->ClearIObjects();
380   mySelection->ClearFilters();
381   
382   reject();
383 }
384
385 //=======================================================================
386 // name    : SMESHGUI_MultiEditDlg::onSelectionDone
387 // Purpose : SLOT called when selection changed
388 //=======================================================================
389 void SMESHGUI_MultiEditDlg::onSelectionDone()
390 {
391   if ( myBusy || !isEnabled() ) return;
392   myBusy = true;
393
394   int nbSel = mySelection->IObjectCount();
395   myListBox->clearSelection();
396
397   if ( mySubmeshChk->isChecked() || myGroupChk->isChecked() )
398   {
399     QLineEdit* aNameEdit = mySubmeshChk->isChecked() ? mySubmesh : myGroup;
400     int nbSel = mySelection->IObjectCount();
401     if ( nbSel == 1 )
402     {
403       Handle(SALOME_InteractiveObject) anIO = mySelection->firstIObject();
404       anIO.IsNull() ? aNameEdit->clear() : aNameEdit->setText( anIO->getName() );
405
406       if ( mySubmeshChk->isChecked() )
407       {
408         SMESH::SMESH_subMesh_var aSubMesh =
409           SMESH::IObjectToInterface<SMESH::SMESH_subMesh>( anIO );
410         if ( !aSubMesh->_is_nil() )
411           myMesh = aSubMesh->GetFather();
412       }
413       else
414       {
415         SMESH::SMESH_GroupBase_var aGroup =
416           SMESH::IObjectToInterface<SMESH::SMESH_GroupBase>( anIO );
417         if ( !aGroup->_is_nil() )
418           myMesh = aGroup->GetMesh();
419       }
420     }
421     else if ( nbSel > 1 )
422     {
423       QString aStr = mySubmeshChk->isChecked() ? 
424         tr( "SMESH_SUBMESH_SELECTED" ) : tr( "SMESH_GROUP_SELECTED" );
425       aNameEdit->setText( aStr.arg( nbSel ) );
426     }
427     else
428       aNameEdit->clear();
429   }
430   else if ( nbSel == 1 )
431   {
432     QString aListStr = "";
433     int aNbItems = SMESH::GetNameOfSelectedElements(mySelection, aListStr);
434     if ( aNbItems > 0 )
435     {
436       QStringList anElements = QStringList::split(" ", aListStr);
437       QListBoxItem* anItem = 0;
438       for ( QStringList::iterator it = anElements.begin(); it != anElements.end(); ++it)
439       {
440         anItem = myListBox->findItem( *it, Qt::ExactMatch );
441         if (anItem) myListBox->setSelected( anItem, true );
442       }
443     }
444
445     myMesh = SMESH::GetMeshByIO( mySelection->firstIObject() );
446   }
447
448   if ( nbSel == 1 ) {
449     myActor = SMESH::FindActorByEntry(mySelection->firstIObject()->getEntry());
450     if (!myActor)
451       myActor = SMESH::FindActorByObject( myMesh );
452     VTKViewer_InteractorStyleSALOME* aStyle = SMESH::GetInteractorStyle();
453     Handle(VTKViewer_Filter) aFilter = aStyle->GetFilter( myFilterType );
454     if ( !aFilter.IsNull() )
455       aFilter->SetActor( myActor );
456 //if ( myActor )
457 //    SMESH::SetPickable(myActor);
458   }
459   myBusy = false;
460
461   updateButtons();
462 }
463
464 //=======================================================================
465 // name    : SMESHGUI_MultiEditDlg::onDeactivate
466 // Purpose : SLOT called when dialog must be deativated
467 //=======================================================================
468 void SMESHGUI_MultiEditDlg::onDeactivate()
469 {
470   setEnabled( false );
471 }
472
473 //=======================================================================
474 // name    : SMESHGUI_MultiEditDlg::enterEvent
475 // Purpose : Event filter
476 //=======================================================================
477 void SMESHGUI_MultiEditDlg::enterEvent( QEvent* )
478 {
479   if ( !isEnabled() ) {
480     SMESHGUI::GetSMESHGUI()->EmitSignalDeactivateDialog();
481     setEnabled( true );
482     setSelectionMode();
483   }
484 }
485
486
487 //=================================================================================
488 // function : closeEvent()
489 // purpose  :
490 //=================================================================================
491 void SMESHGUI_MultiEditDlg::closeEvent( QCloseEvent* e )
492 {
493   onClose() ;
494 }
495 //=======================================================================
496 //function : hideEvent
497 //purpose  : caused by ESC key
498 //=======================================================================
499
500 void SMESHGUI_MultiEditDlg::hideEvent ( QHideEvent * e )
501 {
502   if ( !isMinimized() )
503     onClose();
504 }
505
506 //=======================================================================
507 // name    : SMESHGUI_MultiEditDlg::onFilterBtn
508 // Purpose : SLOT. Called when "Filter" button pressed.
509 //           Start "Selection filters" dialog
510 //=======================================================================
511 void SMESHGUI_MultiEditDlg::onFilterBtn()
512 {
513   if ( myFilterDlg == 0 )
514   {
515     myFilterDlg = new SMESHGUI_FilterDlg( (QWidget*)parent(), entityType() ? SMESH::VOLUME : SMESH::FACE );
516     connect( myFilterDlg, SIGNAL( Accepted() ), SLOT( onFilterAccepted() ) );
517   }
518   else
519     myFilterDlg->Init( entityType() ? SMESH::VOLUME : SMESH::FACE );
520
521   myFilterDlg->SetSelection( mySelection );
522   myFilterDlg->SetMesh( myMesh );
523   myFilterDlg->SetSourceWg( myListBox );
524
525   myFilterDlg->show();
526 }
527
528 //=================================================================================
529 // function : onFilterAccepted()
530 // purpose  : SLOT. Called when Filter dlg closed with OK button.
531 //            Uncheck "Select submesh" and "Select group" checkboxes
532 //=================================================================================
533 void SMESHGUI_MultiEditDlg::onFilterAccepted()
534 {
535   myIds.Clear();
536   for ( int i = 0, n = myListBox->count(); i < n; i++ )
537     myIds.Add( myListBox->text( i ).toInt() );
538
539   emit ListContensChanged();
540
541   if ( mySubmeshChk->isChecked() || myGroupChk->isChecked() )
542   {
543     mySubmeshChk->blockSignals( true );
544     myGroupChk->blockSignals( true );
545     mySubmeshChk->setChecked( false );
546     myGroupChk->setChecked( false );
547     mySubmeshChk->blockSignals( false );
548     myGroupChk->blockSignals( false );
549   }
550   updateButtons();
551 }
552
553 //=======================================================================
554 // name    : SMESHGUI_MultiEditDlg::isIdValid
555 // Purpose : Verify whether Id of element satisfies to filters from viewer
556 //=======================================================================
557 bool SMESHGUI_MultiEditDlg::isIdValid( const int theId ) const
558 {
559   VTKViewer_InteractorStyleSALOME* aStyle = SMESH::GetInteractorStyle();
560   Handle(SMESHGUI_Filter) aFilter =
561     Handle(SMESHGUI_Filter)::DownCast( aStyle->GetFilter( myFilterType ) );
562
563   return ( !aFilter.IsNull() && aFilter->IsObjValid( theId ) );
564 }
565
566 //=======================================================================
567 // name    : SMESHGUI_MultiEditDlg::onAddBtn
568 // Purpose : SLOT. Called when "Add" button pressed.
569 //           Add selected in viewer entities in list box
570 //=======================================================================
571 void SMESHGUI_MultiEditDlg::onAddBtn()
572 {
573   int nbSelected = mySelection->IObjectCount();
574   if ( nbSelected == 0 ) 
575     return;
576
577   TColStd_IndexedMapOfInteger toBeAdded;
578   
579   if ( !mySubmeshChk->isChecked() && !myGroupChk->isChecked() ) 
580   {
581     if ( nbSelected == 1 ) 
582       SMESH::GetSelected( mySelection, toBeAdded );
583   }
584   else if ( mySubmeshChk->isChecked() ) 
585   {
586     SALOME_ListIteratorOfListIO anIter( mySelection->StoredIObjects() );
587     for ( ; anIter.More(); anIter.Next() )
588     {
589       SMESH::SMESH_subMesh_var aSubMesh = SMESH::IObjectToInterface<SMESH::SMESH_subMesh>( anIter.Value() );
590       if ( !aSubMesh->_is_nil() )
591       {
592         if ( aSubMesh->GetFather()->GetId() == myMesh->GetId() )
593         {
594           SMESH::long_array_var anIds = aSubMesh->GetElementsId();
595           for ( int i = 0, n = anIds->length(); i < n; i++ )
596           {
597             if ( isIdValid( anIds[ i ] ) )
598               toBeAdded.Add( anIds[ i ] );
599           }
600         }
601       }
602     }
603   }
604   else if ( myGroupChk->isChecked() ) 
605   {
606     SALOME_ListIteratorOfListIO anIter( mySelection->StoredIObjects() );
607     for ( ; anIter.More(); anIter.Next() )
608     {
609       SMESH::SMESH_GroupBase_var aGroup =
610         SMESH::IObjectToInterface<SMESH::SMESH_GroupBase>( anIter.Value() );
611       if ( !aGroup->_is_nil() && ( aGroup->GetType() == SMESH::FACE && entityType() == 0 || aGroup->GetType() == SMESH::VOLUME && entityType() == 1 ) )
612       {
613         if ( aGroup->GetMesh()->GetId() == myMesh->GetId() )
614         {
615           SMESH::long_array_var anIds = aGroup->GetListOfID();
616           for ( int i = 0, n = anIds->length(); i < n; i++ )
617           {
618             if ( isIdValid( anIds[ i ] ) )
619               toBeAdded.Add( anIds[ i ] );
620           }
621         }
622       }
623     }
624   }
625
626   myBusy = true;
627   bool isGroupOrSubmesh = ( mySubmeshChk->isChecked() || myGroupChk->isChecked() );
628   mySubmeshChk->setChecked( false );
629   myGroupChk->setChecked( false );
630   for( int i = 1; i <= toBeAdded.Extent(); i++ )
631     if ( myIds.Add( toBeAdded(i) ) ) {
632       QListBoxItem * item = new QListBoxText( QString( "%1" ).arg( toBeAdded(i) ));
633       myListBox->insertItem( item );
634       myListBox->setSelected( item, true );
635     }
636   myBusy = false;
637
638   emit ListContensChanged(); 
639
640   if ( isGroupOrSubmesh )
641     onListSelectionChanged();
642
643   updateButtons();
644 }
645
646 //=======================================================================
647 // name    : SMESHGUI_MultiEditDlg::updateButtons
648 // Purpose : Enable/disable buttons of dialog in accordance with current state
649 //=======================================================================
650 void SMESHGUI_MultiEditDlg::updateButtons()
651 {
652   bool isOk = isValid(false);
653   myOkBtn->setEnabled( isOk );
654   myApplyBtn->setEnabled( isOk );
655
656   bool isListBoxNonEmpty = myListBox->count() > 0;
657   bool isToAll = myToAllChk->isChecked();
658   myFilterBtn->setEnabled( !isToAll );
659   myRemoveBtn->setEnabled( isListBoxNonEmpty && !isToAll );
660   mySortBtn->setEnabled( isListBoxNonEmpty &&!isToAll );
661   
662   if ( isToAll ||
663        myMesh->_is_nil() ||
664        mySelection->IObjectCount() != 1 ||
665        (SMESH::IObjectToInterface<SMESH::SMESH_subMesh>( mySelection->firstIObject() )->_is_nil() &&
666         SMESH::IObjectToInterface<SMESH::SMESH_GroupBase>( mySelection->firstIObject() )->_is_nil() &&
667         SMESH::IObjectToInterface<SMESH::SMESH_Mesh>( mySelection->firstIObject() )->_is_nil()) )
668     myAddBtn->setEnabled( false );
669   else
670     myAddBtn->setEnabled( true );
671   
672   mySubmeshChk->setEnabled( !isToAll );
673   mySubmeshBtn->setEnabled( mySubmeshChk->isChecked() );
674   mySubmesh->setEnabled( mySubmeshChk->isChecked() );
675   
676   myGroupChk->setEnabled( !isToAll );
677   myGroupBtn->setEnabled( myGroupChk->isChecked() );
678   myGroup->setEnabled( myGroupChk->isChecked() );
679   
680   if ( !mySubmeshChk->isChecked() )
681     mySubmesh->clear();
682   if ( !myGroupChk->isChecked() )
683     myGroup->clear();
684     
685 }
686
687 //=======================================================================
688 // name    : SMESHGUI_MultiEditDlg::onRemoveBtn
689 // Purpose : SLOT. Called when "Remove" button pressed.
690 //           Remove selected in list box entities
691 //=======================================================================
692 void SMESHGUI_MultiEditDlg::onRemoveBtn()
693 {
694   myBusy = true;
695   
696   for ( int i = 0, n = myListBox->count(); i < n; i++ )
697   {
698     for ( int i = myListBox->count(); i > 0; i--) {
699       if ( myListBox->isSelected( i - 1 ) ) 
700       {
701         int anId = myListBox->text( i - 1 ).toInt();
702         myIds.Remove( anId );
703         myIds.Remove( anId );
704               myListBox->removeItem( i-1 );
705       }
706     }        
707   }
708   myBusy = false;
709
710   emit ListContensChanged();  
711   updateButtons();
712 }
713
714 //=======================================================================
715 // name    : SMESHGUI_MultiEditDlg::onSortListBtn
716 // Purpose : SLOT. Called when "Sort list" button pressed.
717 //           Sort entities of list box
718 //=======================================================================
719 void SMESHGUI_MultiEditDlg::onSortListBtn()
720 {
721   myBusy = true;
722
723   int i, k = myListBox->count();
724   if ( k > 0 ) 
725   {
726     QStringList aSelected;
727     std::vector<int> anArray( k );
728     QListBoxItem* anItem;
729     for ( anItem = myListBox->firstItem(), i = 0; anItem != 0; anItem = anItem->next(), i++) 
730     {
731       anArray[ i ] = anItem->text().toInt();
732       if ( anItem->isSelected() ) 
733         aSelected.append( anItem->text() );
734     }
735     
736     std::sort( anArray.begin(), anArray.end() );
737     myListBox->clear();
738     for ( i = 0; i < k; i++ ) 
739       myListBox->insertItem( QString::number( anArray[ i ] ) );
740
741     for ( QStringList::iterator it = aSelected.begin(); it != aSelected.end(); ++it ) 
742     {
743       anItem = myListBox->findItem( *it, Qt::ExactMatch );
744       if ( anItem ) 
745         myListBox->setSelected( anItem, true );
746     }
747   }
748   myBusy = false;
749 }
750
751 //=======================================================================
752 // name    : SMESHGUI_MultiEditDlg::onListSelectionChanged
753 // Purpose : SLOT. Called when selection in list box changed.
754 //           Highlight in selected entities
755 //=======================================================================
756 void SMESHGUI_MultiEditDlg::onListSelectionChanged()
757 {
758   if ( myActor == 0 || myBusy )
759     return;
760   
761   if ( mySubmeshChk->isChecked() || myGroupChk->isChecked() ) 
762     return;
763
764   SMESH_Actor * anActor = SMESH::FindActorByObject( myMesh );
765   if ( !anActor )
766     anActor = myActor;
767   TVisualObjPtr anObj = anActor->GetObject();
768
769   TColStd_MapOfInteger anIndexes;
770   for ( QListBoxItem* anItem = myListBox->firstItem(); anItem != 0; anItem = anItem->next() ) 
771   {
772     if ( anItem->isSelected() ) 
773     {
774       int anId = anItem->text().toInt();
775       if ( anObj->GetElemVTKId( anId ) >= 0 ) // avoid exception in hilight
776         anIndexes.Add(anId);
777     }
778   }
779   
780   mySelection->ClearIObjects();
781   mySelection->AddOrRemoveIndex( anActor->getIO(), anIndexes, false, false );
782   mySelection->AddIObject( anActor->getIO() );
783 }
784
785 //=======================================================================
786 // name    : SMESHGUI_MultiEditDlg::onSubmeshChk
787 // Purpose : SLOT. Called when state of "SubMesh" check box changed.
788 //           Activate/deactivate selection of submeshes
789 //=======================================================================
790 void SMESHGUI_MultiEditDlg::onSubmeshChk()
791 {
792   bool isChecked = mySubmeshChk->isChecked();
793   mySubmeshBtn->setEnabled( isChecked );
794   mySubmesh->setEnabled( isChecked );
795   if ( !isChecked )
796     mySubmesh->clear();
797   if ( isChecked && myGroupChk->isChecked() )
798       myGroupChk->setChecked( false );
799       
800   setSelectionMode();      
801 }
802
803 //=======================================================================
804 // name    : SMESHGUI_MultiEditDlg::onGroupChk
805 // Purpose : SLOT. Called when state of "Group" check box changed.
806 //           Activate/deactivate selection of groupes
807 //=======================================================================
808 void SMESHGUI_MultiEditDlg::onGroupChk()
809 {
810   bool isChecked = myGroupChk->isChecked();
811   myGroupBtn->setEnabled( isChecked );
812   myGroup->setEnabled( isChecked );
813   if ( !isChecked )
814     myGroup->clear();
815   if ( isChecked && mySubmeshChk->isChecked() )
816       mySubmeshChk->setChecked( false );
817
818   setSelectionMode();
819 }
820
821 //=======================================================================
822 // name    : SMESHGUI_MultiEditDlg::onToAllChk
823 // Purpose : SLOT. Called when state of "Apply to all" check box changed.
824 //           Activate/deactivate selection 
825 //=======================================================================
826 void SMESHGUI_MultiEditDlg::onToAllChk()
827 {
828   bool isChecked = myToAllChk->isChecked();
829
830   if ( isChecked )
831     myListBox->clear();
832
833   myIds.Clear();
834
835   emit ListContensChanged();
836     
837   updateButtons();
838   setSelectionMode();
839
840   if ( myActor )
841     mySelection->AddIObject( myActor->getIO(), true );
842 }
843
844
845 //=======================================================================
846 // name    : SMESHGUI_MultiEditDlg::setSelectionMode
847 // Purpose : Set selection mode
848 //=======================================================================
849 void SMESHGUI_MultiEditDlg::setSelectionMode()
850 {
851   SMESH::RemoveFilters();
852     
853   mySelection->ClearIObjects();
854   mySelection->ClearFilters();
855   
856   if ( mySubmeshChk->isChecked() ) {
857     QAD_Application::getDesktop()->SetSelectionMode( ActorSelection, true );
858     mySelection->AddFilter( mySubmeshFilter );
859   }
860   else if ( myGroupChk->isChecked() ) {
861     QAD_Application::getDesktop()->SetSelectionMode( ActorSelection, true );
862     mySelection->AddFilter( myGroupFilter );
863   }
864   if ( entityType() ) {
865     QAD_Application::getDesktop()->SetSelectionMode( VolumeSelection, true );
866     SMESH::SetFilter( new SMESHGUI_VolumesFilter() );
867   }
868   else {
869     QAD_Application::getDesktop()->SetSelectionMode( FaceSelection, true );
870     if ( myFilterType == SMESHGUI_TriaFilter )
871       SMESH::SetFilter( new SMESHGUI_TriangleFilter() );
872     else if ( myFilterType == SMESHGUI_QuadFilter )
873       SMESH::SetFilter( new SMESHGUI_QuadrangleFilter() );
874     else
875       SMESH::SetFilter( new SMESHGUI_FacesFilter() );
876   }
877 }
878
879 //=======================================================================
880 // name    : SMESHGUI_MultiEditDlg::onApply
881 // Purpose : SLOT. Called when "Apply" button clicked. 
882 //=======================================================================
883 bool SMESHGUI_MultiEditDlg::onApply()
884 {
885   if ( SMESHGUI::GetSMESHGUI()->ActiveStudyLocked() )
886     return false;
887   if ( !isValid( true ) )
888     return false;
889
890   SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditor();
891   if ( aMeshEditor->_is_nil()  )
892     return false;
893
894   myBusy = true;
895
896   SMESH::long_array_var anIds = getIds();
897
898   bool aResult = process( aMeshEditor, anIds.inout() );
899   if ( aResult )
900   {
901     if ( myActor )
902     {
903       mySelection->ClearIObjects();
904       SMESH::UpdateView();
905       mySelection->AddIObject( myActor->getIO(), false );
906     }
907
908     myListBox->clear();
909     myIds.Clear();
910     emit ListContensChanged();
911
912     updateButtons();
913   }
914
915   myBusy = false;
916   return aResult;
917 }
918
919 //=======================================================================
920 // name    : SMESHGUI_MultiEditDlg::on3d2dChanged
921 // Purpose : 
922 //=======================================================================
923 void SMESHGUI_MultiEditDlg::on3d2dChanged(int type)
924 {
925   if ( myEntityType != type ) {
926     myEntityType = type;
927     
928     myListBox->clear();
929     myIds.Clear();
930
931     emit ListContensChanged();
932     
933     if ( type )
934       myFilterType = SMESHGUI_VolumeFilter;
935     else 
936       myFilterType = SMESHGUI_FaceFilter;
937
938     updateButtons();
939     setSelectionMode();
940   }
941 }
942
943 //=======================================================================
944 // name    : SMESHGUI_MultiEditDlg::entityType
945 // Purpose : 
946 //=======================================================================
947
948 int SMESHGUI_MultiEditDlg::entityType() 
949 {
950   return myEntityType;
951 }
952
953 /*
954   Class       : SMESHGUI_ChangeOrientationDlg
955   Description : Modification of orientation of faces
956 */
957
958 SMESHGUI_ChangeOrientationDlg::SMESHGUI_ChangeOrientationDlg( QWidget*          theParent, 
959                                                               SALOME_Selection* theSelection,
960                                                               const char*       theName )
961 : SMESHGUI_MultiEditDlg( theParent, theSelection, SMESHGUI_FaceFilter, true, theName )
962 {
963   setCaption( tr( "CAPTION" ) );
964 }
965
966 SMESHGUI_ChangeOrientationDlg::~SMESHGUI_ChangeOrientationDlg()
967 {
968 }
969
970 bool SMESHGUI_ChangeOrientationDlg::process( SMESH::SMESH_MeshEditor_ptr theEditor,
971                                              const SMESH::long_array&    theIds )
972 {
973   return theEditor->Reorient( theIds );
974 }
975
976 /*
977   Class       : SMESHGUI_UnionOfTrianglesDlg
978   Description : Construction of quadrangles by automatic association of triangles
979 */
980
981 SMESHGUI_UnionOfTrianglesDlg::SMESHGUI_UnionOfTrianglesDlg( QWidget*          theParent,
982                                                             SALOME_Selection* theSelection,
983                                                             const char*       theName )
984 : SMESHGUI_MultiEditDlg( theParent, theSelection, SMESHGUI_TriaFilter, false, theName )
985 {
986   setCaption( tr( "CAPTION" ) );
987 }
988
989 SMESHGUI_UnionOfTrianglesDlg::~SMESHGUI_UnionOfTrianglesDlg()
990 {
991 }
992
993 bool SMESHGUI_UnionOfTrianglesDlg::process( SMESH::SMESH_MeshEditor_ptr theEditor,
994                                             const SMESH::long_array&    theIds )
995 {
996   return theEditor->TriToQuad(theIds, SMESH::NumericalFunctor::_nil(), 1. );
997 }
998
999 /*
1000   Class       : SMESHGUI_CuttingOfQuadsDlg
1001   Description : Construction of quadrangles by automatic association of triangles
1002 */
1003
1004 SMESHGUI_CuttingOfQuadsDlg::SMESHGUI_CuttingOfQuadsDlg( QWidget*          theParent,
1005                                                         SALOME_Selection* theSelection,
1006                                                         const char*       theName )
1007 : SMESHGUI_MultiEditDlg( theParent, theSelection, SMESHGUI_QuadFilter, false, theName )
1008 {
1009
1010   setCaption( tr( "CAPTION" ) );
1011   myPreviewActor = 0;
1012
1013   myUseDiagChk = new QCheckBox( tr( "USE_DIAGONAL_2_4" ), mySelGrp );
1014   myPreviewChk = new QCheckBox( tr( "PREVIEW" ), mySelGrp );
1015
1016   connect( myPreviewChk, SIGNAL( stateChanged( int ) ), this, SLOT( onPreviewChk() ) );
1017   connect( myUseDiagChk, SIGNAL( stateChanged( int ) ), this, SLOT( onPreviewChk() ) );
1018   connect( this, SIGNAL( ListContensChanged() ), this, SLOT( onPreviewChk() ) );
1019 }
1020
1021 SMESHGUI_CuttingOfQuadsDlg::~SMESHGUI_CuttingOfQuadsDlg()
1022 {
1023 }
1024
1025 void SMESHGUI_CuttingOfQuadsDlg::onClose()
1026 {
1027   erasePreview();
1028   SMESHGUI_MultiEditDlg::onClose();
1029 }
1030
1031 bool SMESHGUI_CuttingOfQuadsDlg::process( SMESH::SMESH_MeshEditor_ptr theEditor,
1032                                           const SMESH::long_array&    theIds )
1033 {
1034   return theEditor->SplitQuad( theIds, !myUseDiagChk->isChecked() );
1035 }
1036
1037 void SMESHGUI_CuttingOfQuadsDlg::onPreviewChk()
1038 {
1039   myPreviewChk->isChecked() ? displayPreview() : erasePreview();
1040 }
1041
1042 void SMESHGUI_CuttingOfQuadsDlg::erasePreview()
1043 {
1044   if ( myPreviewActor == 0 )
1045     return;
1046     
1047   if ( VTKViewer_ViewFrame* vf = SMESH::GetCurrentVtkView() )
1048   {
1049     vf->RemoveActor(myPreviewActor);
1050     vf->Repaint();
1051   }
1052   myPreviewActor->Delete();
1053   myPreviewActor = 0;
1054 }
1055
1056 void SMESHGUI_CuttingOfQuadsDlg::displayPreview()
1057 {
1058   if ( myActor == 0 )
1059     return;
1060
1061   if ( myPreviewActor != 0 )
1062     erasePreview();
1063
1064   // get Ids of elements
1065   SMESH::long_array_var anElemIds = getIds();
1066   if ( getIds()->length() == 0 )
1067     return;
1068
1069   SMDS_Mesh* aMesh = myActor->GetObject()->GetMesh();
1070   if ( aMesh == 0 )
1071     return;
1072
1073   bool isDiag24 = myUseDiagChk->isChecked();
1074
1075   //Create grid
1076   vtkUnstructuredGrid* aGrid = vtkUnstructuredGrid::New();
1077     
1078   vtkIdType aNbCells = anElemIds->length() * 2;
1079   vtkIdType aCellsSize = 4 * aNbCells;
1080   vtkCellArray* aConnectivity = vtkCellArray::New();
1081   aConnectivity->Allocate( aCellsSize, 0 );
1082
1083   vtkPoints* aPoints = vtkPoints::New();
1084   aPoints->SetNumberOfPoints( anElemIds->length() * 4 );
1085   
1086   vtkUnsignedCharArray* aCellTypesArray = vtkUnsignedCharArray::New();
1087   aCellTypesArray->SetNumberOfComponents( 1 );
1088   aCellTypesArray->Allocate( aNbCells * aCellTypesArray->GetNumberOfComponents() );
1089
1090   vtkIdList *anIdList = vtkIdList::New();
1091   anIdList->SetNumberOfIds( 3 );
1092
1093   TColStd_DataMapOfIntegerInteger anIdToVtk;
1094
1095   int aNodes[ 4 ];
1096   int nbPoints = -1;
1097   for ( int i = 0, n = anElemIds->length(); i < n; i++ )
1098   {
1099     const SMDS_MeshElement* anElem = aMesh->FindElement( anElemIds[ i ] );
1100     if ( anElem == 0 || anElem->NbNodes() != 4 )
1101       continue;
1102       
1103     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
1104     int k = 0;
1105     while( anIter->more() )
1106       if ( const SMDS_MeshNode* aNode = (SMDS_MeshNode*)anIter->next() )
1107       {
1108         if ( !anIdToVtk.IsBound( aNode->GetID() ) )
1109         {
1110           aPoints->SetPoint( ++nbPoints, aNode->X(), aNode->Y(), aNode->Z() );
1111           anIdToVtk.Bind( aNode->GetID(), nbPoints );
1112         }
1113         
1114         aNodes[ k++ ] = aNode->GetID();
1115       }
1116
1117     if ( k != 4 )
1118       continue;
1119
1120     if ( !isDiag24 )
1121     {
1122       anIdList->SetId( 0, anIdToVtk( aNodes[ 0 ] ) );
1123       anIdList->SetId( 1, anIdToVtk( aNodes[ 1 ] ) );
1124       anIdList->SetId( 2, anIdToVtk( aNodes[ 2 ] ) );
1125       aConnectivity->InsertNextCell( anIdList );
1126       aCellTypesArray->InsertNextValue( VTK_TRIANGLE );
1127
1128       anIdList->SetId( 0, anIdToVtk( aNodes[ 2 ] ) );
1129       anIdList->SetId( 1, anIdToVtk( aNodes[ 3 ] ) );
1130       anIdList->SetId( 2, anIdToVtk( aNodes[ 0 ] ) );
1131       aConnectivity->InsertNextCell( anIdList );
1132       aCellTypesArray->InsertNextValue( VTK_TRIANGLE );
1133     }
1134     else
1135     {
1136       anIdList->SetId( 0, anIdToVtk( aNodes[ 1 ] ) );
1137       anIdList->SetId( 1, anIdToVtk( aNodes[ 2 ] ) );
1138       anIdList->SetId( 2, anIdToVtk( aNodes[ 3 ] ) );
1139       aConnectivity->InsertNextCell( anIdList );
1140       aCellTypesArray->InsertNextValue( VTK_TRIANGLE );
1141
1142       anIdList->SetId( 0, anIdToVtk( aNodes[ 3 ] ) );
1143       anIdList->SetId( 1, anIdToVtk( aNodes[ 0 ] ) );
1144       anIdList->SetId( 2, anIdToVtk( aNodes[ 1 ] ) );
1145       aConnectivity->InsertNextCell( anIdList );
1146       aCellTypesArray->InsertNextValue( VTK_TRIANGLE );
1147     }
1148   }
1149
1150   vtkIntArray* aCellLocationsArray = vtkIntArray::New();
1151   aCellLocationsArray->SetNumberOfComponents( 1 );
1152   aCellLocationsArray->SetNumberOfTuples( aNbCells );
1153
1154   aConnectivity->InitTraversal();
1155   for( vtkIdType idType = 0, *pts, npts; aConnectivity->GetNextCell( npts, pts ); idType++ )
1156     aCellLocationsArray->SetValue( idType, aConnectivity->GetTraversalLocation( npts ) );
1157
1158   aGrid->SetPoints( aPoints );
1159   aGrid->SetCells( aCellTypesArray, aCellLocationsArray,aConnectivity );
1160
1161   // Create and display actor
1162   vtkDataSetMapper* aMapper = vtkDataSetMapper::New();
1163   aMapper->SetInput( aGrid );
1164   
1165   myPreviewActor = SALOME_Actor::New();
1166   myPreviewActor->PickableOff();
1167   myPreviewActor->SetMapper( aMapper );
1168
1169   vtkProperty* aProp = vtkProperty::New();
1170   aProp->SetRepresentationToWireframe();
1171   aProp->SetColor( 250, 0, 250 );
1172   aProp->SetLineWidth( myActor->GetLineWidth() + 1 );
1173   myPreviewActor->SetProperty( aProp );
1174
1175   SMESH::GetCurrentVtkView()->AddActor( myPreviewActor );
1176   SMESH::GetCurrentVtkView()->Repaint();
1177
1178   aProp->Delete();
1179   aPoints->Delete();
1180   aConnectivity->Delete();
1181   aGrid->Delete();
1182   aMapper->Delete();
1183   anIdList->Delete();
1184   aCellTypesArray->Delete();
1185   aCellLocationsArray->Delete();
1186 }
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209