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

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