//============================================================================= // MuseScore // Music Composition & Notation // // Copyright (C) 2013 Werner Schweer and others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 // as published by the Free Software Foundation and appearing in // the file LICENSE.GPL //============================================================================= #include "exampleview.h" #include "preferences.h" #include "libmscore/score.h" #include "libmscore/element.h" #include "libmscore/page.h" #include "libmscore/system.h" #include "libmscore/icon.h" #include "libmscore/chord.h" #include "libmscore/xml.h" namespace Ms { //--------------------------------------------------------- // ExampleView //--------------------------------------------------------- ExampleView::ExampleView(QWidget* parent) : QFrame(parent) { _score = 0; setAcceptDrops(true); setFocusPolicy(Qt::StrongFocus); resetMatrix(); _fgPixmap = nullptr; if (preferences.getBool(PREF_UI_CANVAS_FG_USECOLOR)) _fgColor = preferences.getColor(PREF_UI_CANVAS_FG_COLOR); else { _fgPixmap = new QPixmap(preferences.getString(PREF_UI_CANVAS_FG_WALLPAPER)); if (_fgPixmap == 0 || _fgPixmap->isNull()) qDebug("no valid pixmap %s", qPrintable(preferences.getString(PREF_UI_CANVAS_FG_WALLPAPER))); } // setup drag canvas state sm = new QStateMachine(this); QState* stateActive = new QState; QState* s1 = new QState(stateActive); s1->setObjectName("example-normal"); s1->assignProperty(this, "cursor", QCursor(Qt::ArrowCursor)); QState* s = new QState(stateActive); s->setObjectName("example-drag"); s->assignProperty(this, "cursor", QCursor(Qt::SizeAllCursor)); QEventTransition* cl = new QEventTransition(this, QEvent::MouseButtonRelease); cl->setTargetState(s1); s->addTransition(cl); s1->addTransition(new DragTransitionExampleView(this)); sm->addState(stateActive); stateActive->setInitialState(s1); sm->setInitialState(stateActive); sm->start(); } //--------------------------------------------------------- // ~ExampleView //--------------------------------------------------------- ExampleView::~ExampleView() { if (_fgPixmap) delete _fgPixmap; } //--------------------------------------------------------- // resetMatrix // used to reset scrolling in case time signature num or denom changed //--------------------------------------------------------- void ExampleView::resetMatrix() { double mag = 0.9 * guiScaling * (DPI_DISPLAY / DPI); // 90% of nominal qreal _spatium = SPATIUM20 * mag; // example would normally be 10sp from top of page; this leaves 3sp margin above // _matrix = QTransform(mag, 0.0, 0.0, mag, _spatium, -_spatium * 7.0); _matrix = QTransform(mag, 0.0, 0.0, mag, _spatium, -_spatium * 10.0); imatrix = _matrix.inverted(); } void ExampleView::layoutChanged() { } void ExampleView::dataChanged(const QRectF&) { } void ExampleView::updateAll() { update(); } void ExampleView::adjustCanvasPosition(const Element* /*el*/, bool /*playBack*/) { } //--------------------------------------------------------- // setScore //--------------------------------------------------------- void ExampleView::setScore(Score* s) { delete _score; _score = s; _score->addViewer(this); _score->setLayoutMode(LayoutMode::LINE); _score->doLayout(); update(); } void ExampleView::removeScore() { } void ExampleView::changeEditElement(Element*) { } QCursor ExampleView::cursor() const { return QCursor(); } void ExampleView::setCursor(const QCursor&) { } int ExampleView::gripCount() const { return 0; } void ExampleView::setDropRectangle(const QRectF&) { } void ExampleView::cmdAddSlur(Note* /*firstNote*/, Note* /*lastNote*/) { } void ExampleView::startEdit() { } void ExampleView::startEdit(Element*, Grip /*startGrip*/) { } Element* ExampleView::elementNear(QPointF) { return 0; } void ExampleView::drawBackground(QPainter* p, const QRectF& r) const { if (_fgPixmap == 0 || _fgPixmap->isNull()) p->fillRect(r, _fgColor); else { p->drawTiledPixmap(r, *_fgPixmap, r.topLeft() - QPoint(lrint(_matrix.dx()), lrint(_matrix.dy()))); } } //--------------------------------------------------------- // drawElements //--------------------------------------------------------- void ExampleView::drawElements(QPainter& painter, const QList& el) { for (Element* e : el) { e->itemDiscovered = 0; QPointF pos(e->pagePos()); painter.translate(pos); e->draw(&painter); painter.translate(-pos); } } //--------------------------------------------------------- // paintEvent //--------------------------------------------------------- void ExampleView::paintEvent(QPaintEvent* ev) { if (_score) { QPainter p(this); p.setRenderHint(QPainter::Antialiasing, preferences.getBool(PREF_UI_CANVAS_MISC_ANTIALIASEDDRAWING)); p.setRenderHint(QPainter::TextAntialiasing, true); const QRect r(ev->rect()); drawBackground(&p, r); p.setTransform(_matrix); QRectF fr = imatrix.mapRect(QRectF(r)); QRegion r1(r); Page* page = _score->pages().front(); QList ell = page->items(fr); qStableSort(ell.begin(), ell.end(), elementLessThan); drawElements(p, ell); } QFrame::paintEvent(ev); } //--------------------------------------------------------- // dragEnterEvent //--------------------------------------------------------- void ExampleView::dragEnterEvent(QDragEnterEvent* event) { const QMimeData* data = event->mimeData(); if (data->hasFormat(mimeSymbolFormat)) { event->acceptProposedAction(); QByteArray a = data->data(mimeSymbolFormat); // qDebug("ExampleView::dragEnterEvent Symbol: <%s>", a.data()); XmlReader e(a); QPointF dragOffset; Fraction duration; // dummy ElementType type = Element::readType(e, &dragOffset, &duration); dragElement = Element::create(type, _score); if (dragElement) { dragElement->setParent(0); dragElement->read(e); dragElement->layout(); } return; } } //--------------------------------------------------------- // dragLeaveEvent //--------------------------------------------------------- void ExampleView::dragLeaveEvent(QDragLeaveEvent*) { if (dragElement) { delete dragElement; dragElement = 0; } setDropTarget(0); } //--------------------------------------------------------- // moveElement //--------------------------------------------------------- static void moveElement(void* data, Element* e) { QPointF* pos = (QPointF*)data; e->score()->addRefresh(e->canvasBoundingRect()); e->setPos(*pos); e->score()->addRefresh(e->canvasBoundingRect()); } //--------------------------------------------------------- // dragMoveEvent //--------------------------------------------------------- void ExampleView::dragMoveEvent(QDragMoveEvent* event) { event->acceptProposedAction(); if (!dragElement || dragElement->type() != ElementType::ICON) return; QPointF pos(imatrix.map(QPointF(event->pos()))); QList el = elementsAt(pos); bool found = false; foreach(const Element* e, el) { if (e->type() == ElementType::NOTE) { setDropTarget(const_cast(e)); found = true; break; } } if (!found) setDropTarget(0); dragElement->scanElements(&pos, moveElement, false); _score->update(); return; } //--------------------------------------------------------- // setDropTarget //--------------------------------------------------------- void ExampleView::setDropTarget(const Element* el) { if (dropTarget != el) { if (dropTarget) { dropTarget->setDropTarget(false); dropTarget = 0; } dropTarget = el; if (dropTarget) { dropTarget->setDropTarget(true); } } if (!dropAnchor.isNull()) { QRectF r; r.setTopLeft(dropAnchor.p1()); r.setBottomRight(dropAnchor.p2()); dropAnchor = QLineF(); } if (dropRectangle.isValid()) { dropRectangle = QRectF(); } update(); } //--------------------------------------------------------- // dropEvent //--------------------------------------------------------- void ExampleView::dropEvent(QDropEvent* event) { QPointF pos(imatrix.map(QPointF(event->pos()))); if (!dragElement) return; if (dragElement->type() != ElementType::ICON) { delete dragElement; dragElement = 0; return; } foreach (Element* e, elementsAt(pos)) { if (e->type() == ElementType::NOTE) { Icon* icon = static_cast(dragElement); Chord* chord = static_cast(e)->chord(); emit beamPropertyDropped(chord, icon); switch (icon->iconType()) { case IconType::SBEAM: chord->setBeamMode(Beam::Mode::BEGIN); break; case IconType::MBEAM: chord->setBeamMode(Beam::Mode::AUTO); break; case IconType::BEAM32: chord->setBeamMode(Beam::Mode::BEGIN32); break; case IconType::BEAM64: chord->setBeamMode(Beam::Mode::BEGIN64); break; default: break; } score()->doLayout(); break; } } event->acceptProposedAction(); delete dragElement; dragElement = 0; setDropTarget(0); } //--------------------------------------------------------- // mousePressEvent //--------------------------------------------------------- void ExampleView::mousePressEvent(QMouseEvent* event) { startMove = imatrix.map(QPointF(event->pos())); QPointF pos(imatrix.map(QPointF(event->pos()))); foreach (Element* e, elementsAt(pos)) { if (e->type() == ElementType::NOTE) { emit noteClicked(static_cast(e)); break; } } } //--------------------------------------------------------- // sizeHint //--------------------------------------------------------- QSize ExampleView::sizeHint() const { qreal mag = 0.9 * guiScaling * (DPI_DISPLAY / DPI); qreal _spatium = SPATIUM20 * mag; // staff is 4sp tall with 3sp margin above; this leaves 3sp margin below qreal height = 10.0 * _spatium; if (score() && score()->pages().size() > 0) height = score()->pages()[0]->tbbox().height() * mag + (6 * _spatium); return QSize(1000 * mag, height); } //--------------------------------------------------------- // dragExampleView // constrained scrolling ensuring that this ExampleView won't be moved past the borders of its QFrame //--------------------------------------------------------- void ExampleView::dragExampleView(QMouseEvent* ev) { QPoint d = ev->pos() - _matrix.map(startMove).toPoint(); int dx = d.x(); if (dx == 0) return; constraintCanvas(&dx); // Perform the actual scrolling _matrix.setMatrix(_matrix.m11(), _matrix.m12(), _matrix.m13(), _matrix.m21(), _matrix.m22(), _matrix.m23(), _matrix.dx()+dx, _matrix.dy(), _matrix.m33()); imatrix = _matrix.inverted(); scroll(dx, 0); } void DragTransitionExampleView::onTransition(QEvent* e) { QStateMachine::WrappedEvent* we = static_cast(e); QMouseEvent* me = static_cast(we->event()); canvas->dragExampleView(me); } //--------------------------------------------------------- // wheelEvent //--------------------------------------------------------- void ExampleView::wheelEvent(QWheelEvent* event) { QPoint pixelsScrolled = event->pixelDelta(); QPoint stepsScrolled = event->angleDelta(); int dx = 0, dy = 0; if (!pixelsScrolled.isNull()) { dx = pixelsScrolled.x(); dy = pixelsScrolled.y(); } else if (!stepsScrolled.isNull()) { dx = static_cast(stepsScrolled.x()) * qMax(2, width() / 10) / 120; dy = static_cast(stepsScrolled.y()) * qMax(2, height() / 10) / 120; } if (dx == 0) { if (dy == 0) return; else dx = dy; } constraintCanvas(&dx); _matrix.setMatrix(_matrix.m11(), _matrix.m12(), _matrix.m13(), _matrix.m21(), _matrix.m22(), _matrix.m23(), _matrix.dx()+dx, _matrix.dy(), _matrix.m33()); imatrix = _matrix.inverted(); scroll(dx, 0); } //----------------------------------------------------------------------------- // constraintCanvas //----------------------------------------------------------------------------- void ExampleView::constraintCanvas (int* dxx) { int dx = *dxx; Q_ASSERT(_score->pages().front()->system(0)); // should exist if doLayout ran // form rectangle bounding the the system with a spatium margin and translate relative to view space qreal xstart = _score->pages().front()->system(0)->bbox().left() - SPATIUM20; qreal xend = _score->pages().front()->system(0)->bbox().right() + 2.0 * SPATIUM20; QRectF systemScaledViewRect(xstart * _matrix.m11(), 0, xend * _matrix.m11(), 0); systemScaledViewRect.translate(_matrix.dx(), 0); qreal frameWidth = static_cast(this)->frameRect().width(); // constrain the dx of scrolling so that this ExampleView won't be moved past the borders of its QFrame if (dx > 0) { // when moving right, ensure the left edge of systemScaledViewRect won't be right of frame's left edge if (systemScaledViewRect.left() + dx > 0) dx = -systemScaledViewRect.left(); } else { // never move left if entire system already fits entirely within the frame if (systemScaledViewRect.width() < frameWidth) dx = 0; // when moving left, ensure the right edge of systemScaledViewRect won't be left of frame's right edge else if (systemScaledViewRect.right() + dx < frameWidth) dx = frameWidth - systemScaledViewRect.right(); } *dxx = dx; } }