Mercurial > hg > svgui
view layer/TimeRulerLayer.cpp @ 349:369a197737c7
* Various fixes to object lifetime management, particularly in the spectrum
layer and for notification of main model deletion.
The main purpose of this is to improve the behaviour of the spectrum, but I
think it may also help with #1840922 Various crashes in Layer Summary window.
author | Chris Cannam |
---|---|
date | Wed, 23 Jan 2008 15:43:27 +0000 |
parents | 2f83b6e3b8ca |
children | 8ebc2ce2a210 |
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ /* Sonic Visualiser An audio file viewer and annotation editor. Centre for Digital Music, Queen Mary, University of London. This file copyright 2006 Chris Cannam. 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 "TimeRulerLayer.h" #include "LayerFactory.h" #include "data/model/Model.h" #include "base/RealTime.h" #include "base/ColourDatabase.h" #include "view/View.h" #include <QPainter> #include <iostream> #include <cmath> using std::cerr; using std::endl; TimeRulerLayer::TimeRulerLayer() : SingleColourLayer(), m_model(0), m_labelHeight(LabelTop) { } void TimeRulerLayer::setModel(Model *model) { if (m_model == model) return; m_model = model; emit modelReplaced(); } bool TimeRulerLayer::snapToFeatureFrame(View *v, int &frame, size_t &resolution, SnapType snap) const { if (!m_model) { resolution = 1; return false; } bool q; int tick = getMajorTickSpacing(v, q); RealTime rtick = RealTime::fromMilliseconds(tick); int rate = m_model->getSampleRate(); RealTime rt = RealTime::frame2RealTime(frame, rate); double ratio = rt / rtick; int rounded = int(ratio); RealTime rdrt = rtick * rounded; int left = RealTime::realTime2Frame(rdrt, rate); resolution = RealTime::realTime2Frame(rtick, rate); int right = left + resolution; // std::cerr << "TimeRulerLayer::snapToFeatureFrame: type " // << int(snap) << ", frame " << frame << " (time " // << rt << ", tick " << rtick << ", rounded " << rdrt << ") "; switch (snap) { case SnapLeft: frame = left; break; case SnapRight: frame = right; break; case SnapNearest: { if (abs(frame - left) > abs(right - frame)) { frame = right; } else { frame = left; } break; } case SnapNeighbouring: { int dl = -1, dr = -1; int x = v->getXForFrame(frame); if (left > v->getStartFrame() && left < v->getEndFrame()) { dl = abs(v->getXForFrame(left) - x); } if (right > v->getStartFrame() && right < v->getEndFrame()) { dr = abs(v->getXForFrame(right) - x); } int fuzz = 2; if (dl >= 0 && dr >= 0) { if (dl < dr) { if (dl <= fuzz) { frame = left; } } else { if (dr < fuzz) { frame = right; } } } else if (dl >= 0) { if (dl <= fuzz) { frame = left; } } else if (dr >= 0) { if (dr <= fuzz) { frame = right; } } } } // std::cerr << " -> " << frame << " (resolution = " << resolution << ")" << std::endl; return true; } int TimeRulerLayer::getMajorTickSpacing(View *v, bool &quarterTicks) const { // return value is in milliseconds if (!m_model || !v) return 1000; int sampleRate = m_model->getSampleRate(); if (!sampleRate) return 1000; long startFrame = v->getStartFrame(); long endFrame = v->getEndFrame(); int minPixelSpacing = 50; RealTime rtStart = RealTime::frame2RealTime(startFrame, sampleRate); RealTime rtEnd = RealTime::frame2RealTime(endFrame, sampleRate); int count = v->width() / minPixelSpacing; if (count < 1) count = 1; RealTime rtGap = (rtEnd - rtStart) / count; int incms; quarterTicks = 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; quarterTicks = true; } if (s > 0) { incms *= 5; s /= 5; quarterTicks = false; } if (s > 0) { incms *= 2; s /= 2; } if (s > 0) { incms *= 6; s /= 6; quarterTicks = true; } while (s > 0) { incms *= 10; s /= 10; quarterTicks = 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; } } return incms; } void TimeRulerLayer::paint(View *v, 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 = v->getStartFrame(); long endFrame = v->getEndFrame(); int zoomLevel = v->getZoomLevel(); long rectStart = startFrame + (rect.x() - 100) * zoomLevel; long rectEnd = startFrame + (rect.x() + rect.width() + 100) * zoomLevel; // std::cerr << "TimeRulerLayer::paint: calling paint.save()" << std::endl; paint.save(); //!!! paint.setClipRect(v->rect()); int minPixelSpacing = 50; bool quarter = false; int incms = getMajorTickSpacing(v, quarter); RealTime rt = RealTime::frame2RealTime(rectStart, sampleRate); long ms = rt.sec * 1000 + rt.msec(); ms = (ms / incms) * incms - incms; RealTime incRt = RealTime::fromMilliseconds(incms); 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); paint.setClipRect(rect); QColor greyColour = getPartialShades(v)[1]; while (1) { rt = RealTime::fromMilliseconds(ms); 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, v->height()); paint.setPen(getBaseQColor()); paint.drawLine(x, 0, x, 5); paint.drawLine(x, v->height() - 6, x, v->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 = v->height() / 2 - metrics.height() / 2 + metrics.ascent(); break; case LabelBottom: y = v->height() - metrics.height() + metrics.ascent() - 6; } int tw = metrics.width(text); if (v->getViewManager() && v->getViewManager()->getOverlayMode() != ViewManager::NoOverlays) { if (v->getLayer(0) == this) { // backmost layer, don't worry about outlining the text paint.drawText(x+2 - tw/2, y, text); } else { v->drawVisibleText(paint, x+2 - tw/2, y, text, View::OutlinedText); } } 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, v->height()); } else sz = 3; } else { sz = 7; } } paint.drawLine(x, 0, x, sz); paint.drawLine(x, v->height() - sz - 1, x, v->height() - 1); } } paint.restore(); } int TimeRulerLayer::getDefaultColourHint(bool darkbg, bool &impose) { impose = true; return ColourDatabase::getInstance()->getColourIndex (QString(darkbg ? "White" : "Black")); } QString TimeRulerLayer::getLayerPresentationName() const { LayerFactory *factory = LayerFactory::getInstance(); QString layerName = factory->getLayerPresentationName (factory->getLayerType(this)); return layerName; } void TimeRulerLayer::toXml(QTextStream &stream, QString indent, QString extraAttributes) const { SingleColourLayer::toXml(stream, indent, extraAttributes); } void TimeRulerLayer::setProperties(const QXmlAttributes &attributes) { SingleColourLayer::setProperties(attributes); }