Revision control

++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ #include <QtCharts/QCandlestickSet>
++ #include <QtGui/QPainter>
++ #include <private/abstractdomain_p.h>
++ #include <private/candlestick_p.h>
++ #include <private/qchart_p.h>
++
++ QT_BEGIN_NAMESPACE
++
++ Candlestick::Candlestick(QCandlestickSet *set, AbstractDomain *domain, QGraphicsObject *parent)
++ : QGraphicsObject(parent),
++ m_set(set),
++ m_domain(domain),
++ m_timePeriod(0.0),
++ m_maximumColumnWidth(-1.0), // no maximum column width by default
++ m_minimumColumnWidth(-1.0), // no minimum column width by default
++ m_bodyWidth(0.5),
++ m_bodyOutlineVisible(true),
++ m_capsWidth(0.5),
++ m_capsVisible(false),
++ m_brush(QChartPrivate::defaultBrush()),
++ m_pen(QChartPrivate::defaultPen()),
++ m_hovering(false),
++ m_mousePressed(false)
++ {
++ setAcceptHoverEvents(true);
++ setAcceptedMouseButtons(Qt::MouseButtonMask);
++ setFlag(QGraphicsObject::ItemIsSelectable);
++ }
++
++ Candlestick::~Candlestick()
++ {
++ // End hover event, if candlestick is deleted during it.
++ if (m_hovering)
++ emit hovered(false, m_set);
++ }
++
++ void Candlestick::setTimePeriod(qreal timePeriod)
++ {
++ m_timePeriod = timePeriod;
++ }
++
++ void Candlestick::setMaximumColumnWidth(qreal maximumColumnWidth)
++ {
++ m_maximumColumnWidth = maximumColumnWidth;
++ }
++
++ void Candlestick::setMinimumColumnWidth(qreal minimumColumnWidth)
++ {
++ m_minimumColumnWidth = minimumColumnWidth;
++ }
++
++ void Candlestick::setBodyWidth(qreal bodyWidth)
++ {
++ m_bodyWidth = bodyWidth;
++ }
++
++ void Candlestick::setBodyOutlineVisible(bool bodyOutlineVisible)
++ {
++ m_bodyOutlineVisible = bodyOutlineVisible;
++ }
++
++ void Candlestick::setCapsWidth(qreal capsWidth)
++ {
++ m_capsWidth = capsWidth;
++ }
++
++ void Candlestick::setCapsVisible(bool capsVisible)
++ {
++ m_capsVisible = capsVisible;
++ }
++
++ void Candlestick::setIncreasingColor(const QColor &color)
++ {
++ m_increasingColor = color;
++
++ update();
++ }
++
++ void Candlestick::setDecreasingColor(const QColor &color)
++ {
++ m_decreasingColor = color;
++
++ update();
++ }
++
++ void Candlestick::setBrush(const QBrush &brush)
++ {
++ m_brush = brush;
++
++ update();
++ }
++
++ void Candlestick::setPen(const QPen &pen)
++ {
++ qreal widthDiff = pen.widthF() - m_pen.widthF();
++ m_boundingRect.adjust(-widthDiff, -widthDiff, widthDiff, widthDiff);
++
++ m_pen = pen;
++
++ update();
++ }
++
++ void Candlestick::setLayout(const CandlestickData &data)
++ {
++ m_data = data;
++
++ updateGeometry(m_domain);
++ update();
++ }
++
++ void Candlestick::mousePressEvent(QGraphicsSceneMouseEvent *event)
++ {
++ m_mousePressed = true;
++ emit pressed(m_set);
++ QGraphicsItem::mousePressEvent(event);
++ }
++
++ void Candlestick::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
++ {
++ Q_UNUSED(event);
++
++ m_hovering = true;
++ emit hovered(m_hovering, m_set);
++ }
++
++ void Candlestick::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
++ {
++ Q_UNUSED(event);
++
++ m_hovering = false;
++ emit hovered(m_hovering, m_set);
++ }
++
++ void Candlestick::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
++ {
++ emit released(m_set);
++ if (m_mousePressed)
++ emit clicked(m_set);
++ m_mousePressed = false;
++ QGraphicsItem::mouseReleaseEvent(event);
++ }
++
++ void Candlestick::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
++ {
++ // For candlestick a pressed signal needs to be explicitly fired for mouseDoubleClickEvent.
++ emit pressed(m_set);
++ emit doubleClicked(m_set);
++ QGraphicsItem::mouseDoubleClickEvent(event);
++ }
++
++ QRectF Candlestick::boundingRect() const
++ {
++ return m_boundingRect;
++ }
++
++ void Candlestick::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
++ {
++ Q_UNUSED(option);
++ Q_UNUSED(widget);
++
++ bool increasingTrend = (m_data.m_open < m_data.m_close);
++ QColor color = increasingTrend ? m_increasingColor : m_decreasingColor;
++
++ QBrush brush(m_brush);
++ brush.setColor(color);
++
++ painter->save();
++ painter->setBrush(brush);
++ painter->setPen(m_pen);
++ painter->setClipRect(m_boundingRect);
++ if (m_capsVisible)
++ painter->drawPath(m_capsPath);
++ painter->drawPath(m_wicksPath);
++ if (!m_bodyOutlineVisible)
++ painter->setPen(QColor(Qt::transparent));
++ painter->drawRect(m_bodyRect);
++ painter->restore();
++ }
++
++ void Candlestick::updateGeometry(AbstractDomain *domain)
++ {
++ m_domain = domain;
++
++ prepareGeometryChange();
++
++ m_capsPath = QPainterPath();
++ m_wicksPath = QPainterPath();
++ m_boundingRect = QRectF();
++
++ if (!m_data.m_series->chart())
++ return;
++
++ QList<QAbstractAxis *> axes = m_data.m_series->chart()->axes(Qt::Horizontal, m_data.m_series);
++ if (axes.isEmpty())
++ return;
++
++ QAbstractAxis *axisX = axes.value(0);
++ if (!axisX)
++ return;
++
++ qreal columnWidth = 0.0;
++ qreal columnCenter = 0.0;
++ switch (axisX->type()) {
++ case QAbstractAxis::AxisTypeBarCategory:
++ columnWidth = 1.0 / m_data.m_seriesCount;
++ columnCenter = m_data.m_index - 0.5
++ + m_data.m_seriesIndex * columnWidth
++ + columnWidth / 2.0;
++ break;
++ case QAbstractAxis::AxisTypeDateTime:
++ case QAbstractAxis::AxisTypeValue:
++ columnWidth = m_timePeriod;
++ columnCenter = m_data.m_timestamp;
++ break;
++ default:
++ qWarning() << "Unexpected axis type";
++ return;
++ }
++
++ const qreal bodyWidth = m_bodyWidth * columnWidth;
++ const qreal bodyLeft = columnCenter - (bodyWidth / 2.0);
++ const qreal bodyRight = bodyLeft + bodyWidth;
++
++ const qreal upperBody = qMax(m_data.m_open, m_data.m_close);
++ const qreal lowerBody = qMin(m_data.m_open, m_data.m_close);
++ const bool upperWickVisible = (m_data.m_high > upperBody);
++ const bool lowerWickVisible = (m_data.m_low < lowerBody);
++
++ QPointF geometryPoint;
++ bool validData;
++
++ // upper extreme
++ geometryPoint = m_domain->calculateGeometryPoint(QPointF(bodyLeft, m_data.m_high), validData);
++ if (!validData)
++ return;
++ const qreal geometryUpperExtreme = geometryPoint.y();
++ // upper body
++ geometryPoint = m_domain->calculateGeometryPoint(QPointF(bodyLeft, upperBody), validData);
++ if (!validData)
++ return;
++ const qreal geometryBodyLeft = geometryPoint.x();
++ const qreal geometryUpperBody = geometryPoint.y();
++ // lower body
++ geometryPoint = m_domain->calculateGeometryPoint(QPointF(bodyRight, lowerBody), validData);
++ if (!validData)
++ return;
++ const qreal geometryBodyRight = geometryPoint.x();
++ const qreal geometryLowerBody = geometryPoint.y();
++ // lower extreme
++ geometryPoint = m_domain->calculateGeometryPoint(QPointF(bodyRight, m_data.m_low), validData);
++ if (!validData)
++ return;
++ const qreal geometryLowerExtreme = geometryPoint.y();
++
++ // Real Body
++ m_bodyRect.setCoords(geometryBodyLeft, geometryUpperBody, geometryBodyRight, geometryLowerBody);
++ if (m_maximumColumnWidth != -1.0) {
++ if (m_bodyRect.width() > m_maximumColumnWidth) {
++ qreal extra = (m_bodyRect.width() - m_maximumColumnWidth) / 2.0;
++ m_bodyRect.adjust(extra, 0.0, 0.0, 0.0);
++ m_bodyRect.setWidth(m_maximumColumnWidth);
++ }
++ }
++ if (m_minimumColumnWidth != -1.0) {
++ if (m_bodyRect.width() < m_minimumColumnWidth) {
++ qreal extra = (m_minimumColumnWidth - m_bodyRect.width()) / 2.0;
++ m_bodyRect.adjust(-extra, 0.0, 0.0, 0.0);
++ m_bodyRect.setWidth(m_minimumColumnWidth);
++ }
++ }
++
++ const qreal geometryCapsExtra = (m_bodyRect.width() - (m_bodyRect.width() * m_capsWidth)) /2.0;
++ const qreal geometryCapsLeft = m_bodyRect.left() + geometryCapsExtra;
++ const qreal geometryCapsRight = m_bodyRect.right() - geometryCapsExtra;
++
++ // Upper Wick and Cap
++ if (upperWickVisible) {
++ m_capsPath.moveTo(geometryCapsLeft, geometryUpperExtreme);
++ m_capsPath.lineTo(geometryCapsRight, geometryUpperExtreme);
++ m_wicksPath.moveTo((geometryCapsLeft + geometryCapsRight) / 2.0, geometryUpperExtreme);
++ m_wicksPath.lineTo((geometryCapsLeft + geometryCapsRight) / 2.0, geometryUpperBody);
++ }
++ // Lower Wick and Cap
++ if (lowerWickVisible) {
++ m_capsPath.moveTo(geometryCapsLeft, geometryLowerExtreme);
++ m_capsPath.lineTo(geometryCapsRight, geometryLowerExtreme);
++ m_wicksPath.moveTo((geometryCapsLeft + geometryCapsRight) / 2.0, geometryLowerBody);
++ m_wicksPath.lineTo((geometryCapsLeft + geometryCapsRight) / 2.0, geometryLowerExtreme);
++ }
++ m_wicksPath.closeSubpath();
++
++ // bounding rectangle top
++ qreal boundingRectTop;
++ if (upperWickVisible)
++ boundingRectTop = m_wicksPath.boundingRect().top();
++ else
++ boundingRectTop = m_bodyRect.top();
++ boundingRectTop = qMax(boundingRectTop, parentItem()->boundingRect().top());
++ // bounding rectangle right
++ qreal boundingRectRight = qMin(m_bodyRect.right(), parentItem()->boundingRect().right());
++ // bounding rectangle bottom
++ qreal boundingRectBottom;
++ if (lowerWickVisible)
++ boundingRectBottom = m_wicksPath.boundingRect().bottom();
++ else
++ boundingRectBottom = m_bodyRect.bottom();
++ boundingRectBottom = qMin(boundingRectBottom, parentItem()->boundingRect().bottom());
++ // bounding rectangle left
++ qreal boundingRectLeft = qMax(m_bodyRect.left(), parentItem()->boundingRect().left());
++
++ m_boundingRect.setTop(boundingRectTop);
++ m_boundingRect.setRight(boundingRectRight);
++ m_boundingRect.setBottom(boundingRectBottom);
++ m_boundingRect.setLeft(boundingRectLeft);
++
++ qreal extra = m_pen.widthF();
++ m_boundingRect.adjust(-extra, -extra, extra, extra);
++ }
++
++ QT_END_NAMESPACE
++
++ #include "moc_candlestick_p.cpp"