1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Rosegarden
5 A MIDI and audio sequencer and musical notation editor.
6 Copyright 2000-2021 the Rosegarden development team.
7
8 Other copyrights also apply to some parts of this work. Please
9 see the AUTHORS file and individual file headers for details.
10
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License as
13 published by the Free Software Foundation; either version 2 of the
14 License, or (at your option) any later version. See the file
15 COPYING included with this distribution for more information.
16 */
17
18
19 #include "NotationChord.h"
20
21 #include "base/Sets.h"
22 #include "base/Event.h"
23 #include "base/NotationRules.h"
24 #include "base/NotationTypes.h"
25 #include "base/Quantizer.h"
26 #include "misc/Debug.h"
27 #include "NotationProperties.h"
28 #include "NoteStyleFactory.h"
29
30 namespace Rosegarden
31 {
32
33 template <>
34 Event *
getAsEvent(const NotationElementList::iterator & i)35 AbstractSet<NotationElement, NotationElementList>::getAsEvent(const NotationElementList::iterator &i)
36 {
37 return (*i)->event();
38 }
39
NotationChord(NotationElementList & c,NotationElementList::iterator i,const Quantizer * quantizer,const NotationProperties & properties,const Clef & clef,const::Rosegarden::Key & key)40 NotationChord::NotationChord(NotationElementList &c,
41 NotationElementList::iterator i,
42 const Quantizer *quantizer,
43 const NotationProperties &properties,
44 const Clef &clef,
45 const ::Rosegarden::Key &key) :
46 GenericChord < NotationElement,
47 NotationElementList, true > (c, i, quantizer,
48 NotationProperties::STEM_UP),
49 m_properties(properties),
50 m_clef(clef),
51 m_key(key)
52 {
53 initialise();
54 }
55
56 int
getHeight(const Iterator & i) const57 NotationChord::getHeight(const Iterator &i) const
58 {
59 //!!! We use HEIGHT_ON_STAFF in preference to the passed clef/key,
60 //but what if the clef/key changed since HEIGHT_ON_STAFF was
61 //written? Who updates the properties then? Check this.
62
63 long h = 0;
64 if (getAsEvent(i)->get
65 <Int>(NotationProperties::HEIGHT_ON_STAFF, h)) {
66 return h;
67 }
68
69 try {
70 Pitch pitch(*getAsEvent(i));
71 h = pitch.getHeightOnStaff(m_clef, m_key);
72 } catch (...) {
73 // no pitch!
74 }
75
76 // set non-persistent, not setMaybe, as we know the property is absent:
77 getAsEvent(i)->set
78 <Int>(NotationProperties::HEIGHT_ON_STAFF, h, false);
79 return h;
80 }
81
82 bool
hasStem() const83 NotationChord::hasStem() const
84 {
85 // true if any of the notes is stemmed
86
87 Iterator i(getInitialNote());
88 for (;;) {
89 long note;
90 if (!getAsEvent(i)->get
91 <Int>(BaseProperties::NOTE_TYPE, note)) return true;
92 if (NoteStyleFactory::getStyleForEvent(getAsEvent(i))->hasStem(note))
93 return true;
94 if (i == getFinalNote())
95 return false;
96 ++i;
97 }
98 return false;
99 }
100
101 bool
hasStemUp() const102 NotationChord::hasStemUp() const
103 {
104 NotationRules rules;
105
106 // believe anything found in any of the notes, if in a persistent
107 // property or a property apparently set by the beaming algorithm
108
109 Iterator i(getInitialNote());
110
111 for (;;) {
112 Event *e = getAsEvent(i);
113 /*!!!
114 if (e->has(m_properties.VIEW_LOCAL_STEM_UP)) {
115 return e->get<Bool>(m_properties.VIEW_LOCAL_STEM_UP);
116 }
117 */
118 if (e->has(NotationProperties::STEM_UP)) {
119 return e->get
120 <Bool>(NotationProperties::STEM_UP);
121 }
122
123 if (e->has(NotationProperties::BEAM_ABOVE)) {
124 if (e->has(NotationProperties::BEAMED) &&
125 e->get
126 <Bool>(NotationProperties::BEAMED)) {
127 return e->get
128 <Bool>(NotationProperties::BEAM_ABOVE);
129 }
130 else {
131 return !e->get
132 <Bool>(NotationProperties::BEAM_ABOVE);
133 }
134 }
135
136 if (i == getFinalNote())
137 break;
138 ++i;
139 }
140
141 return rules.isStemUp(getHighestNoteHeight(),getLowestNoteHeight());
142 }
143
144 bool
hasNoteHeadShifted() const145 NotationChord::hasNoteHeadShifted() const
146 {
147 int ph = 10000;
148
149 for (unsigned int i = 0; i < size(); ++i) {
150 int h = getHeight((*this)[i]);
151 if (h == ph + 1)
152 return true;
153 ph = h;
154 }
155
156 return false;
157 }
158
159 bool
isNoteHeadShifted(const Iterator & itr) const160 NotationChord::isNoteHeadShifted(const Iterator &itr) const
161 {
162 unsigned int i;
163 for (i = 0; i < size(); ++i) {
164 if ((*this)[i] == itr)
165 break;
166 }
167
168 if (i == size()) {
169 RG_WARNING << "NotationChord::isNoteHeadShifted: Warning: Unable to find note head " << getAsEvent(itr);
170 return false;
171 }
172
173 int h = getHeight((*this)[i]);
174
175 if (hasStemUp()) {
176 if ((i > 0) && (h == getHeight((*this)[i - 1]) + 1)) {
177 return (!isNoteHeadShifted((*this)[i - 1]));
178 }
179 } else {
180 if ((i < size() - 1) && (h == getHeight((*this)[i + 1]) - 1)) {
181 return (!isNoteHeadShifted((*this)[i + 1]));
182 }
183 }
184
185 return false;
186 }
187
188 void
applyAccidentalShiftProperties()189 NotationChord::applyAccidentalShiftProperties()
190 {
191 // Some rules:
192 //
193 // The top accidental always gets the minimum shift (i.e. normally
194 // right next to the note head or stem).
195 //
196 // The bottom accidental gets the next least: the same, if the
197 // interval is more than a sixth, or the next shift out otherwise.
198 //
199 // We then progress up from the bottom accidental upwards.
200 //
201 // These rules aren't really enough, but they might do for now!
202
203 //!!! Uh-oh... we have a catch-22 here. We can't determine the
204 // proper minimum shift until we know which way the stem goes,
205 // because if we have a shifted note head and the stem goes down,
206 // we need to shift one place further than otherwise. But we
207 // don't know for sure which way the stem goes until we've
208 // calculated the beam, and we don't do that until after we've
209 // worked out the x-coordinates based on (among other things) the
210 // accidental shift.
211
212 int minShift = 0;
213 bool extra = false;
214
215 if (!hasStemUp() && hasNoteHeadShifted()) {
216 minShift = 1; // lazy
217 extra = true;
218 }
219
220 int lastShift = minShift;
221 int lastHeight = 0, maxHeight = 999;
222 int lastWidth = 1;
223
224 for (iterator i = end(); i != begin(); ) {
225
226 --i;
227 Event *e = getAsEvent(*i);
228
229 Accidental acc;
230 if (e->get
231 <String>(m_properties.DISPLAY_ACCIDENTAL, acc) &&
232 acc != Accidentals::NoAccidental) {
233 e->setMaybe<Int>(m_properties.ACCIDENTAL_SHIFT, minShift);
234 e->setMaybe<Bool>(m_properties.ACCIDENTAL_EXTRA_SHIFT, extra);
235 maxHeight = lastHeight = getHeight(*i);
236 break;
237 }
238 }
239
240 if (maxHeight == 999) {
241 return ;
242 }
243
244 for (iterator i = begin(); i != end(); ++i) {
245
246 Event *e = getAsEvent(*i);
247 int height = getHeight(*i);
248
249 if (height == maxHeight && e->has(m_properties.ACCIDENTAL_SHIFT)) {
250 // finished -- we've come around to the highest one again
251 break;
252 }
253
254 Accidental acc;
255
256 if (e->get
257 <String>(m_properties.DISPLAY_ACCIDENTAL, acc) &&
258 acc != Accidentals::NoAccidental) {
259
260 int shift = lastShift;
261
262 if (height < lastHeight) { // lastHeight was the first, up top
263 if (lastHeight - height < 6) {
264 shift = lastShift + lastWidth;
265 }
266 } else {
267 if (height - lastHeight >= 6) {
268 if (maxHeight - height >= 6) {
269 shift = minShift;
270 } else {
271 shift = minShift + 1;
272 }
273 } else {
274 shift = lastShift + lastWidth;
275 }
276 }
277
278 e->setMaybe<Int>(m_properties.ACCIDENTAL_SHIFT, shift);
279
280 lastHeight = height;
281 lastShift = shift;
282
283 lastWidth = 1;
284 bool c = false;
285 if (e->get
286 <Bool>(m_properties.DISPLAY_ACCIDENTAL_IS_CAUTIONARY, c)
287 && c) {
288 lastWidth = 2;
289 }
290 }
291 }
292 }
293
294 int
getMaxAccidentalShift(bool & extra) const295 NotationChord::getMaxAccidentalShift(bool &extra) const
296 {
297 int maxShift = 0;
298
299 for (const_iterator i = begin(); i != end(); ++i) {
300 Event *e = getAsEvent(*i);
301 if (e->has(m_properties.ACCIDENTAL_SHIFT)) {
302 int shift = e->get
303 <Int>(m_properties.ACCIDENTAL_SHIFT);
304 if (shift > maxShift) {
305 maxShift = shift;
306 e->get
307 <Bool>(m_properties.ACCIDENTAL_EXTRA_SHIFT, extra);
308 }
309 }
310 }
311
312 return maxShift;
313 }
314
315 int
getAccidentalShift(const Iterator & i,bool & extra) const316 NotationChord::getAccidentalShift(const Iterator &i, bool &extra) const
317 {
318 if (getAsEvent(i)->has(m_properties.ACCIDENTAL_SHIFT)) {
319 int shift = getAsEvent(i)->get
320 <Int>(m_properties.ACCIDENTAL_SHIFT);
321 getAsEvent(i)->get
322 <Bool>(m_properties.ACCIDENTAL_EXTRA_SHIFT, extra);
323 return shift;
324 } else {
325 return 0;
326 }
327 }
328
329 }
330