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 &notationStaff = 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