From fcc543c69c1c4bd678a177bc9233d940fa75bdcd Mon Sep 17 00:00:00 2001 From: "Joachim Wuttke (o)" <j.wuttke@fz-juelich.de> Date: Mon, 4 Mar 2024 13:33:39 +0100 Subject: [PATCH] src pair Dirs.* extracted from ProjectManager --- App/main.cpp | 6 + GUI/Support/Data/Dirs.cpp | 392 ++------------------------------------ GUI/Support/Data/Dirs.h | 57 ++---- 3 files changed, 38 insertions(+), 417 deletions(-) diff --git a/App/main.cpp b/App/main.cpp index c35b994c080..5505eab5b38 100644 --- a/App/main.cpp +++ b/App/main.cpp @@ -14,6 +14,7 @@ #include "App/AppOptions.h" #include "Base/Util/Assert.h" +#include "GUI/Support/Data/Dirs.h" #include "GUI/Support/Util/Path.h" #include "GUI/View/Layout/ApplicationSettings.h" #include "GUI/View/Main/MainWindow.h" @@ -28,6 +29,9 @@ #include <QtGlobal> #include <iostream> +// Global variables +BA_GUI_API_ std::unique_ptr<Dirs> gDirs; + void custom_terminate_handler() { try { @@ -74,6 +78,8 @@ int main(int argc, char* argv[]) if (!QDir().exists(dir)) QDir().mkpath(dir); + gDirs = std::make_unique<Dirs>(); + MainWindow win; GUI::Global::mainWindow = &win; diff --git a/GUI/Support/Data/Dirs.cpp b/GUI/Support/Data/Dirs.cpp index ce26507323a..44eb199b2aa 100644 --- a/GUI/Support/Data/Dirs.cpp +++ b/GUI/Support/Data/Dirs.cpp @@ -3,7 +3,7 @@ // BornAgain: simulate and fit reflection and scattering // //! @file GUI/Support/Data/Dirs.cpp -//! @brief Implements class ProjectManager. +//! @brief Implements class Dirs. //! //! @homepage http://www.bornagainproject.org //! @license GNU General Public License v3 or higher (see COPYING) @@ -12,30 +12,16 @@ // // ************************************************************************************************ -#include "GUI/View/Manager/ProjectManager.h" +#include "GUI/Support/Data/Dirs.h" #include "Base/Util/Assert.h" -#include "GUI/Model/Project/ProjectUtil.h" -#include "GUI/Support/Util/MessageService.h" -#include "GUI/View/Info/MessageBox.h" -#include "GUI/View/Layout/ApplicationSettings.h" -#include "GUI/View/Layout/mainwindow_constants.h" -#include "GUI/View/Manager/AutosaveController.h" -#include "GUI/View/Manager/NewProjectDialog.h" -#include "GUI/View/Manager/ProjectLoadProblemDialog.h" -#include "GUI/View/Tool/Globals.h" -#include <QApplication> -#include <QDateTime> -#include <QFileDialog> -#include <QMessageBox> +#include <QDir> +#include <QFileInfo> #include <QSettings> -#include <memory> namespace { -const QString S_PROJECTMANAGER = "ProjectManager"; -const QString S_AUTOSAVE = "EnableAutosave"; +const QString S_DIRS = "Dirs"; const QString S_DEFAULTPROJECTPATH = "DefaultProjectPath"; -const QString S_RECENTPROJECTS = "RecentProjects"; const QString S_LASTUSEDIMPORTDIR = "LastUsedImportDir"; const QString S_LASTUSEDIMPORFILTER1D = "LastUsedImportFilter1D"; const QString S_LASTUSEDIMPORFILTER2D = "LastUsedImportFilter2D"; @@ -43,41 +29,26 @@ const QString S_LASTUSEDIMPORFILTER2D = "LastUsedImportFilter2D"; } // namespace -ProjectManager* ProjectManager::s_instance = nullptr; - -ProjectManager::ProjectManager(QObject* parent) - : QObject(parent) -{ - ASSERT(!s_instance); // it's a singleton - s_instance = this; -} - -ProjectManager::~ProjectManager() +Dirs::Dirs() { - s_instance = nullptr; - gDoc.release(); + readSettings(); } -ProjectManager* ProjectManager::instance() +Dirs::~Dirs() { - ASSERT(s_instance); - return s_instance; + writeSettings(); } -//! Reads settings of ProjectManager from global settings. +//! Reads settings of Dirs from global settings. -void ProjectManager::readSettings() +void Dirs::readSettings() { QSettings settings; m_working_directory = QDir::homePath(); - if (settings.childGroups().contains(S_PROJECTMANAGER)) { - settings.beginGroup(S_PROJECTMANAGER); - - if (!settings.contains(S_AUTOSAVE)) - settings.setValue(S_AUTOSAVE, true); + if (settings.childGroups().contains(S_DIRS)) { + settings.beginGroup(S_DIRS); m_working_directory = settings.value(S_DEFAULTPROJECTPATH).toString(); - m_recent_projects = settings.value(S_RECENTPROJECTS).toStringList(); if (settings.contains(S_LASTUSEDIMPORTDIR)) m_import_directory = settings.value(S_LASTUSEDIMPORTDIR, QString()).toString(); @@ -85,20 +56,17 @@ void ProjectManager::readSettings() m_import_filter1D = settings.value(S_LASTUSEDIMPORFILTER1D, m_import_filter1D).toString(); m_import_filter2D = settings.value(S_LASTUSEDIMPORFILTER2D, m_import_filter2D).toString(); - setAutosaveEnabled(settings.value(S_AUTOSAVE).toBool()); - settings.endGroup(); } } -//! Saves settings of ProjectManager in global settings. +//! Saves settings of Dirs in global settings. -void ProjectManager::writeSettings() +void Dirs::writeSettings() { QSettings settings; - settings.beginGroup(S_PROJECTMANAGER); + settings.beginGroup(S_DIRS); settings.setValue(S_DEFAULTPROJECTPATH, m_working_directory); - settings.setValue(S_RECENTPROJECTS, m_recent_projects); if (!m_import_directory.isEmpty()) settings.setValue(S_LASTUSEDIMPORTDIR, m_import_directory); @@ -108,344 +76,24 @@ void ProjectManager::writeSettings() settings.endGroup(); } -//! Returns list of recent projects, validates if projects still exists on disk. - -QStringList ProjectManager::recentProjects() -{ - QStringList updatedList; - for (const QString& fname : m_recent_projects) - if (QFile fin(fname); fin.exists()) - updatedList.append(fname); - m_recent_projects = updatedList; - return m_recent_projects; -} - -//! Returns name of the current project directory. - -QString ProjectManager::projectDir() const -{ - if (gDoc) - return gDoc->validProjectDir(); - return ""; -} - -//! Returns directory name which was used by the user to import files. - -QString ProjectManager::userImportDir() const -{ - if (m_import_directory.isEmpty()) { - if (gDoc) - return gDoc->userExportDir(); - return ""; - } - return m_import_directory; -} //! Sets user import directory in system settings. - -void ProjectManager::setImportDir(const QString& dirname) +void Dirs::setImportDir(const QString& dirname) { m_import_directory = dirname; } //! Sets user import directory in system settings. -void ProjectManager::setImportDirFromFilePath(const QString& filePath) +void Dirs::setImportDirFromFilePath(const QString& filePath) { m_import_directory = QFileInfo(filePath).absolutePath(); } -void ProjectManager::setRecentlyUsedImportFilter1D(const QString& filter) +void Dirs::setRecentlyUsedImportFilter1D(const QString& filter) { m_import_filter1D = filter; } -void ProjectManager::setRecentlyUsedImportFilter2D(const QString& filter) +void Dirs::setRecentlyUsedImportFilter2D(const QString& filter) { m_import_filter2D = filter; } - -bool ProjectManager::isAutosaveEnabled() const -{ - return static_cast<bool>(m_autosave); -} - -AutosaveController* ProjectManager::autosaveController() const -{ - return m_autosave.get(); -} - -void ProjectManager::setAutosaveEnabled(bool value) -{ - if (value) - m_autosave = std::make_unique<AutosaveController>(); - else - m_autosave.reset(); - - QSettings settings; - settings.setValue(S_PROJECTMANAGER + "/" + S_AUTOSAVE, value); -} - -//! Clears list of recent projects. - -void ProjectManager::clearRecentProjects() -{ - m_recent_projects.clear(); - emit recentListModified(); -} - -//! Processes new project request (close old project, rise dialog for project name, create project). - -ProjectDocument* ProjectManager::newProject() -{ - if (!closeCurrentProject()) - return nullptr; - createNewProject(); - emit documentOpenedOrClosed(true); - return gDoc.get(); -} - -//! Processes close current project request. Call save/discard/cancel dialog, if necessary. -//! Returns false if saving was canceled. - -bool ProjectManager::closeCurrentProject() -{ - if (!gDoc) - return true; - - if (gDoc->isModified()) { - QMessageBox msgBox; - msgBox.setText("The project has been modified."); - msgBox.setInformativeText("Do you want to save your changes?"); - msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); - msgBox.setDefaultButton(QMessageBox::Save); - - switch (msgBox.exec()) { - case QMessageBox::Save: - if (!saveProject()) - return false; - break; - case QMessageBox::Discard: - break; - case QMessageBox::Cancel: - return false; - default: - break; - } - } - - deleteCurrentProject(); - emit documentOpenedOrClosed(false); - return true; -} - -//! Processes save project request. - -bool ProjectManager::saveProject(QString projectPullPath) -{ - if (projectPullPath.isEmpty()) { - if (gDoc->hasValidNameAndPath()) - projectPullPath = gDoc->projectFullPath(); - else - projectPullPath = acquireProjectPullPath(); - } - - if (projectPullPath.isEmpty()) - return false; - - gDoc->setProjectName(GUI::Util::Project::projectName(projectPullPath)); - gDoc->setProjectDir(GUI::Util::Project::projectDir(projectPullPath)); - - try { - gDoc->saveProjectFileWithData(projectPullPath); - } catch (const std::exception& ex) { - QString message = QString("Failed to save project under '%1'. \n\n").arg(projectPullPath); - message.append("Exception was thrown.\n\n"); - message.append(ex.what()); - - QMessageBox::warning(GUI::Global::mainWindow, "Error while saving project", message); - return false; - } - addToRecentProjects(); - return true; -} - -//! Processes 'save project as' request. - -bool ProjectManager::saveProjectAs() -{ - QString projectFileName = acquireProjectPullPath(); - - if (projectFileName.isEmpty()) - return false; - - return saveProject(projectFileName); -} - -//! Opens existing project. If fname is empty, will popup file selection dialog. - -void ProjectManager::openProject(QString projectPullPath) -{ - if (!closeCurrentProject()) - return; - - if (projectPullPath.isEmpty()) { - const QString ext = QString(GUI::Util::Project::projectFileExtension); - projectPullPath = QFileDialog::getOpenFileName( - GUI::Global::mainWindow, "Open project file", workingDirectory(), - "BornAgain project Files (*" + ext + ")", nullptr, - appSettings->useNativeFileDialog() ? QFileDialog::Options() - : QFileDialog::DontUseNativeDialog); - if (projectPullPath.isEmpty()) - return; - } - - createNewProject(); - MessageService messageService; - const auto readResult = loadProject(projectPullPath, messageService); - - if (readResult == ProjectDocument::ReadResult::ok) - addToRecentProjects(); - else if (readResult == ProjectDocument::ReadResult::error) { - riseProjectLoadFailedDialog(messageService); - deleteCurrentProject(); - } else if (readResult == ProjectDocument::ReadResult::warning) { - riseProjectLoadProblemDialog(messageService); - addToRecentProjects(); - } - if (gDoc) - emit documentOpenedOrClosed(true); -} - -//! Calls dialog window to define project path and name. - -void ProjectManager::createNewProject() -{ - if (gDoc) - throw std::runtime_error("ProjectManager::createNewProject -> Project already exists"); - - gDoc.reset(new ProjectDocument); - - if (m_autosave) - m_autosave->setDocument(gDoc.get()); - - gDoc->setProjectName("Untitled"); - - connect(gDoc.get(), &ProjectDocument::modifiedStateChanged, this, - &ProjectManager::documentModified); -} - -void ProjectManager::deleteCurrentProject() -{ - emit aboutToCloseDocument(); - if (m_autosave) - m_autosave->removeAutosaveDir(); - gDoc.release(); -} - -//! Load project data from file name. If autosave info exists, opens dialog for project restore. - -ProjectDocument::ReadResult ProjectManager::loadProject(const QString& fullPathAndName, - MessageService& messageService) -{ - auto readResult = ProjectDocument::ReadResult::ok; - - const bool useAutosave = GUI::Util::Project::hasAutosavedData(fullPathAndName); - const QString autosaveFullPath = GUI::Util::Project::autosaveFullPath(fullPathAndName); - if (qApp) - QApplication::setOverrideCursor(Qt::WaitCursor); - if (useAutosave && restoreProjectDialog(fullPathAndName, autosaveFullPath)) { - readResult = gDoc->loadProjectFileWithData(autosaveFullPath, messageService); - gDoc->setProjectFullPath(fullPathAndName); - // restored project should be marked by '*' - gDoc->setModified(); - } else { - readResult = gDoc->loadProjectFileWithData(fullPathAndName, messageService); - } - if (qApp) - QApplication::restoreOverrideCursor(); - return readResult; -} - -//! Returns project file name from dialog. Returns empty string if dialog was canceled. - -QString ProjectManager::acquireProjectPullPath() -{ - NewProjectDialog dialog(GUI::Global::mainWindow, workingDirectory(), untitledProjectName()); - - if (dialog.exec() != QDialog::Accepted) - return ""; - - m_working_directory = dialog.getWorkingDirectory(); - - return dialog.getProjectFileName(); -} - -//! Add name of the current project to the name of recent projects - -void ProjectManager::addToRecentProjects() -{ - QString fname = gDoc->projectFullPath(); - m_recent_projects.removeAll(fname); - m_recent_projects.prepend(fname); - while (m_recent_projects.size() > GUI::Style::MAX_RECENT_PROJECTS) - m_recent_projects.removeLast(); - - emit recentListModified(); -} - -//! Returns default project path. -//! Will return 'Untitled' if the directory with such name doesn't exist in project -//! path. Otherwise will return Untitled1, Untitled2 etc. - -QString ProjectManager::untitledProjectName() -{ - QString result = "Untitled"; - QDir projectDir = workingDirectory() + "/" + result; - if (projectDir.exists()) { - for (size_t i = 1; i < 99; ++i) { - result = QString("Untitled") + QString::number(i); - projectDir.setPath(workingDirectory() + "/" + result); - if (!projectDir.exists()) - break; - } - } - return result; -} - -void ProjectManager::riseProjectLoadFailedDialog(const MessageService& messageService) -{ - QString message = QString("Failed to load the project '%1' \n\n").arg(gDoc->projectFullPath()); - - for (const auto& details : messageService.errors()) - message.append(details + "\n"); - - QMessageBox::warning(GUI::Global::mainWindow, "Error while opening project file", message); -} - -void ProjectManager::riseProjectLoadProblemDialog(const MessageService& messageService) -{ - auto* problemDialog = new ProjectLoadProblemDialog(messageService.warnings(true)); - problemDialog->show(); - problemDialog->raise(); -} - -//! Rises dialog if the project should be restored from autosave. Returns true, if yes. - -bool ProjectManager::restoreProjectDialog(const QString& projectFileName, - const QString autosaveName) -{ - const QString title("Recover project"); - const QString lmProject = - QFileInfo(projectFileName).lastModified().toString("hh:mm:ss, MMMM d, yyyy"); - const QString lmAutoSave = - QFileInfo(autosaveName).lastModified().toString("hh:mm:ss, MMMM d, yyyy"); - - QString message = QString("Project '%1' contains autosaved data.\n\n" - "Project saved at %2\nAutosave from %3") - .arg(GUI::Util::Project::projectName(projectFileName)) - .arg(lmProject) - .arg(lmAutoSave); - - return GUI::Message::question(GUI::Global::mainWindow, title, message, - "\nDo you want to restore from autosave?\n", - "Yes, please restore.", "No, keep loading original"); -} diff --git a/GUI/Support/Data/Dirs.h b/GUI/Support/Data/Dirs.h index cafc4739299..884f11c2f90 100644 --- a/GUI/Support/Data/Dirs.h +++ b/GUI/Support/Data/Dirs.h @@ -3,7 +3,7 @@ // BornAgain: simulate and fit reflection and scattering // //! @file GUI/Support/Data/Dirs.h -//! @brief Defines class ProjectManager. +//! @brief Defines class Dirs. //! //! @homepage http://www.bornagainproject.org //! @license GNU General Public License v3 or higher (see COPYING) @@ -15,26 +15,19 @@ #ifndef BORNAGAIN_GUI_SUPPORT_DATA_DIRS_H #define BORNAGAIN_GUI_SUPPORT_DATA_DIRS_H -#include "GUI/Model/Project/ProjectDocument.h" - -class AutosaveController; +#include "Wrap/WinDllMacros.h" +#include <QStringList> //! Handles activity related to opening/save projects. -class ProjectManager : public QObject { - Q_OBJECT +class Dirs { public: - ProjectManager(QObject* parent); - ~ProjectManager() override; + Dirs(); + ~Dirs(); - static ProjectManager* instance(); + static Dirs* instance(); - void readSettings(); - void writeSettings(); - QStringList recentProjects(); - QString projectDir() const; - QString userImportDir() const; QString recentlyUsedImportFilter1D() const { return m_import_filter1D; } QString recentlyUsedImportFilter2D() const { return m_import_filter2D; } void setImportDir(const QString& dirname); @@ -42,38 +35,11 @@ public: void setRecentlyUsedImportFilter1D(const QString& filter); void setRecentlyUsedImportFilter2D(const QString& filter); - bool isAutosaveEnabled() const; - AutosaveController* autosaveController() const; - -signals: - void aboutToCloseDocument(); - void documentOpenedOrClosed(bool opened); - void documentModified(); - void recentListModified(); - -public slots: - void setAutosaveEnabled(bool value); - void clearRecentProjects(); - ProjectDocument* newProject(); - bool closeCurrentProject(); - bool saveProject(QString projectPullPath = ""); - bool saveProjectAs(); - void openProject(QString projectPullPath = ""); - private: - void createNewProject(); - void deleteCurrentProject(); - ProjectDocument::ReadResult loadProject(const QString& fullPathAndName, - MessageService& messageService); - QString acquireProjectPullPath(); - void addToRecentProjects(); + void readSettings(); + void writeSettings(); QString workingDirectory() { return m_working_directory; } - QString untitledProjectName(); - - void riseProjectLoadFailedDialog(const MessageService& messageService); - void riseProjectLoadProblemDialog(const MessageService& messageService); - bool restoreProjectDialog(const QString& projectFileName, QString autosaveName); //!< Name of directory where project directory was created. QString m_working_directory; @@ -88,9 +54,10 @@ private: QString m_import_filter2D; QStringList m_recent_projects; - std::unique_ptr<AutosaveController> m_autosave; - static ProjectManager* s_instance; + static Dirs* s_instance; }; +BA_GUI_API_ extern std::unique_ptr<Dirs> gDirs; + #endif // BORNAGAIN_GUI_SUPPORT_DATA_DIRS_H -- GitLab