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