Salome HOME
Improve viewer performance
[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_Operation.h"
13 #include "ModuleBase_IWorkshop.h"
14 #include "ModuleBase_IModule.h"
15 #include <ModuleBase_IViewer.h>
16 #include "ModuleBase_OperationDescription.h"
17 #include "ModuleBase_OperationFeature.h"
18
19 #include "ModelAPI_CompositeFeature.h"
20 #include "ModelAPI_Session.h"
21
22 #include <QMessageBox>
23 #include <QApplication>
24 #include <QKeyEvent>
25
26 XGUI_OperationMgr::XGUI_OperationMgr(QObject* theParent,
27                                      ModuleBase_IWorkshop* theWorkshop)
28 : QObject(theParent), myIsValidationLock(false), myIsApplyEnabled(false),
29   myWorkshop(theWorkshop)
30 {
31 }
32
33 XGUI_OperationMgr::~XGUI_OperationMgr()
34 {
35 }
36
37 ModuleBase_Operation* XGUI_OperationMgr::currentOperation() const
38 {
39   return myOperations.count() > 0 ? myOperations.last() : 0;
40 }
41
42 bool XGUI_OperationMgr::isCurrentOperation(ModuleBase_Operation* theOperation)
43 {
44   if(!hasOperation())
45     return false;
46   return currentOperation() == theOperation;
47 }
48
49 bool XGUI_OperationMgr::hasOperation() const
50 {
51   return !myOperations.isEmpty() && (myOperations.last() != NULL);
52 }
53
54 bool XGUI_OperationMgr::hasOperation(const QString& theId) const
55 {
56   foreach(ModuleBase_Operation* aOp, myOperations) {
57     if (aOp->id() == theId)
58       return true;
59   }
60   return false;
61 }
62
63 ModuleBase_Operation* XGUI_OperationMgr::findOperation(const QString& theId) const
64 {
65   foreach(ModuleBase_Operation* aOp, myOperations) {
66     if (aOp->id() == theId)
67       return aOp;
68   }
69   return 0;
70 }
71
72
73 int XGUI_OperationMgr::operationsCount() const
74 {
75   return myOperations.count();
76 }
77
78 QStringList XGUI_OperationMgr::operationList() const
79 {
80   QStringList result;
81   foreach(ModuleBase_Operation* eachOperation, myOperations) {
82     ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>(eachOperation);
83     if (aFOperation) {
84       FeaturePtr aFeature = aFOperation->feature();
85       if(aFeature) {
86         result << QString::fromStdString(aFeature->getKind());
87       }
88     }
89   }
90   return result;
91 }
92
93 ModuleBase_Operation* XGUI_OperationMgr::previousOperation(ModuleBase_Operation* theOperation) const
94 {
95   int idx = myOperations.lastIndexOf(theOperation);
96   if(idx == -1 || idx == 0) {
97     return NULL;
98   }
99   return myOperations.at(idx - 1);
100 }
101
102 bool XGUI_OperationMgr::eventFilter(QObject *theObject, QEvent *theEvent)
103 {
104   if (theEvent->type() == QEvent::KeyRelease) {
105     QKeyEvent* aKeyEvent = dynamic_cast<QKeyEvent*>(theEvent);
106     if(aKeyEvent) {
107       return onKeyReleased(aKeyEvent);
108     }
109   }
110   return QObject::eventFilter(theObject, theEvent);
111 }
112
113 bool XGUI_OperationMgr::startOperation(ModuleBase_Operation* theOperation)
114 {
115   if (hasOperation())
116     currentOperation()->postpone();
117   myOperations.append(theOperation);
118
119   connect(theOperation, SIGNAL(started()), SLOT(onOperationStarted()));
120   connect(theOperation, SIGNAL(aborted()), SLOT(onOperationAborted()));
121   connect(theOperation, SIGNAL(committed()), SLOT(onOperationCommitted()));
122   connect(theOperation, SIGNAL(stopped()), SLOT(onOperationStopped()));
123   connect(theOperation, SIGNAL(resumed()), SLOT(onOperationResumed()));
124   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
125                                                                         (theOperation);
126   if (aFOperation)
127     connect(aFOperation, SIGNAL(activatedByPreselection()),
128             SIGNAL(operationActivatedByPreselection()));
129
130   theOperation->start();
131   onValidateOperation();
132   return true;
133 }
134
135 bool XGUI_OperationMgr::abortAllOperations()
136 {
137   bool aResult = true;
138   if(!hasOperation())
139     return aResult;
140
141   if (operationsCount() == 1) {
142     ModuleBase_Operation* aCurrentOperation = currentOperation();
143     if (canStopOperation(aCurrentOperation)) {
144       abortOperation(aCurrentOperation);
145     }
146     else
147       aResult = false;
148   }
149   else {
150     aResult = QMessageBox::question(qApp->activeWindow(),
151                                     tr("Abort operation"),
152                                     tr("All active operations will be aborted."),
153                                     QMessageBox::Ok | QMessageBox::Cancel,
154                                     QMessageBox::Cancel) == QMessageBox::Ok;
155     while(aResult && hasOperation()) {
156       abortOperation(currentOperation());
157     }
158   }
159   return aResult;
160 }
161
162 bool XGUI_OperationMgr::commitAllOperations()
163 {
164   bool isCompositeCommitted = false;
165   while (hasOperation()) {
166     ModuleBase_Operation* anOperation = currentOperation();
167     if (isApplyEnabled()) {
168       onCommitOperation();
169     } else {
170       abortOperation(anOperation);
171     }
172     ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
173                                                                             (anOperation);
174     if (aFOperation) {
175       FeaturePtr aFeature = aFOperation->feature();
176       CompositeFeaturePtr aComposite = 
177           std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aFeature);
178       isCompositeCommitted = aComposite.get();
179       if (isCompositeCommitted)
180         break;
181     }
182   }
183   return true;
184 }
185
186 void XGUI_OperationMgr::onValidateOperation()
187 {
188   if (!hasOperation())
189     return;
190   //ModuleBase_Operation* anOperation = currentOperation();
191   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
192                                                                           (currentOperation());
193   if(aFOperation && aFOperation->feature().get()) {
194     //bool aCanCommit = myWorkshop->module()->canCommitOperation();
195     //setApplyEnabled(!myIsValidationLock && aCanCommit && anOperation->isValid());
196     setApplyEnabled(myWorkshop->module()->getFeatureError(aFOperation->feature()).isEmpty());
197   }
198 }
199
200 void XGUI_OperationMgr::setLockValidating(bool toLock)
201 {
202   myIsValidationLock = toLock;
203   onValidateOperation();
204 }
205
206 void XGUI_OperationMgr::setApplyEnabled(const bool theEnabled)
207 {
208   myIsApplyEnabled = theEnabled;
209   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
210                                                                           (currentOperation());
211   if (aFOperation) {
212     workshop()->errorMgr()->updateActions(aFOperation->feature());
213   }
214   //emit validationStateChanged(theEnabled);
215 }
216
217 void XGUI_OperationMgr::updateApplyOfOperations(ModuleBase_Operation* theOperation)
218 {
219   XGUI_ErrorMgr* anErrorMgr = workshop()->errorMgr();
220   if (theOperation) {
221     ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>(theOperation);
222     if (aFOperation)
223       anErrorMgr->updateAcceptAllAction(aFOperation->feature());
224     //emit nestedStateChanged(theOperation->getDescription()->operationId().toStdString(),
225     //                        theOperation->isValid());
226   }
227   else {
228     foreach(ModuleBase_Operation* anOperation, myOperations) {
229       if (anOperation)
230         updateApplyOfOperations(anOperation);
231       //emit nestedStateChanged(anOperation->getDescription()->operationId().toStdString(),
232       //                        anOperation->isValid());
233     }
234   }
235 }
236
237 bool XGUI_OperationMgr::isApplyEnabled() const
238 {
239   return myIsApplyEnabled;
240 }
241
242 bool XGUI_OperationMgr::isParentOperationValid() const
243 {
244   bool isValid = false;
245   // the enable state of the parent operation of the nested one is defined by the rules that
246   // firstly there are nested operations and secondly the parent operation is valid
247   ModuleBase_Operation* aPrevOp = 0;
248   Operations::const_iterator anIt = myOperations.end();
249   if (anIt != myOperations.begin()) { // there are items in the operations list
250     --anIt;
251     aPrevOp = *anIt; // the last top operation, the operation which is started
252     if (anIt != myOperations.begin()) { // find the operation where the started operation is nested
253       --anIt;
254       aPrevOp = *anIt;
255     }
256   }
257   return aPrevOp && aPrevOp->isValid();
258 }
259
260 bool XGUI_OperationMgr::canStopOperation(ModuleBase_Operation* theOperation)
261 {
262   //in case of nested (sketch) operation no confirmation needed
263   if (isGrantedOperation(theOperation))
264     return true;
265   if (theOperation && theOperation->isModified()) {
266     QString aMessage = tr("%1 operation will be aborted.").arg(theOperation->id());
267     int anAnswer = QMessageBox::question(qApp->activeWindow(),
268                                          tr("Abort operation"),
269                                          aMessage,
270                                          QMessageBox::Ok | QMessageBox::Cancel,
271                                          QMessageBox::Cancel);
272     return anAnswer == QMessageBox::Ok;
273   }
274   return true;
275 }
276
277 bool XGUI_OperationMgr::commitOperation()
278 {
279   if (hasOperation() && currentOperation()->isValid()) {
280     onCommitOperation();
281     return true;
282   }
283   return false;
284 }
285
286 void XGUI_OperationMgr::resumeOperation(ModuleBase_Operation* theOperation)
287 {
288   theOperation->resume();
289 }
290
291 bool XGUI_OperationMgr::isGrantedOperation(ModuleBase_Operation* theOperation)
292 {
293   bool isGranted = false;
294
295   QListIterator<ModuleBase_Operation*> anIt(myOperations);
296   anIt.toBack();
297   ModuleBase_Operation* aPreviousOperation = 0;
298   while (anIt.hasPrevious()) {
299     ModuleBase_Operation* anOp = anIt.previous();
300     if (anOp == theOperation) {
301       if (anIt.hasPrevious())
302         aPreviousOperation = anIt.previous();
303       break;
304     }
305   }
306   if (aPreviousOperation)
307     isGranted = aPreviousOperation->isGranted(theOperation->id());
308
309   return isGranted;
310 }
311
312 bool XGUI_OperationMgr::canStartOperation(const QString& theId, const bool isAdditionallyGranted)
313 {
314   bool aCanStart = true;
315   ModuleBase_Operation* aCurrentOp = currentOperation();
316   if (aCurrentOp) {
317     bool aGranted = aCurrentOp->isGranted(theId) || isAdditionallyGranted;
318     if (!aGranted) {
319       if (canStopOperation(aCurrentOp)) {
320         if (myIsApplyEnabled && aCurrentOp->isModified())
321           aCurrentOp->commit();
322         else
323           abortOperation(aCurrentOp);
324       } else {
325         aCanStart = false;
326       }
327     }
328   }
329   return aCanStart;
330 }
331
332 void XGUI_OperationMgr::abortOperation(ModuleBase_Operation* theOperation)
333 {
334   ModuleBase_Operation* aCurrentOperation = currentOperation();
335   if (theOperation == aCurrentOperation)
336     theOperation->abort();
337   else {
338     // it is possible to trigger upper operation(e.g. sketch, current is sketch line)
339     // all operation from the current to triggered should also be aborted
340     // operations over the parameter one are not aborted(e.g. extrusion cut, sketch abort)
341     while(hasOperation()) {
342       ModuleBase_Operation* aCurrentOperation = currentOperation();
343       aCurrentOperation->abort();
344       if(theOperation == aCurrentOperation)
345         break;
346     }
347   }
348 }
349
350 void XGUI_OperationMgr::onCommitOperation()
351 {
352   ModuleBase_Operation* anOperation = currentOperation();
353   if (anOperation)
354     anOperation->commit();
355 }
356
357 void XGUI_OperationMgr::onAbortOperation()
358 {
359   ModuleBase_Operation* aCurrentOperation = currentOperation();
360   if (aCurrentOperation && canStopOperation(aCurrentOperation)) {
361     abortOperation(aCurrentOperation);
362   }
363 }
364
365 void XGUI_OperationMgr::onOperationStarted()
366 {
367   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
368   updateApplyOfOperations(aSenderOperation);
369   emit operationStarted(aSenderOperation);
370 }
371
372 void XGUI_OperationMgr::onOperationAborted()
373 {
374   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
375   emit operationAborted(aSenderOperation);
376 }
377
378 void XGUI_OperationMgr::onOperationCommitted()
379 {
380   // apply state for all features from the stack of operations should be updated
381   updateApplyOfOperations();
382
383   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
384   emit operationCommitted(aSenderOperation);
385 }
386
387 void XGUI_OperationMgr::onOperationResumed()
388 {
389   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
390   emit operationResumed(aSenderOperation);
391 }
392
393 void XGUI_OperationMgr::onOperationStopped()
394 {
395   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
396   ModuleBase_Operation* aCurrentOperation = currentOperation();
397   if (!aSenderOperation || !aCurrentOperation || aSenderOperation != aCurrentOperation)
398     return;
399
400   myOperations.removeAll(aCurrentOperation);
401   aCurrentOperation->deleteLater();
402
403   emit operationStopped(aCurrentOperation);
404
405   // get last operation which can be resumed
406   ModuleBase_Operation* aResultOp = 0;
407   QListIterator<ModuleBase_Operation*> anIt(myOperations);
408   anIt.toBack();
409   while (anIt.hasPrevious()) {
410     ModuleBase_Operation* anOp = anIt.previous();
411     if (anOp) {
412       aResultOp = anOp;
413       break;
414     }
415   }
416   if (aResultOp) {
417     bool isModified = aCurrentOperation->isModified();
418     aResultOp->setIsModified(aResultOp->isModified() || isModified);
419     resumeOperation(aResultOp);
420     onValidateOperation();
421   }
422 }
423
424 bool XGUI_OperationMgr::onKeyReleased(QKeyEvent* theEvent)
425 {
426   // Let the manager decide what to do with the given key combination.
427   ModuleBase_Operation* anOperation = currentOperation();
428   bool isAccepted = true;
429   switch (theEvent->key()) {
430     case Qt::Key_Return:
431     case Qt::Key_Enter: {
432       emit keyEnterReleased();
433       commitOperation();
434     }
435     case Qt::Key_N:
436     case Qt::Key_P: {
437       bool noModifiers = (theEvent->modifiers() == Qt::NoModifier);
438       if (noModifiers) {
439         ModuleBase_IViewer* aViewer = myWorkshop->viewer();
440         Handle(AIS_InteractiveContext) aContext = aViewer->AISContext();
441         if (!aContext.IsNull()) {
442           Handle(V3d_View) aView = aViewer->activeView();
443           if ((theEvent->key() == Qt::Key_N))
444             aContext->HilightNextDetected(aView);
445           else if ((theEvent->key() == Qt::Key_P))
446             aContext->HilightPreviousDetected(aView);
447         }
448       }
449     }
450
451     break;
452     default:
453       isAccepted = false;
454       break;
455   }
456   //if(anOperation) {
457   //  anOperation->keyReleased(theEvent->key());
458   //}
459   return isAccepted;
460 }
461
462 XGUI_Workshop* XGUI_OperationMgr::workshop() const
463 {
464   XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myWorkshop);
465   return aConnector->workshop();
466 }
467