##// END OF EJS Templates
Fixes legend markers updates
Michal Klocek -
r2085:224fca6f4967
parent child
Show More
@@ -1,867 +1,859
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 "qabstractbarseries.h"
22 22 #include "qabstractbarseries_p.h"
23 23 #include "qbarset.h"
24 24 #include "qbarset_p.h"
25 25 #include "domain_p.h"
26 26 #include "legendmarker_p.h"
27 27 #include "chartdataset_p.h"
28 28 #include "charttheme_p.h"
29 29 #include "qvalueaxis.h"
30 30 #include "qbarcategoryaxis.h"
31 31
32 32 QTCOMMERCIALCHART_BEGIN_NAMESPACE
33 33
34 34 /*!
35 35 \class QAbstractBarSeries
36 36 \brief Series for creating a bar chart
37 37 \mainclass
38 38
39 39 QAbstractBarSeries represents a series of data shown as bars. The purpose of this class is to draw bars to
40 40 the position defined by data. Single bar is defined by QPointF, where x value is the x-coordinate of the bar
41 41 and y-value is the height of the bar. The category names are ignored with this series and x-axis
42 42 shows the x-values.
43 43
44 44 See the \l {BarChart Example} {bar chart example} to learn how to create a simple bar chart.
45 45 \image examples_barchart.png
46 46
47 47 \sa QBarSet, QStackedBarSeries, QPercentBarSeries
48 48 */
49 49 /*!
50 50 \qmlclass AbstractBarSeries QAbstractBarSeries
51 51 \inherits QAbstractSeries
52 52
53 53 The following QML shows how to create a simple bar chart:
54 54 \snippet ../demos/qmlchart/qml/qmlchart/View6.qml 1
55 55
56 56 \beginfloatleft
57 57 \image demos_qmlchart6.png
58 58 \endfloat
59 59 \clearfloat
60 60 */
61 61
62 62 /*!
63 63 \property QAbstractBarSeries::barWidth
64 64 The width of the bars of the series. The unit of \a width is the unit of x-axis. The minimum width for bars
65 65 is zero and negative values are treated as zero. Setting the width to zero means that width of the bar on screen
66 66 is one pixel no matter what the scale of x-axis is. Bars wider than zero are scaled with x-axis.
67 67 Note that with QBarSeries this value means the width of one group of bars instead of just one bar.
68 68 \sa QBarSeries
69 69 */
70 70 /*!
71 71 \qmlproperty real AbstractBarSeries::barWidth
72 72 The width of the bars of the series. The unit of width is the unit of x-axis. The minimum width for bars
73 73 is zero and negative values are treated as zero. Setting the width to zero means that width of the bar on screen
74 74 is one pixel no matter what the scale of x-axis is. Bars wider than zero are scaled with x-axis.
75 75 Note that with QBarSeries this value means the width of one group of bars instead of just one bar.
76 76 */
77 77
78 78 /*!
79 79 \property QAbstractBarSeries::count
80 80 Holds the number of sets in series.
81 81 */
82 82 /*!
83 83 \qmlproperty int AbstractBarSeries::count
84 84 Holds the number of sets in series.
85 85 */
86 86
87 87 /*!
88 88 \property QAbstractBarSeries::labelsVisible
89 89 Defines the visibility of the labels in series
90 90 */
91 91 /*!
92 92 \qmlproperty bool AbstractBarSeries::labelsVisible
93 93 Defines the visibility of the labels in series
94 94 */
95 95
96 96 /*!
97 97 \fn void QAbstractBarSeries::clicked(int index, QBarSet *barset)
98 98 The signal is emitted if the user clicks with a mouse on top of QBarSet \a barset.
99 99 Clicked bar inside set is indexed by \a index
100 100 */
101 101 /*!
102 102 \qmlsignal AbstractBarSeries::onClicked(int index, BarSet barset)
103 103 The signal is emitted if the user clicks with a mouse on top of BarSet.
104 104 Clicked bar inside set is indexed by \a index
105 105 */
106 106
107 107 /*!
108 108 \fn void QAbstractBarSeries::hovered(bool status, QBarSet* barset)
109 109
110 110 The signal is emitted if mouse is hovered on top of series.
111 111 Parameter \a barset is the pointer of barset, where hover happened.
112 112 Parameter \a status is true, if mouse entered on top of series, false if mouse left from top of series.
113 113 */
114 114 /*!
115 115 \qmlsignal AbstractBarSeries::onHovered(bool status, BarSet barset)
116 116
117 117 The signal is emitted if mouse is hovered on top of series.
118 118 Parameter \a barset is the pointer of barset, where hover happened.
119 119 Parameter \a status is true, if mouse entered on top of series, false if mouse left from top of series.
120 120 */
121 121
122 122 /*!
123 123 \fn void QAbstractBarSeries::countChanged()
124 124 This signal is emitted when barset count has been changed, for example by append or remove.
125 125 */
126 126 /*!
127 127 \qmlsignal AbstractBarSeries::onCountChanged()
128 128 This signal is emitted when barset count has been changed, for example by append or remove.
129 129 */
130 130
131 131 /*!
132 132 \fn void QAbstractBarSeries::labelsVisibleChanged()
133 133 This signal is emitted when labels visibility have changed.
134 134 \sa isLabelsVisible(), setLabelsVisible()
135 135 */
136 136
137 137 /*!
138 138 \fn void QAbstractBarSeries::barsetsAdded(QList<QBarSet*> sets)
139 139 This signal is emitted when \a sets have been added to the series.
140 140 \sa append(), insert()
141 141 */
142 142 /*!
143 143 \qmlsignal AbstractBarSeries::onAdded(BarSet barset)
144 144 Emitted when \a barset has been added to the series.
145 145 */
146 146
147 147 /*!
148 148 \fn void QAbstractBarSeries::barsetsRemoved(QList<QBarSet*> sets)
149 149 This signal is emitted when \a sets have been removed from the series.
150 150 \sa remove()
151 151 */
152 152 /*!
153 153 \qmlsignal AbstractBarSeries::onRemoved(BarSet barset)
154 154 Emitted when \a barset has been removed from the series.
155 155 */
156 156
157 157 /*!
158 158 \qmlmethod BarSet AbstractBarSeries::at(int index)
159 159 Returns bar set at \a index. Returns null if the index is not valid.
160 160 */
161 161
162 162 /*!
163 163 \qmlmethod BarSet AbstractBarSeries::append(string label, VariantList values)
164 164 Adds a new bar set with \a label and \a values to \a index. Values can be a list of reals or a list of XYPoints.
165 165 For example:
166 166 \code
167 167 myBarSeries.append("set 1", [0, 0.2, 0.2, 0.5, 0.4, 1.5, 0.9]);
168 168 myBarSeries.append("set 2", [Qt.point(0, 1), Qt.point(2, 2.5), Qt.point(3.5, 2.2)]);
169 169 \endcode
170 170 */
171 171
172 172 /*!
173 173 \qmlmethod BarSet AbstractBarSeries::insert(int index, string label, VariantList values)
174 174 Inserts a new bar set with \a label and \a values to \a index. Values can be a list of reals or a list of XYPoints.
175 175 If index is zero or smaller, the new barset is prepended. If the index is count or bigger, the new barset is
176 176 appended.
177 177 \sa AbstractBarSeries::append()
178 178 */
179 179
180 180 /*!
181 181 \qmlmethod bool AbstractBarSeries::remove(BarSet barset)
182 182 Removes the barset from the series. Returns true if successful, false otherwise.
183 183 */
184 184
185 185 /*!
186 186 \qmlmethod AbstractBarSeries::clear()
187 187 Removes all barsets from the series.
188 188 */
189 189
190 190 /*!
191 191 Destructs abstractbarseries and owned barsets.
192 192 */
193 193 QAbstractBarSeries::~QAbstractBarSeries()
194 194 {
195 195
196 196 }
197 197
198 198 /*!
199 199 \internal
200 200 */
201 QAbstractBarSeries::QAbstractBarSeries(QAbstractBarSeriesPrivate &d, QObject *parent) :
202 QAbstractSeries(d,parent)
201 QAbstractBarSeries::QAbstractBarSeries(QAbstractBarSeriesPrivate &o, QObject *parent) :
202 QAbstractSeries(o,parent)
203 203 {
204 Q_D(QAbstractSeries);
205 QObject::connect(this,SIGNAL(countChanged()),d,SIGNAL(countChanged()));
204 206 }
205 207
206 208 /*!
207 209 Sets the width of the bars of the series. The unit of \a width is the unit of x-axis. The minimum width for bars
208 210 is zero and negative values are treated as zero. Setting the width to zero means that width of the bar on screen
209 211 is one pixel no matter what the scale of x-axis is. Bars wider than zero are scaled with x-axis.
210 212 Note that with \link QBarSeries \endlink this value means the width of one group of bars instead of just one bar.
211 213 */
212 214 void QAbstractBarSeries::setBarWidth(qreal width)
213 215 {
214 216 Q_D(QAbstractBarSeries);
215 217 d->setBarWidth(width);
216 218 }
217 219
218 220 /*!
219 221 Returns the width of the bars of the series.
220 222 \sa setBarWidth()
221 223 */
222 224 qreal QAbstractBarSeries::barWidth() const
223 225 {
224 226 Q_D(const QAbstractBarSeries);
225 227 return d->barWidth();
226 228 }
227 229
228 230 /*!
229 231 Adds a set of bars to series. Takes ownership of \a set. If the set is null or is already in series, it won't be appended.
230 232 Returns true, if appending succeeded.
231 233 */
232 234 bool QAbstractBarSeries::append(QBarSet *set)
233 235 {
234 236 Q_D(QAbstractBarSeries);
235 237 bool success = d->append(set);
236 238 if (success) {
237 239 QList<QBarSet*> sets;
238 240 sets.append(set);
239 241 set->setParent(this);
240 242 emit barsetsAdded(sets);
241 243 emit countChanged();
242 244 }
243 245 return success;
244 246 }
245 247
246 248 /*!
247 249 Removes barset from series. Releases ownership of \a set. Deletes the set, if remove
248 250 was successful.
249 251 Returns true, if set was removed.
250 252 */
251 253 bool QAbstractBarSeries::remove(QBarSet *set)
252 254 {
253 255 Q_D(QAbstractBarSeries);
254 256 bool success = d->remove(set);
255 257 if (success) {
256 258 QList<QBarSet*> sets;
257 259 sets.append(set);
258 260 set->setParent(0);
259 261 emit barsetsRemoved(sets);
260 262 emit countChanged();
261 263 delete set;
262 264 set = 0;
263 265 }
264 266 return success;
265 267 }
266 268
267 269 /*!
268 270 Takes a single \a set from the series. Does not delete the barset object.
269 271
270 272 NOTE: The series remains as the barset's parent object. You must set the
271 273 parent object to take full ownership.
272 274
273 275 Returns true if take was successful.
274 276 */
275 277 bool QAbstractBarSeries::take(QBarSet *set)
276 278 {
277 279 Q_D(QAbstractBarSeries);
278 280 bool success = d->remove(set);
279 281 if (success) {
280 282 QList<QBarSet*> sets;
281 283 sets.append(set);
282 284 emit barsetsRemoved(sets);
283 285 emit countChanged();
284 286 }
285 287 return success;
286 288 }
287 289
288 290 /*!
289 291 Adds a list of barsets to series. Takes ownership of \a sets.
290 292 Returns true, if all sets were appended successfully. If any of the sets is null or is already appended to series,
291 293 nothing is appended and function returns false. If any of the sets is in list more than once, nothing is appended
292 294 and function returns false.
293 295 */
294 296 bool QAbstractBarSeries::append(QList<QBarSet* > sets)
295 297 {
296 298 Q_D(QAbstractBarSeries);
297 299 bool success = d->append(sets);
298 300 if (success) {
299 301 emit barsetsAdded(sets);
300 302 emit countChanged();
301 303 }
302 304 return success;
303 305 }
304 306
305 307 /*!
306 308 Insert a set of bars to series at \a index postion. Takes ownership of \a set. If the set is null or is already in series, it won't be appended.
307 309 Returns true, if inserting succeeded.
308 310
309 311 */
310 312 bool QAbstractBarSeries::insert(int index, QBarSet *set)
311 313 {
312 314 Q_D(QAbstractBarSeries);
313 315 bool success = d->insert(index, set);
314 316 if (success) {
315 317 QList<QBarSet*> sets;
316 318 sets.append(set);
317 319 emit barsetsAdded(sets);
318 320 emit countChanged();
319 321 }
320 322 return success;
321 323 }
322 324
323 325 /*!
324 326 Removes all barsets from the series. Deletes removed sets.
325 327 */
326 328 void QAbstractBarSeries::clear()
327 329 {
328 330 Q_D(QAbstractBarSeries);
329 331 QList<QBarSet *> sets = barSets();
330 332 bool success = d->remove(sets);
331 333 if (success) {
332 334 emit barsetsRemoved(sets);
333 335 emit countChanged();
334 336 foreach (QBarSet* set, sets) {
335 337 delete set;
336 338 }
337 339 }
338 340 }
339 341
340 342 /*!
341 343 Returns number of sets in series.
342 344 */
343 345 int QAbstractBarSeries::count() const
344 346 {
345 347 Q_D(const QAbstractBarSeries);
346 348 return d->m_barSets.count();
347 349 }
348 350
349 351 /*!
350 352 Returns a list of sets in series. Keeps ownership of sets.
351 353 */
352 354 QList<QBarSet*> QAbstractBarSeries::barSets() const
353 355 {
354 356 Q_D(const QAbstractBarSeries);
355 357 return d->m_barSets;
356 358 }
357 359
358 360 /*!
359 361 Sets the visibility of labels in series to \a visible
360 362 */
361 363 void QAbstractBarSeries::setLabelsVisible(bool visible)
362 364 {
363 365 Q_D(QAbstractBarSeries);
364 366 if (d->m_labelsVisible != visible) {
365 367 d->setLabelsVisible(visible);
366 368 emit labelsVisibleChanged();
367 369 }
368 370 }
369 371
370 372 /*!
371 373 Returns the visibility of labels
372 374 */
373 375 bool QAbstractBarSeries::isLabelsVisible() const
374 376 {
375 377 Q_D(const QAbstractBarSeries);
376 378 return d->m_labelsVisible;
377 379 }
378 380
379 381 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
380 382
381 383 QAbstractBarSeriesPrivate::QAbstractBarSeriesPrivate(QAbstractBarSeries *q) :
382 384 QAbstractSeriesPrivate(q),
383 385 m_barWidth(0.5), // Default value is 50% of category width
384 386 m_labelsVisible(false),
385 387 m_visible(true)
386 388 {
387 389 }
388 390
389 391 int QAbstractBarSeriesPrivate::categoryCount() const
390 392 {
391 393 // No categories defined. return count of longest set.
392 394 int count = 0;
393 395 for (int i=0; i<m_barSets.count(); i++) {
394 396 if (m_barSets.at(i)->count() > count) {
395 397 count = m_barSets.at(i)->count();
396 398 }
397 399 }
398 400
399 401 return count;
400 402 }
401 403
402 404 void QAbstractBarSeriesPrivate::setBarWidth(qreal width)
403 405 {
404 406 if (width < 0.0) {
405 407 width = 0.0;
406 408 }
407 409 m_barWidth = width;
408 410 emit updatedLayout();
409 411 }
410 412
411 413 qreal QAbstractBarSeriesPrivate::barWidth() const
412 414 {
413 415 return m_barWidth;
414 416 }
415 417
416 418 QBarSet* QAbstractBarSeriesPrivate::barsetAt(int index)
417 419 {
418 420 return m_barSets.at(index);
419 421 }
420 422
421 423 void QAbstractBarSeriesPrivate::setVisible(bool visible)
422 424 {
423 425 m_visible = visible;
424 426 emit visibleChanged();
425 427 }
426 428
427 429 void QAbstractBarSeriesPrivate::setLabelsVisible(bool visible)
428 430 {
429 431 m_labelsVisible = visible;
430 432 emit labelsVisibleChanged(visible);
431 433 }
432 434
433 435 qreal QAbstractBarSeriesPrivate::min()
434 436 {
435 437 if (m_barSets.count() <= 0) {
436 438 return 0;
437 439 }
438 440 qreal min = INT_MAX;
439 441
440 442 for (int i = 0; i < m_barSets.count(); i++) {
441 443 int categoryCount = m_barSets.at(i)->count();
442 444 for (int j = 0; j < categoryCount; j++) {
443 445 qreal temp = m_barSets.at(i)->at(j);
444 446 if (temp < min)
445 447 min = temp;
446 448 }
447 449 }
448 450 return min;
449 451 }
450 452
451 453 qreal QAbstractBarSeriesPrivate::max()
452 454 {
453 455 if (m_barSets.count() <= 0) {
454 456 return 0;
455 457 }
456 458 qreal max = INT_MIN;
457 459
458 460 for (int i = 0; i < m_barSets.count(); i++) {
459 461 int categoryCount = m_barSets.at(i)->count();
460 462 for (int j = 0; j < categoryCount; j++) {
461 463 qreal temp = m_barSets.at(i)->at(j);
462 464 if (temp > max)
463 465 max = temp;
464 466 }
465 467 }
466 468
467 469 return max;
468 470 }
469 471
470 472 qreal QAbstractBarSeriesPrivate::valueAt(int set, int category)
471 473 {
472 474 if ((set < 0) || (set >= m_barSets.count())) {
473 475 // No set, no value.
474 476 return 0;
475 477 } else if ((category < 0) || (category >= m_barSets.at(set)->count())) {
476 478 // No category, no value.
477 479 return 0;
478 480 }
479 481
480 482 return m_barSets.at(set)->at(category);
481 483 }
482 484
483 485 qreal QAbstractBarSeriesPrivate::percentageAt(int set, int category)
484 486 {
485 487 if ((set < 0) || (set >= m_barSets.count())) {
486 488 // No set, no value.
487 489 return 0;
488 490 } else if ((category < 0) || (category >= m_barSets.at(set)->count())) {
489 491 // No category, no value.
490 492 return 0;
491 493 }
492 494
493 495 qreal value = m_barSets.at(set)->at(category);
494 496 qreal sum = categorySum(category);
495 497 if ( qFuzzyIsNull(sum) ) {
496 498 return 0;
497 499 }
498 500
499 501 return value / sum;
500 502 }
501 503
502 504 qreal QAbstractBarSeriesPrivate::categorySum(int category)
503 505 {
504 506 qreal sum(0);
505 507 int count = m_barSets.count(); // Count sets
506 508 for (int set = 0; set < count; set++) {
507 509 if (category < m_barSets.at(set)->count())
508 510 sum += m_barSets.at(set)->at(category);
509 511 }
510 512 return sum;
511 513 }
512 514
513 515 qreal QAbstractBarSeriesPrivate::absoluteCategorySum(int category)
514 516 {
515 517 qreal sum(0);
516 518 int count = m_barSets.count(); // Count sets
517 519 for (int set = 0; set < count; set++) {
518 520 if (category < m_barSets.at(set)->count())
519 521 sum += qAbs(m_barSets.at(set)->at(category));
520 522 }
521 523 return sum;
522 524 }
523 525
524 526 qreal QAbstractBarSeriesPrivate::maxCategorySum()
525 527 {
526 528 qreal max = INT_MIN;
527 529 int count = categoryCount();
528 530 for (int i = 0; i < count; i++) {
529 531 qreal sum = categorySum(i);
530 532 if (sum > max)
531 533 max = sum;
532 534 }
533 535 return max;
534 536 }
535 537
536 538 qreal QAbstractBarSeriesPrivate::minX()
537 539 {
538 540 if (m_barSets.count() <= 0) {
539 541 return 0;
540 542 }
541 543 qreal min = INT_MAX;
542 544
543 545 for (int i = 0; i < m_barSets.count(); i++) {
544 546 int categoryCount = m_barSets.at(i)->count();
545 547 for (int j = 0; j < categoryCount; j++) {
546 548 qreal temp = m_barSets.at(i)->d_ptr.data()->m_values.at(j).x();
547 549 if (temp < min)
548 550 min = temp;
549 551 }
550 552 }
551 553 return min;
552 554 }
553 555
554 556 qreal QAbstractBarSeriesPrivate::maxX()
555 557 {
556 558 if (m_barSets.count() <= 0) {
557 559 return 0;
558 560 }
559 561 qreal max = INT_MIN;
560 562
561 563 for (int i = 0; i < m_barSets.count(); i++) {
562 564 int categoryCount = m_barSets.at(i)->count();
563 565 for (int j = 0; j < categoryCount; j++) {
564 566 qreal temp = m_barSets.at(i)->d_ptr.data()->m_values.at(j).x();
565 567 if (temp > max)
566 568 max = temp;
567 569 }
568 570 }
569 571
570 572 return max;
571 573 }
572 574
573 575 qreal QAbstractBarSeriesPrivate::categoryTop(int category)
574 576 {
575 577 // Returns top (sum of all positive values) of category.
576 578 // Returns 0, if all values are negative
577 579 qreal top(0);
578 580 int count = m_barSets.count();
579 581 for (int set = 0; set < count; set++) {
580 582 if (category < m_barSets.at(set)->count()) {
581 583 qreal temp = m_barSets.at(set)->at(category);
582 584 if (temp > 0) {
583 585 top += temp;
584 586 }
585 587 }
586 588 }
587 589 return top;
588 590 }
589 591
590 592 qreal QAbstractBarSeriesPrivate::categoryBottom(int category)
591 593 {
592 594 // Returns bottom (sum of all negative values) of category
593 595 // Returns 0, if all values are positive
594 596 qreal bottom(0);
595 597 int count = m_barSets.count();
596 598 for (int set = 0; set < count; set++) {
597 599 if (category < m_barSets.at(set)->count()) {
598 600 qreal temp = m_barSets.at(set)->at(category);
599 601 if (temp < 0) {
600 602 bottom += temp;
601 603 }
602 604 }
603 605 }
604 606 return bottom;
605 607 }
606 608
607 609 qreal QAbstractBarSeriesPrivate::top()
608 610 {
609 611 // Returns top of all categories
610 612 qreal top(0);
611 613 int count = categoryCount();
612 614 for (int i=0; i<count; i++) {
613 615 qreal temp = categoryTop(i);
614 616 if (temp > top) {
615 617 top = temp;
616 618 }
617 619 }
618 620 return top;
619 621 }
620 622
621 623 qreal QAbstractBarSeriesPrivate::bottom()
622 624 {
623 625 // Returns bottom of all categories
624 626 qreal bottom(0);
625 627 int count = categoryCount();
626 628 for (int i=0; i<count; i++) {
627 629 qreal temp = categoryBottom(i);
628 630 if (temp < bottom) {
629 631 bottom = temp;
630 632 }
631 633 }
632 634 return bottom;
633 635 }
634 636
635 637
636 638 void QAbstractBarSeriesPrivate::scaleDomain(Domain& domain)
637 639 {
638 640 qreal minX(domain.minX());
639 641 qreal minY(domain.minY());
640 642 qreal maxX(domain.maxX());
641 643 qreal maxY(domain.maxY());
642 644
643 645 qreal seriesMinX = this->minX();
644 646 qreal seriesMaxX = this->maxX();
645 647 qreal y = max();
646 648 minX = qMin(minX, seriesMinX - (qreal)0.5);
647 649 minY = qMin(minY, y);
648 650 maxX = qMax(maxX, seriesMaxX + (qreal)0.5);
649 651 maxY = qMax(maxY, y);
650 652
651 653 domain.setRange(minX,maxX,minY,maxY);
652 654 }
653 655
654 656 ChartElement* QAbstractBarSeriesPrivate::createGraphics(ChartPresenter* presenter)
655 657 {
656 658 Q_UNUSED(presenter);
657 659 qWarning() << "QAbstractBarSeriesPrivate::createGraphics called";
658 660 return 0;
659 661 }
660 662
661 663 QList<LegendMarker*> QAbstractBarSeriesPrivate::createLegendMarker(QLegend* legend)
662 664 {
663 665 Q_Q(QAbstractBarSeries);
664 666 QList<LegendMarker*> markers;
665 667 foreach(QBarSet* set, q->barSets()) {
666 668 BarLegendMarker* marker = new BarLegendMarker(q,set,legend);
667 669 markers << marker;
668 670 }
669 671
670 672 return markers;
671 673 }
672 674
673 675 bool QAbstractBarSeriesPrivate::append(QBarSet *set)
674 676 {
675 Q_Q(QAbstractBarSeries);
676 677 if ((m_barSets.contains(set)) || (set == 0)) {
677 678 // Fail if set is already in list or set is null.
678 679 return false;
679 680 }
680 681
681 682 m_barSets.append(set);
682 683 QObject::connect(set->d_ptr.data(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout()));
683 684 QObject::connect(set->d_ptr.data(), SIGNAL(updatedBars()), this, SIGNAL(updatedBars()));
684 685 QObject::connect(set->d_ptr.data(), SIGNAL(restructuredBars()), this, SIGNAL(restructuredBars()));
685 686
686 687 emit restructuredBars(); // this notifies barchartitem
687 emit legendPropertiesUpdated(q); // this notifies legend
688 688 return true;
689 689 }
690 690
691 691 bool QAbstractBarSeriesPrivate::remove(QBarSet *set)
692 692 {
693 Q_Q(QAbstractBarSeries);
694 693 if (!m_barSets.contains(set)) {
695 694 // Fail if set is not in list
696 695 return false;
697 696 }
698 697
699 698 m_barSets.removeOne(set);
700 699 QObject::disconnect(set->d_ptr.data(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout()));
701 700 QObject::disconnect(set->d_ptr.data(), SIGNAL(updatedBars()), this, SIGNAL(updatedBars()));
702 701 QObject::disconnect(set->d_ptr.data(), SIGNAL(restructuredBars()), this, SIGNAL(restructuredBars()));
703 702
704 703 emit restructuredBars(); // this notifies barchartitem
705 emit legendPropertiesUpdated(q);
706 704 return true;
707 705 }
708 706
709 707 bool QAbstractBarSeriesPrivate::append(QList<QBarSet* > sets)
710 708 {
711 Q_Q(QAbstractBarSeries);
712 709 foreach (QBarSet* set, sets) {
713 710 if ((set == 0) || (m_barSets.contains(set))) {
714 711 // Fail if any of the sets is null or is already appended.
715 712 return false;
716 713 }
717 714 if (sets.count(set) != 1) {
718 715 // Also fail if same set is more than once in given list.
719 716 return false;
720 717 }
721 718 }
722 719
723 720 foreach (QBarSet* set, sets) {
724 721 m_barSets.append(set);
725 722 QObject::connect(set->d_ptr.data(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout()));
726 723 QObject::connect(set->d_ptr.data(), SIGNAL(updatedBars()), this, SIGNAL(updatedBars()));
727 724 QObject::connect(set->d_ptr.data(), SIGNAL(restructuredBars()), this, SIGNAL(restructuredBars()));
728 725 }
729 726
730 727 emit restructuredBars(); // this notifies barchartitem
731 emit legendPropertiesUpdated(q); // this notifies legend
732 728 return true;
733 729 }
734 730
735 731 bool QAbstractBarSeriesPrivate::remove(QList<QBarSet* > sets)
736 732 {
737 Q_Q(QAbstractBarSeries);
738 733 if (sets.count() == 0) {
739 734 return false;
740 735 }
741 736 foreach (QBarSet* set, sets) {
742 737 if ((set == 0) || (!m_barSets.contains(set))) {
743 738 // Fail if any of the sets is null or is not in series
744 739 return false;
745 740 }
746 741 if (sets.count(set) != 1) {
747 742 // Also fail if same set is more than once in given list.
748 743 return false;
749 744 }
750 745 }
751 746
752 747 foreach (QBarSet* set, sets) {
753 748 m_barSets.removeOne(set);
754 749 QObject::disconnect(set->d_ptr.data(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout()));
755 750 QObject::disconnect(set->d_ptr.data(), SIGNAL(updatedBars()), this, SIGNAL(updatedBars()));
756 751 QObject::disconnect(set->d_ptr.data(), SIGNAL(restructuredBars()), this, SIGNAL(restructuredBars()));
757 752 }
758 753
759 754 emit restructuredBars(); // this notifies barchartitem
760 emit legendPropertiesUpdated(q);
761 755 return true;
762 756 }
763 757
764 758 bool QAbstractBarSeriesPrivate::insert(int index, QBarSet *set)
765 759 {
766 Q_Q(QAbstractBarSeries);
767 760 if ((m_barSets.contains(set)) || (set == 0)) {
768 761 // Fail if set is already in list or set is null.
769 762 return false;
770 763 }
771 764
772 765 m_barSets.insert(index, set);
773 766 QObject::connect(set->d_ptr.data(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout()));
774 767 QObject::connect(set->d_ptr.data(), SIGNAL(updatedBars()), this, SIGNAL(updatedBars()));
775 768 QObject::connect(set->d_ptr.data(), SIGNAL(restructuredBars()), this, SIGNAL(restructuredBars()));
776 769
777 770 emit restructuredBars(); // this notifies barchartitem
778 emit legendPropertiesUpdated(q);
779 771 return true;
780 772 }
781 773
782 774 void QAbstractBarSeriesPrivate::initializeAxis(QAbstractAxis* axis)
783 775 {
784 776 Q_Q(QAbstractBarSeries);
785 777
786 778 if(axis->type()==QAbstractAxis::AxisTypeBarCategory) {
787 779
788 780 switch(q->type()) {
789 781
790 782 case QAbstractSeries::SeriesTypeHorizontalBar:
791 783 case QAbstractSeries::SeriesTypeHorizontalPercentBar:
792 784 case QAbstractSeries::SeriesTypeHorizontalStackedBar: {
793 785
794 786 if(axis->orientation()==Qt::Vertical)
795 787 {
796 788 populateCategories(qobject_cast<QBarCategoryAxis*>(axis));
797 789 }
798 790 break;
799 791 }
800 792 case QAbstractSeries::SeriesTypeBar:
801 793 case QAbstractSeries::SeriesTypePercentBar:
802 794 case QAbstractSeries::SeriesTypeStackedBar: {
803 795
804 796 if(axis->orientation()==Qt::Horizontal)
805 797 {
806 798 populateCategories(qobject_cast<QBarCategoryAxis*>(axis));
807 799 }
808 800 break;
809 801 }
810 802 default:
811 803 qWarning()<<"Unexpected series type";
812 804 break;
813 805
814 806 }
815 807 }
816 808 }
817 809
818 810 QAbstractAxis::AxisType QAbstractBarSeriesPrivate::defaultAxisType(Qt::Orientation orientation) const
819 811 {
820 812 Q_Q(const QAbstractBarSeries);
821 813
822 814 switch(q->type()) {
823 815
824 816 case QAbstractSeries::SeriesTypeHorizontalBar:
825 817 case QAbstractSeries::SeriesTypeHorizontalPercentBar:
826 818 case QAbstractSeries::SeriesTypeHorizontalStackedBar: {
827 819
828 820 if(orientation==Qt::Vertical)
829 821 {
830 822 return QAbstractAxis::AxisTypeBarCategory;
831 823 }
832 824 break;
833 825 }
834 826 case QAbstractSeries::SeriesTypeBar:
835 827 case QAbstractSeries::SeriesTypePercentBar:
836 828 case QAbstractSeries::SeriesTypeStackedBar: {
837 829
838 830 if(orientation==Qt::Horizontal)
839 831 {
840 832 return QAbstractAxis::AxisTypeBarCategory;
841 833 }
842 834 break;
843 835 }
844 836 default:
845 837 qWarning()<<"Unexpected series type";
846 838 break;
847 839
848 840 }
849 841 return QAbstractAxis::AxisTypeValue;
850 842
851 843 }
852 844
853 845 void QAbstractBarSeriesPrivate::populateCategories(QBarCategoryAxis* axis)
854 846 {
855 847 QStringList categories;
856 848 if(axis->categories().isEmpty()) {
857 849 for (int i(1); i < categoryCount()+1; i++)
858 850 categories << QString::number(i);
859 851 axis->append(categories);
860 852 }
861 853 }
862 854
863 855 #include "moc_qabstractbarseries.cpp"
864 856 #include "moc_qabstractbarseries_p.cpp"
865 857
866 858
867 859 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,124 +1,125
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 "qbarseries.h"
22 22 #include "qbarseries_p.h"
23 23 #include "barchartitem_p.h"
24 24 #include "chartdataset_p.h"
25 25 #include "charttheme_p.h"
26 26 #include "baranimation_p.h"
27 27 #include "qvalueaxis.h"
28 28 #include "qbarcategoryaxis.h"
29 29
30 30 QTCOMMERCIALCHART_BEGIN_NAMESPACE
31 31
32 32 /*!
33 33 \class QBarSeries
34 34 \brief Series for creating bar chart
35 35 \mainclass
36 36
37 37 QBarSeries represents a series of data shown as bars. The purpose of this class is to draw bars
38 38 as groups, where bars in same category are grouped next to each other. QBarSeries groups the data
39 39 from sets to categories, which are defined by a QStringList.
40 40
41 41 See the \l {BarChart Example} {bar chart example} to learn how to create a grouped bar chart.
42 42 \image examples_barchart.png
43 43
44 44 \sa QBarSet, QPercentBarSeries, QAbstractBarSeries, QStackedBarSeries
45 45 */
46 46 /*!
47 47 \qmlclass BarSeries QBarSeries
48 48 \inherits AbstractBarSeries
49 49
50 50 The following QML shows how to create a simple grouped bar chart:
51 51 \snippet ../demos/qmlchart/qml/qmlchart/View6.qml 1
52 52 \beginfloatleft
53 53 \image demos_qmlchart6.png
54 54 \endfloat
55 55 \clearfloat
56 56 */
57 57
58 58 /*!
59 59 Constructs empty QBarSeries.
60 60 QBarSeries is QObject which is a child of a \a parent.
61 61 */
62 62 QBarSeries::QBarSeries(QObject *parent)
63 63 : QAbstractBarSeries(*new QBarSeriesPrivate(this), parent)
64 64 {
65
65 66 }
66 67
67 68 /*!
68 69 Returns QChartSeries::SeriesTypeBar.
69 70 */
70 71 QAbstractSeries::SeriesType QBarSeries::type() const
71 72 {
72 73 return QAbstractSeries::SeriesTypeBar;
73 74 }
74 75
75 76 /*!
76 77 Destructor. Removes series from chart.
77 78 */
78 79 QBarSeries::~QBarSeries()
79 80 {
80 81 Q_D(QBarSeries);
81 82 if(d->m_dataset) {
82 83 d->m_dataset->removeSeries(this);
83 84 }
84 85 }
85 86 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
86 87
87 88 QBarSeriesPrivate::QBarSeriesPrivate(QBarSeries *q) : QAbstractBarSeriesPrivate(q)
88 89 {
89 90
90 91 }
91 92
92 93 void QBarSeriesPrivate::scaleDomain(Domain& domain)
93 94 {
94 95 qreal minX(domain.minX());
95 96 qreal minY(domain.minY());
96 97 qreal maxX(domain.maxX());
97 98 qreal maxY(domain.maxY());
98 99
99 100 qreal x = categoryCount();
100 101 minX = qMin(minX, - (qreal)0.5);
101 102 minY = qMin(minY, min());
102 103 maxX = qMax(maxX, x - (qreal)0.5);
103 104 maxY = qMax(maxY, max());
104 105
105 106 domain.setRange(minX,maxX,minY,maxY);
106 107 }
107 108
108 109
109 110 ChartElement* QBarSeriesPrivate::createGraphics(ChartPresenter* presenter)
110 111 {
111 112 Q_Q(QBarSeries);
112 113
113 114 BarChartItem* bar = new BarChartItem(q,presenter);
114 115 if(presenter->animationOptions().testFlag(QChart::SeriesAnimations)) {
115 116 bar->setAnimation(new BarAnimation(bar));
116 117 }
117 118 presenter->chartTheme()->decorate(q, presenter->dataSet()->seriesIndex(q));
118 119 return bar;
119 120 }
120 121
121 122 #include "moc_qbarseries.cpp"
122 123
123 124 QTCOMMERCIALCHART_END_NAMESPACE
124 125
@@ -1,388 +1,387
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 "legendlayout_p.h"
22 22 #include "chartpresenter_p.h"
23 23 #include "legendmarker_p.h"
24 24 #include "qlegend_p.h"
25 25 #include <QDebug>
26 26
27 27 QTCOMMERCIALCHART_BEGIN_NAMESPACE
28 28
29 29 LegendLayout::LegendLayout(QLegend* legend):
30 30 m_legend(legend)
31 31 {
32 32
33 33 }
34 34
35 35 LegendLayout::~LegendLayout()
36 36 {
37 37
38 38 }
39 39
40 40 void LegendLayout::setOffset(qreal x, qreal y)
41 41 {
42 42 bool scrollHorizontal = true;
43 43 switch(m_legend->alignment()) {
44 44 case Qt::AlignTop:
45 45 case Qt::AlignBottom: {
46 46 scrollHorizontal = true;
47 47 break;
48 48 }
49 49 case Qt::AlignLeft:
50 50 case Qt::AlignRight: {
51 51 scrollHorizontal = false;
52 52 break;
53 53 }
54 54 }
55 55
56 56 // If detached, the scrolling direction is vertical instead of horizontal and vice versa.
57 57 if (!m_legend->isAttachedToChart()) {
58 58 scrollHorizontal = !scrollHorizontal;
59 59 }
60 60
61 61 QRectF boundingRect = geometry();
62 62 qreal left, top, right, bottom;
63 63 getContentsMargins(&left, &top, &right, &bottom);
64 64 boundingRect.adjust(left,top,-right,-bottom);
65 65
66 66 // Limit offset between m_minOffset and m_maxOffset
67 67 if (scrollHorizontal) {
68 68 if(m_width<=boundingRect.width()) return;
69 69
70 70 if (x != m_offsetX) {
71 71 m_offsetX = qBound(m_minOffsetX, x, m_maxOffsetX);
72 72 m_legend->d_ptr->items()->setPos(-m_offsetX,boundingRect.top());
73 73 }
74 74 }
75 75 else {
76 76 if(m_height<=boundingRect.height()) return;
77 77
78 78 if (y != m_offsetY) {
79 79 m_offsetY = qBound(m_minOffsetY, y, m_maxOffsetY);
80 80 m_legend->d_ptr->items()->setPos(boundingRect.left(),-m_offsetY);
81 81 }
82 82 }
83 83 }
84 84
85 85 QPointF LegendLayout::offset() const
86 86 {
87 87 return QPointF(m_offsetX,m_offsetY);
88 88 }
89 89
90 90 void LegendLayout::setGeometry(const QRectF& rect)
91 91 {
92 92 m_legend->d_ptr->items()->setVisible(m_legend->isVisible());
93 93
94 94 QGraphicsLayout::setGeometry(rect);
95 95
96 96 if(m_legend->isAttachedToChart()) {
97 97 setAttachedGeometry(rect);
98 98 } else {
99 99 setDettachedGeometry(rect);
100 100 }
101 101
102 102 }
103 103
104 104 void LegendLayout::setAttachedGeometry(const QRectF& rect)
105 105 {
106 106 if (!rect.isValid()) return;
107
108 107 m_offsetX=0;
109 108 m_offsetY=0;
110 109
111 110 QSizeF size(0,0);
112 111
113 112 if( m_legend->d_ptr->markers().isEmpty()) return;
114 113
115 114 m_width=0;
116 115 m_height=0;
117 116
118 117 qreal left, top, right, bottom;
119 118 getContentsMargins(&left, &top, &right, &bottom);
120 119
121 120 QRectF geometry = rect.adjusted(left,top,-right,-bottom);
122 121
123 122 switch(m_legend->alignment()) {
124 123
125 124 case Qt::AlignTop:
126 125 case Qt::AlignBottom: {
127 126 QPointF point(0,0);
128 127 foreach (LegendMarker* marker, m_legend->d_ptr->markers()) {
129 128 if (marker->isVisible()) {
130 129 marker->setGeometry(geometry);
131 130 marker->setPos(point.x(),geometry.height()/2 - marker->boundingRect().height()/2);
132 131 const QRectF& rect = marker->boundingRect();
133 132 size = size.expandedTo(rect.size());
134 133 qreal w = rect.width();
135 134 m_width+=w;
136 135 point.setX(point.x() + w);
137 136 }
138 137 }
139 138 if(m_width<geometry.width()) {
140 139 m_legend->d_ptr->items()->setPos(geometry.width()/2-m_width/2,geometry.top());
141 140
142 141 }
143 142 else {
144 143 m_legend->d_ptr->items()->setPos(geometry.topLeft());
145 144 }
146 145 m_height=size.height();
147 146 }
148 147 break;
149 148 case Qt::AlignLeft:
150 149 case Qt::AlignRight: {
151 150 QPointF point(0,0);
152 151 foreach (LegendMarker* marker, m_legend->d_ptr->markers()) {
153 152 if (marker->isVisible()) {
154 153 marker->setGeometry(geometry);
155 154 marker->setPos(point);
156 155 const QRectF& rect = marker->boundingRect();
157 156 qreal h = rect.height();
158 157 size = size.expandedTo(rect.size());
159 158 m_height+=h;
160 159 point.setY(point.y() + h);
161 160 }
162 161 }
163 162 if(m_height<geometry.height()) {
164 163 m_legend->d_ptr->items()->setPos(geometry.left(),geometry.height()/2-m_height/2);
165 164 }
166 165 else {
167 166 m_legend->d_ptr->items()->setPos(geometry.topLeft());
168 167 }
169 168 m_width=size.width();
170 169 }
171 170 break;
172 171 }
173 172
174 173
175 174 m_minOffsetX = -left;
176 175 m_minOffsetY = - top;
177 176 m_maxOffsetX = m_width - geometry.width() - right;
178 177 m_maxOffsetY = m_height - geometry.height() - bottom;
179 178 }
180 179
181 180 void LegendLayout::setDettachedGeometry(const QRectF& rect)
182 181 {
183 182 if (!rect.isValid()) return;
184 183
185 184 // Detached layout is different.
186 185 // In detached mode legend may have multiple rows and columns, so layout calculations
187 186 // differ a log from attached mode.
188 187 // Also the scrolling logic is bit different.
189 188
190 189 m_offsetX=0;
191 190 m_offsetY=0;
192 191
193 192 qreal left, top, right, bottom;
194 193 getContentsMargins(&left, &top, &right, &bottom);
195 194 QRectF geometry = rect.adjusted(left,top,-right,-bottom);
196 195
197 196 QSizeF size(0,0);
198 197
199 198 QList<LegendMarker *> markers = m_legend->d_ptr->markers();
200 199
201 200 if(markers.isEmpty()) return;
202 201
203 202 switch (m_legend->alignment()) {
204 203 case Qt::AlignTop: {
205 204 QPointF point(0,0);
206 205 m_width = 0;
207 206 m_height = 0;
208 207 for (int i=0; i<markers.count(); i++) {
209 208 LegendMarker *marker = markers.at(i);
210 209 if (marker->isVisible()) {
211 210 marker->setGeometry(geometry);
212 211 marker->setPos(point.x(),point.y());
213 212 const QRectF& boundingRect = marker->boundingRect();
214 213 qreal w = boundingRect.width();
215 214 qreal h = boundingRect.height();
216 215 m_width = qMax(m_width,w);
217 216 m_height = qMax(m_height,h);
218 217 point.setX(point.x() + w);
219 218 if (point.x() + w > geometry.left() + geometry.width() - right) {
220 219 // Next item would go off rect.
221 220 point.setX(0);
222 221 point.setY(point.y() + h);
223 222 if (i+1 < markers.count()) {
224 223 m_height += h;
225 224 }
226 225 }
227 226 }
228 227 }
229 228 m_legend->d_ptr->items()->setPos(geometry.topLeft());
230 229
231 230 m_minOffsetX = -left;
232 231 m_minOffsetY = -top;
233 232 m_maxOffsetX = m_width - geometry.width() - right;
234 233 m_maxOffsetY = m_height - geometry.height() - bottom;
235 234 }
236 235 break;
237 236 case Qt::AlignBottom: {
238 237 QPointF point(0,geometry.height());
239 238 m_width = 0;
240 239 m_height = 0;
241 240 for (int i=0; i<markers.count(); i++) {
242 241 LegendMarker *marker = markers.at(i);
243 242 if (marker->isVisible()) {
244 243 marker->setGeometry(geometry);
245 244 const QRectF& boundingRect = marker->boundingRect();
246 245 qreal w = boundingRect.width();
247 246 qreal h = boundingRect.height();
248 247 m_width = qMax(m_width,w);
249 248 m_height = qMax(m_height,h);
250 249 marker->setPos(point.x(),point.y() - h);
251 250 point.setX(point.x() + w);
252 251 if (point.x() + w > geometry.left() + geometry.width() - right) {
253 252 // Next item would go off rect.
254 253 point.setX(0);
255 254 point.setY(point.y() - h);
256 255 if (i+1 < markers.count()) {
257 256 m_height += h;
258 257 }
259 258 }
260 259 }
261 260 }
262 261 m_legend->d_ptr->items()->setPos(geometry.topLeft());
263 262
264 263 m_minOffsetX = -left;
265 264 m_minOffsetY = -m_height + geometry.height() - top;
266 265 m_maxOffsetX = m_width - geometry.width() - right;
267 266 m_maxOffsetY = -bottom;
268 267 }
269 268 break;
270 269 case Qt::AlignLeft: {
271 270 QPointF point(0,0);
272 271 m_width = 0;
273 272 m_height = 0;
274 273 qreal maxWidth = 0;
275 274 for (int i=0; i<markers.count(); i++) {
276 275 LegendMarker *marker = markers.at(i);
277 276 if (marker->isVisible()) {
278 277 marker->setGeometry(geometry);
279 278 const QRectF& boundingRect = marker->boundingRect();
280 279 qreal w = boundingRect.width();
281 280 qreal h = boundingRect.height();
282 281 m_height = qMax(m_height,h);
283 282 maxWidth = qMax(maxWidth,w);
284 283 marker->setPos(point.x(),point.y());
285 284 point.setY(point.y() + h);
286 285 if (point.y() + h > geometry.bottom() - bottom) {
287 286 // Next item would go off rect.
288 287 point.setX(point.x() + maxWidth);
289 288 point.setY(0);
290 289 if (i+1 < markers.count()) {
291 290 m_width += maxWidth;
292 291 maxWidth = 0;
293 292 }
294 293 }
295 294 }
296 295 }
297 296 m_width += maxWidth;
298 297 m_legend->d_ptr->items()->setPos(geometry.topLeft());
299 298
300 299 m_minOffsetX = -left;
301 300 m_minOffsetY = -top;
302 301 m_maxOffsetX = m_width - geometry.width() - right;
303 302 m_maxOffsetY = m_height - geometry.height() - bottom;
304 303 }
305 304 break;
306 305 case Qt::AlignRight: {
307 306 QPointF point(geometry.width(),0);
308 307 m_width = 0;
309 308 m_height = 0;
310 309 qreal maxWidth = 0;
311 310 for (int i=0; i<markers.count(); i++) {
312 311 LegendMarker *marker = markers.at(i);
313 312 if (marker->isVisible()) {
314 313 marker->setGeometry(geometry);
315 314 const QRectF& boundingRect = marker->boundingRect();
316 315 qreal w = boundingRect.width();
317 316 qreal h = boundingRect.height();
318 317 m_height = qMax(m_height,h);
319 318 maxWidth = qMax(maxWidth,w);
320 319 marker->setPos(point.x() - w,point.y());
321 320 point.setY(point.y() + h);
322 321 if (point.y() + h > geometry.bottom()-bottom) {
323 322 // Next item would go off rect.
324 323 point.setX(point.x() - maxWidth);
325 324 point.setY(0);
326 325 if (i+1 < markers.count()) {
327 326 m_width += maxWidth;
328 327 maxWidth = 0;
329 328 }
330 329 }
331 330 }
332 331 }
333 332 m_width += maxWidth;
334 333 m_legend->d_ptr->items()->setPos(geometry.topLeft());
335 334
336 335 m_minOffsetX = - m_width + geometry.width() - left;
337 336 m_minOffsetY = -top;
338 337 m_maxOffsetX = - right;
339 338 m_maxOffsetY = m_height - geometry.height() - bottom;
340 339 }
341 340 break;
342 341 default:
343 342 break;
344 343 }
345 344
346 345 }
347 346
348 347 QSizeF LegendLayout::sizeHint ( Qt::SizeHint which, const QSizeF & constraint) const
349 348 {
350 349 QSizeF size(0, 0);
351 350 qreal left, top, right, bottom;
352 351 getContentsMargins(&left, &top, &right, &bottom);
353 352
354 353 if(constraint.isValid()) {
355 354 foreach(LegendMarker* marker, m_legend->d_ptr->markers()) {
356 355 size = size.expandedTo(marker->effectiveSizeHint(which));
357 356 }
358 357 size = size.boundedTo(constraint);
359 358 }
360 359 else if (constraint.width() >= 0) {
361 360 qreal width = 0;
362 361 qreal height = 0;
363 362 foreach(LegendMarker* marker, m_legend->d_ptr->markers()) {
364 363 width+=marker->effectiveSizeHint(which).width();
365 364 height=qMax(height,marker->effectiveSizeHint(which).height());
366 365 }
367 366
368 367 size = QSizeF(qMin(constraint.width(),width), height);
369 368 }
370 369 else if (constraint.height() >= 0) {
371 370 qreal width = 0;
372 371 qreal height = 0;
373 372 foreach(LegendMarker* marker, m_legend->d_ptr->markers()) {
374 373 width=qMax(width,marker->effectiveSizeHint(which).width());
375 374 height+=height,marker->effectiveSizeHint(which).height();
376 375 }
377 376 size = QSizeF(width,qMin(constraint.height(),height));
378 377 }
379 378 else {
380 379 foreach(LegendMarker* marker, m_legend->d_ptr->markers()) {
381 380 size = size.expandedTo(marker->effectiveSizeHint(which));
382 381 }
383 382 }
384 383 size += QSize(left + right, top + bottom);
385 384 return size;
386 385 }
387 386
388 387 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,253 +1,254
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 "legendmarker_p.h"
22 22 #include "qxyseries.h"
23 23 #include "qxyseries_p.h"
24 24 #include "qlegend.h"
25 25 #include "qabstractbarseries.h"
26 26 #include "qpieseries.h"
27 27 #include "qpieslice.h"
28 28 #include "qbarset.h"
29 29 #include "qbarset_p.h"
30 30 #include "qareaseries.h"
31 31 #include "qareaseries_p.h"
32 32 #include <QPainter>
33 33 #include <QGraphicsSceneEvent>
34 34 #include <QGraphicsSimpleTextItem>
35 #include <QDebug>
35 #include <QGraphicsLayout>
36 36
37 37 QTCOMMERCIALCHART_BEGIN_NAMESPACE
38 38
39 39 LegendMarker::LegendMarker(QAbstractSeries *series, QLegend *legend) :
40 40 QGraphicsObject(legend),
41 41 m_series(series),
42 42 m_markerRect(0,0,10.0,10.0),
43 43 m_boundingRect(0,0,0,0),
44 44 m_legend(legend),
45 45 m_textItem(new QGraphicsSimpleTextItem(this)),
46 46 m_rectItem(new QGraphicsRectItem(this)),
47 47 m_margin(4),
48 48 m_space(4)
49 49 {
50 50 //setAcceptedMouseButtons(Qt::LeftButton|Qt::RightButton);
51 51 m_rectItem->setRect(m_markerRect);
52 52 }
53 53
54 54 void LegendMarker::setPen(const QPen &pen)
55 55 {
56 56 m_rectItem->setPen(pen);
57 57 }
58 58
59 59 QPen LegendMarker::pen() const
60 60 {
61 61 return m_rectItem->pen();
62 62 }
63 63
64 64 void LegendMarker::setBrush(const QBrush &brush)
65 65 {
66 66 m_rectItem->setBrush(brush);
67 67 }
68 68
69 69 QBrush LegendMarker::brush() const
70 70 {
71 71 return m_rectItem->brush();
72 72 }
73 73
74 74 void LegendMarker::setFont(const QFont &font)
75 75 {
76 76 m_textItem->setFont(font);
77 77 QFontMetrics fn(font);
78 78 m_markerRect = QRectF(0,0,fn.height()/2,fn.height()/2);
79 79 updateGeometry();
80 80 }
81 81
82 82 QFont LegendMarker::font() const
83 83 {
84 84 return m_textItem->font();
85 85 }
86 86
87 87 void LegendMarker::setLabel(const QString label)
88 88 {
89 89 m_text = label;
90 90 updateGeometry();
91 m_legend->layout()->invalidate();
91 92 }
92 93
93 94 QString LegendMarker::label() const
94 95 {
95 96 return m_text;
96 97 }
97 98
98 99 QRectF LegendMarker::boundingRect() const
99 100 {
100 101 return m_boundingRect;
101 102 }
102 103
103 104 void LegendMarker::setLabelBrush(const QBrush &brush)
104 105 {
105 106 m_textItem->setBrush(brush);
106 107 }
107 108
108 109 QBrush LegendMarker::labelBrush() const
109 110 {
110 111 return m_textItem->brush();
111 112 }
112 113
113 114
114 115 void LegendMarker::setGeometry(const QRectF& rect)
115 116 {
116 117 QFontMetrics fn (font());
117 118
118 119 int width = rect.width();
119 120 qreal x = m_margin + m_markerRect.width() + m_space + m_margin;
120 121 qreal y = qMax(m_markerRect.height()+2*m_margin,fn.height()+2*m_margin);
121 122
122 123 if (fn.boundingRect(m_text).width() + x > width)
123 124 {
124 125 QString string = m_text + "...";
125 126 while(fn.boundingRect(string).width() + x > width && string.length() > 3)
126 127 string.remove(string.length() - 4, 1);
127 128 m_textItem->setText(string);
128 129 }
129 130 else
130 131 m_textItem->setText(m_text);
131 132
132 133 const QRectF& textRect = m_textItem->boundingRect();
133 134
134 135
135 136 m_textItem->setPos(x-m_margin,y/2 - textRect.height()/2);
136 137 m_rectItem->setRect(m_markerRect);
137 138 m_rectItem->setPos(m_margin,y/2 - m_markerRect.height()/2);
138 139
139 140 prepareGeometryChange();
140 141 m_boundingRect = QRectF(0,0,x+textRect.width()+m_margin,y);
141 142 }
142 143
143 144 void LegendMarker::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
144 145 {
145 146 Q_UNUSED(option)
146 147 Q_UNUSED(widget)
147 148 Q_UNUSED(painter)
148 149 }
149 150
150 151 QSizeF LegendMarker::sizeHint(Qt::SizeHint which, const QSizeF& constraint) const
151 152 {
152 153 Q_UNUSED(constraint)
153 154
154 155 QFontMetrics fn(m_textItem->font());
155 156 QSizeF sh;
156 157
157 158 switch (which) {
158 159 case Qt::MinimumSize:
159 160 sh = QSizeF(fn.boundingRect("...").width() + 2*m_margin + m_space +m_markerRect.width(),qMax(m_markerRect.height()+2*m_margin,fn.height()+2*m_margin));
160 161 break;
161 162 case Qt::PreferredSize:
162 163 sh = QSizeF(fn.boundingRect(m_text).width() + 2*m_margin + m_space +m_markerRect.width(),qMax(m_markerRect.height()+2*m_margin,fn.height()+2*m_margin));
163 164 break;
164 165 default:
165 166 break;
166 167 }
167 168
168 169 return sh;
169 170 }
170 171
171 172 void LegendMarker::mousePressEvent(QGraphicsSceneMouseEvent *event)
172 173 {
173 174 QGraphicsObject::mousePressEvent(event);
174 175 //TODO: selected signal removed for now
175 176 }
176 177
177 178 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
178 179
179 180 AreaLegendMarker::AreaLegendMarker(QAreaSeries *series,QLegend *legend) : LegendMarker(series,legend),
180 181 m_series(series)
181 182 {
182 183 //QObject::connect(this, SIGNAL(selected()), series, SIGNAL(selected()));
183 184 QObject::connect(series->d_func(),SIGNAL(updated()), this, SLOT(updated()));
184 185 QObject::connect(series, SIGNAL(nameChanged()), this, SLOT(updated()));
185 186 updated();
186 187 }
187 188
188 189 void AreaLegendMarker::updated()
189 190 {
190 191 setBrush(m_series->brush());
191 192 setLabel(m_series->name());
192 193 }
193 194
194 195 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
195 196
196 197 BarLegendMarker::BarLegendMarker(QAbstractBarSeries *barseries,QBarSet *barset, QLegend *legend) : LegendMarker(barseries,legend),
197 198 m_barset(barset)
198 199 {
199 200 //QObject::connect(this, SIGNAL(selected()),barset->d_ptr.data(), SIGNAL(selected()));
200 201 QObject::connect(barset->d_ptr.data(), SIGNAL(updatedBars()), this, SLOT(updated()));
201 202 updated();
202 203 }
203 204
204 205 void BarLegendMarker::updated()
205 206 {
206 207 setBrush(m_barset->brush());
207 208 setLabel(m_barset->label());
208 209 }
209 210
210 211 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
211 212
212 213 PieLegendMarker::PieLegendMarker(QPieSeries* series,QPieSlice *pieslice, QLegend *legend) : LegendMarker(series,legend),
213 214 m_pieslice(pieslice)
214 215 {
215 216 QObject::connect(pieslice, SIGNAL(labelChanged()), this, SLOT(updated()));
216 217 QObject::connect(pieslice, SIGNAL(brushChanged()), this, SLOT(updated()));
217 218 updated();
218 219 }
219 220
220 221 void PieLegendMarker::updated()
221 222 {
222 223 setBrush(m_pieslice->brush());
223 224 setLabel(m_pieslice->label());
224 225 }
225 226
226 227 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
227 228
228 229 XYLegendMarker::XYLegendMarker(QXYSeries *series, QLegend *legend) : LegendMarker(series,legend),
229 230 m_series(series)
230 231 {
231 232 //QObject::connect(this, SIGNAL(selected()), series, SIGNAL(selected()));
232 233 QObject::connect(series->d_func(),SIGNAL(updated()), this, SLOT(updated()));
233 234 QObject::connect(series, SIGNAL(nameChanged()), this, SLOT(updated()));
234 235 updated();
235 236 }
236 237
237 238 void XYLegendMarker::updated()
238 239 {
239 240 setLabel(m_series->name());
240 241
241 242 if(m_series->type()== QAbstractSeries::SeriesTypeScatter)
242 243 {
243 244 setBrush(m_series->brush());
244 245
245 246 }
246 247 else {
247 248 setBrush(QBrush(m_series->pen().color()));
248 249 }
249 250 }
250 251
251 252 #include "moc_legendmarker_p.cpp"
252 253
253 254 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,524 +1,525
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 "qlegend.h"
22 22 #include "qlegend_p.h"
23 23 #include "qabstractseries.h"
24 24 #include "qabstractseries_p.h"
25 25 #include "qchart_p.h"
26 26 #include "legendlayout_p.h"
27 27 #include "legendmarker_p.h"
28 28 #include "qxyseries.h"
29 29 #include "qlineseries.h"
30 30 #include "qareaseries.h"
31 31 #include "qscatterseries.h"
32 32 #include "qsplineseries.h"
33 33 #include "qabstractbarseries.h"
34 34 #include "qstackedbarseries.h"
35 35 #include "qpercentbarseries.h"
36 36 #include "qbarset.h"
37 37 #include "qpieseries.h"
38 38 #include "qpieseries_p.h"
39 39 #include "qpieslice.h"
40 40 #include "chartpresenter_p.h"
41 41 #include <QPainter>
42 42 #include <QPen>
43 43 #include <QTimer>
44 44 #include <QGraphicsLayout>
45 45 #include <QGraphicsSceneEvent>
46 46
47 47 QTCOMMERCIALCHART_BEGIN_NAMESPACE
48 48
49 49 /*!
50 50 \class QLegend
51 51 \brief Legend object
52 52 \mainclass
53 53
54 54 QLegend is a graphical object, whics displays legend of the chart. Legend state is updated by QChart, when
55 55 series have been changed. By default, legend is drawn by QChart, but user can set a new parent to legend and
56 56 handle the drawing manually.
57 57 User isn't supposed to create or delete legend objects, but can reference it via QChart class.
58 58
59 59 \image examples_percentbarchart_legend.png
60 60
61 61 \sa QChart
62 62 */
63 63 /*!
64 64 \qmlclass Legend QLegend
65 65 \brief Legend is part of QtCommercial Chart QML API.
66 66
67 67 Legend is a graphical object, whics displays legend of the chart. Legend state is updated by ChartView, when
68 68 series have been changed. Legend is used via ChartView class. For example:
69 69 \code
70 70 ChartView {
71 71 legend.visible: true
72 72 legend.alignment: Qt.AlignBottom
73 73 // Add a few series...
74 74 }
75 75 \endcode
76 76
77 77 \image examples_percentbarchart_legend.png
78 78 */
79 79
80 80 /*!
81 81 \property QLegend::alignment
82 82 \brief The alignment of the legend.
83 83
84 84 Legend paints on the defined position in the chart. The following alignments are supported:
85 85 Qt::AlignTop, Qt::AlignBottom, Qt::AlignLeft, Qt::AlignRight. If you set more than one flag the result is undefined.
86 86 */
87 87 /*!
88 88 \qmlproperty Qt.Alignment Legend::alignment
89 89 \brief The alignment of the legend.
90 90
91 91 Legend paints on the defined position in the chart. The following alignments are supported:
92 92 Qt.AlignTop, Qt.AlignBottom, Qt.AlignLeft, Qt.AlignRight. If you set more than one flag the result is undefined.
93 93 */
94 94
95 95 /*!
96 96 \property QLegend::backgroundVisible
97 97 Whether the legend background is visible or not.
98 98 */
99 99 /*!
100 100 \qmlproperty bool Legend::backgroundVisible
101 101 Whether the legend background is visible or not.
102 102 */
103 103
104 104 /*!
105 105 \property QLegend::color
106 106 The color of the legend, i.e. the background (brush) color. Note that if you change the color
107 107 of the legend, the style of the legend brush is set to Qt::SolidPattern.
108 108 */
109 109 /*!
110 110 \qmlproperty color Legend::color
111 111 The color of the legend, i.e. the background (brush) color.
112 112 */
113 113
114 114 /*!
115 115 \property QLegend::borderColor
116 116 The border color of the legend, i.e. the line color.
117 117 */
118 118 /*!
119 119 \qmlproperty color Legend::borderColor
120 120 The border color of the legend, i.e. the line color.
121 121 */
122 122
123 123 /*!
124 124 \property QLegend::font
125 125 The font of markers used by legend
126 126 */
127 127 /*!
128 128 \qmlproperty color Legend::font
129 129 The font of markers used by legend
130 130 */
131 131
132 132 /*!
133 133 \property QLegend::labelColor
134 134 The color of brush used to draw labels.
135 135 */
136 136 /*!
137 137 \qmlproperty color QLegend::labelColor
138 138 The color of brush used to draw labels.
139 139 */
140 140
141 141 /*!
142 142 \fn void QLegend::backgroundVisibleChanged(bool)
143 143 The visibility of the legend background changed to \a visible.
144 144 */
145 145
146 146 /*!
147 147 \fn void QLegend::colorChanged(QColor)
148 148 The color of the legend background changed to \a color.
149 149 */
150 150
151 151 /*!
152 152 \fn void QLegend::borderColorChanged(QColor)
153 153 The border color of the legend background changed to \a color.
154 154 */
155 155
156 156 /*!
157 157 \fn void QLegend::fontChanged(QFont)
158 158 The font of markers of the legend changed to \a font.
159 159 */
160 160
161 161 /*!
162 162 \fn void QLegend::labelColorChanged(QColor color)
163 163 This signal is emitted when the color of brush used to draw labels has changed to \a color.
164 164 */
165 165
166 166 /*!
167 167 Constructs the legend object and sets the parent to \a parent
168 168 */
169 169
170 170 QLegend::QLegend(QChart *chart):QGraphicsWidget(chart),
171 171 d_ptr(new QLegendPrivate(chart->d_ptr->m_presenter,chart,this))
172 172 {
173 173 setZValue(ChartPresenter::LegendZValue);
174 174 setFlags(QGraphicsItem::ItemClipsChildrenToShape);
175 175 QObject::connect(chart->d_ptr->m_dataset,SIGNAL(seriesAdded(QAbstractSeries*,Domain*)),d_ptr.data(),SLOT(handleSeriesAdded(QAbstractSeries*,Domain*)));
176 176 QObject::connect(chart->d_ptr->m_dataset,SIGNAL(seriesRemoved(QAbstractSeries*)),d_ptr.data(),SLOT(handleSeriesRemoved(QAbstractSeries*)));
177 177 // QObject::connect(chart->d_ptr->m_dataset,SIGNAL(seriesUpdated(QAbstractSeries*)),d_ptr.data(),SLOT(handleSeriesUpdated(QAbstractSeries*)));
178 178 setLayout(d_ptr->m_layout);
179 179 }
180 180
181 181 /*!
182 182 Destroys the legend object. Legend is always owned by a QChart, so an application should never call this.
183 183 */
184 184 QLegend::~QLegend()
185 185 {
186 186 }
187 187
188 188 /*!
189 189 \internal
190 190 */
191 191 void QLegend::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
192 192 {
193 193 Q_UNUSED(option)
194 194 Q_UNUSED(widget)
195 195
196 196 if(!d_ptr->m_backgroundVisible) return;
197 197
198 198 painter->setOpacity(opacity());
199 199 painter->setPen(d_ptr->m_pen);
200 200 painter->setBrush(d_ptr->m_brush);
201 201 painter->drawRoundRect(rect(),d_ptr->roundness(rect().width()),d_ptr->roundness(rect().height()));
202 202
203 203 }
204 204
205 205
206 206 /*!
207 207 Sets the \a brush of legend. Brush affects the background of legend.
208 208 */
209 209 void QLegend::setBrush(const QBrush &brush)
210 210 {
211 211 if (d_ptr->m_brush != brush) {
212 212 d_ptr->m_brush = brush;
213 213 update();
214 214 emit colorChanged(brush.color());
215 215 }
216 216 }
217 217
218 218 /*!
219 219 Returns the brush used by legend.
220 220 */
221 221 QBrush QLegend::brush() const
222 222 {
223 223 return d_ptr->m_brush;
224 224 }
225 225
226 226 void QLegend::setColor(QColor color)
227 227 {
228 228 QBrush b = d_ptr->m_brush;
229 229 if (b.style() != Qt::SolidPattern || b.color() != color) {
230 230 b.setStyle(Qt::SolidPattern);
231 231 b.setColor(color);
232 232 setBrush(b);
233 233 }
234 234 }
235 235
236 236 QColor QLegend::color()
237 237 {
238 238 return d_ptr->m_brush.color();
239 239 }
240 240
241 241 /*!
242 242 Sets the \a pen of legend. Pen affects the legend borders.
243 243 */
244 244 void QLegend::setPen(const QPen &pen)
245 245 {
246 246 if (d_ptr->m_pen != pen) {
247 247 d_ptr->m_pen = pen;
248 248 update();
249 249 emit borderColorChanged(pen.color());
250 250 }
251 251 }
252 252
253 253 /*!
254 254 Returns the pen used by legend
255 255 */
256 256
257 257 QPen QLegend::pen() const
258 258 {
259 259 return d_ptr->m_pen;
260 260 }
261 261
262 262 void QLegend::setFont(const QFont &font)
263 263 {
264 264 if (d_ptr->m_font != font) {
265 265 d_ptr->m_font = font;
266 266
267 267 foreach (LegendMarker *marker, d_ptr->markers()) {
268 268 marker->setFont(d_ptr->m_font);
269 269 }
270 270 layout()->invalidate();
271 271 emit fontChanged(font);
272 272 }
273 273 }
274 274
275 275 QFont QLegend::font() const
276 276 {
277 277 return d_ptr->m_font;
278 278 }
279 279
280 280 void QLegend::setBorderColor(QColor color)
281 281 {
282 282 QPen p = d_ptr->m_pen;
283 283 if (p.color() != color) {
284 284 p.setColor(color);
285 285 setPen(p);
286 286 }
287 287 }
288 288
289 289 QColor QLegend::borderColor()
290 290 {
291 291 return d_ptr->m_pen.color();
292 292 }
293 293
294 294 /*!
295 295 Set brush used to draw labels to \a brush.
296 296 */
297 297 void QLegend::setLabelBrush(const QBrush &brush)
298 298 {
299 299 if (d_ptr->m_labelBrush != brush) {
300 300 d_ptr->m_labelBrush = brush;
301 301 foreach (LegendMarker *marker, d_ptr->markers()) {
302 302 marker->setLabelBrush(d_ptr->m_labelBrush);
303 303 // Note: The pen of the marker rectangle could be exposed in the public QLegend API
304 304 // instead of mapping it from label brush color
305 305 marker->setPen(brush.color());
306 306 }
307 307 emit labelColorChanged(brush.color());
308 308 }
309 309 }
310 310
311 311 /*!
312 312 Brush used to draw labels.
313 313 */
314 314 QBrush QLegend::labelBrush() const
315 315 {
316 316 return d_ptr->m_labelBrush;
317 317 }
318 318
319 319 void QLegend::setLabelColor(QColor color)
320 320 {
321 321 QBrush b = d_ptr->m_labelBrush;
322 322 if (b.style() != Qt::SolidPattern || b.color() != color) {
323 323 b.setStyle(Qt::SolidPattern);
324 324 b.setColor(color);
325 325 setLabelBrush(b);
326 326 }
327 327 }
328 328
329 329 QColor QLegend::labelColor() const
330 330 {
331 331 return d_ptr->m_labelBrush.color();
332 332 }
333 333
334 334
335 335 void QLegend::setAlignment(Qt::Alignment alignment)
336 336 {
337 337 if(d_ptr->m_alignment!=alignment) {
338 338 d_ptr->m_alignment = alignment;
339 339 if(isAttachedToChart()){
340 340 d_ptr->m_presenter->layout()->invalidate();
341 341 }else{
342 342 layout()->invalidate();
343 343 }
344 344 }
345 345 }
346 346
347 347 Qt::Alignment QLegend::alignment() const
348 348 {
349 349 return d_ptr->m_alignment;
350 350 }
351 351
352 352 /*!
353 353 Detaches the legend from chart. Chart won't change layout of the legend.
354 354 */
355 355 void QLegend::detachFromChart()
356 356 {
357 357 d_ptr->m_attachedToChart = false;
358 358 d_ptr->m_layout->invalidate();
359 359 setParent(0);
360 360
361 361 }
362 362
363 363 /*!
364 364 Attaches the legend to chart. Chart may change layout of the legend.
365 365 */
366 366 void QLegend::attachToChart()
367 367 {
368 368 d_ptr->m_attachedToChart = true;
369 369 d_ptr->m_presenter->layout()->invalidate();
370 370 setParent(d_ptr->m_chart);
371 371 }
372 372
373 373 /*!
374 374 Returns true, if legend is attached to chart.
375 375 */
376 376 bool QLegend::isAttachedToChart()
377 377 {
378 378 return d_ptr->m_attachedToChart;
379 379 }
380 380
381 381 /*!
382 382 Sets the visibility of legend background to \a visible
383 383 */
384 384 void QLegend::setBackgroundVisible(bool visible)
385 385 {
386 386 if(d_ptr->m_backgroundVisible != visible) {
387 387 d_ptr->m_backgroundVisible = visible;
388 388 update();
389 389 emit backgroundVisibleChanged(visible);
390 390 }
391 391 }
392 392
393 393 /*!
394 394 Returns the visibility of legend background
395 395 */
396 396 bool QLegend::isBackgroundVisible() const
397 397 {
398 398 return d_ptr->m_backgroundVisible;
399 399 }
400 400
401 401 /*!
402 402 \internal \a event see QGraphicsWidget for details
403 403 */
404 404 void QLegend::hideEvent(QHideEvent *event)
405 405 {
406 406 if(isAttachedToChart()) d_ptr->m_presenter->layout()->invalidate();
407 407 QGraphicsWidget::hideEvent(event);
408 408 }
409 409
410 410 /*!
411 411 \internal \a event see QGraphicsWidget for details
412 412 */
413 413 void QLegend::showEvent(QShowEvent *event)
414 414 {
415 415 if(isAttachedToChart()) {
416 416 d_ptr->m_presenter->layout()->invalidate();
417 417 d_ptr->items()->setVisible(false);
418 418 layout()->invalidate();
419 419 }
420 420 QGraphicsWidget::showEvent(event);
421 421 //layout activation will show the items
422 422 }
423 423
424 424 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
425 425
426 426 QLegendPrivate::QLegendPrivate(ChartPresenter* presenter, QChart *chart, QLegend *q):
427 427 q_ptr(q),
428 428 m_presenter(presenter),
429 429 m_layout(new LegendLayout(q)),
430 430 m_chart(chart),
431 431 m_items(new QGraphicsItemGroup(q)),
432 432 m_alignment(Qt::AlignTop),
433 433 m_brush(QBrush()),
434 434 m_pen(QPen()),
435 435 m_labelBrush(QBrush()),
436 436 m_diameter(5),
437 437 m_attachedToChart(true),
438 438 m_backgroundVisible(false)
439 439 {
440 440
441 441 }
442 442
443 443 QLegendPrivate::~QLegendPrivate()
444 444 {
445 445
446 446 }
447 447
448 448 void QLegendPrivate::setOffset(qreal x, qreal y)
449 449 {
450 450 m_layout->setOffset(x,y);
451 451 }
452 452
453 453 QPointF QLegendPrivate::offset() const
454 454 {
455 455 return m_layout->offset();
456 456 }
457 457
458 458 int QLegendPrivate::roundness(qreal size)
459 459 {
460 460 return 100*m_diameter/int(size);
461 461 }
462 462
463 463 void QLegendPrivate::handleSeriesAdded(QAbstractSeries *series, Domain *domain)
464 464 {
465 465 Q_UNUSED(domain)
466 466
467 467 QList<LegendMarker*> markers = series->d_ptr->createLegendMarker(q_ptr);
468 468
469 469 foreach (LegendMarker* marker, markers) {
470 470 marker->setFont(m_font);
471 471 marker->setLabelBrush(m_labelBrush);
472 472 marker->setVisible(series->isVisible());
473 473 m_items->addToGroup(marker);
474 474 m_markers<<marker;
475 475 }
476 476
477 477 QObject::connect(series, SIGNAL(visibleChanged()), this, SLOT(handleSeriesVisibleChanged()));
478 QObject::connect(series->d_ptr.data(), SIGNAL(legendPropertiesUpdated(QAbstractSeries*)), this, SLOT(handleLegendPropertiesUpdated(QAbstractSeries*)));
478 QObject::connect(series->d_ptr.data(), SIGNAL(countChanged()), this, SLOT(handleCountChanged()));
479 479
480 m_items->setVisible(false);
480 481 q_ptr->layout()->invalidate();
481 q_ptr->layout()->activate();
482 m_presenter->layout()->invalidate();
482 483 }
483 484
484 485 void QLegendPrivate::handleSeriesRemoved(QAbstractSeries *series)
485 486 {
486 487 foreach (LegendMarker *marker, m_markers) {
487 488 if (marker->series() == series) {
488 489 delete marker;
489 490 m_markers.removeAll(marker);
490 491 }
491 492 }
492 493
493 494 QObject::disconnect(series, SIGNAL(visibleChanged()), this, SLOT(handleSeriesVisibleChanged()));
494 QObject::disconnect(series->d_ptr.data(), SIGNAL(legendPropertiesUpdated(QAbstractSeries*)), this, SLOT(handleLegendPropertiesUpdated(QAbstractSeries*)));
495 QObject::disconnect(series->d_ptr.data(), SIGNAL(countChanged()), this, SLOT(handleCountChanged()));
495 496
496 497 q_ptr->layout()->invalidate();
497 498 }
498 499
499 500 void QLegendPrivate::handleSeriesVisibleChanged()
500 501 {
501 502 QAbstractSeries* series = qobject_cast<QAbstractSeries *> (sender());
503 Q_ASSERT(series);
502 504
503 505 foreach (LegendMarker* marker, m_markers) {
504 506 if (marker->series() == series) {
505 507 marker->setVisible(series->isVisible());
506 508 }
507 509 }
508 510
509 511 q_ptr->layout()->invalidate();
510 512 }
511 513
512 void QLegendPrivate::handleLegendPropertiesUpdated(QAbstractSeries *series)
514 void QLegendPrivate::handleCountChanged()
513 515 {
514 // Handle new or removed markers
515 // Handle changes of marker pen/brush/label. every property that legend is interested
516 handleSeriesRemoved(series);
517 Domain domain;
518 handleSeriesAdded(series, &domain);
516 QAbstractSeriesPrivate* series = qobject_cast<QAbstractSeriesPrivate *> (sender());
517 Q_ASSERT(series);
518 handleSeriesRemoved(series->q_ptr);
519 handleSeriesAdded(series->q_ptr, 0);
519 520 }
520 521
521 522 #include "moc_qlegend.cpp"
522 523 #include "moc_qlegend_p.cpp"
523 524
524 525 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,87 +1,87
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 // W A R N I N G
22 22 // -------------
23 23 //
24 24 // This file is not part of the QtCommercial Chart API. It exists purely as an
25 25 // implementation detail. This header file may change from version to
26 26 // version without notice, or even be removed.
27 27 //
28 28 // We mean it.
29 29
30 30 #ifndef QLEGEND_P_H
31 31 #define QLEGEND_P_H
32 32
33 33 #include "qlegend.h"
34 34
35 35 QTCOMMERCIALCHART_BEGIN_NAMESPACE
36 36
37 37 class QChart;
38 38 class ChartPresenter;
39 39 class QAbstractSeries;
40 40 class LegendLayout;
41 41 class LegendMarker;
42 42 class Domain;
43 43
44 44 class QLegendPrivate : public QObject
45 45 {
46 46 Q_OBJECT
47 47 public:
48 48 QLegendPrivate(ChartPresenter *presenter, QChart *chart, QLegend *q);
49 49 ~QLegendPrivate();
50 50
51 51 void setOffset(qreal x, qreal y);
52 52 QPointF offset() const;
53 53 int roundness(qreal size);
54 54
55 55 QList<LegendMarker*> markers() { return m_markers; }
56 56 QGraphicsItemGroup* items() { return m_items; }
57 57
58 58 public Q_SLOTS:
59 59 void handleSeriesAdded(QAbstractSeries *series, Domain *domain);
60 60 void handleSeriesRemoved(QAbstractSeries *series);
61 61 void handleSeriesVisibleChanged();
62 void handleLegendPropertiesUpdated(QAbstractSeries *series);
62 void handleCountChanged();
63 63
64 64 private:
65 65 QLegend *q_ptr;
66 66 ChartPresenter *m_presenter;
67 67 LegendLayout *m_layout;
68 68 QChart* m_chart;
69 69 QGraphicsItemGroup* m_items;
70 70 QList<LegendMarker*> m_markers;
71 71 Qt::Alignment m_alignment;
72 72 QBrush m_brush;
73 73 QPen m_pen;
74 74 QFont m_font;
75 75 QBrush m_labelBrush;
76 76
77 77 qreal m_diameter;
78 78 bool m_attachedToChart;
79 79 bool m_backgroundVisible;
80 80
81 81 friend class QLegend;
82 82
83 83 };
84 84
85 85 QTCOMMERCIALCHART_END_NAMESPACE
86 86
87 87 #endif
@@ -1,898 +1,879
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 "qpieseries.h"
22 22 #include "qpieseries_p.h"
23 23 #include "qpieslice.h"
24 24 #include "qpieslice_p.h"
25 25 #include "pieslicedata_p.h"
26 26 #include "chartdataset_p.h"
27 27 #include "charttheme_p.h"
28 28 #include "legendmarker_p.h"
29 29 #include "qabstractaxis.h"
30 30 #include "pieanimation_p.h"
31 31
32 32 QTCOMMERCIALCHART_BEGIN_NAMESPACE
33 33
34 34 /*!
35 35 \class QPieSeries
36 36 \brief Pie series API for QtCommercial Charts
37 37
38 38 The pie series defines a pie chart which consists of pie slices which are defined as QPieSlice objects.
39 39 The slices can have any values as the QPieSeries will calculate its relative value to the sum of all slices.
40 40 The actual slice size is determined by that relative value.
41 41
42 42 Pie size and position on the chart is controlled by using relative values which range from 0.0 to 1.0
43 43 These relate to the actual chart rectangle.
44 44
45 45 By default the pie is defined as a full pie but it can also be a partial pie.
46 46 This can be done by setting a starting angle and angle span to the series.
47 47 Full pie is 360 degrees where 0 is at 12 a'clock.
48 48
49 49 See the \l {PieChart Example} {pie chart example} or \l {DonutChart Example} {donut chart example} to learn how to use QPieSeries.
50 50 \table 100%
51 51 \row
52 52 \o \image examples_piechart.png
53 53 \o \image examples_donut.png
54 54 \endtable
55 55 */
56 56 /*!
57 57 \qmlclass PieSeries QPieSeries
58 58 \inherits AbstractSeries
59 59
60 60 The following QML shows how to create a simple pie chart.
61 61
62 62 \snippet ../demos/qmlchart/qml/qmlchart/View1.qml 1
63 63
64 64 \beginfloatleft
65 65 \image demos_qmlchart1.png
66 66 \endfloat
67 67 \clearfloat
68 68 */
69 69
70 70 /*!
71 71 \property QPieSeries::horizontalPosition
72 72 \brief Defines the horizontal position of the pie.
73 73
74 74 The value is a relative value to the chart rectangle where:
75 75
76 76 \list
77 77 \o 0.0 is the absolute left.
78 78 \o 1.0 is the absolute right.
79 79 \endlist
80 80 Default value is 0.5 (center).
81 81 \sa verticalPosition
82 82 */
83 83
84 84 /*!
85 85 \qmlproperty real PieSeries::horizontalPosition
86 86
87 87 Defines the horizontal position of the pie.
88 88
89 89 The value is a relative value to the chart rectangle where:
90 90
91 91 \list
92 92 \o 0.0 is the absolute left.
93 93 \o 1.0 is the absolute right.
94 94 \endlist
95 95 Default value is 0.5 (center).
96 96 \sa verticalPosition
97 97 */
98 98
99 99 /*!
100 100 \property QPieSeries::verticalPosition
101 101 \brief Defines the vertical position of the pie.
102 102
103 103 The value is a relative value to the chart rectangle where:
104 104
105 105 \list
106 106 \o 0.0 is the absolute top.
107 107 \o 1.0 is the absolute bottom.
108 108 \endlist
109 109 Default value is 0.5 (center).
110 110 \sa horizontalPosition
111 111 */
112 112
113 113 /*!
114 114 \qmlproperty real PieSeries::verticalPosition
115 115
116 116 Defines the vertical position of the pie.
117 117
118 118 The value is a relative value to the chart rectangle where:
119 119
120 120 \list
121 121 \o 0.0 is the absolute top.
122 122 \o 1.0 is the absolute bottom.
123 123 \endlist
124 124 Default value is 0.5 (center).
125 125 \sa horizontalPosition
126 126 */
127 127
128 128 /*!
129 129 \property QPieSeries::size
130 130 \brief Defines the pie size.
131 131
132 132 The value is a relative value to the chart rectangle where:
133 133
134 134 \list
135 135 \o 0.0 is the minimum size (pie not drawn).
136 136 \o 1.0 is the maximum size that can fit the chart.
137 137 \endlist
138 138
139 139 When setting this property the holeSize property is adjusted if necessary, to ensure that the hole size is not greater than the outer size.
140 140
141 141 Default value is 0.7.
142 142 */
143 143
144 144 /*!
145 145 \qmlproperty real PieSeries::size
146 146
147 147 Defines the pie size.
148 148
149 149 The value is a relative value to the chart rectangle where:
150 150
151 151 \list
152 152 \o 0.0 is the minimum size (pie not drawn).
153 153 \o 1.0 is the maximum size that can fit the chart.
154 154 \endlist
155 155
156 156 Default value is 0.7.
157 157 */
158 158
159 159 /*!
160 160 \property QPieSeries::holeSize
161 161 \brief Defines the donut hole size.
162 162
163 163 The value is a relative value to the chart rectangle where:
164 164
165 165 \list
166 166 \o 0.0 is the minimum size (full pie drawn, without any hole inside).
167 167 \o 1.0 is the maximum size that can fit the chart. (donut has no width)
168 168 \endlist
169 169
170 170 The value is never greater then size property.
171 171 Default value is 0.0.
172 172 */
173 173
174 174 /*!
175 175 \qmlproperty real PieSeries::holeSize
176 176
177 177 Defines the donut hole size.
178 178
179 179 The value is a relative value to the chart rectangle where:
180 180
181 181 \list
182 182 \o 0.0 is the minimum size (full pie drawn, without any hole inside).
183 183 \o 1.0 is the maximum size that can fit the chart. (donut has no width)
184 184 \endlist
185 185
186 186 When setting this property the size property is adjusted if necessary, to ensure that the inner size is not greater than the outer size.
187 187
188 188 Default value is 0.0.
189 189 */
190 190
191 191 /*!
192 192 \property QPieSeries::startAngle
193 193 \brief Defines the starting angle of the pie.
194 194
195 195 Full pie is 360 degrees where 0 degrees is at 12 a'clock.
196 196
197 197 Default is value is 0.
198 198 */
199 199
200 200 /*!
201 201 \qmlproperty real PieSeries::startAngle
202 202
203 203 Defines the starting angle of the pie.
204 204
205 205 Full pie is 360 degrees where 0 degrees is at 12 a'clock.
206 206
207 207 Default is value is 0.
208 208 */
209 209
210 210 /*!
211 211 \property QPieSeries::endAngle
212 212 \brief Defines the ending angle of the pie.
213 213
214 214 Full pie is 360 degrees where 0 degrees is at 12 a'clock.
215 215
216 216 Default is value is 360.
217 217 */
218 218
219 219 /*!
220 220 \qmlproperty real PieSeries::endAngle
221 221
222 222 Defines the ending angle of the pie.
223 223
224 224 Full pie is 360 degrees where 0 degrees is at 12 a'clock.
225 225
226 226 Default is value is 360.
227 227 */
228 228
229 229 /*!
230 230 \property QPieSeries::count
231 231
232 232 Number of slices in the series.
233 233 */
234 234
235 235 /*!
236 236 \qmlproperty int PieSeries::count
237 237
238 238 Number of slices in the series.
239 239 */
240 240
241 241 /*!
242 242 \fn void QPieSeries::countChanged()
243 243 Emitted when the slice count has changed.
244 244 \sa count
245 245 */
246 246 /*!
247 247 \qmlsignal PieSeries::onCountChanged()
248 248 Emitted when the slice count has changed.
249 249 */
250 250
251 251 /*!
252 252 \property QPieSeries::sum
253 253
254 254 Sum of all slices.
255 255
256 256 The series keeps track of the sum of all slices it holds.
257 257 */
258 258
259 259 /*!
260 260 \qmlproperty real PieSeries::sum
261 261
262 262 Sum of all slices.
263 263
264 264 The series keeps track of the sum of all slices it holds.
265 265 */
266 266
267 267 /*!
268 268 \fn void QPieSeries::sumChanged()
269 269 Emitted when the sum of all slices has changed.
270 270 \sa sum
271 271 */
272 272 /*!
273 273 \qmlsignal PieSeries::onSumChanged()
274 274 Emitted when the sum of all slices has changed. This may happen for example if you add or remove slices, or if you
275 275 change value of a slice.
276 276 */
277 277
278 278 /*!
279 279 \fn void QPieSeries::added(QList<QPieSlice*> slices)
280 280
281 281 This signal is emitted when \a slices have been added to the series.
282 282
283 283 \sa append(), insert()
284 284 */
285 285 /*!
286 286 \qmlsignal PieSeries::onAdded(PieSlice slice)
287 287 Emitted when \a slice has been added to the series.
288 288 */
289 289
290 290 /*!
291 291 \fn void QPieSeries::removed(QList<QPieSlice*> slices)
292 292 This signal is emitted when \a slices have been removed from the series.
293 293 \sa remove()
294 294 */
295 295 /*!
296 296 \qmlsignal PieSeries::onRemoved(PieSlice slice)
297 297 Emitted when \a slice has been removed from the series.
298 298 */
299 299
300 300 /*!
301 301 \fn void QPieSeries::clicked(QPieSlice* slice)
302 302 This signal is emitted when a \a slice has been clicked.
303 303 \sa QPieSlice::clicked()
304 304 */
305 305 /*!
306 306 \qmlsignal PieSeries::onClicked(PieSlice slice)
307 307 This signal is emitted when a \a slice has been clicked.
308 308 */
309 309
310 310 /*!
311 311 \fn void QPieSeries::hovered(QPieSlice* slice, bool state)
312 312 This signal is emitted when user has hovered over or away from the \a slice.
313 313 \a state is true when user has hovered over the slice and false when hover has moved away from the slice.
314 314 \sa QPieSlice::hovered()
315 315 */
316 316 /*!
317 317 \qmlsignal PieSeries::onHovered(PieSlice slice, bool state)
318 318 This signal is emitted when user has hovered over or away from the \a slice. \a state is true when user has hovered
319 319 over the slice and false when hover has moved away from the slice.
320 320 */
321 321
322 322 /*!
323 323 \qmlmethod PieSlice PieSeries::at(int index)
324 324 Returns slice at \a index. Returns null if the index is not valid.
325 325 */
326 326
327 327 /*!
328 328 \qmlmethod PieSlice PieSeries::find(string label)
329 329 Returns the first slice with \a label. Returns null if the index is not valid.
330 330 */
331 331
332 332 /*!
333 333 \qmlmethod PieSlice PieSeries::append(string label, real value)
334 334 Adds a new slice with \a label and \a value to the pie.
335 335 */
336 336
337 337 /*!
338 338 \qmlmethod bool PieSeries::remove(PieSlice slice)
339 339 Removes the \a slice from the pie. Returns true if the removal was successful, false otherwise.
340 340 */
341 341
342 342 /*!
343 343 \qmlmethod PieSeries::clear()
344 344 Removes all slices from the pie.
345 345 */
346 346
347 347 /*!
348 348 Constructs a series object which is a child of \a parent.
349 349 */
350 350 QPieSeries::QPieSeries(QObject *parent) :
351 351 QAbstractSeries(*new QPieSeriesPrivate(this),parent)
352 352 {
353
353 Q_D(QPieSeries);
354 QObject::connect(this,SIGNAL(countChanged()),d,SIGNAL(countChanged()));
354 355 }
355 356
356 357 /*!
357 358 Destroys the series and its slices.
358 359 */
359 360 QPieSeries::~QPieSeries()
360 361 {
361 362 // NOTE: d_prt destroyed by QObject
362 363 }
363 364
364 365 /*!
365 366 Returns QChartSeries::SeriesTypePie.
366 367 */
367 368 QAbstractSeries::SeriesType QPieSeries::type() const
368 369 {
369 370 return QAbstractSeries::SeriesTypePie;
370 371 }
371 372
372 373 /*!
373 374 Appends a single \a slice to the series.
374 375 Slice ownership is passed to the series.
375 376
376 377 Returns true if append was succesfull.
377 378 */
378 379 bool QPieSeries::append(QPieSlice* slice)
379 380 {
380 381 return append(QList<QPieSlice*>() << slice);
381 382 }
382 383
383 384 /*!
384 385 Appends an array of \a slices to the series.
385 386 Slice ownership is passed to the series.
386 387
387 388 Returns true if append was successful.
388 389 */
389 390 bool QPieSeries::append(QList<QPieSlice*> slices)
390 391 {
391 392 Q_D(QPieSeries);
392 393
393 394 if (slices.count() == 0)
394 395 return false;
395 396
396 397 foreach (QPieSlice* s, slices) {
397 398 if (!s || d->m_slices.contains(s))
398 399 return false;
399 400 if (s->series()) // already added to some series
400 401 return false;
401 402 }
402 403
403 404 foreach (QPieSlice* s, slices) {
404 405 s->setParent(this);
405 406 QPieSlicePrivate::fromSlice(s)->m_series = this;
406 407 d->m_slices << s;
407 408 }
408 409
409 410 d->updateDerivativeData();
410 411
411 412 foreach (QPieSlice* s, slices) {
412 413 connect(s, SIGNAL(valueChanged()), d, SLOT(sliceValueChanged()));
413 414 connect(s, SIGNAL(clicked()), d, SLOT(sliceClicked()));
414 415 connect(s, SIGNAL(hovered(bool)), d, SLOT(sliceHovered(bool)));
415
416 connect(s, SIGNAL(labelChanged()), d, SLOT(updateLegendProperties()));
417 connect(s, SIGNAL(penChanged()), d, SLOT(updateLegendProperties()));
418 connect(s, SIGNAL(brushChanged()), d, SLOT(updateLegendProperties()));
419 connect(s, SIGNAL(labelBrushChanged()), d, SLOT(updateLegendProperties()));
420 connect(s, SIGNAL(labelFontChanged()), d, SLOT(updateLegendProperties()));
421 connect(s, SIGNAL(labelFontChanged()), d, SLOT(updateLegendProperties()));
422 416 }
423 417
424 418 emit added(slices);
425 419 emit countChanged();
426 420
427 421 return true;
428 422 }
429 423
430 424 /*!
431 425 Appends a single \a slice to the series and returns a reference to the series.
432 426 Slice ownership is passed to the series.
433 427 */
434 428 QPieSeries& QPieSeries::operator << (QPieSlice* slice)
435 429 {
436 430 append(slice);
437 431 return *this;
438 432 }
439 433
440 434
441 435 /*!
442 436 Appends a single slice to the series with give \a value and \a label.
443 437 Slice ownership is passed to the series.
444 438 */
445 439 QPieSlice* QPieSeries::append(QString label, qreal value)
446 440 {
447 441 QPieSlice* slice = new QPieSlice(label, value);
448 442 append(slice);
449 443 return slice;
450 444 }
451 445
452 446 /*!
453 447 Inserts a single \a slice to the series before the slice at \a index position.
454 448 Slice ownership is passed to the series.
455 449
456 450 Returns true if insert was successful.
457 451 */
458 452 bool QPieSeries::insert(int index, QPieSlice* slice)
459 453 {
460 454 Q_D(QPieSeries);
461 455
462 456 if (index < 0 || index > d->m_slices.count())
463 457 return false;
464 458
465 459 if (!slice || d->m_slices.contains(slice))
466 460 return false;
467 461
468 462 if (slice->series()) // already added to some series
469 463 return false;
470 464
471 465 slice->setParent(this);
472 466 QPieSlicePrivate::fromSlice(slice)->m_series = this;
473 467 d->m_slices.insert(index, slice);
474 468
475 469 d->updateDerivativeData();
476 470
477 471 connect(slice, SIGNAL(valueChanged()), d, SLOT(sliceValueChanged()));
478 472 connect(slice, SIGNAL(clicked()), d, SLOT(sliceClicked()));
479 473 connect(slice, SIGNAL(hovered(bool)), d, SLOT(sliceHovered(bool)));
480 474
481 connect(slice, SIGNAL(labelChanged()), d, SLOT(updateLegendProperties()));
482 connect(slice, SIGNAL(penChanged()), d, SLOT(updateLegendProperties()));
483 connect(slice, SIGNAL(brushChanged()), d, SLOT(updateLegendProperties()));
484 connect(slice, SIGNAL(labelBrushChanged()), d, SLOT(updateLegendProperties()));
485 connect(slice, SIGNAL(labelFontChanged()), d, SLOT(updateLegendProperties()));
486 connect(slice, SIGNAL(labelFontChanged()), d, SLOT(updateLegendProperties()));
487 475
488 476 emit added(QList<QPieSlice*>() << slice);
489 477 emit countChanged();
490 478
491 479 return true;
492 480 }
493 481
494 482 /*!
495 483 Removes a single \a slice from the series and deletes the slice.
496 484
497 485 Do not reference the pointer after this call.
498 486
499 487 Returns true if remove was successful.
500 488 */
501 489 bool QPieSeries::remove(QPieSlice* slice)
502 490 {
503 491 Q_D(QPieSeries);
504 492
505 493 if (!d->m_slices.removeOne(slice))
506 494 return false;
507 495
508 496 d->updateDerivativeData();
509 497
510 498 emit removed(QList<QPieSlice*>() << slice);
511 499 emit countChanged();
512 500
513 501 delete slice;
514 502 slice = 0;
515 503
516 504 return true;
517 505 }
518 506
519 507 /*!
520 508 Takes a single \a slice from the series. Does not destroy the slice object.
521 509
522 510 NOTE: The series remains as the slice's parent object. You must set the
523 511 parent object to take full ownership.
524 512
525 513 Returns true if take was successful.
526 514 */
527 515 bool QPieSeries::take(QPieSlice* slice)
528 516 {
529 517 Q_D(QPieSeries);
530 518
531 519 if (!d->m_slices.removeOne(slice))
532 520 return false;
533 521
534 522 QPieSlicePrivate::fromSlice(slice)->m_series = 0;
535 523 slice->disconnect(d);
536 524
537 525 d->updateDerivativeData();
538 526
539 527 emit removed(QList<QPieSlice*>() << slice);
540 528 emit countChanged();
541 529
542 530 return true;
543 531 }
544 532
545 533 /*!
546 534 Clears all slices from the series.
547 535 */
548 536 void QPieSeries::clear()
549 537 {
550 538 Q_D(QPieSeries);
551 539 if (d->m_slices.count() == 0)
552 540 return;
553 541
554 542 QList<QPieSlice*> slices = d->m_slices;
555 543 foreach (QPieSlice* s, d->m_slices)
556 544 d->m_slices.removeOne(s);
557 545
558 546 d->updateDerivativeData();
559 547
560 548 emit removed(slices);
561 549 emit countChanged();
562 550
563 551 foreach (QPieSlice* s, slices)
564 552 delete s;
565 553 }
566 554
567 555 /*!
568 556 Returns a list of slices that belong to this series.
569 557 */
570 558 QList<QPieSlice*> QPieSeries::slices() const
571 559 {
572 560 Q_D(const QPieSeries);
573 561 return d->m_slices;
574 562 }
575 563
576 564 /*!
577 565 returns the number of the slices in this series.
578 566 */
579 567 int QPieSeries::count() const
580 568 {
581 569 Q_D(const QPieSeries);
582 570 return d->m_slices.count();
583 571 }
584 572
585 573 /*!
586 574 Returns true is the series is empty.
587 575 */
588 576 bool QPieSeries::isEmpty() const
589 577 {
590 578 Q_D(const QPieSeries);
591 579 return d->m_slices.isEmpty();
592 580 }
593 581
594 582 /*!
595 583 Returns the sum of all slice values in this series.
596 584
597 585 \sa QPieSlice::value(), QPieSlice::setValue(), QPieSlice::percentage()
598 586 */
599 587 qreal QPieSeries::sum() const
600 588 {
601 589 Q_D(const QPieSeries);
602 590 return d->m_sum;
603 591 }
604 592
605 593 void QPieSeries::setHoleSize(qreal holeSize)
606 594 {
607 595 Q_D(QPieSeries);
608 596 holeSize = qBound((qreal)0.0, holeSize, (qreal)1.0);
609 597 d->setSizes(holeSize, qMax(d->m_pieRelativeSize, holeSize));
610 598 }
611 599
612 600 qreal QPieSeries::holeSize() const
613 601 {
614 602 Q_D(const QPieSeries);
615 603 return d->m_holeRelativeSize;
616 604 }
617 605
618 606 void QPieSeries::setHorizontalPosition(qreal relativePosition)
619 607 {
620 608 Q_D(QPieSeries);
621 609
622 610 if (relativePosition < 0.0)
623 611 relativePosition = 0.0;
624 612 if (relativePosition > 1.0)
625 613 relativePosition = 1.0;
626 614
627 615 if (!qFuzzyIsNull(d->m_pieRelativeHorPos - relativePosition)) {
628 616 d->m_pieRelativeHorPos = relativePosition;
629 617 emit d->horizontalPositionChanged();
630 618 }
631 619 }
632 620
633 621 qreal QPieSeries::horizontalPosition() const
634 622 {
635 623 Q_D(const QPieSeries);
636 624 return d->m_pieRelativeHorPos;
637 625 }
638 626
639 627 void QPieSeries::setVerticalPosition(qreal relativePosition)
640 628 {
641 629 Q_D(QPieSeries);
642 630
643 631 if (relativePosition < 0.0)
644 632 relativePosition = 0.0;
645 633 if (relativePosition > 1.0)
646 634 relativePosition = 1.0;
647 635
648 636 if (!qFuzzyIsNull(d->m_pieRelativeVerPos - relativePosition)) {
649 637 d->m_pieRelativeVerPos = relativePosition;
650 638 emit d->verticalPositionChanged();
651 639 }
652 640 }
653 641
654 642 qreal QPieSeries::verticalPosition() const
655 643 {
656 644 Q_D(const QPieSeries);
657 645 return d->m_pieRelativeVerPos;
658 646 }
659 647
660 648 void QPieSeries::setPieSize(qreal relativeSize)
661 649 {
662 650 Q_D(QPieSeries);
663 651 relativeSize = qBound((qreal)0.0, relativeSize, (qreal)1.0);
664 652 d->setSizes(qMin(d->m_holeRelativeSize, relativeSize), relativeSize);
665 653
666 654 }
667 655
668 656 qreal QPieSeries::pieSize() const
669 657 {
670 658 Q_D(const QPieSeries);
671 659 return d->m_pieRelativeSize;
672 660 }
673 661
674 662
675 663 void QPieSeries::setPieStartAngle(qreal angle)
676 664 {
677 665 Q_D(QPieSeries);
678 666 if (qFuzzyIsNull(d->m_pieStartAngle - angle))
679 667 return;
680 668 d->m_pieStartAngle = angle;
681 669 d->updateDerivativeData();
682 670 emit d->pieStartAngleChanged();
683 671 }
684 672
685 673 qreal QPieSeries::pieStartAngle() const
686 674 {
687 675 Q_D(const QPieSeries);
688 676 return d->m_pieStartAngle;
689 677 }
690 678
691 679 /*!
692 680 Sets the end angle of the pie.
693 681
694 682 Full pie is 360 degrees where 0 degrees is at 12 a'clock.
695 683
696 684 \a angle must be greater than start angle.
697 685
698 686 \sa pieEndAngle(), pieStartAngle(), setPieStartAngle()
699 687 */
700 688 void QPieSeries::setPieEndAngle(qreal angle)
701 689 {
702 690 Q_D(QPieSeries);
703 691 if (qFuzzyIsNull(d->m_pieEndAngle - angle))
704 692 return;
705 693 d->m_pieEndAngle = angle;
706 694 d->updateDerivativeData();
707 695 emit d->pieEndAngleChanged();
708 696 }
709 697
710 698 /*!
711 699 Returns the end angle of the pie.
712 700
713 701 Full pie is 360 degrees where 0 degrees is at 12 a'clock.
714 702
715 703 \sa setPieEndAngle(), pieStartAngle(), setPieStartAngle()
716 704 */
717 705 qreal QPieSeries::pieEndAngle() const
718 706 {
719 707 Q_D(const QPieSeries);
720 708 return d->m_pieEndAngle;
721 709 }
722 710
723 711 /*!
724 712 Sets the all the slice labels \a visible or invisible.
725 713
726 714 Note that this affects only the current slices in the series.
727 715 If user adds a new slice the default label visibility is false.
728 716
729 717 \sa QPieSlice::isLabelVisible(), QPieSlice::setLabelVisible()
730 718 */
731 719 void QPieSeries::setLabelsVisible(bool visible)
732 720 {
733 721 Q_D(QPieSeries);
734 722 foreach (QPieSlice* s, d->m_slices)
735 723 s->setLabelVisible(visible);
736 724 }
737 725
738 726 /*!
739 727 Sets the all the slice labels \a position
740 728
741 729 Note that this affects only the current slices in the series.
742 730 If user adds a new slice the default label position is LabelOutside
743 731
744 732 \sa QPieSlice::labelPosition(), QPieSlice::setLabelPosition()
745 733 */
746 734 void QPieSeries::setLabelsPosition(QPieSlice::LabelPosition position)
747 735 {
748 736 Q_D(QPieSeries);
749 737 foreach (QPieSlice* s, d->m_slices)
750 738 s->setLabelPosition(position);
751 739 }
752 740
753 741 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
754 742
755 743
756 744 QPieSeriesPrivate::QPieSeriesPrivate(QPieSeries *parent) :
757 745 QAbstractSeriesPrivate(parent),
758 746 m_pieRelativeHorPos(0.5),
759 747 m_pieRelativeVerPos(0.5),
760 748 m_pieRelativeSize(0.7),
761 749 m_pieStartAngle(0),
762 750 m_pieEndAngle(360),
763 751 m_sum(0),
764 752 m_holeRelativeSize(0.0)
765 753 {
766 754 }
767 755
768 756 QPieSeriesPrivate::~QPieSeriesPrivate()
769 757 {
770 758 }
771 759
772 760 void QPieSeriesPrivate::updateDerivativeData()
773 761 {
774 762 // calculate sum of all slices
775 763 qreal sum = 0;
776 764 foreach (QPieSlice* s, m_slices)
777 765 sum += s->value();
778 766
779 767 if (!qFuzzyIsNull(m_sum - sum)) {
780 768 m_sum = sum;
781 769 emit q_func()->sumChanged();
782 770 }
783 771
784 772 // nothing to show..
785 773 if (qFuzzyIsNull(m_sum))
786 774 return;
787 775
788 776 // update slice attributes
789 777 qreal sliceAngle = m_pieStartAngle;
790 778 qreal pieSpan = m_pieEndAngle - m_pieStartAngle;
791 779 QVector<QPieSlice*> changed;
792 780 foreach (QPieSlice* s, m_slices) {
793 781 QPieSlicePrivate *d = QPieSlicePrivate::fromSlice(s);
794 782 d->setPercentage(s->value() / m_sum);
795 783 d->setStartAngle(sliceAngle);
796 784 d->setAngleSpan(pieSpan * s->percentage());
797 785 sliceAngle += s->angleSpan();
798 786 }
799 787
800 788
801 789 emit calculatedDataChanged();
802 790 }
803 791
804 792 void QPieSeriesPrivate::setSizes(qreal innerSize, qreal outerSize)
805 793 {
806 794 bool changed = false;
807 795
808 796 if (!qFuzzyIsNull(m_holeRelativeSize - innerSize)) {
809 797 m_holeRelativeSize = innerSize;
810 798 changed = true;
811 799 }
812 800
813 801 if (!qFuzzyIsNull(m_pieRelativeSize - outerSize)) {
814 802 m_pieRelativeSize = outerSize;
815 803 changed = true;
816 804 }
817 805
818 806 if (changed)
819 807 emit pieSizeChanged();
820 808 }
821 809
822 810 QPieSeriesPrivate* QPieSeriesPrivate::fromSeries(QPieSeries *series)
823 811 {
824 812 return series->d_func();
825 813 }
826 814
827 815 void QPieSeriesPrivate::sliceValueChanged()
828 816 {
829 817 Q_ASSERT(m_slices.contains(qobject_cast<QPieSlice *>(sender())));
830 818 updateDerivativeData();
831 819 }
832 820
833 821 void QPieSeriesPrivate::sliceClicked()
834 822 {
835 823 QPieSlice* slice = qobject_cast<QPieSlice *>(sender());
836 824 Q_ASSERT(m_slices.contains(slice));
837 825 Q_Q(QPieSeries);
838 826 emit q->clicked(slice);
839 827 }
840 828
841 829 void QPieSeriesPrivate::sliceHovered(bool state)
842 830 {
843 831 QPieSlice* slice = qobject_cast<QPieSlice *>(sender());
844 832 Q_ASSERT(m_slices.contains(slice));
845 833 Q_Q(QPieSeries);
846 834 emit q->hovered(slice, state);
847 835 }
848 836
849 void QPieSeriesPrivate::updateLegendProperties()
850 {
851 // This slot listens to all properties of slices, which may interest legend and signals it.
852 Q_Q(QPieSeries);
853 emit legendPropertiesUpdated(q);
854 }
855
856 837 void QPieSeriesPrivate::scaleDomain(Domain& domain)
857 838 {
858 839 Q_UNUSED(domain);
859 840 // does not apply to pie
860 841 }
861 842
862 843 ChartElement* QPieSeriesPrivate::createGraphics(ChartPresenter* presenter)
863 844 {
864 845 Q_Q(QPieSeries);
865 846 PieChartItem* pie = new PieChartItem(q,presenter);
866 847 if(presenter->animationOptions().testFlag(QChart::SeriesAnimations)) {
867 848 pie->setAnimation(new PieAnimation(pie));
868 849 }
869 850 presenter->chartTheme()->decorate(q, presenter->dataSet()->seriesIndex(q));
870 851 return pie;
871 852 }
872 853
873 854 QList<LegendMarker*> QPieSeriesPrivate::createLegendMarker(QLegend* legend)
874 855 {
875 856 Q_Q(QPieSeries);
876 857 QList<LegendMarker*> markers;
877 858 foreach(QPieSlice* slice, q->slices()) {
878 859 PieLegendMarker* marker = new PieLegendMarker(q,slice,legend);
879 860 markers << marker;
880 861 }
881 862 return markers;
882 863 }
883 864
884 865 void QPieSeriesPrivate::initializeAxis(QAbstractAxis* axis)
885 866 {
886 867 Q_UNUSED(axis);
887 868 }
888 869
889 870 QAbstractAxis::AxisType QPieSeriesPrivate::defaultAxisType(Qt::Orientation orientation) const
890 871 {
891 872 Q_UNUSED(orientation);
892 873 return QAbstractAxis::AxisTypeNoAxis;
893 874 }
894 875
895 876 #include "moc_qpieseries.cpp"
896 877 #include "moc_qpieseries_p.cpp"
897 878
898 879 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,89 +1,88
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 // W A R N I N G
22 22 // -------------
23 23 //
24 24 // This file is not part of the QtCommercial Chart API. It exists purely as an
25 25 // implementation detail. This header file may change from version to
26 26 // version without notice, or even be removed.
27 27 //
28 28 // We mean it.
29 29
30 30 #ifndef QPIESERIES_P_H
31 31 #define QPIESERIES_P_H
32 32
33 33 #include "qpieseries.h"
34 34 #include "qabstractseries_p.h"
35 35
36 36 QTCOMMERCIALCHART_BEGIN_NAMESPACE
37 37 class QLegendPrivate;
38 38
39 39 class QPieSeriesPrivate : public QAbstractSeriesPrivate
40 40 {
41 41 Q_OBJECT
42 42
43 43 public:
44 44 QPieSeriesPrivate(QPieSeries *parent);
45 45 ~QPieSeriesPrivate();
46 46
47 47 void scaleDomain(Domain& domain);
48 48 ChartElement* createGraphics(ChartPresenter *presenter);
49 49 QList<LegendMarker*> createLegendMarker(QLegend *legend);
50 50 void initializeAxis(QAbstractAxis* axis);
51 51 QAbstractAxis::AxisType defaultAxisType(Qt::Orientation orientation) const;
52 52
53 53 void updateDerivativeData();
54 54 void setSizes(qreal innerSize, qreal outerSize);
55 55
56 56 static QPieSeriesPrivate* fromSeries(QPieSeries *series);
57 57
58 58 signals:
59 59 void calculatedDataChanged();
60 60 void pieSizeChanged();
61 61 void pieStartAngleChanged();
62 62 void pieEndAngleChanged();
63 63 void horizontalPositionChanged();
64 64 void verticalPositionChanged();
65 65
66 66 public Q_SLOTS:
67 67 void sliceValueChanged();
68 68 void sliceClicked();
69 69 void sliceHovered(bool state);
70 void updateLegendProperties();
71 70
72 71 private:
73 72 QList<QPieSlice*> m_slices;
74 73 qreal m_pieRelativeHorPos;
75 74 qreal m_pieRelativeVerPos;
76 75 qreal m_pieRelativeSize;
77 76 qreal m_pieStartAngle;
78 77 qreal m_pieEndAngle;
79 78 qreal m_sum;
80 79 qreal m_holeRelativeSize;
81 80
82 81 private:
83 82 friend class QLegendPrivate;
84 83 Q_DECLARE_PUBLIC(QPieSeries)
85 84 };
86 85
87 86 QTCOMMERCIALCHART_END_NAMESPACE
88 87
89 88 #endif // QPIESERIES_P_H
@@ -1,75 +1,76
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 // W A R N I N G
22 22 // -------------
23 23 //
24 24 // This file is not part of the QtCommercial Chart API. It exists purely as an
25 25 // implementation detail. This header file may change from version to
26 26 // version without notice, or even be removed.
27 27 //
28 28 // We mean it.
29 29
30 30 #ifndef QABSTRACTSERIES_P_H
31 31 #define QABSTRACTSERIES_P_H
32 32
33 33 #include "qabstractseries.h"
34 34
35 35 QTCOMMERCIALCHART_BEGIN_NAMESPACE
36 36
37 37 class Domain;
38 38 class ChartPresenter;
39 39 class ChartElement;
40 40 class LegendMarker;
41 41 class QLegend;
42 42 class ChartDataSet;
43 43 class QAbstractAxis;
44 44
45 45 class QAbstractSeriesPrivate : public QObject
46 46 {
47 47 Q_OBJECT
48 48 public:
49 49 QAbstractSeriesPrivate(QAbstractSeries *q);
50 50 ~QAbstractSeriesPrivate();
51 51
52 52 virtual void scaleDomain(Domain& domain) = 0;
53 53 virtual ChartElement* createGraphics(ChartPresenter* presenter) = 0;
54 54 virtual QList<LegendMarker*> createLegendMarker(QLegend* legend) = 0;
55 55 virtual void initializeAxis(QAbstractAxis* axis) = 0;
56 56 virtual QAbstractAxis::AxisType defaultAxisType(Qt::Orientation) const = 0;
57 57
58 58 Q_SIGNALS:
59 void legendPropertiesUpdated(QAbstractSeries* series);
59 void countChanged();
60 60
61 61 protected:
62 62 QAbstractSeries *q_ptr;
63 63 QChart *m_chart;
64 64 ChartDataSet *m_dataset;
65 65 QString m_name;
66 66 bool m_visible;
67 67 qreal m_opacity;
68 68
69 69 friend class QAbstractSeries;
70 70 friend class ChartDataSet;
71 friend class QLegendPrivate;
71 72 };
72 73
73 74 QTCOMMERCIALCHART_END_NAMESPACE
74 75
75 76 #endif
General Comments 0
You need to be logged in to leave comments. Login now