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