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 ¬e,
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