Salome HOME
Restore deflection management
[modules/shaper.git] / src / XGUI / XGUI_OperationMgr.cpp
1 // Copyright (C) 2014-2019  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include "XGUI_OperationMgr.h"
21
22 #include "XGUI_ActiveControlMgr.h"
23 #include "XGUI_ActiveControlSelector.h"
24 #include "XGUI_FacesPanelSelector.h"
25 #include "XGUI_ModuleConnector.h"
26 #include "XGUI_Workshop.h"
27 #include "XGUI_ErrorMgr.h"
28 #include "XGUI_FacesPanel.h"
29 #include "XGUI_Tools.h"
30 #include "XGUI_ObjectsBrowser.h"
31 #include "XGUI_ContextMenuMgr.h"
32
33 #include <ModuleBase_IPropertyPanel.h>
34 #include <ModuleBase_ModelWidget.h>
35 #include "ModuleBase_Operation.h"
36 #include "ModuleBase_IWorkshop.h"
37 #include "ModuleBase_IModule.h"
38 #include <ModuleBase_IViewer.h>
39 #include "ModuleBase_OperationDescription.h"
40 #include "ModuleBase_OperationFeature.h"
41 #include "ModuleBase_Tools.h"
42
43 #include <Config_Translator.h>
44
45 #include <ModelAPI_CompositeFeature.h>
46 #include <ModelAPI_Session.h>
47
48 #include <XGUI_PropertyPanel.h>
49 #include <QToolButton>
50 #include <QLineEdit>
51
52 #include <QMessageBox>
53 #include <QApplication>
54 #include <QKeyEvent>
55
56 //#define DEBUG_CURRENT_FEATURE
57
58 /// Processes "Delete" key event of application. This key is used by several application actions.
59 /// There is a logical order of the actions processing. So the key can not be set for actions
60 /// as a shortcut. The class listens the key event and call operation manager processor.
61 class XGUI_ShortCutListener : public QObject
62 {
63 public:
64   /// Constructor
65   /// \param theParent the parent to be deleted when the parent is deleted
66   /// \param theOperationMgr the class to perform deletion
67   XGUI_ShortCutListener(QObject* theParent, XGUI_OperationMgr* theOperationMgr)
68     : QObject(theParent), myOperationMgr(theOperationMgr), myIsActive(false)
69   {
70     qApp->installEventFilter(this);
71   }
72   ~XGUI_ShortCutListener() {}
73
74   /// Switch on short cut listener
75   void setActive(const bool theIsActive) { myIsActive = theIsActive; }
76
77   /// Redefinition of virtual function to process Delete key release
78   virtual bool eventFilter(QObject *theObject, QEvent *theEvent)
79   {
80     bool isAccepted = false;
81     if (myIsActive && (!qApp->modalWindow())) {
82       if (theEvent->type() == QEvent::KeyRelease) {
83         QKeyEvent* aKeyEvent = dynamic_cast<QKeyEvent*>(theEvent);
84         if (aKeyEvent) {
85           myOperationMgr->setSHIFTPressed(aKeyEvent->modifiers() & Qt::ShiftModifier);
86           switch (aKeyEvent->key()) {
87             case Qt::Key_Delete:
88               isAccepted = myOperationMgr->onProcessDelete(theObject);
89             break;
90             default:
91               isAccepted = myOperationMgr->onKeyReleased(theObject, aKeyEvent);
92               break;
93           }
94         }
95       }
96       else if (theEvent->type() == QEvent::KeyPress) {
97         if (myOperationMgr->hasOperation()) {
98           QKeyEvent* aKeyEvent = dynamic_cast<QKeyEvent*>(theEvent);
99           myOperationMgr->setSHIFTPressed(aKeyEvent->modifiers() & Qt::ShiftModifier);
100           isAccepted = myOperationMgr->onKeyPressed(theObject, aKeyEvent);
101         }
102       }
103     }
104     if (!isAccepted)
105       isAccepted = QObject::eventFilter(theObject, theEvent);
106     return isAccepted;
107   }
108
109 private:
110   XGUI_OperationMgr* myOperationMgr; /// processor for key event
111   bool myIsActive; /// boolean state whether the event filter perform own signal processing
112 };
113
114 XGUI_OperationMgr::XGUI_OperationMgr(QObject* theParent,
115                                      ModuleBase_IWorkshop* theWorkshop)
116 : QObject(theParent), myWorkshop(theWorkshop), mySHIFTPressed(false), myActiveMessageBox(0)
117 {
118   /// we need to install filter to the application in order to react to 'Delete' key button
119   /// this key can not be a short cut for a corresponded action because we need to set
120   /// the actions priority
121   myShortCutListener = new XGUI_ShortCutListener(theParent, this);
122 }
123
124 XGUI_OperationMgr::~XGUI_OperationMgr()
125 {
126 }
127
128 void XGUI_OperationMgr::activate()
129 {
130   myShortCutListener->setActive(true);
131 }
132
133 void XGUI_OperationMgr::deactivate()
134 {
135   myShortCutListener->setActive(false);
136 }
137
138 ModuleBase_Operation* XGUI_OperationMgr::currentOperation() const
139 {
140   return myOperations.count() > 0 ? myOperations.last() : 0;
141 }
142
143 bool XGUI_OperationMgr::isCurrentOperation(ModuleBase_Operation* theOperation)
144 {
145   if(!hasOperation())
146     return false;
147   return currentOperation() == theOperation;
148 }
149
150 bool XGUI_OperationMgr::hasOperation() const
151 {
152   return !myOperations.isEmpty() && (myOperations.last() != NULL);
153 }
154
155 bool XGUI_OperationMgr::hasOperation(const QString& theId) const
156 {
157   foreach(ModuleBase_Operation* aOp, myOperations) {
158     if (aOp->id() == theId)
159       return true;
160   }
161   return false;
162 }
163
164 ModuleBase_Operation* XGUI_OperationMgr::findOperation(const QString& theId) const
165 {
166   QList<ModuleBase_Operation*>::const_iterator anIt = myOperations.end();
167   while (anIt != myOperations.begin()) {
168     --anIt;
169     ModuleBase_Operation* anOperation = *anIt;
170     if (anOperation->id() == theId)
171       return anOperation;
172   }
173   return 0;
174 }
175
176
177 int XGUI_OperationMgr::operationsCount() const
178 {
179   return myOperations.count();
180 }
181
182 QStringList XGUI_OperationMgr::operationList() const
183 {
184   QStringList result;
185   foreach(ModuleBase_Operation* eachOperation, myOperations) {
186     ModuleBase_OperationFeature* aFOperation =
187       dynamic_cast<ModuleBase_OperationFeature*>(eachOperation);
188     if (aFOperation) {
189       FeaturePtr aFeature = aFOperation->feature();
190       if(aFeature) {
191         result << QString::fromStdString(aFeature->getKind());
192       }
193     }
194   }
195   return result;
196 }
197
198 ModuleBase_Operation* XGUI_OperationMgr::previousOperation(ModuleBase_Operation* theOperation) const
199 {
200   int idx = myOperations.lastIndexOf(theOperation);
201   if(idx == -1 || idx == 0) {
202     return NULL;
203   }
204   return myOperations.at(idx - 1);
205 }
206
207 ModuleBase_ModelWidget* XGUI_OperationMgr::activeWidget() const
208 {
209   ModuleBase_ModelWidget* anActiveWidget = 0;
210   ModuleBase_Operation* anOperation = currentOperation();
211   if (anOperation) {
212     ModuleBase_IPropertyPanel* aPanel = anOperation->propertyPanel();
213     if (aPanel)
214       anActiveWidget = aPanel->activeWidget();
215   }
216   return anActiveWidget;
217 }
218
219 bool XGUI_OperationMgr::startOperation(ModuleBase_Operation* theOperation)
220 {
221   if (hasOperation())
222     currentOperation()->postpone();
223   myOperations.append(theOperation);
224
225   connect(theOperation, SIGNAL(beforeStarted()), SLOT(onBeforeOperationStarted()));
226   connect(theOperation, SIGNAL(beforeAborted()), SLOT(onBeforeOperationAborted()));
227   connect(theOperation, SIGNAL(beforeCommitted()), SLOT(onBeforeOperationCommitted()));
228
229   connect(theOperation, SIGNAL(started()), SLOT(onOperationStarted()));
230   connect(theOperation, SIGNAL(aborted()), SLOT(onOperationAborted()));
231   connect(theOperation, SIGNAL(committed()), SLOT(onOperationCommitted()));
232
233   connect(theOperation, SIGNAL(stopped()), SLOT(onOperationStopped()));
234   connect(theOperation, SIGNAL(resumed()), SLOT(onOperationResumed()));
235
236   bool isStarted = theOperation->start();
237   if (isStarted)
238     onValidateOperation();
239   return isStarted;
240 }
241
242 bool XGUI_OperationMgr::abortAllOperations(const XGUI_MessageKind& theMessageKind)
243 {
244   bool aResult = true;
245   if(!hasOperation())
246     return aResult;
247
248   if (operationsCount() == 1) {
249     ModuleBase_Operation* aCurrentOperation = currentOperation();
250     if (canStopOperation(aCurrentOperation, theMessageKind)) {
251       abortOperation(aCurrentOperation);
252     }
253     else
254       aResult = false;
255   }
256   else {
257     if (theMessageKind == XGUI_AbortOperationMessage) {
258       myActiveMessageBox = createMessageBox(tr("All active operations will be aborted."));
259       aResult = myActiveMessageBox->exec() == QMessageBox::Ok;
260       myActiveMessageBox = 0;
261     }
262     else if (theMessageKind == XGUI_InformationMessage) {
263       QString aMessage = tr("Please validate all your active operations before saving.");
264       myActiveMessageBox = createInformationBox(aMessage);
265       myActiveMessageBox->exec();
266       myActiveMessageBox = 0;
267       aResult = false; // do not perform abort
268     }
269     while(aResult && hasOperation()) {
270       abortOperation(currentOperation());
271     }
272   }
273   return aResult;
274 }
275
276 bool XGUI_OperationMgr::commitAllOperations()
277 {
278   bool isCompositeCommitted = false, anOperationProcessed = false;
279   while (hasOperation()) {
280     ModuleBase_Operation* anOperation = currentOperation();
281     if (XGUI_Tools::workshop(myWorkshop)->errorMgr()->isApplyEnabled()) {
282       anOperationProcessed = commitOperation();
283     } else {
284       abortOperation(anOperation);
285       anOperationProcessed = true;
286     }
287     ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
288                                                                             (anOperation);
289     if (aFOperation) {
290       FeaturePtr aFeature = aFOperation->feature();
291       CompositeFeaturePtr aComposite =
292           std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aFeature);
293       isCompositeCommitted = aComposite.get();
294       if (isCompositeCommitted)
295         break;
296     }
297     // not processed[committed] operation might be used in composite feature,
298     // so the while will be stopped by the previous check.
299     // this code is not necessary, but logically should be done when the processing will not
300     // be done for not composite feature by some reasons
301     if (!anOperationProcessed)
302       break;
303   }
304   return true;
305 }
306
307 void XGUI_OperationMgr::onValidateOperation()
308 {
309   if (!hasOperation())
310     return;
311   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
312                                                                           (currentOperation());
313   if(aFOperation && aFOperation->feature().get())
314     XGUI_Tools::workshop(myWorkshop)->errorMgr()->updateActions(aFOperation->feature());
315 }
316
317 void XGUI_OperationMgr::updateApplyOfOperations(ModuleBase_Operation* theOperation)
318 {
319   XGUI_ErrorMgr* anErrorMgr = XGUI_Tools::workshop(myWorkshop)->errorMgr();
320   if (theOperation) {
321     ModuleBase_OperationFeature* aFOperation =
322       dynamic_cast<ModuleBase_OperationFeature*>(theOperation);
323     if (aFOperation)
324       anErrorMgr->updateAcceptAllAction(aFOperation->feature());
325   }
326   else {
327     foreach(ModuleBase_Operation* anOperation, myOperations) {
328       if (anOperation)
329         updateApplyOfOperations(anOperation);
330     }
331   }
332   // Apply button of the current operation should also be updated
333   onValidateOperation();
334 }
335
336 bool XGUI_OperationMgr::canStopOperation(ModuleBase_Operation* theOperation,
337                                          const XGUI_OperationMgr::XGUI_MessageKind& theMessageKind)
338 {
339   //in case of nested (sketch) operation no confirmation needed
340   if (isGrantedOperation(theOperation->id()))
341     return true;
342   if (theOperation && theOperation->isModified()) {
343     ModuleBase_OperationFeature* aOp = dynamic_cast<ModuleBase_OperationFeature*>(theOperation);
344     std::string aContext;
345     if (aOp)
346       aContext = aOp->feature()->getKind();
347     QString aTitle = Config_Translator::translate(aContext,
348       theOperation->getDescription()->description().toStdString()).c_str();
349
350     if (theMessageKind == XGUI_AbortOperationMessage) {
351       QString aMessage = tr("%1 operation will be aborted.").arg(aTitle);
352       myActiveMessageBox = createMessageBox(aMessage);
353       bool aResult = myActiveMessageBox->exec() == QMessageBox::Ok;
354       myActiveMessageBox = 0;
355       return aResult;
356     }
357     else if (theMessageKind == XGUI_InformationMessage) {
358       QString aMessage = tr("Please validate your %1 before saving.").arg(aTitle);
359       myActiveMessageBox = createInformationBox(aMessage);
360       myActiveMessageBox->exec();
361       myActiveMessageBox = 0;
362       return false;
363     }
364   }
365   return true;
366 }
367
368 void XGUI_OperationMgr::resumeOperation(ModuleBase_Operation* theOperation)
369 {
370   theOperation->resume();
371 }
372
373 bool XGUI_OperationMgr::isGrantedOperation(const QString& theId)
374 {
375   bool isGranted = false;
376
377   QListIterator<ModuleBase_Operation*> anIt(myOperations);
378   anIt.toBack();
379   ModuleBase_Operation* aPreviousOperation = 0;
380   while (anIt.hasPrevious() && !isGranted) {
381     ModuleBase_Operation* anOp = anIt.previous();
382     if (anOp)
383       isGranted = anOp->isGranted(theId);
384   }
385   return isGranted;
386 }
387
388 void XGUI_OperationMgr::setCurrentFeature(const FeaturePtr& theFeature)
389 {
390   SessionPtr aMgr = ModelAPI_Session::get();
391   DocumentPtr aDoc = aMgr->activeDocument();
392   bool aIsOp = aMgr->isOperation();
393   if (!aIsOp)
394     aMgr->startOperation(QString("Set current feature: %1")
395     .arg(theFeature->getKind().c_str()).toStdString());
396   aDoc->setCurrentFeature(theFeature, false);
397 #ifdef DEBUG_CURRENT_FEATURE
398   qDebug(QString("   document->setCurrentFeature(false) = %1    SET").arg(
399          ModuleBase_Tools::objectName(
400          ModelAPI_Session::get()->activeDocument()->currentFeature(false))).toStdString().c_str());
401 #endif
402
403   if (!aIsOp)
404     aMgr->finishOperation();
405 }
406
407 bool XGUI_OperationMgr::canStartOperation(const QString& theId, bool& isCommitted)
408 {
409   bool aCanStart = true;
410   isCommitted = false;
411   ModuleBase_Operation* aCurrentOp = currentOperation();
412   if (aCurrentOp) {
413     bool aGranted = aCurrentOp->isGranted(theId);
414     // the started operation is granted for the current one,
415     // e.g. current - Sketch, started - Line
416     if (aGranted) {
417       aCanStart = true;
418     }
419     else {
420       if (!isGrantedOperation(theId)) {
421         // the operation is not granted in the current list of operations
422         // e.g. Edit Parameter when Sketch, Line in Sketch is active.
423         aCanStart = abortAllOperations();
424       }
425       else if (canStopOperation(aCurrentOp)) {
426         // the started operation is granted in the parrent operation,
427         // e.g. current - Line in Sketch, started Circle
428         stopOperation(aCurrentOp, isCommitted);
429       } else {
430         aCanStart = false;
431       }
432     }
433   }
434   return aCanStart;
435 }
436
437 void XGUI_OperationMgr::stopOperation(ModuleBase_Operation* theOperation, bool& isCommitted)
438 {
439   if (XGUI_Tools::workshop(myWorkshop)->errorMgr()->isApplyEnabled() &&
440       theOperation->isModified()) {
441     isCommitted = theOperation->commit();
442   } else {
443     isCommitted = false;
444     abortOperation(theOperation);
445   }
446 }
447
448 void XGUI_OperationMgr::abortOperation(ModuleBase_Operation* theOperation)
449 {
450   ModuleBase_Operation* aCurrentOperation = currentOperation();
451   if (theOperation && (theOperation == aCurrentOperation))
452     theOperation->abort();
453   else {
454     // it is possible to trigger upper operation(e.g. sketch, current is sketch line)
455     // all operation from the current to triggered should also be aborted
456     // operations over the parameter one are not aborted(e.g. extrusion cut, sketch abort)
457     while(hasOperation()) {
458       ModuleBase_Operation* aCurrentOperation = currentOperation();
459       aCurrentOperation->abort();
460       if(theOperation == aCurrentOperation)
461         break;
462     }
463   }
464 }
465
466 bool XGUI_OperationMgr::commitOperation()
467 {
468   bool isCommitted = false;
469   ModuleBase_Operation* anOperation = currentOperation();
470   if (anOperation && myWorkshop->module()->canCommitOperation())
471     isCommitted = anOperation->commit();
472   return isCommitted;
473 }
474
475 void XGUI_OperationMgr::onAbortOperation()
476 {
477   ModuleBase_Operation* aCurrentOperation = currentOperation();
478   if (aCurrentOperation && canStopOperation(aCurrentOperation)) {
479     abortOperation(aCurrentOperation);
480   }
481 }
482
483 void XGUI_OperationMgr::onAbortAllOperation()
484 {
485   abortAllOperations();
486 }
487
488 void XGUI_OperationMgr::onBeforeOperationStarted()
489 {
490   ModuleBase_Operation* aCurrentOperation = dynamic_cast<ModuleBase_Operation*>(sender());
491   if (!aCurrentOperation)
492     return;
493
494   /// Set current feature and remeber old current feature
495   ModuleBase_OperationFeature* aFOperation =
496     dynamic_cast<ModuleBase_OperationFeature*>(aCurrentOperation);
497   if (aFOperation) {
498     SessionPtr aMgr = ModelAPI_Session::get();
499     DocumentPtr aDoc = aMgr->activeDocument();
500     // the parameter of current feature should be false, we should use all feature, not only
501     // visible in order to correctly save the previous feature of the nested operation, where the
502     // features can be not visible in the tree. The problem case is Edit sketch entitity(line)
503     // in the Sketch, created in ExtrusionCut operation. The entity disappears by commit.
504     // When sketch entity operation started, the sketch should be cashed here as the current.
505     // Otherwise(the flag is true), the ExtrusionCut is cashed, when commit happens, the sketch
506     // is disabled, sketch entity is disabled as extrusion cut is created earliest then sketch.
507     // As a result the sketch disappears from the viewer.
508     // However after commit it is displayed back.
509     aFOperation->setPreviousCurrentFeature(aDoc->currentFeature(false));
510
511 #ifdef DEBUG_CURRENT_FEATURE
512     FeaturePtr aFeature = aFOperation->feature();
513     QString aKind = aFeature ? aFeature->getKind().c_str() : "";
514     qDebug("");
515     qDebug(QString("onBeforeOperationStarted() isEditOperation = %1, feature = %2")
516             .arg(aFOperation->isEditOperation())
517             .arg(ModuleBase_Tools::objectName(aFeature)).toStdString().c_str());
518     qDebug(QString("   document->currentFeature(false) = %1 : DO: setPreviousCurrentFeature").arg(
519             ModuleBase_Tools::objectName(aDoc->currentFeature(false))).toStdString().c_str());
520 #endif
521
522     if (aFOperation->isEditOperation()) {// it should be performed by the feature edit only
523       // in create operation, the current feature is changed by addFeature()
524       aDoc->setCurrentFeature(aFOperation->feature(), false);
525 #ifdef DEBUG_CURRENT_FEATURE
526       qDebug(QString("   document->setCurrentFeature(false) = %1").arg(
527              ModuleBase_Tools::objectName(aDoc->currentFeature(false))).toStdString().c_str());
528 #endif
529       // this is the only place where flushes must be called after setCurrentFeature for the
530       // current moment: after this the opertion is not finished, so, the ObjectBrowser
531       // state may be corrupted (issue #1457)
532       static Events_Loop* aLoop = Events_Loop::loop();
533       static Events_ID aCreateEvent = aLoop->eventByName(EVENT_OBJECT_CREATED);
534       aLoop->flush(aCreateEvent);
535       static Events_ID aDeleteEvent = aLoop->eventByName(EVENT_OBJECT_DELETED);
536       aLoop->flush(aDeleteEvent);
537     }
538   }
539 }
540
541 void XGUI_OperationMgr::onOperationStarted()
542 {
543   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
544   updateApplyOfOperations(aSenderOperation);
545   XGUI_Workshop* aWorkshop = XGUI_Tools::workshop(myWorkshop);
546   aWorkshop->operationStarted(aSenderOperation);
547 }
548
549 void XGUI_OperationMgr::onBeforeOperationAborted()
550 {
551   onBeforeOperationCommitted();
552 }
553
554 void XGUI_OperationMgr::onOperationAborted()
555 {
556   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
557   XGUI_Workshop* aWorkshop = XGUI_Tools::workshop(myWorkshop);
558   aWorkshop->setStatusBarMessage("");
559   emit operationAborted(aSenderOperation);
560 }
561
562 void XGUI_OperationMgr::onBeforeOperationCommitted()
563 {
564   ModuleBase_Operation* aCurrentOperation = dynamic_cast<ModuleBase_Operation*>(sender());
565   if (!aCurrentOperation)
566     return;
567
568   /// Restore the previous current feature
569   ModuleBase_OperationFeature* aFOperation =
570     dynamic_cast<ModuleBase_OperationFeature*>(aCurrentOperation);
571   if (aFOperation) {
572 #ifdef DEBUG_CURRENT_FEATURE
573     QString aKind = aFOperation->feature()->getKind().c_str();
574     qDebug(QString("onBeforeOperationCommitted() isEditOperation = %1, feature = %2")
575             .arg(aFOperation->isEditOperation())
576             .arg(ModuleBase_Tools::objectName(aFOperation->feature())).toStdString().c_str());
577     qDebug(QString("   document->currentFeature(false) = %1").arg(
578             ModuleBase_Tools::objectName(
579             ModelAPI_Session::get()->activeDocument()->currentFeature(false)))
580             .toStdString().c_str());
581 #endif
582
583     if (aFOperation->isEditOperation()) {
584       /// Restore the previous current feature
585       setCurrentFeature(aFOperation->previousCurrentFeature());
586     }
587     else { // create operation
588       // the Top created feature should stays the current. In nested operations,
589       // like Line in the Sketch or
590       // Sketch in ExtrusionCut, a previous feature should be restored on commit.
591       // It is performed here
592       // in order to perform it in the current transaction without opening a new one.
593       if (myOperations.front() != aFOperation)
594         setCurrentFeature(aFOperation->previousCurrentFeature());
595     }
596     ModuleBase_IModule* aModule = myWorkshop->module();
597     if (aModule)
598       aModule->beforeOperationStopped(aFOperation);
599   }
600 }
601
602 void XGUI_OperationMgr::onOperationCommitted()
603 {
604   // apply state for all features from the stack of operations should be updated
605   updateApplyOfOperations();
606
607   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
608   emit operationCommitted(aSenderOperation);
609 }
610
611 void XGUI_OperationMgr::onOperationResumed()
612 {
613   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
614   emit operationResumed(aSenderOperation);
615 }
616
617 void XGUI_OperationMgr::onOperationStopped()
618 {
619   ModuleBase_Operation* aSenderOperation = dynamic_cast<ModuleBase_Operation*>(sender());
620   ModuleBase_Operation* aCurrentOperation = currentOperation();
621   if (!aSenderOperation || !aCurrentOperation || aSenderOperation != aCurrentOperation)
622     return;
623
624   myOperations.removeAll(aCurrentOperation);
625   aCurrentOperation->deleteLater();
626
627   emit operationStopped(aCurrentOperation);
628
629   // get last operation which can be resumed
630   ModuleBase_Operation* aResultOp = 0;
631   QListIterator<ModuleBase_Operation*> anIt(myOperations);
632   anIt.toBack();
633   while (anIt.hasPrevious()) {
634     ModuleBase_Operation* anOp = anIt.previous();
635     if (anOp) {
636       aResultOp = anOp;
637       break;
638     }
639   }
640   if (aResultOp) {
641     //bool isModified = aCurrentOperation->isModified();
642     //aResultOp->setIsModified(aResultOp->isModified() || isModified);
643     resumeOperation(aResultOp);
644     onValidateOperation();
645   }
646 }
647
648 bool XGUI_OperationMgr::onKeyReleased(QObject *theObject, QKeyEvent* theEvent)
649 {
650   bool isAccepted = false;
651
652   // Let the manager decide what to do with the given key combination.
653   ModuleBase_Operation* anOperation = currentOperation();
654   switch (theEvent->key()) {
655     case Qt::Key_Tab:
656     case Qt::Key_Backtab:
657     {
658       ModuleBase_Operation* aOperation = currentOperation();
659       if (aOperation) {
660         ModuleBase_IPropertyPanel* aPanel = anOperation->propertyPanel();
661         if (aPanel) {
662           QWidget* aFocusedWidget = qApp->focusWidget();
663           bool isPPChildObject = aFocusedWidget && isChildObject(aFocusedWidget, aPanel);
664           if (!isPPChildObject) {
665             // check for case when the operation is started but property panel is not filled
666             XGUI_PropertyPanel* aPP = dynamic_cast<XGUI_PropertyPanel*>(aPanel);
667             aPP->setFocusNextPrevChild(theEvent->key() == Qt::Key_Tab);
668             isAccepted = true;
669           }
670         }
671       }
672     }
673     break;
674     case Qt::Key_Return:
675     case Qt::Key_Enter: {
676       isAccepted = onProcessEnter(theObject);
677     }
678     break;
679     case Qt::Key_N:
680     case Qt::Key_P: {
681       bool noModifiers = (theEvent->modifiers() == Qt::NoModifier);
682       if (noModifiers) {
683         ModuleBase_IViewer* aViewer = myWorkshop->viewer();
684         Handle(AIS_InteractiveContext) aContext = aViewer->AISContext();
685         if (!aContext.IsNull()) {
686           Handle(V3d_View) aView = aViewer->activeView();
687           if ((theEvent->key() == Qt::Key_N))
688             aContext->HilightNextDetected(aView);
689           else if ((theEvent->key() == Qt::Key_P))
690             aContext->HilightPreviousDetected(aView);
691           aViewer->updateHighlight();
692           isAccepted = true;
693         }
694       }
695       }
696       break;
697    case Qt::Key_H:
698      if ((theEvent->modifiers() == Qt::NoModifier))
699       myWorkshop->viewer()->hideSelectionHighlight();
700      break;
701    default:
702       isAccepted = false;
703       break;
704   }
705   //if(anOperation) {
706   //  anOperation->keyReleased(theEvent->key());
707   //}
708   return isAccepted;
709 }
710
711 bool XGUI_OperationMgr::onKeyPressed(QObject *theObject, QKeyEvent* theEvent)
712 {
713   // Let the manager decide what to do with the given key combination.
714   ModuleBase_Operation* anOperation = currentOperation();
715   bool isAccepted = false;
716   switch (theEvent->key()) {
717     case Qt::Key_Escape: {
718       // processing in message box
719       if (myActiveMessageBox)
720       {
721         myActiveMessageBox->reject();
722         isAccepted = true;
723       }
724       // processing in the active widget
725       ModuleBase_Operation* aOperation = currentOperation();
726       if (!isAccepted && aOperation) {
727         ModuleBase_IPropertyPanel* aPanel = aOperation->propertyPanel();
728         if (aPanel) {
729           ModuleBase_ModelWidget* anActiveWgt = aPanel->activeWidget();
730           if (anActiveWgt)
731           {
732             isAccepted = anActiveWgt && anActiveWgt->processAction(ActionEscape);
733             if (isAccepted) {
734               ModuleBase_OperationFeature* aFOperation =
735                 dynamic_cast<ModuleBase_OperationFeature*>(currentOperation());
736               if (aFOperation)
737                 aFOperation->setNeedToBeAborted(true);
738             }
739           }
740         }
741       }
742       if (!isAccepted)
743       {
744         XGUI_ActiveControlSelector* anActiveSelector =
745           XGUI_Tools::workshop(myWorkshop)->activeControlMgr()->activeSelector();
746         if (anActiveSelector && anActiveSelector->getType() == XGUI_FacesPanelSelector::Type())
747           isAccepted = XGUI_Tools::workshop(myWorkshop)->facesPanel()->processAction(ActionEscape);
748       }
749       // default Escape button functionality
750       if (!isAccepted && aOperation) {
751         onAbortOperation();
752         isAccepted = true;
753       }
754     }
755     break;
756     case Qt::Key_H:
757       if ((theEvent->modifiers() == Qt::NoModifier))
758         myWorkshop->viewer()->showSelectionHighlight();
759       break;
760   }
761   return isAccepted;
762 }
763
764 bool XGUI_OperationMgr::onProcessEnter(QObject* theObject)
765 {
766   bool isAccepted = false;
767   ModuleBase_Operation* aOperation = currentOperation();
768   // to avoid enter processing when operation has not been started yet
769   if (!aOperation)
770     return isAccepted;
771   ModuleBase_IPropertyPanel* aPanel = aOperation->propertyPanel();
772   if (!aPanel)
773     return isAccepted;
774   // the next code is obsolete as we want to process Enter in property panel always
775   // only property panel enter is processed in order to do not process enter in application dialogs
776   //bool isPPChild = isChildObject(theObject, aPanel);
777   //if (!isPPChild)
778   //  return isAccepted;
779
780   ModuleBase_ModelWidget* anActiveWgt = aPanel->activeWidget();
781   bool isAborted = false;
782   if (!anActiveWgt) {
783     QWidget* aFocusWidget = aPanel->focusWidget();
784     QToolButton* aCancelBtn =
785       dynamic_cast<XGUI_PropertyPanel*>(aPanel)->findButton(PROP_PANEL_CANCEL);
786     if (aFocusWidget && aCancelBtn && aFocusWidget == aCancelBtn) {
787       abortOperation(aOperation);
788       isAccepted = true;
789       isAborted = true;
790     }
791   }
792   if (!isAborted) {
793     isAccepted = anActiveWgt && anActiveWgt->processAction(ActionEnter);
794     if (!isAccepted) {
795       isAccepted =
796         myWorkshop->module()->processEnter(anActiveWgt ? anActiveWgt->attributeID() : "");
797       if (!isAccepted) {
798         /// functionality is similar to Apply click
799         ModuleBase_OperationFeature* aFOperation =
800           dynamic_cast<ModuleBase_OperationFeature*>(currentOperation());
801         if (!aFOperation ||
802             myWorkshop->module()->getFeatureError(aFOperation->feature()).isEmpty()) {
803           // key released is emitted to apply the current value to the model
804           // if it was modified in PP
805           emit keyEnterReleased();
806           commitOperation();
807           isAccepted = true;
808         }
809         else
810           isAccepted = false;
811       }
812     }
813   }
814   return isAccepted;
815 }
816
817 bool editorControl(QObject* theObject)
818 {
819   QLineEdit* aLineEdit = dynamic_cast<QLineEdit*>(theObject);
820   return aLineEdit;
821 }
822
823 bool XGUI_OperationMgr::onProcessDelete(QObject* theObject)
824 {
825   bool isAccepted = false;
826   ModuleBase_Operation* aOperation = currentOperation();
827   ModuleBase_ModelWidget* anActiveWgt = 0;
828   // firstly the widget should process Delete action
829   ModuleBase_IPropertyPanel* aPanel;
830   bool isPPChildObject = false;
831   if (aOperation) {
832     aPanel = aOperation->propertyPanel();
833     if (aPanel) {
834       isPPChildObject = isChildObject(theObject, aPanel);
835       // process delete in active widget only if delete sender is child of property panel
836       // it is necessary for the case when OB is shown, user perform selection and click Delete
837       if (isPPChildObject) {
838         anActiveWgt = aPanel->activeWidget();
839         if (anActiveWgt) {
840           isAccepted = anActiveWgt->processAction(ActionDelete);
841         }
842       }
843     }
844   }
845   if (!isAccepted)
846   {
847     XGUI_ActiveControlSelector* anActiveSelector =
848       XGUI_Tools::workshop(myWorkshop)->activeControlMgr()->activeSelector();
849     if (anActiveSelector && anActiveSelector->getType() == XGUI_FacesPanelSelector::Type())
850       isAccepted = XGUI_Tools::workshop(myWorkshop)->facesPanel()->processAction(ActionDelete);
851   }
852   if (!isAccepted) {
853     // after widget, object browser and viewer should process delete
854     /// other widgets such as line edit controls should not lead to
855     /// processing delete by workshop
856     XGUI_ObjectsBrowser* aBrowser = XGUI_Tools::workshop(myWorkshop)->objectBrowser();
857     QWidget* aViewPort = myWorkshop->viewer()->activeViewPort();
858     bool isToDeleteObject = true;
859     XGUI_Workshop* aWorkshop = XGUI_Tools::workshop(myWorkshop);
860     XGUI_ContextMenuMgr* aContextMenuMgr = aWorkshop->contextMenuMgr();
861     if (theObject == aBrowser->treeView()) {
862       aContextMenuMgr->updateObjectBrowserMenu();
863       isToDeleteObject = aContextMenuMgr->action("DELETE_CMD")->isEnabled();
864     }
865     else if (isChildObject(theObject, aViewPort)) {
866       aContextMenuMgr->updateViewerMenu();
867       isToDeleteObject = aContextMenuMgr->action("DELETE_CMD")->isEnabled();
868     }
869     else if (isPPChildObject) {
870       // property panel child object is processed to process delete performed on Apply button of PP
871       isToDeleteObject = true;
872     }
873     else if (editorControl(theObject)) {
874       isToDeleteObject = false; /// Line Edit of Rename operation in ObjectBrowser
875       isAccepted = true;
876     }
877
878     if (isToDeleteObject) {
879       aWorkshop->deleteObjects();
880       isAccepted = true;
881     }
882   }
883
884   return isAccepted;
885 }
886
887 bool XGUI_OperationMgr::isChildObject(const QObject* theObject, const QObject* theParent)
888 {
889   bool isPPChild = false;
890   if (theParent && theObject) {
891     QObject* aParent = (QObject*)theObject;
892     while (aParent ) {
893       isPPChild = aParent == theParent;
894       if (isPPChild)
895         break;
896       aParent = aParent->parent();
897     }
898   }
899   return isPPChild;
900 }
901
902 QMessageBox* XGUI_OperationMgr::createMessageBox(const QString& theMessage)
903 {
904   QMessageBox * aMessageBox = new QMessageBox(QMessageBox::Question,
905     QObject::tr("Abort operation"), theMessage, QMessageBox::Ok | QMessageBox::Cancel,
906     qApp->activeWindow());
907   aMessageBox->setDefaultButton(QMessageBox::Cancel);
908   aMessageBox->setEscapeButton(QMessageBox::No); // operation manager should process Esc key
909
910   return aMessageBox;
911 }
912
913 QMessageBox* XGUI_OperationMgr::createInformationBox(const QString& theMessage)
914 {
915   QMessageBox * aMessageBox = new QMessageBox(QMessageBox::Question,
916     QObject::tr("Validate operation"), theMessage, QMessageBox::Ok,
917     qApp->activeWindow());
918   aMessageBox->setDefaultButton(QMessageBox::Ok);
919   aMessageBox->setEscapeButton(QMessageBox::No); // operation manager should process Esc key
920
921   return aMessageBox;
922 }