##// END OF EJS Templates
gdpbarchart moved to test. Few small doc fixes
Marek Rosa -
r940:7021ab6fbe7f
parent child
Show More
@@ -1,5 +1,4
1 1 TEMPLATE = subdirs
2 2 SUBDIRS += chartthemes \
3 3 piechartcustomization \
4 gdpbarchart \
5 4 qmlchart
@@ -1,211 +1,211
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2012 Digia Plc
4 4 ** All rights reserved.
5 5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 6 **
7 7 ** This file is part of the Qt Commercial Charts Add-on.
8 8 **
9 9 ** $QT_BEGIN_LICENSE$
10 10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 11 ** accordance with the Qt Commercial License Agreement provided with the
12 12 ** Software or, alternatively, in accordance with the terms contained in
13 13 ** a written agreement between you and Digia.
14 14 **
15 15 ** If you have questions regarding the use of this file, please use
16 16 ** contact form at http://qt.digia.com
17 17 ** $QT_END_LICENSE$
18 18 **
19 19 ****************************************************************************/
20 20
21 21 #include "qareaseries.h"
22 22 #include "qareaseries_p.h"
23 23 #include "qlineseries.h"
24 24
25 25 QTCOMMERCIALCHART_BEGIN_NAMESPACE
26 26
27 27 /*!
28 28 \class QAreaSeries
29 29 \brief The QAreaSeries class is used for making area charts.
30 30
31 31 \mainclass
32 32
33 33 An area chart is used to show quantitative data. It is based on line chart, in the way that area between axis and the line
34 34 is emphasized with color. Since the area chart is based on line chart, QAreaSeries constructor needs QLineSeries instance,
35 35 which defines "upper" boundary of the area. "Lower" boundary is defined by default by axis X. Instead of axis X "lower" boundary can be specified by other line.
36 36 In that case QAreaSeries should be initiated with two QLineSerie instances. Please note terms "upper" and "lower" boundary can be misleading in cases
37 37 where "lower" boundary had bigger values than the "upper" one, however the main point that area between these two boundary lines will be filled.
38 38
39 39 \image areachart.png
40 40
41 41 Creating basic area chart is simple:
42 42 \code
43 43 QLineSeries* lineSeries = new QLineSeries();
44 44 series->append(0, 6);
45 45 series->append(2, 4);
46 46 QAreaSeries* areaSeries = new QAreaSeries(lineSeries);
47 47 ...
48 48 chartView->addSeries(areaSeries);
49 49 \endcode
50 50 */
51 51
52 52 /*!
53 53 \fn virtual QSeriesType QAreaSeries::type() const
54 54 \brief Returns type of series.
55 55 \sa QSeries, QSeriesType
56 56 */
57 57
58 58 /*!
59 59 \fn QLineSeries* QAreaSeries::upperSeries() const
60 60 \brief Returns upperSeries used to define one of area boundaries.
61 61 */
62 62
63 63 /*!
64 64 \fn QLineSeries* QAreaSeries::lowerSeries() const
65 65 \brief Returns lowerSeries used to define one of area boundaries. Note if QAreaSeries where counstucted wihtout a\ lowerSeries
66 66 this function return Null pointer.
67 67 */
68 68
69 69 /*!
70 70 \fn QPen QAreaSeries::pen() const
71 71 \brief Returns the pen used to draw line for this series.
72 72 \sa setPen()
73 73 */
74 74
75 75 /*!
76 76 \fn QPen QAreaSeries::brush() const
77 77 \brief Returns the brush used to draw line for this series.
78 78 \sa setBrush()
79 79 */
80 80
81 81 /*!
82 82 \fn bool QAreaSeries::pointsVisible() const
83 83 \brief Returns if the points are drawn for this series.
84 84 \sa setPointsVisible()
85 85 */
86 86
87 87 /*!
88 88 \fn void QAreaSeries::clicked(const QPointF& point)
89 89 \brief Signal is emitted when user clicks the \a point on area chart.
90 90 */
91 91
92 92 /*!
93 \fn void QAreaSeries::updated()
93 \fn void QAreaSeriesPrivate::updated()
94 94 \brief \internal
95 95 */
96 96
97 97 /*!
98 98 Constructs area series object which is a child of \a upperSeries. Area will be spanned between \a
99 99 upperSeries line and \a lowerSeries line. If no \a lowerSeries is passed to constructor, area is specified by axis x (y=0) instead.
100 100 When series object is added to QChartView or QChart instance ownerships is transfered.
101 101 */
102 102 QAreaSeries::QAreaSeries(QLineSeries *upperSeries, QLineSeries *lowerSeries)
103 103 : QSeries(*new QAreaSeriesPrivate(upperSeries,lowerSeries,this),upperSeries)
104 104 {
105 105 }
106 106
107 107 /*!
108 108 Destroys the object. Series added to QChartView or QChart instances are owned by those,
109 109 and are deleted when mentioned object are destroyed.
110 110 */
111 111 QAreaSeries::~QAreaSeries()
112 112 {
113 113 }
114 114
115 115
116 116 QSeries::QSeriesType QAreaSeries::type() const
117 117 {
118 118 return QSeries::SeriesTypeArea;
119 119 }
120 120
121 121 QLineSeries* QAreaSeries::upperSeries() const
122 122 {
123 123 Q_D(const QAreaSeries);
124 124 return d->m_upperSeries;
125 125 }
126 126
127 127 QLineSeries* QAreaSeries::lowerSeries() const
128 128 {
129 129 Q_D(const QAreaSeries);
130 130 return d->m_lowerSeries;
131 131 }
132 132
133 133 /*!
134 134 Sets \a pen used for drawing area outline.
135 135 */
136 136 void QAreaSeries::setPen(const QPen &pen)
137 137 {
138 138 Q_D(QAreaSeries);
139 139 if (d->m_pen != pen) {
140 140 d->m_pen = pen;
141 141 emit d->updated();
142 142 }
143 143 }
144 144
145 145 QPen QAreaSeries::pen() const
146 146 {
147 147 Q_D(const QAreaSeries);
148 148 return d->m_pen;
149 149 }
150 150
151 151 /*!
152 152 Sets \a brush used for filling the area.
153 153 */
154 154 void QAreaSeries::setBrush(const QBrush &brush)
155 155 {
156 156 Q_D(QAreaSeries);
157 157 if (d->m_brush != brush) {
158 158 d->m_brush = brush;
159 159 emit d->updated();
160 160 }
161 161 }
162 162
163 163 QBrush QAreaSeries::brush() const
164 164 {
165 165 Q_D(const QAreaSeries);
166 166 return d->m_brush;
167 167 }
168 168 /*!
169 169 Sets if data points are \a visible and should be drawn on line.
170 170 */
171 171 void QAreaSeries::setPointsVisible(bool visible)
172 172 {
173 173 Q_D(QAreaSeries);
174 174 if (d->m_pointsVisible != visible) {
175 175 d->m_pointsVisible = visible;
176 176 emit d->updated();
177 177 }
178 178 }
179 179
180 180 bool QAreaSeries::pointsVisible() const
181 181 {
182 182 Q_D(const QAreaSeries);
183 183 return d->m_pointsVisible;
184 184 }
185 185
186 186 bool QAreaSeries::setModel(QAbstractItemModel* model)
187 187 {
188 188 Q_UNUSED(model);
189 189 qWarning()<<"Not implemented";
190 190 return false;
191 191 }
192 192
193 193 QAbstractItemModel* QAreaSeries::model() const
194 194 {
195 195 return 0;
196 196 }
197 197
198 198 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
199 199
200 200 QAreaSeriesPrivate::QAreaSeriesPrivate(QLineSeries *upperSeries, QLineSeries *lowerSeries,QAreaSeries* q):QSeriesPrivate(q),
201 201 m_upperSeries(upperSeries),
202 202 m_lowerSeries(lowerSeries),
203 203 m_pointsVisible(false)
204 204 {
205 205
206 206 };
207 207
208 208 #include "moc_qareaseries.cpp"
209 209 #include "moc_qareaseries_p.cpp"
210 210
211 211 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,321 +1,321
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2012 Digia Plc
4 4 ** All rights reserved.
5 5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 6 **
7 7 ** This file is part of the Qt Commercial Charts Add-on.
8 8 **
9 9 ** $QT_BEGIN_LICENSE$
10 10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 11 ** accordance with the Qt Commercial License Agreement provided with the
12 12 ** Software or, alternatively, in accordance with the terms contained in
13 13 ** a written agreement between you and Digia.
14 14 **
15 15 ** If you have questions regarding the use of this file, please use
16 16 ** contact form at http://qt.digia.com
17 17 ** $QT_END_LICENSE$
18 18 **
19 19 ****************************************************************************/
20 20
21 21 #include "qbarset.h"
22 22 #include "qbarset_p.h"
23 23 #include <QToolTip>
24 24
25 25 QTCOMMERCIALCHART_BEGIN_NAMESPACE
26 26
27 27 /*!
28 28 \class QBarSet
29 29 \brief part of QtCommercial chart API.
30 30
31 31 QBarSet represents one set of bars. Set of bars contains one data value for each category.
32 32 First value of set is assumed to belong to first category, second to second category and so on.
33 33 If set has fewer values than there are categories, then the missing values are assumed to be
34 34 at the end of set. For missing values in middle of a set, numerical value of zero is used.
35 35
36 36 \mainclass
37 37
38 38 \sa QBarSeries, QStackedBarSeries, QPercentBarSeries
39 39 */
40 40
41 41 /*!
42 42 \fn void QBarSet::clicked(QString category, Qt::MouseButtons button)
43 43 \brief signals that set has been clicked
44 44 Parameter \a category describes on which category was clicked
45 45 Parameter \a button mouse button
46 46 */
47 47
48 48 /*!
49 49 \fn void QBarSet::hoverEnter(QPoint pos)
50 50 \brief signals that mouse has entered over the set at position \a pos.
51 51 */
52 52
53 53 /*!
54 54 \fn void QBarSet::hoverLeave()
55 55 \brief signals that mouse has left from the set.
56 56 */
57 57
58 58 /*!
59 \fn void QBarSet::showToolTip(QPoint pos, QString tip)
59 \fn void QBarSetPrivate::showToolTip(QPoint pos, QString tip)
60 60 \brief \internal \a pos \a tip
61 61 */
62 62
63 63
64 64 /*!
65 65 Constructs QBarSet with a name of \a name and with parent of \a parent
66 66 */
67 67 QBarSet::QBarSet(QString name, QObject *parent)
68 68 : QObject(parent)
69 69 ,d_ptr(new QBarSetPrivate(name,this))
70 70 // ,m_name(name)
71 71 // ,m_labelsVisible(false)
72 72 {
73 73 }
74 74
75 75 /*!
76 76 Sets new \a name for set.
77 77 */
78 78 void QBarSet::setName(QString name)
79 79 {
80 80 d_ptr->m_name = name;
81 81 }
82 82
83 83 /*!
84 84 Returns name of the set.
85 85 */
86 86 QString QBarSet::name() const
87 87 {
88 88 return d_ptr->m_name;
89 89 }
90 90
91 91 /*!
92 92 Appends new value \a value to the end of set.
93 93 */
94 94 QBarSet& QBarSet::operator << (const qreal &value)
95 95 {
96 96 d_ptr->m_values.append(value);
97 97 emit d_ptr->structureChanged();
98 98 return *this;
99 99 }
100 100
101 101 /*!
102 102 Inserts new \a value on the \a i position.
103 103 The value that is currently at this postion is moved to postion i + 1
104 104 \sa removeValue()
105 105 */
106 106 void QBarSet::insertValue(int i, qreal value)
107 107 {
108 108 d_ptr->m_values.insert(i, value);
109 109 }
110 110
111 111 /*!
112 112 Removes the value specified by \a i
113 113 \sa insertValue()
114 114 */
115 115 void QBarSet::removeValue(int i)
116 116 {
117 117 d_ptr->m_values.removeAt(i);
118 118 }
119 119
120 120 /*!
121 121 Returns count of values in set.
122 122 */
123 123 int QBarSet::count() const
124 124 {
125 125 return d_ptr->m_values.count();
126 126 }
127 127
128 128 /*!
129 129 Returns value of set indexed by \a index
130 130 */
131 131 qreal QBarSet::valueAt(int index) const
132 132 {
133 133 return d_ptr->m_values.at(index);
134 134 }
135 135
136 136 /*!
137 137 Sets a new value \a value to set, indexed by \a index
138 138 */
139 139 void QBarSet::setValue(int index, qreal value)
140 140 {
141 141 d_ptr->m_values.replace(index,value);
142 142 emit d_ptr->valueChanged();
143 143 }
144 144
145 145 /*!
146 146 Returns sum of all values in barset.
147 147 */
148 148 qreal QBarSet::sum() const
149 149 {
150 150 qreal total(0);
151 151 for (int i=0; i < d_ptr->m_values.count(); i++) {
152 152 total += d_ptr->m_values.at(i);
153 153 }
154 154 return total;
155 155 }
156 156
157 157 /*!
158 158 Sets pen for set. Bars of this set are drawn using \a pen
159 159 */
160 160 void QBarSet::setPen(const QPen &pen)
161 161 {
162 162 if(d_ptr->m_pen!=pen){
163 163 d_ptr->m_pen = pen;
164 164 emit d_ptr->valueChanged();
165 165 }
166 166 }
167 167
168 168 /*!
169 169 Returns pen of the set.
170 170 */
171 171 QPen QBarSet::pen() const
172 172 {
173 173 return d_ptr->m_pen;
174 174 }
175 175
176 176 /*!
177 177 Sets brush for the set. Bars of this set are drawn using \a brush
178 178 */
179 179 void QBarSet::setBrush(const QBrush &brush)
180 180 {
181 181 if(d_ptr->m_brush!=brush){
182 182 d_ptr->m_brush = brush;
183 183 emit d_ptr->valueChanged();
184 184 }
185 185 }
186 186
187 187 /*!
188 188 Returns brush of the set.
189 189 */
190 190 QBrush QBarSet::brush() const
191 191 {
192 192 return d_ptr->m_brush;
193 193 }
194 194
195 195 /*!
196 196 Sets \a pen of the values that are drawn on top of this barset
197 197 */
198 198 void QBarSet::setLabelPen(const QPen &pen)
199 199 {
200 200 if(d_ptr->m_labelPen!=pen){
201 201 d_ptr->m_labelPen = pen;
202 202 emit d_ptr->valueChanged();
203 203 }
204 204 }
205 205
206 206 /*!
207 207 Returns pen of the values that are drawn on top of this barset
208 208 */
209 209 QPen QBarSet::labelPen() const
210 210 {
211 211 return d_ptr->m_labelPen;
212 212 }
213 213
214 214 /*!
215 215 Sets \a brush of the values that are drawn on top of this barset
216 216 */
217 217 void QBarSet::setLabelBrush(const QBrush &brush)
218 218 {
219 219 if(d_ptr->m_labelBrush!=brush){
220 220 d_ptr->m_labelBrush = brush;
221 221 emit d_ptr->valueChanged();
222 222 }
223 223 }
224 224
225 225 /*!
226 226 Returns brush of the values that are drawn on top of this barset
227 227 */
228 228 QBrush QBarSet::labelBrush() const
229 229 {
230 230 return d_ptr->m_labelBrush;
231 231 }
232 232
233 233 /*!
234 234 Sets the \a font for values that are drawn on top of this barset
235 235 */
236 236 void QBarSet::setLabelFont(const QFont &font)
237 237 {
238 238 if(d_ptr->m_labelFont!=font) {
239 239 d_ptr->m_labelFont = font;
240 240 emit d_ptr->valueChanged();
241 241 }
242 242
243 243 }
244 244
245 245 /*!
246 246 Returns the pen for values that are drawn on top of this set
247 247 */
248 248 QFont QBarSet::labelFont() const
249 249 {
250 250 return d_ptr->m_labelFont;
251 251 }
252 252
253 253 /*!
254 254 Sets visibility of bar labels. If \a visible is true, labels are drawn on top of barsets.
255 255 */
256 256
257 257 void QBarSet::setLabelsVisible(bool visible)
258 258 {
259 259 if(d_ptr->m_labelsVisible!=visible) {
260 260 d_ptr->m_labelsVisible = visible;
261 261 emit d_ptr->labelsVisibleChanged(visible);
262 262 }
263 263 }
264 264
265 265 /*!
266 266 Returns the visibility of values
267 267 */
268 268 bool QBarSet::labelsVisible() const
269 269 {
270 270 return d_ptr->m_labelsVisible;
271 271 }
272 272
273 273 /*
274 274 void QBarSet::barHoverEnterEvent(QPoint pos)
275 275 {
276 276 emit showToolTip(pos, m_name);
277 277 emit hoverEnter(pos);
278 278 }
279 279 */
280 280 /*
281 281 void QBarSet::barHoverLeaveEvent()
282 282 {
283 283 // Emit signal to user of charts
284 284 emit hoverLeave();
285 285 }
286 286 */
287 287
288 288 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
289 289
290 290 QBarSetPrivate::QBarSetPrivate(QString name, QBarSet *parent) : QObject(parent),
291 291 q_ptr(parent),
292 292 m_name(name),
293 293 m_labelsVisible(false)
294 294 {
295 295
296 296 }
297 297
298 298 QBarSetPrivate::~QBarSetPrivate()
299 299 {
300 300
301 301 }
302 302
303 303
304 304 //TODO: fixme , refactor it and get rid of it
305 305 void QBarSetPrivate::barHoverEnterEvent(QPoint pos)
306 306 {
307 307 emit showToolTip(pos, m_name);
308 308 emit hoverEnter(pos);
309 309 }
310 310
311 311 //TODO: fixme , refactor it and get rid of it
312 312 void QBarSetPrivate::barHoverLeaveEvent()
313 313 {
314 314 // Emit signal to user of charts
315 315 emit hoverLeave();
316 316 }
317 317
318 318 #include "moc_qbarset.cpp"
319 319 #include "moc_qbarset_p.cpp"
320 320
321 321 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,150 +1,150
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2012 Digia Plc
4 4 ** All rights reserved.
5 5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 6 **
7 7 ** This file is part of the Qt Commercial Charts Add-on.
8 8 **
9 9 ** $QT_BEGIN_LICENSE$
10 10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 11 ** accordance with the Qt Commercial License Agreement provided with the
12 12 ** Software or, alternatively, in accordance with the terms contained in
13 13 ** a written agreement between you and Digia.
14 14 **
15 15 ** If you have questions regarding the use of this file, please use
16 16 ** contact form at http://qt.digia.com
17 17 ** $QT_END_LICENSE$
18 18 **
19 19 ****************************************************************************/
20 20
21 21 #include "qseries.h"
22 22 #include "qseries_p.h"
23 23
24 24 QTCOMMERCIALCHART_BEGIN_NAMESPACE
25 25
26 26 /*!
27 27 \class QSeries
28 28 \brief Base class for all QtCommercial Chart series.
29 29 \mainclass
30 30
31 31 Usually you use the series type specific inherited classes instead of the base class.
32 32 \sa QScatterSeries
33 33 */
34 34
35 35 /*!
36 36 \enum QSeries::QSeriesType
37 37
38 38 The type of the series object.
39 39
40 40 \value SeriesTypeLine
41 41 \value SeriesTypeArea
42 42 \value SeriesTypeBar
43 43 \value SeriesTypeStackedBar
44 44 \value SeriesTypePercentBar
45 45 \value SeriesTypePie
46 46 \value SeriesTypeScatter
47 47 \value SeriesTypeSpline
48 48 */
49 49
50 50 /*!
51 51 \fn QSeries::QSeries(QObject *parent)
52 52 \brief Constructs ChartSeries object with \a parent.
53 53 */
54 54
55 55 /*!
56 56 \fn QSeries::~QSeries()
57 57 \brief Virtual destructor for the chart series.
58 58 */
59 59
60 60 /*!
61 61 \fn QSeriesType QSeries::type() const
62 62 \brief The type of the series.
63 63 */
64 64
65 65 /*!
66 66 \fn bool QSeries::setModel(QAbstractItemModel *model)
67 67 \brief Use the \a model to provide data for the series. The model overrides possible user data
68 68 set with QChartSeries type specific data setters. For example if you call both
69 69 QScatterSeries::addData() and QScatterSeries::setModel, only the data provided by the model is
70 70 used by the series. Returns true if the model is valid for the series.
71 71 */
72 72
73 73 /*!
74 74 \fn QAbstractItemModel* QSeries::model() const
75 75 \brief Returns the pointer to the model that is used as the series data source
76 76 */
77 77
78 78 /*!
79 79 \property QSeries::name
80 80 \brief name of the series property
81 81 */
82 82
83 83 /*!
84 \fn void QSeries::setName(QString name)
84 \fn void QSeries::setName(const QString& name)
85 85 \brief Sets a \a name for the series.
86 86
87 87 The name of a series is shown in the legend for QXYSeries.
88 88 \sa QChart::setTitle()
89 89 \sa QPieSlice::setLabel()
90 90 \sa QBarSet::setName()
91 91 */
92 92
93 93 /*!
94 94 \fn QString QSeries::name() const
95 95 \brief Returns the name of the series.
96 96 \sa setName()
97 97 */
98 98
99 99 QSeries::QSeries(QObject *parent) : QObject(parent),
100 100 d_ptr(new QSeriesPrivate(this))
101 101 {
102 102
103 103 }
104 104
105 105 QSeries::QSeries(QSeriesPrivate &d,QObject *parent) : QObject(parent),
106 106 d_ptr(&d)
107 107 {
108 108
109 109 }
110 110
111 111 QSeries::~QSeries()
112 112 {
113 113
114 114 }
115 115
116 116 QAbstractItemModel* QSeries::model() const
117 117 {
118 118 return d_ptr->m_model;
119 119 }
120 120
121 121 void QSeries::setName(const QString& name)
122 122 {
123 123 d_ptr->m_name = name;
124 124 }
125 125
126 126 QString QSeries::name() const
127 127 {
128 128 return d_ptr->m_name;
129 129 }
130 130
131 131 ///////////////////////////////////////////////////////////////////////////////////////////////////
132 132
133 133 QSeriesPrivate::QSeriesPrivate(QSeries* q): q_ptr(q),m_model(0)
134 134 {
135 135
136 136 }
137 137
138 138 QSeriesPrivate::~QSeriesPrivate()
139 139 {
140 140
141 141 }
142 142
143 143
144 144
145 145
146 146 #include "moc_qseries.cpp"
147 147
148 148 QTCOMMERCIALCHART_END_NAMESPACE
149 149
150 150
@@ -1,632 +1,632
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2012 Digia Plc
4 4 ** All rights reserved.
5 5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 6 **
7 7 ** This file is part of the Qt Commercial Charts Add-on.
8 8 **
9 9 ** $QT_BEGIN_LICENSE$
10 10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 11 ** accordance with the Qt Commercial License Agreement provided with the
12 12 ** Software or, alternatively, in accordance with the terms contained in
13 13 ** a written agreement between you and Digia.
14 14 **
15 15 ** If you have questions regarding the use of this file, please use
16 16 ** contact form at http://qt.digia.com
17 17 ** $QT_END_LICENSE$
18 18 **
19 19 ****************************************************************************/
20 20
21 21 #include "qxyseries.h"
22 22 #include "qxyseries_p.h"
23 23 #include <QAbstractItemModel>
24 24
25 25 QTCOMMERCIALCHART_BEGIN_NAMESPACE
26 26
27 27 /*!
28 28 \class QXYSeries
29 29 \brief The QXYSeries class is a base class for line, spline and scatter series.
30 30 */
31 31
32 32 /*!
33 33 \fn QPen QXYSeries::pen() const
34 34 \brief Returns pen used to draw points for series.
35 35 \sa setPen()
36 36 */
37 37
38 38 /*!
39 39 \fn QBrush QXYSeries::brush() const
40 40 \brief Returns brush used to draw points for series.
41 41 \sa setBrush()
42 42 */
43 43
44 44 /*!
45 45 \fn void QXYSeries::clicked(const QPointF& point)
46 46 \brief Signal is emitted when user clicks the \a point on chart.
47 47 */
48 48
49 49 /*!
50 \fn void QXYSeries::pointReplaced(int index)
50 \fn void QXYSeriesPrivate::pointReplaced(int index)
51 51 \brief \internal \a index
52 52 */
53 53
54 54 /*!
55 \fn void QXYSeries::pointAdded(int index)
55 \fn void QXYSeriesPrivate::pointAdded(int index)
56 56 \brief \internal \a index
57 57 */
58 58
59 59 /*!
60 \fn void QXYSeries::pointRemoved(int index)
60 \fn void QXYSeriesPrivate::pointRemoved(int index)
61 61 \brief \internal \a index
62 62 */
63 63
64 64 /*!
65 \fn void QXYSeries::updated()
65 \fn void QXYSeriesPrivate::updated()
66 66 \brief \internal
67 67 */
68 68
69 69 /*!
70 70 \fn int QXYSeries::mapFirst() const
71 71 Returns the index of the model's item that is used as a first one for the series.
72 72 \sa mapCount()
73 73 */
74 74
75 75 /*!
76 76 \fn int QXYSeries::mapCount() const
77 77 Returns the number of the items that are taken from the model.
78 78 If -1 it means all the items of the model following the first one are used.
79 79 \sa mapFirst()
80 80 */
81 81
82 82 /*!
83 83 Constructs empty series object which is a child of \a parent.
84 84 When series object is added to QChartView or QChart instance ownerships is transfered.
85 85 */
86 86 QXYSeries::QXYSeries(QObject *parent):QSeries(*new QXYSeriesPrivate(this),parent)
87 87 {
88 88
89 89 }
90 90
91 91 QXYSeries::QXYSeries(QXYSeriesPrivate &d,QObject *parent):QSeries(d,parent)
92 92 {
93 93
94 94 }
95 95 /*!
96 96 Destroys the object. Series added to QChartView or QChart instances are owned by those,
97 97 and are deleted when mentioned object are destroyed.
98 98 */
99 99 QXYSeries::~QXYSeries()
100 100 {
101 101 }
102 102
103 103 /*!
104 104 Adds data point \a x \a y to the series. Points are connected with lines on the chart.
105 105 */
106 106 void QXYSeries::append(qreal x,qreal y)
107 107 {
108 108 Q_D(QXYSeries);
109 109 Q_ASSERT(d->m_x.size() == d->m_y.size());
110 110 d->m_x<<x;
111 111 d->m_y<<y;
112 112 emit d->pointAdded(d->m_x.size()-1);
113 113 }
114 114
115 115 /*!
116 116 This is an overloaded function.
117 117 Adds data \a point to the series. Points are connected with lines on the chart.
118 118 */
119 119 void QXYSeries::append(const QPointF &point)
120 120 {
121 121 append(point.x(),point.y());
122 122 }
123 123
124 124 /*!
125 125 This is an overloaded function.
126 126 Adds list of data \a points to the series. Points are connected with lines on the chart.
127 127 */
128 128 void QXYSeries::append(const QList<QPointF> points)
129 129 {
130 130 foreach(const QPointF& point , points) {
131 131 append(point.x(),point.y());
132 132 }
133 133 }
134 134
135 135 /*!
136 136 Modifies \a y value for given \a x a value.
137 137 */
138 138 void QXYSeries::replace(qreal x,qreal y)
139 139 {
140 140 Q_D(QXYSeries);
141 141 int index = d->m_x.indexOf(x);
142 142 d->m_x[index] = x;
143 143 d->m_y[index] = y;
144 144 emit d->pointReplaced(index);
145 145 }
146 146
147 147 /*!
148 148 This is an overloaded function.
149 149 Replaces current y value of for given \a point x value with \a point y value.
150 150 */
151 151 void QXYSeries::replace(const QPointF &point)
152 152 {
153 153 replace(point.x(),point.y());
154 154 }
155 155
156 156 /*!
157 157 Removes first \a x value and related y value.
158 158 */
159 159 void QXYSeries::remove(qreal x)
160 160 {
161 161 Q_D(QXYSeries);
162 162 int index = d->m_x.indexOf(x);
163 163
164 164 if (index == -1) return;
165 165
166 166 d->m_x.remove(index);
167 167 d->m_y.remove(index);
168 168
169 169 emit d->pointRemoved(index);
170 170 }
171 171
172 172 /*!
173 173 Removes current \a x and \a y value.
174 174 */
175 175 void QXYSeries::remove(qreal x,qreal y)
176 176 {
177 177 Q_D(QXYSeries);
178 178 int index =-1;
179 179 do {
180 180 index = d->m_x.indexOf(x,index+1);
181 181 } while (index !=-1 && d->m_y.at(index)!=y);
182 182
183 183 if (index==-1) return;
184 184
185 185 d->m_x.remove(index);
186 186 d->m_y.remove(index);
187 187 emit d->pointRemoved(index);
188 188 }
189 189
190 190 /*!
191 191 Removes current \a point x value. Note \a point y value is ignored.
192 192 */
193 193 void QXYSeries::remove(const QPointF &point)
194 194 {
195 195 remove(point.x(),point.y());
196 196 }
197 197
198 198 /*!
199 199 Removes all data points from the series.
200 200 */
201 201 void QXYSeries::removeAll()
202 202 {
203 203 Q_D(QXYSeries);
204 204 d->m_x.clear();
205 205 d->m_y.clear();
206 206 }
207 207
208 208 /*!
209 209 \internal \a pos
210 210 */
211 211 qreal QXYSeries::x(int pos) const
212 212 {
213 213 Q_D(const QXYSeries);
214 214 if (d->m_model) {
215 215 if (d->m_mapOrientation == Qt::Vertical)
216 216 // consecutive data is read from model's column
217 217 return d->m_model->data(d->m_model->index(pos + d->m_mapFirst, d->m_mapX), Qt::DisplayRole).toDouble();
218 218 else
219 219 // consecutive data is read from model's row
220 220 return d->m_model->data(d->m_model->index(d->m_mapX, pos + d->m_mapFirst), Qt::DisplayRole).toDouble();
221 221 } else {
222 222 // model is not specified, return the data from series' internal data store
223 223 return d->m_x.at(pos);
224 224 }
225 225 }
226 226
227 227 /*!
228 228 \internal \a pos
229 229 */
230 230 qreal QXYSeries::y(int pos) const
231 231 {
232 232 Q_D(const QXYSeries);
233 233 if (d->m_model) {
234 234 if (d->m_mapOrientation == Qt::Vertical)
235 235 // consecutive data is read from model's column
236 236 return d->m_model->data(d->m_model->index(pos + d->m_mapFirst, d->m_mapY), Qt::DisplayRole).toDouble();
237 237 else
238 238 // consecutive data is read from model's row
239 239 return d->m_model->data(d->m_model->index(d->m_mapY, pos + d->m_mapFirst), Qt::DisplayRole).toDouble();
240 240 } else {
241 241 // model is not specified, return the data from series' internal data store
242 242 return d->m_y.at(pos);
243 243 }
244 244 }
245 245
246 246 /*!
247 247 Returns number of data points within series.
248 248 */
249 249 int QXYSeries::count() const
250 250 {
251 251 Q_D(const QXYSeries);
252 252
253 253 Q_ASSERT(d->m_x.size() == d->m_y.size());
254 254
255 255 if (d->m_model) {
256 256 if (d->m_mapOrientation == Qt::Vertical) {
257 257 // data is in a column. Return the number of mapped items if the model's column have enough items
258 258 // or the number of items that can be mapped
259 259 if (d->m_mapLimited)
260 260 return qMin(d->m_mapCount, qMax(d->m_model->rowCount() - d->m_mapFirst, 0));
261 261 else
262 262 return qMax(d->m_model->rowCount() - d->m_mapFirst, 0);
263 263 } else {
264 264 // data is in a row. Return the number of mapped items if the model's row have enough items
265 265 // or the number of items that can be mapped
266 266 if (d->m_mapLimited)
267 267 return qMin(d->m_mapCount, qMax(d->m_model->columnCount() - d->m_mapFirst, 0));
268 268 else
269 269 return qMax(d->m_model->columnCount() - d->m_mapFirst, 0);
270 270 }
271 271 }
272 272
273 273 // model is not specified, return the number of points in the series internal data store
274 274 return d->m_x.size();
275 275 }
276 276
277 277 /*!
278 278 Returns the data points of the series.
279 279 */
280 280 QList<QPointF> QXYSeries::data()
281 281 {
282 282 Q_D(QXYSeries);
283 283 QList<QPointF> data;
284 284 for (int i(0); i < d->m_x.count() && i < d->m_y.count(); i++)
285 285 data.append(QPointF(d->m_x.at(i), d->m_y.at(i)));
286 286 return data;
287 287 }
288 288
289 289
290 290 /*!
291 291 Sets \a pen used for drawing points on the chart. If the pen is not defined, the
292 292 pen from chart theme is used.
293 293 \sa QChart::setTheme()
294 294 */
295 295 void QXYSeries::setPen(const QPen &pen)
296 296 {
297 297 Q_D(QXYSeries);
298 298 if (d->m_pen!=pen) {
299 299 d->m_pen = pen;
300 300 emit d->updated();
301 301 }
302 302 }
303 303
304 304 QPen QXYSeries::pen() const
305 305 {
306 306 Q_D(const QXYSeries);
307 307 return d->m_pen;
308 308 }
309 309
310 310 /*!
311 311 Sets \a brush used for drawing points on the chart. If the brush is not defined, brush
312 312 from chart theme setting is used.
313 313 \sa QChart::setTheme()
314 314 */
315 315 void QXYSeries::setBrush(const QBrush &brush)
316 316 {
317 317 Q_D(QXYSeries);
318 318 if (d->m_brush!=brush) {
319 319 d->m_brush = brush;
320 320 emit d->updated();
321 321 }
322 322 }
323 323
324 324 QBrush QXYSeries::brush() const
325 325 {
326 326 Q_D(const QXYSeries);
327 327 return d->m_brush;
328 328 }
329 329
330 330
331 331 /*!
332 332 Sets if data points are \a visible and should be drawn on line.
333 333 */
334 334 void QXYSeries::setPointsVisible(bool visible)
335 335 {
336 336 Q_D(QXYSeries);
337 337 if (d->m_pointsVisible != visible){
338 338 d->m_pointsVisible = visible;
339 339 emit d->updated();
340 340 }
341 341 }
342 342
343 343
344 344 bool QXYSeries::pointsVisible() const
345 345 {
346 346 Q_D(const QXYSeries);
347 347 return d->m_pointsVisible;
348 348 }
349 349
350 350
351 351 /*!
352 352 Stream operator for adding a data \a point to the series.
353 353 \sa append()
354 354 */
355 355 QXYSeries& QXYSeries::operator<< (const QPointF &point)
356 356 {
357 357 append(point);
358 358 return *this;
359 359 }
360 360
361 361
362 362 /*!
363 363 Stream operator for adding a list of \a points to the series.
364 364 \sa append()
365 365 */
366 366
367 367 QXYSeries& QXYSeries::operator<< (const QList<QPointF> points)
368 368 {
369 369 append(points);
370 370 return *this;
371 371 }
372 372
373 373 /*!
374 374 \internal
375 375 */
376 376 void QXYSeries::modelUpdated(QModelIndex topLeft, QModelIndex bottomRight)
377 377 {
378 378 Q_UNUSED(bottomRight)
379 379 Q_D(QXYSeries);
380 380 if (d->m_mapOrientation == Qt::Vertical) {
381 381 if (topLeft.row() >= d->m_mapFirst && (!d->m_mapLimited || topLeft.row() < d->m_mapFirst + d->m_mapCount))
382 382 emit d->pointReplaced(topLeft.row() - d->m_mapFirst);
383 383 } else {
384 384 if (topLeft.column() >= d->m_mapFirst && (!d->m_mapLimited || topLeft.column() < d->m_mapFirst + d->m_mapCount))
385 385 emit d->pointReplaced(topLeft.column() - d->m_mapFirst);
386 386 }
387 387 }
388 388
389 389 /*!
390 390 \internal
391 391 */
392 392 void QXYSeries::modelDataAboutToBeAdded(QModelIndex parent, int start, int end)
393 393 {
394 394 Q_UNUSED(parent)
395 395 // Q_UNUSED(end)
396 396 Q_D(QXYSeries);
397 397 if (d->m_mapLimited) {
398 398 if (start >= d->m_mapFirst + d->m_mapCount) {
399 399 // the added data is below mapped area
400 400 // therefore it has no relevance
401 401 return;
402 402 } else {
403 403 // the added data is in the mapped area or before it and update is needed
404 404
405 405 // check how many mapped items there is currently (before new items are added)
406 406 // if the number of items currently is equal the m_mapCount then some needs to be removed from xychartitem
407 407 // internal storage before new ones can be added
408 408
409 409 int itemsToRemove = qMin(count() - qMax(start - d->m_mapFirst, 0), end - start + 1);
410 410 if (d->m_mapCount == count()) {
411 411 for (int i = 0; i < itemsToRemove; i++)
412 412 emit d->pointRemoved(qMin(end, count()) - i);
413 413 }
414 414 }
415 415 } else {
416 416 // map is not limited (it includes all the items starting from m_mapFirst till the end of model)
417 417 // nothing to do
418 418 // emit pointAdded(qMax(start - m_mapFirst, 0));
419 419 }
420 420 }
421 421
422 422 /*!
423 423 \internal
424 424 */
425 425 void QXYSeries::modelDataAdded(QModelIndex parent, int start, int end)
426 426 {
427 427 Q_UNUSED(parent)
428 428 // Q_UNUSED(end)
429 429 Q_D(QXYSeries);
430 430 if (d->m_mapLimited) {
431 431 if (start >= d->m_mapFirst + d->m_mapCount) {
432 432 // the added data is below mapped area
433 433 // therefore it has no relevance
434 434 return;
435 435 } else {
436 436 // the added data is in the mapped area or before it
437 437 // update needed
438 438 if (count() > 0) {
439 439 int toBeAdded = qMin(d->m_mapCount - (start - d->m_mapFirst), end - start + 1);
440 440 for (int i = 0; i < toBeAdded; i++)
441 441 if (start + i >= d->m_mapFirst)
442 442 emit d->pointAdded(start + i);
443 443 }
444 444 }
445 445 } else {
446 446 // map is not limited (it included all the items starting from m_mapFirst till the end of model)
447 447 for (int i = 0; i < end - start + 1; i++)
448 448 emit d->pointAdded(start + i);
449 449 }
450 450 }
451 451
452 452 /*!
453 453 \internal
454 454 */
455 455 void QXYSeries::modelDataAboutToBeRemoved(QModelIndex parent, int start, int end)
456 456 {
457 457 Q_UNUSED(parent)
458 458 // Q_UNUSED(end)
459 459 Q_D(QXYSeries);
460 460 if (d->m_mapLimited) {
461 461 if (start >= d->m_mapFirst + d->m_mapCount) {
462 462 // the removed data is below mapped area
463 463 // therefore it has no relevance
464 464 return;
465 465 } else {
466 466 // the removed data is in the mapped area or before it
467 467 // update needed
468 468
469 469 // check how many items need to be removed from the xychartitem storage
470 470 // the number equals the number of items that are removed and that lay before
471 471 // or in the mapped area. Items that lay beyond the map do not count
472 472 // the max is the current number of items in storage (count())
473 473 int itemsToRemove = qMin(count(), qMin(end, d->m_mapFirst + d->m_mapCount - 1) - start + 1);
474 474 for (int i = 0; i < itemsToRemove; i++)
475 475 emit d->pointRemoved(start);
476 476 }
477 477 } else {
478 478 // map is not limited (it included all the items starting from m_mapFirst till the end of model)
479 479 for (int i = 0; i < end - start + 1; i++)
480 480 emit d->pointRemoved(start);
481 481 }
482 482 }
483 483
484 484 /*!
485 485 \internal
486 486 */
487 487 void QXYSeries::modelDataRemoved(QModelIndex parent, int start, int end)
488 488 {
489 489
490 490 Q_UNUSED(parent)
491 491 Q_UNUSED(end)
492 492 Q_D(QXYSeries);
493 493 // how many items there were before data was removed
494 494 // int oldCount = count() - 1;
495 495
496 496 if (d->m_mapLimited) {
497 497 if (start >= d->m_mapFirst + d->m_mapCount) {
498 498 // the removed data is below mapped area
499 499 // therefore it has no relevance
500 500 return;
501 501 } else {
502 502 // if the current items count in the whole model is bigger than the index of the last item
503 503 // that was removed than it means there are some extra items available
504 504
505 505 int removedItemsCount = qMin(count(), qMin(end, d->m_mapFirst + d->m_mapCount - 1) - start + 1);
506 506 int extraItemsAvailable = 0;
507 507 if (d->m_mapOrientation == Qt::Vertical) {
508 508 extraItemsAvailable = qMax(d->m_model->rowCount() + (end - start + 1) - qMax(end + 1, d->m_mapFirst + d->m_mapCount), 0);
509 509 } else {
510 510 extraItemsAvailable = qMax(d->m_model->columnCount() + (end - start + 1) - qMax(end + 1, d->m_mapFirst + d->m_mapCount), 0);
511 511 }
512 512
513 513 // if there are excess items available (below the mapped area) use them to repopulate mapped area
514 514 int toBeAdded = qMin(extraItemsAvailable, removedItemsCount);
515 515 for (int k = 0; k < toBeAdded; k++)
516 516 emit d->pointAdded(d->m_mapFirst + d->m_mapCount - removedItemsCount + k);
517 517 }
518 518 } else {
519 519 // data was removed from XYSeries interal storage. Nothing more to do
520 520 }
521 521 }
522 522
523 523 /*!
524 524 \fn bool QXYSeries::setModel(QAbstractItemModel *model)
525 525 Sets the \a model to be used as a data source
526 526 \sa setModelMapping(), setModelMappingRange()
527 527 */
528 528 bool QXYSeries::setModel(QAbstractItemModel *model)
529 529 {
530 530 Q_D(QXYSeries);
531 531 // disconnect signals from old model
532 532 if (d->m_model) {
533 533 QObject::disconnect(d->m_model, 0, this, 0);
534 534 d->m_mapX = -1;
535 535 d->m_mapY = -1;
536 536 d->m_mapFirst = 0;
537 537 d->m_mapCount = 0;
538 538 d->m_mapLimited = false;
539 539 d->m_mapOrientation = Qt::Vertical;
540 540 }
541 541
542 542 // set new model
543 543 if (model) {
544 544 d->m_model = model;
545 545 return true;
546 546 } else {
547 547 d->m_model = 0;
548 548 return false;
549 549 }
550 550 }
551 551
552 552 /*!
553 553 \fn bool QXYSeries::setModelMapping(int modelX, int modelY, Qt::Orientation orientation)
554 554 Sets the \a modelX to be used as a data source for x coordinate and \a modelY to be used
555 555 as a data source for y coordinate. The \a orientation paramater specifies whether the data
556 556 is in columns or in rows.
557 557 \sa setModel(), setModelMappingRange()
558 558 */
559 559 void QXYSeries::setModelMapping(int modelX, int modelY, Qt::Orientation orientation)
560 560 {
561 561 Q_D(QXYSeries);
562 562 if (d->m_model == 0)
563 563 return;
564 564 d->m_mapX = modelX;
565 565 d->m_mapY = modelY;
566 566 d->m_mapFirst = 0;
567 567 d->m_mapOrientation = orientation;
568 568 if (d->m_mapOrientation == Qt::Vertical) {
569 569 connect(d->m_model,SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(modelUpdated(QModelIndex, QModelIndex)));
570 570 connect(d->m_model,SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)), this, SLOT(modelDataAboutToBeAdded(QModelIndex,int,int)));
571 571 connect(d->m_model,SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(modelDataAdded(QModelIndex,int,int)));
572 572 connect(d->m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), this, SLOT(modelDataAboutToBeRemoved(QModelIndex,int,int)));
573 573 connect(d->m_model, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(modelDataRemoved(QModelIndex,int,int)));
574 574 } else {
575 575 connect(d->m_model,SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(modelUpdated(QModelIndex, QModelIndex)));
576 576 connect(d->m_model,SIGNAL(columnsAboutToBeInserted(QModelIndex, int, int)), this, SLOT(modelDataAboutToBeAdded(QModelIndex,int,int)));
577 577 connect(d->m_model,SIGNAL(columnsInserted(QModelIndex, int, int)), this, SLOT(modelDataAdded(QModelIndex,int,int)));
578 578 connect(d->m_model, SIGNAL(columnsAboutToBeRemoved(QModelIndex, int, int)), this, SLOT(modelDataAboutToBeRemoved(QModelIndex,int,int)));
579 579 connect(d->m_model, SIGNAL(columnsRemoved(QModelIndex, int, int)), this, SLOT(modelDataRemoved(QModelIndex,int,int)));
580 580 }
581 581 }
582 582
583 583 /*!
584 584 \fn bool QXYSeries::setModelMappingRange(int first, int count)
585 585 Allows limiting the model mapping.
586 586 Parameter \a first specifies which element of the model should be used as a first one of the series.
587 587 Parameter \a count specifies how many elements should be mapped. If count is not specified (defaults to -1)
588 588 then all the items following \a first item in a model are used.
589 589 \sa setModel(), setModelMapping()
590 590 */
591 591 void QXYSeries::setModelMappingRange(int first, int count)
592 592 {
593 593 Q_D(QXYSeries);
594 594 d->m_mapFirst = first;
595 595 if (count == 0) {
596 596 d->m_mapLimited = false;
597 597 } else {
598 598 d->m_mapCount = count;
599 599 d->m_mapLimited = true;
600 600 }
601 601 }
602 602
603 603 int QXYSeries::mapFirst() const
604 604 {
605 605 Q_D(const QXYSeries);
606 606 return d->m_mapFirst;
607 607 }
608 608
609 609 int QXYSeries::mapCount() const
610 610 {
611 611 Q_D(const QXYSeries);
612 612 return d->m_mapCount;
613 613 }
614 614
615 615 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
616 616
617 617 QXYSeriesPrivate::QXYSeriesPrivate(QXYSeries *q): QSeriesPrivate(q),
618 618 m_mapX(-1),
619 619 m_mapY(-1),
620 620 m_mapFirst(0),
621 621 m_mapCount(0),
622 622 m_mapLimited(false),
623 623 m_mapOrientation( Qt::Vertical),
624 624 m_pointsVisible(false)
625 625 {
626 626
627 627 }
628 628
629 629 #include "moc_qxyseries.cpp"
630 630 #include "moc_qxyseries_p.cpp"
631 631
632 632 QTCOMMERCIALCHART_END_NAMESPACE
1 NO CONTENT: file renamed from demos/gdpbarchart/gdpData to test/gdpbarchart/gdpData
@@ -1,14 +1,16
1 !include( ../demos.pri ):error( "Couldn't find the demos.pri file!" )
1 !include( ../test.pri ) {
2 error( "Couldn't find the test.pri file!" )
3 }
2 4
3 5 QT+= sql
4 6 TARGET = gdpbarchart
5 7 SOURCES += main.cpp\
6 8 widget.cpp
7 9 HEADERS += widget.h
8 10
9 11 !mac {
10 12 FILE = $$PWD/gdpData
11 13 win32:{FILE = $$replace(FILE, "/","\\")}
12 14 # TODO: QMAKE_POST_LINK does not work on mac; how to copy the data file?
13 15 QMAKE_POST_LINK += $$QMAKE_COPY $$FILE $$CHART_BUILD_BIN_DIR
14 16 }
1 NO CONTENT: file renamed from demos/gdpbarchart/main.cpp to test/gdpbarchart/main.cpp
1 NO CONTENT: file renamed from demos/gdpbarchart/widget.cpp to test/gdpbarchart/widget.cpp
1 NO CONTENT: file renamed from demos/gdpbarchart/widget.h to test/gdpbarchart/widget.h
@@ -1,8 +1,9
1 1 TEMPLATE = subdirs
2 2 SUBDIRS += \
3 3 chartwidgettest \
4 wavechart
4 wavechart \
5 gdpbarchart
5 6
6 7 !win32:{
7 8 SUBDIRS += auto
8 9 }
General Comments 0
You need to be logged in to leave comments. Login now