LCOV - code coverage report
Current view: top level - src/project/internal - projectconfiguration.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 387 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              : #include "projectconfiguration.h"
      23              : 
      24              : #include <QJsonDocument>
      25              : #include <QJsonObject>
      26              : #include <QJsonValue>
      27              : #include <QJsonArray>
      28              : 
      29              : #include "settings.h"
      30              : 
      31              : #include "engraving/infrastructure/mscio.h"
      32              : 
      33              : #include "internal/notationproject.h"
      34              : #include "types/projectmeta.h"
      35              : 
      36              : #include "log.h"
      37              : 
      38              : using namespace mu;
      39              : using namespace mu::project;
      40              : using namespace mu::notation;
      41              : using namespace muse;
      42              : 
      43              : static const std::string module_name("project");
      44              : 
      45              : static const Settings::Key COMPAT_RECENT_FILES_DATA(module_name, "project/recentList");
      46              : static const Settings::Key USER_TEMPLATES_PATH(module_name, "application/paths/myTemplates");
      47              : static const Settings::Key LAST_OPENED_PROJECTS_PATH(module_name, "application/paths/lastOpenedProjectsPath");
      48              : static const Settings::Key LAST_SAVED_PROJECTS_PATH(module_name, "application/paths/lastSavedProjectsPath");
      49              : static const Settings::Key USER_PROJECTS_PATH(module_name, "application/paths/myScores");
      50              : static const Settings::Key LAST_OPENED_CONVERT_FILE_PATH(module_name, "application/paths/lastOpenedConvertFilePath");
      51              : static const Settings::Key SHOULD_ASK_SAVE_LOCATION_TYPE(module_name, "project/shouldAskSaveLocationType");
      52              : static const Settings::Key LAST_USED_SAVE_LOCATION_TYPE(module_name, "project/lastUsedSaveLocationType");
      53              : static const Settings::Key SHOULD_WARN_BEFORE_PUBLISH(module_name, "project/shouldWarnBeforePublish");
      54              : static const Settings::Key SHOULD_WARN_BEFORE_SAVING_PUBLICLY_TO_CLOUD(module_name, "project/shouldWarnBeforeSavingPubliclyToCloud");
      55              : static const Settings::Key HOME_SCORES_PAGE_TAB_INDEX(module_name, "project/homeScoresPageTabIndex");
      56              : static const Settings::Key HOME_SCORES_PAGE_VIEW_TYPE(module_name, "project/homeScoresPageViewType");
      57              : static const Settings::Key PREFERRED_SCORE_CREATION_MODE_KEY(module_name, "project/preferredScoreCreationMode");
      58              : static const Settings::Key MIGRATION_OPTIONS(module_name, "project/migration");
      59              : static const Settings::Key AUTOSAVE_ENABLED_KEY(module_name, "project/autoSaveEnabled");
      60              : static const Settings::Key AUTOSAVE_INTERVAL_KEY(module_name, "project/autoSaveInterval");
      61              : static const Settings::Key ALSO_SHARE_AUDIO_COM_AFTER_PUBLISH(module_name, "project/alsoShareAudioCom");
      62              : static const Settings::Key SHOW_ALSO_SHARE_AUDIO_COM_DIALOG(module_name, "project/showAlsoShareAudioComDialog");
      63              : static const Settings::Key HAS_ASKED_ALSO_SHARE_AUDIO_COM(module_name, "project/hasAskedAlsoShareAudioCom");
      64              : static const Settings::Key SHOULD_DESTINATION_FOLDER_BE_OPENED_ON_EXPORT(module_name, "project/shouldDestinationFolderBeOpenedOnExport");
      65              : static const Settings::Key OPEN_DETAILED_PROJECT_UPLOADED_DIALOG(module_name, "project/openDetailedProjectUploadedDialog");
      66              : static const Settings::Key HAS_ASKED_AUDIO_GENERATION_SETTINGS(module_name, "project/hasAskedAudioGenerationSettings");
      67              : static const Settings::Key GENERATE_AUDIO_TIME_PERIOD_TYPE_KEY(module_name, "project/generateAudioTimePeriodType");
      68              : static const Settings::Key NUMBER_OF_SAVES_TO_GENERATE_AUDIO_KEY(module_name, "project/numberOfSavesToGenerateAudio");
      69              : static const Settings::Key SHOW_CONVERT_FILE_PROCESSING_DIALOG(module_name, "project/showConvertFileProcessingDialog");
      70              : static const Settings::Key SHOW_CLOUD_IS_NOT_AVAILABLE_WARNING(module_name, "project/showCloudIsNotAvailableWarning");
      71              : static const Settings::Key DISABLE_VERSION_CHECKING(module_name, "project/disableVersionChecking");
      72              : static const Settings::Key CREATE_BACKUP_BEFORE_SAVING(module_name, "project/createBackupBeforeSaving");
      73              : 
      74              : static const std::string DEFAULT_FILE_SUFFIX(".mscz");
      75              : 
      76            0 : ProjectConfiguration::ProjectConfiguration(const muse::modularity::ContextPtr& iocCtx)
      77            0 :     : muse::Contextable(iocCtx)
      78              : {
      79            0 : }
      80              : 
      81            0 : void ProjectConfiguration::init()
      82              : {
      83            0 :     settings()->setDefaultValue(USER_TEMPLATES_PATH, Val(globalConfiguration()->userDataPath() + "/Templates"));
      84            0 :     settings()->valueChanged(USER_TEMPLATES_PATH).onReceive(nullptr, [this](const Val& val) {
      85            0 :         m_userTemplatesPathChanged.send(val.toPath());
      86            0 :     });
      87              : 
      88            0 :     settings()->setDefaultValue(USER_PROJECTS_PATH, Val(globalConfiguration()->userDataPath() + "/Scores"));
      89              : 
      90            0 :     settings()->valueChanged(USER_PROJECTS_PATH).onReceive(nullptr, [this](const Val& val) {
      91            0 :         m_userScoresPathChanged.send(val.toPath());
      92            0 :     });
      93              : 
      94            0 :     Val preferredScoreCreationMode = Val(PreferredScoreCreationMode::FromInstruments);
      95            0 :     settings()->setDefaultValue(PREFERRED_SCORE_CREATION_MODE_KEY, preferredScoreCreationMode);
      96              : 
      97            0 :     settings()->setDefaultValue(HOME_SCORES_PAGE_TAB_INDEX, Val(0));
      98              : 
      99            0 :     settings()->setDefaultValue(HOME_SCORES_PAGE_VIEW_TYPE, Val(HomeScoresPageViewType::Grid));
     100              : 
     101            0 :     settings()->setDefaultValue(SHOULD_ASK_SAVE_LOCATION_TYPE, Val(true));
     102            0 :     settings()->setDefaultValue(LAST_USED_SAVE_LOCATION_TYPE, Val(SaveLocationType::Undefined));
     103              : 
     104            0 :     settings()->setDefaultValue(SHOULD_WARN_BEFORE_PUBLISH, Val(true));
     105            0 :     settings()->setDefaultValue(SHOULD_WARN_BEFORE_SAVING_PUBLICLY_TO_CLOUD, Val(true));
     106              : 
     107            0 :     settings()->setDefaultValue(AUTOSAVE_ENABLED_KEY, Val(true));
     108            0 :     settings()->valueChanged(AUTOSAVE_ENABLED_KEY).onReceive(nullptr, [this](const Val& val) {
     109            0 :         m_autoSaveEnabledChanged.send(val.toBool());
     110            0 :     });
     111              : 
     112            0 :     settings()->setDefaultValue(AUTOSAVE_INTERVAL_KEY, Val(5));
     113            0 :     settings()->valueChanged(AUTOSAVE_INTERVAL_KEY).onReceive(nullptr, [this](const Val& val) {
     114            0 :         m_autoSaveIntervalChanged.send(val.toInt());
     115            0 :     });
     116              : 
     117            0 :     settings()->setDefaultValue(ALSO_SHARE_AUDIO_COM_AFTER_PUBLISH, Val(true));
     118            0 :     settings()->valueChanged(ALSO_SHARE_AUDIO_COM_AFTER_PUBLISH).onReceive(nullptr, [this](const Val& val) {
     119            0 :         m_alsoShareAudioComChanged.send(val.toBool());
     120            0 :     });
     121              : 
     122            0 :     settings()->setDefaultValue(SHOW_ALSO_SHARE_AUDIO_COM_DIALOG, Val(true));
     123            0 :     settings()->setDefaultValue(HAS_ASKED_ALSO_SHARE_AUDIO_COM, Val(false));
     124              : 
     125            0 :     settings()->setDefaultValue(SHOULD_DESTINATION_FOLDER_BE_OPENED_ON_EXPORT, Val(false));
     126            0 :     settings()->setDefaultValue(OPEN_DETAILED_PROJECT_UPLOADED_DIALOG, Val(true));
     127            0 :     settings()->setDefaultValue(HAS_ASKED_AUDIO_GENERATION_SETTINGS, Val(false));
     128            0 :     settings()->setDefaultValue(GENERATE_AUDIO_TIME_PERIOD_TYPE_KEY, Val(static_cast<int>(GenerateAudioTimePeriodType::Never)));
     129            0 :     settings()->valueChanged(GENERATE_AUDIO_TIME_PERIOD_TYPE_KEY).onReceive(nullptr, [this](const Val& val) {
     130            0 :         m_generateAudioTimePeriodTypeChanged.send(val.toInt());
     131            0 :     });
     132            0 :     settings()->setDefaultValue(NUMBER_OF_SAVES_TO_GENERATE_AUDIO_KEY, Val(10));
     133            0 :     settings()->valueChanged(NUMBER_OF_SAVES_TO_GENERATE_AUDIO_KEY).onReceive(nullptr, [this](const Val& val) {
     134            0 :         m_numberOfSavesToGenerateAudioChanged.send(val.toInt());
     135            0 :     });
     136            0 :     settings()->setDefaultValue(SHOW_CONVERT_FILE_PROCESSING_DIALOG, Val(true));
     137              : 
     138            0 :     settings()->setDefaultValue(SHOW_CLOUD_IS_NOT_AVAILABLE_WARNING, Val(true));
     139              : 
     140            0 :     settings()->setDefaultValue(DISABLE_VERSION_CHECKING, Val(false));
     141              : 
     142            0 :     settings()->setDefaultValue(CREATE_BACKUP_BEFORE_SAVING, Val(true));
     143            0 :     settings()->setDescription(CREATE_BACKUP_BEFORE_SAVING, muse::trc("project",
     144              :                                                                       "Create backup of file on disk before saving new changes"));
     145            0 :     settings()->setCanBeManuallyEdited(CREATE_BACKUP_BEFORE_SAVING, true);
     146              : 
     147            0 :     if (!userTemplatesPath().empty()) {
     148            0 :         fileSystem()->makePath(userTemplatesPath());
     149              :     }
     150              : 
     151            0 :     if (!userProjectsPath().empty()) {
     152            0 :         fileSystem()->makePath(userProjectsPath());
     153              :     }
     154              : 
     155            0 :     fileSystem()->makePath(cloudProjectsPath());
     156            0 : }
     157              : 
     158            0 : muse::io::path_t ProjectConfiguration::recentFilesJsonPath() const
     159              : {
     160            0 :     return globalConfiguration()->userAppDataPath().appendingComponent("recent_files.json");
     161              : }
     162              : 
     163            0 : ByteArray ProjectConfiguration::compatRecentFilesData() const
     164              : {
     165            0 :     std::string data = settings()->value(COMPAT_RECENT_FILES_DATA).toString();
     166              : 
     167            0 :     return ByteArray(data.data(), data.size());
     168            0 : }
     169              : 
     170            0 : muse::io::path_t ProjectConfiguration::myFirstProjectPath() const
     171              : {
     172            0 :     return appTemplatesPath() + "/My_First_Score.mscx";
     173              : }
     174              : 
     175            0 : muse::io::path_t ProjectConfiguration::appTemplatesPath() const
     176              : {
     177            0 :     return globalConfiguration()->appDataPath() + "/templates";
     178              : }
     179              : 
     180            0 : io::paths_t ProjectConfiguration::availableTemplateDirs() const
     181              : {
     182            0 :     io::paths_t dirs;
     183              : 
     184            0 :     muse::io::path_t defaultTemplatesPath = this->appTemplatesPath();
     185            0 :     dirs.push_back(defaultTemplatesPath);
     186              : 
     187            0 :     muse::io::path_t userTemplatesPath = this->userTemplatesPath();
     188            0 :     if (!userTemplatesPath.empty() && userTemplatesPath != defaultTemplatesPath) {
     189            0 :         dirs.push_back(userTemplatesPath);
     190              :     }
     191              : 
     192            0 :     return dirs;
     193            0 : }
     194              : 
     195            0 : muse::io::path_t ProjectConfiguration::templateCategoriesJsonPath(const muse::io::path_t& templatesDir) const
     196              : {
     197            0 :     return templatesDir + "/categories.json";
     198              : }
     199              : 
     200            0 : muse::io::path_t ProjectConfiguration::userTemplatesPath() const
     201              : {
     202            0 :     return settings()->value(USER_TEMPLATES_PATH).toPath();
     203              : }
     204              : 
     205            0 : void ProjectConfiguration::setUserTemplatesPath(const muse::io::path_t& path)
     206              : {
     207            0 :     settings()->setSharedValue(USER_TEMPLATES_PATH, Val(path));
     208            0 : }
     209              : 
     210            0 : muse::async::Channel<muse::io::path_t> ProjectConfiguration::userTemplatesPathChanged() const
     211              : {
     212            0 :     return m_userTemplatesPathChanged;
     213              : }
     214              : 
     215            0 : muse::io::path_t ProjectConfiguration::lastOpenedProjectsPath() const
     216              : {
     217            0 :     return settings()->value(LAST_OPENED_PROJECTS_PATH).toPath();
     218              : }
     219              : 
     220            0 : void ProjectConfiguration::setLastOpenedProjectsPath(const muse::io::path_t& path)
     221              : {
     222            0 :     settings()->setSharedValue(LAST_OPENED_PROJECTS_PATH, Val(path));
     223            0 : }
     224              : 
     225            0 : muse::io::path_t ProjectConfiguration::lastSavedProjectsPath() const
     226              : {
     227            0 :     return settings()->value(LAST_SAVED_PROJECTS_PATH).toPath();
     228              : }
     229              : 
     230            0 : void ProjectConfiguration::setLastSavedProjectsPath(const muse::io::path_t& path)
     231              : {
     232            0 :     settings()->setSharedValue(LAST_SAVED_PROJECTS_PATH, Val(path));
     233            0 : }
     234              : 
     235            0 : muse::io::path_t ProjectConfiguration::userProjectsPath() const
     236              : {
     237            0 :     return settings()->value(USER_PROJECTS_PATH).toPath();
     238              : }
     239              : 
     240            0 : void ProjectConfiguration::setUserProjectsPath(const muse::io::path_t& path)
     241              : {
     242            0 :     settings()->setSharedValue(USER_PROJECTS_PATH, Val(path));
     243            0 : }
     244              : 
     245            0 : muse::async::Channel<muse::io::path_t> ProjectConfiguration::userProjectsPathChanged() const
     246              : {
     247            0 :     return m_userScoresPathChanged;
     248              : }
     249              : 
     250            0 : muse::io::path_t ProjectConfiguration::defaultUserProjectsPath() const
     251              : {
     252            0 :     return settings()->defaultValue(USER_PROJECTS_PATH).toPath();
     253              : }
     254              : 
     255            0 : void ProjectConfiguration::setLastOpenedConvertFilePath(const muse::io::path_t& path)
     256              : {
     257            0 :     settings()->setSharedValue(LAST_OPENED_CONVERT_FILE_PATH, Val(path));
     258            0 : }
     259              : 
     260            0 : muse::io::path_t ProjectConfiguration::defaultConvertFilePath() const
     261              : {
     262            0 :     muse::io::path_t dir = settings()->value(LAST_OPENED_CONVERT_FILE_PATH).toPath();
     263            0 :     if (dir.empty()) {
     264            0 :         dir = globalConfiguration()->documentsPath();
     265              :     }
     266              : 
     267            0 :     return dir;
     268            0 : }
     269              : 
     270            0 : bool ProjectConfiguration::shouldAskSaveLocationType() const
     271              : {
     272            0 :     return settings()->value(SHOULD_ASK_SAVE_LOCATION_TYPE).toBool();
     273              : }
     274              : 
     275            0 : void ProjectConfiguration::setShouldAskSaveLocationType(bool shouldAsk)
     276              : {
     277            0 :     settings()->setSharedValue(SHOULD_ASK_SAVE_LOCATION_TYPE, Val(shouldAsk));
     278            0 : }
     279              : 
     280            0 : muse::io::path_t ProjectConfiguration::legacyCloudProjectsPath() const
     281              : {
     282            0 :     return globalConfiguration()->userDataPath() + "/Cloud Scores";
     283              : }
     284              : 
     285            0 : muse::io::path_t ProjectConfiguration::cloudProjectsPath() const
     286              : {
     287            0 :     return globalConfiguration()->userAppDataPath() + "/cloud_scores";
     288              : }
     289              : 
     290            0 : bool ProjectConfiguration::isCloudProject(const muse::io::path_t& projectPath) const
     291              : {
     292            0 :     muse::io::path_t dirpath = io::dirpath(projectPath);
     293            0 :     return dirpath == legacyCloudProjectsPath() || dirpath == cloudProjectsPath();
     294            0 : }
     295              : 
     296            0 : bool ProjectConfiguration::isLegacyCloudProject(const muse::io::path_t& projectPath) const
     297              : {
     298            0 :     return io::dirpath(projectPath) == legacyCloudProjectsPath();
     299              : }
     300              : 
     301            0 : muse::io::path_t ProjectConfiguration::cloudProjectPath(int scoreId) const
     302              : {
     303            0 :     return cloudProjectsPath().appendingComponent(QString::number(scoreId)).appendingSuffix(DEFAULT_FILE_SUFFIX);
     304              : }
     305              : 
     306            0 : int ProjectConfiguration::cloudScoreIdFromPath(const muse::io::path_t& projectPath) const
     307              : {
     308            0 :     return io::filename(projectPath, false).toQString().toInt();
     309              : }
     310              : 
     311            0 : muse::io::path_t ProjectConfiguration::cloudProjectSavingPath(int scoreId) const
     312              : {
     313            0 :     if (scoreId != 0) {
     314            0 :         return cloudProjectPath(scoreId);
     315              :     }
     316              : 
     317            0 :     muse::io::path_t path;
     318            0 :     int counter = 0;
     319              : 
     320              :     do {
     321            0 :         path = cloudProjectsPath()
     322            0 :                .appendingComponent(QStringLiteral("not_uploaded_") + QString::number(counter++))
     323            0 :                .appendingSuffix(DEFAULT_FILE_SUFFIX);
     324            0 :     } while (fileSystem()->exists(path));
     325              : 
     326            0 :     return path;
     327            0 : }
     328              : 
     329            0 : muse::io::path_t ProjectConfiguration::defaultSavingFilePath(INotationProjectPtr project, const std::string& filenameAddition,
     330              :                                                              const std::string& suffix) const
     331              : {
     332            0 :     muse::io::path_t folderPath;
     333            0 :     muse::io::path_t filename;
     334            0 :     std::string theSuffix = suffix;
     335            0 :     std::string theFilenameAddition = filenameAddition;
     336              : 
     337            0 :     muse::io::path_t projectPath = project->path();
     338            0 :     bool isLocalProject = !project->isCloudProject();
     339              : 
     340            0 :     if (isLocalProject) {
     341            0 :         if (project->isNewlyCreated()) {
     342            0 :             if (io::isAbsolute(projectPath)) {
     343            0 :                 filename = io::filename(projectPath, false);
     344            0 :                 folderPath = io::dirpath(projectPath);
     345              :             } else {
     346            0 :                 filename = io::filename(projectPath, true);
     347              :             }
     348              :         } else {
     349            0 :             projectPath = engraving::containerPath(projectPath);
     350            0 :             folderPath = io::dirpath(projectPath);
     351            0 :             filename = io::filename(projectPath, false);
     352              : 
     353            0 :             if (theSuffix.empty()) {
     354            0 :                 theSuffix = io::suffix(projectPath);
     355              :             }
     356              :         }
     357              :     }
     358              : 
     359            0 :     if (folderPath.empty()) {
     360            0 :         folderPath = userProjectsPath();
     361              :     }
     362              : 
     363            0 :     if (folderPath.empty()) {
     364            0 :         folderPath = lastSavedProjectsPath();
     365              :     }
     366              : 
     367            0 :     if (folderPath.empty()) {
     368            0 :         folderPath = defaultUserProjectsPath();
     369              :     }
     370              : 
     371            0 :     if (filename.empty()) {
     372            0 :         filename = project->metaInfo().title;
     373              :     }
     374              : 
     375            0 :     if (filename.empty()) {
     376            0 :         filename = muse::qtrc("project", "Untitled");
     377              :     }
     378              : 
     379            0 :     if (theSuffix.empty()) {
     380            0 :         theSuffix = DEFAULT_FILE_SUFFIX;
     381              :     }
     382              : 
     383            0 :     if (project->isNewlyCreated() && filename.toQString() == NotationProject::scoreDefaultTitle()) {
     384            0 :         theFilenameAddition += uniqueFileNameAddition(filename + theFilenameAddition, folderPath, theSuffix);
     385              :     }
     386              : 
     387              :     return folderPath
     388            0 :            .appendingComponent(filename + theFilenameAddition)
     389            0 :            .appendingSuffix(theSuffix);
     390            0 : }
     391              : 
     392            0 : std::string ProjectConfiguration::uniqueFileNameAddition(const io::path_t& filename, const io::path_t& folderPath,
     393              :                                                          const std::string& suffix) const
     394              : {
     395              :     // Return a filename addition which would make filename unique if it wasn't already
     396            0 :     const std::string theSuffix = suffix.empty() ? DEFAULT_FILE_SUFFIX : suffix;
     397            0 :     std::string addition;
     398            0 :     int id = 1;
     399            0 :     while (fileSystem()->exists(folderPath
     400            0 :                                 .appendingComponent(filename + addition)
     401            0 :                                 .appendingSuffix(theSuffix))) {
     402            0 :         addition = " (" + std::to_string(id) + ")";
     403            0 :         id++;
     404              :     }
     405            0 :     return addition;
     406            0 : }
     407              : 
     408            0 : SaveLocationType ProjectConfiguration::lastUsedSaveLocationType() const
     409              : {
     410            0 :     return settings()->value(LAST_USED_SAVE_LOCATION_TYPE).toEnum<SaveLocationType>();
     411              : }
     412              : 
     413            0 : void ProjectConfiguration::setLastUsedSaveLocationType(SaveLocationType type)
     414              : {
     415            0 :     settings()->setSharedValue(LAST_USED_SAVE_LOCATION_TYPE, Val(type));
     416            0 : }
     417              : 
     418            0 : bool ProjectConfiguration::shouldWarnBeforePublish() const
     419              : {
     420            0 :     return settings()->value(SHOULD_WARN_BEFORE_PUBLISH).toBool();
     421              : }
     422              : 
     423            0 : void ProjectConfiguration::setShouldWarnBeforePublish(bool shouldWarn)
     424              : {
     425            0 :     return settings()->setSharedValue(SHOULD_WARN_BEFORE_PUBLISH, Val(shouldWarn));
     426              : }
     427              : 
     428            0 : bool ProjectConfiguration::shouldWarnBeforeSavingPubliclyToCloud() const
     429              : {
     430            0 :     return settings()->value(SHOULD_WARN_BEFORE_SAVING_PUBLICLY_TO_CLOUD).toBool();
     431              : }
     432              : 
     433            0 : void ProjectConfiguration::setShouldWarnBeforeSavingPubliclyToCloud(bool shouldWarn)
     434              : {
     435            0 :     settings()->setSharedValue(SHOULD_WARN_BEFORE_SAVING_PUBLICLY_TO_CLOUD, Val(shouldWarn));
     436            0 : }
     437              : 
     438            0 : int ProjectConfiguration::homeScoresPageTabIndex() const
     439              : {
     440            0 :     return settings()->value(HOME_SCORES_PAGE_TAB_INDEX).toInt();
     441              : }
     442              : 
     443            0 : void ProjectConfiguration::setHomeScoresPageTabIndex(int index)
     444              : {
     445              :     // Intentionally not directly synced between instances
     446              :     // (it would be weird if you switch the tab in one instance, and the others suddenly switch too)
     447            0 :     settings()->setLocalValue(HOME_SCORES_PAGE_TAB_INDEX, Val(index));
     448            0 : }
     449              : 
     450            0 : IProjectConfiguration::HomeScoresPageViewType ProjectConfiguration::homeScoresPageViewType() const
     451              : {
     452            0 :     return settings()->value(HOME_SCORES_PAGE_VIEW_TYPE).toEnum<HomeScoresPageViewType>();
     453              : }
     454              : 
     455            0 : void ProjectConfiguration::setHomeScoresPageViewType(HomeScoresPageViewType type)
     456              : {
     457              :     // Intentionally not directly synced between instances
     458              :     // (it would be weird if you switch the view in one instance, and the others suddenly switch too)
     459            0 :     settings()->setLocalValue(HOME_SCORES_PAGE_VIEW_TYPE, Val(type));
     460            0 : }
     461              : 
     462            0 : QColor ProjectConfiguration::templatePreviewBackgroundColor() const
     463              : {
     464            0 :     return notationConfiguration()->backgroundColor();
     465              : }
     466              : 
     467            0 : muse::async::Notification ProjectConfiguration::templatePreviewBackgroundChanged() const
     468              : {
     469            0 :     return notationConfiguration()->backgroundChanged();
     470              : }
     471              : 
     472            0 : ProjectConfiguration::PreferredScoreCreationMode ProjectConfiguration::preferredScoreCreationMode() const
     473              : {
     474            0 :     return settings()->value(PREFERRED_SCORE_CREATION_MODE_KEY).toEnum<PreferredScoreCreationMode>();
     475              : }
     476              : 
     477            0 : void ProjectConfiguration::setPreferredScoreCreationMode(PreferredScoreCreationMode mode)
     478              : {
     479            0 :     settings()->setSharedValue(PREFERRED_SCORE_CREATION_MODE_KEY, Val(mode));
     480            0 : }
     481              : 
     482            0 : static MigrationType migrationTypeFromString(const QString& str)
     483              : {
     484            0 :     if (str == "Pre_3_6") {
     485            0 :         return MigrationType::Pre_3_6;
     486              :     }
     487            0 :     if (str == "Ver_3_6") {
     488            0 :         return MigrationType::Ver_3_6;
     489              :     }
     490            0 :     LOGE() << "Unknown migration type string: " << str;
     491            0 :     return MigrationType::Unknown;
     492              : }
     493              : 
     494            0 : MigrationOptions ProjectConfiguration::migrationOptions(MigrationType type) const
     495              : {
     496            0 :     auto it = m_migrationOptions.find(type);
     497            0 :     if (it != m_migrationOptions.end()) {
     498            0 :         return it->second;
     499              :     }
     500              : 
     501            0 :     QString json = settings()->value(MIGRATION_OPTIONS).toQString();
     502            0 :     QJsonArray array = QJsonDocument::fromJson(json.toUtf8()).array();
     503              : 
     504            0 :     MigrationOptions result;
     505              : 
     506            0 :     for (const auto val : array) {
     507            0 :         QJsonObject obj = val.toObject();
     508              : 
     509            0 :         auto migrationType = migrationTypeFromString(obj["migrationType"].toString());
     510            0 :         if (migrationType == MigrationType::Unknown) {
     511            0 :             continue;
     512              :         }
     513              : 
     514            0 :         QJsonObject optionsObj = obj["options"].toObject();
     515              : 
     516            0 :         MigrationOptions opt;
     517            0 :         opt.appVersion = optionsObj["appVersion"].toInt(0);
     518            0 :         opt.isApplyMigration = optionsObj["isApplyMigration"].toBool();
     519            0 :         opt.isAskAgain = optionsObj["isAskAgain"].toBool();
     520            0 :         opt.isApplyLeland = optionsObj["isApplyLeland"].toBool();
     521            0 :         opt.isApplyEdwin = optionsObj["isApplyEdwin"].toBool();
     522            0 :         opt.isRemapPercussion = optionsObj["isRemapPercussion"].toBool();
     523              : 
     524            0 :         m_migrationOptions[migrationType] = opt;
     525              : 
     526            0 :         if (migrationType == type) {
     527            0 :             result = opt;
     528              :         }
     529            0 :     }
     530              : 
     531            0 :     return result;
     532            0 : }
     533              : 
     534            0 : static QString migrationTypeToString(MigrationType type)
     535              : {
     536            0 :     switch (type) {
     537            0 :     case MigrationType::Pre_3_6:
     538            0 :         return QStringLiteral("Pre_3_6");
     539            0 :     case MigrationType::Ver_3_6:
     540            0 :         return QStringLiteral("Ver_3_6");
     541            0 :     case MigrationType::Unknown:
     542            0 :         break;
     543              :     }
     544            0 :     return QString();
     545              : }
     546              : 
     547            0 : void ProjectConfiguration::setMigrationOptions(MigrationType type, const MigrationOptions& opt, bool persistent)
     548              : {
     549            0 :     m_migrationOptions[type] = opt;
     550              : 
     551            0 :     if (!persistent) {
     552            0 :         return;
     553              :     }
     554              : 
     555            0 :     QJsonArray array;
     556              : 
     557            0 :     for (auto it = m_migrationOptions.cbegin(); it != m_migrationOptions.cend(); ++it) {
     558            0 :         const MigrationOptions& o = it->second;
     559              : 
     560            0 :         QJsonObject options;
     561            0 :         options["appVersion"] = o.appVersion;
     562            0 :         options["isApplyMigration"] = o.isApplyMigration;
     563            0 :         options["isAskAgain"] = o.isAskAgain;
     564            0 :         options["isApplyLeland"] = o.isApplyLeland;
     565            0 :         options["isApplyEdwin"] = o.isApplyEdwin;
     566            0 :         options["isRemapPercussion"] = o.isRemapPercussion;
     567              : 
     568            0 :         QJsonObject obj;
     569            0 :         obj["migrationType"] = migrationTypeToString(it->first);
     570            0 :         obj["options"] = options;
     571              : 
     572            0 :         array << obj;
     573            0 :     }
     574              : 
     575            0 :     QString json = QJsonDocument(array).toJson(QJsonDocument::Compact);
     576            0 :     settings()->setSharedValue(MIGRATION_OPTIONS, Val(json));
     577            0 : }
     578              : 
     579            0 : bool ProjectConfiguration::isAutoSaveEnabled() const
     580              : {
     581            0 :     return settings()->value(AUTOSAVE_ENABLED_KEY).toBool();
     582              : }
     583              : 
     584            0 : void ProjectConfiguration::setAutoSaveEnabled(bool enabled)
     585              : {
     586            0 :     settings()->setSharedValue(AUTOSAVE_ENABLED_KEY, Val(enabled));
     587            0 : }
     588              : 
     589            0 : muse::async::Channel<bool> ProjectConfiguration::autoSaveEnabledChanged() const
     590              : {
     591            0 :     return m_autoSaveEnabledChanged;
     592              : }
     593              : 
     594            0 : int ProjectConfiguration::autoSaveIntervalMinutes() const
     595              : {
     596            0 :     return settings()->value(AUTOSAVE_INTERVAL_KEY).toInt();
     597              : }
     598              : 
     599            0 : void ProjectConfiguration::setAutoSaveInterval(int minutes)
     600              : {
     601            0 :     settings()->setSharedValue(AUTOSAVE_INTERVAL_KEY, Val(minutes));
     602            0 : }
     603              : 
     604            0 : muse::async::Channel<int> ProjectConfiguration::autoSaveIntervalChanged() const
     605              : {
     606            0 :     return m_autoSaveIntervalChanged;
     607              : }
     608              : 
     609            0 : bool ProjectConfiguration::alsoShareAudioCom() const
     610              : {
     611            0 :     return settings()->value(ALSO_SHARE_AUDIO_COM_AFTER_PUBLISH).toBool();
     612              : }
     613              : 
     614            0 : void ProjectConfiguration::setAlsoShareAudioCom(bool share)
     615              : {
     616            0 :     settings()->setSharedValue(ALSO_SHARE_AUDIO_COM_AFTER_PUBLISH, Val(share));
     617            0 : }
     618              : 
     619            0 : muse::async::Channel<bool> ProjectConfiguration::alsoShareAudioComChanged() const
     620              : {
     621            0 :     return m_alsoShareAudioComChanged;
     622              : }
     623              : 
     624            0 : bool ProjectConfiguration::showAlsoShareAudioComDialog() const
     625              : {
     626            0 :     return settings()->value(SHOW_ALSO_SHARE_AUDIO_COM_DIALOG).toBool();
     627              : }
     628              : 
     629            0 : void ProjectConfiguration::setShowAlsoShareAudioComDialog(bool show)
     630              : {
     631            0 :     settings()->setSharedValue(SHOW_ALSO_SHARE_AUDIO_COM_DIALOG, Val(show));
     632            0 : }
     633              : 
     634            0 : bool ProjectConfiguration::hasAskedAlsoShareAudioCom() const
     635              : {
     636            0 :     return settings()->value(HAS_ASKED_ALSO_SHARE_AUDIO_COM).toBool();
     637              : }
     638              : 
     639            0 : void ProjectConfiguration::setHasAskedAlsoShareAudioCom(bool has)
     640              : {
     641            0 :     settings()->setSharedValue(HAS_ASKED_ALSO_SHARE_AUDIO_COM, Val(has));
     642            0 : }
     643              : 
     644            0 : muse::io::path_t ProjectConfiguration::newProjectTemporaryPath() const
     645              : {
     646            0 :     return globalConfiguration()->userAppDataPath() + "/new_project" + DEFAULT_FILE_SUFFIX;
     647              : }
     648              : 
     649            0 : bool ProjectConfiguration::shouldDestinationFolderBeOpenedOnExport() const
     650              : {
     651            0 :     return settings()->value(SHOULD_DESTINATION_FOLDER_BE_OPENED_ON_EXPORT).toBool();
     652              : }
     653              : 
     654            0 : void ProjectConfiguration::setShouldDestinationFolderBeOpenedOnExport(bool shouldDestinationFolderBeOpenedOnExport)
     655              : {
     656            0 :     settings()->setSharedValue(SHOULD_DESTINATION_FOLDER_BE_OPENED_ON_EXPORT, Val(shouldDestinationFolderBeOpenedOnExport));
     657            0 : }
     658              : 
     659            0 : QUrl ProjectConfiguration::supportForumUrl() const
     660              : {
     661            0 :     if (languagesService()->currentLanguage().code.startsWith("en")) {
     662              :         // The English support forum
     663            0 :         return QUrl("https://musescore.org/forum/6");
     664              :     }
     665              : 
     666              :     // The general forum page, where the support forum is linked at the top
     667              :     // (except in English; there you have the Announcements forum)
     668            0 :     return QUrl("https://musescore.org/forum");
     669              : }
     670              : 
     671            0 : QUrl ProjectConfiguration::dotComBugReportUrl() const
     672              : {
     673              :     // The general .com bug report page
     674            0 :     return QUrl("https://musescore.com/groups/bug-reports");
     675              : }
     676              : 
     677            0 : QUrl ProjectConfiguration::scoreUploadingGuidelinesUrl() const
     678              : {
     679            0 :     return QUrl("https://musescore.com/score-uploading-guidelines");
     680              : }
     681              : 
     682            0 : QUrl ProjectConfiguration::termsOfUseUrl() const
     683              : {
     684              :     return QUrl("https://musescore.com/redirect/af?mu_target=https%3A%2F%2Fmusescore.com%2Flegal%2Fterms"
     685              :                 "%3Futm_medium%3DReferral%26utm_source%3Dmss%26utm_campaign%3DMU_WW_REF_MSS_DESKTOP_ALL_261026_crosslink"
     686              :                 "%26utm_content%3Dconvert_to_score"
     687            0 :                 "&utm_medium=Referral&utm_source=mss&utm_campaign=MU_WW_REF_MSS_DESKTOP_ALL_261026_crosslink&utm_content=convert_to_score");
     688              : }
     689              : 
     690            0 : bool ProjectConfiguration::openDetailedProjectUploadedDialog() const
     691              : {
     692            0 :     return settings()->value(OPEN_DETAILED_PROJECT_UPLOADED_DIALOG).toBool();
     693              : }
     694              : 
     695            0 : void ProjectConfiguration::setOpenDetailedProjectUploadedDialog(bool show)
     696              : {
     697            0 :     settings()->setSharedValue(OPEN_DETAILED_PROJECT_UPLOADED_DIALOG, Val(show));
     698            0 : }
     699              : 
     700            0 : bool ProjectConfiguration::hasAskedAudioGenerationSettings() const
     701              : {
     702            0 :     return settings()->value(HAS_ASKED_AUDIO_GENERATION_SETTINGS).toBool();
     703              : }
     704              : 
     705            0 : void ProjectConfiguration::setHasAskedAudioGenerationSettings(bool has)
     706              : {
     707            0 :     settings()->setSharedValue(HAS_ASKED_AUDIO_GENERATION_SETTINGS, Val(has));
     708            0 : }
     709              : 
     710            0 : GenerateAudioTimePeriodType ProjectConfiguration::generateAudioTimePeriodType() const
     711              : {
     712            0 :     return static_cast<GenerateAudioTimePeriodType>(settings()->value(GENERATE_AUDIO_TIME_PERIOD_TYPE_KEY).toInt());
     713              : }
     714              : 
     715            0 : void ProjectConfiguration::setGenerateAudioTimePeriodType(GenerateAudioTimePeriodType type)
     716              : {
     717            0 :     settings()->setSharedValue(GENERATE_AUDIO_TIME_PERIOD_TYPE_KEY, Val(static_cast<int>(type)));
     718            0 : }
     719              : 
     720            0 : muse::async::Channel<int> ProjectConfiguration::generateAudioTimePeriodTypeChanged() const
     721              : {
     722            0 :     return m_generateAudioTimePeriodTypeChanged;
     723              : }
     724              : 
     725            0 : int ProjectConfiguration::numberOfSavesToGenerateAudio() const
     726              : {
     727            0 :     return settings()->value(NUMBER_OF_SAVES_TO_GENERATE_AUDIO_KEY).toInt();
     728              : }
     729              : 
     730            0 : void ProjectConfiguration::setNumberOfSavesToGenerateAudio(int number)
     731              : {
     732            0 :     settings()->setSharedValue(NUMBER_OF_SAVES_TO_GENERATE_AUDIO_KEY, Val(number));
     733            0 : }
     734              : 
     735            0 : muse::async::Channel<int> ProjectConfiguration::numberOfSavesToGenerateAudioChanged() const
     736              : {
     737            0 :     return m_numberOfSavesToGenerateAudioChanged;
     738              : }
     739              : 
     740            0 : muse::io::path_t ProjectConfiguration::temporaryMp3FilePathTemplate() const
     741              : {
     742            0 :     return globalConfiguration()->userAppDataPath() + "/audioFile_XXXXXX.mp3";
     743              : }
     744              : 
     745            0 : muse::io::path_t ProjectConfiguration::projectBackupPath(const muse::io::path_t& projectPath) const
     746              : {
     747            0 :     muse::io::path_t projectDir = io::absoluteDirpath(projectPath);
     748            0 :     muse::io::path_t projectName = io::filename(projectPath);
     749              : 
     750            0 :     return projectDir + "/.mscbackup/." + projectName + "~";
     751            0 : }
     752              : 
     753            0 : bool ProjectConfiguration::showCloudIsNotAvailableWarning() const
     754              : {
     755            0 :     return settings()->value(SHOW_CLOUD_IS_NOT_AVAILABLE_WARNING).toBool();
     756              : }
     757              : 
     758            0 : void ProjectConfiguration::setShowCloudIsNotAvailableWarning(bool show)
     759              : {
     760            0 :     settings()->setSharedValue(SHOW_CLOUD_IS_NOT_AVAILABLE_WARNING, Val(show));
     761            0 : }
     762              : 
     763            0 : bool ProjectConfiguration::createBackupBeforeSaving() const
     764              : {
     765            0 :     return settings()->value(CREATE_BACKUP_BEFORE_SAVING).toBool();
     766              : }
     767              : 
     768            0 : void ProjectConfiguration::setCreateBackupBeforeSaving(bool create)
     769              : {
     770            0 :     settings()->setSharedValue(CREATE_BACKUP_BEFORE_SAVING, Val(create));
     771            0 : }
     772              : 
     773            0 : bool ProjectConfiguration::disableVersionChecking() const
     774              : {
     775            0 :     return settings()->value(DISABLE_VERSION_CHECKING).toBool();
     776              : }
     777              : 
     778            0 : void ProjectConfiguration::setDisableVersionChecking(bool disable)
     779              : {
     780            0 :     settings()->setSharedValue(DISABLE_VERSION_CHECKING, Val(disable));
     781            0 : }
     782              : 
     783            0 : bool ProjectConfiguration::showConvertFileProcessingDialog() const
     784              : {
     785            0 :     return settings()->value(SHOW_CONVERT_FILE_PROCESSING_DIALOG).toBool();
     786              : }
     787              : 
     788            0 : void ProjectConfiguration::setShowConvertFileProcessingDialog(bool show)
     789              : {
     790            0 :     settings()->setSharedValue(SHOW_CONVERT_FILE_PROCESSING_DIALOG, Val(show));
     791            0 : }
     792              : 
     793            0 : muse::io::path_t ProjectConfiguration::watchedConvertsJsonPath() const
     794              : {
     795            0 :     return globalConfiguration()->userAppDataPath().appendingComponent("watched_converts.json");
     796              : }
        

Generated by: LCOV version 2.0-1