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 "[RawNoteRuler]"
19
20 #include <QPaintEvent>
21 #include "RawNoteRuler.h"
22
23 #include "misc/Debug.h"
24 #include "base/BaseProperties.h"
25 #include "base/Composition.h"
26 #include "base/NotationTypes.h"
27 #include "base/NotationQuantizer.h"
28 #include "base/RulerScale.h"
29 #include "base/Segment.h"
30 #include "DefaultVelocityColour.h"
31 #include "gui/general/GUIPalette.h"
32 #include "misc/Strings.h"
33
34 #include <QColor>
35 #include <QPainter>
36 #include <QRect>
37 #include <QSize>
38 #include <QToolTip>
39 #include <QWidget>
40
41
42 namespace Rosegarden
43 {
44
RawNoteRuler(RulerScale * rulerScale,Segment * segment,int height,QWidget * parent)45 RawNoteRuler::RawNoteRuler(RulerScale *rulerScale,
46 Segment *segment,
47 int height,
48 QWidget *parent) :
49 QWidget(parent),
50 m_height(height),
51 m_currentXOffset(0),
52 m_width( -1),
53 m_segment(segment),
54 m_rulerScale(rulerScale)
55 {
56 // setBackgroundColor(GUIPalette::getColour(GUIPalette::RawNoteRulerBackground));
57 this->setToolTip("");
58
59 if (m_segment) m_segment->addObserver(this);
60 }
61
~RawNoteRuler()62 RawNoteRuler::~RawNoteRuler()
63 {
64 // QToolTip::remove(this);
65
66 if (m_segment) m_segment->removeObserver(this);
67 }
68
69 void
setCurrentSegment(Segment * segment)70 RawNoteRuler::setCurrentSegment(Segment *segment)
71 {
72 if (segment == m_segment) return; // Don't waste CPU time
73
74 if (m_segment) m_segment->removeObserver(this);
75 m_segment = segment;
76 if (m_segment) m_segment->addObserver(this);
77 }
78
79 void
segmentDeleted(const Segment *)80 RawNoteRuler::segmentDeleted(const Segment *)
81 {
82 m_segment = nullptr;
83 }
84
85 void
slotScrollHoriz(int x)86 RawNoteRuler::slotScrollHoriz(int x)
87 {
88 //### int w = width(), h = height();
89 int dx = x - ( -m_currentXOffset);
90 if (dx == 0)
91 return ;
92 m_currentXOffset = -x;
93
94 update();
95
96 //### bitBlt is no more working with Qt4
97 // if (dx > w*3 / 4 || dx < -w*3 / 4) {
98 // update();
99 // return ;
100 // }
101 //
102 // if (dx > 0) { // moving right, so the existing stuff moves left
103 // bitBlt(this, 0, 0, this, dx, 0, w - dx, h);
104 // repaint(w - dx, 0, dx, h);
105 // } else { // moving left, so the existing stuff moves right
106 // bitBlt(this, -dx, 0, this, 0, 0, w + dx, h);
107 // repaint(0, 0, -dx, h);
108 // }
109 }
110
111 QSize
sizeHint() const112 RawNoteRuler::sizeHint() const
113 {
114 double width =
115 m_rulerScale->getBarPosition(m_rulerScale->getLastVisibleBar()) +
116 m_rulerScale->getBarWidth(m_rulerScale->getLastVisibleBar());
117
118 QSize res(std::max(int(width), m_width), m_height);
119
120 return res;
121 }
122
123 QSize
minimumSizeHint() const124 RawNoteRuler::minimumSizeHint() const
125 {
126 double firstBarWidth = m_rulerScale->getBarWidth(0);
127 QSize res = QSize(int(firstBarWidth), m_height);
128 return res;
129 }
130
131 std::pair<timeT, timeT>
getExtents(Segment::iterator i)132 RawNoteRuler::getExtents(Segment::iterator i)
133 {
134 const Quantizer *q =
135 m_segment->getComposition()->getNotationQuantizer();
136
137 timeT u0 = (*i)->getAbsoluteTime();
138 timeT u1 = u0 + (*i)->getDuration();
139
140 timeT q0 = q->getQuantizedAbsoluteTime(*i);
141 timeT q1 = q0 + q->getQuantizedDuration(*i);
142
143 timeT t0 = std::min(u0, q0);
144 timeT t1 = std::max(u1, q1);
145
146 return std::pair<timeT, timeT>(t0, t1);
147 }
148
149 Segment::iterator
addChildren(Segment * s,Segment::iterator to,timeT rightBound,EventTreeNode * node)150 RawNoteRuler::addChildren(Segment *s,
151 Segment::iterator to,
152 timeT rightBound,
153 EventTreeNode *node)
154 {
155 Segment::iterator i = node->node;
156
157 std::pair<timeT, timeT> iex = getExtents(i);
158 Segment::iterator j = i;
159 Segment::iterator rightmost = to;
160
161 #ifdef DEBUG_RAW_NOTE_RULER
162
163 RG_DEBUG << "addChildren called for extents " << iex.first << "->" << iex.second << ", rightBound " << rightBound;
164 #endif
165
166 for (++j; j != to && s->isBeforeEndMarker(j); ) {
167
168 if (!(*j)->isa(Note::EventType)) {
169 ++j;
170 continue;
171 }
172 std::pair<timeT, timeT> jex = getExtents(j);
173
174 #ifdef DEBUG_RAW_NOTE_RULER
175
176 RG_DEBUG << "addChildren: event at " << (*j)->getAbsoluteTime() << ", extents " << jex.first << "->" << jex.second;
177 #endif
178
179 if (jex.first == jex.second) {
180 ++j;
181 continue;
182 }
183 if (jex.first >= iex.second || jex.first >= rightBound)
184 break;
185
186 #ifdef DEBUG_RAW_NOTE_RULER
187
188 RG_DEBUG << "addChildren: adding";
189 #endif
190
191 EventTreeNode *subnode = new EventTreeNode(j);
192
193 Segment::iterator subRightmost = addChildren(s, to, rightBound, subnode);
194 if (subRightmost != to)
195 rightmost = subRightmost;
196 else
197 rightmost = j;
198
199 node->children.push_back(subnode);
200 j = s->findTime(jex.second);
201 }
202
203 return rightmost;
204 }
205
206 void
buildForest(Segment * s,Segment::iterator from,Segment::iterator to)207 RawNoteRuler::buildForest(Segment *s,
208 Segment::iterator from,
209 Segment::iterator to)
210 {
211 for (EventTreeNode::NodeList::iterator i = m_forest.begin();
212 i != m_forest.end(); ++i) {
213 delete *i;
214 }
215 m_forest.clear();
216
217 timeT endTime = (s->isBeforeEndMarker(to) ? (*to)->getAbsoluteTime() :
218 s->getEndMarkerTime());
219
220 for (Segment::iterator i = from; i != to && s->isBeforeEndMarker(i); ) {
221
222 if (!(*i)->isa(Note::EventType)) {
223 ++i;
224 continue;
225 }
226
227 std::pair<timeT, timeT> iex = getExtents(i);
228
229 #ifdef DEBUG_RAW_NOTE_RULER
230
231 RG_DEBUG << "buildForest: event at " << (*i)->getAbsoluteTime() << ", extents " << iex.first << "->" << iex.second;
232 #endif
233
234 if (iex.first == iex.second) {
235 ++i;
236 continue;
237 }
238 if (iex.first >= endTime)
239 break;
240
241 EventTreeNode *node = new EventTreeNode(i);
242 Segment::iterator rightmost = addChildren(s, to, iex.second, node);
243 m_forest.push_back(node);
244
245 if (rightmost != to) {
246 i = rightmost;
247 ++i;
248 } else {
249 i = s->findTime(iex.second);
250 }
251
252 #ifdef DEBUG_RAW_NOTE_RULER
253 RG_DEBUG << "findTime " << iex.second << " returned iterator at " << (i == s->end() ? -1 : (*i)->getAbsoluteTime());
254 #endif
255
256 }
257 }
258
259 void
dumpSubtree(EventTreeNode * node,int depth)260 RawNoteRuler::dumpSubtree(EventTreeNode *node, int depth)
261 {
262 if (!node)
263 return ;
264 #ifdef DEBUG_RAW_NOTE_RULER
265
266 for (int i = 0; i < depth; ++i)
267 std::cerr << " ";
268 if (depth > 0)
269 std::cerr << "->";
270 std::cerr << (*node->node)->getAbsoluteTime() << ","
271 << (*node->node)->getDuration() << " [";
272 long pitch = 0;
273 if ((*node->node)->get
274 <Int>(PITCH, pitch)) {
275 std::cerr << pitch << "]" << std::endl;
276 }
277 else {
278 std::cerr << "no-pitch]" << std::endl;
279 }
280 for (EventTreeNode::NodeList::iterator i = node->children.begin();
281 i != node->children.end(); ++i) {
282 dumpSubtree(*i, depth + 1);
283 }
284 #endif
285 (void)depth; // avoid warnings
286 }
287
288 void
dumpForest(EventTreeNode::NodeList * forest)289 RawNoteRuler::dumpForest(EventTreeNode::NodeList *forest)
290 {
291 #ifdef DEBUG_RAW_NOTE_RULER
292 std::cerr << "\nFOREST:\n" << std::endl;
293
294 for (unsigned int i = 0; i < forest->size(); ++i) {
295
296 std::cerr << "\nTREE " << i << ":\n" << std::endl;
297 dumpSubtree((*forest)[i], 0);
298 }
299
300 std::cerr << std::endl;
301 #endif
302
303 (void)forest; // avoid warnings
304 }
305
306 int
getDepth()307 RawNoteRuler::EventTreeNode::getDepth()
308 {
309 int subchildrenDepth = 0;
310 for (NodeList::iterator i = children.begin();
311 i != children.end(); ++i) {
312 int subchildDepth = (*i)->getDepth();
313 if (subchildDepth > subchildrenDepth)
314 subchildrenDepth = subchildDepth;
315 }
316 return subchildrenDepth + 1;
317 }
318
319 int
getChildrenAboveOrBelow(bool below,int p)320 RawNoteRuler::EventTreeNode::getChildrenAboveOrBelow(bool below, int p)
321 {
322 long pitch(p);
323 if (pitch < 0)
324 (*node)->get
325 <Int>(BaseProperties::PITCH, pitch);
326
327 int max = 0;
328
329 for (NodeList::iterator i = children.begin();
330 i != children.end(); ++i) {
331 int forThisChild = (*i)->getChildrenAboveOrBelow(below, pitch);
332 long thisChildPitch = pitch;
333 (*(*i)->node)->get
334 <Int>(BaseProperties::PITCH, thisChildPitch);
335 if (below ? (thisChildPitch < pitch) : (thisChildPitch > pitch)) {
336 ++forThisChild;
337 }
338 if (forThisChild > max)
339 max = forThisChild;
340 }
341
342 return max;
343 }
344
345 void
drawNode(QPainter & paint,DefaultVelocityColour & vc,EventTreeNode * node,double height,double yorigin)346 RawNoteRuler::drawNode(QPainter &paint, DefaultVelocityColour &vc,
347 EventTreeNode *node, double height, double yorigin)
348 {
349 int depth = node->getDepth();
350 int above = node->getChildrenAboveOrBelow(false);
351
352 #ifdef DEBUG_RAW_NOTE_RULER
353
354 int below = node->getChildrenAboveOrBelow(true);
355
356 NOTATION_DEBUG << "RawNoteRuler::drawNode: children above: "
357 << above << ", below: " << below << endl;
358 #endif
359
360 int toFit = depth;
361
362 double heightPer = double(height) / toFit;
363 if (heightPer > m_height / 4)
364 heightPer = m_height / 4;
365 if (heightPer < 2)
366 heightPer = 2;
367
368 double myOrigin = yorigin + (heightPer * above);
369 long myPitch = 60;
370 (*node->node)->get
371 <Int>(BaseProperties::PITCH, myPitch);
372
373 long velocity = 100;
374 (*node->node)->get
375 <Int>(BaseProperties::VELOCITY, velocity);
376 QColor colour = vc.getColour(velocity);
377
378 timeT start = (*node->node)->getAbsoluteTime();
379 timeT end = (*node->node)->getDuration() + start;
380
381 double u0 = m_rulerScale->getXForTime(start);
382 double u1 = m_rulerScale->getXForTime(end);
383
384 u0 += m_currentXOffset;
385 u1 += m_currentXOffset;
386
387 start = m_segment->getComposition()->getNotationQuantizer()->
388 getQuantizedAbsoluteTime(*node->node);
389 end = start + m_segment->getComposition()->getNotationQuantizer()->
390 getQuantizedDuration(*node->node);
391
392 double q0 = m_rulerScale->getXForTime(start);
393 double q1 = m_rulerScale->getXForTime(end);
394
395 q0 += m_currentXOffset;
396 q1 += m_currentXOffset;
397
398 #ifdef DEBUG_RAW_NOTE_RULER
399
400 NOTATION_DEBUG << "RawNoteRuler: (" << int(start) << "," << myOrigin
401 << ") -> (" << int(end) << "," << myOrigin << ")" << endl;
402 #endif
403
404 int qi0 = int(q0);
405 int ui0 = int(u0);
406 int qi1 = int(q1);
407 int ui1 = int(u1);
408 // int qiw = int(q1-q0) - 1;
409 int uiw = int(u1 - u0) - 1;
410 // int iy = int(myOrigin + (height - heightPer) / 2);
411 int iy = int(myOrigin);
412 int ih = int(heightPer);
413
414 #ifdef DEBUG_RAW_NOTE_RULER
415
416 NOTATION_DEBUG << "RawNoteRuler: height " << height << ", heightPer "
417 << heightPer << ", iy " << iy << endl;
418 #endif
419
420 paint.setPen(colour);
421 paint.setBrush(colour);
422 paint.drawRect(ui0 + 1, iy + 1, uiw, ih - 1);
423
424 paint.setPen(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground));
425 paint.setBrush(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground));
426 paint.drawLine(qi0, iy, qi1 - 1, iy);
427 paint.drawLine(qi0, iy + ih, qi1 - 1, iy + ih);
428 paint.drawLine(ui0, iy + 1, ui0, iy + ih - 1);
429 paint.drawLine(ui1 - 1, iy + 1, ui1 - 1, iy + ih - 1);
430
431 for (EventTreeNode::NodeList::iterator i = node->children.begin();
432 i != node->children.end(); ++i) {
433
434 long nodePitch = myPitch;
435 (*(*i)->node)->get
436 <Int>(BaseProperties::PITCH, nodePitch);
437
438 if (nodePitch < myPitch) {
439
440 drawNode(paint, vc, *i,
441 height - heightPer - myOrigin, myOrigin + heightPer);
442
443 } else {
444
445 drawNode(paint, vc, *i,
446 myOrigin - yorigin, yorigin);
447 }
448 }
449 }
450
451 void
paintEvent(QPaintEvent * e)452 RawNoteRuler::paintEvent(QPaintEvent* e)
453 {
454 if (!m_segment || !m_segment->getComposition())
455 return ;
456
457 // Tooltips
458 {
459 // QToolTip::remove(this);
460 TrackId trackId = m_segment->getTrack();
461 Track *track =
462 m_segment->getComposition()->getTrackById(trackId);
463 int trackPosition = -1;
464 if (track)
465 trackPosition = track->getPosition();
466
467 this->setToolTip(tr("Track #%1, Segment \"%2\" (runtime id %3)")
468 .arg(trackPosition + 1)
469 .arg(strtoqstr(m_segment->getLabel()))
470 .arg(m_segment->getRuntimeId()));
471 }
472
473 // START_TIMING;
474
475 QPainter paint(this);
476 paint.setClipRegion(e->region());
477 paint.setClipRect(e->rect().normalized());
478
479 QRect clipRect = paint.clipRegion().boundingRect();
480
481 timeT from = m_rulerScale->getTimeForX
482 (clipRect.x() - m_currentXOffset - 100);
483 timeT to = m_rulerScale->getTimeForX
484 (clipRect.x() + clipRect.width() - m_currentXOffset + 100);
485
486 paint.setPen(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground));
487 paint.setBrush(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground));
488 paint.drawLine(0, 0, width(), 0);
489
490 // draw the extent of the segment using its color
491
492 QColor brushColor = m_segment->getComposition()->
493 getSegmentColourMap().getColour(m_segment->getColourIndex());
494 paint.setPen(brushColor);
495 paint.setBrush(brushColor);
496 int x0 = int(m_rulerScale->getXForTime(m_segment->getStartTime()) +
497 m_currentXOffset);
498 int x1 = int(m_rulerScale->getXForTime(m_segment->getEndMarkerTime()) +
499 m_currentXOffset);
500 paint.drawRect(x0, 1, x1-x0+1, height()-1);
501
502 // draw the bar divisions
503
504 int firstBar = m_segment->getComposition()->getBarNumber(from);
505 int lastBar = m_segment->getComposition()->getBarNumber(to);
506 std::vector<int> divisions;
507
508 for (int barNo = firstBar; barNo <= lastBar; ++barNo) {
509
510 bool isNew = false;
511 TimeSignature timeSig =
512 m_segment->getComposition()->getTimeSignatureInBar(barNo, isNew);
513 if (isNew || barNo == firstBar) {
514 timeSig.getDivisions(3, divisions);
515 if (timeSig == TimeSignature()) // special case for 4/4
516 divisions[0] = 2;
517 }
518
519 timeT barStart = m_segment->getComposition()->getBarStart(barNo);
520 timeT base = timeSig.getBarDuration();
521 timeT barEnd = barStart + base;
522
523 paint.setPen(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground));
524 paint.setBrush(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground));
525
526 int x = int(m_rulerScale->getXForTime(barStart) +
527 m_currentXOffset);
528 paint.drawLine(x, 1, x, m_height);
529
530 for (int depth = 0; depth < 3; ++depth) {
531
532 int grey = depth * 60 + 60;
533 paint.setPen(QColor(grey, grey, grey));
534 paint.setBrush(QColor(grey, grey, grey));
535
536 base /= divisions[depth];
537 timeT t(barStart + base);
538 while (t < barEnd) {
539 if ((t - barStart) % (base * divisions[depth]) != 0) {
540 int x = int(m_rulerScale->getXForTime(t) +
541 m_currentXOffset);
542 paint.drawLine(x, 1, x, m_height);
543 }
544 t += base;
545 }
546 }
547 }
548
549 // PRINT_ELAPSED("RawNoteRuler::paintEvent: drawing bar lines and divisions");
550
551 #ifdef DEBUG_RAW_NOTE_RULER
552 NOTATION_DEBUG << "RawNoteRuler: from is " << from << ", to is " << to;
553 #endif
554
555 Segment::iterator i = m_segment->findNearestTime(from);
556 if (i == m_segment->end())
557 i = m_segment->begin();
558
559 // somewhat experimental, as is this whole class
560 Segment::iterator j = m_segment->findTime(to);
561 buildForest(m_segment, i, j);
562
563 // PRINT_ELAPSED("RawNoteRuler::paintEvent: buildForest");
564
565 dumpForest(&m_forest);
566
567 // PRINT_ELAPSED("RawNoteRuler::paintEvent: dumpForest");
568
569 for (EventTreeNode::NodeList::iterator fi = m_forest.begin();
570 fi != m_forest.end(); ++fi) {
571
572 // Each tree in the forest should represent a note that starts
573 // at a time when no other notes are playing (at least of
574 // those that started no earlier than the paint start time).
575 // Each node in that tree represents a note that starts
576 // playing during its parent node's note, or at the same time
577 // as it.
578
579 drawNode(paint, *DefaultVelocityColour::getInstance(), *fi, m_height - 3, 2);
580
581 }
582
583 // PRINT_ELAPSED("RawNoteRuler::paintEvent: complete");
584 }
585
586 }
587