Salome HOME
Crossed cursor correction: it stayed in the application if the feature is aborted.
[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 #include "XGUI_Tools.h"
12 #include "XGUI_ObjectsBrowser.h"
13 #include "XGUI_ContextMenuMgr.h"
14
15 #include <ModuleBase_IPropertyPanel.h>
16 #include <ModuleBase_ModelWidget.h>
17 #include "ModuleBase_Operation.h"
18 #include "ModuleBase_IWorkshop.h"
19 #include "ModuleBase_IModule.h"
20 #include <ModuleBase_IViewer.h>
21 #include "ModuleBase_OperationDescription.h"
22 #include "ModuleBase_OperationFeature.h"
23 #include "ModuleBase_Tools.h"
24
25 #include "ModelAPI_CompositeFeature.h"
26 #include "ModelAPI_Session.h"
27
28 #include <XGUI_PropertyPanel.h>
29 #include <QToolButton>
30
31 #include <QMessageBox>
32 #include <QApplication>
33 #include <QKeyEvent>
34
35 //#define DEBUG_CURRENT_FEATURE
36
37 /// Processes "Delete" key event of application. This key is used by several application actions.
38 /// There is a logical order of the actions processing. So the key can not be set for actions
39 /// as a shortcut. The class listens the key event and call operation manager processor.
40 class XGUI_ShortCutListener : public QObject
41 {
42 public:
43   /// Constructor
44   /// \param theParent the parent to be deleted when the parent is deleted
45   /// \param theOperationMgr the class to perform deletion
46   XGUI_ShortCutListener(QObject* theParent, XGUI_OperationMgr* theOperationMgr)
47     : QObject(theParent), myOperationMgr(theOperationMgr)
48   {
49     qApp->installEventFilter(this);
50   }
51   ~XGUI_ShortCutListener() {}
52
53   /// Switch on short cut listener
54   void setActive(const bool theIsActive) { myIsActive = theIsActive; }
55
56   /// Redefinition of virtual function to process Delete key release
57   virtual bool eventFilter(QObject *theObject, QEvent *theEvent)
58   {
59     bool isAccepted = false;
60     if (myIsActive && theEvent->type() == QEvent::KeyRelease) {
61       QKeyEvent* aKeyEvent = dynamic_cast<QKeyEvent*>(theEvent);
62       if(aKeyEvent) {
63         switch (aKeyEvent->key()) {
64           case Qt::Key_Delete: {
65             isAccepted = myOperationMgr->onProcessDelete(theObject);
66           }
67         }
68       }
69     }
70     if (!isAccepted)
71       isAccepted = QObject::eventFilter(theObject, theEvent);
72     return isAccepted;
73   }
74
75 private:
76   XGUI_OperationMgr* myOperationMgr; /// processor for key event
77   bool myIsActive; /// boolean state whether the event filter perform own signal processing
78 };
79
80 XGUI_OperationMgr::XGUI_OperationMgr(QObject* theParent,
81                                      ModuleBase_IWorkshop* theWorkshop)
82 : QObject(theParent), myWorkshop(theWorkshop)
83 {
84   /// we need to install filter to the application in order to react to 'Delete' key button
85   /// this key can not be a short cut for a corresponded action because we need to set
86   /// the actions priority
87   myShortCutListener = new XGUI_ShortCutListener(theParent, this);
88 }
89
90 XGUI_OperationMgr::~XGUI_OperationMgr()
91 {
92 }
93
94 void XGUI_OperationMgr::activate()
95 {
96   myShortCutListener->setActive(true);
97 }
98
99 void XGUI_OperationMgr::deactivate()
100 {
101   myShortCutListener->setActive(false);
102 }
103
104 ModuleBase_Operation* XGUI_OperationMgr::currentOperation() const
105 {
106   return myOperations.count() > 0 ? myOperations.last() : 0;
107 }
108
109 bool XGUI_OperationMgr::isCurrentOperation(ModuleBase_Operation* theOperation)
110 {
111   if(!hasOperation())
112     return false;
113   return currentOperation() == theOperation;
114 }
115
116 bool XGUI_OperationMgr::hasOperation() const
117 {
118   return !myOperations.isEmpty() && (myOperations.last() != NULL);
119 }
120
121 bool XGUI_OperationMgr::hasOperation(const QString& theId) const
122 {
123   foreach(ModuleBase_Operation* aOp, myOperations) {
124     if (aOp->id() == theId)
125       return true;
126   }
127   return false;
128 }
129
130 ModuleBase_Operation* XGUI_OperationMgr::findOperation(const QString& theId) const
131 {
132   QList<ModuleBase_Operation*>::const_iterator anIt = myOperations.end();
133   while (anIt != myOperations.begin()) {
134     --anIt;
135     ModuleBase_Operation* anOperation = *anIt;
136     if (anOperation->id() == theId)
137       return anOperation;
138   }
139   return 0;
140 }
141
142
143 int XGUI_OperationMgr::operationsCount() const
144 {
145   return myOperations.count();
146 }
147
148 QStringList XGUI_OperationMgr::operationList() const
149 {
150   QStringList result;
151   foreach(ModuleBase_Operation* eachOperation, myOperations) {
152     ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>(eachOperation);
153     if (aFOperation) {
154       FeaturePtr aFeature = aFOperation->feature();
155       if(aFeature) {
156         result << QString::fromStdString(aFeature->getKind());
157       }
158     }
159   }
160   return result;
161 }
162
163 ModuleBase_Operation* XGUI_OperationMgr::previousOperation(ModuleBase_Operation* theOperation) const
164 {
165   int idx = myOperations.lastIndexOf(theOperation);
166   if(idx == -1 || idx == 0) {
167     return NULL;
168   }
169   return myOperations.at(idx - 1);
170 }
171
172 bool XGUI_OperationMgr::eventFilter(QObject *theObject, QEvent *theEvent)
173 {
174   bool isAccepted = false;
175   if (theEvent->type() == QEvent::KeyRelease) {
176     QKeyEvent* aKeyEvent = dynamic_cast<QKeyEvent*>(theEvent);
177     if(aKeyEvent)
178       isAccepted = onKeyReleased(theObject, aKeyEvent);
179   }
180   if (!isAccepted)
181     isAccepted = QObject::eventFilter(theObject, theEvent);
182
183   return isAccepted;
184 }
185
186 bool XGUI_OperationMgr::startOperation(ModuleBase_Operation* theOperation)
187 {
188   if (hasOperation())
189     currentOperation()->postpone();
190   myOperations.append(theOperation);
191
192   connect(theOperation, SIGNAL(beforeStarted()), SLOT(onBeforeOperationStarted()));
193   connect(theOperation, SIGNAL(beforeAborted()), SLOT(onBeforeOperationAborted()));
194   connect(theOperation, SIGNAL(beforeCommitted()), SLOT(onBeforeOperationCommitted()));
195
196   connect(theOperation, SIGNAL(started()), SLOT(onOperationStarted()));
197   connect(theOperation, SIGNAL(aborted()), SLOT(onOperationAborted()));
198   connect(theOperation, SIGNAL(committed()), SLOT(onOperationCommitted()));
199
200   connect(theOperation, SIGNAL(stopped()), SLOT(onOperationStopped()));
201   connect(theOperation, SIGNAL(resumed()), SLOT(onOperationResumed()));
202
203   bool isStarted = theOperation->start();
204   if (isStarted)
205     onValidateOperation();
206   return isStarted;
207 }
208
209 bool XGUI_OperationMgr::abortAllOperations()
210 {
211   bool aResult = true;
212   if(!hasOperation())
213     return aResult;
214
215   if (operationsCount() == 1) {
216     ModuleBase_Operation* aCurrentOperation = currentOperation();
217     if (canStopOperation(aCurrentOperation)) {
218       abortOperation(aCurrentOperation);
219     }
220     else
221       aResult = false;
222   }
223   else {
224     aResult = QMessageBox::question(qApp->activeWindow(),
225                                     tr("Abort operation"),
226                                     tr("All active operations will be aborted."),
227                                     QMessageBox::Ok | QMessageBox::Cancel,
228                                     QMessageBox::Cancel) == QMessageBox::Ok;
229     while(aResult && hasOperation()) {
230       abortOperation(currentOperation());
231     }
232   }
233   return aResult;
234 }
235
236 bool XGUI_OperationMgr::commitAllOperations()
237 {
238   bool isCompositeCommitted = false, anOperationProcessed = false;
239   while (hasOperation()) {
240     ModuleBase_Operation* anOperation = currentOperation();
241     if (XGUI_Tools::workshop(myWorkshop)->errorMgr()->isApplyEnabled()) {
242       anOperationProcessed = onCommitOperation();
243     } else {
244       abortOperation(anOperation);
245       anOperationProcessed = true;
246     }
247     ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
248                                                                             (anOperation);
249     if (aFOperation) {
250       FeaturePtr aFeature = aFOperation->feature();
251       CompositeFeaturePtr aComposite = 
252           std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aFeature);
253       isCompositeCommitted = aComposite.get();
254       if (isCompositeCommitted)
255         break;
256     }
257     // not processed[committed] operation might be used in composite feature,
258     // so the while will be stopped by the previous check.
259     // this code is not necessary, but logically should be done when the processing will not
260     // be done for not composite feature by some reasons
261     if (!anOperationProcessed)
262       break;
263   }
264   return true;
265 }
266
267 void XGUI_OperationMgr::onValidateOperation()
268 {
269   if (!hasOperation())
270     return;
271   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
272                                                                           (currentOperation());
273   if(aFOperation && aFOperation->feature().get())
274     XGUI_Tools::workshop(myWorkshop)->errorMgr()->updateActions(aFOperation->feature());
275 }
276
277 void XGUI_OperationMgr::updateApplyOfOperations(ModuleBase_Operation* theOperation)
278 {
279   XGUI_ErrorMgr* anErrorMgr = XGUI_Tools::workshop(myWorkshop)->errorMgr();
280   if (theOperation) {
281     ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>(theOperation);
282     if (aFOperation)
283       anErrorMgr->updateAcceptAllAction(aFOperation->feature());
284   }
285   else {
286     foreach(ModuleBase_Operation* anOperation, myOperations) {
287       if (anOperation)
288         updateApplyOfOperations(anOperation);
289     }
290   }
291   // Apply button of the current operation should also be updated
292   onValidateOperation();
293 }
294
295 bool XGUI_OperationMgr::canStopOperation(ModuleBase_Operation* theOperation)
296 {
297   //in case of nested (sketch) operation no confirmation needed
298   if (isGrantedOperation(theOperation->id()))
299     return true;
300   if (theOperation && theOperation->isModified()) {
301     QString aMessage = tr("%1 operation will be aborted.").arg(theOperation->id());
302     int anAnswer = QMessageBox::question(qApp->activeWindow(),
303                                          tr("Abort operation"),
304                                          aMessage,
305                                          QMessageBox::Ok | QMessageBox::Cancel,
306                                          QMessageBox::Cancel);
307     return anAnswer == QMessageBox::Ok;
308   }
309   return true;
310 }
311
312 bool XGUI_OperationMgr::commitOperation()
313 {
314   //if (hasOperation() && currentOperation()->isValid()) {
315   //  onCommitOperation();
316   //  return true;
317   //}
318   //return false;
319   return onCommitOperation();
320 }
321
322 void XGUI_OperationMgr::resumeOperation(ModuleBase_Operation* theOperation)
323 {
324   theOperation->resume();
325 }
326
327 bool XGUI_OperationMgr::isGrantedOperation(const QString& theId)
328 {
329   bool isGranted = false;
330
331   QListIterator<ModuleBase_Operation*> anIt(myOperations);
332   anIt.toBack();
333   ModuleBase_Operation* aPreviousOperation = 0;
334   while (anIt.hasPrevious() && !isGranted) {
335     ModuleBase_Operation* anOp = anIt.previous();
336     if (anOp)
337       isGranted = anOp->isGranted(theId);
338   }
339   return isGranted;
340 }
341
342 void XGUI_OperationMgr::setCurrentFeature(const FeaturePtr& theFeature)
343 {
344   SessionPtr aMgr = ModelAPI_Session::get();
345   DocumentPtr aDoc = aMgr->activeDocument();
346   bool aIsOp = aMgr->isOperation();
347   if (!aIsOp)
348     aMgr->startOperation(QString("Set current feature: %1").arg(theFeature->getKind().c_str()).toStdString());
349   aDoc->setCurrentFeature(theFeature, false);
350   if (!aIsOp)
351     aMgr->finishOperation();
352 }
353
354 bool XGUI_OperationMgr::canStartOperation(const QString& theId)
355 {
356   bool aCanStart = true;
357   ModuleBase_Operation* aCurrentOp = currentOperation();
358   if (aCurrentOp) {
359     bool aGranted = aCurrentOp->isGranted(theId);
360     // the started operation is granted for the current one,
361     // e.g. current - Sketch, started - Line
362     if (aGranted) {
363       aCanStart = true;
364     }
365     else {
366       if (!isGrantedOperation(theId)) {
367         // the operation is not granted in the current list of operations
368         // e.g. Edit Parameter when Sketch, Line in Sketch is active.
369         aCanStart = abortAllOperations();
370       }
371       else if (canStopOperation(aCurrentOp)) {
372         // the started operation is granted in the parrent operation,
373         // e.g. current - Line in Sketch, started Circle 
374         stopOperation(aCurrentOp);
375       } else {
376         aCanStart = false;
377       }
378     }
379   }
380   return aCanStart;
381 }
382
383 void XGUI_OperationMgr::stopOperation(ModuleBase_Operation* theOperation)
384 {
385   if (XGUI_Tools::workshop(myWorkshop)->errorMgr()->isApplyEnabled() && theOperation->isModified())
386     theOperation->commit();
387   else
388     abortOperation(theOperation);
389 }
390
391 void XGUI_OperationMgr::abortOperation(ModuleBase_Operation* theOperation)
392 {
393   ModuleBase_Operation* aCurrentOperation = currentOperation();
394   if (theOperation == aCurrentOperation)
395     theOperation->abort();
396   else {
397     // it is possible to trigger upper operation(e.g. sketch, current is sketch line)
398     // all operation from the current to triggered should also be aborted
399     // operations over the parameter one are not aborted(e.g. extrusion cut, sketch abort)
400     while(hasOperation()) {
401       ModuleBase_Operation* aCurrentOperation = currentOperation();
402       aCurrentOperation->abort();
403       if(theOperation == aCurrentOperation)
404         break;
405     }
406   }
407 }
408
409 bool XGUI_OperationMgr::onCommitOperation()
410 {
411   bool isCommitted = false;
412   ModuleBase_Operation* anOperation = currentOperation();
413   if (anOperation && myWorkshop->module()->canCommitOperation())
414     isCommitted = anOperation->commit();
415   return isCommitted;
416 }
417
418 void XGUI_OperationMgr::onAbortOperation()
419 {
420   ModuleBase_Operation* aCurrentOperation = currentOperation();
421   if (aCurrentOperation && canStopOperation(aCurrentOperation)) {
422     abortOperation(aCurrentOperation);
423   }
424 }
425
426 void XGUI_OperationMgr::onBeforeOperationStarted()
427 {
428   ModuleBase_Operation* aCurrentOperation = dynamic_cast<ModuleBase_Operation*>(sender());
429   if (!aCurrentOperation)
430     return;
431
432   /// Set current feature and remeber old current feature
433   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>(aCurrentOperation);
434   if (aFOperation) {
435     SessionPtr aMgr = ModelAPI_Session::get();
436     DocumentPtr aDoc = aMgr->activeDocument();
437     // the parameter of current feature should be false, we should use all feature, not only visible
438     // in order to correctly save the previous feature of the nested operation, where the
439     // features can be not visible in the tree. The problem case is Edit sketch entitity(line)
440     // in the Sketch, created in ExtrusionCut operation. The entity disappears by commit.
441     // When sketch entity operation started, the sketch should be cashed here as the current.
442     // Otherwise(the flag is true), the ExtrusionCut is cashed, when commit happens, the sketch
443     // is disabled, sketch entity is disabled as extrusion cut is created earliest then sketch.
444     // As a result the sketch disappears from the viewer. However after commit it is displayed back.
445     aFOperation->setPreviousCurrentFeature(aDoc->currentFeature(false));
446
447 #ifdef DEBUG_CURRENT_FEATURE
448     FeaturePtr aFeature = aFOperation->feature();
449     QString aKind = aFeature ? aFeature->getKind().c_str() : "";
450     qDebug(QString("onBeforeOperationStarted(), edit operation = %1, feature = %2")
451             .arg(aFOperation->isEditOperation())
452             .arg(ModuleBase_Tools::objectInfo(aFeature)).toStdString().c_str());
453
454     qDebug(QString("\tdocument->currentFeature(false) = %1").arg(
455             ModuleBase_Tools::objectInfo(ModelAPI_Session::get()->activeDocument()->currentFeature(false))).toStdString().c_str());
456 #endif
457
458     if (aFOperation->isEditOperation()) {// it should be performed by the feature edit only
459       // in create operation, the current feature is changed by addFeature()
460       aDoc->setCurrentFeature(aFOperation->feature(), false);
461       // this is the only place where flushes must be called after setCurrentFeature for the current
462       // moment: after this the opertion is not finished, so, the ObjectBrowser state may be corrupted
463       // (issue #1457)
464       static Events_Loop* aLoop = Events_Loop::loop();
465       static Events_ID aCreateEvent = aLoop->eventByName(EVENT_OBJECT_CREATED);
466       aLoop->flush(aCreateEvent);
467       static Events_ID aDeleteEvent = aLoop->eventByName(EVENT_OBJECT_DELETED);
468       aLoop->flush(aDeleteEvent);
469     }
470
471 #ifdef DEBUG_CURRENT_FEATURE
472     qDebug("\tdocument->setCurrentFeature");
473     qDebug(QString("\tdocument->currentFeature(false) = %1").arg(
474             ModuleBase_Tools::objectInfo(ModelAPI_Session::get()->activeDocument()->currentFeature(false))).toStdString().c_str());
475 #endif
476   }
477 }
478
479 void XGUI_OperationMgr::onOperationStarted()
480 {
481   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
482   updateApplyOfOperations(aSenderOperation);
483   XGUI_Workshop* aWorkshop = XGUI_Tools::workshop(myWorkshop);
484   aWorkshop->operationStarted(aSenderOperation);
485 }
486
487 void XGUI_OperationMgr::onBeforeOperationAborted()
488 {
489   onBeforeOperationCommitted();
490 }
491
492 void XGUI_OperationMgr::onOperationAborted()
493 {
494   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
495   emit operationAborted(aSenderOperation);
496 }
497
498 void XGUI_OperationMgr::onBeforeOperationCommitted()
499 {
500   ModuleBase_Operation* aCurrentOperation = dynamic_cast<ModuleBase_Operation*>(sender());
501   if (!aCurrentOperation)
502     return;
503
504   /// Restore the previous current feature
505   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>(aCurrentOperation);
506   if (aFOperation) {
507 #ifdef DEBUG_CURRENT_FEATURE
508     QString aKind = aFOperation->feature()->getKind().c_str();
509     qDebug(QString("onBeforeOperationCommitted(), edit operation = %1, feature = %2")
510             .arg(aFOperation->isEditOperation())
511             .arg(ModuleBase_Tools::objectInfo(aFOperation->feature())).toStdString().c_str());
512
513     qDebug(QString("\tdocument->currentFeature(false) = %1").arg(
514             ModuleBase_Tools::objectInfo(ModelAPI_Session::get()->activeDocument()->currentFeature(false))).toStdString().c_str());
515 #endif
516
517     if (aFOperation->isEditOperation()) {
518       /// Restore the previous current feature
519       setCurrentFeature(aFOperation->previousCurrentFeature());
520     }
521     else { // create operation
522       // the Top created feature should stays the current. In nested operations, like Line in the Sketch or
523       // Sketch in ExtrusionCut, a previous feature should be restored on commit. It is performed here
524       // in order to perform it in the current transaction without opening a new one.
525       if (myOperations.front() != aFOperation)
526         setCurrentFeature(aFOperation->previousCurrentFeature());
527     }
528 #ifdef DEBUG_CURRENT_FEATURE
529     qDebug("\tdocument->setCurrentFeature");
530     qDebug(QString("\tdocument->currentFeature(false) = %1").arg(
531             ModuleBase_Tools::objectInfo(ModelAPI_Session::get()->activeDocument()->currentFeature(false))).toStdString().c_str());
532 #endif
533     ModuleBase_IModule* aModule = myWorkshop->module();
534     if (aModule)
535       aModule->beforeOperationStopped(aFOperation);
536   }
537 }
538
539 void XGUI_OperationMgr::onOperationCommitted()
540 {
541   // apply state for all features from the stack of operations should be updated
542   updateApplyOfOperations();
543
544   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
545   emit operationCommitted(aSenderOperation);
546 }
547
548 void XGUI_OperationMgr::onOperationResumed()
549 {
550   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
551   emit operationResumed(aSenderOperation);
552 }
553
554 void XGUI_OperationMgr::onOperationStopped()
555 {
556   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
557   ModuleBase_Operation* aCurrentOperation = currentOperation();
558   if (!aSenderOperation || !aCurrentOperation || aSenderOperation != aCurrentOperation)
559     return;
560
561   myOperations.removeAll(aCurrentOperation);
562   aCurrentOperation->deleteLater();
563
564   emit operationStopped(aCurrentOperation);
565
566   // get last operation which can be resumed
567   ModuleBase_Operation* aResultOp = 0;
568   QListIterator<ModuleBase_Operation*> anIt(myOperations);
569   anIt.toBack();
570   while (anIt.hasPrevious()) {
571     ModuleBase_Operation* anOp = anIt.previous();
572     if (anOp) {
573       aResultOp = anOp;
574       break;
575     }
576   }
577   if (aResultOp) {
578     bool isModified = aCurrentOperation->isModified();
579     aResultOp->setIsModified(aResultOp->isModified() || isModified);
580     resumeOperation(aResultOp);
581     onValidateOperation();
582   }
583 }
584
585 bool XGUI_OperationMgr::onKeyReleased(QObject *theObject, QKeyEvent* theEvent)
586 {
587   // Let the manager decide what to do with the given key combination.
588   ModuleBase_Operation* anOperation = currentOperation();
589   bool isAccepted = false;
590   switch (theEvent->key()) {
591     case Qt::Key_Return:
592     case Qt::Key_Enter: {
593       isAccepted = onProcessEnter(theObject);
594     }
595     break;
596     case Qt::Key_N:
597     case Qt::Key_P: {
598       bool noModifiers = (theEvent->modifiers() == Qt::NoModifier);
599       if (noModifiers) {
600         ModuleBase_IViewer* aViewer = myWorkshop->viewer();
601         Handle(AIS_InteractiveContext) aContext = aViewer->AISContext();
602         if (!aContext.IsNull()) {
603           Handle(V3d_View) aView = aViewer->activeView();
604           if ((theEvent->key() == Qt::Key_N))
605             aContext->HilightNextDetected(aView);
606           else if ((theEvent->key() == Qt::Key_P))
607             aContext->HilightPreviousDetected(aView);
608         }
609       }
610     }
611     break;
612     break;
613     default:
614       isAccepted = false;
615       break;
616   }
617   //if(anOperation) {
618   //  anOperation->keyReleased(theEvent->key());
619   //}
620   return isAccepted;
621 }
622
623 bool XGUI_OperationMgr::onProcessEnter(QObject* theObject)
624 {
625   bool isAccepted = false;
626   ModuleBase_Operation* aOperation = currentOperation();
627   // to avoid enter processing when operation has not been started yet
628   if (!aOperation)
629     return isAccepted;
630   ModuleBase_IPropertyPanel* aPanel = aOperation->propertyPanel();
631   // only property panel enter is processed in order to do not process enter in application dialogs
632   bool isPPChild = isChildObject(theObject, aPanel);
633   if (!isPPChild)
634     return isAccepted;
635
636   ModuleBase_ModelWidget* anActiveWgt = aPanel->activeWidget();
637   bool isAborted = false;
638   if (!anActiveWgt) {
639     QWidget* aFocusWidget = aPanel->focusWidget();
640     QToolButton* aCancelBtn = dynamic_cast<XGUI_PropertyPanel*>(aPanel)->findButton(PROP_PANEL_CANCEL);
641     if (aFocusWidget && aCancelBtn && aFocusWidget == aCancelBtn) {
642       abortOperation(aOperation);
643       isAccepted = true;
644       isAborted = true;
645     }
646   }
647   if (!isAborted) {
648     isAccepted = anActiveWgt && anActiveWgt->processEnter();
649     if (!isAccepted) {
650       isAccepted = myWorkshop->module()->processEnter(anActiveWgt ? anActiveWgt->attributeID() : "");
651       if (!isAccepted) {
652         /// functionality is similar to Apply click
653         ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>(currentOperation());
654         if (!aFOperation || myWorkshop->module()->getFeatureError(aFOperation->feature()).isEmpty()) {
655           // key released is emitted to apply the current value to the model if it was modified in PP
656           emit keyEnterReleased();
657           commitOperation();
658           isAccepted = true;
659         }
660         else
661           isAccepted = false;
662       }
663     }
664   }
665   return isAccepted;
666 }
667
668 bool XGUI_OperationMgr::onProcessDelete(QObject* theObject)
669 {
670   bool isAccepted = false;
671   ModuleBase_Operation* aOperation = currentOperation();
672   ModuleBase_ModelWidget* anActiveWgt = 0;
673   // firstly the widget should process Delete action
674   ModuleBase_IPropertyPanel* aPanel;
675   bool isPPChildObject = false;
676   if (aOperation) {
677     aPanel = aOperation->propertyPanel();
678     if (aPanel) {
679       isPPChildObject = isChildObject(theObject, aPanel);
680       // process delete in active widget only if delete sender is child of property panel
681       // it is necessary for the case when OB is shown, user perform selection and click Delete
682       if (isPPChildObject) {
683         anActiveWgt = aPanel->activeWidget();
684         if (anActiveWgt) {
685           isAccepted = anActiveWgt->processDelete();
686         }
687       }
688     }
689   }
690   if (!isAccepted) {
691     // after widget, object browser and viewer should process delete
692     /// other widgets such as line edit controls should not lead to
693     /// processing delete by workshop
694     XGUI_ObjectsBrowser* aBrowser = XGUI_Tools::workshop(myWorkshop)->objectBrowser();
695     QWidget* aViewPort = myWorkshop->viewer()->activeViewPort();
696     bool isToDeleteObject = false;
697     XGUI_Workshop* aWorkshop = XGUI_Tools::workshop(myWorkshop);
698     XGUI_ContextMenuMgr* aContextMenuMgr = aWorkshop->contextMenuMgr();
699     if (theObject == aBrowser->treeView()) {
700       aContextMenuMgr->updateObjectBrowserMenu();
701       isToDeleteObject = aContextMenuMgr->action("DELETE_CMD")->isEnabled();
702     }
703     else if (isChildObject(theObject, aViewPort)) {
704       aContextMenuMgr->updateViewerMenu();
705       isToDeleteObject = aContextMenuMgr->action("DELETE_CMD")->isEnabled();
706     }
707     else if (isPPChildObject) {
708       // property panel child object is processed to process delete performed on Apply button of PP
709       isToDeleteObject = true;
710     }
711     if (isToDeleteObject) {
712       aWorkshop->deleteObjects();
713       isAccepted = true;
714     }
715   }
716
717   return isAccepted;
718 }
719
720 bool XGUI_OperationMgr::isChildObject(const QObject* theObject, const QObject* theParent)
721 {
722   bool isPPChild = false;
723   if (theParent && theObject) {
724     QObject* aParent = (QObject*)theObject;
725     while (aParent ) {
726       isPPChild = aParent == theParent;
727       if (isPPChild)
728         break;
729       aParent = aParent->parent();
730     }
731   }
732   return isPPChild;
733 }