Revision control

++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ #include <QtCharts/qabstractaxis.h>
++ #include <QtCharts/qlogvalueaxis.h>
++ #include <QtCharts/qvalueaxis.h>
++ #include <QtCore/qmath.h>
++ #include <QtGui/qtextdocument.h>
++ #include <QtWidgets/qgraphicslayout.h>
++ #include <private/abstractchartlayout_p.h>
++ #include <private/abstractdomain_p.h>
++ #include <private/cartesianchartaxis_p.h>
++ #include <private/chartpresenter_p.h>
++ #include <private/linearrowitem_p.h>
++ #include <private/qabstractaxis_p.h>
++
++ QT_BEGIN_NAMESPACE
++
++ CartesianChartAxis::CartesianChartAxis(QAbstractAxis *axis, QGraphicsItem *item , bool intervalAxis)
++ : ChartAxisElement(axis, item, intervalAxis)
++ {
++ Q_ASSERT(item);
++ }
++
++
++ CartesianChartAxis::~CartesianChartAxis()
++ {
++ }
++
++ void CartesianChartAxis::createItems(int count)
++ {
++ if (arrowItems().size() == 0) {
++ QGraphicsLineItem *arrow = new LineArrowItem(this, this);
++ arrow->setAcceptedMouseButtons({});
++ arrow->setPen(axis()->linePen());
++ arrowGroup()->addToGroup(arrow);
++ }
++
++ if (intervalAxis() && gridItems().size() == 0) {
++ for (int i = 0 ; i < 2 ; i ++){
++ QGraphicsLineItem *item = new QGraphicsLineItem(this);
++ item->setAcceptedMouseButtons({});
++ item->setPen(axis()->gridLinePen());
++ gridGroup()->addToGroup(item);
++ QGraphicsRectItem *shades = new QGraphicsRectItem(this);
++ shades->setAcceptedMouseButtons({});
++ shades->setPen(axis()->shadesPen());
++ shades->setBrush(axis()->shadesBrush());
++ shadeGroup()->addToGroup(shades);
++ }
++ }
++
++ QGraphicsTextItem *title = titleItem();
++ title->setFont(axis()->titleFont());
++ title->setDefaultTextColor(axis()->titleBrush().color());
++ title->setHtml(axis()->titleText());
++
++ for (int i = 0; i < count; ++i) {
++ QGraphicsLineItem *arrow = new QGraphicsLineItem(this);
++ arrow->setAcceptedMouseButtons({});
++ QGraphicsLineItem *grid = new QGraphicsLineItem(this);
++ grid->setAcceptedMouseButtons({});
++ QGraphicsTextItem *label;
++ if (axis()->type() == QAbstractAxis::AxisTypeValue) {
++ label = new ValueAxisLabel(this);
++ connect(static_cast<ValueAxisLabel *>(label), &ValueAxisLabel::valueChanged,
++ this, &ChartAxisElement::valueLabelEdited);
++ if (labelsEditable())
++ static_cast<ValueAxisLabel *>(label)->setEditable(true);
++ #if QT_CONFIG(charts_datetime_axis)
++ } else if (axis()->type() == QAbstractAxis::AxisTypeDateTime) {
++ DateTimeAxisLabel *dateTimeLabel = new DateTimeAxisLabel(this);
++ label = dateTimeLabel;
++ connect(dateTimeLabel, &DateTimeAxisLabel::dateTimeChanged,
++ this, &ChartAxisElement::dateTimeLabelEdited);
++ if (labelsEditable())
++ dateTimeLabel->setEditable(true);
++ dateTimeLabel->setFormat(static_cast<QDateTimeAxis*>(axis())->format());
++ #endif
++ } else {
++ label = new QGraphicsTextItem(this);
++ }
++
++ label->setAcceptedMouseButtons({});
++ label->document()->setDocumentMargin(ChartPresenter::textMargin());
++ arrow->setPen(axis()->linePen());
++ grid->setPen(axis()->gridLinePen());
++ label->setFont(axis()->labelsFont());
++ label->setDefaultTextColor(axis()->labelsBrush().color());
++ label->setRotation(axis()->labelsAngle());
++ arrowGroup()->addToGroup(arrow);
++ gridGroup()->addToGroup(grid);
++ labelGroup()->addToGroup(label);
++
++ if (gridItems().size() == 1 || (((gridItems().size() + 1) % 2) && gridItems().size() > 0)) {
++ QGraphicsRectItem *shades = new QGraphicsRectItem(this);
++ shades->setPen(axis()->shadesPen());
++ shades->setBrush(axis()->shadesBrush());
++ shadeGroup()->addToGroup(shades);
++ }
++ }
++ }
++
++ void CartesianChartAxis::updateMinorTickItems()
++ {
++ int currentCount = minorArrowItems().size();
++ int expectedCount = 0;
++ if (axis()->type() == QAbstractAxis::AxisTypeValue) {
++ QValueAxis *valueAxis = qobject_cast<QValueAxis *>(axis());
++ if (valueAxis->tickType() == QValueAxis::TicksFixed) {
++ expectedCount = valueAxis->minorTickCount() * (valueAxis->tickCount() - 1);
++ expectedCount = qMax(expectedCount, 0);
++ } else {
++ const qreal interval = valueAxis->tickInterval();
++ const qreal anchor = valueAxis->tickAnchor();
++ const qreal max = valueAxis->max();
++ const qreal min = valueAxis->min();
++ const int _minorTickCount = valueAxis->minorTickCount();
++
++ // Find the closest major tick <= the min of the range, even if it's not drawn!
++ // This is where we'll start counting minor ticks from, because minor ticks
++ // might need to be drawn even before the first major tick.
++ const qreal ticksFromAnchor = (anchor - min) / interval;
++ const qreal firstMajorTick = anchor - std::ceil(ticksFromAnchor) * interval;
++
++ const qreal deltaMinor = interval / qreal(_minorTickCount + 1);
++ qreal minorTick = firstMajorTick + deltaMinor;
++ int minorCounter = 0;
++
++ while (minorTick < min) {
++ minorTick += deltaMinor;
++ minorCounter++;
++ }
++
++ QList<qreal> points;
++
++ // Calculate the points on axis value space. Conversion to graphical points
++ // will be done on axis specific geometry update function
++ while (minorTick <= max) {
++ if (minorCounter < _minorTickCount) {
++ expectedCount++;
++ minorCounter++;
++ points << (minorTick - min);
++ } else {
++ minorCounter = 0;
++ }
++ minorTick += deltaMinor;
++ }
++
++ setDynamicMinorTickLayout(points);
++ }
++ } else if (axis()->type() == QAbstractAxis::AxisTypeLogValue) {
++ QLogValueAxis *logValueAxis = qobject_cast<QLogValueAxis *>(axis());
++
++ int minorTickCount = logValueAxis->minorTickCount();
++ if (minorTickCount < 0)
++ minorTickCount = qMax(qFloor(logValueAxis->base()) - 2, 0);
++
++ expectedCount = minorTickCount * (logValueAxis->tickCount() + 1);
++ expectedCount = qMax(expectedCount, logValueAxis->minorTickCount());
++ } else {
++ // minor ticks are not supported
++ return;
++ }
++
++ int diff = expectedCount - currentCount;
++ if (diff > 0) {
++ for (int i = 0; i < diff; ++i) {
++ QGraphicsLineItem *minorGridLineItem = new QGraphicsLineItem(this);
++ minorGridLineItem->setPen(axis()->minorGridLinePen());
++ minorGridGroup()->addToGroup(minorGridLineItem);
++
++ QGraphicsLineItem *minorArrowLineItem = new QGraphicsLineItem(this);
++ minorArrowLineItem->setPen(axis()->linePen());
++ minorArrowGroup()->addToGroup(minorArrowLineItem);
++ }
++ } else {
++ QList<QGraphicsItem *> minorGridItemsList = minorGridItems();
++ QList<QGraphicsItem *> minorArrowItemsList = minorArrowItems();
++ for (int i = 0; i > diff; --i) {
++ if (!minorGridItemsList.isEmpty())
++ delete minorGridItemsList.takeLast();
++
++ if (!minorArrowItemsList.isEmpty())
++ delete minorArrowItemsList.takeLast();
++ }
++ }
++ }
++
++ void CartesianChartAxis::deleteItems(int count)
++ {
++ QList<QGraphicsItem *> lines = gridItems();
++ QList<QGraphicsItem *> labels = labelItems();
++ QList<QGraphicsItem *> shades = shadeItems();
++ QList<QGraphicsItem *> axis = arrowItems();
++
++ for (int i = 0; i < count; ++i) {
++ if (lines.size() == 1 || (((lines.size() + 1) % 2) && lines.size() > 0))
++ delete(shades.takeLast());
++ delete(lines.takeLast());
++ delete(labels.takeLast());
++ delete(axis.takeLast());
++ }
++ }
++
++ void CartesianChartAxis::updateLayout(const QList<qreal> &layout)
++ {
++ int diff = ChartAxisElement::layout().size() - layout.size();
++
++ if (diff > 0)
++ deleteItems(diff);
++ else if (diff <= 0)
++ createItems(-diff);
++
++ updateMinorTickItems();
++
++ if (animation()) {
++ switch (presenter()->state()) {
++ case ChartPresenter::ZoomInState:
++ animation()->setAnimationType(AxisAnimation::ZoomInAnimation);
++ animation()->setAnimationPoint(presenter()->statePoint());
++ break;
++ case ChartPresenter::ZoomOutState:
++ animation()->setAnimationType(AxisAnimation::ZoomOutAnimation);
++ animation()->setAnimationPoint(presenter()->statePoint());
++ break;
++ case ChartPresenter::ScrollUpState:
++ case ChartPresenter::ScrollLeftState:
++ animation()->setAnimationType(AxisAnimation::MoveBackwordAnimation);
++ break;
++ case ChartPresenter::ScrollDownState:
++ case ChartPresenter::ScrollRightState:
++ animation()->setAnimationType(AxisAnimation::MoveForwardAnimation);
++ break;
++ case ChartPresenter::ShowState:
++ animation()->setAnimationType(AxisAnimation::DefaultAnimation);
++ break;
++ }
++ animation()->setValues(ChartAxisElement::layout(), layout);
++ presenter()->startAnimation(animation());
++ } else {
++ setLayout(layout);
++ updateGeometry();
++ }
++ }
++
++ bool CartesianChartAxis::emptyAxis() const
++ {
++ return axisGeometry().isEmpty()
++ || gridGeometry().isEmpty()
++ || qFuzzyIsNull(max() - min());
++ }
++
++ void CartesianChartAxis::setGeometry(const QRectF &axis, const QRectF &grid)
++ {
++ m_gridRect = grid;
++ setAxisGeometry(axis);
++
++ if (emptyAxis()) {
++ prepareGeometryChange();
++ return;
++ }
++
++ const QList<qreal> layout = calculateLayout();
++ updateLayout(layout);
++ }
++
++ QSizeF CartesianChartAxis::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
++ {
++ Q_UNUSED(which);
++ Q_UNUSED(constraint);
++ return QSizeF();
++ }
++
++ void CartesianChartAxis::setDateTimeLabelsFormat(const QString &format)
++ {
++ if (max() <= min()
++ || layout().size() < 1
++ || axis()->type() != QAbstractAxis::AxisTypeDateTime) {
++ return;
++ }
++ #if QT_CONFIG(charts_datetime_axis)
++ for (int i = 0; i < layout().size(); i++)
++ static_cast<DateTimeAxisLabel *>(labelItems().at(i))->setFormat(format);
++ #endif
++ }
++
++ void CartesianChartAxis::handleArrowPenChanged(const QPen &pen)
++ {
++ const auto arrows = arrowItems();
++ for (QGraphicsItem *item : arrows)
++ static_cast<QGraphicsLineItem *>(item)->setPen(pen);
++ }
++
++ void CartesianChartAxis::handleGridPenChanged(const QPen &pen)
++ {
++ const auto items = gridItems();
++ for (QGraphicsItem *item : items)
++ static_cast<QGraphicsLineItem *>(item)->setPen(pen);
++ }
++
++ void CartesianChartAxis::handleMinorArrowPenChanged(const QPen &pen)
++ {
++ const auto items = minorArrowItems();
++ for (QGraphicsItem *item : items)
++ static_cast<QGraphicsLineItem *>(item)->setPen(pen);
++ }
++
++ void CartesianChartAxis::handleMinorGridPenChanged(const QPen &pen)
++ {
++ const auto items = minorGridItems();
++ for (QGraphicsItem *item : items)
++ static_cast<QGraphicsLineItem *>(item)->setPen(pen);
++ }
++
++ void CartesianChartAxis::handleGridLineColorChanged(const QColor &color)
++ {
++ const auto items = gridItems();
++ for (QGraphicsItem *item : items) {
++ QGraphicsLineItem *lineItem = static_cast<QGraphicsLineItem *>(item);
++ QPen pen = lineItem->pen();
++ pen.setColor(color);
++ lineItem->setPen(pen);
++ }
++ }
++
++ void CartesianChartAxis::handleMinorGridLineColorChanged(const QColor &color)
++ {
++ const auto items = minorGridItems();
++ for (QGraphicsItem *item : items) {
++ QGraphicsLineItem *lineItem = static_cast<QGraphicsLineItem *>(item);
++ QPen pen = lineItem->pen();
++ pen.setColor(color);
++ lineItem->setPen(pen);
++ }
++ }
++
++ void CartesianChartAxis::handleShadesBrushChanged(const QBrush &brush)
++ {
++ const auto items = shadeItems();
++ for (QGraphicsItem *item : items)
++ static_cast<QGraphicsRectItem *>(item)->setBrush(brush);
++ }
++
++ void CartesianChartAxis::handleShadesPenChanged(const QPen &pen)
++ {
++ const auto items = shadeItems();
++ for (QGraphicsItem *item : items)
++ static_cast<QGraphicsRectItem *>(item)->setPen(pen);
++ }
++
++ void CartesianChartAxis::updateLabelsValues(QValueAxis *axis)
++ {
++ const QList<qreal> &layout = ChartAxisElement::layout();
++ if (layout.isEmpty())
++ return;
++
++ if (axis->tickType() == QValueAxis::TicksFixed) {
++ for (int i = 0; i < layout.size(); ++i) {
++ qreal value = axis->isReverse()
++ ? min() + ((layout.size() - 1 - i) * (max() - min()) / (layout.size() - 1))
++ : min() + (i * (max() - min()) / (layout.size() - 1));
++ static_cast<ValueAxisLabel *>(labelItems().at(i))->setValue(value);
++ }
++ } else {
++ const qreal anchor = axis->tickAnchor();
++ const qreal interval = axis->tickInterval();
++ const qreal ticksFromAnchor = (anchor - min()) / interval;
++ const qreal firstMajorTick = anchor - std::floor(ticksFromAnchor) * interval;
++
++ int i = axis->isReverse() ? labelItems().size()-1 : 0;
++ qreal value = firstMajorTick;
++ while (value <= max()) {
++ static_cast<ValueAxisLabel *>(labelItems().at(i))->setValue(value);
++ value += interval;
++ i += axis->isReverse() ? -1 : 1;
++ }
++ }
++ }
++
++ void CartesianChartAxis::updateLabelsDateTimes()
++ {
++ if (max() <= min() || layout().size() < 1)
++ return;
++ #if QT_CONFIG(charts_datetime_axis)
++ for (int i = 0; i < layout().size(); i++) {
++ qreal value = min() + (i * (max() - min()) / (layout().size() - 1));
++ static_cast<DateTimeAxisLabel *>(labelItems().at(i))->setValue(
++ QDateTime::fromMSecsSinceEpoch(value));
++ }
++ #endif
++ }
++
++ QT_END_NAMESPACE
++
++ #include "moc_cartesianchartaxis_p.cpp"