Salome HOME
Porting SMESH module to Qt 4
[modules/smesh.git] / src / SMESHGUI / SMESHGUI_SingleEditDlg.cxx
1 // Copyright (C) 2003  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
2 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS 
3 //
4 // This library is free software; you can redistribute it and/or 
5 // modify it under the terms of the GNU Lesser General Public 
6 // License as published by the Free Software Foundation; either 
7 // version 2.1 of the License. 
8 //
9 // This library is distributed in the hope that it will be useful, 
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
12 // Lesser General Public License for more details. 
13 //
14 // You should have received a copy of the GNU Lesser General Public 
15 // License along with this library; if not, write to the Free Software 
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
17 //
18 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19 //
20 // File   : SMESHGUI_SingleEditDlg.cxx
21 // Author : Sergey LITONIN, Open CASCADE S.A.S.
22 //
23
24 // SMESH includes
25 #include "SMESHGUI_SingleEditDlg.h"
26
27 #include "SMESHGUI.h"
28 #include "SMESHGUI_Utils.h"
29 #include "SMESHGUI_VTKUtils.h"
30 #include "SMESHGUI_MeshUtils.h"
31
32 #include <SMESH_Actor.h>
33 #include <SMDS_Mesh.hxx>
34
35 // SALOME GUI includes
36 #include <LightApp_SelectionMgr.h>
37 #include <LightApp_Application.h>
38 #include <SUIT_ResourceMgr.h>
39 #include <SUIT_MessageBox.h>
40 #include <SUIT_Desktop.h>
41 #include <SUIT_Session.h>
42
43 #include <SVTK_Selector.h>
44 #include <SVTK_ViewWindow.h>
45 #include <SALOME_ListIO.hxx>
46
47 // OCCT includes
48 #include <TColStd_MapOfInteger.hxx>
49 #include <TColStd_IndexedMapOfInteger.hxx>
50
51 // Qt includes
52 #include <QVBoxLayout>
53 #include <QHBoxLayout>
54 #include <QLineEdit>
55 #include <QPushButton>
56 #include <QGroupBox>
57 #include <QLabel>
58 #include <QValidator>
59 #include <QKeyEvent>
60
61 #define SPACING 6
62 #define MARGIN  11
63
64 /*!
65  *  Class       : SMESHGUI_DiagValidator
66  *  Description : validate munual input of edge like "id1-id2"
67  */
68 class SMESHGUI_DiagValidator: public QValidator
69 {
70 public:
71   SMESHGUI_DiagValidator (QWidget* parent):
72     QValidator(parent) {}
73
74   State validate (QString& text, int& pos) const
75   {
76     text = text.trimmed();
77     text.replace(QRegExp("[^0-9]+"), "-");
78     if (text == "-")
79       text = "";
80     int ind = text.indexOf(QRegExp("-[0-9]+-"));
81     if (ind > 0) { // leave only two ids
82       ind = text.indexOf('-', ind + 1);
83       if (ind > 0)
84         text.truncate(ind);
85     }
86     if (pos > text.length())
87       pos = text.length();
88     return Acceptable;
89   }
90 };
91
92 /*!
93  *  Class       : SMESHGUI_SingleEditDlg
94  *  Description : Inversion of the diagonal of a pseudo-quadrangle formed by
95  *                2 neighboring triangles with 1 common edge
96  */
97
98 //=======================================================================
99 // name    : SMESHGUI_SingleEditDlg()
100 // Purpose : Constructor
101 //=======================================================================
102 SMESHGUI_SingleEditDlg
103 ::SMESHGUI_SingleEditDlg(SMESHGUI* theModule)
104   : QDialog(SMESH::GetDesktop(theModule)),
105     mySelector(SMESH::GetViewWindow(theModule)->GetSelector()),
106     mySelectionMgr(SMESH::GetSelectionMgr(theModule)),
107     mySMESHGUI(theModule)
108 {
109   setModal(false);
110
111   QVBoxLayout* aDlgLay = new QVBoxLayout(this);
112   aDlgLay->setMargin(MARGIN);
113   aDlgLay->setSpacing(SPACING);
114
115   QWidget* aMainFrame = createMainFrame  (this);
116   QWidget* aBtnFrame  = createButtonFrame(this);
117
118   aDlgLay->addWidget(aMainFrame);
119   aDlgLay->addWidget(aBtnFrame);
120
121   Init();
122 }
123
124 //=======================================================================
125 // name    : createMainFrame()
126 // Purpose : Create frame containing dialog's input fields
127 //=======================================================================
128 QWidget* SMESHGUI_SingleEditDlg::createMainFrame (QWidget* theParent)
129 {
130   QGroupBox* aMainGrp = new QGroupBox(tr("EDGE_BETWEEN"), theParent);
131   QHBoxLayout* aLay = new QHBoxLayout(aMainGrp);
132   aLay->setMargin(MARGIN);
133   aLay->setSpacing(SPACING);
134
135   QPixmap aPix (SMESH::GetResourceMgr( mySMESHGUI )->loadPixmap("SMESH", tr("ICON_SELECT")));
136
137   QLabel* aLab = new QLabel(tr("SMESH_EDGE"), aMainGrp);
138   QPushButton* aBtn = new QPushButton(aMainGrp);
139   aBtn->setIcon(aPix);
140   myEdge = new QLineEdit(aMainGrp);
141   myEdge->setValidator(new SMESHGUI_DiagValidator(this));
142
143   aLay->addWidget(aLab);
144   aLay->addWidget(aBtn);
145   aLay->addWidget(myEdge);
146
147   return aMainGrp;
148 }
149
150 //=======================================================================
151 // name    : createButtonFrame()
152 // Purpose : Create frame containing buttons
153 //=======================================================================
154 QWidget* SMESHGUI_SingleEditDlg::createButtonFrame (QWidget* theParent)
155 {
156   QGroupBox* aFrame = new QGroupBox(theParent);
157
158   myOkBtn     = new QPushButton(tr("SMESH_BUT_OK"   ), aFrame);
159   myApplyBtn  = new QPushButton(tr("SMESH_BUT_APPLY"), aFrame);
160   myCloseBtn  = new QPushButton(tr("SMESH_BUT_CLOSE"), aFrame);
161   myHelpBtn   = new QPushButton(tr("SMESH_BUT_HELP"),  aFrame);
162
163   QHBoxLayout* aLay = new QHBoxLayout(aFrame);
164   aLay->setMargin(MARGIN);
165   aLay->setSpacing(SPACING);
166
167   aLay->addWidget(myOkBtn);
168   aLay->addSpacing(10);
169   aLay->addWidget(myApplyBtn);
170   aLay->addSpacing(10);
171   aLay->addStretch();
172   aLay->addWidget(myCloseBtn);
173   aLay->addWidget(myHelpBtn);
174
175   return aFrame;
176 }
177
178 //=======================================================================
179 // name    : isValid()
180 // Purpose : Verify validity of input data
181 //=======================================================================
182 bool SMESHGUI_SingleEditDlg::isValid (const bool theMess) const
183 {
184   int id1, id2;
185   return getNodeIds(myEdge->text(), id1, id2);
186 }
187
188 //=======================================================================
189 // name    : getNodeIds()
190 // Purpose : Retrieve node ids from string
191 //=======================================================================
192 bool SMESHGUI_SingleEditDlg::getNodeIds (const QString& theStr,
193                                          int& theId1, int&  theId2) const
194 {
195   if (!theStr.contains('-'))
196     return false;
197
198   bool ok1, ok2;
199   QString str1 = theStr.section('-', 0, 0, QString::SectionSkipEmpty);
200   QString str2 = theStr.section('-', 1, 1, QString::SectionSkipEmpty);
201   theId1 = str1.toInt(&ok1);
202   theId2 = str2.toInt(&ok2);
203
204   return ok1 & ok2;
205 }
206
207 //=======================================================================
208 // name    : ~SMESHGUI_SingleEditDlg()
209 // Purpose : Destructor
210 //=======================================================================
211 SMESHGUI_SingleEditDlg::~SMESHGUI_SingleEditDlg()
212 {
213 }
214
215 //=======================================================================
216 // name    : Init()
217 // Purpose : Init dialog fields, connect signals and slots, show dialog
218 //=======================================================================
219 void SMESHGUI_SingleEditDlg::Init()
220 {
221   mySMESHGUI->SetActiveDialogBox((QDialog*)this);
222   myBusy = false;
223   myActor = 0;
224
225   // main buttons
226   connect(myOkBtn,    SIGNAL(clicked()), SLOT(onOk()));
227   connect(myCloseBtn, SIGNAL(clicked()), SLOT(onClose()));
228   connect(myApplyBtn, SIGNAL(clicked()), SLOT(onApply()));
229   connect(myHelpBtn,  SIGNAL(clicked()), SLOT(onHelp()));
230
231   // selection and SMESHGUI
232   connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), SLOT(onSelectionDone()));
233   connect(mySMESHGUI, SIGNAL(SignalDeactivateActiveDialog()), SLOT(onDeactivate()));
234   connect(mySMESHGUI, SIGNAL(SignalCloseAllDialogs()), SLOT(onClose()));
235   connect(myEdge, SIGNAL(textChanged(const QString&)), SLOT(onTextChange(const QString&)));
236
237   myOkBtn->setEnabled(false);
238   myApplyBtn->setEnabled(false);
239   setEnabled(true);
240
241   // set selection mode
242   if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
243     aViewWindow->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   if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
266     aViewWindow->SetSelectionMode(ActorSelection);
267   //mySelectionMgr->clearSelected();
268   disconnect(mySelectionMgr, 0, this, 0);
269   disconnect(mySMESHGUI, 0, this, 0);
270   mySMESHGUI->ResetState();
271   reject();
272 }
273
274 //=================================================================================
275 // function : onHelp()
276 // purpose  :
277 //=================================================================================
278 void SMESHGUI_SingleEditDlg::onHelp()
279 {
280   LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication());
281   if (app) 
282     app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), myHelpFileName);
283   else {
284     QString platform;
285 #ifdef WIN32
286     platform = "winapplication";
287 #else
288     platform = "application";
289 #endif
290     SUIT_MessageBox::warning(this, tr("WRN_WARNING"),
291                              tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE").
292                              arg(app->resourceMgr()->stringValue("ExternalBrowser", 
293                                                                  platform)).
294                              arg(myHelpFileName));
295   }
296 }
297
298 //=======================================================================
299 //function : findTriangles()
300 //purpose  : find triangles sharing theNode1-theNode2 link
301 //           THIS IS A PIECE OF SMESH_MeshEditor.cxx
302 //           TO DO: make it available in SMDS for ex.
303 //=======================================================================
304 static bool findTriangles (const SMDS_MeshNode *    theNode1,
305                            const SMDS_MeshNode *    theNode2,
306                            const SMDS_MeshElement*& theTria1,
307                            const SMDS_MeshElement*& theTria2)
308 {
309   if (!theNode1 || !theNode2) return false;
310
311   theTria1 = theTria2 = 0;
312
313   set< const SMDS_MeshElement* > emap;
314   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator();
315   while (it->more()) {
316     const SMDS_MeshElement* elem = it->next();
317     if (elem->GetType() == SMDSAbs_Face && elem->NbNodes() == 3)
318       emap.insert(elem);
319   }
320   it = theNode2->GetInverseElementIterator();
321   while (it->more()) {
322     const SMDS_MeshElement* elem = it->next();
323     if (elem->GetType() == SMDSAbs_Face &&
324          emap.find(elem) != emap.end())
325       if (theTria1) {
326         theTria2 = elem;
327         break;
328       } else {
329         theTria1 = elem;
330       }
331   }
332   return (theTria1 && theTria2);
333 }
334
335 //=======================================================================
336 //function : onTextChange()
337 //purpose  :
338 //=======================================================================
339 void SMESHGUI_SingleEditDlg::onTextChange (const QString& theNewText)
340 {
341   if (myBusy) return;
342
343   myOkBtn->setEnabled(false);
344   myApplyBtn->setEnabled(false);
345
346   // hilight entered edge
347   if(myActor){
348     if(SMDS_Mesh* aMesh = myActor->GetObject()->GetMesh()){
349       myBusy = true; // block onSelectionDone()
350       Handle(SALOME_InteractiveObject) anIO = myActor->getIO();
351       SALOME_ListIO aList;
352       aList.Append(anIO);
353       mySelectionMgr->setSelectedObjects(aList,false);
354       
355       TColStd_IndexedMapOfInteger selectedIndices;
356       TColStd_MapOfInteger newIndices;
357       mySelector->GetIndex(anIO,selectedIndices);
358       myBusy = false;
359
360       QStringList aListId = theNewText.split("-", QString::SkipEmptyParts);
361       if (aListId.count() != 2)
362         return;
363
364       int i;
365       bool allOk = true;
366       const SMDS_MeshNode* a2Nodes[2];
367       for (i = 0; i < aListId.count(); i++) {
368         if(const SMDS_MeshNode *aNode = aMesh->FindNode(aListId[ i ].toInt()))
369           a2Nodes[ i ] = aNode;
370         else
371           allOk = false;
372       }
373       
374       // find a triangle and an edge nb
375       const SMDS_MeshElement* tria[2];
376       allOk &= a2Nodes[0] != a2Nodes[1] && findTriangles(a2Nodes[0],a2Nodes[1],tria[0],tria[1]);
377       myBusy = true; // block onSelectionDone()
378       if(allOk)
379       {
380         newIndices.Add(tria[0]->GetID());
381
382         const SMDS_MeshNode* a3Nodes [3];
383         SMDS_ElemIteratorPtr it;
384         int edgeInd = 2;
385         for (i = 0, it = tria[0]->nodesIterator(); it->more(); i++) {
386           a3Nodes[ i ] = static_cast<const SMDS_MeshNode*>(it->next());
387           if (i > 0) {
388             allOk = (a3Nodes[ i ] == a2Nodes[ 0 ] && a3Nodes[ i - 1] == a2Nodes[ 1 ]) ||
389               (a3Nodes[ i ] == a2Nodes[ 1 ] && a3Nodes[ i - 1] == a2Nodes[ 0 ]);
390             if (allOk) {
391               edgeInd = i - 1;
392               break;
393             }
394           }
395         }
396         newIndices.Add(-edgeInd-1);
397         
398         myOkBtn->setEnabled(true);
399         myApplyBtn->setEnabled(true);
400       }
401       mySelector->AddOrRemoveIndex(anIO,newIndices, false);
402       SMESH::GetViewWindow(mySMESHGUI)->highlight( anIO, true, true );
403
404       myBusy = false;
405     }
406   }
407 }
408
409 //=======================================================================
410 // name    : onSelectionDone()
411 // Purpose : SLOT called when selection changed
412 //=======================================================================
413 void SMESHGUI_SingleEditDlg::onSelectionDone()
414 {
415   if (myBusy) return;
416
417   int anId1 = 0, anId2 = 0;
418
419   myOkBtn->setEnabled(false);
420   myApplyBtn->setEnabled(false);
421
422   SALOME_ListIO aList;
423   mySelectionMgr->selectedObjects(aList);
424
425   if (aList.Extent() != 1) {
426     myEdge->clear();
427     return;
428   }
429
430   Handle(SALOME_InteractiveObject) anIO = aList.First();
431   myActor = SMESH::FindActorByEntry(anIO->getEntry());
432   if(myActor){
433     TVisualObjPtr aVisualObj = myActor->GetObject();
434     if(SMDS_Mesh* aMesh = aVisualObj->GetMesh())
435     {
436       const SMDS_MeshElement* tria[2];
437       if( SMESH::GetEdgeNodes( mySelector, aVisualObj, anId1, anId2 ) >= 1 &&
438           findTriangles( aMesh->FindNode( anId1 ), aMesh->FindNode( anId2 ), tria[0],tria[1] ) )
439       {
440         QString aText = QString("%1-%2").arg(anId1).arg(anId2);
441         myBusy = true;
442         myEdge->setText(aText);
443         myBusy = false;
444         
445         myOkBtn->setEnabled(true);
446         myApplyBtn->setEnabled(true);
447       }
448       else
449       {
450         myEdge->clear();
451       }
452     }
453   }
454 }
455
456 //=======================================================================
457 // name    : onDeactivate()
458 // Purpose : SLOT called when dialog must be deativated
459 //=======================================================================
460 void SMESHGUI_SingleEditDlg::onDeactivate()
461 {
462   setEnabled(false);
463 }
464
465 //=======================================================================
466 // name    : enterEvent()
467 // Purpose : Event filter
468 //=======================================================================
469 void SMESHGUI_SingleEditDlg::enterEvent (QEvent*)
470 {
471   if (!isEnabled()) {
472     mySMESHGUI->EmitSignalDeactivateDialog();
473     if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
474       aViewWindow->SetSelectionMode(EdgeOfCellSelection);
475     setEnabled(true);
476   }
477 }
478
479 //=================================================================================
480 // function : closeEvent()
481 // purpose  :
482 //=================================================================================
483 void SMESHGUI_SingleEditDlg::closeEvent (QCloseEvent*)
484 {
485   onClose();
486 }
487
488 //=======================================================================
489 //function : hideEvent()
490 //purpose  : caused by ESC key
491 //=======================================================================
492 void SMESHGUI_SingleEditDlg::hideEvent (QHideEvent*)
493 {
494   if (!isMinimized())
495     onClose();
496 }
497
498 //=================================================================================
499 // function : onApply()
500 // purpose  : SLOT. Called when apply button is pressed
501 //=================================================================================
502 bool SMESHGUI_SingleEditDlg::onApply()
503 {
504   if (mySMESHGUI->isActiveStudyLocked())
505     return false;
506   // verify validity of input data
507   if (!isValid(true))
508     return false;
509
510   // get mesh, actor and nodes
511   SALOME_ListIO aList;
512   mySelectionMgr->selectedObjects(aList);
513
514   SMESH::SMESH_Mesh_var aMesh = SMESH::GetMeshByIO(aList.First());
515
516   if (aMesh->_is_nil()) {
517     SUIT_MessageBox::information(SMESH::GetDesktop(mySMESHGUI), 
518                                  tr("SMESH_ERROR"),
519                                  tr("SMESHG_NO_MESH"));
520     return false;
521   }
522
523   SMESH::SMESH_MeshEditor_var aMeshEditor = aMesh->GetMeshEditor();
524   int anId1= 0, anId2 = 0;
525   if (aMeshEditor->_is_nil() || !getNodeIds(myEdge->text(), anId1, anId2))
526     return false;
527
528   // perform operation
529   bool aResult = process(aMeshEditor.in(), anId1, anId2);
530
531   // update actor
532   if (aResult) {
533     mySelector->ClearIndex();
534     mySelectionMgr->setSelectedObjects(aList, false);
535     onSelectionDone();
536     SMESH::UpdateView();
537   }
538
539   return aResult;
540 }
541
542 //=================================================================================
543 // function : keyPressEvent()
544 // purpose  :
545 //=================================================================================
546 void SMESHGUI_SingleEditDlg::keyPressEvent( QKeyEvent* e )
547 {
548   QDialog::keyPressEvent( e );
549   if ( e->isAccepted() )
550     return;
551
552   if ( e->key() == Qt::Key_F1 ) {
553     e->accept();
554     onHelp();
555   }
556 }
557
558 /*!
559  *  Class       : SMESHGUI_TrianglesInversionDlg
560  *  Description : Inversion of the diagonal of a pseudo-quadrangle formed by
561  *                2 neighboring triangles with 1 common edge
562  */
563
564 SMESHGUI_TrianglesInversionDlg
565 ::SMESHGUI_TrianglesInversionDlg(SMESHGUI* theModule)
566 : SMESHGUI_SingleEditDlg(theModule)
567 {
568   setWindowTitle(tr("CAPTION"));
569   myHelpFileName = "diagonal_inversion_of_elements_page.html";
570 }
571
572 SMESHGUI_TrianglesInversionDlg::~SMESHGUI_TrianglesInversionDlg()
573 {
574 }
575
576 bool SMESHGUI_TrianglesInversionDlg::process (SMESH::SMESH_MeshEditor_ptr theMeshEditor,
577                                               const int theId1, const int theId2)
578 {
579   return theMeshEditor->InverseDiag(theId1, theId2);
580 }
581
582 /*!
583  *  Class       : SMESHGUI_UnionOfTwoTrianglesDlg
584  *  Description : Construction of a quadrangle by deletion of the
585  *                common border of 2 neighboring triangles
586  */
587
588 SMESHGUI_UnionOfTwoTrianglesDlg
589 ::SMESHGUI_UnionOfTwoTrianglesDlg(SMESHGUI* theModule)
590 : SMESHGUI_SingleEditDlg(theModule)
591 {
592   setWindowTitle(tr("CAPTION"));
593   myHelpFileName = "uniting_two_triangles_page.html";
594 }
595
596 SMESHGUI_UnionOfTwoTrianglesDlg::~SMESHGUI_UnionOfTwoTrianglesDlg()
597 {
598 }
599
600 bool SMESHGUI_UnionOfTwoTrianglesDlg::process (SMESH::SMESH_MeshEditor_ptr theMeshEditor,
601                                                const int theId1, const int theId2)
602 {
603   return theMeshEditor->DeleteDiag(theId1, theId2);
604 }