Salome HOME
Enter processing from object browser.
[modules/shaper.git] / src / XGUI / XGUI_OperationMgr.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
2
3 // File:        XGUI_OperationMgr.cpp
4 // Created:     20 Apr 2014
5 // Author:      Natalia ERMOLAEVA
6
7 #include "XGUI_OperationMgr.h"
8 #include "XGUI_ModuleConnector.h"
9 #include "XGUI_Workshop.h"
10 #include "XGUI_ErrorMgr.h"
11
12 #include <ModuleBase_IPropertyPanel.h>
13 #include <ModuleBase_ModelWidget.h>
14 #include "ModuleBase_Operation.h"
15 #include "ModuleBase_IWorkshop.h"
16 #include "ModuleBase_IModule.h"
17 #include <ModuleBase_IViewer.h>
18 #include "ModuleBase_OperationDescription.h"
19 #include "ModuleBase_OperationFeature.h"
20
21 #include "ModelAPI_CompositeFeature.h"
22 #include "ModelAPI_Session.h"
23
24 #include <QMessageBox>
25 #include <QApplication>
26 #include <QKeyEvent>
27
28 XGUI_OperationMgr::XGUI_OperationMgr(QObject* theParent,
29                                      ModuleBase_IWorkshop* theWorkshop)
30 : QObject(theParent), myIsValidationLock(false), myIsApplyEnabled(false),
31   myWorkshop(theWorkshop)
32 {
33 }
34
35 XGUI_OperationMgr::~XGUI_OperationMgr()
36 {
37 }
38
39 ModuleBase_Operation* XGUI_OperationMgr::currentOperation() const
40 {
41   return myOperations.count() > 0 ? myOperations.last() : 0;
42 }
43
44 bool XGUI_OperationMgr::isCurrentOperation(ModuleBase_Operation* theOperation)
45 {
46   if(!hasOperation())
47     return false;
48   return currentOperation() == theOperation;
49 }
50
51 bool XGUI_OperationMgr::hasOperation() const
52 {
53   return !myOperations.isEmpty() && (myOperations.last() != NULL);
54 }
55
56 bool XGUI_OperationMgr::hasOperation(const QString& theId) const
57 {
58   foreach(ModuleBase_Operation* aOp, myOperations) {
59     if (aOp->id() == theId)
60       return true;
61   }
62   return false;
63 }
64
65 ModuleBase_Operation* XGUI_OperationMgr::findOperation(const QString& theId) const
66 {
67   foreach(ModuleBase_Operation* aOp, myOperations) {
68     if (aOp->id() == theId)
69       return aOp;
70   }
71   return 0;
72 }
73
74
75 int XGUI_OperationMgr::operationsCount() const
76 {
77   return myOperations.count();
78 }
79
80 QStringList XGUI_OperationMgr::operationList() const
81 {
82   QStringList result;
83   foreach(ModuleBase_Operation* eachOperation, myOperations) {
84     ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>(eachOperation);
85     if (aFOperation) {
86       FeaturePtr aFeature = aFOperation->feature();
87       if(aFeature) {
88         result << QString::fromStdString(aFeature->getKind());
89       }
90     }
91   }
92   return result;
93 }
94
95 ModuleBase_Operation* XGUI_OperationMgr::previousOperation(ModuleBase_Operation* theOperation) const
96 {
97   int idx = myOperations.lastIndexOf(theOperation);
98   if(idx == -1 || idx == 0) {
99     return NULL;
100   }
101   return myOperations.at(idx - 1);
102 }
103
104 bool XGUI_OperationMgr::eventFilter(QObject *theObject, QEvent *theEvent)
105 {
106   if (theEvent->type() == QEvent::KeyRelease) {
107     QKeyEvent* aKeyEvent = dynamic_cast<QKeyEvent*>(theEvent);
108     if(aKeyEvent) {
109       return onKeyReleased(aKeyEvent);
110     }
111   }
112   return QObject::eventFilter(theObject, theEvent);
113 }
114
115 bool XGUI_OperationMgr::startOperation(ModuleBase_Operation* theOperation)
116 {
117   if (hasOperation())
118     currentOperation()->postpone();
119   myOperations.append(theOperation);
120
121   connect(theOperation, SIGNAL(beforeStarted()), SLOT(onBeforeOperationStarted()));
122   connect(theOperation, SIGNAL(beforeAborted()), SLOT(onBeforeOperationAborted()));
123   connect(theOperation, SIGNAL(beforeCommitted()), SLOT(onBeforeOperationCommitted()));
124
125   connect(theOperation, SIGNAL(started()), SLOT(onOperationStarted()));
126   connect(theOperation, SIGNAL(aborted()), SLOT(onOperationAborted()));
127   connect(theOperation, SIGNAL(committed()), SLOT(onOperationCommitted()));
128
129   connect(theOperation, SIGNAL(stopped()), SLOT(onOperationStopped()));
130   connect(theOperation, SIGNAL(resumed()), SLOT(onOperationResumed()));
131   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
132                                                                         (theOperation);
133   if (aFOperation)
134     connect(aFOperation, SIGNAL(activatedByPreselection()),
135             SIGNAL(operationActivatedByPreselection()));
136
137   theOperation->start();
138   onValidateOperation();
139   return true;
140 }
141
142 bool XGUI_OperationMgr::abortAllOperations()
143 {
144   bool aResult = true;
145   if(!hasOperation())
146     return aResult;
147
148   if (operationsCount() == 1) {
149     ModuleBase_Operation* aCurrentOperation = currentOperation();
150     if (canStopOperation(aCurrentOperation)) {
151       abortOperation(aCurrentOperation);
152     }
153     else
154       aResult = false;
155   }
156   else {
157     aResult = QMessageBox::question(qApp->activeWindow(),
158                                     tr("Abort operation"),
159                                     tr("All active operations will be aborted."),
160                                     QMessageBox::Ok | QMessageBox::Cancel,
161                                     QMessageBox::Cancel) == QMessageBox::Ok;
162     while(aResult && hasOperation()) {
163       abortOperation(currentOperation());
164     }
165   }
166   return aResult;
167 }
168
169 bool XGUI_OperationMgr::commitAllOperations()
170 {
171   bool isCompositeCommitted = false;
172   while (hasOperation()) {
173     ModuleBase_Operation* anOperation = currentOperation();
174     if (isApplyEnabled()) {
175       onCommitOperation();
176     } else {
177       abortOperation(anOperation);
178     }
179     ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
180                                                                             (anOperation);
181     if (aFOperation) {
182       FeaturePtr aFeature = aFOperation->feature();
183       CompositeFeaturePtr aComposite = 
184           std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aFeature);
185       isCompositeCommitted = aComposite.get();
186       if (isCompositeCommitted)
187         break;
188     }
189   }
190   return true;
191 }
192
193 void XGUI_OperationMgr::onValidateOperation()
194 {
195   if (!hasOperation())
196     return;
197   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
198                                                                           (currentOperation());
199   if(aFOperation && aFOperation->feature().get())
200     setApplyEnabled(myWorkshop->module()->getFeatureError(aFOperation->feature()).isEmpty());
201 }
202
203 void XGUI_OperationMgr::setLockValidating(bool toLock)
204 {
205   myIsValidationLock = toLock;
206   onValidateOperation();
207 }
208
209 void XGUI_OperationMgr::setApplyEnabled(const bool theEnabled)
210 {
211   myIsApplyEnabled = theEnabled;
212   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
213                                                                           (currentOperation());
214   if (aFOperation) {
215     workshop()->errorMgr()->updateActions(aFOperation->feature());
216   }
217   //emit validationStateChanged(theEnabled);
218 }
219
220 void XGUI_OperationMgr::updateApplyOfOperations(ModuleBase_Operation* theOperation)
221 {
222   XGUI_ErrorMgr* anErrorMgr = workshop()->errorMgr();
223   if (theOperation) {
224     ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>(theOperation);
225     if (aFOperation)
226       anErrorMgr->updateAcceptAllAction(aFOperation->feature());
227     //emit nestedStateChanged(theOperation->getDescription()->operationId().toStdString(),
228     //                        theOperation->isValid());
229   }
230   else {
231     foreach(ModuleBase_Operation* anOperation, myOperations) {
232       if (anOperation)
233         updateApplyOfOperations(anOperation);
234       //emit nestedStateChanged(anOperation->getDescription()->operationId().toStdString(),
235       //                        anOperation->isValid());
236     }
237   }
238 }
239
240 bool XGUI_OperationMgr::isApplyEnabled() const
241 {
242   return myIsApplyEnabled;
243 }
244
245 bool XGUI_OperationMgr::isParentOperationValid() const
246 {
247   bool isValid = false;
248   // the enable state of the parent operation of the nested one is defined by the rules that
249   // firstly there are nested operations and secondly the parent operation is valid
250   ModuleBase_Operation* aPrevOp = 0;
251   Operations::const_iterator anIt = myOperations.end();
252   if (anIt != myOperations.begin()) { // there are items in the operations list
253     --anIt;
254     aPrevOp = *anIt; // the last top operation, the operation which is started
255     if (anIt != myOperations.begin()) { // find the operation where the started operation is nested
256       --anIt;
257       aPrevOp = *anIt;
258     }
259   }
260   return aPrevOp && aPrevOp->isValid();
261 }
262
263 bool XGUI_OperationMgr::canStopOperation(ModuleBase_Operation* theOperation)
264 {
265   //in case of nested (sketch) operation no confirmation needed
266   if (isGrantedOperation(theOperation->id()))
267     return true;
268   if (theOperation && theOperation->isModified()) {
269     QString aMessage = tr("%1 operation will be aborted.").arg(theOperation->id());
270     int anAnswer = QMessageBox::question(qApp->activeWindow(),
271                                          tr("Abort operation"),
272                                          aMessage,
273                                          QMessageBox::Ok | QMessageBox::Cancel,
274                                          QMessageBox::Cancel);
275     return anAnswer == QMessageBox::Ok;
276   }
277   return true;
278 }
279
280 bool XGUI_OperationMgr::commitOperation()
281 {
282   if (hasOperation() && currentOperation()->isValid()) {
283     onCommitOperation();
284     return true;
285   }
286   return false;
287 }
288
289 void XGUI_OperationMgr::resumeOperation(ModuleBase_Operation* theOperation)
290 {
291   theOperation->resume();
292 }
293
294 bool XGUI_OperationMgr::isGrantedOperation(const QString& theId)
295 {
296   bool isGranted = false;
297
298   QListIterator<ModuleBase_Operation*> anIt(myOperations);
299   anIt.toBack();
300   ModuleBase_Operation* aPreviousOperation = 0;
301   while (anIt.hasPrevious()) {
302     ModuleBase_Operation* anOp = anIt.previous();
303     if (anOp)
304       isGranted = anOp->isGranted(theId);
305   }
306   return isGranted;
307 }
308
309 void XGUI_OperationMgr::setCurrentFeature(const FeaturePtr& theFeature)
310 {
311   SessionPtr aMgr = ModelAPI_Session::get();
312   DocumentPtr aDoc = aMgr->activeDocument();
313   bool aIsOp = aMgr->isOperation();
314   if (!aIsOp)
315     aMgr->startOperation();
316   aDoc->setCurrentFeature(theFeature, false);
317   if (!aIsOp)
318     aMgr->finishOperation();
319 }
320
321 bool XGUI_OperationMgr::canStartOperation(const QString& theId)
322 {
323   bool aCanStart = true;
324   ModuleBase_Operation* aCurrentOp = currentOperation();
325   if (aCurrentOp) {
326     bool aGranted = aCurrentOp->isGranted(theId);
327     // the started operation is granted for the current one,
328     // e.g. current - Sketch, started - Line
329     if (aGranted) {
330       aCanStart = true;
331     }
332     else {
333       if (!isGrantedOperation(theId)) {
334         // the operation is not granted in the current list of operations
335         // e.g. Edit Parameter when Sketch, Line in Sketch is active.
336         aCanStart = abortAllOperations();
337       }
338       else if (canStopOperation(aCurrentOp)) {
339         // the started operation is granted in the parrent operation,
340         // e.g. current - Line in Sketch, started Circle 
341         if (myIsApplyEnabled && aCurrentOp->isModified())
342           aCurrentOp->commit();
343         else
344           abortOperation(aCurrentOp);
345       } else {
346         aCanStart = false;
347       }
348     }
349   }
350   return aCanStart;
351 }
352
353 void XGUI_OperationMgr::abortOperation(ModuleBase_Operation* theOperation)
354 {
355   ModuleBase_Operation* aCurrentOperation = currentOperation();
356   if (theOperation == aCurrentOperation)
357     theOperation->abort();
358   else {
359     // it is possible to trigger upper operation(e.g. sketch, current is sketch line)
360     // all operation from the current to triggered should also be aborted
361     // operations over the parameter one are not aborted(e.g. extrusion cut, sketch abort)
362     while(hasOperation()) {
363       ModuleBase_Operation* aCurrentOperation = currentOperation();
364       aCurrentOperation->abort();
365       if(theOperation == aCurrentOperation)
366         break;
367     }
368   }
369 }
370
371 void XGUI_OperationMgr::onCommitOperation()
372 {
373   ModuleBase_Operation* anOperation = currentOperation();
374   if (anOperation)
375     anOperation->commit();
376 }
377
378 void XGUI_OperationMgr::onAbortOperation()
379 {
380   ModuleBase_Operation* aCurrentOperation = currentOperation();
381   if (aCurrentOperation && canStopOperation(aCurrentOperation)) {
382     abortOperation(aCurrentOperation);
383   }
384 }
385
386 void XGUI_OperationMgr::onBeforeOperationStarted()
387 {
388   ModuleBase_Operation* aCurrentOperation = dynamic_cast<ModuleBase_Operation*>(sender());
389   if (!aCurrentOperation)
390     return;
391
392   /// Set current feature and remeber old current feature
393   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>(aCurrentOperation);
394   if (aFOperation) {
395     SessionPtr aMgr = ModelAPI_Session::get();
396     DocumentPtr aDoc = aMgr->activeDocument();
397     // the parameter of current feature should be false, we should use all feature, not only visible
398     // in order to correctly save the previous feature of the nested operation, where the
399     // features can be not visible in the tree. The problem case is Edit sketch entitity(line)
400     // in the Sketch, created in ExtrusionCut operation. The entity disappears by commit.
401     // When sketch entity operation started, the sketch should be cashed here as the current.
402     // Otherwise(the flag is true), the ExtrusionCut is cashed, when commit happens, the sketch
403     // is disabled, sketch entity is disabled as extrusion cut is created earliest then sketch.
404     // As a result the sketch disappears from the viewer. However after commit it is displayed back.
405     aFOperation->setPreviousCurrentFeature(aDoc->currentFeature(false));
406     if (aFOperation->isEditOperation()) // it should be performed by the feature edit only
407       // in create operation, the current feature is changed by addFeature()
408       aDoc->setCurrentFeature(aFOperation->feature(), false);
409   }
410 }
411
412 void XGUI_OperationMgr::onOperationStarted()
413 {
414   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
415   updateApplyOfOperations(aSenderOperation);
416   emit operationStarted(aSenderOperation);
417 }
418
419 void XGUI_OperationMgr::onBeforeOperationAborted()
420 {
421   onBeforeOperationCommitted();
422 }
423
424 void XGUI_OperationMgr::onOperationAborted()
425 {
426   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
427   emit operationAborted(aSenderOperation);
428 }
429
430 void XGUI_OperationMgr::onBeforeOperationCommitted()
431 {
432   ModuleBase_Operation* aCurrentOperation = dynamic_cast<ModuleBase_Operation*>(sender());
433   if (!aCurrentOperation)
434     return;
435
436   /// Restore the previous current feature
437   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>(aCurrentOperation);
438   if (aFOperation) {
439     if (aFOperation->isEditOperation()) {
440       /// Restore the previous current feature
441       setCurrentFeature(aFOperation->previousCurrentFeature());
442     }
443     else { // create operation
444       // the Top created feature should stays the current. In nested operations, like Line in the Sketch or
445       // Sketch in ExtrusionCut, a previous feature should be restored on commit. It is performed here
446       // in order to perform it in the current transaction without opening a new one.
447       if (myOperations.front() != aFOperation)
448         setCurrentFeature(aFOperation->previousCurrentFeature());
449     }
450   }
451 }
452
453 void XGUI_OperationMgr::onOperationCommitted()
454 {
455   // apply state for all features from the stack of operations should be updated
456   updateApplyOfOperations();
457
458   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
459   emit operationCommitted(aSenderOperation);
460 }
461
462 void XGUI_OperationMgr::onOperationResumed()
463 {
464   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
465   emit operationResumed(aSenderOperation);
466 }
467
468 void XGUI_OperationMgr::onOperationStopped()
469 {
470   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
471   ModuleBase_Operation* aCurrentOperation = currentOperation();
472   if (!aSenderOperation || !aCurrentOperation || aSenderOperation != aCurrentOperation)
473     return;
474
475   myOperations.removeAll(aCurrentOperation);
476   aCurrentOperation->deleteLater();
477
478   emit operationStopped(aCurrentOperation);
479
480   // get last operation which can be resumed
481   ModuleBase_Operation* aResultOp = 0;
482   QListIterator<ModuleBase_Operation*> anIt(myOperations);
483   anIt.toBack();
484   while (anIt.hasPrevious()) {
485     ModuleBase_Operation* anOp = anIt.previous();
486     if (anOp) {
487       aResultOp = anOp;
488       break;
489     }
490   }
491   if (aResultOp) {
492     bool isModified = aCurrentOperation->isModified();
493     aResultOp->setIsModified(aResultOp->isModified() || isModified);
494     resumeOperation(aResultOp);
495     onValidateOperation();
496   }
497 }
498
499 bool XGUI_OperationMgr::onKeyReleased(QKeyEvent* theEvent)
500 {
501   qDebug("XGUI_OperationMgr::onKeyReleased");
502
503   QObject* aSender = sender();
504
505   // Let the manager decide what to do with the given key combination.
506   ModuleBase_Operation* anOperation = currentOperation();
507   bool isAccepted = true;
508   switch (theEvent->key()) {
509     case Qt::Key_Return:
510     case Qt::Key_Enter: {
511       qDebug("XGUI_OperationMgr::onKeyReleased: Key_Return");
512       ModuleBase_Operation* aOperation = currentOperation();
513       ModuleBase_IPropertyPanel* aPanel = aOperation->propertyPanel();
514       ModuleBase_ModelWidget* aActiveWgt = aPanel->activeWidget();
515       if (!aActiveWgt || !aActiveWgt->processEnter()) {
516         ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>(currentOperation());
517         if (!aFOperation || myWorkshop->module()->getFeatureError(aFOperation->feature()).isEmpty()) {
518           emit keyEnterReleased();
519           commitOperation();
520         }
521         else
522           isAccepted = false;
523       }
524       //else
525       //  isAccepted = false;
526     }
527     case Qt::Key_N:
528     case Qt::Key_P: {
529       bool noModifiers = (theEvent->modifiers() == Qt::NoModifier);
530       if (noModifiers) {
531         ModuleBase_IViewer* aViewer = myWorkshop->viewer();
532         Handle(AIS_InteractiveContext) aContext = aViewer->AISContext();
533         if (!aContext.IsNull()) {
534           Handle(V3d_View) aView = aViewer->activeView();
535           if ((theEvent->key() == Qt::Key_N))
536             aContext->HilightNextDetected(aView);
537           else if ((theEvent->key() == Qt::Key_P))
538             aContext->HilightPreviousDetected(aView);
539         }
540       }
541     }
542
543     break;
544     default:
545       isAccepted = false;
546       break;
547   }
548   //if(anOperation) {
549   //  anOperation->keyReleased(theEvent->key());
550   //}
551   return isAccepted;
552 }
553
554 XGUI_Workshop* XGUI_OperationMgr::workshop() const
555 {
556   XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myWorkshop);
557   return aConnector->workshop();
558 }
559