Chris@57: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ cannam@45: cannam@45: /* Chris@57: EasyMercurial Chris@57: Chris@57: Based on HgExplorer by Jari Korhonen Chris@57: Copyright (c) 2010 Jari Korhonen Chris@244: Copyright (c) 2011 Chris Cannam Chris@244: Copyright (c) 2011 Queen Mary, University of London Chris@57: cannam@45: This program is free software; you can redistribute it and/or cannam@45: modify it under the terms of the GNU General Public License as cannam@45: published by the Free Software Foundation; either version 2 of the cannam@45: License, or (at your option) any later version. See the file cannam@45: COPYING included with this distribution for more information. cannam@45: */ cannam@45: cannam@45: #include "panner.h" cannam@45: #include "panned.h" Chris@113: #include "debug.h" cannam@45: cannam@45: #include cannam@45: #include cannam@45: #include cannam@45: cannam@45: #include cannam@45: cannam@45: class PannerScene : public QGraphicsScene cannam@45: { cannam@45: public: cannam@45: friend class Panner; cannam@45: }; cannam@45: cannam@45: Panner::Panner() : Chris@389: m_clicked(false), Chris@389: m_moved(false) cannam@45: { cannam@45: setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); cannam@45: setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); Chris@48: setOptimizationFlags(QGraphicsView::DontSavePainterState | Chris@48: QGraphicsView::IndirectPainting); cannam@45: setMouseTracking(true); cannam@45: setInteractive(false); cannam@45: } cannam@45: cannam@45: void Chris@113: Panner::fit(QRectF r) Chris@113: { Chris@113: Qt::AspectRatioMode m = Qt::IgnoreAspectRatio; Chris@113: if (height() > width()) { Chris@113: // Our panner is vertical; if the source is not tall, Chris@113: // don't stretch it to fit in the height, it'd look weird Chris@113: if (r.height() < height() * 2) { Chris@113: m = Qt::KeepAspectRatio; Chris@113: } Chris@113: } else { Chris@113: // Similarly, but horizontal Chris@113: if (r.width() < width() * 2) { Chris@113: m = Qt::KeepAspectRatio; Chris@113: } Chris@113: } Chris@113: DEBUG << "Panner: fit mode " << m << endl; Chris@113: fitInView(r, m); Chris@113: } Chris@113: Chris@113: void cannam@45: Panner::setScene(QGraphicsScene *s) cannam@45: { cannam@45: if (scene()) { Chris@133: disconnect(scene(), SIGNAL(changed(const QList &)), Chris@132: this, SLOT(slotSceneChanged(const QList &))); cannam@45: disconnect(scene(), SIGNAL(sceneRectChanged(const QRectF &)), cannam@45: this, SLOT(slotSceneRectChanged(const QRectF &))); cannam@45: } cannam@45: QGraphicsView::setScene(s); Chris@136: m_cache = QPixmap(); Chris@113: if (scene()) { Chris@113: QRectF r = sceneRect(); Chris@113: DEBUG << "scene rect: " << r << ", my rect " << rect() << endl; Chris@113: fit(r); Chris@136: connect(scene(), SIGNAL(changed(const QList &)), Chris@136: this, SLOT(slotSceneChanged(const QList &))); Chris@136: connect(scene(), SIGNAL(sceneRectChanged(const QRectF &)), Chris@136: this, SLOT(slotSceneRectChanged(const QRectF &))); Chris@113: } cannam@45: } cannam@45: cannam@45: void cannam@45: Panner::connectToPanned(Panned *p) cannam@45: { cannam@45: connect(p, SIGNAL(pannedRectChanged(QRectF)), cannam@45: this, SLOT(slotSetPannedRect(QRectF))); cannam@45: cannam@45: connect(this, SIGNAL(pannedRectChanged(QRectF)), cannam@45: p, SLOT(slotSetPannedRect(QRectF))); Chris@53: Chris@53: connect(this, SIGNAL(zoomIn()), Chris@53: p, SLOT(zoomIn())); Chris@53: Chris@53: connect(this, SIGNAL(zoomOut()), Chris@53: p, SLOT(zoomOut())); cannam@45: } cannam@45: cannam@45: void cannam@45: Panner::slotSetPannedRect(QRectF rect) cannam@45: { cannam@45: m_pannedRect = rect; cannam@45: viewport()->update(); cannam@45: } cannam@45: cannam@45: void cannam@45: Panner::resizeEvent(QResizeEvent *) cannam@45: { Chris@250: DEBUG << "Panner::resizeEvent" << endl; Chris@113: if (scene()) fit(sceneRect()); cannam@45: m_cache = QPixmap(); cannam@45: } cannam@45: cannam@45: void cannam@45: Panner::slotSceneRectChanged(const QRectF &newRect) cannam@45: { Chris@250: DEBUG << "Panner::slotSceneRectChanged" << endl; cannam@45: if (!scene()) return; // spurious Chris@113: fit(newRect); cannam@45: m_cache = QPixmap(); cannam@45: viewport()->update(); cannam@45: } cannam@45: cannam@45: void Chris@132: Panner::slotSceneChanged(const QList &) Chris@132: { Chris@250: DEBUG << "Panner::slotSceneChanged" << endl; Chris@132: if (!scene()) return; // spurious Chris@132: m_cache = QPixmap(); Chris@132: viewport()->update(); Chris@132: } Chris@132: Chris@132: void cannam@45: Panner::paintEvent(QPaintEvent *e) cannam@45: { cannam@45: QPaintEvent *e2 = new QPaintEvent(e->region().boundingRect()); cannam@45: QGraphicsView::paintEvent(e2); cannam@45: cannam@45: QPainter paint; cannam@45: paint.begin(viewport()); cannam@45: paint.setClipRegion(e->region()); cannam@45: cannam@45: QPainterPath path; cannam@45: path.addRect(rect()); cannam@45: path.addPolygon(mapFromScene(m_pannedRect)); cannam@45: cannam@45: QColor c(QColor::fromHsv(211, 194, 238)); cannam@45: c.setAlpha(80); cannam@45: paint.setPen(Qt::NoPen); cannam@45: paint.setBrush(c); cannam@45: paint.drawPath(path); cannam@45: cannam@45: paint.setBrush(Qt::NoBrush); cannam@45: paint.setPen(QPen(QColor::fromHsv(211, 194, 238), 0)); cannam@45: paint.drawConvexPolygon(mapFromScene(m_pannedRect)); cannam@45: cannam@45: paint.end(); cannam@45: cannam@45: emit pannerChanged(m_pannedRect); cannam@45: } cannam@45: cannam@45: void cannam@45: Panner::updateScene(const QList &rects) cannam@45: { Chris@250: DEBUG << "Panner::updateScene" << endl; Chris@250: // if (!m_cache.isNull()) m_cache = QPixmap(); cannam@45: QGraphicsView::updateScene(rects); cannam@45: } cannam@45: cannam@45: void cannam@45: Panner::drawItems(QPainter *painter, int numItems, cannam@45: QGraphicsItem *items[], cannam@45: const QStyleOptionGraphicsItem options[]) cannam@45: { cannam@45: if (m_cache.size() != viewport()->size()) { cannam@45: Chris@131: DEBUG << "Panner: cache size " << m_cache.size() << " != viewport size " << viewport()->size() << ": recreating cache" << endl; Chris@48: cannam@45: QGraphicsScene *s = scene(); cannam@45: if (!s) return; cannam@45: PannerScene *ps = static_cast(s); cannam@45: cannam@45: m_cache = QPixmap(viewport()->size()); cannam@45: m_cache.fill(Qt::transparent); cannam@45: QPainter cachePainter; cannam@45: cachePainter.begin(&m_cache); cannam@45: cachePainter.setTransform(viewportTransform()); cannam@45: ps->drawItems(&cachePainter, numItems, items, options); cannam@45: cachePainter.end(); Chris@250: Chris@250: DEBUG << "done" << endl; cannam@45: } cannam@45: cannam@45: painter->save(); cannam@45: painter->setTransform(QTransform()); cannam@45: painter->drawPixmap(0, 0, m_cache); cannam@45: painter->restore(); cannam@45: } cannam@45: cannam@45: void cannam@45: Panner::mousePressEvent(QMouseEvent *e) cannam@45: { cannam@45: if (e->button() != Qt::LeftButton) { cannam@45: QGraphicsView::mouseDoubleClickEvent(e); cannam@45: return; cannam@45: } cannam@45: m_clicked = true; Chris@389: m_moved = false; cannam@45: m_clickedRect = m_pannedRect; cannam@45: m_clickedPoint = e->pos(); cannam@45: } cannam@45: cannam@45: void cannam@45: Panner::mouseDoubleClickEvent(QMouseEvent *e) cannam@45: { cannam@45: if (e->button() != Qt::LeftButton) { cannam@45: QGraphicsView::mouseDoubleClickEvent(e); cannam@45: return; cannam@45: } cannam@45: cannam@45: moveTo(e->pos()); cannam@45: } cannam@45: cannam@45: void cannam@45: Panner::mouseMoveEvent(QMouseEvent *e) cannam@45: { cannam@45: if (!m_clicked) return; cannam@45: QPointF cp = mapToScene(m_clickedPoint); cannam@45: QPointF mp = mapToScene(e->pos()); cannam@45: QPointF delta = mp - cp; Chris@389: if (!m_moved) { Chris@389: if ((m_clickedPoint - e->pos()).manhattanLength() > 2) { Chris@389: m_moved = true; Chris@389: } else { Chris@389: return; Chris@389: } Chris@389: } cannam@45: QRectF nr = m_clickedRect; cannam@45: nr.translate(delta); Chris@173: m_pannedRect = nr; cannam@45: emit pannedRectChanged(m_pannedRect); cannam@45: viewport()->update(); cannam@45: } cannam@45: cannam@45: void cannam@45: Panner::mouseReleaseEvent(QMouseEvent *e) cannam@45: { cannam@45: if (e->button() != Qt::LeftButton) { cannam@45: QGraphicsView::mouseDoubleClickEvent(e); cannam@45: return; cannam@45: } cannam@45: cannam@45: if (m_clicked) { Chris@389: if (m_moved) { Chris@389: mouseMoveEvent(e); Chris@389: } else { Chris@389: moveTo(e->pos()); Chris@389: } cannam@45: } cannam@45: cannam@45: m_clicked = false; cannam@45: viewport()->update(); cannam@45: } cannam@45: cannam@45: void cannam@45: Panner::wheelEvent(QWheelEvent *e) cannam@45: { Chris@53: int d = e->delta(); Chris@53: if (d > 0) { Chris@53: while (d > 0) { Chris@53: emit zoomOut(); Chris@53: d -= 120; Chris@53: } cannam@45: } else { Chris@53: while (d < 0) { Chris@53: emit zoomIn(); Chris@53: d += 120; Chris@53: } cannam@45: } cannam@45: } cannam@45: cannam@45: void cannam@45: Panner::moveTo(QPoint p) cannam@45: { cannam@45: QPointF sp = mapToScene(p); cannam@45: QRectF nr = m_pannedRect; Chris@389: double dx = sp.x() - nr.center().x(); Chris@389: double dy = sp.y() - nr.center().y(); Chris@389: nr.translate(dx, dy); cannam@45: slotSetPannedRect(nr); cannam@45: emit pannedRectChanged(m_pannedRect); cannam@45: viewport()->update(); cannam@45: } cannam@45: