# HG changeset patch # User Chris Cannam # Date 1289393051 0 # Node ID 4286836bb3c90aa80bdb66e7bec0a8d93a4f30b4 # Parent bed7ab59f62e16d87267e8c8ba7a1cc8561836e0 * Some more work on graph layout; ensure LANG is set for parseable UTF8 output when running Hg diff -r bed7ab59f62e -r 4286836bb3c9 grapher.cpp --- a/grapher.cpp Tue Nov 09 17:51:12 2010 +0000 +++ b/grapher.cpp Wed Nov 10 12:44:11 2010 +0000 @@ -1,87 +1,189 @@ #include "grapher.h" -#include -#include +#include #include -typedef QSet ColumnSet; -typedef QMap GridAlloc; -typedef QMap IdChangesetMap; -typedef QSet ChangesetSet; +int +Grapher::findAvailableColumn(int row, int parent, bool preferParentCol) +{ + int col = parent; + if (preferParentCol) { + if (!m_alloc[row].contains(col)) { + return col; + } + } + while (col > 0) { + if (!m_alloc[row].contains(--col)) return col; + } + while (col < 0) { + if (!m_alloc[row].contains(++col)) return col; + } + col = parent; + int sign = (col < 0 ? -1 : 1); + while (1) { + col += sign; + if (!m_alloc[row].contains(col)) return col; + } +} -ChangesetItem * -layout(Changeset *cs, - IdChangesetMap idCsetMap, - ChangesetItemMap items, - GridAlloc &alloc, - ChangesetSet &handled) +void +Grapher::layoutRow(QString id) { - if (!cs) { - throw std::string("Null Changeset"); + if (m_handled.contains(id)) { + return; } - if (!items.contains(cs)) { - throw std::string("Changeset not in item map"); + if (!m_idCsetMap.contains(id)) { + throw LayoutException(QString("Changeset %1 not in ID map").arg(id)); } - ChangesetItem *item = items[cs]; - if (handled.contains(cs)) { - return item; + if (!m_items.contains(id)) { + throw LayoutException(QString("Changeset %1 not in item map").arg(id)); } + Changeset *cs = m_idCsetMap[id]; + ChangesetItem *item = m_items[id]; + std::cerr << "Looking at " << id.toStdString() << std::endl; + int row = 0; - int col = 0; - if (!cs->parents().empty()) { + int nparents = cs->parents().size(); + + if (nparents > 0) { bool haveRow = false; foreach (QString parentId, cs->parents()) { - if (parentId == "") continue; //!!! - std::cerr << "recursing to parent \"" << parentId.toStdString() << "\" of \"" << cs->id().toStdString() << "\"" << std::endl; - ChangesetItem *parentItem = - layout(idCsetMap[parentId], - idCsetMap, - items, - alloc, - handled); + + if (!m_idCsetMap.contains(parentId)) continue; + if (!m_items.contains(parentId)) continue; + + if (!m_handled.contains(parentId)) { + layoutRow(parentId); + } + + ChangesetItem *parentItem = m_items[parentId]; if (!haveRow || parentItem->row() < row) { row = parentItem->row(); haveRow = true; } - col += parentItem->column(); } - col /= cs->parents().size(); row = row - 1; - while (alloc[row].contains(col)) { - if (col > 0) col = -col; - else col = -col + 1; - } - alloc[row].insert(col); - } - item->setColumn(col); + } + + std::cerr << "putting " << cs->id().toStdString() << " at row " << row + << std::endl; + item->setRow(row); - item->setX(col * 100); item->setY(row * 100); - handled.insert(cs); - return item; + m_handled.insert(id); } void -Grapher::layout(Changesets csets, ChangesetItemMap items) +Grapher::layoutCol(QString id) { - IdChangesetMap idCsetMap; - foreach (Changeset *cs, csets) { - std::cerr << cs->id().toStdString() << std::endl; - if (cs->id() == "") { - throw std::string("Changeset has no ID"); + if (m_handled.contains(id)) { + std::cerr << "Already looked at " << id.toStdString() << std::endl; + return; + } + if (!m_idCsetMap.contains(id)) { + throw LayoutException(QString("Changeset %1 not in ID map").arg(id)); + } + if (!m_items.contains(id)) { + throw LayoutException(QString("Changeset %1 not in item map").arg(id)); + } + Changeset *cs = m_idCsetMap[id]; + ChangesetItem *item = m_items[id]; + std::cerr << "Looking at " << id.toStdString() << std::endl; + + int col = 0; + int nparents = cs->parents().size(); + + if (nparents > 0) { + bool preferParentCol = true; + foreach (QString parentId, cs->parents()) { + + if (!m_idCsetMap.contains(parentId)) continue; + if (!m_items.contains(parentId)) continue; + + if (nparents == 1) { + // when introducing a new branch, aim _not_ to + // position child on the same column as parent + Changeset *parent = m_idCsetMap[parentId]; + if (parent->branch() != cs->branch()) { + preferParentCol = false; + } + } + + if (!m_handled.contains(parentId)) { + layoutCol(parentId); + } + + ChangesetItem *parentItem = m_items[parentId]; + col += parentItem->column(); } - if (idCsetMap.contains(cs->id())) { - throw std::string("Changeset ID is already in map"); - } - idCsetMap[cs->id()] = cs; + + col /= cs->parents().size(); + col = findAvailableColumn(item->row(), col, preferParentCol); + m_alloc[item->row()].insert(col); } - GridAlloc alloc; - ChangesetSet handled; + std::cerr << "putting " << cs->id().toStdString() << " at col " << col << std::endl; + + item->setColumn(col); + item->setX(col * 100); + m_handled.insert(id); +} + +void +Grapher::layout(Changesets csets) +{ + m_idCsetMap.clear(); + m_items.clear(); + m_alloc.clear(); + foreach (Changeset *cs, csets) { - ::layout(cs, idCsetMap, items, alloc, handled); + + QString id = cs->id(); + std::cerr << id.toStdString() << std::endl; + + if (id == "") { + throw LayoutException("Changeset has no ID"); + } + if (m_idCsetMap.contains(id)) { + throw LayoutException(QString("Duplicate changeset ID %1").arg(id)); + } + + m_idCsetMap[id] = cs; + + ChangesetItem *item = new ChangesetItem(cs); + item->setX(0); + item->setY(0); + m_items[id] = item; + m_scene->addItem(item); + } + + // Layout in reverse order, i.e. forward chronological order. + // This ensures that parents will normally be laid out before + // their children -- though we can recurse from layout() if we + // find any weird exceptions + m_handled.clear(); + for (int i = csets.size() - 1; i >= 0; --i) { + layoutRow(csets[i]->id()); + } + m_handled.clear(); + for (int i = csets.size() - 1; i >= 0; --i) { + layoutCol(csets[i]->id()); + } + + foreach (Changeset *cs, csets) { + QString id = cs->id(); + if (!m_items.contains(id)) continue; + ChangesetItem *me = m_items[id]; + foreach (QString parentId, cs->parents()) { + if (!m_items.contains(parentId)) continue; + ChangesetItem *parent = m_items[parentId]; + QGraphicsLineItem *line = new QGraphicsLineItem; + line->setLine(me->x() + 25, me->y() + 50, + parent->x() + 25, parent->y()); + m_scene->addItem(line); + } } } diff -r bed7ab59f62e -r 4286836bb3c9 grapher.h --- a/grapher.h Tue Nov 09 17:51:12 2010 +0000 +++ b/grapher.h Wed Nov 10 12:44:11 2010 +0000 @@ -4,12 +4,48 @@ #include "changeset.h" #include "changesetitem.h" -typedef QMap ChangesetItemMap; +#include +#include + +#include class Grapher { public: - void layout(Changesets csets, ChangesetItemMap items); + Grapher(QGraphicsScene *scene) { m_scene = scene; } + + void layout(Changesets csets); + + class LayoutException : public std::exception { + public: + LayoutException(QString message) throw() : m_message(message) { } + virtual ~LayoutException() throw() { } + virtual const char *what() const throw() { + return m_message.toLocal8Bit().data(); + } + protected: + QString m_message; + }; + +private: + void layoutRow(QString id); + void layoutCol(QString id); + int findAvailableColumn(int row, int parent, bool preferParentCol); + + QGraphicsScene *m_scene; + + typedef QMap IdChangesetMap; + IdChangesetMap m_idCsetMap; + + typedef QMap IdItemMap; + IdItemMap m_items; + + typedef QSet ColumnSet; + typedef QMap GridAlloc; + GridAlloc m_alloc; + + typedef QSet IdSet; + IdSet m_handled; }; #endif diff -r bed7ab59f62e -r 4286836bb3c9 hgexplorer.pro --- a/hgexplorer.pro Tue Nov 09 17:51:12 2010 +0000 +++ b/hgexplorer.pro Wed Nov 10 12:44:11 2010 +0000 @@ -15,7 +15,9 @@ settingsdialog.h \ changeset.h \ changesetitem.h \ - logparser.h + logparser.h \ + panner.h \ + panned.h SOURCES = main.cpp \ mainwindow.cpp \ hgexpwidget.cpp \ @@ -25,7 +27,9 @@ common.cpp \ changeset.cpp \ changesetitem.cpp \ - logparser.cpp + logparser.cpp \ + panner.cpp \ + panned.cpp # ! [0] RESOURCES = hgexplorer.qrc diff -r bed7ab59f62e -r 4286836bb3c9 hgexpwidget.cpp --- a/hgexpwidget.cpp Tue Nov 09 17:51:12 2010 +0000 +++ b/hgexpwidget.cpp Wed Nov 10 12:44:11 2010 +0000 @@ -12,6 +12,8 @@ #include "changeset.h" #include "changesetitem.h" #include "grapher.h" +#include "panner.h" +#include "panned.h" #include @@ -91,7 +93,17 @@ addTab(workPageWidget, tr("Work")); // History graph page - historyGraphPageWidget = new QGraphicsView; + historyGraphPageWidget = new QWidget; + Panned *panned = new Panned; + Panner *panner = new Panner; + historyGraphWidget = panned; + historyGraphPanner = panner; + QGridLayout *layout = new QGridLayout; + layout->addWidget(historyGraphWidget, 0, 0); + layout->addWidget(historyGraphPanner, 0, 1); + panner->setMaximumWidth(80); + panner->connectToPanned(panned); + historyGraphPageWidget->setLayout(layout); addTab(historyGraphPageWidget, tr("History (graph)")); @@ -244,56 +256,20 @@ localRepoHgLogList -> addItems(splitChangeSets(hgLogList)); //!!! - QGraphicsView *gv = static_cast(historyGraphPageWidget); - gv->scene()->deleteLater(); + Panned *panned = static_cast(historyGraphWidget); + Panner *panner = static_cast(historyGraphPanner); QGraphicsScene *scene = new QGraphicsScene(); Changesets csets = parseChangeSets(hgLogList); if (csets.empty()) return; - ChangesetItemMap csetItemMap; - foreach (Changeset *cs, csets) { - ChangesetItem *item = new ChangesetItem(cs); - item->setX(0); - item->setY(0); - csetItemMap[cs] = item; - scene->addItem(item); - } try { - Grapher().layout(csets, csetItemMap); + Grapher(scene).layout(csets); } catch (std::string s) { std::cerr << "Internal error: Layout failed: " << s << std::endl; } -/* - QMap idCsetMap; - foreach (Changeset *cs, csets) { - if (cs->id() == "") { - throw std::string("Changeset has no ID"); - } - if (idCsetMap.contains(cs->id())) { - throw std::string("Changeset ID is already in map"); - } - idCsetMap[cs->id()] = cs; - } - typedef QSet ColumnSet; - typedef QMap GridAlloc; - typedef QMap ChangesetItemMap; - ChangesetItemMap csetItemMap; - foreach (Changeset *cs, csets) { - ChangesetItem *item = new ChangesetItem(cs); - item->setX(0); - item->setY(-cs->number() * 100); - csetItemMap[cs] = item; - scene->addItem(item); - } - QSet handled; - for (int i = csets.size() - 1; i >= 0; --i) { - Changeset *cs = csets[i]; - if (handled.contains(cs)) continue; - - - handled.insert(cs); - } -*/ - gv->setScene(scene); + panned->scene()->deleteLater(); + panned->setScene(scene); + panner->scene()->deleteLater(); + panner->setScene(scene); } diff -r bed7ab59f62e -r 4286836bb3c9 hgexpwidget.h --- a/hgexpwidget.h Tue Nov 09 17:51:12 2010 +0000 +++ b/hgexpwidget.h Wed Nov 10 12:44:11 2010 +0000 @@ -50,6 +50,8 @@ QGroupBox *grpRemoteRepo; QWidget *workPageWidget; QWidget *historyGraphPageWidget; + QWidget *historyGraphWidget; + QWidget *historyGraphPanner; QWidget *historyPageWidget; QWidget *headsPageWidget; diff -r bed7ab59f62e -r 4286836bb3c9 hgrunner.cpp --- a/hgrunner.cpp Tue Nov 09 17:51:12 2010 +0000 +++ b/hgrunner.cpp Wed Nov 10 12:44:11 2010 +0000 @@ -13,6 +13,11 @@ { proc = new QProcess(this); + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert("LANG", "en_US.utf8"); + env.insert("LC_ALL", "en_US.utf8"); + proc->setProcessEnvironment(env); + setTextVisible(false); setVisible(false); isRunning = false; @@ -39,8 +44,8 @@ { exitCode = procExitCode; exitStatus = procExitStatus; - stdOut = proc -> readAllStandardOutput(); - stdErr = proc -> readAllStandardError(); + stdOut = QString::fromUtf8(proc -> readAllStandardOutput()); + stdErr = QString::fromUtf8(proc -> readAllStandardError()); std::cerr << "stdout was " << stdOut.toStdString() << std::endl; } diff -r bed7ab59f62e -r 4286836bb3c9 panned.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/panned.cpp Wed Nov 10 12:44:11 2010 +0000 @@ -0,0 +1,90 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + Copyright 2000-2010 the Rosegarden development team. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "panned.h" + +#include + +#include + +Panned::Panned() +{ +} + +void +Panned::resizeEvent(QResizeEvent *ev) +{ + QPointF near = mapToScene(0, 0); + QPointF far = mapToScene(width(), height()); + QSizeF sz(far.x()-near.x(), far.y()-near.y()); + QRectF pr(near, sz); + + if (pr != m_pannedRect) { + m_pannedRect = pr; + emit pannedRectChanged(pr); + } + + QGraphicsView::resizeEvent(ev); +} + +void +Panned::paintEvent(QPaintEvent *e) +{ + QGraphicsView::paintEvent(e); +} + +void +Panned::drawForeground(QPainter *paint, const QRectF &) +{ + QPointF near = mapToScene(0, 0); + QPointF far = mapToScene(width(), height()); + QSizeF sz(far.x()-near.x(), far.y()-near.y()); + QRectF pr(near, sz); + + if (pr != m_pannedRect) { + if (pr.x() != m_pannedRect.x()) emit pannedContentsScrolled(); + m_pannedRect = pr; + emit pannedRectChanged(pr); + } +} + +void +Panned::slotSetPannedRect(QRectF pr) +{ + centerOn(pr.center()); +// setSceneRect(pr); +// m_pannedRect = pr; +} + +void +Panned::wheelEvent(QWheelEvent *ev) +{ + emit wheelEventReceived(ev); + QGraphicsView::wheelEvent(ev); +} + +void +Panned::slotEmulateWheelEvent(QWheelEvent *ev) +{ + QGraphicsView::wheelEvent(ev); +} + +void +Panned::leaveEvent(QEvent *) +{ + emit mouseLeaves(); +} diff -r bed7ab59f62e -r 4286836bb3c9 panned.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/panned.h Wed Nov 10 12:44:11 2010 +0000 @@ -0,0 +1,55 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + Copyright 2000-2010 the Rosegarden development team. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_PANNED_H_ +#define _RG_PANNED_H_ + +#include + +class QWheelEvent; +class QEvent; + +class Panned : public QGraphicsView +{ + Q_OBJECT + +public: + Panned(); + virtual ~Panned() { } + +signals: + void pannedRectChanged(QRectF); + void wheelEventReceived(QWheelEvent *); + void pannedContentsScrolled(); + void mouseLeaves(); + +public slots: + void slotSetPannedRect(QRectF); + void slotEmulateWheelEvent(QWheelEvent *ev); + +protected: + QRectF m_pannedRect; + + virtual void paintEvent(QPaintEvent *); + virtual void resizeEvent(QResizeEvent *); + virtual void drawForeground(QPainter *, const QRectF &); + virtual void wheelEvent(QWheelEvent *); + virtual void leaveEvent(QEvent *); +}; + +#endif + diff -r bed7ab59f62e -r 4286836bb3c9 panner.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/panner.cpp Wed Nov 10 12:44:11 2010 +0000 @@ -0,0 +1,226 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + Copyright 2000-2010 the Rosegarden development team. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "panner.h" +#include "panned.h" + +#include +#include +#include + +#include + +class PannerScene : public QGraphicsScene +{ +public: + friend class Panner; +}; + +Panner::Panner() : + m_clicked(false) +{ + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setOptimizationFlags(QGraphicsView::DontSavePainterState); + setMouseTracking(true); + setInteractive(false); +} + +void +Panner::setScene(QGraphicsScene *s) +{ + if (scene()) { + disconnect(scene(), SIGNAL(sceneRectChanged(const QRectF &)), + this, SLOT(slotSceneRectChanged(const QRectF &))); + } + QGraphicsView::setScene(s); + if (scene()) fitInView(sceneRect(), Qt::IgnoreAspectRatio); + m_cache = QPixmap(); + connect(scene(), SIGNAL(sceneRectChanged(const QRectF &)), + this, SLOT(slotSceneRectChanged(const QRectF &))); +} + +void +Panner::connectToPanned(Panned *p) +{ + connect(p, SIGNAL(pannedRectChanged(QRectF)), + this, SLOT(slotSetPannedRect(QRectF))); + + connect(this, SIGNAL(pannedRectChanged(QRectF)), + p, SLOT(slotSetPannedRect(QRectF))); +} + +void +Panner::slotSetPannedRect(QRectF rect) +{ + m_pannedRect = rect; + viewport()->update(); +} + +void +Panner::resizeEvent(QResizeEvent *) +{ + if (scene()) fitInView(sceneRect(), Qt::IgnoreAspectRatio); + m_cache = QPixmap(); +} + +void +Panner::slotSceneRectChanged(const QRectF &newRect) +{ + if (!scene()) return; // spurious + fitInView(newRect, Qt::IgnoreAspectRatio); + m_cache = QPixmap(); + viewport()->update(); +} + +void +Panner::paintEvent(QPaintEvent *e) +{ + QPaintEvent *e2 = new QPaintEvent(e->region().boundingRect()); + QGraphicsView::paintEvent(e2); + + QPainter paint; + paint.begin(viewport()); + paint.setClipRegion(e->region()); + + QPainterPath path; + path.addRect(rect()); + path.addPolygon(mapFromScene(m_pannedRect)); + + QColor c(QColor::fromHsv(211, 194, 238)); + c.setAlpha(80); + paint.setPen(Qt::NoPen); + paint.setBrush(c); + paint.drawPath(path); + + paint.setBrush(Qt::NoBrush); + paint.setPen(QPen(QColor::fromHsv(211, 194, 238), 0)); + paint.drawConvexPolygon(mapFromScene(m_pannedRect)); + + paint.end(); + + emit pannerChanged(m_pannedRect); +} + +void +Panner::updateScene(const QList &rects) +{ + if (!m_cache.isNull()) m_cache = QPixmap(); + QGraphicsView::updateScene(rects); +} + +void +Panner::drawItems(QPainter *painter, int numItems, + QGraphicsItem *items[], + const QStyleOptionGraphicsItem options[]) +{ + if (m_cache.size() != viewport()->size()) { + + QGraphicsScene *s = scene(); + if (!s) return; + PannerScene *ps = static_cast(s); + + m_cache = QPixmap(viewport()->size()); + m_cache.fill(Qt::transparent); + QPainter cachePainter; + cachePainter.begin(&m_cache); + cachePainter.setTransform(viewportTransform()); + ps->drawItems(&cachePainter, numItems, items, options); + cachePainter.end(); + } + + painter->save(); + painter->setTransform(QTransform()); + painter->drawPixmap(0, 0, m_cache); + painter->restore(); +} + +void +Panner::mousePressEvent(QMouseEvent *e) +{ + if (e->button() != Qt::LeftButton) { + QGraphicsView::mouseDoubleClickEvent(e); + return; + } + m_clicked = true; + m_clickedRect = m_pannedRect; + m_clickedPoint = e->pos(); +} + +void +Panner::mouseDoubleClickEvent(QMouseEvent *e) +{ + if (e->button() != Qt::LeftButton) { + QGraphicsView::mouseDoubleClickEvent(e); + return; + } + + moveTo(e->pos()); +} + +void +Panner::mouseMoveEvent(QMouseEvent *e) +{ + if (!m_clicked) return; + QPointF cp = mapToScene(m_clickedPoint); + QPointF mp = mapToScene(e->pos()); + QPointF delta = mp - cp; + QRectF nr = m_clickedRect; + nr.translate(delta); + slotSetPannedRect(nr); + emit pannedRectChanged(m_pannedRect); + viewport()->update(); +} + +void +Panner::mouseReleaseEvent(QMouseEvent *e) +{ + if (e->button() != Qt::LeftButton) { + QGraphicsView::mouseDoubleClickEvent(e); + return; + } + + if (m_clicked) { + mouseMoveEvent(e); + } + + m_clicked = false; + viewport()->update(); +} + +void +Panner::wheelEvent(QWheelEvent *e) +{ + if (e->delta() > 0) { + emit zoomOut(); + } else { + emit zoomIn(); + } +} + +void +Panner::moveTo(QPoint p) +{ + QPointF sp = mapToScene(p); + QRectF nr = m_pannedRect; + double d = sp.x() - nr.center().x(); + nr.translate(d, 0); + slotSetPannedRect(nr); + emit pannedRectChanged(m_pannedRect); + viewport()->update(); +} + diff -r bed7ab59f62e -r 4286836bb3c9 panner.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/panner.h Wed Nov 10 12:44:11 2010 +0000 @@ -0,0 +1,75 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + Copyright 2000-2010 the Rosegarden development team. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_PANNER_H_ +#define _RG_PANNER_H_ + +#include + +class Panned; + +class Panner : public QGraphicsView +{ + Q_OBJECT + +public: + Panner(); + virtual ~Panner() { } + + virtual void setScene(QGraphicsScene *); + + void connectToPanned(Panned *p); + +signals: + void pannedRectChanged(QRectF); + void pannerChanged(QRectF); + void zoomIn(); + void zoomOut(); + +public slots: + void slotSetPannedRect(QRectF); + +protected slots: + void slotSceneRectChanged(const QRectF &); + +protected: + QRectF m_pannedRect; + + void moveTo(QPoint); + + virtual void paintEvent(QPaintEvent *); + virtual void mousePressEvent(QMouseEvent *e); + virtual void mouseMoveEvent(QMouseEvent *e); + virtual void mouseReleaseEvent(QMouseEvent *e); + virtual void mouseDoubleClickEvent(QMouseEvent *e); + virtual void wheelEvent(QWheelEvent *e); + + virtual void resizeEvent(QResizeEvent *); + + virtual void updateScene(const QList &); + virtual void drawItems(QPainter *, int, QGraphicsItem *[], + const QStyleOptionGraphicsItem []); + + bool m_clicked; + QRectF m_clickedRect; + QPoint m_clickedPoint; + + QPixmap m_cache; +}; + +#endif +