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