annotate 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
rev   line source
Chris@58 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@59 4 Sonic Visualiser
Chris@59 5 An audio file viewer and annotation editor.
Chris@59 6 Centre for Digital Music, Queen Mary, University of London.
Chris@59 7 This file copyright 2006 Chris Cannam.
Chris@0 8
Chris@59 9 This program is free software; you can redistribute it and/or
Chris@59 10 modify it under the terms of the GNU General Public License as
Chris@59 11 published by the Free Software Foundation; either version 2 of the
Chris@59 12 License, or (at your option) any later version. See the file
Chris@59 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@0 16 #include "TimeRulerLayer.h"
Chris@0 17
Chris@335 18 #include "LayerFactory.h"
Chris@335 19
Chris@128 20 #include "data/model/Model.h"
Chris@0 21 #include "base/RealTime.h"
Chris@287 22 #include "base/ColourDatabase.h"
Chris@128 23 #include "view/View.h"
Chris@0 24
Chris@0 25 #include <QPainter>
Chris@0 26
Chris@0 27 #include <iostream>
Chris@271 28 #include <cmath>
Chris@0 29
Chris@0 30 using std::cerr;
Chris@0 31 using std::endl;
Chris@0 32
Chris@44 33 TimeRulerLayer::TimeRulerLayer() :
Chris@287 34 SingleColourLayer(),
Chris@0 35 m_model(0),
Chris@0 36 m_labelHeight(LabelTop)
Chris@0 37 {
Chris@44 38
Chris@0 39 }
Chris@0 40
Chris@0 41 void
Chris@0 42 TimeRulerLayer::setModel(Model *model)
Chris@0 43 {
Chris@0 44 if (m_model == model) return;
Chris@0 45 m_model = model;
Chris@0 46 emit modelReplaced();
Chris@0 47 }
Chris@0 48
Chris@271 49 bool
Chris@271 50 TimeRulerLayer::snapToFeatureFrame(View *v, int &frame,
Chris@271 51 size_t &resolution, SnapType snap) const
Chris@271 52 {
Chris@271 53 if (!m_model) {
Chris@271 54 resolution = 1;
Chris@271 55 return false;
Chris@271 56 }
Chris@271 57
Chris@271 58 bool q;
Chris@271 59 int tick = getMajorTickSpacing(v, q);
Chris@271 60 RealTime rtick = RealTime::fromMilliseconds(tick);
Chris@271 61 int rate = m_model->getSampleRate();
Chris@271 62
Chris@271 63 RealTime rt = RealTime::frame2RealTime(frame, rate);
Chris@271 64 double ratio = rt / rtick;
Chris@271 65
Chris@272 66 int rounded = int(ratio);
Chris@271 67 RealTime rdrt = rtick * rounded;
Chris@271 68
Chris@271 69 int left = RealTime::realTime2Frame(rdrt, rate);
Chris@271 70 resolution = RealTime::realTime2Frame(rtick, rate);
Chris@271 71 int right = left + resolution;
Chris@271 72
Chris@272 73 // std::cerr << "TimeRulerLayer::snapToFeatureFrame: type "
Chris@272 74 // << int(snap) << ", frame " << frame << " (time "
Chris@272 75 // << rt << ", tick " << rtick << ", rounded " << rdrt << ") ";
Chris@272 76
Chris@271 77 switch (snap) {
Chris@271 78
Chris@271 79 case SnapLeft:
Chris@271 80 frame = left;
Chris@271 81 break;
Chris@271 82
Chris@271 83 case SnapRight:
Chris@271 84 frame = right;
Chris@271 85 break;
Chris@271 86
Chris@271 87 case SnapNearest:
Chris@271 88 {
Chris@271 89 if (abs(frame - left) > abs(right - frame)) {
Chris@271 90 frame = right;
Chris@271 91 } else {
Chris@271 92 frame = left;
Chris@271 93 }
Chris@271 94 break;
Chris@271 95 }
Chris@271 96
Chris@271 97 case SnapNeighbouring:
Chris@271 98 {
Chris@271 99 int dl = -1, dr = -1;
Chris@271 100 int x = v->getXForFrame(frame);
Chris@271 101
Chris@271 102 if (left > v->getStartFrame() &&
Chris@271 103 left < v->getEndFrame()) {
Chris@271 104 dl = abs(v->getXForFrame(left) - x);
Chris@271 105 }
Chris@271 106
Chris@271 107 if (right > v->getStartFrame() &&
Chris@271 108 right < v->getEndFrame()) {
Chris@271 109 dr = abs(v->getXForFrame(right) - x);
Chris@271 110 }
Chris@271 111
Chris@271 112 int fuzz = 2;
Chris@271 113
Chris@271 114 if (dl >= 0 && dr >= 0) {
Chris@271 115 if (dl < dr) {
Chris@271 116 if (dl <= fuzz) {
Chris@271 117 frame = left;
Chris@271 118 }
Chris@271 119 } else {
Chris@271 120 if (dr < fuzz) {
Chris@271 121 frame = right;
Chris@271 122 }
Chris@271 123 }
Chris@271 124 } else if (dl >= 0) {
Chris@271 125 if (dl <= fuzz) {
Chris@271 126 frame = left;
Chris@271 127 }
Chris@271 128 } else if (dr >= 0) {
Chris@271 129 if (dr <= fuzz) {
Chris@271 130 frame = right;
Chris@271 131 }
Chris@271 132 }
Chris@271 133 }
Chris@271 134 }
Chris@271 135
Chris@272 136 // std::cerr << " -> " << frame << " (resolution = " << resolution << ")" << std::endl;
Chris@272 137
Chris@271 138 return true;
Chris@271 139 }
Chris@271 140
Chris@271 141 int
Chris@271 142 TimeRulerLayer::getMajorTickSpacing(View *v, bool &quarterTicks) const
Chris@271 143 {
Chris@271 144 // return value is in milliseconds
Chris@271 145
Chris@271 146 if (!m_model || !v) return 1000;
Chris@271 147
Chris@271 148 int sampleRate = m_model->getSampleRate();
Chris@271 149 if (!sampleRate) return 1000;
Chris@271 150
Chris@271 151 long startFrame = v->getStartFrame();
Chris@271 152 long endFrame = v->getEndFrame();
Chris@271 153
Chris@271 154 int minPixelSpacing = 50;
Chris@271 155
Chris@271 156 RealTime rtStart = RealTime::frame2RealTime(startFrame, sampleRate);
Chris@271 157 RealTime rtEnd = RealTime::frame2RealTime(endFrame, sampleRate);
Chris@271 158
Chris@271 159 int count = v->width() / minPixelSpacing;
Chris@271 160 if (count < 1) count = 1;
Chris@271 161 RealTime rtGap = (rtEnd - rtStart) / count;
Chris@271 162
Chris@271 163 int incms;
Chris@271 164 quarterTicks = false;
Chris@271 165
Chris@271 166 if (rtGap.sec > 0) {
Chris@271 167 incms = 1000;
Chris@271 168 int s = rtGap.sec;
Chris@271 169 if (s > 0) { incms *= 5; s /= 5; }
Chris@271 170 if (s > 0) { incms *= 2; s /= 2; }
Chris@271 171 if (s > 0) { incms *= 6; s /= 6; quarterTicks = true; }
Chris@271 172 if (s > 0) { incms *= 5; s /= 5; quarterTicks = false; }
Chris@271 173 if (s > 0) { incms *= 2; s /= 2; }
Chris@271 174 if (s > 0) { incms *= 6; s /= 6; quarterTicks = true; }
Chris@271 175 while (s > 0) {
Chris@271 176 incms *= 10;
Chris@271 177 s /= 10;
Chris@271 178 quarterTicks = false;
Chris@271 179 }
Chris@271 180 } else {
Chris@271 181 incms = 1;
Chris@271 182 int ms = rtGap.msec();
Chris@271 183 if (ms > 0) { incms *= 10; ms /= 10; }
Chris@271 184 if (ms > 0) { incms *= 10; ms /= 10; }
Chris@271 185 if (ms > 0) { incms *= 5; ms /= 5; }
Chris@271 186 if (ms > 0) { incms *= 2; ms /= 2; }
Chris@271 187 }
Chris@271 188
Chris@271 189 return incms;
Chris@271 190 }
Chris@271 191
Chris@0 192 void
Chris@44 193 TimeRulerLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@0 194 {
Chris@0 195 // std::cerr << "TimeRulerLayer::paint (" << rect.x() << "," << rect.y()
Chris@0 196 // << ") [" << rect.width() << "x" << rect.height() << "]" << std::endl;
Chris@0 197
Chris@0 198 if (!m_model || !m_model->isOK()) return;
Chris@0 199
Chris@0 200 int sampleRate = m_model->getSampleRate();
Chris@0 201 if (!sampleRate) return;
Chris@0 202
Chris@44 203 long startFrame = v->getStartFrame();
Chris@44 204 long endFrame = v->getEndFrame();
Chris@0 205
Chris@44 206 int zoomLevel = v->getZoomLevel();
Chris@0 207
Chris@0 208 long rectStart = startFrame + (rect.x() - 100) * zoomLevel;
Chris@0 209 long rectEnd = startFrame + (rect.x() + rect.width() + 100) * zoomLevel;
Chris@0 210
Chris@0 211 // std::cerr << "TimeRulerLayer::paint: calling paint.save()" << std::endl;
Chris@0 212 paint.save();
Chris@44 213 //!!! paint.setClipRect(v->rect());
Chris@0 214
Chris@0 215 int minPixelSpacing = 50;
Chris@0 216
Chris@0 217 bool quarter = false;
Chris@271 218 int incms = getMajorTickSpacing(v, quarter);
Chris@0 219
Chris@0 220 RealTime rt = RealTime::frame2RealTime(rectStart, sampleRate);
Chris@0 221 long ms = rt.sec * 1000 + rt.msec();
Chris@0 222 ms = (ms / incms) * incms - incms;
Chris@0 223
Chris@30 224 RealTime incRt = RealTime::fromMilliseconds(incms);
Chris@0 225 long incFrame = RealTime::realTime2Frame(incRt, sampleRate);
Chris@0 226 int incX = incFrame / zoomLevel;
Chris@0 227 int ticks = 10;
Chris@0 228 if (incX < minPixelSpacing * 2) {
Chris@0 229 ticks = quarter ? 4 : 5;
Chris@0 230 }
Chris@0 231
Chris@0 232 QRect oldClipRect = rect;
Chris@0 233 QRect newClipRect(oldClipRect.x() - 25, oldClipRect.y(),
Chris@0 234 oldClipRect.width() + 50, oldClipRect.height());
Chris@0 235 paint.setClipRect(newClipRect);
Chris@55 236 paint.setClipRect(rect);
Chris@0 237
Chris@287 238 QColor greyColour = getPartialShades(v)[1];
Chris@0 239
Chris@0 240 while (1) {
Chris@0 241
Chris@30 242 rt = RealTime::fromMilliseconds(ms);
Chris@0 243 ms += incms;
Chris@0 244
Chris@0 245 long frame = RealTime::realTime2Frame(rt, sampleRate);
Chris@0 246 if (frame >= rectEnd) break;
Chris@0 247
Chris@0 248 int x = (frame - startFrame) / zoomLevel;
Chris@0 249 if (x < rect.x() || x >= rect.x() + rect.width()) continue;
Chris@0 250
Chris@0 251 paint.setPen(greyColour);
Chris@44 252 paint.drawLine(x, 0, x, v->height());
Chris@0 253
Chris@287 254 paint.setPen(getBaseQColor());
Chris@0 255 paint.drawLine(x, 0, x, 5);
Chris@44 256 paint.drawLine(x, v->height() - 6, x, v->height() - 1);
Chris@0 257
Chris@0 258 QString text(QString::fromStdString(rt.toText()));
Chris@0 259
Chris@0 260 int y;
Chris@0 261 QFontMetrics metrics = paint.fontMetrics();
Chris@0 262 switch (m_labelHeight) {
Chris@0 263 default:
Chris@0 264 case LabelTop:
Chris@0 265 y = 6 + metrics.ascent();
Chris@0 266 break;
Chris@0 267 case LabelMiddle:
Chris@44 268 y = v->height() / 2 - metrics.height() / 2 + metrics.ascent();
Chris@0 269 break;
Chris@0 270 case LabelBottom:
Chris@44 271 y = v->height() - metrics.height() + metrics.ascent() - 6;
Chris@0 272 }
Chris@0 273
Chris@0 274 int tw = metrics.width(text);
Chris@0 275
Chris@70 276 if (v->getViewManager() && v->getViewManager()->getOverlayMode() !=
Chris@70 277 ViewManager::NoOverlays) {
Chris@70 278
Chris@70 279 if (v->getLayer(0) == this) {
Chris@70 280 // backmost layer, don't worry about outlining the text
Chris@70 281 paint.drawText(x+2 - tw/2, y, text);
Chris@70 282 } else {
Chris@70 283 v->drawVisibleText(paint, x+2 - tw/2, y, text, View::OutlinedText);
Chris@70 284 }
Chris@70 285 }
Chris@0 286
Chris@0 287 paint.setPen(greyColour);
Chris@0 288
Chris@0 289 for (int i = 1; i < ticks; ++i) {
Chris@0 290 rt = rt + (incRt / ticks);
Chris@0 291 frame = RealTime::realTime2Frame(rt, sampleRate);
Chris@0 292 x = (frame - startFrame) / zoomLevel;
Chris@0 293 int sz = 5;
Chris@0 294 if (ticks == 10) {
Chris@0 295 if ((i % 2) == 1) {
Chris@0 296 if (i == 5) {
Chris@44 297 paint.drawLine(x, 0, x, v->height());
Chris@0 298 } else sz = 3;
Chris@0 299 } else {
Chris@0 300 sz = 7;
Chris@0 301 }
Chris@0 302 }
Chris@0 303 paint.drawLine(x, 0, x, sz);
Chris@44 304 paint.drawLine(x, v->height() - sz - 1, x, v->height() - 1);
Chris@0 305 }
Chris@0 306 }
Chris@0 307
Chris@0 308 paint.restore();
Chris@0 309 }
Chris@287 310
Chris@287 311 int
Chris@287 312 TimeRulerLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@287 313 {
Chris@287 314 impose = true;
Chris@287 315 return ColourDatabase::getInstance()->getColourIndex
Chris@287 316 (QString(darkbg ? "White" : "Black"));
Chris@287 317 }
Chris@287 318
Chris@335 319 QString TimeRulerLayer::getLayerPresentationName() const
Chris@335 320 {
Chris@335 321 LayerFactory *factory = LayerFactory::getInstance();
Chris@335 322 QString layerName = factory->getLayerPresentationName
Chris@335 323 (factory->getLayerType(this));
Chris@335 324 return layerName;
Chris@335 325 }
Chris@335 326
Chris@316 327 void
Chris@316 328 TimeRulerLayer::toXml(QTextStream &stream,
Chris@316 329 QString indent, QString extraAttributes) const
Chris@6 330 {
Chris@316 331 SingleColourLayer::toXml(stream, indent, extraAttributes);
Chris@6 332 }
Chris@6 333
Chris@11 334 void
Chris@11 335 TimeRulerLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 336 {
Chris@287 337 SingleColourLayer::setProperties(attributes);
Chris@11 338 }
Chris@11 339