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 "[MatrixScene]"
19
20 #include "MatrixScene.h"
21
22 #include "MatrixMouseEvent.h"
23 #include "MatrixViewSegment.h"
24 #include "MatrixWidget.h"
25 #include "MatrixElement.h"
26
27 #include "gui/application/RosegardenMainWindow.h"
28 #include "document/RosegardenDocument.h"
29 #include "document/CommandHistory.h"
30 #include "misc/ConfigGroups.h"
31
32 #include "misc/Debug.h"
33 #include "base/RulerScale.h"
34 #include "base/SnapGrid.h"
35
36 #include "gui/general/GUIPalette.h"
37 #include "gui/widgets/Panned.h"
38
39 #include "base/BaseProperties.h"
40 #include "base/NotationRules.h"
41 #include "gui/studio/StudioControl.h"
42
43 #include <QGraphicsSceneMouseEvent>
44 #include <QGraphicsLineItem>
45 #include <QSettings>
46 #include <QPointF>
47 #include <QRectF>
48
49 #include <algorithm> // for std::sort
50
51 //#define DEBUG_MOUSE
52
53 namespace Rosegarden
54 {
55
56
57 using namespace BaseProperties;
58
MatrixScene()59 MatrixScene::MatrixScene() :
60 m_widget(nullptr),
61 m_document(nullptr),
62 m_scale(nullptr),
63 m_referenceScale(nullptr),
64 m_snapGrid(nullptr),
65 m_resolution(8),
66 m_selection(nullptr),
67 m_currentSegmentIndex(0)
68 {
69 connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
70 this, SLOT(slotCommandExecuted()));
71 }
72
~MatrixScene()73 MatrixScene::~MatrixScene()
74 {
75 RG_DEBUG << "MatrixScene::~MatrixScene() - start";
76
77 if (m_document) {
78 if (!isCompositionDeleted()) { // implemented in CompositionObserver
79 m_document->getComposition().removeObserver(this);
80 }
81 }
82 for (unsigned int i = 0; i < m_viewSegments.size(); ++i) {
83 delete m_viewSegments[i];
84 }
85 delete m_snapGrid;
86 delete m_referenceScale;
87 delete m_scale;
88 delete m_selection;
89
90 RG_DEBUG << "MatrixScene::~MatrixScene() - end";
91 }
92
93 namespace
94 {
95 // Functor for std::sort that compares the track positions of two Segments.
96 struct TrackPositionLess {
TrackPositionLessRosegarden::__anon8c58d50c0111::TrackPositionLess97 TrackPositionLess() :
98 m_composition(RosegardenMainWindow::self()->getDocument()->
99 getComposition())
100 {
101 }
102
operator ()Rosegarden::__anon8c58d50c0111::TrackPositionLess103 bool operator()(const Segment *lhs, const Segment *rhs)
104 {
105 // ??? Could also sort by Segment name and Segment start time.
106 const int lPos =
107 m_composition.getTrackById(lhs->getTrack())->getPosition();
108 const int rPos =
109 m_composition.getTrackById(rhs->getTrack())->getPosition();
110 return (lPos < rPos);
111 }
112
113 private:
114 const Composition &m_composition;
115 };
116 }
117
118 void
setSegments(RosegardenDocument * document,std::vector<Segment * > segments)119 MatrixScene::setSegments(RosegardenDocument *document,
120 std::vector<Segment *> segments)
121 {
122 if (m_document && document != m_document) {
123 m_document->getComposition().removeObserver(this);
124 }
125
126 m_document = document;
127 m_segments = segments;
128
129 // Sort the Segments into TrackPosition order. This makes the
130 // Segment changer wheel in the Matrix editor
131 // (MatrixWidget::m_segmentChanger) easier to understand.
132 std::sort(m_segments.begin(), m_segments.end(), TrackPositionLess());
133
134 m_document->getComposition().addObserver(this);
135
136 SegmentSelection selection;
137 selection.insert(segments.begin(), segments.end());
138
139 delete m_snapGrid;
140 delete m_scale;
141 delete m_referenceScale;
142 m_scale = new SegmentsRulerScale(&m_document->getComposition(),
143 selection,
144 0,
145 Note(Note::Shortest).getDuration() / 2.0);
146
147 m_referenceScale = new ZoomableRulerScale(m_scale);
148 m_snapGrid = new SnapGrid(m_referenceScale);
149
150 for (unsigned int i = 0; i < m_viewSegments.size(); ++i) {
151 delete m_viewSegments[i];
152 }
153 m_viewSegments.clear();
154
155 // We should show diamonds instead of bars whenever we are in
156 // "drum mode" (i.e. whenever we were invoked using the percussion
157 // matrix menu option instead of the normal matrix one).
158
159 // The question of whether to show the key names instead of the
160 // piano keyboard is a separate one, handled in MatrixWidget, and
161 // it depends solely on whether a key mapping exists for the
162 // instrument (it is independent of whether this is a percussion
163 // matrix or not).
164
165 // Nevertheless, if the key names are shown, we need a little more space
166 // between horizontal lines. That's why m_resolution depends from
167 // keyMapping.
168
169 // But since several segments may be edited in the same matrix, we
170 // have to deal with simultaneous display of segments using piano keyboard
171 // and segments using key mapping.
172 // Key mapping may be displayed with piano keyboard resolution (even if
173 // space is a bit short for the text) but the opposite is not possible.
174 // So the only (easy) way I found is to use the resolution fitting with
175 // piano keyboard when at least one segment needs it.
176
177 bool drumMode = false;
178 bool keyMapping = false;
179 if (m_widget) {
180 drumMode = m_widget->isDrumMode();
181 keyMapping = m_widget->hasOnlyKeyMapping();
182 }
183 m_resolution = 8;
184 if (keyMapping) m_resolution = 11;
185
186 bool haveSetSnap = false;
187
188 for (unsigned int i = 0; i < m_segments.size(); ++i) {
189
190 int snapGridSize = m_segments[i]->getSnapGridSize();
191
192 if (snapGridSize != -1) {
193 m_snapGrid->setSnapTime(snapGridSize);
194 haveSetSnap = true;
195 }
196
197 MatrixViewSegment *vs = new MatrixViewSegment(this,
198 m_segments[i],
199 drumMode);
200 (void)vs->getViewElementList(); // make sure it has been created
201 m_viewSegments.push_back(vs);
202 }
203
204 if (!haveSetSnap) {
205 QSettings settings;
206 settings.beginGroup(MatrixViewConfigGroup);
207 timeT snap = settings.value("Snap Grid Size",
208 (int)SnapGrid::SnapToBeat).toInt();
209 m_snapGrid->setSnapTime(snap);
210 settings.endGroup();
211 for (unsigned int i = 0; i < m_segments.size(); ++i) {
212 m_segments[i]->setSnapGridSize(snap);
213 }
214 }
215
216 recreateLines();
217 updateCurrentSegment();
218 }
219
220 Segment *
getCurrentSegment()221 MatrixScene::getCurrentSegment()
222 {
223 if (m_segments.empty()) return nullptr;
224 if (m_currentSegmentIndex >= int(m_segments.size())) {
225 m_currentSegmentIndex = int(m_segments.size()) - 1;
226 }
227 return m_segments[m_currentSegmentIndex];
228 }
229
230 void
setCurrentSegment(Segment * s)231 MatrixScene::setCurrentSegment(Segment *s)
232 {
233 for (int i = 0; i < int(m_segments.size()); ++i) {
234 if (s == m_segments[i]) {
235 m_currentSegmentIndex = i;
236 recreatePitchHighlights();
237 updateCurrentSegment();
238 return;
239 }
240 }
241 RG_WARNING << "WARNING: MatrixScene::setCurrentSegment: unknown segment" << s;
242 }
243
244 Segment *
getPriorSegment()245 MatrixScene::getPriorSegment()
246 {
247 if (m_currentSegmentIndex == 0) return nullptr;
248 return m_segments[m_currentSegmentIndex-1];
249 }
250
251 Segment *
getNextSegment()252 MatrixScene::getNextSegment()
253 {
254 if ((unsigned int) m_currentSegmentIndex + 1 >= m_segments.size()) return nullptr;
255 return m_segments[m_currentSegmentIndex+1];
256 }
257
258 MatrixViewSegment *
getCurrentViewSegment()259 MatrixScene::getCurrentViewSegment()
260 {
261 if (m_viewSegments.empty()) return nullptr;
262 return m_viewSegments[0];
263 }
264
265 bool
segmentsContainNotes() const266 MatrixScene::segmentsContainNotes() const
267 {
268 for (unsigned int i = 0; i < m_segments.size(); ++i) {
269
270 const Segment *segment = m_segments[i];
271
272 for (Segment::const_iterator i = segment->begin();
273 segment->isBeforeEndMarker(i); ++i) {
274
275 if (((*i)->getType() == Note::EventType)) {
276 return true;
277 }
278 }
279 }
280
281 return false;
282 }
283
284 void
recreateLines()285 MatrixScene::recreateLines()
286 {
287 timeT start = 0, end = 0;
288
289 // Determine total distance that requires lines (considering multiple segments
290 for (unsigned int i = 0; i < m_segments.size(); ++i) {
291 if (i == 0 || m_segments[i]->getClippedStartTime() < start) {
292 start = m_segments[i]->getClippedStartTime();
293 }
294 if (i == 0 || m_segments[i]->getEndMarkerTime() > end) {
295 end = m_segments[i]->getEndMarkerTime();
296 }
297 }
298
299 // Pen Width?
300 double pw = 0;
301
302 double startPos = m_scale->getXForTime(start);
303 double endPos = m_scale->getXForTime(end);
304
305 // Draw horizontal lines
306 int i = 0;
307 while (i < 127) {
308 int y = (i + 1) * (m_resolution + 1);
309 QGraphicsLineItem *line;
310 if (i < (int)m_horizontals.size()) {
311 line = m_horizontals[i];
312 } else {
313 line = new QGraphicsLineItem;
314 line->setZValue(-9);
315 line->setPen(QPen(GUIPalette::getColour
316 (GUIPalette::MatrixHorizontalLine), pw));
317 addItem(line);
318 m_horizontals.push_back(line);
319 }
320 line->setLine(startPos, y, endPos, y);
321 line->show();
322 ++i;
323 }
324
325
326 // Hide the other lines, if there are any. Just a double check.
327 while (i < (int)m_horizontals.size()) {
328 m_horizontals[i]->hide();
329 ++i;
330 }
331
332 setSceneRect(QRectF(startPos, 0, endPos - startPos, 128 * (m_resolution + 1)));
333
334 Composition *c = &m_document->getComposition();
335
336 int firstbar = c->getBarNumber(start), lastbar = c->getBarNumber(end);
337
338 // Draw Vertical Lines
339 i = 0;
340 for (int bar = firstbar; bar <= lastbar; ++bar) {
341
342 std::pair<timeT, timeT> range = c->getBarRange(bar);
343
344 bool discard = false; // was not initalied...hmmm...try false?
345 TimeSignature timeSig = c->getTimeSignatureInBar(bar, discard);
346
347 double x0 = m_scale->getXForTime(range.first);
348 double x1 = m_scale->getXForTime(range.second);
349 double width = x1 - x0;
350
351 double gridLines; // number of grid lines per bar may be fractional
352
353 // If the snap time is zero we default to beat markers
354 //
355 if (m_snapGrid && m_snapGrid->getSnapTime(x0)) {
356 gridLines = double(timeSig.getBarDuration()) /
357 double(m_snapGrid->getSnapTime(x0));
358 } else {
359 gridLines = timeSig.getBeatsPerBar();
360 }
361
362 double dx = width / gridLines;
363 double x = x0;
364
365 for (int index = 0; index < gridLines; ++index) {
366
367 // Check to see if we are withing the first segments start time.
368 if (x < startPos) {
369 x += dx;
370 continue;
371 }
372
373 // Exit if we have passed the end of last segment end time.
374 if (x > endPos) {
375 break;
376 }
377
378 QGraphicsLineItem *line;
379
380 if (i < (int)m_verticals.size()) {
381 line = m_verticals[i];
382 } else {
383 line = new QGraphicsLineItem;
384 addItem(line);
385 m_verticals.push_back(line);
386 }
387
388 if (index == 0) {
389 // index 0 is the bar line
390 line->setPen(QPen(GUIPalette::getColour(GUIPalette::MatrixBarLine), pw));
391 } else {
392 line->setPen(QPen(GUIPalette::getColour(GUIPalette::BeatLine), pw));
393 }
394
395 line->setZValue(index > 0 ? -10 : -8);
396 line->setLine(x, 0, x, 128 * (m_resolution + 1));
397
398 line->show();
399 x += dx;
400 ++i;
401 }
402 }
403
404 // Hide the other lines. We are not using them right now.
405 while (i < (int)m_verticals.size()) {
406 m_verticals[i]->hide();
407 ++i;
408 }
409
410 recreatePitchHighlights();
411
412 // Force update so all vertical lines are drawn correctly
413 update();
414 }
415
416 void
recreatePitchHighlights()417 MatrixScene::recreatePitchHighlights()
418 {
419 Segment *segment = getCurrentSegment();
420 if (!segment) return;
421
422 timeT k0 = segment->getClippedStartTime();
423 timeT k1 = segment->getClippedStartTime();
424
425 int i = 0;
426
427 while (k0 < segment->getEndMarkerTime()) {
428
429 Rosegarden::Key key = segment->getKeyAtTime(k0);
430
431 // offset the highlights according to how far this key's tonic pitch is
432 // from C major (0)
433 int offset = key.getTonicPitch();
434
435 // correct for segment transposition, moving representation the opposite
436 // of pitch to cancel it out (C (MIDI pitch 0) in Bb (-2) is concert
437 // Bb (10), so 0 -1 == 11 -1 == 10, we have to go +1 == 11 +1 == 0)
438 int correction = segment->getTranspose(); // eg. -2
439 correction *= -1; // eg. 2
440
441 // key is Bb for Bb instrument, getTonicPitch() returned 10, correction
442 // is +2
443 offset -= correction;
444 offset += 12;
445 offset %= 12;
446
447 if (!segment->getNextKeyTime(k0, k1)) k1 = segment->getEndMarkerTime();
448
449 if (k0 == k1) {
450 RG_WARNING << "WARNING: MatrixScene::recreatePitchHighlights: k0 == k1 ==" << k0;
451 break;
452 }
453
454 double x0 = m_scale->getXForTime(k0);
455 double x1 = m_scale->getXForTime(k1);
456
457 // calculate the highlights relative to C major, plus offset
458 // (I think this enough to do the job. It passes casual tests.)
459 const int hcount = 3;
460 int hsteps[hcount];
461 hsteps[0] = scale_Cmajor[0] + offset; // tonic
462 hsteps[2] = scale_Cmajor[4] + offset; // fifth
463
464 if (key.isMinor()) {
465 hsteps[1] = scale_Cminor[2] + offset; // minor third
466 } else {
467 hsteps[1] = scale_Cmajor[2] + offset; // major third
468 }
469
470 for (int j = 0; j < hcount; ++j) {
471
472 int pitch = hsteps[j];
473 while (pitch < 128) {
474
475 QGraphicsRectItem *rect;
476
477 if (i < (int)m_highlights.size()) {
478 rect = m_highlights[i];
479 } else {
480 rect = new QGraphicsRectItem;
481 rect->setZValue(-11);
482 rect->setPen(Qt::NoPen);
483 addItem(rect);
484 m_highlights.push_back(rect);
485 }
486
487 if (j == 0) {
488 rect->setBrush(GUIPalette::getColour
489 (GUIPalette::MatrixTonicHighlight));
490 } else {
491 rect->setBrush(GUIPalette::getColour
492 (GUIPalette::MatrixPitchHighlight));
493 }
494
495 // rect->setRect(0.5, 0.5, x1 - x0, m_resolution + 1);
496 rect->setRect(0, 0, x1 - x0, m_resolution + 1);
497 rect->setPos(x0, (127 - pitch) * (m_resolution + 1));
498 rect->show();
499
500 pitch += 12;
501
502 ++i;
503 }
504 }
505
506 k0 = k1;
507 }
508 while (i < (int)m_highlights.size()) {
509 m_highlights[i]->hide();
510 ++i;
511 }
512 }
513
514 void
setupMouseEvent(QGraphicsSceneMouseEvent * e,MatrixMouseEvent & mme) const515 MatrixScene::setupMouseEvent(QGraphicsSceneMouseEvent *e,
516 MatrixMouseEvent &mme) const
517 {
518 double sx = e->scenePos().x();
519 int sy = lrint(e->scenePos().y());
520
521 mme.viewpos = e->screenPos();
522
523 mme.sceneX = sx;
524 mme.sceneY = sy;
525
526 mme.modifiers = e->modifiers();
527 mme.buttons = e->buttons();
528
529 mme.element = nullptr;
530
531 QList<QGraphicsItem *> l = items(e->scenePos());
532 // MATRIX_DEBUG << "Found " << l.size() << " items at " << e->scenePos();
533 for (int i = 0; i < l.size(); ++i) {
534 MatrixElement *element = MatrixElement::getMatrixElement(l[i]);
535 if (element) {
536 // items are in z-order from top, so this is most salient
537 mme.element = element;
538 break;
539 }
540 }
541
542 mme.viewSegment = m_viewSegments[m_currentSegmentIndex];
543
544 mme.time = m_scale->getTimeForX(sx);
545
546 if (e->modifiers() & Qt::ShiftModifier) {
547 mme.snappedLeftTime = mme.time;
548 mme.snappedRightTime = mme.time;
549 mme.snapUnit = Note(Note::Shortest).getDuration();
550 } else {
551 // mme.snappedLeftTime = m_snapGrid->snapX(sx, SnapGrid::SnapLeft);
552 // mme.snappedRightTime = m_snapGrid->snapX(sx, SnapGrid::SnapRight);
553 mme.snappedLeftTime = m_snapGrid->snapTime(mme.time, SnapGrid::SnapLeft);
554 mme.snappedRightTime = m_snapGrid->snapTime(mme.time, SnapGrid::SnapRight);
555 mme.snapUnit = m_snapGrid->getSnapTime(sx);
556 }
557
558 if (mme.viewSegment) {
559 timeT start = mme.viewSegment->getSegment().getClippedStartTime();
560 timeT end = mme.viewSegment->getSegment().getEndMarkerTime();
561 if (mme.snappedLeftTime < start) mme.snappedLeftTime = start;
562 if (mme.snappedLeftTime + mme.snapUnit > end) {
563 mme.snappedLeftTime = end - mme.snapUnit;
564 }
565 if (mme.snappedRightTime < start) mme.snappedRightTime = start;
566 if (mme.snappedRightTime > end) mme.snappedRightTime = end;
567 }
568
569 mme.pitch = calculatePitchFromY(sy);
570
571 #ifdef DEBUG_MOUSE
572 MATRIX_DEBUG << "MatrixScene::setupMouseEvent: sx = " << sx
573 << ", sy = " << sy
574 << ", modifiers = " << mme.modifiers
575 << ", buttons = " << mme.buttons
576 << ", element = " << mme.element
577 << ", viewSegment = " << mme.viewSegment
578 << ", time = " << mme.time
579 << ", pitch = " << mme.pitch
580 << endl;
581 #endif
582 }
583
calculatePitchFromY(int y) const584 int MatrixScene::calculatePitchFromY(int y) const {
585 int pitch = 127 - (y / (m_resolution + 1));
586 if (pitch < 0) pitch = 0;
587 if (pitch > 127) pitch = 127;
588 return pitch;
589 }
590
591 void
mousePressEvent(QGraphicsSceneMouseEvent * e)592 MatrixScene::mousePressEvent(QGraphicsSceneMouseEvent *e)
593 {
594 MatrixMouseEvent nme;
595 setupMouseEvent(e, nme);
596 emit mousePressed(&nme);
597 }
598
599 void
mouseMoveEvent(QGraphicsSceneMouseEvent * e)600 MatrixScene::mouseMoveEvent(QGraphicsSceneMouseEvent *e)
601 {
602 MatrixMouseEvent nme;
603 setupMouseEvent(e, nme);
604 emit mouseMoved(&nme);
605 }
606
607 void
mouseReleaseEvent(QGraphicsSceneMouseEvent * e)608 MatrixScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *e)
609 {
610 MatrixMouseEvent nme;
611 setupMouseEvent(e, nme);
612 emit mouseReleased(&nme);
613 }
614
615 void
mouseDoubleClickEvent(QGraphicsSceneMouseEvent * e)616 MatrixScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *e)
617 {
618 MatrixMouseEvent nme;
619 setupMouseEvent(e, nme);
620 emit mouseDoubleClicked(&nme);
621 }
622
623 void
slotCommandExecuted()624 MatrixScene::slotCommandExecuted()
625 {
626 checkUpdate();
627 }
628
629 void
checkUpdate()630 MatrixScene::checkUpdate()
631 {
632 bool updateSelectionElementStatus = false;
633
634 for (unsigned int i = 0; i < m_viewSegments.size(); ++i) {
635
636 SegmentRefreshStatus &rs = m_viewSegments[i]->getRefreshStatus();
637
638 if (rs.needsRefresh()) {
639 m_viewSegments[i]->updateElements(rs.from(), rs.to());
640 if (!updateSelectionElementStatus && m_selection) {
641 updateSelectionElementStatus =
642 (m_viewSegments[i]->getSegment() == m_selection->getSegment());
643 }
644 }
645
646 rs.setNeedsRefresh(false);
647 }
648
649 if (updateSelectionElementStatus) {
650 setSelectionElementStatus(m_selection, true);
651 }
652 }
653
654 void
segmentEndMarkerTimeChanged(const Segment *,bool)655 MatrixScene::segmentEndMarkerTimeChanged(const Segment *, bool)
656 {
657 MATRIX_DEBUG << "MatrixScene::segmentEndMarkerTimeChanged";
658 recreateLines();
659 }
660
661 void
timeSignatureChanged(const Composition * c)662 MatrixScene::timeSignatureChanged(const Composition *c)
663 {
664 if (!m_document || !c || (c != &m_document->getComposition())) return;
665 }
666
667 void
segmentRemoved(const Composition * c,Segment * s)668 MatrixScene::segmentRemoved(const Composition *c, Segment *s)
669 {
670 MATRIX_DEBUG << "MatrixScene::segmentRemoved(" << c << "," << s << ")";
671
672 if (!m_document || !c || (c != &m_document->getComposition())) return;
673
674 for (std::vector<MatrixViewSegment *>::iterator i = m_viewSegments.begin();
675 i != m_viewSegments.end(); ++i) {
676 if (s == &(*i)->getSegment()) {
677 emit segmentDeleted(s);
678 delete *i;
679 m_viewSegments.erase(i);
680 break;
681 }
682 }
683
684 if (m_viewSegments.empty()) {
685 MATRIX_DEBUG << "(Scene is now empty)";
686 emit sceneDeleted();
687 }
688 }
689
690 void
handleEventAdded(Event * e)691 MatrixScene::handleEventAdded(Event *e)
692 {
693 if (e->getType() == Rosegarden::Key::EventType) {
694 recreatePitchHighlights();
695 }
696 }
697
698 void
handleEventRemoved(Event * e)699 MatrixScene::handleEventRemoved(Event *e)
700 {
701 if (m_selection && m_selection->contains(e)) m_selection->removeEvent(e);
702 if (e->getType() == Rosegarden::Key::EventType) {
703 recreatePitchHighlights();
704 }
705 update();
706 emit eventRemoved(e);
707 }
708
709 void
setSelection(EventSelection * s,bool preview)710 MatrixScene::setSelection(EventSelection *s, bool preview)
711 {
712 if (!m_selection && !s) return;
713 if (m_selection == s) return;
714 if (m_selection && s && *m_selection == *s) {
715 // selections are identical, no need to update elements, but
716 // still need to replace the old selection to avoid a leak
717 // (can't just delete s in case caller still refers to it)
718 EventSelection *oldSelection = m_selection;
719 m_selection = s;
720 delete oldSelection;
721 return;
722 }
723
724 EventSelection *oldSelection = m_selection;
725 m_selection = s;
726
727 if (oldSelection) {
728 setSelectionElementStatus(oldSelection, false);
729 }
730
731 if (m_selection) {
732 setSelectionElementStatus(m_selection, true);
733 emit QGraphicsScene::selectionChanged();
734 }
735
736 if (preview) previewSelection(m_selection, oldSelection);
737 delete oldSelection;
738 emit QGraphicsScene::selectionChanged();
739 }
740
741 void
slotRulerSelectionChanged(EventSelection * s)742 MatrixScene::slotRulerSelectionChanged(EventSelection *s)
743 {
744 RG_DEBUG << "MatrixScene: caught " << (s ? "useful" : "null" ) << " selection change from ruler";
745 if (m_selection) {
746 if (s) m_selection->addFromSelection(s);
747 setSelectionElementStatus(m_selection, true);
748 }
749 }
750
751 void
setSingleSelectedEvent(MatrixViewSegment * vs,MatrixElement * e,bool preview)752 MatrixScene::setSingleSelectedEvent(MatrixViewSegment *vs,
753 MatrixElement *e,
754 bool preview)
755 {
756 if (!vs || !e) return;
757 EventSelection *s = new EventSelection(vs->getSegment());
758 s->addEvent(e->event());
759 setSelection(s, preview);
760 }
761
762 void
setSingleSelectedEvent(Segment * seg,Event * e,bool preview)763 MatrixScene::setSingleSelectedEvent(Segment *seg,
764 Event *e,
765 bool preview)
766 {
767 if (!seg || !e) return;
768 EventSelection *s = new EventSelection(*seg);
769 s->addEvent(e);
770 setSelection(s, preview);
771 }
772
773 void
selectAll()774 MatrixScene::selectAll()
775 {
776 Segment *segment = getCurrentSegment();
777 if (!segment) return;
778 Segment::iterator it = segment->begin();
779 EventSelection *selection = new EventSelection(*segment);
780
781 for (; segment->isBeforeEndMarker(it); ++it) {
782 if ((*it)->isa(Note::EventType)) {
783 selection->addEvent(*it);
784 }
785 }
786
787 setSelection(selection, false);
788 }
789
790 void
setSelectionElementStatus(EventSelection * s,bool set)791 MatrixScene::setSelectionElementStatus(EventSelection *s, bool set)
792 {
793 if (!s) return;
794
795 MatrixViewSegment *vs = nullptr;
796
797 for (std::vector<MatrixViewSegment *>::iterator i = m_viewSegments.begin();
798 i != m_viewSegments.end(); ++i) {
799
800 if (&(*i)->getSegment() == &s->getSegment()) {
801 vs = *i;
802 break;
803 }
804 }
805
806 if (!vs) return;
807
808 for (EventSelection::eventcontainer::iterator i = s->getSegmentEvents().begin();
809 i != s->getSegmentEvents().end(); ++i) {
810
811 Event *e = *i;
812
813 ViewElementList::iterator vsi = vs->findEvent(e);
814 if (vsi == vs->getViewElementList()->end()) continue;
815
816 MatrixElement *el = static_cast<MatrixElement *>(*vsi);
817 el->setSelected(set);
818 }
819 }
820
821 void
previewSelection(EventSelection * s,EventSelection * oldSelection)822 MatrixScene::previewSelection(EventSelection *s,
823 EventSelection *oldSelection)
824 {
825 if (!s) return;
826 if (!m_document->isSoundEnabled()) return;
827
828 for (EventSelection::eventcontainer::iterator i = s->getSegmentEvents().begin();
829 i != s->getSegmentEvents().end(); ++i) {
830
831 Event *e = *i;
832 if (oldSelection && oldSelection->contains(e)) continue;
833
834 long pitch;
835 if (e->get<Int>(BaseProperties::PITCH, pitch)) {
836 long velocity = -1;
837 (void)(e->get<Int>(BaseProperties::VELOCITY, velocity));
838 if (!(e->has(BaseProperties::TIED_BACKWARD) &&
839 e->get<Bool>(BaseProperties::TIED_BACKWARD))) {
840 playNote(s->getSegment(), pitch, velocity);
841 }
842 }
843 }
844 }
845
846 void
updateCurrentSegment()847 MatrixScene::updateCurrentSegment()
848 {
849 MATRIX_DEBUG << "MatrixScene::updateCurrentSegment: current is " << m_currentSegmentIndex;
850 for (int i = 0; i < (int)m_viewSegments.size(); ++i) {
851 bool current = (i == m_currentSegmentIndex);
852 ViewElementList *vel = m_viewSegments[i]->getViewElementList();
853 for (ViewElementList::const_iterator j = vel->begin();
854 j != vel->end(); ++j) {
855 MatrixElement *mel = dynamic_cast<MatrixElement *>(*j);
856 if (!mel) continue;
857 mel->setCurrent(current);
858 }
859 if (current) emit currentViewSegmentChanged(m_viewSegments[i]);
860 }
861
862 // changing the current segment may have overridden selection border colours
863 setSelectionElementStatus(m_selection, true);
864 }
865
866 void
setSnap(timeT t)867 MatrixScene::setSnap(timeT t)
868 {
869 MATRIX_DEBUG << "MatrixScene::slotSetSnap: time is " << t;
870 m_snapGrid->setSnapTime(t);
871
872 for (size_t i = 0; i < m_segments.size(); ++i) {
873 m_segments[i]->setSnapGridSize(t);
874 }
875
876 QSettings settings;
877 settings.beginGroup(MatrixViewConfigGroup);
878 settings.setValue("Snap Grid Size", (int)t);
879 settings.endGroup();
880
881 recreateLines();
882 }
883
884 bool
constrainToSegmentArea(QPointF & scenePos)885 MatrixScene::constrainToSegmentArea(QPointF &scenePos)
886 {
887 bool ok = true;
888
889 int pitch = 127 - (lrint(scenePos.y()) / (m_resolution + 1));
890 if (pitch < 0) {
891 ok = false;
892 scenePos.setY(127 * (m_resolution + 1));
893 } else if (pitch > 127) {
894 ok = false;
895 scenePos.setY(0);
896 }
897
898 timeT t = m_scale->getTimeForX(scenePos.x());
899 timeT start = 0, end = 0;
900 for (size_t i = 0; i < m_segments.size(); ++i) {
901 timeT t0 = m_segments[i]->getClippedStartTime();
902 timeT t1 = m_segments[i]->getEndMarkerTime();
903 if (i == 0 || t0 < start) start = t0;
904 if (i == 0 || t1 > end) end = t1;
905 }
906 if (t < start) {
907 ok = false;
908 scenePos.setX(m_scale->getXForTime(start));
909 } else if (t > end) {
910 ok = false;
911 scenePos.setX(m_scale->getXForTime(end));
912 }
913
914 return ok;
915 }
916
917 void
playNote(Segment & segment,int pitch,int velocity)918 MatrixScene::playNote(Segment &segment, int pitch, int velocity)
919 {
920 // std::cout << "Scene is playing a note of pitch: " << pitch
921 // << " + " << segment.getTranspose();
922 if (!m_document) return;
923
924 Instrument *instrument = m_document->getStudio().getInstrumentFor(&segment);
925
926 StudioControl::playPreviewNote(instrument,
927 pitch + segment.getTranspose(),
928 velocity,
929 RealTime(0, 250000000));
930 }
931
932 void
setHorizontalZoomFactor(double factor)933 MatrixScene::setHorizontalZoomFactor(double factor)
934 {
935 for (Segment *segment : m_segments) {
936 segment->matrixHZoomFactor = factor;
937 }
938 }
939
940 void
setVerticalZoomFactor(double factor)941 MatrixScene::setVerticalZoomFactor(double factor)
942 {
943 for (Segment *segment : m_segments) {
944 segment->matrixVZoomFactor = factor;
945 }
946 }
947
948
949 }
950