// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE CandlestickChartItem::CandlestickChartItem(QCandlestickSeries *series, QGraphicsItem *item) : ChartItem(series->d_func(), item), m_series(series), m_seriesIndex(0), m_seriesCount(0), m_timePeriod(0.0), m_animation(nullptr) { setAcceptedMouseButtons({}); connect(series, SIGNAL(candlestickSetsAdded(QList)), this, SLOT(handleCandlestickSetsAdd(QList))); connect(series, SIGNAL(candlestickSetsRemoved(QList)), this, SLOT(handleCandlestickSetsRemove(QList))); connect(series->d_func(), SIGNAL(updated()), this, SLOT(handleCandlesticksUpdated())); connect(series->d_func(), SIGNAL(updatedLayout()), this, SLOT(handleLayoutUpdated())); connect(series->d_func(), SIGNAL(updatedCandlesticks()), this, SLOT(handleCandlesticksUpdated())); setZValue(ChartPresenter::CandlestickSeriesZValue); handleCandlestickSetsAdd(m_series->sets()); } CandlestickChartItem::~CandlestickChartItem() { } void CandlestickChartItem::setAnimation(CandlestickAnimation *animation) { m_animation = animation; if (m_animation) { foreach (Candlestick *item, m_candlesticks.values()) m_animation->addCandlestick(item); handleDomainUpdated(); } } QRectF CandlestickChartItem::boundingRect() const { return m_boundingRect; } void CandlestickChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(painter); Q_UNUSED(option); Q_UNUSED(widget); } void CandlestickChartItem::handleDomainUpdated() { if ((domain()->size().width() <= 0) || (domain()->size().height() <= 0)) return; // Set bounding rectangle to same as domain size. Add one pixel at the top (-1.0) and the bottom // as 0.0 would snip a bit off from the wick at the grid line. m_boundingRect.setRect(0.0, -1.0, domain()->size().width(), domain()->size().height() + 1.0); foreach (Candlestick *item, m_candlesticks.values()) { item->updateGeometry(domain()); if (m_animation) presenter()->startAnimation(m_animation->candlestickAnimation(item)); } } void CandlestickChartItem::handleLayoutUpdated() { bool timestampChanged = false; foreach (QCandlestickSet *set, m_candlesticks.keys()) { qreal oldTimestamp = m_candlesticks.value(set)->m_data.m_timestamp; qreal newTimestamp = set->timestamp(); if (Q_UNLIKELY(oldTimestamp != newTimestamp)) { removeTimestamp(oldTimestamp); addTimestamp(newTimestamp); timestampChanged = true; } } if (timestampChanged) updateTimePeriod(); foreach (Candlestick *item, m_candlesticks.values()) { if (m_animation) m_animation->setAnimationStart(item); item->setTimePeriod(m_timePeriod); item->setMaximumColumnWidth(m_series->maximumColumnWidth()); item->setMinimumColumnWidth(m_series->minimumColumnWidth()); item->setBodyWidth(m_series->bodyWidth()); item->setCapsWidth(m_series->capsWidth()); bool dirty = updateCandlestickGeometry(item, item->m_data.m_index); if (dirty && m_animation) presenter()->startAnimation(m_animation->candlestickChangeAnimation(item)); else item->updateGeometry(domain()); } } void CandlestickChartItem::handleCandlesticksUpdated() { foreach (QCandlestickSet *set, m_candlesticks.keys()) updateCandlestickAppearance(m_candlesticks.value(set), set); } void CandlestickChartItem::handleCandlestickSeriesChange() { int seriesIndex = 0; int seriesCount = 0; int index = 0; const auto allSeries = m_series->chart()->series(); for (QAbstractSeries *series : allSeries) { if (series->type() == QAbstractSeries::SeriesTypeCandlestick) { if (m_series == static_cast(series)) seriesIndex = index; index++; } } seriesCount = index; bool changed; if ((m_seriesIndex != seriesIndex) || (m_seriesCount != seriesCount)) changed = true; else changed = false; if (changed) { m_seriesIndex = seriesIndex; m_seriesCount = seriesCount; handleDataStructureChanged(); } } void CandlestickChartItem::handleCandlestickSetsAdd(const QList &sets) { foreach (QCandlestickSet *set, sets) { Candlestick *item = m_candlesticks.value(set, 0); if (item) { qWarning() << "There is already a candlestick for this set in the hash"; continue; } item = new Candlestick(set, domain(), this); m_candlesticks.insert(set, item); addTimestamp(set->timestamp()); connect(item, SIGNAL(clicked(QCandlestickSet *)), m_series, SIGNAL(clicked(QCandlestickSet *))); connect(item, SIGNAL(hovered(bool, QCandlestickSet *)), m_series, SIGNAL(hovered(bool, QCandlestickSet *))); connect(item, SIGNAL(pressed(QCandlestickSet *)), m_series, SIGNAL(pressed(QCandlestickSet *))); connect(item, SIGNAL(released(QCandlestickSet *)), m_series, SIGNAL(released(QCandlestickSet *))); connect(item, SIGNAL(doubleClicked(QCandlestickSet *)), m_series, SIGNAL(doubleClicked(QCandlestickSet *))); connect(item, SIGNAL(clicked(QCandlestickSet *)), set, SIGNAL(clicked())); connect(item, SIGNAL(hovered(bool, QCandlestickSet *)), set, SIGNAL(hovered(bool))); connect(item, SIGNAL(pressed(QCandlestickSet *)), set, SIGNAL(pressed())); connect(item, SIGNAL(released(QCandlestickSet *)), set, SIGNAL(released())); connect(item, SIGNAL(doubleClicked(QCandlestickSet *)), set, SIGNAL(doubleClicked())); } handleDataStructureChanged(); } void CandlestickChartItem::handleCandlestickSetsRemove(const QList &sets) { foreach (QCandlestickSet *set, sets) { Candlestick *item = m_candlesticks.value(set); m_candlesticks.remove(set); removeTimestamp(set->timestamp()); if (m_animation) { ChartAnimation *animation = m_animation->candlestickAnimation(item); if (animation) { animation->stop(); delete animation; } } delete item; } handleDataStructureChanged(); } void CandlestickChartItem::handleDataStructureChanged() { updateTimePeriod(); for (int i = 0; i < m_series->count(); ++i) { QCandlestickSet *set = m_series->sets().at(i); Candlestick *item = m_candlesticks.value(set); updateCandlestickGeometry(item, i); updateCandlestickAppearance(item, set); item->updateGeometry(domain()); if (m_animation) m_animation->addCandlestick(item); } handleDomainUpdated(); } bool CandlestickChartItem::updateCandlestickGeometry(Candlestick *item, int index) { bool changed = false; QCandlestickSet *set = m_series->sets().at(index); CandlestickData &data = item->m_data; if ((data.m_open != set->open()) || (data.m_high != set->high()) || (data.m_low != set->low()) || (data.m_close != set->close())) { changed = true; } data.m_timestamp = set->timestamp(); data.m_open = set->open(); data.m_high = set->high(); data.m_low = set->low(); data.m_close = set->close(); data.m_index = index; data.m_maxX = domain()->maxX(); data.m_minX = domain()->minX(); data.m_maxY = domain()->maxY(); data.m_minY = domain()->minY(); data.m_series = m_series; data.m_seriesIndex = m_seriesIndex; data.m_seriesCount = m_seriesCount; return changed; } void CandlestickChartItem::updateCandlestickAppearance(Candlestick *item, QCandlestickSet *set) { item->setTimePeriod(m_timePeriod); item->setMaximumColumnWidth(m_series->maximumColumnWidth()); item->setMinimumColumnWidth(m_series->minimumColumnWidth()); item->setBodyWidth(m_series->bodyWidth()); item->setBodyOutlineVisible(m_series->bodyOutlineVisible()); item->setCapsWidth(m_series->capsWidth()); item->setCapsVisible(m_series->capsVisible()); item->setIncreasingColor(m_series->increasingColor()); item->setDecreasingColor(m_series->decreasingColor()); // Set the decorative issues for the candlestick so that // the brush and pen already defined for the set are kept. if (set->brush() == Qt::NoBrush) item->setBrush(m_series->brush()); else item->setBrush(set->brush()); if (set->pen() == Qt::NoPen) item->setPen(m_series->pen()); else item->setPen(set->pen()); } void CandlestickChartItem::addTimestamp(qreal timestamp) { int index = 0; for (int i = m_timestamps.size() - 1; i >= 0; --i) { if (timestamp > m_timestamps.at(i)) { index = i + 1; break; } } m_timestamps.insert(index, timestamp); } void CandlestickChartItem::removeTimestamp(qreal timestamp) { m_timestamps.removeOne(timestamp); } void CandlestickChartItem::updateTimePeriod() { if (m_timestamps.size() == 0) { m_timePeriod = 0; return; } if (m_timestamps.size() == 1) { m_timePeriod = qAbs(domain()->maxX() - domain()->minX()); return; } qreal timePeriod = qAbs(m_timestamps.at(1) - m_timestamps.at(0)); for (int i = 1; i < m_timestamps.size(); ++i) { timePeriod = qMin(timePeriod, qAbs(m_timestamps.at(i) - m_timestamps.at(i - 1))); } m_timePeriod = timePeriod; } QT_END_NAMESPACE #include "moc_candlestickchartitem_p.cpp"