annotate layer/TimeRulerLayer.cpp @ 77:fd348f36c0d3

* Implement harmonic cursor in spectrogram * Implement layer export. This doesn't quite do the right thing for the SV XML layer export yet -- it doesn't include layer display information, so when imported, it only creates an invisible model. Could also do with fixing CSV file import so as to work correctly for note and text layers.
author Chris Cannam
date Mon, 10 Apr 2006 17:22:59 +0000
parents bf306158803d
children 4b98bda7e94d
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@0 18 #include "base/Model.h"
Chris@0 19 #include "base/RealTime.h"
Chris@0 20 #include "base/View.h"
Chris@0 21
Chris@0 22 #include <QPainter>
Chris@0 23
Chris@0 24 #include <iostream>
Chris@0 25
Chris@0 26 using std::cerr;
Chris@0 27 using std::endl;
Chris@0 28
Chris@44 29 TimeRulerLayer::TimeRulerLayer() :
Chris@44 30 Layer(),
Chris@0 31 m_model(0),
Chris@0 32 m_colour(Qt::black),
Chris@0 33 m_labelHeight(LabelTop)
Chris@0 34 {
Chris@44 35
Chris@0 36 }
Chris@0 37
Chris@0 38 void
Chris@0 39 TimeRulerLayer::setModel(Model *model)
Chris@0 40 {
Chris@0 41 if (m_model == model) return;
Chris@0 42 m_model = model;
Chris@0 43 emit modelReplaced();
Chris@0 44 }
Chris@0 45
Chris@0 46 void
Chris@0 47 TimeRulerLayer::setBaseColour(QColor colour)
Chris@0 48 {
Chris@0 49 if (m_colour == colour) return;
Chris@0 50 m_colour = colour;
Chris@0 51 emit layerParametersChanged();
Chris@0 52 }
Chris@0 53
Chris@0 54 Layer::PropertyList
Chris@0 55 TimeRulerLayer::getProperties() const
Chris@0 56 {
Chris@0 57 PropertyList list;
Chris@0 58 list.push_back(tr("Colour"));
Chris@0 59 return list;
Chris@0 60 }
Chris@0 61
Chris@0 62 Layer::PropertyType
Chris@0 63 TimeRulerLayer::getPropertyType(const PropertyName &name) const
Chris@0 64 {
Chris@0 65 return ValueProperty;
Chris@0 66 }
Chris@0 67
Chris@0 68 int
Chris@0 69 TimeRulerLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@0 70 int *min, int *max) const
Chris@0 71 {
Chris@0 72 int deft = 0;
Chris@0 73
Chris@0 74 if (name == tr("Colour")) {
Chris@0 75
Chris@10 76 if (min) *min = 0;
Chris@10 77 if (max) *max = 5;
Chris@0 78
Chris@0 79 if (m_colour == Qt::black) deft = 0;
Chris@0 80 else if (m_colour == Qt::darkRed) deft = 1;
Chris@0 81 else if (m_colour == Qt::darkBlue) deft = 2;
Chris@0 82 else if (m_colour == Qt::darkGreen) deft = 3;
Chris@0 83 else if (m_colour == QColor(200, 50, 255)) deft = 4;
Chris@0 84 else if (m_colour == QColor(255, 150, 50)) deft = 5;
Chris@0 85
Chris@0 86 } else {
Chris@0 87
Chris@0 88 deft = Layer::getPropertyRangeAndValue(name, min, max);
Chris@0 89 }
Chris@0 90
Chris@0 91 return deft;
Chris@0 92 }
Chris@0 93
Chris@0 94 QString
Chris@0 95 TimeRulerLayer::getPropertyValueLabel(const PropertyName &name,
Chris@0 96 int value) const
Chris@0 97 {
Chris@0 98 if (name == tr("Colour")) {
Chris@0 99 switch (value) {
Chris@0 100 default:
Chris@0 101 case 0: return tr("Black");
Chris@0 102 case 1: return tr("Red");
Chris@0 103 case 2: return tr("Blue");
Chris@0 104 case 3: return tr("Green");
Chris@0 105 case 4: return tr("Purple");
Chris@0 106 case 5: return tr("Orange");
Chris@0 107 }
Chris@0 108 }
Chris@0 109 return tr("<unknown>");
Chris@0 110 }
Chris@0 111
Chris@0 112 void
Chris@0 113 TimeRulerLayer::setProperty(const PropertyName &name, int value)
Chris@0 114 {
Chris@0 115 if (name == tr("Colour")) {
Chris@0 116 switch (value) {
Chris@0 117 default:
Chris@0 118 case 0: setBaseColour(Qt::black); break;
Chris@0 119 case 1: setBaseColour(Qt::darkRed); break;
Chris@0 120 case 2: setBaseColour(Qt::darkBlue); break;
Chris@0 121 case 3: setBaseColour(Qt::darkGreen); break;
Chris@0 122 case 4: setBaseColour(QColor(200, 50, 255)); break;
Chris@0 123 case 5: setBaseColour(QColor(255, 150, 50)); break;
Chris@0 124 }
Chris@0 125 }
Chris@0 126 }
Chris@0 127
Chris@0 128 void
Chris@44 129 TimeRulerLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@0 130 {
Chris@0 131 // std::cerr << "TimeRulerLayer::paint (" << rect.x() << "," << rect.y()
Chris@0 132 // << ") [" << rect.width() << "x" << rect.height() << "]" << std::endl;
Chris@0 133
Chris@0 134 if (!m_model || !m_model->isOK()) return;
Chris@0 135
Chris@0 136 int sampleRate = m_model->getSampleRate();
Chris@0 137 if (!sampleRate) return;
Chris@0 138
Chris@44 139 long startFrame = v->getStartFrame();
Chris@44 140 long endFrame = v->getEndFrame();
Chris@0 141
Chris@44 142 int zoomLevel = v->getZoomLevel();
Chris@0 143
Chris@0 144 long rectStart = startFrame + (rect.x() - 100) * zoomLevel;
Chris@0 145 long rectEnd = startFrame + (rect.x() + rect.width() + 100) * zoomLevel;
Chris@55 146 // if (rectStart < startFrame) rectStart = startFrame;
Chris@55 147 // if (rectEnd > endFrame) rectEnd = endFrame;
Chris@0 148
Chris@0 149 // std::cerr << "TimeRulerLayer::paint: calling paint.save()" << std::endl;
Chris@0 150 paint.save();
Chris@44 151 //!!! paint.setClipRect(v->rect());
Chris@0 152
Chris@0 153 int minPixelSpacing = 50;
Chris@0 154
Chris@0 155 RealTime rtStart = RealTime::frame2RealTime(startFrame, sampleRate);
Chris@0 156 RealTime rtEnd = RealTime::frame2RealTime(endFrame, sampleRate);
Chris@44 157 // cerr << "startFrame " << startFrame << ", endFrame " << v->getEndFrame() << ", rtStart " << rtStart << ", rtEnd " << rtEnd << endl;
Chris@44 158 int count = v->width() / minPixelSpacing;
Chris@0 159 if (count < 1) count = 1;
Chris@0 160 RealTime rtGap = (rtEnd - rtStart) / count;
Chris@0 161 // cerr << "rtGap is " << rtGap << endl;
Chris@0 162
Chris@0 163 int incms;
Chris@0 164 bool quarter = false;
Chris@0 165
Chris@0 166 if (rtGap.sec > 0) {
Chris@0 167 incms = 1000;
Chris@0 168 int s = rtGap.sec;
Chris@0 169 if (s > 0) { incms *= 5; s /= 5; }
Chris@0 170 if (s > 0) { incms *= 2; s /= 2; }
Chris@0 171 if (s > 0) { incms *= 6; s /= 6; quarter = true; }
Chris@0 172 if (s > 0) { incms *= 5; s /= 5; quarter = false; }
Chris@0 173 if (s > 0) { incms *= 2; s /= 2; }
Chris@0 174 if (s > 0) { incms *= 6; s /= 6; quarter = true; }
Chris@0 175 while (s > 0) {
Chris@0 176 incms *= 10;
Chris@0 177 s /= 10;
Chris@0 178 quarter = false;
Chris@0 179 }
Chris@0 180 } else {
Chris@0 181 incms = 1;
Chris@0 182 int ms = rtGap.msec();
Chris@0 183 if (ms > 0) { incms *= 10; ms /= 10; }
Chris@0 184 if (ms > 0) { incms *= 10; ms /= 10; }
Chris@0 185 if (ms > 0) { incms *= 5; ms /= 5; }
Chris@0 186 if (ms > 0) { incms *= 2; ms /= 2; }
Chris@0 187 }
Chris@0 188 // cerr << "incms is " << incms << endl;
Chris@0 189
Chris@0 190 RealTime rt = RealTime::frame2RealTime(rectStart, sampleRate);
Chris@0 191 long ms = rt.sec * 1000 + rt.msec();
Chris@0 192 ms = (ms / incms) * incms - incms;
Chris@0 193
Chris@30 194 RealTime incRt = RealTime::fromMilliseconds(incms);
Chris@0 195 long incFrame = RealTime::realTime2Frame(incRt, sampleRate);
Chris@0 196 int incX = incFrame / zoomLevel;
Chris@0 197 int ticks = 10;
Chris@0 198 if (incX < minPixelSpacing * 2) {
Chris@0 199 ticks = quarter ? 4 : 5;
Chris@0 200 }
Chris@0 201
Chris@0 202 QRect oldClipRect = rect;
Chris@0 203 QRect newClipRect(oldClipRect.x() - 25, oldClipRect.y(),
Chris@0 204 oldClipRect.width() + 50, oldClipRect.height());
Chris@0 205 paint.setClipRect(newClipRect);
Chris@55 206 paint.setClipRect(rect);
Chris@0 207
Chris@0 208 QColor greyColour(m_colour);
Chris@0 209 if (m_colour == Qt::black) {
Chris@0 210 greyColour = QColor(200,200,200);
Chris@0 211 } else {
Chris@0 212 greyColour = m_colour.light(150);
Chris@0 213 }
Chris@0 214
Chris@0 215 while (1) {
Chris@0 216
Chris@30 217 rt = RealTime::fromMilliseconds(ms);
Chris@0 218 ms += incms;
Chris@0 219
Chris@0 220 long frame = RealTime::realTime2Frame(rt, sampleRate);
Chris@0 221 if (frame >= rectEnd) break;
Chris@0 222
Chris@0 223 int x = (frame - startFrame) / zoomLevel;
Chris@0 224 if (x < rect.x() || x >= rect.x() + rect.width()) continue;
Chris@0 225
Chris@0 226 paint.setPen(greyColour);
Chris@44 227 paint.drawLine(x, 0, x, v->height());
Chris@0 228
Chris@0 229 paint.setPen(m_colour);
Chris@0 230 paint.drawLine(x, 0, x, 5);
Chris@44 231 paint.drawLine(x, v->height() - 6, x, v->height() - 1);
Chris@0 232
Chris@0 233 QString text(QString::fromStdString(rt.toText()));
Chris@0 234
Chris@0 235 int y;
Chris@0 236 QFontMetrics metrics = paint.fontMetrics();
Chris@0 237 switch (m_labelHeight) {
Chris@0 238 default:
Chris@0 239 case LabelTop:
Chris@0 240 y = 6 + metrics.ascent();
Chris@0 241 break;
Chris@0 242 case LabelMiddle:
Chris@44 243 y = v->height() / 2 - metrics.height() / 2 + metrics.ascent();
Chris@0 244 break;
Chris@0 245 case LabelBottom:
Chris@44 246 y = v->height() - metrics.height() + metrics.ascent() - 6;
Chris@0 247 }
Chris@0 248
Chris@0 249 int tw = metrics.width(text);
Chris@0 250
Chris@70 251 if (v->getViewManager() && v->getViewManager()->getOverlayMode() !=
Chris@70 252 ViewManager::NoOverlays) {
Chris@70 253
Chris@70 254 if (v->getLayer(0) == this) {
Chris@70 255 // backmost layer, don't worry about outlining the text
Chris@70 256 paint.drawText(x+2 - tw/2, y, text);
Chris@70 257 } else {
Chris@70 258 v->drawVisibleText(paint, x+2 - tw/2, y, text, View::OutlinedText);
Chris@70 259 }
Chris@70 260 }
Chris@0 261
Chris@0 262 paint.setPen(greyColour);
Chris@0 263
Chris@0 264 for (int i = 1; i < ticks; ++i) {
Chris@0 265 rt = rt + (incRt / ticks);
Chris@0 266 frame = RealTime::realTime2Frame(rt, sampleRate);
Chris@0 267 x = (frame - startFrame) / zoomLevel;
Chris@0 268 int sz = 5;
Chris@0 269 if (ticks == 10) {
Chris@0 270 if ((i % 2) == 1) {
Chris@0 271 if (i == 5) {
Chris@44 272 paint.drawLine(x, 0, x, v->height());
Chris@0 273 } else sz = 3;
Chris@0 274 } else {
Chris@0 275 sz = 7;
Chris@0 276 }
Chris@0 277 }
Chris@0 278 paint.drawLine(x, 0, x, sz);
Chris@44 279 paint.drawLine(x, v->height() - sz - 1, x, v->height() - 1);
Chris@0 280 }
Chris@0 281 }
Chris@0 282
Chris@0 283 paint.restore();
Chris@0 284 }
Chris@0 285
Chris@6 286 QString
Chris@6 287 TimeRulerLayer::toXmlString(QString indent, QString extraAttributes) const
Chris@6 288 {
Chris@6 289 return Layer::toXmlString(indent, extraAttributes +
Chris@6 290 QString(" colour=\"%1\"").arg(encodeColour(m_colour)));
Chris@6 291 }
Chris@6 292
Chris@11 293 void
Chris@11 294 TimeRulerLayer::setProperties(const QXmlAttributes &attributes)
Chris@11 295 {
Chris@11 296 QString colourSpec = attributes.value("colour");
Chris@11 297 if (colourSpec != "") {
Chris@11 298 QColor colour(colourSpec);
Chris@11 299 if (colour.isValid()) {
Chris@11 300 setBaseColour(QColor(colourSpec));
Chris@11 301 }
Chris@11 302 }
Chris@11 303 }
Chris@11 304
Chris@0 305
Chris@0 306 #ifdef INCLUDE_MOCFILES
Chris@0 307 #include "TimeRulerLayer.moc.cpp"
Chris@0 308 #endif
Chris@0 309