/* Rosegarden A MIDI and audio sequencer and musical notation editor. This program is Copyright 2000-2008 Guillaume Laurent , Chris Cannam , Richard Bown The moral rights of Guillaume Laurent, Chris Cannam, and Richard Bown to claim authorship of this work have been asserted. 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 "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 #include #include #include #include #include #include namespace Rosegarden { RawNoteRuler::RawNoteRuler(RulerScale *rulerScale, Segment *segment, double xorigin, int height, TQWidget *parent, const char *name) : TQWidget(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)); TQToolTip::add(this,""); } RawNoteRuler::~RawNoteRuler() { TQToolTip::remove(this); // nothing else } void RawNoteRuler::slotScrollHoriz(int x) { int w = width(), h = height(); int dx = x - ( -m_currentXOffset); if (dx == 0) return ; m_currentXOffset = -x; 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); } } TQSize RawNoteRuler::sizeHint() const { double width = m_rulerScale->getBarPosition(m_rulerScale->getLastVisibleBar()) + m_rulerScale->getBarWidth(m_rulerScale->getLastVisibleBar()) + m_xorigin; TQSize res(std::max(int(width), m_width), m_height); return res; } TQSize RawNoteRuler::minimumSizeHint() const { double firstBarWidth = m_rulerScale->getBarWidth(0) + m_xorigin; TQSize res = TQSize(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(TQPainter &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); TQColor 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(TQPaintEvent* e) { if (!m_segment || !m_segment->getComposition()) return ; // Tooltips { TQToolTip::remove(this); TrackId trackId = m_segment->getTrack(); Track *track = m_segment->getComposition()->getTrackById(trackId); int trackPosition = -1; if (track) trackPosition = track->getPosition(); TQToolTip::add(this,i18n("Track #%1, Segment \"%2\" (runtime id %3)") .arg(trackPosition + 1) .arg(m_segment->getLabel().c_str()) .arg(m_segment->getRuntimeId())); } // START_TIMING; TQPainter paint(this); paint.setClipRegion(e->region()); paint.setClipRect(e->rect().normalize()); TQRect 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 TQColor 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->getEndTime()) + 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(TQColor(grey, grey, grey)); paint.setBrush(TQColor(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"