Salome HOME
new script for testing hypothesis "NbSegments" (PAL8238)
[modules/smesh.git] / src / SMESHGUI / SMESHGUI_SingleEditDlg.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_SingleEditDlg.cxx
25 //  Author : Sergey LITONIN
26 //  Module : SMESH
27
28 #include "SMESHGUI_SingleEditDlg.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 "SMDS_Mesh.hxx"
38
39 #include "SalomeApp_SelectionMgr.h"
40 #include "SUIT_ResourceMgr.h"
41 #include "SUIT_MessageBox.h"
42 #include "SUIT_Desktop.h"
43
44 #include "SVTK_Selector.h"
45 #include "SVTK_ViewWindow.h"
46 #include "SALOME_ListIO.hxx"
47
48 #include "utilities.h"
49
50 // OCCT Includes
51 #include <TColStd_MapOfInteger.hxx>
52 #include <TColStd_IndexedMapOfInteger.hxx>
53
54 // QT Includes
55 #include <qframe.h>
56 #include <qlayout.h>
57 #include <qlineedit.h>
58 #include <qpushbutton.h>
59 #include <qgroupbox.h>
60 #include <qlabel.h>
61 #include <qmessagebox.h>
62 #include <qvalidator.h>
63
64
65 #define SPACING 5
66 #define MARGIN  10
67
68
69 /*!
70  *  Class       : SMESHGUI_DiagValidator
71  *  Description : validate munual input of edge like "id1-id2"
72  */
73 class SMESHGUI_DiagValidator: public QValidator
74 {
75  public:
76   SMESHGUI_DiagValidator (QWidget * parent, const char * name = 0):
77     QValidator(parent,name) {}
78
79   State validate (QString & text, int & pos) const
80   {
81     text.stripWhiteSpace();
82     text.replace(QRegExp("[^0-9]+"), "-");
83     if (text == "-")
84       text = "";
85     int ind = text.find(QRegExp("-[0-9]+-"));
86     if (ind > 0) { // leave only two ids
87       ind = text.find('-', ind + 1);
88       if (ind > 0)
89         text.truncate(ind);
90     }
91     if (pos > text.length())
92       pos = text.length();
93     return Acceptable;
94   }
95 };
96
97 /*!
98  *  Class       : SMESHGUI_SingleEditDlg
99  *  Description : Inversion of the diagonal of a pseudo-quadrangle formed by
100  *                2 neighboring triangles with 1 common edge
101  */
102
103 //=======================================================================
104 // name    : SMESHGUI_SingleEditDlg()
105 // Purpose : Constructor
106 //=======================================================================
107 SMESHGUI_SingleEditDlg
108 ::SMESHGUI_SingleEditDlg(SMESHGUI* theModule, 
109                          const char* theName):
110   QDialog(SMESH::GetDesktop(theModule), 
111           theName, 
112           false, 
113           WStyle_Customize | WStyle_NormalBorder | WStyle_Title | WStyle_SysMenu),
114     mySelector(SMESH::GetViewWindow(theModule)->GetSelector()),
115     mySelectionMgr(SMESH::GetSelectionMgr(theModule)),
116     mySMESHGUI(theModule)
117 {
118   QVBoxLayout* aDlgLay = new QVBoxLayout(this, MARGIN, SPACING);
119
120   QFrame* aMainFrame = createMainFrame  (this);
121   QFrame* aBtnFrame  = createButtonFrame(this);
122
123   aDlgLay->addWidget(aMainFrame);
124   aDlgLay->addWidget(aBtnFrame);
125
126   aDlgLay->setStretchFactor(aMainFrame, 1);
127
128   Init();
129 }
130
131 //=======================================================================
132 // name    : createMainFrame()
133 // Purpose : Create frame containing dialog's input fields
134 //=======================================================================
135 QFrame* SMESHGUI_SingleEditDlg::createMainFrame (QWidget* theParent)
136 {
137   QGroupBox* aMainGrp = new QGroupBox(1, Qt::Vertical, tr("EDGE_BETWEEN"), theParent);
138
139   QPixmap aPix (SMESH::GetResourceMgr( mySMESHGUI )->loadPixmap("SMESH", tr("ICON_SELECT")));
140
141   new QLabel(tr("SMESH_EDGE"), aMainGrp);
142   (new QPushButton(aMainGrp))->setPixmap(aPix);
143   myEdge = new QLineEdit(aMainGrp);
144   myEdge->setValidator(new SMESHGUI_DiagValidator(this, "validator"));
145
146   return aMainGrp;
147 }
148
149 //=======================================================================
150 // name    : createButtonFrame()
151 // Purpose : Create frame containing buttons
152 //=======================================================================
153 QFrame* SMESHGUI_SingleEditDlg::createButtonFrame (QWidget* theParent)
154 {
155   QFrame* aFrame = new QFrame(theParent);
156   aFrame->setFrameStyle(QFrame::Box | QFrame::Sunken);
157
158   myOkBtn     = new QPushButton(tr("SMESH_BUT_OK"   ), aFrame);
159   myApplyBtn  = new QPushButton(tr("SMESH_BUT_APPLY"), aFrame);
160   myCloseBtn  = new QPushButton(tr("SMESH_BUT_CLOSE"), aFrame);
161
162   QSpacerItem* aSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum);
163
164   QHBoxLayout* aLay = new QHBoxLayout(aFrame, MARGIN, SPACING);
165
166   aLay->addWidget(myOkBtn);
167   aLay->addWidget(myApplyBtn);
168   aLay->addItem(aSpacer);
169   aLay->addWidget(myCloseBtn);
170
171   return aFrame;
172 }
173
174 //=======================================================================
175 // name    : isValid()
176 // Purpose : Verify validity of input data
177 //=======================================================================
178 bool SMESHGUI_SingleEditDlg::isValid (const bool theMess) const
179 {
180   int id1, id2;
181   return getNodeIds(myEdge->text(), id1, id2);
182 }
183
184 //=======================================================================
185 // name    : getNodeIds()
186 // Purpose : Retrieve node ids from string
187 //=======================================================================
188 bool SMESHGUI_SingleEditDlg::getNodeIds (const QString& theStr,
189                                          int& theId1, int&  theId2) const
190 {
191   if (!theStr.contains('-'))
192     return false;
193
194   bool ok1, ok2;
195   QString str1 = theStr.section('-', 0, 0, QString::SectionSkipEmpty);
196   QString str2 = theStr.section('-', 1, 1, QString::SectionSkipEmpty);
197   theId1 = str1.toInt(&ok1);
198   theId2 = str2.toInt(&ok2);
199
200   return ok1 & ok2;
201 }
202
203 //=======================================================================
204 // name    : ~SMESHGUI_SingleEditDlg()
205 // Purpose : Destructor
206 //=======================================================================
207 SMESHGUI_SingleEditDlg::~SMESHGUI_SingleEditDlg()
208 {
209 }
210
211 //=======================================================================
212 // name    : Init()
213 // Purpose : Init dialog fields, connect signals and slots, show dialog
214 //=======================================================================
215 void SMESHGUI_SingleEditDlg::Init()
216 {
217   mySMESHGUI->SetActiveDialogBox((QDialog*)this);
218   myBusy = false;
219   myActor = 0;
220
221   // main buttons
222   connect(myOkBtn,    SIGNAL(clicked()), SLOT(onOk()));
223   connect(myCloseBtn, SIGNAL(clicked()), SLOT(onClose()));
224   connect(myApplyBtn, SIGNAL(clicked()), SLOT(onApply()));
225
226   // selection and SMESHGUI
227   connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), SLOT(onSelectionDone()));
228   connect(mySMESHGUI, SIGNAL(SignalDeactivateActiveDialog()), SLOT(onDeactivate()));
229   connect(mySMESHGUI, SIGNAL(SignalCloseAllDialogs()), SLOT(onClose()));
230   connect(myEdge, SIGNAL(textChanged(const QString&)), SLOT(onTextChange(const QString&)));
231
232   myOkBtn->setEnabled(false);
233   myApplyBtn->setEnabled(false);
234   setEnabled(true);
235
236   int x, y;
237   mySMESHGUI->DefineDlgPosition(this, x, y);
238   this->move(x, y);
239   this->show();
240
241   // set selection mode
242   if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
243     aViewWindow->SetSelectionMode(EdgeOfCellSelection);
244
245   onSelectionDone();
246 }
247
248 //=======================================================================
249 // name    : onOk()
250 // Purpose : SLOT called when "Ok" button pressed.
251 //           Assign filters VTK viewer and close dialog
252 //=======================================================================
253 void SMESHGUI_SingleEditDlg::onOk()
254 {
255   if (onApply())
256     onClose();
257 }
258
259 //=======================================================================
260 // name    : onClose()
261 // Purpose : SLOT called when "Close" button pressed. Close dialog
262 //=======================================================================
263 void SMESHGUI_SingleEditDlg::onClose()
264 {
265   if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
266     aViewWindow->SetSelectionMode(ActorSelection);
267   mySelectionMgr->clearSelected();
268   disconnect(mySelectionMgr, 0, this, 0);
269   disconnect(mySMESHGUI, 0, this, 0);
270   mySMESHGUI->ResetState();
271   reject();
272 }
273
274 //=======================================================================
275 //function : findTriangles()
276 //purpose  : find triangles sharing theNode1-theNode2 link
277 //           THIS IS A PIECE OF SMESH_MeshEditor.cxx
278 //           TO DO: make it available in SMDS for ex.
279 //=======================================================================
280 static bool findTriangles (const SMDS_MeshNode *    theNode1,
281                            const SMDS_MeshNode *    theNode2,
282                            const SMDS_MeshElement*& theTria1,
283                            const SMDS_MeshElement*& theTria2)
284 {
285   if (!theNode1 || !theNode2) return false;
286
287   theTria1 = theTria2 = 0;
288
289   set< const SMDS_MeshElement* > emap;
290   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator();
291   while (it->more()) {
292     const SMDS_MeshElement* elem = it->next();
293     if (elem->GetType() == SMDSAbs_Face && elem->NbNodes() == 3)
294       emap.insert(elem);
295   }
296   it = theNode2->GetInverseElementIterator();
297   while (it->more()) {
298     const SMDS_MeshElement* elem = it->next();
299     if (elem->GetType() == SMDSAbs_Face &&
300          emap.find(elem) != emap.end())
301       if (theTria1) {
302         theTria2 = elem;
303         break;
304       } else {
305         theTria1 = elem;
306       }
307   }
308   return (theTria1 && theTria2);
309 }
310
311 //=======================================================================
312 //function : onTextChange()
313 //purpose  :
314 //=======================================================================
315 void SMESHGUI_SingleEditDlg::onTextChange (const QString& theNewText)
316 {
317   if (myBusy) return;
318
319   myOkBtn->setEnabled(false);
320   myApplyBtn->setEnabled(false);
321
322   // hilight entered edge
323   if(myActor){
324     if(SMDS_Mesh* aMesh = myActor->GetObject()->GetMesh()){
325       myBusy = true; // block onSelectionDone()
326       Handle(SALOME_InteractiveObject) anIO = myActor->getIO();
327       SALOME_ListIO aList;
328       aList.Append(anIO);
329       mySelectionMgr->setSelectedObjects(aList,false);
330       
331       TColStd_IndexedMapOfInteger selectedIndices;
332       TColStd_MapOfInteger newIndices;
333       mySelector->GetIndex(anIO,selectedIndices);
334       myBusy = false;
335
336       QStringList aListId = QStringList::split("-", theNewText, false);
337       if (aListId.count() != 2)
338         return;
339
340       int i;
341       bool allOk = true;
342       const SMDS_MeshNode* a2Nodes[2];
343       for (i = 0; i < aListId.count(); i++) {
344         if(const SMDS_MeshNode *aNode = aMesh->FindNode(aListId[ i ].toInt()))
345           a2Nodes[ i ] = aNode;
346         else
347           allOk = false;
348       }
349       
350       // find a triangle and an edge nb
351       const SMDS_MeshElement* tria[2];
352       allOk &= a2Nodes[0] != a2Nodes[1] && findTriangles(a2Nodes[0],a2Nodes[1],tria[0],tria[1]);
353       if(allOk){
354         myBusy = true; // block onSelectionDone()
355         newIndices.Add(tria[0]->GetID());
356         mySelector->AddOrRemoveIndex(anIO,newIndices, true);
357
358         const SMDS_MeshNode* a3Nodes [3];
359         SMDS_ElemIteratorPtr it;
360         int edgeInd = 2;
361         for (i = 0, it = tria[0]->nodesIterator(); it->more(); i++) {
362           a3Nodes[ i ] = static_cast<const SMDS_MeshNode*>(it->next());
363           if (i > 0) {
364             allOk = (a3Nodes[ i ] == a2Nodes[ 0 ] && a3Nodes[ i - 1] == a2Nodes[ 1 ]) ||
365               (a3Nodes[ i ] == a2Nodes[ 1 ] && a3Nodes[ i - 1] == a2Nodes[ 0 ]);
366             if (allOk) {
367               edgeInd = i - 1;
368               break;
369             }
370           }
371         }
372
373         newIndices.Clear();
374         newIndices.Add(-edgeInd-1);
375         mySelector->AddOrRemoveIndex(anIO,newIndices,true);
376         myBusy = false;
377         
378         myOkBtn->setEnabled(true);
379         myApplyBtn->setEnabled(true);
380       }
381     }
382   }
383 }
384
385 //=======================================================================
386 // name    : onSelectionDone()
387 // Purpose : SLOT called when selection changed
388 //=======================================================================
389 void SMESHGUI_SingleEditDlg::onSelectionDone()
390 {
391   if (myBusy) return;
392
393   int anId1 = 0, anId2 = 0;
394
395   myOkBtn->setEnabled(false);
396   myApplyBtn->setEnabled(false);
397
398   SALOME_ListIO aList;
399   mySelectionMgr->selectedObjects(aList);
400
401   if (aList.Extent() != 1) {
402     myEdge->clear();
403     return;
404   }
405
406   Handle(SALOME_InteractiveObject) anIO = aList.First();
407   myActor = SMESH::FindActorByEntry(anIO->getEntry());
408   if(myActor){
409     TVisualObjPtr aVisualObj = myActor->GetObject();
410     if(SMDS_Mesh* aMesh = aVisualObj->GetMesh()){
411       if(SMESH::GetEdgeNodes(mySelector, aVisualObj, anId1, anId2) >= 1){
412         QString aText = QString("%1-%2").arg(anId1).arg(anId2);
413         myBusy = true;
414         myEdge->setText(aText);
415         myBusy = false;
416         
417         const SMDS_MeshElement* tria[2];
418         if (findTriangles(aMesh->FindNode(anId1), aMesh->FindNode(anId2), tria[0],tria[1])) {
419           myOkBtn->setEnabled(true);
420           myApplyBtn->setEnabled(true);
421         }
422       } else {
423         myEdge->clear();
424       }
425     }
426   }
427 }
428
429 //=======================================================================
430 // name    : onDeactivate()
431 // Purpose : SLOT called when dialog must be deativated
432 //=======================================================================
433 void SMESHGUI_SingleEditDlg::onDeactivate()
434 {
435   setEnabled(false);
436 }
437
438 //=======================================================================
439 // name    : enterEvent()
440 // Purpose : Event filter
441 //=======================================================================
442 void SMESHGUI_SingleEditDlg::enterEvent (QEvent*)
443 {
444   if (!isEnabled()) {
445     mySMESHGUI->EmitSignalDeactivateDialog();
446     if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
447       aViewWindow->SetSelectionMode(EdgeOfCellSelection);
448     setEnabled(true);
449   }
450 }
451
452 //=================================================================================
453 // function : closeEvent()
454 // purpose  :
455 //=================================================================================
456 void SMESHGUI_SingleEditDlg::closeEvent (QCloseEvent*)
457 {
458   onClose();
459 }
460
461 //=======================================================================
462 //function : hideEvent()
463 //purpose  : caused by ESC key
464 //=======================================================================
465 void SMESHGUI_SingleEditDlg::hideEvent (QHideEvent*)
466 {
467   if (!isMinimized())
468     onClose();
469 }
470
471 //=================================================================================
472 // function : onApply()
473 // purpose  : SLOT. Called when apply button is pressed
474 //=================================================================================
475 bool SMESHGUI_SingleEditDlg::onApply()
476 {
477   if (mySMESHGUI->isActiveStudyLocked())
478     return false;
479   // verify validity of input data
480   if (!isValid(true))
481     return false;
482
483   // get mesh, actor and nodes
484   SALOME_ListIO aList;
485   mySelectionMgr->selectedObjects(aList);
486
487   SMESH::SMESH_Mesh_var aMesh = SMESH::GetMeshByIO(aList.First());
488
489   if (aMesh->_is_nil()) {
490     SUIT_MessageBox::info1(SMESH::GetDesktop(mySMESHGUI), 
491                            tr("SMESH_ERROR"),
492                            tr("SMESHG_NO_MESH"), 
493                            tr("SMESH_BUT_OK"));
494     return false;
495   }
496
497   SMESH::SMESH_MeshEditor_var aMeshEditor = aMesh->GetMeshEditor();
498   int anId1= 0, anId2 = 0;
499   if (aMeshEditor->_is_nil() || !getNodeIds(myEdge->text(), anId1, anId2))
500     return false;
501
502   // perform operation
503   bool aResult = process(aMeshEditor.in(), anId1, anId2);
504
505   // update actor
506   if (aResult) {
507     mySelectionMgr->setSelectedObjects(aList, false);
508     SMESH::UpdateView();
509   }
510
511   return aResult;
512 }
513
514 /*!
515  *  Class       : SMESHGUI_TrianglesInversionDlg
516  *  Description : Inversion of the diagonal of a pseudo-quadrangle formed by
517  *                2 neighboring triangles with 1 common edge
518  */
519
520 SMESHGUI_TrianglesInversionDlg
521 ::SMESHGUI_TrianglesInversionDlg(SMESHGUI* theModule, 
522                                  const char* theName)
523 : SMESHGUI_SingleEditDlg(theModule,theName)
524 {
525   setCaption(tr("CAPTION"));
526 }
527
528 SMESHGUI_TrianglesInversionDlg::~SMESHGUI_TrianglesInversionDlg()
529 {
530 }
531
532 bool SMESHGUI_TrianglesInversionDlg::process (SMESH::SMESH_MeshEditor_ptr theMeshEditor,
533                                               const int theId1, const int theId2)
534 {
535   return theMeshEditor->InverseDiag(theId1, theId2);
536 }
537
538 /*!
539  *  Class       : SMESHGUI_UnionOfTwoTrianglesDlg
540  *  Description : Construction of a quadrangle by deletion of the
541  *                common border of 2 neighboring triangles
542  */
543
544 SMESHGUI_UnionOfTwoTrianglesDlg
545 ::SMESHGUI_UnionOfTwoTrianglesDlg(SMESHGUI* theModule, 
546                                   const char* theName)
547 : SMESHGUI_SingleEditDlg(theModule,theName)
548 {
549   setCaption(tr("CAPTION"));
550 }
551
552 SMESHGUI_UnionOfTwoTrianglesDlg::~SMESHGUI_UnionOfTwoTrianglesDlg()
553 {
554 }
555
556 bool SMESHGUI_UnionOfTwoTrianglesDlg::process (SMESH::SMESH_MeshEditor_ptr theMeshEditor,
557                                                const int theId1, const int theId2)
558 {
559   return theMeshEditor->DeleteDiag(theId1, theId2);
560 }