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