Human-friendly links
[GPXSee.git] / src / graphview.cpp
blobd27e2ba524d1095a7e9b2f7b3b111cd318d4a0aa
1 #include <QGraphicsScene>
2 #include <QEvent>
3 #include <QMouseEvent>
4 #include <QPaintEngine>
5 #include <QPaintDevice>
6 #include "opengl.h"
7 #include "config.h"
8 #include "axisitem.h"
9 #include "slideritem.h"
10 #include "sliderinfoitem.h"
11 #include "infoitem.h"
12 #include "griditem.h"
13 #include "graph.h"
14 #include "graphitem.h"
15 #include "pathitem.h"
16 #include "graphview.h"
19 #define MARGIN 10.0
21 GraphView::GraphView(QWidget *parent)
22 : QGraphicsView(parent)
24 _scene = new QGraphicsScene(this);
25 setScene(_scene);
27 setBackgroundBrush(QBrush(Qt::white));
28 setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
29 setRenderHint(QPainter::Antialiasing, true);
30 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
31 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
33 _xAxis = new AxisItem(AxisItem::X);
34 _xAxis->setZValue(2.0);
35 _yAxis = new AxisItem(AxisItem::Y);
36 _yAxis->setZValue(2.0);
37 _slider = new SliderItem();
38 _slider->setZValue(3.0);
39 _sliderInfo = new SliderInfoItem(_slider);
40 _sliderInfo->setZValue(3.0);
41 _info = new InfoItem();
42 _grid = new GridItem();
44 connect(_slider, SIGNAL(positionChanged(const QPointF&)), this,
45 SLOT(emitSliderPositionChanged(const QPointF&)));
47 _width = 1;
49 _xScale = 1;
50 _yScale = 1;
51 _yOffset = 0;
53 _precision = 0;
54 _minYRange = 0.01;
56 _sliderPos = 0;
58 _units = Metric;
59 _graphType = Distance;
60 _xLabel = tr("Distance");
63 GraphView::~GraphView()
65 if (_xAxis->scene() != _scene)
66 delete _xAxis;
67 if (_yAxis->scene() != _scene)
68 delete _yAxis;
69 if (_slider->scene() != _scene)
70 delete _slider;
71 if (_info->scene() != _scene)
72 delete _info;
73 if (_grid->scene() != _scene)
74 delete _grid;
76 for (int i = 0; i < _graphs.count(); i++)
77 if (_graphs.at(i)->scene() != _scene)
78 delete _graphs[i];
81 void GraphView::createXLabel()
83 _xAxis->setLabel(QString("%1 [%2]").arg(_xLabel).arg(_xUnits));
86 void GraphView::createYLabel()
88 _yAxis->setLabel(QString("%1 [%2]").arg(_yLabel).arg(_yUnits));
91 void GraphView::setYLabel(const QString &label)
93 _yLabel = label;
94 createYLabel();
97 void GraphView::setYUnits(const QString &units)
99 _yUnits = units;
100 createYLabel();
103 void GraphView::setXUnits()
105 if (_graphType == Distance) {
106 if (_units == Metric) {
107 if (bounds().width() < KMINM) {
108 _xUnits = tr("m");
109 _xScale = 1;
110 } else {
111 _xUnits = tr("km");
112 _xScale = M2KM;
114 } else {
115 if (bounds().width() < MIINM) {
116 _xUnits = tr("ft");
117 _xScale = M2FT;
118 } else {
119 _xUnits = tr("mi");
120 _xScale = M2MI;
123 } else {
124 if (bounds().width() < MININS) {
125 _xUnits = tr("s");
126 _xScale = 1;
127 } else if (bounds().width() < HINS) {
128 _xUnits = tr("min");
129 _xScale = MIN2S;
130 } else {
131 _xUnits = tr("h");
132 _xScale = H2S;
136 createXLabel();
139 void GraphView::setUnits(Units units)
141 _units = units;
142 setXUnits();
145 void GraphView::setGraphType(GraphType type)
147 _graphType = type;
148 _bounds = QRectF();
150 for (int i = 0; i < _graphs.count(); i++) {
151 _graphs.at(i)->setGraphType(type);
152 if (_graphs.at(i)->scene() == _scene)
153 _bounds |= _graphs.at(i)->bounds();
156 if (type == Distance)
157 _xLabel = tr("Distance");
158 else
159 _xLabel = tr("Time");
160 setXUnits();
162 redraw();
165 void GraphView::showGrid(bool show)
167 _grid->setVisible(show);
170 void GraphView::loadGraph(const Graph &graph, PathItem *path, int id)
172 if (graph.size() < 2)
173 return;
175 GraphItem *gi = new GraphItem(graph);
176 gi->setGraphType(_graphType);
177 gi->setId(id);
178 gi->setColor(_palette.nextColor());
179 gi->setWidth(_width);
181 connect(this, SIGNAL(sliderPositionChanged(qreal)), gi,
182 SLOT(emitSliderPositionChanged(qreal)));
183 connect(gi, SIGNAL(sliderPositionChanged(qreal)), path,
184 SLOT(moveMarker(qreal)));
185 connect(path, SIGNAL(selected(bool)), gi, SLOT(selected(bool)));
187 _graphs.append(gi);
189 if (!_hide.contains(id)) {
190 _visible.append(gi);
191 _scene->addItem(gi);
192 _bounds |= gi->bounds();
193 setXUnits();
197 void GraphView::removeItem(QGraphicsItem *item)
199 if (item->scene() == _scene)
200 _scene->removeItem(item);
203 void GraphView::addItem(QGraphicsItem *item)
205 if (item->scene() != _scene)
206 _scene->addItem(item);
209 void GraphView::showGraph(bool show, int id)
211 if (show)
212 _hide.remove(id);
213 else
214 _hide.insert(id);
216 _visible.clear();
217 _bounds = QRectF();
218 for (int i = 0; i < _graphs.count(); i++) {
219 GraphItem* gi = _graphs.at(i);
220 if (_hide.contains(gi->id()))
221 removeItem(gi);
222 else {
223 addItem(gi);
224 _visible.append(gi);
225 _bounds |= gi->bounds();
230 void GraphView::redraw()
232 redraw(viewport()->size() - QSizeF(MARGIN, MARGIN));
235 QRectF GraphView::bounds() const
237 QRectF br(_bounds);
238 br.moveTopLeft(QPointF(br.left(), -br.top() - br.height()));
239 return br;
242 void GraphView::redraw(const QSizeF &size)
244 QRectF r;
245 QSizeF mx, my;
246 RangeF rx, ry;
247 qreal sx, sy;
250 if (_visible.isEmpty() || _bounds.isNull()) {
251 removeItem(_xAxis);
252 removeItem(_yAxis);
253 removeItem(_slider);
254 removeItem(_info);
255 removeItem(_grid);
256 _scene->setSceneRect(QRectF());
257 return;
260 addItem(_xAxis);
261 addItem(_yAxis);
262 addItem(_slider);
263 addItem(_info);
264 addItem(_grid);
266 rx = RangeF(bounds().left() * _xScale, bounds().right() * _xScale);
267 ry = RangeF(bounds().top() * _yScale + _yOffset, bounds().bottom() * _yScale
268 + _yOffset);
269 if (ry.size() < _minYRange)
270 ry.resize(_minYRange);
272 _xAxis->setRange(rx);
273 _yAxis->setRange(ry);
274 mx = _xAxis->margin();
275 my = _yAxis->margin();
277 r = _bounds;
278 if (r.height() < _minYRange)
279 r.adjust(0, -(_minYRange/2 - r.height()/2), 0,
280 _minYRange/2 - r.height()/2);
282 sx = (size.width() - (my.width() + mx.width())) / r.width();
283 sy = (size.height() - (mx.height() + my.height())
284 - _info->boundingRect().height()) / r.height();
286 for (int i = 0; i < _visible.size(); i++)
287 _visible.at(i)->setScale(sx, sy);
289 QPointF p(r.left() * sx, r.top() * sy);
290 QSizeF s(r.width() * sx, r.height() * sy);
291 r = QRectF(p, s);
292 if (r.height() < _minYRange * sy)
293 r.adjust(0, -(_minYRange/2 * sy - r.height()/2), 0,
294 (_minYRange/2) * sy - r.height()/2);
296 _xAxis->setSize(r.width());
297 _yAxis->setSize(r.height());
298 _xAxis->setPos(r.bottomLeft());
299 _yAxis->setPos(r.bottomLeft());
301 _grid->setSize(r.size());
302 _grid->setTicks(_xAxis->ticks(), _yAxis->ticks());
303 _grid->setPos(r.bottomLeft());
305 _slider->setArea(r);
306 updateSliderPosition();
308 r |= _xAxis->sceneBoundingRect();
309 r |= _yAxis->sceneBoundingRect();
310 _info->setPos(r.topLeft() + QPointF(r.width()/2
311 - _info->boundingRect().width()/2, -_info->boundingRect().height()));
313 _scene->setSceneRect(_scene->itemsBoundingRect());
316 void GraphView::resizeEvent(QResizeEvent *)
318 redraw();
321 void GraphView::mousePressEvent(QMouseEvent *e)
323 if (e->button() == Qt::LeftButton)
324 newSliderPosition(mapToScene(e->pos()));
326 QGraphicsView::mousePressEvent(e);
329 void GraphView::plot(QPainter *painter, const QRectF &target)
331 qreal ratio = painter->paintEngine()->paintDevice()->logicalDpiX()
332 / SCREEN_DPI;
333 QSizeF canvas = QSizeF(target.width() / ratio, target.height() / ratio);
335 setUpdatesEnabled(false);
336 redraw(canvas);
337 if (_slider->pos().x() == _slider->area().left())
338 _slider->hide();
339 _scene->render(painter, target);
340 _slider->show();
341 redraw();
342 setUpdatesEnabled(true);
345 void GraphView::clear()
347 _slider->clear();
348 _info->clear();
350 for (int i = 0; i < _graphs.count(); i++)
351 delete _graphs[i];
353 _graphs.clear();
354 _visible.clear();
355 _palette.reset();
357 _bounds = QRectF();
358 _sliderPos = 0;
360 _scene->setSceneRect(0, 0, 0, 0);
363 void GraphView::updateSliderPosition()
365 if (bounds().width() <= 0)
366 return;
368 if (_sliderPos <= bounds().right() && _sliderPos >= bounds().left()) {
369 _slider->setPos((_sliderPos / bounds().width())
370 * _slider->area().width(), _slider->area().bottom());
371 _slider->setVisible(!_visible.isEmpty());
372 } else {
373 _slider->setPos(_slider->area().left(), _slider->area().bottom());
374 _slider->setVisible(false);
377 updateSliderInfo();
380 void GraphView::updateSliderInfo()
382 _sliderInfo->setVisible(_visible.count() == 1);
383 if (!_sliderInfo->isVisible())
384 return;
386 QRectF br(_visible.first()->bounds());
387 if (br.height() < _minYRange)
388 br.adjust(0, -(_minYRange/2 - br.height()/2), 0,
389 _minYRange/2 - br.height()/2);
391 qreal y = _visible.first()->yAtX(_sliderPos);
392 qreal r = (y - br.bottom()) / br.height();
394 qreal pos = (_sliderPos / bounds().width()) * _slider->area().width();
395 SliderInfoItem::Side s = (pos + _sliderInfo->boundingRect().width()
396 > _slider->area().right()) ? SliderInfoItem::Left : SliderInfoItem::Right;
398 _sliderInfo->setSide(s);
399 _sliderInfo->setPos(QPointF(0, _slider->boundingRect().height() * r));
400 _sliderInfo->setText(QString::number(-y * _yScale + _yOffset, 'f',
401 _precision));
404 void GraphView::emitSliderPositionChanged(const QPointF &pos)
406 if (_slider->area().width() <= 0)
407 return;
409 _sliderPos = (pos.x() / _slider->area().width()) * bounds().width();
410 _sliderPos = qMax(_sliderPos, bounds().left());
411 _sliderPos = qMin(_sliderPos, bounds().right());
412 updateSliderPosition();
414 emit sliderPositionChanged(_sliderPos);
417 void GraphView::setSliderPosition(qreal pos)
419 if (_visible.isEmpty())
420 return;
422 _sliderPos = pos;
423 updateSliderPosition();
426 void GraphView::newSliderPosition(const QPointF &pos)
428 if (_slider->area().contains(pos))
429 _slider->setPos(pos);
432 void GraphView::addInfo(const QString &key, const QString &value)
434 _info->insert(key, value);
437 void GraphView::clearInfo()
439 _info->clear();
442 void GraphView::setPalette(const Palette &palette)
444 _palette = palette;
445 _palette.reset();
447 for (int i = 0; i < _graphs.count(); i++)
448 _graphs.at(i)->setColor(_palette.nextColor());
451 void GraphView::setGraphWidth(int width)
453 _width = width;
455 for (int i = 0; i < _graphs.count(); i++)
456 _graphs.at(i)->setWidth(width);
459 void GraphView::useOpenGL(bool use)
461 if (use)
462 setViewport(new OPENGL_WIDGET);
463 else
464 setViewport(new QWidget);