/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ /* Rosegarden A MIDI and audio sequencer and musical notation editor. Copyright 2000-2011 the Rosegarden development team. Other copyrights also apply to some parts of this work. Please see the AUTHORS file and individual file headers for details. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See the file COPYING included with this distribution for more information. */ #include #include "RawNoteRuler.h" #include "misc/Debug.h" #include "base/BaseProperties.h" #include "base/Composition.h" #include "base/NotationTypes.h" #include "base/NotationQuantizer.h" #include "base/RulerScale.h" #include "base/Segment.h" #include "DefaultVelocityColour.h" #include "gui/general/GUIPalette.h" #include "misc/Strings.h" #include #include #include #include #include #include namespace Rosegarden { RawNoteRuler::RawNoteRuler(RulerScale *rulerScale, Segment *segment, double xorigin, int height, QWidget *parent, const char *name) : QWidget(parent, name), m_xorigin(xorigin), m_height(height), m_currentXOffset(0), m_width( -1), m_segment(segment), m_rulerScale(rulerScale) { setBackgroundColor(GUIPalette::getColour(GUIPalette::RawNoteRulerBackground)); this->setToolTip(""); if (m_segment) m_segment->addObserver(this); } RawNoteRuler::~RawNoteRuler() { // QToolTip::remove(this); if (m_segment) m_segment->removeObserver(this); } void RawNoteRuler::setCurrentSegment(Segment *segment) { if (segment == m_segment) return; // Don't waste CPU time if (m_segment) m_segment->removeObserver(this); m_segment = segment; if (m_segment) m_segment->addObserver(this); } void RawNoteRuler::segmentDeleted(const Segment *) { if (m_segment) m_segment->removeObserver(this); m_segment = 0; } void RawNoteRuler::slotScrollHoriz(int x) { //### int w = width(), h = height(); int dx = x - ( -m_currentXOffset); if (dx == 0) return ; m_currentXOffset = -x; update(); //### bitBlt is no more working with Qt4 // if (dx > w*3 / 4 || dx < -w*3 / 4) { // update(); // return ; // } // // if (dx > 0) { // moving right, so the existing stuff moves left // bitBlt(this, 0, 0, this, dx, 0, w - dx, h); // repaint(w - dx, 0, dx, h); // } else { // moving left, so the existing stuff moves right // bitBlt(this, -dx, 0, this, 0, 0, w + dx, h); // repaint(0, 0, -dx, h); // } } QSize RawNoteRuler::sizeHint() const { double width = m_rulerScale->getBarPosition(m_rulerScale->getLastVisibleBar()) + m_rulerScale->getBarWidth(m_rulerScale->getLastVisibleBar()) + m_xorigin; QSize res(std::max(int(width), m_width), m_height); return res; } QSize RawNoteRuler::minimumSizeHint() const { double firstBarWidth = m_rulerScale->getBarWidth(0) + m_xorigin; QSize res = QSize(int(firstBarWidth), m_height); return res; } std::pair RawNoteRuler::getExtents(Segment::iterator i) { const Quantizer *q = m_segment->getComposition()->getNotationQuantizer(); timeT u0 = (*i)->getAbsoluteTime(); timeT u1 = u0 + (*i)->getDuration(); timeT q0 = q->getQuantizedAbsoluteTime(*i); timeT q1 = q0 + q->getQuantizedDuration(*i); timeT t0 = std::min(u0, q0); timeT t1 = std::max(u1, q1); return std::pair(t0, t1); } Segment::iterator RawNoteRuler::addChildren(Segment *s, Segment::iterator to, timeT rightBound, EventTreeNode *node) { Segment::iterator i = node->node; std::pair iex = getExtents(i); Segment::iterator j = i; Segment::iterator rightmost = to; #ifdef DEBUG_RAW_NOTE_RULER RG_DEBUG << "addChildren called for extents " << iex.first << "->" << iex.second << ", rightBound " << rightBound << endl; #endif for (++j; j != to && s->isBeforeEndMarker(j); ) { if (!(*j)->isa(Note::EventType)) { ++j; continue; } std::pair jex = getExtents(j); #ifdef DEBUG_RAW_NOTE_RULER RG_DEBUG << "addChildren: event at " << (*j)->getAbsoluteTime() << ", extents " << jex.first << "->" << jex.second << endl; #endif if (jex.first == jex.second) { ++j; continue; } if (jex.first >= iex.second || jex.first >= rightBound) break; #ifdef DEBUG_RAW_NOTE_RULER RG_DEBUG << "addChildren: adding" << endl; #endif EventTreeNode *subnode = new EventTreeNode(j); Segment::iterator subRightmost = addChildren(s, to, rightBound, subnode); if (subRightmost != to) rightmost = subRightmost; else rightmost = j; node->children.push_back(subnode); j = s->findTime(jex.second); } return rightmost; } void RawNoteRuler::buildForest(Segment *s, Segment::iterator from, Segment::iterator to) { for (EventTreeNode::NodeList::iterator i = m_forest.begin(); i != m_forest.end(); ++i) { delete *i; } m_forest.clear(); timeT endTime = (s->isBeforeEndMarker(to) ? (*to)->getAbsoluteTime() : s->getEndMarkerTime()); for (Segment::iterator i = from; i != to && s->isBeforeEndMarker(i); ) { if (!(*i)->isa(Note::EventType)) { ++i; continue; } std::pair iex = getExtents(i); #ifdef DEBUG_RAW_NOTE_RULER RG_DEBUG << "buildForest: event at " << (*i)->getAbsoluteTime() << ", extents " << iex.first << "->" << iex.second << endl; #endif if (iex.first == iex.second) { ++i; continue; } if (iex.first >= endTime) break; EventTreeNode *node = new EventTreeNode(i); Segment::iterator rightmost = addChildren(s, to, iex.second, node); m_forest.push_back(node); if (rightmost != to) { i = rightmost; ++i; } else { i = s->findTime(iex.second); } #ifdef DEBUG_RAW_NOTE_RULER RG_DEBUG << "findTime " << iex.second << " returned iterator at " << (i == s->end() ? -1 : (*i)->getAbsoluteTime()) << endl; #endif } } void RawNoteRuler::dumpSubtree(EventTreeNode *node, int depth) { if (!node) return ; #ifdef DEBUG_RAW_NOTE_RULER for (int i = 0; i < depth; ++i) std::cerr << " "; if (depth > 0) std::cerr << "->"; std::cerr << (*node->node)->getAbsoluteTime() << "," << (*node->node)->getDuration() << " ["; long pitch = 0; if ((*node->node)->get (PITCH, pitch)) { std::cerr << pitch << "]" << std::endl; } else { std::cerr << "no-pitch]" << std::endl; } for (EventTreeNode::NodeList::iterator i = node->children.begin(); i != node->children.end(); ++i) { dumpSubtree(*i, depth + 1); } #endif (void)depth; // avoid warnings } void RawNoteRuler::dumpForest(EventTreeNode::NodeList *forest) { #ifdef DEBUG_RAW_NOTE_RULER std::cerr << "\nFOREST:\n" << std::endl; for (unsigned int i = 0; i < forest->size(); ++i) { std::cerr << "\nTREE " << i << ":\n" << std::endl; dumpSubtree((*forest)[i], 0); } std::cerr << std::endl; #endif (void)forest; // avoid warnings } int RawNoteRuler::EventTreeNode::getDepth() { int subchildrenDepth = 0; for (NodeList::iterator i = children.begin(); i != children.end(); ++i) { int subchildDepth = (*i)->getDepth(); if (subchildDepth > subchildrenDepth) subchildrenDepth = subchildDepth; } return subchildrenDepth + 1; } int RawNoteRuler::EventTreeNode::getChildrenAboveOrBelow(bool below, int p) { long pitch(p); if (pitch < 0) (*node)->get (BaseProperties::PITCH, pitch); int max = 0; for (NodeList::iterator i = children.begin(); i != children.end(); ++i) { int forThisChild = (*i)->getChildrenAboveOrBelow(below, pitch); long thisChildPitch = pitch; (*(*i)->node)->get (BaseProperties::PITCH, thisChildPitch); if (below ? (thisChildPitch < pitch) : (thisChildPitch > pitch)) { ++forThisChild; } if (forThisChild > max) max = forThisChild; } return max; } void RawNoteRuler::drawNode(QPainter &paint, DefaultVelocityColour &vc, EventTreeNode *node, double height, double yorigin) { int depth = node->getDepth(); int above = node->getChildrenAboveOrBelow(false); #ifdef DEBUG_RAW_NOTE_RULER int below = node->getChildrenAboveOrBelow(true); NOTATION_DEBUG << "RawNoteRuler::drawNode: children above: " << above << ", below: " << below << endl; #endif int toFit = depth; double heightPer = double(height) / toFit; if (heightPer > m_height / 4) heightPer = m_height / 4; if (heightPer < 2) heightPer = 2; double myOrigin = yorigin + (heightPer * above); long myPitch = 60; (*node->node)->get (BaseProperties::PITCH, myPitch); long velocity = 100; (*node->node)->get (BaseProperties::VELOCITY, velocity); QColor colour = vc.getColour(velocity); timeT start = (*node->node)->getAbsoluteTime(); timeT end = (*node->node)->getDuration() + start; double u0 = m_rulerScale->getXForTime(start); double u1 = m_rulerScale->getXForTime(end); u0 += m_currentXOffset + m_xorigin; u1 += m_currentXOffset + m_xorigin; start = m_segment->getComposition()->getNotationQuantizer()-> getQuantizedAbsoluteTime(*node->node); end = start + m_segment->getComposition()->getNotationQuantizer()-> getQuantizedDuration(*node->node); double q0 = m_rulerScale->getXForTime(start); double q1 = m_rulerScale->getXForTime(end); q0 += m_currentXOffset + m_xorigin; q1 += m_currentXOffset + m_xorigin; #ifdef DEBUG_RAW_NOTE_RULER NOTATION_DEBUG << "RawNoteRuler: (" << int(start) << "," << myOrigin << ") -> (" << int(end) << "," << myOrigin << ")" << endl; #endif int qi0 = int(q0); int ui0 = int(u0); int qi1 = int(q1); int ui1 = int(u1); // int qiw = int(q1-q0) - 1; int uiw = int(u1 - u0) - 1; // int iy = int(myOrigin + (height - heightPer) / 2); int iy = int(myOrigin); int ih = int(heightPer); #ifdef DEBUG_RAW_NOTE_RULER NOTATION_DEBUG << "RawNoteRuler: height " << height << ", heightPer " << heightPer << ", iy " << iy << endl; #endif paint.setPen(colour); paint.setBrush(colour); paint.drawRect(ui0 + 1, iy + 1, uiw, ih - 1); paint.setPen(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground)); paint.setBrush(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground)); paint.drawLine(qi0, iy, qi1 - 1, iy); paint.drawLine(qi0, iy + ih, qi1 - 1, iy + ih); paint.drawLine(ui0, iy + 1, ui0, iy + ih - 1); paint.drawLine(ui1 - 1, iy + 1, ui1 - 1, iy + ih - 1); for (EventTreeNode::NodeList::iterator i = node->children.begin(); i != node->children.end(); ++i) { long nodePitch = myPitch; (*(*i)->node)->get (BaseProperties::PITCH, nodePitch); if (nodePitch < myPitch) { drawNode(paint, vc, *i, height - heightPer - myOrigin, myOrigin + heightPer); } else { drawNode(paint, vc, *i, myOrigin - yorigin, yorigin); } } } void RawNoteRuler::paintEvent(QPaintEvent* e) { if (!m_segment || !m_segment->getComposition()) return ; // Tooltips { // QToolTip::remove(this); TrackId trackId = m_segment->getTrack(); Track *track = m_segment->getComposition()->getTrackById(trackId); int trackPosition = -1; if (track) trackPosition = track->getPosition(); this->setToolTip(tr("Track #%1, Segment \"%2\" (runtime id %3)") .arg(trackPosition + 1) .arg(strtoqstr(m_segment->getLabel())) .arg(m_segment->getRuntimeId())); } // START_TIMING; QPainter paint(this); paint.setClipRegion(e->region()); paint.setClipRect(e->rect().normalize()); QRect clipRect = paint.clipRegion().boundingRect(); timeT from = m_rulerScale->getTimeForX (clipRect.x() - m_currentXOffset - 100 - m_xorigin); timeT to = m_rulerScale->getTimeForX (clipRect.x() + clipRect.width() - m_currentXOffset + 100 - m_xorigin); paint.setPen(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground)); paint.setBrush(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground)); paint.drawLine(0, 0, width(), 0); // draw the extent of the segment using its color QColor brushColor = GUIPalette::convertColour(m_segment->getComposition()-> getSegmentColourMap().getColourByIndex(m_segment->getColourIndex())); paint.setPen(brushColor); paint.setBrush(brushColor); int x0 = int(m_rulerScale->getXForTime(m_segment->getStartTime()) + m_currentXOffset + m_xorigin); int x1 = int(m_rulerScale->getXForTime(m_segment->getEndMarkerTime()) + m_currentXOffset + m_xorigin); paint.drawRect(x0, 1, x1-x0+1, height()-1); // draw the bar divisions int firstBar = m_segment->getComposition()->getBarNumber(from); int lastBar = m_segment->getComposition()->getBarNumber(to); std::vector divisions; for (int barNo = firstBar; barNo <= lastBar; ++barNo) { bool isNew = false; TimeSignature timeSig = m_segment->getComposition()->getTimeSignatureInBar(barNo, isNew); if (isNew || barNo == firstBar) { timeSig.getDivisions(3, divisions); if (timeSig == TimeSignature()) // special case for 4/4 divisions[0] = 2; } timeT barStart = m_segment->getComposition()->getBarStart(barNo); timeT base = timeSig.getBarDuration(); timeT barEnd = barStart + base; paint.setPen(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground)); paint.setBrush(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground)); int x = int(m_rulerScale->getXForTime(barStart) + m_currentXOffset + m_xorigin); paint.drawLine(x, 1, x, m_height); for (int depth = 0; depth < 3; ++depth) { int grey = depth * 60 + 60; paint.setPen(QColor(grey, grey, grey)); paint.setBrush(QColor(grey, grey, grey)); base /= divisions[depth]; timeT t(barStart + base); while (t < barEnd) { if ((t - barStart) % (base * divisions[depth]) != 0) { int x = int(m_rulerScale->getXForTime(t) + m_currentXOffset + m_xorigin); paint.drawLine(x, 1, x, m_height); } t += base; } } } // PRINT_ELAPSED("RawNoteRuler::paintEvent: drawing bar lines and divisions"); #ifdef DEBUG_RAW_NOTE_RULER NOTATION_DEBUG << "RawNoteRuler: from is " << from << ", to is " << to << endl; #endif Segment::iterator i = m_segment->findNearestTime(from); if (i == m_segment->end()) i = m_segment->begin(); // somewhat experimental, as is this whole class Segment::iterator j = m_segment->findTime(to); buildForest(m_segment, i, j); // PRINT_ELAPSED("RawNoteRuler::paintEvent: buildForest"); dumpForest(&m_forest); // PRINT_ELAPSED("RawNoteRuler::paintEvent: dumpForest"); for (EventTreeNode::NodeList::iterator fi = m_forest.begin(); fi != m_forest.end(); ++fi) { // Each tree in the forest should represent a note that starts // at a time when no other notes are playing (at least of // those that started no earlier than the paint start time). // Each node in that tree represents a note that starts // playing during its parent node's note, or at the same time // as it. drawNode(paint, *DefaultVelocityColour::getInstance(), *fi, m_height - 3, 2); } // PRINT_ELAPSED("RawNoteRuler::paintEvent: complete"); } } #include "RawNoteRuler.moc"