1 /*********************************************************************************
2 * *
3 * copyright : (C) 2007 Theodore Kisner <tsk@humanityforward.org> *
4 * copyright : (C) 2000-2010 Barth Netterfield <netterfield@astro.utoronto.ca> *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 ********************************************************************************/
12
13 #include "plotrenderitem.h"
14
15 #include "plotitem.h"
16 #include "plotitemmanager.h"
17 #include "application.h"
18 #include "objectstore.h"
19 #include "updatemanager.h"
20 #include "sharedaxisboxitem.h"
21 #include "image.h"
22 #include "debug.h"
23 #include "applicationsettings.h"
24
25 #include <QTime>
26 #include <QMenu>
27 #include <QMainWindow>
28 #include <QGraphicsSceneHoverEvent>
29 #include <QGraphicsSceneMouseEvent>
30 #include <QGraphicsSceneContextMenuEvent>
31 #include <QKeyEvent>
32 #include <math.h>
33 #include <limits>
34
35
36 // #define CURVE_DRAWING_TIME
37
38 namespace Kst {
39
PlotRenderItem(PlotItem * parentItem)40 PlotRenderItem::PlotRenderItem(PlotItem *parentItem)
41 : ViewItem(parentItem->view()), _referencePointMode(false), _highlightPointActive(false), _invertHighlight(false) {
42
43 setTypeName(tr("Plot Render"));
44 setParentViewItem(parentItem);
45 setHasStaticGeometry(true);
46 setAllowedGripModes(0);
47 setAllowedGrips(0);
48
49 connect(parentItem, SIGNAL(geometryChanged()),
50 this, SLOT(updateGeometry()));
51 connect(parentItem, SIGNAL(updatePlotRect()),
52 this, SLOT(updateGeometry()));
53 connect(parentItem->view(), SIGNAL(viewModeChanged(View::ViewMode)),
54 this, SLOT(updateViewMode()));
55 connect(parentItem->referenceModeDisabled, SIGNAL(triggered()),
56 this, SLOT(referenceModeDisabled()));
57 connect(parentItem->referenceMode, SIGNAL(triggered()),
58 this, SLOT(referenceMode()));
59
60 updateGeometry(); //the initial rect
61 updateViewMode(); //the initial view
62
63 disconnect(this, SIGNAL(geometryChanged()), view(), SLOT(viewChanged()));
64 }
65
66
~PlotRenderItem()67 PlotRenderItem::~PlotRenderItem() {
68 }
69
70
plotItem() const71 PlotItem *PlotRenderItem::plotItem() const {
72 return static_cast<PlotItem*>(parentItem());
73 }
74
75
referenceMode()76 void PlotRenderItem::referenceMode() {
77 _referencePointMode = true;
78 if (_highlightPointActive) {
79 _referencePoint = _highlightPoint;
80 } else {
81 _referencePoint = plotItem()->mapToProjection(_lastPos);
82 }
83 update();
84 }
85
86
referenceModeDisabled()87 void PlotRenderItem::referenceModeDisabled() {
88 _referencePointMode = false;
89 }
90
91
setReferencePoint(const QPointF & point)92 void PlotRenderItem::setReferencePoint(const QPointF& point) {
93 _referencePointMode = true;
94 _referencePoint = point;
95 }
96
97
renderType() const98 PlotRenderItem::RenderType PlotRenderItem::renderType() const {
99 return _type;
100 }
101
102
setRenderType(PlotRenderItem::RenderType type)103 void PlotRenderItem::setRenderType(PlotRenderItem::RenderType type) {
104 _type = type;
105 }
106
107
plotRect() const108 QRectF PlotRenderItem::plotRect() const {
109 QRectF plotRect = rect();
110 plotRect = plotRect.normalized();
111 plotRect.moveTopLeft(QPointF(0.0, 0.0));
112 return plotRect;
113 }
114
115
projectionRect() const116 QRectF PlotRenderItem::projectionRect() const {
117 return plotItem()->projectionRect();
118 }
119
120
relationList() const121 RelationList PlotRenderItem::relationList() const {
122 return _relationList;
123 }
124
125
addRelation(RelationPtr relation)126 void PlotRenderItem::addRelation(RelationPtr relation) {
127 if (relation) {
128 _relationList.append(relation);
129 if (_relationList.count() == 1) {
130 if (plotItem()->xAxis()->axisZoomMode() != PlotAxis::FixedExpression) {
131 plotItem()->zoomXMaximum();
132 }
133 if (plotItem()->yAxis()->axisZoomMode() != PlotAxis::FixedExpression) {
134 plotItem()->zoomYMaximum();
135 }
136 plotItem()->xAxis()->setAxisReversed(relation->invertXHint());
137 plotItem()->yAxis()->setAxisReversed(relation->invertYHint());
138 }
139 }
140 }
141
142
removeRelation(RelationPtr relation)143 void PlotRenderItem::removeRelation(RelationPtr relation) {
144 if (relation) {
145 _relationList.removeAll(relation);
146 plotItem()->registerChange();
147 UpdateManager::self()->doUpdates(true);
148 }
149 }
150
151
clearRelations()152 void PlotRenderItem::clearRelations() {
153 _relationList.clear();
154 plotItem()->zoomMaximum();
155 }
156
setRelationsList(const RelationList & relations)157 void PlotRenderItem::setRelationsList(const RelationList &relations) {
158 _relationList.clear();
159
160 foreach (const RelationPtr & relation, relations) {
161 _relationList.append(relation);
162 }
163 plotItem()->registerChange();
164 UpdateManager::self()->doUpdates(true);
165 }
166
save(QXmlStreamWriter & xml)167 void PlotRenderItem::save(QXmlStreamWriter &xml) {
168 Q_UNUSED(xml);
169 }
170
171
saveInPlot(QXmlStreamWriter & xml)172 void PlotRenderItem::saveInPlot(QXmlStreamWriter &xml) {
173 xml.writeAttribute("name", typeName());
174 xml.writeAttribute("type", QVariant(_type).toString());
175 if (_referencePointMode) {
176 xml.writeStartElement("referencepoint");
177 xml.writeAttribute("x", QVariant(_referencePoint.x()).toString());
178 xml.writeAttribute("y", QVariant(_referencePoint.y()).toString());
179 xml.writeEndElement();
180 }
181 foreach (RelationPtr relation, relationList()) {
182 xml.writeStartElement("relation");
183 xml.writeAttribute("tag", relation->Name());
184 xml.writeEndElement();
185 }
186 QList<QGraphicsItem*> list = QGraphicsItem::childItems();
187 foreach (QGraphicsItem *item, list) {
188 ViewItem *viewItem = dynamic_cast<ViewItem*>(item);
189 if (!viewItem)
190 continue;
191
192 viewItem->save(xml);
193 }
194 }
195
196
configureFromXml(QXmlStreamReader & xml,ObjectStore * store)197 bool PlotRenderItem::configureFromXml(QXmlStreamReader &xml, ObjectStore *store) {
198 bool validTag = true;
199
200 QString primaryTag = xml.name().toString();
201 QXmlStreamAttributes attrs = xml.attributes();
202 QStringRef av;
203 av = attrs.value("type");
204 if (!av.isNull()) {
205 setRenderType((RenderType)av.toString().toInt());
206 }
207 QString expectedEnd;
208 while (!(xml.isEndElement() && (xml.name().toString() == primaryTag))) {
209 if (xml.isStartElement() && xml.name().toString() == "relation") {
210 expectedEnd = xml.name().toString();
211 attrs = xml.attributes();
212 QString tagName = attrs.value("tag").toString();
213 RelationPtr relation = kst_cast<Relation>(store->retrieveObject(tagName));
214 if (relation) {
215 addRelation(relation);
216 }
217 } else if (xml.isStartElement() && xml.name().toString() == "referencepoint") {
218 expectedEnd = xml.name().toString();
219 double x = 0, y = 0;
220 attrs = xml.attributes();
221 av = attrs.value("x");
222 if (!av.isNull()) {
223 x = av.toString().toDouble();
224 }
225 av = attrs.value("y");
226 if (!av.isNull()) {
227 y = av.toString().toDouble();
228 }
229 setReferencePoint(QPointF(x, y));
230 } else if (xml.isStartElement()) {
231 if (!parse(xml, validTag) && validTag) {
232 GraphicsFactory::parse(xml, store, view(), this);
233 }
234 } else if (xml.isEndElement()) {
235 if (xml.name().toString() != expectedEnd) {
236 validTag = false;
237 break;
238 }
239 }
240 xml.readNext();
241 }
242
243 return validTag;
244 }
245
246
addToMenuForContextEvent(QMenu & menu)247 void PlotRenderItem::addToMenuForContextEvent(QMenu &menu) {
248 plotItem()->addToMenuForContextEvent(menu);
249 }
250
251
paint(QPainter * painter)252 void PlotRenderItem::paint(QPainter *painter) {
253 if (!rect().isValid()) {
254 return;
255 }
256 if (plotItem()->maskedByMaximization()) {
257 return;
258 }
259
260 painter->setRenderHint(QPainter::Antialiasing, false);
261
262
263 #ifdef CURVE_DRAWING_TIME
264 QTime time;
265 time.start();
266 #endif
267
268 painter->save();
269
270 if (plotItem()->xAxis()->axisReversed()) {
271 painter->scale(-1, 1);
272 painter->translate(-1.0 * rect().right() - rect().left(), 0);
273 }
274 if (plotItem()->yAxis()->axisReversed()) {
275 painter->scale(1, -1);
276 painter->translate(0, -1.0 * rect().bottom() - rect().top());
277 }
278 painter->setClipRect(rect());
279 paintRelations(painter);
280
281 painter->restore();
282
283 if (!view()->isPrinting()) {
284 processHoverMoveEvent(_hoverPos, true);
285 }
286
287 paintReferencePoint(painter);
288 paintHighlightPoint(painter);
289
290 if (!view()->isPrinting()) {
291 if (_selectionRect.isValid()) {
292 painter->setPen(QPen(QBrush(Qt::black), 1.0, Qt::DotLine));
293 painter->drawRect(_selectionRect.rect());
294 }
295 }
296
297 #ifdef CURVE_DRAWING_TIME
298 int elapsed = time.elapsed();
299 qDebug()<<"curve drawing took" << elapsed << "to render.";
300 #endif
301 }
302
303
paintReferencePoint(QPainter * painter)304 void PlotRenderItem::paintReferencePoint(QPainter *painter) {
305 if (_referencePointMode && plotItem()->projectionRect().contains(_referencePoint)) {
306 QPointF point = plotItem()->mapToPlot(_referencePoint);
307 painter->save();
308 painter->setPen(QPen(QColor("gray"), 1));
309 painter->setRenderHint(QPainter::Antialiasing, ApplicationSettings::self()->antialiasPlots());
310 CurvePointSymbol::draw(7, painter, point.x(), point.y(), 7);
311 painter->restore();
312 }
313 }
314
315
paintHighlightPoint(QPainter * painter)316 void PlotRenderItem::paintHighlightPoint(QPainter *painter) {
317 if (_highlightPointActive && kstApp->mainWindow()->isHighlightPoint() && plotItem()->projectionRect().contains(_highlightPoint)) {
318 QPointF point = plotItem()->mapToPlot(_highlightPoint);
319 painter->save();
320 painter->setPen(QPen(QColor("gray"), 1));
321 painter->setBrush(Qt::SolidPattern);
322 QColor highlightColor(QColor(0, 0, 0, 127));
323 if (_invertHighlight) {
324 highlightColor = QColor(255, 255, 255, 127);
325 }
326 painter->setBrush(highlightColor);
327
328 painter->setRenderHint(QPainter::Antialiasing, ApplicationSettings::self()->antialiasPlots());
329
330 painter->drawEllipse(point, 3, 3);
331 painter->restore();
332 }
333 }
334
multiRenderItemLabel(bool isX) const335 QString PlotRenderItem::multiRenderItemLabel(bool isX) const {
336 QString units;
337 QString quantity;
338 LabelInfo label_info;
339 LabelInfo first_label_info;
340
341 units.clear();
342 quantity.clear();
343
344 bool nameSame = true;
345 bool unitsSame = true;
346 bool quantitySame = true;
347
348 int count;
349
350 count = relationList().size();
351 if (isX) {
352 first_label_info = relationList().at(0)->xLabelInfo();
353 } else {
354 first_label_info = relationList().at(0)->yLabelInfo();
355 }
356 for (int i=1; i<count; ++i) {
357 if (isX) {
358 label_info = relationList().at(i)->xLabelInfo();
359 } else {
360 label_info = relationList().at(i)->yLabelInfo();
361 }
362 if (label_info.name != first_label_info.name) {
363 nameSame = false;
364 }
365 if (!label_info.units.isEmpty()) {
366 if (units.isEmpty()) {
367 units = label_info.units;
368 } else {
369 if (label_info.units != units) {
370 unitsSame = false;
371 }
372 }
373 }
374 if (!label_info.quantity.isEmpty()) {
375 if (quantity.isEmpty()) {
376 quantity = label_info.quantity;
377 } else {
378 if (label_info.quantity != quantity) {
379 quantitySame = false;
380 }
381 }
382 }
383 }
384
385 if (nameSame && unitsSame && quantitySame) {
386 return label_info.singleRenderItemLabel();
387 }
388
389 if (!quantitySame) {
390 quantity.clear();
391 }
392 if (!unitsSame) {
393 units.clear();
394 }
395
396 if (!units.isEmpty() && !quantity.isEmpty()) {
397 return QString("%1 \\[%2\\]").arg(quantity).arg(units);
398 } else if (!units.isEmpty()) {
399 return units;
400 } else {
401 return quantity;
402 }
403 }
404
405
leftLabel() const406 QString PlotRenderItem::leftLabel() const {
407 if (relationList().size() == 1) {
408 return relationList().at(0)->yLabelInfo().singleRenderItemLabel();
409 } else if (relationList().size()>1) { // multiple curves: quantity [units]
410 return multiRenderItemLabel(false);
411 } else {
412 return QString();
413 }
414 }
415
416
bottomLabel() const417 QString PlotRenderItem::bottomLabel() const {
418 if (relationList().size() == 1) {
419 return relationList().at(0)->xLabelInfo().singleRenderItemLabel();
420 } else if (relationList().size()>1) { // multiple curves: quantity [units]
421 return multiRenderItemLabel(true);
422 } else {
423 return QString();
424 }
425 }
426
427
rightLabel() const428 QString PlotRenderItem::rightLabel() const {
429 // right labels should only be used where there is more than one
430 // projection in the plot...
431 return QString();
432 }
433
434
topLabel() const435 QString PlotRenderItem::topLabel() const {
436 if (relationList().size() == 1) {
437 LabelInfo label_info = relationList().at(0)->titleInfo();
438 QString label = label_info.singleRenderItemLabel();
439 if (label.isEmpty()) {
440 label_info = relationList().at(0)->yLabelInfo();
441 if (label_info.singleRenderItemLabel().isEmpty()) {
442 label = relationList().at(0)->descriptiveName();
443 } else if ((!label_info.name.isEmpty()) && (!label_info.quantity.isEmpty())) {
444 LabelInfo xlabel_info = relationList().at(0)->xLabelInfo();
445 if ((!xlabel_info.name.isEmpty()) && (!xlabel_info.quantity.isEmpty())) {
446 label = tr("%1 vs %2", "describes a plot. %1 is X axis. %2 is Y axis").arg(label_info.name).arg(xlabel_info.name);
447 } else {
448 label = label_info.name;
449 }
450 }
451 }
452 return label;
453 } else {
454 return QString();
455 }
456 }
457
458
keyPressEvent(QKeyEvent * event)459 void PlotRenderItem::keyPressEvent(QKeyEvent *event) {
460 if (view()->viewMode() != View::Data) {
461 event->ignore();
462 return;
463 }
464
465 Qt::KeyboardModifiers modifiers = 0;
466
467
468 if (event->key()== Qt::Key_Shift) {
469 modifiers |= Qt::ShiftModifier;
470 } else if (event->key() == Qt::Key_Control) {
471 modifiers |= Qt::ControlModifier;
472 }
473
474 setCursorsMode(_lastPos, modifiers);
475
476 ViewItem::keyPressEvent(event);
477
478 updateSelectionRect();
479 }
480
481
keyReleaseEvent(QKeyEvent * event)482 void PlotRenderItem::keyReleaseEvent(QKeyEvent *event) {
483 if (view()->viewMode() != View::Data) {
484 event->ignore();
485 return;
486 }
487 const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
488 if (modifiers & Qt::SHIFT || zoomOnlyMode() == View::ZoomOnlyY) {
489 //view()->setCursor(Qt::SizeVerCursor);
490 } else if (modifiers & Qt::CTRL || zoomOnlyMode() == View::ZoomOnlyX) {
491 //view()->setCursor(Qt::SizeHorCursor);
492 } else {
493 view()->setCursor(Qt::CrossCursor);
494 QList<PlotItem*> plots = sharedOrTiedPlots(true, true);
495 foreach (PlotItem *plot, plots) {
496 plot->renderItem()->resetSelectionRect();
497 }
498 }
499 ViewItem::keyReleaseEvent(event);
500 }
501
502
resetSelectionRect()503 void PlotRenderItem::resetSelectionRect() {
504 if (_selectionRect.isValid()) {
505 _selectionRect.reset();
506 updateCursor(_lastPos);
507 update();
508 }
509 }
510
511
updateSelectionRect()512 void PlotRenderItem::updateSelectionRect() {
513 if (_selectionRect.isValid()) {
514 update(); //FIXME should optimize instead of redrawing entire curve?
515 }
516 }
517
wheelEvent(QGraphicsSceneWheelEvent * event)518 void PlotRenderItem::wheelEvent(QGraphicsSceneWheelEvent *event) {
519 const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
520 bool zoom = false;
521 if (modifiers & Qt::SHIFT) {
522 if (event->delta()>0) {
523 plotItem()->zoomYIn();
524 } else {
525 plotItem()->zoomYOut();
526 }
527 zoom = true;
528 }
529 if (modifiers & Qt::CTRL){
530 if (event->delta()>0) {
531 plotItem()->zoomXIn();
532 } else {
533 plotItem()->zoomXOut();
534 }
535 zoom = true;
536 }
537
538 if (zoom) {
539 return;
540 }
541
542 if ((modifiers & Qt::ALT) || zoomOnlyMode() == View::ZoomOnlyY) {
543 if (event->delta()>0) {
544 plotItem()->zoomYDown();
545 } else {
546 plotItem()->zoomYUp();
547 }
548 } else {
549 if (event->delta()>0) {
550 plotItem()->zoomXLeft();
551 } else {
552 plotItem()->zoomXRight();
553 }
554 }
555 }
556
mouseMoveEvent(QGraphicsSceneMouseEvent * event)557 void PlotRenderItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
558 if (view()->viewMode() != View::Data) {
559 event->ignore();
560 return;
561 }
562
563 const QPointF p = event->pos();
564
565 qreal y = (p.y() - rect().bottom())/(rect().top()-rect().bottom())*(plotItem()->yMax()-plotItem()->yMin())+plotItem()->yMin();
566 y = qMin(y, plotItem()->yMax());
567 y = qMax(y, plotItem()->yMin());
568
569 qreal x = (p.x() - rect().left())/(rect().right()-rect().left())*(plotItem()->xMax()-plotItem()->xMin())+plotItem()->xMin();
570 x = qMin(x, plotItem()->xMax());
571 x = qMax(x, plotItem()->xMin());
572
573
574 const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
575 if (modifiers & Qt::SHIFT || zoomOnlyMode() == View::ZoomOnlyY) {
576 view()->setCursor(Qt::SizeVerCursor);
577 QList<PlotItem*> plots = sharedOrTiedPlots(false, true);
578 foreach (PlotItem *plot, plots) {
579 plot->renderItem()->dragYZoomMouseCursor(y);
580 }
581 } else if (modifiers & Qt::CTRL || zoomOnlyMode() == View::ZoomOnlyX) {
582 QList<PlotItem*> plots = sharedOrTiedPlots(true, false);
583 foreach (PlotItem *plot, plots) {
584 plot->renderItem()->dragXZoomMouseCursor(x);
585 }
586 } else {
587 _selectionRect.setTo(p);
588 }
589
590 updateSelectionRect();
591 }
592
593
mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)594 void PlotRenderItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) {
595 const QPointF point = plotItem()->mapToProjection(event->pos());
596 double range = 4.0*(plotItem()->xMax() - plotItem()->xMin())/double(rect().width());
597 double distance = 1000;
598 bool first = true;
599 RelationPtr closestRelation = 0;
600 foreach (RelationPtr relation, _relationList) {
601 double relationsDistance = relation->distanceToPoint(point.x(), range, point.y());
602 if (first) {
603 distance = relationsDistance;
604 closestRelation = relation;
605 first = false;
606 } else {
607 if (distance > relationsDistance) {
608 distance = relationsDistance;
609 closestRelation = relation;
610 }
611 }
612 }
613 if (closestRelation) {
614 closestRelation->showEditDialog();
615 }
616 }
617
618
mousePressEvent(QGraphicsSceneMouseEvent * event)619 void PlotRenderItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
620 if (view()->viewMode() != View::Data) {
621 event->ignore();
622 return;
623 }
624
625 if (event->button() == Qt::MidButton) {
626 plotItem()->zoomPrevious();
627 event->ignore();
628 }
629
630 const QPointF p = event->pos();
631 const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
632 if (modifiers & Qt::SHIFT || zoomOnlyMode() == View::ZoomOnlyY) {
633 view()->setCursor(Qt::SizeVerCursor);
634 _selectionRect.setFrom(QPointF(rect().left(), p.y()));
635 _selectionRect.setTo(QPointF(rect().right(), p.y()));
636 } else if (modifiers & Qt::CTRL || zoomOnlyMode() == View::ZoomOnlyX) {
637 view()->setCursor(Qt::SizeHorCursor);
638 _selectionRect.setFrom(QPointF(p.x(), rect().top()));
639 _selectionRect.setTo(QPointF(p.x(), rect().bottom()));
640 } else {
641 _selectionRect.setFrom(p);
642 }
643 }
644
645
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)646 void PlotRenderItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
647 if (view()->viewMode() != View::Data) {
648 event->ignore();
649 return;
650 }
651
652 updateCursor(event->pos());
653 const QRectF projection = plotItem()->mapToProjection(_selectionRect.rect());
654
655 QList<PlotItem*> plots = sharedOrTiedPlots(true, true);
656 foreach (PlotItem *plot, plots) {
657 plot->renderItem()->_selectionRect.reset();
658 }
659 const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
660 if (modifiers & Qt::SHIFT || zoomOnlyMode() == View::ZoomOnlyY) {
661 plotItem()->zoomYRange(projection);
662 } else if (modifiers & Qt::CTRL || zoomOnlyMode() == View::ZoomOnlyX) {
663 plotItem()->zoomXRange(projection);
664 } else {
665 plotItem()->zoomFixedExpression(projection);
666 }
667 }
668
hoverYZoomMouseCursor(qreal y)669 void PlotRenderItem::hoverYZoomMouseCursor(qreal y) {
670 qreal py;
671
672 py = (y-plotItem()->yMin())/(plotItem()->yMax() - plotItem()->yMin())*(rect().top()-rect().bottom()) + rect().bottom();
673 py = qMin(py, rect().bottom());
674 py = qMax(py, rect().top());
675
676 _selectionRect.setFrom(QPointF(rect().left(), py));
677 _selectionRect.setTo(QPointF(rect().right(), py));
678
679 //qDebug() << "tied: " << plotItem()->isTiedZoom() << PlotItemManager::self()->tiedZoomPlotsForView(view()).size();
680 update(); //FIXME should optimize instead of redrawing entire curve!
681
682 }
683
hoverXZoomMouseCursor(qreal x)684 void PlotRenderItem::hoverXZoomMouseCursor(qreal x) {
685 qreal px;
686
687 px = (x-plotItem()->xMin())/(plotItem()->xMax() - plotItem()->xMin())*(rect().right()-rect().left()) + rect().left();
688 px = qMax(px, rect().left());
689 px = qMin(px, rect().right());
690
691
692 _selectionRect.setFrom(QPointF(px, rect().top()));
693 _selectionRect.setTo(QPointF(px, rect().bottom()));
694
695 update(); //FIXME should optimize instead of redrawing entire curve!
696 }
697
dragYZoomMouseCursor(qreal y)698 void PlotRenderItem::dragYZoomMouseCursor(qreal y) {
699 qreal py;
700
701 py = (y-plotItem()->yMin())/(plotItem()->yMax() - plotItem()->yMin())*(rect().top()-rect().bottom()) + rect().bottom();
702 py = qMin(py, rect().bottom());
703 py = qMax(py, rect().top());
704
705 _selectionRect.setTo(QPointF(rect().right(), py));
706 update(); //FIXME should optimize instead of redrawing entire curve!
707
708 }
709
dragXZoomMouseCursor(qreal x)710 void PlotRenderItem::dragXZoomMouseCursor(qreal x) {
711 qreal px;
712
713 px = (x-plotItem()->xMin())/(plotItem()->xMax() - plotItem()->xMin())*(rect().right()-rect().left()) + rect().left();
714 px = qMax(px, rect().left());
715 px = qMin(px, rect().right());
716
717 _selectionRect.setTo(QPointF(px, rect().bottom()));
718 update(); //FIXME should optimize instead of redrawing entire curve!
719 }
720
721
sharedOrTiedPlots(bool sharedX,bool sharedY)722 QList<PlotItem*> PlotRenderItem::sharedOrTiedPlots(bool sharedX, bool sharedY) {
723 QList<PlotItem*> plots;
724 QList<PlotItem*> shared_plots;
725 QList<PlotItem*> tied_plots;
726
727 if (plotItem()->isInSharedAxisBox()) {
728 shared_plots = plotItem()->sharedAxisBox()->getSharedPlots();
729 bool keep;
730 foreach (PlotItem *plot, shared_plots) {
731 keep = (sharedX && plotItem()->sharedAxisBox()->isXAxisShared()) ||
732 (sharedY && plotItem()->sharedAxisBox()->isYAxisShared());
733 if (keep) {
734 plots.append(plot);
735 }
736 }
737 }
738
739 if (plotItem()->isTiedZoom()) {
740 tied_plots = PlotItemManager::self()->tiedZoomPlotsForView(view());
741 foreach (PlotItem *plot, tied_plots) {
742 if (!plots.contains(plot)) {
743 plots.append(plot);
744 }
745 }
746 }
747
748 if (plots.size()<1) {
749 plots.append(plotItem());
750 }
751
752 return plots;
753 }
754
755
756 //FIXME: store event or pos, and re-call this when window is redrawn
hoverMoveEvent(QGraphicsSceneHoverEvent * event)757 void PlotRenderItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
758
759 ViewItem::hoverMoveEvent(event);
760
761 if (view()->viewMode() != View::Data) {
762 event->ignore();
763 return;
764 }
765
766 QPointF p = event->pos();
767
768 setCursorsMode(p);
769 }
770
771
setCursorsMode(QPointF p,Qt::KeyboardModifiers modifiers_in)772 void PlotRenderItem::setCursorsMode(QPointF p, Qt::KeyboardModifiers modifiers_in) {
773
774 double y = (p.y() - rect().bottom())/(rect().top()-rect().bottom())*(plotItem()->yMax()-plotItem()->yMin())+plotItem()->yMin();
775 double x = (p.x() - rect().left())/(rect().right()-rect().left())*(plotItem()->xMax()-plotItem()->xMin())+plotItem()->xMin();
776
777 _hoverPos = p;
778 const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers() | modifiers_in;
779 if (modifiers & Qt::SHIFT || zoomOnlyMode() == View::ZoomOnlyY) {
780 _lastPos = p;
781 view()->setCursor(Qt::SizeVerCursor);
782 QList<PlotItem*> plots = sharedOrTiedPlots(false,true);
783 foreach (PlotItem *plot, plots) {
784 plot->renderItem()->hoverYZoomMouseCursor(y);
785 }
786 } else if (modifiers & Qt::CTRL || zoomOnlyMode() == View::ZoomOnlyX) {
787 _lastPos = p;
788 view()->setCursor(Qt::SizeHorCursor);
789 QList<PlotItem*> plots = sharedOrTiedPlots(true,false);
790 foreach (PlotItem *plot, plots) {
791 plot->renderItem()->hoverXZoomMouseCursor(x);
792 }
793 } else {
794 QList<PlotItem*> plots = sharedOrTiedPlots(true,true);
795 foreach (PlotItem *plot, plots) {
796 plot->renderItem()->resetSelectionRect();
797 }
798 updateCursor(p);
799 }
800
801 processHoverMoveEvent(p);
802
803 if (kstApp->mainWindow()->isHighlightPoint()) {
804 update();
805 }
806 }
807
processHoverMoveEvent(const QPointF & p,bool delayed)808 void PlotRenderItem::processHoverMoveEvent(const QPointF &p, bool delayed) {
809
810 if (p.isNull()) {
811 return;
812 }
813
814 const QPointF point = plotItem()->mapToProjection(p);
815 statusMessagePoint = point;
816 if (kstApp->mainWindow()->isHighlightPoint()) {
817 highlightNearestDataPoint(point, delayed);
818 } else {
819 QPointF matchedPoint;
820 double imageZ;
821 bool bFoundImage = false;
822 QString message;
823 QString imageName;
824
825 foreach(RelationPtr relation, relationList()) {
826 if (Image* image = kst_cast<Image>(relation)) {
827 if (!bFoundImage && image->getNearestZ(point.x(), point.y(), imageZ, matchedPoint)) {
828 bFoundImage = true;
829 imageName = image->CleanedName();
830 }
831 }
832 }
833 _highlightPointActive = false;
834 if (bFoundImage) {
835 message = QString("%1 (%2, %3, %4)").arg(imageName).
836 arg(plotItem()->xAxis()->statusBarString(point.x())).
837 arg(plotItem()->yAxis()->statusBarString(point.y())).
838 arg(QString::number(imageZ, 'G', 16));
839 } else {
840 message = QString("(%1, %2)").
841 arg(plotItem()->xAxis()->statusBarString(point.x())).
842 arg(plotItem()->yAxis()->statusBarString(point.y()));
843 }
844 if (_referencePointMode) {
845 message += QString(" [Offset: %1, %2]").
846 arg(QString::number(point.x() - _referencePoint.x(), 'G', 16)).
847 arg(QString::number(point.y() - _referencePoint.y(), 'G', 16));
848 }
849 kstApp->mainWindow()->setStatusMessage(message,0,delayed);
850 }
851 }
852
853
highlightNearestDataPoint(const QPointF & position,bool delayed)854 void PlotRenderItem::highlightNearestDataPoint(const QPointF& position, bool delayed) {
855 QString curveMsg;
856 QString imageMsg;
857
858 _highlightPointActive = false;
859 _invertHighlight = false;
860
861 if (!relationList().isEmpty()) {
862 QString curveName, imageName;
863
864 bool bFirst = true;
865 bool bFoundImage = false;
866
867 double distance, minDistance = 1.0E300;
868 double x, y;
869 QPointF matchedPoint;
870 double imageZ;
871 double dxPerPix = double(projectionRect().width())/double(rect().width());
872
873 foreach(RelationPtr relation, relationList()) {
874 if (Curve* curve = kst_cast<Curve>(relation)) {
875 int index = curve->getIndexNearXY(position.x(), dxPerPix, position.y());
876 curve->point(index, x, y);
877 distance = fabs(position.y() - y);
878 if (bFirst || distance < minDistance) {
879 matchedPoint = QPointF(x, y);
880 statusMessagePoint = matchedPoint;
881 bFirst = false;
882 minDistance = distance;
883 curveName = curve->CleanedName();
884 if (curve->color() == Qt::black) {
885 _invertHighlight = true;
886 }
887 }
888 } else if (Image* image = kst_cast<Image>(relation)) {
889 if (!bFoundImage && image->getNearestZ(position.x(), position.y(), imageZ, matchedPoint)) {
890 bFoundImage = true;
891 imageName = image->CleanedName();
892 }
893 }
894 }
895 if (!curveName.isEmpty()) {
896 QString message = curveName + QString(" (%1, %2)").
897 arg(plotItem()->xAxis()->
898 statusBarString(matchedPoint.x())).
899 arg(QString::number(matchedPoint.y(), 'G', 16));
900 if (_referencePointMode) {
901 message += QString(" [Offset: %1, %2]").
902 arg(QString::number(matchedPoint.x() - _referencePoint.x(),
903 'G', 16)).
904 arg(QString::number(matchedPoint.y() - _referencePoint.y(),
905 'G', 16));
906 }
907 kstApp->mainWindow()->setStatusMessage(message, 0, delayed);
908 _highlightPointActive = true;
909 _highlightPoint = QPointF(matchedPoint.x(), matchedPoint.y());
910 } else if (!imageName.isEmpty()) {
911 statusMessagePoint = position;
912 QString message = imageName + QString(" (%1, %2, %3)").
913 arg(plotItem()->xAxis()->
914 statusBarString(matchedPoint.x())).
915 arg(QString::number(matchedPoint.y(), 'G', 16)).
916 arg(QString::number(imageZ, 'G', 16));
917 kstApp->mainWindow()->setStatusMessage(message, 0, delayed);
918 _highlightPointActive = true;
919 _highlightPoint = QPointF(matchedPoint.x(), matchedPoint.y());
920 } else {
921 QString message = QString("(%1 %2)").arg(QString::number(position.x(),
922 'G', 16)).arg(QString::number(position.y(), 'G', 16));
923 kstApp->mainWindow()->setStatusMessage(message, 0, delayed);
924 }
925 }
926 }
927
928
hoverEnterEvent(QGraphicsSceneHoverEvent * event)929 void PlotRenderItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event) {
930 ViewItem::hoverEnterEvent(event);
931 if (view()->viewMode() != View::Data) {
932 event->ignore();
933 return;
934 }
935
936 setFocus(Qt::MouseFocusReason);
937
938 // Qt bug: http://bugreports.qt.nokia.com/browse/QTBUG-8188
939 if (!hasFocus()) {
940 QEvent activate(QEvent::WindowActivate);
941 View* v = view();
942 if (v) {
943 QApplication::sendEvent(v->scene(), &activate);
944 setFocus(Qt::MouseFocusReason);
945 if (!hasFocus()) {
946 Debug::self()->log("PlotRenderItem::hoverEnterEvent: could not set focus", Debug::Warning);
947 }
948 }
949 }
950
951 updateCursor(event->pos());
952
953 const QPointF p = plotItem()->mapToProjection(event->pos());
954 statusMessagePoint = p;
955 QString message = QString("(%1, %2)").
956 arg(plotItem()->xAxis()->statusBarString(p.x())).
957 arg(plotItem()->yAxis()->statusBarString(p.y()));
958 kstApp->mainWindow()->setStatusMessage(message);
959 }
960
hoverLeaveEvent(QGraphicsSceneHoverEvent * event)961 void PlotRenderItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) {
962 ViewItem::hoverLeaveEvent(event);
963
964 _hoverPos = QPointF(0,0);
965
966 _highlightPointActive = false;
967
968 if (view()->viewMode() != View::Data) {
969 event->ignore();
970 return;
971 }
972
973 clearFocus();
974 resetSelectionRect();
975
976 updateCursor(event->pos());
977
978 kstApp->mainWindow()->setStatusMessage(QString());
979 }
980
981
shape() const982 QPainterPath PlotRenderItem::shape() const {
983 QPainterPath selectPath;
984 selectPath.setFillRule(Qt::WindingFill);
985 selectPath.addPolygon(rect());
986 selectPath.addPath(checkBox());
987 return selectPath;
988 }
989
990
updateViewItemParent(bool force_toplevel)991 bool PlotRenderItem::updateViewItemParent(bool force_toplevel) {
992 Q_UNUSED(force_toplevel)
993 return false; //never reparent a plot renderer
994 }
995
996
updateGeometry()997 void PlotRenderItem::updateGeometry() {
998 setViewRect(plotItem()->plotRect());
999 }
1000
1001
updateViewMode()1002 void PlotRenderItem::updateViewMode() {
1003 switch (view()->viewMode()) {
1004 case View::Data:
1005 view()->setCursor(Qt::CrossCursor);
1006 break;
1007 case View::Layout:
1008 view()->setCursor(Qt::ArrowCursor);
1009 break;
1010 default:
1011 break;
1012 }
1013 }
1014
1015
edit()1016 void PlotRenderItem::edit() {
1017 plotItem()->edit();
1018 }
1019
1020
raise()1021 void PlotRenderItem::raise() {
1022 plotItem()->raise();
1023 }
1024
1025
lower()1026 void PlotRenderItem::lower() {
1027 plotItem()->lower();
1028 }
1029
1030
createAutoLayout()1031 void PlotRenderItem::createAutoLayout() {
1032 plotItem()->createAutoLayout();
1033 }
1034
1035
createCustomLayout(int columns)1036 void PlotRenderItem::createCustomLayout(int columns) {
1037 plotItem()->createCustomLayout(columns);
1038 }
1039
1040
remove()1041 void PlotRenderItem::remove() {
1042 plotItem()->remove();
1043 }
1044
1045
updateCursor(const QPointF & pos)1046 void PlotRenderItem::updateCursor(const QPointF &pos) {
1047 _lastPos = pos;
1048 if (checkBox().contains(pos)) {
1049 view()->setCursor(Qt::ArrowCursor);
1050 } else {
1051 updateViewMode();
1052 }
1053 }
1054
sanitizeMaxMin(double * min_in,double * max_in)1055 void PlotRenderItem::sanitizeMaxMin(double *min_in, double *max_in) {
1056 double min = *min_in;
1057 double max = *max_in;
1058
1059 if (fabs(min) < std::numeric_limits<qreal>::min()*10.0) {
1060 min = 0.0;
1061 }
1062 if (fabs(max) < std::numeric_limits<qreal>::min()*10.0) {
1063 max = 0.0;
1064 }
1065
1066 bool min_ok = (min == min) && (min<std::numeric_limits<qreal>::max()/10.0) &&
1067 (min > -(std::numeric_limits<qreal>::max()/10.0));
1068 bool max_ok = (max == max) && (max<std::numeric_limits<qreal>::max()/10.0) &&
1069 (max > -(std::numeric_limits<qreal>::max()/10.0));
1070
1071 min_ok |= (min==0);
1072 max_ok |= (max==0);
1073
1074 if (!min_ok) {
1075 min = -1E150;
1076 }
1077 if (!max_ok) {
1078 max = 1E150;
1079 }
1080
1081 if (min > max) {
1082 qSwap(min, max);
1083 } else if ((min == max) || (fabs(max-min) < std::numeric_limits<qreal>::min()*10.0)) {
1084 min = min - min/1000.0 - 0.1;
1085 max = max + max/1000.0 + 0.1;
1086 }
1087
1088 *min_in = min;
1089 *max_in = max;
1090 }
1091
1092
computedProjectionRect() const1093 QRectF PlotRenderItem::computedProjectionRect() const {
1094 qreal minX, minY, maxX, maxY;
1095
1096 //initialize to current projection rect...
1097 projectionRect().getCoords(&minX, &minY, &maxX, &maxY);
1098
1099 double minX_d = minX, minY_d = minY, maxX_d = maxX, maxY_d = maxY;
1100
1101 // FIXME: We should not be using QRectF, because we should
1102 // always use doubles for data ranges, and QRectF uses qreal
1103 // which is defined as 'double' on x86 and 'float' on ARM.
1104 computeXAxisRange(&minX_d, &maxX_d);
1105 computeYAxisRange(&minY_d, &maxY_d);
1106
1107 return QRectF(QPointF(minX_d, minY_d),
1108 QPointF(maxX_d, maxY_d));
1109 }
1110
1111
computeXAxisRange(double * min,double * max) const1112 void PlotRenderItem::computeXAxisRange(double *min, double *max) const {
1113 double minimum = *min;
1114 double maximum = *max;
1115
1116 switch (plotItem()->xAxis()->axisZoomMode()) {
1117 case PlotAxis::Auto:
1118 computeAuto(Qt::Horizontal, &minimum, &maximum);
1119 break;
1120 case PlotAxis::AutoBorder: //auto mode, plus a 2.5% border on top and bottom.
1121 computeAuto(Qt::Horizontal, &minimum, &maximum);
1122 computeBorder(Qt::Horizontal, &minimum, &maximum);
1123 break;
1124 case PlotAxis::SpikeInsensitive: //auto with algorithm to detect spikes TBD
1125 computeNoSpike(Qt::Horizontal, &minimum, &maximum);
1126 break;
1127 case PlotAxis::MeanCentered: //the mean of all active curves
1128 computeMeanCentered(Qt::Horizontal, &minimum, &maximum);
1129 break;
1130 case PlotAxis::FixedExpression: // limits are set by user interaction
1131 default:
1132 break;
1133 }
1134
1135 *min = minimum;
1136 *max = maximum;
1137
1138 sanitizeMaxMin(min,max);
1139 }
1140
1141
computeYAxisRange(double * min,double * max) const1142 void PlotRenderItem::computeYAxisRange(double *min, double *max) const {
1143 double minimum = *min;
1144 double maximum = *max;
1145
1146 switch (plotItem()->yAxis()->axisZoomMode()) {
1147 case PlotAxis::Auto:
1148 computeAuto(Qt::Vertical, &minimum, &maximum);
1149 break;
1150 case PlotAxis::AutoBorder: //auto mode, plus a 2.5% border on top and bottom.
1151 computeAuto(Qt::Vertical, &minimum, &maximum);
1152 computeBorder(Qt::Vertical, &minimum, &maximum);
1153 break;
1154 case PlotAxis::SpikeInsensitive: //auto with algorithm to detect spikes TBD
1155 computeNoSpike(Qt::Vertical, &minimum, &maximum);
1156 break;
1157 case PlotAxis::MeanCentered: //the mean of all active curves
1158 computeMeanCentered(Qt::Vertical, &minimum, &maximum);
1159 break;
1160 case PlotAxis::FixedExpression: // limits are set by user interaction
1161 default:
1162 break;
1163 }
1164
1165 *min = minimum;
1166 *max = maximum;
1167
1168 sanitizeMaxMin(min,max);
1169 }
1170
1171
computeAuto(Qt::Orientation orientation,double * min,double * max) const1172 void PlotRenderItem::computeAuto(Qt::Orientation orientation, double *min, double *max) const {
1173 //The previous values are of no consequence as this algorithm does not depend
1174 //on the previous values. So start over so that first active relation initializes.
1175 double minimum =-0.1;
1176 double maximum = 0.1;;
1177 bool unInitialized = true;
1178
1179 bool axisLog = orientation == Qt::Horizontal ? plotItem()->xAxis()->axisLog() : plotItem()->yAxis()->axisLog();
1180
1181 foreach (RelationPtr relation, relationList()) {
1182 if (relation->ignoreAutoScale())
1183 continue;
1184
1185 double minPos_ = orientation == Qt::Horizontal ? relation->minPosX() : relation->minPosY();
1186 double min_ = orientation == Qt::Horizontal ? relation->minX() : relation->minY();
1187 double max_ = orientation == Qt::Horizontal ? relation->maxX() : relation->maxY();
1188
1189 if (min_==min_) { // don't use NaN's
1190 //If the axis is in log mode, the lower extent will be the
1191 //minimum value larger than zero.
1192 if (axisLog) {
1193 minimum = unInitialized ? minPos_ : qMin(minPos_, minimum);
1194 } else {
1195 minimum = unInitialized ? min_ : qMin(min_, minimum);
1196 }
1197 }
1198
1199 if (max_==max_) {
1200 maximum = unInitialized ? max_ : qMax(max_, maximum);
1201 if (min_==min_) {
1202 unInitialized = false;
1203 }
1204 }
1205 }
1206
1207 if (axisLog) {
1208 if (unInitialized){
1209 maximum = 100.0;
1210 minimum = 0.01;
1211 }
1212 if (minimum < 0) {
1213 minimum = 0;
1214 }
1215 if (minimum >= maximum) {
1216 if (minimum <=0) maximum = 1;
1217 else maximum = minimum * 1.1;
1218 minimum = minimum * 0.9;
1219 }
1220 } else {
1221 if (maximum <= minimum) {
1222 minimum = maximum -0.1;
1223 maximum += 0.1;
1224 }
1225 }
1226
1227 *min = minimum;
1228 *max = maximum;
1229 }
1230
1231
computeBorder(Qt::Orientation orientation,double * min,double * max) const1232 void PlotRenderItem::computeBorder(Qt::Orientation orientation, double *min, double *max) const {
1233 double minimum = *min;
1234 double maximum = *max;
1235
1236 bool axisLog = orientation == Qt::Horizontal ? plotItem()->xAxis()->axisLog() : plotItem()->yAxis()->axisLog();
1237 double logBase = 10.0/*orientation == Qt::Horizontal ? xLogBase() : yLogBase()*/;
1238
1239 if (axisLog) {
1240 minimum = log10(minimum)/log10(logBase);
1241 maximum = maximum > 0.0 ? log10(maximum) : 0.0;
1242 double d = qAbs(maximum - minimum) * 0.025;
1243 maximum = pow(logBase, maximum + d);
1244 minimum = pow(logBase, minimum - d);
1245 } else {
1246 double d = qAbs(maximum - minimum) * 0.025;
1247 maximum += d;
1248 minimum -= d;
1249 }
1250
1251 *min = minimum;
1252 *max = maximum;
1253 }
1254
1255
computeMeanCentered(Qt::Orientation orientation,double * min,double * max) const1256 void PlotRenderItem::computeMeanCentered(Qt::Orientation orientation, double *min, double *max) const {
1257 double minimum = *min;
1258 double maximum = *max;
1259
1260 int count = 0;
1261 double mid = 0.0;
1262
1263 foreach (RelationPtr relation, relationList()) {
1264 if (relation->ignoreAutoScale())
1265 continue;
1266
1267 mid += orientation == Qt::Horizontal ? relation->midX() : relation->midY();
1268 ++count;
1269 }
1270
1271 if (count) {
1272 mid /= double(count);
1273 double delta = maximum - minimum;
1274 minimum = mid - delta / 2.0;
1275 maximum = mid + delta / 2.0;
1276 }
1277
1278 *min = minimum;
1279 *max = maximum;
1280 }
1281
1282
computeNoSpike(Qt::Orientation orientation,double * min,double * max) const1283 void PlotRenderItem::computeNoSpike(Qt::Orientation orientation, double *min, double *max) const {
1284 //The previous values are of no consequence as this algorithm does not depend
1285 //on the previous values. So start over so that first active relation initializes.
1286 bool unInitialized = true;
1287 bool axisLog = orientation == Qt::Horizontal ? plotItem()->xAxis()->axisLog() : plotItem()->yAxis()->axisLog();
1288 int ns_zoom_level = orientation == Qt::Horizontal ? plotItem()->xAxis()->nsZoomLevel() : plotItem()->yAxis()->nsZoomLevel();
1289 double minimum = axisLog ? 0.0 : -0.1;
1290 double maximum = 0.2;
1291
1292 foreach (RelationPtr relation, relationList()) {
1293 if (relation->ignoreAutoScale())
1294 continue;
1295
1296 double minPos_ = orientation == Qt::Horizontal ? relation->minPosX() : relation->minPosY();
1297 double min_ = orientation == Qt::Horizontal ? relation->ns_minX(ns_zoom_level) : relation->ns_minY(ns_zoom_level);
1298 double max_ = orientation == Qt::Horizontal ? relation->ns_maxX(ns_zoom_level) : relation->ns_maxY(ns_zoom_level);
1299
1300 //If the axis is in log mode, the lower extent will be the
1301 //minimum value larger than zero.
1302 if (axisLog)
1303 minimum = unInitialized ? minPos_ : qMin(minPos_, minimum);
1304 else
1305 minimum = unInitialized ? min_ : qMin(min_, minimum);
1306
1307 maximum = unInitialized ? max_ : qMax(max_, maximum);
1308
1309 double delta = maximum - minimum;
1310 maximum += delta/10;
1311
1312 if (!axisLog) {
1313 minimum -= delta/10;
1314 }
1315
1316 unInitialized = false;
1317 }
1318
1319 if (maximum <= minimum) {
1320 minimum = axisLog ? 0.0 : -0.1;
1321 maximum = 0.2;
1322 }
1323
1324 if (axisLog && minimum < 0.0) {
1325 minimum = pow(10, -350.0);
1326 }
1327
1328 *min = minimum;
1329 *max = maximum;
1330
1331 maximum += (maximum-minimum)*0.2;
1332 if (!axisLog) {
1333 minimum -= (maximum-minimum)*0.2;
1334 }
1335
1336 }
1337
1338
tryShortcut(const QString & keySequence)1339 bool PlotRenderItem::tryShortcut(const QString &keySequence) {
1340 if (ViewItem::tryShortcut(keySequence)) {
1341 return true;
1342 } else {
1343 return plotItem()->tryShortcut(keySequence);
1344 }
1345 }
1346
1347
contextMenuEvent(QGraphicsSceneContextMenuEvent * event)1348 void PlotRenderItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
1349 if (plotItem() && plotItem()->parentItem() && plotItem()->isInSharedAxisBox()) {
1350 if (plotItem()->view()->viewMode() == View::Layout) {
1351 plotItem()->sharedAxisBox()->triggerContextEvent(event);
1352 return;
1353 }
1354 }
1355 ViewItem::contextMenuEvent(event);
1356 }
1357
1358
1359 }
1360
1361 // vim: ts=2 sw=2 et
1362