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 cannam@45: Chris@57: Based on HgExplorer by Jari Korhonen Chris@57: Copyright (c) 2010 Jari Korhonen Chris@644: Copyright (c) 2013 Chris Cannam Chris@644: Copyright (c) 2013 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 "panned.h" Chris@131: #include "debug.h" cannam@45: cannam@45: #include Chris@53: #include Chris@168: #include Chris@168: Chris@168: #include cannam@45: cannam@45: #include cannam@45: Chris@168: Panned::Panned() : Chris@168: m_dragging(false) cannam@45: { Chris@168: m_dragTimer = new QTimer(this); Chris@168: m_dragTimerMs = 50; Chris@168: connect(m_dragTimer, SIGNAL(timeout()), this, SLOT(dragTimerTimeout())); Chris@56: setRenderHints(QPainter::Antialiasing | Chris@56: QPainter::TextAntialiasing); Chris@679: Chris@679: double baseEm; Chris@679: #ifdef Q_OS_MAC Chris@679: baseEm = 17.0; Chris@679: #else Chris@679: baseEm = 15.0; Chris@679: #endif Chris@679: double em = QFontMetrics(QFont()).height(); Chris@679: double ratio = em / baseEm; Chris@679: Chris@679: QMatrix m = matrix(); Chris@679: m.scale(ratio, ratio); Chris@679: setMatrix(m); cannam@45: } cannam@45: cannam@45: void cannam@45: Panned::resizeEvent(QResizeEvent *ev) cannam@45: { Chris@131: DEBUG << "Panned::resizeEvent()" << endl; Chris@131: Chris@60: QPointF nearpt = mapToScene(0, 0); Chris@60: QPointF farpt = mapToScene(width(), height()); Chris@60: QSizeF sz(farpt.x()-nearpt.x(), farpt.y()-nearpt.y()); Chris@60: QRectF pr(nearpt, sz); cannam@45: Chris@132: QGraphicsView::resizeEvent(ev); Chris@132: cannam@45: if (pr != m_pannedRect) { Chris@132: DEBUG << "Panned: setting panned rect to " << pr << endl; cannam@45: m_pannedRect = pr; Chris@133: centerOn(pr.center()); Chris@133: emit pannedRectChanged(pr); Chris@133: } Chris@133: } Chris@133: Chris@133: void Chris@133: Panned::setScene(QGraphicsScene *s) Chris@133: { Chris@133: if (!scene()) { Chris@133: QGraphicsView::setScene(s); Chris@133: return; Chris@133: } Chris@133: Chris@133: QPointF nearpt = mapToScene(0, 0); Chris@133: QPointF farpt = mapToScene(width(), height()); Chris@133: QSizeF sz(farpt.x()-nearpt.x(), farpt.y()-nearpt.y()); Chris@133: QRectF pr(nearpt, sz); Chris@133: Chris@133: QGraphicsView::setScene(s); Chris@133: Chris@133: DEBUG << "Panned::setScene: pr = " << pr << ", sceneRect = " << sceneRect() << endl; Chris@133: Chris@133: if (scene() && sceneRect().intersects(pr)) { Chris@133: DEBUG << "Panned::setScene: restoring old rect " << pr << endl; Chris@133: m_pannedRect = pr; Chris@133: centerOn(pr.center()); cannam@45: emit pannedRectChanged(pr); cannam@45: } cannam@45: } cannam@45: cannam@45: void cannam@45: Panned::paintEvent(QPaintEvent *e) cannam@45: { cannam@45: QGraphicsView::paintEvent(e); cannam@45: } cannam@45: cannam@45: void Chris@671: Panned::drawForeground(QPainter *, const QRectF &) cannam@45: { Chris@60: QPointF nearpt = mapToScene(0, 0); Chris@60: QPointF farpt = mapToScene(width(), height()); Chris@60: QSizeF sz(farpt.x()-nearpt.x(), farpt.y()-nearpt.y()); Chris@60: QRectF pr(nearpt, sz); cannam@45: cannam@45: if (pr != m_pannedRect) { Chris@133: DEBUG << "Panned::drawForeground: visible rect " << pr << " differs from panned rect " << m_pannedRect << ", updating panned rect" <modifiers() & Qt::ControlModifier) { Chris@53: int d = ev->delta(); Chris@53: if (d > 0) { Chris@53: while (d > 0) { Chris@53: zoomOut(); Chris@53: d -= 120; Chris@53: } Chris@53: } else { Chris@53: while (d < 0) { Chris@53: zoomIn(); Chris@53: d += 120; Chris@53: } Chris@53: } Chris@53: } else { Chris@53: emit wheelEventReceived(ev); Chris@53: QGraphicsView::wheelEvent(ev); Chris@53: } cannam@45: } cannam@45: cannam@45: void cannam@45: Panned::slotEmulateWheelEvent(QWheelEvent *ev) cannam@45: { cannam@45: QGraphicsView::wheelEvent(ev); cannam@45: } cannam@45: cannam@45: void Chris@168: Panned::mousePressEvent(QMouseEvent *ev) Chris@168: { Chris@168: if (dragMode() != QGraphicsView::ScrollHandDrag || Chris@168: ev->button() != Qt::LeftButton) { Chris@168: QGraphicsView::mousePressEvent(ev); Chris@168: return; Chris@168: } Chris@168: Chris@168: DEBUG << "Panned::mousePressEvent: have left button in drag mode" << endl; Chris@168: Chris@168: setDragMode(QGraphicsView::NoDrag); Chris@168: QGraphicsView::mousePressEvent(ev); Chris@168: setDragMode(QGraphicsView::ScrollHandDrag); Chris@168: Chris@168: if (!ev->isAccepted()) { Chris@168: ev->accept(); Chris@168: m_dragging = true; Chris@168: m_lastDragPos = ev->pos(); Chris@403: m_lastDragStart = ev->pos(); Chris@168: m_lastOrigin = QPoint(horizontalScrollBar()->value(), Chris@168: verticalScrollBar()->value()); Chris@168: m_velocity = QPointF(0, 0); Chris@168: m_dragTimer->start(m_dragTimerMs); Chris@403: m_dragDirection = UnknownDrag; Chris@403: } Chris@403: } Chris@403: Chris@403: void Chris@403: Panned::updateDragDirection(QPoint pos) Chris@403: { Chris@403: if (m_dragDirection == FreeDrag) { Chris@403: return; Chris@168: } Chris@168: Chris@403: QPoint overall = pos - m_lastDragStart; Chris@403: Chris@403: int smallThreshold = 10; Chris@403: int largeThreshold = 30; Chris@403: int dx = qAbs(overall.x()); Chris@403: int dy = qAbs(overall.y()); Chris@403: Chris@403: switch (m_dragDirection) { Chris@403: Chris@403: case UnknownDrag: Chris@403: if (dx > smallThreshold) { Chris@403: if (dy > smallThreshold) { Chris@403: m_dragDirection = FreeDrag; Chris@403: } else { Chris@403: m_dragDirection = HorizontalDrag; Chris@403: } Chris@403: } else if (dy > smallThreshold) { Chris@403: m_dragDirection = VerticalDrag; Chris@403: } Chris@403: break; Chris@403: Chris@403: case HorizontalDrag: Chris@403: if (dy > largeThreshold) { Chris@403: m_dragDirection = FreeDrag; Chris@403: } Chris@403: break; Chris@403: Chris@403: case VerticalDrag: Chris@403: if (dx > largeThreshold) { Chris@403: m_dragDirection = FreeDrag; Chris@403: } Chris@403: break; Chris@671: Chris@671: case FreeDrag: Chris@671: // stick with it Chris@671: break; Chris@403: }; Chris@168: } Chris@168: Chris@168: void Chris@168: Panned::mouseMoveEvent(QMouseEvent *ev) Chris@168: { Chris@168: if (!m_dragging) { Chris@168: QGraphicsView::mouseMoveEvent(ev); Chris@168: return; Chris@168: } Chris@168: DEBUG << "Panned::mouseMoveEvent: dragging" << endl; Chris@168: ev->accept(); Chris@403: updateDragDirection(ev->pos()); Chris@168: QScrollBar *hBar = horizontalScrollBar(); Chris@168: QScrollBar *vBar = verticalScrollBar(); Chris@168: QPoint delta = ev->pos() - m_lastDragPos; Chris@403: if (m_dragDirection != VerticalDrag) { Chris@403: hBar->setValue(hBar->value() + Chris@403: (isRightToLeft() ? delta.x() : -delta.x())); Chris@403: } Chris@403: if (m_dragDirection != HorizontalDrag) { Chris@403: vBar->setValue(vBar->value() - delta.y()); Chris@403: } Chris@168: m_lastDragPos = ev->pos(); Chris@168: } Chris@168: Chris@168: void Chris@168: Panned::mouseReleaseEvent(QMouseEvent *ev) Chris@168: { Chris@168: if (!m_dragging) { Chris@168: QGraphicsView::mouseReleaseEvent(ev); Chris@168: return; Chris@168: } Chris@168: DEBUG << "Panned::mouseReleaseEvent: dragging" << endl; Chris@168: ev->accept(); Chris@168: m_dragging = false; Chris@168: } Chris@168: Chris@168: void Chris@168: Panned::dragTimerTimeout() Chris@168: { Chris@168: QPoint origin = QPoint(horizontalScrollBar()->value(), Chris@168: verticalScrollBar()->value()); Chris@168: if (m_dragging) { Chris@168: m_velocity = QPointF Chris@168: (float(origin.x() - m_lastOrigin.x()) / m_dragTimerMs, Chris@168: float(origin.y() - m_lastOrigin.y()) / m_dragTimerMs); Chris@168: m_lastOrigin = origin; Chris@168: DEBUG << "Panned::dragTimerTimeout: velocity = " << m_velocity << endl; Chris@168: } else { Chris@168: if (origin == m_lastOrigin) { Chris@168: m_dragTimer->stop(); Chris@168: } Chris@168: float x = m_velocity.x(), y = m_velocity.y(); Chris@168: if (fabsf(x) > 1.0/m_dragTimerMs) x = x * 0.9f; Chris@168: else x = 0.f; Chris@168: if (fabsf(y) > 1.0/m_dragTimerMs) y = y * 0.9f; Chris@168: else y = 0.f; Chris@168: m_velocity = QPointF(x, y); Chris@168: DEBUG << "Panned::dragTimerTimeout: velocity adjusted to " << m_velocity << endl; Chris@168: m_lastOrigin = origin; Chris@168: //!!! need to store origin in floats Chris@403: if (m_dragDirection != VerticalDrag) { Chris@403: horizontalScrollBar()->setValue(m_lastOrigin.x() + Chris@403: m_velocity.x() * m_dragTimerMs); Chris@403: } Chris@403: if (m_dragDirection != HorizontalDrag) { Chris@403: verticalScrollBar()->setValue(m_lastOrigin.y() + Chris@403: m_velocity.y() * m_dragTimerMs); Chris@403: } Chris@168: } Chris@168: } Chris@168: Chris@168: void cannam@45: Panned::leaveEvent(QEvent *) cannam@45: { cannam@45: emit mouseLeaves(); cannam@45: }