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