Salome HOME
Merging with WPdev
[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 /*!
72  *  Class       : SMESHGUI_DiagValidator
73  *  Description : validate munual input of edge like "id1-id2"
74  */
75 class SMESHGUI_DiagValidator: public QValidator
76 {
77  public:
78   SMESHGUI_DiagValidator (QWidget * parent, const char * name = 0):
79     QValidator(parent,name) {}
80
81   State validate (QString & text, int & pos) const
82   {
83     text.stripWhiteSpace();
84     text.replace(QRegExp("[^0-9]+"), "-");
85     if (text == "-")
86       text = "";
87     int ind = text.find(QRegExp("-[0-9]+-"));
88     if (ind > 0) { // leave only two ids
89       ind = text.find('-', ind + 1);
90       if (ind > 0)
91         text.truncate(ind);
92     }
93     if (pos > text.length())
94       pos = text.length();
95     return Acceptable;
96   }
97 };
98
99 /*!
100  *  Class       : SMESHGUI_SingleEditDlg
101  *  Description : Inversion of the diagonal of a pseudo-quadrangle formed by
102  *                2 neighboring triangles with 1 common edge
103  */
104
105 //=======================================================================
106 // name    : SMESHGUI_SingleEditDlg()
107 // Purpose : Constructor
108 //=======================================================================
109 SMESHGUI_SingleEditDlg
110 ::SMESHGUI_SingleEditDlg(SMESHGUI* theModule, 
111                          const char* theName):
112   QDialog(SMESH::GetDesktop(theModule), 
113           theName, 
114           false, 
115           WStyle_Customize | WStyle_NormalBorder | WStyle_Title | WStyle_SysMenu),
116     mySelector(SMESH::GetViewWindow(theModule)->GetSelector()),
117     mySelectionMgr(SMESH::GetSelectionMgr(theModule)),
118     mySMESHGUI(theModule)
119 {
120   QVBoxLayout* aDlgLay = new QVBoxLayout(this, MARGIN, SPACING);
121
122   QFrame* aMainFrame = createMainFrame  (this);
123   QFrame* aBtnFrame  = createButtonFrame(this);
124
125   aDlgLay->addWidget(aMainFrame);
126   aDlgLay->addWidget(aBtnFrame);
127
128   aDlgLay->setStretchFactor(aMainFrame, 1);
129
130   Init();
131 }
132
133 //=======================================================================
134 // name    : createMainFrame()
135 // Purpose : Create frame containing dialog's input fields
136 //=======================================================================
137 QFrame* SMESHGUI_SingleEditDlg::createMainFrame (QWidget* theParent)
138 {
139   QGroupBox* aMainGrp = new QGroupBox(1, Qt::Vertical, tr("EDGE_BETWEEN"), theParent);
140
141   QPixmap aPix (SMESH::GetResourceMgr( mySMESHGUI )->loadPixmap("SMESH", tr("ICON_SELECT")));
142
143   new QLabel(tr("SMESH_EDGE"), aMainGrp);
144   (new QPushButton(aMainGrp))->setPixmap(aPix);
145   myEdge = new QLineEdit(aMainGrp);
146   myEdge->setValidator(new SMESHGUI_DiagValidator(this, "validator"));
147
148   return aMainGrp;
149 }
150
151 //=======================================================================
152 // name    : createButtonFrame()
153 // Purpose : Create frame containing buttons
154 //=======================================================================
155 QFrame* SMESHGUI_SingleEditDlg::createButtonFrame (QWidget* theParent)
156 {
157   QFrame* aFrame = new QFrame(theParent);
158   aFrame->setFrameStyle(QFrame::Box | QFrame::Sunken);
159
160   myOkBtn     = new QPushButton(tr("SMESH_BUT_OK"   ), aFrame);
161   myApplyBtn  = new QPushButton(tr("SMESH_BUT_APPLY"), aFrame);
162   myCloseBtn  = new QPushButton(tr("SMESH_BUT_CLOSE"), aFrame);
163   myHelpBtn   = new QPushButton (tr("SMESH_BUT_HELP"), aFrame);
164
165   QSpacerItem* aSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum);
166
167   QHBoxLayout* aLay = new QHBoxLayout(aFrame, MARGIN, SPACING);
168
169   aLay->addWidget(myOkBtn);
170   aLay->addWidget(myApplyBtn);
171   aLay->addItem(aSpacer);
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   this->show();
242
243   // set selection mode
244   if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
245     aViewWindow->SetSelectionMode(EdgeOfCellSelection);
246
247   onSelectionDone();
248 }
249
250 //=======================================================================
251 // name    : onOk()
252 // Purpose : SLOT called when "Ok" button pressed.
253 //           Assign filters VTK viewer and close dialog
254 //=======================================================================
255 void SMESHGUI_SingleEditDlg::onOk()
256 {
257   if (onApply())
258     onClose();
259 }
260
261 //=======================================================================
262 // name    : onClose()
263 // Purpose : SLOT called when "Close" button pressed. Close dialog
264 //=======================================================================
265 void SMESHGUI_SingleEditDlg::onClose()
266 {
267   if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
268     aViewWindow->SetSelectionMode(ActorSelection);
269   //mySelectionMgr->clearSelected();
270   disconnect(mySelectionMgr, 0, this, 0);
271   disconnect(mySMESHGUI, 0, this, 0);
272   mySMESHGUI->ResetState();
273   reject();
274 }
275
276 //=================================================================================
277 // function : onHelp()
278 // purpose  :
279 //=================================================================================
280 void SMESHGUI_SingleEditDlg::onHelp()
281 {
282   LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication());
283   if (app) 
284     app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), myHelpFileName);
285   else {
286                 QString platform;
287 #ifdef WIN32
288                 platform = "winapplication";
289 #else
290                 platform = "application";
291 #endif
292     SUIT_MessageBox::warn1(0, QObject::tr("WRN_WARNING"),
293                            QObject::tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE").
294                            arg(app->resourceMgr()->stringValue("ExternalBrowser", platform)).arg(myHelpFileName),
295                            QObject::tr("BUT_OK"));
296   }
297 }
298
299 //=======================================================================
300 //function : findTriangles()
301 //purpose  : find triangles sharing theNode1-theNode2 link
302 //           THIS IS A PIECE OF SMESH_MeshEditor.cxx
303 //           TO DO: make it available in SMDS for ex.
304 //=======================================================================
305 static bool findTriangles (const SMDS_MeshNode *    theNode1,
306                            const SMDS_MeshNode *    theNode2,
307                            const SMDS_MeshElement*& theTria1,
308                            const SMDS_MeshElement*& theTria2)
309 {
310   if (!theNode1 || !theNode2) return false;
311
312   theTria1 = theTria2 = 0;
313
314   set< const SMDS_MeshElement* > emap;
315   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator();
316   while (it->more()) {
317     const SMDS_MeshElement* elem = it->next();
318     if (elem->GetType() == SMDSAbs_Face && elem->NbNodes() == 3)
319       emap.insert(elem);
320   }
321   it = theNode2->GetInverseElementIterator();
322   while (it->more()) {
323     const SMDS_MeshElement* elem = it->next();
324     if (elem->GetType() == SMDSAbs_Face &&
325          emap.find(elem) != emap.end())
326       if (theTria1) {
327         theTria2 = elem;
328         break;
329       } else {
330         theTria1 = elem;
331       }
332   }
333   return (theTria1 && theTria2);
334 }
335
336 //=======================================================================
337 //function : onTextChange()
338 //purpose  :
339 //=======================================================================
340 void SMESHGUI_SingleEditDlg::onTextChange (const QString& theNewText)
341 {
342   if (myBusy) return;
343
344   myOkBtn->setEnabled(false);
345   myApplyBtn->setEnabled(false);
346
347   // hilight entered edge
348   if(myActor){
349     if(SMDS_Mesh* aMesh = myActor->GetObject()->GetMesh()){
350       myBusy = true; // block onSelectionDone()
351       Handle(SALOME_InteractiveObject) anIO = myActor->getIO();
352       SALOME_ListIO aList;
353       aList.Append(anIO);
354       mySelectionMgr->setSelectedObjects(aList,false);
355       
356       TColStd_IndexedMapOfInteger selectedIndices;
357       TColStd_MapOfInteger newIndices;
358       mySelector->GetIndex(anIO,selectedIndices);
359       myBusy = false;
360
361       QStringList aListId = QStringList::split("-", theNewText, false);
362       if (aListId.count() != 2)
363         return;
364
365       int i;
366       bool allOk = true;
367       const SMDS_MeshNode* a2Nodes[2];
368       for (i = 0; i < aListId.count(); i++) {
369         if(const SMDS_MeshNode *aNode = aMesh->FindNode(aListId[ i ].toInt()))
370           a2Nodes[ i ] = aNode;
371         else
372           allOk = false;
373       }
374       
375       // find a triangle and an edge nb
376       const SMDS_MeshElement* tria[2];
377       allOk &= a2Nodes[0] != a2Nodes[1] && findTriangles(a2Nodes[0],a2Nodes[1],tria[0],tria[1]);
378       myBusy = true; // block onSelectionDone()
379       if(allOk)
380       {
381         newIndices.Add(tria[0]->GetID());
382
383         const SMDS_MeshNode* a3Nodes [3];
384         SMDS_ElemIteratorPtr it;
385         int edgeInd = 2;
386         for (i = 0, it = tria[0]->nodesIterator(); it->more(); i++) {
387           a3Nodes[ i ] = static_cast<const SMDS_MeshNode*>(it->next());
388           if (i > 0) {
389             allOk = (a3Nodes[ i ] == a2Nodes[ 0 ] && a3Nodes[ i - 1] == a2Nodes[ 1 ]) ||
390               (a3Nodes[ i ] == a2Nodes[ 1 ] && a3Nodes[ i - 1] == a2Nodes[ 0 ]);
391             if (allOk) {
392               edgeInd = i - 1;
393               break;
394             }
395           }
396         }
397         newIndices.Add(-edgeInd-1);
398         
399         myOkBtn->setEnabled(true);
400         myApplyBtn->setEnabled(true);
401       }
402       mySelector->AddOrRemoveIndex(anIO,newIndices, false);
403       SMESH::GetViewWindow(mySMESHGUI)->highlight( anIO, true, true );
404
405       myBusy = false;
406     }
407   }
408 }
409
410 //=======================================================================
411 // name    : onSelectionDone()
412 // Purpose : SLOT called when selection changed
413 //=======================================================================
414 void SMESHGUI_SingleEditDlg::onSelectionDone()
415 {
416   if (myBusy) return;
417
418   int anId1 = 0, anId2 = 0;
419
420   myOkBtn->setEnabled(false);
421   myApplyBtn->setEnabled(false);
422
423   SALOME_ListIO aList;
424   mySelectionMgr->selectedObjects(aList);
425
426   if (aList.Extent() != 1) {
427     myEdge->clear();
428     return;
429   }
430
431   Handle(SALOME_InteractiveObject) anIO = aList.First();
432   myActor = SMESH::FindActorByEntry(anIO->getEntry());
433   if(myActor){
434     TVisualObjPtr aVisualObj = myActor->GetObject();
435     if(SMDS_Mesh* aMesh = aVisualObj->GetMesh())
436     {
437       const SMDS_MeshElement* tria[2];
438       if( SMESH::GetEdgeNodes( mySelector, aVisualObj, anId1, anId2 ) >= 1 &&
439           findTriangles( aMesh->FindNode( anId1 ), aMesh->FindNode( anId2 ), tria[0],tria[1] ) )
440       {
441         QString aText = QString("%1-%2").arg(anId1).arg(anId2);
442         myBusy = true;
443         myEdge->setText(aText);
444         myBusy = false;
445         
446         myOkBtn->setEnabled(true);
447         myApplyBtn->setEnabled(true);
448       }
449       else
450       {
451         myEdge->clear();
452       }
453     }
454   }
455 }
456
457 //=======================================================================
458 // name    : onDeactivate()
459 // Purpose : SLOT called when dialog must be deativated
460 //=======================================================================
461 void SMESHGUI_SingleEditDlg::onDeactivate()
462 {
463   setEnabled(false);
464 }
465
466 //=======================================================================
467 // name    : enterEvent()
468 // Purpose : Event filter
469 //=======================================================================
470 void SMESHGUI_SingleEditDlg::enterEvent (QEvent*)
471 {
472   if (!isEnabled()) {
473     mySMESHGUI->EmitSignalDeactivateDialog();
474     if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ))
475       aViewWindow->SetSelectionMode(EdgeOfCellSelection);
476     setEnabled(true);
477   }
478 }
479
480 //=================================================================================
481 // function : closeEvent()
482 // purpose  :
483 //=================================================================================
484 void SMESHGUI_SingleEditDlg::closeEvent (QCloseEvent*)
485 {
486   onClose();
487 }
488
489 //=======================================================================
490 //function : hideEvent()
491 //purpose  : caused by ESC key
492 //=======================================================================
493 void SMESHGUI_SingleEditDlg::hideEvent (QHideEvent*)
494 {
495   if (!isMinimized())
496     onClose();
497 }
498
499 //=================================================================================
500 // function : onApply()
501 // purpose  : SLOT. Called when apply button is pressed
502 //=================================================================================
503 bool SMESHGUI_SingleEditDlg::onApply()
504 {
505   if (mySMESHGUI->isActiveStudyLocked())
506     return false;
507   // verify validity of input data
508   if (!isValid(true))
509     return false;
510
511   // get mesh, actor and nodes
512   SALOME_ListIO aList;
513   mySelectionMgr->selectedObjects(aList);
514
515   SMESH::SMESH_Mesh_var aMesh = SMESH::GetMeshByIO(aList.First());
516
517   if (aMesh->_is_nil()) {
518     SUIT_MessageBox::info1(SMESH::GetDesktop(mySMESHGUI), 
519                            tr("SMESH_ERROR"),
520                            tr("SMESHG_NO_MESH"), 
521                            tr("SMESH_BUT_OK"));
522     return false;
523   }
524
525   SMESH::SMESH_MeshEditor_var aMeshEditor = aMesh->GetMeshEditor();
526   int anId1= 0, anId2 = 0;
527   if (aMeshEditor->_is_nil() || !getNodeIds(myEdge->text(), anId1, anId2))
528     return false;
529
530   // perform operation
531   bool aResult = process(aMeshEditor.in(), anId1, anId2);
532
533   // update actor
534   if (aResult) {
535     mySelector->ClearIndex();
536     mySelectionMgr->setSelectedObjects(aList, false);
537     onSelectionDone();
538     SMESH::UpdateView();
539   }
540
541   return aResult;
542 }
543
544 //=================================================================================
545 // function : keyPressEvent()
546 // purpose  :
547 //=================================================================================
548 void SMESHGUI_SingleEditDlg::keyPressEvent( QKeyEvent* e )
549 {
550   QDialog::keyPressEvent( e );
551   if ( e->isAccepted() )
552     return;
553
554   if ( e->key() == Key_F1 )
555     {
556       e->accept();
557       onHelp();
558     }
559 }
560
561 /*!
562  *  Class       : SMESHGUI_TrianglesInversionDlg
563  *  Description : Inversion of the diagonal of a pseudo-quadrangle formed by
564  *                2 neighboring triangles with 1 common edge
565  */
566
567 SMESHGUI_TrianglesInversionDlg
568 ::SMESHGUI_TrianglesInversionDlg(SMESHGUI* theModule, 
569                                  const char* theName)
570 : SMESHGUI_SingleEditDlg(theModule,theName)
571 {
572   setCaption(tr("CAPTION"));
573   myHelpFileName = "/files/diagonal_iversion_of_elements.htm";
574 }
575
576 SMESHGUI_TrianglesInversionDlg::~SMESHGUI_TrianglesInversionDlg()
577 {
578 }
579
580 bool SMESHGUI_TrianglesInversionDlg::process (SMESH::SMESH_MeshEditor_ptr theMeshEditor,
581                                               const int theId1, const int theId2)
582 {
583   return theMeshEditor->InverseDiag(theId1, theId2);
584 }
585
586 /*!
587  *  Class       : SMESHGUI_UnionOfTwoTrianglesDlg
588  *  Description : Construction of a quadrangle by deletion of the
589  *                common border of 2 neighboring triangles
590  */
591
592 SMESHGUI_UnionOfTwoTrianglesDlg
593 ::SMESHGUI_UnionOfTwoTrianglesDlg(SMESHGUI* theModule, 
594                                   const char* theName)
595 : SMESHGUI_SingleEditDlg(theModule,theName)
596 {
597   setCaption(tr("CAPTION"));
598   myHelpFileName = "/files/uniting_two_triangles.htm";
599 }
600
601 SMESHGUI_UnionOfTwoTrianglesDlg::~SMESHGUI_UnionOfTwoTrianglesDlg()
602 {
603 }
604
605 bool SMESHGUI_UnionOfTwoTrianglesDlg::process (SMESH::SMESH_MeshEditor_ptr theMeshEditor,
606                                                const int theId1, const int theId2)
607 {
608   return theMeshEditor->DeleteDiag(theId1, theId2);
609 }