svgui
1.9
|
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ 00002 00003 /* 00004 Sonic Visualiser 00005 An audio file viewer and annotation editor. 00006 Centre for Digital Music, Queen Mary, University of London. 00007 This file copyright 2006 Chris Cannam and QMUL. 00008 00009 This program is free software; you can redistribute it and/or 00010 modify it under the terms of the GNU General Public License as 00011 published by the Free Software Foundation; either version 2 of the 00012 License, or (at your option) any later version. See the file 00013 COPYING included with this distribution for more information. 00014 */ 00015 00016 #include "Layer.h" 00017 #include "view/View.h" 00018 #include "data/model/Model.h" 00019 #include "widgets/CommandHistory.h" 00020 00021 #include <iostream> 00022 00023 #include <QMutexLocker> 00024 #include <QMouseEvent> 00025 #include <QTextStream> 00026 00027 #include <QDomDocument> 00028 #include <QDomElement> 00029 #include <QDomNamedNodeMap> 00030 #include <QDomAttr> 00031 00032 #include "LayerFactory.h" 00033 #include "base/PlayParameterRepository.h" 00034 00035 #include <cmath> 00036 00037 Layer::Layer() : 00038 m_haveDraggingRect(false), 00039 m_haveCurrentMeasureRect(false) 00040 { 00041 } 00042 00043 Layer::~Layer() 00044 { 00045 // SVDEBUG << "Layer::~Layer(" << this << ")" << endl; 00046 } 00047 00048 void 00049 Layer::connectSignals(const Model *model) 00050 { 00051 connect(model, SIGNAL(modelChanged()), 00052 this, SIGNAL(modelChanged())); 00053 00054 connect(model, SIGNAL(modelChangedWithin(int, int)), 00055 this, SIGNAL(modelChangedWithin(int, int))); 00056 00057 connect(model, SIGNAL(completionChanged()), 00058 this, SIGNAL(modelCompletionChanged())); 00059 00060 connect(model, SIGNAL(alignmentCompletionChanged()), 00061 this, SIGNAL(modelAlignmentCompletionChanged())); 00062 } 00063 00064 QString 00065 Layer::getPropertyContainerIconName() const 00066 { 00067 return LayerFactory::getInstance()->getLayerIconName 00068 (LayerFactory::getInstance()->getLayerType(this)); 00069 } 00070 00071 void 00072 Layer::setPresentationName(QString name) 00073 { 00074 m_presentationName = name; 00075 } 00076 00077 QString 00078 Layer::getLayerPresentationName() const 00079 { 00080 if (m_presentationName != "") return m_presentationName; 00081 00082 LayerFactory *factory = LayerFactory::getInstance(); 00083 QString layerName = factory->getLayerPresentationName 00084 (factory->getLayerType(this)); 00085 00086 QString modelName; 00087 if (getModel()) modelName = getModel()->objectName(); 00088 00089 QString text; 00090 if (modelName != "") { 00091 text = QString("%1: %2").arg(modelName).arg(layerName); 00092 } else { 00093 text = layerName; 00094 } 00095 00096 return text; 00097 } 00098 00099 void 00100 Layer::setObjectName(const QString &name) 00101 { 00102 QObject::setObjectName(name); 00103 emit layerNameChanged(); 00104 } 00105 00106 PlayParameters * 00107 Layer::getPlayParameters() 00108 { 00109 // cerr << "Layer (" << this << ", " << objectName() << ")::getPlayParameters: model is "<< getModel() << endl; 00110 const Model *model = getModel(); 00111 if (model) { 00112 return PlayParameterRepository::getInstance()->getPlayParameters(model); 00113 } 00114 return 0; 00115 } 00116 00117 void 00118 Layer::setLayerDormant(const View *v, bool dormant) 00119 { 00120 const void *vv = (const void *)v; 00121 QMutexLocker locker(&m_dormancyMutex); 00122 m_dormancy[vv] = dormant; 00123 } 00124 00125 bool 00126 Layer::isLayerDormant(const View *v) const 00127 { 00128 const void *vv = (const void *)v; 00129 QMutexLocker locker(&m_dormancyMutex); 00130 if (m_dormancy.find(vv) == m_dormancy.end()) return false; 00131 return m_dormancy.find(vv)->second; 00132 } 00133 00134 void 00135 Layer::showLayer(View *view, bool show) 00136 { 00137 setLayerDormant(view, !show); 00138 emit layerParametersChanged(); 00139 } 00140 00141 bool 00142 Layer::getXScaleValue(const View *v, int x, float &value, QString &unit) const 00143 { 00144 if (!hasTimeXAxis()) return false; 00145 00146 const Model *m = getModel(); 00147 if (!m) return false; 00148 00149 value = float(v->getFrameForX(x)) / m->getSampleRate(); 00150 unit = "s"; 00151 return true; 00152 } 00153 00154 bool 00155 Layer::getYScaleDifference(const View *v, int y0, int y1, 00156 float &diff, QString &unit) const 00157 { 00158 float v0, v1; 00159 if (!getYScaleValue(v, y0, v0, unit) || 00160 !getYScaleValue(v, y1, v1, unit)) { 00161 diff = 0.f; 00162 return false; 00163 } 00164 diff = fabsf(v1 - v0); 00165 return true; 00166 } 00167 00168 int 00169 Layer::alignToReference(View *v, int frame) const 00170 { 00171 const Model *m = getModel(); 00172 SVDEBUG << "Layer::alignToReference(" << frame << "): model = " << m << ", alignment reference = " << (m ? m->getAlignmentReference() : 0) << endl; 00173 if (m && m->getAlignmentReference()) { 00174 return m->alignToReference(frame); 00175 } else { 00176 return v->alignToReference(frame); 00177 } 00178 } 00179 00180 int 00181 Layer::alignFromReference(View *v, int frame) const 00182 { 00183 const Model *m = getModel(); 00184 SVDEBUG << "Layer::alignFromReference(" << frame << "): model = " << m << ", alignment reference = " << (m ? m->getAlignmentReference() : 0) << endl; 00185 if (m && m->getAlignmentReference()) { 00186 return m->alignFromReference(frame); 00187 } else { 00188 return v->alignFromReference(frame); 00189 } 00190 } 00191 00192 bool 00193 Layer::clipboardHasDifferentAlignment(View *v, const Clipboard &clip) const 00194 { 00195 // Notes on pasting to an aligned layer: 00196 // 00197 // Each point may have a reference frame that may differ from the 00198 // point's given frame (in its source model). If it has no 00199 // reference frame, we have to assume the source model was not 00200 // aligned or was the reference model: when cutting or copying 00201 // points from a layer, we must always set their reference frame 00202 // correctly if we are aligned. 00203 // 00204 // When pasting: 00205 // - if point's reference and aligned frames differ: 00206 // - if this layer is aligned: 00207 // - if point's aligned frame matches this layer's aligned version 00208 // of point's reference frame: 00209 // - we can paste at reference frame or our frame 00210 // - else 00211 // - we can paste at reference frame, result of aligning reference 00212 // frame in our model, or literal source frame 00213 // - else 00214 // - we can paste at reference (our) frame, or literal source frame 00215 // - else 00216 // - if this layer is aligned: 00217 // - we can paste at reference (point's only available) frame, 00218 // or result of aligning reference frame in our model 00219 // - else 00220 // - we can only paste at reference frame 00221 // 00222 // Which of these alternatives are useful? 00223 // 00224 // Example: we paste between two tracks that are aligned to the 00225 // same reference, and the points are at 10s and 20s in the source 00226 // track, corresponding to 5s and 10s in the reference but 20s and 00227 // 30s in the target track. 00228 // 00229 // The obvious default is to paste at 20s and 30s; if we aren't 00230 // doing that, would it be better to paste at 5s and 10s or at 10s 00231 // and 20s? We probably don't ever want to do the former, do we? 00232 // We either want to be literal all the way through, or aligned 00233 // all the way through. 00234 00235 for (Clipboard::PointList::const_iterator i = clip.getPoints().begin(); 00236 i != clip.getPoints().end(); ++i) { 00237 00238 // In principle, we want to know whether the aligned version 00239 // of the reference frame in our layer is the same as the 00240 // source frame contained in the clipboard point. However, 00241 // because of rounding during alignment, that won't 00242 // necessarily be the case even if the clipboard point came 00243 // from our layer! What we need to check is whether, if we 00244 // aligned the clipboard point's frame back to the reference 00245 // using this layer's alignment, we would obtain the same 00246 // reference frame as that for the clipboard point. 00247 00248 // What if the clipboard point has no reference frame? Then 00249 // we have to treat it as having its own frame as the 00250 // reference (i.e. having been copied from the reference 00251 // model). 00252 00253 int sourceFrame = i->getFrame(); 00254 int referenceFrame = sourceFrame; 00255 if (i->haveReferenceFrame()) { 00256 referenceFrame = i->getReferenceFrame(); 00257 } 00258 int myMappedFrame = alignToReference(v, sourceFrame); 00259 00260 // cerr << "sourceFrame = " << sourceFrame << ", referenceFrame = " << referenceFrame << " (have = " << i->haveReferenceFrame() << "), myMappedFrame = " << myMappedFrame << endl; 00261 00262 if (myMappedFrame != referenceFrame) return true; 00263 } 00264 00265 return false; 00266 } 00267 00268 bool 00269 Layer::MeasureRect::operator<(const MeasureRect &mr) const 00270 { 00271 if (haveFrames) { 00272 if (startFrame == mr.startFrame) { 00273 if (endFrame != mr.endFrame) { 00274 return endFrame < mr.endFrame; 00275 } 00276 } else { 00277 return startFrame < mr.startFrame; 00278 } 00279 } else { 00280 if (pixrect.x() == mr.pixrect.x()) { 00281 if (pixrect.width() != mr.pixrect.width()) { 00282 return pixrect.width() < mr.pixrect.width(); 00283 } 00284 } else { 00285 return pixrect.x() < mr.pixrect.x(); 00286 } 00287 } 00288 00289 // the two rects are equal in x and width 00290 00291 if (pixrect.y() == mr.pixrect.y()) { 00292 return pixrect.height() < mr.pixrect.height(); 00293 } else { 00294 return pixrect.y() < mr.pixrect.y(); 00295 } 00296 } 00297 00298 void 00299 Layer::MeasureRect::toXml(QTextStream &stream, QString indent) const 00300 { 00301 stream << indent; 00302 stream << QString("<measurement "); 00303 00304 if (haveFrames) { 00305 stream << QString("startFrame=\"%1\" endFrame=\"%2\" ") 00306 .arg(startFrame).arg(endFrame); 00307 } else { 00308 stream << QString("startX=\"%1\" endX=\"%2\" ") 00309 .arg(pixrect.x()).arg(pixrect.x() << pixrect.width()); 00310 } 00311 00312 stream << QString("startY=\"%1\" endY=\"%2\"/>\n") 00313 .arg(startY).arg(endY); 00314 } 00315 00316 void 00317 Layer::addMeasurementRect(const QXmlAttributes &attributes) 00318 { 00319 MeasureRect rect; 00320 QString fs = attributes.value("startFrame"); 00321 int x0 = 0, x1 = 0; 00322 if (fs != "") { 00323 rect.startFrame = fs.toInt(); 00324 rect.endFrame = attributes.value("endFrame").toInt(); 00325 rect.haveFrames = true; 00326 } else { 00327 x0 = attributes.value("startX").toInt(); 00328 x1 = attributes.value("endX").toInt(); 00329 rect.haveFrames = false; 00330 } 00331 rect.startY = attributes.value("startY").toDouble(); 00332 rect.endY = attributes.value("endY").toDouble(); 00333 rect.pixrect = QRect(x0, 0, x1 - x0, 0); 00334 addMeasureRectToSet(rect); 00335 } 00336 00337 QString 00338 Layer::AddMeasurementRectCommand::getName() const 00339 { 00340 return tr("Make Measurement"); 00341 } 00342 00343 void 00344 Layer::AddMeasurementRectCommand::execute() 00345 { 00346 m_layer->addMeasureRectToSet(m_rect); 00347 } 00348 00349 void 00350 Layer::AddMeasurementRectCommand::unexecute() 00351 { 00352 m_layer->deleteMeasureRectFromSet(m_rect); 00353 } 00354 00355 QString 00356 Layer::DeleteMeasurementRectCommand::getName() const 00357 { 00358 return tr("Delete Measurement"); 00359 } 00360 00361 void 00362 Layer::DeleteMeasurementRectCommand::execute() 00363 { 00364 m_layer->deleteMeasureRectFromSet(m_rect); 00365 } 00366 00367 void 00368 Layer::DeleteMeasurementRectCommand::unexecute() 00369 { 00370 m_layer->addMeasureRectToSet(m_rect); 00371 } 00372 00373 void 00374 Layer::measureStart(View *v, QMouseEvent *e) 00375 { 00376 setMeasureRectFromPixrect(v, m_draggingRect, 00377 QRect(e->x(), e->y(), 0, 0)); 00378 m_haveDraggingRect = true; 00379 } 00380 00381 void 00382 Layer::measureDrag(View *v, QMouseEvent *e) 00383 { 00384 if (!m_haveDraggingRect) return; 00385 00386 setMeasureRectFromPixrect(v, m_draggingRect, 00387 QRect(m_draggingRect.pixrect.x(), 00388 m_draggingRect.pixrect.y(), 00389 e->x() - m_draggingRect.pixrect.x(), 00390 e->y() - m_draggingRect.pixrect.y())); 00391 } 00392 00393 void 00394 Layer::measureEnd(View *v, QMouseEvent *e) 00395 { 00396 if (!m_haveDraggingRect) return; 00397 measureDrag(v, e); 00398 00399 if (!m_draggingRect.pixrect.isNull()) { 00400 CommandHistory::getInstance()->addCommand 00401 (new AddMeasurementRectCommand(this, m_draggingRect)); 00402 } 00403 00404 m_haveDraggingRect = false; 00405 } 00406 00407 void 00408 Layer::measureDoubleClick(View *, QMouseEvent *) 00409 { 00410 // nothing, in the base class 00411 } 00412 00413 void 00414 Layer::deleteCurrentMeasureRect() 00415 { 00416 if (!m_haveCurrentMeasureRect) return; 00417 00418 MeasureRectSet::const_iterator focusRectItr = 00419 findFocusedMeasureRect(m_currentMeasureRectPoint); 00420 00421 if (focusRectItr == m_measureRects.end()) return; 00422 00423 CommandHistory::getInstance()->addCommand 00424 (new DeleteMeasurementRectCommand(this, *focusRectItr)); 00425 } 00426 00427 void 00428 Layer::paintMeasurementRects(View *v, QPainter &paint, 00429 bool showFocus, QPoint focusPoint) const 00430 { 00431 updateMeasurePixrects(v); 00432 00433 MeasureRectSet::const_iterator focusRectItr = m_measureRects.end(); 00434 00435 if (m_haveDraggingRect) { 00436 00437 paintMeasurementRect(v, paint, m_draggingRect, true); 00438 00439 } else if (showFocus) { 00440 00441 focusRectItr = findFocusedMeasureRect(focusPoint); 00442 } 00443 00444 m_haveCurrentMeasureRect = false; 00445 00446 for (MeasureRectSet::const_iterator i = m_measureRects.begin(); 00447 i != m_measureRects.end(); ++i) { 00448 00449 bool focused = (i == focusRectItr); 00450 paintMeasurementRect(v, paint, *i, focused); 00451 00452 if (focused) { 00453 m_haveCurrentMeasureRect = true; 00454 m_currentMeasureRectPoint = focusPoint; 00455 } 00456 } 00457 } 00458 00459 bool 00460 Layer::nearestMeasurementRectChanged(View *v, QPoint prev, QPoint now) const 00461 { 00462 updateMeasurePixrects(v); 00463 00464 MeasureRectSet::const_iterator i0 = findFocusedMeasureRect(prev); 00465 MeasureRectSet::const_iterator i1 = findFocusedMeasureRect(now); 00466 00467 return (i0 != i1); 00468 } 00469 00470 void 00471 Layer::updateMeasurePixrects(View *v) const 00472 { 00473 int sf = v->getStartFrame(); 00474 int ef = v->getEndFrame(); 00475 00476 for (MeasureRectSet::const_iterator i = m_measureRects.begin(); 00477 i != m_measureRects.end(); ++i) { 00478 00479 // This logic depends on the fact that if one measure rect in 00480 // a layer has frame values, they all will. That is in fact 00481 // the case, because haveFrames is based on whether the layer 00482 // hasTimeXAxis() or not. Measure rect ordering in the rect 00483 // set wouldn't work correctly either, if haveFrames could 00484 // vary. 00485 00486 if (i->haveFrames) { 00487 if (i->startFrame >= ef) break; 00488 if (i->endFrame <= sf) continue; 00489 } 00490 00491 int x0 = i->pixrect.x(); 00492 int x1 = x0 + i->pixrect.width(); 00493 00494 if (i->haveFrames) { 00495 if (i->startFrame >= v->getStartFrame()) { 00496 x0 = v->getXForFrame(i->startFrame); 00497 } 00498 if (i->endFrame <= int(v->getEndFrame())) { 00499 x1 = v->getXForFrame(i->endFrame); 00500 } 00501 } 00502 00503 i->pixrect = QRect(x0, i->pixrect.y(), x1 - x0, i->pixrect.height()); 00504 00505 updateMeasureRectYCoords(v, *i); 00506 } 00507 } 00508 00509 void 00510 Layer::updateMeasureRectYCoords(View *v, const MeasureRect &r) const 00511 { 00512 int y0 = lrint(r.startY * v->height()); 00513 int y1 = lrint(r.endY * v->height()); 00514 r.pixrect = QRect(r.pixrect.x(), y0, r.pixrect.width(), y1 - y0); 00515 } 00516 00517 void 00518 Layer::setMeasureRectYCoord(View *v, MeasureRect &r, bool start, int y) const 00519 { 00520 if (start) { 00521 r.startY = double(y) / double(v->height()); 00522 r.endY = r.startY; 00523 } else { 00524 r.endY = double(y) / double(v->height()); 00525 } 00526 } 00527 00528 void 00529 Layer::setMeasureRectFromPixrect(View *v, MeasureRect &r, QRect pixrect) const 00530 { 00531 r.pixrect = pixrect; 00532 r.haveFrames = hasTimeXAxis(); 00533 if (r.haveFrames) { 00534 r.startFrame = v->getFrameForX(pixrect.x()); 00535 r.endFrame = v->getFrameForX(pixrect.x() + pixrect.width()); 00536 } 00537 setMeasureRectYCoord(v, r, true, pixrect.y()); 00538 setMeasureRectYCoord(v, r, false, pixrect.y() + pixrect.height()); 00539 } 00540 00541 Layer::MeasureRectSet::const_iterator 00542 Layer::findFocusedMeasureRect(QPoint focusPoint) const 00543 { 00544 float frDist = 0; 00545 MeasureRectSet::const_iterator focusRectItr = m_measureRects.end(); 00546 00547 for (MeasureRectSet::const_iterator i = m_measureRects.begin(); 00548 i != m_measureRects.end(); ++i) { 00549 00550 if (!i->pixrect.adjusted(-2, -2, 2, 2).contains(focusPoint)) continue; 00551 00552 int cx = i->pixrect.x() + i->pixrect.width()/2; 00553 int cy = i->pixrect.y() + i->pixrect.height()/2; 00554 int xd = focusPoint.x() - cx; 00555 int yd = focusPoint.y() - cy; 00556 00557 float d = sqrt(float(xd * xd + yd * yd)); 00558 00559 if (focusRectItr == m_measureRects.end() || d < frDist) { 00560 focusRectItr = i; 00561 frDist = d; 00562 } 00563 } 00564 00565 return focusRectItr; 00566 } 00567 00568 void 00569 Layer::paintMeasurementRect(View *v, QPainter &paint, 00570 const MeasureRect &r, bool focus) const 00571 { 00572 if (r.haveFrames) { 00573 00574 int x0 = -1; 00575 int x1 = v->width() + 1; 00576 00577 if (r.startFrame >= v->getStartFrame()) { 00578 x0 = v->getXForFrame(r.startFrame); 00579 } 00580 if (r.endFrame <= v->getEndFrame()) { 00581 x1 = v->getXForFrame(r.endFrame); 00582 } 00583 00584 QRect pr = QRect(x0, r.pixrect.y(), x1 - x0, r.pixrect.height()); 00585 r.pixrect = pr; 00586 } 00587 00588 v->drawMeasurementRect(paint, this, r.pixrect.normalized(), focus); 00589 } 00590 00591 void 00592 Layer::toXml(QTextStream &stream, 00593 QString indent, QString extraAttributes) const 00594 { 00595 stream << indent; 00596 00597 if (m_presentationName != "") { 00598 extraAttributes = QString("%1 presentationName=\"%2\"") 00599 .arg(extraAttributes).arg(encodeEntities(m_presentationName)); 00600 } 00601 00602 stream << QString("<layer id=\"%2\" type=\"%1\" name=\"%3\" model=\"%4\" %5") 00603 .arg(encodeEntities(LayerFactory::getInstance()->getLayerTypeName 00604 (LayerFactory::getInstance()->getLayerType(this)))) 00605 .arg(getObjectExportId(this)) 00606 .arg(encodeEntities(objectName())) 00607 .arg(getObjectExportId(getModel())) 00608 .arg(extraAttributes); 00609 00610 if (m_measureRects.empty()) { 00611 stream << QString("/>\n"); 00612 return; 00613 } 00614 00615 stream << QString(">\n"); 00616 00617 for (MeasureRectSet::const_iterator i = m_measureRects.begin(); 00618 i != m_measureRects.end(); ++i) { 00619 i->toXml(stream, indent + " "); 00620 } 00621 00622 stream << QString("</layer>\n"); 00623 } 00624 00625 void 00626 Layer::toBriefXml(QTextStream &stream, 00627 QString indent, QString extraAttributes) const 00628 { 00629 stream << indent; 00630 00631 if (m_presentationName != "") { 00632 extraAttributes = QString("%1 presentationName=\"%2\"") 00633 .arg(extraAttributes).arg(encodeEntities(m_presentationName)); 00634 } 00635 00636 stream << QString("<layer id=\"%2\" type=\"%1\" name=\"%3\" model=\"%4\" %5/>\n") 00637 .arg(encodeEntities(LayerFactory::getInstance()->getLayerTypeName 00638 (LayerFactory::getInstance()->getLayerType(this)))) 00639 .arg(getObjectExportId(this)) 00640 .arg(encodeEntities(objectName())) 00641 .arg(getObjectExportId(getModel())) 00642 .arg(extraAttributes); 00643 } 00644