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