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