1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
3 // File: XGUI_OperationMgr.cpp
4 // Created: 20 Apr 2014
5 // Author: Natalia ERMOLAEVA
7 #include "XGUI_OperationMgr.h"
8 #include "XGUI_ModuleConnector.h"
9 #include "XGUI_Workshop.h"
10 #include "XGUI_ErrorMgr.h"
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"
21 #include "ModelAPI_CompositeFeature.h"
22 #include "ModelAPI_Session.h"
24 #include <QMessageBox>
25 #include <QApplication>
28 XGUI_OperationMgr::XGUI_OperationMgr(QObject* theParent,
29 ModuleBase_IWorkshop* theWorkshop)
30 : QObject(theParent), myIsApplyEnabled(false), myWorkshop(theWorkshop)
34 XGUI_OperationMgr::~XGUI_OperationMgr()
38 ModuleBase_Operation* XGUI_OperationMgr::currentOperation() const
40 return myOperations.count() > 0 ? myOperations.last() : 0;
43 bool XGUI_OperationMgr::isCurrentOperation(ModuleBase_Operation* theOperation)
47 return currentOperation() == theOperation;
50 bool XGUI_OperationMgr::hasOperation() const
52 return !myOperations.isEmpty() && (myOperations.last() != NULL);
55 bool XGUI_OperationMgr::hasOperation(const QString& theId) const
57 foreach(ModuleBase_Operation* aOp, myOperations) {
58 if (aOp->id() == theId)
64 ModuleBase_Operation* XGUI_OperationMgr::findOperation(const QString& theId) const
66 foreach(ModuleBase_Operation* aOp, myOperations) {
67 if (aOp->id() == theId)
74 int XGUI_OperationMgr::operationsCount() const
76 return myOperations.count();
79 QStringList XGUI_OperationMgr::operationList() const
82 foreach(ModuleBase_Operation* eachOperation, myOperations) {
83 ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>(eachOperation);
85 FeaturePtr aFeature = aFOperation->feature();
87 result << QString::fromStdString(aFeature->getKind());
94 ModuleBase_Operation* XGUI_OperationMgr::previousOperation(ModuleBase_Operation* theOperation) const
96 int idx = myOperations.lastIndexOf(theOperation);
97 if(idx == -1 || idx == 0) {
100 return myOperations.at(idx - 1);
103 bool XGUI_OperationMgr::eventFilter(QObject *theObject, QEvent *theEvent)
105 if (theEvent->type() == QEvent::KeyRelease) {
106 QKeyEvent* aKeyEvent = dynamic_cast<QKeyEvent*>(theEvent);
108 return onKeyReleased(aKeyEvent);
111 return QObject::eventFilter(theObject, theEvent);
114 bool XGUI_OperationMgr::startOperation(ModuleBase_Operation* theOperation)
117 currentOperation()->postpone();
118 myOperations.append(theOperation);
120 connect(theOperation, SIGNAL(beforeStarted()), SLOT(onBeforeOperationStarted()));
121 connect(theOperation, SIGNAL(beforeAborted()), SLOT(onBeforeOperationAborted()));
122 connect(theOperation, SIGNAL(beforeCommitted()), SLOT(onBeforeOperationCommitted()));
124 connect(theOperation, SIGNAL(started()), SLOT(onOperationStarted()));
125 connect(theOperation, SIGNAL(aborted()), SLOT(onOperationAborted()));
126 connect(theOperation, SIGNAL(committed()), SLOT(onOperationCommitted()));
128 connect(theOperation, SIGNAL(stopped()), SLOT(onOperationStopped()));
129 connect(theOperation, SIGNAL(resumed()), SLOT(onOperationResumed()));
130 ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
133 connect(aFOperation, SIGNAL(activatedByPreselection()),
134 SIGNAL(operationActivatedByPreselection()));
136 theOperation->start();
137 onValidateOperation();
141 bool XGUI_OperationMgr::abortAllOperations()
147 if (operationsCount() == 1) {
148 ModuleBase_Operation* aCurrentOperation = currentOperation();
149 if (canStopOperation(aCurrentOperation)) {
150 abortOperation(aCurrentOperation);
156 aResult = QMessageBox::question(qApp->activeWindow(),
157 tr("Abort operation"),
158 tr("All active operations will be aborted."),
159 QMessageBox::Ok | QMessageBox::Cancel,
160 QMessageBox::Cancel) == QMessageBox::Ok;
161 while(aResult && hasOperation()) {
162 abortOperation(currentOperation());
168 bool XGUI_OperationMgr::commitAllOperations()
170 bool isCompositeCommitted = false;
171 while (hasOperation()) {
172 ModuleBase_Operation* anOperation = currentOperation();
173 if (isApplyEnabled()) {
176 abortOperation(anOperation);
178 ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
181 FeaturePtr aFeature = aFOperation->feature();
182 CompositeFeaturePtr aComposite =
183 std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aFeature);
184 isCompositeCommitted = aComposite.get();
185 if (isCompositeCommitted)
192 void XGUI_OperationMgr::onValidateOperation()
196 ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
197 (currentOperation());
198 if(aFOperation && aFOperation->feature().get())
199 setApplyEnabled(myWorkshop->module()->getFeatureError(aFOperation->feature()).isEmpty());
202 void XGUI_OperationMgr::setApplyEnabled(const bool theEnabled)
204 myIsApplyEnabled = theEnabled;
205 ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
206 (currentOperation());
208 workshop()->errorMgr()->updateActions(aFOperation->feature());
210 //emit validationStateChanged(theEnabled);
213 void XGUI_OperationMgr::updateApplyOfOperations(ModuleBase_Operation* theOperation)
215 XGUI_ErrorMgr* anErrorMgr = workshop()->errorMgr();
217 ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>(theOperation);
219 anErrorMgr->updateAcceptAllAction(aFOperation->feature());
220 //emit nestedStateChanged(theOperation->getDescription()->operationId().toStdString(),
221 // theOperation->isValid());
224 foreach(ModuleBase_Operation* anOperation, myOperations) {
226 updateApplyOfOperations(anOperation);
227 //emit nestedStateChanged(anOperation->getDescription()->operationId().toStdString(),
228 // anOperation->isValid());
233 bool XGUI_OperationMgr::isApplyEnabled() const
235 return myIsApplyEnabled;
238 bool XGUI_OperationMgr::isParentOperationValid() const
240 bool isValid = false;
241 // the enable state of the parent operation of the nested one is defined by the rules that
242 // firstly there are nested operations and secondly the parent operation is valid
243 ModuleBase_Operation* aPrevOp = 0;
244 Operations::const_iterator anIt = myOperations.end();
245 if (anIt != myOperations.begin()) { // there are items in the operations list
247 aPrevOp = *anIt; // the last top operation, the operation which is started
248 if (anIt != myOperations.begin()) { // find the operation where the started operation is nested
253 return aPrevOp && aPrevOp->isValid();
256 bool XGUI_OperationMgr::canStopOperation(ModuleBase_Operation* theOperation)
258 //in case of nested (sketch) operation no confirmation needed
259 if (isGrantedOperation(theOperation->id()))
261 if (theOperation && theOperation->isModified()) {
262 QString aMessage = tr("%1 operation will be aborted.").arg(theOperation->id());
263 int anAnswer = QMessageBox::question(qApp->activeWindow(),
264 tr("Abort operation"),
266 QMessageBox::Ok | QMessageBox::Cancel,
267 QMessageBox::Cancel);
268 return anAnswer == QMessageBox::Ok;
273 bool XGUI_OperationMgr::commitOperation()
275 if (hasOperation() && currentOperation()->isValid()) {
282 void XGUI_OperationMgr::resumeOperation(ModuleBase_Operation* theOperation)
284 theOperation->resume();
287 bool XGUI_OperationMgr::isGrantedOperation(const QString& theId)
289 bool isGranted = false;
291 QListIterator<ModuleBase_Operation*> anIt(myOperations);
293 ModuleBase_Operation* aPreviousOperation = 0;
294 while (anIt.hasPrevious()) {
295 ModuleBase_Operation* anOp = anIt.previous();
297 isGranted = anOp->isGranted(theId);
302 void XGUI_OperationMgr::setCurrentFeature(const FeaturePtr& theFeature)
304 SessionPtr aMgr = ModelAPI_Session::get();
305 DocumentPtr aDoc = aMgr->activeDocument();
306 bool aIsOp = aMgr->isOperation();
308 aMgr->startOperation();
309 aDoc->setCurrentFeature(theFeature, false);
311 aMgr->finishOperation();
314 bool XGUI_OperationMgr::canStartOperation(const QString& theId)
316 bool aCanStart = true;
317 ModuleBase_Operation* aCurrentOp = currentOperation();
319 bool aGranted = aCurrentOp->isGranted(theId);
320 // the started operation is granted for the current one,
321 // e.g. current - Sketch, started - Line
326 if (!isGrantedOperation(theId)) {
327 // the operation is not granted in the current list of operations
328 // e.g. Edit Parameter when Sketch, Line in Sketch is active.
329 aCanStart = abortAllOperations();
331 else if (canStopOperation(aCurrentOp)) {
332 // the started operation is granted in the parrent operation,
333 // e.g. current - Line in Sketch, started Circle
334 if (myIsApplyEnabled && aCurrentOp->isModified())
335 aCurrentOp->commit();
337 abortOperation(aCurrentOp);
346 void XGUI_OperationMgr::abortOperation(ModuleBase_Operation* theOperation)
348 ModuleBase_Operation* aCurrentOperation = currentOperation();
349 if (theOperation == aCurrentOperation)
350 theOperation->abort();
352 // it is possible to trigger upper operation(e.g. sketch, current is sketch line)
353 // all operation from the current to triggered should also be aborted
354 // operations over the parameter one are not aborted(e.g. extrusion cut, sketch abort)
355 while(hasOperation()) {
356 ModuleBase_Operation* aCurrentOperation = currentOperation();
357 aCurrentOperation->abort();
358 if(theOperation == aCurrentOperation)
364 void XGUI_OperationMgr::onCommitOperation()
366 ModuleBase_Operation* anOperation = currentOperation();
368 anOperation->commit();
371 void XGUI_OperationMgr::onAbortOperation()
373 ModuleBase_Operation* aCurrentOperation = currentOperation();
374 if (aCurrentOperation && canStopOperation(aCurrentOperation)) {
375 abortOperation(aCurrentOperation);
379 void XGUI_OperationMgr::onBeforeOperationStarted()
381 ModuleBase_Operation* aCurrentOperation = dynamic_cast<ModuleBase_Operation*>(sender());
382 if (!aCurrentOperation)
385 /// Set current feature and remeber old current feature
386 ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>(aCurrentOperation);
388 SessionPtr aMgr = ModelAPI_Session::get();
389 DocumentPtr aDoc = aMgr->activeDocument();
390 // the parameter of current feature should be false, we should use all feature, not only visible
391 // in order to correctly save the previous feature of the nested operation, where the
392 // features can be not visible in the tree. The problem case is Edit sketch entitity(line)
393 // in the Sketch, created in ExtrusionCut operation. The entity disappears by commit.
394 // When sketch entity operation started, the sketch should be cashed here as the current.
395 // Otherwise(the flag is true), the ExtrusionCut is cashed, when commit happens, the sketch
396 // is disabled, sketch entity is disabled as extrusion cut is created earliest then sketch.
397 // As a result the sketch disappears from the viewer. However after commit it is displayed back.
398 aFOperation->setPreviousCurrentFeature(aDoc->currentFeature(false));
399 if (aFOperation->isEditOperation()) // it should be performed by the feature edit only
400 // in create operation, the current feature is changed by addFeature()
401 aDoc->setCurrentFeature(aFOperation->feature(), false);
405 void XGUI_OperationMgr::onOperationStarted()
407 ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
408 updateApplyOfOperations(aSenderOperation);
409 emit operationStarted(aSenderOperation);
412 void XGUI_OperationMgr::onBeforeOperationAborted()
414 onBeforeOperationCommitted();
417 void XGUI_OperationMgr::onOperationAborted()
419 ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
420 emit operationAborted(aSenderOperation);
423 void XGUI_OperationMgr::onBeforeOperationCommitted()
425 ModuleBase_Operation* aCurrentOperation = dynamic_cast<ModuleBase_Operation*>(sender());
426 if (!aCurrentOperation)
429 /// Restore the previous current feature
430 ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>(aCurrentOperation);
432 if (aFOperation->isEditOperation()) {
433 /// Restore the previous current feature
434 setCurrentFeature(aFOperation->previousCurrentFeature());
436 else { // create operation
437 // the Top created feature should stays the current. In nested operations, like Line in the Sketch or
438 // Sketch in ExtrusionCut, a previous feature should be restored on commit. It is performed here
439 // in order to perform it in the current transaction without opening a new one.
440 if (myOperations.front() != aFOperation)
441 setCurrentFeature(aFOperation->previousCurrentFeature());
446 void XGUI_OperationMgr::onOperationCommitted()
448 // apply state for all features from the stack of operations should be updated
449 updateApplyOfOperations();
451 ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
452 emit operationCommitted(aSenderOperation);
455 void XGUI_OperationMgr::onOperationResumed()
457 ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
458 emit operationResumed(aSenderOperation);
461 void XGUI_OperationMgr::onOperationStopped()
463 ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
464 ModuleBase_Operation* aCurrentOperation = currentOperation();
465 if (!aSenderOperation || !aCurrentOperation || aSenderOperation != aCurrentOperation)
468 myOperations.removeAll(aCurrentOperation);
469 aCurrentOperation->deleteLater();
471 emit operationStopped(aCurrentOperation);
473 // get last operation which can be resumed
474 ModuleBase_Operation* aResultOp = 0;
475 QListIterator<ModuleBase_Operation*> anIt(myOperations);
477 while (anIt.hasPrevious()) {
478 ModuleBase_Operation* anOp = anIt.previous();
485 bool isModified = aCurrentOperation->isModified();
486 aResultOp->setIsModified(aResultOp->isModified() || isModified);
487 resumeOperation(aResultOp);
488 onValidateOperation();
492 bool XGUI_OperationMgr::onKeyReleased(QKeyEvent* theEvent)
494 QObject* aSender = sender();
496 // Let the manager decide what to do with the given key combination.
497 ModuleBase_Operation* anOperation = currentOperation();
498 bool isAccepted = true;
499 switch (theEvent->key()) {
501 case Qt::Key_Enter: {
502 ModuleBase_Operation* aOperation = currentOperation();
503 ModuleBase_IPropertyPanel* aPanel = aOperation->propertyPanel();
504 ModuleBase_ModelWidget* aActiveWgt = aPanel->activeWidget();
505 if (!aActiveWgt || !aActiveWgt->processEnter()) {
506 ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>(currentOperation());
507 if (!aFOperation || myWorkshop->module()->getFeatureError(aFOperation->feature()).isEmpty()) {
508 emit keyEnterReleased();
518 bool noModifiers = (theEvent->modifiers() == Qt::NoModifier);
520 ModuleBase_IViewer* aViewer = myWorkshop->viewer();
521 Handle(AIS_InteractiveContext) aContext = aViewer->AISContext();
522 if (!aContext.IsNull()) {
523 Handle(V3d_View) aView = aViewer->activeView();
524 if ((theEvent->key() == Qt::Key_N))
525 aContext->HilightNextDetected(aView);
526 else if ((theEvent->key() == Qt::Key_P))
527 aContext->HilightPreviousDetected(aView);
538 // anOperation->keyReleased(theEvent->key());
543 XGUI_Workshop* XGUI_OperationMgr::workshop() const
545 XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myWorkshop);
546 return aConnector->workshop();