Salome HOME
Merge branch 'occ/24009'
[modules/smesh.git] / src / SMESHGUI / SMESHGUI_SingleEditDlg.cxx
1 // Copyright (C) 2007-2021  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 // File   : SMESHGUI_SingleEditDlg.cxx
24 // Author : Sergey LITONIN, Open CASCADE S.A.S.
25
26 #include <SVTK_Selector.h>
27
28 // SMESH includes
29 //
30 #include "SMESHGUI_SingleEditDlg.h"
31
32 #include "SMESHGUI.h"
33 #include "SMESHGUI_Utils.h"
34 #include "SMESHGUI_VTKUtils.h"
35 #include "SMESHGUI_MeshUtils.h"
36
37 #include <SMESH_Actor.h>
38 #include <SMDS_Mesh.hxx>
39
40 // SALOME GUI includes
41 #include <LightApp_SelectionMgr.h>
42 #include <LightApp_Application.h>
43 #include <SUIT_ResourceMgr.h>
44 #include <SUIT_MessageBox.h>
45 #include <SUIT_Desktop.h>
46 #include <SUIT_Session.h>
47
48 #include <SVTK_ViewWindow.h>
49 #include <SALOME_ListIO.hxx>
50
51 // OCCT includes
52 #include <TColStd_MapOfInteger.hxx>
53 #include <TColStd_IndexedMapOfInteger.hxx>
54
55 // Qt includes
56 #include <QVBoxLayout>
57 #include <QHBoxLayout>
58 #include <QLineEdit>
59 #include <QPushButton>
60 #include <QGroupBox>
61 #include <QLabel>
62 #include <QValidator>
63 #include <QKeyEvent>
64
65 #define SPACING 6
66 #define MARGIN  11
67
68 /*!
69   \class BusyLocker
70   \brief Simple 'busy state' flag locker.
71   \internal
72 */
73
74 class BusyLocker
75 {
76 public:
77   //! Constructor. Sets passed boolean flag to \c true.
78   BusyLocker( bool& busy ) : myBusy( busy ) { myBusy = true; }
79   //! Destructor. Clear external boolean flag passed as parameter to the constructor to \c false.
80   ~BusyLocker() { myBusy = false; }
81 private:
82   bool& myBusy; //! External 'busy state' boolean flag
83 };
84
85 /*!
86  *  Class       : SMESHGUI_SingleEditDlg
87  *  Description : Inversion of the diagonal of a pseudo-quadrangle formed by
88  *                2 neighboring triangles with 1 common edge
89  */
90
91 //=======================================================================
92 // name    : SMESHGUI_SingleEditDlg()
93 // Purpose : Constructor
94 //=======================================================================
95 SMESHGUI_SingleEditDlg
96 ::SMESHGUI_SingleEditDlg(SMESHGUI* theModule)
97   : QDialog(SMESH::GetDesktop(theModule)),
98     mySelectionMgr(SMESH::GetSelectionMgr(theModule)),
99     mySelector(SMESH::GetViewWindow(theModule)->GetSelector()),
100     mySMESHGUI(theModule)
101 {
102   setModal(false);
103
104   QVBoxLayout* aDlgLay = new QVBoxLayout(this);
105   aDlgLay->setMargin(MARGIN);
106   aDlgLay->setSpacing(SPACING);
107
108   QWidget* aMainFrame = createMainFrame  (this);
109   QWidget* aBtnFrame  = createButtonFrame(this);
110
111   aDlgLay->addWidget(aMainFrame);
112   aDlgLay->addWidget(aBtnFrame);
113
114   Init();
115 }
116
117 //=======================================================================
118 // name    : createMainFrame()
119 // Purpose : Create frame containing dialog's input fields
120 //=======================================================================
121 QWidget* SMESHGUI_SingleEditDlg::createMainFrame (QWidget* theParent)
122 {
123   QGroupBox* aMainGrp = new QGroupBox(tr("EDGE_BETWEEN"), theParent);
124   QHBoxLayout* aLay = new QHBoxLayout(aMainGrp);
125   aLay->setMargin(MARGIN);
126   aLay->setSpacing(SPACING);
127
128   QPixmap aPix (SMESH::GetResourceMgr( mySMESHGUI )->loadPixmap("SMESH", tr("ICON_SELECT")));
129
130   QLabel* aLab = new QLabel(tr("SMESH_EDGE"), aMainGrp);
131   QPushButton* aBtn = new QPushButton(aMainGrp);
132   aBtn->setIcon(aPix);
133   myEdge = new QLineEdit(aMainGrp);
134   myEdge->setValidator(new QRegExpValidator(QRegExp("[\\d]*-[\\d]*"), this));
135
136   aLay->addWidget(aLab);
137   aLay->addWidget(aBtn);
138   aLay->addWidget(myEdge);
139
140   return aMainGrp;
141 }
142
143 //=======================================================================
144 // name    : createButtonFrame()
145 // Purpose : Create frame containing buttons
146 //=======================================================================
147 QWidget* SMESHGUI_SingleEditDlg::createButtonFrame (QWidget* theParent)
148 {
149   QGroupBox* aFrame = new QGroupBox(theParent);
150
151   myOkBtn     = new QPushButton(tr("SMESH_BUT_APPLY_AND_CLOSE"), aFrame);
152   myApplyBtn  = new QPushButton(tr("SMESH_BUT_APPLY"), aFrame);
153   myCloseBtn  = new QPushButton(tr("SMESH_BUT_CLOSE"), aFrame);
154   myHelpBtn   = new QPushButton(tr("SMESH_BUT_HELP"),  aFrame);
155
156   QHBoxLayout* aLay = new QHBoxLayout(aFrame);
157   aLay->setMargin(MARGIN);
158   aLay->setSpacing(SPACING);
159
160   aLay->addWidget(myOkBtn);
161   aLay->addSpacing(10);
162   aLay->addWidget(myApplyBtn);
163   aLay->addSpacing(10);
164   aLay->addStretch();
165   aLay->addWidget(myCloseBtn);
166   aLay->addWidget(myHelpBtn);
167
168   return aFrame;
169 }
170
171 //=======================================================================
172 // name    : isValid()
173 // Purpose : Verify validity of input data
174 //=======================================================================
175 bool SMESHGUI_SingleEditDlg::isValid (const bool /*theMess*/) const
176 {
177   int id1, id2;
178   return getNodeIds(myEdge->text(), id1, id2);
179 }
180
181 //=======================================================================
182 // name    : getNodeIds()
183 // Purpose : Retrieve node ids from string
184 //=======================================================================
185 bool SMESHGUI_SingleEditDlg::getNodeIds (const QString& theStr,
186                                          int& theId1, int&  theId2) const
187 {
188   if (!theStr.contains('-'))
189     return false;
190
191   bool ok1, ok2;
192   QString str1 = theStr.section('-', 0, 0, QString::SectionSkipEmpty);
193   QString str2 = theStr.section('-', 1, 1, QString::SectionSkipEmpty);
194   theId1 = str1.toInt(&ok1);
195   theId2 = str2.toInt(&ok2);
196
197   return ok1 & ok2;
198 }
199
200 //=======================================================================
201 // name    : ~SMESHGUI_SingleEditDlg()
202 // Purpose : Destructor
203 //=======================================================================
204 SMESHGUI_SingleEditDlg::~SMESHGUI_SingleEditDlg()
205 {
206 }
207
208 //=======================================================================
209 // name    : Init()
210 // Purpose : Init dialog fields, connect signals and slots, show dialog
211 //=======================================================================
212 void SMESHGUI_SingleEditDlg::Init()
213 {
214   mySMESHGUI->SetActiveDialogBox((QDialog*)this);
215   myBusy = false;
216   myActor = 0;
217
218   // main buttons
219   connect(myOkBtn,    SIGNAL(clicked()), SLOT(onOk()));
220   connect(myCloseBtn, SIGNAL(clicked()), SLOT(reject()));
221   connect(myApplyBtn, SIGNAL(clicked()), SLOT(onApply()));
222   connect(myHelpBtn,  SIGNAL(clicked()), SLOT(onHelp()));
223
224   // selection and SMESHGUI
225   connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), SLOT(onSelectionDone()));
226   connect(mySMESHGUI, SIGNAL(SignalDeactivateActiveDialog()), SLOT(onDeactivate()));
227   connect(mySMESHGUI, SIGNAL(SignalCloseAllDialogs()), SLOT(reject()));
228   connect(mySMESHGUI, SIGNAL(SignalActivatedViewManager()), SLOT(onOpenView()));
229   connect(mySMESHGUI, SIGNAL(SignalCloseView()),            SLOT(onCloseView()));
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   // set selection mode
237   if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
238     aViewWindow->SetSelectionMode(EdgeOfCellSelection);
239
240   onSelectionDone();
241 }
242
243 //=======================================================================
244 // name    : onOk()
245 // Purpose : SLOT called when "Ok" button pressed.
246 //           Assign filters VTK viewer and close dialog
247 //=======================================================================
248 void SMESHGUI_SingleEditDlg::onOk()
249 {
250   if (onApply())
251     reject();
252 }
253
254 //=======================================================================
255 // name    : reject()
256 // Purpose : SLOT called when "Close" button pressed. Close dialog
257 //=======================================================================
258 void SMESHGUI_SingleEditDlg::reject()
259 {
260   if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
261     aViewWindow->SetSelectionMode(ActorSelection);
262   //mySelectionMgr->clearSelected();
263   disconnect(mySelectionMgr, 0, this, 0);
264   disconnect(mySMESHGUI, 0, this, 0);
265   mySMESHGUI->ResetState();
266   QDialog::reject();
267 }
268
269 //=================================================================================
270 // function : onHelp()
271 // purpose  :
272 //=================================================================================
273 void SMESHGUI_SingleEditDlg::onHelp()
274 {
275   LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication());
276   if (app) 
277     app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), myHelpFileName);
278   else {
279     QString platform;
280 #ifdef WIN32
281     platform = "winapplication";
282 #else
283     platform = "application";
284 #endif
285     SUIT_MessageBox::warning(this, tr("WRN_WARNING"),
286                              tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE").
287                              arg(app->resourceMgr()->stringValue("ExternalBrowser", 
288                                                                  platform)).
289                              arg(myHelpFileName));
290   }
291 }
292
293 //=======================================================================
294 //function : findTriangles()
295 //purpose  : find triangles sharing theNode1-theNode2 link
296 //           THIS IS A PIECE OF SMESH_MeshEditor.cxx
297 //           TO DO: make it available in SMDS for ex.
298 //=======================================================================
299 static bool findTriangles (const SMDS_MeshNode *    theNode1,
300                            const SMDS_MeshNode *    theNode2,
301                            const SMDS_MeshElement*& theTria1,
302                            const SMDS_MeshElement*& theTria2)
303 {
304   if (!theNode1 || !theNode2) return false;
305
306   theTria1 = theTria2 = 0;
307
308   std::set< const SMDS_MeshElement* > emap;
309   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator();
310   while (it->more()) {
311     const SMDS_MeshElement* elem = it->next();
312     if (elem->GetType() == SMDSAbs_Face && elem->NbCornerNodes() == 3)
313       emap.insert(elem);
314   }
315   it = theNode2->GetInverseElementIterator();
316   while (it->more()) {
317     const SMDS_MeshElement* elem = it->next();
318     if (elem->GetType() == SMDSAbs_Face &&
319          emap.find(elem) != emap.end())
320     {
321       if (theTria1) {
322         theTria2 = elem;
323         break;
324       } else {
325         theTria1 = elem;
326       }
327     }
328   }
329   return (theTria1 && theTria2);
330 }
331
332 //=======================================================================
333 //function : onTextChange()
334 //purpose  :
335 //=======================================================================
336 void SMESHGUI_SingleEditDlg::onTextChange (const QString& /*theNewText*/)
337 {
338   if (myBusy) return;
339   BusyLocker lock(myBusy);
340
341   myOkBtn->setEnabled(false);
342   myApplyBtn->setEnabled(false);
343
344   // highlight entered edge
345   if(myActor){
346     if(SMDS_Mesh* aMesh = myActor->GetObject()->GetMesh()){
347       Handle(SALOME_InteractiveObject) anIO = myActor->getIO();
348       SALOME_ListIO aList;
349       aList.Append(anIO);
350       mySelectionMgr->setSelectedObjects(aList,false);
351       
352       SVTK_IndexedMapOfIds selectedIndices;
353       SVTK_ListOfInteger newIndices;
354       mySelector->GetCompositeIndex(anIO,selectedIndices);
355
356       int id1, id2;
357       if ( !getNodeIds(myEdge->text(), id1, id2) )
358         return;
359
360       const SMDS_MeshNode* aNode1 = aMesh->FindNode( id1 );
361       const SMDS_MeshNode* aNode2 = aMesh->FindNode( id2 );
362
363       if ( !aNode1 || !aNode2 || aNode1 == aNode2 )
364         return;
365
366       // find a triangle and an edge index
367       const SMDS_MeshElement* tria1;
368       const SMDS_MeshElement* tria2;
369
370       if ( findTriangles(aNode1,aNode2,tria1,tria2) )
371       {
372         newIndices.push_back( aNode1->GetID() );
373         newIndices.push_back( aNode2->GetID() );
374
375         myOkBtn->setEnabled(true);
376         myApplyBtn->setEnabled(true);
377       }
378       mySelector->AddOrRemoveCompositeIndex(anIO, newIndices, false);
379       SMESH::GetViewWindow(mySMESHGUI)->highlight( anIO, true, true );
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   BusyLocker lock(myBusy);
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     {
412       const SMDS_MeshElement* tria[2];
413
414       bool valid = false;
415       SVTK_IndexedMapOfIds anIds;
416       mySelector->GetCompositeIndex(anIO,anIds);
417       if( anIds.Extent() == 1 && anIds(1).size() == 2 ) {
418         anId1 = anIds(1)[0];
419         anId2 = anIds(1)[1];
420         valid = true;
421       }
422
423       if( valid &&
424           findTriangles( aMesh->FindNode( anId1 ), aMesh->FindNode( anId2 ), tria[0],tria[1] ) )
425       {
426         QString aText = QString("%1-%2").arg(anId1).arg(anId2);
427         myEdge->setText(aText);
428
429         myOkBtn->setEnabled(true);
430         myApplyBtn->setEnabled(true);
431       }
432       else
433       {
434         myEdge->clear();
435       }
436     }
437   }
438 }
439
440 //=======================================================================
441 // name    : onDeactivate()
442 // Purpose : SLOT called when dialog must be deactivated
443 //=======================================================================
444 void SMESHGUI_SingleEditDlg::onDeactivate()
445 {
446   setEnabled(false);
447 }
448
449 //=================================================================================
450 // function : onOpenView()
451 // purpose  :
452 //=================================================================================
453 void SMESHGUI_SingleEditDlg::onOpenView()
454 {
455   if ( !mySelector ) {
456     mySelector = SMESH::GetViewWindow( mySMESHGUI )->GetSelector();
457     mySMESHGUI->EmitSignalDeactivateDialog();
458     setEnabled(true);
459   }
460 }
461
462 //=================================================================================
463 // function : onCloseView()
464 // purpose  :
465 //=================================================================================
466 void SMESHGUI_SingleEditDlg::onCloseView()
467 {
468   onDeactivate();
469   mySelector = 0;
470 }
471
472 //=======================================================================
473 // name    : enterEvent()
474 // Purpose : Event filter
475 //=======================================================================
476 void SMESHGUI_SingleEditDlg::enterEvent (QEvent*)
477 {
478   if (!isEnabled()) {
479     mySMESHGUI->EmitSignalDeactivateDialog();
480     SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI );
481     if ( aViewWindow) {
482       aViewWindow->SetSelectionMode(EdgeOfCellSelection);
483       if (!mySelector)
484         mySelector = aViewWindow->GetSelector();
485     }
486     setEnabled(true);
487   }
488 }
489
490 //=================================================================================
491 // function : onApply()
492 // purpose  : SLOT. Called when apply button is pressed
493 //=================================================================================
494 bool SMESHGUI_SingleEditDlg::onApply()
495 {
496   if (SMESHGUI::isStudyLocked())
497     return false;
498   // verify validity of input data
499   if (!isValid(true))
500     return false;
501
502   // get mesh, actor and nodes
503   SALOME_ListIO aList;
504   mySelectionMgr->selectedObjects(aList);
505
506   SMESH::SMESH_Mesh_var aMesh = SMESH::GetMeshByIO(aList.First());
507
508   if (aMesh->_is_nil()) {
509     SUIT_MessageBox::information(SMESH::GetDesktop(mySMESHGUI), 
510                                  tr("SMESH_ERROR"),
511                                  tr("SMESHG_NO_MESH"));
512     return false;
513   }
514
515   SMESH::SMESH_MeshEditor_var aMeshEditor = aMesh->GetMeshEditor();
516   int anId1= 0, anId2 = 0;
517   if (aMeshEditor->_is_nil() || !getNodeIds(myEdge->text(), anId1, anId2))
518     return false;
519
520   // perform operation
521   bool aResult = process(aMeshEditor.in(), anId1, anId2);
522
523   // update actor
524   if (aResult) {
525     mySelector->ClearIndex();
526     mySelector->ClearCompositeIndex();
527     mySelectionMgr->setSelectedObjects(aList, false);
528     onSelectionDone();
529     SMESH::UpdateView();
530     SMESHGUI::Modified();
531   }
532
533   return aResult;
534 }
535
536 //=================================================================================
537 // function : keyPressEvent()
538 // purpose  :
539 //=================================================================================
540 void SMESHGUI_SingleEditDlg::keyPressEvent( QKeyEvent* e )
541 {
542   QDialog::keyPressEvent( e );
543   if ( e->isAccepted() )
544     return;
545
546   if ( e->key() == Qt::Key_F1 ) {
547     e->accept();
548     onHelp();
549   }
550 }
551
552 /*!
553  *  Class       : SMESHGUI_TrianglesInversionDlg
554  *  Description : Inversion of the diagonal of a pseudo-quadrangle formed by
555  *                2 neighboring triangles with 1 common edge
556  */
557
558 SMESHGUI_TrianglesInversionDlg
559 ::SMESHGUI_TrianglesInversionDlg(SMESHGUI* theModule)
560 : SMESHGUI_SingleEditDlg(theModule)
561 {
562   setWindowTitle(tr("CAPTION"));
563   myHelpFileName = "diagonal_inversion_of_elements.html";
564 }
565
566 SMESHGUI_TrianglesInversionDlg::~SMESHGUI_TrianglesInversionDlg()
567 {
568 }
569
570 bool SMESHGUI_TrianglesInversionDlg::process (SMESH::SMESH_MeshEditor_ptr theMeshEditor,
571                                               const int theId1, const int theId2)
572 {
573   return theMeshEditor->InverseDiag(theId1, theId2);
574 }
575
576 /*!
577  *  Class       : SMESHGUI_UnionOfTwoTrianglesDlg
578  *  Description : Construction of a quadrangle by deletion of the
579  *                common border of 2 neighboring triangles
580  */
581
582 SMESHGUI_UnionOfTwoTrianglesDlg
583 ::SMESHGUI_UnionOfTwoTrianglesDlg(SMESHGUI* theModule)
584 : SMESHGUI_SingleEditDlg(theModule)
585 {
586   setWindowTitle(tr("CAPTION"));
587   myHelpFileName = "uniting_two_triangles.html";
588 }
589
590 SMESHGUI_UnionOfTwoTrianglesDlg::~SMESHGUI_UnionOfTwoTrianglesDlg()
591 {
592 }
593
594 bool SMESHGUI_UnionOfTwoTrianglesDlg::process (SMESH::SMESH_MeshEditor_ptr theMeshEditor,
595                                                const int theId1, const int theId2)
596 {
597   return theMeshEditor->DeleteDiag(theId1, theId2);
598 }