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 :
23 : #pragma once
24 :
25 : #include <map>
26 : #include <optional>
27 : #include <unordered_set>
28 : #include <vector>
29 : #include <QPointF>
30 : #include <QQuickItem>
31 :
32 : #include "project/iprojectsession.h"
33 : #include "async/asyncable.h"
34 : #include "ui/iuiconfiguration.h"
35 : #include "ui/iuicontextconfiguration.h"
36 : #include "notation/notationtypes.h"
37 : #include "notation/inotationconfiguration.h"
38 : #include "notation/inotationcontextconfiguration.h"
39 : #include "engraving/automation/automationdata.h"
40 : #include "engraving/automation/automationtypes.h"
41 :
42 : namespace muse::uicomponents {
43 : class PolylinePlot;
44 : }
45 :
46 : namespace mu::engraving {
47 : class Staff;
48 : struct ScoreChanges;
49 : }
50 :
51 : namespace mu::notation {
52 : class NotationAutomationController : public muse::Contextable, public muse::async::Asyncable
53 : {
54 : muse::ContextInject<mu::project::IProjectSession> projectSession = { this };
55 : muse::ContextInject<muse::ui::IUiContextConfiguration> uiContextConfiguration = { this };
56 : muse::GlobalInject<muse::ui::IUiConfiguration> uiConfiguration;
57 : muse::GlobalInject<INotationConfiguration> notationConfiguration;
58 : muse::ContextInject<INotationContextConfiguration> notationContextConfiguration = { this };
59 : muse::GlobalInject<mu::engraving::IEngravingConfiguration> engravingConfiguration;
60 :
61 : public:
62 : NotationAutomationController(QQuickItem* linesParent, const muse::modularity::ContextPtr& iocCtx);
63 :
64 : void init();
65 : void setViewMatrix(const muse::draw::Transform& viewMatrix);
66 :
67 : private:
68 : // Necessary since SysStaff doesn't hold a reference to its system, which is needed
69 : // for calculating a SysStaff's relative position...
70 : struct SysStaffKey {
71 : const System* system = nullptr;
72 : const staff_idx_t staffIdx = muse::nidx;
73 :
74 0 : bool isValid() const
75 : {
76 0 : return system && !system->measures().empty() && staffIdx != muse::nidx;
77 : }
78 :
79 : bool operator==(const SysStaffKey& k) const
80 : {
81 : IF_ASSERT_FAILED(isValid() && k.isValid()) {
82 : return false;
83 : }
84 : return system == k.system && staffIdx == k.staffIdx;
85 : }
86 :
87 0 : bool operator<(const SysStaffKey& k) const
88 : {
89 0 : IF_ASSERT_FAILED(isValid() && k.isValid()) {
90 0 : return false;
91 : }
92 0 : if (system == k.system) {
93 0 : return staffIdx < k.staffIdx;
94 : }
95 0 : return system->first()->index() < k.system->first()->index();
96 : }
97 : };
98 :
99 : using PolylinesSet = std::unordered_set<muse::uicomponents::PolylinePlot*>;
100 : using SysStaffToPolylinesMap = std::map<const SysStaffKey, const PolylinesSet>;
101 :
102 : struct PointData {
103 : enum class PointType : unsigned char {
104 : UNKNOWN,
105 : IN,
106 : OUT,
107 : BOTH
108 : };
109 : int polylinePointIndex = -1;
110 : int tick = -1;
111 : QPointF qPointF;
112 : PointType pointType = PointType::UNKNOWN;
113 : };
114 :
115 : using PointsDataMap = std::map<SysStaffKey, QVector<PointData> >;
116 :
117 : struct TickStaffRange {
118 : int tickFrom = -1;
119 : int tickTo = -1;
120 : staff_idx_t staffIdxFrom = muse::nidx;
121 : staff_idx_t staffIdxTo = muse::nidx;
122 : };
123 :
124 : struct PendingScoreState {
125 : bool hasChanges = false;
126 : bool structural = false;
127 : std::optional<TickStaffRange> boundary;
128 : };
129 :
130 : SysStaffToPolylinesMap createPolylinesForSystem(const System* system);
131 : muse::uicomponents::PolylinePlot* createPolylineForStaff(const System* system, staff_idx_t staffIdx);
132 : QVector<PointData> pointsDataInStaff(const mu::engraving::Staff* staff, const muse::RectF& sysStaffCanvasRect, int startTick,
133 : int endTick) const;
134 :
135 : mu::engraving::AutomationType currentAutomationType() const;
136 :
137 : void applyPolylineStyle(muse::uicomponents::PolylinePlot* polyline, const SysStaffKey& key) const;
138 : void applyPolylineColors(muse::uicomponents::PolylinePlot* polyline, const SysStaffKey& key) const;
139 : // TODO: apply within a range? (for efficiency)
140 : void applyPolylineColorsUnderLine(muse::uicomponents::PolylinePlot* polyline, const SysStaffKey& key) const;
141 :
142 : QColor inversionRelativeColor(const muse::ui::ThemeStyleKey& key) const;
143 :
144 : void updatePolylinesGeometry();
145 : void updatePolylinesColors();
146 : void onCurrentNotationChanged();
147 : void rebuildAllPolylines();
148 :
149 : void updateStaffPointsInRange(const SysStaffKey& key, int tickFrom, int tickTo);
150 :
151 : void mergePendingChanges(const mu::engraving::AutomationChanges& changes);
152 : void mergePendingScoreChanges(const mu::engraving::ScoreChanges& changes);
153 : void scheduleUpdate();
154 : void processPendingChanges();
155 : void applyAutomationChanges(const mu::engraving::AutomationChanges& changes);
156 :
157 : bool requestEditPoint(const PointData& oldPointData, const SysStaffKey& key, qreal x, qreal y);
158 : bool requestAddPoint(const SysStaffKey& key, qreal x, qreal y);
159 : bool requestRemovePoint(const PointData& pointData, const SysStaffKey& key);
160 : void editAutomationPoints(const mu::engraving::AutomationCurveKey& key, mu::engraving::AutomationPointEdits& edits);
161 :
162 : const mu::engraving::AutomationPoint* automationPointAt(const SysStaffKey& key, int tick) const;
163 :
164 : INotationAutomationPtr automation() const;
165 : mu::engraving::AutomationDataConstPtr automationData() const;
166 : INotationPtr currentNotation() const;
167 : mu::engraving::Score* score() const;
168 :
169 : QQuickItem* m_linesParent = nullptr;
170 :
171 : SysStaffToPolylinesMap m_stavesToLinesMap;
172 : PointsDataMap m_pointsDataByStaff;
173 :
174 : muse::draw::Transform m_viewMatrix;
175 :
176 : mu::engraving::AutomationChanges m_pendingChanges;
177 : PendingScoreState m_pendingScoreState;
178 : bool m_updateScheduled = false;
179 :
180 : muse::uicomponents::PolylinePlot* m_currentlyEditedPolyline = nullptr;
181 : };
182 : }
|