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