1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
5
6#include <QtCharts/QCandlestickSeries>
7#include <QtCharts/QCandlestickSet>
8#include <private/candlestickchartitem_p.h>
9#include <private/candlestick_p.h>
10#include <private/candlestickdata_p.h>
11#include <private/qcandlestickseries_p.h>
12#include <private/candlestickanimation_p.h>
13
14QT_BEGIN_NAMESPACE
15
16CandlestickChartItem::CandlestickChartItem(QCandlestickSeries *series, QGraphicsItem *item)
17 : ChartItem(series->d_func(), item),
18 m_series(series),
19 m_seriesIndex(0),
20 m_seriesCount(0),
21 m_timePeriod(0.0),
22 m_animation(nullptr)
23{
24 setAcceptedMouseButtons({});
25 connect(sender: series, SIGNAL(candlestickSetsAdded(QList<QCandlestickSet *>)),
26 receiver: this, SLOT(handleCandlestickSetsAdd(QList<QCandlestickSet *>)));
27 connect(sender: series, SIGNAL(candlestickSetsRemoved(QList<QCandlestickSet *>)),
28 receiver: this, SLOT(handleCandlestickSetsRemove(QList<QCandlestickSet *>)));
29
30 connect(sender: series->d_func(), SIGNAL(updated()), receiver: this, SLOT(handleCandlesticksUpdated()));
31 connect(sender: series->d_func(), SIGNAL(updatedLayout()), receiver: this, SLOT(handleLayoutUpdated()));
32 connect(sender: series->d_func(), SIGNAL(updatedCandlesticks()),
33 receiver: this, SLOT(handleCandlesticksUpdated()));
34
35 setZValue(ChartPresenter::CandlestickSeriesZValue);
36
37 handleCandlestickSetsAdd(sets: m_series->sets());
38}
39
40CandlestickChartItem::~CandlestickChartItem()
41{
42}
43
44void CandlestickChartItem::setAnimation(CandlestickAnimation *animation)
45{
46 m_animation = animation;
47
48 if (m_animation) {
49 foreach (Candlestick *item, m_candlesticks.values())
50 m_animation->addCandlestick(candlestick: item);
51
52 handleDomainUpdated();
53 }
54}
55
56QRectF CandlestickChartItem::boundingRect() const
57{
58 return m_boundingRect;
59}
60
61void CandlestickChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
62 QWidget *widget)
63{
64 Q_UNUSED(painter);
65 Q_UNUSED(option);
66 Q_UNUSED(widget);
67}
68
69void CandlestickChartItem::handleDomainUpdated()
70{
71 if ((domain()->size().width() <= 0) || (domain()->size().height() <= 0))
72 return;
73
74 // Set bounding rectangle to same as domain size. Add one pixel at the top (-1.0) and the bottom
75 // as 0.0 would snip a bit off from the wick at the grid line.
76 m_boundingRect.setRect(ax: 0.0, ay: -1.0, aaw: domain()->size().width(), aah: domain()->size().height() + 1.0);
77
78 foreach (Candlestick *item, m_candlesticks.values()) {
79 item->updateGeometry(domain: domain());
80
81 if (m_animation)
82 presenter()->startAnimation(animation: m_animation->candlestickAnimation(candlestick: item));
83 }
84}
85
86void CandlestickChartItem::handleLayoutUpdated()
87{
88 bool timestampChanged = false;
89 foreach (QCandlestickSet *set, m_candlesticks.keys()) {
90 qreal oldTimestamp = m_candlesticks.value(key: set)->m_data.m_timestamp;
91 qreal newTimestamp = set->timestamp();
92 if (Q_UNLIKELY(oldTimestamp != newTimestamp)) {
93 removeTimestamp(timestamp: oldTimestamp);
94 addTimestamp(timestamp: newTimestamp);
95 timestampChanged = true;
96 }
97 }
98 if (timestampChanged)
99 updateTimePeriod();
100
101 foreach (Candlestick *item, m_candlesticks.values()) {
102 if (m_animation)
103 m_animation->setAnimationStart(item);
104
105 item->setTimePeriod(m_timePeriod);
106 item->setMaximumColumnWidth(m_series->maximumColumnWidth());
107 item->setMinimumColumnWidth(m_series->minimumColumnWidth());
108 item->setBodyWidth(m_series->bodyWidth());
109 item->setCapsWidth(m_series->capsWidth());
110
111 bool dirty = updateCandlestickGeometry(item, index: item->m_data.m_index);
112 if (dirty && m_animation)
113 presenter()->startAnimation(animation: m_animation->candlestickChangeAnimation(candlestick: item));
114 else
115 item->updateGeometry(domain: domain());
116 }
117}
118
119void CandlestickChartItem::handleCandlesticksUpdated()
120{
121 foreach (QCandlestickSet *set, m_candlesticks.keys())
122 updateCandlestickAppearance(item: m_candlesticks.value(key: set), set);
123}
124
125void CandlestickChartItem::handleCandlestickSeriesChange()
126{
127 int seriesIndex = 0;
128 int seriesCount = 0;
129
130 int index = 0;
131 const auto allSeries = m_series->chart()->series();
132 for (QAbstractSeries *series : allSeries) {
133 if (series->type() == QAbstractSeries::SeriesTypeCandlestick) {
134 if (m_series == static_cast<QCandlestickSeries *>(series))
135 seriesIndex = index;
136 index++;
137 }
138 }
139 seriesCount = index;
140
141 bool changed;
142 if ((m_seriesIndex != seriesIndex) || (m_seriesCount != seriesCount))
143 changed = true;
144 else
145 changed = false;
146
147 if (changed) {
148 m_seriesIndex = seriesIndex;
149 m_seriesCount = seriesCount;
150 handleDataStructureChanged();
151 }
152}
153
154void CandlestickChartItem::handleCandlestickSetsAdd(const QList<QCandlestickSet *> &sets)
155{
156 foreach (QCandlestickSet *set, sets) {
157 Candlestick *item = m_candlesticks.value(key: set, defaultValue: 0);
158 if (item) {
159 qWarning() << "There is already a candlestick for this set in the hash";
160 continue;
161 }
162
163 item = new Candlestick(set, domain(), this);
164 m_candlesticks.insert(key: set, value: item);
165 addTimestamp(timestamp: set->timestamp());
166
167 connect(sender: item, SIGNAL(clicked(QCandlestickSet *)),
168 receiver: m_series, SIGNAL(clicked(QCandlestickSet *)));
169 connect(sender: item, SIGNAL(hovered(bool, QCandlestickSet *)),
170 receiver: m_series, SIGNAL(hovered(bool, QCandlestickSet *)));
171 connect(sender: item, SIGNAL(pressed(QCandlestickSet *)),
172 receiver: m_series, SIGNAL(pressed(QCandlestickSet *)));
173 connect(sender: item, SIGNAL(released(QCandlestickSet *)),
174 receiver: m_series, SIGNAL(released(QCandlestickSet *)));
175 connect(sender: item, SIGNAL(doubleClicked(QCandlestickSet *)),
176 receiver: m_series, SIGNAL(doubleClicked(QCandlestickSet *)));
177 connect(sender: item, SIGNAL(clicked(QCandlestickSet *)), receiver: set, SIGNAL(clicked()));
178 connect(sender: item, SIGNAL(hovered(bool, QCandlestickSet *)), receiver: set, SIGNAL(hovered(bool)));
179 connect(sender: item, SIGNAL(pressed(QCandlestickSet *)), receiver: set, SIGNAL(pressed()));
180 connect(sender: item, SIGNAL(released(QCandlestickSet *)), receiver: set, SIGNAL(released()));
181 connect(sender: item, SIGNAL(doubleClicked(QCandlestickSet *)), receiver: set, SIGNAL(doubleClicked()));
182 }
183
184 handleDataStructureChanged();
185}
186
187void CandlestickChartItem::handleCandlestickSetsRemove(const QList<QCandlestickSet *> &sets)
188{
189 foreach (QCandlestickSet *set, sets) {
190 Candlestick *item = m_candlesticks.value(key: set);
191
192 m_candlesticks.remove(key: set);
193 removeTimestamp(timestamp: set->timestamp());
194
195 if (m_animation) {
196 ChartAnimation *animation = m_animation->candlestickAnimation(candlestick: item);
197 if (animation) {
198 animation->stop();
199 delete animation;
200 }
201 }
202
203 delete item;
204 }
205
206 handleDataStructureChanged();
207}
208
209void CandlestickChartItem::handleDataStructureChanged()
210{
211 updateTimePeriod();
212
213 for (int i = 0; i < m_series->count(); ++i) {
214 QCandlestickSet *set = m_series->sets().at(i);
215 Candlestick *item = m_candlesticks.value(key: set);
216
217 updateCandlestickGeometry(item, index: i);
218 updateCandlestickAppearance(item, set);
219
220 item->updateGeometry(domain: domain());
221
222 if (m_animation)
223 m_animation->addCandlestick(candlestick: item);
224 }
225
226 handleDomainUpdated();
227}
228
229bool CandlestickChartItem::updateCandlestickGeometry(Candlestick *item, int index)
230{
231 bool changed = false;
232
233 QCandlestickSet *set = m_series->sets().at(i: index);
234 CandlestickData &data = item->m_data;
235
236 if ((data.m_open != set->open())
237 || (data.m_high != set->high())
238 || (data.m_low != set->low())
239 || (data.m_close != set->close())) {
240 changed = true;
241 }
242
243 data.m_timestamp = set->timestamp();
244 data.m_open = set->open();
245 data.m_high = set->high();
246 data.m_low = set->low();
247 data.m_close = set->close();
248 data.m_index = index;
249
250 data.m_maxX = domain()->maxX();
251 data.m_minX = domain()->minX();
252 data.m_maxY = domain()->maxY();
253 data.m_minY = domain()->minY();
254
255 data.m_series = m_series;
256 data.m_seriesIndex = m_seriesIndex;
257 data.m_seriesCount = m_seriesCount;
258
259 return changed;
260}
261
262void CandlestickChartItem::updateCandlestickAppearance(Candlestick *item, QCandlestickSet *set)
263{
264 item->setTimePeriod(m_timePeriod);
265 item->setMaximumColumnWidth(m_series->maximumColumnWidth());
266 item->setMinimumColumnWidth(m_series->minimumColumnWidth());
267 item->setBodyWidth(m_series->bodyWidth());
268 item->setBodyOutlineVisible(m_series->bodyOutlineVisible());
269 item->setCapsWidth(m_series->capsWidth());
270 item->setCapsVisible(m_series->capsVisible());
271 item->setIncreasingColor(m_series->increasingColor());
272 item->setDecreasingColor(m_series->decreasingColor());
273
274 // Set the decorative issues for the candlestick so that
275 // the brush and pen already defined for the set are kept.
276 if (set->brush() == Qt::NoBrush)
277 item->setBrush(m_series->brush());
278 else
279 item->setBrush(set->brush());
280
281 if (set->pen() == Qt::NoPen)
282 item->setPen(m_series->pen());
283 else
284 item->setPen(set->pen());
285}
286
287void CandlestickChartItem::addTimestamp(qreal timestamp)
288{
289 int index = 0;
290 for (int i = m_timestamps.size() - 1; i >= 0; --i) {
291 if (timestamp > m_timestamps.at(i)) {
292 index = i + 1;
293 break;
294 }
295 }
296 m_timestamps.insert(i: index, t: timestamp);
297}
298
299void CandlestickChartItem::removeTimestamp(qreal timestamp)
300{
301 m_timestamps.removeOne(t: timestamp);
302}
303
304void CandlestickChartItem::updateTimePeriod()
305{
306 if (m_timestamps.size() == 0) {
307 m_timePeriod = 0;
308 return;
309 }
310
311 if (m_timestamps.size() == 1) {
312 m_timePeriod = qAbs(t: domain()->maxX() - domain()->minX());
313 return;
314 }
315
316 qreal timePeriod = qAbs(t: m_timestamps.at(i: 1) - m_timestamps.at(i: 0));
317 for (int i = 1; i < m_timestamps.size(); ++i) {
318 timePeriod = qMin(a: timePeriod, b: qAbs(t: m_timestamps.at(i) - m_timestamps.at(i: i - 1)));
319 }
320 m_timePeriod = timePeriod;
321}
322
323QT_END_NAMESPACE
324
325#include "moc_candlestickchartitem_p.cpp"
326

source code of qtcharts/src/charts/candlestickchart/candlestickchartitem.cpp