You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rosegarden/src/gui/rulers/RawNoteRuler.cpp

574 lines
16 KiB

/* -*- 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.
This program is Copyright 2000-2008
Guillaume Laurent <glaurent@telegraph-road.org>,
Chris Cannam <cannam@all-day-breakfast.com>,
Richard Bown <richard.bown@ferventsoftware.com>
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 <tdelocale.h>
#include <tqcolor.h>
#include <tqpainter.h>
#include <tqrect.h>
#include <tqsize.h>
#include <tqtooltip.h>
#include <tqwidget.h>
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<timeT, timeT>
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<timeT, timeT>(t0, t1);
}
Segment::iterator
RawNoteRuler::addChildren(Segment *s,
Segment::iterator to,
timeT rightBound,
EventTreeNode *node)
{
Segment::iterator i = node->node;
std::pair<timeT, timeT> 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<timeT, timeT> 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<timeT, timeT> 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
<Int>(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
<Int>(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
<Int>(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
<Int>(BaseProperties::PITCH, myPitch);
long velocity = 100;
(*node->node)->get
<Int>(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
<Int>(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<int> 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"