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 "[NotationScene]"
19 #define RG_NO_DEBUG_PRINT 1
20 
21 #include "NotationScene.h"
22 
23 #include "base/Segment.h"
24 #include "base/SegmentLinker.h"
25 #include "base/BaseProperties.h"
26 
27 #include "NotationStaff.h"
28 #include "NotationHLayout.h"
29 #include "NotationVLayout.h"
30 #include "NotePixmapFactory.h"
31 #include "ClefKeyContext.h"
32 #include "NotationProperties.h"
33 #include "NotationTool.h"
34 #include "NotationWidget.h"
35 #include "NotationMouseEvent.h"
36 #include "NoteFontFactory.h"
37 #include "gui/widgets/Panned.h"
38 
39 #include "misc/Debug.h"
40 #include "misc/Strings.h"
41 
42 #include "misc/ConfigGroups.h"
43 #include "document/CommandHistory.h"
44 #include "document/RosegardenDocument.h"
45 #include "base/Profiler.h"
46 
47 #include "gui/studio/StudioControl.h"
48 #include "sound/MappedEvent.h"
49 
50 #include <QApplication>
51 #include <QSettings>
52 #include <QGraphicsSceneMouseEvent>
53 #include <QKeyEvent>
54 
55 using std::vector;
56 
57 namespace Rosegarden
58 {
59 
60 static int instanceCount = 0;
61 
NotationScene()62 NotationScene::NotationScene() :
63     m_widget(nullptr),
64     m_document(nullptr),
65     m_properties(),
66     m_notePixmapFactory(nullptr),
67     m_notePixmapFactorySmall(nullptr),
68     m_clefKeyContext(new ClefKeyContext),
69     m_selection(nullptr),
70     m_hlayout(nullptr),
71     m_vlayout(nullptr),
72     m_title(nullptr),
73     m_subtitle(nullptr),
74     m_composer(nullptr),
75     m_copyright(nullptr),
76     m_pageMode(StaffLayout::LinearMode),
77     m_printSize(5),
78     m_leftGutter(0),
79     m_currentStaff(0),
80     m_visibleStaffs(0),
81     m_compositionRefreshStatusId(0),
82     m_timeSignatureChanged(false),
83     m_updatesSuspended(false),
84     m_minTrack(0),
85     m_maxTrack(0),
86     m_finished(false),
87     m_sceneIsEmpty(false),
88     m_showRepeated(false),
89     m_editRepeated(false),
90     m_haveInittedCurrentStaff(false),
91     m_previewNoteStaff(nullptr)
92 {
93     QString prefix(QString("NotationScene%1::").arg(instanceCount++));
94     m_properties.reset(new NotationProperties(qstrtostr(prefix)));
95 
96 //    qRegisterMetaType<NotationMouseEvent>("Rosegarden::NotationMouseEvent");
97 
98     m_segmentsDeleted.clear();
99     setNotePixmapFactories();
100 }
101 
~NotationScene()102 NotationScene::~NotationScene()
103 {
104       if (m_document) {
105         if (!isCompositionDeleted()) { // implemented in CompositionObserver
106             m_document->getComposition().removeObserver(this);
107         }
108     }
109     delete m_hlayout;
110     delete m_vlayout;
111     delete m_notePixmapFactory;
112     delete m_notePixmapFactorySmall;
113     delete m_title;
114     delete m_subtitle;
115     delete m_composer;
116     delete m_copyright;
117     delete m_selection;
118 
119     for (unsigned int i = 0; i < m_segments.size(); ++i)
120         m_segments[i]->removeObserver(m_clefKeyContext);
121     delete m_clefKeyContext;
122 
123     for (unsigned int i = 0; i < m_staffs.size(); ++i) delete m_staffs[i];
124 
125     for (std::vector<Segment *>::iterator it = m_clones.begin();
126          it != m_clones.end(); ++it) {
127         delete (*it);
128     }
129 }
130 
131 void
setNotePixmapFactories(QString fontName,int size)132 NotationScene::setNotePixmapFactories(QString fontName, int size)
133 {
134     delete m_notePixmapFactory;
135     delete m_notePixmapFactorySmall;
136 
137     m_notePixmapFactory = new NotePixmapFactory(fontName, size);
138 
139     fontName = m_notePixmapFactory->getFontName();
140     size = m_notePixmapFactory->getSize();
141 
142     std::vector<int> sizes = NoteFontFactory::getScreenSizes(fontName);
143     int small = size;
144     for (unsigned int i = 0; i < sizes.size(); ++i) {
145         if (sizes[i] == size || sizes[i] > size*3 / 4) break;
146         small = sizes[i];
147     }
148 
149     // small NPF needs to know target size for grace noteheads and normal size
150     // so it can scale everything else sensibly
151     m_notePixmapFactorySmall = new NotePixmapFactory(fontName, size, small);
152 
153     if (m_hlayout) m_hlayout->setNotePixmapFactory(m_notePixmapFactory);
154     if (m_vlayout) m_vlayout->setNotePixmapFactory(m_notePixmapFactory);
155 
156     for (unsigned int i = 0; i < m_staffs.size(); ++i) {
157         m_staffs[i]->setNotePixmapFactories(m_notePixmapFactory,
158                                             m_notePixmapFactorySmall);
159     }
160 }
161 
162 QString
getFontName() const163 NotationScene::getFontName() const
164 {
165     return m_notePixmapFactory->getFontName();
166 }
167 
168 void
setFontName(QString name)169 NotationScene::setFontName(QString name)
170 {
171     if (name == getFontName()) return;
172     setNotePixmapFactories(name, getFontSize());
173     if (!m_updatesSuspended) {
174         positionStaffs();
175         layoutAll();
176     }
177 }
178 
179 int
getFontSize() const180 NotationScene::getFontSize() const
181 {
182     return m_notePixmapFactory->getSize();
183 }
184 
185 void
setFontSize(int size)186 NotationScene::setFontSize(int size)
187 {
188     if (size == getFontSize()) return;
189     setNotePixmapFactories(getFontName(), size);
190     if (!m_updatesSuspended) {
191         positionStaffs();
192         layoutAll();
193     }
194 }
195 
196 int
getHSpacing() const197 NotationScene::getHSpacing() const
198 {
199     return m_hlayout->getSpacing();
200 }
201 
202 void
setHSpacing(int spacing)203 NotationScene::setHSpacing(int spacing)
204 {
205     if (spacing == getHSpacing()) return;
206     m_hlayout->setSpacing(spacing);
207     if (!m_updatesSuspended) {
208         positionStaffs();
209         layoutAll();
210     }
211 }
212 
213 int
getLeftGutter() const214 NotationScene::getLeftGutter() const
215 {
216     return m_leftGutter;
217 }
218 
219 void
setLeftGutter(int gutter)220 NotationScene::setLeftGutter(int gutter)
221 {
222     m_leftGutter = gutter;
223     positionStaffs();
224 }
225 
226 void
setNotationWidget(NotationWidget * w)227 NotationScene::setNotationWidget(NotationWidget *w)
228 {
229     m_widget = w;
230 }
231 
232 const RulerScale *
getRulerScale() const233 NotationScene::getRulerScale() const
234 {
235     return m_hlayout;
236 }
237 
238 NotationStaff *
getCurrentStaff()239 NotationScene::getCurrentStaff()
240 {
241     if (m_currentStaff < (int)m_staffs.size()) {
242         return m_staffs[m_currentStaff];
243     } else {
244         return nullptr;
245     }
246 }
247 
248 void
setCurrentStaff(NotationStaff * staff)249 NotationScene::setCurrentStaff(NotationStaff *staff)
250 {
251     // To unallow the direct edition of a repeated segment do it never be
252     // the current one
253     if (m_showRepeated && !m_editRepeated) {
254         if (staff->getSegment().isTmp()) return;
255     }
256 
257     for (int i = 0; i < int(m_staffs.size()); ++i) {
258         if (m_staffs[i] == staff) {
259             if (m_currentStaff != i) {
260                 m_currentStaff = i;
261                 emit currentStaffChanged();
262                 emit currentViewSegmentChanged(staff);
263             }
264             return;
265         }
266     }
267 }
268 
269 void
setStaffs(RosegardenDocument * document,vector<Segment * > segments)270 NotationScene::setStaffs(RosegardenDocument *document,
271                           vector<Segment *> segments)
272 {
273 
274     if (m_document && document != m_document) {
275         m_document->getComposition().removeObserver(this);
276     }
277 
278     // ClefKeyContext doesn't keep any segments list. So notation scene
279     // has to maintain segment observer connections for it.
280     for (unsigned int i = 0; i < m_segments.size(); ++i) {
281         m_segments[i]->removeObserver(m_clefKeyContext);
282     }
283 
284      // Delete clones of repeating segment if any
285     for (std::vector<Segment *>::iterator it = m_clones.begin();
286         it != m_clones.end(); ++it) {
287         delete (*it);
288     }
289     m_clones.clear();
290 
291     m_document = document;
292     m_externalSegments = segments;
293     Composition * composition = &m_document->getComposition();
294 
295 
296     /// Look for repeating segments
297 
298     // Get display/edition settings
299     QSettings settings;
300     settings.beginGroup(NotationViewConfigGroup);
301     m_showRepeated =  settings.value("showrepeated", true).toBool();
302     m_editRepeated =  settings.value("editrepeated", false).toBool();
303     settings.endGroup();
304 
305     if (m_showRepeated) {
306         createClonesFromRepeatedSegments();
307         // External segments and clones are now mixed inside m_segments
308     } else {
309         m_segments = m_externalSegments;
310         // No clone in that case
311     }
312 
313 
314     composition->addObserver(this);
315 
316     m_compositionRefreshStatusId = composition->getNewRefreshStatusId();
317 
318     delete m_hlayout;
319     delete m_vlayout;
320 
321     m_hlayout = new NotationHLayout(composition,
322                                     m_notePixmapFactory,
323                                     *m_properties,
324                                     this);
325 
326     m_vlayout = new NotationVLayout(composition,
327                                     m_notePixmapFactory,
328                                     *m_properties,
329                                     this);
330 
331     for (unsigned int i = 0; i < m_staffs.size(); ++i) {
332         delete m_staffs[i];
333     }
334     m_staffs.clear();
335 
336     std::set<TrackId> trackIds;
337 
338     for (unsigned int i = 0; i < m_segments.size(); ++i) {
339         NotationStaff *staff = new NotationStaff
340             (this,
341              m_segments[i],
342              nullptr, // no snap grid for notation
343              i,
344              m_notePixmapFactory,
345              m_notePixmapFactorySmall);
346 
347         m_staffs.push_back(staff);
348 
349         // To assume segments are trackId ordered is no more true (was it ?)
350         // since clones may be found at the end of segments vector.
351         // The trackIds set is used to count how many visible staffs we have.
352         TrackId id = m_segments[i]->getTrack();
353         trackIds.insert(id);
354     }
355 
356     m_visibleStaffs = trackIds.size();
357 
358     m_clefKeyContext->setSegments(this);
359 
360     // Remember the names of the tracks
361     for (std::set<TrackId>::iterator i = trackIds.begin();
362          i != trackIds.end(); ++i) {
363         Track *track = composition->getTrackById(*i);
364         Q_ASSERT(track);
365         m_trackLabels[*i] = track->getLabel();
366     }
367 
368     // ClefKeyContext doesn't keep any segments list. So notation scene
369     // has to maintain segment observer connections for it.
370     for (unsigned int i = 0; i < m_segments.size(); ++i) {
371         m_segments[i]->addObserver(m_clefKeyContext);
372     }
373 
374     // We don't know a good current staff now.  This is correct even
375     // if we are resetting an existing NotationScene because the old
376     // current staff may not even exist.
377     m_haveInittedCurrentStaff = false;
378 
379     if (!m_updatesSuspended) {
380         positionStaffs();
381         layoutAll();
382         initCurrentStaffIndex();
383     }
384 
385 
386     connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
387             this, SLOT(slotCommandExecuted()));
388 }
389 
390 
391 void
createClonesFromRepeatedSegments()392 NotationScene::createClonesFromRepeatedSegments()
393 {
394     const Segment::Participation participation =
395         m_editRepeated ? Segment::editableClone : Segment::justForShow;
396 
397     // Create clones (if needed)
398     for (std::vector<Segment *>::iterator it = m_externalSegments.begin();
399         it != m_externalSegments.end(); ++it) {
400         if ((*it)->isRepeating()) {
401             timeT targetStart = (*it)->getStartTime();
402             timeT targetEnd = (*it)->getEndMarkerTime();
403             timeT repeatEnd = (*it)->getRepeatEndTime();
404             timeT targetDuration = targetEnd - targetStart;
405             TrackId track = (*it)->getTrack();
406             int verse = (*it)->getVerse();
407 //             RG_DEBUG << "Creating clones   track=" << track
408 //                       << " targetStart=" << targetStart
409 //                       << " targetEnd=" << targetEnd
410 //                       << " repeatEnd=" << repeatEnd << "\n";
411             for (timeT ts = targetStart + targetDuration;
412                 ts < repeatEnd; ts += targetDuration) {
413                 timeT te = ts + targetDuration;
414                 // RG_DEBUG << "   clone [" << ts << ", " << te << "]";
415 
416                 /// Segment *s = (*it)->clone();
417                 Segment *s = SegmentLinker::createLinkedSegment(*it);
418                 s->setStartTime(ts);
419                 s->setTrack(track);
420                 s->setVerse(++verse);
421                 s->setParticipation(participation);
422                 s->setTmp();  // To avoid crash related to composition
423                               // being undefined and to get notation
424                               // with grey color
425                 if (repeatEnd < te) {
426                     s->setEndMarkerTime(repeatEnd);
427                     // RG_DEBUG << " shortened to " << repeatEnd;
428                 }
429                 m_clones.push_back(s);
430             }
431             (*it)->setAsReference();
432         }
433     }
434 
435     // Add possible clones to the list of segments
436     m_segments = m_externalSegments;
437     for (std::vector<Segment *>::iterator it = m_clones.begin();
438         it != m_clones.end(); ++it) {
439         m_segments.push_back(*it);
440     }
441 }
442 
443 
444 void
suspendLayoutUpdates()445 NotationScene::suspendLayoutUpdates()
446 {
447     m_updatesSuspended = true;
448 }
449 
450 void
resumeLayoutUpdates()451 NotationScene::resumeLayoutUpdates()
452 {
453     m_updatesSuspended = false;
454     // may be more work than we really want to do, depending on what
455     // happened while updates were suspended
456     positionStaffs();
457     layoutAll();
458     initCurrentStaffIndex();
459 }
460 
461 NotationStaff *
getStaffForSceneCoords(double x,int y) const462 NotationScene::getStaffForSceneCoords(double x, int y) const
463 {
464     // (i)  Do not change staff, if mouse was clicked within the current staff.
465 
466     StaffLayout *s = nullptr;
467 
468     if (m_currentStaff < (int)m_staffs.size()) {
469         s = m_staffs[m_currentStaff];
470     }
471 
472     if (s && s->containsSceneCoords(x, y)) {
473 
474         StaffLayout::StaffLayoutCoords coords =
475             s->getLayoutCoordsForSceneCoords(x, y);
476 
477         timeT t = m_hlayout->getTimeForX(coords.first);
478 
479         if (m_staffs[m_currentStaff]->includesTime(t)) {
480             return m_staffs[m_currentStaff];
481         }
482     }
483 
484     // (ii) Find staff under cursor, if clicked outside the current staff.
485 
486     for (unsigned int i = 0; i < m_staffs.size(); ++i) {
487 
488         // Never return a staff which can't be edited directly
489         if (m_showRepeated && !m_editRepeated) {
490             if (m_staffs[i]->getSegment().isTmp()) continue;
491         }
492 
493         StaffLayout *s = m_staffs[i];
494 
495         if (s->containsSceneCoords(x, y)) {
496 
497             StaffLayout::StaffLayoutCoords coords =
498                 s->getLayoutCoordsForSceneCoords(x, y);
499 
500 	    timeT t = m_hlayout->getTimeForX(coords.first);
501 
502 	    if (m_staffs[i]->includesTime(t)) {
503                 return m_staffs[i];
504             }
505         }
506     }
507 
508     return nullptr;
509 }
510 
511 NotationStaff *
getStaffAbove(timeT t)512 NotationScene::getStaffAbove(timeT t)
513 {
514     return getNextStaffVertically(-1, t);
515 }
516 
517 NotationStaff *
getStaffBelow(timeT t)518 NotationScene::getStaffBelow(timeT t)
519 {
520     return getNextStaffVertically(1, t);
521 }
522 
523 NotationStaff *
getPriorStaffOnTrack()524 NotationScene::getPriorStaffOnTrack()
525 {
526     return getNextStaffHorizontally(-1, false);
527 }
528 
529 NotationStaff *
getNextStaffOnTrack()530 NotationScene::getNextStaffOnTrack()
531 {
532     return getNextStaffHorizontally(1, false);
533 }
534 
535 NotationStaff *
getStaffBySegmentMarking(const QString & marking) const536 NotationScene::getStaffBySegmentMarking(const QString& marking) const
537 {
538     for (unsigned int i=0; i<m_staffs.size(); ++i) {
539         NotationStaff* staff = m_staffs[i];
540         QString staffMarking = staff->getMarking();
541         if (staffMarking == marking) {
542             return staff;
543         }
544     }
545     return nullptr;
546 }
547 
548 NotationStaff *
getStaffbyTrackAndTime(const Track * track,timeT targetTime)549 NotationScene::getStaffbyTrackAndTime(const Track *track, timeT targetTime)
550 {
551     // Prepare a fallback: If this is the right track but no staff
552     // includes time t, we'll return the fallback instead.  We
553     // don't try to find the best fallback.
554     NotationStaff * fallback = nullptr;
555     timeT minTime = 1.0e10;
556     for (unsigned int i = 0; i < m_staffs.size(); ++i) {
557         if (m_staffs[i]->getSegment().getTrack() == track->getId()) {
558             if(m_staffs[i]->includesTime(targetTime)) {
559                 return m_staffs[i];
560             } else {
561                 // find the closest staff to the targetTime
562                 timeT dt1 = abs(targetTime - m_staffs[i]->getStartTime());
563                 if (dt1 < minTime) {
564                     minTime = dt1;
565                     fallback = m_staffs[i];
566                 }
567                 timeT dt2 = abs(targetTime - m_staffs[i]->getEndTime());
568                 if (dt2 < minTime) {
569                     minTime = dt2;
570                     fallback = m_staffs[i];
571                 }
572             }
573         }
574     }
575     // We found segments on the track, but none that include time
576     // t.  In this circumstance, we still want to return a staff
577     // so return the fallback.
578     return fallback;
579 }
580 
581 // @params
582 // direction is 1 if higher-numbered tracks are wanted, -1 if
583 // lower-numbered ones are.
584 // t is a time that the found staff should contain if possible.
585 NotationStaff *
getNextStaffVertically(int direction,timeT t)586 NotationScene::getNextStaffVertically(int direction, timeT t)
587 {
588     if (m_staffs.size() < 2 || m_currentStaff >= (int)m_staffs.size()) return nullptr;
589 
590     NotationStaff *current = m_staffs[m_currentStaff];
591     Composition *composition = &m_document->getComposition();
592     Track *track = composition->getTrackById(current->getSegment().getTrack());
593     if (!track) return nullptr;
594 
595     int position = track->getPosition();
596     Track *newTrack = nullptr;
597 
598     while ((newTrack = composition->getTrackByPosition(position + direction))) {
599         NotationStaff * staff = getStaffbyTrackAndTime(newTrack, t);
600         if (staff) { return staff; }
601         position += direction;
602     }
603 
604     return nullptr;
605 }
606 
607 NotationStaff *
getNextStaffHorizontally(int direction,bool cycle)608 NotationScene::getNextStaffHorizontally(int direction, bool cycle)
609 {
610     if (m_staffs.size() < 2 || m_currentStaff >= (int)m_staffs.size()) return nullptr;
611 
612     NotationStaff *current = m_staffs[m_currentStaff];
613     //Composition *composition = &m_document->getComposition();
614     TrackId trackId = current->getSegment().getTrack();
615 
616     QMultiMap<timeT, NotationStaff *> timeMap;
617     for (size_t i = 0; i < m_staffs.size(); ++i) {
618         if (m_staffs[i]->getSegment().getTrack() == trackId) {
619             timeMap.insert(m_staffs[i]->getSegment().getClippedStartTime(), m_staffs[i]);
620         }
621     }
622 
623     QMultiMap<timeT, NotationStaff *>::iterator i =
624         timeMap.find(current->getSegment().getClippedStartTime(), current);
625 
626     if (i == timeMap.end()) {
627         RG_WARNING << "Argh! Can't find staff I just put in map";
628         return nullptr;
629     }
630 
631     if (direction < 0) {
632         if (i == timeMap.begin()) {
633             if (cycle) i = timeMap.end();
634             else return nullptr;
635         }
636         --i;
637     } else {
638         ++i;
639         if (i == timeMap.end()) {
640             if (cycle) i = timeMap.begin();
641             else return nullptr;
642         }
643     }
644 
645     return i.value();
646 }
647 
648 // Initialize which staff is current.  We try to choose one containing
649 // the playback pointer.
650 void
initCurrentStaffIndex()651 NotationScene::initCurrentStaffIndex()
652 {
653     // Only do this if we haven't done it before since the last reset,
654     // otherwise we'll annoy the user.
655     if (m_haveInittedCurrentStaff) { return; }
656     m_haveInittedCurrentStaff = true;
657 
658     // Can't do much if we have no staffs.
659     if (m_staffs.empty()) { return; }
660 
661     Composition &composition = m_document->getComposition();
662     timeT targetTime = composition.getPosition();
663 
664     // Try the globally selected track (which we may not even include
665     // any segments from)
666     {
667         const Track *track = composition.getTrackById(composition.getSelectedTrack());
668         NotationStaff *staff = track ? getStaffbyTrackAndTime(track, targetTime) : nullptr;
669         if (staff) {
670             setCurrentStaff(staff);
671             return;
672         }
673     }
674 
675     // Try m_minTrack, which we surely include some segment from.
676     {
677         // Careful, m_minTrack is an int indicating position, not a
678         // TrackId, and must be converted.
679         const Track *track =
680             composition.getTrackByPosition(m_minTrack);
681         NotationStaff *staff = getStaffbyTrackAndTime(track, targetTime);
682         if (staff) {
683             setCurrentStaff(staff);
684             return;
685         }
686     }
687 
688     // We shouldn't reach here.
689     RG_WARNING << "Argh! Failed to find a staff!";
690 }
691 
692 
693 Segment *
getCurrentSegment()694 NotationScene::getCurrentSegment()
695 {
696     NotationStaff *s = nullptr;
697 
698     if (m_currentStaff < (int)m_staffs.size()) {
699         s = m_staffs[m_currentStaff];
700     }
701 
702     if (s) return &s->getSegment();
703     return nullptr;
704 }
705 
706 bool
segmentsContainNotes() const707 NotationScene::segmentsContainNotes() const
708 {
709     for (unsigned int i = 0; i < m_segments.size(); ++i) {
710 
711         const Segment *segment = m_segments[i];
712 
713         for (Segment::const_iterator i = segment->begin();
714              segment->isBeforeEndMarker(i); ++i) {
715 
716             if (((*i)->getType() == Note::EventType)) {
717                 return true;
718             }
719         }
720     }
721 
722     return false;
723 }
724 
725 void
setupMouseEvent(QGraphicsSceneMouseEvent * e,NotationMouseEvent & nme)726 NotationScene::setupMouseEvent(QGraphicsSceneMouseEvent *e,
727                                NotationMouseEvent &nme)
728 {
729     setupMouseEvent(e->scenePos(), e->buttons(), e->modifiers(), nme);
730 }
731 
732 void
setupMouseEvent(QGraphicsSceneWheelEvent * e,NotationMouseEvent & nme)733 NotationScene::setupMouseEvent(QGraphicsSceneWheelEvent *e,
734                                NotationMouseEvent &nme)
735 {
736     setupMouseEvent(e->scenePos(), e->buttons(), e->modifiers(), nme);
737 }
738 
739 
740 void
setupMouseEvent(QPointF scenePos,Qt::MouseButtons buttons,Qt::KeyboardModifiers modifiers,NotationMouseEvent & nme)741 NotationScene::setupMouseEvent(QPointF scenePos, Qt::MouseButtons buttons,
742                                Qt::KeyboardModifiers modifiers,
743                                NotationMouseEvent &nme)
744 {
745     Profiler profiler("NotationScene::setupMouseEvent");
746 
747     double sx = scenePos.x();
748     int sy = lrint(scenePos.y());
749 
750     nme.sceneX = sx;
751     nme.sceneY = sy;
752 
753     nme.modifiers = modifiers;
754     nme.buttons = buttons;
755     nme.element = nullptr;
756     nme.staff = getStaffForSceneCoords(sx, sy);
757 
758     bool haveClickHeight = false;
759 
760     //!!! are any of our tools able to make proper use of e->time?
761     // would it be more useful if it was the absolute time of e->element
762     // rather than the click time on the staff?
763 
764     if (nme.staff) {
765 
766         Event *clefEvent = nullptr, *keyEvent = nullptr;
767         NotationElementList::iterator i =
768             nme.staff->getElementUnderSceneCoords(sx, sy, clefEvent, keyEvent);
769 
770         if (i != nme.staff->getViewElementList()->end()) {
771             nme.element = dynamic_cast<NotationElement *>(*i);
772         }
773         if (clefEvent) nme.clef = Clef(*clefEvent);
774 
775 //        RG_DEBUG << "clef = " << nme.clef.getClefType() << " (have = " << (clefEvent != 0) << ")";
776 
777         if (keyEvent) nme.key = ::Rosegarden::Key(*keyEvent);
778 
779 //        RG_DEBUG << "key = " << nme.key.getName() << " (have = " << (keyEvent != 0) << ")";
780 
781         nme.time = nme.staff->getTimeAtSceneCoords(sx, sy);
782         nme.height = nme.staff->getHeightAtSceneCoords(sx, sy);
783         haveClickHeight = true;
784 
785     } else {
786         nme.element = nullptr;
787         nme.time = 0;
788         nme.height = 0;
789     }
790 
791     // we've discovered what the context is -- now check whether we're
792     // clicking on something specific
793 
794     const QList<QGraphicsItem *> collisions = items(scenePos);
795 
796     NotationElement *clickedNote = nullptr;
797     NotationElement *clickedVagueNote = nullptr;
798     NotationElement *clickedNonNote = nullptr;
799 
800     for (QList<QGraphicsItem *>::const_iterator i = collisions.begin();
801          i != collisions.end(); ++i) {
802 
803         NotationElement *element = NotationElement::getNotationElement(*i);
804         if (!element) continue;
805 
806         // #957364 (Notation: Hard to select upper note in chords of
807         // seconds) -- adjust x-coord for shifted note head
808 
809         double cx = element->getSceneX();
810         int nbw = 10;
811 
812         nbw = m_notePixmapFactory->getNoteBodyWidth();
813         bool shifted = false;
814 
815         if (element->event()->get<Bool>
816             (m_properties->NOTE_HEAD_SHIFTED, shifted) && shifted) {
817             cx += nbw;
818         }
819 
820         if (element->isNote() && haveClickHeight) {
821 
822             long eventHeight = 0;
823 
824             if (element->event()->get<Int>
825                 (NotationProperties::HEIGHT_ON_STAFF, eventHeight)) {
826 
827                 if (eventHeight == nme.height) {
828 
829                     if (!clickedNote &&
830                         nme.sceneX >= cx &&
831                         nme.sceneX <= cx + nbw) {
832                         clickedNote = element;
833                     } else if (!clickedVagueNote &&
834                                nme.sceneX >= cx - 2 &&
835                                nme.sceneX <= cx + nbw + 2) {
836                         clickedVagueNote = element;
837                     }
838 
839                 } else if (eventHeight - 1 == nme.height ||
840                            eventHeight + 1 == nme.height) {
841                     if (!clickedVagueNote) {
842                         clickedVagueNote = element;
843                     }
844                 }
845             }
846         } else if (!element->isNote()) {
847             if (!clickedNonNote) {
848                 clickedNonNote = element;
849             }
850         }
851     }
852 
853     nme.exact = false;
854 
855     if (clickedNote) {
856         nme.element = clickedNote;
857         nme.exact = true;
858     } else if (clickedNonNote) {
859         nme.element = clickedNonNote;
860         nme.exact = true;
861     } else if (clickedVagueNote) {
862         nme.element = clickedVagueNote;
863         nme.exact = true;
864     }
865 
866     /*RG_DEBUG << "NotationScene::setupMouseEvent: sx = " << sx
867                    << ", sy = " << sy
868                    << ", modifiers = " << nme.modifiers
869                    << ", buttons = " << nme.buttons
870                    << ", element = " << nme.element
871                    << ", staff = " << nme.staff
872                    << " (id = " << (nme.staff ? nme.staff->getId() : -1) << ")"
873                    << ", clef = " << nme.clef.getClefType()
874                    << ", key = " << nme.key.getName()
875                    << ", time = " << nme.time
876                    << ", height = " << nme.height;*/
877 }
878 
879 void
slotMouseLeavesView()880 NotationScene::slotMouseLeavesView()
881 {
882     clearPreviewNote(m_previewNoteStaff);
883 }
884 
885 void
mousePressEvent(QGraphicsSceneMouseEvent * e)886 NotationScene::mousePressEvent(QGraphicsSceneMouseEvent *e)
887 {
888     NotationMouseEvent nme;
889     setupMouseEvent(e, nme);
890     ///! Warning, this short-circuits NotationView::setCurrentStaff...
891     if (nme.staff) setCurrentStaff(nme.staff);
892     emit mousePressed(&nme);
893 }
894 
895 void
mouseMoveEvent(QGraphicsSceneMouseEvent * e)896 NotationScene::mouseMoveEvent(QGraphicsSceneMouseEvent *e)
897 {
898     NotationMouseEvent nme;
899     setupMouseEvent(e, nme);
900     emit mouseMoved(&nme);
901 }
902 
903 void
mouseReleaseEvent(QGraphicsSceneMouseEvent * e)904 NotationScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *e)
905 {
906     NotationMouseEvent nme;
907     setupMouseEvent(e, nme);
908     emit mouseReleased(&nme);
909 }
910 
911 void
mouseDoubleClickEvent(QGraphicsSceneMouseEvent * e)912 NotationScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *e)
913 {
914     NotationMouseEvent nme;
915     setupMouseEvent(e, nme);
916     emit mouseDoubleClicked(&nme);
917 }
918 
919 void
wheelEvent(QGraphicsSceneWheelEvent * e)920 NotationScene::wheelEvent(QGraphicsSceneWheelEvent *e)
921 {
922     if (m_widget->getCurrentTool()->needsWheelEvents()) {
923         NotationMouseEvent nme;
924         setupMouseEvent(e, nme);
925         emit wheelTurned(e->delta(), &nme);
926         e->accept();    // Don't pass the event to the view
927     }
928 }
929 
930 void
keyPressEvent(QKeyEvent * keyEvent)931 NotationScene::keyPressEvent(QKeyEvent * keyEvent)
932 {
933     processKeyboardEvent(keyEvent);
934 }
935 
936 void
keyReleaseEvent(QKeyEvent * keyEvent)937 NotationScene::keyReleaseEvent(QKeyEvent * keyEvent)
938 {
939     processKeyboardEvent(keyEvent);
940 }
941 
942 void
processKeyboardEvent(QKeyEvent * keyEvent)943 NotationScene::processKeyboardEvent(QKeyEvent * keyEvent)
944 {
945     int key = keyEvent->key();
946     if ((key == Qt::Key_Shift) || (key == Qt::Key_Control)) {
947 
948         // Get the global coordinates of the cursor and convert them to
949         // scene coordinates
950         QPoint globalPos = QCursor::pos();
951         QPoint pos = m_widget->getView()->viewport()->mapFromGlobal(globalPos);
952         QPointF scenePos = m_widget->getView()->mapToScene(pos);
953 
954         // Create a NotationMouseEvent related to the QKeyEvent.
955         // Use queryKeyboardModifiers() rather than keyboardModifiers()
956         // to ensure the current value of modifiers is read.
957         NotationMouseEvent nme;
958         setupMouseEvent(scenePos, QApplication::mouseButtons(),
959                         QApplication::queryKeyboardModifiers(), nme);
960 
961         // Handle it as a mouse event
962         emit mouseMoved(&nme);
963     }
964 }
965 
966 int
getPageWidth()967 NotationScene::getPageWidth()
968 {
969     if (m_pageMode != StaffLayout::MultiPageMode) {
970 
971         if (isInPrintMode()) {
972             return sceneRect().width();
973         }
974 
975         return m_widget->width() -
976 //!!!            m_widget->verticalScrollBar()->width() -
977             m_leftGutter - 10;
978 
979     } else {
980 
981         //!!! For the moment we use A4 for this calculation
982 
983         double printSizeMm = 25.4 * ((double)m_printSize / 72.0);
984         double mmPerPixel = printSizeMm / (double)m_notePixmapFactory->getSize();
985         return (int)(210.0 / mmPerPixel);
986     }
987 }
988 
989 int
getPageHeight()990 NotationScene::getPageHeight()
991 {
992     if (m_pageMode != StaffLayout::MultiPageMode) {
993 
994         if (isInPrintMode()) {
995             return sceneRect().height();
996         }
997 
998         return m_widget->height();
999 
1000     } else {
1001 
1002         //!!! For the moment we use A4 for this calculation
1003 
1004         double printSizeMm = 25.4 * ((double)m_printSize / 72.0);
1005         double mmPerPixel = printSizeMm / (double)m_notePixmapFactory->getSize();
1006         return (int)(297.0 / mmPerPixel);
1007     }
1008 }
1009 
1010 void
getPageMargins(int & left,int & top)1011 NotationScene::getPageMargins(int &left, int &top)
1012 {
1013     if (m_pageMode != StaffLayout::MultiPageMode) {
1014 
1015         left = 0;
1016         top = 0;
1017 
1018     } else {
1019 
1020         //!!! For the moment we use A4 for this calculation
1021 
1022         double printSizeMm = 25.4 * ((double)m_printSize / 72.0);
1023         double mmPerPixel = printSizeMm / (double)m_notePixmapFactory->getSize();
1024         left = (int)(20.0 / mmPerPixel);
1025         top = (int)(15.0 / mmPerPixel);
1026     }
1027 }
1028 
1029 void
setPageMode(StaffLayout::PageMode mode)1030 NotationScene::setPageMode(StaffLayout::PageMode mode)
1031 {
1032     if (m_pageMode == mode) return;
1033     m_pageMode = mode;
1034 
1035     int pageWidth = getPageWidth();
1036     int topMargin = 0, leftMargin = 0;
1037     getPageMargins(leftMargin, topMargin);
1038 
1039     m_hlayout->setPageMode(m_pageMode != StaffLayout::LinearMode);
1040     m_hlayout->setPageWidth(pageWidth - leftMargin * 2);
1041 
1042     NOTATION_DEBUG << "NotationScene::setPageMode: set layout's page width to "
1043                    << (pageWidth - leftMargin * 2);
1044 
1045     if (!m_updatesSuspended) {
1046         positionStaffs();
1047         layoutAll();
1048     }
1049 /*!!!
1050     positionPages();
1051 
1052 
1053     if (!m_printMode) {
1054         updateView();
1055         slotSetInsertCursorPosition(getInsertionTime(), false, false);
1056         slotSetPointerPosition(getDocument()->getComposition().getPosition(), false);
1057     }
1058 */
1059 }
1060 
1061 void
slotCommandExecuted()1062 NotationScene::slotCommandExecuted()
1063 {
1064     checkUpdate();
1065 }
1066 
1067 void
timeSignatureChanged(const Composition * c)1068 NotationScene::timeSignatureChanged(const Composition *c)
1069 {
1070     if (!m_document || !c || (c != &m_document->getComposition())) return;
1071     m_timeSignatureChanged = true;
1072 }
1073 
1074 timeT
getInsertionTime() const1075 NotationScene::getInsertionTime() const
1076 {
1077     if (!m_document) return 0;
1078     return snapTimeToNoteBoundary(m_document->getComposition().getPosition());
1079 }
1080 
1081 NotationScene::CursorCoordinates
getCursorCoordinates(timeT t) const1082 NotationScene::getCursorCoordinates(timeT t) const
1083 {
1084     if (m_staffs.empty() || !m_hlayout) return CursorCoordinates();
1085 
1086     NotationStaff *topStaff = nullptr;
1087     NotationStaff *bottomStaff = nullptr;
1088     for (uint i = 0; i < m_staffs.size(); ++i) {
1089         if (!m_staffs[i]) continue;
1090         if (!topStaff || m_staffs[i]->getY() < topStaff->getY()) {
1091             topStaff = m_staffs[i];
1092         }
1093         if (!bottomStaff || m_staffs[i]->getY() > bottomStaff->getY()) {
1094             bottomStaff = m_staffs[i];
1095         }
1096     }
1097 
1098     NotationStaff *currentStaff = nullptr;
1099     if (m_currentStaff < (int)m_staffs.size()) {
1100         currentStaff = m_staffs[m_currentStaff];
1101     }
1102 
1103     timeT snapped = snapTimeToNoteBoundary(t);
1104 
1105     double x = m_hlayout->getXForTime(t);
1106     double sx = m_hlayout->getXForTimeByEvent(snapped);
1107 
1108     StaffLayout::StaffLayoutCoords top =
1109         topStaff->getSceneCoordsForLayoutCoords
1110         (x, topStaff->getLayoutYForHeight(24));
1111 
1112     StaffLayout::StaffLayoutCoords bottom =
1113         bottomStaff->getSceneCoordsForLayoutCoords
1114         (x, bottomStaff->getLayoutYForHeight(-16));
1115 
1116     StaffLayout::StaffLayoutCoords singleTop = top;
1117     StaffLayout::StaffLayoutCoords singleBottom = bottom;
1118 
1119     if (currentStaff) {
1120         singleTop =
1121             currentStaff->getSceneCoordsForLayoutCoords
1122             (sx, currentStaff->getLayoutYForHeight(16));
1123         singleBottom =
1124             currentStaff->getSceneCoordsForLayoutCoords
1125             (sx, currentStaff->getLayoutYForHeight(-8));
1126     }
1127 
1128     CursorCoordinates cc;
1129     cc.allStaffs = QLineF(top.first, top.second,
1130                           bottom.first, bottom.second);
1131     cc.currentStaff = QLineF(singleTop.first, singleTop.second,
1132                              singleBottom.first, singleBottom.second);
1133     return cc;
1134 }
1135 
1136 timeT
snapTimeToNoteBoundary(timeT t) const1137 NotationScene::snapTimeToNoteBoundary(timeT t) const
1138 {
1139     NotationStaff *s = nullptr;
1140     if (m_currentStaff < (int)m_staffs.size()) {
1141         s = m_staffs[m_currentStaff];
1142     }
1143     if (!s) return t;
1144 
1145     ViewElementList *v = s->getViewElementList();
1146     ViewElementList::iterator i = v->findNearestTime(t);
1147     if (i == v->end()) i = v->begin();
1148     if (i == v->end()) return t;
1149 
1150     return (*i)->getViewAbsoluteTime();
1151 }
1152 
1153 void
checkUpdate()1154 NotationScene::checkUpdate()
1155 {
1156     bool need = false;
1157     bool all = false;
1158     timeT start = 0, end = 0;
1159     int count = 0;
1160     NotationStaff *single = nullptr;
1161 
1162     bool compositionModified = m_document &&
1163         m_document->getComposition().getRefreshStatus
1164         (m_compositionRefreshStatusId).needsRefresh();
1165 
1166     for (unsigned int i = 0; i < m_staffs.size(); ++i) {
1167 
1168         SegmentRefreshStatus &rs = m_staffs[i]->getRefreshStatus();
1169 
1170         if (m_timeSignatureChanged ||
1171             (rs.needsRefresh() && compositionModified)) {
1172 
1173             need = true;
1174             all = true;
1175 
1176             // don't break, because we want to reset refresh statuses
1177             // on other segments as well
1178 
1179         } else if (rs.needsRefresh()) {
1180 
1181             if (!need || rs.from() < start) start = rs.from();
1182             if (!need || rs.to() > end) end = rs.to();
1183 
1184             need = true;
1185 
1186             single = m_staffs[i];
1187             ++count;
1188         }
1189 
1190         rs.setNeedsRefresh(false);
1191     }
1192 
1193     m_timeSignatureChanged = false;
1194     m_document->getComposition().getRefreshStatus
1195         (m_compositionRefreshStatusId).setNeedsRefresh(false);
1196 
1197     if (need) {
1198         if (all) layoutAll();
1199         else {
1200             // Test count to fix bug #2973777
1201             if (count == 1) layout(single, start, end);
1202             else  layout(nullptr, start, end);
1203         }
1204     }
1205 }
1206 
1207 ///YG: Only for debug
1208 void
dumpVectors()1209 NotationScene::dumpVectors()
1210 {
1211     RG_DEBUG << "dumpVectors begin";
1212     for (unsigned int i=0; i<m_externalSegments.size(); ++i) {
1213         RG_DEBUG << "extern" << i << ":" << m_externalSegments[i] <<
1214             m_externalSegments[i]->getLabel().c_str();
1215         if (m_externalSegments[i]->isTmp()) RG_DEBUG << " TMP";
1216         if (m_externalSegments[i]->isLinked()) RG_DEBUG << " LINKED";
1217         if (m_externalSegments[i]->isTrulyLinked()) RG_DEBUG << " TRULYLINKED";
1218         RG_DEBUG << "start=" << m_externalSegments[i]->getStartTime()
1219                   << "endMrkr=" << m_externalSegments[i]->getEndMarkerTime();
1220     }
1221     for (unsigned int i=0; i<m_clones.size(); ++i) {
1222         RG_DEBUG << "clones" << i << ":" << m_clones[i];
1223         if (m_clones[i]->isTmp()) RG_DEBUG << " TMP";
1224         RG_DEBUG << "start=" << m_clones[i]->getStartTime()
1225                   << "endMrkr=" << m_clones[i]->getEndMarkerTime();
1226     }
1227     for (unsigned int i=0; i<m_segments.size(); ++i) {
1228         RG_DEBUG << "segment" << i << ":" << m_segments[i] <<
1229             m_segments[i]->getLabel().c_str();
1230         if (m_segments[i]->isTmp()) RG_DEBUG << " TMP";
1231         m_segments[i]->dumpObservers();
1232     }
1233     for (unsigned int i=0; i<m_staffs.size(); ++i) {
1234         RG_DEBUG << "staff" << i << ":" << &m_staffs[i]->getSegment() <<
1235             m_staffs[i]->getSegment().getLabel().c_str();
1236         if (m_staffs[i]->getSegment().isTmp()) RG_DEBUG << " TMP";
1237     }
1238     RG_DEBUG << "dumpVectors end";
1239 }
1240 
1241 void
segmentRemoved(const Composition * c,Segment * s)1242 NotationScene::segmentRemoved(const Composition *c, Segment *s)
1243 {
1244     NOTATION_DEBUG << "NotationScene::segmentRemoved(" << c << "," << s << ")";
1245     if (!m_document || !c || (c != &m_document->getComposition())) return;
1246 
1247     for (std::vector<NotationStaff *>::iterator i = m_staffs.begin();
1248          i != m_staffs.end(); ++i) {
1249         if (s == &(*i)->getSegment()) {
1250 
1251             m_segmentsDeleted.push_back(s); // Remember segment to be deleted
1252 
1253             // The segmentDeleted() signal is about to be emitted. Therefore
1254             // the whole scene is going to be deleted then restored (from
1255             // NotationView) and to continue processing at best is useless and
1256             // at the worst may cause a crash when the segment is deleted.
1257             disconnect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
1258                        this, SLOT(slotCommandExecuted()));
1259             suspendLayoutUpdates();   // Useful ???
1260 
1261             if (m_segmentsDeleted.size() == m_externalSegments.size()) {
1262                 // There will be no more segment in scene.
1263                 m_sceneIsEmpty = true;
1264             }
1265 
1266             // Signal must be emitted only once. Nevertheless, all removed
1267             // segments have to be remembered.
1268             if (!m_finished) emit sceneNeedsRebuilding();
1269             m_finished = true; // Stop further processing from this scene
1270 
1271             break;
1272         }
1273     }
1274 }
1275 
1276 void
segmentRepeatChanged(const Composition * c,Segment * s,bool)1277 NotationScene::segmentRepeatChanged(const Composition *c, Segment *s, bool)
1278 {
1279     if (!m_document || !c || (c != &m_document->getComposition())) return;
1280 
1281     // Signal must be emitted only once (or the same scene will be recreated
1282     // several time which may be very time consuming).
1283     if (m_finished) return;
1284 
1285     for (std::vector<Segment *>::iterator i = m_externalSegments.begin();
1286          i != m_externalSegments.end(); ++i) {
1287         if (s == *i) {
1288             // The segmentRepeatModified() signal is about to be emitted.
1289             // Therefore the whole scene is going to be deleted then restored
1290             // (from NotationView) and there is no point to continue processing
1291             // signals from the current NotationScene.
1292             disconnect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
1293                        this, SLOT(slotCommandExecuted()));
1294             suspendLayoutUpdates();
1295             m_finished = true;    // Stop further processing from this scene
1296 
1297             emit sceneNeedsRebuilding();
1298             break;
1299         }
1300     }
1301 }
1302 
1303 void
segmentRepeatEndChanged(const Composition * c,Segment * s,timeT)1304 NotationScene::segmentRepeatEndChanged(const Composition *c, Segment *s, timeT)
1305 {
1306     if (!m_document || !c || (c != &m_document->getComposition())) return;
1307 
1308     // Signal must be emitted only once (or the same scene will be recreated
1309     // several time which may be very time consuming).
1310     if (m_finished) return;
1311 
1312     for (std::vector<Segment *>::iterator i = m_externalSegments.begin();
1313          i != m_externalSegments.end(); ++i) {
1314         if (s == *i) {
1315 
1316             // The segmentRepeatModified() signal is about to be emitted.
1317             // Therefore the whole scene is going to be deleted then restored
1318             // (from NotationView) and to continue processing at best is
1319             // useless and at worst may cause a crash related to deleted clones.
1320             disconnect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
1321                        this, SLOT(slotCommandExecuted()));
1322             suspendLayoutUpdates();
1323             m_finished = true;    // Stop further processing from this scene
1324 
1325             emit sceneNeedsRebuilding();
1326             break;
1327         }
1328     }
1329 }
1330 
1331 void
segmentStartChanged(const Composition * c,Segment * s,timeT)1332 NotationScene::segmentStartChanged(const Composition *c, Segment *s, timeT)
1333 {
1334     if (!m_document || !c || (c != &m_document->getComposition())) return;
1335 
1336     // Signal must be emitted only once (else the same scene will be recreated
1337     // several time which may be very time consuming).
1338     if (m_finished) return;
1339 
1340     for (std::vector<Segment *>::iterator i = m_externalSegments.begin();
1341          i != m_externalSegments.end(); ++i) {
1342         if ((s == *i) && (s->isRepeating())) {
1343 
1344             // The segmentRepeatModified() signal is about to be emitted.
1345             // Therefore the whole scene is going to be deleted then restored
1346             // (from NotationView) and to continue processing at best is
1347             // useless and at worst may cause a crash related to deleted clones.
1348             disconnect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
1349                        this, SLOT(slotCommandExecuted()));
1350             suspendLayoutUpdates();
1351             m_finished = true;    // Stop further processing from this scene
1352 
1353             emit sceneNeedsRebuilding();
1354             break;
1355         }
1356     }
1357 }
1358 
1359 void
segmentEndMarkerChanged(const Composition * c,Segment * s,bool)1360 NotationScene::segmentEndMarkerChanged(const Composition *c, Segment *s, bool)
1361 {
1362     if (!m_document || !c || (c != &m_document->getComposition())) return;
1363 
1364     // Signal must be emitted only once (or the same scene will be recreated
1365     // several time which may be very time consuming).
1366     if (m_finished) return;
1367 
1368     for (std::vector<Segment *>::iterator i = m_externalSegments.begin();
1369          i != m_externalSegments.end(); ++i) {
1370         if ((s == *i) && (s->isRepeating())) {
1371 
1372             // The segmentRepeatModified() signal is about to be emitted.
1373             // Therefore the whole scene is going to be deleted then restored
1374             // (from NotationView) and to continue processing at best is
1375             // useless and at worst may cause a crash related to deleted clones.
1376             disconnect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
1377                        this, SLOT(slotCommandExecuted()));
1378             suspendLayoutUpdates();
1379             m_finished = true;    // Stop further processing from this scene
1380 
1381             emit sceneNeedsRebuilding();
1382             break;
1383         }
1384     }
1385 }
1386 
1387 void
trackChanged(const Composition * c,Track * t)1388 NotationScene::trackChanged(const Composition *c, Track *t)
1389 {
1390     if (!m_document || !c || (c != &m_document->getComposition())) return;
1391 
1392     // Signal must be emitted only once (or the same scene will be recreated
1393     // several time which may be very time consuming).
1394     if (m_finished) return;
1395 
1396     TrackId trackId = t->getId();   // Id of changed track
1397 
1398     for (std::vector<Segment *>::iterator i = m_externalSegments.begin();
1399          i != m_externalSegments.end(); ++i) {
1400 
1401         // Is the segment part of the changed track ?
1402         if ((*i)->getTrack() == trackId) {
1403 
1404             // The scene needs a rebuild only if what has changed is the
1405             // name of the track
1406             if (t->getLabel() == m_trackLabels[trackId]) break;
1407 
1408             // The whole scene is going to be deleted then restored
1409             // (from NotationView). To continue processing at best is
1410             // useless and at worst may cause a crash related to deleted clones.
1411             disconnect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
1412                        this, SLOT(slotCommandExecuted()));
1413             suspendLayoutUpdates();
1414             m_finished = true;    // Stop further processing from this scene
1415 
1416             emit sceneNeedsRebuilding();
1417             break;
1418         }
1419    }
1420 }
1421 
1422 void
positionStaffs()1423 NotationScene::positionStaffs()
1424 {
1425     NOTATION_DEBUG << "NotationView::positionStaffs";
1426     if (m_staffs.empty()) return;
1427 
1428     QSettings settings;
1429     settings.beginGroup(NotationViewConfigGroup);
1430 
1431     m_printSize = settings.value("printingnotesize", 5).toUInt() ;
1432 
1433     m_minTrack = m_maxTrack = 0;
1434     bool haveMinTrack = false;
1435 
1436     m_trackHeights.clear();
1437     m_trackCoords.clear();
1438 
1439     int pageWidth, pageHeight, leftMargin, topMargin;
1440     pageWidth = getPageWidth();
1441     pageHeight = getPageHeight();
1442     leftMargin = 0, topMargin = 0;
1443     getPageMargins(leftMargin, topMargin);
1444 
1445     int accumulatedHeight;
1446     int rowsPerPage = 1;
1447     int legerLines = 8;
1448     if (m_pageMode != StaffLayout::LinearMode) legerLines = 7;
1449     int rowGapPercent = (m_staffs.size() > 1 ? 40 : 10);
1450     int aimFor = -1;
1451 
1452     bool done = false;
1453 
1454     int titleHeight = 0;
1455 
1456     delete m_title;
1457     delete m_subtitle;
1458     delete m_composer;
1459     delete m_copyright;
1460     m_title = m_subtitle = m_composer = m_copyright = nullptr;
1461 
1462     if (m_pageMode == StaffLayout::MultiPageMode) {
1463 
1464         const Configuration &metadata =
1465             m_document->getComposition().getMetadata();
1466 
1467         QFont defaultFont(NotePixmapFactory::defaultSerifFontFamily);
1468 
1469         QVariant fv = settings.value("textfont", defaultFont);
1470         QFont font(defaultFont);
1471         if (fv.canConvert<QFont>()) font = fv.value<QFont>();
1472 
1473         font.setPixelSize(m_notePixmapFactory->getSize() * 5);
1474         QFontMetrics metrics(font);
1475 
1476         if (metadata.has(CompositionMetadataKeys::Title)) {
1477             QString title(strtoqstr(metadata.get<String>
1478                                     (CompositionMetadataKeys::Title)));
1479             m_title = new QGraphicsTextItem(title);
1480             m_title->setDefaultTextColor(Qt::black);
1481             addItem(m_title);
1482             m_title->setFont(font);
1483             m_title->setPos(m_leftGutter + pageWidth / 2 - metrics.boundingRect(title).width() / 2,
1484                             20 + topMargin / 4 + metrics.ascent());
1485             m_title->show();
1486             titleHeight += metrics.height() * 3 / 2 + topMargin / 4;
1487         }
1488 
1489         font.setPixelSize(m_notePixmapFactory->getSize() * 3);
1490         metrics = QFontMetrics(font);
1491 
1492         if (metadata.has(CompositionMetadataKeys::Subtitle)) {
1493             QString subtitle(strtoqstr(metadata.get<String>
1494                                        (CompositionMetadataKeys::Subtitle)));
1495             m_subtitle = new QGraphicsTextItem(subtitle);
1496             m_subtitle->setDefaultTextColor(Qt::black);
1497             addItem(m_subtitle);
1498             m_subtitle->setFont(font);
1499             m_subtitle->setPos(m_leftGutter + pageWidth / 2 - metrics.boundingRect(subtitle).width() / 2,
1500                                20 + titleHeight + metrics.ascent());
1501             m_subtitle->show();
1502             titleHeight += metrics.height() * 3 / 2;
1503         }
1504 
1505         if (metadata.has(CompositionMetadataKeys::Composer)) {
1506             QString composer(strtoqstr(metadata.get<String>
1507                                        (CompositionMetadataKeys::Composer)));
1508             m_composer = new QGraphicsTextItem(composer);
1509             m_composer->setDefaultTextColor(Qt::black);
1510             addItem(m_composer);
1511             m_composer->setFont(font);
1512             m_composer->setPos(m_leftGutter + pageWidth - metrics.boundingRect(composer).width() - leftMargin,
1513                                20 + titleHeight + metrics.ascent());
1514             m_composer->show();
1515             titleHeight += metrics.height() * 3 / 2;
1516         }
1517 
1518         font.setPixelSize(m_notePixmapFactory->getSize() * 2);
1519         metrics = QFontMetrics(font);
1520 
1521         if (metadata.has(CompositionMetadataKeys::Copyright)) {
1522             QString copyright(strtoqstr(metadata.get<String>
1523                                         (CompositionMetadataKeys::Copyright)));
1524             m_copyright = new QGraphicsTextItem(copyright);
1525             m_copyright->setDefaultTextColor(Qt::black);
1526             addItem(m_copyright);
1527             m_copyright->setFont(font);
1528             m_copyright->setPos(m_leftGutter + leftMargin,
1529                                 20 + pageHeight - topMargin - metrics.descent());
1530             m_copyright->show();
1531         }
1532     }
1533     settings.endGroup();
1534 
1535     while (1) {
1536 
1537         accumulatedHeight = 0;
1538         int maxTrackHeight = 0;
1539 
1540         m_trackHeights.clear();
1541 
1542         for (unsigned int i = 0; i < m_staffs.size(); ++i) {
1543 
1544             m_staffs[i]->setLegerLineCount(legerLines);
1545 
1546             int height = m_staffs[i]->getHeightOfRow();
1547             Segment &segment = m_staffs[i]->getSegment();
1548             TrackId trackId = segment.getTrack();
1549             Composition *composition = segment.getComposition();
1550             Q_ASSERT(composition);
1551             Track *track = composition->getTrackById(trackId);
1552 
1553             if (!track)
1554                 continue; // This Should Not Happen, My Friend
1555 
1556             int trackPosition = track->getPosition();
1557 
1558             TrackIntMap::iterator hi = m_trackHeights.find(trackPosition);
1559             if (hi == m_trackHeights.end()) {
1560                 m_trackHeights.insert(TrackIntMap::value_type
1561                                     (trackPosition, height));
1562             } else if (height > hi->second) {
1563                 hi->second = height;
1564             }
1565 
1566             if (height > maxTrackHeight)
1567                 maxTrackHeight = height;
1568 
1569             if (trackPosition < m_minTrack || !haveMinTrack) {
1570                 m_minTrack = trackPosition;
1571                 haveMinTrack = true;
1572             }
1573             if (trackPosition > m_maxTrack) {
1574                 m_maxTrack = trackPosition;
1575             }
1576         }
1577 
1578         for (int i = m_minTrack; i <= m_maxTrack; ++i) {
1579             TrackIntMap::iterator hi = m_trackHeights.find(i);
1580             if (hi != m_trackHeights.end()) {
1581                 m_trackCoords[i] = accumulatedHeight;
1582                 accumulatedHeight += hi->second;
1583             }
1584         }
1585 
1586         accumulatedHeight += maxTrackHeight * rowGapPercent / 100;
1587 
1588         if (done)
1589             break;
1590 
1591         if (m_pageMode != StaffLayout::MultiPageMode) {
1592 
1593             rowsPerPage = 0;
1594             done = true;
1595             break;
1596 
1597         } else {
1598 
1599             // Check how well all this stuff actually fits on the
1600             // page.  If things don't fit as well as we'd like, modify
1601             // at most one parameter so as to save some space, then
1602             // loop around again and see if it worked.  This iterative
1603             // approach is inefficient but the time spent here is
1604             // neglible in context, and it's a simple way to code it.
1605 
1606             int staffPageHeight = pageHeight - topMargin * 2 - titleHeight;
1607             rowsPerPage = staffPageHeight / accumulatedHeight;
1608 
1609             if (rowsPerPage < 1) {
1610 
1611                 if (legerLines > 5)
1612                     --legerLines;
1613                 else if (rowGapPercent > 20)
1614                     rowGapPercent -= 10;
1615                 else if (legerLines > 4)
1616                     --legerLines;
1617                 else if (rowGapPercent > 0)
1618                     rowGapPercent -= 10;
1619                 else if (legerLines > 3)
1620                     --legerLines;
1621                 else if (m_printSize > 3)
1622                     --m_printSize;
1623                 else { // just accept that we'll have to overflow
1624                     rowsPerPage = 1;
1625                     done = true;
1626                 }
1627 
1628             } else {
1629 
1630                 if (aimFor == rowsPerPage) {
1631 
1632                     titleHeight +=
1633                         (staffPageHeight - (rowsPerPage * accumulatedHeight)) / 2;
1634 
1635                     done = true;
1636 
1637                 } else {
1638 
1639                     if (aimFor == -1)
1640                         aimFor = rowsPerPage + 1;
1641 
1642                     // we can perhaps accommodate another row, with care
1643                     if (legerLines > 5)
1644                         --legerLines;
1645                     else if (rowGapPercent > 20)
1646                         rowGapPercent -= 10;
1647                     else if (legerLines > 3)
1648                         --legerLines;
1649                     else if (rowGapPercent > 0)
1650                         rowGapPercent -= 10;
1651                     else { // no, we can't
1652                         rowGapPercent = 0;
1653                         legerLines = 8;
1654                         done = true;
1655                     }
1656                 }
1657             }
1658         }
1659     }
1660 
1661     m_hlayout->setPageWidth(pageWidth - leftMargin * 2);
1662 
1663     int topGutter = 0;
1664 
1665     if (m_pageMode == StaffLayout::MultiPageMode) {
1666 
1667         topGutter = 20;
1668 
1669     } else if (m_pageMode == StaffLayout::ContinuousPageMode) {
1670 
1671         // fewer leger lines above staff than in linear mode --
1672         // compensate for this on the top staff
1673         topGutter = m_notePixmapFactory->getLineSpacing() * 2;
1674     }
1675 
1676     for (unsigned int i = 0; i < m_staffs.size(); ++i) {
1677 
1678         TrackId trackId = m_staffs[i]->getSegment().getTrack();
1679         Track *track =
1680             m_staffs[i]->getSegment().getComposition()->
1681             getTrackById(trackId);
1682 
1683         if (!track)
1684             continue; // Once Again, My Friend, You Should Never See Me Here
1685 
1686         int trackPosition = track->getPosition();
1687 
1688         m_staffs[i]->setTitleHeight(titleHeight);
1689         m_staffs[i]->setRowSpacing(accumulatedHeight);
1690 
1691         if (trackPosition < m_maxTrack) {
1692             m_staffs[i]->setConnectingLineLength(m_trackHeights[trackPosition]);
1693         }
1694 
1695         if (trackPosition == m_minTrack &&
1696             m_pageMode != StaffLayout::LinearMode) {
1697             m_staffs[i]->setBarNumbersEvery(5);
1698         } else {
1699             m_staffs[i]->setBarNumbersEvery(0);
1700         }
1701 
1702         m_staffs[i]->setX(m_leftGutter);
1703         m_staffs[i]->setY(topGutter + m_trackCoords[trackPosition] + topMargin);
1704         m_staffs[i]->setPageWidth(pageWidth - leftMargin * 2);
1705         m_staffs[i]->setRowsPerPage(rowsPerPage);
1706         m_staffs[i]->setPageMode(m_pageMode);
1707         m_staffs[i]->setMargin(leftMargin);
1708 
1709         NOTATION_DEBUG << "NotationScene::positionStaffs: set staff's page width to "
1710                        << (pageWidth - leftMargin * 2);
1711 
1712     }
1713 
1714     // Notation headers must be regenerated
1715     emit staffsPositionned();
1716 }
1717 
1718 void
layoutAll()1719 NotationScene::layoutAll()
1720 {
1721     Profiler profiler("NotationScene::layoutAll", true);
1722     layout(nullptr, 0, 0);
1723 }
1724 
1725 void
layout(NotationStaff * singleStaff,timeT startTime,timeT endTime)1726 NotationScene::layout(NotationStaff *singleStaff,
1727                       timeT startTime, timeT endTime)
1728 {
1729     Profiler profiler("NotationScene::layout", true);
1730     NOTATION_DEBUG << "NotationScene::layout: from " << startTime << " to " << endTime;
1731 
1732     bool full = (singleStaff == nullptr && startTime == endTime);
1733 
1734     m_hlayout->setViewSegmentCount(m_staffs.size());
1735 
1736     if (full) {
1737 
1738         Profiler profiler("NotationScene::layout: Reset layouts for full scan", true);
1739 
1740         m_hlayout->reset();
1741         m_vlayout->reset();
1742 
1743         bool first = true;
1744 
1745         for (unsigned int i = 0; i < m_segments.size(); ++i) {
1746 
1747             timeT thisStart = m_segments[i]->getClippedStartTime();
1748             timeT thisEnd = m_segments[i]->getEndMarkerTime();
1749 
1750             if (first || thisStart < startTime) startTime = thisStart;
1751             if (first || thisEnd > endTime) endTime = thisEnd;
1752 
1753             first = false;
1754         }
1755     }
1756 
1757     NOTATION_DEBUG << "overall start time =" << startTime << ", end time =" << endTime;
1758 
1759     {
1760         Profiler profiler("NotationScene::layout: Scan layouts", true);
1761     for (unsigned int i = 0; i < m_staffs.size(); ++i) {
1762 
1763         NotationStaff *staff = m_staffs[i];
1764 
1765         if (singleStaff && staff != singleStaff) continue;
1766 
1767         m_hlayout->scanViewSegment(*staff, startTime, endTime, full);
1768         m_vlayout->scanViewSegment(*staff, startTime, endTime, full);
1769     }
1770     }
1771 
1772     m_hlayout->finishLayout(startTime, endTime, full);
1773     m_vlayout->finishLayout(startTime, endTime, full);
1774 
1775     double maxWidth = 0.0;
1776     int maxHeight = 0;
1777 
1778     for (unsigned int i = 0; i < m_staffs.size(); ++i) {
1779 
1780         StaffLayout &staff = *m_staffs[i];
1781         staff.sizeStaff(*m_hlayout);
1782 
1783         if (staff.getTotalWidth() + staff.getX() > maxWidth) {
1784             maxWidth = staff.getTotalWidth() + staff.getX() + 1;
1785         }
1786 
1787         if (staff.getTotalHeight() + staff.getY() > maxHeight) {
1788             maxHeight = staff.getTotalHeight() + staff.getY() + 1;
1789         }
1790     }
1791 
1792     int topMargin = 0, leftMargin = 0;
1793     getPageMargins(leftMargin, topMargin);
1794 
1795     int pageWidth = getPageWidth();
1796     int pageHeight = getPageHeight();
1797 
1798     if (m_pageMode == StaffLayout::LinearMode) {
1799         maxWidth = ((maxWidth / pageWidth) + 1) * pageWidth;
1800         if (maxHeight < pageHeight) {
1801             maxHeight = pageHeight;
1802         }
1803     } else {
1804         if (maxWidth < pageWidth) {
1805             maxWidth = pageWidth;
1806         }
1807         if (maxHeight < pageHeight + topMargin*2) {
1808             maxHeight = pageHeight + topMargin * 2;
1809         }
1810     }
1811 
1812     setSceneRect(QRectF(0, 0, maxWidth, maxHeight));
1813 
1814     {
1815         Profiler profiler("NotationScene::layout: regeneration", true);
1816 
1817     for (unsigned int i = 0; i < m_staffs.size(); ++i) {
1818 
1819         NotationStaff *staff = m_staffs[i];
1820 
1821         // Secondary is true if this regeneration was caused by edits
1822         // to another staff, and the content of this staff has not
1823         // itself changed.
1824 
1825         // N.B. This test is exactly the opposite of the one used in
1826         // Rosegarden 1.7.x!  I think the prior code was simply wrong
1827         // and probably the cause of some refresh errors, but it seems
1828         // risky to "fix" it in a dead-end branch; at least here it
1829         // will necessarily get some testing.
1830 
1831         bool secondary = (singleStaff && (singleStaff != staff));
1832         staff->regenerate(startTime, endTime, secondary);
1833     }
1834     }
1835 
1836     emit layoutUpdated(startTime,endTime);
1837 }
1838 
1839 void
handleEventRemoved(Event * e)1840 NotationScene::handleEventRemoved(Event *e)
1841 {
1842     if (m_selection && m_selection->contains(e)) m_selection->removeEvent(e);
1843     emit eventRemoved(e);
1844 }
1845 
1846 void
setSelection(EventSelection * s,bool preview)1847 NotationScene::setSelection(EventSelection *s,
1848                             bool preview)
1849 {
1850     NOTATION_DEBUG << "NotationScene::setSelection: " << s;
1851 
1852     if (!m_selection && !s) return;
1853     if (m_selection == s) return;
1854     if (m_selection && s && *m_selection == *s) {
1855         // selections are identical, no need to update elements, but
1856         // still need to replace the old selection to avoid a leak
1857         // (can't just delete s in case caller still refers to it)
1858         EventSelection *oldSelection = m_selection;
1859         m_selection = s;
1860         delete oldSelection;
1861         return;
1862     }
1863 
1864     EventSelection *oldSelection = m_selection;
1865     m_selection = s;
1866 
1867     NotationStaff *oldStaff = nullptr, *newStaff = nullptr;
1868 
1869     if (oldSelection) {
1870         oldStaff = setSelectionElementStatus(oldSelection, false);
1871     }
1872 
1873     if (m_selection) {
1874         newStaff = setSelectionElementStatus(m_selection, true);
1875     }
1876 
1877     if (newStaff) {
1878         setCurrentStaff(newStaff);
1879     }
1880 
1881     if (oldSelection && m_selection && oldStaff && newStaff &&
1882         (oldStaff == newStaff)) {
1883 
1884         timeT oldFrom = oldSelection->getStartTime();
1885         timeT oldTo = oldSelection->getEndTime();
1886         timeT newFrom = m_selection->getStartTime();
1887         timeT newTo = m_selection->getEndTime();
1888 
1889         // if the regions overlap, render once
1890         if ((oldFrom <= newFrom && oldTo >= newFrom) ||
1891             (newFrom <= oldFrom && newTo >= oldFrom)) {
1892             newStaff->renderElements(std::min(oldFrom, newFrom),
1893                                      std::max(oldTo, newTo));
1894         } else {
1895             newStaff->renderElements(oldFrom, oldTo);
1896             newStaff->renderElements(newFrom, newTo);
1897         }
1898     } else {
1899         if (oldSelection && oldStaff) {
1900         oldStaff->renderElements(oldSelection->getStartTime(),
1901                                  oldSelection->getEndTime());
1902         }
1903         if (m_selection && newStaff) {
1904         newStaff->renderElements(m_selection->getStartTime(),
1905                                  m_selection->getEndTime());
1906         }
1907     }
1908 
1909     if (preview) previewSelection(m_selection, oldSelection);
1910 
1911     delete oldSelection;
1912 
1913     emit selectionChanged(m_selection);
1914     emit QGraphicsScene::selectionChanged();
1915 }
1916 
1917 void
setSingleSelectedEvent(NotationStaff * staff,NotationElement * e,bool preview)1918 NotationScene::setSingleSelectedEvent(NotationStaff *staff,
1919                                       NotationElement *e,
1920                                       bool preview)
1921 {
1922     if (!staff || !e) return;
1923     EventSelection *s = new EventSelection(staff->getSegment());
1924     s->addEvent(e->event());
1925     setSelection(s, preview);
1926 }
1927 
1928 void
setSingleSelectedEvent(Segment * seg,Event * e,bool preview)1929 NotationScene::setSingleSelectedEvent(Segment *seg,
1930                                       Event *e,
1931                                       bool preview)
1932 {
1933     if (!seg || !e) return;
1934     EventSelection *s = new EventSelection(*seg);
1935     s->addEvent(e);
1936     setSelection(s, preview);
1937 }
1938 
1939 NotationStaff *
setSelectionElementStatus(EventSelection * s,bool set)1940 NotationScene::setSelectionElementStatus(EventSelection *s, bool set)
1941 {
1942     if (!s) return nullptr;
1943 
1944     NotationStaff *staff = nullptr;
1945 
1946     for (std::vector<NotationStaff *>::iterator i = m_staffs.begin();
1947          i != m_staffs.end(); ++i) {
1948 
1949         if (&(*i)->getSegment() == &s->getSegment()) {
1950             staff = *i;
1951             break;
1952         }
1953     }
1954 
1955     if (!staff) return nullptr;
1956 
1957     for (EventSelection::eventcontainer::iterator i = s->getSegmentEvents().begin();
1958          i != s->getSegmentEvents().end(); ++i) {
1959 
1960         Event *e = *i;
1961 
1962         ViewElementList::iterator staffi = staff->findEvent(e);
1963         if (staffi == staff->getViewElementList()->end()) continue;
1964 
1965         NotationElement *el = static_cast<NotationElement *>(*staffi);
1966 
1967         el->setSelected(set);
1968     }
1969 
1970     return staff;
1971 }
1972 
1973 void
previewSelection(EventSelection * s,EventSelection * oldSelection)1974 NotationScene::previewSelection(EventSelection *s,
1975                                 EventSelection *oldSelection)
1976 {
1977     if (!s) return;
1978     if (!m_document->isSoundEnabled()) return;
1979 
1980     for (EventSelection::eventcontainer::iterator i = s->getSegmentEvents().begin();
1981          i != s->getSegmentEvents().end(); ++i) {
1982 
1983         Event *e = *i;
1984         if (oldSelection && oldSelection->contains(e)) continue;
1985 
1986         long pitch;
1987         if (e->get<Int>(BaseProperties::PITCH, pitch)) {
1988             long velocity = -1;
1989             (void)(e->get<Int>(BaseProperties::VELOCITY, velocity));
1990             if (!(e->has(BaseProperties::TIED_BACKWARD) &&
1991                   e->get<Bool>(BaseProperties::TIED_BACKWARD))) {
1992                 playNote(s->getSegment(), pitch, velocity);
1993             }
1994         }
1995     }
1996 }
1997 
1998 void
showPreviewNote(NotationStaff * staff,double layoutX,int pitch,int height,const Note & note,bool grace,Accidental accidental,bool cautious,QColor color,int velocity,bool play)1999 NotationScene::showPreviewNote(NotationStaff *staff, double layoutX,
2000                                int pitch, int height,
2001                                const Note &note,
2002                                bool grace,
2003                                Accidental accidental,
2004                                bool cautious,
2005                                QColor color,
2006                                int velocity,
2007                                bool play
2008                               )
2009 {
2010     if (staff) {
2011         staff->showPreviewNote(layoutX, height, note, grace,
2012                                accidental, cautious, color);
2013         m_previewNoteStaff = staff;
2014 
2015         if (play) playNote(staff->getSegment(), pitch, velocity);
2016     }
2017 }
2018 
2019 void
clearPreviewNote(NotationStaff * staff)2020 NotationScene::clearPreviewNote(NotationStaff *staff)
2021 {
2022     if (staff) {
2023         staff->clearPreviewNote();
2024         m_previewNoteStaff = nullptr;
2025     }
2026 }
2027 
2028 void
playNote(Segment & segment,int pitch,int velocity)2029 NotationScene::playNote(Segment &segment, int pitch, int velocity)
2030 {
2031     if (!m_document) return;
2032 
2033     Instrument *instrument = m_document->getStudio().getInstrumentFor(&segment);
2034 
2035     StudioControl::playPreviewNote(instrument,
2036                                    pitch + segment.getTranspose(),
2037                                    velocity,
2038                                    RealTime(0, 250000000));
2039 }
2040 
2041 bool
constrainToSegmentArea(QPointF & scenePos)2042 NotationScene::constrainToSegmentArea(QPointF &scenePos)
2043 {
2044     bool ok = true;
2045 
2046     NotationStaff *currentStaff = getCurrentStaff();
2047     if (!currentStaff) return ok;
2048 
2049     QRectF area = currentStaff->getSceneArea();
2050 
2051     double y = scenePos.y();
2052     if (y < area.top()) {
2053         scenePos.setY(area.top());
2054         ok = false;
2055     } else if (y > area.bottom()) {
2056         scenePos.setY(area.bottom());
2057         ok = false;
2058     }
2059 
2060     double x = scenePos.x();
2061     if (x < area.left()) {
2062         scenePos.setX(area.left());
2063         ok = false;
2064     } else if (x > area.right()) {
2065         scenePos.setX(area.right());
2066         ok = false;
2067     }
2068 
2069     return ok;
2070 }
2071 
2072 bool
isEventRedundant(Event * ev,Segment & seg) const2073 NotationScene::isEventRedundant(Event *ev, Segment &seg) const
2074 {
2075     if (ev->isa(Clef::EventType)) {
2076         Clef clef = Clef(*ev);
2077         timeT time = ev->getAbsoluteTime();
2078         TrackId track = seg.getTrack();
2079         Clef previousClef = m_clefKeyContext->getClefFromContext(track, time);
2080 
2081 // std::cout << "time=" << time << " clef=" << clef.getClefType()
2082 //           << " previous=" << previousClef.getClefType() << "\n";
2083 
2084 // m_clefKeyContext->dumpClefContext();
2085 
2086         return clef == previousClef;
2087     }
2088 
2089     if (ev->isa(Key::EventType)) {
2090         Key key = Key(*ev);
2091         timeT time = ev->getAbsoluteTime();
2092         TrackId track = seg.getTrack();
2093         Key previousKey = m_clefKeyContext->getKeyFromContext(track, time);
2094 
2095         return key == previousKey;
2096     }
2097 
2098     return false;
2099 }
2100 
2101 bool
isEventRedundant(Clef & clef,timeT time,Segment & seg) const2102 NotationScene::isEventRedundant(Clef &clef, timeT time, Segment &seg) const
2103 {
2104     TrackId track = seg.getTrack();
2105     Clef previousClef = m_clefKeyContext->getClefFromContext(track, time);
2106 
2107 // std::cout << "time=" << time << " clef=" << clef.getClefType()
2108 //           << " previous=" << previousClef.getClefType() << "\n";
2109 
2110 // m_clefKeyContext->dumpClefContext();
2111 
2112     return clef == previousClef;
2113 }
2114 
2115 bool
isEventRedundant(Key & key,timeT time,Segment & seg) const2116 NotationScene::isEventRedundant(Key &key, timeT time, Segment &seg) const
2117 {
2118     TrackId track = seg.getTrack();
2119     Key previousKey = m_clefKeyContext->getKeyFromContext(track, time);
2120 
2121     return key == previousKey;
2122 }
2123 
2124 bool
isAnotherStaffNearTime(NotationStaff * currentStaff,timeT t)2125 NotationScene::isAnotherStaffNearTime(NotationStaff *currentStaff, timeT t)
2126 {
2127     int bar = 0;
2128     Composition *composition = currentStaff->getSegment().getComposition();
2129     if (composition) bar = composition->getBarNumber(t);
2130 
2131     for (std::vector<NotationStaff *>::iterator i = m_staffs.begin();
2132          i != m_staffs.end(); ++i) {
2133         if (*i == currentStaff) continue;
2134 
2135         Segment &s = (*i)->getSegment();
2136         timeT start = s.getStartTime();
2137         timeT end = s.getEndMarkerTime();
2138         if ((start <= t) && (end >= t)) return true;
2139 
2140         if (composition) {
2141             int staffFirstBar = composition->getBarNumber(start);
2142             int staffLastBar = composition->getBarNumber(end);
2143             if ((staffFirstBar <= bar) && (staffLastBar >= bar)) return true;
2144         }
2145     }
2146 
2147     return false;
2148 }
2149 
2150 void
updateRefreshStatuses(TrackId track,timeT time)2151 NotationScene::updateRefreshStatuses(TrackId track, timeT time)
2152 {
2153     std::vector<Segment *>::iterator it;
2154     for ( it = m_segments.begin(); it != m_segments.end(); ++it) {
2155         if ((*it)->getTrack() != track) continue;
2156         timeT segEndTime = (*it)->getEndMarkerTime();
2157         if (time < segEndTime) (*it)->updateRefreshStatuses(time, segEndTime);
2158     }
2159 }
2160 
2161 void
updatePageSize()2162 NotationScene::updatePageSize()
2163 {
2164     layout(nullptr, 0, 0);
2165 }
2166 
2167 ///YG: Only for debug
2168 void
dumpBarDataMap()2169 NotationScene::dumpBarDataMap()
2170 {
2171     m_hlayout->dumpBarDataMap();
2172 }
2173 
2174 
2175 }
2176 
2177 
2178