Salome HOME
Manage selection sensitivity in SALOME
[modules/shaper.git] / src / SHAPERGUI / SHAPERGUI.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3
4 #include "SHAPERGUI.h"
5 #include "SHAPERGUI_DataModel.h"
6 #include "SHAPERGUI_OCCSelector.h"
7 #include <SHAPERGUI_NestedButton.h>
8
9 #include <XGUI_Workshop.h>
10 #include <XGUI_PropertyPanel.h>
11 #include <XGUI_ContextMenuMgr.h>
12 #include <XGUI_ObjectsBrowser.h>
13 #include <XGUI_OperationMgr.h>
14 #include <XGUI_Displayer.h>
15 #include <XGUI_MenuMgr.h>
16
17 #include <ModuleBase_Operation.h>
18 #include <ModuleBase_Preferences.h>
19 #include <ModuleBase_ActionInfo.h>
20 #include <ModuleBase_IModule.h>
21
22 #include <LightApp_Application.h>
23 #include <LightApp_SelectionMgr.h>
24 #include <LightApp_OCCSelector.h>
25 #include <LightApp_Study.h>
26 #include <OCCViewer_ViewModel.h>
27
28 #include <SUIT_Selector.h>
29 #include <SUIT_Desktop.h>
30 #include <SUIT_ViewManager.h>
31 #include <SUIT_ResourceMgr.h>
32
33 #include <QtxPopupMgr.h>
34 #include <QtxActionMenuMgr.h>
35 #include <QtxActionToolMgr.h>
36 #include <QtxResourceMgr.h>
37
38 #include <Config_PropManager.h>
39 #include <Config_ModuleReader.h>
40
41 #include <AIS_ListOfInteractive.hxx>
42 #include <AIS_ListIteratorOfListOfInteractive.hxx>
43
44 #include <QDockWidget>
45 #include <QAction>
46 #include <QTimer>
47 #include <QMenu>
48
49
50 extern "C" {
51 SHAPERGUI_EXPORT CAM_Module* createModule()
52 {
53   return new SHAPERGUI();
54 }
55
56 SHAPERGUI_EXPORT char* getModuleVersion()
57 {
58   return (char*)"0.0";
59 }
60 } // extern "C"
61
62 /** 
63 * Class for preferences management
64 */
65 class SHAPERGUI_PrefMgr: public ModuleBase_IPrefMgr
66 {
67 public:
68   /// Constructor
69   /// \param theMgr preferences manager of SALOME
70   /// \param theModName name of the module
71   SHAPERGUI_PrefMgr(LightApp_Preferences* theMgr, const QString& theModName):myMgr(theMgr), myModName(theModName) {}
72
73   virtual int addPreference(const QString& theLbl, int pId, 
74                             SUIT_PreferenceMgr::PrefItemType theType,
75                             const QString& theSection, const QString& theName )
76   {
77     return myMgr->addPreference(myModName, theLbl, pId, theType, theSection, theName);
78   }
79
80   virtual void setItemProperty(const QString& thePropName,
81                                const QVariant& theValue,
82                                const int theId = -1)
83   {
84     myMgr->setItemProperty(thePropName, theValue, theId);
85   }
86
87
88   virtual SUIT_PreferenceMgr* prefMgr() const { return myMgr; }
89
90 private:
91   LightApp_Preferences* myMgr;
92   QString myModName;
93 };
94
95
96
97
98 //******************************************************
99 SHAPERGUI::SHAPERGUI()
100     : LightApp_Module("SHAPER"),
101       mySelector(0), myIsOpened(0), myPopupMgr(0)
102 {
103   myWorkshop = new XGUI_Workshop(this);
104   connect(myWorkshop, SIGNAL(commandStatusUpdated()),
105           this, SLOT(onUpdateCommandStatus()));
106
107   myProxyViewer = new SHAPERGUI_SalomeViewer(this);
108
109   ModuleBase_Preferences::setResourceMgr(application()->resourceMgr());
110   ModuleBase_Preferences::loadCustomProps();
111 }
112
113 //******************************************************
114 SHAPERGUI::~SHAPERGUI()
115 {
116 }
117
118 //******************************************************
119 void SHAPERGUI::initialize(CAM_Application* theApp)
120 {
121   LightApp_Module::initialize(theApp);
122   inspectSalomeModules();
123
124   myWorkshop->startApplication();
125   LightApp_Application* anApp = dynamic_cast<LightApp_Application*>(theApp);
126   if (anApp)
127   {
128     connect(anApp, SIGNAL(preferenceResetToDefaults()), this, SLOT(onDefaultPreferences()));
129   }
130 }
131
132 //******************************************************
133 void SHAPERGUI::windows(QMap<int, int>& theWndMap) const
134 {
135   theWndMap.insert(LightApp_Application::WT_PyConsole, Qt::BottomDockWidgetArea);
136 }
137
138 //******************************************************
139 void SHAPERGUI::viewManagers(QStringList& theList) const
140 {
141   theList.append(OCCViewer_Viewer::Type());
142 }
143
144 //******************************************************
145 void SHAPERGUI::connectToStudy(CAM_Study* theStudy)
146 {
147   // if there are created viewer managers, we should try to create viewer
148   // selector and initialize viewer with it. It sets interactive contect to the 
149   // proxy viewer. If study is opened, CAM application calls this method before the open() of data model
150   // the SHAPER data model is specific and during open(load) redisplay signals are flushed, so
151   // we need to connect to the viewer before it. Here, it seems the most appropriate place for this
152   // according to SALOME architecture.
153   if (!mySelector) {
154     ViewManagerList OCCViewManagers;
155     application()->viewManagers(OCCViewer_Viewer::Type(), OCCViewManagers);
156     if (OCCViewManagers.size() > 0) {
157       mySelector = createSelector(OCCViewManagers.first());
158     }
159   }
160   LightApp_Module::connectToStudy(theStudy);
161 }
162
163 //******************************************************
164 bool SHAPERGUI::activateModule(SUIT_Study* theStudy)
165 {
166   bool isDone = LightApp_Module::activateModule(theStudy);
167   SHAPERGUI_DataModel* aDataModel = dynamic_cast<SHAPERGUI_DataModel*>(dataModel());
168   aDataModel->initRootObject();
169
170   if (isDone) {
171     setMenuShown(true);
172     setToolShown(true);
173
174     QObject* aObj = myWorkshop->objectBrowser()->parent();
175     QDockWidget* aObjDoc = dynamic_cast<QDockWidget*>(aObj);
176     if (aObjDoc) {
177       QAction* aViewAct = aObjDoc->toggleViewAction();
178       aViewAct->setEnabled(true);
179       myWorkshop->objectBrowser()->setVisible(true);
180       aObjDoc->setVisible(true);
181       desktop()->tabifyDockWidget(aObjDoc, myWorkshop->propertyPanel());
182     }
183
184     if (!mySelector) {
185       ViewManagerList OCCViewManagers;
186       application()->viewManagers(OCCViewer_Viewer::Type(), OCCViewManagers);
187       if (OCCViewManagers.size() > 0) {
188         mySelector = createSelector(OCCViewManagers.first());
189       }
190     }
191     // it should be pefromed after the selector creation in order to have AISContext 
192     myWorkshop->activateModule();
193     //action(myEraseAll)->setEnabled(false);
194
195     if (myIsOpened) {
196       myWorkshop->objectBrowser()->rebuildDataTree();
197       myWorkshop->updateCommandStatus();
198       myIsOpened = false;
199     }
200     else
201       myWorkshop->updateCommandStatus();
202   }
203   SUIT_ResourceMgr* aResMgr = application()->resourceMgr();
204   myIsStorePositions = aResMgr->booleanValue("Study", "store_positions", true);
205   myIsEditEnabled = getApp()->isEditEnabled();
206   getApp()->setEditEnabled(false);
207
208   // this following row is caused by #187 bug.
209   // SALOME saves the dock widget positions before deactivateModule() and
210   // load it after the module activation. So, if the panel is visible before
211   // deactivate, it becomes visible after activate.
212   // In order to avoid the visible property panel, the widget position save is
213   // switch off in this module
214   aResMgr->setValue("Study", "store_positions", false);
215
216   // Synchronize displayed objects
217   Handle(AIS_InteractiveContext) aContext;
218   if (mySelector && mySelector->viewer())
219     aContext = mySelector->viewer()->getAISContext();
220
221   if (!aContext.IsNull()) {
222     XGUI_Displayer* aDisp = myWorkshop->displayer();
223     QObjectPtrList aObjList = aDisp->displayedObjects();
224
225     AIS_ListOfInteractive aList;
226     aContext->DisplayedObjects(aList);
227     AIS_ListIteratorOfListOfInteractive aLIt;
228     Handle(AIS_InteractiveObject) anAISIO;
229     foreach (ObjectPtr aObj, aObjList) {
230       AISObjectPtr aPrs = aDisp->getAISObject(aObj);
231       Handle(AIS_InteractiveObject) aAIS = aPrs->impl<Handle(AIS_InteractiveObject)>();
232       bool aFound = false;
233       for (aLIt.Initialize(aList); aLIt.More(); aLIt.Next()) {
234         anAISIO = aLIt.Value();
235         if (anAISIO.Access() == aAIS.Access()) {
236           aFound = true;
237           break;
238         }
239       }
240       if (!aFound) {
241         aObj->setDisplayed(false);
242         //aDisp->erase(aObj, false);
243       }
244     }
245     Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
246   }
247   myProxyViewer->activateViewer(true);
248   return isDone;
249 }
250
251 //******************************************************
252 bool SHAPERGUI::deactivateModule(SUIT_Study* theStudy)
253 {
254   myProxyViewer->activateViewer(false);
255   setMenuShown(false);
256   setToolShown(false);
257
258   myWorkshop->deactivateModule();
259
260   QObject* aObj = myWorkshop->objectBrowser()->parent();
261   QDockWidget* aObjDoc = dynamic_cast<QDockWidget*>(aObj);
262   if (aObjDoc) {
263     aObjDoc->setVisible(false);
264     myWorkshop->objectBrowser()->setVisible(false);
265     QAction* aViewAct = aObjDoc->toggleViewAction();
266     aViewAct->setEnabled(false);
267   }
268
269   // the active operation should be stopped for the next activation.
270   // There should not be active operation and visualized preview.
271   // Abort operation should be performed before the selection's remove
272   // because the displayed objects should be removed from the viewer, but
273   // the AIS context is obtained from the selector.
274   ModuleBase_Operation* anOperation = myWorkshop->operationMgr()->currentOperation();
275   while (anOperation) {
276     anOperation->abort();
277     anOperation = myWorkshop->operationMgr()->currentOperation();
278   }
279   // Delete selector because it has to be redefined on next activation
280   if (mySelector) {
281     myProxyViewer->setSelector(0);
282     delete mySelector;
283     mySelector = 0;
284   }
285
286   //myWorkshop->contextMenuMgr()->disconnectViewer();
287
288   SUIT_ResourceMgr* aResMgr = application()->resourceMgr();
289   aResMgr->setValue("Study", "store_positions", myIsStorePositions);
290   getApp()->setEditEnabled(myIsEditEnabled);
291
292   return LightApp_Module::deactivateModule(theStudy);
293 }
294
295 //******************************************************
296 void SHAPERGUI::onViewManagerAdded(SUIT_ViewManager* theMgr)
297 {
298   if (!mySelector) {
299     mySelector = createSelector(theMgr);
300     myWorkshop->module()->activateSelectionFilters();
301     myWorkshop->synchronizeViewer();
302   }
303 }
304
305 //******************************************************
306 void SHAPERGUI::onViewManagerRemoved(SUIT_ViewManager* theMgr)
307 {
308   if (mySelector) {
309     if (theMgr->getType() == OCCViewer_Viewer::Type()) {
310       OCCViewer_Viewer* aViewer = static_cast<OCCViewer_Viewer*>(theMgr->getViewModel());
311       if (mySelector->viewer() == aViewer) {
312         XGUI_Displayer* aDisp = myWorkshop->displayer();
313         QObjectPtrList aObjects = aDisp->displayedObjects();
314         foreach(ObjectPtr aObj, aObjects)
315           aObj->setDisplayed(false);
316         Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
317         myProxyViewer->setSelector(0);
318         delete mySelector;
319         mySelector = 0;
320
321         myWorkshop->module()->clearViewer();
322       }
323     }
324   }
325 }
326
327 //******************************************************
328 QtxPopupMgr* SHAPERGUI::popupMgr()
329 {
330   if (!myPopupMgr)
331     myPopupMgr = new QtxPopupMgr( 0, this );
332   return myPopupMgr;
333 }
334
335 //******************************************************
336 void SHAPERGUI::onDefaultPreferences()
337 {
338   // reset main resources
339   ModuleBase_Preferences::resetResourcePreferences(preferences());
340   // reset plugin's resources
341   ModuleBase_Preferences::resetConfigPropPreferences(preferences());
342
343   myWorkshop->displayer()->redisplayObjects();
344 }
345
346 //******************************************************
347 void SHAPERGUI::onUpdateCommandStatus()
348 {
349   getApp()->updateActions();
350 }
351
352 //******************************************************
353 SHAPERGUI_OCCSelector* SHAPERGUI::createSelector(SUIT_ViewManager* theMgr)
354 {
355   if (theMgr->getType() == OCCViewer_Viewer::Type()) {
356     OCCViewer_Viewer* aViewer = static_cast<OCCViewer_Viewer*>(theMgr->getViewModel());
357     SHAPERGUI_OCCSelector* aSelector = new SHAPERGUI_OCCSelector(aViewer, getApp()->selectionMgr());
358     LightApp_SelectionMgr* aMgr = getApp()->selectionMgr();
359     QList<SUIT_Selector*> aList;
360     aMgr->selectors(aList);
361     foreach(SUIT_Selector* aSel, aList)
362     {
363       aSel->setEnabled(aSel == aSelector);
364     }
365     myProxyViewer->setSelector(aSelector);
366     return aSelector;
367   }
368   return 0;
369 }
370
371 //******************************************************
372 CAM_DataModel* SHAPERGUI::createDataModel()
373 {
374   return new SHAPERGUI_DataModel(this);
375 }
376
377 QAction* SHAPERGUI::addFeature(const QString& theWBName, const ActionInfo& theInfo,
378                                const bool isAddSeparator)
379 {
380   return addFeature(theWBName,
381                     theInfo.id,
382                     theInfo.text,
383                     theInfo.toolTip,
384                     theInfo.icon,
385                     theInfo.shortcut,
386                     theInfo.checkable,
387                     isAddSeparator);
388 }
389
390 //******************************************************
391 QAction* SHAPERGUI::addFeature(const QString& theWBName, const QString& theId,
392                                const QString& theTitle, const QString& theTip,
393                                const QIcon& theIcon, const QKeySequence& theKeys,
394                                bool isCheckable, const bool isAddSeparator)
395 {
396   static QString aLastTool = "";
397   static int aNb = 0;
398   if (aLastTool.isEmpty())
399     aLastTool = theWBName;
400   else if (theWBName != aLastTool) {
401     aLastTool = theWBName;
402     if (aNb > 20) {
403       desktop()->addToolBarBreak();
404       aNb = 0;
405     }
406   }
407   aNb++;
408
409   int aId = myActionsList.size();
410   myActionsList.append(theId);
411   SUIT_Desktop* aDesk = application()->desktop();
412   int aKeys = 0;
413   for (unsigned int i = 0; i < theKeys.count(); i++)
414     aKeys += theKeys[i];
415   QAction* aAction = createAction(aId, theTip, theIcon, theTitle, theTip, aKeys, aDesk,
416                                   isCheckable);
417   aAction->setData(theId);
418
419   int aWBMenu = createMenu(theWBName, -1, -1, 50/*10-Window, 1000 - Help*/);
420   int aItemId = createMenu(aId, aWBMenu);
421   if (isAddSeparator)
422     createMenu(separator(), aWBMenu);
423
424   int aWBTool = createTool(theWBName, theWBName);
425   int aToolId = createTool(aId, aWBTool);
426   if (isAddSeparator)
427     createTool(separator(), aWBTool);
428
429   return aAction;
430 }
431
432 bool SHAPERGUI::isFeatureOfNested(const QAction* theAction)
433 {
434   return dynamic_cast<const SHAPERGUI_NestedButton*>(theAction);
435 }
436
437 QAction* SHAPERGUI::addFeatureOfNested(const QString& theWBName,
438                                        const ActionInfo& theInfo,
439                                        const QList<QAction*>& theNestedActions)
440 {
441   myActionsList.append(theInfo.id);
442   SUIT_Desktop* aDesk = application()->desktop();
443   SHAPERGUI_NestedButton* anAction = new SHAPERGUI_NestedButton(aDesk, theNestedActions);
444   anAction->setData(theInfo.id);
445   anAction->setCheckable(theInfo.checkable);
446   anAction->setChecked(theInfo.checked);
447   anAction->setEnabled(theInfo.enabled);
448   anAction->setVisible(theInfo.visible);
449   anAction->setIcon(theInfo.icon);
450   anAction->setText(theInfo.text);
451   anAction->setToolTip(theInfo.toolTip);
452   anAction->setShortcut(theInfo.shortcut);
453   anAction->setFont(theInfo.font);
454
455   int aWBMenu = createMenu(theWBName, -1, -1, 50);
456   int aItemId = createMenu(anAction, aWBMenu);
457   createMenu(separator(), aWBMenu); /// nested action is always separated of others
458
459   int aWBTool = createTool(theWBName, theWBName);
460   int aToolId = createTool(anAction, aWBTool);
461   createTool(separator(), aWBTool); /// nested action is always separated of others
462
463   return anAction;
464 }
465
466
467 //******************************************************
468 QAction* SHAPERGUI::addDesktopCommand(const QString& theId, const QString& theTitle,
469                                            const QString& theTip, const QIcon& theIcon,
470                                            const QKeySequence& theKeys, bool isCheckable,
471                                            const char* theMenuSourceText, const int theMenuPosition)
472 {
473   int aMenu = createMenu(tr(theMenuSourceText), -1, -1);
474
475   int aId = myActionsList.size();
476   myActionsList.append(theId);
477   SUIT_Desktop* aDesk = application()->desktop();
478   int aKeys = 0;
479   for (unsigned int i = 0; i < theKeys.count(); i++)
480     aKeys += theKeys[i];
481   QAction* aAction = createAction(aId, theTip, theIcon, theTitle, theTip, aKeys, aDesk,
482                                   isCheckable);
483   aAction->setData(theId);
484   createMenu(aId, aMenu, theMenuPosition);
485   return aAction;
486 }
487
488 //******************************************************
489 void SHAPERGUI::addDesktopMenuSeparator(const char* theMenuSourceText, const int theMenuPosition)
490 {
491   int aMenu = createMenu(tr(theMenuSourceText), -1, -1);
492   createMenu(separator(), aMenu, -1, theMenuPosition);
493 }
494
495 bool SHAPERGUI::addActionInToolbar( QAction* theAction, const QString& theToolBarTitle )
496 {
497   if( !theAction )
498     return false;
499
500   SUIT_Desktop* aDesktop = application()->desktop();
501   if( !aDesktop )
502     return false;
503
504   QtxActionToolMgr* aToolMgr = aDesktop->toolMgr();
505   if( !aToolMgr )
506     return false;
507
508   aToolMgr->append( theAction, theToolBarTitle );
509   return true;
510 }
511
512 //******************************************************
513 QList<QAction*> SHAPERGUI::commandList() const
514 {
515   QList<QAction*> aActions;
516   for (int i = 0; i < myActionsList.size(); i++) {
517     QAction* aCmd = action(i);
518     if (aCmd && myActionsList.contains(aCmd->data().toString()))
519       aActions.append(aCmd);
520   }
521
522   return aActions;
523 }
524
525 //******************************************************
526 QMainWindow* SHAPERGUI::desktop() const
527 {
528   return application()->desktop();
529 }
530
531 void SHAPERGUI::setFeatureInfo(const QString& theFeatureId,
532                                const std::shared_ptr<Config_FeatureMessage>& theMessage)
533 {
534   myFeaturesInfo.insert(theFeatureId, theMessage);
535 }
536
537 std::shared_ptr<Config_FeatureMessage> SHAPERGUI::featureInfo(const QString& theFeatureId)
538 {
539   std::shared_ptr<Config_FeatureMessage> aMessage;
540   if (myFeaturesInfo.contains(theFeatureId))
541     aMessage =  myFeaturesInfo[theFeatureId];
542   return aMessage;
543 }
544
545 //******************************************************
546 void SHAPERGUI::selectionChanged()
547 {
548   LightApp_Module::selectionChanged();
549   myWorkshop->salomeViewerSelectionChanged();
550 }
551
552 //******************************************************
553 void SHAPERGUI::contextMenuPopup(const QString& theClient, QMenu* theMenu, QString& theTitle)
554 {
555   myWorkshop->contextMenuMgr()->updateViewerMenu();
556   myWorkshop->contextMenuMgr()->addViewerMenu(theMenu);
557   LightApp_Module::contextMenuPopup(theClient, theMenu, theTitle);
558 }
559
560
561 //******************************************************
562 void SHAPERGUI::createPreferences()
563 {
564   LightApp_Preferences* pref = preferences();
565   if (!pref)
566     return;
567   ModuleBase_Preferences::updateConfigByResources();
568   QString aModName = moduleName();
569
570   QtxPreferenceItem* item = pref->findItem(aModName, true );
571   if ( item && (!item->isEmpty() )) {
572     item->parentItem()->removeItem(item);
573     delete item;
574   }
575
576   int catId = pref->addPreference(aModName, -1 );
577   if ( catId == -1 )
578     return;
579   SHAPERGUI_PrefMgr aMgr(pref, aModName);
580   ModuleBase_Preferences::createEditContent(&aMgr, catId);
581
582   int viewTab = pref->addItem(tr("Viewer"), catId);
583   // Create other parameters group in viewer tab
584   int otherGroup = pref->addItem(tr("Default selection"), viewTab);
585   pref->setItemProperty("columns", 3, otherGroup);
586   pref->addItem(tr("Faces"), otherGroup, 
587                          SUIT_PreferenceMgr::Bool,
588                          ModuleBase_Preferences::VIEWER_SECTION, "face-selection");
589   pref->addItem(tr("Edges"), otherGroup, 
590                          SUIT_PreferenceMgr::Bool,
591                          ModuleBase_Preferences::VIEWER_SECTION, "edge-selection");
592   pref->addItem(tr("Vertices"), otherGroup, 
593                          SUIT_PreferenceMgr::Bool,
594                          ModuleBase_Preferences::VIEWER_SECTION, "vertex-selection");
595   
596   int sensitivityGroup = pref->addItem(tr("Selection sensitivity"), viewTab);
597   pref->setItemProperty("columns", 2, sensitivityGroup);
598   pref->addItem(tr("Vertex"), sensitivityGroup, SUIT_PreferenceMgr::Double,
599                 ModuleBase_Preferences::VIEWER_SECTION, "point-selection-sensitivity");
600   pref->addItem(tr("Edge"), sensitivityGroup, SUIT_PreferenceMgr::Double,
601                 ModuleBase_Preferences::VIEWER_SECTION, "edge-selection-sensitivity");
602   pref->retrieve();
603 }
604
605 //******************************************************
606 void SHAPERGUI::preferencesChanged(const QString& theSection, const QString& theParam)
607 {
608   SUIT_ResourceMgr* aResMgr = application()->resourceMgr();
609   QString aVal = aResMgr->stringValue(theSection, theParam);
610   Config_Prop* aProp = Config_PropManager::findProp(theSection.toStdString(), theParam.toStdString());
611   std::string aValue = aVal.toStdString();
612   if (aValue.empty()) {
613     aValue = aProp->defaultValue();
614     aResMgr->setValue(theSection, theParam, QString(aValue.c_str()));
615
616     LightApp_Preferences* pref = preferences();
617     if (pref)
618       pref->retrieve();
619   }
620   aProp->setValue(aValue);
621
622   myWorkshop->displayer()->redisplayObjects();
623 }
624
625 void SHAPERGUI::putInfo(const QString& theInfo, const int theMSecs)
626 {
627   application()->putInfo(theInfo, theMSecs);
628 }
629
630 void SHAPERGUI::inspectSalomeModules()
631 {
632   QStringList aModuleNames;
633   getApp()->modules(aModuleNames, false);
634   foreach(QString eachModule, aModuleNames) {
635     Config_ModuleReader::addDependencyModule(eachModule.toStdString());
636   }
637 }
638
639 bool SHAPERGUI::abortAllOperations()
640 {
641   return workshop()->operationMgr()->abortAllOperations();
642 }
643
644 void SHAPERGUI::createFeatureActions()
645 {
646   myWorkshop->menuMgr()->createFeatureActions();
647 }