LCOV - code coverage report
Current view: top level - src/project/internal - closeprojectscenario.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 95.8 % 71 68
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2026 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #include "closeprojectscenario.h"
      24              : 
      25              : #include "translation.h"
      26              : #include "types/projecturis.h"
      27              : 
      28              : #include "playback/playbackcommands.h"
      29              : 
      30              : #include "log.h"
      31              : 
      32              : using namespace muse;
      33              : using namespace mu::project;
      34              : using namespace mu::playback;
      35              : using muse::async::Promise;
      36              : 
      37            2 : Promise<Ret> CloseProjectScenario::resolvedPromise(const Ret& ret)
      38              : {
      39            4 :     return async::make_promise<Ret>([ret](auto resolve) {
      40            2 :         return resolve(ret);
      41            4 :     });
      42              : }
      43              : 
      44           14 : Promise<Ret> CloseProjectScenario::closeOpenedProject(bool goToHome)
      45              : {
      46           28 :     return runIfNotBusy(BusyStatus::Closing, [this, goToHome]() -> Promise<Ret> {
      47           13 :         INotationProjectPtr project = currentNotationProject();
      48           13 :         if (!project) {
      49            1 :             return resolvedPromise(make_ok());
      50              :         }
      51              : 
      52           12 :         if (playbackState()->isPlaying()) {
      53            1 :             commandDispatcher()->dispatch(STOP_COMMAND);
      54              :         }
      55              : 
      56           12 :         if (!project->isNeedSave()) {
      57            6 :             return doCloseProject(goToHome);
      58              :         }
      59              : 
      60           12 :         return askAboutSavingScore(project)
      61            6 :                .then<Ret>(this, [this, goToHome](const IInteractive::Result& res, auto resolve) {
      62            6 :             IInteractive::Button btn = res.standardButton();
      63              : 
      64            6 :             if (btn == IInteractive::Button::Cancel) {
      65            1 :                 return resolve(make_ret(Ret::Code::Cancel));
      66              :             }
      67              : 
      68            5 :             if (btn != IInteractive::Button::Save) {
      69            3 :                 doCloseProject(goToHome).onResolve(this, [resolve](const Ret& ret) {
      70            3 :                     (void)resolve(ret);
      71              :                 });
      72              : 
      73            3 :                 return Promise<Ret>::dummy_result();
      74              :             }
      75              : 
      76              :             //! NOTE The score is only let go of once its changes are safely written
      77            2 :             saveProjectScenario()->saveProject().onResolve(this, [this, goToHome, resolve](const Ret& ret) {
      78            2 :                 if (!ret) {
      79            1 :                     (void)resolve(ret);
      80            1 :                     return;
      81              :                 }
      82              : 
      83            1 :                 doCloseProject(goToHome).onResolve(this, [resolve](const Ret& closeRet) {
      84            1 :                     (void)resolve(closeRet);
      85              :                 });
      86              :             });
      87              : 
      88            2 :             return Promise<Ret>::dummy_result();
      89            6 :         });
      90           41 :     });
      91              : }
      92              : 
      93           16 : bool CloseProjectScenario::isBusy(BusyStatus status) const
      94              : {
      95           16 :     return m_busyStatuses.contains(status);
      96              : }
      97              : 
      98            0 : muse::async::Notification CloseProjectScenario::busyChanged() const
      99              : {
     100            0 :     return m_busyChanged;
     101              : }
     102              : 
     103           13 : INotationProjectPtr CloseProjectScenario::currentNotationProject() const
     104              : {
     105           13 :     return projectSession()->project();
     106              : }
     107              : 
     108           25 : void CloseProjectScenario::setBusy(BusyStatus status, bool isBusy)
     109              : {
     110           25 :     bool wasBusy = m_busyStatuses.contains(status);
     111           25 :     if (wasBusy == isBusy) {
     112            0 :         return;
     113              :     }
     114              : 
     115           25 :     if (isBusy) {
     116           13 :         m_busyStatuses.insert(status);
     117              :     } else {
     118           12 :         m_busyStatuses.erase(status);
     119              :     }
     120              : 
     121           25 :     m_busyChanged.notify();
     122              : }
     123              : 
     124           14 : Promise<Ret> CloseProjectScenario::runIfNotBusy(BusyStatus status, const std::function<Promise<Ret>()>& flow)
     125              : {
     126           14 :     if (isBusy(status)) {
     127            1 :         return resolvedPromise(make_ret(Ret::Code::Busy));
     128              :     }
     129              : 
     130           13 :     setBusy(status, true);
     131              : 
     132           26 :     return flow().then<Ret>(this, [this, status](const Ret& ret, auto resolve) {
     133           12 :         setBusy(status, false);
     134           12 :         return resolve(ret);
     135           13 :     });
     136              : }
     137              : 
     138            6 : Promise<IInteractive::Result> CloseProjectScenario::askAboutSavingScore(const INotationProjectPtr& project)
     139              : {
     140            6 :     std::string title = muse::qtrc("project", "Do you want to save changes to the score “%1” before closing?")
     141            6 :                         .arg(project->displayName()).toStdString();
     142              : 
     143            6 :     std::string body = muse::trc("project", "Your changes will be lost if you don’t save them.");
     144              : 
     145           30 :     return interactive()->warning(title, body, {
     146              :         IInteractive::Button::DontSave,
     147              :         IInteractive::Button::Cancel,
     148              :         IInteractive::Button::Save
     149           18 :     }, IInteractive::Button::Save);
     150            6 : }
     151              : 
     152           10 : Promise<Ret> CloseProjectScenario::doCloseProject(bool goToHome)
     153              : {
     154           20 :     return interactive()->closeAllDialogs().then<Ret>(this, [this, goToHome](const Ret&, auto resolve) {
     155            9 :         projectSession()->setProject(nullptr);
     156              : 
     157            9 :         if (goToHome) {
     158            8 :             openHomePageIfNeed();
     159              :         }
     160              : 
     161            9 :         return resolve(make_ok());
     162           20 :     });
     163              : }
     164              : 
     165            8 : void CloseProjectScenario::openHomePageIfNeed()
     166              : {
     167            8 :     if (interactive()->isOpened(HOME_PAGE_URI).val) {
     168            1 :         return;
     169              :     }
     170              : 
     171            7 :     interactive()->open(HOME_PAGE_URI);
     172              : }
        

Generated by: LCOV version 2.0-1