BornAgain  1.19.79
Simulate and fit neutron and x-ray scattering at grazing incidence
ActionManager.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/View/Main/ActionManager.cpp
6 //! @brief Implements class ActionManager
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2021
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
16 #include "Base/Util/Assert.h"
17 #include "Base/Util/SysUtils.h"
20 #include "GUI/Util/Path.h"
21 #include "GUI/View/Job/JobView.h"
27 #include <QDesktopServices>
28 #include <QDir>
29 #include <QMenuBar>
30 #include <QSettings>
31 #include <QShortcut>
32 #include <QUrl>
33 
35  : QObject(parent)
36  , m_mainWindow(parent)
37  , m_newAction(nullptr)
38  , m_openAction(nullptr)
39  , m_saveAction(nullptr)
40  , m_saveAsAction(nullptr)
41  , m_exitAction(nullptr)
42  , m_aboutAction(nullptr)
43  , m_menuBar(nullptr)
44  , m_fileMenu(nullptr)
45  , m_settingsMenu(nullptr)
46  , m_viewMenu(nullptr)
47  , m_recentProjectsMenu(nullptr)
48  , m_helpMenu(nullptr)
49  , m_simulateShortcut(nullptr)
50 {
51  createActions();
52  createMenus();
54 
57 
60 
62 }
63 
65 {
66  const bool documentExists = gSessionData->projectDocument.has_value();
67  m_saveAction->setEnabled(documentExists);
68  m_saveAsAction->setEnabled(documentExists);
69  m_closeProjectAction->setEnabled(documentExists);
70 }
71 
73 {
74  ProjectManager* projectManager = m_mainWindow->projectManager();
75  ASSERT(projectManager);
76 
77  // new project action
78  m_newAction = new QAction("&New Project", m_mainWindow);
79  m_newAction->setShortcuts(QKeySequence::New);
80  m_newAction->setStatusTip("Create a new project");
81  connect(m_newAction, &QAction::triggered, projectManager, &ProjectManager::newProject);
82 
83  // open project action
84  m_openAction = new QAction("&Open Project", m_mainWindow);
85  m_openAction->setShortcuts(QKeySequence::Open);
86  m_openAction->setStatusTip("Open an existing project");
87  connect(m_openAction, &QAction::triggered, projectManager,
88  [projectManager]() { projectManager->openProject(); });
89 
90  // save project action
91  m_saveAction = new QAction("&Save Project", m_mainWindow);
92  m_saveAction->setShortcuts(QKeySequence::Save);
93  m_saveAction->setStatusTip("Save project");
94  m_saveAction->setShortcutContext(Qt::ApplicationShortcut);
95  connect(m_saveAction, &QAction::triggered, projectManager,
96  [projectManager]() { projectManager->saveProject(); });
97 
98  // save-as project action
99  m_saveAsAction = new QAction("Save &As...", m_mainWindow);
100  m_saveAsAction->setShortcuts(QKeySequence::SaveAs);
101  m_saveAsAction->setStatusTip("Save project under different name");
102  connect(m_saveAsAction, &QAction::triggered, projectManager, &ProjectManager::saveProjectAs);
103 
104  // close project action
105  m_closeProjectAction = new QAction("Close Project", m_mainWindow);
106  m_closeProjectAction->setStatusTip("Save project under different name");
107  connect(m_closeProjectAction, &QAction::triggered, projectManager,
109 
110  // exit application action
111  m_exitAction = new QAction("&Quit", this);
112  m_exitAction->setShortcuts(QKeySequence::Quit);
113  m_exitAction->setStatusTip("Exit the application");
114  connect(m_exitAction, &QAction::triggered, m_mainWindow, &MainWindow::close);
115 
116  // visit web doc action
117  m_webdocAction = new QAction("&Visit web docs", this);
118  m_webdocAction->setStatusTip("Open BornAgain documentation in default browser");
119  connect(m_webdocAction, &QAction::triggered, this,
120  []() { QDesktopServices::openUrl(QUrl("https://www.bornagainproject.org/latest")); });
121 
122  // about application action
123  m_aboutAction = new QAction("&About BornAgain", this);
124  m_aboutAction->setStatusTip("About the application");
125  connect(m_aboutAction, &QAction::triggered, this, &ActionManager::onAboutApplication);
126 }
127 
129 {
130  m_menuBar = new QMenuBar(nullptr); // No parent (System menu bar on Mac OS X)
131 
132 #ifndef Q_OS_MAC
133  m_mainWindow->setMenuBar(m_menuBar);
134 #endif
135 
136  // File Menu
137  m_fileMenu = m_menuBar->addMenu("&File");
138  m_fileMenu->addAction(m_newAction);
139  m_fileMenu->addAction(m_openAction);
140  connect(m_fileMenu, &QMenu::aboutToShow, this, &ActionManager::onAboutToShowFileMenu);
141 
142  m_recentProjectsMenu = m_fileMenu->addMenu("&Recent Projects");
143 
144  m_fileMenu->addSeparator();
145  m_fileMenu->addAction(m_saveAction);
146  m_fileMenu->addAction(m_saveAsAction);
147  m_fileMenu->addAction(m_closeProjectAction);
148 
149  m_fileMenu->addSeparator();
150  m_fileMenu->addAction(m_exitAction);
151 
152  // Settings Menu
153  m_settingsMenu = new QMenu("&Settings", m_mainWindow);
154  onAboutToShowSettingsMenu(); // MacOS feature: action should exist already, otherwise menuBar
155  // will not add menu
156  connect(m_settingsMenu, &QMenu::aboutToShow, this, &ActionManager::onAboutToShowSettingsMenu);
157  m_menuBar->addMenu(m_settingsMenu);
158 
159  // View menu
160  m_viewMenu = new QMenu("&View", m_mainWindow);
161  onAboutToShowViewMenu(); // MacOS feature: action should exist already, otherwise menuBar will
162  // not add menu
163  connect(m_viewMenu, &QMenu::aboutToShow, this, &ActionManager::onAboutToShowViewMenu);
164  m_menuBar->addMenu(m_viewMenu);
165 
166  // Help Menu
167  m_helpMenu = m_menuBar->addMenu("&Help");
168  m_helpMenu->addAction(m_webdocAction);
169  m_helpMenu->addAction(m_aboutAction);
170 
172 }
173 
175 {
176  m_simulateShortcut = new QShortcut(QKeySequence("Ctrl+r"), m_mainWindow);
177  m_simulateShortcut->setContext((Qt::ApplicationShortcut));
178  connect(m_simulateShortcut, &QShortcut::activated, m_mainWindow,
180 }
181 
183 {
184  m_recentProjectsMenu->clear();
185 
186  bool hasRecentProjects = false;
187  int orderNr = 1;
188  for (const QString& file : m_mainWindow->projectManager()->recentProjects()) {
189  hasRecentProjects = true;
190  QString actionText = GUI::Util::Path::withTildeHomePath(QDir::toNativeSeparators(file));
191  if (orderNr < 10)
192  actionText = QString("&%1 ").arg(orderNr) + actionText;
193  QAction* action = m_recentProjectsMenu->addAction(actionText);
194  action->setData(QVariant::fromValue(file));
195  connect(action, &QAction::triggered, m_mainWindow, &MainWindow::openRecentProject);
196  orderNr++;
197  }
198  m_recentProjectsMenu->setEnabled(hasRecentProjects);
199 
200  if (hasRecentProjects) {
201  m_recentProjectsMenu->addSeparator();
202  QAction* action = m_recentProjectsMenu->addAction("&Clear Menu");
203  connect(action, &QAction::triggered, m_mainWindow->projectManager(),
205  }
206 }
207 
209 {
210  m_settingsMenu->clear();
211  m_settingsMenu->setToolTipsVisible(true);
212 
213  QSettings settings;
214 
215  settings.beginGroup(GUI::Constants::S_SESSIONMODELVIEW);
216  QAction* action = m_settingsMenu->addAction("&Model tech view");
217  action->setToolTip("Additional developer's view will appear in left control tab bar");
218  action->setCheckable(true);
219  action->setChecked(settings.value(GUI::Constants::S_VIEWISACTIVE, false).toBool());
220  connect(action, &QAction::toggled, this, &ActionManager::setSessionModelViewActive);
221  settings.endGroup();
222 
223  action = m_settingsMenu->addAction("&Enable autosave");
224  action->setToolTip("Project will be saved periodically in project's autosave directory.\n"
225  "When opening project, recover option will be suggested, if possible.");
226  action->setCheckable(true);
227  action->setChecked(m_mainWindow->projectManager()->isAutosaveEnabled());
228  connect(action, &QAction::toggled, m_mainWindow->projectManager(),
230 
231  action = m_settingsMenu->addAction("&Create project on startup");
232  action->setCheckable(true);
233  action->setChecked(appSettings->createNewProjectOnStartup());
234  connect(action, &QAction::toggled,
235  [](bool b) { appSettings->setCreateNewProjectOnStartup(b); });
236 
237  m_settingsMenu->addSeparator();
238 
239  auto* unitActions = new QActionGroup(this);
240  const auto addUnitAction = [=](const QString& text, bool isAngstrom) {
241  auto* action = m_settingsMenu->addAction(
242  text, [=]() { appSettings->setDefaultUnitIsAngstrom(isAngstrom); });
243  action->setCheckable(true);
244  action->setChecked(appSettings->defaultUnitIsAngstrom() == isAngstrom);
245  unitActions->addAction(action);
246  };
247 
248  addUnitAction("Lengths in nanometer", false);
249  addUnitAction(u8"Lengths in \u00c5ngstrom", true);
250 
251  m_settingsMenu->addSeparator();
252 
253  auto* styleActions = new QActionGroup(this);
254  const auto addStyleAction = [=](const QString& text, ApplicationSettings::Style style) {
255  auto* action = m_settingsMenu->addAction(text, [=]() {
256  appSettings->setStyleToUse(style);
257  appSettings->loadStyle(style);
258  });
259  action->setCheckable(true);
260  action->setChecked(appSettings->currentStyle() == style);
261  styleActions->addAction(action);
262  };
263 
264  addStyleAction("Native style", ApplicationSettings::Style::native);
265  addStyleAction("Light style", ApplicationSettings::Style::light);
266 
267  // The following menu entry is commented since the dark style is not completed so far. Once it
268  // is complete, just uncomment the line. See also issue #220
269  //
270  // addStyleAction("Dark style", ApplicationSettings::Style::dark);
271 }
272 
274 {
275  m_viewMenu->clear();
276 
277  auto* view = m_mainWindow->currentView();
278  if (auto* sampleView = dynamic_cast<SampleView*>(view); sampleView != nullptr)
279  sampleView->fillViewMenu(m_viewMenu);
280  if (auto* jobView = dynamic_cast<JobView*>(view); jobView != nullptr)
281  jobView->fillViewMenu(m_viewMenu);
282 }
283 
285 {
286  QSettings settings;
287  settings.beginGroup(GUI::Constants::S_SESSIONMODELVIEW);
288  settings.setValue(GUI::Constants::S_VIEWISACTIVE, status);
289  settings.endGroup();
291 }
292 
294 {
295  AboutDialog dialog(m_mainWindow);
296  dialog.exec();
297 }
298 
300 {
301  // not every view support view menu entries -> hide it, if empty
303  m_viewMenu->menuAction()->setVisible(!m_viewMenu->actions().isEmpty());
304 }
Defines class AboutDialog.
Defines class ActionManager.
ApplicationSettings * appSettings
global pointer to the instance
Defines class ApplicationSettings.
Defines class JobView.
Defines class MainWindow.
Defines class Helpers functions.
Defines class ProjectManager.
Defines class SampleView.
SessionData * gSessionData
global pointer to the single instance
Definition: SessionData.cpp:17
Defines struct SessionData.
About BornAgain dialog.
Definition: AboutDialog.h:24
ActionManager(MainWindow *parent)
MainWindow * m_mainWindow
Definition: ActionManager.h:45
void onCurrentViewChanged()
QMenu * m_viewMenu
Definition: ActionManager.h:59
void createGlobalShortcuts()
QAction * m_exitAction
Definition: ActionManager.h:52
QMenu * m_helpMenu
Definition: ActionManager.h:61
QAction * m_newAction
Definition: ActionManager.h:47
QMenu * m_settingsMenu
Definition: ActionManager.h:58
void onAboutToShowViewMenu()
QAction * m_saveAsAction
Definition: ActionManager.h:50
void onAboutToShowFileMenu()
void onAboutToShowSettingsMenu()
void onAboutApplication()
void createActions()
QAction * m_closeProjectAction
Definition: ActionManager.h:51
QMenu * m_fileMenu
Definition: ActionManager.h:57
QMenu * m_recentProjectsMenu
Definition: ActionManager.h:60
QShortcut * m_simulateShortcut
Definition: ActionManager.h:63
QAction * m_openAction
Definition: ActionManager.h:48
void setSessionModelViewActive(bool status)
QAction * m_saveAction
Definition: ActionManager.h:49
QMenuBar * m_menuBar
Definition: ActionManager.h:56
QAction * m_webdocAction
Definition: ActionManager.h:53
void updateActionEnabling()
QAction * m_aboutAction
Definition: ActionManager.h:54
void setStyleToUse(Style style)
void setDefaultUnitIsAngstrom(bool b) const
ApplicationSettings::Style currentStyle()
bool createNewProjectOnStartup() const
bool defaultUnitIsAngstrom() const
void loadStyle(ApplicationSettings::Style style)
void setCreateNewProjectOnStartup(bool b)
The JobView class is a main view to show list of jobs, job results and widgets for real time and fitt...
Definition: JobView.h:37
QWidget * currentView() const
Definition: MainWindow.cpp:127
void onSessionModelViewActive(bool isActive)
Inserts/removes developers SessionModelView on the left tabbar.
Definition: MainWindow.cpp:180
void onRunSimulationShortcut()
Definition: MainWindow.cpp:170
void openRecentProject()
Definition: MainWindow.cpp:162
void currentViewChanged(ViewId newView)
ProjectManager * projectManager()
Definition: MainWindow.cpp:122
Handles activity related to opening/save projects.
bool saveProject(QString projectFileName="")
Processes save project request.
static ProjectManager * instance()
void clearRecentProjects()
Clears list of recent projects.
void setAutosaveEnabled(bool value)
bool closeCurrentProject()
Processes close current project request. Call save/discard/cancel dialog, if necessary....
void openProject(QString fileName="")
Opens existing project. If fileName is empty, will popup file selection dialog.
bool isAutosaveEnabled() const
void newProject()
Processes new project request (close old project, rise dialog for project name, create project).
bool saveProjectAs()
Processes 'save project as' request.
QStringList recentProjects()
Returns list of recent projects, validates if projects still exists on disk.
void documentOpenedOrClosed(bool opened)
Defines namespace GUI::Constants.
const char S_VIEWISACTIVE[]
const char S_SESSIONMODELVIEW[]
QString withTildeHomePath(const QString &path)
Definition: Path.cpp:45
std::optional< ProjectDocument * > projectDocument
Definition: SessionData.h:27