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