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 "articulationpatternitem.h"
24 :
25 : #include <iterator>
26 :
27 : #include "internal/articulationstringutils.h"
28 :
29 : using namespace muse::mpe;
30 :
31 : static const QString SINGLE_NOTE_MENU_ID = "0";
32 : static const QString MULTI_NOTE_MENU_ID = "1";
33 :
34 : static const QString SINGLE_NOTE_MENU_TITLE = "Single note";
35 : static const QString MULTI_NOTE_MENU_TITLE = "Multi note";
36 :
37 0 : ArticulationPatternItem::ArticulationPatternItem(QObject* parent, const ArticulationType type, const bool isSingleNoteType)
38 0 : : QAbstractListModel(parent), m_isSingleNoteType(isSingleNoteType)
39 : {
40 0 : updateType(type);
41 0 : }
42 :
43 0 : const QString& ArticulationPatternItem::title() const
44 : {
45 0 : return m_title;
46 : }
47 :
48 0 : const ArticulationType& ArticulationPatternItem::type() const
49 : {
50 0 : return m_type;
51 : }
52 :
53 0 : ArticulationPattern ArticulationPatternItem::patternData() const
54 : {
55 0 : ArticulationPattern result;
56 :
57 0 : for (const auto& item : m_items) {
58 0 : result.emplace(item->positionFrom(), item->patternSegmentData());
59 : }
60 :
61 0 : return result;
62 0 : }
63 :
64 0 : void ArticulationPatternItem::updateType(const ArticulationType type)
65 : {
66 0 : m_type = type;
67 :
68 0 : if (type != ArticulationType::Undefined) {
69 0 : m_title = articulationTypeToString(type);
70 : } else {
71 0 : m_title = QString();
72 : }
73 :
74 0 : emit titleChanged();
75 0 : }
76 :
77 0 : ArticulationPatternSegment ArticulationPatternItem::buildBlankPatternSegment() const
78 : {
79 0 : return ArticulationPatternSegment(ArrangementPattern(HUNDRED_PERCENT /*durationFactor*/, 0 /*timestampOffset*/),
80 0 : PitchPattern(ArticulationMap::EXPECTED_SIZE, TEN_PERCENT, 0),
81 0 : ExpressionPattern(ArticulationMap::EXPECTED_SIZE, TEN_PERCENT, 0));
82 : }
83 :
84 0 : ArticulationPatternSegmentItem* ArticulationPatternItem::currentPatternSegment() const
85 : {
86 0 : return m_currentPattern;
87 : }
88 :
89 0 : void ArticulationPatternItem::setCurrentPatternSegment(ArticulationPatternSegmentItem* newCurrentPattern)
90 : {
91 0 : if (m_currentPattern == newCurrentPattern) {
92 0 : return;
93 : }
94 0 : m_currentPattern = newCurrentPattern;
95 0 : emit currentPatternSegmentChanged();
96 : }
97 :
98 0 : void ArticulationPatternItem::load(const ArticulationPattern& pattern)
99 : {
100 0 : beginResetModel();
101 :
102 0 : m_items.clear();
103 0 : qDeleteAll(m_items);
104 :
105 0 : for (auto it = pattern.begin(); it != pattern.end(); ++it) {
106 0 : auto next = std::next(it, 1);
107 :
108 0 : float scopePositionFrom = it->first;
109 0 : float scopePositionTo = HUNDRED_PERCENT;
110 :
111 0 : if (next != pattern.cend()) {
112 0 : scopePositionTo = next->first;
113 : }
114 :
115 0 : m_items << new ArticulationPatternSegmentItem(this, it->second, scopePositionFrom, scopePositionTo);
116 : }
117 :
118 0 : if (pattern.empty()) {
119 0 : m_items << new ArticulationPatternSegmentItem(this, buildBlankPatternSegment(), 0, HUNDRED_PERCENT);
120 : }
121 :
122 0 : endResetModel();
123 :
124 0 : setCurrentPatternSegment(m_items.first());
125 0 : }
126 :
127 0 : void ArticulationPatternItem::appendNewSegment()
128 : {
129 0 : if (!m_currentPattern) {
130 0 : return;
131 : }
132 :
133 0 : float halvedDuration = (m_currentPattern->positionTo() - m_currentPattern->positionFrom()) / 2;
134 :
135 0 : m_currentPattern->setPositionTo(m_currentPattern->positionFrom() + halvedDuration);
136 :
137 : ArticulationPatternSegmentItem* newSegment = new ArticulationPatternSegmentItem(this,
138 0 : buildBlankPatternSegment(),
139 0 : m_currentPattern->positionTo(),
140 0 : m_currentPattern->positionTo() + halvedDuration);
141 :
142 0 : int currentSegmentIndex = m_items.indexOf(m_currentPattern);
143 :
144 0 : beginInsertRows(QModelIndex(), currentSegmentIndex + 1, currentSegmentIndex + 1);
145 0 : m_items.insert(currentSegmentIndex + 1, newSegment);
146 0 : endInsertRows();
147 :
148 0 : setCurrentPatternSegment(newSegment);
149 : }
150 :
151 0 : void ArticulationPatternItem::removeCurrentSegment()
152 : {
153 0 : if (!m_currentPattern) {
154 0 : return;
155 : }
156 :
157 0 : int currentSegmentIndex = m_items.indexOf(m_currentPattern);
158 0 : int currentSegmentDuration = m_currentPattern->positionFrom() - m_currentPattern->positionTo();
159 :
160 0 : beginRemoveRows(QModelIndex(), currentSegmentIndex, currentSegmentIndex);
161 0 : m_items.removeAt(currentSegmentIndex);
162 0 : delete m_currentPattern;
163 0 : endRemoveRows();
164 :
165 0 : int newCurrentSegmentIndex = currentSegmentIndex - 1;
166 0 : if (m_items.at(newCurrentSegmentIndex)) {
167 0 : setCurrentPatternSegment(m_items.at(newCurrentSegmentIndex));
168 0 : m_currentPattern->setPositionTo(m_currentPattern->positionFrom() + currentSegmentDuration);
169 : }
170 : }
171 :
172 0 : bool ArticulationPatternItem::isAbleToRemoveCurrentSegment()
173 : {
174 0 : return rowCount() > 1;
175 : }
176 :
177 0 : int ArticulationPatternItem::rowCount(const QModelIndex&) const
178 : {
179 0 : return m_items.size();
180 : }
181 :
182 0 : QVariant ArticulationPatternItem::data(const QModelIndex& index, int /*role*/) const
183 : {
184 0 : if (!index.isValid() || index.row() >= rowCount()) {
185 0 : return QVariant();
186 : }
187 :
188 0 : return QVariant::fromValue(m_items.at(index.row()));
189 : }
190 :
191 0 : QHash<int, QByteArray> ArticulationPatternItem::roleNames() const
192 : {
193 : static const QHash<int, QByteArray> roles = {
194 0 : { PatternSegmentItem, "patternSegmentItem" }
195 0 : };
196 :
197 0 : return roles;
198 0 : }
199 :
200 0 : bool ArticulationPatternItem::isActive() const
201 : {
202 0 : return m_isActive;
203 : }
204 :
205 0 : void ArticulationPatternItem::setIsActive(bool newIsChecked)
206 : {
207 0 : if (m_isActive == newIsChecked) {
208 0 : return;
209 : }
210 0 : m_isActive = newIsChecked;
211 0 : emit isActiveChanged();
212 : }
213 :
214 0 : bool ArticulationPatternItem::isSingleNoteType() const
215 : {
216 0 : return m_isSingleNoteType;
217 : }
218 :
219 0 : bool ArticulationPatternItem::isSelected() const
220 : {
221 0 : return m_isSelected;
222 : }
223 :
224 0 : void ArticulationPatternItem::setIsSelected(bool newIsSelected)
225 : {
226 0 : if (m_isSelected == newIsSelected) {
227 0 : return;
228 : }
229 0 : m_isSelected = newIsSelected;
230 0 : emit isSelectedChanged();
231 : }
|