LCOV - code coverage report
Current view: top level - muse/framework/actions/internal - actionsdispatcher.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 12.5 % 104 13
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 "actionsdispatcher.h"
      23              : 
      24              : #include <sstream>
      25              : 
      26              : #include "actionable.h"
      27              : #include "global/stringutils.h"
      28              : 
      29              : #include "log.h"
      30              : #include "rcommand/commandtypes.h"
      31              : 
      32              : using namespace muse::actions;
      33              : using namespace muse::async;
      34              : 
      35           32 : ActionsDispatcher::~ActionsDispatcher()
      36              : {
      37          544 :     for (auto it = m_clients.begin(); it != m_clients.end(); ++it) {
      38          512 :         Clients& clients = it->second;
      39         1024 :         for (auto cit = clients.begin(); cit != clients.end(); ++cit) {
      40          512 :             Actionable* client = cit->first;
      41          512 :             client->setDispatcher(nullptr);
      42              :         }
      43              :     }
      44           32 : }
      45              : 
      46            0 : void ActionsDispatcher::dispatch(const ActionCode& actionCode)
      47              : {
      48            0 :     static ActionData dummy;
      49            0 :     dispatch(actionCode, dummy);
      50            0 : }
      51              : 
      52            0 : void ActionsDispatcher::dispatch(const ActionCode& actionCode, const ActionData& data)
      53              : {
      54              :     // is query?
      55            0 :     ActionQuery query = ActionQuery(actionCode);
      56            0 :     if (query.isValid()) {
      57            0 :         IF_ASSERT_FAILED(data.empty()) {
      58            0 :             LOGE() << "not supported action data with query";
      59              :         }
      60            0 :         dispatch(query);
      61            0 :         return;
      62              :     }
      63              : 
      64            0 :     if (muse::strings::startsWith(actionCode, rcommand::COMMAND_SCHEME)) {
      65            0 :         commandDispatcher()->dispatch(rcommand::Command(actionCode));
      66            0 :         return;
      67              :     }
      68              : 
      69              :     // code
      70            0 :     auto it = m_clients.find(actionCode);
      71            0 :     if (it == m_clients.end()) {
      72            0 :         LOGW() << "not a registered action: " << actionCode;
      73            0 :         m_notRegistered.send(actionCode);
      74            0 :         return;
      75              :     }
      76              : 
      77            0 :     doDispatch(it->second, actionCode, data);
      78            0 : }
      79              : 
      80            0 : void ActionsDispatcher::dispatch(const ActionQuery& actionQuery)
      81              : {
      82            0 :     if (muse::strings::startsWith(actionQuery.uri().scheme(), rcommand::COMMAND_SCHEME)) {
      83            0 :         commandDispatcher()->dispatch(actionQuery);
      84            0 :         return;
      85              :     }
      86              : 
      87              :     //! NOTE Try find full query
      88            0 :     const std::string full = actionQuery.toString();
      89            0 :     ActionCode code = full;
      90            0 :     auto it = m_clients.find(code);
      91            0 :     if (it == m_clients.end()) {
      92              :         //! Try find just uri
      93            0 :         code = actionQuery.uri().toString();
      94            0 :         it = m_clients.find(code);
      95            0 :         if (it == m_clients.end()) {
      96            0 :             LOGW() << "not a registered action: '" << code << "'";
      97            0 :             m_notRegistered.send(code);
      98              :             //dump();
      99            0 :             return;
     100              :         }
     101              :     }
     102              : 
     103            0 :     ActionData data;
     104            0 :     data.setArg<std::string>(0, full);
     105            0 :     doDispatch(it->second, code, data);
     106            0 : }
     107              : 
     108            0 : void ActionsDispatcher::dump() const
     109              : {
     110            0 :     std::stringstream s;
     111            0 :     for (const auto& p : m_clients) {
     112            0 :         s << "'" << p.first << "'\n";
     113              :     }
     114              : 
     115            0 :     LOGDA() << "\n" << s.str();
     116            0 : }
     117              : 
     118            0 : void ActionsDispatcher::doDispatch(const Clients& clients, const ActionCode& actionCode, const ActionData& data)
     119              : {
     120            0 :     int canReceiveCount = 0;
     121            0 :     for (auto cit = clients.cbegin(); cit != clients.cend(); ++cit) {
     122            0 :         const Actionable* client = cit->first;
     123            0 :         if (client->canReceiveAction(actionCode)) {
     124            0 :             ++canReceiveCount;
     125            0 :             const CallBacks& callbacks = cit->second;
     126            0 :             auto cbit = callbacks.find(actionCode);
     127            0 :             IF_ASSERT_FAILED(cbit != callbacks.end()) {
     128            0 :                 continue;
     129              :             }
     130              : 
     131              :             // Notify that we're about to perform an action.
     132            0 :             m_preDispatch.send(actionCode);
     133              : 
     134              :             // Perform the action.
     135            0 :             const ActionCallBackWithNameAndData& callback = cbit->second;
     136            0 :             LOGI() << "try call action: " << actionCode;
     137            0 :             callback(actionCode, data);
     138              :         }
     139              :     }
     140              : 
     141            0 :     if (canReceiveCount == 0) {
     142            0 :         LOGI() << "no one can handle the action: " << actionCode;
     143            0 :         return;
     144              :     }
     145              : 
     146            0 :     if (canReceiveCount > 1) {
     147            0 :         LOGW() << "More than one client can handle the action, this is not a typical situation.";
     148              :     }
     149              : 
     150              :     // Notify that we have performed an action.
     151            0 :     m_postDispatch.send(actionCode);
     152              : }
     153              : 
     154            0 : Channel<ActionCode> ActionsDispatcher::preDispatch() const
     155              : {
     156            0 :     return m_preDispatch;
     157              : }
     158              : 
     159            0 : Channel<ActionCode> ActionsDispatcher::postDispatch() const
     160              : {
     161            0 :     return m_postDispatch;
     162              : }
     163              : 
     164            0 : Channel<ActionCode> ActionsDispatcher::notRegistered() const
     165              : {
     166            0 :     return m_notRegistered;
     167              : }
     168              : 
     169            0 : void ActionsDispatcher::unReg(Actionable* client)
     170              : {
     171            0 :     for (auto it = m_clients.begin(); it != m_clients.end(); ++it) {
     172            0 :         Clients& clients = it->second;
     173            0 :         clients.erase(client);
     174              :     }
     175            0 :     client->setDispatcher(nullptr);
     176            0 : }
     177              : 
     178          512 : void ActionsDispatcher::reg(Actionable* client, const ActionCode& actionCode, const ActionCallBackWithNameAndData& call)
     179              : {
     180          512 :     client->setDispatcher(this);
     181              : 
     182          512 :     Clients& clients = m_clients[actionCode];
     183          512 :     CallBacks& callbacks = clients[client];
     184          512 :     callbacks.insert({ actionCode, call });
     185          512 : }
     186              : 
     187            0 : void ActionsDispatcher::reg(Actionable* client, const ActionQuery& actionQuery, const ActionCallBackWithQuery& call)
     188              : {
     189            0 :     reg(client, actionQuery.toString(), [call](const ActionCode&, const ActionData& data) {
     190            0 :         call(ActionQuery(data.arg<std::string>(0)));
     191            0 :     });
     192            0 : }
     193              : 
     194            0 : bool ActionsDispatcher::isReg(Actionable* client) const
     195              : {
     196            0 :     return client->isDispatcher(this);
     197              : }
     198              : 
     199            0 : ActionCodeList ActionsDispatcher::actionList() const
     200              : {
     201            0 :     ActionCodeList list;
     202            0 :     list.reserve(m_clients.size());
     203            0 :     for (const auto& p : m_clients) {
     204            0 :         list.push_back(p.first);
     205              :     }
     206            0 :     return list;
     207            0 : }
        

Generated by: LCOV version 2.0-1