From 63c194896da0a2208f75800558b19a25dcec4088 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 19 Apr 2020 03:29:39 +1000 Subject: [PATCH] core: implemented yes/no to all when closing multiple modified projects --- app/core.cpp | 161 ++++++++++++++++------ app/core.h | 25 +++- app/panel/project/project.cpp | 6 +- app/panel/project/project.h | 2 + app/project/projectsavemanager.cpp | 2 +- app/project/projectsavemanager.h | 6 +- app/widget/timelinewidget/tool/import.cpp | 4 +- app/window/mainwindow/mainmenu.cpp | 2 +- app/window/mainwindow/mainwindow.cpp | 6 +- 9 files changed, 157 insertions(+), 57 deletions(-) diff --git a/app/core.cpp b/app/core.cpp index 12ccdee90..3cc7ede0b 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -274,6 +274,17 @@ const QStringList &Core::GetRecentProjects() const return recent_projects_; } +ProjectPtr Core::GetSharedPtrFromProject(Project *project) const +{ + foreach (ProjectPtr p, open_projects_) { + if (p.get() == project) { + return p; + } + } + + return nullptr; +} + void Core::SetTool(const Tool::Item &tool) { tool_ = tool; @@ -328,7 +339,7 @@ void Core::DialogPreferencesShow() void Core::DialogProjectPropertiesShow() { - ProjectPropertiesDialog ppd(GetActiveProject(), main_window_); + ProjectPropertiesDialog ppd(GetActiveProject().get(), main_window_); ppd.exec(); } @@ -357,7 +368,7 @@ void Core::CreateNewFolder() if (active_project_panel == nullptr // Check that we found a Project panel || (active_project = active_project_panel->project()) == nullptr) { // and that we could find an active Project - QMessageBox::critical(main_window_, tr("Failed to create new folder"), tr("Failed to find active Project panel")); + QMessageBox::critical(main_window_, tr("Failed to create new folder"), tr("Failed to find active project")); return; } @@ -383,15 +394,15 @@ void Core::CreateNewFolder() void Core::CreateNewSequence() { - Project* active_project = GetActiveProject(); + ProjectPtr active_project = GetActiveProject(); if (!active_project) { - QMessageBox::critical(main_window_, tr("Failed to create new sequence"), tr("Failed to find active Project panel")); + QMessageBox::critical(main_window_, tr("Failed to create new sequence"), tr("Failed to find active project")); return; } // Create new sequence - SequencePtr new_sequence = CreateNewSequenceForProject(active_project); + SequencePtr new_sequence = CreateNewSequenceForProject(active_project.get()); // Set all defaults for the sequence new_sequence->set_default_parameters(); @@ -429,7 +440,7 @@ void Core::AddOpenProject(ProjectPtr p) // If we currently have an empty project, close it first if (!open_projects_.isEmpty() && open_projects_.last()->is_new()) { - CloseProject(open_projects_.last().get(), false); + CloseProject(open_projects_.last(), false); } connect(p.get(), &Project::ModifiedChanged, this, &Core::ProjectWasModified); @@ -496,6 +507,7 @@ void Core::DeclareTypesForQt() qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); + qRegisterMetaType(); } void Core::StartGUI(bool full_screen) @@ -545,7 +557,7 @@ void Core::StartGUI(bool full_screen) autorecovery_timer_.start(); } -void Core::SaveProjectInternal(Project *project) +void Core::SaveProjectInternal(ProjectPtr project) { // Create save manager ProjectSaveManager* psm = new ProjectSaveManager(project); @@ -566,22 +578,22 @@ void Core::SaveAutorecovery() } } -void Core::ProjectSaveSucceeded(Project* p) +void Core::ProjectSaveSucceeded(ProjectPtr p) { PushRecentlyOpenedProject(p->filename()); p->set_modified(false); } -Project *Core::GetActiveProject() const +ProjectPtr Core::GetActiveProject() const { ProjectPanel* active_project_panel = PanelManager::instance()->MostRecentlyFocused(); - if (active_project_panel) { - return active_project_panel->project(); - } else { - return nullptr; + if (active_project_panel && active_project_panel->project()) { + return GetSharedPtrFromProject(active_project_panel->project()); } + + return nullptr; } ProjectViewModel *Core::GetActiveProjectModel() const @@ -636,7 +648,7 @@ QString Core::PasteStringFromClipboard() bool Core::SaveActiveProject() { - Project* active_project = GetActiveProject(); + ProjectPtr active_project = GetActiveProject(); if (active_project) { return SaveProject(active_project); @@ -647,7 +659,7 @@ bool Core::SaveActiveProject() bool Core::SaveActiveProjectAs() { - Project* active_project = GetActiveProject(); + ProjectPtr active_project = GetActiveProject(); if (active_project) { return SaveProjectAs(active_project); @@ -659,7 +671,7 @@ bool Core::SaveActiveProjectAs() bool Core::SaveAllProjects() { foreach (ProjectPtr p, open_projects_) { - if (!SaveProject(p.get())) { + if (!SaveProject(p)) { return false; } } @@ -674,12 +686,12 @@ bool Core::CloseActiveProject() bool Core::CloseAllExceptActiveProject() { - Project* active_proj = GetActiveProject(); + ProjectPtr active_proj = GetActiveProject(); QList copy = open_projects_; foreach (ProjectPtr p, copy) { - if (p.get() != active_proj) { - if (!CloseProject(p.get(), true)) { + if (p != active_proj) { + if (!CloseProject(p, true)) { return false; } } @@ -774,7 +786,7 @@ QString Core::GetRecentProjectsFilePath() return QDir(GetConfigurationLocation()).filePath(QStringLiteral("recent")); } -bool Core::SaveProject(Project *p) +bool Core::SaveProject(ProjectPtr p) { if (p->filename().isEmpty()) { return SaveProjectAs(p); @@ -785,7 +797,7 @@ bool Core::SaveProject(Project *p) } } -bool Core::SaveProjectAs(Project *p) +bool Core::SaveProjectAs(ProjectPtr p) { QString fn = QFileDialog::getSaveFileName(main_window_, tr("Save Project As"), @@ -908,34 +920,81 @@ void Core::OpenProjectFromRecentList(int index) } } -bool Core::CloseProject(Project *p, bool auto_open_new) +bool Core::CloseProject(ProjectPtr p, bool auto_open_new) +{ + CloseProjectBehavior b = kCloseProjectOnlyOne; + return CloseProject(p, auto_open_new, b); +} + +bool Core::CloseProject(ProjectPtr p, bool auto_open_new, CloseProjectBehavior &confirm_behavior) { for (int i=0;iis_modified()) { - QMessageBox mb(main_window_); + if (p->is_modified() && confirm_behavior != kCloseProjectDontSave) { - mb.setIcon(QMessageBox::Question); - mb.setWindowTitle(tr("Unsaved Changes")); - mb.setText(tr("The project '%1' has unsaved changes. Would you like to save them?") - .arg(p->name())); + bool save_this_project; - QPushButton* yes_btn = mb.addButton(tr("Save"), QMessageBox::YesRole); - mb.addButton(tr("Don't Save"), QMessageBox::NoRole); - QPushButton* cancel_btn = mb.addButton(QMessageBox::Cancel); + if (confirm_behavior == kCloseProjectAsk || confirm_behavior == kCloseProjectOnlyOne) { + QMessageBox mb(main_window_); - mb.exec(); + mb.setIcon(QMessageBox::Question); + mb.setWindowTitle(tr("Unsaved Changes")); + mb.setText(tr("The project '%1' has unsaved changes. Would you like to save them?") + .arg(p->name())); - if (mb.clickedButton() == cancel_btn - || (mb.clickedButton() == yes_btn && !SaveActiveProject())) { - // Don't close if the user clicked cancel on this messagebox OR they cancelled a save as operation + QPushButton* yes_btn = mb.addButton(tr("Save"), QMessageBox::YesRole); + + QPushButton* yes_to_all_btn; + if (confirm_behavior == kCloseProjectOnlyOne) { + yes_to_all_btn = nullptr; + } else { + yes_to_all_btn = mb.addButton(tr("Save All"), QMessageBox::YesRole); + } + + mb.addButton(tr("Don't Save"), QMessageBox::NoRole); + + QPushButton* no_to_all_btn; + if (confirm_behavior == kCloseProjectOnlyOne) { + no_to_all_btn = nullptr; + } else { + no_to_all_btn = mb.addButton(tr("Don't Save All"), QMessageBox::NoRole); + } + + QPushButton* cancel_btn = mb.addButton(QMessageBox::Cancel); + + mb.exec(); + + if (mb.clickedButton() == cancel_btn) { + // Stop closing projects if the user clicked cancel + return false; + } else if (mb.clickedButton() == yes_to_all_btn) { + // Set flag that other CloseProject commands are going to use + confirm_behavior = kCloseProjectSave; + } else if (mb.clickedButton() == no_to_all_btn) { + // Set flag that other CloseProject commands are going to use + confirm_behavior = kCloseProjectDontSave; + } + + save_this_project = (mb.clickedButton() == yes_btn || mb.clickedButton() == yes_to_all_btn); + + } else { + // We must be saving this project + save_this_project = true; + } + + if (save_this_project && !SaveProject(p)) { + // The save failed, stop closing projects return false; } + } - disconnect(p, &Project::ModifiedChanged, this, &Core::ProjectWasModified); - emit ProjectClosed(p); + // For safety, the undo stack is cleared so no commands try to affect a freed project + undo_stack_.clear(); + + disconnect(p.get(), &Project::ModifiedChanged, this, &Core::ProjectWasModified); + emit ProjectClosed(p.get()); open_projects_.removeAt(i); break; } @@ -953,10 +1012,34 @@ bool Core::CloseAllProjects(bool auto_open_new) { QList copy = open_projects_; + // See how many projects are modified so we can set "behavior" correctly + // (i.e. whether to show "Yes/No To All" buttons or not) + int modified_count = 0; foreach (ProjectPtr p, copy) { - if (!CloseProject(p.get(), auto_open_new)) { + if (p->is_modified()) { + modified_count++; + } + } + + CloseProjectBehavior behavior; + + if (modified_count > 1) { + behavior = kCloseProjectAsk; + } else { + behavior = kCloseProjectOnlyOne; + } + + foreach (ProjectPtr p, copy) { + // If this is the only remaining project and the user hasn't chose "yes/no to all", hide those buttons + if (modified_count == 1 && behavior == kCloseProjectAsk) { + behavior = kCloseProjectOnlyOne; + } + + if (!CloseProject(p, auto_open_new, behavior)) { return false; } + + modified_count--; } return true; diff --git a/app/core.h b/app/core.h index 808bcac00..8c5f8985d 100644 --- a/app/core.h +++ b/app/core.h @@ -124,6 +124,11 @@ public: */ const QStringList& GetRecentProjects() const; + /** + * @brief Convenience function to retrieve a Project's shared pointer + */ + ProjectPtr GetSharedPtrFromProject(Project* project) const; + /** * @brief Get the currently active project * @@ -134,7 +139,7 @@ public: * * The active Project file, or nullptr if the heuristic couldn't find one. */ - Project* GetActiveProject() const; + ProjectPtr GetActiveProject() const; ProjectViewModel* GetActiveProjectModel() const; Folder* GetSelectedFolderInActiveProject() const; @@ -206,10 +211,18 @@ public: */ void OpenProjectFromRecentList(int index); + enum CloseProjectBehavior { + kCloseProjectOnlyOne, + kCloseProjectAsk, + kCloseProjectSave, + kCloseProjectDontSave + }; + /** * @brief Closes a project */ - bool CloseProject(Project* p, bool auto_open_new); + bool CloseProject(ProjectPtr p, bool auto_open_new, CloseProjectBehavior& confirm_behavior); + bool CloseProject(ProjectPtr p, bool auto_open_new); /** * @brief Closes all open projects @@ -365,12 +378,12 @@ private: /** * @brief Saves a specific project */ - bool SaveProject(Project* p); + bool SaveProject(ProjectPtr p); /** * @brief Performs a "save as" on a specific project */ - bool SaveProjectAs(Project* p); + bool SaveProjectAs(ProjectPtr p); /** * @brief Adds a filename to the top of the recently opened projects list (or moves it if it already exists) @@ -401,7 +414,7 @@ private: /** * @brief Internal function for saving a project to a file */ - void SaveProjectInternal(Project* project); + void SaveProjectInternal(ProjectPtr project); /** * @brief Internal main window object @@ -459,7 +472,7 @@ private: private slots: void SaveAutorecovery(); - void ProjectSaveSucceeded(Project *p); + void ProjectSaveSucceeded(ProjectPtr p); /** * @brief Adds a project to the "open projects" list diff --git a/app/panel/project/project.cpp b/app/panel/project/project.cpp index 079ba6d0c..a0e540af8 100644 --- a/app/panel/project/project.cpp +++ b/app/panel/project/project.cpp @@ -69,12 +69,12 @@ ProjectPanel::ProjectPanel(QWidget *parent) : Retranslate(); } -Project *ProjectPanel::project() const +Project* ProjectPanel::project() const { return explorer_->project(); } -void ProjectPanel::set_project(Project *p) +void ProjectPanel::set_project(Project* p) { if (project()) { disconnect(project(), &Project::NameChanged, this, &ProjectPanel::UpdateSubtitle); @@ -92,6 +92,8 @@ void ProjectPanel::set_project(Project *p) UpdateSubtitle(); + emit ProjectNameChanged(); + if (p) { setWindowModified(p->is_modified()); } else { diff --git a/app/panel/project/project.h b/app/panel/project/project.h index 4e7ce3e7a..e77511b2d 100644 --- a/app/panel/project/project.h +++ b/app/panel/project/project.h @@ -82,4 +82,6 @@ private slots: OLIVE_NAMESPACE_EXIT +Q_DECLARE_METATYPE(OLIVE_NAMESPACE::ProjectPtr) + #endif // PROJECT_PANEL_H diff --git a/app/project/projectsavemanager.cpp b/app/project/projectsavemanager.cpp index b6ebb12d8..f04055649 100644 --- a/app/project/projectsavemanager.cpp +++ b/app/project/projectsavemanager.cpp @@ -25,7 +25,7 @@ OLIVE_NAMESPACE_ENTER -ProjectSaveManager::ProjectSaveManager(Project *project) : +ProjectSaveManager::ProjectSaveManager(ProjectPtr project) : project_(project) { SetTitle(tr("Saving '%1'").arg(project->filename())); diff --git a/app/project/projectsavemanager.h b/app/project/projectsavemanager.h index 4e0560ee2..88be5f840 100644 --- a/app/project/projectsavemanager.h +++ b/app/project/projectsavemanager.h @@ -30,16 +30,16 @@ class ProjectSaveManager : public Task { Q_OBJECT public: - ProjectSaveManager(Project* project); + ProjectSaveManager(ProjectPtr project); signals: - void ProjectSaveSucceeded(Project* p); + void ProjectSaveSucceeded(ProjectPtr p); protected: virtual void Action() override; private: - Project* project_; + ProjectPtr project_; }; diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index 213500ef6..73264b243 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -321,10 +321,10 @@ void TimelineWidget::ImportTool::DropGhosts(bool insert) } if (behavior != kDWSDisable) { - Project* active_project = Core::instance()->GetActiveProject(); + ProjectPtr active_project = Core::instance()->GetActiveProject(); if (active_project) { - SequencePtr new_sequence = Core::instance()->CreateNewSequenceForProject(active_project); + SequencePtr new_sequence = Core::instance()->CreateNewSequenceForProject(active_project.get()); new_sequence->set_default_parameters(); diff --git a/app/window/mainwindow/mainmenu.cpp b/app/window/mainwindow/mainmenu.cpp index 8d67bd9b7..133c8ea09 100644 --- a/app/window/mainwindow/mainmenu.cpp +++ b/app/window/mainwindow/mainmenu.cpp @@ -307,7 +307,7 @@ void MainMenu::TimecodeDisplayTriggered() void MainMenu::FileMenuAboutToShow() { - Project* active_project = Core::instance()->GetActiveProject(); + Project* active_project = Core::instance()->GetActiveProject().get(); file_project_properties_item_->setEnabled(active_project); file_save_item_->setEnabled(active_project); diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index 03f0a59d9..e8f628cb6 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -212,7 +212,7 @@ void MainWindow::SetTaskbarButtonProgress(int value, int max) } #endif -void MainWindow::FolderOpen(Project *p, Item *i, bool floating) +void MainWindow::FolderOpen(Project* p, Item *i, bool floating) { ProjectPanel* panel = PanelManager::instance()->CreatePanel(this); @@ -287,7 +287,7 @@ void MainWindow::ToggleMaximizedPanel() } } -void MainWindow::ProjectOpen(Project* p) +void MainWindow::ProjectOpen(Project *p) { // See if this project is already open, and switch to it if so foreach (ProjectPanel* pl, project_panels_) { @@ -397,7 +397,7 @@ void MainWindow::ProjectCloseRequested() ProjectPanel* panel = static_cast(sender()); Project* p = panel->project(); - Core::instance()->CloseProject(p, true); + Core::instance()->CloseProject(Core::instance()->GetSharedPtrFromProject(p), true); } void MainWindow::FolderCloseRequested()