LCOV - code coverage report
Current view: top level - muse/framework/workspace/internal - workspacemanager.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 43.1 % 248 107
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-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 "workspacemanager.h"
      23              : 
      24              : #include "types/uri.h"
      25              : 
      26              : #include "log.h"
      27              : 
      28              : using namespace muse;
      29              : using namespace muse::workspace;
      30              : 
      31              : static const std::string WORKSPACE_EXT(".mws");
      32              : 
      33            0 : static bool containsWorkspace(const IWorkspacePtrList& list, const IWorkspacePtr& workspace)
      34              : {
      35            0 :     return std::find(list.cbegin(), list.cend(), workspace) != list.cend();
      36              : }
      37              : 
      38            3 : void WorkspaceManager::init()
      39              : {
      40            3 :     configuration()->currentWorkspaceNameChanged().onReceive(this, [this](const std::string&) {
      41            0 :         setupCurrentWorkspace();
      42            0 :     });
      43              : 
      44            3 :     load();
      45            3 : }
      46              : 
      47            3 : void WorkspaceManager::deinit()
      48              : {
      49            3 :     saveCurrentWorkspace();
      50              : 
      51            3 :     m_currentWorkspace = nullptr;
      52            3 :     m_defaultWorkspace = nullptr;
      53            3 :     m_workspaces.clear();
      54            3 : }
      55              : 
      56            0 : bool WorkspaceManager::isInited() const
      57              : {
      58            0 :     return m_defaultWorkspace != nullptr;
      59              : }
      60              : 
      61            4 : IWorkspacePtr WorkspaceManager::defaultWorkspace() const
      62              : {
      63            4 :     return m_defaultWorkspace;
      64              : }
      65              : 
      66            5 : IWorkspacePtr WorkspaceManager::currentWorkspace() const
      67              : {
      68            5 :     return m_currentWorkspace;
      69              : }
      70              : 
      71            0 : async::Notification WorkspaceManager::currentWorkspaceAboutToBeChanged() const
      72              : {
      73            0 :     return m_currentWorkspaceAboutToBeChanged;
      74              : }
      75              : 
      76            0 : async::Notification WorkspaceManager::currentWorkspaceChanged() const
      77              : {
      78            0 :     return m_currentWorkspaceChanged;
      79              : }
      80              : 
      81            2 : IWorkspacePtrList WorkspaceManager::workspaces() const
      82              : {
      83            2 :     IWorkspacePtrList iworkspaces;
      84            2 :     iworkspaces.reserve(m_workspaces.size());
      85            4 :     for (const WorkspacePtr& w : m_workspaces) {
      86            2 :         iworkspaces.push_back(w);
      87              :     }
      88            2 :     return iworkspaces;
      89            0 : }
      90              : 
      91            0 : Ret WorkspaceManager::setWorkspaces(const IWorkspacePtrList& workspaces)
      92              : {
      93            0 :     Ret ret = removeMissingWorkspaces(workspaces);
      94              : 
      95            0 :     if (ret) {
      96            0 :         ret = addNonExistentWorkspaces(workspaces);
      97              :     }
      98              : 
      99            0 :     m_workspacesListChanged.notify();
     100              : 
     101            0 :     return ret;
     102            0 : }
     103              : 
     104            0 : async::Notification WorkspaceManager::workspacesListChanged() const
     105              : {
     106            0 :     return m_workspacesListChanged;
     107              : }
     108              : 
     109            0 : IWorkspacePtr WorkspaceManager:: cloneWorkspace(const IWorkspacePtr& workspace, const std::string& newWorkspaceName) const
     110              : {
     111            0 :     return std::make_shared<Workspace>(makeNewWorkspacePath(newWorkspaceName), dynamic_cast<Workspace*>(workspace.get()), iocContext());
     112              : }
     113              : 
     114            0 : void WorkspaceManager::changeCurrentWorkspace(const std::string& newWorkspaceName)
     115              : {
     116            0 :     prepareCurrentWorkspaceForChange();
     117            0 :     doChangeCurrentWorkspace(newWorkspaceName);
     118            0 : }
     119              : 
     120            0 : void WorkspaceManager::doChangeCurrentWorkspace(const std::string& newWorkspaceName)
     121              : {
     122            0 :     if (configuration()->currentWorkspaceName() == newWorkspaceName || newWorkspaceName.empty()) {
     123            0 :         return;
     124              :     }
     125              : 
     126            0 :     configuration()->setCurrentWorkspaceName(newWorkspaceName);
     127              : }
     128              : 
     129            0 : void WorkspaceManager::createAndAppendNewWorkspace()
     130              : {
     131            0 :     prepareCurrentWorkspaceForChange();
     132              : 
     133            0 :     IWorkspacePtrList workspaces = this->workspaces();
     134              : 
     135            0 :     QStringList workspaceNames;
     136            0 :     for (const IWorkspacePtr& workspace: workspaces) {
     137            0 :         workspaceNames << QString::fromStdString(workspace->name());
     138              :     }
     139              : 
     140            0 :     UriQuery uri("muse://workspace/create");
     141            0 :     uri.addParam("sync", Val(true));
     142            0 :     uri.addParam("workspaceNames", Val(workspaceNames.join(',')));
     143              : 
     144            0 :     auto i = modularity::ioc(iocContext())->resolve<IInteractive>("workspace");
     145            0 :     RetVal<Val> obj = i->openSync(uri);
     146            0 :     if (!obj.ret) {
     147            0 :         return;
     148              :     }
     149              : 
     150            0 :     QVariantMap meta = obj.val.toQVariant().toMap();
     151            0 :     QString name = meta.value("name").toString();
     152            0 :     IF_ASSERT_FAILED(!name.isEmpty()) {
     153            0 :         return;
     154              :     }
     155              : 
     156            0 :     IWorkspacePtr newWorkspace = cloneWorkspace(currentWorkspace(), name.toStdString());
     157            0 :     if (!newWorkspace) {
     158            0 :         return;
     159              :     }
     160              : 
     161            0 :     workspaces.emplace_back(newWorkspace);
     162              : 
     163            0 :     setWorkspaces(workspaces);
     164              : 
     165            0 :     configuration()->setCurrentWorkspaceName(name.toStdString());
     166            0 : }
     167              : 
     168            0 : void WorkspaceManager::openConfigureWorkspacesDialog()
     169              : {
     170            0 :     prepareCurrentWorkspaceForChange();
     171              : 
     172            0 :     RetVal<Val> result = interactive()->openSync("muse://workspace/select");
     173            0 :     if (!result.ret) {
     174            0 :         return;
     175              :     }
     176              : 
     177            0 :     std::string selectedWorkspace = result.val.toString();
     178            0 :     doChangeCurrentWorkspace(selectedWorkspace);
     179            0 : }
     180              : 
     181            0 : WorkspacePtr WorkspaceManager::doNewWorkspace(const std::string& workspaceName) const
     182              : {
     183            0 :     return std::make_shared<Workspace>(makeNewWorkspacePath(workspaceName), iocContext());
     184              : }
     185              : 
     186            0 : io::path_t WorkspaceManager::makeNewWorkspacePath(const std::string& workspaceName) const
     187              : {
     188            0 :     return configuration()->userWorkspacesPath() + "/" + workspaceName + WORKSPACE_EXT;
     189              : }
     190              : 
     191            4 : void WorkspaceManager::appendNewWorkspace(WorkspacePtr workspace)
     192              : {
     193            4 :     setupConnectionsToNewWorkspace(workspace);
     194            4 :     m_workspaces.push_back(workspace);
     195            4 : }
     196              : 
     197            4 : void WorkspaceManager::setupConnectionsToNewWorkspace(const IWorkspacePtr workspace)
     198              : {
     199            4 :     std::string newWorkspaceName = workspace->name();
     200            4 :     workspace->reloadNotification().onNotify(this, [this, newWorkspaceName](){
     201            0 :         if (m_currentWorkspace->name() == newWorkspaceName) {
     202            0 :             m_currentWorkspaceChanged.notify();
     203              :         }
     204            0 :     });
     205            4 : }
     206              : 
     207            0 : Ret WorkspaceManager::removeMissingWorkspaces(const IWorkspacePtrList& newWorkspaceList)
     208              : {
     209            0 :     IWorkspacePtrList oldWorkspaceList = workspaces();
     210            0 :     for (const IWorkspacePtr& oldWorkspace : oldWorkspaceList) {
     211            0 :         if (containsWorkspace(newWorkspaceList, oldWorkspace)) {
     212            0 :             continue;
     213              :         }
     214              : 
     215            0 :         Ret ret = removeWorkspace(oldWorkspace);
     216            0 :         if (!ret) {
     217            0 :             return ret;
     218              :         }
     219            0 :     }
     220              : 
     221            0 :     if (!containsWorkspace(newWorkspaceList, m_currentWorkspace)) {
     222            0 :         m_currentWorkspace = nullptr;
     223              :     }
     224              : 
     225            0 :     return make_ret(Ret::Code::Ok);
     226            0 : }
     227              : 
     228            0 : Ret WorkspaceManager::removeWorkspace(const IWorkspacePtr& workspace)
     229              : {
     230            0 :     if (!canRemoveWorkspace(workspace)) {
     231            0 :         return make_ret(Ret::Code::Ok);
     232              :     }
     233              : 
     234            0 :     std::string workspaceName = workspace->name();
     235            0 :     for (auto it = m_workspaces.begin(); it != m_workspaces.end(); ++it) {
     236            0 :         if (it->get()->name() == workspaceName) {
     237            0 :             Ret ret = fileSystem()->remove(it->get()->filePath());
     238            0 :             if (ret) {
     239            0 :                 m_workspaces.erase(it);
     240              :             }
     241              : 
     242            0 :             return ret;
     243            0 :         }
     244              :     }
     245              : 
     246            0 :     return make_ret(Ret::Code::Ok);
     247            0 : }
     248              : 
     249            0 : bool WorkspaceManager::canRemoveWorkspace(const IWorkspacePtr& workspace) const
     250              : {
     251            0 :     return !workspace->isBuiltin();
     252              : }
     253              : 
     254            0 : Ret WorkspaceManager::addNonExistentWorkspaces(const IWorkspacePtrList& newWorkspaceList)
     255              : {
     256            0 :     IWorkspacePtrList existentWorkspaces = workspaces();
     257            0 :     for (const IWorkspacePtr& workspace : newWorkspaceList) {
     258            0 :         if (containsWorkspace(existentWorkspaces, workspace)) {
     259            0 :             continue;
     260              :         }
     261              : 
     262            0 :         Ret ret = addWorkspace(workspace);
     263            0 :         if (!ret) {
     264            0 :             return ret;
     265              :         }
     266            0 :     }
     267              : 
     268            0 :     return make_ret(Ret::Code::Ok);
     269            0 : }
     270              : 
     271            0 : Ret WorkspaceManager::addWorkspace(IWorkspacePtr workspace)
     272              : {
     273            0 :     auto writable = std::dynamic_pointer_cast<Workspace>(workspace);
     274            0 :     if (!writable) {
     275            0 :         return make_ret(Ret::Code::Ok);
     276              :     }
     277              : 
     278            0 :     Ret ret = writable->save();
     279              : 
     280            0 :     if (ret) {
     281            0 :         appendNewWorkspace(writable);
     282              :     }
     283              : 
     284            0 :     return ret;
     285            0 : }
     286              : 
     287            3 : void WorkspaceManager::load()
     288              : {
     289            3 :     reloadWorkspaceFiles();
     290              : 
     291            3 :     setupDefaultWorkspace();
     292            3 :     setupCurrentWorkspace();
     293            3 : }
     294              : 
     295            4 : void WorkspaceManager::reloadWorkspaceFiles()
     296              : {
     297            4 :     m_workspaces.clear();
     298              : 
     299            4 :     io::paths_t files = findWorkspaceFiles();
     300            8 :     for (const io::path_t& file : files) {
     301            4 :         auto workspace = std::make_shared<Workspace>(file, iocContext());
     302            4 :         appendNewWorkspace(workspace);
     303            4 :     }
     304              : 
     305            4 :     m_workspacesListChanged.notify();
     306            4 : }
     307              : 
     308            4 : io::paths_t WorkspaceManager::findWorkspaceFiles() const
     309              : {
     310            4 :     auto findFiles = [this](const io::path_t& dirPath) {
     311            4 :         std::string filter = "*" + WORKSPACE_EXT;
     312           16 :         RetVal<io::paths_t> files = fileSystem()->scanFiles(dirPath, { filter });
     313            4 :         if (!files.ret) {
     314            0 :             LOGE() << files.ret.toString();
     315              :         }
     316              : 
     317            8 :         return files.val;
     318            8 :     };
     319              : 
     320            4 :     io::paths_t builtinWorkspacesPaths = configuration()->builtinWorkspacesFilePaths();
     321              : 
     322            4 :     io::paths_t userWorkspacesPaths = findFiles(configuration()->userWorkspacesPath());
     323            4 :     std::set<io::path_t> userWorkspacesFileNames;
     324            4 :     for (const io::path_t& dirPath : userWorkspacesPaths) {
     325            0 :         userWorkspacesFileNames.insert(io::filename(dirPath));
     326              :     }
     327              : 
     328            4 :     muse::remove_if(builtinWorkspacesPaths, [=](const io::path_t& path) -> bool {
     329            4 :         io::path_t fileName = io::filename(path);
     330            8 :         return muse::contains(userWorkspacesFileNames, fileName);
     331            4 :     });
     332              : 
     333            4 :     io::paths_t result;
     334            4 :     muse::join(result, builtinWorkspacesPaths);
     335            4 :     muse::join(result, userWorkspacesPaths);
     336              : 
     337            4 :     return result;
     338            4 : }
     339              : 
     340            3 : void WorkspaceManager::setupDefaultWorkspace()
     341              : {
     342            3 :     std::string defaultWorkspaceName = configuration()->defaultWorkspaceName();
     343            3 :     WorkspacePtr workspace = findAndInit(defaultWorkspaceName);
     344            3 :     if (workspace) {
     345            3 :         m_defaultWorkspace = workspace;
     346            3 :         return;
     347              :     }
     348              : 
     349            0 :     LOGW() << "not found default workspace, will be created new";
     350              : 
     351            0 :     m_defaultWorkspace = doNewWorkspace(defaultWorkspaceName);
     352            0 :     m_workspaces.push_back(m_defaultWorkspace);
     353              : 
     354            0 :     Ret ret = fileSystem()->makePath(configuration()->userWorkspacesPath());
     355            0 :     if (!ret) {
     356            0 :         LOGE() << ret.toString();
     357            0 :         return;
     358              :     }
     359              : 
     360            0 :     ret = m_defaultWorkspace->load();
     361            0 :     if (!ret) {
     362            0 :         LOGE() << ret.toString();
     363            0 :         return;
     364              :     }
     365            6 : }
     366              : 
     367            3 : void WorkspaceManager::setupCurrentWorkspace()
     368              : {
     369            3 :     std::string workspaceName = configuration()->currentWorkspaceName();
     370            3 :     if (m_currentWorkspace && m_currentWorkspace->isLoaded()) {
     371            0 :         if (m_currentWorkspace->name() == workspaceName) {
     372            0 :             return;
     373              :         }
     374              : 
     375            0 :         saveCurrentWorkspace();
     376              : 
     377              :         //! NOTE Perhaps we need to unload the current workspace (clear memory)
     378              :     }
     379              : 
     380            3 :     WorkspacePtr workspace = findAndInit(workspaceName);
     381            3 :     if (!workspace) {
     382              :         //! NOTE The workspace may have been created by another context/process
     383              :         //! and saved to disk. Try reloading the file list before falling back.
     384            1 :         reloadWorkspaceFiles();
     385            1 :         workspace = findAndInit(workspaceName);
     386              :     }
     387              : 
     388            3 :     if (!workspace) {
     389            1 :         std::string defaultWorkspaceName = configuration()->defaultWorkspaceName();
     390            1 :         LOGW() << "failed get workspace: " << workspaceName << ", will use " << defaultWorkspaceName;
     391              : 
     392              :         //! NOTE Already should be inited
     393            1 :         IF_ASSERT_FAILED(m_defaultWorkspace) {
     394            0 :             setupDefaultWorkspace();
     395              :         }
     396              : 
     397            1 :         workspace = m_defaultWorkspace;
     398            1 :         configuration()->setCurrentWorkspaceName(defaultWorkspaceName);
     399            1 :     }
     400              : 
     401            3 :     m_currentWorkspace = workspace;
     402            3 :     m_currentWorkspaceChanged.notify();
     403            3 : }
     404              : 
     405            7 : WorkspacePtr WorkspaceManager::findByName(const std::string& name) const
     406              : {
     407            9 :     for (auto workspace : m_workspaces) {
     408            7 :         if (workspace->name() == name) {
     409            5 :             return workspace;
     410              :         }
     411            2 :     }
     412              : 
     413            2 :     return nullptr;
     414              : }
     415              : 
     416            7 : WorkspacePtr WorkspaceManager::findAndInit(const std::string& name) const
     417              : {
     418            7 :     WorkspacePtr workspace = findByName(name);
     419            7 :     if (!workspace) {
     420            2 :         return nullptr;
     421              :     }
     422              : 
     423            5 :     if (!workspace->isLoaded()) {
     424            3 :         Ret ret = workspace->load();
     425            3 :         if (!ret) {
     426            0 :             LOGE() << "failed load workspace: " << name;
     427            0 :             return nullptr;
     428              :         }
     429            3 :     }
     430              : 
     431            5 :     return workspace;
     432            7 : }
     433              : 
     434            3 : void WorkspaceManager::saveCurrentWorkspace()
     435              : {
     436            3 :     if (!m_currentWorkspace) {
     437            0 :         return;
     438              :     }
     439              : 
     440            3 :     Ret ret = m_currentWorkspace->save();
     441            3 :     if (!ret) {
     442            0 :         LOGE() << "failed save current workspace, err: " << ret.toString();
     443              :     }
     444            3 : }
     445              : 
     446            0 : void WorkspaceManager::prepareCurrentWorkspaceForChange()
     447              : {
     448            0 :     m_currentWorkspaceAboutToBeChanged.notify();
     449            0 : }
        

Generated by: LCOV version 2.0-1