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