LCOV - code coverage report
Current view: top level - src/project/internal - projectautosaver.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 95 0
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) 2021 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 "projectautosaver.h"
      24              : 
      25              : #include "engraving/infrastructure/mscio.h"
      26              : 
      27              : #include "project/inotationproject.h"
      28              : 
      29              : #include "defer.h"
      30              : #include "log.h"
      31              : 
      32              : using namespace muse;
      33              : using namespace mu::project;
      34              : 
      35            0 : void ProjectAutoSaver::init()
      36              : {
      37            0 :     m_timer.setSingleShot(true);
      38            0 :     m_timer.setTimerType(Qt::VeryCoarseTimer);
      39            0 :     m_timer.setInterval(configuration()->autoSaveIntervalMinutes() * 60000);
      40              : 
      41            0 :     QObject::connect(&m_timer, &QTimer::timeout, [this]() { onTrySave(); });
      42              : 
      43            0 :     if (configuration()->isAutoSaveEnabled()) {
      44            0 :         m_timer.start();
      45              :     }
      46              : 
      47            0 :     configuration()->autoSaveEnabledChanged().onReceive(this, [this](bool enabled) {
      48            0 :         if (enabled != m_timer.isActive()) {
      49            0 :             if (enabled) {
      50            0 :                 m_timer.start();
      51              :             } else {
      52            0 :                 m_timer.stop();
      53              :             }
      54              :         }
      55            0 :     });
      56              : 
      57            0 :     configuration()->autoSaveIntervalChanged().onReceive(this, [this](int minutes) {
      58            0 :         m_timer.setInterval(minutes * 60000);
      59            0 :     });
      60              : 
      61            0 :     update();
      62              : 
      63            0 :     projectSession()->projectChanged().onNotify(this, [this]() {
      64            0 :         if (INotationProjectPtr project = currentProject()) {
      65            0 :             if (project->isNewlyCreated() && !project->isImported()) {
      66            0 :                 Ret ret = project->save(configuration()->newProjectTemporaryPath(), SaveMode::AutoSave);
      67            0 :                 if (!ret) {
      68            0 :                     LOGE() << "[autosave] failed to save project, err: " << ret.toString();
      69            0 :                     return;
      70              :                 }
      71            0 :             }
      72              : 
      73            0 :             project->pathChanged().onNotify(this, [this]() {
      74            0 :                 update();
      75            0 :             });
      76              : 
      77            0 :             project->needSaveChanged().onNotify(this, [this]() {
      78            0 :                 update();
      79            0 :             });
      80            0 :         }
      81              : 
      82            0 :         update();
      83              :     });
      84            0 : }
      85              : 
      86            0 : bool ProjectAutoSaver::projectHasUnsavedChanges(const muse::io::path_t& projectPath) const
      87              : {
      88            0 :     muse::io::path_t autoSavePath = projectAutoSavePath(projectPath);
      89            0 :     return fileSystem()->exists(autoSavePath);
      90            0 : }
      91              : 
      92            0 : void ProjectAutoSaver::removeProjectUnsavedChanges(const muse::io::path_t& projectPath)
      93              : {
      94            0 :     muse::io::path_t path = projectPath;
      95            0 :     if (!isAutosaveOfNewlyCreatedProject(projectPath)) {
      96            0 :         path = projectAutoSavePath(projectPath);
      97              :     }
      98              : 
      99            0 :     fileSystem()->remove(path);
     100            0 : }
     101              : 
     102            0 : bool ProjectAutoSaver::isAutosaveOfNewlyCreatedProject(const muse::io::path_t& projectPath) const
     103              : {
     104            0 :     return projectPath == configuration()->newProjectTemporaryPath();
     105              : }
     106              : 
     107            0 : muse::io::path_t ProjectAutoSaver::projectOriginalPath(const muse::io::path_t& projectAutoSavePath) const
     108              : {
     109            0 :     IF_ASSERT_FAILED(io::suffix(projectAutoSavePath) == AUTOSAVE_SUFFIX) {
     110            0 :         return engraving::mainFilePath(projectAutoSavePath);
     111              :     }
     112              : 
     113            0 :     muse::io::path_t withoutAutosaveSuffix = io::filename(projectAutoSavePath, false);
     114              : 
     115            0 :     return engraving::mainFilePath(io::absoluteDirpath(projectAutoSavePath).appendingComponent(withoutAutosaveSuffix));
     116            0 : }
     117              : 
     118            0 : muse::io::path_t ProjectAutoSaver::projectAutoSavePath(const muse::io::path_t& projectPath) const
     119              : {
     120            0 :     return engraving::containerPath(projectPath).appendingSuffix(AUTOSAVE_SUFFIX);
     121              : }
     122              : 
     123            0 : INotationProjectPtr ProjectAutoSaver::currentProject() const
     124              : {
     125            0 :     return projectSession()->project();
     126              : }
     127              : 
     128            0 : void ProjectAutoSaver::update()
     129              : {
     130            0 :     TRACEFUNC;
     131              : 
     132            0 :     muse::io::path_t newProjectPath;
     133              : 
     134            0 :     auto project = currentProject();
     135            0 :     if (project && project->needAutoSave()) {
     136            0 :         newProjectPath = projectPath(project);
     137              :     }
     138              : 
     139            0 :     if (!m_lastProjectPathNeedingAutosave.empty()
     140            0 :         && m_lastProjectPathNeedingAutosave != newProjectPath) {
     141            0 :         removeProjectUnsavedChanges(m_lastProjectPathNeedingAutosave);
     142              :     }
     143              : 
     144            0 :     m_lastProjectPathNeedingAutosave = newProjectPath;
     145            0 : }
     146              : 
     147            0 : void ProjectAutoSaver::onTrySave()
     148              : {
     149            0 :     TRACEFUNC;
     150              : 
     151            0 :     DEFER {
     152            0 :         if (configuration()->isAutoSaveEnabled()) {
     153            0 :             m_timer.start();
     154              :         }
     155            0 :     };
     156              : 
     157            0 :     LOGI() << "[autosave] trying to auto save";
     158              : 
     159            0 :     INotationProjectPtr project = projectSession()->project();
     160            0 :     if (!project) {
     161            0 :         LOGI() << "[autosave] no project";
     162            0 :         return;
     163              :     }
     164              : 
     165            0 :     if (!project->needAutoSave()) {
     166            0 :         LOGI() << "[autosave] project does not need save";
     167            0 :         return;
     168              :     }
     169              : 
     170            0 :     if (!project->canSave()) {
     171            0 :         LOGI() << "[autosave] project could not be saved";
     172            0 :         return;
     173              :     }
     174              : 
     175            0 :     muse::io::path_t projectPath = this->projectPath(project);
     176            0 :     muse::io::path_t savePath = project->isNewlyCreated() ? projectPath : projectAutoSavePath(projectPath);
     177              : 
     178            0 :     Ret ret = project->save(savePath, SaveMode::AutoSave);
     179            0 :     if (!ret) {
     180            0 :         LOGE() << "[autosave] failed to save project, err: " << ret.toString();
     181            0 :         return;
     182              :     }
     183              : 
     184            0 :     project->setNeedAutoSave(false);
     185              : 
     186            0 :     LOGI() << "[autosave] successfully saved project";
     187            0 : }
     188              : 
     189            0 : muse::io::path_t ProjectAutoSaver::projectPath(INotationProjectPtr project) const
     190              : {
     191            0 :     return project->isNewlyCreated() ? configuration()->newProjectTemporaryPath() : project->path();
     192              : }
        

Generated by: LCOV version 2.0-1