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

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