Salome HOME
92642d384dfc542ddc83f431682057c81b84a0bd
[modules/smesh.git] / src / SMESHGUI / SMESHGUI_MergeDlg.cxx
1 // Copyright (C) 2007-2015  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, 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 // SMESH SMESHGUI : GUI for SMESH component
24 // File   : SMESHGUI_MergeDlg.cxx
25 // Author : Open CASCADE S.A.S.
26 // SMESH includes
27 //
28 #include "SMESHGUI_MergeDlg.h"
29
30 #include "SMESHGUI.h"
31 #include "SMESHGUI_Utils.h"
32 #include "SMESHGUI_VTKUtils.h"
33 #include "SMESHGUI_MeshUtils.h"
34 #include "SMESHGUI_SpinBox.h"
35
36 #include <SMESH_Actor.h>
37 #include <SMESH_TypeFilter.hxx>
38 #include <SMESH_LogicalFilter.hxx>
39 #include <SMDS_Mesh.hxx>
40
41 // SALOME GUI includes
42 #include <SUIT_Desktop.h>
43 #include <SUIT_ResourceMgr.h>
44 #include <SUIT_Session.h>
45 #include <SUIT_MessageBox.h>
46 #include <SUIT_OverrideCursor.h>
47
48 #include <LightApp_Application.h>
49 #include <LightApp_SelectionMgr.h>
50
51 #include <SVTK_ViewModel.h>
52 #include <SVTK_ViewWindow.h>
53 #include <SALOME_ListIO.hxx>
54
55 // OCCT includes
56 #include <TColStd_MapOfInteger.hxx>
57 #include <TColStd_MapIteratorOfMapOfInteger.hxx>
58
59 // IDL includes
60 #include <SALOMEconfig.h>
61 #include CORBA_SERVER_HEADER(SMESH_Group)
62 #include CORBA_SERVER_HEADER(SMESH_MeshEditor)
63
64 // VTK includes
65 #include <vtkUnstructuredGrid.h>
66 #include <vtkRenderer.h>
67 #include <vtkActor2D.h>
68 #include <vtkPoints.h>
69 #include <vtkDataSetMapper.h>
70 #include <vtkMaskPoints.h>
71 #include <vtkSelectVisiblePoints.h>
72 #include <vtkLabeledDataMapper.h>
73 #include <vtkTextProperty.h>
74 #include <vtkIntArray.h>
75 #include <vtkProperty2D.h>
76 #include <vtkPointData.h>
77
78 // Qt includes
79 #include <QApplication>
80 #include <QGroupBox>
81 #include <QLabel>
82 #include <QLineEdit>
83 #include <QListWidget>
84 #include <QPushButton>
85 #include <QRadioButton>
86 #include <QCheckBox>
87 #include <QHBoxLayout>
88 #include <QVBoxLayout>
89 #include <QGridLayout>
90 #include <QKeyEvent>
91 #include <QButtonGroup>
92
93 #define SPACING 6
94 #define MARGIN  11
95
96 namespace
97 {
98   enum ActionType { MERGE_NODES, MERGE_ELEMENTS, TYPE_AUTO=0, TYPE_MANUAL };
99 }
100 namespace SMESH
101 {
102   class TIdPreview
103   { // to display in the viewer IDs of the selected elements
104     SVTK_ViewWindow* myViewWindow;
105
106     vtkUnstructuredGrid* myIdGrid;
107     SALOME_Actor* myIdActor;
108
109     vtkUnstructuredGrid* myPointsNumDataSet;
110     vtkMaskPoints* myPtsMaskPoints;
111     vtkSelectVisiblePoints* myPtsSelectVisiblePoints;
112     vtkLabeledDataMapper* myPtsLabeledDataMapper;
113     vtkTextProperty* aPtsTextProp;
114     bool myIsPointsLabeled;
115     vtkActor2D* myPointLabels;
116
117     std::vector<int> myIDs;
118
119   public:
120     TIdPreview(SVTK_ViewWindow* theViewWindow):
121       myViewWindow(theViewWindow)
122     {
123       myIdGrid = vtkUnstructuredGrid::New();
124
125       // Create and display actor
126       vtkDataSetMapper* aMapper = vtkDataSetMapper::New();
127       aMapper->SetInputData( myIdGrid );
128
129       myIdActor = SALOME_Actor::New();
130       myIdActor->SetInfinitive(true);
131       myIdActor->VisibilityOff();
132       myIdActor->PickableOff();
133
134       myIdActor->SetMapper( aMapper );
135       aMapper->Delete();
136
137       myViewWindow->AddActor(myIdActor);
138
139       //Definition of points numbering pipeline
140       myPointsNumDataSet = vtkUnstructuredGrid::New();
141
142       myPtsMaskPoints = vtkMaskPoints::New();
143       myPtsMaskPoints->SetInputData(myPointsNumDataSet);
144       myPtsMaskPoints->SetOnRatio(1);
145
146       myPtsSelectVisiblePoints = vtkSelectVisiblePoints::New();
147       myPtsSelectVisiblePoints->SetInputConnection(myPtsMaskPoints->GetOutputPort());
148       myPtsSelectVisiblePoints->SelectInvisibleOff();
149       myPtsSelectVisiblePoints->SetTolerance(0.1);
150     
151       myPtsLabeledDataMapper = vtkLabeledDataMapper::New();
152       myPtsLabeledDataMapper->SetInputConnection(myPtsSelectVisiblePoints->GetOutputPort());
153       myPtsLabeledDataMapper->SetLabelModeToLabelScalars();
154     
155       vtkTextProperty* aPtsTextProp = vtkTextProperty::New();
156       aPtsTextProp->SetFontFamilyToTimes();
157       static int aPointsFontSize = 12;
158       aPtsTextProp->SetFontSize(aPointsFontSize);
159       aPtsTextProp->SetBold(1);
160       aPtsTextProp->SetItalic(0);
161       aPtsTextProp->SetShadow(0);
162       myPtsLabeledDataMapper->SetLabelTextProperty(aPtsTextProp);
163       aPtsTextProp->Delete();
164   
165       myIsPointsLabeled = false;
166
167       myPointLabels = vtkActor2D::New();
168       myPointLabels->SetMapper(myPtsLabeledDataMapper);
169       myPointLabels->GetProperty()->SetColor(1,1,1);
170       myPointLabels->SetVisibility(myIsPointsLabeled);
171
172       AddToRender(myViewWindow->getRenderer());
173     }
174
175     void SetPointsData ( SMDS_Mesh* theMesh, 
176                          TColStd_MapOfInteger & theNodesIdMap )
177     {
178       vtkPoints* aPoints = vtkPoints::New();
179       aPoints->SetNumberOfPoints(theNodesIdMap.Extent());
180       myIDs.clear();
181       
182       TColStd_MapIteratorOfMapOfInteger idIter( theNodesIdMap );
183       for( int i = 0; idIter.More(); idIter.Next(), i++ ) {
184         const SMDS_MeshNode* aNode = theMesh->FindNode(idIter.Key());
185         aPoints->SetPoint( i, aNode->X(), aNode->Y(), aNode->Z() );
186         myIDs.push_back(idIter.Key());
187       }
188
189       myIdGrid->SetPoints(aPoints);
190
191       aPoints->Delete();
192
193       myIdActor->GetMapper()->Update();
194     }
195
196     void SetElemsData( TColStd_MapOfInteger & theElemsIdMap, 
197                        std::list<gp_XYZ> & aGrCentersXYZ )
198     {
199       vtkPoints* aPoints = vtkPoints::New();
200       aPoints->SetNumberOfPoints(theElemsIdMap.Extent());
201       myIDs.clear();
202       
203       TColStd_MapIteratorOfMapOfInteger idIter( theElemsIdMap );
204       for( ; idIter.More(); idIter.Next() ) {
205         myIDs.push_back(idIter.Key());
206       }
207
208       gp_XYZ aXYZ;
209       std::list<gp_XYZ>::iterator coordIt = aGrCentersXYZ.begin();
210       for( int i = 0; coordIt != aGrCentersXYZ.end(); coordIt++, i++ ) {
211         aXYZ = *coordIt;
212         aPoints->SetPoint( i, aXYZ.X(), aXYZ.Y(), aXYZ.Z() );
213       }
214       myIdGrid->SetPoints(aPoints);
215       aPoints->Delete();
216       
217       myIdActor->GetMapper()->Update();
218     }
219
220     void AddToRender(vtkRenderer* theRenderer)
221     {
222       myIdActor->AddToRender(theRenderer);
223
224       myPtsSelectVisiblePoints->SetRenderer(theRenderer);
225       theRenderer->AddActor2D(myPointLabels);
226     }
227
228     void RemoveFromRender(vtkRenderer* theRenderer)
229     {
230       myIdActor->RemoveFromRender(theRenderer);
231
232       myPtsSelectVisiblePoints->SetRenderer(theRenderer);
233       theRenderer->RemoveActor(myPointLabels);
234     }
235
236     void SetPointsLabeled( bool theIsPointsLabeled, bool theIsActorVisible = true )
237     {
238       myIsPointsLabeled = theIsPointsLabeled && myIdGrid->GetNumberOfPoints();
239       
240       if ( myIsPointsLabeled ) {
241         myPointsNumDataSet->ShallowCopy(myIdGrid);
242         vtkDataSet *aDataSet = myPointsNumDataSet;
243         int aNbElem = myIDs.size();
244         vtkIntArray *anArray = vtkIntArray::New();
245         anArray->SetNumberOfValues( aNbElem );
246         for ( int i = 0; i < aNbElem; i++ )
247           anArray->SetValue( i, myIDs[i] );
248         aDataSet->GetPointData()->SetScalars( anArray );
249         anArray->Delete();
250         myPtsMaskPoints->SetInputData( aDataSet );
251         myPointLabels->SetVisibility( theIsActorVisible );
252       }
253       else {
254         myPointLabels->SetVisibility( false );
255       }
256     }
257     
258     ~TIdPreview()
259     {
260       RemoveFromRender(myViewWindow->getRenderer());
261
262       myIdGrid->Delete();
263
264       myViewWindow->RemoveActor(myIdActor);
265       myIdActor->Delete();
266
267       //Deleting of points numbering pipeline
268       //---------------------------------------
269       myPointsNumDataSet->Delete();
270       
271       //myPtsLabeledDataMapper->RemoveAllInputs();        //vtk 5.0 porting
272       myPtsLabeledDataMapper->Delete();
273
274       //myPtsSelectVisiblePoints->UnRegisterAllOutputs(); //vtk 5.0 porting
275       myPtsSelectVisiblePoints->Delete();
276
277       //myPtsMaskPoints->UnRegisterAllOutputs();          //vtk 5.0 porting
278       myPtsMaskPoints->Delete();
279
280       myPointLabels->Delete();
281
282 //       myTimeStamp->Delete();
283     }
284   };
285 }
286
287 static const char * IconFirst[] = {
288 "18 10 2 1",
289 "       g None",
290 ".      g #000000",
291 "         .     .  ",
292 "  ..    ..    ..  ",
293 "  ..   ...   ...  ",
294 "  ..  ....  ....  ",
295 "  .. ..... .....  ",
296 "  .. ..... .....  ",
297 "  ..  ....  ....  ",
298 "  ..   ...   ...  ",
299 "  ..    ..    ..  ",
300 "         .     .  "};
301
302 //=================================================================================
303 // class    : SMESHGUI_MergeDlg()
304 // purpose  :
305 //=================================================================================
306 SMESHGUI_MergeDlg::SMESHGUI_MergeDlg (SMESHGUI* theModule, int theAction)
307   : QDialog(SMESH::GetDesktop(theModule)),
308     mySMESHGUI(theModule),
309     mySelectionMgr(SMESH::GetSelectionMgr(theModule)),
310     myAction(theAction)
311 {
312   setModal(false);
313   setAttribute(Qt::WA_DeleteOnClose, true);
314   setWindowTitle(myAction == MERGE_ELEMENTS ? tr("SMESH_MERGE_ELEMENTS") : tr("SMESH_MERGE_NODES"));
315
316   myIdPreview = new SMESH::TIdPreview(SMESH::GetViewWindow( mySMESHGUI ));
317
318   SUIT_ResourceMgr* aResMgr = SMESH::GetResourceMgr( mySMESHGUI );
319   QPixmap IconMergeNodes (aResMgr->loadPixmap("SMESH", tr("ICON_SMESH_MERGE_NODES")));
320   QPixmap IconMergeElems (aResMgr->loadPixmap("SMESH", tr("ICON_DLG_MERGE_ELEMENTS")));
321   QPixmap IconSelect     (aResMgr->loadPixmap("SMESH", tr("ICON_SELECT")));
322   QPixmap IconAdd        (aResMgr->loadPixmap("SMESH", tr("ICON_APPEND")));
323   QPixmap IconRemove     (aResMgr->loadPixmap("SMESH", tr("ICON_REMOVE")));
324
325   setSizeGripEnabled(true);
326
327   QVBoxLayout* DlgLayout = new QVBoxLayout(this);
328   DlgLayout->setSpacing(SPACING);
329   DlgLayout->setMargin(MARGIN);
330
331   /***************************************************************/
332   GroupConstructors = new QGroupBox(myAction == MERGE_ELEMENTS ? 
333                                     tr("SMESH_MERGE_ELEMENTS") : 
334                                     tr("SMESH_MERGE_NODES"), 
335                                     this);
336
337   QButtonGroup* ButtonGroup = new QButtonGroup(this);
338   QHBoxLayout* GroupConstructorsLayout = new QHBoxLayout(GroupConstructors);
339   GroupConstructorsLayout->setSpacing(SPACING);
340   GroupConstructorsLayout->setMargin(MARGIN);
341
342   RadioButton = new QRadioButton(GroupConstructors);
343   RadioButton->setIcon(myAction == MERGE_ELEMENTS ? IconMergeElems : IconMergeNodes);
344   RadioButton->setChecked(true);
345   GroupConstructorsLayout->addWidget(RadioButton);
346   ButtonGroup->addButton(RadioButton, 0);
347
348   /***************************************************************/
349   // Controls for mesh defining
350   GroupMesh = new QGroupBox(tr("SMESH_SELECT_WHOLE_MESH"), this);
351   QHBoxLayout* GroupMeshLayout = new QHBoxLayout(GroupMesh);
352   GroupMeshLayout->setSpacing(SPACING);
353   GroupMeshLayout->setMargin(MARGIN);
354
355   TextLabelName = new QLabel(tr("SMESH_NAME"), GroupMesh);
356   SelectMeshButton = new QPushButton(GroupMesh);
357   SelectMeshButton->setIcon(IconSelect);
358   LineEditMesh = new QLineEdit(GroupMesh);
359   LineEditMesh->setReadOnly(true);
360
361   GroupMeshLayout->addWidget(TextLabelName);
362   GroupMeshLayout->addWidget(SelectMeshButton);
363   GroupMeshLayout->addWidget(LineEditMesh);
364
365   /***************************************************************/
366   // Controls for switch dialog behaviour (myTypeId)
367
368   TypeBox = new QGroupBox( tr( "SMESH_MODE" ), this );
369   GroupType = new QButtonGroup( this );
370   QHBoxLayout* aTypeBoxLayout = new QHBoxLayout( TypeBox );
371   aTypeBoxLayout->setMargin( MARGIN );
372   aTypeBoxLayout->setSpacing( SPACING );
373
374   QRadioButton* rb1 = new QRadioButton( tr( "SMESH_AUTOMATIC" ), TypeBox );
375   QRadioButton* rb2 = new QRadioButton( tr( "SMESH_MANUAL" ),   TypeBox );
376   GroupType->addButton( rb1, 0 );
377   GroupType->addButton( rb2, 1 );
378   aTypeBoxLayout->addWidget( rb1 );
379   aTypeBoxLayout->addWidget( rb2 );
380
381   myTypeId = TYPE_AUTO;
382
383   /***************************************************************/
384   // Controls for coincident elements detecting
385   GroupCoincident = new QGroupBox(myAction == MERGE_ELEMENTS ? 
386                                   tr("COINCIDENT_ELEMENTS") : 
387                                   tr("COINCIDENT_NODES"), 
388                                   this);
389
390   QVBoxLayout* aCoincidentLayout = new QVBoxLayout(GroupCoincident);
391   aCoincidentLayout->setSpacing(SPACING);
392   aCoincidentLayout->setMargin(MARGIN);
393
394   if (myAction == MERGE_NODES) // case merge nodes
395   {
396     QWidget* foo = new QWidget(GroupCoincident);
397     TextLabelTolerance = new QLabel(tr("SMESH_TOLERANCE"), foo);
398     SpinBoxTolerance = new SMESHGUI_SpinBox(foo);
399     SpinBoxTolerance->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
400
401     SeparateCornersAndMedium = new QCheckBox(tr("SEPARATE_CORNERS_AND_MEDIUM"), foo);
402     SeparateCornersAndMedium->setEnabled( false );
403
404     GroupExclude = new QGroupBox(tr("EXCLUDE_GROUPS"), foo);
405     GroupExclude->setCheckable( true );
406     GroupExclude->setChecked( false );
407     ListExclude = new QListWidget( GroupExclude );
408     QVBoxLayout* GroupExcludeLayout = new QVBoxLayout(GroupExclude);
409     GroupExcludeLayout->setSpacing(SPACING);
410     GroupExcludeLayout->setMargin(MARGIN);
411     GroupExcludeLayout->addWidget(ListExclude);
412
413     QGroupBox*  GroupKeep = new QGroupBox(tr("KEEP_NODES"), foo);
414     SelectKeepNodesButton = new QPushButton( GroupKeep );
415     SelectKeepNodesButton->setIcon( IconSelect );
416     QLabel*       selectLabel = new QLabel(tr("SELECT"));
417     QRadioButton*   idsButton = new QRadioButton(tr("NODE_IDS"), GroupKeep);
418     QRadioButton* groupButton = new QRadioButton(tr("GROUP_SUBMESH"), GroupKeep);
419     KeepFromButGroup = new QButtonGroup( this );
420     KeepFromButGroup->addButton( idsButton,   0 );
421     KeepFromButGroup->addButton( groupButton, 1 );
422     groupButton->setChecked( true );
423     KeepList = new QListWidget( GroupKeep );
424     KeepList->setSelectionMode(QAbstractItemView::ExtendedSelection);
425     KeepList->setFlow(QListView::TopToBottom);
426     AddKeepNodesButton    = new QPushButton(tr("SMESH_BUT_ADD"), GroupKeep );
427     RemoveKeepNodesButton = new QPushButton(tr("SMESH_BUT_REMOVE"), GroupKeep );
428     QGridLayout* GroupKeepLayout = new QGridLayout(GroupKeep);
429     GroupKeepLayout->setSpacing( SPACING );
430     GroupKeepLayout->setMargin ( MARGIN );
431     GroupKeepLayout->addWidget( SelectKeepNodesButton, 0, 0 );
432     GroupKeepLayout->addWidget( selectLabel,           0, 1 );
433     GroupKeepLayout->addWidget( idsButton,             0, 2 );
434     GroupKeepLayout->addWidget( groupButton,           0, 3, 1, 2 );
435     GroupKeepLayout->addWidget( KeepList,              1, 0, 2, 4 );
436     GroupKeepLayout->addWidget( AddKeepNodesButton,    1, 4, 1, 1 );
437     GroupKeepLayout->addWidget( RemoveKeepNodesButton, 2, 4, 1, 1 );
438
439     QGridLayout* fooLayout = new QGridLayout( foo );
440     fooLayout->setSpacing(SPACING);
441     fooLayout->setMargin(0);
442     fooLayout->addWidget(TextLabelTolerance,       0, 0 );
443     fooLayout->addWidget(SpinBoxTolerance,         0, 1 );
444     fooLayout->addWidget(SeparateCornersAndMedium, 1, 0 );
445     fooLayout->addWidget(GroupExclude,             2, 0, 1, 2 );
446     fooLayout->addWidget(GroupKeep,                3, 0, 1, 2 );
447     aCoincidentLayout->addWidget(foo);
448
449     // Costruction of the logical filter
450     QList<SUIT_SelectionFilter*> aListOfFilters;
451     aListOfFilters << new SMESH_TypeFilter (SMESH::SUBMESH)
452                    << new SMESH_TypeFilter (SMESH::GROUP);
453     mySubMeshOrGroupFilter =
454       new SMESH_LogicalFilter (aListOfFilters, SMESH_LogicalFilter::LO_OR, /*takeOwnership=*/true);
455   }
456   else {
457     TextLabelTolerance     = 0;
458     SpinBoxTolerance       = 0;
459     GroupExclude           = 0;
460     ListExclude            = 0;
461     KeepFromButGroup       = 0;
462     SelectKeepNodesButton  = 0;
463     AddKeepNodesButton     = 0;
464     RemoveKeepNodesButton  = 0;
465     KeepList               = 0;
466     mySubMeshOrGroupFilter = 0;
467   }
468
469   GroupCoincidentWidget = new QWidget(GroupCoincident);
470   QGridLayout* GroupCoincidentLayout = new QGridLayout(GroupCoincidentWidget);
471   GroupCoincidentLayout->setSpacing(SPACING);
472   GroupCoincidentLayout->setMargin(0);
473
474   ListCoincident = new QListWidget(GroupCoincidentWidget);
475   ListCoincident->setSelectionMode(QListWidget::ExtendedSelection);
476
477   DetectButton      = new QPushButton(tr("DETECT"),           GroupCoincidentWidget);
478   AddGroupButton    = new QPushButton(tr("SMESH_BUT_ADD"),    GroupCoincidentWidget);
479   RemoveGroupButton = new QPushButton(tr("SMESH_BUT_REMOVE"), GroupCoincidentWidget);
480
481   SelectAllCB = new QCheckBox(tr("SELECT_ALL"), GroupCoincidentWidget);
482   ShowIDs = new QCheckBox(myAction == MERGE_ELEMENTS ? tr("SHOW_ELEMS_IDS") : tr("SHOW_NODES_IDS"), GroupCoincidentWidget);
483
484   GroupCoincidentLayout->addWidget(ListCoincident,    0,   0, 4, 2);
485   GroupCoincidentLayout->addWidget(DetectButton,      0,   2);
486   GroupCoincidentLayout->addWidget(AddGroupButton,    2, 2);
487   GroupCoincidentLayout->addWidget(RemoveGroupButton, 3, 2);
488   GroupCoincidentLayout->addWidget(SelectAllCB,       4, 0);
489   GroupCoincidentLayout->addWidget(ShowIDs,           4, 1);
490   GroupCoincidentLayout->setRowMinimumHeight(1, 10);
491   GroupCoincidentLayout->setRowStretch(1, 5);
492
493   aCoincidentLayout->addWidget(GroupCoincidentWidget);
494
495   /***************************************************************/
496   // Controls for editing the selected group
497   GroupEdit = new QGroupBox(tr("EDIT_SELECTED_GROUP"), this);
498   QGridLayout* GroupEditLayout = new QGridLayout(GroupEdit);
499   GroupEditLayout->setSpacing(SPACING);
500   GroupEditLayout->setMargin(MARGIN);
501
502   ListEdit = new QListWidget(GroupEdit);
503   //ListEdit->setRowMode(QListBox::FixedNumber);
504   //ListEdit->setHScrollBarMode(QScrollView::AlwaysOn);
505   //ListEdit->setVScrollBarMode(QScrollView::AlwaysOff);
506   ListEdit->setFlow( QListView::LeftToRight );
507   ListEdit->setSelectionMode(QListWidget::ExtendedSelection);
508
509   AddElemButton = new QPushButton(GroupEdit);
510   AddElemButton->setIcon(IconAdd);
511   RemoveElemButton = new QPushButton(GroupEdit);
512   RemoveElemButton->setIcon(IconRemove);
513   SetFirstButton = new QPushButton(GroupEdit);
514   SetFirstButton->setIcon(QPixmap(IconFirst));
515
516   GroupEditLayout->addWidget(ListEdit,         0, 0, 2, 1);
517   GroupEditLayout->addWidget(AddElemButton,    0, 1);
518   GroupEditLayout->addWidget(RemoveElemButton, 0, 2);
519   GroupEditLayout->addWidget(SetFirstButton,   1, 1, 1, 2);
520
521   /***************************************************************/
522   GroupButtons = new QGroupBox(this);
523   QHBoxLayout* GroupButtonsLayout = new QHBoxLayout(GroupButtons);
524   GroupButtonsLayout->setSpacing(SPACING);
525   GroupButtonsLayout->setMargin(MARGIN);
526
527   buttonOk = new QPushButton(tr("SMESH_BUT_APPLY_AND_CLOSE"), GroupButtons);
528   buttonOk->setAutoDefault(true);
529   buttonOk->setDefault(true);
530   buttonApply = new QPushButton(tr("SMESH_BUT_APPLY"), GroupButtons);
531   buttonApply->setAutoDefault(true);
532   buttonCancel = new QPushButton(tr("SMESH_BUT_CLOSE"), GroupButtons);
533   buttonCancel->setAutoDefault(true);
534   buttonHelp = new QPushButton(tr("SMESH_BUT_HELP"), GroupButtons);
535   buttonHelp->setAutoDefault(true);
536
537   GroupButtonsLayout->addWidget(buttonOk);
538   GroupButtonsLayout->addSpacing(10);
539   GroupButtonsLayout->addWidget(buttonApply);
540   GroupButtonsLayout->addSpacing(10);
541   GroupButtonsLayout->addStretch();
542   GroupButtonsLayout->addWidget(buttonCancel);
543   GroupButtonsLayout->addWidget(buttonHelp);
544
545   /***************************************************************/
546   DlgLayout->addWidget(GroupConstructors);
547   DlgLayout->addWidget(GroupMesh);
548   DlgLayout->addWidget(TypeBox);
549   DlgLayout->addWidget(GroupCoincident);
550   DlgLayout->addWidget(GroupEdit);
551   DlgLayout->addWidget(GroupButtons);
552
553   GroupCoincidentWidget->setVisible( myAction != MERGE_NODES );
554   GroupCoincident      ->setVisible( myAction == MERGE_NODES );
555   //if GroupExclude->setVisible( myAction == MERGE_NODES );
556   GroupEdit->hide();
557
558   this->resize(10,10);
559
560   ShowIDs->setChecked( true );
561
562   Init(); // Initialisations
563 }
564
565 //=================================================================================
566 // function : ~SMESHGUI_MergeDlg()
567 // purpose  : Destroys the object and frees any allocated resources
568 //=================================================================================
569 SMESHGUI_MergeDlg::~SMESHGUI_MergeDlg()
570 {
571   delete myIdPreview;
572 }
573
574 //=================================================================================
575 // function : Init()
576 // purpose  :
577 //=================================================================================
578 void SMESHGUI_MergeDlg::Init()
579 {
580   if ( myAction == MERGE_NODES ) {
581     SpinBoxTolerance->RangeStepAndValidator(0.0, COORD_MAX, 0.00001, "len_tol_precision");
582     SpinBoxTolerance->SetValue(1e-05);
583   }
584
585   RadioButton->setChecked(true);
586
587   GroupType->button(0)->setChecked(true);
588
589   myEditCurrentArgument = (QWidget*)LineEditMesh;
590
591   myActor = 0;
592   mySubMeshOrGroup = SMESH::SMESH_subMesh::_nil();
593
594   mySelector = (SMESH::GetViewWindow( mySMESHGUI ))->GetSelector();
595
596   mySMESHGUI->SetActiveDialogBox((QDialog*)this);
597   myIsBusy = false;
598
599   /* signals and slots connections */
600   connect(buttonOk,     SIGNAL(clicked()), this, SLOT(ClickOnOk()));
601   connect(buttonCancel, SIGNAL(clicked()), this, SLOT(reject()));
602   connect(buttonApply,  SIGNAL(clicked()), this, SLOT(ClickOnApply()));
603   connect(buttonHelp,   SIGNAL(clicked()), this, SLOT(ClickOnHelp()));
604
605   if ( KeepList )
606   {
607     connect(SelectKeepNodesButton, SIGNAL (clicked()), this, SLOT(SetEditCurrentArgument()));
608     connect(KeepFromButGroup, SIGNAL (buttonClicked(int)), SLOT(onKeepNodeSourceChanged(int)));
609     connect(AddKeepNodesButton, SIGNAL (clicked()), this, SLOT(onAddKeepNode()));
610     connect(RemoveKeepNodesButton, SIGNAL (clicked()), this, SLOT(onRemoveKeepNode()));
611     connect(KeepList, SIGNAL (itemSelectionChanged()), this, SLOT(onSelectKeepNode()));
612   }
613   connect(SelectMeshButton, SIGNAL (clicked()), this, SLOT(SetEditCurrentArgument()));
614   connect(DetectButton, SIGNAL (clicked()), this, SLOT(onDetect()));
615   connect(ListCoincident, SIGNAL (itemSelectionChanged()), this, SLOT(onSelectGroup()));
616   connect(AddGroupButton, SIGNAL (clicked()), this, SLOT(onAddGroup()));
617   connect(RemoveGroupButton, SIGNAL (clicked()), this, SLOT(onRemoveGroup()));
618   connect(SelectAllCB, SIGNAL(toggled(bool)), this, SLOT(onSelectAll(bool)));
619   connect(ShowIDs, SIGNAL(toggled(bool)), this, SLOT(onSelectGroup()));
620   connect(ListEdit, SIGNAL (itemSelectionChanged()), this, SLOT(onSelectElementFromGroup()));
621   connect(AddElemButton, SIGNAL (clicked()), this, SLOT(onAddElement()));
622   connect(RemoveElemButton, SIGNAL (clicked()), this, SLOT(onRemoveElement()));
623   connect(SetFirstButton, SIGNAL( clicked() ), this, SLOT( onSetFirst()));
624   connect(GroupType, SIGNAL(buttonClicked(int)), this, SLOT(onTypeChanged(int)));
625
626   connect(mySMESHGUI, SIGNAL (SignalDeactivateActiveDialog()), this, SLOT(DeactivateActiveDialog()));
627   connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(SelectionIntoArgument()));
628   /* to close dialog if study change */
629   connect(mySMESHGUI, SIGNAL (SignalCloseAllDialogs()), this, SLOT(reject()));
630   connect(mySMESHGUI, SIGNAL (SignalActivatedViewManager()), this,  SLOT(onOpenView()));
631   connect(mySMESHGUI, SIGNAL (SignalCloseView()), this, SLOT(onCloseView()));
632   // Init Mesh field from selection
633   SelectionIntoArgument();
634
635   // Update Buttons
636   updateControls();
637   
638   if ( myAction == MERGE_NODES )
639     myHelpFileName = "merging_nodes_page.html";
640   else
641     myHelpFileName = "merging_elements_page.html";
642 }
643
644 //=================================================================================
645 // function : FindGravityCenter()
646 // purpose  :
647 //=================================================================================
648 void SMESHGUI_MergeDlg::FindGravityCenter(TColStd_MapOfInteger & theElemsIdMap, 
649                                           std::list< gp_XYZ > & theGrCentersXYZ)
650 {
651   if (!myActor)
652     return;
653
654   SMDS_Mesh* aMesh = 0;
655   aMesh = myActor->GetObject()->GetMesh();
656   if (!aMesh)
657     return;
658
659   int nbNodes;
660
661   TColStd_MapIteratorOfMapOfInteger idIter( theElemsIdMap );
662   for( ; idIter.More(); idIter.Next() ) {
663     const SMDS_MeshElement* anElem = aMesh->FindElement(idIter.Key());
664     if ( !anElem )
665       continue;
666
667     gp_XYZ anXYZ(0., 0., 0.);
668     SMDS_ElemIteratorPtr nodeIt = anElem->nodesIterator();
669     for ( nbNodes = 0; nodeIt->more(); nbNodes++ ) {
670       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
671       anXYZ.Add( gp_XYZ( node->X(), node->Y(), node->Z() ) );
672     }
673     anXYZ.Divide( nbNodes );
674     
675     theGrCentersXYZ.push_back( anXYZ );
676   }
677 }
678
679 //=================================================================================
680 // function : ClickOnApply()
681 // purpose  :
682 //=================================================================================
683 bool SMESHGUI_MergeDlg::ClickOnApply()
684 {
685   if (mySMESHGUI->isActiveStudyLocked() || myMesh->_is_nil())
686     return false;
687
688   try {
689     if (myTypeId == TYPE_AUTO)
690       onDetect();
691
692     SUIT_OverrideCursor aWaitCursor;
693     SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditor();
694
695     SMESH::long_array_var anIds = new SMESH::long_array;
696     SMESH::array_of_long_array_var aGroupsOfElements = new SMESH::array_of_long_array;
697
698     if ( ListCoincident->count() == 0) {
699       if ( myAction == MERGE_NODES )
700         SUIT_MessageBox::warning(this,
701                                  tr("SMESH_WARNING"),
702                                  tr("SMESH_NO_NODES_DETECTED"));
703       else
704         SUIT_MessageBox::warning(this,
705                                  tr("SMESH_WARNING"),
706                                  tr("SMESH_NO_ELEMENTS_DETECTED"));
707       return false;
708     }
709
710     aGroupsOfElements->length(ListCoincident->count());
711
712     int anArrayNum = 0;
713     for (int i = 0; i < ListCoincident->count(); i++) {
714       QStringList aListIds = ListCoincident->item(i)->text().split(" ", QString::SkipEmptyParts);
715
716       anIds->length(aListIds.count());
717       for (int i = 0; i < aListIds.count(); i++)
718         anIds[i] = aListIds[i].toInt();
719
720       aGroupsOfElements[anArrayNum++] = anIds.inout();
721     }
722
723     SMESH::ListOfIDSources_var nodesToKeep;
724     SMESH::IDSource_wrap tmpIdSource;
725     if ( myAction == MERGE_NODES )
726     {
727       nodesToKeep = new SMESH::ListOfIDSources();
728       int i, nb = KeepList->count();
729       if ( isKeepNodesIDsSelection() )
730       {
731         SMESH::long_array_var anIdList = new SMESH::long_array();
732         anIdList->length(nb);
733         for (i = 0; i < nb; i++)
734           anIdList[i] = KeepList->item(i)->text().toInt();
735
736         if ( nb > 0 )
737         {
738           tmpIdSource = aMeshEditor->MakeIDSource( anIdList, SMESH::NODE );
739           nodesToKeep->length( 1 );
740           nodesToKeep[0] = SMESH::SMESH_IDSource::_duplicate( tmpIdSource.in() );
741         }
742       }
743       else
744       {
745         nodesToKeep->length( nb );
746         int nbObj = 0;
747         for (i = 0; i < nb; i++)
748         {
749           QString entry = KeepList->item( i )->data( Qt::UserRole ).toString();
750           Handle(SALOME_InteractiveObject) anIO =
751             new SALOME_InteractiveObject( entry.toStdString().c_str(), "SMESH" );
752           SMESH::SMESH_IDSource_var idSrc =
753             SMESH::IObjectToInterface<SMESH::SMESH_IDSource>( anIO );
754           if ( !idSrc->_is_nil() )
755             nodesToKeep[ nbObj++ ] = SMESH::SMESH_IDSource::_duplicate( idSrc );
756         }
757         nodesToKeep->length( nbObj );
758       }
759       KeepList->clear();
760     }
761
762     if( myAction == MERGE_NODES )
763       aMeshEditor->MergeNodes (aGroupsOfElements.inout(), nodesToKeep);
764     else
765       aMeshEditor->MergeElements (aGroupsOfElements.inout());
766
767     if ( myTypeId == TYPE_AUTO ) {
768       if (myAction == MERGE_NODES )
769         SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_INFORMATION"),
770                                      tr("SMESH_MERGED_NODES").arg(QString::number(ListCoincident->count()).toLatin1().data()));
771       else
772         SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_INFORMATION"),
773                                      tr("SMESH_MERGED_ELEMENTS").arg(QString::number(ListCoincident->count()).toLatin1().data()));
774     }
775     nodesToKeep->length(0); // release before tmpIdSource calls UnRegister()
776
777   }
778   catch(...) {
779   }
780
781   ListCoincident->clear();
782
783   myEditCurrentArgument = (QWidget*)LineEditMesh;
784
785   SMESH::UpdateView();
786   SMESHGUI::Modified();
787
788   return true;
789 }
790
791 //=================================================================================
792 // function : ClickOnOk()
793 // purpose  :
794 //=================================================================================
795 void SMESHGUI_MergeDlg::ClickOnOk()
796 {
797   if (ClickOnApply())
798     reject();
799 }
800
801 //=================================================================================
802 // function : reject()
803 // purpose  :
804 //=================================================================================
805 void SMESHGUI_MergeDlg::reject()
806 {
807   myIdPreview->SetPointsLabeled(false);
808   SMESH::SetPointRepresentation(false);
809   disconnect(mySelectionMgr, 0, this, 0);
810   disconnect(mySMESHGUI, 0, this, 0);
811   mySMESHGUI->ResetState();
812
813   mySelectionMgr->clearFilters();
814   //mySelectionMgr->clearSelected();
815
816   if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
817     aViewWindow->SetSelectionMode(ActorSelection);
818
819   QDialog::reject();
820 }
821
822 //=================================================================================
823 // function : onOpenView()
824 // purpose  :
825 //=================================================================================
826 void SMESHGUI_MergeDlg::onOpenView()
827 {
828   if ( mySelector ) {
829     SMESH::SetPointRepresentation(false);
830   }
831   else {
832     mySelector = SMESH::GetViewWindow( mySMESHGUI )->GetSelector();
833     ActivateThisDialog();
834   }
835 }
836
837 //=================================================================================
838 // function : onCloseView()
839 // purpose  :
840 //=================================================================================
841 void SMESHGUI_MergeDlg::onCloseView()
842 {
843   DeactivateActiveDialog();
844   mySelector = 0;
845 }
846
847 //=================================================================================
848 // function : ClickOnHelp()
849 // purpose  :
850 //=================================================================================
851 void SMESHGUI_MergeDlg::ClickOnHelp()
852 {
853   LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication());
854   if (app) 
855     app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), myHelpFileName);
856   else {
857     QString platform;
858 #ifdef WIN32
859     platform = "winapplication";
860 #else
861     platform = "application";
862 #endif
863     SUIT_MessageBox::warning(this, tr("WRN_WARNING"),
864                              tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE").
865                              arg(app->resourceMgr()->stringValue("ExternalBrowser", 
866                                                                  platform)).
867                              arg(myHelpFileName));
868   }
869 }
870
871 //=================================================================================
872 // function : onEditGroup()
873 // purpose  :
874 //=================================================================================
875 void SMESHGUI_MergeDlg::onEditGroup()
876 {
877   QList<QListWidgetItem*> selItems = ListCoincident->selectedItems();
878   if ( selItems.count() != 1 ) {
879     ListEdit->clear();
880     return;
881   }
882
883   QStringList aNewIds;
884
885   for (int i = 0; i < ListEdit->count(); i++ )
886     aNewIds.append(ListEdit->item(i)->text());
887
888   ListCoincident->clearSelection();
889   selItems.first()->setText(aNewIds.join(" "));
890   selItems.first()->setSelected(true);
891 }
892
893 //=================================================================================
894 // function : updateControls()
895 // purpose  :
896 //=================================================================================
897 void SMESHGUI_MergeDlg::updateControls()
898 {
899   if (ListEdit->count() == 0)
900     SetFirstButton->setEnabled(false);
901
902   bool groupsEmpty = ( myTypeId != TYPE_AUTO );
903   for (int i = 0; i < ListCoincident->count() && groupsEmpty; i++) {
904     QStringList aListIds = ListCoincident->item(i)->text().split(" ", QString::SkipEmptyParts);
905     groupsEmpty = ( aListIds.count() < 2 );
906   }
907   bool enable = ( !myMesh->_is_nil() && !groupsEmpty );
908   buttonOk->setEnabled(enable);
909   buttonApply->setEnabled(enable);
910   DetectButton->setEnabled( !myMesh->_is_nil() );
911
912   if ( myAction == MERGE_NODES )
913   {
914     bool has2ndOrder = (( !myMesh->_is_nil() ) &&
915                         ( myMesh->NbEdgesOfOrder( SMESH::ORDER_QUADRATIC ) > 0 ||
916                           myMesh->NbFacesOfOrder( SMESH::ORDER_QUADRATIC ) > 0 ||
917                           myMesh->NbVolumesOfOrder( SMESH::ORDER_QUADRATIC ) > 0 ));
918
919     SeparateCornersAndMedium->setEnabled( has2ndOrder );
920
921     if ( myEditCurrentArgument != KeepList )
922     {
923       AddKeepNodesButton->setEnabled( false );
924       RemoveKeepNodesButton->setEnabled( false );
925       KeepList->clearSelection();
926     }
927   }
928 }
929
930 //=================================================================================
931 // function : onDetect()
932 // purpose  :
933 //=================================================================================
934 void SMESHGUI_MergeDlg::onDetect()
935 {
936   if ( myMesh->_is_nil() || LineEditMesh->text().isEmpty() )
937     return;
938
939   try {
940     SUIT_OverrideCursor aWaitCursor;
941     SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditor();
942
943     ListCoincident->clear();
944     ListEdit->clear();
945
946     SMESH::array_of_long_array_var aGroupsArray;
947     SMESH::ListOfIDSources_var aExcludeGroups = new SMESH::ListOfIDSources;
948
949     SMESH::SMESH_IDSource_var src;
950     if ( mySubMeshOrGroup->_is_nil() ) src = SMESH::SMESH_IDSource::_duplicate( myMesh );
951     else src = SMESH::SMESH_IDSource::_duplicate( mySubMeshOrGroup );
952
953     switch (myAction) {
954     case MERGE_NODES :
955       for ( int i = 0; GroupExclude->isChecked() && i < ListExclude->count(); i++ ) {
956         if ( ListExclude->item( i )->checkState() == Qt::Checked ) {
957           aExcludeGroups->length( aExcludeGroups->length()+1 );
958           aExcludeGroups[ aExcludeGroups->length()-1 ] = SMESH::SMESH_IDSource::_duplicate( myGroups[i] );
959         }
960       }
961       aMeshEditor->FindCoincidentNodesOnPartBut(src.in(),
962                                                 SpinBoxTolerance->GetValue(), 
963                                                 aGroupsArray.out(),
964                                                 aExcludeGroups.in(),
965                                                 SeparateCornersAndMedium->isEnabled() &&
966                                                 SeparateCornersAndMedium->isChecked());
967       break;
968     case MERGE_ELEMENTS :
969       aMeshEditor->FindEqualElements(src.in(), aGroupsArray.out());
970       break;
971     }
972     
973     for (int i = 0; i < aGroupsArray->length(); i++) {
974       SMESH::long_array& aGroup = aGroupsArray[i];
975
976       QStringList anIDs;
977       for (int j = 0; j < aGroup.length(); j++)
978         anIDs.append(QString::number(aGroup[j]));
979
980       ListCoincident->addItem(anIDs.join(" "));
981     }
982   } catch(...) {
983   }
984
985   ListCoincident->selectAll();
986   updateControls();
987   SMESH::UpdateView();
988 }
989
990 //=================================================================================
991 // function : onSelectGroup()
992 // purpose  :
993 //=================================================================================
994 void SMESHGUI_MergeDlg::onSelectGroup()
995 {
996   if (myIsBusy || !myActor)
997     return;
998
999   if( ListCoincident->count() != ListCoincident->selectedItems().count() )
1000     SelectAllCB->setChecked( false );
1001
1002   myEditCurrentArgument = (QWidget*)ListCoincident;
1003
1004   myIsBusy = true;
1005   ListEdit->clear();
1006
1007   TColStd_MapOfInteger anIndices;
1008   QList<QListWidgetItem*> selItems = ListCoincident->selectedItems();
1009   QListWidgetItem* anItem;
1010   QStringList aListIds;
1011
1012   ListEdit->clear();
1013
1014   foreach(anItem, selItems) {
1015     aListIds = anItem->text().split(" ", QString::SkipEmptyParts);
1016     for (int i = 0; i < aListIds.count(); i++)
1017       anIndices.Add(aListIds[i].toInt());
1018   }
1019
1020   if (selItems.count() == 1) {
1021     ListEdit->addItems(aListIds);
1022     ListEdit->selectAll();
1023   }
1024
1025   mySelector->AddOrRemoveIndex(myActor->getIO(), anIndices, false);
1026   SALOME_ListIO aList;
1027   aList.Append(myActor->getIO());
1028   mySelectionMgr->setSelectedObjects(aList,false);
1029   
1030   if (ShowIDs->isChecked()) 
1031     if ( myAction == MERGE_NODES ) {
1032       myIdPreview->SetPointsData(myActor->GetObject()->GetMesh(), anIndices);
1033       myIdPreview->SetPointsLabeled(!anIndices.IsEmpty(), myActor->GetVisibility());
1034     }
1035     else {
1036       std::list< gp_XYZ > aGrCentersXYZ;
1037       FindGravityCenter(anIndices, aGrCentersXYZ);
1038       myIdPreview->SetElemsData( anIndices, aGrCentersXYZ);
1039       myIdPreview->SetPointsLabeled(!anIndices.IsEmpty(), myActor->GetVisibility());
1040     }
1041   else
1042     myIdPreview->SetPointsLabeled(false);
1043
1044   updateControls();
1045   myIsBusy = false;
1046 }
1047
1048 //=================================================================================
1049 // function : onSelectAll()
1050 // purpose  :
1051 //=================================================================================
1052 void SMESHGUI_MergeDlg::onSelectAll (bool isToggled)
1053 {
1054   if ( isToggled )
1055     ListCoincident->selectAll();
1056   else
1057     ListCoincident->clearSelection();
1058 }
1059
1060 //=================================================================================
1061 // function : onSelectElementFromGroup()
1062 // purpose  :
1063 //=================================================================================
1064 void SMESHGUI_MergeDlg::onSelectElementFromGroup()
1065 {
1066   if (myIsBusy || !myActor)
1067     return;
1068
1069   TColStd_MapOfInteger anIndices;
1070   QList<QListWidgetItem*> selItems = ListEdit->selectedItems();
1071   QListWidgetItem* anItem;
1072
1073   foreach(anItem, selItems)
1074     anIndices.Add(anItem->text().toInt());
1075
1076   SetFirstButton->setEnabled(selItems.count() == 1);
1077
1078   mySelector->AddOrRemoveIndex(myActor->getIO(), anIndices, false);
1079   SALOME_ListIO aList;
1080   aList.Append(myActor->getIO());
1081   mySelectionMgr->setSelectedObjects(aList);
1082   
1083   if (ShowIDs->isChecked())
1084     if (myAction == MERGE_NODES) {
1085       myIdPreview->SetPointsData(myActor->GetObject()->GetMesh(), anIndices);
1086       myIdPreview->SetPointsLabeled(!anIndices.IsEmpty(), myActor->GetVisibility());
1087     }
1088     else {
1089       std::list< gp_XYZ > aGrCentersXYZ;
1090       FindGravityCenter(anIndices, aGrCentersXYZ);
1091       myIdPreview->SetElemsData(anIndices, aGrCentersXYZ);
1092       myIdPreview->SetPointsLabeled(!anIndices.IsEmpty(), myActor->GetVisibility());
1093     }
1094   else 
1095     myIdPreview->SetPointsLabeled(false);
1096 }
1097
1098 //=================================================================================
1099 // function : onAddGroup()
1100 // purpose  :
1101 //=================================================================================
1102 void SMESHGUI_MergeDlg::onAddGroup()
1103 {
1104   if ( myMesh->_is_nil() || LineEditMesh->text().isEmpty() )
1105     return;
1106
1107   QString anIDs = "";
1108   int aNbElements = 0;
1109   aNbElements = SMESH::GetNameOfSelectedNodes(mySelector, myActor->getIO(), anIDs);
1110
1111   if (aNbElements < 1)
1112     return;
1113   
1114   ListCoincident->clearSelection();
1115   ListCoincident->addItem(anIDs);
1116   int nbGroups = ListCoincident->count();
1117   if (nbGroups) {
1118     ListCoincident->setCurrentRow(nbGroups-1);
1119     ListCoincident->item(nbGroups-1)->setSelected(true);
1120   }
1121   else {
1122     // VSR ? this code seems to be never executed!!!
1123     ListCoincident->setCurrentRow(0);
1124     //ListCoincident->setSelected(0, true); // VSR: no items - no selection
1125   }
1126
1127   updateControls();
1128 }
1129
1130 //=================================================================================
1131 // function : onRemoveGroup()
1132 // purpose  :
1133 //=================================================================================
1134 void SMESHGUI_MergeDlg::onRemoveGroup()
1135 {
1136   if (myEditCurrentArgument != (QWidget*)ListCoincident)
1137     return;
1138   myIsBusy = true;
1139
1140   QList<QListWidgetItem*> selItems = ListCoincident->selectedItems();
1141   QListWidgetItem* anItem;
1142
1143   foreach(anItem, selItems)
1144     delete anItem;
1145
1146   ListEdit->clear();
1147   myIdPreview->SetPointsLabeled(false);
1148   updateControls();
1149   SMESH::UpdateView();
1150   myIsBusy = false;
1151
1152   if( ListCoincident->count() == 0 ) {
1153     myEditCurrentArgument = (QWidget*)LineEditMesh;
1154     SelectAllCB->setChecked( false );
1155   }
1156 }
1157
1158 //=================================================================================
1159 // function : onAddElement()
1160 // purpose  :
1161 //=================================================================================
1162 void SMESHGUI_MergeDlg::onAddElement()
1163 {
1164   if (!myActor)
1165     return;
1166   myIsBusy = true;
1167
1168   QString aListStr = "";
1169   int aNbNnodes = 0;
1170
1171   aNbNnodes = SMESH::GetNameOfSelectedNodes(mySelector, myActor->getIO(), aListStr);
1172   if (aNbNnodes < 1)
1173     return;
1174
1175   QStringList aNodes = aListStr.split(" ", QString::SkipEmptyParts);
1176
1177   for (QStringList::iterator it = aNodes.begin(); it != aNodes.end(); ++it) {
1178     QList<QListWidgetItem*> found = ListEdit->findItems(*it, Qt::MatchExactly);
1179     if ( found.count() == 0 ) {
1180       QListWidgetItem* anItem = new QListWidgetItem(*it);
1181       ListEdit->addItem(anItem);
1182       anItem->setSelected(true);
1183     }
1184     else {
1185       QListWidgetItem* anItem;
1186       foreach(anItem, found) anItem->setSelected(true);
1187     }
1188   }
1189
1190   myIsBusy = false;
1191   onEditGroup();
1192 }
1193
1194 //=================================================================================
1195 // function : onRemoveElement()
1196 // purpose  :
1197 //=================================================================================
1198 void SMESHGUI_MergeDlg::onRemoveElement()
1199 {
1200   if (myEditCurrentArgument != (QWidget*)ListCoincident)
1201     return;
1202   myIsBusy = true;
1203
1204   QList<QListWidgetItem*> selItems = ListEdit->selectedItems();
1205   QListWidgetItem* anItem;
1206
1207   foreach(anItem, selItems)
1208     delete anItem;
1209   
1210   myIsBusy = false;
1211   onEditGroup();
1212
1213   if( ListCoincident->count() == 0 ) {
1214     myEditCurrentArgument = (QWidget*)LineEditMesh;
1215     SelectAllCB->setChecked( false );
1216   }
1217 }
1218
1219 //=================================================================================
1220 // function : onSetFirst()
1221 // purpose  :
1222 //=================================================================================
1223 void SMESHGUI_MergeDlg::onSetFirst()
1224 {
1225   if (myEditCurrentArgument != (QWidget*)ListCoincident)
1226     return;
1227   myIsBusy = true;
1228   
1229   QList<QListWidgetItem*> selItems = ListEdit->selectedItems();
1230   QListWidgetItem* anItem;
1231   
1232   foreach(anItem, selItems) {
1233     ListEdit->takeItem(ListEdit->row(anItem));
1234     ListEdit->insertItem(0, anItem);
1235   }
1236
1237   myIsBusy = false;
1238   onEditGroup();
1239 }
1240
1241 //=================================================================================
1242 // function : SetEditCurrentArgument()
1243 // purpose  :
1244 //=================================================================================
1245 void SMESHGUI_MergeDlg::SetEditCurrentArgument()
1246 {
1247   QPushButton* send = (QPushButton*)sender();
1248
1249   disconnect(mySelectionMgr, 0, this, 0);
1250   mySelectionMgr->clearSelected();
1251   mySelectionMgr->clearFilters();
1252
1253   if (send == SelectMeshButton)
1254   {
1255     myEditCurrentArgument = (QWidget*)LineEditMesh;
1256     SMESH::SetPointRepresentation(false);
1257     if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
1258       aViewWindow->SetSelectionMode(ActorSelection);
1259     if (myTypeId == TYPE_MANUAL)
1260       mySelectionMgr->installFilter(myMeshOrSubMeshOrGroupFilter);
1261   }
1262   else if ( send == SelectKeepNodesButton && send )
1263   {
1264     myEditCurrentArgument = (QWidget*)KeepList;
1265     KeepList->setWrapping( isKeepNodesIDsSelection() );
1266     if ( isKeepNodesIDsSelection() )
1267     {
1268       SMESH::SetPointRepresentation( true );
1269       if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
1270         aViewWindow->SetSelectionMode( NodeSelection );
1271     }
1272     else
1273     {
1274       if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
1275         aViewWindow->SetSelectionMode( ActorSelection );
1276       mySelectionMgr->installFilter( mySubMeshOrGroupFilter );
1277     }
1278   }
1279
1280   myEditCurrentArgument->setFocus();
1281   connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(SelectionIntoArgument()));
1282   SelectionIntoArgument();
1283 }
1284
1285 //=================================================================================
1286 // function : SelectionIntoArgument()
1287 // purpose  : Called when selection has changed or other case
1288 //=================================================================================
1289 void SMESHGUI_MergeDlg::SelectionIntoArgument()
1290 {
1291   if (myEditCurrentArgument == (QWidget*)LineEditMesh)
1292   {
1293     QString aString = "";
1294     LineEditMesh->setText(aString);
1295
1296     ListCoincident->clear();
1297     ListEdit->clear();
1298     myActor = 0;
1299     myMesh = SMESH::SMESH_Mesh::_nil();
1300     QString aCurrentEntry = myEntry;
1301
1302     int nbSel = SMESH::GetNameOfSelectedIObjects(mySelectionMgr, aString);
1303     if (nbSel != 1) {
1304       myIdPreview->SetPointsLabeled(false);
1305       SMESH::SetPointRepresentation(false);
1306       mySelectionMgr->clearFilters();
1307       if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
1308         aViewWindow->SetSelectionMode(ActorSelection);
1309       return;
1310     }
1311
1312     SALOME_ListIO aList;
1313     mySelectionMgr->selectedObjects(aList);
1314
1315     Handle(SALOME_InteractiveObject) IO = aList.First();
1316     myEntry = IO->getEntry();
1317     myMesh = SMESH::GetMeshByIO(IO);
1318
1319     if ( myEntry != aCurrentEntry && KeepList )
1320       KeepList->clear();
1321
1322     if (myMesh->_is_nil())
1323       return;
1324
1325     LineEditMesh->setText(aString);
1326
1327     myActor = SMESH::FindActorByEntry(IO->getEntry());
1328     if (!myActor)
1329       myActor = SMESH::FindActorByObject(myMesh);
1330
1331     if ( myActor && myTypeId == TYPE_MANUAL && mySelector->IsSelectionEnabled() ) {
1332       mySubMeshOrGroup = SMESH::SMESH_IDSource::_nil();
1333       mySelectionMgr->installFilter(myMeshOrSubMeshOrGroupFilter);
1334
1335       if ((!SMESH::IObjectToInterface<SMESH::SMESH_subMesh>(IO)->_is_nil() || //SUBMESH OR GROUP
1336            !SMESH::IObjectToInterface<SMESH::SMESH_GroupBase>(IO)->_is_nil()) &&
1337           !SMESH::IObjectToInterface<SMESH::SMESH_IDSource>(IO)->_is_nil())
1338         mySubMeshOrGroup = SMESH::IObjectToInterface<SMESH::SMESH_IDSource>(IO);
1339
1340       if (myAction == MERGE_NODES) {
1341         SMESH::SetPointRepresentation(true);
1342         if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
1343           aViewWindow->SetSelectionMode(NodeSelection);
1344       }
1345       else
1346         if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
1347           aViewWindow->SetSelectionMode(CellSelection);
1348     }
1349
1350     // process groups
1351     if ( myAction == MERGE_NODES && !myMesh->_is_nil() && myEntry != aCurrentEntry ) {
1352       myGroups.clear();
1353       ListExclude->clear();
1354       SMESH::ListOfGroups_var aListOfGroups = myMesh->GetGroups();
1355       for( int i = 0, n = aListOfGroups->length(); i < n; i++ ) {
1356         SMESH::SMESH_GroupBase_var aGroup = aListOfGroups[i];
1357         if ( !aGroup->_is_nil() ) { // && aGroup->GetType() == SMESH::NODE
1358           QString aGroupName( aGroup->GetName() );
1359           if ( !aGroupName.isEmpty() ) {
1360             myGroups.append(SMESH::SMESH_GroupBase::_duplicate(aGroup));
1361             QListWidgetItem* item = new QListWidgetItem( aGroupName );
1362             item->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable );
1363             item->setCheckState( Qt::Unchecked );
1364             ListExclude->addItem( item );
1365           }
1366         }
1367       }
1368     }
1369
1370     updateControls();
1371   }
1372
1373   else if (myEditCurrentArgument == (QWidget*)KeepList && KeepList)
1374   {
1375     AddKeepNodesButton->setEnabled( false );
1376     RemoveKeepNodesButton->setEnabled( false );
1377     if ( isKeepNodesIDsSelection() )
1378     {
1379       if (!myMesh->_is_nil() && !myActor)
1380         myActor = SMESH::FindActorByObject(myMesh);
1381
1382       if ( mySelector && myActor )
1383       {
1384         KeepList->clearSelection();
1385         QString anIDs = "";
1386         int aNbNodes = SMESH::GetNameOfSelectedNodes(mySelector, myActor->getIO(), anIDs);
1387         if (aNbNodes > 0)
1388         {
1389           QStringList anNodes = anIDs.split( " ", QString::SkipEmptyParts);
1390           QList<QListWidgetItem*> listItemsToSel;
1391           QListWidgetItem* anItem;
1392           int nbFound = 0;
1393           for (QStringList::iterator it = anNodes.begin(); it != anNodes.end(); ++it)
1394           {
1395             QList<QListWidgetItem*> found = KeepList->findItems(*it, Qt::MatchExactly);
1396             foreach(anItem, found)
1397               if (!anItem->isSelected())
1398                 listItemsToSel.push_back(anItem);
1399             nbFound += found.count();
1400           }
1401           bool blocked = KeepList->signalsBlocked();
1402           KeepList->blockSignals(true);
1403           foreach(anItem, listItemsToSel) anItem->setSelected(true);
1404           KeepList->blockSignals(blocked);
1405           //onSelectKeepNode();
1406           AddKeepNodesButton->setEnabled( nbFound < aNbNodes );
1407           RemoveKeepNodesButton->setEnabled( nbFound > 0 );
1408         }
1409       }
1410     }
1411     else if ( !myMesh->_is_nil() )
1412     {
1413       SALOME_ListIO aList;
1414       mySelectionMgr->selectedObjects(aList);
1415       bool hasNewSelected = false;
1416       SALOME_ListIteratorOfListIO anIt (aList);
1417       for ( ; anIt.More() && !hasNewSelected; anIt.Next())
1418         if ( anIt.Value()->hasEntry() )
1419           hasNewSelected = isNewKeepNodesGroup( anIt.Value()->getEntry() );
1420
1421       AddKeepNodesButton->setEnabled( hasNewSelected );
1422       //RemoveKeepNodesButton->setEnabled( KeepList->selectedItems().count() );
1423     }
1424   }
1425 }
1426
1427 //=================================================================================
1428 // function : DeactivateActiveDialog()
1429 // purpose  :
1430 //=================================================================================
1431 void SMESHGUI_MergeDlg::DeactivateActiveDialog()
1432 {
1433   if (GroupConstructors->isEnabled()) {
1434     GroupConstructors->setEnabled(false);
1435     TypeBox->setEnabled(false);
1436     GroupMesh->setEnabled(false);
1437     GroupCoincident->setEnabled(false);
1438     GroupEdit->setEnabled(false);
1439     GroupButtons->setEnabled(false);
1440     mySMESHGUI->ResetState();
1441     mySMESHGUI->SetActiveDialogBox(0);
1442   }
1443
1444   mySelectionMgr->clearSelected();
1445   disconnect(mySelectionMgr, 0, this, 0);
1446 }
1447
1448 //=================================================================================
1449 // function : ActivateThisDialog()
1450 // purpose  :
1451 //=================================================================================
1452 void SMESHGUI_MergeDlg::ActivateThisDialog()
1453 {
1454   /* Emit a signal to deactivate the active dialog */
1455   mySMESHGUI->EmitSignalDeactivateDialog();
1456   GroupConstructors->setEnabled(true);
1457   TypeBox->setEnabled(true);
1458   GroupMesh->setEnabled(true);
1459   GroupCoincident->setEnabled(true);
1460   GroupEdit->setEnabled(true);
1461   GroupButtons->setEnabled(true);
1462
1463   connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(SelectionIntoArgument()));
1464   mySMESHGUI->SetActiveDialogBox((QDialog*)this);
1465   SelectionIntoArgument();
1466 }
1467
1468 //=================================================================================
1469 // function : enterEvent()
1470 // purpose  :
1471 //=================================================================================
1472 void SMESHGUI_MergeDlg::enterEvent (QEvent*)
1473 {
1474   if ( !GroupConstructors->isEnabled() ) {
1475     SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI );
1476     if ( aViewWindow && !mySelector) {
1477       mySelector = aViewWindow->GetSelector();
1478     }
1479     ActivateThisDialog();
1480   }
1481 }
1482
1483 //=================================================================================
1484 // function : keyPressEvent()
1485 // purpose  :
1486 //=================================================================================
1487 void SMESHGUI_MergeDlg::keyPressEvent( QKeyEvent* e)
1488 {
1489   QDialog::keyPressEvent( e );
1490   if ( e->isAccepted() )
1491     return;
1492
1493   if ( e->key() == Qt::Key_F1 ) {
1494     e->accept();
1495     ClickOnHelp();
1496   }
1497 }
1498
1499 //=================================================================================
1500 // function : onTypeChanged()
1501 // purpose  : the type radio button management
1502 //=================================================================================
1503 void SMESHGUI_MergeDlg::onTypeChanged (int id)
1504 {
1505   if (myTypeId == id)
1506     return;
1507
1508   myTypeId = id;
1509   switch (id)
1510   {
1511   case TYPE_AUTO: // automatic
1512
1513     myIdPreview->SetPointsLabeled(false);
1514     SMESH::SetPointRepresentation(false);
1515     if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
1516       aViewWindow->SetSelectionMode(ActorSelection);
1517     mySelectionMgr->clearFilters();
1518     if (myAction == MERGE_NODES)
1519       GroupCoincidentWidget->hide();
1520     else
1521       GroupCoincident->hide();
1522     GroupEdit->hide();
1523     break;
1524
1525   case TYPE_MANUAL: // manual
1526
1527     SMESH::UpdateView();
1528
1529     // Costruction of the logical filter
1530     SMESH_TypeFilter* aMeshOrSubMeshFilter = new SMESH_TypeFilter (SMESH::MESHorSUBMESH);
1531     SMESH_TypeFilter* aSmeshGroupFilter    = new SMESH_TypeFilter (SMESH::GROUP);
1532     
1533     QList<SUIT_SelectionFilter*> aListOfFilters;
1534     if (aMeshOrSubMeshFilter) aListOfFilters.append(aMeshOrSubMeshFilter);
1535     if (aSmeshGroupFilter)    aListOfFilters.append(aSmeshGroupFilter);
1536     
1537     myMeshOrSubMeshOrGroupFilter =
1538       new SMESH_LogicalFilter (aListOfFilters, SMESH_LogicalFilter::LO_OR);
1539
1540     if (myAction == MERGE_NODES) {
1541       GroupCoincidentWidget->show();
1542       SMESH::SetPointRepresentation(true);
1543       if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
1544         if( mySelector->IsSelectionEnabled() )
1545           aViewWindow->SetSelectionMode(NodeSelection);
1546     }
1547     else {
1548       GroupCoincident->show();
1549       if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
1550         if( mySelector->IsSelectionEnabled() )
1551           aViewWindow->SetSelectionMode(CellSelection);
1552     }
1553     GroupEdit->show();
1554     break;
1555   }
1556   updateControls();
1557
1558   qApp->processEvents();
1559   updateGeometry();
1560   resize(10,10);
1561
1562   SelectionIntoArgument();
1563 }
1564
1565 //=======================================================================
1566 //function : isKeepNodesIDsSelection
1567 //purpose  : Return true of Nodes to keep are selected by IDs
1568 //=======================================================================
1569
1570 bool SMESHGUI_MergeDlg::isKeepNodesIDsSelection()
1571 {
1572   return KeepFromButGroup && KeepFromButGroup->checkedId() == 0;
1573 }
1574
1575 //=======================================================================
1576 //function : isNewKeepNodesGroup
1577 //purpose  : Return true if an object with given entry is NOT present in KeepList
1578 //=======================================================================
1579
1580 bool SMESHGUI_MergeDlg::isNewKeepNodesGroup( const char* entry )
1581 {
1582   if ( !entry || isKeepNodesIDsSelection() )
1583     return false;
1584
1585   for ( int i = 0; i < KeepList->count(); i++ )
1586     if ( KeepList->item( i )->data( Qt::UserRole ).toString() == entry )
1587       return false;
1588
1589   return true;
1590 }
1591
1592 //=======================================================================
1593 //function : onAddKeepNode
1594 //purpose  : SLOT called when [Add] of Nodes To Keep group is pressed
1595 //=======================================================================
1596
1597 void SMESHGUI_MergeDlg::onAddKeepNode()
1598 {
1599   if ( myIsBusy )
1600     return;
1601   myIsBusy = true;
1602
1603   if ( isKeepNodesIDsSelection() )
1604   {
1605     //KeepList->clearSelection();
1606     QString anIDs = "";
1607     int aNbNodes = 0;
1608     if ( myActor )
1609       aNbNodes = SMESH::GetNameOfSelectedNodes(mySelector, myActor->getIO(), anIDs);
1610     if (aNbNodes > 0)
1611     {
1612       QStringList anNodes = anIDs.split( " ", QString::SkipEmptyParts);
1613       QList<QListWidgetItem*> listItemsToSel;
1614       QListWidgetItem* anItem;
1615       for (QStringList::iterator it = anNodes.begin(); it != anNodes.end(); ++it)
1616       {
1617         QList<QListWidgetItem*> found = KeepList->findItems(*it, Qt::MatchExactly);
1618         if (found.count() == 0) {
1619           anItem = new QListWidgetItem(*it);
1620           KeepList->addItem(anItem);
1621           if (!anItem->isSelected())
1622             listItemsToSel.push_back(anItem);
1623         }
1624         else {
1625           foreach(anItem, found)
1626             if (!anItem->isSelected())
1627               listItemsToSel.push_back(anItem);
1628         }
1629       }
1630       bool blocked = KeepList->signalsBlocked();
1631       KeepList->blockSignals(true);
1632       foreach(anItem, listItemsToSel) anItem->setSelected(true);
1633       KeepList->blockSignals(blocked);
1634       //onSelectKeepNode();
1635     }
1636     RemoveKeepNodesButton->setEnabled( aNbNodes > 0 );
1637   }
1638   else
1639   {
1640     SALOME_ListIO aList;
1641     mySelectionMgr->selectedObjects(aList);
1642     SALOME_ListIteratorOfListIO anIt (aList);
1643     for ( ; anIt.More(); anIt.Next()) {
1644       Handle(SALOME_InteractiveObject) anIO = anIt.Value();
1645       if ( isNewKeepNodesGroup( anIO->getEntry() ))
1646       {
1647         QListWidgetItem* anItem = new QListWidgetItem( anIO->getName() );
1648         anItem->setData( Qt::UserRole, QString( anIO->getEntry() ));
1649         KeepList->addItem(anItem);
1650       }
1651     }
1652     //RemoveKeepNodesButton->setEnabled( KeepList->selectedItems().count() );
1653   }
1654
1655   AddKeepNodesButton->setEnabled( false );
1656
1657   myIsBusy = false;
1658 }
1659
1660 //=======================================================================
1661 //function : onRemoveKeepNode
1662 //purpose  : SLOT called when [Remove] of Nodes To Keep group is pressed
1663 //=======================================================================
1664
1665 void SMESHGUI_MergeDlg::onRemoveKeepNode()
1666 {
1667   // if ( isKeepNodesIDsSelection() )
1668   // {
1669   // }
1670   // else
1671   {
1672     QList<QListWidgetItem*> selItems = KeepList->selectedItems();
1673     QListWidgetItem* item;
1674     foreach(item, selItems) delete item;
1675   }
1676   if ( isKeepNodesIDsSelection() )
1677   {
1678     AddKeepNodesButton->setEnabled( false );
1679   }
1680   RemoveKeepNodesButton->setEnabled( false );
1681 }
1682
1683 //=======================================================================
1684 //function : onSelectKeepNode
1685 //purpose  : SLOT called when selection in KeepList changes
1686 //=======================================================================
1687
1688 void SMESHGUI_MergeDlg::onSelectKeepNode()
1689 {
1690   if ( myIsBusy || !isEnabled() ) return;
1691   myIsBusy = true;
1692
1693   if ( isKeepNodesIDsSelection() )
1694   {
1695     if ( myActor )
1696     {
1697       mySelectionMgr->clearSelected();
1698       TColStd_MapOfInteger aIndexes;
1699       QList<QListWidgetItem*> selItems = KeepList->selectedItems();
1700       QListWidgetItem* anItem;
1701       foreach(anItem, selItems) aIndexes.Add(anItem->text().toInt());
1702       mySelector->AddOrRemoveIndex(myActor->getIO(), aIndexes, false);
1703       SALOME_ListIO aList;
1704       aList.Append(myActor->getIO());
1705       mySelectionMgr->setSelectedObjects(aList,false);
1706
1707       AddKeepNodesButton->setEnabled( false );
1708       RemoveKeepNodesButton->setEnabled( aIndexes.Extent() > 0 );
1709     }
1710   }
1711   else
1712   {
1713     RemoveKeepNodesButton->setEnabled( KeepList->selectedItems().count() );
1714   }
1715   myIsBusy = false;
1716 }
1717
1718 //=======================================================================
1719 //function : onKeepNodeSourceChanged
1720 //purpose  : SLOT called when type of source of Nodes To Keep change from
1721 //           IDs to groups or vice versa
1722 //=======================================================================
1723
1724 void SMESHGUI_MergeDlg::onKeepNodeSourceChanged(int isGroup)
1725 {
1726   KeepList->clear();
1727   SelectKeepNodesButton->click();
1728 }