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.
574 lines
16 KiB
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 <klocale.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);
|
|
tqrepaint(w - dx, 0, dx, h);
|
|
} else { // moving left, so the existing stuff moves right
|
|
bitBlt(this, -dx, 0, this, 0, 0, w + dx, h);
|
|
tqrepaint(0, 0, -dx, h);
|
|
}
|
|
}
|
|
|
|
TQSize
|
|
RawNoteRuler::tqsizeHint() 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::tqminimumSizeHint() 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->tqchildren.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->tqchildren.begin();
|
|
i != node->tqchildren.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 subtqchildrenDepth = 0;
|
|
for (NodeList::iterator i = tqchildren.begin();
|
|
i != tqchildren.end(); ++i) {
|
|
int subchildDepth = (*i)->getDepth();
|
|
if (subchildDepth > subtqchildrenDepth)
|
|
subtqchildrenDepth = subchildDepth;
|
|
}
|
|
return subtqchildrenDepth + 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 = tqchildren.begin();
|
|
i != tqchildren.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: tqchildren 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->tqchildren.begin();
|
|
i != node->tqchildren.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)")
|
|
.tqarg(trackPosition + 1)
|
|
.tqarg(m_segment->getLabel().c_str())
|
|
.tqarg(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"
|