LCOV - code coverage report
Current view: top level - src/appshell/internal - shortcutresolver.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 78 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) 2026 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 "shortcutresolver.h"
      23              : 
      24              : #include <functional>
      25              : 
      26              : #include "global/containers.h"
      27              : 
      28              : #include "log.h"
      29              : 
      30              : using namespace muse;
      31              : using namespace muse::shortcuts;
      32              : using namespace mu::appshell;
      33              : 
      34              : static const std::string NAVIGATION_SCOPE = "NAVIGATION";
      35              : static const std::string PLAYBACK_SCOPE = "PLAYBACK";
      36              : static const std::string NOTATION_PAGE_SCOPE = "NOTATION_PAGE";
      37              : static const std::string NOTATION_SCOPE = "NOTATION";
      38              : static const std::string NOTATION_TAB_SCOPE = "NOTATION_TAB";
      39              : static const std::string NOTATION_SELECTION_SCOPE = "NOTATION_SELECTION";
      40              : static const std::string NOTATION_SINGLE_SELECTION_SCOPE = "NOTATION_SINGLE_SELECTION";
      41              : static const std::string NOTATION_RANGE_SELECTION_SCOPE = "NOTATION_RANGE_SELECTION";
      42              : static const std::string NOTATION_NOTE_INPUT_SCOPE = "NOTATION_NOTE_INPUT";
      43              : static const std::string NOTATION_TEXT_EDITING_SCOPE = "NOTATION_TEXT_EDITING";
      44              : static const std::string PROJECT_SCOPE = "PROJECT";
      45              : static const std::string APP_SCOPE = "APP";
      46              : static const std::string PALETTE_SCOPE = "PALETTE";
      47              : static const std::string INSTRUMENTS_SCOPE = "INSTRUMENTS";
      48              : static const std::string EXTENSIONS_SCOPE = "EXTENSIONS";
      49              : static const std::string AUDIO_SCOPE = "AUDIO";
      50              : static const std::string DOCK_SCOPE = "DOCK";
      51              : static const std::string WORKSPACE_SCOPE = "WORKSPACE";
      52              : static const std::string UPDATE_SCOPE = "UPDATE";
      53              : static const std::string DIAGNOSTICS_SCOPE = "DIAGNOSTICS";
      54              : static const std::string TESTFLOW_SCOPE = "TESTFLOW";
      55              : 
      56              : static const QString NOTATION_PANEL_NAME("ScoreView");
      57              : 
      58            0 : int ShortcutResolver::scopePriority(const std::string& scope) const
      59              : {
      60            0 :     if (m_scopePriorityMap.empty()) {
      61            0 :         m_scopePriorityMap = {
      62            0 :             { NAVIGATION_SCOPE, []() { return 1; } },
      63            0 :             { PLAYBACK_SCOPE, []() { return 2; } },
      64            0 :             { NOTATION_PAGE_SCOPE, []() { return 3; } },
      65            0 :             { NOTATION_SCOPE, [this]() { return isNotationFocused() ? 4 : -1; } },
      66            0 :             { NOTATION_TAB_SCOPE, [this]() { return isNotationFocusedAndTabStaff() ? 5 : -1; } },
      67            0 :             { NOTATION_SELECTION_SCOPE, [this]() { return isNotationFocusedAndHasSelection() ? 6 : -1; } },
      68            0 :             { NOTATION_SINGLE_SELECTION_SCOPE, [this]() { return isNotationFocusedAndSingleSelection() ? 7 : -1; } },
      69            0 :             { NOTATION_RANGE_SELECTION_SCOPE, [this]() { return isNotationFocusedAndRangeSelection() ? 7 : -1; } },
      70            0 :             { NOTATION_NOTE_INPUT_SCOPE, [this]() { return isNotationFocusedAndNoteInputMode() ? 8 : -1; } },
      71            0 :             { NOTATION_TEXT_EDITING_SCOPE, [this]() { return isNotationFocusedAndTextEditing() ? 9 : -1; } },
      72            0 :         };
      73              :     }
      74              : 
      75            0 :     auto priority = muse::value(m_scopePriorityMap, scope, nullptr);
      76            0 :     if (priority) {
      77            0 :         return priority();
      78              :     }
      79            0 :     LOGD() << "Unknown scope: " << scope;
      80            0 :     return 0;
      81            0 : }
      82              : 
      83            0 : bool ShortcutResolver::isNotationFocused() const
      84              : {
      85            0 :     auto activePanel = navigationController()->activePanel();
      86            0 :     return activePanel && activePanel->name() == NOTATION_PANEL_NAME;
      87              : }
      88              : 
      89            0 : bool ShortcutResolver::isNotationFocusedAndNoteInputMode() const
      90              : {
      91            0 :     return isNotationFocused() && notationCommandsController()->isNoteInputMode();
      92              : }
      93              : 
      94            0 : bool ShortcutResolver::isNotationFocusedAndTabStaff() const
      95              : {
      96            0 :     return isNotationFocused() && notationCommandsController()->isTablatureStaff();
      97              : }
      98              : 
      99            0 : bool ShortcutResolver::isNotationFocusedAndHasSelection() const
     100              : {
     101            0 :     return isNotationFocused() && notationCommandsController()->hasSelection();
     102              : }
     103              : 
     104            0 : bool ShortcutResolver::isNotationFocusedAndSingleSelection() const
     105              : {
     106            0 :     return isNotationFocused() && notationCommandsController()->isSingleSelection();
     107              : }
     108              : 
     109            0 : bool ShortcutResolver::isNotationFocusedAndRangeSelection() const
     110              : {
     111            0 :     return isNotationFocused() && notationCommandsController()->isRangeSelection();
     112              : }
     113              : 
     114            0 : bool ShortcutResolver::isNotationFocusedAndTextEditing() const
     115              : {
     116            0 :     return isNotationFocused() && notationCommandsController()->isTextEditing();
     117              : }
     118              : 
     119            0 : Shortcut ShortcutResolver::selectActive(const ShortcutList& list) const
     120              : {
     121            0 :     IF_ASSERT_FAILED(list.size() > 0) {
     122            0 :         return Shortcut();
     123              :     }
     124              : 
     125            0 :     ShortcutList sorted = list;
     126            0 :     std::stable_sort(sorted.begin(), sorted.end(), [this](const Shortcut& a, const Shortcut& b) {
     127            0 :         if (a.scope != b.scope) {
     128            0 :             return scopePriority(a.scope) > scopePriority(b.scope);
     129              :         }
     130            0 :         return a.command != rcommand::NONE_COMMAND && b.command == rcommand::NONE_COMMAND;
     131              :     });
     132              : 
     133            0 :     Shortcut probableShortcut = sorted.front();
     134            0 :     if (scopePriority(probableShortcut.scope) < 0) {
     135              :         //! NOTE If the probable shortcut is not active, return an invalid shortcut.
     136            0 :         return Shortcut();
     137              :     }
     138              : 
     139            0 :     return probableShortcut;
     140            0 : }
     141              : 
     142            0 : muse::TranslatableString ShortcutResolver::scopeTitle(const std::string& scopeCode) const
     143              : {
     144              :     const static std::map<std::string, TranslatableString> scopeTitles = {
     145            0 :         { NAVIGATION_SCOPE, TranslatableString("shortcuts", "NAVIGATION") },
     146            0 :         { PLAYBACK_SCOPE, TranslatableString("shortcuts", "PLAYBACK") },
     147            0 :         { NOTATION_PAGE_SCOPE, TranslatableString("shortcuts", "NOTATION PAGE") },
     148            0 :         { NOTATION_SCOPE, TranslatableString("shortcuts", "NOTATION") },
     149            0 :         { NOTATION_TAB_SCOPE, TranslatableString("shortcuts", "NOTATION: TAB") },
     150            0 :         { NOTATION_SELECTION_SCOPE, TranslatableString("shortcuts", "NOTATION: SELECTION") },
     151            0 :         { NOTATION_SINGLE_SELECTION_SCOPE, TranslatableString("shortcuts", "NOTATION: SINGLE SELECTION") },
     152            0 :         { NOTATION_RANGE_SELECTION_SCOPE, TranslatableString("shortcuts", "NOTATION: RANGE SELECTION") },
     153            0 :         { NOTATION_NOTE_INPUT_SCOPE, TranslatableString("shortcuts", "NOTATION: NOTE INPUT") },
     154            0 :         { NOTATION_TEXT_EDITING_SCOPE, TranslatableString("shortcuts", "NOTATION: TEXT EDITING") },
     155            0 :         { PROJECT_SCOPE, TranslatableString("shortcuts", "PROJECT") },
     156            0 :         { APP_SCOPE, TranslatableString("shortcuts", "APP") },
     157            0 :         { PALETTE_SCOPE, TranslatableString("shortcuts", "PALETTE") },
     158            0 :         { INSTRUMENTS_SCOPE, TranslatableString("shortcuts", "INSTRUMENTS") },
     159            0 :         { EXTENSIONS_SCOPE, TranslatableString("shortcuts", "EXTENSIONS") },
     160            0 :         { AUDIO_SCOPE, TranslatableString("shortcuts", "AUDIO") },
     161            0 :         { DOCK_SCOPE, TranslatableString("shortcuts", "DOCK") },
     162            0 :         { WORKSPACE_SCOPE, TranslatableString("shortcuts", "WORKSPACE") },
     163            0 :         { UPDATE_SCOPE, TranslatableString("shortcuts", "UPDATE") },
     164            0 :         { DIAGNOSTICS_SCOPE, TranslatableString("shortcuts", "DIAGNOSTICS") },
     165            0 :         { TESTFLOW_SCOPE, TranslatableString("shortcuts", "TESTFLOW") },
     166            0 :     };
     167              : 
     168            0 :     const std::string upperScopeCode = strings::toUpper(scopeCode);
     169              : 
     170            0 :     auto it = scopeTitles.find(upperScopeCode);
     171            0 :     if (it != scopeTitles.end()) {
     172            0 :         return it->second;
     173              :     }
     174            0 :     LOGW() << "Unknown scope: " << scopeCode;
     175            0 :     return TranslatableString::untranslatable(String::fromStdString(upperScopeCode));
     176            0 : }
        

Generated by: LCOV version 2.0-1