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) 2022 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 "stringtunings.h"
24 :
25 : #include "types/typesconv.h"
26 : #include "utils.h"
27 :
28 : #include "part.h"
29 : #include "score.h"
30 : #include "segment.h"
31 : #include "staff.h"
32 : #include "../editing/editpart.h"
33 :
34 : #include "containers.h"
35 : #include "translation.h"
36 :
37 : using namespace mu;
38 : using namespace mu::engraving;
39 :
40 : // STYLE
41 : static const ElementStyle STRING_TUNINGS_STYLE {
42 : { Sid::staffTextPlacement, Pid::PLACEMENT },
43 : { Sid::staffTextMinDistance, Pid::MIN_DISTANCE },
44 : };
45 :
46 19 : StringTunings::StringTunings(DummyParentOr<Segment> parent, TextStyleType textStyleType)
47 19 : : StaffTextBase(ElementType::STRING_TUNINGS, parent, textStyleType, ElementFlag::MOVABLE | ElementFlag::ON_STAFF)
48 : {
49 19 : initElementStyle(&STRING_TUNINGS_STYLE);
50 19 : }
51 :
52 0 : StringTunings::StringTunings(const StringTunings& s)
53 0 : : StaffTextBase(s)
54 : {
55 0 : m_preset = s.m_preset;
56 0 : m_visibleStrings = s.m_visibleStrings;
57 0 : m_stringData = s.m_stringData;
58 :
59 0 : m_noStringVisible = s.m_noStringVisible;
60 0 : m_stringsNumber = s.m_stringsNumber;
61 0 : }
62 :
63 0 : StringTunings* StringTunings::clone() const
64 : {
65 0 : return new StringTunings(*this);
66 : }
67 :
68 0 : bool StringTunings::isEditable() const
69 : {
70 0 : return false;
71 : }
72 :
73 266 : PropertyValue StringTunings::getProperty(Pid id) const
74 : {
75 266 : if (id == Pid::STRINGTUNINGS_STRINGS_COUNT) {
76 0 : Fraction tick = this->tick();
77 0 : if (staff()->isTabStaff(tick)) {
78 0 : return staff()->lines(tick);
79 : } else {
80 0 : for (const Staff* _staff : staff()->staffList()) {
81 0 : if (_staff == staff()) {
82 0 : continue;
83 : }
84 :
85 0 : if (_staff->score() == staff()->score() && _staff->isTabStaff(tick)) {
86 0 : return _staff->lines(tick);
87 : }
88 0 : }
89 : }
90 :
91 0 : if (m_stringsNumber.has_value()) {
92 0 : return m_stringsNumber.value();
93 : }
94 :
95 0 : return stringData()->strings();
96 266 : } else if (id == Pid::STRINGTUNINGS_PRESET) {
97 19 : return m_preset;
98 247 : } else if (id == Pid::STRINGTUNINGS_VISIBLE_STRINGS) {
99 0 : std::vector<int> visibleStrings;
100 0 : for (string_idx_t string : m_visibleStrings) {
101 0 : visibleStrings.emplace_back(static_cast<int>(string));
102 : }
103 :
104 0 : return visibleStrings;
105 0 : }
106 :
107 247 : return StaffTextBase::getProperty(id);
108 : }
109 :
110 380 : PropertyValue StringTunings::propertyDefault(Pid id) const
111 : {
112 380 : if (id == Pid::STRINGTUNINGS_STRINGS_COUNT) {
113 0 : return stringData()->strings();
114 380 : } else if (id == Pid::STRINGTUNINGS_PRESET) {
115 38 : return String();
116 361 : } else if (id == Pid::STRINGTUNINGS_VISIBLE_STRINGS) {
117 0 : return {};
118 : }
119 :
120 361 : return StaffTextBase::propertyDefault(id);
121 : }
122 :
123 361 : bool StringTunings::setProperty(Pid id, const PropertyValue& val)
124 : {
125 361 : if (id == Pid::STRINGTUNINGS_STRINGS_COUNT) {
126 0 : Fraction tick = this->tick();
127 0 : if (staff()->isTabStaff(tick)) {
128 0 : staff()->staffType(tick)->setLines(val.toInt());
129 : } else {
130 0 : for (Staff* _staff : staff()->staffList()) {
131 0 : if (_staff == staff()) {
132 0 : continue;
133 : }
134 :
135 0 : if (_staff->score() == staff()->score() && _staff->isTabStaff(tick)) {
136 0 : _staff->staffType(tick)->setLines(val.toInt());
137 : }
138 0 : }
139 : }
140 :
141 0 : m_stringsNumber = val.toInt();
142 361 : } else if (id == Pid::STRINGTUNINGS_PRESET) {
143 0 : m_preset = val.value<String>();
144 361 : } else if (id == Pid::STRINGTUNINGS_VISIBLE_STRINGS) {
145 0 : m_visibleStrings.clear();
146 0 : std::vector<int> ignoredStrings = val.value<std::vector<int> >();
147 0 : for (int string : ignoredStrings) {
148 0 : m_visibleStrings.emplace_back(static_cast<string_idx_t>(string));
149 : }
150 0 : } else {
151 361 : return StaffTextBase::setProperty(id, val);
152 : }
153 :
154 0 : triggerLayout();
155 0 : return true;
156 : }
157 :
158 0 : String StringTunings::accessibleInfo() const
159 : {
160 0 : const StringData* stringData = this->stringData();
161 0 : if (stringData->isNull()) {
162 0 : return String();
163 : }
164 :
165 0 : String elementName = score() ? score()->getTextStyleUserName(TextStyleType::STRING_TUNINGS).translated()
166 0 : : TConv::translatedUserName(TextStyleType::STRING_TUNINGS);
167 0 : String info;
168 :
169 0 : const std::vector<instrString>& stringList = stringData->stringList();
170 0 : int numOfStrings = static_cast<int>(stringList.size());
171 0 : for (int i = 0; i < numOfStrings; ++i) {
172 0 : string_idx_t index = numOfStrings - i - 1;
173 0 : if (muse::contains(m_visibleStrings, index)) {
174 0 : const instrString str = stringList[index];
175 0 : String pitchStr = pitch2string(str.pitch, str.useFlat);
176 0 : if (pitchStr.empty()) {
177 0 : LOGE() << "Invalid get pitch name for " << str.pitch;
178 0 : continue;
179 : }
180 :
181 0 : info += muse::mtrc("engraving", "String %1").arg(String::number(i + 1)) + u", "
182 0 : + muse::mtrc("engraving", "Value %1").arg(pitchStr) + u"; ";
183 0 : }
184 : }
185 :
186 0 : return String(u"%1: %2").arg(elementName, info);
187 0 : }
188 :
189 0 : String StringTunings::screenReaderInfo() const
190 : {
191 0 : return accessibleInfo();
192 : }
193 :
194 60 : const StringData* StringTunings::stringData() const
195 : {
196 60 : return &m_stringData;
197 : }
198 :
199 19 : void StringTunings::setStringData(const StringData& stringData)
200 : {
201 19 : m_stringData.set(stringData);
202 19 : triggerLayout();
203 19 : }
204 :
205 0 : void StringTunings::undoStringData(const StringData& stringData)
206 : {
207 0 : score()->undo(new ChangeStringData(this, stringData));
208 0 : triggerLayout();
209 0 : }
210 :
211 0 : const String& StringTunings::preset() const
212 : {
213 0 : return m_preset;
214 : }
215 :
216 0 : void StringTunings::setPreset(const String& preset)
217 : {
218 0 : m_preset = preset;
219 0 : }
220 :
221 19 : const std::vector<string_idx_t>& StringTunings::visibleStrings() const
222 : {
223 19 : return m_visibleStrings;
224 : }
225 :
226 0 : void StringTunings::setVisibleStrings(const std::vector<string_idx_t>& visibleStrings)
227 : {
228 0 : m_visibleStrings = visibleStrings;
229 0 : }
230 :
231 19 : void StringTunings::updateText()
232 : {
233 19 : String updatedText = generateText();
234 19 : undoChangeProperty(Pid::TEXT, updatedText, PropertyFlags::STYLED);
235 :
236 19 : if (updatedText.empty()) {
237 19 : m_noStringVisible = true;
238 : } else {
239 0 : m_noStringVisible = false;
240 : }
241 19 : }
242 :
243 19 : bool StringTunings::noStringVisible() const
244 : {
245 19 : return m_noStringVisible;
246 : }
247 :
248 456 : void StringTunings::triggerLayout() const
249 : {
250 456 : if (!part()) {
251 437 : return StaffTextBase::triggerLayout();
252 : }
253 :
254 19 : Fraction startTick = tick();
255 :
256 19 : StringTunings* nextStringTuning = originPart()->nextStringTuning(startTick);
257 :
258 19 : Fraction endTick = nextStringTuning ? nextStringTuning->tick() : score()->endTick();
259 :
260 19 : score()->setLayout(startTick, endTick, staffIdx(), staffIdx(), this);
261 : }
262 :
263 19 : String StringTunings::generateText() const
264 : {
265 19 : const StringData* stringData = this->stringData();
266 19 : if (!stringData || stringData->isNull()) {
267 0 : return u"";
268 : }
269 :
270 0 : auto guitarStringSymbol = [](int i) { return String(u"<sym>guitarString") + String::number(i) + u"</sym>"; };
271 :
272 19 : const std::vector<instrString>& stringList = stringData->stringList();
273 19 : std::vector<String> visibleStringList;
274 19 : int numOfStrings = static_cast<int>(stringList.size());
275 127 : for (int i = 0; i < numOfStrings; ++i) {
276 108 : string_idx_t index = numOfStrings - i - 1;
277 108 : if (muse::contains(m_visibleStrings, index)) {
278 0 : const instrString str = stringList[index];
279 0 : String pitchStr = pitch2string(str.pitch, str.useFlat);
280 0 : if (pitchStr.empty()) {
281 0 : LOGE() << "Invalid get pitch name for " << str.pitch;
282 0 : continue;
283 : }
284 :
285 0 : Char accidental;
286 0 : if (pitchStr.size() > 1) {
287 0 : Char sym(pitchStr[1]);
288 0 : if (!sym.isDigit()) {
289 0 : accidental = sym;
290 : }
291 : }
292 :
293 0 : visibleStringList.emplace_back(String(guitarStringSymbol(i + 1) + u" \u2013 "
294 0 : + String(pitchStr[0]).toUpper() + accidental) + u" ");
295 0 : }
296 : }
297 :
298 19 : if (visibleStringList.empty()) {
299 19 : return u"";
300 : }
301 :
302 0 : size_t columnCount = 0;
303 0 : size_t rowCount = 0;
304 :
305 0 : if (visibleStringList.size() < 4) {
306 0 : rowCount = visibleStringList.size();
307 0 : columnCount = 1;
308 : } else {
309 0 : rowCount = std::ceil(static_cast<double>(visibleStringList.size()) / 2);
310 0 : columnCount = 2;
311 : }
312 :
313 0 : String result;
314 0 : for (size_t i = 0; i < rowCount; ++i) {
315 0 : for (size_t j = 0; j < columnCount; ++j) {
316 0 : size_t index = i + j * rowCount;
317 0 : if (index < visibleStringList.size()) {
318 0 : result += visibleStringList[index];
319 : }
320 0 : result += u"\t";
321 : }
322 0 : result += u"\n";
323 : }
324 :
325 0 : return result;
326 19 : }
|