Chris@57
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
cannam@45
|
2
|
cannam@45
|
3 /*
|
Chris@57
|
4 EasyMercurial
|
Chris@57
|
5
|
Chris@57
|
6 Based on HgExplorer by Jari Korhonen
|
Chris@57
|
7 Copyright (c) 2010 Jari Korhonen
|
Chris@644
|
8 Copyright (c) 2013 Chris Cannam
|
Chris@644
|
9 Copyright (c) 2013 Queen Mary, University of London
|
Chris@57
|
10
|
cannam@45
|
11 This program is free software; you can redistribute it and/or
|
cannam@45
|
12 modify it under the terms of the GNU General Public License as
|
cannam@45
|
13 published by the Free Software Foundation; either version 2 of the
|
cannam@45
|
14 License, or (at your option) any later version. See the file
|
cannam@45
|
15 COPYING included with this distribution for more information.
|
cannam@45
|
16 */
|
cannam@45
|
17
|
cannam@45
|
18 #include "panner.h"
|
cannam@45
|
19 #include "panned.h"
|
Chris@113
|
20 #include "debug.h"
|
cannam@45
|
21
|
cannam@45
|
22 #include <QPolygon>
|
cannam@45
|
23 #include <QMouseEvent>
|
cannam@45
|
24 #include <QColor>
|
cannam@45
|
25
|
cannam@45
|
26 #include <iostream>
|
cannam@45
|
27
|
cannam@45
|
28 class PannerScene : public QGraphicsScene
|
cannam@45
|
29 {
|
cannam@45
|
30 public:
|
cannam@45
|
31 friend class Panner;
|
cannam@45
|
32 };
|
cannam@45
|
33
|
cannam@45
|
34 Panner::Panner() :
|
Chris@389
|
35 m_clicked(false),
|
Chris@389
|
36 m_moved(false)
|
cannam@45
|
37 {
|
cannam@45
|
38 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
cannam@45
|
39 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
Chris@48
|
40 setOptimizationFlags(QGraphicsView::DontSavePainterState |
|
Chris@48
|
41 QGraphicsView::IndirectPainting);
|
cannam@45
|
42 setMouseTracking(true);
|
cannam@45
|
43 setInteractive(false);
|
cannam@45
|
44 }
|
cannam@45
|
45
|
cannam@45
|
46 void
|
Chris@113
|
47 Panner::fit(QRectF r)
|
Chris@113
|
48 {
|
Chris@113
|
49 Qt::AspectRatioMode m = Qt::IgnoreAspectRatio;
|
Chris@113
|
50 if (height() > width()) {
|
Chris@113
|
51 // Our panner is vertical; if the source is not tall,
|
Chris@113
|
52 // don't stretch it to fit in the height, it'd look weird
|
Chris@113
|
53 if (r.height() < height() * 2) {
|
Chris@113
|
54 m = Qt::KeepAspectRatio;
|
Chris@113
|
55 }
|
Chris@113
|
56 } else {
|
Chris@113
|
57 // Similarly, but horizontal
|
Chris@113
|
58 if (r.width() < width() * 2) {
|
Chris@113
|
59 m = Qt::KeepAspectRatio;
|
Chris@113
|
60 }
|
Chris@113
|
61 }
|
Chris@113
|
62 DEBUG << "Panner: fit mode " << m << endl;
|
Chris@113
|
63 fitInView(r, m);
|
Chris@113
|
64 }
|
Chris@113
|
65
|
Chris@113
|
66 void
|
cannam@45
|
67 Panner::setScene(QGraphicsScene *s)
|
cannam@45
|
68 {
|
cannam@45
|
69 if (scene()) {
|
Chris@133
|
70 disconnect(scene(), SIGNAL(changed(const QList<QRectF> &)),
|
Chris@132
|
71 this, SLOT(slotSceneChanged(const QList<QRectF> &)));
|
cannam@45
|
72 disconnect(scene(), SIGNAL(sceneRectChanged(const QRectF &)),
|
cannam@45
|
73 this, SLOT(slotSceneRectChanged(const QRectF &)));
|
cannam@45
|
74 }
|
cannam@45
|
75 QGraphicsView::setScene(s);
|
Chris@136
|
76 m_cache = QPixmap();
|
Chris@113
|
77 if (scene()) {
|
Chris@113
|
78 QRectF r = sceneRect();
|
Chris@113
|
79 DEBUG << "scene rect: " << r << ", my rect " << rect() << endl;
|
Chris@113
|
80 fit(r);
|
Chris@136
|
81 connect(scene(), SIGNAL(changed(const QList<QRectF> &)),
|
Chris@136
|
82 this, SLOT(slotSceneChanged(const QList<QRectF> &)));
|
Chris@136
|
83 connect(scene(), SIGNAL(sceneRectChanged(const QRectF &)),
|
Chris@136
|
84 this, SLOT(slotSceneRectChanged(const QRectF &)));
|
Chris@113
|
85 }
|
cannam@45
|
86 }
|
cannam@45
|
87
|
cannam@45
|
88 void
|
cannam@45
|
89 Panner::connectToPanned(Panned *p)
|
cannam@45
|
90 {
|
cannam@45
|
91 connect(p, SIGNAL(pannedRectChanged(QRectF)),
|
cannam@45
|
92 this, SLOT(slotSetPannedRect(QRectF)));
|
cannam@45
|
93
|
cannam@45
|
94 connect(this, SIGNAL(pannedRectChanged(QRectF)),
|
cannam@45
|
95 p, SLOT(slotSetPannedRect(QRectF)));
|
Chris@53
|
96
|
Chris@53
|
97 connect(this, SIGNAL(zoomIn()),
|
Chris@53
|
98 p, SLOT(zoomIn()));
|
Chris@53
|
99
|
Chris@53
|
100 connect(this, SIGNAL(zoomOut()),
|
Chris@53
|
101 p, SLOT(zoomOut()));
|
cannam@45
|
102 }
|
cannam@45
|
103
|
cannam@45
|
104 void
|
cannam@45
|
105 Panner::slotSetPannedRect(QRectF rect)
|
cannam@45
|
106 {
|
cannam@45
|
107 m_pannedRect = rect;
|
cannam@45
|
108 viewport()->update();
|
cannam@45
|
109 }
|
cannam@45
|
110
|
cannam@45
|
111 void
|
cannam@45
|
112 Panner::resizeEvent(QResizeEvent *)
|
cannam@45
|
113 {
|
Chris@250
|
114 DEBUG << "Panner::resizeEvent" << endl;
|
Chris@113
|
115 if (scene()) fit(sceneRect());
|
cannam@45
|
116 m_cache = QPixmap();
|
cannam@45
|
117 }
|
cannam@45
|
118
|
cannam@45
|
119 void
|
cannam@45
|
120 Panner::slotSceneRectChanged(const QRectF &newRect)
|
cannam@45
|
121 {
|
Chris@250
|
122 DEBUG << "Panner::slotSceneRectChanged" << endl;
|
cannam@45
|
123 if (!scene()) return; // spurious
|
Chris@113
|
124 fit(newRect);
|
cannam@45
|
125 m_cache = QPixmap();
|
cannam@45
|
126 viewport()->update();
|
cannam@45
|
127 }
|
cannam@45
|
128
|
cannam@45
|
129 void
|
Chris@132
|
130 Panner::slotSceneChanged(const QList<QRectF> &)
|
Chris@132
|
131 {
|
Chris@250
|
132 DEBUG << "Panner::slotSceneChanged" << endl;
|
Chris@132
|
133 if (!scene()) return; // spurious
|
Chris@132
|
134 m_cache = QPixmap();
|
Chris@132
|
135 viewport()->update();
|
Chris@132
|
136 }
|
Chris@132
|
137
|
Chris@132
|
138 void
|
cannam@45
|
139 Panner::paintEvent(QPaintEvent *e)
|
cannam@45
|
140 {
|
cannam@45
|
141 QPaintEvent *e2 = new QPaintEvent(e->region().boundingRect());
|
cannam@45
|
142 QGraphicsView::paintEvent(e2);
|
cannam@45
|
143
|
cannam@45
|
144 QPainter paint;
|
cannam@45
|
145 paint.begin(viewport());
|
cannam@45
|
146 paint.setClipRegion(e->region());
|
cannam@45
|
147
|
cannam@45
|
148 QPainterPath path;
|
cannam@45
|
149 path.addRect(rect());
|
cannam@45
|
150 path.addPolygon(mapFromScene(m_pannedRect));
|
cannam@45
|
151
|
cannam@45
|
152 QColor c(QColor::fromHsv(211, 194, 238));
|
cannam@45
|
153 c.setAlpha(80);
|
cannam@45
|
154 paint.setPen(Qt::NoPen);
|
cannam@45
|
155 paint.setBrush(c);
|
cannam@45
|
156 paint.drawPath(path);
|
cannam@45
|
157
|
cannam@45
|
158 paint.setBrush(Qt::NoBrush);
|
cannam@45
|
159 paint.setPen(QPen(QColor::fromHsv(211, 194, 238), 0));
|
cannam@45
|
160 paint.drawConvexPolygon(mapFromScene(m_pannedRect));
|
cannam@45
|
161
|
cannam@45
|
162 paint.end();
|
cannam@45
|
163
|
cannam@45
|
164 emit pannerChanged(m_pannedRect);
|
cannam@45
|
165 }
|
cannam@45
|
166
|
cannam@45
|
167 void
|
cannam@45
|
168 Panner::updateScene(const QList<QRectF> &rects)
|
cannam@45
|
169 {
|
Chris@250
|
170 DEBUG << "Panner::updateScene" << endl;
|
Chris@250
|
171 // if (!m_cache.isNull()) m_cache = QPixmap();
|
cannam@45
|
172 QGraphicsView::updateScene(rects);
|
cannam@45
|
173 }
|
cannam@45
|
174
|
cannam@45
|
175 void
|
cannam@45
|
176 Panner::drawItems(QPainter *painter, int numItems,
|
cannam@45
|
177 QGraphicsItem *items[],
|
cannam@45
|
178 const QStyleOptionGraphicsItem options[])
|
cannam@45
|
179 {
|
cannam@45
|
180 if (m_cache.size() != viewport()->size()) {
|
cannam@45
|
181
|
Chris@131
|
182 DEBUG << "Panner: cache size " << m_cache.size() << " != viewport size " << viewport()->size() << ": recreating cache" << endl;
|
Chris@48
|
183
|
cannam@45
|
184 QGraphicsScene *s = scene();
|
cannam@45
|
185 if (!s) return;
|
cannam@45
|
186 PannerScene *ps = static_cast<PannerScene *>(s);
|
cannam@45
|
187
|
cannam@45
|
188 m_cache = QPixmap(viewport()->size());
|
cannam@45
|
189 m_cache.fill(Qt::transparent);
|
cannam@45
|
190 QPainter cachePainter;
|
cannam@45
|
191 cachePainter.begin(&m_cache);
|
cannam@45
|
192 cachePainter.setTransform(viewportTransform());
|
cannam@45
|
193 ps->drawItems(&cachePainter, numItems, items, options);
|
cannam@45
|
194 cachePainter.end();
|
Chris@250
|
195
|
Chris@250
|
196 DEBUG << "done" << endl;
|
cannam@45
|
197 }
|
cannam@45
|
198
|
cannam@45
|
199 painter->save();
|
cannam@45
|
200 painter->setTransform(QTransform());
|
cannam@45
|
201 painter->drawPixmap(0, 0, m_cache);
|
cannam@45
|
202 painter->restore();
|
cannam@45
|
203 }
|
cannam@45
|
204
|
cannam@45
|
205 void
|
cannam@45
|
206 Panner::mousePressEvent(QMouseEvent *e)
|
cannam@45
|
207 {
|
cannam@45
|
208 if (e->button() != Qt::LeftButton) {
|
cannam@45
|
209 QGraphicsView::mouseDoubleClickEvent(e);
|
cannam@45
|
210 return;
|
cannam@45
|
211 }
|
cannam@45
|
212 m_clicked = true;
|
Chris@389
|
213 m_moved = false;
|
cannam@45
|
214 m_clickedRect = m_pannedRect;
|
cannam@45
|
215 m_clickedPoint = e->pos();
|
cannam@45
|
216 }
|
cannam@45
|
217
|
cannam@45
|
218 void
|
cannam@45
|
219 Panner::mouseDoubleClickEvent(QMouseEvent *e)
|
cannam@45
|
220 {
|
cannam@45
|
221 if (e->button() != Qt::LeftButton) {
|
cannam@45
|
222 QGraphicsView::mouseDoubleClickEvent(e);
|
cannam@45
|
223 return;
|
cannam@45
|
224 }
|
cannam@45
|
225
|
cannam@45
|
226 moveTo(e->pos());
|
cannam@45
|
227 }
|
cannam@45
|
228
|
cannam@45
|
229 void
|
cannam@45
|
230 Panner::mouseMoveEvent(QMouseEvent *e)
|
cannam@45
|
231 {
|
cannam@45
|
232 if (!m_clicked) return;
|
cannam@45
|
233 QPointF cp = mapToScene(m_clickedPoint);
|
cannam@45
|
234 QPointF mp = mapToScene(e->pos());
|
cannam@45
|
235 QPointF delta = mp - cp;
|
Chris@389
|
236 if (!m_moved) {
|
Chris@389
|
237 if ((m_clickedPoint - e->pos()).manhattanLength() > 2) {
|
Chris@389
|
238 m_moved = true;
|
Chris@389
|
239 } else {
|
Chris@389
|
240 return;
|
Chris@389
|
241 }
|
Chris@389
|
242 }
|
cannam@45
|
243 QRectF nr = m_clickedRect;
|
cannam@45
|
244 nr.translate(delta);
|
Chris@173
|
245 m_pannedRect = nr;
|
cannam@45
|
246 emit pannedRectChanged(m_pannedRect);
|
cannam@45
|
247 viewport()->update();
|
cannam@45
|
248 }
|
cannam@45
|
249
|
cannam@45
|
250 void
|
cannam@45
|
251 Panner::mouseReleaseEvent(QMouseEvent *e)
|
cannam@45
|
252 {
|
cannam@45
|
253 if (e->button() != Qt::LeftButton) {
|
cannam@45
|
254 QGraphicsView::mouseDoubleClickEvent(e);
|
cannam@45
|
255 return;
|
cannam@45
|
256 }
|
cannam@45
|
257
|
cannam@45
|
258 if (m_clicked) {
|
Chris@389
|
259 if (m_moved) {
|
Chris@389
|
260 mouseMoveEvent(e);
|
Chris@389
|
261 } else {
|
Chris@389
|
262 moveTo(e->pos());
|
Chris@389
|
263 }
|
cannam@45
|
264 }
|
cannam@45
|
265
|
cannam@45
|
266 m_clicked = false;
|
cannam@45
|
267 viewport()->update();
|
cannam@45
|
268 }
|
cannam@45
|
269
|
cannam@45
|
270 void
|
cannam@45
|
271 Panner::wheelEvent(QWheelEvent *e)
|
cannam@45
|
272 {
|
Chris@53
|
273 int d = e->delta();
|
Chris@53
|
274 if (d > 0) {
|
Chris@53
|
275 while (d > 0) {
|
Chris@53
|
276 emit zoomOut();
|
Chris@53
|
277 d -= 120;
|
Chris@53
|
278 }
|
cannam@45
|
279 } else {
|
Chris@53
|
280 while (d < 0) {
|
Chris@53
|
281 emit zoomIn();
|
Chris@53
|
282 d += 120;
|
Chris@53
|
283 }
|
cannam@45
|
284 }
|
cannam@45
|
285 }
|
cannam@45
|
286
|
cannam@45
|
287 void
|
cannam@45
|
288 Panner::moveTo(QPoint p)
|
cannam@45
|
289 {
|
cannam@45
|
290 QPointF sp = mapToScene(p);
|
cannam@45
|
291 QRectF nr = m_pannedRect;
|
Chris@389
|
292 double dx = sp.x() - nr.center().x();
|
Chris@389
|
293 double dy = sp.y() - nr.center().y();
|
Chris@389
|
294 nr.translate(dx, dy);
|
cannam@45
|
295 slotSetPannedRect(nr);
|
cannam@45
|
296 emit pannedRectChanged(m_pannedRect);
|
cannam@45
|
297 viewport()->update();
|
cannam@45
|
298 }
|
cannam@45
|
299
|