Salome HOME
Merge branch 'hydro/imps_2015' into V7_dev
[modules/smesh.git] / src / SMESHGUI / SMESHGUI_SingleEditDlg.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 // File   : SMESHGUI_SingleEditDlg.cxx
24 // Author : Sergey LITONIN, Open CASCADE S.A.S.
25 // SMESH includes
26 //
27 #include "SMESHGUI_SingleEditDlg.h"
28
29 #include "SMESHGUI.h"
30 #include "SMESHGUI_Utils.h"
31 #include "SMESHGUI_VTKUtils.h"
32 #include "SMESHGUI_MeshUtils.h"
33
34 #include <SMESH_Actor.h>
35 #include <SMDS_Mesh.hxx>
36
37 // SALOME GUI includes
38 #include <LightApp_SelectionMgr.h>
39 #include <LightApp_Application.h>
40 #include <SUIT_ResourceMgr.h>
41 #include <SUIT_MessageBox.h>
42 #include <SUIT_Desktop.h>
43 #include <SUIT_Session.h>
44
45 #include <SVTK_Selector.h>
46 #include <SVTK_ViewWindow.h>
47 #include <SALOME_ListIO.hxx>
48
49 // OCCT includes
50 #include <TColStd_MapOfInteger.hxx>
51 #include <TColStd_IndexedMapOfInteger.hxx>
52
53 // Qt includes
54 #include <QVBoxLayout>
55 #include <QHBoxLayout>
56 #include <QLineEdit>
57 #include <QPushButton>
58 #include <QGroupBox>
59 #include <QLabel>
60 #include <QValidator>
61 #include <QKeyEvent>
62
63 #define SPACING 6
64 #define MARGIN  11
65
66 /*!
67   \class BusyLocker
68   \brief Simple 'busy state' flag locker.
69   \internal
70 */
71
72 class BusyLocker
73 {
74 public:
75   //! Constructor. Sets passed boolean flag to \c true.
76   BusyLocker( bool& busy ) : myBusy( busy ) { myBusy = true; }
77   //! Destructor. Clear external boolean flag passed as parameter to the constructor to \c false.
78   ~BusyLocker() { myBusy = false; }
79 private:
80   bool& myBusy; //! External 'busy state' boolean flag
81 };
82
83 /*!
84  *  Class       : SMESHGUI_SingleEditDlg
85  *  Description : Inversion of the diagonal of a pseudo-quadrangle formed by
86  *                2 neighboring triangles with 1 common edge
87  */
88
89 //=======================================================================
90 // name    : SMESHGUI_SingleEditDlg()
91 // Purpose : Constructor
92 //=======================================================================
93 SMESHGUI_SingleEditDlg
94 ::SMESHGUI_SingleEditDlg(SMESHGUI* theModule)
95   : QDialog(SMESH::GetDesktop(theModule)),
96     mySelectionMgr(SMESH::GetSelectionMgr(theModule)),
97     mySelector(SMESH::GetViewWindow(theModule)->GetSelector()),
98     mySMESHGUI(theModule)
99 {
100   setModal(false);
101
102   QVBoxLayout* aDlgLay = new QVBoxLayout(this);
103   aDlgLay->setMargin(MARGIN);
104   aDlgLay->setSpacing(SPACING);
105
106   QWidget* aMainFrame = createMainFrame  (this);
107   QWidget* aBtnFrame  = createButtonFrame(this);
108
109   aDlgLay->addWidget(aMainFrame);
110   aDlgLay->addWidget(aBtnFrame);
111
112   Init();
113 }
114
115 //=======================================================================
116 // name    : createMainFrame()
117 // Purpose : Create frame containing dialog's input fields
118 //=======================================================================
119 QWidget* SMESHGUI_SingleEditDlg::createMainFrame (QWidget* theParent)
120 {
121   QGroupBox* aMainGrp = new QGroupBox(tr("EDGE_BETWEEN"), theParent);
122   QHBoxLayout* aLay = new QHBoxLayout(aMainGrp);
123   aLay->setMargin(MARGIN);
124   aLay->setSpacing(SPACING);
125
126   QPixmap aPix (SMESH::GetResourceMgr( mySMESHGUI )->loadPixmap("SMESH", tr("ICON_SELECT")));
127
128   QLabel* aLab = new QLabel(tr("SMESH_EDGE"), aMainGrp);
129   QPushButton* aBtn = new QPushButton(aMainGrp);
130   aBtn->setIcon(aPix);
131   myEdge = new QLineEdit(aMainGrp);
132   myEdge->setValidator(new QRegExpValidator(QRegExp("[\\d]*-[\\d]*"), this));
133
134   aLay->addWidget(aLab);
135   aLay->addWidget(aBtn);
136   aLay->addWidget(myEdge);
137
138   return aMainGrp;
139 }
140
141 //=======================================================================
142 // name    : createButtonFrame()
143 // Purpose : Create frame containing buttons
144 //=======================================================================
145 QWidget* SMESHGUI_SingleEditDlg::createButtonFrame (QWidget* theParent)
146 {
147   QGroupBox* aFrame = new QGroupBox(theParent);
148
149   myOkBtn     = new QPushButton(tr("SMESH_BUT_APPLY_AND_CLOSE"), aFrame);
150   myApplyBtn  = new QPushButton(tr("SMESH_BUT_APPLY"), aFrame);
151   myCloseBtn  = new QPushButton(tr("SMESH_BUT_CLOSE"), aFrame);
152   myHelpBtn   = new QPushButton(tr("SMESH_BUT_HELP"),  aFrame);
153
154   QHBoxLayout* aLay = new QHBoxLayout(aFrame);
155   aLay->setMargin(MARGIN);
156   aLay->setSpacing(SPACING);
157
158   aLay->addWidget(myOkBtn);
159   aLay->addSpacing(10);
160   aLay->addWidget(myApplyBtn);
161   aLay->addSpacing(10);
162   aLay->addStretch();
163   aLay->addWidget(myCloseBtn);
164   aLay->addWidget(myHelpBtn);
165
166   return aFrame;
167 }
168
169 //=======================================================================
170 // name    : isValid()
171 // Purpose : Verify validity of input data
172 //=======================================================================
173 bool SMESHGUI_SingleEditDlg::isValid (const bool theMess) const
174 {
175   int id1, id2;
176   return getNodeIds(myEdge->text(), id1, id2);
177 }
178
179 //=======================================================================
180 // name    : getNodeIds()
181 // Purpose : Retrieve node ids from string
182 //=======================================================================
183 bool SMESHGUI_SingleEditDlg::getNodeIds (const QString& theStr,
184                                          int& theId1, int&  theId2) const
185 {
186   if (!theStr.contains('-'))
187     return false;
188
189   bool ok1, ok2;
190   QString str1 = theStr.section('-', 0, 0, QString::SectionSkipEmpty);
191   QString str2 = theStr.section('-', 1, 1, QString::SectionSkipEmpty);
192   theId1 = str1.toInt(&ok1);
193   theId2 = str2.toInt(&ok2);
194
195   return ok1 & ok2;
196 }
197
198 //=======================================================================
199 // name    : ~SMESHGUI_SingleEditDlg()
200 // Purpose : Destructor
201 //=======================================================================
202 SMESHGUI_SingleEditDlg::~SMESHGUI_SingleEditDlg()
203 {
204 }
205
206 //=======================================================================
207 // name    : Init()
208 // Purpose : Init dialog fields, connect signals and slots, show dialog
209 //=======================================================================
210 void SMESHGUI_SingleEditDlg::Init()
211 {
212   mySMESHGUI->SetActiveDialogBox((QDialog*)this);
213   myBusy = false;
214   myActor = 0;
215
216   // main buttons
217   connect(myOkBtn,    SIGNAL(clicked()), SLOT(onOk()));
218   connect(myCloseBtn, SIGNAL(clicked()), SLOT(reject()));
219   connect(myApplyBtn, SIGNAL(clicked()), SLOT(onApply()));
220   connect(myHelpBtn,  SIGNAL(clicked()), SLOT(onHelp()));
221
222   // selection and SMESHGUI
223   connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), SLOT(onSelectionDone()));
224   connect(mySMESHGUI, SIGNAL(SignalDeactivateActiveDialog()), SLOT(onDeactivate()));
225   connect(mySMESHGUI, SIGNAL(SignalCloseAllDialogs()), SLOT(reject()));
226   connect(mySMESHGUI, SIGNAL(SignalActivatedViewManager()), SLOT(onOpenView()));
227   connect(mySMESHGUI, SIGNAL(SignalCloseView()),            SLOT(onCloseView()));
228   connect(myEdge, SIGNAL(textChanged(const QString&)), SLOT(onTextChange(const QString&)));
229
230   myOkBtn->setEnabled(false);
231   myApplyBtn->setEnabled(false);
232   setEnabled(true);
233
234   // set selection mode
235   if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
236     aViewWindow->SetSelectionMode(EdgeOfCellSelection);
237
238   onSelectionDone();
239 }
240
241 //=======================================================================
242 // name    : onOk()
243 // Purpose : SLOT called when "Ok" button pressed.
244 //           Assign filters VTK viewer and close dialog
245 //=======================================================================
246 void SMESHGUI_SingleEditDlg::onOk()
247 {
248   if (onApply())
249     reject();
250 }
251
252 //=======================================================================
253 // name    : reject()
254 // Purpose : SLOT called when "Close" button pressed. Close dialog
255 //=======================================================================
256 void SMESHGUI_SingleEditDlg::reject()
257 {
258   if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
259     aViewWindow->SetSelectionMode(ActorSelection);
260   //mySelectionMgr->clearSelected();
261   disconnect(mySelectionMgr, 0, this, 0);
262   disconnect(mySMESHGUI, 0, this, 0);
263   mySMESHGUI->ResetState();
264   QDialog::reject();
265 }
266
267 //=================================================================================
268 // function : onHelp()
269 // purpose  :
270 //=================================================================================
271 void SMESHGUI_SingleEditDlg::onHelp()
272 {
273   LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication());
274   if (app) 
275     app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), myHelpFileName);
276   else {
277     QString platform;
278 #ifdef WIN32
279     platform = "winapplication";
280 #else
281     platform = "application";
282 #endif
283     SUIT_MessageBox::warning(this, tr("WRN_WARNING"),
284                              tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE").
285                              arg(app->resourceMgr()->stringValue("ExternalBrowser", 
286                                                                  platform)).
287                              arg(myHelpFileName));
288   }
289 }
290
291 //=======================================================================
292 //function : findTriangles()
293 //purpose  : find triangles sharing theNode1-theNode2 link
294 //           THIS IS A PIECE OF SMESH_MeshEditor.cxx
295 //           TO DO: make it available in SMDS for ex.
296 //=======================================================================
297 static bool findTriangles (const SMDS_MeshNode *    theNode1,
298                            const SMDS_MeshNode *    theNode2,
299                            const SMDS_MeshElement*& theTria1,
300                            const SMDS_MeshElement*& theTria2)
301 {
302   if (!theNode1 || !theNode2) return false;
303
304   theTria1 = theTria2 = 0;
305
306   std::set< const SMDS_MeshElement* > emap;
307   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator();
308   while (it->more()) {
309     const SMDS_MeshElement* elem = it->next();
310     if (elem->GetType() == SMDSAbs_Face && elem->NbCornerNodes() == 3)
311       emap.insert(elem);
312   }
313   it = theNode2->GetInverseElementIterator();
314   while (it->more()) {
315     const SMDS_MeshElement* elem = it->next();
316     if (elem->GetType() == SMDSAbs_Face &&
317          emap.find(elem) != emap.end())
318     {
319       if (theTria1) {
320         theTria2 = elem;
321         break;
322       } else {
323         theTria1 = elem;
324       }
325     }
326   }
327   return (theTria1 && theTria2);
328 }
329
330 //=======================================================================
331 //function : onTextChange()
332 //purpose  :
333 //=======================================================================
334 void SMESHGUI_SingleEditDlg::onTextChange (const QString& theNewText)
335 {
336   if (myBusy) return;
337   BusyLocker lock(myBusy);
338
339   myOkBtn->setEnabled(false);
340   myApplyBtn->setEnabled(false);
341
342   // hilight entered edge
343   if(myActor){
344     if(SMDS_Mesh* aMesh = myActor->GetObject()->GetMesh()){
345       Handle(SALOME_InteractiveObject) anIO = myActor->getIO();
346       SALOME_ListIO aList;
347       aList.Append(anIO);
348       mySelectionMgr->setSelectedObjects(aList,false);
349       
350       TColStd_IndexedMapOfInteger selectedIndices;
351       TColStd_MapOfInteger newIndices;
352       mySelector->GetIndex(anIO,selectedIndices);
353
354       int id1, id2;
355       if ( !getNodeIds(myEdge->text(), id1, id2) )
356         return;
357
358       const SMDS_MeshNode* aNode1 = aMesh->FindNode( id1 );
359       const SMDS_MeshNode* aNode2 = aMesh->FindNode( id2 );
360
361       if ( !aNode1 || !aNode2 || aNode1 == aNode2 )
362         return;
363
364       // find a triangle and an edge index
365       const SMDS_MeshElement* tria1;
366       const SMDS_MeshElement* tria2;
367
368       if ( findTriangles(aNode1,aNode2,tria1,tria2) )
369       {
370         newIndices.Add(tria1->GetID());
371
372         const SMDS_MeshNode* a3Nodes[3];
373         SMDS_ElemIteratorPtr it;
374         int edgeInd = 2, i;
375         for (i = 0, it = tria1->nodesIterator(); it->more(); i++) {
376           a3Nodes[ i ] = static_cast<const SMDS_MeshNode*>(it->next());
377           if (i > 0 && ( (a3Nodes[ i ] == aNode1 && a3Nodes[ i - 1] == aNode2) ||
378                          (a3Nodes[ i ] == aNode2 && a3Nodes[ i - 1] == aNode1) ) ) {
379             edgeInd = i - 1;
380             break;
381           }
382         }
383         newIndices.Add(-edgeInd-1);
384         
385         myOkBtn->setEnabled(true);
386         myApplyBtn->setEnabled(true);
387       }
388       mySelector->AddOrRemoveIndex(anIO,newIndices, false);
389       SMESH::GetViewWindow(mySMESHGUI)->highlight( anIO, true, true );
390     }
391   }
392 }
393
394 //=======================================================================
395 // name    : onSelectionDone()
396 // Purpose : SLOT called when selection changed
397 //=======================================================================
398 void SMESHGUI_SingleEditDlg::onSelectionDone()
399 {
400   if (myBusy) return;
401   BusyLocker lock(myBusy);
402
403   int anId1 = 0, anId2 = 0;
404
405   myOkBtn->setEnabled(false);
406   myApplyBtn->setEnabled(false);
407
408   SALOME_ListIO aList;
409   mySelectionMgr->selectedObjects(aList);
410
411   if (aList.Extent() != 1) {
412     myEdge->clear();
413     return;
414   }
415
416   Handle(SALOME_InteractiveObject) anIO = aList.First();
417   myActor = SMESH::FindActorByEntry(anIO->getEntry());
418   if(myActor){
419     TVisualObjPtr aVisualObj = myActor->GetObject();
420     if(SMDS_Mesh* aMesh = aVisualObj->GetMesh())
421     {
422       const SMDS_MeshElement* tria[2];
423       if( SMESH::GetEdgeNodes( mySelector, aVisualObj, anId1, anId2 ) >= 1 &&
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 deativated
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 (mySMESHGUI->isActiveStudyLocked())
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     mySelectionMgr->setSelectedObjects(aList, false);
527     onSelectionDone();
528     SMESH::UpdateView();
529     SMESHGUI::Modified();
530   }
531
532   return aResult;
533 }
534
535 //=================================================================================
536 // function : keyPressEvent()
537 // purpose  :
538 //=================================================================================
539 void SMESHGUI_SingleEditDlg::keyPressEvent( QKeyEvent* e )
540 {
541   QDialog::keyPressEvent( e );
542   if ( e->isAccepted() )
543     return;
544
545   if ( e->key() == Qt::Key_F1 ) {
546     e->accept();
547     onHelp();
548   }
549 }
550
551 /*!
552  *  Class       : SMESHGUI_TrianglesInversionDlg
553  *  Description : Inversion of the diagonal of a pseudo-quadrangle formed by
554  *                2 neighboring triangles with 1 common edge
555  */
556
557 SMESHGUI_TrianglesInversionDlg
558 ::SMESHGUI_TrianglesInversionDlg(SMESHGUI* theModule)
559 : SMESHGUI_SingleEditDlg(theModule)
560 {
561   setWindowTitle(tr("CAPTION"));
562   myHelpFileName = "diagonal_inversion_of_elements_page.html";
563 }
564
565 SMESHGUI_TrianglesInversionDlg::~SMESHGUI_TrianglesInversionDlg()
566 {
567 }
568
569 bool SMESHGUI_TrianglesInversionDlg::process (SMESH::SMESH_MeshEditor_ptr theMeshEditor,
570                                               const int theId1, const int theId2)
571 {
572   return theMeshEditor->InverseDiag(theId1, theId2);
573 }
574
575 /*!
576  *  Class       : SMESHGUI_UnionOfTwoTrianglesDlg
577  *  Description : Construction of a quadrangle by deletion of the
578  *                common border of 2 neighboring triangles
579  */
580
581 SMESHGUI_UnionOfTwoTrianglesDlg
582 ::SMESHGUI_UnionOfTwoTrianglesDlg(SMESHGUI* theModule)
583 : SMESHGUI_SingleEditDlg(theModule)
584 {
585   setWindowTitle(tr("CAPTION"));
586   myHelpFileName = "uniting_two_triangles_page.html";
587 }
588
589 SMESHGUI_UnionOfTwoTrianglesDlg::~SMESHGUI_UnionOfTwoTrianglesDlg()
590 {
591 }
592
593 bool SMESHGUI_UnionOfTwoTrianglesDlg::process (SMESH::SMESH_MeshEditor_ptr theMeshEditor,
594                                                const int theId1, const int theId2)
595 {
596   return theMeshEditor->DeleteDiag(theId1, theId2);
597 }