labplot  2.8.2
About: LabPlot is an application for plotting and analysis of 2D and 3D functions and data. It is a complete rewrite of LabPlot1 and lacks in the first release a lot of features available in the predecessor. On the other hand, the GUI and the usability is more superior.
  Fossies Dox: labplot-2.8.2.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

Axis.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  File : Axis.cpp
3  Project : LabPlot
4  Description : Axis for cartesian coordinate systems.
5  --------------------------------------------------------------------
6  Copyright : (C) 2011-2018 Alexander Semke (alexander.semke@web.de)
7  Copyright : (C) 2013-2020 Stefan Gerlach (stefan.gerlach@uni.kn)
8 
9  ***************************************************************************/
10 
11 /***************************************************************************
12  * *
13  * This program is free software; you can redistribute it and/or modify *
14  * it under the terms of the GNU General Public License as published by *
15  * the Free Software Foundation; either version 2 of the License, or *
16  * (at your option) any later version. *
17  * *
18  * This program is distributed in the hope that it will be useful, *
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
21  * GNU General Public License for more details. *
22  * *
23  * You should have received a copy of the GNU General Public License *
24  * along with this program; if not, write to the Free Software *
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
26  * Boston, MA 02110-1301 USA *
27  * *
28  ***************************************************************************/
29 
39 #include "backend/lib/macros.h"
40 // #include "backend/lib/trace.h"
41 #include "kdefrontend/GuiTools.h"
42 
43 #include <KConfig>
44 #include <KLocalizedString>
45 
46 #include <QGraphicsSceneContextMenuEvent>
47 #include <QMenu>
48 #include <QPainter>
49 #include <QTextDocument>
50 
51 extern "C" {
52 #include <gsl/gsl_math.h>
53 #include "backend/nsl/nsl_math.h"
54 }
55 
56 /**
57  * \class AxisGrid
58  * \brief Helper class to get the axis grid drawn with the z-Value=0.
59  *
60  * The painting of the grid lines is separated from the painting of the axis itself.
61  * This allows to use a different z-values for the grid lines (z=0, drawn below all other objects )
62  * and for the axis (z=FLT_MAX, drawn on top of all other objects)
63  *
64  * \ingroup worksheet
65  */
66 class AxisGrid : public QGraphicsItem {
67 public:
68  explicit AxisGrid(AxisPrivate* a) {
69  axis = a;
70  setFlag(QGraphicsItem::ItemIsSelectable, false);
71  setFlag(QGraphicsItem::ItemIsFocusable, false);
72  setAcceptHoverEvents(false);
73  }
74 
75  QRectF boundingRect() const override {
76  QPainterPath gridShape;
79  QRectF boundingRectangle = gridShape.boundingRect();
80  return boundingRectangle;
81  }
82 
83  void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override {
84  Q_UNUSED(option)
85  Q_UNUSED(widget)
86 
87  if (!axis->isVisible()) return;
88  if (axis->linePath.isEmpty()) return;
89 
90  //draw major grid
91  if (axis->majorGridPen.style() != Qt::NoPen) {
92  painter->setOpacity(axis->majorGridOpacity);
93  painter->setPen(axis->majorGridPen);
94  painter->setBrush(Qt::NoBrush);
95  painter->drawPath(axis->majorGridPath);
96  }
97 
98  //draw minor grid
99  if (axis->minorGridPen.style() != Qt::NoPen) {
100  painter->setOpacity(axis->minorGridOpacity);
101  painter->setPen(axis->minorGridPen);
102  painter->setBrush(Qt::NoBrush);
103  painter->drawPath(axis->minorGridPath);
104  }
105  }
106 
107 private:
109 };
110 
111 /**
112  * \class Axis
113  * \brief Axis for cartesian coordinate systems.
114  *
115  * \ingroup worksheet
116  */
117 Axis::Axis(const QString& name, Orientation orientation)
118  : WorksheetElement(name, AspectType::Axis), d_ptr(new AxisPrivate(this)) {
119  d_ptr->orientation = orientation;
120  init();
121 }
122 
123 Axis::Axis(const QString& name, Orientation orientation, AxisPrivate* dd)
124  : WorksheetElement(name, AspectType::Axis), d_ptr(dd) {
125  d_ptr->orientation = orientation;
126  init();
127 }
128 
130  Q_D(Axis);
131  d->plot = dynamic_cast<CartesianPlot*>(parentAspect());
132  Q_ASSERT(d->plot);
133  d->cSystem = dynamic_cast<const CartesianCoordinateSystem*>(d->plot->coordinateSystem());
134 }
135 
136 void Axis::init() {
137  Q_D(Axis);
138 
139  KConfig config;
140  KConfigGroup group = config.group("Axis");
141 
142  d->autoScale = true;
143  d->position = Axis::Position::Custom;
144  d->offset = group.readEntry("PositionOffset", 0);
145  d->scale = (Scale) group.readEntry("Scale", static_cast<int>(Scale::Linear));
146  d->autoScale = group.readEntry("AutoScale", true);
147  d->start = group.readEntry("Start", 0);
148  d->end = group.readEntry("End", 10);
149  d->zeroOffset = group.readEntry("ZeroOffset", 0);
150  d->scalingFactor = group.readEntry("ScalingFactor", 1.0);
151 
152  d->linePen.setStyle( (Qt::PenStyle) group.readEntry("LineStyle", (int) Qt::SolidLine) );
153  d->linePen.setWidthF( group.readEntry("LineWidth", Worksheet::convertToSceneUnits( 1.0, Worksheet::Unit::Point) ) );
154  d->lineOpacity = group.readEntry("LineOpacity", 1.0);
155  d->arrowType = (Axis::ArrowType) group.readEntry("ArrowType", static_cast<int>(ArrowType::NoArrow));
156  d->arrowPosition = (Axis::ArrowPosition) group.readEntry("ArrowPosition", static_cast<int>(ArrowPosition::Right));
157  d->arrowSize = group.readEntry("ArrowSize", Worksheet::convertToSceneUnits(10, Worksheet::Unit::Point));
158 
159  // axis title
160  d->title = new TextLabel(this->name(), TextLabel::Type::AxisTitle);
161  connect( d->title, &TextLabel::changed, this, &Axis::labelChanged);
162  addChild(d->title);
163  d->title->setHidden(true);
164  d->title->graphicsItem()->setParentItem(d);
165  d->title->graphicsItem()->setFlag(QGraphicsItem::ItemIsMovable, false);
166  d->title->graphicsItem()->setFlag(QGraphicsItem::ItemIsFocusable, false);
167  d->title->graphicsItem()->setAcceptHoverEvents(false);
168  d->title->setText(this->name());
169  if (d->orientation == Orientation::Vertical) d->title->setRotationAngle(90);
170  d->titleOffsetX = Worksheet::convertToSceneUnits(2, Worksheet::Unit::Point); //distance to the axis tick labels
171  d->titleOffsetY = Worksheet::convertToSceneUnits(2, Worksheet::Unit::Point); //distance to the axis tick labels
172 
173  d->majorTicksDirection = (Axis::TicksDirection) group.readEntry("MajorTicksDirection", (int) Axis::ticksOut);
174  d->majorTicksType = (TicksType) group.readEntry("MajorTicksType", static_cast<int>(TicksType::TotalNumber));
175  d->majorTicksNumber = group.readEntry("MajorTicksNumber", 11);
176  d->majorTicksSpacing = group.readEntry("MajorTicksIncrement", 0.0); // set to 0, so axisdock determines the value to not have to many labels the first time switched to Spacing
177 
178  d->majorTicksPen.setStyle((Qt::PenStyle) group.readEntry("MajorTicksLineStyle", (int)Qt::SolidLine) );
179  d->majorTicksPen.setColor( group.readEntry("MajorTicksColor", QColor(Qt::black) ) );
180  d->majorTicksPen.setWidthF( group.readEntry("MajorTicksWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Unit::Point) ) );
181  d->majorTicksLength = group.readEntry("MajorTicksLength", Worksheet::convertToSceneUnits(6.0, Worksheet::Unit::Point));
182  d->majorTicksOpacity = group.readEntry("MajorTicksOpacity", 1.0);
183 
184  d->minorTicksDirection = (TicksDirection) group.readEntry("MinorTicksDirection", (int) Axis::ticksOut);
185  d->minorTicksType = (TicksType) group.readEntry("MinorTicksType", static_cast<int>(TicksType::TotalNumber));
186  d->minorTicksNumber = group.readEntry("MinorTicksNumber", 1);
187  d->minorTicksIncrement = group.readEntry("MinorTicksIncrement", 0.0); // see MajorTicksIncrement
188  d->minorTicksPen.setStyle((Qt::PenStyle) group.readEntry("MinorTicksLineStyle", (int)Qt::SolidLine) );
189  d->minorTicksPen.setColor( group.readEntry("MinorTicksColor", QColor(Qt::black) ) );
190  d->minorTicksPen.setWidthF( group.readEntry("MinorTicksWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Unit::Point) ) );
191  d->minorTicksLength = group.readEntry("MinorTicksLength", Worksheet::convertToSceneUnits(3.0, Worksheet::Unit::Point));
192  d->minorTicksOpacity = group.readEntry("MinorTicksOpacity", 1.0);
193 
194  //Labels
195  d->labelsFormat = (LabelsFormat) group.readEntry("LabelsFormat", static_cast<int>(LabelsFormat::Decimal));
196  d->labelsAutoPrecision = group.readEntry("LabelsAutoPrecision", true);
197  d->labelsPrecision = group.readEntry("LabelsPrecision", 1);
198  d->labelsDateTimeFormat = group.readEntry("LabelsDateTimeFormat", "yyyy-MM-dd hh:mm:ss");
199  d->labelsPosition = (LabelsPosition) group.readEntry("LabelsPosition", (int) LabelsPosition::Out);
200  d->labelsOffset = group.readEntry("LabelsOffset", Worksheet::convertToSceneUnits( 5.0, Worksheet::Unit::Point ));
201  d->labelsRotationAngle = group.readEntry("LabelsRotation", 0);
202  d->labelsFont = group.readEntry("LabelsFont", QFont());
203  d->labelsFont.setPixelSize( Worksheet::convertToSceneUnits( 10.0, Worksheet::Unit::Point ) );
204  d->labelsColor = group.readEntry("LabelsFontColor", QColor(Qt::black));
205  d->labelsBackgroundType = (LabelsBackgroundType) group.readEntry("LabelsBackgroundType", static_cast<int>(LabelsBackgroundType::Transparent));
206  d->labelsBackgroundColor = group.readEntry("LabelsBackgroundColor", QColor(Qt::white));
207  d->labelsPrefix = group.readEntry("LabelsPrefix", "" );
208  d->labelsSuffix = group.readEntry("LabelsSuffix", "" );
209  d->labelsOpacity = group.readEntry("LabelsOpacity", 1.0);
210 
211  //major grid
212  d->majorGridPen.setStyle( (Qt::PenStyle) group.readEntry("MajorGridStyle", (int) Qt::SolidLine) );
213  d->majorGridPen.setColor(group.readEntry("MajorGridColor", QColor(Qt::gray)) );
214  d->majorGridPen.setWidthF( group.readEntry("MajorGridWidth", Worksheet::convertToSceneUnits( 1.0, Worksheet::Unit::Point ) ) );
215  d->majorGridOpacity = group.readEntry("MajorGridOpacity", 1.0);
216 
217  //minor grid
218  d->minorGridPen.setStyle( (Qt::PenStyle) group.readEntry("MinorGridStyle", (int) Qt::DotLine) );
219  d->minorGridPen.setColor(group.readEntry("MinorGridColor", QColor(Qt::gray)) );
220  d->minorGridPen.setWidthF( group.readEntry("MinorGridWidth", Worksheet::convertToSceneUnits( 1.0, Worksheet::Unit::Point ) ) );
221  d->minorGridOpacity = group.readEntry("MinorGridOpacity", 1.0);
222 }
223 
224 /*!
225  * For the most frequently edited properties, create Actions and ActionGroups for the context menu.
226  * For some ActionGroups the actual actions are created in \c GuiTool,
227  */
229  visibilityAction = new QAction(QIcon::fromTheme("view-visible"), i18n("Visible"), this);
230  visibilityAction->setCheckable(true);
231  connect(visibilityAction, &QAction::triggered, this, &Axis::visibilityChangedSlot);
232 
233  //Orientation
234  orientationActionGroup = new QActionGroup(this);
235  orientationActionGroup->setExclusive(true);
236  connect(orientationActionGroup, &QActionGroup::triggered, this, &Axis::orientationChangedSlot);
237 
238  orientationHorizontalAction = new QAction(QIcon::fromTheme("labplot-axis-horizontal"), i18n("Horizontal"), orientationActionGroup);
239  orientationHorizontalAction->setCheckable(true);
240 
241  orientationVerticalAction = new QAction(QIcon::fromTheme("labplot-axis-vertical"), i18n("Vertical"), orientationActionGroup);
242  orientationVerticalAction->setCheckable(true);
243 
244  //Line
245  lineStyleActionGroup = new QActionGroup(this);
246  lineStyleActionGroup->setExclusive(true);
247  connect(lineStyleActionGroup, &QActionGroup::triggered, this, &Axis::lineStyleChanged);
248 
249  lineColorActionGroup = new QActionGroup(this);
250  lineColorActionGroup->setExclusive(true);
251  connect(lineColorActionGroup, &QActionGroup::triggered, this, &Axis::lineColorChanged);
252 
253  //Ticks
254  //TODO
255 }
256 
258  this->initActions();
259 
260  //Orientation
261  orientationMenu = new QMenu(i18n("Orientation"));
262  orientationMenu->setIcon(QIcon::fromTheme("labplot-axis-horizontal"));
265 
266  //Line
267  lineMenu = new QMenu(i18n("Line"));
268  lineMenu->setIcon(QIcon::fromTheme("draw-line"));
269  lineStyleMenu = new QMenu(i18n("Style"), lineMenu);
270  lineStyleMenu->setIcon(QIcon::fromTheme("object-stroke-style"));
271  lineMenu->setIcon(QIcon::fromTheme("draw-line"));
272  lineMenu->addMenu( lineStyleMenu );
273 
274  lineColorMenu = new QMenu(i18n("Color"), lineMenu);
275  lineColorMenu->setIcon(QIcon::fromTheme("fill-color"));
277  lineMenu->addMenu( lineColorMenu );
278 }
279 
281  if (!orientationMenu)
282  initMenus();
283 
284  Q_D(const Axis);
285  QMenu* menu = WorksheetElement::createContextMenu();
286  QAction* firstAction = menu->actions().at(1); //skip the first action because of the "title-action"
287 
288  visibilityAction->setChecked(isVisible());
289  menu->insertAction(firstAction, visibilityAction);
290 
291  //Orientation
292  if (d->orientation == Orientation::Horizontal)
293  orientationHorizontalAction->setChecked(true);
294  else
295  orientationVerticalAction->setChecked(true);
296 
297  menu->insertMenu(firstAction, orientationMenu);
298 
299  //Line styles
302 
304 
305  menu->insertMenu(firstAction, lineMenu);
306  menu->insertSeparator(firstAction);
307 
308  return menu;
309 }
310 
311 /*!
312  Returns an icon to be used in the project explorer.
313 */
314 QIcon Axis::icon() const{
315  Q_D(const Axis);
316  QIcon ico;
317  if (d->orientation == Orientation::Horizontal)
318  ico = QIcon::fromTheme("labplot-axis-horizontal");
319  else
320  ico = QIcon::fromTheme("labplot-axis-vertical");
321 
322  return ico;
323 }
324 
326  if (orientationMenu) {
327  delete orientationMenu;
328  delete lineMenu;
329  }
330 
331  //no need to delete d->title, since it was added with addChild in init();
332 
333  //no need to delete the d-pointer here - it inherits from QGraphicsItem
334  //and is deleted during the cleanup in QGraphicsScene
335 }
336 
337 QGraphicsItem *Axis::graphicsItem() const {
338  return d_ptr;
339 }
340 
341 /*!
342  * overrides the implementation in WorksheetElement and sets the z-value to the maximal possible,
343  * axes are drawn on top of all other object in the plot.
344  */
345 void Axis::setZValue(qreal) {
346  Q_D(Axis);
347  d->setZValue(std::numeric_limits<double>::max());
348  d->gridItem->setParentItem(d->parentItem());
349  d->gridItem->setZValue(0);
350 }
351 
353  Q_D(Axis);
354  d->retransform();
355 }
356 
358  Q_D(Axis);
359  d->retransformTickLabelStrings();
360 }
361 
363  Q_D(Axis);
364  d->suppressRetransform = value;
365 }
366 
367 void Axis::handleResize(double horizontalRatio, double verticalRatio, bool pageResize) {
368  Q_D(Axis);
369  Q_UNUSED(pageResize);
370 
371  double ratio = 0;
372  if (horizontalRatio > 1.0 || verticalRatio > 1.0)
373  ratio = qMax(horizontalRatio, verticalRatio);
374  else
375  ratio = qMin(horizontalRatio, verticalRatio);
376 
377  QPen pen = d->linePen;
378  pen.setWidthF(pen.widthF() * ratio);
379  d->linePen = pen;
380 
381  d->majorTicksLength *= ratio; // ticks are perpendicular to axis line -> verticalRatio relevant
382  d->minorTicksLength *= ratio;
383  d->labelsFont.setPixelSize( d->labelsFont.pixelSize() * ratio ); //TODO: take into account rotated labels
384  d->labelsOffset *= ratio;
385  d->title->handleResize(horizontalRatio, verticalRatio, pageResize);
386 }
387 
388 /* ============================ getter methods ================= */
389 BASIC_SHARED_D_READER_IMPL(Axis, bool, autoScale, autoScale)
390 BASIC_SHARED_D_READER_IMPL(Axis, Axis::Orientation, orientation, orientation)
391 BASIC_SHARED_D_READER_IMPL(Axis, Axis::Position, position, position)
393 BASIC_SHARED_D_READER_IMPL(Axis, double, offset, offset)
394 BASIC_SHARED_D_READER_IMPL(Axis, double, start, start)
395 BASIC_SHARED_D_READER_IMPL(Axis, double, end, end)
396 BASIC_SHARED_D_READER_IMPL(Axis, qreal, scalingFactor, scalingFactor)
397 BASIC_SHARED_D_READER_IMPL(Axis, qreal, zeroOffset, zeroOffset)
398 
400 BASIC_SHARED_D_READER_IMPL(Axis, qreal, titleOffsetX, titleOffsetX)
401 BASIC_SHARED_D_READER_IMPL(Axis, qreal, titleOffsetY, titleOffsetY)
402 
403 CLASS_SHARED_D_READER_IMPL(Axis, QPen, linePen, linePen)
404 BASIC_SHARED_D_READER_IMPL(Axis, qreal, lineOpacity, lineOpacity)
405 BASIC_SHARED_D_READER_IMPL(Axis, Axis::ArrowType, arrowType, arrowType)
406 BASIC_SHARED_D_READER_IMPL(Axis, Axis::ArrowPosition, arrowPosition, arrowPosition)
407 BASIC_SHARED_D_READER_IMPL(Axis, qreal, arrowSize, arrowSize)
408 
409 BASIC_SHARED_D_READER_IMPL(Axis, Axis::TicksDirection, majorTicksDirection, majorTicksDirection)
410 BASIC_SHARED_D_READER_IMPL(Axis, Axis::TicksType, majorTicksType, majorTicksType)
411 BASIC_SHARED_D_READER_IMPL(Axis, int, majorTicksNumber, majorTicksNumber)
412 BASIC_SHARED_D_READER_IMPL(Axis, qreal, majorTicksSpacing, majorTicksSpacing)
413 BASIC_SHARED_D_READER_IMPL(Axis, const AbstractColumn*, majorTicksColumn, majorTicksColumn)
414 QString& Axis::majorTicksColumnPath() const { return d_ptr->majorTicksColumnPath; }
415 BASIC_SHARED_D_READER_IMPL(Axis, qreal, majorTicksLength, majorTicksLength)
416 CLASS_SHARED_D_READER_IMPL(Axis, QPen, majorTicksPen, majorTicksPen)
417 BASIC_SHARED_D_READER_IMPL(Axis, qreal, majorTicksOpacity, majorTicksOpacity)
418 
419 BASIC_SHARED_D_READER_IMPL(Axis, Axis::TicksDirection, minorTicksDirection, minorTicksDirection)
420 BASIC_SHARED_D_READER_IMPL(Axis, Axis::TicksType, minorTicksType, minorTicksType)
421 BASIC_SHARED_D_READER_IMPL(Axis, int, minorTicksNumber, minorTicksNumber)
422 BASIC_SHARED_D_READER_IMPL(Axis, qreal, minorTicksSpacing, minorTicksIncrement)
423 BASIC_SHARED_D_READER_IMPL(Axis, const AbstractColumn*, minorTicksColumn, minorTicksColumn)
424 QString& Axis::minorTicksColumnPath() const { return d_ptr->minorTicksColumnPath; }
425 BASIC_SHARED_D_READER_IMPL(Axis, qreal, minorTicksLength, minorTicksLength)
426 CLASS_SHARED_D_READER_IMPL(Axis, QPen, minorTicksPen, minorTicksPen)
427 BASIC_SHARED_D_READER_IMPL(Axis, qreal, minorTicksOpacity, minorTicksOpacity)
428 
429 BASIC_SHARED_D_READER_IMPL(Axis, Axis::LabelsFormat, labelsFormat, labelsFormat);
430 BASIC_SHARED_D_READER_IMPL(Axis, bool, labelsAutoPrecision, labelsAutoPrecision);
431 BASIC_SHARED_D_READER_IMPL(Axis, int, labelsPrecision, labelsPrecision);
432 BASIC_SHARED_D_READER_IMPL(Axis, QString, labelsDateTimeFormat, labelsDateTimeFormat);
433 BASIC_SHARED_D_READER_IMPL(Axis, Axis::LabelsPosition, labelsPosition, labelsPosition);
434 BASIC_SHARED_D_READER_IMPL(Axis, qreal, labelsOffset, labelsOffset);
435 BASIC_SHARED_D_READER_IMPL(Axis, qreal, labelsRotationAngle, labelsRotationAngle);
436 CLASS_SHARED_D_READER_IMPL(Axis, QColor, labelsColor, labelsColor);
437 CLASS_SHARED_D_READER_IMPL(Axis, QFont, labelsFont, labelsFont);
438 BASIC_SHARED_D_READER_IMPL(Axis, Axis::LabelsBackgroundType, labelsBackgroundType, labelsBackgroundType);
439 CLASS_SHARED_D_READER_IMPL(Axis, QColor, labelsBackgroundColor, labelsBackgroundColor);
440 CLASS_SHARED_D_READER_IMPL(Axis, QString, labelsPrefix, labelsPrefix);
441 CLASS_SHARED_D_READER_IMPL(Axis, QString, labelsSuffix, labelsSuffix);
442 BASIC_SHARED_D_READER_IMPL(Axis, qreal, labelsOpacity, labelsOpacity);
443 
444 CLASS_SHARED_D_READER_IMPL(Axis, QPen, majorGridPen, majorGridPen)
445 BASIC_SHARED_D_READER_IMPL(Axis, qreal, majorGridOpacity, majorGridOpacity)
446 CLASS_SHARED_D_READER_IMPL(Axis, QPen, minorGridPen, minorGridPen)
447 BASIC_SHARED_D_READER_IMPL(Axis, qreal, minorGridOpacity, minorGridOpacity)
448 
449 /* ============================ setter methods and undo commands ================= */
450 STD_SETTER_CMD_IMPL_F_S(Axis, SetAutoScale, bool, autoScale, retransform);
451 void Axis::setAutoScale(bool autoScale) {
452  Q_D(Axis);
453  if (autoScale != d->autoScale) {
454  exec(new AxisSetAutoScaleCmd(d, autoScale, ki18n("%1: set axis auto scaling")));
455 
456  if (autoScale) {
457  auto* plot = qobject_cast<CartesianPlot*>(parentAspect());
458  if (!plot)
459  return;
460 
461  if (d->orientation == Axis::Orientation::Horizontal) {
462  d->end = plot->xMax();
463  d->start = plot->xMin();
464  } else {
465  d->end = plot->yMax();
466  d->start = plot->yMin();
467  }
468  retransform();
469  emit endChanged(d->end);
470  emit startChanged(d->start);
471  }
472  }
473 }
474 
475 STD_SWAP_METHOD_SETTER_CMD_IMPL(Axis, SetVisible, bool, swapVisible);
476 void Axis::setVisible(bool on) {
477  Q_D(Axis);
478  exec(new AxisSetVisibleCmd(d, on, on ? ki18n("%1: set visible") : ki18n("%1: set invisible")));
479 }
480 
481 bool Axis::isVisible() const {
482  Q_D(const Axis);
483  return d->isVisible();
484 }
485 
486 void Axis::setDefault(bool value) {
487  Q_D(Axis);
488  d->isDefault = value;
489 }
490 
491 bool Axis::isDefault() const {
492  Q_D(const Axis);
493  return d->isDefault;
494 }
495 
496 void Axis::setPrinting(bool on) {
497  Q_D(Axis);
498  d->setPrinting(on);
499 }
500 
501 bool Axis::isHovered() const {
502  Q_D(const Axis);
503  return d->isHovered();
504 }
505 
506 STD_SETTER_CMD_IMPL_F_S(Axis, SetOrientation, Axis::Orientation, orientation, retransform);
507 void Axis::setOrientation(Orientation orientation) {
508  Q_D(Axis);
509  if (orientation != d->orientation)
510  exec(new AxisSetOrientationCmd(d, orientation, ki18n("%1: set axis orientation")));
511 }
512 
513 STD_SETTER_CMD_IMPL_F_S(Axis, SetPosition, Axis::Position, position, retransform);
514 void Axis::setPosition(Position position) {
515  Q_D(Axis);
516  if (position != d->position)
517  exec(new AxisSetPositionCmd(d, position, ki18n("%1: set axis position")));
518 }
519 
520 STD_SETTER_CMD_IMPL_F_S(Axis, SetScaling, Axis::Scale, scale, retransformTicks);
521 void Axis::setScale(Scale scale) {
522  Q_D(Axis);
523  if (scale != d->scale)
524  exec(new AxisSetScalingCmd(d, scale, ki18n("%1: set axis scale")));
525 }
526 
527 STD_SETTER_CMD_IMPL_F(Axis, SetOffset, double, offset, retransform);
528 void Axis::setOffset(double offset, bool undo) {
529  Q_D(Axis);
530  if (offset != d->offset) {
531  if (undo) {
532  exec(new AxisSetOffsetCmd(d, offset, ki18n("%1: set axis offset")));
533  } else {
534  d->offset = offset;
535  //don't need to call retransform() afterward
536  //since the only usage of this call is in CartesianPlot, where retransform is called for all children anyway.
537  }
538  emit positionChanged(offset);
539  }
540 }
541 
542 STD_SETTER_CMD_IMPL_F_S(Axis, SetStart, double, start, retransform);
543 void Axis::setStart(double start) {
544  Q_D(Axis);
545  if (start != d->start)
546  exec(new AxisSetStartCmd(d, start, ki18n("%1: set axis start")));
547 }
548 
549 STD_SETTER_CMD_IMPL_F_S(Axis, SetEnd, double, end, retransform);
550 void Axis::setEnd(double end) {
551  Q_D(Axis);
552  if (end != d->end)
553  exec(new AxisSetEndCmd(d, end, ki18n("%1: set axis end")));
554 }
555 
556 STD_SETTER_CMD_IMPL_F_S(Axis, SetZeroOffset, qreal, zeroOffset, retransform);
557 void Axis::setZeroOffset(qreal zeroOffset) {
558  Q_D(Axis);
559  if (zeroOffset != d->zeroOffset)
560  exec(new AxisSetZeroOffsetCmd(d, zeroOffset, ki18n("%1: set axis zero offset")));
561 }
562 
563 STD_SETTER_CMD_IMPL_F_S(Axis, SetScalingFactor, qreal, scalingFactor, retransform);
564 void Axis::setScalingFactor(qreal scalingFactor) {
565  Q_D(Axis);
566  if (scalingFactor != d->scalingFactor)
567  exec(new AxisSetScalingFactorCmd(d, scalingFactor, ki18n("%1: set axis scaling factor")));
568 }
569 
570 //Title
571 STD_SETTER_CMD_IMPL_F_S(Axis, SetTitleOffsetX, qreal, titleOffsetX, retransform);
572 void Axis::setTitleOffsetX(qreal offset) {
573  Q_D(Axis);
574  if (offset != d->titleOffsetX)
575  exec(new AxisSetTitleOffsetXCmd(d, offset, ki18n("%1: set title offset")));
576 }
577 
578 STD_SETTER_CMD_IMPL_F_S(Axis, SetTitleOffsetY, qreal, titleOffsetY, retransform);
579 void Axis::setTitleOffsetY(qreal offset) {
580  Q_D(Axis);
581  if (offset != d->titleOffsetY)
582  exec(new AxisSetTitleOffsetYCmd(d, offset, ki18n("%1: set title offset")));
583 }
584 
585 //Line
586 STD_SETTER_CMD_IMPL_F_S(Axis, SetLinePen, QPen, linePen, recalcShapeAndBoundingRect);
587 void Axis::setLinePen(const QPen &pen) {
588  Q_D(Axis);
589  if (pen != d->linePen)
590  exec(new AxisSetLinePenCmd(d, pen, ki18n("%1: set line style")));
591 }
592 
593 STD_SETTER_CMD_IMPL_F_S(Axis, SetLineOpacity, qreal, lineOpacity, update);
594 void Axis::setLineOpacity(qreal opacity) {
595  Q_D(Axis);
596  if (opacity != d->lineOpacity)
597  exec(new AxisSetLineOpacityCmd(d, opacity, ki18n("%1: set line opacity")));
598 }
599 
600 STD_SETTER_CMD_IMPL_F_S(Axis, SetArrowType, Axis::ArrowType, arrowType, retransformArrow);
601 void Axis::setArrowType(ArrowType type) {
602  Q_D(Axis);
603  if (type != d->arrowType)
604  exec(new AxisSetArrowTypeCmd(d, type, ki18n("%1: set arrow type")));
605 }
606 
607 STD_SETTER_CMD_IMPL_F_S(Axis, SetArrowPosition, Axis::ArrowPosition, arrowPosition, retransformArrow);
608 void Axis::setArrowPosition(ArrowPosition position) {
609  Q_D(Axis);
610  if (position != d->arrowPosition)
611  exec(new AxisSetArrowPositionCmd(d, position, ki18n("%1: set arrow position")));
612 }
613 
614 STD_SETTER_CMD_IMPL_F_S(Axis, SetArrowSize, qreal, arrowSize, retransformArrow);
615 void Axis::setArrowSize(qreal arrowSize) {
616  Q_D(Axis);
617  if (arrowSize != d->arrowSize)
618  exec(new AxisSetArrowSizeCmd(d, arrowSize, ki18n("%1: set arrow size")));
619 }
620 
621 //Major ticks
622 STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksDirection, Axis::TicksDirection, majorTicksDirection, retransformTicks);
623 void Axis::setMajorTicksDirection(TicksDirection majorTicksDirection) {
624  Q_D(Axis);
625  if (majorTicksDirection != d->majorTicksDirection)
626  exec(new AxisSetMajorTicksDirectionCmd(d, majorTicksDirection, ki18n("%1: set major ticks direction")));
627 }
628 
629 STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksType, Axis::TicksType, majorTicksType, retransformTicks);
630 void Axis::setMajorTicksType(TicksType majorTicksType) {
631  Q_D(Axis);
632  if (majorTicksType!= d->majorTicksType)
633  exec(new AxisSetMajorTicksTypeCmd(d, majorTicksType, ki18n("%1: set major ticks type")));
634 }
635 STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksNumber, int, majorTicksNumber, retransformTicks);
636 void Axis::setMajorTicksNumber(int majorTicksNumber) {
637  Q_D(Axis);
638  if (majorTicksNumber != d->majorTicksNumber)
639  exec(new AxisSetMajorTicksNumberCmd(d, majorTicksNumber, ki18n("%1: set the total number of the major ticks")));
640 }
641 
642 STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksSpacing, qreal, majorTicksSpacing, retransformTicks);
643 void Axis::setMajorTicksSpacing(qreal majorTicksSpacing) {
644  Q_D(Axis);
645  if (majorTicksSpacing != d->majorTicksSpacing)
646  exec(new AxisSetMajorTicksSpacingCmd(d, majorTicksSpacing, ki18n("%1: set the spacing of the major ticks")));
647 }
648 
649 STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksColumn, const AbstractColumn*, majorTicksColumn, retransformTicks)
650 void Axis::setMajorTicksColumn(const AbstractColumn* column) {
651  Q_D(Axis);
652  if (column != d->majorTicksColumn) {
653  exec(new AxisSetMajorTicksColumnCmd(d, column, ki18n("%1: assign major ticks' values")));
654 
655  if (column) {
656  connect(column, &AbstractColumn::dataChanged, this, &Axis::retransformTicks);
659  //TODO: add disconnect in the undo-function
660  }
661  }
662 }
663 
664 STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksPen, QPen, majorTicksPen, recalcShapeAndBoundingRect);
665 void Axis::setMajorTicksPen(const QPen& pen) {
666  Q_D(Axis);
667  if (pen != d->majorTicksPen)
668  exec(new AxisSetMajorTicksPenCmd(d, pen, ki18n("%1: set major ticks style")));
669 }
670 
671 STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksLength, qreal, majorTicksLength, retransformTicks);
672 void Axis::setMajorTicksLength(qreal majorTicksLength) {
673  Q_D(Axis);
674  if (majorTicksLength != d->majorTicksLength)
675  exec(new AxisSetMajorTicksLengthCmd(d, majorTicksLength, ki18n("%1: set major ticks length")));
676 }
677 
678 STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksOpacity, qreal, majorTicksOpacity, update);
679 void Axis::setMajorTicksOpacity(qreal opacity) {
680  Q_D(Axis);
681  if (opacity != d->majorTicksOpacity)
682  exec(new AxisSetMajorTicksOpacityCmd(d, opacity, ki18n("%1: set major ticks opacity")));
683 }
684 
685 //Minor ticks
686 STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksDirection, Axis::TicksDirection, minorTicksDirection, retransformTicks);
687 void Axis::setMinorTicksDirection(TicksDirection minorTicksDirection) {
688  Q_D(Axis);
689  if (minorTicksDirection != d->minorTicksDirection)
690  exec(new AxisSetMinorTicksDirectionCmd(d, minorTicksDirection, ki18n("%1: set minor ticks direction")));
691 }
692 
693 STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksType, Axis::TicksType, minorTicksType, retransformTicks);
694 void Axis::setMinorTicksType(TicksType minorTicksType) {
695  Q_D(Axis);
696  if (minorTicksType!= d->minorTicksType)
697  exec(new AxisSetMinorTicksTypeCmd(d, minorTicksType, ki18n("%1: set minor ticks type")));
698 }
699 
700 STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksNumber, int, minorTicksNumber, retransformTicks);
701 void Axis::setMinorTicksNumber(int minorTicksNumber) {
702  Q_D(Axis);
703  if (minorTicksNumber != d->minorTicksNumber)
704  exec(new AxisSetMinorTicksNumberCmd(d, minorTicksNumber, ki18n("%1: set the total number of the minor ticks")));
705 }
706 
707 STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksSpacing, qreal, minorTicksIncrement, retransformTicks);
708 void Axis::setMinorTicksSpacing(qreal minorTicksSpacing) {
709  Q_D(Axis);
710  if (minorTicksSpacing != d->minorTicksIncrement)
711  exec(new AxisSetMinorTicksSpacingCmd(d, minorTicksSpacing, ki18n("%1: set the spacing of the minor ticks")));
712 }
713 
714 STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksColumn, const AbstractColumn*, minorTicksColumn, retransformTicks)
715 void Axis::setMinorTicksColumn(const AbstractColumn* column) {
716  Q_D(Axis);
717  if (column != d->minorTicksColumn) {
718  exec(new AxisSetMinorTicksColumnCmd(d, column, ki18n("%1: assign minor ticks' values")));
719 
720  if (column) {
721  connect(column, &AbstractColumn::dataChanged, this, &Axis::retransformTicks);
724  //TODO: add disconnect in the undo-function
725  }
726  }
727 }
728 
729 STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksPen, QPen, minorTicksPen, recalcShapeAndBoundingRect);
730 void Axis::setMinorTicksPen(const QPen& pen) {
731  Q_D(Axis);
732  if (pen != d->minorTicksPen)
733  exec(new AxisSetMinorTicksPenCmd(d, pen, ki18n("%1: set minor ticks style")));
734 }
735 
736 STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksLength, qreal, minorTicksLength, retransformTicks);
737 void Axis::setMinorTicksLength(qreal minorTicksLength) {
738  Q_D(Axis);
739  if (minorTicksLength != d->minorTicksLength)
740  exec(new AxisSetMinorTicksLengthCmd(d, minorTicksLength, ki18n("%1: set minor ticks length")));
741 }
742 
743 STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksOpacity, qreal, minorTicksOpacity, update);
744 void Axis::setMinorTicksOpacity(qreal opacity) {
745  Q_D(Axis);
746  if (opacity != d->minorTicksOpacity)
747  exec(new AxisSetMinorTicksOpacityCmd(d, opacity, ki18n("%1: set minor ticks opacity")));
748 }
749 
750 //Labels
751 STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsFormat, Axis::LabelsFormat, labelsFormat, retransformTicks);
752 void Axis::setLabelsFormat(LabelsFormat labelsFormat) {
753  Q_D(Axis);
754  if (labelsFormat != d->labelsFormat) {
755 
756  //TODO: this part is not undo/redo-aware
757  if (labelsFormat == LabelsFormat::Decimal)
758  d->labelsFormatDecimalOverruled = true;
759  else
760  d->labelsFormatDecimalOverruled = false;
761 
762  exec(new AxisSetLabelsFormatCmd(d, labelsFormat, ki18n("%1: set labels format")));
763  }
764 }
765 
766 STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsAutoPrecision, bool, labelsAutoPrecision, retransformTickLabelStrings);
767 void Axis::setLabelsAutoPrecision(bool labelsAutoPrecision) {
768  Q_D(Axis);
769  if (labelsAutoPrecision != d->labelsAutoPrecision)
770  exec(new AxisSetLabelsAutoPrecisionCmd(d, labelsAutoPrecision, ki18n("%1: set labels precision")));
771 }
772 
773 STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsPrecision, int, labelsPrecision, retransformTickLabelStrings);
774 void Axis::setLabelsPrecision(int labelsPrecision) {
775  Q_D(Axis);
776  if (labelsPrecision != d->labelsPrecision)
777  exec(new AxisSetLabelsPrecisionCmd(d, labelsPrecision, ki18n("%1: set labels precision")));
778 }
779 
780 STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsDateTimeFormat, QString, labelsDateTimeFormat, retransformTickLabelStrings);
781 void Axis::setLabelsDateTimeFormat(const QString& format) {
782  Q_D(Axis);
783  if (format != d->labelsDateTimeFormat)
784  exec(new AxisSetLabelsDateTimeFormatCmd(d, format, ki18n("%1: set labels datetime format")));
785 }
786 
787 STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsPosition, Axis::LabelsPosition, labelsPosition, retransformTickLabelPositions);
788 void Axis::setLabelsPosition(LabelsPosition labelsPosition) {
789  Q_D(Axis);
790  if (labelsPosition != d->labelsPosition)
791  exec(new AxisSetLabelsPositionCmd(d, labelsPosition, ki18n("%1: set labels position")));
792 }
793 
794 STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsOffset, double, labelsOffset, retransformTickLabelPositions);
795 void Axis::setLabelsOffset(double offset) {
796  Q_D(Axis);
797  if (offset != d->labelsOffset)
798  exec(new AxisSetLabelsOffsetCmd(d, offset, ki18n("%1: set label offset")));
799 }
800 
801 STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsRotationAngle, qreal, labelsRotationAngle, retransformTickLabelPositions);
802 void Axis::setLabelsRotationAngle(qreal angle) {
803  Q_D(Axis);
804  if (angle != d->labelsRotationAngle)
805  exec(new AxisSetLabelsRotationAngleCmd(d, angle, ki18n("%1: set label rotation angle")));
806 }
807 
808 STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsColor, QColor, labelsColor, update);
809 void Axis::setLabelsColor(const QColor& color) {
810  Q_D(Axis);
811  if (color != d->labelsColor)
812  exec(new AxisSetLabelsColorCmd(d, color, ki18n("%1: set label color")));
813 }
814 
815 STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsFont, QFont, labelsFont, retransformTickLabelStrings);
816 void Axis::setLabelsFont(const QFont& font) {
817  Q_D(Axis);
818  if (font != d->labelsFont)
819  exec(new AxisSetLabelsFontCmd(d, font, ki18n("%1: set label font")));
820 }
821 
822 STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsBackgroundType, Axis::LabelsBackgroundType, labelsBackgroundType, update);
823 void Axis::setLabelsBackgroundType(LabelsBackgroundType type) {
824  Q_D(Axis);
825  if (type != d->labelsBackgroundType)
826  exec(new AxisSetLabelsBackgroundTypeCmd(d, type, ki18n("%1: set labels background type")));
827 }
828 
829 STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsBackgroundColor, QColor, labelsBackgroundColor, update);
830 void Axis::setLabelsBackgroundColor(const QColor& color) {
831  Q_D(Axis);
832  if (color != d->labelsBackgroundColor)
833  exec(new AxisSetLabelsBackgroundColorCmd(d, color, ki18n("%1: set label background color")));
834 }
835 
836 STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsPrefix, QString, labelsPrefix, retransformTickLabelStrings);
837 void Axis::setLabelsPrefix(const QString& prefix) {
838  Q_D(Axis);
839  if (prefix != d->labelsPrefix)
840  exec(new AxisSetLabelsPrefixCmd(d, prefix, ki18n("%1: set label prefix")));
841 }
842 
843 STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsSuffix, QString, labelsSuffix, retransformTickLabelStrings);
844 void Axis::setLabelsSuffix(const QString& suffix) {
845  Q_D(Axis);
846  if (suffix != d->labelsSuffix)
847  exec(new AxisSetLabelsSuffixCmd(d, suffix, ki18n("%1: set label suffix")));
848 }
849 
850 STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsOpacity, qreal, labelsOpacity, update);
851 void Axis::setLabelsOpacity(qreal opacity) {
852  Q_D(Axis);
853  if (opacity != d->labelsOpacity)
854  exec(new AxisSetLabelsOpacityCmd(d, opacity, ki18n("%1: set labels opacity")));
855 }
856 
857 //Major grid
858 STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorGridPen, QPen, majorGridPen, retransformMajorGrid);
859 void Axis::setMajorGridPen(const QPen& pen) {
860  Q_D(Axis);
861  if (pen != d->majorGridPen)
862  exec(new AxisSetMajorGridPenCmd(d, pen, ki18n("%1: set major grid style")));
863 }
864 
865 STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorGridOpacity, qreal, majorGridOpacity, updateGrid);
866 void Axis::setMajorGridOpacity(qreal opacity) {
867  Q_D(Axis);
868  if (opacity != d->majorGridOpacity)
869  exec(new AxisSetMajorGridOpacityCmd(d, opacity, ki18n("%1: set major grid opacity")));
870 }
871 
872 //Minor grid
873 STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorGridPen, QPen, minorGridPen, retransformMinorGrid);
874 void Axis::setMinorGridPen(const QPen& pen) {
875  Q_D(Axis);
876  if (pen != d->minorGridPen)
877  exec(new AxisSetMinorGridPenCmd(d, pen, ki18n("%1: set minor grid style")));
878 }
879 
880 STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorGridOpacity, qreal, minorGridOpacity, updateGrid);
881 void Axis::setMinorGridOpacity(qreal opacity) {
882  Q_D(Axis);
883  if (opacity != d->minorGridOpacity)
884  exec(new AxisSetMinorGridOpacityCmd(d, opacity, ki18n("%1: set minor grid opacity")));
885 }
886 
887 //##############################################################################
888 //#################################### SLOTs ################################
889 //##############################################################################
891  Q_D(Axis);
892  d->recalcShapeAndBoundingRect();
893 }
894 
896  Q_D(Axis);
897  d->retransformTicks();
898 }
899 
901  Q_D(Axis);
902  if (aspect == d->majorTicksColumn) {
903  d->majorTicksColumn = nullptr;
904  d->retransformTicks();
905  }
906 }
907 
909  Q_D(Axis);
910  if (aspect == d->minorTicksColumn) {
911  d->minorTicksColumn = nullptr;
912  d->retransformTicks();
913  }
914 }
915 
916 //##############################################################################
917 //###### SLOTs for changes triggered via QActions in the context menu ########
918 //##############################################################################
921  this->setOrientation(Axis::Orientation::Horizontal);
922  else
923  this->setOrientation(Axis::Orientation::Vertical);
924 }
925 
927  Q_D(const Axis);
928  QPen pen = d->linePen;
930  this->setLinePen(pen);
931 }
932 
934  Q_D(const Axis);
935  QPen pen = d->linePen;
937  this->setLinePen(pen);
938 }
939 
941  Q_D(const Axis);
942  this->setVisible(!d->isVisible());
943 }
944 
945 //#####################################################################
946 //################### Private implementation ##########################
947 //#####################################################################
948 AxisPrivate::AxisPrivate(Axis* owner) : gridItem(new AxisGrid(this)), q(owner) {
949  setFlag(QGraphicsItem::ItemIsSelectable, true);
950  setFlag(QGraphicsItem::ItemIsFocusable, true);
951  setAcceptHoverEvents(true);
952 }
953 
954 QString AxisPrivate::name() const{
955  return q->name();
956 }
957 
959  bool oldValue = isVisible();
960 
961  //When making a graphics item invisible, it gets deselected in the scene.
962  //In this case we don't want to deselect the item in the project explorer.
963  //We need to supress the deselection in the view.
964  auto* worksheet = static_cast<Worksheet*>(q->parent(AspectType::Worksheet));
965  worksheet->suppressSelectionChangedEvent(true);
966  setVisible(on);
967  gridItem->setVisible(on);
968  worksheet->suppressSelectionChangedEvent(false);
969 
970  emit q->visibilityChanged(on);
971  return oldValue;
972 }
973 
975  return boundingRectangle;
976 }
977 
978 /*!
979  Returns the shape of the XYCurve as a QPainterPath in local coordinates
980 */
981 QPainterPath AxisPrivate::shape() const{
982  return axisShape;
983 }
984 
985 /*!
986  recalculates the position of the axis on the worksheet
987  */
989  if (suppressRetransform || !plot)
990  return;
991 
992 // PERFTRACE(name().toLatin1() + ", AxisPrivate::retransform()");
993  m_suppressRecalc = true;
994  retransformLine();
995  m_suppressRecalc = false;
997 }
998 
1000  if (suppressRetransform)
1001  return;
1002 
1003  linePath = QPainterPath();
1004  lines.clear();
1005 
1006  QPointF startPoint;
1007  QPointF endPoint;
1008 
1011  offset = plot->yMax();
1012  else if (position == Axis::Position::Bottom)
1013  offset = plot->yMin();
1014  else if (position == Axis::Position::Centered)
1015  offset = plot->yMin() + (plot->yMax()-plot->yMin())/2;
1016 
1017  startPoint.setX(start);
1018  startPoint.setY(offset);
1019  endPoint.setX(end);
1020  endPoint.setY(offset);
1021  } else { // vertical
1023  offset = plot->xMin();
1024  else if (position == Axis::Position::Right)
1025  offset = plot->xMax();
1026  else if (position == Axis::Position::Centered)
1027  offset = plot->xMin() + (plot->xMax()-plot->xMin())/2;
1028 
1029  startPoint.setX(offset);
1030  startPoint.setY(start);
1031  endPoint.setY(end);
1032  endPoint.setX(offset);
1033  }
1034 
1035  lines.append(QLineF(startPoint, endPoint));
1037  for (const auto& line : lines) {
1038  linePath.moveTo(line.p1());
1039  linePath.lineTo(line.p2());
1040  }
1041 
1042  if (linePath.isEmpty()) {
1044  return;
1045  } else {
1046  retransformArrow();
1047  retransformTicks();
1048  }
1049 }
1050 
1052  if (suppressRetransform)
1053  return;
1054 
1055  arrowPath = QPainterPath();
1056  if (arrowType == Axis::ArrowType::NoArrow || lines.isEmpty()) {
1058  return;
1059  }
1060 
1062  const QPointF& endPoint = lines.at(lines.size()-1).p2();
1063  this->addArrow(endPoint, 1);
1064  }
1065 
1067  const QPointF& endPoint = lines.at(0).p1();
1068  this->addArrow(endPoint, -1);
1069  }
1070 
1072 }
1073 
1074 void AxisPrivate::addArrow(QPointF startPoint, int direction) {
1075  static const double cos_phi = cos(M_PI/6.);
1076 
1078  QPointF endPoint = QPointF(startPoint.x() + direction*arrowSize, startPoint.y());
1079  arrowPath.moveTo(startPoint);
1080  arrowPath.lineTo(endPoint);
1081 
1082  switch (arrowType) {
1084  break;
1086  arrowPath.moveTo(endPoint);
1087  arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/4, endPoint.y()-arrowSize/4*cos_phi));
1088  arrowPath.moveTo(endPoint);
1089  arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/4, endPoint.y()+arrowSize/4*cos_phi));
1090  break;
1092  arrowPath.moveTo(endPoint);
1093  arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/2, endPoint.y()-arrowSize/2*cos_phi));
1094  arrowPath.moveTo(endPoint);
1095  arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/2, endPoint.y()+arrowSize/2*cos_phi));
1096  break;
1098  arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/4, endPoint.y()-arrowSize/4*cos_phi));
1099  arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/4, endPoint.y()+arrowSize/4*cos_phi));
1100  arrowPath.lineTo(endPoint);
1101  break;
1103  arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/2, endPoint.y()-arrowSize/2*cos_phi));
1104  arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/2, endPoint.y()+arrowSize/2*cos_phi));
1105  arrowPath.lineTo(endPoint);
1106  break;
1108  arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/4, endPoint.y()-arrowSize/4*cos_phi));
1109  arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/8, endPoint.y()));
1110  arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/4, endPoint.y()+arrowSize/4*cos_phi));
1111  arrowPath.lineTo(endPoint);
1112  break;
1114  arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/2, endPoint.y()-arrowSize/2*cos_phi));
1115  arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/4, endPoint.y()));
1116  arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/2, endPoint.y()+arrowSize/2*cos_phi));
1117  arrowPath.lineTo(endPoint);
1118  break;
1119  }
1120  } else { //vertical orientation
1121  QPointF endPoint = QPointF(startPoint.x(), startPoint.y()-direction*arrowSize);
1122  arrowPath.moveTo(startPoint);
1123  arrowPath.lineTo(endPoint);
1124 
1125  switch (arrowType) {
1127  break;
1129  arrowPath.moveTo(endPoint);
1130  arrowPath.lineTo(QPointF(endPoint.x()-arrowSize/4*cos_phi, endPoint.y()+direction*arrowSize/4));
1131  arrowPath.moveTo(endPoint);
1132  arrowPath.lineTo(QPointF(endPoint.x()+arrowSize/4*cos_phi, endPoint.y()+direction*arrowSize/4));
1133  break;
1135  arrowPath.moveTo(endPoint);
1136  arrowPath.lineTo(QPointF(endPoint.x()-arrowSize/2*cos_phi, endPoint.y()+direction*arrowSize/2));
1137  arrowPath.moveTo(endPoint);
1138  arrowPath.lineTo(QPointF(endPoint.x()+arrowSize/2*cos_phi, endPoint.y()+direction*arrowSize/2));
1139  break;
1141  arrowPath.lineTo(QPointF(endPoint.x()-arrowSize/4*cos_phi, endPoint.y()+direction*arrowSize/4));
1142  arrowPath.lineTo(QPointF(endPoint.x()+arrowSize/4*cos_phi, endPoint.y()+direction*arrowSize/4));
1143  arrowPath.lineTo(endPoint);
1144  break;
1146  arrowPath.lineTo(QPointF(endPoint.x()-arrowSize/2*cos_phi, endPoint.y()+direction*arrowSize/2));
1147  arrowPath.lineTo(QPointF(endPoint.x()+arrowSize/2*cos_phi, endPoint.y()+direction*arrowSize/2));
1148  arrowPath.lineTo(endPoint);
1149  break;
1151  arrowPath.lineTo(QPointF(endPoint.x()-arrowSize/4*cos_phi, endPoint.y()+direction*arrowSize/4));
1152  arrowPath.lineTo(QPointF(endPoint.x(), endPoint.y()+direction*arrowSize/8));
1153  arrowPath.lineTo(QPointF(endPoint.x()+arrowSize/4*cos_phi, endPoint.y()+direction*arrowSize/4));
1154  arrowPath.lineTo(endPoint);
1155  break;
1157  arrowPath.lineTo(QPointF(endPoint.x()-arrowSize/2*cos_phi, endPoint.y()+direction*arrowSize/2));
1158  arrowPath.lineTo(QPointF(endPoint.x(), endPoint.y()+direction*arrowSize/4));
1159  arrowPath.lineTo(QPointF(endPoint.x()+arrowSize/2*cos_phi, endPoint.y()+direction*arrowSize/2));
1160  arrowPath.lineTo(endPoint);
1161  break;
1162  }
1163  }
1164 }
1165 
1166 //! helper function for retransformTicks()
1167 bool AxisPrivate::transformAnchor(QPointF* anchorPoint) {
1168  QVector<QPointF> points;
1169  points.append(*anchorPoint);
1170  points = cSystem->mapLogicalToScene(points);
1171 
1172  if (points.count() != 1) { // point is not mappable or in a coordinate gap
1173  return false;
1174  } else {
1175  *anchorPoint = points.at(0);
1176  return true;
1177  }
1178 }
1179 
1180 /*!
1181  recalculates the position of the axis ticks.
1182  */
1184  if (suppressRetransform)
1185  return;
1186 
1187  //TODO: check that start and end are > 0 for log and >=0 for sqrt, etc.
1188 
1189  majorTicksPath = QPainterPath();
1190  minorTicksPath = QPainterPath();
1191  majorTickPoints.clear();
1192  minorTickPoints.clear();
1193  tickLabelValues.clear();
1194 
1196  retransformTickLabelPositions(); //this calls recalcShapeAndBoundingRect()
1197  return;
1198  }
1199 
1200  //determine the increment for the major ticks
1201  double majorTicksIncrement = 0;
1202  int tmpMajorTicksNumber = 0;
1204  //the total number of major ticks is given - > determine the increment
1205  tmpMajorTicksNumber = majorTicksNumber;
1206  switch (scale) {
1207  case Axis::Scale::Linear:
1208  majorTicksIncrement = end-start;
1209  break;
1210  case Axis::Scale::Log10:
1211  majorTicksIncrement = log10(end)-log10(start);
1212  break;
1213  case Axis::Scale::Log2:
1214  majorTicksIncrement = log2(end)-log2(start);
1215  break;
1216  case Axis::Scale::Ln:
1217  majorTicksIncrement = log(end)-log(start);
1218  break;
1219  case Axis::Scale::Sqrt:
1220  majorTicksIncrement = sqrt(end)-sqrt(start);
1221  break;
1222  case Axis::Scale::X2:
1223  majorTicksIncrement = end*end - start*start;
1224  }
1225  if (majorTicksNumber > 1)
1226  majorTicksIncrement /= majorTicksNumber - 1;
1227  } else if (majorTicksType == Axis::TicksType::Spacing) {
1228  //the increment of the major ticks is given -> determine the number
1229  majorTicksIncrement = majorTicksSpacing * GSL_SIGN(end-start);
1230  switch (scale) {
1231  case Axis::Scale::Linear:
1232  tmpMajorTicksNumber = qRound((end-start)/majorTicksIncrement + 1);
1233  break;
1234  case Axis::Scale::Log10:
1235  tmpMajorTicksNumber = qRound((log10(end)-log10(start))/majorTicksIncrement + 1);
1236  break;
1237  case Axis::Scale::Log2:
1238  tmpMajorTicksNumber = qRound((log2(end)-log2(start))/majorTicksIncrement + 1);
1239  break;
1240  case Axis::Scale::Ln:
1241  tmpMajorTicksNumber = qRound((log(end)-log(start))/majorTicksIncrement + 1);
1242  break;
1243  case Axis::Scale::Sqrt:
1244  tmpMajorTicksNumber = qRound((sqrt(end)-sqrt(start))/majorTicksIncrement + 1);
1245  break;
1246  case Axis::Scale::X2:
1247  tmpMajorTicksNumber = qRound((end*end - start*start)/majorTicksIncrement + 1);
1248  }
1249  } else { //custom column was provided
1250  if (majorTicksColumn) {
1251  tmpMajorTicksNumber = majorTicksColumn->rowCount();
1252  } else {
1253  retransformTickLabelPositions(); //this calls recalcShapeAndBoundingRect()
1254  return;
1255  }
1256  }
1257 
1258  int tmpMinorTicksNumber;
1260  tmpMinorTicksNumber = minorTicksNumber;
1262  tmpMinorTicksNumber = fabs(end - start)/ (majorTicksNumber - 1)/minorTicksIncrement - 1;
1263  else
1264  (minorTicksColumn) ? tmpMinorTicksNumber = minorTicksColumn->rowCount() : tmpMinorTicksNumber = 0;
1265 
1266  QPointF anchorPoint;
1267  QPointF startPoint;
1268  QPointF endPoint;
1269  qreal majorTickPos = 0.0;
1270  qreal minorTickPos;
1271  qreal nextMajorTickPos = 0.0;
1272  const int xDirection = cSystem->xDirection();
1273  const int yDirection = cSystem->yDirection();
1274  const double middleX = plot->xMin() + (plot->xMax() - plot->xMin())/2;
1275  const double middleY = plot->yMin() + (plot->yMax() - plot->yMin())/2;
1276  bool valid;
1277 
1278  //DEBUG("tmpMajorTicksNumber = " << tmpMajorTicksNumber)
1279  for (int iMajor = 0; iMajor < tmpMajorTicksNumber; iMajor++) {
1280  //DEBUG("major tick " << iMajor)
1281  //calculate major tick's position
1283  switch (scale) {
1284  case Axis::Scale::Linear:
1285  majorTickPos = start + majorTicksIncrement * iMajor;
1286  nextMajorTickPos = majorTickPos + majorTicksIncrement;
1287  break;
1288  case Axis::Scale::Log10:
1289  majorTickPos = start * pow(10, majorTicksIncrement*iMajor);
1290  nextMajorTickPos = majorTickPos * pow(10, majorTicksIncrement);
1291  break;
1292  case Axis::Scale::Log2:
1293  majorTickPos = start * pow(2, majorTicksIncrement*iMajor);
1294  nextMajorTickPos = majorTickPos * pow(2, majorTicksIncrement);
1295  break;
1296  case Axis::Scale::Ln:
1297  majorTickPos = start * exp(majorTicksIncrement*iMajor);
1298  nextMajorTickPos = majorTickPos * exp(majorTicksIncrement);
1299  break;
1300  case Axis::Scale::Sqrt:
1301  majorTickPos = pow(sqrt(start) + majorTicksIncrement*iMajor, 2);
1302  nextMajorTickPos = pow(sqrt(start) + majorTicksIncrement*(iMajor+1), 2);
1303  break;
1304  case Axis::Scale::X2:
1305  majorTickPos = sqrt(start*start + majorTicksIncrement*iMajor);
1306  nextMajorTickPos = sqrt(start*start + majorTicksIncrement*(iMajor+1));
1307  break;
1308  }
1309  } else { // custom column
1310  if (!majorTicksColumn->isValid(iMajor) || majorTicksColumn->isMasked(iMajor))
1311  continue;
1312  majorTickPos = majorTicksColumn->valueAt(iMajor);
1313  // set next major tick pos for minor ticks
1314  if (iMajor < tmpMajorTicksNumber - 1) {
1315  if (majorTicksColumn->isValid(iMajor+1) && !majorTicksColumn->isMasked(iMajor+1))
1316  nextMajorTickPos = majorTicksColumn->valueAt(iMajor+1);
1317  } else // last major tick
1318  tmpMinorTicksNumber = 0;
1319  }
1320 
1321  //calculate start and end points for major tick's line
1324  anchorPoint.setX(majorTickPos);
1325  anchorPoint.setY(offset);
1326  valid = transformAnchor(&anchorPoint);
1327  if (valid) {
1328  if (offset < middleY) {
1329  startPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksIn) ? yDirection * majorTicksLength : 0);
1330  endPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksOut) ? -yDirection * majorTicksLength : 0);
1331  } else {
1332  startPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksOut) ? yDirection * majorTicksLength : 0);
1333  endPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksIn) ? -yDirection * majorTicksLength : 0);
1334  }
1335  }
1336  } else { // vertical
1337  anchorPoint.setY(majorTickPos);
1338  anchorPoint.setX(offset);
1339  valid = transformAnchor(&anchorPoint);
1340 
1341  if (valid) {
1342  if (offset < middleX) {
1343  startPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksIn) ? xDirection * majorTicksLength : 0, 0);
1344  endPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksOut) ? -xDirection * majorTicksLength : 0, 0);
1345  } else {
1346  startPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksOut) ? xDirection * majorTicksLength : 0, 0);
1347  endPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksIn) ? -xDirection * majorTicksLength : 0, 0);
1348  }
1349  }
1350  }
1351 
1352  double value = scalingFactor * majorTickPos + zeroOffset;
1353 
1354  //if custom column is used, we can have duplicated values in it and we need only unique values
1355  if (majorTicksType == Axis::TicksType::CustomColumn && tickLabelValues.indexOf(value) != -1)
1356  valid = false;
1357 
1358  //add major tick's line to the painter path
1359  if (valid) {
1360  majorTicksPath.moveTo(startPoint);
1361  majorTicksPath.lineTo(endPoint);
1362  majorTickPoints << anchorPoint;
1363  tickLabelValues << value;
1364  }
1365  }
1366 
1367  //minor ticks
1368  //DEBUG(" tmpMinorTicksNumber = " << tmpMinorTicksNumber)
1369  if (Axis::noTicks != minorTicksDirection && tmpMajorTicksNumber > 1 && tmpMinorTicksNumber > 0 && iMajor < tmpMajorTicksNumber - 1 && nextMajorTickPos != majorTickPos) {
1370  //minor ticks are placed at equidistant positions independent of the selected scaling for the major ticks positions
1371  double minorTicksIncrement = (nextMajorTickPos - majorTickPos)/(tmpMinorTicksNumber + 1);
1372  //DEBUG(" nextMajorTickPos = " << nextMajorTickPos)
1373  //DEBUG(" majorTickPos = " << majorTickPos)
1374  //DEBUG(" minorTicksIncrement = " << minorTicksIncrement)
1375 
1376  for (int iMinor = 0; iMinor < tmpMinorTicksNumber; iMinor++) {
1377  //calculate minor tick's position
1379  minorTickPos = majorTickPos + (iMinor + 1) * minorTicksIncrement;
1380  } else {
1381  if (!minorTicksColumn->isValid(iMinor) || minorTicksColumn->isMasked(iMinor))
1382  continue;
1383  minorTickPos = minorTicksColumn->valueAt(iMinor);
1384 
1385  //in the case a custom column is used for the minor ticks, we draw them _once_ for the whole range of the axis.
1386  //execute the minor ticks loop only once.
1387  if (iMajor > 0)
1388  break;
1389  }
1390  //DEBUG(" minorTickPos = " << minorTickPos)
1391 
1392  //calculate start and end points for minor tick's line
1394  anchorPoint.setX(minorTickPos);
1395  anchorPoint.setY(offset);
1396  valid = transformAnchor(&anchorPoint);
1397 
1398  if (valid) {
1399  if (offset < middleY) {
1400  startPoint = anchorPoint + QPointF(0, (minorTicksDirection & Axis::ticksIn) ? yDirection * minorTicksLength : 0);
1401  endPoint = anchorPoint + QPointF(0, (minorTicksDirection & Axis::ticksOut) ? -yDirection * minorTicksLength : 0);
1402  } else {
1403  startPoint = anchorPoint + QPointF(0, (minorTicksDirection & Axis::ticksOut) ? yDirection * minorTicksLength : 0);
1404  endPoint = anchorPoint + QPointF(0, (minorTicksDirection & Axis::ticksIn) ? -yDirection * minorTicksLength : 0);
1405  }
1406  }
1407  } else { // vertical
1408  anchorPoint.setY(minorTickPos);
1409  anchorPoint.setX(offset);
1410  valid = transformAnchor(&anchorPoint);
1411 
1412  if (valid) {
1413  if (offset < middleX) {
1414  startPoint = anchorPoint + QPointF((minorTicksDirection & Axis::ticksIn) ? xDirection * minorTicksLength : 0, 0);
1415  endPoint = anchorPoint + QPointF((minorTicksDirection & Axis::ticksOut) ? -xDirection * minorTicksLength : 0, 0);
1416  } else {
1417  startPoint = anchorPoint + QPointF((minorTicksDirection & Axis::ticksOut) ? xDirection * minorTicksLength : 0, 0);
1418  endPoint = anchorPoint + QPointF((minorTicksDirection & Axis::ticksIn) ? -xDirection * minorTicksLength : 0, 0);
1419  }
1420  }
1421  }
1422 
1423  //add minor tick's line to the painter path
1424  if (valid) {
1425  minorTicksPath.moveTo(startPoint);
1426  minorTicksPath.lineTo(endPoint);
1427  minorTickPoints << anchorPoint;
1428  }
1429  }
1430  }
1431  }
1432 
1433  //tick positions where changed -> update the position of the tick labels and grid lines
1437 }
1438 
1439 /*!
1440  creates the tick label strings starting with the most optimal
1441  (=the smallest possible number of float digits) precision for the floats
1442 */
1444  DEBUG(Q_FUNC_INFO)
1445  if (suppressRetransform)
1446  return;
1447 
1448  if (labelsAutoPrecision) {
1449  //check, whether we need to increase the current precision
1450  int newPrecision = upperLabelsPrecision(labelsPrecision, labelsFormat);
1451  if (newPrecision != labelsPrecision) {
1452  labelsPrecision = newPrecision;
1454  } else {
1455  //check, whether we can reduce the current precision
1457  if (newPrecision != labelsPrecision) {
1458  labelsPrecision = newPrecision;
1460  }
1461  }
1462  }
1463  //DEBUG("labelsPrecision =" << labelsPrecision);
1464 
1465  //automatically switch from 'decimal' to 'scientific' format for big numbers (>10^4)
1466  //and back to decimal when the numbers get smaller after the auto-switch again
1468  for (auto value : tickLabelValues) {
1469  if (std::abs(value) > 1e4) {
1472  labelsFormatAutoChanged = true;
1473  break;
1474  }
1475  }
1476  } else if (labelsFormatAutoChanged ) {
1477  //check whether we still have big numbers
1478  bool changeBack = true;
1479  for (auto value : tickLabelValues) {
1480  if (std::abs(value) > 1e4) {
1481  changeBack = false;
1482  break;
1483  }
1484  }
1485 
1486  if (changeBack) {
1487  labelsFormatAutoChanged = false;
1490  }
1491  }
1492 
1493  tickLabelStrings.clear();
1494  QString str;
1499  QString nullStr = numberLocale.toString(0., 'f', labelsPrecision);
1500  for (const auto value : tickLabelValues) {
1501  str = numberLocale.toString(value, 'f', labelsPrecision);
1502  if (str == "-" + nullStr) str = nullStr;
1503  str = labelsPrefix + str + labelsSuffix;
1504  tickLabelStrings << str;
1505  }
1507  QString nullStr = numberLocale.toString(0., 'e', labelsPrecision);
1508  for (const auto value : tickLabelValues) {
1509  if (value == 0) // just show "0"
1510  str = numberLocale.toString(value, 'f', 0);
1511  else
1512  str = numberLocale.toString(value, 'e', labelsPrecision);
1513  if (str == "-" + nullStr) str = nullStr;
1514  str = labelsPrefix + str + labelsSuffix;
1515  tickLabelStrings << str;
1516  }
1518  for (const auto value : tickLabelValues) {
1519  if (value == 0) // just show "0"
1520  str = numberLocale.toString(value, 'f', 0);
1521  else {
1522  str = "10<sup>" + numberLocale.toString(log10(qAbs(value)), 'f', labelsPrecision) + "</sup>";
1523  if (value < 0)
1524  str.prepend("-");
1525  }
1526  str = labelsPrefix + str + labelsSuffix;
1527  tickLabelStrings << str;
1528  }
1529  } else if (labelsFormat == Axis::LabelsFormat::Powers2) {
1530  for (const auto value : tickLabelValues) {
1531  if (value == 0) // just show "0"
1532  str = numberLocale.toString(value, 'f', 0);
1533  else {
1534  str = "2<span style=\"vertical-align:super\">" + numberLocale.toString(log2(qAbs(value)), 'f', labelsPrecision) + "</span>";
1535  if (value < 0)
1536  str.prepend("-");
1537  }
1538  str = labelsPrefix + str + labelsSuffix;
1539  tickLabelStrings << str;
1540  }
1541  } else if (labelsFormat == Axis::LabelsFormat::PowersE) {
1542  for (const auto value : tickLabelValues) {
1543  if (value == 0) // just show "0"
1544  str = numberLocale.toString(value, 'f', 0);
1545  else {
1546  str = "e<span style=\"vertical-align:super\">" + numberLocale.toString(log(qAbs(value)), 'f', labelsPrecision) + "</span>";
1547  if (value < 0)
1548  str.prepend("-");
1549  }
1550  str = labelsPrefix + str + labelsSuffix;
1551  tickLabelStrings << str;
1552  }
1554  for (const auto value : tickLabelValues) {
1555  if (value == 0) // just show "0"
1556  str = numberLocale.toString(value, 'f', 0);
1557  else
1558  str = "<span>" + numberLocale.toString(value / M_PI, 'f', labelsPrecision) + "</span>" + QChar(0x03C0);
1559  str = labelsPrefix + str + labelsSuffix;
1560  tickLabelStrings << str;
1561  }
1562  }
1563  } else {
1564  for (const auto value : tickLabelValues) {
1565  QDateTime dateTime;
1566  dateTime.setMSecsSinceEpoch(value);
1567  str = dateTime.toString(labelsDateTimeFormat);
1568  str = labelsPrefix + str + labelsSuffix;
1569  tickLabelStrings << str;
1570  }
1571  }
1572 
1573  //recalculate the position of the tick labels
1575 }
1576 
1577 /*!
1578  returns the smallest upper limit for the precision
1579  where no duplicates for the tick label float occur.
1580  */
1582  DEBUG(Q_FUNC_INFO << ", precision = " << precision);
1583 
1584  // avoid problems with zero range axis
1585  if (tickLabelValues.isEmpty() || qFuzzyCompare(tickLabelValues.constFirst(), tickLabelValues.constLast())) {
1586  DEBUG(Q_FUNC_INFO << ", zero range axis detected. ticklabel values: ")
1587  QDEBUG(Q_FUNC_INFO << tickLabelValues)
1588  return 0;
1589  }
1590 
1591  //round float to the current precision and look for duplicates.
1592  //if there are duplicates, increase the precision.
1593  QVector<double> tempValues;
1594  switch (format) {
1597  for (const auto value : tickLabelValues)
1598  tempValues.append( nsl_math_round_places(value, precision) );
1599  break;
1601  for (const auto value : tickLabelValues)
1602  tempValues.append( nsl_math_round_precision(value, precision) );
1603  break;
1605  for (const auto value : tickLabelValues)
1606  tempValues.append( nsl_math_round_places(log10(qAbs(value)), precision) );
1607  break;
1609  for (const auto value : tickLabelValues)
1610  tempValues.append( nsl_math_round_places(log2(qAbs(value)), precision) );
1611  break;
1613  for (const auto value : tickLabelValues)
1614  tempValues.append( nsl_math_round_places(log(qAbs(value)), precision) );
1615  }
1616 
1617  for (int i = 0; i < tempValues.size(); ++i) {
1618  for (int j = 0; j < tempValues.size(); ++j) {
1619  if (i == j)
1620  continue;
1621 
1622  //duplicate for the current precision found, increase the precision and check again
1623  if (tempValues.at(i) == tempValues.at(j))
1624  return upperLabelsPrecision(precision + 1, format);
1625  }
1626  }
1627 
1628  //no duplicates for the current precision found: return the current value
1629  DEBUG(Q_FUNC_INFO << ", upper precision = " << precision);
1630  return precision;
1631 }
1632 
1633 /*!
1634  returns highest lower limit for the precision
1635  where no duplicates for the tick label float occur.
1636 */
1638  DEBUG(Q_FUNC_INFO << ", precision = " << precision);
1639  //round value to the current precision and look for duplicates.
1640  //if there are duplicates, decrease the precision.
1641  QVector<double> tempValues;
1642  switch (format) {
1645  for (auto value : tickLabelValues)
1646  tempValues.append( nsl_math_round_places(value, precision-1) );
1647  break;
1649  for (auto value : tickLabelValues)
1650  tempValues.append( nsl_math_round_precision(value, precision-1) );
1651  break;
1653  for (auto value : tickLabelValues)
1654  tempValues.append( nsl_math_round_places(log10(qAbs(value)), precision-1) );
1655  break;
1657  for (auto value : tickLabelValues)
1658  tempValues.append( nsl_math_round_places(log2(qAbs(value)), precision-1) );
1659  break;
1661  for (auto value : tickLabelValues)
1662  tempValues.append( nsl_math_round_places(log(qAbs(value)), precision-1) );
1663  }
1664 
1665 
1666  //check whether we have duplicates with reduced precision
1667  //-> current precision cannot be reduced, return the current value
1668  for (int i = 0; i < tempValues.size(); ++i) {
1669  for (int j = 0; j < tempValues.size(); ++j) {
1670  if (i == j) continue;
1671  if (tempValues.at(i) == tempValues.at(j))
1672  return precision;
1673  }
1674  }
1675 
1676  if (precision == 0) {
1677  bool hasDoubles = false;
1678  for (auto value : tickLabelValues) {
1679  if (floor(value) != value) {
1680  hasDoubles = true;
1681  break;
1682  }
1683  }
1684 
1685  //if we have double values we don't want to show them as integers, keep at least one float digit.
1686  if (hasDoubles)
1687  return 1;
1688  else
1689  return 0;
1690  } else {
1691  //no duplicates found, reduce further, and check again
1692  return lowerLabelsPrecision(precision - 1, format);
1693  }
1694 }
1695 
1696 /*!
1697  recalculates the position of the tick labels.
1698  Called when the geometry related properties (position, offset, font size, suffix, prefix) of the labels are changed.
1699  */
1701  tickLabelPoints.clear();
1704  return;
1705  }
1706 
1707  QFontMetrics fm(labelsFont);
1708  double width = 0;
1709  double height = fm.ascent();
1710  QPointF pos;
1711  const double middleX = plot->xMin() + (plot->xMax() - plot->xMin())/2;
1712  const double middleY = plot->yMin() + (plot->yMax() - plot->yMin())/2;
1713  const int xDirection = cSystem->xDirection();
1714  const int yDirection = cSystem->yDirection();
1715 
1716  QPointF startPoint, endPoint, anchorPoint;
1717 
1718  QTextDocument td;
1719  td.setDefaultFont(labelsFont);
1720  const double cosine = cos(labelsRotationAngle * M_PI / 180.); // calculate only one time
1721  const double sine = sin(labelsRotationAngle * M_PI / 180.); // calculate only one time
1722  for ( int i = 0; i < majorTickPoints.size(); i++ ) {
1726  width = fm.boundingRect(tickLabelStrings.at(i)).width();
1727  } else {
1728  td.setHtml(tickLabelStrings.at(i));
1729  width = td.size().width();
1730  height = td.size().height();
1731  }
1732  } else { // Datetime
1733  width = fm.boundingRect(tickLabelStrings.at(i)).width();
1734  }
1735 
1736  const double diffx = cosine * width;
1737  const double diffy = sine * width;
1738  anchorPoint = majorTickPoints.at(i);
1739 
1740  //center align all labels with respect to the end point of the tick line
1742  if (offset < middleY) {
1743  startPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksIn) ? yDirection * majorTicksLength : 0);
1744  endPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksOut) ? -yDirection * majorTicksLength : 0);
1745  } else {
1746  startPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksOut) ? yDirection * majorTicksLength : 0);
1747  endPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksIn) ? -yDirection * majorTicksLength : 0);
1748  }
1749 
1750  // for rotated labels (angle is not zero), align label's corner at the position of the tick
1751  if (fabs(fabs(labelsRotationAngle) - 180.) < 1.e-2) { // +-180°
1753  pos.setX(endPoint.x() + width/2);
1754  pos.setY(endPoint.y() + labelsOffset );
1755  } else {
1756  pos.setX(startPoint.x() + width/2);
1757  pos.setY(startPoint.y() - height - labelsOffset);
1758  }
1759  } else if (labelsRotationAngle <= -0.01) { // [-0.01°, -180°)
1761  pos.setX(endPoint.x() + sine * height/2);
1762  pos.setY(endPoint.y() + labelsOffset + cosine * height/2);
1763  } else {
1764  pos.setX(startPoint.x() + sine * height/2 - diffx);
1765  pos.setY(startPoint.y() - labelsOffset + cosine * height/2 + diffy);
1766  }
1767  } else if (labelsRotationAngle >= 0.01) { // [0.01°, 180°)
1769  pos.setX(endPoint.x() - diffx + sine * height/2);
1770  pos.setY(endPoint.y() + labelsOffset + diffy + cosine * height/2);
1771  } else {
1772  pos.setX(startPoint.x() + sine * height/2);
1773  pos.setY(startPoint.y() - labelsOffset + cosine * height/2);
1774  }
1775  } else { // 0°
1777  pos.setX(endPoint.x() - width/2);
1778  pos.setY(endPoint.y() + height + labelsOffset);
1779  } else {
1780  pos.setX(startPoint.x() - width/2);
1781  pos.setY(startPoint.y() - labelsOffset);
1782  }
1783  }
1784  // ---------------------- vertical -------------------------
1785  } else {
1786  if (offset < middleX) {
1787  startPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksIn) ? xDirection * majorTicksLength : 0, 0);
1788  endPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksOut) ? -xDirection * majorTicksLength : 0, 0);
1789  } else {
1790  startPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksOut) ? xDirection * majorTicksLength : 0, 0);
1791  endPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksIn) ? -xDirection * majorTicksLength : 0, 0);
1792  }
1793 
1794  if (fabs(labelsRotationAngle - 90.) < 1.e-2) { // +90°
1796  pos.setX(endPoint.x() - labelsOffset);
1797  pos.setY(endPoint.y() + width/2 );
1798  } else {
1799  pos.setX(startPoint.x() + labelsOffset);
1800  pos.setY(startPoint.y() + width/2);
1801  }
1802  } else if (fabs(labelsRotationAngle + 90.) < 1.e-2) { // -90°
1804  pos.setX(endPoint.x() - labelsOffset - height);
1805  pos.setY(endPoint.y() - width/2);
1806  } else {
1807  pos.setX(startPoint.x() + labelsOffset);
1808  pos.setY(startPoint.y() - width/2);
1809  }
1810  } else if (fabs(fabs(labelsRotationAngle) - 180.) < 1.e-2) { // +-180°
1812  pos.setX(endPoint.x() - labelsOffset);
1813  pos.setY(endPoint.y() - height/2);
1814  } else {
1815  pos.setX(startPoint.x() + labelsOffset + width);
1816  pos.setY(startPoint.y() - height/2);
1817  }
1818  } else if (fabs(labelsRotationAngle) >= 0.01 && fabs(labelsRotationAngle) <= 89.99) { // [0.01°, 90°)
1820  // left
1821  pos.setX(endPoint.x() - labelsOffset - diffx + sine * height/2);
1822  pos.setY(endPoint.y() + cosine * height/2 + diffy);
1823  } else {
1824  pos.setX(startPoint.x() + labelsOffset + sine * height/2);
1825  pos.setY(startPoint.y() + cosine * height/2);
1826  }
1827  } else if (fabs(labelsRotationAngle) >= 90.01 && fabs(labelsRotationAngle) <= 179.99) { // [90.01, 180)
1829  // left
1830  pos.setX(endPoint.x() - labelsOffset + sine * height/2);
1831  pos.setY(endPoint.y() + cosine * height/2);
1832  } else {
1833  pos.setX(startPoint.x() + labelsOffset - diffx + sine * height/2);
1834  pos.setY(startPoint.y() + diffy + cosine * height/2);
1835  }
1836  } else { // 0°
1838  pos.setX(endPoint.x() - width - labelsOffset);
1839  pos.setY(endPoint.y() + height/2);
1840  } else {
1841  pos.setX(startPoint.x() + labelsOffset);
1842  pos.setY(startPoint.y() + height/2);
1843  }
1844  }
1845  }
1846  tickLabelPoints << pos;
1847  }
1848 
1850 }
1851 
1853  if (suppressRetransform)
1854  return;
1855 
1856  majorGridPath = QPainterPath();
1857  if (majorGridPen.style() == Qt::NoPen || majorTickPoints.size() == 0) {
1859  return;
1860  }
1861 
1862  //major tick points are already in scene coordinates, convert them back to logical...
1863  //TODO: mapping should work without SuppressPageClipping-flag, check float comparisons in the map-function.
1864  //Currently, grid lines disappear somtimes without this flag
1866 
1867  if (logicalMajorTickPoints.isEmpty())
1868  return;
1869 
1870  //TODO:
1871  //when iterating over all grid lines, skip the first and the last points for auto scaled axes,
1872  //since we don't want to paint any grid lines at the plot boundaries
1873  bool skipLowestTick, skipUpperTick;
1874  if (orientation == Axis::Orientation::Horizontal) { //horizontal axis
1875  skipLowestTick = qFuzzyCompare(logicalMajorTickPoints.at(0).x(), plot->xMin());
1876  skipUpperTick = qFuzzyCompare(logicalMajorTickPoints.at(logicalMajorTickPoints.size()-1).x(), plot->xMax());
1877  } else {
1878  skipLowestTick = qFuzzyCompare(logicalMajorTickPoints.at(0).y(), plot->yMin());
1879  skipUpperTick = qFuzzyCompare(logicalMajorTickPoints.at(logicalMajorTickPoints.size()-1).y(), plot->yMax());
1880  }
1881 
1882  int start, end;
1883  if (skipLowestTick) {
1884  if (logicalMajorTickPoints.size() > 1)
1885  start = 1;
1886  else
1887  start = 0;
1888  } else {
1889  start = 0;
1890  }
1891 
1892  if (skipUpperTick) {
1893  if (logicalMajorTickPoints.size() > 1)
1894  end = logicalMajorTickPoints.size() - 1;
1895  else
1896  end = 0;
1897 
1898  } else {
1899  end = logicalMajorTickPoints.size();
1900  }
1901 
1903  if (orientation == Axis::Orientation::Horizontal) { //horizontal axis
1904  double yMin = plot->yMin();
1905  double yMax = plot->yMax();
1906 
1907  for (int i = start; i < end; ++i) {
1908  const QPointF& point = logicalMajorTickPoints.at(i);
1909  lines.append( QLineF(point.x(), yMin, point.x(), yMax) );
1910  }
1911  } else { //vertical axis
1912  double xMin = plot->xMin();
1913  double xMax = plot->xMax();
1914 
1915  //skip the first and the last points, since we don't want to paint any grid lines at the plot boundaries
1916  for (int i = start; i < end; ++i) {
1917  const QPointF& point = logicalMajorTickPoints.at(i);
1918  lines.append( QLineF(xMin, point.y(), xMax, point.y()) );
1919  }
1920  }
1921 
1923  for (const auto& line : lines) {
1924  majorGridPath.moveTo(line.p1());
1925  majorGridPath.lineTo(line.p2());
1926  }
1927 
1929 }
1930 
1932  if (suppressRetransform)
1933  return;
1934 
1935  minorGridPath = QPainterPath();
1936  if (minorGridPen.style() == Qt::NoPen) {
1938  return;
1939  }
1940 
1941  //minor tick points are already in scene coordinates, convert them back to logical...
1942  //TODO: mapping should work without SuppressPageClipping-flag, check float comparisons in the map-function.
1943  //Currently, grid lines disappear somtimes without this flag
1945 
1947  if (orientation == Axis::Orientation::Horizontal) { //horizontal axis
1948  double yMin = plot->yMin();
1949  double yMax = plot->yMax();
1950 
1951  for (const auto point : logicalMinorTickPoints)
1952  lines.append( QLineF(point.x(), yMin, point.x(), yMax) );
1953  } else { //vertical axis
1954  double xMin = plot->xMin();
1955  double xMax = plot->xMax();
1956 
1957  for (const auto point: logicalMinorTickPoints)
1958  lines.append( QLineF(xMin, point.y(), xMax, point.y()) );
1959  }
1960 
1962  for (const auto& line : lines) {
1963  minorGridPath.moveTo(line.p1());
1964  minorGridPath.lineTo(line.p2());
1965  }
1966 
1968 }
1969 
1970 /*!
1971  * called when the opacity of the grid was changes, update the grid graphics item
1972  */
1973 //TODO: this function is only needed for loaded projects where update() doesn't seem to be enough
1974 //and we have to call gridItem->update() explicitly.
1975 //This is not required for newly created plots/axes. Why is this difference?
1977  gridItem->update();
1978 }
1979 
1981  if (m_suppressRecalc)
1982  return;
1983 
1984  prepareGeometryChange();
1985 
1986  if (linePath.isEmpty()) {
1987  axisShape = QPainterPath();
1988  boundingRectangle = QRectF();
1989  title->setPositionInvalid(true);
1991  return;
1992  } else {
1993  title->setPositionInvalid(false);
1994  }
1995 
2000 
2001  QPainterPath tickLabelsPath = QPainterPath();
2003  QTransform trafo;
2004  QPainterPath tempPath;
2005  QFontMetrics fm(labelsFont);
2006  QTextDocument td;
2007  td.setDefaultFont(labelsFont);
2008  for (int i = 0; i < tickLabelPoints.size(); i++) {
2009  tempPath = QPainterPath();
2011  tempPath.addRect(fm.boundingRect(tickLabelStrings.at(i)));
2012  } else {
2013  td.setHtml(tickLabelStrings.at(i));
2014  tempPath.addRect(QRectF(0, -td.size().height(), td.size().width(), td.size().height()));
2015  }
2016 
2017  trafo.reset();
2018  trafo.translate( tickLabelPoints.at(i).x(), tickLabelPoints.at(i).y() );
2019 
2020  trafo.rotate(-labelsRotationAngle);
2021  tempPath = trafo.map(tempPath);
2022 
2023  tickLabelsPath.addPath(WorksheetElement::shapeFromPath(tempPath, linePen));
2024  }
2025  axisShape.addPath(WorksheetElement::shapeFromPath(tickLabelsPath, QPen()));
2026  }
2027 
2028  //add title label, if available
2029  if ( title->isVisible() && !title->text().text.isEmpty() ) {
2030  const QRectF& titleRect = title->graphicsItem()->boundingRect();
2031  if (titleRect.width() != 0.0 && titleRect.height() != 0.0) {
2032  //determine the new position of the title label:
2033  //we calculate the new position here and not in retransform(),
2034  //since it depends on the size and position of the tick labels, tickLabelsPath, available here.
2035  QRectF rect = linePath.boundingRect();
2036  qreal offsetX = titleOffsetX; //the distance to the axis line
2037  qreal offsetY = titleOffsetY; //the distance to the axis line
2039  offsetY -= titleRect.height()/2;
2041  offsetY -= labelsOffset + tickLabelsPath.boundingRect().height();
2042  title->setPosition( QPointF( (rect.topLeft().x() + rect.topRight().x())/2 + titleOffsetX, rect.bottomLeft().y() - offsetY ) );
2043  } else {
2044  offsetX -= titleRect.width()/2;
2046  offsetX -= labelsOffset+ tickLabelsPath.boundingRect().width();
2047  title->setPosition( QPointF( rect.topLeft().x() + offsetX, (rect.topLeft().y() + rect.bottomLeft().y())/2 - titleOffsetY) );
2048  }
2049  axisShape.addPath(WorksheetElement::shapeFromPath(title->graphicsItem()->mapToParent(title->graphicsItem()->shape()), linePen));
2050  }
2051  }
2052 
2053  boundingRectangle = axisShape.boundingRect();
2054 
2055  //if the axis goes beyond the current bounding box of the plot (too high offset is used, too long labels etc.)
2056  //request a prepareGeometryChange() for the plot in order to properly keep track of geometry changes
2057  if (plot)
2059 }
2060 
2061 /*!
2062  paints the content of the axis. Reimplemented from \c QGraphicsItem.
2063  \sa QGraphicsItem::paint()
2064  */
2065 void AxisPrivate::paint(QPainter *painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
2066  Q_UNUSED(option)
2067  Q_UNUSED(widget)
2068 
2069  if (!isVisible())
2070  return;
2071 
2072  if (linePath.isEmpty())
2073  return;
2074 
2075  //draw the line
2076  if (linePen.style() != Qt::NoPen) {
2077  painter->setOpacity(lineOpacity);
2078  painter->setPen(linePen);
2079  painter->setBrush(Qt::SolidPattern);
2080  painter->drawPath(linePath);
2081 
2082  //draw the arrow
2084  painter->drawPath(arrowPath);
2085  }
2086 
2087  //draw the major ticks
2089  painter->setOpacity(majorTicksOpacity);
2090  painter->setPen(majorTicksPen);
2091  painter->setBrush(Qt::NoBrush);
2092  painter->drawPath(majorTicksPath);
2093  }
2094 
2095  //draw the minor ticks
2097  painter->setOpacity(minorTicksOpacity);
2098  painter->setPen(minorTicksPen);
2099  painter->setBrush(Qt::NoBrush);
2100  painter->drawPath(minorTicksPath);
2101  }
2102 
2103  // draw tick labels
2105  painter->setOpacity(labelsOpacity);
2106  painter->setPen(QPen(labelsColor));
2107  painter->setFont(labelsFont);
2108  QTextDocument doc;
2109  doc.setDefaultFont(labelsFont);
2110  QFontMetrics fm(labelsFont);
2113  //QDEBUG(Q_FUNC_INFO << ", axis tick label strings: " << tickLabelStrings)
2114  for (int i = 0; i < tickLabelPoints.size(); i++) {
2115  painter->translate(tickLabelPoints.at(i));
2116  painter->save();
2117  painter->rotate(-labelsRotationAngle);
2118 
2121  const QRect& rect = fm.boundingRect(tickLabelStrings.at(i));
2122  painter->fillRect(rect, labelsBackgroundColor);
2123  }
2124  painter->drawText(QPoint(0, 0), tickLabelStrings.at(i));
2125  } else {
2126  QString style("p {color: %1;}");
2127  doc.setDefaultStyleSheet(style.arg(labelsColor.name()));
2128  doc.setHtml("<p>" + tickLabelStrings.at(i) + "</p>");
2129  QSizeF size = doc.size();
2130  int height = size.height();
2132  int width = size.width();
2133  painter->fillRect(0, -height, width, height, labelsBackgroundColor);
2134  }
2135  painter->translate(0, -height);
2136  doc.drawContents(painter);
2137  }
2138  painter->restore();
2139  painter->translate(-tickLabelPoints.at(i));
2140  }
2141  } else { // datetime
2142  for (int i = 0; i < tickLabelPoints.size(); i++) {
2143  painter->translate(tickLabelPoints.at(i));
2144  painter->save();
2145  painter->rotate(-labelsRotationAngle);
2147  const QRect& rect = fm.boundingRect(tickLabelStrings.at(i));
2148  painter->fillRect(rect, labelsBackgroundColor);
2149  }
2150  painter->drawText(QPoint(0, 0), tickLabelStrings.at(i));
2151  painter->restore();
2152  painter->translate(-tickLabelPoints.at(i));
2153  }
2154  }
2155  }
2156 
2157  if (m_hovered && !isSelected() && !m_printing) {
2158  painter->setPen(QPen(QApplication::palette().color(QPalette::Shadow), 2, Qt::SolidLine));
2159  painter->drawPath(axisShape);
2160  }
2161 
2162  if (isSelected() && !m_printing) {
2163  painter->setPen(QPen(QApplication::palette().color(QPalette::Highlight), 2, Qt::SolidLine));
2164  painter->drawPath(axisShape);
2165  }
2166 }
2167 
2168 void AxisPrivate::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) {
2169  q->createContextMenu()->exec(event->screenPos());
2170 }
2171 
2172 void AxisPrivate::hoverEnterEvent(QGraphicsSceneHoverEvent*) {
2173  if (!isSelected()) {
2174  m_hovered = true;
2175  emit q->hovered();
2176  update(axisShape.boundingRect());
2177  }
2178 }
2179 
2180 void AxisPrivate::hoverLeaveEvent(QGraphicsSceneHoverEvent*) {
2181  if (m_hovered) {
2182  m_hovered = false;
2183  emit q->unhovered();
2184  update(axisShape.boundingRect());
2185  }
2186 }
2187 
2188 void AxisPrivate::mousePressEvent(QGraphicsSceneMouseEvent* event) {
2189  auto* plot = static_cast<CartesianPlot*>(q->parentAspect());
2190  if (!plot->isLocked()) {
2191  m_panningStarted = true;
2192  m_panningStart = event->pos();
2193  } else
2194  QGraphicsItem::mousePressEvent(event);
2195 }
2196 
2197 void AxisPrivate::mouseMoveEvent(QGraphicsSceneMouseEvent* event) {
2198  if (m_panningStarted) {
2200  setCursor(Qt::SizeHorCursor);
2201  const int deltaXScene = (m_panningStart.x() - event->pos().x());
2202  if (abs(deltaXScene) < 5)
2203  return;
2204 
2205  auto* plot = static_cast<CartesianPlot*>(q->parentAspect());
2206  if (deltaXScene > 0)
2207  plot->shiftRightX();
2208  else
2209  plot->shiftLeftX();
2210  } else {
2211  setCursor(Qt::SizeVerCursor);
2212  const int deltaYScene = (m_panningStart.y() - event->pos().y());
2213  if (abs(deltaYScene) < 5)
2214  return;
2215 
2216  auto* plot = static_cast<CartesianPlot*>(q->parentAspect());
2217  if (deltaYScene > 0)
2218  plot->shiftUpY();
2219  else
2220  plot->shiftDownY();
2221  }
2222 
2223  m_panningStart = event->pos();
2224  }
2225 }
2226 
2227 
2228 void AxisPrivate::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) {
2229  setCursor(Qt::ArrowCursor);
2230  m_panningStarted = false;
2231  QGraphicsItem::mouseReleaseEvent(event);
2232 }
2233 
2235  m_printing = on;
2236 }
2237 
2239  return m_hovered;
2240 }
2241 
2242 //##############################################################################
2243 //################## Serialization/Deserialization ###########################
2244 //##############################################################################
2245 //! Save as XML
2246 void Axis::save(QXmlStreamWriter* writer) const {
2247  Q_D(const Axis);
2248 
2249  writer->writeStartElement("axis");
2250  writeBasicAttributes(writer);
2251  writeCommentElement(writer);
2252 
2253  //general
2254  writer->writeStartElement( "general" );
2255  writer->writeAttribute( "autoScale", QString::number(d->autoScale) );
2256  writer->writeAttribute( "orientation", QString::number(static_cast<int>(d->orientation)) );
2257  writer->writeAttribute( "position", QString::number(static_cast<int>(d->position)) );
2258  writer->writeAttribute( "scale", QString::number(static_cast<int>(d->scale)) );
2259  writer->writeAttribute( "offset", QString::number(d->offset) );
2260  writer->writeAttribute( "start", QString::number(d->start) );
2261  writer->writeAttribute( "end", QString::number(d->end) );
2262  writer->writeAttribute( "scalingFactor", QString::number(d->scalingFactor) );
2263  writer->writeAttribute( "zeroOffset", QString::number(d->zeroOffset) );
2264  writer->writeAttribute( "titleOffsetX", QString::number(d->titleOffsetX) );
2265  writer->writeAttribute( "titleOffsetY", QString::number(d->titleOffsetY) );
2266  writer->writeAttribute( "visible", QString::number(d->isVisible()) );
2267  writer->writeEndElement();
2268 
2269  //label
2270  d->title->save(writer);
2271 
2272  //line
2273  writer->writeStartElement( "line" );
2274  WRITE_QPEN(d->linePen);
2275  writer->writeAttribute( "opacity", QString::number(d->lineOpacity) );
2276  writer->writeAttribute( "arrowType", QString::number(static_cast<int>(d->arrowType)) );
2277  writer->writeAttribute( "arrowPosition", QString::number(static_cast<int>(d->arrowPosition)) );
2278  writer->writeAttribute( "arrowSize", QString::number(d->arrowSize) );
2279  writer->writeEndElement();
2280 
2281  //major ticks
2282  writer->writeStartElement( "majorTicks" );
2283  writer->writeAttribute( "direction", QString::number(d->majorTicksDirection) );
2284  writer->writeAttribute( "type", QString::number(static_cast<int>(d->majorTicksType)) );
2285  writer->writeAttribute( "number", QString::number(d->majorTicksNumber) );
2286  writer->writeAttribute( "increment", QString::number(d->majorTicksSpacing) );
2287  WRITE_COLUMN(d->majorTicksColumn, majorTicksColumn);
2288  writer->writeAttribute( "length", QString::number(d->majorTicksLength) );
2289  WRITE_QPEN(d->majorTicksPen);
2290  writer->writeAttribute( "opacity", QString::number(d->majorTicksOpacity) );
2291  writer->writeEndElement();
2292 
2293  //minor ticks
2294  writer->writeStartElement( "minorTicks" );
2295  writer->writeAttribute( "direction", QString::number(d->minorTicksDirection) );
2296  writer->writeAttribute( "type", QString::number(static_cast<int>(d->minorTicksType)) );
2297  writer->writeAttribute( "number", QString::number(d->minorTicksNumber) );
2298  writer->writeAttribute( "increment", QString::number(d->minorTicksIncrement) );
2299  WRITE_COLUMN(d->minorTicksColumn, minorTicksColumn);
2300  writer->writeAttribute( "length", QString::number(d->minorTicksLength) );
2301  WRITE_QPEN(d->minorTicksPen);
2302  writer->writeAttribute( "opacity", QString::number(d->minorTicksOpacity) );
2303  writer->writeEndElement();
2304 
2305  //extra ticks
2306 
2307  //labels
2308  writer->writeStartElement( "labels" );
2309  writer->writeAttribute( "position", QString::number(static_cast<int>(d->labelsPosition)) );
2310  writer->writeAttribute( "offset", QString::number(d->labelsOffset) );
2311  writer->writeAttribute( "rotation", QString::number(d->labelsRotationAngle) );
2312  writer->writeAttribute( "format", QString::number(static_cast<int>(d->labelsFormat)) );
2313  writer->writeAttribute( "precision", QString::number(d->labelsPrecision) );
2314  writer->writeAttribute( "autoPrecision", QString::number(d->labelsAutoPrecision) );
2315  writer->writeAttribute( "dateTimeFormat", d->labelsDateTimeFormat );
2316  WRITE_QCOLOR(d->labelsColor);
2317  WRITE_QFONT(d->labelsFont);
2318  writer->writeAttribute( "prefix", d->labelsPrefix );
2319  writer->writeAttribute( "suffix", d->labelsSuffix );
2320  writer->writeAttribute( "opacity", QString::number(d->labelsOpacity) );
2321  writer->writeAttribute( "backgroundType", QString::number(static_cast<int>(d->labelsBackgroundType)) );
2322  writer->writeAttribute( "backgroundColor_r", QString::number(d->labelsBackgroundColor.red()) );
2323  writer->writeAttribute( "backgroundColor_g", QString::number(d->labelsBackgroundColor.green()) );
2324  writer->writeAttribute( "backgroundColor_b", QString::number(d->labelsBackgroundColor.blue()) );
2325  writer->writeEndElement();
2326 
2327  //grid
2328  writer->writeStartElement( "majorGrid" );
2329  WRITE_QPEN(d->majorGridPen);
2330  writer->writeAttribute( "opacity", QString::number(d->majorGridOpacity) );
2331  writer->writeEndElement();
2332 
2333  writer->writeStartElement( "minorGrid" );
2334  WRITE_QPEN(d->minorGridPen);
2335  writer->writeAttribute( "opacity", QString::number(d->minorGridOpacity) );
2336  writer->writeEndElement();
2337 
2338  writer->writeEndElement(); // close "axis" section
2339 }
2340 
2341 //! Load from XML
2342 bool Axis::load(XmlStreamReader* reader, bool preview) {
2343  Q_D(Axis);
2344 
2345  if (!readBasicAttributes(reader))
2346  return false;
2347 
2348  KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used");
2349  QXmlStreamAttributes attribs;
2350  QString str;
2351 
2352  while (!reader->atEnd()) {
2353  reader->readNext();
2354  if (reader->isEndElement() && reader->name() == "axis")
2355  break;
2356 
2357  if (!reader->isStartElement())
2358  continue;
2359 
2360  if (!preview && reader->name() == "comment") {
2361  if (!readCommentElement(reader)) return false;
2362  } else if (!preview && reader->name() == "general") {
2363  attribs = reader->attributes();
2364 
2365  READ_INT_VALUE("autoScale", autoScale, bool);
2366  READ_INT_VALUE("orientation", orientation, Orientation);
2367  READ_INT_VALUE("position", position, Axis::Position);
2368  READ_INT_VALUE("scale", scale, Axis::Scale);
2369  READ_DOUBLE_VALUE("offset", offset);
2370  READ_DOUBLE_VALUE("start", start);
2371  READ_DOUBLE_VALUE("end", end);
2372  READ_DOUBLE_VALUE("scalingFactor", scalingFactor);
2373  READ_DOUBLE_VALUE("zeroOffset", zeroOffset);
2374  READ_DOUBLE_VALUE("titleOffsetX", titleOffsetX);
2375  READ_DOUBLE_VALUE("titleOffsetY", titleOffsetY);
2376 
2377  str = attribs.value("visible").toString();
2378  if (str.isEmpty())
2379  reader->raiseWarning(attributeWarning.subs("visible").toString());
2380  else
2381  d->setVisible(str.toInt());
2382  } else if (reader->name() == "textLabel") {
2383  d->title->load(reader, preview);
2384  } else if (!preview && reader->name() == "line") {
2385  attribs = reader->attributes();
2386 
2387  READ_QPEN(d->linePen);
2388  READ_DOUBLE_VALUE("opacity", lineOpacity);
2389  READ_INT_VALUE("arrowType", arrowType, Axis::ArrowType);
2390  READ_INT_VALUE("arrowPosition", arrowPosition, Axis::ArrowPosition);
2391  READ_DOUBLE_VALUE("arrowSize", arrowSize);
2392  } else if (!preview && reader->name() == "majorTicks") {
2393  attribs = reader->attributes();
2394 
2395  READ_INT_VALUE("direction", majorTicksDirection, Axis::TicksDirection);
2396  READ_INT_VALUE("type", majorTicksType, Axis::TicksType);
2397  READ_INT_VALUE("number", majorTicksNumber, int);
2398  READ_DOUBLE_VALUE("increment", majorTicksSpacing);
2399  READ_COLUMN(majorTicksColumn);
2400  READ_DOUBLE_VALUE("length", majorTicksLength);
2401  READ_QPEN(d->majorTicksPen);
2402  READ_DOUBLE_VALUE("opacity", majorTicksOpacity);
2403  } else if (!preview && reader->name() == "minorTicks") {
2404  attribs = reader->attributes();
2405 
2406  READ_INT_VALUE("direction", minorTicksDirection, Axis::TicksDirection);
2407  READ_INT_VALUE("type", minorTicksType, Axis::TicksType);
2408  READ_INT_VALUE("number", minorTicksNumber, int);
2409  READ_DOUBLE_VALUE("increment", minorTicksIncrement);
2410  READ_COLUMN(minorTicksColumn);
2411  READ_DOUBLE_VALUE("length", minorTicksLength);
2412  READ_QPEN(d->minorTicksPen);
2413  READ_DOUBLE_VALUE("opacity", minorTicksOpacity);
2414  } else if (!preview && reader->name() == "labels") {
2415  attribs = reader->attributes();
2416 
2417  READ_INT_VALUE("position", labelsPosition, Axis::LabelsPosition);
2418  READ_DOUBLE_VALUE("offset", labelsOffset);
2419  READ_DOUBLE_VALUE("rotation", labelsRotationAngle);
2420  READ_INT_VALUE("format", labelsFormat, Axis::LabelsFormat);
2421  READ_INT_VALUE("precision", labelsPrecision, int);
2422  READ_INT_VALUE("autoPrecision", labelsAutoPrecision, bool);
2423  d->labelsDateTimeFormat = attribs.value("dateTimeFormat").toString();
2424  READ_QCOLOR(d->labelsColor);
2425  READ_QFONT(d->labelsFont);
2426 
2427  //don't produce any warning if no prefix or suffix is set (empty string is allowed here in xml)
2428  d->labelsPrefix = attribs.value("prefix").toString();
2429  d->labelsSuffix = attribs.value("suffix").toString();
2430 
2431  READ_DOUBLE_VALUE("opacity", labelsOpacity);
2432 
2433  READ_INT_VALUE("backgroundType", labelsBackgroundType, Axis::LabelsBackgroundType);
2434  str = attribs.value("backgroundColor_r").toString();
2435  if(!str.isEmpty())
2436  d->labelsBackgroundColor.setRed(str.toInt());
2437 
2438  str = attribs.value("backgroundColor_g").toString();
2439  if(!str.isEmpty())
2440  d->labelsBackgroundColor.setGreen(str.toInt());
2441 
2442  str = attribs.value("backgroundColor_b").toString();
2443  if(!str.isEmpty())
2444  d->labelsBackgroundColor.setBlue(str.toInt());
2445  } else if (!preview && reader->name() == "majorGrid") {
2446  attribs = reader->attributes();
2447 
2448  READ_QPEN(d->majorGridPen);
2449  READ_DOUBLE_VALUE("opacity", majorGridOpacity);
2450  } else if (!preview && reader->name() == "minorGrid") {
2451  attribs = reader->attributes();
2452 
2453  READ_QPEN(d->minorGridPen);
2454  READ_DOUBLE_VALUE("opacity", minorGridOpacity);
2455  } else { // unknown element
2456  reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString()));
2457  if (!reader->skipToEndElement()) return false;
2458  }
2459  }
2460 
2461  return true;
2462 }
2463 
2464 //##############################################################################
2465 //######################### Theme management ##################################
2466 //##############################################################################
2467 void Axis::loadThemeConfig(const KConfig& config) {
2468  const KConfigGroup& group = config.group("Axis");
2469 
2470  //we don't want to show the major and minor grid lines for non-first horizontal/vertical axes
2471  //determine the index of the axis among other axes having the same orientation
2472  bool firstAxis = true;
2473  for (const auto* axis : parentAspect()->children<Axis>()) {
2474  if (orientation() == axis->orientation()) {
2475  if (axis == this) {
2476  break;
2477  } else {
2478  firstAxis = false;
2479  break;
2480  }
2481  }
2482  }
2483 
2484  QPen p;
2485 
2486  // Tick label
2487  this->setLabelsColor(group.readEntry("LabelsFontColor", QColor(Qt::black)));
2488  this->setLabelsOpacity(group.readEntry("LabelsOpacity", 1.0));
2489 
2490  //use plot area color for the background color of the labels
2491  const KConfigGroup& groupPlot = config.group("CartesianPlot");
2492  this->setLabelsBackgroundColor(groupPlot.readEntry("BackgroundFirstColor", QColor(Qt::white)));
2493 
2494  //Line
2495  this->setLineOpacity(group.readEntry("LineOpacity", 1.0));
2496  p.setStyle((Qt::PenStyle)group.readEntry("LineStyle", (int)Qt::SolidLine));
2497  p.setColor(group.readEntry("LineColor", QColor(Qt::black)));
2498  p.setWidthF(group.readEntry("LineWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Unit::Point)));
2499  this->setLinePen(p);
2500 
2501  //Major grid
2502  if (firstAxis) {
2503  p.setStyle((Qt::PenStyle)group.readEntry("MajorGridStyle", (int)Qt::SolidLine));
2504  p.setColor(group.readEntry("MajorGridColor", QColor(Qt::gray)));
2505  p.setWidthF(group.readEntry("MajorGridWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Unit::Point)));
2506  } else
2507  p.setStyle(Qt::NoPen);
2508  this->setMajorGridPen(p);
2509  this->setMajorGridOpacity(group.readEntry("MajorGridOpacity", 1.0));
2510 
2511  //Major ticks
2512  p.setStyle((Qt::PenStyle)group.readEntry("MajorTicksLineStyle", (int)Qt::SolidLine));
2513  p.setColor(group.readEntry("MajorTicksColor", QColor(Qt::black)));
2514  p.setWidthF(group.readEntry("MajorTicksWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Unit::Point)));
2515  this->setMajorTicksPen(p);
2516  this->setMajorTicksOpacity(group.readEntry("MajorTicksOpacity", 1.0));
2517 
2518  //Minor grid
2519  if (firstAxis) {
2520  p.setStyle((Qt::PenStyle)group.readEntry("MinorGridStyle", (int)Qt::DotLine));
2521  p.setColor(group.readEntry("MinorGridColor", QColor(Qt::gray)));
2522  p.setWidthF(group.readEntry("MinorGridWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Unit::Point)));
2523  } else
2524  p.setStyle(Qt::NoPen);
2525  this->setMinorGridOpacity(group.readEntry("MinorGridOpacity", 1.0));
2526  this->setMinorGridPen(p);
2527 
2528  //Minor ticks
2529  p.setStyle((Qt::PenStyle)group.readEntry("MinorTicksLineStyle", (int)Qt::SolidLine));
2530  p.setColor(group.readEntry("MinorTicksColor", QColor(Qt::black)));
2531  p.setWidthF(group.readEntry("MinorTicksWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Unit::Point)));
2532  this->setMinorTicksPen(p);
2533  this->setMinorTicksOpacity(group.readEntry("MinorTicksOpacity", 1.0));
2534 
2535  //load the theme for the title label
2536  Q_D(Axis);
2537  d->title->loadThemeConfig(config);
2538 }
2539 
2540 void Axis::saveThemeConfig(const KConfig& config) {
2541  KConfigGroup group = config.group("Axis");
2542 
2543  // Tick label
2544  group.writeEntry("LabelsFontColor", this->labelsColor());
2545  group.writeEntry("LabelsOpacity", this->labelsOpacity());
2546  group.writeEntry("LabelsBackgroundColor", this->labelsBackgroundColor());
2547 
2548  //Line
2549  group.writeEntry("LineOpacity", this->lineOpacity());
2550  group.writeEntry("LineColor", this->linePen().color());
2551  group.writeEntry("LineStyle", (int) this->linePen().style());
2552  group.writeEntry("LineWidth", this->linePen().widthF());
2553 
2554  //Major ticks
2555  group.writeEntry("MajorGridOpacity", this->majorGridOpacity());
2556  group.writeEntry("MajorGridColor", this->majorGridPen().color());
2557  group.writeEntry("MajorGridStyle", (int) this->majorGridPen().style());
2558  group.writeEntry("MajorGridWidth", this->majorGridPen().widthF());
2559  group.writeEntry("MajorTicksColor", this->majorTicksPen().color());
2560  group.writeEntry("MajorTicksLineStyle", (int) this->majorTicksPen().style());
2561  group.writeEntry("MajorTicksWidth", this->majorTicksPen().widthF());
2562  group.writeEntry("MajorTicksOpacity", this->majorTicksOpacity());
2563  group.writeEntry("MajorTicksType", (int)this->majorTicksType());
2564 
2565  //Minor ticks
2566  group.writeEntry("MinorGridOpacity", this->minorGridOpacity());
2567  group.writeEntry("MinorGridColor", this->minorGridPen().color());
2568  group.writeEntry("MinorGridStyle", (int) this->minorGridPen().style());
2569  group.writeEntry("MinorGridWidth", this->minorGridPen().widthF());
2570  group.writeEntry("MinorTicksColor", this->minorTicksPen().color());
2571  group.writeEntry("MinorTicksLineStyle", (int) this->minorTicksPen().style());
2572  group.writeEntry("MinorTicksWidth", this->minorTicksPen().widthF());
2573  group.writeEntry("MinorTicksOpacity", this->minorTicksOpacity());
2574  group.writeEntry("MinorTicksType", (int)this->minorTicksType());
2575 
2576  //same the theme config for the title label
2577  Q_D(Axis);
2578  d->title->saveThemeConfig(config);
2579 }
AspectType
STD_SETTER_CMD_IMPL_F(Axis, SetOffset, double, offset, retransform)
STD_SETTER_CMD_IMPL_F_S(Axis, SetAutoScale, bool, autoScale, retransform)
CLASS_SHARED_D_READER_IMPL(Axis, QColor, labelsColor, labelsColor)
BASIC_SHARED_D_READER_IMPL(Axis, Axis::LabelsFormat, labelsFormat, labelsFormat)
STD_SWAP_METHOD_SETTER_CMD_IMPL(Axis, SetVisible, bool, swapVisible)
static const QRgb white
Definition: ImageEditor.cpp:37
static const QRgb black
Definition: ImageEditor.cpp:38
Base class of all persistent objects in a Project.
AspectType type() const
bool readCommentElement(XmlStreamReader *)
Load comment from an XML element.
void addChild(AbstractAspect *)
Add the given Aspect to my list of children.
AbstractAspect * parent(AspectType type) const
In the parent-child hierarchy, return the first parent of type.
void aspectAboutToBeRemoved(const AbstractAspect *)
Emitted before an aspect is removed from its parent.
QString name() const
void writeBasicAttributes(QXmlStreamWriter *) const
Save name and creation time to XML.
AbstractAspect * parentAspect() const
Return my parent Aspect or 0 if I currently don't have one.
void exec(QUndoCommand *)
Execute the given command, pushing it on the undoStack() if available.
bool readBasicAttributes(XmlStreamReader *)
Load name and creation time from XML.
void writeCommentElement(QXmlStreamWriter *) const
Save the comment to XML.
AbstractAspectPrivate * d
Interface definition for data with column logic.
virtual double valueAt(int row) const
Return the double value in row 'row'.
bool isMasked(int row) const
Return whether a certain row is masked.
virtual int rowCount() const =0
Return the data vector size.
void dataChanged(const AbstractColumn *source)
Data of the column has changed.
bool isValid(int row) const
Convenience method for mode-independent testing of validity.
Helper class to get the axis grid drawn with the z-Value=0.
Definition: Axis.cpp:66
AxisGrid(AxisPrivate *a)
Definition: Axis.cpp:68
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
Definition: Axis.cpp:83
QRectF boundingRect() const override
Definition: Axis.cpp:75
AxisPrivate * axis
Definition: Axis.cpp:108
qreal lineOpacity
Definition: AxisPrivate.h:82
AxisPrivate(Axis *)
Definition: Axis.cpp:948
int majorTicksNumber
number of major ticks
Definition: AxisPrivate.h:95
QPointF m_panningStart
Definition: AxisPrivate.h:178
bool labelsAutoPrecision
Definition: AxisPrivate.h:116
void recalcShapeAndBoundingRect()
Definition: Axis.cpp:1980
qreal majorTicksOpacity
Definition: AxisPrivate.h:101
QPainterPath majorGridPath
Definition: AxisPrivate.h:139
qreal titleOffsetX
Definition: AxisPrivate.h:89
QVector< QPointF > minorTickPoints
position of the major ticks on the axis.
Definition: AxisPrivate.h:169
Axis::Orientation orientation
horizontal or vertical
Definition: AxisPrivate.h:70
qreal minorGridOpacity
Definition: AxisPrivate.h:134
qreal majorTicksLength
major tick length (in page units!)
Definition: AxisPrivate.h:99
void mouseReleaseEvent(QGraphicsSceneMouseEvent *) override
Definition: Axis.cpp:2228
QVector< QPointF > tickLabelPoints
position of the major tick labels (left lower edge of label's bounding rect)
Definition: AxisPrivate.h:170
Axis::LabelsBackgroundType labelsBackgroundType
Definition: AxisPrivate.h:122
int lowerLabelsPrecision(int precision, Axis::LabelsFormat)
Definition: Axis.cpp:1637
void retransformTickLabelPositions()
Definition: Axis.cpp:1700
double start
start coordinate of the axis line
Definition: AxisPrivate.h:74
QString labelsSuffix
Definition: AxisPrivate.h:127
bool labelsFormatDecimalOverruled
Definition: AxisPrivate.h:142
bool suppressRetransform
Definition: AxisPrivate.h:141
void addArrow(QPointF point, int direction)
Definition: Axis.cpp:1074
qreal arrowSize
Definition: AxisPrivate.h:85
QPainterPath linePath
Definition: AxisPrivate.h:138
Axis::Position position
left, right, bottom, top or custom (usually not changed after creation)
Definition: AxisPrivate.h:71
void retransformTicks()
Definition: Axis.cpp:1183
QFont labelsFont
Definition: AxisPrivate.h:121
void hoverLeaveEvent(QGraphicsSceneHoverEvent *) override
Definition: Axis.cpp:2180
QVector< QPointF > majorTickPoints
position of the major ticks on the axis.
Definition: AxisPrivate.h:168
bool labelsFormatAutoChanged
Definition: AxisPrivate.h:143
qreal labelsOffset
offset, distance to the end of the tick line (in page units)
Definition: AxisPrivate.h:124
const CartesianCoordinateSystem * cSystem
Definition: AxisPrivate.h:146
bool isHovered() const
Definition: Axis.cpp:2238
qreal zeroOffset
Definition: AxisPrivate.h:77
AxisGrid * gridItem
Definition: AxisPrivate.h:130
Axis::TicksType minorTicksType
the way how the number of minor ticks is specified - either as a total number or an increment
Definition: AxisPrivate.h:104
QString name() const
Definition: Axis.cpp:954
qreal majorTicksSpacing
spacing (step) for the major ticks
Definition: AxisPrivate.h:96
void updateGrid()
Definition: Axis.cpp:1976
const AbstractColumn * majorTicksColumn
column containing values for major ticks' positions
Definition: AxisPrivate.h:97
void mousePressEvent(QGraphicsSceneMouseEvent *) override
Definition: Axis.cpp:2188
qreal minorTicksLength
minor tick length (in page units!)
Definition: AxisPrivate.h:109
QPen linePen
Definition: AxisPrivate.h:81
qreal majorGridOpacity
Definition: AxisPrivate.h:132
bool m_suppressRecalc
Definition: AxisPrivate.h:175
bool m_printing
Definition: AxisPrivate.h:176
void retransformLine()
Definition: Axis.cpp:999
CartesianPlot * plot
Definition: AxisPrivate.h:145
QColor labelsBackgroundColor
Definition: AxisPrivate.h:123
bool swapVisible(bool)
Definition: Axis.cpp:958
QPainterPath axisShape
Definition: AxisPrivate.h:166
QPainterPath majorTicksPath
Definition: AxisPrivate.h:163
bool m_hovered
Definition: AxisPrivate.h:174
Axis *const q
Definition: AxisPrivate.h:136
void contextMenuEvent(QGraphicsSceneContextMenuEvent *) override
Definition: Axis.cpp:2168
int upperLabelsPrecision(int precision, Axis::LabelsFormat)
Definition: Axis.cpp:1581
TextLabel * title
Definition: AxisPrivate.h:88
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *widget=nullptr) override
Definition: Axis.cpp:2065
void setPrinting(bool)
Definition: Axis.cpp:2234
const AbstractColumn * minorTicksColumn
column containing values for minor ticks' positions
Definition: AxisPrivate.h:107
qreal minorTicksOpacity
Definition: AxisPrivate.h:111
Axis::TicksType majorTicksType
the way how the number of major ticks is specified - either as a total number or an increment
Definition: AxisPrivate.h:94
QPen majorGridPen
Definition: AxisPrivate.h:131
void retransform()
Definition: Axis.cpp:988
Axis::TicksDirection majorTicksDirection
major ticks direction: inwards, outwards, both, or none
Definition: AxisPrivate.h:93
QPen minorGridPen
Definition: AxisPrivate.h:133
bool transformAnchor(QPointF *)
helper function for retransformTicks()
Definition: Axis.cpp:1167
void mouseMoveEvent(QGraphicsSceneMouseEvent *) override
Definition: Axis.cpp:2197
Axis::LabelsPosition labelsPosition
Definition: AxisPrivate.h:118
Axis::ArrowPosition arrowPosition
Definition: AxisPrivate.h:84
Axis::LabelsFormat labelsFormat
Definition: AxisPrivate.h:114
QVector< QString > tickLabelStrings
the actual text of the major tick labels
Definition: AxisPrivate.h:172
QPainterPath arrowPath
Definition: AxisPrivate.h:162
Axis::Scale scale
Definition: AxisPrivate.h:72
QVector< QLineF > lines
Definition: AxisPrivate.h:80
int minorTicksNumber
number of minor ticks (between each two major ticks)
Definition: AxisPrivate.h:105
double offset
offset from zero in the direction perpendicular to the axis
Definition: AxisPrivate.h:73
void retransformMinorGrid()
Definition: Axis.cpp:1931
QString labelsPrefix
Definition: AxisPrivate.h:126
QString labelsDateTimeFormat
Definition: AxisPrivate.h:117
Axis::TicksDirection minorTicksDirection
minor ticks direction: inwards, outwards, both, or none
Definition: AxisPrivate.h:103
QRectF boundingRectangle
Definition: AxisPrivate.h:165
void retransformMajorGrid()
Definition: Axis.cpp:1852
QPainterPath shape() const override
Definition: Axis.cpp:981
void retransformArrow()
Definition: Axis.cpp:1051
QColor labelsColor
Definition: AxisPrivate.h:120
qreal minorTicksIncrement
spacing (step) for the minor ticks
Definition: AxisPrivate.h:106
qreal labelsRotationAngle
Definition: AxisPrivate.h:119
QPainterPath minorTicksPath
Definition: AxisPrivate.h:164
qreal scalingFactor
Definition: AxisPrivate.h:76
QPainterPath minorGridPath
Definition: AxisPrivate.h:140
QVector< double > tickLabelValues
major tick labels values
Definition: AxisPrivate.h:171
QRectF boundingRect() const override
Definition: Axis.cpp:974
QPen majorTicksPen
Definition: AxisPrivate.h:100
qreal titleOffsetY
Definition: AxisPrivate.h:90
bool m_panningStarted
Definition: AxisPrivate.h:177
Axis::ArrowType arrowType
Definition: AxisPrivate.h:83
qreal labelsOpacity
Definition: AxisPrivate.h:125
void hoverEnterEvent(QGraphicsSceneHoverEvent *) override
Definition: Axis.cpp:2172
QPen minorTicksPen
Definition: AxisPrivate.h:110
double end
end coordinate of the axis line
Definition: AxisPrivate.h:75
int labelsPrecision
Definition: AxisPrivate.h:115
void retransformTickLabelStrings()
Definition: Axis.cpp:1443
Axis for cartesian coordinate systems.
Definition: Axis.h:42
LabelsPosition
Definition: Axis.h:60
void setDefault(bool)
Definition: Axis.cpp:486
Position
Definition: Axis.h:46
QMenu * lineColorMenu
Definition: Axis.h:176
QMenu * orientationMenu
Definition: Axis.h:173
void labelsPrecisionChanged(int)
Scale
Definition: Axis.h:59
QMenu * lineMenu
Definition: Axis.h:174
QIcon icon() const override
Definition: Axis.cpp:314
ArrowPosition
Definition: Axis.h:58
QGraphicsItem * graphicsItem() const override
Return the graphics item representing this element.
Definition: Axis.cpp:337
void labelsFormatChanged(Axis::LabelsFormat)
void init()
Definition: Axis.cpp:136
@ ticksOut
Definition: Axis.h:51
@ noTicks
Definition: Axis.h:49
@ ticksIn
Definition: Axis.h:50
AxisPrivate *const d_ptr
Definition: Axis.h:155
QAction * orientationHorizontalAction
Definition: Axis.h:166
LabelsFormat
Definition: Axis.h:47
QMenu * lineStyleMenu
Definition: Axis.h:175
void majorTicksColumnAboutToBeRemoved(const AbstractAspect *)
Definition: Axis.cpp:900
LabelsBackgroundType
Definition: Axis.h:61
~Axis() override
Definition: Axis.cpp:325
void retransform() override
Tell the element to newly transform its graphics item into its coordinate system.
Definition: Axis.cpp:352
void saveThemeConfig(const KConfig &) override
Definition: Axis.cpp:2540
void labelChanged()
Definition: Axis.cpp:890
QString & majorTicksColumnPath() const
Definition: Axis.cpp:414
void orientationChangedSlot(QAction *)
Definition: Axis.cpp:919
bool isVisible() const override
Return whether the element is (at least) partially visible.
Definition: Axis.cpp:481
TicksType
Definition: Axis.h:56
bool isHovered() const
Definition: Axis.cpp:501
Axis(const QString &, Orientation orientation=Orientation::Horizontal)
Definition: Axis.cpp:117
double offset() const
QActionGroup * lineStyleActionGroup
Definition: Axis.h:170
void setOffset(const double, const bool=true)
Definition: Axis.cpp:528
ArrowType
Definition: Axis.h:57
void positionChanged(Position)
void handleResize(double horizontalRatio, double verticalRatio, bool pageResize) override
Definition: Axis.cpp:367
void minorTicksColumnAboutToBeRemoved(const AbstractAspect *)
Definition: Axis.cpp:908
void initActions()
Definition: Axis.cpp:228
void setPrinting(bool) override
Switches the printing mode on/off.
Definition: Axis.cpp:496
void finalizeAdd() override
Definition: Axis.cpp:129
void initMenus()
Definition: Axis.cpp:257
void save(QXmlStreamWriter *) const override
Save as XML.
Definition: Axis.cpp:2246
void visibilityChanged(bool)
void retransformTickLabelStrings()
Definition: Axis.cpp:357
void setZValue(qreal) override
Definition: Axis.cpp:345
QActionGroup * lineColorActionGroup
Definition: Axis.h:171
void setVisible(bool) override
Show/hide the element.
Definition: Axis.cpp:476
void retransformTicks()
Definition: Axis.cpp:895
void visibilityChangedSlot()
Definition: Axis.cpp:940
QMenu * createContextMenu() override
Return a new context menu.
Definition: Axis.cpp:280
void loadThemeConfig(const KConfig &) override
Definition: Axis.cpp:2467
void lineColorChanged(QAction *)
Definition: Axis.cpp:933
QAction * visibilityAction
Definition: Axis.h:165
void lineStyleChanged(QAction *)
Definition: Axis.cpp:926
QString & minorTicksColumnPath() const
Definition: Axis.cpp:424
QActionGroup * orientationActionGroup
Definition: Axis.h:169
void setSuppressRetransform(bool)
Definition: Axis.cpp:362
QAction * orientationVerticalAction
Definition: Axis.h:167
bool load(XmlStreamReader *, bool preview) override
Load from XML.
Definition: Axis.cpp:2342
bool isDefault() const
Definition: Axis.cpp:491
Cartesian coordinate system for plots.
QVector< QPointF > mapLogicalToScene(const QVector< QPointF > &, MappingFlags flags=MappingFlag::DefaultMapping) const override
int yDirection() const
Determine the vertical direction relative to the page.
int xDirection() const
Determine the horizontal direction relative to the page.
QVector< QPointF > mapSceneToLogical(const QVector< QPointF > &, MappingFlags flags=MappingFlag::DefaultMapping) const override
A xy-plot.
Definition: CartesianPlot.h:58
bool isLocked() const
static void updatePenStyles(QComboBox *, const QColor &)
Definition: GuiTools.cpp:56
static void fillColorMenu(QMenu *, QActionGroup *)
Definition: GuiTools.cpp:174
static QColor & colorFromAction(QActionGroup *, QAction *)
Definition: GuiTools.cpp:219
static Qt::PenStyle penStyleFromAction(QActionGroup *, QAction *)
Definition: GuiTools.cpp:132
static void selectPenStyleAction(QActionGroup *, Qt::PenStyle)
Definition: GuiTools.cpp:126
static void selectColorAction(QActionGroup *, const QColor &)
Definition: GuiTools.cpp:201
A label supporting rendering of html- and tex-formatted texts.
Definition: TextLabel.h:44
void changed()
void setPositionInvalid(bool)
Definition: TextLabel.cpp:287
bool isVisible() const override
Return whether the element is (at least) partially visible.
Definition: TextLabel.cpp:343
QGraphicsItem * graphicsItem() const override
Return the graphics item representing this element.
Definition: TextLabel.cpp:156
void setPosition(QPointF)
Definition: TextLabel.cpp:265
Base class for all Worksheet children.
static QPainterPath shapeFromPath(const QPainterPath &, const QPen &)
QMenu * createContextMenu() override
Return a new context menu.
Top-level container for worksheet elements like plot, labels, etc.
Definition: Worksheet.h:46
void suppressSelectionChangedEvent(bool)
Definition: Worksheet.cpp:408
static double convertToSceneUnits(const double value, const Worksheet::Unit unit)
Definition: Worksheet.cpp:113
XML stream parser that supports errors as well as warnings. This class also adds line and column numb...
void raiseWarning(const QString &)
#define READ_DOUBLE_VALUE(name, var)
Definition: macros.h:475
#define WRITE_QCOLOR(color)
Definition: macros.h:286
#define DEBUG(x)
Definition: macros.h:50
#define SET_NUMBER_LOCALE
Definition: macros.h:75
#define READ_COLUMN(columnName)
Definition: macros.h:462
#define READ_INT_VALUE(name, var, type)
Definition: macros.h:468
#define READ_QPEN(pen)
Definition: macros.h:324
#define WRITE_QPEN(pen)
Definition: macros.h:315
#define QDEBUG(x)
Definition: macros.h:47
#define READ_QCOLOR(color)
Definition: macros.h:293
#define WRITE_COLUMN(column, columnName)
Definition: macros.h:451
#define WRITE_QFONT(font)
Definition: macros.h:361
#define READ_QFONT(font)
Definition: macros.h:370
@ Scale
Definition: OriginObj.h:70
@ Shadow
Definition: OriginObj.h:71
#define i18n(m)
Definition: nsl_common.h:38
double nsl_math_round_places(double value, unsigned int n)
Definition: nsl_math.c:84
double nsl_math_round_precision(double value, unsigned int p)
Definition: nsl_math.c:99