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 #define RG_MODULE_STRING "[NotationHLayout]"
19 #define RG_NO_DEBUG_PRINT 1
20
21 #include "NotationHLayout.h"
22
23 #include "base/Composition.h"
24 #include "base/LayoutEngine.h"
25 #include "base/NotationQuantizer.h"
26 #include "base/NotationTypes.h"
27 #include "base/Profiler.h"
28 #include "base/RulerScale.h"
29 #include "base/Segment.h"
30 #include "base/SegmentNotationHelper.h"
31 #include "base/ViewElement.h"
32 #include "base/ViewSegment.h"
33 #include "document/RosegardenDocument.h"
34 #include "gui/editors/guitar/Chord.h"
35 #include "gui/editors/notation/ClefKeyContext.h"
36 #include "gui/editors/notation/NotationChord.h"
37 #include "gui/editors/notation/NotationElement.h"
38 #include "gui/editors/notation/NotationGroup.h"
39 #include "gui/editors/notation/NotationProperties.h"
40 #include "gui/editors/notation/NotationScene.h"
41 #include "gui/editors/notation/NotationStaff.h"
42 #include "gui/editors/notation/NotePixmapFactory.h"
43 #include "misc/Strings.h"
44 #include "misc/Debug.h"
45 #include "misc/ConfigGroups.h"
46
47 #include <QApplication>
48 #include <QSettings>
49 #include <QObject>
50
51 #include <cmath>
52 #include <limits>
53
54 namespace Rosegarden
55 {
56
57 using namespace BaseProperties;
58
59
NotationHLayout(Composition * c,NotePixmapFactory * npf,const NotationProperties & properties,QObject * parent)60 NotationHLayout::NotationHLayout(Composition *c, NotePixmapFactory *npf,
61 const NotationProperties &properties,
62 QObject* parent) :
63 HorizontalLayoutEngine(c),
64 m_totalWidth(0.),
65 m_pageMode(false),
66 m_pageWidth(0.),
67 m_spacing(100),
68 m_proportion(60),
69 m_keySigCancelMode(1),
70 m_hideRedundance(true),
71 m_npf(npf),
72 m_notationQuantizer(c->getNotationQuantizer()),
73 m_properties(properties),
74 m_timePerProgressIncrement(0),
75 m_staffCount(0),
76 m_scene(static_cast<NotationScene *>(parent))
77 {
78 // RG_DEBUG << "NotationHLayout()";
79
80 QSettings settings;
81 settings.beginGroup(NotationOptionsConfigGroup);
82 m_keySigCancelMode = settings.value("keysigcancelmode", 1).toInt() ;
83 m_hideRedundance = settings.value("hideredundantclefkey", "true").toBool();
84 settings.endGroup();
85
86 settings.beginGroup(NotationViewConfigGroup);
87 m_showRepeated = settings.value("showrepeated", true).toBool();
88 m_distributeVerses = settings.value("distributeverses", true).toBool();
89 settings.endGroup();
90 }
91
~NotationHLayout()92 NotationHLayout::~NotationHLayout()
93 {
94 // empty
95 }
96
97 std::vector<int>
getAvailableSpacings()98 NotationHLayout::getAvailableSpacings()
99 {
100 if (m_availableSpacings.empty()) {
101 m_availableSpacings.push_back(30);
102 m_availableSpacings.push_back(60);
103 m_availableSpacings.push_back(85);
104 m_availableSpacings.push_back(100);
105 m_availableSpacings.push_back(130);
106 m_availableSpacings.push_back(170);
107 m_availableSpacings.push_back(220);
108 m_availableSpacings.push_back(250);
109 m_availableSpacings.push_back(300);
110 m_availableSpacings.push_back(350);
111 m_availableSpacings.push_back(400);
112 }
113 return m_availableSpacings;
114 }
115
116 std::vector<int>
getAvailableProportions()117 NotationHLayout::getAvailableProportions()
118 {
119 if (m_availableProportions.empty()) {
120 m_availableProportions.push_back(0);
121 m_availableProportions.push_back(20);
122 m_availableProportions.push_back(40);
123 m_availableProportions.push_back(60);
124 m_availableProportions.push_back(80);
125 m_availableProportions.push_back(100);
126 }
127 return m_availableProportions;
128 }
129
130 NotationHLayout::BarDataList &
getBarData(ViewSegment & staff)131 NotationHLayout::getBarData(ViewSegment &staff)
132 {
133 BarDataMap::iterator i = m_barData.find(&staff);
134 if (i == m_barData.end()) {
135 m_barData[&staff] = BarDataList();
136 }
137
138 return m_barData[&staff];
139 }
140
141 const NotationHLayout::BarDataList &
getBarData(ViewSegment & staff) const142 NotationHLayout::getBarData(ViewSegment &staff) const
143 {
144 return ((NotationHLayout *)this)->getBarData(staff);
145 }
146
147 NotationElementList::iterator
getStartOfQuantizedSlice(NotationElementList * notes,timeT t) const148 NotationHLayout::getStartOfQuantizedSlice(NotationElementList *notes,
149 timeT t) const
150 {
151 NotationElementList::iterator i = notes->findTime(t);
152 NotationElementList::iterator j(i);
153
154 while (true) {
155 if (i == notes->begin())
156 return i;
157 --j;
158 if ((*j)->getViewAbsoluteTime() < t)
159 return i;
160 i = j;
161 }
162 }
163
164 NotePixmapFactory *
getNotePixmapFactory(ViewSegment & staff)165 NotationHLayout::getNotePixmapFactory(ViewSegment &staff)
166 {
167 NotationStaff *ns = dynamic_cast<NotationStaff *>(&staff);
168 if (ns) return &ns->getNotePixmapFactory(false);
169 else return nullptr;
170 }
171
172 NotePixmapFactory *
getGraceNotePixmapFactory(ViewSegment & staff)173 NotationHLayout::getGraceNotePixmapFactory(ViewSegment &staff)
174 {
175 NotationStaff *ns = dynamic_cast<NotationStaff *>(&staff);
176 if (ns) return &ns->getNotePixmapFactory(true);
177 else return nullptr;
178 }
179
180 void
scanViewSegment(ViewSegment & staff,timeT startTime,timeT endTime,bool full)181 NotationHLayout::scanViewSegment(ViewSegment &staff, timeT startTime,
182 timeT endTime, bool full)
183 {
184 //throwIfCancelled();
185 Profiler profiler("NotationHLayout::scanViewSegment");
186
187 Segment &segment(staff.getSegment());
188 timeT segStartTime = segment.getStartTime();
189 timeT segEndTime = segment.getEndMarkerTime();
190
191 int startBarOfViewSegment = getComposition()->getBarNumber(segment.getStartTime());
192
193 if (full) {
194 clearBarList(staff);
195 startTime = segStartTime;
196 endTime = segEndTime;
197 } else {
198 // Time must be limited to values inside segment to avoid the extension
199 // of the staff toward the start of the composition (experienced with
200 // linked segments) with various effects as the display of an
201 // unnecessary time signature at a wrong place.
202 startTime = getComposition()->getBarStartForTime(startTime);
203 endTime = getComposition()->getBarEndForTime(endTime);
204 if (segStartTime > startTime) startTime = segStartTime;
205 if (segEndTime < endTime) endTime = segEndTime;
206 }
207
208 NotationElementList *notes = staff.getViewElementList();
209 BarDataList &barList(getBarData(staff));
210
211 NotePixmapFactory *npf = getNotePixmapFactory(staff);
212
213 int startBarNo = getComposition()->getBarNumber(startTime);
214 int endBarNo = getComposition()->getBarNumber(endTime);
215 /*
216 if (endBarNo > startBarNo &&
217 getComposition()->getBarStart(endBarNo) == segment.getEndMarkerTime()) {
218 --endBarNo;
219 }
220 */
221 TrackId trackId = segment.getTrack();
222 std::string name =
223 segment.getComposition()->getTrackById(trackId)->getLabel();
224 m_staffNameWidths[&staff] =
225 npf->getNoteBodyWidth() * 2 +
226 npf->getTextWidth(Text(name, Text::StaffName));
227
228 RG_DEBUG << "scanViewSegment: full scan " << full << ", times " << startTime << "->" << endTime << ", bars " << startBarNo << "->" << endBarNo << ", staff name \"" << segment.getLabel() << "\", width " << m_staffNameWidths[&staff];
229
230 SegmentNotationHelper helper(segment);
231 if (full) {
232 helper.setNotationProperties();
233 } else {
234 helper.setNotationProperties(startTime, endTime);
235 }
236
237 ::Rosegarden::Key key = segment.getKeyAtTime(startTime);
238 Clef clef = segment.getClefAtTime(startTime);
239 TimeSignature timeSignature =
240 getComposition()->getTimeSignatureAt(startTime);
241 float timeSigWidth = npf->getNoteBodyWidth() +
242 npf->getTimeSigWidth(timeSignature);
243 bool barCorrect = true;
244
245 int ottavaShift = 0;
246 timeT ottavaEnd = segEndTime;
247
248 if (full) {
249
250 RG_DEBUG << "full scan: setting haveOttava false";
251
252 m_haveOttavaSomewhere[&staff] = false;
253
254 } else if (m_haveOttavaSomewhere[&staff]) {
255
256 RG_DEBUG << "not full scan but ottava is listed";
257
258 Segment::iterator i = segment.findTime(startTime);
259 while (1) {
260 if ((*i)->isa(Indication::EventType)) {
261 try {
262 Indication indication(**i);
263 if (indication.isOttavaType()) {
264 ottavaShift = indication.getOttavaShift();
265 ottavaEnd = (*i)->getAbsoluteTime() +
266 indication.getIndicationDuration();
267 break;
268 }
269 } catch (...) { }
270 }
271 if (i == segment.begin())
272 break;
273 --i;
274 }
275 }
276
277 RG_DEBUG << "ottava shift at start:" << ottavaShift << ", ottavaEnd " << ottavaEnd;
278
279 QSettings settings;
280 settings.beginGroup(NotationOptionsConfigGroup);
281
282 int accOctaveMode = settings.value("accidentaloctavemode", 1).toInt() ;
283 AccidentalTable::OctaveType octaveType =
284 (accOctaveMode == 0 ? AccidentalTable::OctavesIndependent :
285 accOctaveMode == 1 ? AccidentalTable::OctavesCautionary :
286 AccidentalTable::OctavesEquivalent);
287
288 int accBarMode = settings.value("accidentalbarmode", 0).toInt() ;
289 AccidentalTable::BarResetType barResetType =
290 (accBarMode == 0 ? AccidentalTable::BarResetNone :
291 accBarMode == 1 ? AccidentalTable::BarResetCautionary :
292 AccidentalTable::BarResetExplicit);
293
294 bool showInvisibles = qStrToBool( settings.value("showinvisibles", "true" ) ) ;
295 settings.endGroup();
296
297 if (barResetType != AccidentalTable::BarResetNone) {
298 //!!! very crude and expensive way of making sure we see the
299 // accidentals from previous bar:
300 if (startBarNo > segment.getComposition()->getBarNumber(segment.getStartTime())) {
301 --startBarNo;
302 }
303 }
304
305 AccidentalTable accTable(key, clef, octaveType, barResetType);
306
307 for (int barNo = startBarNo; barNo <= endBarNo; ++barNo) {
308
309 std::pair<timeT, timeT> barTimes =
310 getComposition()->getBarRange(barNo);
311
312 if (barTimes.first >= segment.getEndMarkerTime()) {
313 // clear data if we have any old stuff
314 BarDataList::iterator i(barList.find(barNo));
315 if (i != barList.end()) {
316 barList.erase(i);
317 }
318 continue; // so as to erase any further bars next time around
319 }
320
321 NotationElementList::iterator from =
322 getStartOfQuantizedSlice(notes, barTimes.first);
323
324 RG_DEBUG << "getStartOfQuantizedSlice returned " <<
325 (from != notes->end() ? (*from)->getViewAbsoluteTime() : -1)
326 << " from " << barTimes.first;
327
328 NotationElementList::iterator to =
329 getStartOfQuantizedSlice(notes, barTimes.second);
330
331 if (barTimes.second >= segment.getEndMarkerTime()) {
332 to = notes->end();
333 }
334
335 bool newTimeSig = false;
336 timeSignature = getComposition()->getTimeSignatureInBar
337 (barNo, newTimeSig);
338 RG_DEBUG << "bar " << barNo << ", startBarOfViewSegment " << startBarOfViewSegment
339 << ", newTimeSig " << newTimeSig;
340
341 // When bar is the first one in a segment, the delay computed here
342 // is the difference between event position in bar and event position
343 // in segment.
344 timeT segDelay = (barNo == startBarOfViewSegment) ?
345 segStartTime - getComposition()->getBarStart(barNo) : 0;
346
347 setBarBasicData(staff, barNo, from, barCorrect, timeSignature,
348 newTimeSig, segDelay, trackId);
349 BarDataList::iterator bdli(barList.find(barNo));
350 bdli->second.layoutData.needsLayout = true;
351
352 ChunkList &chunks = bdli->second.chunks;
353 chunks.clear();
354
355 float lyricWidth = 0;
356 // int graceCount = 0;
357
358 typedef std::set
359 <long> GroupIdSet;
360 GroupIdSet groupIds;
361
362 RG_DEBUG << "scanViewSegment: bar " << barNo << ", from " << barTimes.first << ", to " << barTimes.second << " (end " << segment.getEndMarkerTime() << "); from is at " << (from == notes->end() ? -1 : (*from)->getViewAbsoluteTime()) << ", to is at " << (to == notes->end() ? -1 : (*to)->getViewAbsoluteTime());
363
364 timeT actualBarEnd = barTimes.first;
365
366 accTable.newBar();
367
368 for (NotationElementList::iterator itr = from; itr != to; ++itr) {
369
370 NotationElement *el = static_cast<NotationElement*>((*itr));
371 RG_DEBUG << "element is a " << el->event()->getType();
372
373 if (ottavaShift != 0) {
374 if (el->event()->getAbsoluteTime() >= ottavaEnd) {
375 RG_DEBUG << "reached end of ottava";
376 ottavaShift = 0;
377 }
378 }
379
380 // Clefs and key signatures must always be memorized here (even
381 // when they are invisible) as the way other elements are displayed
382 // may depend from them.
383 Key oldKey;
384 if (el->event()->isa(Clef::EventType)) {
385 clef = Clef(*el->event());
386 accTable.newClef(clef);
387 } else if (el->event()->isa(::Rosegarden::Key::EventType)) {
388 oldKey = key;
389 key = ::Rosegarden::Key(*el->event());
390 accTable = AccidentalTable
391 (key, clef, octaveType, barResetType);
392 }
393
394 bool invisible = false;
395 if (el->event()->get<Bool>(INVISIBLE, invisible) && invisible) {
396 if (!showInvisibles)
397 continue;
398 }
399 if (m_hideRedundance &&
400 m_scene->isEventRedundant(el->event(), segment)) continue;
401
402 if (el->event()->has(BEAMED_GROUP_ID)) {
403 RG_DEBUG << "element is beamed";
404 long groupId = el->event()->get<Int>(BEAMED_GROUP_ID);
405 if (groupIds.find(groupId) == groupIds.end()) {
406 RG_DEBUG << "it's a new beamed group, applying stem properties";
407 NotationGroup group(*staff.getViewElementList(),
408 itr,
409 m_notationQuantizer,
410 barTimes,
411 m_properties,
412 clef, key);
413 group.applyStemProperties();
414 groupIds.insert(groupId);
415 }
416 }
417
418 if (el->event()->isa(Clef::EventType)) {
419
420 // RG_DEBUG << "Found clef";
421 chunks.push_back(Chunk(el->event()->getSubOrdering(),
422 getLayoutWidth(*el, npf, key)));
423
424 } else if (el->event()->isa(::Rosegarden::Key::EventType)) {
425
426 // RG_DEBUG << "Found key";
427 chunks.push_back(Chunk(el->event()->getSubOrdering(),
428 getLayoutWidth(*el, npf, oldKey)));
429
430 } else if (el->event()->isa(Text::EventType)) {
431
432 bool isLyric = el->event()->has(Text::TextTypePropertyName) &&
433 el->event()->get<String>(Text::TextTypePropertyName) ==
434 Text::Lyric;
435 bool verseOk = el->event()->has(Text::LyricVersePropertyName) &&
436 el->event()->get<Int>(Text::LyricVersePropertyName) ==
437 segment.getVerseWrapped();
438
439 // the only text events of interest are lyrics, which
440 // contribute to a fixed area following the next chord
441
442 if (isLyric && (!m_distributeVerses || (m_distributeVerses && verseOk))) {
443 lyricWidth = std::max
444 (lyricWidth, float(npf->getTextWidth(Text(*el->event()))));
445 RG_DEBUG << "Setting lyric width to " << lyricWidth
446 << " for text " << el->event()->get<String>(Text::TextPropertyName);
447 }
448
449 chunks.push_back(Chunk(el->event()->getSubOrdering(), 0));
450
451 } else if (el->isNote()) {
452
453 NotePixmapFactory *cnpf = npf;
454 if (el->isGrace()) cnpf = getGraceNotePixmapFactory(staff);
455
456 scanChord(notes, itr, clef, key, accTable,
457 lyricWidth, chunks, cnpf, ottavaShift, to);
458
459 } else if (el->isRest()) {
460
461 chunks.push_back(Chunk(el->getViewDuration(),
462 el->event()->getSubOrdering(),
463 0,
464 getLayoutWidth(*el, npf, key)));
465
466 } else if (el->event()->isa(Indication::EventType)) {
467
468 // RG_DEBUG << "Found indication";
469
470 chunks.push_back(Chunk(el->event()->getSubOrdering(), 0));
471
472 try {
473 Indication indication(*el->event());
474 if (indication.isOttavaType()) {
475 ottavaShift = indication.getOttavaShift();
476 ottavaEnd = el->event()->getAbsoluteTime() +
477 indication.getIndicationDuration();
478 m_haveOttavaSomewhere[&staff] = true;
479 }
480 } catch (...) {
481 RG_DEBUG << "Bad indication!";
482 }
483
484 } else {
485
486 // RG_DEBUG << "Found something I don't know about (type is " << el->event()->getType() << ")";
487 chunks.push_back(Chunk(el->event()->getSubOrdering(),
488 getLayoutWidth(*el, npf, key)));
489 }
490
491 // If the last event in the bar is a controller or pitch bend,
492 // ignore it when calculating actualBarEnd. This fixes a very old
493 // bug whereby inserting a controller into an empty bar would turn
494 // the barline red.
495 if (!(el->event()->isa(Controller::EventType)) && !(el->event()->isa(PitchBend::EventType))) {
496 actualBarEnd = el->getViewAbsoluteTime() + el->getViewDuration();
497 }
498 }
499
500 // RG_DEBUG << "barTimes.first: " << barTimes.first << " .second: " << barTimes.second << " actualBarEnd: " << actualBarEnd;
501 if (actualBarEnd == barTimes.first) actualBarEnd = barTimes.second;
502 barCorrect = (actualBarEnd == barTimes.second);
503 setBarSizeData(staff, barNo, 0.0,
504 timeSigWidth, actualBarEnd - barTimes.first);
505
506 //if ((endTime > startTime) && (barNo % 20 == 0)) {
507 // emit setValue((barTimes.second - startTime) * 95 /
508 // (endTime - startTime));
509 //}
510
511 //throwIfCancelled();
512 }
513 /*
514 BarDataList::iterator ei(barList.end());
515 while (ei != barList.begin() && (--ei)->first > endBarNo) {
516 barList.erase(ei);
517 ei = barList.end();
518 }
519 */
520 }
521
522 void
clearBarList(ViewSegment & staff)523 NotationHLayout::clearBarList(ViewSegment &staff)
524 {
525 BarDataList &bdl = m_barData[&staff];
526 bdl.clear();
527 }
528
529 void
setBarBasicData(ViewSegment & staff,int barNo,NotationElementList::iterator start,bool correct,TimeSignature timeSig,bool newTimeSig,timeT segDelay,TrackId trackId)530 NotationHLayout::setBarBasicData(ViewSegment &staff,
531 int barNo,
532 NotationElementList::iterator start,
533 bool correct,
534 TimeSignature timeSig,
535 bool newTimeSig,
536 timeT segDelay,
537 TrackId trackId
538 )
539 {
540 // RG_DEBUG << "setBarBasicData for " << barNo;
541
542 BarDataList &bdl(m_barData[&staff]);
543
544 BarDataList::iterator i(bdl.find(barNo));
545 if (i == bdl.end()) {
546 NotationElementList::iterator endi = staff.getViewElementList()->end();
547 bdl.insert(BarDataPair(barNo, BarData(endi, true,
548 TimeSignature(), false)));
549 i = bdl.find(barNo);
550 }
551
552 i->second.basicData.start = start;
553 i->second.basicData.correct = correct;
554 i->second.basicData.timeSignature = timeSig;
555 i->second.basicData.newTimeSig = newTimeSig;
556 i->second.basicData.delayInBar = segDelay;
557 i->second.basicData.trackId = trackId;
558 }
559
560 void
setBarSizeData(ViewSegment & staff,int barNo,float fixedWidth,float timeSigFixedWidth,timeT actualDuration)561 NotationHLayout::setBarSizeData(ViewSegment &staff,
562 int barNo,
563 float fixedWidth,
564 float timeSigFixedWidth,
565 timeT actualDuration)
566 {
567 // RG_DEBUG << "setBarSizeData for " << barNo;
568
569 BarDataList &bdl(m_barData[&staff]);
570
571 BarDataList::iterator i(bdl.find(barNo));
572 if (i == bdl.end()) {
573 NotationElementList::iterator endi = staff.getViewElementList()->end();
574 bdl.insert(BarDataPair(barNo, BarData(endi, true,
575 TimeSignature(), false)));
576 i = bdl.find(barNo);
577 }
578
579 i->second.sizeData.actualDuration = actualDuration;
580 i->second.sizeData.idealWidth = 0.0;
581 i->second.sizeData.reconciledWidth = 0.0;
582 i->second.sizeData.clefKeyWidth = 0;
583 i->second.sizeData.fixedWidth = fixedWidth;
584 i->second.sizeData.timeSigFixedWidth = timeSigFixedWidth;
585 }
586
587 void
scanChord(NotationElementList * notes,NotationElementList::iterator & itr,const Clef & clef,const::Rosegarden::Key & key,AccidentalTable & accTable,float & lyricWidth,ChunkList & chunks,NotePixmapFactory * npf,int ottavaShift,NotationElementList::iterator & to)588 NotationHLayout::scanChord(NotationElementList *notes,
589 NotationElementList::iterator &itr,
590 const Clef &clef,
591 const ::Rosegarden::Key &key,
592 AccidentalTable &accTable,
593 float &lyricWidth,
594 ChunkList &chunks,
595 NotePixmapFactory *npf,
596 int ottavaShift,
597 NotationElementList::iterator &to)
598 {
599 NotationChord chord(*notes, itr, m_notationQuantizer, m_properties);
600 Accidental someAccidental = Accidentals::NoAccidental;
601 bool someCautionary = false;
602 bool barEndsInChord = false;
603 bool grace = false;
604
605 // RG_DEBUG << "NotationHLayout::scanChord: "
606 // << chord.size() << "-voice chord at "
607 // << (*itr)->event()->getAbsoluteTime()
608 // << " unquantized, "
609 // << (*itr)->getViewAbsoluteTime()
610 // << " quantized";
611
612 // RG_DEBUG << "Contents:";
613
614 /*
615 for (NotationElementList::iterator i = chord.getInitialElement();
616 i != notes->end(); ++i) {
617 (*i)->event()->dump(std::cerr);
618 if (i == chord.getFinalElement()) break;
619 }
620 */
621 // We don't need to get the chord's notes in pitch order here,
622 // but we do need to ensure we see any random non-note events
623 // that may crop up in the middle of it.
624
625 for (NotationElementList::iterator i = chord.getInitialElement();
626 i != notes->end(); ++i) {
627
628 NotationElement *el = static_cast<NotationElement*>(*i);
629 if (el->isRest()) {
630 el->event()->setMaybe<Bool>(m_properties.REST_TOO_SHORT, true);
631 if (i == chord.getFinalElement())
632 break;
633 continue;
634 }
635
636 if (el->isGrace()) {
637 grace = true;
638 }
639
640 long pitch = 64;
641 if (!el->event()->get<Int>(PITCH, pitch)) {
642 RG_DEBUG <<
643 "WARNING: NotationHLayout::scanChord: couldn't get pitch for element, using default pitch of " << pitch;
644 }
645
646 Accidental explicitAccidental = Accidentals::NoAccidental;
647 (void)el->event()->get<String>(ACCIDENTAL, explicitAccidental);
648
649 Pitch p(pitch, explicitAccidental);
650 int h = p.getHeightOnStaff(clef, key);
651 Accidental acc = p.getDisplayAccidental(key);
652
653 h -= 7 * ottavaShift;
654
655 el->event()->setMaybe<Int>(NotationProperties::OTTAVA_SHIFT, ottavaShift);
656 el->event()->setMaybe<Int>(NotationProperties::HEIGHT_ON_STAFF, h);
657 el->event()->setMaybe<String>(m_properties.CALCULATED_ACCIDENTAL, acc);
658
659 // update display acc for note according to the accTable
660 // (accidentals in force when the last chord ended) and tell
661 // accTable about accidentals from this note.
662
663 bool cautionary = false;
664 if (el->event()->has(m_properties.USE_CAUTIONARY_ACCIDENTAL)) {
665 cautionary = el->event()->get<Bool>(m_properties.USE_CAUTIONARY_ACCIDENTAL);
666 }
667 Accidental dacc = accTable.processDisplayAccidental(acc, h, cautionary);
668 el->event()->setMaybe<String>(m_properties.DISPLAY_ACCIDENTAL, dacc);
669 el->event()->setMaybe<Bool>(m_properties.DISPLAY_ACCIDENTAL_IS_CAUTIONARY,
670 cautionary);
671 if (cautionary) {
672 someCautionary = true;
673 }
674
675 if (someAccidental == Accidentals::NoAccidental)
676 someAccidental = dacc;
677
678 if (i == to)
679 barEndsInChord = true;
680
681 if (i == chord.getFinalElement())
682 break;
683 }
684
685 // tell accTable the chord has ended, so to bring its accidentals
686 // into force for future chords
687 accTable.update();
688
689 chord.applyAccidentalShiftProperties();
690
691 float extraWidth = 0;
692
693 if (someAccidental != Accidentals::NoAccidental) {
694 bool extraShift = false;
695 int shift = chord.getMaxAccidentalShift(extraShift);
696 int e = npf->getAccidentalWidth(someAccidental, shift, extraShift);
697 if (someAccidental != Accidentals::Sharp) {
698 e = std::max(e, npf->getAccidentalWidth(Accidentals::Sharp, shift, extraShift));
699 }
700 if (someCautionary) {
701 e += npf->getNoteBodyWidth();
702 }
703 extraWidth += e;
704 }
705
706 float layoutExtra = 0;
707 if (chord.hasNoteHeadShifted()) {
708 if (chord.hasStemUp()) {
709 layoutExtra += npf->getNoteBodyWidth();
710 } else {
711 extraWidth = std::max(extraWidth, float(npf->getNoteBodyWidth()));
712 }
713 }
714 /*!!!
715 if (grace) {
716 std::cerr << "Grace note: subordering " << chord.getSubOrdering();
717 chunks.push_back(Chunk(-10 + graceCount,
718 extraWidth + npf->getNoteBodyWidth()));
719 if (graceCount < 9) ++graceCount;
720 return;
721 } else {
722 std::cerr << "Non-grace note (grace count was " << graceCount << ")";
723 graceCount = 0;
724 }
725 */
726 NotationElementList::iterator myLongest = chord.getLongestElement();
727 if (myLongest == notes->end()) {
728 RG_DEBUG << "WARNING: NotationHLayout::scanChord: No longest element in chord!";
729 }
730
731 timeT d = (*myLongest)->getViewDuration();
732
733 RG_DEBUG << "Lyric width is " << lyricWidth;
734
735 if (grace) {
736 chunks.push_back(Chunk(d, chord.getSubOrdering(),
737 extraWidth + layoutExtra
738 + getLayoutWidth(**myLongest, npf, key)
739 - npf->getNoteBodyWidth(), // tighten up
740 0));
741 } else {
742 chunks.push_back(Chunk(d, 0, extraWidth,
743 std::max(layoutExtra +
744 getLayoutWidth(**myLongest, npf, key),
745 lyricWidth)));
746 lyricWidth = 0;
747 }
748
749 itr = chord.getFinalElement();
750 if (barEndsInChord) {
751 to = itr;
752 ++to;
753 }
754 }
755
756 struct ChunkLocation {
757 timeT time;
758 short subordering;
ChunkLocationRosegarden::ChunkLocation759 ChunkLocation(timeT t, short s) : time(t), subordering(s) { }
760 };
761
operator <(const ChunkLocation & l0,const ChunkLocation & l1)762 bool operator<(const ChunkLocation &l0, const ChunkLocation &l1) {
763 return ((l0.time < l1.time) ||
764 ((l0.time == l1.time) && (l0.subordering < l1.subordering)));
765 }
766
767
768
769 void
preSquishBar(int barNo)770 NotationHLayout::preSquishBar(int barNo)
771 {
772 typedef std::vector<Chunk *> ChunkRefList;
773 typedef std::map<ChunkLocation, ChunkRefList> ColumnMap;
774 static ColumnMap columns;
775 bool haveSomething = false;
776
777 typedef std::vector<BarData *> BarDataVector;
778 typedef std::map<TrackTimeSig, BarDataVector> TimeSigMap;
779 static TimeSigMap timeSigMap;
780
781 columns.clear();
782 timeSigMap.clear();
783
784 for (BarDataMap::iterator mi = m_barData.begin();
785 mi != m_barData.end(); ++mi) {
786
787 BarDataList &bdl = mi->second;
788 BarDataList::iterator bdli = bdl.find(barNo);
789
790 if (bdli != bdl.end()) {
791
792 haveSomething = true;
793 ChunkList &cl(bdli->second.chunks);
794
795 // Delay between start of bar and start of segment have to be
796 // added to event durations to avoid wrong position of notes
797 // when a segment is not precisely beginning at a start of a bar.
798 // This fixes the "anacrusis problem".
799
800 timeT aggregateTime = bdli->second.basicData.delayInBar;
801 for (ChunkList::iterator cli = cl.begin(); cli != cl.end(); ++cli) {
802
803 // Subordering is typically zero for notes, positive
804 // for rests and negative for other stuff. We want to
805 // handle notes and rests together, but not the others.
806
807 int subordering = cli->subordering;
808 if (subordering > 0)
809 subordering = 0;
810
811 columns[ChunkLocation(aggregateTime, subordering)].push_back(&(*cli));
812
813 aggregateTime += cli->duration;
814 }
815
816 // Sometimes, two time signatures may be displayed in the same bar.
817 // Following code should keep only one of them.
818 // - If the two time signatures are different, don't do anything
819 // - Else keep the first one
820
821 // Elimination of redundant time signature in the same bar - Step 1:
822 // Remember the time bar, keyed with time signature and track Id in
823 // timeSigMap
824 if (bdli->second.basicData.newTimeSig
825 && !bdli->second.basicData.timeSignature.isHidden()) {
826 TrackTimeSig tts
827 = TrackTimeSig(bdli->second.basicData.trackId,
828 bdli->second.basicData.timeSignature);
829 timeSigMap[tts].push_back(&(bdli->second));
830 }
831 }
832 }
833
834 if (!haveSomething)
835 return ;
836
837 // Elimination of redundant time signature in the same bar - Step 2:
838 // Scan every memorized bar and when several time signatures (i.e. several
839 // bars with the same keys in TimeSigMap) are found keep only one whose
840 // segment exists the soonest after the start of the bar.
841
842 // Walk through the "track and time signature" keys
843 for (TimeSigMap::iterator
844 i = timeSigMap.begin(); i != timeSigMap.end(); ++i) {
845
846 // make space for (possibly) multiple time signatures.
847 // There is no suppression of redundant time signatures
848 for (BarDataVector::iterator
849 j = i->second.begin(); j != i->second.end(); ++j) {
850 BarData *dataPtr = *j;
851 dataPtr->sizeData.fixedWidth =
852 dataPtr->sizeData.timeSigFixedWidth;
853 }
854
855 }
856
857 // now modify chunks in-place
858
859 // What we want to do here is idle along the whole set of chunk
860 // lists, inspecting all the chunks that occur at each moment in
861 // turn and choosing a "rate" from the "slowest" of these
862 // (i.e. most space per time)
863
864 float x = 0.0;
865 timeT prevTime = 0;
866 double prevRate = 0.0;
867 float maxStretchy = 0.0;
868
869 RG_DEBUG << "preSquishBar(" << barNo << "): have "
870 << columns.size() << " columns";
871
872 for (ColumnMap::iterator i = columns.begin(); i != columns.end(); ++i) {
873
874 timeT time = i->first.time;
875 ChunkRefList &list = i->second;
876
877 RG_DEBUG << "preSquishBar: "
878 << "column at " << time << " : " << i->first.subordering;
879
880
881 double minRate = -1.0;
882 float totalFixed = 0.0;
883 maxStretchy = 0.0;
884
885 for (ChunkRefList::iterator j = list.begin(); j != list.end(); ++j) {
886 if ((*j)->stretchy > 0.0) {
887 double rate = (*j)->duration / (*j)->stretchy; // time per px
888 RG_DEBUG << "preSquishBar: rate " << rate;
889 if (minRate < 0.0 || rate < minRate)
890 minRate = rate;
891 } else {
892 RG_DEBUG << "preSquishBar: not stretchy";
893 }
894
895 maxStretchy = std::max(maxStretchy, (*j)->stretchy);
896 totalFixed = std::max(totalFixed, (*j)->fixed);
897 }
898
899 RG_DEBUG << "preSquishBar: minRate " << minRate << ", maxStretchy " << maxStretchy << ", totalFixed " << totalFixed;
900
901 // we have the rate from this point, but we want to assign
902 // these elements an x coord based on the rate and distance
903 // from the previous point, plus fixed space for this point
904 // if it's a note (otherwise fixed space goes afterwards)
905
906 if (i->first.subordering == 0)
907 x += totalFixed;
908 if (prevRate > 0.0)
909 x += (time - prevTime) / prevRate;
910
911 for (ChunkRefList::iterator j = list.begin(); j != list.end(); ++j) {
912 RG_DEBUG << "Setting x for time " << time << " to " << x << " in chunk at " << *j;
913 (*j)->x = x;
914 }
915
916 if (i->first.subordering != 0)
917 x += totalFixed;
918
919 prevTime = time;
920 prevRate = minRate;
921 }
922
923 x += maxStretchy;
924
925 for (BarDataMap::iterator mi = m_barData.begin();
926 mi != m_barData.end(); ++mi) {
927
928 BarDataList &bdl = mi->second;
929 BarDataList::iterator bdli = bdl.find(barNo);
930 if (bdli != bdl.end()) {
931
932 bdli->second.sizeData.idealWidth =
933 bdli->second.sizeData.fixedWidth + x;
934
935 if (!bdli->second.basicData.timeSignature.hasHiddenBars()) {
936 bdli->second.sizeData.idealWidth += getBarMargin();
937 } else if (bdli->second.basicData.newTimeSig) {
938 bdli->second.sizeData.idealWidth += getPostBarMargin();
939 }
940
941 bdli->second.sizeData.reconciledWidth =
942 bdli->second.sizeData.idealWidth;
943
944 bdli->second.layoutData.needsLayout = true;
945 }
946 }
947 }
948
949 ViewSegment *
getViewSegmentWithWidestBar(int barNo)950 NotationHLayout::getViewSegmentWithWidestBar(int barNo)
951 {
952 float maxWidth = -1;
953 ViewSegment *widest = nullptr;
954
955 for (BarDataMap::iterator mi = m_barData.begin();
956 mi != m_barData.end(); ++mi) {
957
958 BarDataList &list = mi->second;
959 BarDataList::iterator li = list.find(barNo);
960 if (li != list.end()) {
961
962 RG_DEBUG << "getViewSegmentWithWidestBar: idealWidth is " << li->second.sizeData.idealWidth;
963
964 if (li->second.sizeData.idealWidth == 0.0) {
965 RG_DEBUG << "getViewSegmentWithWidestBar(" << barNo << "): found idealWidth of zero, presquishing";
966 preSquishBar(barNo);
967 }
968
969 if (li->second.sizeData.idealWidth > maxWidth) {
970 maxWidth = li->second.sizeData.idealWidth;
971 widest = mi->first;
972 }
973 }
974 }
975
976 return widest;
977 }
978
979 int
getMaxRepeatedClefAndKeyWidth(int barNo)980 NotationHLayout::getMaxRepeatedClefAndKeyWidth(int barNo)
981 {
982 int max = 0;
983
984 timeT barStart = 0;
985
986 for (BarDataMap::iterator mi = m_barData.begin();
987 mi != m_barData.end(); ++mi) {
988
989 ViewSegment *staff = mi->first;
990 if (mi == m_barData.begin()) {
991 barStart = staff->getSegment().getComposition()->getBarStart(barNo);
992 }
993
994 timeT t;
995 int w = 0;
996
997 Clef clef = staff->getSegment().getClefAtTime(barStart, t);
998 if (t < barStart)
999 w += m_npf->getClefWidth(clef);
1000
1001 ::Rosegarden::Key key = staff->getSegment().getKeyAtTime(barStart, t);
1002 if (t < barStart)
1003 w += m_npf->getKeyWidth(key);
1004
1005 if (w > max)
1006 max = w;
1007 }
1008
1009 RG_DEBUG << "getMaxRepeatedClefAndKeyWidth(" << barNo << "): " << max
1010 ;
1011
1012 if (max > 0)
1013 return max + getFixedItemSpacing() * 2;
1014 else
1015 return 0;
1016 }
1017
1018 void
reconcileBarsLinear()1019 NotationHLayout::reconcileBarsLinear()
1020 {
1021 Profiler profiler("NotationHLayout::reconcileBarsLinear");
1022
1023 // Ensure that concurrent bars on all staffs have the same width,
1024 // which for now we make the maximum width required for this bar
1025 // on any staff. These days getViewSegmentWithWidestBar actually does
1026 // most of the work in its call to preSquishBar, but this function
1027 // still sets the bar line positions etc.
1028
1029 int barNo = getFirstVisibleBar();
1030
1031 m_totalWidth = 0.0;
1032 for (ViewSegmentIntMap::iterator i = m_staffNameWidths.begin();
1033 i != m_staffNameWidths.end(); ++i) {
1034 if (i->second > m_totalWidth)
1035 m_totalWidth = double(i->second);
1036 }
1037
1038 for (;;) {
1039
1040 ViewSegment *widest = getViewSegmentWithWidestBar(barNo);
1041
1042 if (!widest) {
1043 // have we reached the end of the piece?
1044 if (barNo >= getLastVisibleBar()) { // yes
1045 break;
1046 } else {
1047 m_totalWidth += m_spacing / 3;
1048 RG_DEBUG << "Setting bar position for degenerate bar "
1049 << barNo << " to " << m_totalWidth;
1050
1051 m_barPositions[barNo] = m_totalWidth;
1052 ++barNo;
1053 continue;
1054 }
1055 }
1056
1057 float maxWidth = m_barData[widest].find(barNo)->second.sizeData.idealWidth;
1058 if (m_pageWidth > 0.1 && maxWidth > m_pageWidth) {
1059 maxWidth = m_pageWidth;
1060 }
1061
1062 RG_DEBUG << "Setting bar position for bar " << barNo
1063 << " to " << m_totalWidth;
1064
1065 m_barPositions[barNo] = m_totalWidth;
1066 m_totalWidth += maxWidth;
1067
1068 // Now apply width to this bar on all staffs
1069
1070 for (BarDataMap::iterator i = m_barData.begin();
1071 i != m_barData.end(); ++i) {
1072
1073 BarDataList &list = i->second;
1074 BarDataList::iterator bdli = list.find(barNo);
1075 if (bdli != list.end()) {
1076
1077 BarData::SizeData &bd(bdli->second.sizeData);
1078
1079 RG_DEBUG << "Changing width from " << bd.reconciledWidth << " to " << maxWidth;
1080
1081 double diff = maxWidth - bd.reconciledWidth;
1082 if (diff < -0.1 || diff > 0.1) {
1083 RG_DEBUG << "(So needsLayout becomes true)";
1084 bdli->second.layoutData.needsLayout = true;
1085 }
1086 bd.reconciledWidth = maxWidth;
1087 }
1088 }
1089
1090 ++barNo;
1091 }
1092
1093 RG_DEBUG << "Setting bar position for bar " << barNo
1094 << " to " << m_totalWidth;
1095
1096 m_barPositions[barNo] = m_totalWidth;
1097 }
1098
1099 void
reconcileBarsPage()1100 NotationHLayout::reconcileBarsPage()
1101 {
1102 Profiler profiler("NotationHLayout::reconcileBarsPage");
1103
1104 int barNo = getFirstVisibleBar();
1105 int barNoThisRow = 0;
1106
1107 // pair of the recommended number of bars with those bars'
1108 // original total width, for each row
1109 std::vector<std::pair<int, double> > rowData;
1110
1111 double stretchFactor = 10.0;
1112 double maxViewSegmentNameWidth = 0.0;
1113
1114 for (ViewSegmentIntMap::iterator i = m_staffNameWidths.begin();
1115 i != m_staffNameWidths.end(); ++i) {
1116 if (i->second > maxViewSegmentNameWidth) {
1117 maxViewSegmentNameWidth = double(i->second);
1118 }
1119 }
1120
1121 double pageWidthSoFar = maxViewSegmentNameWidth;
1122 m_totalWidth = maxViewSegmentNameWidth + getPreBarMargin();
1123
1124 RG_DEBUG << "reconcileBarsPage: pageWidthSoFar is " << pageWidthSoFar;
1125
1126 for (;;) {
1127
1128 ViewSegment *widest = getViewSegmentWithWidestBar(barNo);
1129 double maxWidth = m_spacing / 3;
1130
1131 if (!widest) {
1132 // have we reached the end of the piece?
1133 if (barNo >= getLastVisibleBar())
1134 break; // yes
1135 } else {
1136 maxWidth =
1137 m_barData[widest].find(barNo)->second.sizeData.idealWidth;
1138 }
1139
1140 // Work on the assumption that this bar is the last in the
1141 // row. How would that make things look?
1142
1143 double nextPageWidth = pageWidthSoFar + maxWidth;
1144 double nextStretchFactor = m_pageWidth / nextPageWidth;
1145
1146 RG_DEBUG << "barNo is " << barNo << ", maxWidth " << maxWidth << ", nextPageWidth " << nextPageWidth << ", nextStretchFactor " << nextStretchFactor << ", m_pageWidth " << m_pageWidth;
1147
1148 // We have to have at least one bar per row
1149
1150 bool tooFar = false;
1151
1152 if (barNoThisRow >= 1) {
1153
1154 // If this stretch factor is "worse" than the previous
1155 // one, we've come too far and have too many bars
1156
1157 if (fabs(1.0 - nextStretchFactor) > fabs(1.0 - stretchFactor)) {
1158 tooFar = true;
1159 }
1160
1161 // If the next stretch factor is less than 1 and would
1162 // make this bar on any of the staffs narrower than it can
1163 // afford to be, then we've got too many bars
1164 //!!! rework this -- we have no concept of "too narrow"
1165 // any more but we can declare we don't want it any
1166 // narrower than e.g. 90% or something based on the spacing
1167 /*!!!
1168 if (!tooFar && (nextStretchFactor < 1.0)) {
1169
1170 for (BarDataMap::iterator i = m_barData.begin();
1171 i != m_barData.end(); ++i) {
1172
1173 BarDataList &list = i->second;
1174 BarDataList::iterator bdli = list.find(barNo);
1175 if (bdli != list.end()) {
1176 BarData::SizeData &bd(bdli->second.sizeData);
1177 if ((nextStretchFactor * bd.idealWidth) <
1178 (double)(bd.fixedWidth + bd.baseWidth)) {
1179 tooFar = true;
1180 break;
1181 }
1182 }
1183 }
1184 }
1185 */
1186 }
1187
1188 if (tooFar) {
1189 rowData.push_back(std::pair<int, double>(barNoThisRow,
1190 pageWidthSoFar));
1191 barNoThisRow = 1;
1192
1193 // When we start a new row, we always need to allow for the
1194 // repeated clef and key at the start of it.
1195 int maxClefKeyWidth = getMaxRepeatedClefAndKeyWidth(barNo);
1196
1197 for (BarDataMap::iterator i = m_barData.begin();
1198 i != m_barData.end(); ++i) {
1199
1200 BarDataList &list = i->second;
1201 BarDataList::iterator bdli = list.find(barNo);
1202
1203 if (bdli != list.end()) {
1204 bdli->second.sizeData.clefKeyWidth = maxClefKeyWidth;
1205 }
1206 }
1207
1208 pageWidthSoFar = maxWidth + maxClefKeyWidth;
1209 stretchFactor = m_pageWidth / pageWidthSoFar;
1210 } else {
1211 ++barNoThisRow;
1212 pageWidthSoFar = nextPageWidth;
1213 stretchFactor = nextStretchFactor;
1214 }
1215
1216 ++barNo;
1217 }
1218
1219 if (barNoThisRow > 0) {
1220 rowData.push_back(std::pair<int, double>(barNoThisRow,
1221 pageWidthSoFar));
1222 }
1223
1224 // Now we need to actually apply the widths
1225
1226 barNo = getFirstVisibleBar();
1227
1228 for (unsigned int row = 0; row < rowData.size(); ++row) {
1229
1230 barNoThisRow = barNo;
1231 int finalBarThisRow = barNo + rowData[row].first - 1;
1232
1233 pageWidthSoFar = (row > 0 ? 0 : maxViewSegmentNameWidth + getPreBarMargin());
1234 stretchFactor = m_pageWidth / rowData[row].second;
1235
1236 for (; barNoThisRow <= finalBarThisRow; ++barNoThisRow, ++barNo) {
1237
1238 bool finalRow = (row == rowData.size() - 1);
1239
1240 ViewSegment *widest = getViewSegmentWithWidestBar(barNo);
1241 if (finalRow && (stretchFactor > 1.0))
1242 stretchFactor = 1.0;
1243 double maxWidth = 0.0;
1244
1245 if (!widest) {
1246 // have we reached the end of the piece? (shouldn't happen)
1247 if (barNo >= getLastVisibleBar())
1248 break; // yes
1249 else
1250 maxWidth = stretchFactor * (m_spacing / 3);
1251 } else {
1252 BarData &bd = m_barData[widest].find(barNo)->second;
1253 maxWidth = (stretchFactor * bd.sizeData.idealWidth) +
1254 bd.sizeData.clefKeyWidth;
1255 RG_DEBUG << "setting maxWidth to " << (stretchFactor * bd.sizeData.idealWidth) << " + " << bd.sizeData.clefKeyWidth << " = " << maxWidth;
1256 }
1257
1258 if (barNoThisRow == finalBarThisRow) {
1259 if (!finalRow ||
1260 (maxWidth > (m_pageWidth - pageWidthSoFar))) {
1261 maxWidth = m_pageWidth - pageWidthSoFar;
1262 RG_DEBUG << "reset maxWidth to " << m_pageWidth << " - " << pageWidthSoFar << " = " << maxWidth;
1263 }
1264 }
1265
1266 m_barPositions[barNo] = m_totalWidth;
1267 m_totalWidth += maxWidth;
1268
1269 for (BarDataMap::iterator i = m_barData.begin();
1270 i != m_barData.end(); ++i) {
1271
1272 BarDataList &list = i->second;
1273 BarDataList::iterator bdli = list.find(barNo);
1274 if (bdli != list.end()) {
1275 BarData::SizeData &bd(bdli->second.sizeData);
1276 double diff = maxWidth - bd.reconciledWidth;
1277 if (diff < -0.1 || diff > 0.1) {
1278 bdli->second.layoutData.needsLayout = true;
1279 }
1280 bd.reconciledWidth = maxWidth;
1281 }
1282 }
1283
1284 pageWidthSoFar += maxWidth;
1285 }
1286 }
1287
1288 m_barPositions[barNo] = m_totalWidth;
1289 }
1290
1291 void
finishLayout(timeT startTime,timeT endTime,bool full)1292 NotationHLayout::finishLayout(timeT startTime, timeT endTime, bool full)
1293 {
1294 Profiler profiler("NotationHLayout::finishLayout");
1295 m_barPositions.clear();
1296
1297 if (m_pageMode && (m_pageWidth > 0.1)) reconcileBarsPage();
1298 else reconcileBarsLinear();
1299
1300 int staffNo = 0;
1301
1302 for (BarDataMap::iterator i(m_barData.begin());
1303 i != m_barData.end(); ++i) {
1304
1305 //emit setValue(100 * staffNo / m_barData.size());
1306
1307 //throwIfCancelled();
1308
1309 timeT timeCovered = endTime - startTime;
1310
1311 if (full) {
1312 NotationElementList *notes = i->first->getViewElementList();
1313 if (notes->begin() != notes->end()) {
1314 NotationElementList::iterator j(notes->end());
1315 timeCovered =
1316 (*--j)->getViewAbsoluteTime() -
1317 (*notes->begin())->getViewAbsoluteTime();
1318 }
1319 }
1320
1321 // Don't crash if more than 100 segments
1322 int k = 100 / m_barData.size();
1323 if (k < 1) k = 1;
1324
1325 m_timePerProgressIncrement = timeCovered / k;
1326
1327 layout(i, startTime, endTime, full);
1328 ++staffNo;
1329 }
1330 }
1331
1332 void
layout(BarDataMap::iterator i,timeT startTime,timeT endTime,bool full)1333 NotationHLayout::layout(BarDataMap::iterator i, timeT startTime, timeT endTime,
1334 bool full)
1335 {
1336 Profiler profiler("NotationHLayout::layout");
1337
1338 ViewSegment &staff = *(i->first);
1339 NotationElementList *notes = staff.getViewElementList();
1340 BarDataList &barList(getBarData(staff));
1341 NotationStaff ¬ationStaff = dynamic_cast<NotationStaff &>(staff);
1342
1343 // these two are for partial layouts:
1344 // bool haveSimpleOffset = false;
1345 // double simpleOffset = 0;
1346
1347 RG_DEBUG << "layout: full layout " << full << ", times " << startTime << "->" << endTime;
1348
1349 double x = 0, barX = 0;
1350 TieMap tieMap;
1351
1352 timeT lastIncrement =
1353 (full && (notes->begin() != notes->end())) ?
1354 (*notes->begin())->getViewAbsoluteTime() : startTime;
1355
1356 Segment &segment = notationStaff.getSegment();
1357 ::Rosegarden::Key key = segment.getKeyAtTime(lastIncrement);
1358 Clef clef = segment.getClefAtTime(lastIncrement);
1359 TimeSignature timeSignature;
1360
1361 int startBar = getComposition()->getBarNumber(startTime);
1362
1363 QSettings settings;
1364 settings.beginGroup(NotationOptionsConfigGroup);
1365
1366 bool showInvisibles = qStrToBool( settings.value("showinvisibles", "true" ) ) ;
1367 settings.endGroup();
1368
1369 for (BarPositionList::iterator bpi = m_barPositions.begin();
1370 bpi != m_barPositions.end(); ++bpi) {
1371
1372 int barNo = bpi->first;
1373 if (!full && barNo < startBar) continue;
1374
1375 RG_DEBUG << "looking for bar "
1376 << bpi->first;
1377 BarDataList::iterator bdi = barList.find(barNo);
1378 if (bdi == barList.end()) continue;
1379 barX = bpi->second;
1380
1381 NotationElementList::iterator from = bdi->second.basicData.start;
1382 NotationElementList::iterator to;
1383
1384 RG_DEBUG << "layout(): starting bar " << barNo << ", x = " << barX << ", width = " << bdi->second.sizeData.idealWidth << ", time = " << (from == notes->end() ? -1 : (*from)->getViewAbsoluteTime());
1385
1386 BarDataList::iterator nbdi(bdi);
1387 if (++nbdi == barList.end()) {
1388 to = notes->end();
1389 } else {
1390 to = nbdi->second.basicData.start;
1391 }
1392
1393 if (from == notes->end()) {
1394 RG_DEBUG << "Start is end";
1395 }
1396 if (from == to) {
1397 RG_DEBUG << "Start is to";
1398 }
1399
1400 if (!bdi->second.layoutData.needsLayout) {
1401
1402 double offset = barX - bdi->second.layoutData.x;
1403
1404 RG_DEBUG << "layout(): bar " << barNo << " has needsLayout false and offset of " << offset;
1405
1406 if (offset > -0.1 && offset < 0.1) {
1407 RG_DEBUG << "layout(): no offset, ignoring";
1408 continue;
1409 }
1410
1411 bdi->second.layoutData.x += offset;
1412
1413 if (bdi->second.basicData.newTimeSig)
1414 bdi->second.layoutData.timeSigX += (int)offset;
1415
1416 for (NotationElementList::iterator it = from;
1417 it != to && it != notes->end(); ++it) {
1418
1419 NotationElement* nel = static_cast<NotationElement*>(*it);
1420 RG_DEBUG << "layout(): shifting element's x to " << ((*it)->getLayoutX() + offset) << " (was " << (*it)->getLayoutX() << ")";
1421 nel->setLayoutX((*it)->getLayoutX() + offset);
1422 double airX, airWidth;
1423 nel->getLayoutAirspace(airX, airWidth);
1424 nel->setLayoutAirspace(airX + offset, airWidth);
1425 }
1426
1427 continue;
1428 }
1429
1430 bdi->second.layoutData.x = barX;
1431 // x = barX + getPostBarMargin();
1432
1433 bool timeSigToPlace = false;
1434 if (bdi->second.basicData.newTimeSig) {
1435 timeSignature = bdi->second.basicData.timeSignature;
1436 timeSigToPlace = !bdi->second.basicData.timeSignature.isHidden();
1437 }
1438 if (timeSigToPlace) {
1439 RG_DEBUG << "layout(): there's a time sig in this bar";
1440 }
1441
1442 bool repeatClefAndKey = false;
1443 if (bdi->second.sizeData.clefKeyWidth > 0) {
1444 repeatClefAndKey = true;
1445 }
1446 if (repeatClefAndKey) {
1447 RG_DEBUG << "layout(): need to repeat clef & key in this bar";
1448 }
1449
1450 double barInset = notationStaff.getBarInset(barNo, repeatClefAndKey);
1451
1452 NotationElement *lastDynamicText = nullptr;
1453 int fretboardCount = 0;
1454 int count = 0;
1455
1456 double offset = 0.0;
1457 double reconciledWidth = bdi->second.sizeData.reconciledWidth;
1458
1459 if (repeatClefAndKey) {
1460 offset = bdi->second.sizeData.clefKeyWidth;
1461 reconciledWidth -= offset;
1462 }
1463
1464 if (bdi->second.basicData.newTimeSig ||
1465 !bdi->second.basicData.timeSignature.hasHiddenBars()) {
1466 offset += getPostBarMargin();
1467 }
1468
1469 ChunkList &chunks = bdi->second.chunks;
1470 ChunkList::iterator chunkitr = chunks.begin();
1471 double reconcileRatio = 1.0;
1472 if (bdi->second.sizeData.idealWidth > 0.0) {
1473 reconcileRatio = reconciledWidth / bdi->second.sizeData.idealWidth;
1474 }
1475
1476 RG_DEBUG << "have " << chunks.size() << " chunks, reconciledWidth " << bdi->second.sizeData.reconciledWidth << ", idealWidth " << bdi->second.sizeData.idealWidth << ", ratio " << reconcileRatio;
1477
1478 double delta = 0;
1479 float sigx = 0.f;
1480
1481 for (NotationElementList::iterator it = from;
1482 it != to && it != notes->end();
1483 ++it) {
1484
1485 NotationElement *el = static_cast<NotationElement*>(*it);
1486 delta = 0;
1487 float fixed = 0;
1488
1489 if (el->event()->isa(Note::EventType)) {
1490 long pitch = 0;
1491 el->event()->get<Int>(PITCH, pitch);
1492 RG_DEBUG << "element is a " << el->event()->getType() << " (pitch " << pitch << ")";
1493 } else {
1494 RG_DEBUG << "element is a " << el->event()->getType();
1495 }
1496
1497 bool invisible = false;
1498 if (el->event()->get<Bool>(INVISIBLE, invisible) && invisible) {
1499 if (!showInvisibles)
1500 continue;
1501 }
1502 if (m_hideRedundance &&
1503 m_scene->isEventRedundant(el->event(), segment)) continue;
1504
1505 // float sigx = 0;
1506
1507 if (chunkitr != chunks.end()) {
1508 RG_DEBUG << "new chunk: addr " << &(*chunkitr) << " duration=" << (*chunkitr).duration << " subordering=" << (*chunkitr).subordering << " fixed=" << (*chunkitr).fixed << " stretchy=" << (*chunkitr).stretchy << " x=" << (*chunkitr).x;
1509 x = barX + offset + reconcileRatio * (*chunkitr).x;
1510 fixed = (*chunkitr).fixed;
1511 // sigx = barX + offset - fixed;
1512 // sigx = x - fixed;
1513 RG_DEBUG << "adjusted x is " << x << ", fixed is " << fixed;
1514
1515 if (timeSigToPlace) {
1516 if (el->event()->isa(Clef::EventType) ||
1517 el->event()->isa(Rosegarden::Key::EventType)) {
1518 sigx = x + (*chunkitr).fixed + (*chunkitr).stretchy;
1519 }
1520 }
1521
1522 ChunkList::iterator chunkscooter(chunkitr);
1523 if (++chunkscooter != chunks.end()) {
1524 delta = (*chunkscooter).x - (*chunkitr).x;
1525 } else {
1526 delta = reconciledWidth -
1527 bdi->second.sizeData.fixedWidth - (*chunkitr).x;
1528 }
1529 delta *= reconcileRatio;
1530
1531 ++chunkitr;
1532 } else {
1533 x = barX + reconciledWidth - getPreBarMargin();
1534 // sigx = x;
1535 delta = 0;
1536 }
1537
1538 if (timeSigToPlace &&
1539 !el->event()->isa(Clef::EventType) &&
1540 !el->event()->isa(::Rosegarden::Key::EventType)) {
1541
1542 if (sigx == 0.f) {
1543 sigx = barX + offset;
1544 }
1545
1546 // RG_DEBUG << "Placing timesig at " << (x - fixed);
1547 // bdi->second.layoutData.timeSigX = (int)(x - fixed);
1548 RG_DEBUG << "Placing timesig at " << sigx << " (would previously have been " << int(x-fixed) << "?)";
1549 bdi->second.layoutData.timeSigX = (int)sigx;
1550 double shift = getFixedItemSpacing() +
1551 m_npf->getTimeSigWidth(timeSignature);
1552 offset += shift;
1553 x += shift;
1554 RG_DEBUG << "and moving next elt to " << x;
1555 timeSigToPlace = false;
1556 }
1557
1558 if (barInset >= 1.0) {
1559 if (el->event()->isa(Clef::EventType) ||
1560 el->event()->isa(::Rosegarden::Key::EventType)) {
1561 RG_DEBUG << "Pulling clef/key back by " << getPreBarMargin();
1562 x -= getPostBarMargin() * 2 / 3;
1563 } else {
1564 barInset = 0.0;
1565 }
1566 }
1567
1568 RG_DEBUG << "layout(): setting element's x to " << x << " (was " << el->getLayoutX() << ")";
1569
1570 double displacedX = 0.0;
1571 long dxRaw = 0;
1572 el->event()->get<Int>(DISPLACED_X, dxRaw);
1573 displacedX = double(dxRaw * m_npf->getNoteBodyWidth()) / 1000.0;
1574
1575 el->setLayoutX(x + displacedX);
1576 el->setLayoutAirspace(x, int(delta));
1577
1578 // #704958 (multiple tuplet spanners created when entering
1579 // triplet chord) -- only do this here for non-notes,
1580 // notes get it from positionChord
1581 if (!el->isNote()) {
1582 sampleGroupElement(staff, clef, key, it);
1583 }
1584
1585 if (el->isNote()) {
1586
1587 // This modifies "it" and "tieMap"
1588 positionChord(staff, it, clef, key, tieMap, to);
1589
1590 } else if (el->isRest()) {
1591
1592 // nothing to do
1593
1594 } else if (el->event()->isa(Clef::EventType)) {
1595
1596 clef = Clef(*el->event());
1597
1598 } else if (el->event()->isa(::Rosegarden::Key::EventType)) {
1599
1600 key = ::Rosegarden::Key(*el->event());
1601
1602 } else if (el->event()->isa(Text::EventType)) {
1603
1604 // if it's a dynamic, make a note of it in case a
1605 // hairpin immediately follows it
1606
1607 if (el->event()->has(Text::TextTypePropertyName) &&
1608 el->event()->get<String>(Text::TextTypePropertyName) ==
1609 Text::Dynamic) {
1610 lastDynamicText = el;
1611 }
1612
1613 } else if (el->event()->isa(Indication::EventType)) {
1614
1615 std::string type;
1616 double ix = x;
1617
1618 // Check for a dynamic text at the same time as the
1619 // indication and if found, move the indication to the
1620 // right to make room. We know the text should have
1621 // preceded the indication in the staff because it has
1622 // a smaller subordering
1623
1624 if (el->event()->get<String>
1625 (Indication::IndicationTypePropertyName, type) &&
1626 (type == Indication::Crescendo ||
1627 type == Indication::Decrescendo) &&
1628 lastDynamicText &&
1629 lastDynamicText->getViewAbsoluteTime() ==
1630 el->getViewAbsoluteTime()) {
1631
1632 ix = x + m_npf->getTextWidth
1633 (Text(*lastDynamicText->event())) +
1634 m_npf->getNoteBodyWidth() / 4;
1635 }
1636
1637 el->setLayoutX(ix + displacedX);
1638 el->setLayoutAirspace(ix, delta - (ix - x));
1639
1640 } else if (el->event()->isa(Guitar::Chord::EventType)) {
1641
1642 int guitarChordWidth = m_npf->getLineSpacing() * 6;
1643 el->setLayoutX(x - (guitarChordWidth / 2)
1644 + fretboardCount * (guitarChordWidth +
1645 m_npf->getNoteBodyWidth()/2)
1646 + displacedX);
1647 ++fretboardCount;
1648
1649 } else {
1650
1651 // nothing else
1652 }
1653
1654 if (m_timePerProgressIncrement > 0 && (++count == 100)) {
1655 count = 0;
1656 timeT sinceIncrement = el->getViewAbsoluteTime() - lastIncrement;
1657 if (sinceIncrement > m_timePerProgressIncrement) {
1658 //emit setValue(sinceIncrement / m_timePerProgressIncrement);
1659 lastIncrement +=
1660 (sinceIncrement / m_timePerProgressIncrement)
1661 * m_timePerProgressIncrement;
1662 //throwIfCancelled();
1663 }
1664 }
1665 }
1666
1667 if (timeSigToPlace) {
1668 // no other events in this bar, so we never managed to place it
1669 x = barX + offset;
1670 RG_DEBUG << "Placing timesig reluctantly at " << x;
1671 bdi->second.layoutData.timeSigX = (int)(x);
1672 timeSigToPlace = false;
1673 }
1674
1675 for (NotationGroupMap::iterator mi = m_groupsExtant.begin();
1676 mi != m_groupsExtant.end(); ++mi) {
1677 mi->second->applyBeam(notationStaff);
1678 mi->second->applyTuplingLine(notationStaff);
1679 delete mi->second;
1680 }
1681 m_groupsExtant.clear();
1682
1683 bdi->second.layoutData.needsLayout = false;
1684 }
1685 }
1686
1687 void
sampleGroupElement(ViewSegment & staff,const Clef & clef,const::Rosegarden::Key & key,const NotationElementList::iterator & itr)1688 NotationHLayout::sampleGroupElement(ViewSegment &staff,
1689 const Clef &clef,
1690 const ::Rosegarden::Key &key,
1691 const NotationElementList::iterator &itr)
1692 {
1693 NotationElement *el = static_cast<NotationElement *>(*itr);
1694
1695 if (el->event()->has(BEAMED_GROUP_ID)) {
1696
1697 //!!! Gosh. We need some clever logic to establish whether
1698 // one group is happening while another has not yet ended --
1699 // perhaps we decide one has ended if we see another, and then
1700 // re-open the case of the first if we meet another note that
1701 // claims to be in it. Then we need to hint to both of the
1702 // groups that they should choose appropriate stem directions
1703 // -- we could just use HEIGHT_ON_STAFF of their first notes
1704 // to determine this, as if that doesn't work, nothing will
1705
1706 long groupId = el->event()->get<Int>(BEAMED_GROUP_ID);
1707 RG_DEBUG << "group id: " << groupId;
1708 if (m_groupsExtant.find(groupId) == m_groupsExtant.end()) {
1709 RG_DEBUG << "(new group)";
1710 m_groupsExtant[groupId] =
1711 new NotationGroup(*staff.getViewElementList(),
1712 m_notationQuantizer,
1713 m_properties, clef, key);
1714 }
1715 m_groupsExtant[groupId]->sample(itr, true);
1716 }
1717 }
1718
1719 timeT
getSpacingDuration(ViewSegment & staff,const NotationElementList::iterator & i)1720 NotationHLayout::getSpacingDuration(ViewSegment &staff,
1721 const NotationElementList::iterator &i)
1722 {
1723 SegmentNotationHelper helper(staff.getSegment());
1724 timeT t((*i)->getViewAbsoluteTime());
1725 timeT d((*i)->getViewDuration());
1726
1727 if (d > 0 && (*i)->event()->getDuration() == 0) return d; // grace note
1728
1729 NotationElementList::iterator j(i), e(staff.getViewElementList()->end());
1730 while (j != e && ((*j)->getViewAbsoluteTime() == t ||
1731 (*j)->getViewDuration() == 0)) {
1732 ++j;
1733 }
1734 if (j == e) {
1735 return d;
1736 } else {
1737 return (*j)->getViewAbsoluteTime() - (*i)->getViewAbsoluteTime();
1738 }
1739 }
1740
1741 timeT
getSpacingDuration(ViewSegment & staff,const NotationChord & chord)1742 NotationHLayout::getSpacingDuration(ViewSegment &staff,
1743 const NotationChord &chord)
1744 {
1745 SegmentNotationHelper helper(staff.getSegment());
1746
1747 NotationElementList::iterator i = chord.getShortestElement();
1748 timeT d((*i)->getViewDuration());
1749
1750 if (d > 0 && (*i)->event()->getDuration() == 0) return d; // grace note
1751
1752 NotationElementList::iterator j(i), e(staff.getViewElementList()->end());
1753 while (j != e && (chord.contains(j) || (*j)->getViewDuration() == 0))
1754 ++j;
1755
1756 if (j != e) {
1757 d = (*j)->getViewAbsoluteTime() - (*i)->getViewAbsoluteTime();
1758 }
1759
1760 return d;
1761 }
1762
1763 void
positionChord(ViewSegment & staff,NotationElementList::iterator & itr,const Clef & clef,const::Rosegarden::Key & key,TieMap & tieMap,NotationElementList::iterator & to)1764 NotationHLayout::positionChord(ViewSegment &staff,
1765 NotationElementList::iterator &itr,
1766 const Clef &clef, const ::Rosegarden::Key &key,
1767 TieMap &tieMap,
1768 NotationElementList::iterator &to)
1769 {
1770 NotationChord chord(*staff.getViewElementList(), itr, m_notationQuantizer,
1771 m_properties, clef, key);
1772 double baseX, delta;
1773 (static_cast<NotationElement *>(*itr))->getLayoutAirspace(baseX, delta);
1774
1775 bool barEndsInChord = false;
1776
1777 RG_DEBUG << "positionChord: x = " << baseX;
1778
1779 // #938545 (Broken notation: Duplicated note can float outside
1780 // stave) -- We need to iterate over all elements in the chord
1781 // range here, not just the ordered set of notes actually in the
1782 // chord. They all have the same x-coord, so there's no
1783 // particular complication here.
1784
1785 for (NotationElementList::iterator citr = chord.getInitialElement();
1786 citr != staff.getViewElementList()->end(); ++citr) {
1787
1788 if (citr == to)
1789 barEndsInChord = true;
1790
1791 // #704958 (multiple tuplet spanners created when entering
1792 // triplet chord) -- layout() updates the beamed group data
1793 // for non-notes, but we have to do it for notes so as to
1794 // ensure every note in the chord is accounted for
1795 sampleGroupElement(staff, clef, key, citr);
1796
1797 NotationElement *elt = static_cast<NotationElement*>(*citr);
1798
1799 double displacedX = 0.0;
1800 long dxRaw = 0;
1801 elt->event()->get<Int>(DISPLACED_X, dxRaw);
1802 displacedX = double(dxRaw * m_npf->getNoteBodyWidth()) / 1000.0;
1803
1804 elt->setLayoutX(baseX + displacedX);
1805 elt->setLayoutAirspace(baseX, delta);
1806
1807 RG_DEBUG << "positionChord: assigned x to elt at " << elt->getViewAbsoluteTime();
1808
1809 if (citr == chord.getFinalElement())
1810 break;
1811 }
1812
1813 // Check for any ties going back, and if so work out how long they
1814 // must have been and assign accordingly.
1815
1816 for (NotationElementList::iterator citr = chord.getInitialElement();
1817 citr != staff.getViewElementList()->end(); ++citr) {
1818
1819 NotationElement *note = static_cast<NotationElement*>(*citr);
1820 if (!note->isNote()) {
1821 if (citr == chord.getFinalElement())
1822 break;
1823 continue;
1824 }
1825
1826 // Make a copy of the next event if available.
1827 NotationElement *nextNote = note;
1828 if (++citr != staff.getViewElementList()->end()) {
1829 nextNote = static_cast<NotationElement*>(*citr);
1830 }
1831 --citr;
1832
1833 bool tiedForwards = false;
1834 bool tiedBack = false;
1835
1836 note->event()->get<Bool>(TIED_FORWARD, tiedForwards);
1837 note->event()->get<Bool>(TIED_BACKWARD, tiedBack);
1838
1839 if (!note->event()->has(PITCH))
1840 continue;
1841 int pitch = note->event()->get<Int>(PITCH);
1842
1843 if (tiedBack) {
1844 TieMap::iterator ti(tieMap.find(pitch));
1845
1846 if (ti != tieMap.end()) {
1847 NotationElementList::iterator otherItr(ti->second);
1848
1849 if ((*otherItr)->getViewAbsoluteTime() +
1850 (*otherItr)->getViewDuration() ==
1851 note->getViewAbsoluteTime()) {
1852
1853 RG_DEBUG << "Second note in tie at " << note->getViewAbsoluteTime() << ": found first note, it matches";
1854
1855 (*otherItr)->event()->setMaybe<Int>
1856 (m_properties.TIE_LENGTH,
1857 (int)(baseX - (*otherItr)->getLayoutX()));
1858
1859 } else {
1860 RG_DEBUG << "Second note in tie at " << note->getViewAbsoluteTime() << ": found first note but it ends at " << ((*otherItr)->getViewAbsoluteTime() + (*otherItr)->getViewDuration());
1861
1862 tieMap.erase(pitch);
1863 }
1864 }
1865 }
1866
1867 if (tiedForwards) {
1868 // note->event()->setMaybe<Int>(m_properties.TIE_LENGTH, 0);
1869
1870 // Don't modify tie info here, unless the next note has become
1871 // something other than a note.
1872 if (!nextNote->isNote()) {
1873 note->event()->setMaybe<Int>(m_properties.TIE_LENGTH, 0);
1874 }
1875
1876 tieMap[pitch] = citr;
1877 } else {
1878 note->event()->unset(m_properties.TIE_LENGTH);
1879 }
1880
1881 if (citr == chord.getFinalElement())
1882 break;
1883 }
1884
1885 itr = chord.getFinalElement();
1886 if (barEndsInChord) {
1887 to = itr;
1888 ++to;
1889 }
1890 }
1891
1892 float
getLayoutWidth(ViewElement & ve,NotePixmapFactory *,const::Rosegarden::Key & previousKey) const1893 NotationHLayout::getLayoutWidth(ViewElement &ve,
1894 NotePixmapFactory */* npf */,
1895 const ::Rosegarden::Key &previousKey) const
1896 {
1897 NotationElement& e = static_cast<NotationElement&>(ve);
1898
1899 if ((e.isNote() || e.isRest()) && e.event()->has(NOTE_TYPE)) {
1900
1901 long noteType = e.event()->get<Int>(NOTE_TYPE);
1902 long dots = 0;
1903 (void)e.event()->get<Int>(NOTE_DOTS, dots);
1904
1905 double bw = 0;
1906
1907 if (e.isNote()) {
1908 bw = m_npf->getNoteBodyWidth(noteType)
1909 + m_npf->getDotWidth() * dots;
1910 } else {
1911 bw = m_npf->getRestWidth(Note(noteType, dots));
1912 }
1913
1914 double multiplier = double(Note(noteType, dots).getDuration()) /
1915 double(Note(Note::Quaver).getDuration());
1916 multiplier -= 1.0;
1917 multiplier *= m_proportion / 100.0;
1918 multiplier += 1.0;
1919
1920 double gap = m_npf->getNoteBodyWidth(noteType) * multiplier;
1921
1922 RG_DEBUG << "note type " << noteType << ", isNote " << e.isNote() << ", dots " << dots << ", multiplier " << multiplier << ", gap " << gap << ", result " << (bw + gap * m_spacing / 100.0);
1923
1924 gap = gap * m_spacing / 100.0;
1925 return bw + gap;
1926
1927 } else {
1928
1929 double w = getFixedItemSpacing();
1930
1931 if (e.event()->isa(Clef::EventType)) {
1932
1933 w += m_npf->getClefWidth(Clef(*e.event()));
1934
1935 } else if (e.event()->isa(::Rosegarden::Key::EventType)) {
1936
1937 ::Rosegarden::Key key(*e.event());
1938
1939 ::Rosegarden::Key cancelKey = previousKey;
1940
1941 if (m_keySigCancelMode == 0) { // only when entering C maj / A min
1942
1943 if (key.getAccidentalCount() != 0)
1944 cancelKey = ::Rosegarden::Key();
1945
1946 } else if (m_keySigCancelMode == 1) { // only when reducing acc count
1947
1948 if (key.getAccidentalCount() &&
1949 !(key.isSharp() == cancelKey.isSharp() &&
1950 key.getAccidentalCount() < cancelKey.getAccidentalCount()
1951 )
1952 ) {
1953 cancelKey = ::Rosegarden::Key();
1954 }
1955 }
1956
1957 w += m_npf->getKeyWidth(key, cancelKey);
1958
1959 } else if (e.event()->isa(Indication::EventType) ||
1960 e.event()->isa(Text::EventType)) {
1961
1962 w = 0;
1963
1964 } else {
1965 // RG_DEBUG << "getLayoutWidth(): no case for event type " << e.event()->getType();
1966 // w += 24;
1967 w = 0;
1968 }
1969
1970 return w;
1971 }
1972 }
1973
getBarMargin() const1974 int NotationHLayout::getBarMargin() const
1975 {
1976 return (int)(m_npf->getBarMargin() * m_spacing / 100.0);
1977 }
1978
getPreBarMargin() const1979 int NotationHLayout::getPreBarMargin() const
1980 {
1981 return getBarMargin() / 3;
1982 }
1983
getPostBarMargin() const1984 int NotationHLayout::getPostBarMargin() const
1985 {
1986 return getBarMargin() - getPreBarMargin();
1987 }
1988
getFixedItemSpacing() const1989 int NotationHLayout::getFixedItemSpacing() const
1990 {
1991 return (int)((m_npf->getNoteBodyWidth() * 2.0 / 3.0) * m_spacing / 100.0);
1992 }
1993
1994 void
reset()1995 NotationHLayout::reset()
1996 {
1997 for (BarDataMap::iterator i = m_barData.begin(); i != m_barData.end(); ++i) {
1998 clearBarList(*i->first);
1999 }
2000
2001 m_barData.clear();
2002 m_barPositions.clear();
2003 m_totalWidth = 0;
2004 }
2005
2006 int
getFirstVisibleBar() const2007 NotationHLayout::getFirstVisibleBar() const
2008 {
2009 int bar = 0;
2010 bool haveBar = false;
2011 for (BarDataMap::const_iterator i = m_barData.begin(); i != m_barData.end(); ++i) {
2012 if (i->second.begin() == i->second.end())
2013 continue;
2014 int barHere = i->second.begin()->first;
2015 if (barHere < bar || !haveBar) {
2016 bar = barHere;
2017 haveBar = true;
2018 }
2019 }
2020
2021 // RG_DEBUG << "getFirstVisibleBar: returning " << bar;
2022
2023 return bar;
2024 }
2025
2026 int
getFirstVisibleBarOnViewSegment(ViewSegment & staff) const2027 NotationHLayout::getFirstVisibleBarOnViewSegment(ViewSegment &staff) const
2028 {
2029 const BarDataList &bdl(getBarData(staff));
2030
2031 int bar = 0;
2032 if (bdl.begin() != bdl.end()) bar = bdl.begin()->first;
2033
2034 // RG_DEBUG << "getFirstVisibleBarOnViewSegment: returning " << bar;
2035 return bar;
2036 }
2037
2038 int
getLastVisibleBar() const2039 NotationHLayout::getLastVisibleBar() const
2040 {
2041 int bar = 0;
2042 bool haveBar = false;
2043 for (BarDataMap::const_iterator i = m_barData.begin();
2044 i != m_barData.end(); ++i) {
2045 if (i->second.begin() == i->second.end())
2046 continue;
2047 int barHere = getLastVisibleBarOnViewSegment(*i->first);
2048 if (barHere > bar || !haveBar) {
2049 bar = barHere;
2050 haveBar = true;
2051 }
2052 }
2053
2054 // RG_DEBUG << "getLastVisibleBar: returning " << bar;
2055
2056 return bar;
2057 }
2058
2059 int
getLastVisibleBarOnViewSegment(ViewSegment & staff) const2060 NotationHLayout::getLastVisibleBarOnViewSegment(ViewSegment &staff) const
2061 {
2062 const BarDataList &bdl(getBarData(staff));
2063 int bar = 0;
2064
2065 if (bdl.begin() != bdl.end()) {
2066 BarDataList::const_iterator i = bdl.end();
2067 bar = ((--i)->first) + 1; // last visible bar_line_
2068 }
2069
2070 // RG_DEBUG << "getLastVisibleBarOnViewSegment: returning " << bar;
2071
2072 return bar;
2073 }
2074
2075 double
getBarPosition(int bar) const2076 NotationHLayout::getBarPosition(int bar) const
2077 {
2078 double position = 0.0;
2079
2080 BarPositionList::const_iterator i = m_barPositions.find(bar);
2081
2082 if (i != m_barPositions.end()) {
2083
2084 position = i->second;
2085
2086 } else {
2087
2088 i = m_barPositions.begin();
2089 if (i != m_barPositions.end()) {
2090 if (bar < i->first)
2091 position = i->second;
2092 else {
2093 i = m_barPositions.end();
2094 --i;
2095 if (bar > i->first)
2096 position = i->second;
2097 }
2098 }
2099 }
2100
2101 // RG_DEBUG << "getBarPosition: returning " << position << " for bar " << bar;
2102
2103 return position;
2104 }
2105
2106 bool
isBarCorrectOnViewSegment(ViewSegment & staff,int i) const2107 NotationHLayout::isBarCorrectOnViewSegment(ViewSegment &staff, int i) const
2108 {
2109 const BarDataList &bdl(getBarData(staff));
2110 ++i;
2111
2112 BarDataList::const_iterator bdli(bdl.find(i));
2113 if (bdli != bdl.end()) return bdli->second.basicData.correct;
2114 else return true;
2115 }
2116
getTimeSignaturePosition(ViewSegment & staff,int barNo,TimeSignature & timeSig,double & timeSigX) const2117 bool NotationHLayout::getTimeSignaturePosition(ViewSegment &staff,
2118 int barNo,
2119 TimeSignature &timeSig,
2120 double &timeSigX) const
2121 {
2122 const BarDataList &bdl(getBarData(staff));
2123
2124 BarDataList::const_iterator bdli(bdl.find(barNo));
2125 if (bdli != bdl.end()) {
2126 timeSig = bdli->second.basicData.timeSignature;
2127 timeSigX = (double)(bdli->second.layoutData.timeSigX);
2128 return bdli->second.basicData.newTimeSig;
2129 } else
2130 return 0;
2131 }
2132
2133 timeT
getTimeForX(double x) const2134 NotationHLayout::getTimeForX(double x) const
2135 {
2136 return RulerScale::getTimeForX(x);
2137 }
2138
2139 double
getXForTime(timeT t) const2140 NotationHLayout::getXForTime(timeT t) const
2141 {
2142 return RulerScale::getXForTime(t);
2143 }
2144
2145 double
getXForTimeByEvent(timeT time) const2146 NotationHLayout::getXForTimeByEvent(timeT time) const
2147 {
2148 // RG_DEBUG << "getXForTime(" << time << ")";
2149
2150 for (BarDataMap::const_iterator i = m_barData.begin(); i != m_barData.end(); ++i) {
2151
2152 ViewSegment *staff = i->first;
2153
2154 if (staff->getSegment().getStartTime() <= time &&
2155 staff->getSegment().getEndMarkerTime() > time) {
2156
2157 ViewElementList::iterator vli =
2158 staff->getViewElementList()->findNearestTime(time);
2159
2160 bool found = false;
2161 double x = 0.0, dx = 0.0;
2162 timeT t = 0, dt = 0;
2163
2164 while (!found) {
2165 if (vli == staff->getViewElementList()->end())
2166 break;
2167 NotationElement *element = static_cast<NotationElement *>(*vli);
2168 if (element->getItem()) {
2169 x = element->getLayoutX();
2170 double temp;
2171 element->getLayoutAirspace(temp, dx);
2172 t = element->event()->getNotationAbsoluteTime();
2173 dt = element->event()->getNotationDuration();
2174 found = true;
2175 break;
2176 }
2177 ++vli;
2178 }
2179
2180 if (found) {
2181 if (time > t) {
2182
2183 while (vli != staff->getViewElementList()->end() &&
2184 ((*vli)->event()->getNotationAbsoluteTime() < time ||
2185 !((static_cast<NotationElement *>(*vli))->getItem())))
2186 ++vli;
2187
2188 if (vli != staff->getViewElementList()->end()) {
2189 NotationElement *element = static_cast<NotationElement *>(*vli);
2190 dx = element->getLayoutX() - x;
2191 dt = element->event()->getNotationAbsoluteTime() - t;
2192 }
2193
2194 if (dt > 0 && dx > 0) {
2195 return x + dx * (time - t) / dt;
2196 }
2197 }
2198
2199 return x - 3;
2200 }
2201 }
2202 }
2203
2204 return RulerScale::getXForTime(time);
2205 }
2206
2207 std::vector<int> NotationHLayout::m_availableSpacings;
2208 std::vector<int> NotationHLayout::m_availableProportions;
2209
2210 /// YG: Only for debug
2211 void
dump(std::string indent)2212 NotationHLayout::BarData::dump(std::string indent)
2213 {
2214 RG_DEBUG << indent
2215 << "basic(start=<x>"
2216 << " correct=" << basicData.correct
2217 << " timeSig=" << basicData.timeSignature.getNumerator()
2218 << "/" << basicData.timeSignature.getDenominator()
2219 << " newTimeSig=" << basicData.newTimeSig
2220 << " delayInBar=" << basicData.delayInBar
2221 << " trackId=" << basicData.trackId << ")";
2222 RG_DEBUG << indent
2223 << "size(ideal=" << sizeData.idealWidth
2224 << "reconcile=" << sizeData.reconciledWidth
2225 << "fixed=" << sizeData.fixedWidth
2226 << "timeSigFixed=" << sizeData.timeSigFixedWidth
2227 << "clefKey=" << sizeData.clefKeyWidth
2228 << "duration=" << sizeData.actualDuration << ")";
2229 RG_DEBUG << indent
2230 << "layout(needs=" << layoutData.needsLayout
2231 << " x=" << layoutData.x
2232 << " timeSigX=" << layoutData.timeSigX << ")";
2233
2234 ChunkList::iterator i;
2235 for (i=chunks.begin(); i!=chunks.end(); ++i) {
2236 RG_DEBUG << indent
2237 << " Chunk duration=" << (*i).duration
2238 << " subord=" << (*i).subordering
2239 << " fixed=" << (*i).fixed
2240 << " stretchy=" << (*i).stretchy
2241 << " x=" << (*i).x;
2242 }
2243 }
2244
2245 /// YG: Only for debug
2246 void
dumpBarDataMap()2247 NotationHLayout::dumpBarDataMap()
2248 {
2249 RG_DEBUG << "dumpBarDataMap begin";
2250 BarDataMap::iterator i;
2251 for (i=m_barData.begin(); i!=m_barData.end(); ++i) {
2252 ViewSegment *vs = (*i).first;
2253 BarDataList bdl = (*i).second;
2254
2255 RG_DEBUG << "------- ViewSegment=" << vs
2256 << " seg=" << &vs->getSegment() << "\n";
2257 BarDataList::iterator j;
2258 for (j=bdl.begin(); j!=bdl.end(); ++j) {
2259 RG_DEBUG << " ------- BarData (" << (*j).first << ")\n";
2260 (*j).second.dump(" ");
2261 }
2262 }
2263 RG_DEBUG << "dumpBarDataMap end";
2264 }
2265
2266 }
2267