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 :
23 : #include "notationpagemodel.h"
24 :
25 : #include "async/async.h"
26 : #include "log.h"
27 :
28 : #include "engraving/dom/part.h"
29 : #include "engraving/dom/score.h"
30 : #include "engraving/dom/staff.h"
31 :
32 : #include "notation/inotation.h"
33 : #include "notation/inotationelements.h" // IWYU pragma: keep
34 : #include "notation/inotationinteraction.h"
35 : #include "notation/inotationnoteinput.h"
36 : #include "notation/inotationselection.h"
37 :
38 : #include "dockwindow/idockwindow.h"
39 : #include "dockwindow_v2/dockcommands.h"
40 :
41 : using namespace muse;
42 : using namespace mu::appshell;
43 : using namespace mu::notation;
44 : using namespace mu::engraving;
45 :
46 0 : NotationPageModel::NotationPageModel(QObject* parent)
47 0 : : QObject(parent), muse::Contextable(muse::iocCtxForQmlObject(this))
48 : {
49 0 : }
50 :
51 0 : bool NotationPageModel::isNavigatorVisible() const
52 : {
53 0 : return appShellState()->isNotationNavigatorVisible();
54 : }
55 :
56 0 : bool NotationPageModel::isBraillePanelVisible() const
57 : {
58 0 : return brailleConfiguration()->braillePanelEnabled();
59 : }
60 :
61 0 : void NotationPageModel::init()
62 : {
63 0 : TRACEFUNC;
64 :
65 0 : if (m_inited) {
66 0 : return;
67 : }
68 :
69 0 : commandsController()->dockToggleRequested().onReceive(this, [this](const DockName& dockName) {
70 0 : toggleDock(dockName);
71 0 : });
72 :
73 0 : projectSession()->currentNotationChanged().onNotify(this, [this]() {
74 0 : onNotationChanged();
75 0 : scheduleUpdatePercussionPanelVisibility();
76 0 : });
77 :
78 0 : extensionsProvider()->manifestListChanged().onNotify(this, [this]() {
79 0 : scheduleUpdateExtensionsToolBarVisibility();
80 0 : });
81 :
82 0 : extensionsProvider()->enabledChanged().onReceive(this, [this](const muse::Uri&) {
83 0 : scheduleUpdateExtensionsToolBarVisibility();
84 0 : });
85 :
86 0 : brailleConfiguration()->braillePanelEnabledChanged().onNotify(this, [this]() {
87 0 : emit isBraillePanelVisibleChanged();
88 0 : });
89 :
90 0 : onNotationChanged();
91 :
92 0 : scheduleUpdatePercussionPanelVisibility();
93 0 : scheduleUpdateExtensionsToolBarVisibility();
94 :
95 0 : notationSceneConfiguration()->percussionPanelAutoShowModeChanged().onNotify(this, [this]() {
96 0 : scheduleUpdatePercussionPanelVisibility();
97 0 : });
98 :
99 0 : m_inited = true;
100 0 : }
101 :
102 0 : QString NotationPageModel::notationToolBarName() const
103 : {
104 0 : return NOTATION_TOOLBAR_NAME;
105 : }
106 :
107 0 : QString NotationPageModel::playbackToolBarName() const
108 : {
109 0 : return PLAYBACK_TOOLBAR_NAME;
110 : }
111 :
112 0 : QString NotationPageModel::undoRedoToolBarName() const
113 : {
114 0 : return UNDO_REDO_TOOLBAR_NAME;
115 : }
116 :
117 0 : QString NotationPageModel::noteInputBarName() const
118 : {
119 0 : return NOTE_INPUT_BAR_NAME;
120 : }
121 :
122 0 : QString NotationPageModel::extensionsToolBarName() const
123 : {
124 0 : return EXTENSIONS_TOOLBAR_NAME;
125 : }
126 :
127 0 : QString NotationPageModel::palettesPanelName() const
128 : {
129 0 : return PALETTES_PANEL_NAME;
130 : }
131 :
132 0 : QString NotationPageModel::layoutPanelName() const
133 : {
134 0 : return LAYOUT_PANEL_NAME;
135 : }
136 :
137 0 : QString NotationPageModel::propertiesPanelName() const
138 : {
139 0 : return PROPERTIES_PANEL_NAME;
140 : }
141 :
142 0 : QString NotationPageModel::selectionFiltersPanelName() const
143 : {
144 0 : return SELECTION_FILTERS_PANEL_NAME;
145 : }
146 :
147 0 : QString NotationPageModel::undoHistoryPanelName() const
148 : {
149 0 : return UNDO_HISTORY_PANEL_NAME;
150 : }
151 :
152 0 : QString NotationPageModel::mixerPanelName() const
153 : {
154 0 : return MIXER_PANEL_NAME;
155 : }
156 :
157 0 : QString NotationPageModel::pianoKeyboardPanelName() const
158 : {
159 0 : return PIANO_KEYBOARD_PANEL_NAME;
160 : }
161 :
162 0 : QString NotationPageModel::timelinePanelName() const
163 : {
164 0 : return TIMELINE_PANEL_NAME;
165 : }
166 :
167 0 : QString NotationPageModel::percussionPanelName() const
168 : {
169 0 : return PERCUSSION_PANEL_NAME;
170 : }
171 :
172 0 : QString NotationPageModel::statusBarName() const
173 : {
174 0 : return NOTATION_STATUSBAR_NAME;
175 : }
176 :
177 0 : void NotationPageModel::onNotationChanged()
178 : {
179 0 : INotationPtr notation = projectSession()->currentNotation();
180 0 : if (!notation) {
181 0 : return;
182 : }
183 :
184 0 : INotationNoteInputPtr noteInput = notation->interaction()->noteInput();
185 0 : noteInput->stateChanged().onNotify(this, [this]() {
186 0 : scheduleUpdatePercussionPanelVisibility();
187 0 : }, Asyncable::Mode::SetReplace /* FIXME */);
188 :
189 0 : INotationInteractionPtr notationInteraction = notation->interaction();
190 0 : notationInteraction->selectionChanged().onNotify(this, [this]() {
191 0 : scheduleUpdatePercussionPanelVisibility();
192 0 : }, Asyncable::Mode::SetReplace /* FIXME */);
193 0 : }
194 :
195 0 : void NotationPageModel::toggleDock(const QString& name)
196 : {
197 0 : if (name == NOTATION_NAVIGATOR_PANEL_NAME) {
198 0 : appShellState()->setIsNotationNavigatorVisible(!isNavigatorVisible());
199 0 : emit isNavigatorVisibleChanged();
200 0 : return;
201 : }
202 :
203 0 : if (name == NOTATION_BRAILLE_PANEL_NAME) {
204 0 : brailleConfiguration()->setBraillePanelEnabled(!isBraillePanelVisible());
205 0 : emit isBraillePanelVisibleChanged();
206 0 : return;
207 : }
208 :
209 0 : commandDispatcher()->dispatch(dock::DOCK_TOGGLE_COMMAND, { { "dock_name", muse::Val(name) } });
210 0 : }
211 :
212 0 : void NotationPageModel::scheduleUpdatePercussionPanelVisibility()
213 : {
214 0 : if (m_updatePercussionPanelVisibilityScheduled) {
215 0 : return;
216 : }
217 :
218 0 : m_updatePercussionPanelVisibilityScheduled = true;
219 :
220 : //! NOTE: ensure we don't update it multiple times in succession
221 0 : muse::async::Async::call(this, [this]() {
222 0 : doUpdatePercussionPanelVisibility();
223 0 : m_updatePercussionPanelVisibilityScheduled = false;
224 0 : });
225 : }
226 :
227 0 : void NotationPageModel::doUpdatePercussionPanelVisibility()
228 : {
229 0 : TRACEFUNC;
230 :
231 : //! NOTE: If the user is entering percussion notes with the piano keyboard, we can assume that they
232 : //! don't want the percussion panel to auto-show...
233 0 : const muse::dock::IDockWindow* window = dockWindowProvider()->window();
234 0 : if (!window || window->isDockOpen(PIANO_KEYBOARD_PANEL_NAME)) {
235 0 : return;
236 : }
237 :
238 0 : auto setPercussionPanelOpen = [this, window](bool open) {
239 0 : if (open == window->isDockOpen(PERCUSSION_PANEL_NAME)) {
240 0 : return;
241 : }
242 :
243 0 : commandDispatcher()->dispatch(dock::DOCK_SET_OPEN_COMMAND,
244 0 : { { "dock_name", muse::Val(PERCUSSION_PANEL_NAME) }, { "open", muse::Val(open) } });
245 0 : };
246 :
247 0 : const PercussionPanelAutoShowMode autoShowMode = notationSceneConfiguration()->percussionPanelAutoShowMode();
248 0 : const INotationPtr notation = projectSession()->currentNotation();
249 0 : if (!notation || !notation->elements() || autoShowMode == PercussionPanelAutoShowMode::NEVER) {
250 0 : return;
251 : }
252 :
253 0 : const INotationNoteInputPtr noteInput = notation->interaction()->noteInput();
254 0 : const bool autoClose = notationSceneConfiguration()->autoClosePercussionPanel();
255 0 : if (noteInput && !noteInput->isNoteInputMode() && autoShowMode == PercussionPanelAutoShowMode::UNPITCHED_STAFF_NOTE_INPUT) {
256 0 : if (autoClose) {
257 0 : setPercussionPanelOpen(false);
258 : }
259 0 : return;
260 : }
261 :
262 0 : const Score* score = notation->elements()->msScore();
263 0 : if (score) {
264 0 : const InputState& inputState = score->inputState();
265 0 : const Staff* staff = inputState.staff();
266 0 : if (inputState.noteEntryMode() && staff && staff->isDrumStaff(inputState.tick())) {
267 0 : setPercussionPanelOpen(true);
268 0 : return;
269 : }
270 : }
271 :
272 0 : const INotationSelectionPtr selection = notation->interaction()->selection();
273 0 : if (!score || !selection || selection->isNone()) {
274 0 : if (autoClose) {
275 0 : setPercussionPanelOpen(false);
276 : }
277 0 : return;
278 : }
279 :
280 0 : if (selection->isRange()) {
281 0 : const INotationSelectionRangePtr rangeSelection = selection->range();
282 0 : if (!rangeSelection) {
283 0 : if (autoClose) {
284 0 : setPercussionPanelOpen(false);
285 : }
286 0 : return;
287 : }
288 0 : for (const Part* p : rangeSelection->selectedParts()) {
289 0 : if (p->hasDrumStaff()) {
290 0 : continue;
291 : }
292 0 : if (autoClose) {
293 0 : setPercussionPanelOpen(false);
294 : }
295 0 : return;
296 0 : }
297 0 : } else {
298 0 : for (const EngravingItem* e : selection->elements()) {
299 0 : const Staff* staff = e->staff();
300 0 : if (staff && staff->isDrumStaff(e->tick())) {
301 0 : continue;
302 : }
303 0 : if (autoClose) {
304 0 : setPercussionPanelOpen(false);
305 : }
306 0 : return;
307 : }
308 : }
309 :
310 0 : setPercussionPanelOpen(true);
311 0 : }
312 :
313 0 : void NotationPageModel::scheduleUpdateExtensionsToolBarVisibility()
314 : {
315 0 : if (m_updateExtensionsToolBarVisibilityScheduled) {
316 0 : return;
317 : }
318 :
319 0 : m_updateExtensionsToolBarVisibilityScheduled = true;
320 :
321 : //! NOTE: ensure we don't update it multiple times in succession
322 0 : muse::async::Async::call(this, [this]() {
323 0 : doUpdateExtensionsToolBarVisibility();
324 0 : m_updateExtensionsToolBarVisibilityScheduled = false;
325 0 : });
326 : }
327 :
328 0 : void NotationPageModel::doUpdateExtensionsToolBarVisibility()
329 : {
330 0 : const muse::dock::IDockWindow* window = dockWindowProvider()->window();
331 0 : if (!window) {
332 0 : return;
333 : }
334 :
335 0 : auto setExtensionsToolBarOpen = [this, window](bool open) {
336 0 : if (open == window->isDockOpen(EXTENSIONS_TOOLBAR_NAME)) {
337 0 : return;
338 : }
339 :
340 0 : commandDispatcher()->dispatch(dock::DOCK_SET_OPEN_COMMAND,
341 0 : { { "dock_name", muse::Val(EXTENSIONS_TOOLBAR_NAME) }, { "open", muse::Val(open) } });
342 0 : };
343 :
344 0 : muse::extensions::ManifestList enabledExtensions = extensionsProvider()->manifestList(muse::extensions::Filter::Enabled);
345 0 : for (const muse::extensions::Manifest& m : enabledExtensions) {
346 0 : for (const muse::extensions::Action& a : m.actions) {
347 0 : if (!a.showOnToolbar) {
348 0 : continue;
349 : }
350 :
351 0 : setExtensionsToolBarOpen(true);
352 0 : return;
353 : }
354 : }
355 :
356 0 : setExtensionsToolBarOpen(false);
357 0 : }
|