Salome HOME
Issue #2344: Fix cleared selection on Cancel of Undo/Redo command
[modules/shaper.git] / src / XGUI / XGUI_ActionsMgr.cpp
1 // Copyright (C) 2014-2017  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
18 // email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
19 //
20
21 #ifndef HAVE_SALOME
22 #include <AppElements_Command.h>
23 #endif
24
25 #include <XGUI_ActionsMgr.h>
26 #include <XGUI_Workshop.h>
27 #include <XGUI_OperationMgr.h>
28 #include <XGUI_SalomeConnector.h>
29 #include <XGUI_Selection.h>
30 #include <XGUI_SelectionMgr.h>
31
32 #include <Events_Loop.h>
33 #include <Events_InfoMessage.h>
34
35 #include <ModelAPI_Session.h>
36 #include <ModelAPI_Events.h>
37 #include <ModelAPI_Validator.h>
38 #include <ModuleBase_Operation.h>
39 #include <ModuleBase_OperationFeature.h>
40 #include <ModuleBase_SelectionValidator.h>
41 #include <ModuleBase_Tools.h>
42
43
44 #include <QAction>
45 #include <QMainWindow>
46
47 #ifdef _DEBUG
48 #include <iostream>
49 #include <QDebug>
50 #endif
51
52 XGUI_ActionsMgr::XGUI_ActionsMgr(XGUI_Workshop* theParent)
53     : QObject(theParent),
54       myWorkshop(theParent),
55       myOperationMgr(theParent->operationMgr())
56 {
57   // Default shortcuts
58   myShortcuts << QKeySequence::Save;
59   myShortcuts << QKeySequence::Undo;
60   myShortcuts << QKeySequence::Redo;
61   myShortcuts << QKeySequence::Open;
62   myShortcuts << QKeySequence::Close;
63
64   //Initialize event listening
65   Events_Loop* aLoop = Events_Loop::loop();
66   static Events_ID aStateResponseEventId =
67       Events_Loop::loop()->eventByName(EVENT_FEATURE_STATE_RESPONSE);
68   aLoop->registerListener(this, aStateResponseEventId, NULL, true);
69 }
70
71 XGUI_ActionsMgr::~XGUI_ActionsMgr()
72 {
73 }
74
75 void XGUI_ActionsMgr::addCommand(QAction* theCmd)
76 {
77   QString aId = theCmd->data().toString();
78   if (aId.isEmpty()) {
79     return;
80   }
81   myActions.insert(aId, theCmd);
82 #ifdef HAVE_SALOME
83     XGUI_Workshop* aWorkshop = static_cast<XGUI_Workshop*>(parent());
84     const std::shared_ptr<Config_FeatureMessage>& anInfo =
85                          aWorkshop->salomeConnector()->featureInfo(aId);
86     if (anInfo.get())
87       myNestedActions[aId] = QString::fromStdString(anInfo->nestedFeatures())
88                                    .split(" ", QString::SkipEmptyParts);
89 #else
90   AppElements_Command* aXCmd = dynamic_cast<AppElements_Command*>(theCmd);
91   myNestedActions[aId] = aXCmd->nestedCommands();
92 #endif
93 }
94
95 void XGUI_ActionsMgr::addNestedCommands(const QString& theId, const QStringList& theCommands)
96 {
97   myNestedActions[theId] = theCommands;
98 }
99
100 QStringList XGUI_ActionsMgr::nestedCommands(const QString& theId) const
101 {
102   if (myNestedActions.contains(theId))
103     return myNestedActions[theId];
104   return QStringList();
105 }
106
107 bool XGUI_ActionsMgr::isNested(const QString& theId) const
108 {
109   foreach(QString aId, myNestedActions.keys())
110   {
111     QStringList aList = myNestedActions[aId];
112     if (aList.contains(theId))
113       return true;
114   }
115   return false;
116 }
117
118 void XGUI_ActionsMgr::updateCommandsStatus()
119 {
120   setAllEnabled();
121   XGUI_Selection* aSelection = myWorkshop->selector()->selection();
122   if (aSelection->getSelected(ModuleBase_ISelection::AllControls).size() > 0)
123     updateOnViewSelection();
124
125   FeaturePtr anActiveFeature = FeaturePtr();
126   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
127                                                          (myOperationMgr->currentOperation());
128   if (aFOperation) {
129     anActiveFeature = aFOperation->feature();
130     QStringList aNested = allNestedCommands(aFOperation);
131     foreach(QString aAction, myActions.keys()) {
132       if (!aNested.contains(aAction))
133         setActionEnabled(aAction, false);
134     }
135   } else
136     setNestedCommandsEnabled(false);
137
138   updateByPlugins(anActiveFeature);
139   updateByDocumentKind();
140   updateCheckState();
141 }
142
143 void XGUI_ActionsMgr::updateCheckState()
144 {
145   QString eachCommand = QString();
146   foreach(eachCommand, myActions.keys()) {
147     setActionChecked(eachCommand, false);
148   }
149   QStringList ltActiveCommands = myOperationMgr->operationList();
150   foreach(eachCommand, ltActiveCommands) {
151     setActionChecked(eachCommand, true);
152   }
153 }
154
155 void XGUI_ActionsMgr::updateOnViewSelection()
156 {
157   if (!myOperationMgr->hasOperation())
158     return;
159
160   QStringList aIdList = myOperationMgr->operationList();
161   //ModuleBase_Operation* anOperation = myOperationMgr->currentOperation();
162   //FeaturePtr anActiveFeature = anOperation->feature();
163   //if(!anActiveFeature.get())
164   if (aIdList.isEmpty())
165     return;
166
167   ModuleBase_Operation* theOperation = myOperationMgr->currentOperation();
168   //QString aFeatureId = QString::fromStdString(anActiveFeature->getKind());
169   XGUI_Selection* aSelection = myWorkshop->selector()->selection();
170   // only viewer selection is processed
171   SessionPtr aMgr = ModelAPI_Session::get();
172   ModelAPI_ValidatorsFactory* aFactory = aMgr->validators();
173   foreach(QString aFeatureId, aIdList) {
174     foreach(QString aId, nestedCommands(aFeatureId)) {
175       ModelAPI_ValidatorsFactory::Validators aValidators;
176       aFactory->validators(aId.toStdString(), aValidators);
177       ModelAPI_ValidatorsFactory::Validators::iterator aValidatorIt = aValidators.begin();
178       for (; aValidatorIt != aValidators.end(); ++aValidatorIt) {
179         const ModuleBase_SelectionValidator* aSelValidator =
180             dynamic_cast<const ModuleBase_SelectionValidator*>
181             (aFactory->validator(aValidatorIt->first));
182         if (aSelValidator)
183           setActionEnabled(aId, aSelValidator->isValid(aSelection, theOperation));
184       }
185     }
186   }
187 }
188
189 QKeySequence XGUI_ActionsMgr::registerShortcut(const QKeySequence& theKeySequence)
190 {
191   if (theKeySequence.isEmpty()) {
192     return QKeySequence();
193   }
194   if (myShortcuts.contains(theKeySequence)) {
195     QString aMessage = tr("Shortcut %1 is already defined. Ignore.");
196     aMessage = aMessage.arg(theKeySequence.toString());
197     Events_InfoMessage("XGUI_ActionsMgr", aMessage.toStdString()).send();
198     return QKeySequence();
199   }
200   myShortcuts.append(theKeySequence);
201   return theKeySequence;
202 }
203
204 QKeySequence XGUI_ActionsMgr::registerShortcut(const QString& theKeySequence)
205 {
206   if (theKeySequence.isEmpty()) {
207     return QKeySequence();
208   }
209   QKeySequence aResult(theKeySequence);
210   registerShortcut(aResult);
211   return aResult;
212 }
213
214 void XGUI_ActionsMgr::processEvent(const std::shared_ptr<Events_Message>& theMessage)
215 {
216   const Events_ID kResponseEvent =
217       Events_Loop::loop()->eventByName(EVENT_FEATURE_STATE_RESPONSE);
218   if (theMessage->eventID() == kResponseEvent) {
219     std::shared_ptr<ModelAPI_FeatureStateMessage> aStateMessage =
220         std::dynamic_pointer_cast<ModelAPI_FeatureStateMessage>(theMessage);
221     if (!aStateMessage.get())
222       return;
223     std::list<std::string> aFeaturesList = aStateMessage->features();
224     std::list<std::string>::iterator it = aFeaturesList.begin();
225     for( ; it != aFeaturesList.end(); ++it) {
226       QString anActionId = QString::fromStdString(*it);
227       bool theDefaultState = false;
228       if (myActions.contains(anActionId)) {
229         theDefaultState = myActions[anActionId]->isEnabled();
230       }
231       setActionEnabled(anActionId, aStateMessage->state(*it, theDefaultState));
232     }
233   } else if (theMessage.get()) {
234     #ifdef _DEBUG
235     std::cout << "XGUI_ActionsMgr::processEvent: unhandled message caught: " << std::endl
236               << theMessage->eventID().eventText() << std::endl;
237     #endif
238   }
239 }
240
241 QAction* XGUI_ActionsMgr::operationStateAction(OperationStateActionId theId)
242 {
243   QAction* aResult = NULL;
244   if (myOperationActions.contains(theId)) {
245     aResult = myOperationActions.value(theId);
246     //if (theParent && aResult->parent() != theParent) {
247     //  aResult->setParent(theParent);
248     //}
249   } else {
250     QWidget* aParent = myWorkshop->desktop();
251     switch (theId) {
252       case Accept:
253       case AcceptAll: {
254         aResult = ModuleBase_Tools::createAction(QIcon(":pictures/button_ok.png"),
255                             "Apply" /*empty to show error*/, aParent);
256       }
257       break;
258       case Abort:
259       case AbortAll: {
260         aResult = ModuleBase_Tools::createAction(QIcon(":pictures/button_cancel.png"), "Cancel",
261                                                  aParent);
262       }
263       break;
264       case Help: {
265         aResult = ModuleBase_Tools::createAction(QIcon(":pictures/button_help.png"), "Help",
266                                                  aParent);
267       }
268       break;
269       case Preview: {
270         aResult = ModuleBase_Tools::createAction(QIcon(), tr("See preview"),
271                                                  aParent, 0, 0, "Compute preview");
272         aResult->setStatusTip(aResult->toolTip());
273       }
274       break;
275       default:
276         break;
277     }
278     myOperationActions.insert(theId, aResult);
279   }
280   return aResult;
281 }
282
283 QAction* XGUI_ActionsMgr::action(const QString& theId)
284 {
285   QAction* anAction = 0;
286   if(myActions.contains(theId)) {
287     anAction = myActions.value(theId);
288   }
289   return anAction;
290 }
291
292 ActionInfo XGUI_ActionsMgr::actionInfoById(const QString& theId)
293 {
294   ActionInfo aResult;
295   if(myActions.contains(theId)) {
296     aResult.initFrom(myActions.value(theId));
297   } else {
298    aResult.id = theId;
299    aResult.text = theId;
300   }
301   return aResult;
302 }
303
304 void XGUI_ActionsMgr::setAllEnabled()
305 {
306   foreach(QString eachAction, myActions.keys()) {
307     if (myActions.contains(eachAction)) {
308       QAction* aAction = myActions[eachAction];
309       aAction->setEnabled(true);
310     }
311   }
312 }
313
314
315 //!
316 void XGUI_ActionsMgr::setNestedCommandsEnabled(bool theEnabled, const QString& theParent)
317 {
318   QStringList ltNestedActions;
319   if (theParent.isEmpty()) {  //Disable ALL nested
320     foreach(QString eachParent, myNestedActions.keys()) {
321       ltNestedActions << myNestedActions[eachParent];
322     }
323   } else {
324     ltNestedActions << myNestedActions[theParent];
325   }
326   foreach(QString eachNested, ltNestedActions) {
327     setActionEnabled(eachNested, theEnabled);
328   }
329 }
330
331 void XGUI_ActionsMgr::setNestedStackEnabled(ModuleBase_Operation* theOperation)
332 {
333   ModuleBase_OperationFeature* anOperation =
334     dynamic_cast<ModuleBase_OperationFeature*>(theOperation);
335   if(!anOperation || !anOperation->feature())
336     return;
337   FeaturePtr aFeature = anOperation->feature();
338   QString aFeatureId = QString::fromStdString(aFeature->getKind());
339   //setActionEnabled(aFeatureId, true);
340   setNestedCommandsEnabled(true, aFeatureId);
341
342   setNestedStackEnabled(myOperationMgr->previousOperation(theOperation));
343 }
344
345 QStringList XGUI_ActionsMgr::allNestedCommands(ModuleBase_Operation* theOperation)
346 {
347   QStringList aFeatures;
348   ModuleBase_OperationFeature* anOperation =
349     dynamic_cast<ModuleBase_OperationFeature*>(theOperation);
350   if(!anOperation || !anOperation->feature())
351     return aFeatures;
352   FeaturePtr aFeature = anOperation->feature();
353   QString aFeatureId = QString::fromStdString(aFeature->getKind());
354
355   aFeatures << myNestedActions[aFeatureId];
356   aFeatures << allNestedCommands(myOperationMgr->previousOperation(theOperation));
357   return aFeatures;
358 }
359
360 void XGUI_ActionsMgr::setActionChecked(const QString& theId, const bool theChecked)
361 {
362   if (myActions.contains(theId)) {
363     QAction* anAction = myActions[theId];
364     if (anAction->isCheckable()) {
365       anAction->setChecked(theChecked);
366     }
367   }
368 }
369
370 void XGUI_ActionsMgr::setActionEnabled(const QString& theId, const bool theEnabled)
371 {
372   if (myActions.contains(theId)) {
373     QAction* aAction = myActions[theId];
374     // Initially all actions are enabled
375     // If it was disabled for any reason then we can not enable it
376     if (aAction->isEnabled())
377       aAction->setEnabled(theEnabled);
378   }
379 }
380
381 /*
382  * Disables all actions which have the Document Kind different to
383  * the current document's kind
384  */
385 void XGUI_ActionsMgr::updateByDocumentKind()
386 {
387   std::string aStdDocKind = ModelAPI_Session::get()->activeDocument()->kind();
388   QString aDocKind = QString::fromStdString(aStdDocKind);
389   XGUI_Workshop* aWorkshop = static_cast<XGUI_Workshop*>(parent());
390   foreach(QAction* eachAction, myActions.values()) {
391     QString aCmdDocKind;
392 #ifdef HAVE_SALOME
393     QString aId = eachAction->data().toString();
394     if (!aId.isEmpty()) {
395       aCmdDocKind = QString::fromStdString(
396                  aWorkshop->salomeConnector()->featureInfo(aId)->documentKind());
397     }
398 #else
399     AppElements_Command* aCmd = dynamic_cast<AppElements_Command*>(eachAction);
400     aCmdDocKind = QString::fromStdString(aCmd->featureMessage()->documentKind());
401 #endif
402     if(!aCmdDocKind.isEmpty() && aCmdDocKind != aDocKind) {
403       eachAction->setEnabled(false);
404     }
405   }
406 }
407
408 void XGUI_ActionsMgr::updateByPlugins(FeaturePtr anActiveFeature)
409 {
410   static Events_ID aStateRequestEventId = Events_Loop::loop()->eventByName(
411       EVENT_FEATURE_STATE_REQUEST);
412   std::shared_ptr<ModelAPI_FeatureStateMessage> aMsg(
413       new ModelAPI_FeatureStateMessage(aStateRequestEventId, this));
414   aMsg->setFeature(anActiveFeature);
415   Events_Loop::loop()->send(aMsg, false);
416 }