LCOV - code coverage report
Current view: top level - muse/framework/midiremote/qml/Muse/MidiRemote - mididevicemappingmodel.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 141 0
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              : 
      23              : #include "mididevicemappingmodel.h"
      24              : 
      25              : #include "rcommand/commandtypes.h"
      26              : #include "translation.h"
      27              : 
      28              : #include "log.h"
      29              : #include <vector>
      30              : 
      31              : using namespace muse::midi;
      32              : using namespace muse::midiremote;
      33              : using namespace muse::rcommand;
      34              : 
      35              : static const QString TITLE_KEY("title");
      36              : static const QString ICON_KEY("icon");
      37              : static const QString STATUS_KEY("status");
      38              : static const QString ENABLED_KEY("enabled");
      39              : static const QString MAPPED_TYPE_KEY("mappedType");
      40              : static const QString MAPPED_VALUE_KEY("mappedValue");
      41              : 
      42            0 : inline static std::vector<Command> allMidiCommands()
      43              : {
      44              :     //! TODO Move to config
      45              :     return {
      46            0 :         Command("command://playback/rewind"),
      47            0 :         Command("command://playback/loop-toggle"),
      48            0 :         Command("command://playback/play"),
      49            0 :         Command("command://playback/stop"),
      50            0 :         Command("command://notation/toggle-note-input"),
      51            0 :         Command("command://notation/set-duration-whole"),
      52            0 :         Command("command://notation/set-duration-half"),
      53            0 :         Command("command://notation/set-duration-quarter"),
      54            0 :         Command("command://notation/set-duration-eighth"),
      55            0 :         Command("command://notation/set-duration-16th"),
      56            0 :         Command("command://notation/set-duration-32nd"),
      57            0 :         Command("command://notation/set-duration-64th"),
      58            0 :         Command("command://notation/undo"),
      59            0 :         Command("command://notation/enter-rest"),
      60            0 :         Command("command://notation/toggle-rest"),
      61            0 :         Command("command://notation/toggle-tie"),
      62            0 :         Command("command://notation/toggle-dot"),
      63            0 :         Command("command://notation/toggle-dot2"),
      64            0 :         Command("command://notation/realtime-advance")
      65            0 :     };
      66            0 : }
      67              : 
      68            0 : MidiDeviceMappingModel::MidiDeviceMappingModel(QObject* parent)
      69            0 :     : QAbstractListModel(parent), Contextable(muse::iocCtxForQmlObject(this))
      70              : {
      71            0 : }
      72              : 
      73            0 : QVariant MidiDeviceMappingModel::data(const QModelIndex& index, int role) const
      74              : {
      75            0 :     if (!index.isValid()) {
      76            0 :         return QVariant();
      77              :     }
      78              : 
      79            0 :     QVariantMap obj = midiMappingToObject(m_midiMappings[index.row()]);
      80              : 
      81            0 :     switch (role) {
      82            0 :     case RoleTitle: return obj[TITLE_KEY].toString();
      83            0 :     case RoleIcon: return obj[ICON_KEY].toInt();
      84            0 :     case RoleStatus: return obj[STATUS_KEY].toString();
      85            0 :     case RoleEnabled: return obj[ENABLED_KEY].toBool();
      86            0 :     case RoleMappedType: return obj[MAPPED_TYPE_KEY].toInt();
      87            0 :     case RoleMappedValue: return obj[MAPPED_VALUE_KEY].toInt();
      88              :     }
      89              : 
      90            0 :     return QVariant();
      91            0 : }
      92              : 
      93            0 : QVariantMap MidiDeviceMappingModel::midiMappingToObject(const MidiControlsMapping& midiMapping) const
      94              : {
      95            0 :     const CommandInfo& info = commandsRegister()->commandInfo(midiMapping.command);
      96              : 
      97            0 :     QVariantMap obj;
      98            0 :     obj[TITLE_KEY] = !info.description.isEmpty() ? info.description.qTranslated() : info.title.qTranslatedWithoutMnemonic();
      99            0 :     obj[ICON_KEY] = static_cast<int>(info.decoration.iconCode);
     100            0 :     obj[ENABLED_KEY] = midiMapping.isValid();
     101            0 :     obj[STATUS_KEY] = midiMapping.isValid() ? midiMapping.event.name().toQString() : muse::qtrc("global", "Inactive");
     102            0 :     obj[MAPPED_TYPE_KEY] = static_cast<int>(midiMapping.event.type);
     103            0 :     obj[MAPPED_VALUE_KEY] = midiMapping.event.value;
     104              : 
     105            0 :     return obj;
     106            0 : }
     107              : 
     108            0 : int MidiDeviceMappingModel::rowCount(const QModelIndex&) const
     109              : {
     110            0 :     return static_cast<int>(m_midiMappings.size());
     111              : }
     112              : 
     113            0 : QHash<int, QByteArray> MidiDeviceMappingModel::roleNames() const
     114              : {
     115              :     return {
     116            0 :         { RoleTitle, TITLE_KEY.toUtf8() },
     117            0 :         { RoleIcon, ICON_KEY.toUtf8() },
     118            0 :         { RoleStatus, STATUS_KEY.toUtf8() },
     119            0 :         { RoleEnabled, ENABLED_KEY.toUtf8() },
     120            0 :         { RoleMappedType, MAPPED_TYPE_KEY.toUtf8() },
     121            0 :         { RoleMappedValue, MAPPED_VALUE_KEY.toUtf8() }
     122            0 :     };
     123            0 : }
     124              : 
     125            0 : void MidiDeviceMappingModel::load()
     126              : {
     127            0 :     midiConfiguration()->useRemoteControlChanged().onReceive(this, [this](bool val) {
     128            0 :         emit useRemoteControlChanged(val);
     129            0 :     }, muse::async::Asyncable::Mode::SetReplace);
     130              : 
     131            0 :     beginResetModel();
     132            0 :     m_midiMappings.clear();
     133              : 
     134            0 :     const MidiMappingList& midiMappings = midiRemote()->midiMappings();
     135              : 
     136            0 :     auto remoteEvent = [&midiMappings](const Command& command) {
     137            0 :         for (const MidiControlsMapping& midiMapping : midiMappings) {
     138            0 :             if (midiMapping.command == command) {
     139            0 :                 return midiMapping.event;
     140              :             }
     141              :         }
     142              : 
     143            0 :         return RemoteEvent();
     144            0 :     };
     145              : 
     146            0 :     for (const Command& command : allMidiCommands()) {
     147            0 :         const CommandInfo& info = commandsRegister()->commandInfo(command);
     148              : 
     149            0 :         if (info.isValid()) {
     150            0 :             MidiControlsMapping midiMapping(command);
     151            0 :             midiMapping.event = remoteEvent(command);
     152            0 :             m_midiMappings.push_back(midiMapping);
     153            0 :         }
     154            0 :     }
     155              : 
     156            0 :     midiRemote()->midiMappingsChanged().onNotify(this, [this](){
     157            0 :         load();
     158            0 :     }, muse::async::Asyncable::Mode::SetReplace);
     159              : 
     160            0 :     endResetModel();
     161            0 : }
     162              : 
     163            0 : bool MidiDeviceMappingModel::apply()
     164              : {
     165            0 :     Ret ret = midiRemote()->setMidiMappings(m_midiMappings);
     166            0 :     if (!ret) {
     167            0 :         LOGE() << ret.toString();
     168              :     }
     169              : 
     170            0 :     return ret;
     171            0 : }
     172              : 
     173            0 : void MidiDeviceMappingModel::reset()
     174              : {
     175            0 :     midiRemote()->resetMidiMappings();
     176            0 : }
     177              : 
     178            0 : bool MidiDeviceMappingModel::useRemoteControl() const
     179              : {
     180            0 :     return midiConfiguration()->useRemoteControl();
     181              : }
     182              : 
     183            0 : void MidiDeviceMappingModel::setUseRemoteControl(bool value)
     184              : {
     185            0 :     if (value == useRemoteControl()) {
     186            0 :         return;
     187              :     }
     188              : 
     189            0 :     midiConfiguration()->setUseRemoteControl(value);
     190            0 :     emit useRemoteControlChanged(value);
     191              : }
     192              : 
     193            0 : QItemSelection MidiDeviceMappingModel::selection() const
     194              : {
     195            0 :     return m_selection;
     196              : }
     197              : 
     198            0 : bool MidiDeviceMappingModel::canEditAction() const
     199              : {
     200            0 :     return currentAction().isValid();
     201              : }
     202              : 
     203            0 : void MidiDeviceMappingModel::setSelection(const QItemSelection& selection)
     204              : {
     205            0 :     if (selection == m_selection) {
     206            0 :         return;
     207              :     }
     208              : 
     209            0 :     m_selection = selection;
     210            0 :     emit selectionChanged(selection);
     211              : }
     212              : 
     213            0 : void MidiDeviceMappingModel::clearSelectedActions()
     214              : {
     215            0 :     for (const QModelIndex& index : m_selection.indexes()) {
     216            0 :         m_midiMappings[index.row()].event = RemoteEvent();
     217            0 :         emit dataChanged(index, index);
     218            0 :     }
     219            0 : }
     220              : 
     221            0 : void MidiDeviceMappingModel::clearAllActions()
     222              : {
     223            0 :     beginResetModel();
     224              : 
     225            0 :     for (MidiControlsMapping& midiMapping: m_midiMappings) {
     226            0 :         midiMapping.event = RemoteEvent();
     227              :     }
     228              : 
     229            0 :     endResetModel();
     230            0 : }
     231              : 
     232            0 : QVariant MidiDeviceMappingModel::currentAction() const
     233              : {
     234            0 :     QModelIndexList indexes = m_selection.indexes();
     235            0 :     if (indexes.empty()) {
     236            0 :         return QVariant();
     237              :     }
     238              : 
     239            0 :     const MidiControlsMapping& midiMapping = m_midiMappings[indexes.first().row()];
     240            0 :     return midiMappingToObject(midiMapping);
     241            0 : }
     242              : 
     243            0 : void MidiDeviceMappingModel::mapCurrentActionToMidiEvent(const QVariant& event)
     244              : {
     245            0 :     QModelIndexList indexes = m_selection.indexes();
     246            0 :     if (indexes.empty()) {
     247            0 :         return;
     248              :     }
     249              : 
     250            0 :     QVariantMap eventMap = event.toMap();
     251            0 :     RemoteEventType type = static_cast<RemoteEventType>(eventMap["type"].toInt());
     252            0 :     int value = eventMap["value"].toInt();
     253              : 
     254            0 :     QModelIndex first = indexes.first();
     255              : 
     256            0 :     m_midiMappings[first.row()].event = RemoteEvent(type, value);
     257            0 :     emit dataChanged(first, first);
     258            0 : }
        

Generated by: LCOV version 2.0-1