BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
projectmanager.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/coregui/mainwindow/projectmanager.cpp
6 //! @brief Implements class ProjectManager
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2018
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
16 #include "Base/Utils/Assert.h"
28 #include <QApplication>
29 #include <QDateTime>
30 #include <QFileDialog>
31 #include <QMessageBox>
32 #include <QSettings>
33 #include <QStandardPaths>
34 
35 namespace {
36 const QString S_PROJECTMANAGER = "ProjectManager";
37 const QString S_AUTOSAVE = "EnableAutosave";
38 const QString S_DEFAULTPROJECTPATH = "DefaultProjectPath";
39 const QString S_RECENTPROJECTS = "RecentProjects";
40 const QString S_LASTUSEDIMPORTDIR = "LastUsedImportDir";
41 const QString S_LASTUSEDIMPORFILTER1D = "LastUsedImportFilter1D";
42 const QString S_LASTUSEDIMPORFILTER2D = "LastUsedImportFilter2D";
43 } // namespace
44 
46  : m_mainWindow(parent)
47  , m_project_document(nullptr)
48  , m_messageService(new MessageService)
49  , m_saveService(new SaveService(this))
50 
51 {
53  AppSvc::subscribe(this);
54 }
55 
57 {
58  AppSvc::unsubscribe(this);
59  delete m_project_document;
60  delete m_messageService;
61 }
62 
63 //! Reads settings of ProjectManager from global settings.
64 
66 {
67  QSettings settings;
68  m_workingDirectory = QDir::homePath();
69  if (settings.childGroups().contains(S_PROJECTMANAGER)) {
70  settings.beginGroup(S_PROJECTMANAGER);
71 
72  if (!settings.contains(S_AUTOSAVE))
73  settings.setValue(S_AUTOSAVE, true);
74 
75  m_workingDirectory = settings.value(S_DEFAULTPROJECTPATH).toString();
76  m_recentProjects = settings.value(S_RECENTPROJECTS).toStringList();
77 
78  if (settings.contains(S_LASTUSEDIMPORTDIR))
79  m_importDirectory = settings.value(S_LASTUSEDIMPORTDIR, QString()).toString();
80 
81  m_importFilter1D = settings.value(S_LASTUSEDIMPORFILTER1D, m_importFilter1D).toString();
82  m_importFilter2D = settings.value(S_LASTUSEDIMPORFILTER2D, m_importFilter2D).toString();
83 
84  setAutosaveEnabled(settings.value(S_AUTOSAVE).toBool());
85 
86  settings.endGroup();
87  }
88 }
89 
90 //! Saves settings of ProjectManager in global settings.
91 
93 {
94  QSettings settings;
95  settings.beginGroup(S_PROJECTMANAGER);
96  settings.setValue(S_DEFAULTPROJECTPATH, m_workingDirectory);
97  settings.setValue(S_RECENTPROJECTS, m_recentProjects);
98 
99  if (!m_importDirectory.isEmpty())
100  settings.setValue(S_LASTUSEDIMPORTDIR, m_importDirectory);
101  settings.setValue(S_LASTUSEDIMPORFILTER1D, m_importFilter1D);
102  settings.setValue(S_LASTUSEDIMPORFILTER2D, m_importFilter2D);
103 
104  settings.endGroup();
105 }
106 
108 {
109  return m_project_document;
110 }
111 
112 //! Returns list of recent projects, validates if projects still exists on disk.
113 
115 {
116  QStringList updatedList;
117  for (QString fileName : m_recentProjects) {
118  QFile fin(fileName);
119  if (fin.exists())
120  updatedList.append(fileName);
121  }
122  m_recentProjects = updatedList;
123  return m_recentProjects;
124 }
125 
126 //! Returns name of the current project directory.
127 
129 {
131  return m_project_document->projectDir();
132 
133  return "";
134 }
135 
136 //! Returns directory name suitable for saving plots.
137 
139 {
140  QString result = projectDir();
141  if (result.isEmpty())
142  result = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
143 
144  return result;
145 }
146 
147 //! Returns directory name which was used by the user to import files.
148 
150 {
151  return m_importDirectory.isEmpty() ? userExportDir() : m_importDirectory;
152 }
153 
155 {
156  return m_importFilter1D;
157 }
158 
160 {
161  return m_importFilter2D;
162 }
163 
164 //! Sets user import directory in system settings.
165 
166 void ProjectManager::setImportDir(const QString& dirname)
167 {
168  m_importDirectory = dirname;
169 }
170 
171 //! Sets user import directory in system settings.
172 
173 void ProjectManager::setImportDirFromFilePath(const QString& filePath)
174 {
175  m_importDirectory = QFileInfo(filePath).absolutePath();
176 }
177 
179 {
180  m_importFilter1D = filter;
181 }
182 
184 {
185  m_importFilter2D = filter;
186 }
187 
189 {
191 }
192 
194 {
196  QSettings settings;
197  settings.setValue(S_PROJECTMANAGER + "/" + S_AUTOSAVE, value);
198 }
199 
200 //! Updates title of main window when the project was modified.
201 
203 {
205  m_mainWindow->setWindowTitle("*" + m_project_document->projectName());
206  } else {
207  m_mainWindow->setWindowTitle(m_project_document->projectName());
208  }
209 }
210 
211 //! Clears list of recent projects.
212 
214 {
215  m_recentProjects.clear();
216  modified();
217 }
218 
219 //! Processes new project request (close old project, rise dialog for project name, create project).
220 
222 {
223  if (!closeCurrentProject())
224  return;
225 
227 
228  QString projectFileName = acquireProjectFileName(false);
229  if (!projectFileName.isEmpty())
230  saveProject(projectFileName);
231 }
232 
233 //! Processes close current project request. Call save/discard/cancel dialog, if necessary.
234 //! Returns false if saving was canceled.
235 
237 {
238  if (!m_project_document)
239  return true;
240 
241  bool projectWasClosed(true);
242 
244  QMessageBox msgBox;
245  msgBox.setText("The project has been modified.");
246  msgBox.setInformativeText("Do you want to save your changes?");
247  msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
248  msgBox.setDefaultButton(QMessageBox::Save);
249  int ret = msgBox.exec();
250 
251  switch (ret) {
252  case QMessageBox::Save:
253  if (!saveProject())
254  projectWasClosed = false;
255  break;
256  case QMessageBox::Discard:
257  break;
258  case QMessageBox::Cancel:
259  projectWasClosed = false;
260  break;
261  default:
262  break;
263  }
264  }
265 
266  if (projectWasClosed)
268 
269  return projectWasClosed;
270 }
271 
272 //! Processes save project request.
273 
274 bool ProjectManager::saveProject(QString projectFileName)
275 {
276  if (projectFileName.isEmpty()) {
278  projectFileName = m_project_document->projectFileName();
279  else
280  projectFileName = acquireProjectFileName(true);
281  }
282 
283  if (projectFileName.isEmpty())
284  return false;
285 
286  try {
287  m_saveService->save(projectFileName);
288  } catch (const std::exception& ex) {
289  QString message = QString("Failed to save project under '%1'. \n\n").arg(projectFileName);
290  message.append("Exception was thrown.\n\n");
291  message.append(ex.what());
292 
293  QMessageBox::warning(m_mainWindow, "Error while saving project", message);
294  return false;
295  }
296 
298  modified();
299 
300  return true;
301 }
302 
303 //! Processes 'save project as' request.
304 
306 {
307  QString projectFileName = acquireProjectFileName(true);
308 
309  if (projectFileName.isEmpty())
310  return false;
311 
312  return saveProject(projectFileName);
313 }
314 
315 //! Opens existing project. If fileName is empty, will popup file selection dialog.
316 
317 void ProjectManager::openProject(QString fileName)
318 {
319  if (!closeCurrentProject())
320  return;
321 
323 
324  if (fileName.isEmpty()) {
325  fileName =
326  QFileDialog::getOpenFileName(m_mainWindow, "Open project file", workingDirectory(),
327  "BornAgain project Files (*.pro)");
328  if (fileName.isEmpty())
329  return;
330  }
331 
332  loadProject(fileName);
333 
334  if (m_project_document->isReady()) {
336  } else if (m_project_document->hasErrors()) {
340  } else if (m_project_document->hasWarnings()) {
343  }
344  emit modified();
345 }
346 
347 //! Calls dialog window to define project path and name.
348 
350 {
351  if (m_project_document)
352  throw GUIHelpers::Error("ProjectManager::createNewProject() -> Project already exists");
353 
355 
359  m_project_document->setProjectName("Untitled");
362 
364 }
365 
367 {
369 
370  delete m_project_document;
371  m_project_document = 0;
373 }
374 
375 //! Load project data from file name. If autosave info exists, opens dialog for project restore.
376 
377 void ProjectManager::loadProject(const QString& projectFileName)
378 {
379  bool useAutosave = m_saveService && ProjectUtils::hasAutosavedData(projectFileName);
380 
381  if (useAutosave && restoreProjectDialog(projectFileName)) {
382  QApplication::setOverrideCursor(Qt::WaitCursor);
384  m_project_document->setProjectFileName(projectFileName);
386  } else {
387  QApplication::setOverrideCursor(Qt::WaitCursor);
388  m_project_document->load(projectFileName);
389  }
390  QApplication::restoreOverrideCursor();
391 }
392 
393 //! Returns project file name from dialog. Returns empty string if dialog was canceled.
394 
396 {
400 
401  if (dialog.exec() != QDialog::Accepted)
402  return "";
403 
405 
406  return dialog.getProjectFileName();
407 }
408 
409 //! Add name of the current project to the name of recent projects
410 
412 {
413  QString fileName = m_project_document->projectFileName();
414  m_recentProjects.removeAll(fileName);
415  m_recentProjects.prepend(fileName);
417  m_recentProjects.removeLast();
418 }
419 
420 //! Returns default project path.
421 
423 {
424  return m_workingDirectory;
425 }
426 
427 //! Will return 'Untitled' if the directory with such name doesn't exist in project
428 //! path. Otherwise will return Untitled1, Untitled2 etc.
429 
431 {
432  QString result = "Untitled";
433  QDir projectDir = workingDirectory() + "/" + result;
434  if (projectDir.exists()) {
435  for (size_t i = 1; i < 99; ++i) {
436  result = QString("Untitled") + QString::number(i);
437 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
438  projectDir.setPath(workingDirectory() + "/" + result);
439 #else
440  projectDir = workingDirectory() + "/" + result;
441 #endif
442  if (!projectDir.exists())
443  break;
444  }
445  }
446  return result;
447 }
448 
450 {
451  QString message =
452  QString("Failed to load the project '%1' \n\n").arg(m_project_document->projectFileName());
453 
455  message.append(details + "\n");
456 
457  QMessageBox::warning(m_mainWindow, "Error while opening project file", message);
458 }
459 
461 {
465 
466  warningDialog->show();
467  warningDialog->raise();
468 }
469 
470 //! Rises dialog if the project should be restored from autosave. Returns true, if yes.
471 
472 bool ProjectManager::restoreProjectDialog(const QString& projectFileName)
473 {
474  QString title("Recover project");
475 
476  QString message =
477  QString("Project '%1' contains autosaved data.\n\n"
478  "Project saved at %2\nAutosave from %3")
479  .arg(ProjectUtils::projectName(projectFileName))
480  .arg(ProjectUtils::lastModified(projectFileName))
482 
483  return GUIHelpers::question(m_mainWindow, title, message,
484  "\nDo you want to restore from autosave?\n", "Yes, please restore.",
485  "No, keep loading original");
486 }
Defines class AppSvc.
Defines class holding all application models.
Defines the macro ASSERT.
#define ASSERT(condition)
Definition: Assert.h:31
Defines class GUIHelpers functions.
Defines class MainWindow.
Defines class ProjectManager.
Defines MessageService class.
Defines class ProjectLoadWarningDialog.
Defines ProjectUtils namespace.
Defines class SaveService.
static void unsubscribe(ProjectManager *projectManager)
Definition: AppSvc.cpp:28
static void subscribe(ProjectManager *projectManager)
Definition: AppSvc.cpp:23
void resetModels()
reset all models to initial state
ApplicationModels * models()
Definition: mainwindow.cpp:159
The service to collect messages from different senders.
QStringList errorDescriptionList(const QObject *sender=0) const
Returns multi-line string representing error messages of given sender.
new project dialog window
QString getWorkingDirectory() const
QString getProjectFileName() const
Project document class handles all data related to the opened project (sample, jobModel,...
void setApplicationModels(ApplicationModels *applicationModels)
QString projectFileName() const
QString documentVersion() const
bool isReady() const
QString projectName() const
void load(const QString &project_file_name)
void setProjectName(const QString &text)
void setLogger(MessageService *messageService)
QString projectDir() const
void setProjectFileName(const QString &text)
bool hasWarnings() const
bool hasErrors() const
void setModified(bool flag)
The dialog to inform user about encountered problems during the loading of old project.
bool saveProject(QString projectFileName="")
Processes save project request.
void setRecentlyUsedImportFilter1D(const QString &filter)
MainWindow * m_mainWindow
void riseProjectLoadFailedDialog()
void createNewProject()
Calls dialog window to define project path and name.
void deleteCurrentProject()
ProjectManager(MainWindow *parent)
QString userExportDir() const
Returns directory name suitable for saving plots.
ProjectDocument * document()
QString untitledProjectName()
Will return 'Untitled' if the directory with such name doesn't exist in project path.
void loadProject(const QString &projectFileName)
Load project data from file name. If autosave info exists, opens dialog for project restore.
QString m_importFilter2D
Recently used import filter for 2D files.
QStringList m_recentProjects
void clearRecentProjects()
Clears list of recent projects.
void onDocumentModified()
Updates title of main window when the project was modified.
void setAutosaveEnabled(bool value)
void setImportDirFromFilePath(const QString &filePath)
Sets user import directory in system settings.
QString recentlyUsedImportFilter2D() const
SaveService * m_saveService
void addToRecentProjects()
Add name of the current project to the name of recent projects.
QString m_importDirectory
QString acquireProjectFileName(bool forSaveAs)
Returns project file name from dialog. Returns empty string if dialog was canceled.
void setRecentlyUsedImportFilter2D(const QString &filter)
bool closeCurrentProject()
Processes close current project request.
void readSettings()
Reads settings of ProjectManager from global settings.
QString m_workingDirectory
Name of directory from there user prefer to import files.
void openProject(QString fileName="")
Opens existing project. If fileName is empty, will popup file selection dialog.
void setImportDir(const QString &dirname)
Sets user import directory in system settings.
QString userImportDir() const
Returns directory name which was used by the user to import files.
ProjectDocument * m_project_document
Name of directory where project directory was created.
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.
void riseProjectLoadWarningDialog()
QStringList recentProjects()
Returns list of recent projects, validates if projects still exists on disk.
QString m_importFilter1D
Recently used import filter for 1D files.
virtual ~ProjectManager()
MessageService * m_messageService
bool restoreProjectDialog(const QString &projectFileName)
Rises dialog if the project should be restored from autosave. Returns true, if yes.
QString projectDir() const
Returns name of the current project directory.
QString workingDirectory()
Returns default project path.
QString recentlyUsedImportFilter1D() const
void writeSettings()
Saves settings of ProjectManager in global settings.
Provides save/autosave of ProjectDocument in a thread.
Definition: SaveService.h:26
void setAutosaveEnabled(bool value)
Definition: SaveService.cpp:64
void stopService()
Definition: SaveService.cpp:98
void save(const QString &project_file_name)
Definition: SaveService.cpp:56
bool isAutosaveEnabled() const
Definition: SaveService.cpp:78
void setDocument(ProjectDocument *document)
Definition: SaveService.cpp:46
Defines namespace Constants.
const int MAX_RECENT_PROJECTS
void warning(QWidget *parent, const QString &title, const QString &text, const QString &detailedText)
Definition: GUIHelpers.cpp:74
bool question(QWidget *parent, const QString &title, const QString &text, const QString &detailedText, const QString &yesText, const QString &noText)
Definition: GUIHelpers.cpp:94
QString lastModified(const QString &fileName)
Returns a string representing modification time of given file.
QString autosaveName(const QString &projectFileName)
Returns name of project for autoSave from given project file name.
QString projectName(const QString &projectFileName)
Returns project name deduced from project file name.
bool hasAutosavedData(const QString &projectFileName)
Returns true if project with given projectFileName contains autosaved data.
Defines class NewProjectDialog.
Defines class ProjectDocument.