view layer/TimeRulerLayer.cpp @ 1:ab83c415a6cd

* Backed out partially complete changes to make the spectrogram only store results up to the requested max frequency. The speed improvement was minimal at the expense of annoyance when changing frequency limit, and although it did save memory, it wasn't yet reliable and fixing it is not a high enough priority.
author Chris Cannam
date Tue, 10 Jan 2006 17:04:02 +0000
parents 2a4f26e85b4c
children 37b110168acf
line wrap: on
line source
/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */

/*
    A waveform viewer and audio annotation editor.
    Chris Cannam, Queen Mary University of London, 2005
   
    This is experimental software.  Not for distribution.
*/

#include "TimeRulerLayer.h"

#include "base/Model.h"
#include "base/RealTime.h"
#include "base/View.h"

#include <QPainter>

#include <iostream>

using std::cerr;
using std::endl;

TimeRulerLayer::TimeRulerLayer(View *w) :
    Layer(w),
    m_model(0),
    m_colour(Qt::black),
    m_labelHeight(LabelTop)
{
    m_view->addLayer(this);
}

void
TimeRulerLayer::setModel(Model *model)
{
    if (m_model == model) return;
    m_model = model;
    emit modelReplaced();
}

void
TimeRulerLayer::setBaseColour(QColor colour)
{
    if (m_colour == colour) return;
    m_colour = colour;
    emit layerParametersChanged();
}

Layer::PropertyList
TimeRulerLayer::getProperties() const
{
    PropertyList list;
    list.push_back(tr("Colour"));
    return list;
}

Layer::PropertyType
TimeRulerLayer::getPropertyType(const PropertyName &name) const
{
    return ValueProperty;
}

int
TimeRulerLayer::getPropertyRangeAndValue(const PropertyName &name,
					 int *min, int *max) const
{
    int deft = 0;

    if (name == tr("Colour")) {

	*min = 0;
	*max = 5;

	if (m_colour == Qt::black) deft = 0;
	else if (m_colour == Qt::darkRed) deft = 1;
	else if (m_colour == Qt::darkBlue) deft = 2;
	else if (m_colour == Qt::darkGreen) deft = 3;
	else if (m_colour == QColor(200, 50, 255)) deft = 4;
	else if (m_colour == QColor(255, 150, 50)) deft = 5;

    } else {
	
	deft = Layer::getPropertyRangeAndValue(name, min, max);
    }

    return deft;
}

QString
TimeRulerLayer::getPropertyValueLabel(const PropertyName &name,
				    int value) const
{
    if (name == tr("Colour")) {
	switch (value) {
	default:
	case 0: return tr("Black");
	case 1: return tr("Red");
	case 2: return tr("Blue");
	case 3: return tr("Green");
	case 4: return tr("Purple");
	case 5: return tr("Orange");
	}
    }
    return tr("<unknown>");
}

void
TimeRulerLayer::setProperty(const PropertyName &name, int value)
{
    if (name == tr("Colour")) {
	switch (value) {
	default:
	case 0:	setBaseColour(Qt::black); break;
	case 1: setBaseColour(Qt::darkRed); break;
	case 2: setBaseColour(Qt::darkBlue); break;
	case 3: setBaseColour(Qt::darkGreen); break;
	case 4: setBaseColour(QColor(200, 50, 255)); break;
	case 5: setBaseColour(QColor(255, 150, 50)); break;
	}
    }
}

void
TimeRulerLayer::paint(QPainter &paint, QRect rect) const
{
//    std::cerr << "TimeRulerLayer::paint (" << rect.x() << "," << rect.y()
//	      << ") [" << rect.width() << "x" << rect.height() << "]" << std::endl;
    
    if (!m_model || !m_model->isOK()) return;

    int sampleRate = m_model->getSampleRate();
    if (!sampleRate) return;

    long startFrame = m_view->getStartFrame();
    long endFrame = m_view->getEndFrame();

    int zoomLevel = m_view->getZoomLevel();

    long rectStart = startFrame + (rect.x() - 100) * zoomLevel;
    long rectEnd = startFrame + (rect.x() + rect.width() + 100) * zoomLevel;
    if (rectStart < startFrame) rectStart = startFrame;
    if (rectEnd > endFrame) rectEnd = endFrame;

//    std::cerr << "TimeRulerLayer::paint: calling paint.save()" << std::endl;
    paint.save();
//!!!    paint.setClipRect(m_view->rect());

    int minPixelSpacing = 50;

    RealTime rtStart = RealTime::frame2RealTime(startFrame, sampleRate);
    RealTime rtEnd = RealTime::frame2RealTime(endFrame, sampleRate);
//    cerr << "startFrame " << startFrame << ", endFrame " << m_view->getEndFrame() << ", rtStart " << rtStart << ", rtEnd " << rtEnd << endl;
    int count = m_view->width() / minPixelSpacing;
    if (count < 1) count = 1;
    RealTime rtGap = (rtEnd - rtStart) / count;
//    cerr << "rtGap is " << rtGap << endl;

    int incms;
    bool quarter = false;

    if (rtGap.sec > 0) {
	incms = 1000;
	int s = rtGap.sec;
	if (s > 0) { incms *= 5; s /= 5; }
	if (s > 0) { incms *= 2; s /= 2; }
	if (s > 0) { incms *= 6; s /= 6; quarter = true; }
	if (s > 0) { incms *= 5; s /= 5; quarter = false; }
	if (s > 0) { incms *= 2; s /= 2; }
	if (s > 0) { incms *= 6; s /= 6; quarter = true; }
	while (s > 0) {
	    incms *= 10;
	    s /= 10;
	    quarter = false;
	}
    } else {
	incms = 1;
	int ms = rtGap.msec();
	if (ms > 0) { incms *= 10; ms /= 10; }
	if (ms > 0) { incms *= 10; ms /= 10; }
	if (ms > 0) { incms *= 5; ms /= 5; }
	if (ms > 0) { incms *= 2; ms /= 2; }
    }
//    cerr << "incms is " << incms << endl;

    RealTime rt = RealTime::frame2RealTime(rectStart, sampleRate);
    long ms = rt.sec * 1000 + rt.msec();
    ms = (ms / incms) * incms - incms;

    RealTime incRt = RealTime(incms / 1000, (incms % 1000) * 1000000);
    long incFrame = RealTime::realTime2Frame(incRt, sampleRate);
    int incX = incFrame / zoomLevel;
    int ticks = 10;
    if (incX < minPixelSpacing * 2) {
	ticks = quarter ? 4 : 5;
    }

    QRect oldClipRect = rect;
    QRect newClipRect(oldClipRect.x() - 25, oldClipRect.y(),
		      oldClipRect.width() + 50, oldClipRect.height());
    paint.setClipRect(newClipRect);

    QColor greyColour(m_colour);
    if (m_colour == Qt::black) {
	greyColour = QColor(200,200,200);
    } else {
	greyColour = m_colour.light(150);
    }

    while (1) {

	rt = RealTime(ms / 1000, (ms % 1000) * 1000000);
	ms += incms;

	long frame = RealTime::realTime2Frame(rt, sampleRate);
	if (frame >= rectEnd) break;

	int x = (frame - startFrame) / zoomLevel;
	if (x < rect.x() || x >= rect.x() + rect.width()) continue;

	paint.setPen(greyColour);
	paint.drawLine(x, 0, x, m_view->height());

	paint.setPen(m_colour);
	paint.drawLine(x, 0, x, 5);
	paint.drawLine(x, m_view->height() - 6, x, m_view->height() - 1);

	QString text(QString::fromStdString(rt.toText()));

	int y;
	QFontMetrics metrics = paint.fontMetrics();
	switch (m_labelHeight) {
	default:
	case LabelTop:
	    y = 6 + metrics.ascent();
	    break;
	case LabelMiddle:
	    y = m_view->height() / 2 - metrics.height() / 2 + metrics.ascent();
	    break;
	case LabelBottom:
	    y = m_view->height() - metrics.height() + metrics.ascent() - 6;
	}

	int tw = metrics.width(text);

	paint.setPen(m_view->palette().background().color());

	//!!! simple drawing function for this please
	//!!! and need getContrastingColour() in widget, or use the
	//palette properly -- get the base class able to draw text
	//using the proper colour (or this technique) automatically
	for (int dx = -1; dx <= 1; ++dx) {
	    for (int dy = -1; dy <= 1; ++dy) {
		if ((dx && dy) || !(dx || dy)) continue;
		paint.drawText(x + 2 - tw / 2 + dx, y + dy, text);
	    }
	}
	
	paint.setPen(m_colour);
	paint.drawText(x + 2 - tw / 2, y, text);
		
	paint.setPen(greyColour);

	for (int i = 1; i < ticks; ++i) {
	    rt = rt + (incRt / ticks);
	    frame = RealTime::realTime2Frame(rt, sampleRate);
	    x = (frame - startFrame) / zoomLevel;
	    int sz = 5;
	    if (ticks == 10) {
		if ((i % 2) == 1) {
		    if (i == 5) {
			paint.drawLine(x, 0, x, m_view->height());
		    } else sz = 3;
		} else {
		    sz = 7;
		}
	    }
	    paint.drawLine(x, 0, x, sz);
	    paint.drawLine(x, m_view->height() - sz - 1, x, m_view->height() - 1);
	}
    }

    paint.restore();
}
    

#ifdef INCLUDE_MOCFILES
#include "TimeRulerLayer.moc.cpp"
#endif