Salome HOME
New design of icons
[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     ModuleBase_Operation* aCurrentOperation = currentOperation();
139     if (canStopOperation(aCurrentOperation)) {
140       abortOperation(aCurrentOperation);
141     }
142     else
143       aResult = false;
144   }
145   else {
146     aResult = QMessageBox::question(qApp->activeWindow(),
147                                     tr("Abort operation"),
148                                     tr("All active operations will be aborted."),
149                                     QMessageBox::Ok | QMessageBox::Cancel,
150                                     QMessageBox::Cancel) == QMessageBox::Ok;
151     while(aResult && hasOperation()) {
152       abortOperation(currentOperation());
153     }
154   }
155   return aResult;
156 }
157
158 bool XGUI_OperationMgr::commitAllOperations()
159 {
160   bool isCompositeCommitted = false;
161   while (hasOperation()) {
162     ModuleBase_Operation* anOperation = currentOperation();
163     if (isApplyEnabled()) {
164       onCommitOperation();
165     } else {
166       abortOperation(anOperation);
167     }
168     ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
169                                                                             (anOperation);
170     if (aFOperation) {
171       FeaturePtr aFeature = aFOperation->feature();
172       CompositeFeaturePtr aComposite = 
173           std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aFeature);
174       isCompositeCommitted = aComposite.get();
175       if (isCompositeCommitted)
176         break;
177     }
178   }
179   return true;
180 }
181
182 void XGUI_OperationMgr::onValidateOperation()
183 {
184   if (!hasOperation())
185     return;
186   ModuleBase_Operation* anOperation = currentOperation();
187   if(anOperation) {
188     bool aCanCommit = myWorkshop->module()->canCommitOperation();
189     setApplyEnabled(!myIsValidationLock && aCanCommit && anOperation->isValid());
190   }
191 }
192
193 void XGUI_OperationMgr::setLockValidating(bool toLock)
194 {
195   myIsValidationLock = toLock;
196   onValidateOperation();
197 }
198
199 void XGUI_OperationMgr::setApplyEnabled(const bool theEnabled)
200 {
201   myIsApplyEnabled = theEnabled;
202   emit validationStateChanged(theEnabled);
203 }
204
205 void XGUI_OperationMgr::updateApplyOfOperations(ModuleBase_Operation* theOperation)
206 {
207   if (theOperation)
208     emit nestedStateChanged(theOperation->getDescription()->operationId().toStdString(),
209                             theOperation->isValid());
210   else {
211     foreach(ModuleBase_Operation* anOperation, myOperations) {
212       emit nestedStateChanged(anOperation->getDescription()->operationId().toStdString(),
213                               anOperation->isValid());
214     }
215   }
216 }
217
218 bool XGUI_OperationMgr::isApplyEnabled() const
219 {
220   return myIsApplyEnabled;
221 }
222
223 bool XGUI_OperationMgr::isParentOperationValid() const
224 {
225   bool isValid = false;
226   // the enable state of the parent operation of the nested one is defined by the rules that
227   // firstly there are nested operations and secondly the parent operation is valid
228   ModuleBase_Operation* aPrevOp = 0;
229   Operations::const_iterator anIt = myOperations.end();
230   if (anIt != myOperations.begin()) { // there are items in the operations list
231     --anIt;
232     aPrevOp = *anIt; // the last top operation, the operation which is started
233     if (anIt != myOperations.begin()) { // find the operation where the started operation is nested
234       --anIt;
235       aPrevOp = *anIt;
236     }
237   }
238   return aPrevOp && aPrevOp->isValid();
239 }
240
241 bool XGUI_OperationMgr::canStopOperation(ModuleBase_Operation* theOperation)
242 {
243   //in case of nested (sketch) operation no confirmation needed
244   if (isGrantedOperation(theOperation))
245     return true;
246   if (theOperation && theOperation->isModified()) {
247     QString aMessage = tr("%1 operation will be aborted.").arg(theOperation->id());
248     int anAnswer = QMessageBox::question(qApp->activeWindow(),
249                                          tr("Abort operation"),
250                                          aMessage,
251                                          QMessageBox::Ok | QMessageBox::Cancel,
252                                          QMessageBox::Cancel);
253     return anAnswer == QMessageBox::Ok;
254   }
255   return true;
256 }
257
258 bool XGUI_OperationMgr::commitOperation()
259 {
260   if (hasOperation() && currentOperation()->isValid()) {
261     onCommitOperation();
262     return true;
263   }
264   return false;
265 }
266
267 void XGUI_OperationMgr::resumeOperation(ModuleBase_Operation* theOperation)
268 {
269   theOperation->resume();
270 }
271
272 bool XGUI_OperationMgr::isGrantedOperation(ModuleBase_Operation* theOperation)
273 {
274   bool isGranted = false;
275
276   QListIterator<ModuleBase_Operation*> anIt(myOperations);
277   anIt.toBack();
278   ModuleBase_Operation* aPreviousOperation = 0;
279   while (anIt.hasPrevious()) {
280     ModuleBase_Operation* anOp = anIt.previous();
281     if (anOp == theOperation) {
282       if (anIt.hasPrevious())
283         aPreviousOperation = anIt.previous();
284       break;
285     }
286   }
287   if (aPreviousOperation)
288     isGranted = aPreviousOperation->isGranted(theOperation->id());
289
290   return isGranted;
291 }
292
293 bool XGUI_OperationMgr::canStartOperation(const QString& theId, const bool isAdditionallyGranted)
294 {
295   bool aCanStart = true;
296   ModuleBase_Operation* aCurrentOp = currentOperation();
297   if (aCurrentOp) {
298     bool aGranted = aCurrentOp->isGranted(theId) || isAdditionallyGranted;
299     if (!aGranted) {
300       if (canStopOperation(aCurrentOp)) {
301         if (myIsApplyEnabled && aCurrentOp->isModified())
302           aCurrentOp->commit();
303         else
304           abortOperation(aCurrentOp);
305       } else {
306         aCanStart = false;
307       }
308     }
309   }
310   return aCanStart;
311 }
312
313 void XGUI_OperationMgr::abortOperation(ModuleBase_Operation* theOperation)
314 {
315   ModuleBase_Operation* aCurrentOperation = currentOperation();
316   if (theOperation == aCurrentOperation)
317     theOperation->abort();
318   else {
319     // it is possible to trigger upper operation(e.g. sketch, current is sketch line)
320     // all operation from the current to triggered should also be aborted
321     // operations over the parameter one are not aborted(e.g. extrusion cut, sketch abort)
322     while(hasOperation()) {
323       ModuleBase_Operation* aCurrentOperation = currentOperation();
324       aCurrentOperation->abort();
325       if(theOperation == aCurrentOperation)
326         break;
327     }
328   }
329 }
330
331 void XGUI_OperationMgr::onCommitOperation()
332 {
333   ModuleBase_Operation* anOperation = currentOperation();
334   if (anOperation)
335     anOperation->commit();
336 }
337
338 void XGUI_OperationMgr::onAbortOperation()
339 {
340   ModuleBase_Operation* aCurrentOperation = currentOperation();
341   if (aCurrentOperation && canStopOperation(aCurrentOperation)) {
342     abortOperation(aCurrentOperation);
343   }
344 }
345
346 void XGUI_OperationMgr::onOperationStarted()
347 {
348   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
349   updateApplyOfOperations(aSenderOperation);
350   emit operationStarted(aSenderOperation);
351 }
352
353 void XGUI_OperationMgr::onOperationAborted()
354 {
355   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
356   emit operationAborted(aSenderOperation);
357 }
358
359 void XGUI_OperationMgr::onOperationCommitted()
360 {
361   updateApplyOfOperations();
362
363   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
364   emit operationCommitted(aSenderOperation);
365 }
366
367 void XGUI_OperationMgr::onOperationResumed()
368 {
369   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
370   emit operationResumed(aSenderOperation);
371 }
372
373 void XGUI_OperationMgr::onOperationStopped()
374 {
375   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
376   ModuleBase_Operation* aCurrentOperation = currentOperation();
377   if (!aSenderOperation || !aCurrentOperation || aSenderOperation != aCurrentOperation)
378     return;
379
380   myOperations.removeAll(aCurrentOperation);
381   aCurrentOperation->deleteLater();
382
383   emit operationStopped(aCurrentOperation);
384
385   // get last operation which can be resumed
386   ModuleBase_Operation* aResultOp = 0;
387   QListIterator<ModuleBase_Operation*> anIt(myOperations);
388   anIt.toBack();
389   while (anIt.hasPrevious()) {
390     ModuleBase_Operation* anOp = anIt.previous();
391     if (anOp) {
392       aResultOp = anOp;
393       break;
394     }
395   }
396   if (aResultOp) {
397     bool isModified = aCurrentOperation->isModified();
398     aResultOp->setIsModified(aResultOp->isModified() || isModified);
399     resumeOperation(aResultOp);
400     onValidateOperation();
401   }
402 }
403
404 bool XGUI_OperationMgr::onKeyReleased(QKeyEvent* theEvent)
405 {
406   // Let the manager decide what to do with the given key combination.
407   ModuleBase_Operation* anOperation = currentOperation();
408   bool isAccepted = true;
409   switch (theEvent->key()) {
410     case Qt::Key_Return:
411     case Qt::Key_Enter: {
412       emit keyEnterReleased();
413       commitOperation();
414     }
415     break;
416     default:
417       isAccepted = false;
418       break;
419   }
420   //if(anOperation) {
421   //  anOperation->keyReleased(theEvent->key());
422   //}
423   return isAccepted;
424 }
425