annotate layer/TimeRulerLayer.cpp @ 312:6de6f78b13a1

* Make it possible to drop audio files, layer files, session files and images onto SV panes. Need to do a bit more work on where we expect the dropped file to go, particularly in the case of audio files -- at the moment they're always opened in new panes, but it may be better to by default replace whatever is in the target pane.
author Chris Cannam
date Wed, 10 Oct 2007 15:18:02 +0000
parents cd2492c5fe45
children c0b9eec70639
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@128 18 #include "data/model/Model.h"
Chris@0 19 #include "base/RealTime.h"
Chris@287 20 #include "base/ColourDatabase.h"
Chris@128 21 #include "view/View.h"
Chris@0 22
Chris@0 23 #include <QPainter>
Chris@0 24
Chris@0 25 #include <iostream>
Chris@271 26 #include <cmath>
Chris@0 27
Chris@0 28 using std::cerr;
Chris@0 29 using std::endl;
Chris@0 30
Chris@44 31 TimeRulerLayer::TimeRulerLayer() :
Chris@287 32 SingleColourLayer(),
Chris@0 33 m_model(0),
Chris@0 34 m_labelHeight(LabelTop)
Chris@0 35 {
Chris@44 36
Chris@0 37 }
Chris@0 38
Chris@0 39 void
Chris@0 40 TimeRulerLayer::setModel(Model *model)
Chris@0 41 {
Chris@0 42 if (m_model == model) return;
Chris@0 43 m_model = model;
Chris@0 44 emit modelReplaced();
Chris@0 45 }
Chris@0 46
Chris@271 47 bool
Chris@271 48 TimeRulerLayer::snapToFeatureFrame(View *v, int &frame,
Chris@271 49 size_t &resolution, SnapType snap) const
Chris@271 50 {
Chris@271 51 if (!m_model) {
Chris@271 52 resolution = 1;
Chris@271 53 return false;
Chris@271 54 }
Chris@271 55
Chris@271 56 bool q;
Chris@271 57 int tick = getMajorTickSpacing(v, q);
Chris@271 58 RealTime rtick = RealTime::fromMilliseconds(tick);
Chris@271 59 int rate = m_model->getSampleRate();
Chris@271 60
Chris@271 61 RealTime rt = RealTime::frame2RealTime(frame, rate);
Chris@271 62 double ratio = rt / rtick;
Chris@271 63
Chris@272 64 int rounded = int(ratio);
Chris@271 65 RealTime rdrt = rtick * rounded;
Chris@271 66
Chris@271 67 int left = RealTime::realTime2Frame(rdrt, rate);
Chris@271 68 resolution = RealTime::realTime2Frame(rtick, rate);
Chris@271 69 int right = left + resolution;
Chris@271 70
Chris@272 71 // std::cerr << "TimeRulerLayer::snapToFeatureFrame: type "
Chris@272 72 // << int(snap) << ", frame " << frame << " (time "
Chris@272 73 // << rt << ", tick " << rtick << ", rounded " << rdrt << ") ";
Chris@272 74
Chris@271 75 switch (snap) {
Chris@271 76
Chris@271 77 case SnapLeft:
Chris@271 78 frame = left;
Chris@271 79 break;
Chris@271 80
Chris@271 81 case SnapRight:
Chris@271 82 frame = right;
Chris@271 83 break;
Chris@271 84
Chris@271 85 case SnapNearest:
Chris@271 86 {
Chris@271 87 if (abs(frame - left) > abs(right - frame)) {
Chris@271 88 frame = right;
Chris@271 89 } else {
Chris@271 90 frame = left;
Chris@271 91 }
Chris@271 92 break;
Chris@271 93 }
Chris@271 94
Chris@271 95 case SnapNeighbouring:
Chris@271 96 {
Chris@271 97 int dl = -1, dr = -1;
Chris@271 98 int x = v->getXForFrame(frame);
Chris@271 99
Chris@271 100 if (left > v->getStartFrame() &&
Chris@271 101 left < v->getEndFrame()) {
Chris@271 102 dl = abs(v->getXForFrame(left) - x);
Chris@271 103 }
Chris@271 104
Chris@271 105 if (right > v->getStartFrame() &&
Chris@271 106 right < v->getEndFrame()) {
Chris@271 107 dr = abs(v->getXForFrame(right) - x);
Chris@271 108 }
Chris@271 109
Chris@271 110 int fuzz = 2;
Chris@271 111
Chris@271 112 if (dl >= 0 && dr >= 0) {
Chris@271 113 if (dl < dr) {
Chris@271 114 if (dl <= fuzz) {
Chris@271 115 frame = left;
Chris@271 116 }
Chris@271 117 } else {
Chris@271 118 if (dr < fuzz) {
Chris@271 119 frame = right;
Chris@271 120 }
Chris@271 121 }
Chris@271 122 } else if (dl >= 0) {
Chris@271 123 if (dl <= fuzz) {
Chris@271 124 frame = left;
Chris@271 125 }
Chris@271 126 } else if (dr >= 0) {
Chris@271 127 if (dr <= fuzz) {
Chris@271 128 frame = right;
Chris@271 129 }
Chris@271 130 }
Chris@271 131 }
Chris@271 132 }
Chris@271 133
Chris@272 134 // std::cerr << " -> " << frame << " (resolution = " << resolution << ")" << std::endl;
Chris@272 135
Chris@271 136 return true;
Chris@271 137 }
Chris@271 138
Chris@271 139 int
Chris@271 140 TimeRulerLayer::getMajorTickSpacing(View *v, bool &quarterTicks) const
Chris@271 141 {
Chris@271 142 // return value is in milliseconds
Chris@271 143
Chris@271 144 if (!m_model || !v) return 1000;
Chris@271 145
Chris@271 146 int sampleRate = m_model->getSampleRate();
Chris@271 147 if (!sampleRate) return 1000;
Chris@271 148
Chris@271 149 long startFrame = v->getStartFrame();
Chris@271 150 long endFrame = v->getEndFrame();
Chris@271 151
Chris@271 152 int minPixelSpacing = 50;
Chris@271 153
Chris@271 154 RealTime rtStart = RealTime::frame2RealTime(startFrame, sampleRate);
Chris@271 155 RealTime rtEnd = RealTime::frame2RealTime(endFrame, sampleRate);
Chris@271 156
Chris@271 157 int count = v->width() / minPixelSpacing;
Chris@271 158 if (count < 1) count = 1;
Chris@271 159 RealTime rtGap = (rtEnd - rtStart) / count;
Chris@271 160
Chris@271 161 int incms;
Chris@271 162 quarterTicks = false;
Chris@271 163
Chris@271 164 if (rtGap.sec > 0) {
Chris@271 165 incms = 1000;
Chris@271 166 int s = rtGap.sec;
Chris@271 167 if (s > 0) { incms *= 5; s /= 5; }
Chris@271 168 if (s > 0) { incms *= 2; s /= 2; }
Chris@271 169 if (s > 0) { incms *= 6; s /= 6; quarterTicks = true; }
Chris@271 170 if (s > 0) { incms *= 5; s /= 5; quarterTicks = false; }
Chris@271 171 if (s > 0) { incms *= 2; s /= 2; }
Chris@271 172 if (s > 0) { incms *= 6; s /= 6; quarterTicks = true; }
Chris@271 173 while (s > 0) {
Chris@271 174 incms *= 10;
Chris@271 175 s /= 10;
Chris@271 176 quarterTicks = false;
Chris@271 177 }
Chris@271 178 } else {
Chris@271 179 incms = 1;
Chris@271 180 int ms = rtGap.msec();
Chris@271 181 if (ms > 0) { incms *= 10; ms /= 10; }
Chris@271 182 if (ms > 0) { incms *= 10; ms /= 10; }
Chris@271 183 if (ms > 0) { incms *= 5; ms /= 5; }
Chris@271 184 if (ms > 0) { incms *= 2; ms /= 2; }
Chris@271 185 }
Chris@271 186
Chris@271 187 return incms;
Chris@271 188 }
Chris@271 189
Chris@0 190 void
Chris@44 191 TimeRulerLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@0 192 {
Chris@0 193 // std::cerr << "TimeRulerLayer::paint (" << rect.x() << "," << rect.y()
Chris@0 194 // << ") [" << rect.width() << "x" << rect.height() << "]" << std::endl;
Chris@0 195
Chris@0 196 if (!m_model || !m_model->isOK()) return;
Chris@0 197
Chris@0 198 int sampleRate = m_model->getSampleRate();
Chris@0 199 if (!sampleRate) return;
Chris@0 200
Chris@44 201 long startFrame = v->getStartFrame();
Chris@44 202 long endFrame = v->getEndFrame();
Chris@0 203
Chris@44 204 int zoomLevel = v->getZoomLevel();
Chris@0 205
Chris@0 206 long rectStart = startFrame + (rect.x() - 100) * zoomLevel;
Chris@0 207 long rectEnd = startFrame + (rect.x() + rect.width() + 100) * zoomLevel;
Chris@0 208
Chris@0 209 // std::cerr << "TimeRulerLayer::paint: calling paint.save()" << std::endl;
Chris@0 210 paint.save();
Chris@44 211 //!!! paint.setClipRect(v->rect());
Chris@0 212
Chris@0 213 int minPixelSpacing = 50;
Chris@0 214
Chris@0 215 bool quarter = false;
Chris@271 216 int incms = getMajorTickSpacing(v, quarter);
Chris@0 217
Chris@0 218 RealTime rt = RealTime::frame2RealTime(rectStart, sampleRate);
Chris@0 219 long ms = rt.sec * 1000 + rt.msec();
Chris@0 220 ms = (ms / incms) * incms - incms;
Chris@0 221
Chris@30 222 RealTime incRt = RealTime::fromMilliseconds(incms);
Chris@0 223 long incFrame = RealTime::realTime2Frame(incRt, sampleRate);
Chris@0 224 int incX = incFrame / zoomLevel;
Chris@0 225 int ticks = 10;
Chris@0 226 if (incX < minPixelSpacing * 2) {
Chris@0 227 ticks = quarter ? 4 : 5;
Chris@0 228 }
Chris@0 229
Chris@0 230 QRect oldClipRect = rect;
Chris@0 231 QRect newClipRect(oldClipRect.x() - 25, oldClipRect.y(),
Chris@0 232 oldClipRect.width() + 50, oldClipRect.height());
Chris@0 233 paint.setClipRect(newClipRect);
Chris@55 234 paint.setClipRect(rect);
Chris@0 235
Chris@287 236 QColor greyColour = getPartialShades(v)[1];
Chris@0 237
Chris@0 238 while (1) {
Chris@0 239
Chris@30 240 rt = RealTime::fromMilliseconds(ms);
Chris@0 241 ms += incms;
Chris@0 242
Chris@0 243 long frame = RealTime::realTime2Frame(rt, sampleRate);
Chris@0 244 if (frame >= rectEnd) break;
Chris@0 245
Chris@0 246 int x = (frame - startFrame) / zoomLevel;
Chris@0 247 if (x < rect.x() || x >= rect.x() + rect.width()) continue;
Chris@0 248
Chris@0 249 paint.setPen(greyColour);
Chris@44 250 paint.drawLine(x, 0, x, v->height());
Chris@0 251
Chris@287 252 paint.setPen(getBaseQColor());
Chris@0 253 paint.drawLine(x, 0, x, 5);
Chris@44 254 paint.drawLine(x, v->height() - 6, x, v->height() - 1);
Chris@0 255
Chris@0 256 QString text(QString::fromStdString(rt.toText()));
Chris@0 257
Chris@0 258 int y;
Chris@0 259 QFontMetrics metrics = paint.fontMetrics();
Chris@0 260 switch (m_labelHeight) {
Chris@0 261 default:
Chris@0 262 case LabelTop:
Chris@0 263 y = 6 + metrics.ascent();
Chris@0 264 break;
Chris@0 265 case LabelMiddle:
Chris@44 266 y = v->height() / 2 - metrics.height() / 2 + metrics.ascent();
Chris@0 267 break;
Chris@0 268 case LabelBottom:
Chris@44 269 y = v->height() - metrics.height() + metrics.ascent() - 6;
Chris@0 270 }
Chris@0 271
Chris@0 272 int tw = metrics.width(text);
Chris@0 273
Chris@70 274 if (v->getViewManager() && v->getViewManager()->getOverlayMode() !=
Chris@70 275 ViewManager::NoOverlays) {
Chris@70 276
Chris@70 277 if (v->getLayer(0) == this) {
Chris@70 278 // backmost layer, don't worry about outlining the text
Chris@70 279 paint.drawText(x+2 - tw/2, y, text);
Chris@70 280 } else {
Chris@70 281 v->drawVisibleText(paint, x+2 - tw/2, y, text, View::OutlinedText);
Chris@70 282 }
Chris@70 283 }
Chris@0 284
Chris@0 285 paint.setPen(greyColour);
Chris@0 286
Chris@0 287 for (int i = 1; i < ticks; ++i) {
Chris@0 288 rt = rt + (incRt / ticks);
Chris@0 289 frame = RealTime::realTime2Frame(rt, sampleRate);
Chris@0 290 x = (frame - startFrame) / zoomLevel;
Chris@0 291 int sz = 5;
Chris@0 292 if (ticks == 10) {
Chris@0 293 if ((i % 2) == 1) {
Chris@0 294 if (i == 5) {
Chris@44 295 paint.drawLine(x, 0, x, v->height());
Chris@0 296 } else sz = 3;
Chris@0 297 } else {
Chris@0 298 sz = 7;
Chris@0 299 }
Chris@0 300 }
Chris@0 301 paint.drawLine(x, 0, x, sz);
Chris@44 302 paint.drawLine(x, v->height() - sz - 1, x, v->height() - 1);
Chris@0 303 }
Chris@0 304 }
Chris@0 305
Chris@0 306 paint.restore();
Chris@0 307 }
Chris@287 308
Chris@287 309 int
Chris@287 310 TimeRulerLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@287 311 {
Chris@287 312 impose = true;
Chris@287 313 return ColourDatabase::getInstance()->getColourIndex
Chris@287 314 (QString(darkbg ? "White" : "Black"));
Chris@287 315 }
Chris@287 316
Chris@6 317 QString
Chris@6 318 TimeRulerLayer::toXmlString(QString indent, QString extraAttributes) const
Chris@6 319 {
Chris@287 320 return SingleColourLayer::toXmlString(indent, extraAttributes);
Chris@6 321 }
Chris@6 322
Chris@11 323 void
Chris@11 324 TimeRulerLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 325 {
Chris@287 326 SingleColourLayer::setProperties(attributes);
Chris@11 327 }
Chris@11 328