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