1 /*
2 * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3 *
4 * See the LICENSE file for terms of use.
5 */
6
7 #include <cmath>
8 #include <cstdio>
9
10 #include "Wt/Chart/WAbstractChartModel.h"
11 #include "Wt/Chart/WPieChart.h"
12 #include "Wt/Chart/WStandardPalette.h"
13
14 #include "Wt/WContainerWidget.h"
15 #include "Wt/WCssDecorationStyle.h"
16 #include "Wt/WText.h"
17 #include "Wt/WPainter.h"
18 #include "Wt/WPolygonArea.h"
19 #include "Wt/WApplication.h"
20 #include "Wt/WEnvironment.h"
21 #include "Wt/WException.h"
22 #include "Wt/WModelIndex.h"
23
24 #include "WebUtils.h"
25
26 #ifndef M_PI
27 #define M_PI 3.14159265358979323846
28 #endif
29
30 namespace Wt {
31 namespace Chart {
32
PieData()33 WPieChart::PieData::PieData()
34 : customBrush(false),
35 explode(0)
36 { }
37
WPieChart()38 WPieChart::WPieChart()
39 : labelsColumn_(-1),
40 dataColumn_(-1),
41 height_(0.0),
42 startAngle_(45),
43 avoidLabelRendering_(0.0),
44 labelOptions_(None),
45 shadow_(false),
46 labelFormat_(WString::fromUTF8("%.3g%%"))
47 {
48 setPalette
49 (std::make_shared<WStandardPalette>(PaletteFlavour::Neutral));
50 setPlotAreaPadding(5);
51 }
52
setLabelsColumn(int modelColumn)53 void WPieChart::setLabelsColumn(int modelColumn)
54 {
55 if (labelsColumn_ != modelColumn) {
56 labelsColumn_ = modelColumn;
57 update();
58 }
59 }
60
setLabelFormat(const WString & format)61 void WPieChart::setLabelFormat(const WString& format)
62 {
63 labelFormat_ = format;
64 update();
65 }
66
labelFormat()67 WString WPieChart::labelFormat() const
68 {
69 return labelFormat_;
70 }
71
setDataColumn(int modelColumn)72 void WPieChart::setDataColumn(int modelColumn)
73 {
74 if (dataColumn_ != modelColumn) {
75 dataColumn_ = modelColumn;
76 update();
77 }
78 }
79
setBrush(int modelRow,const WBrush & brush)80 void WPieChart::setBrush(int modelRow, const WBrush& brush)
81 {
82 pie_[modelRow].customBrush = true;
83 pie_[modelRow].brush = brush;
84 update();
85 }
86
brush(int modelRow)87 WBrush WPieChart::brush(int modelRow) const
88 {
89 if (pie_[modelRow].customBrush)
90 return pie_[modelRow].brush;
91 else
92 return palette()->brush(modelRow);
93 }
94
setExplode(int modelRow,double factor)95 void WPieChart::setExplode(int modelRow, double factor)
96 {
97 pie_[modelRow].explode = factor;
98 update();
99 }
100
explode(int modelRow)101 double WPieChart::explode(int modelRow) const
102 {
103 return pie_[modelRow].explode;
104 }
105
setPerspectiveEnabled(bool enabled,double height)106 void WPieChart::setPerspectiveEnabled(bool enabled, double height)
107 {
108 if ((!enabled && height_ != 0.0) || height_ != height) {
109 height_ = enabled ? height : 0.0;
110 update();
111 }
112 }
113
setShadowEnabled(bool enabled)114 void WPieChart::setShadowEnabled(bool enabled)
115 {
116 if (shadow_ != enabled) {
117 shadow_ = enabled;
118 update();
119 }
120 }
121
setStartAngle(double startAngle)122 void WPieChart::setStartAngle(double startAngle)
123 {
124 if (startAngle_ != startAngle) {
125 startAngle_ = startAngle;
126 update();
127 }
128 }
129
setAvoidLabelRendering(double avoidLabelRendering)130 void WPieChart::setAvoidLabelRendering(double avoidLabelRendering)
131 {
132 if (avoidLabelRendering_ != avoidLabelRendering) {
133 avoidLabelRendering_ = avoidLabelRendering;
134 update();
135 }
136 }
137
setDisplayLabels(WFlags<LabelOption> options)138 void WPieChart::setDisplayLabels(WFlags<LabelOption> options)
139 {
140 labelOptions_ = options;
141
142 update();
143 }
144
145 std::unique_ptr<WWidget>
createLegendItemWidget(int index,WFlags<LabelOption> options)146 WPieChart::createLegendItemWidget(int index, WFlags<LabelOption> options)
147 {
148 std::unique_ptr<WContainerWidget> legendItem(new WContainerWidget());
149 legendItem->setPadding(4);
150
151 auto colorText = legendItem->addWidget(std::make_unique<WText>());
152 colorText->setPadding(10, WFlags<Side>(Side::Left) | Side::Right);
153 colorText->decorationStyle().setBackgroundColor(brush(index).color());
154
155 if (WApplication::instance()->environment().agentIsIE())
156 colorText->setAttributeValue("style", "zoom: 1;");
157
158 double total = 0;
159
160 if (dataColumn_ != -1)
161 for (int i = 0; i < model()->rowCount(); ++i) {
162 double v = model()->data(i, dataColumn_);
163 if (!Utils::isNaN(v))
164 total += v;
165 }
166
167 double value = model()->data(index, dataColumn_);
168 if (!Utils::isNaN(value)) {
169 WString label = labelText(index, value, total, options);
170 if (!label.empty()) {
171 std::unique_ptr<WText> l(new WText(label));
172 l->setPadding(5, Side::Left);
173 l->setToolTip(model()->toolTip(index, dataColumn_));
174 legendItem->addWidget(std::move(l));
175 }
176 }
177
178 return std::move(legendItem);
179 }
180
paint(WPainter & painter,const WRectF & rectangle)181 void WPieChart::paint(WPainter& painter, const WRectF& rectangle) const
182 {
183 double total = 0;
184
185 if (dataColumn_ != -1)
186 for (int i = 0; i < model()->rowCount(); ++i) {
187 double v = model()->data(i, dataColumn_);
188 if (!Utils::isNaN(v))
189 total += v;
190 }
191
192 if (!painter.isActive())
193 throw WException("WPieChart::paint(): painter is not active.");
194
195 WRectF rect = rectangle;
196
197 if (rect.isNull() || rect.isEmpty())
198 rect = painter.window();
199
200 rect.setX(rect.x() + plotAreaPadding(Side::Left));
201 rect.setY(rect.y() + plotAreaPadding(Side::Top));
202 rect.setWidth(rect.width() - plotAreaPadding(Side::Left) - plotAreaPadding(Side::Right));
203 rect.setHeight(rect.height() - plotAreaPadding(Side::Top)
204 - plotAreaPadding(Side::Bottom));
205
206 double side = std::min(rect.width(), rect.height());
207
208 painter.save();
209 painter.translate(rect.left() + (rect.width() - side)/2,
210 rect.top() + (rect.height() - side)/2);
211
212 if (!title().empty())
213 painter.translate(0, 15);
214
215 double cx = std::floor(side/2) + 0.5;
216 double cy = cx;
217 double r = (int)(side/2 + 0.5);
218 double h = height_ * r;
219
220 painter.save();
221 if (h > 0.0) {
222 painter.translate(0, r/2 - h/4);
223 painter.scale(1, 0.5);
224 }
225
226 drawPie(painter, cx, cy, r, h, total);
227 painter.restore();
228
229 painter.translate(0, -h/4);
230 if (!labelOptions_.empty()) {
231 if (total != 0) {
232 double currentAngle = startAngle_;
233
234 for (int i = 0; i < model()->rowCount(); ++i) {
235 double v = model()->data(i, dataColumn_);
236 if (Utils::isNaN(v))
237 continue;
238
239 double spanAngle = -v / total * 360;
240 double midAngle = currentAngle + spanAngle / 2.0;
241 double endAngle = currentAngle + spanAngle;
242 if (endAngle < 0)
243 endAngle += 360;
244 if (midAngle < 0)
245 midAngle += 360;
246
247 double width = 200;
248 double height = 30;
249 double left;
250 double top;
251
252 double f;
253 if (labelOptions_.test(LabelOption::Outside))
254 f = pie_[i].explode + 1.1;
255 else
256 f = pie_[i].explode + 0.7;
257
258 double px = cx + f * r * std::cos(-midAngle / 180.0 * M_PI);
259 double py = cy + f * r * std::sin(-midAngle / 180.0 * M_PI)
260 * (h > 0 ? 0.5 : 1);
261
262 WFlags<AlignmentFlag> alignment;
263
264 WColor c = painter.pen().color();
265 if (labelOptions_.test(LabelOption::Outside)) {
266 if (midAngle < 90) {
267 left = px;
268 top = py - height;
269 alignment = WFlags<AlignmentFlag>(AlignmentFlag::Left) | AlignmentFlag::Bottom;
270 } else if (midAngle < 180) {
271 left = px - width;
272 top = py - height;
273 alignment = WFlags<AlignmentFlag>(AlignmentFlag::Right) | AlignmentFlag::Bottom;
274 } else if (midAngle < 270) {
275 left = px - width;
276 top = py + h/2;
277 alignment = WFlags<AlignmentFlag>(AlignmentFlag::Right) | AlignmentFlag::Top;
278 } else {
279 left = px;
280 top = py + h/2;
281 alignment = WFlags<AlignmentFlag>(AlignmentFlag::Left) | AlignmentFlag::Top;
282 }
283 } else {
284 left = px - width/2;
285 top = py - height/2;
286 alignment = WFlags<AlignmentFlag>(AlignmentFlag::Center) | AlignmentFlag::Middle;
287 c = palette()->fontColor(i);
288 }
289
290 if ((v / total * 100) >= avoidLabelRendering_) {
291 painter.setPen(WPen(c));
292 drawLabel(&painter, WRectF(left, top, width, height),
293 alignment, labelText(i, v, total, labelOptions_), i);
294 }
295
296 currentAngle = endAngle;
297 }
298 }
299 }
300
301 if (!title().empty()) {
302 WFont oldFont = painter.font();
303 painter.setFont(titleFont());
304 painter.drawText(cx - 50, cy - r, 100, 50,
305 WFlags<AlignmentFlag>(AlignmentFlag::Center) | AlignmentFlag::Top, title());
306 painter.setFont(oldFont);
307 }
308
309 painter.restore();
310 }
311
createLabelWidget(std::unique_ptr<WWidget> textWidget,WPainter * painter,const WRectF & rect,Wt::WFlags<Wt::AlignmentFlag> alignmentFlags)312 std::unique_ptr<WContainerWidget> WPieChart::createLabelWidget(std::unique_ptr<WWidget> textWidget,
313 WPainter* painter, const WRectF& rect,
314 Wt::WFlags<Wt::AlignmentFlag> alignmentFlags) const
315 {
316 AlignmentFlag verticalAlign = alignmentFlags & AlignVerticalMask;
317 AlignmentFlag horizontalAlign = alignmentFlags & AlignHorizontalMask;
318
319 // style parent container
320 auto c = std::make_unique<WContainerWidget>();
321 WWidget *tw = textWidget.get();
322 c->addWidget(std::move(textWidget));
323 c->setPositionScheme(PositionScheme::Absolute);
324 c->setAttributeValue("style", "display: flex;");
325
326 const WRectF& normRect = rect.normalized();
327 Wt::WTransform t = painter->worldTransform();
328 Wt::WPointF p = t.map(Wt::WPointF(normRect.left(), normRect.top()));
329
330 c->setWidth(WLength(normRect.width() * t.m11()));
331 c->setHeight(WLength(normRect.height() * t.m22()));
332 c->decorationStyle().setFont(painter->font());
333
334 std::stringstream containerStyle;
335 containerStyle << "top:" << p.y() << "px; left:" << p.x() << "px; "
336 << "display: flex;";
337
338 switch (horizontalAlign) {
339 case AlignmentFlag::Left: containerStyle << " justify-content: flex-start;"; break;
340 case AlignmentFlag::Right: containerStyle << " justify-content: flex-end;"; break;
341 case AlignmentFlag::Center: containerStyle << " justify-content: center;"; break;
342 default: break;
343 }
344
345 c->setAttributeValue("style", containerStyle.str());
346
347 // style inner text.
348
349 std::stringstream innerStyle;
350
351 switch (verticalAlign) {
352 case AlignmentFlag::Top: innerStyle << "align-self: flex-start;"; break;
353 case AlignmentFlag::Bottom: innerStyle << "align-self: flex-end;"; break;
354 case AlignmentFlag::Middle: innerStyle << " align-self: center;"; break;
355 default: break;
356 }
357
358 tw->setAttributeValue("style", innerStyle.str());
359
360 return c;
361 }
362
drawLabel(WPainter * painter,const WRectF & rect,WFlags<AlignmentFlag> alignmentFlags,const WString & text,int row)363 void WPieChart::drawLabel(WPainter* painter, const WRectF& rect,
364 WFlags<AlignmentFlag> alignmentFlags,
365 const WString& text, int row) const
366 {
367 painter->drawText(rect, alignmentFlags, text);
368 }
369
labelText(int index,double v,double total,WFlags<LabelOption> options)370 WString WPieChart::labelText(int index, double v, double total,
371 WFlags<LabelOption> options) const
372 {
373 WString text;
374
375 if (options.test(LabelOption::TextLabel))
376 if (labelsColumn_ != -1)
377 text += model()->displayData(index, labelsColumn_);
378
379 if (options.test(LabelOption::TextPercentage)) {
380 std::string label;
381 double u = v / total * 100;
382
383 std::string format = labelFormat().toUTF8();
384 if (format.empty())
385 label = WLocale::currentLocale().toString(u).toUTF8() + "%";
386 else {
387 #ifndef WT_TARGET_JAVA
388 char buf[30];
389 #else
390 char *buf = 0;
391 #endif
392
393 #ifdef WT_TARGET_JAVA
394 buf =
395 #endif // WT_TARGET_JAVA
396 std::sprintf(buf, format.c_str(), u);
397 label = buf;
398 }
399
400 if (!text.empty())
401 text += ": ";
402 text += WString::fromUTF8(label);
403 }
404
405 return text;
406 }
407
setShadow(WPainter & painter)408 void WPieChart::setShadow(WPainter& painter) const
409 {
410 painter.setShadow(WShadow(5, 15, WColor(0, 0, 0, 20), 40));
411 }
412
drawPie(WPainter & painter,double cx,double cy,double r,double h,double total)413 void WPieChart::drawPie(WPainter& painter, double cx, double cy,
414 double r, double h, double total) const
415 {
416 /*
417 * Draw sides where applicable
418 */
419 if (h > 0) {
420 if (total == 0) {
421 if (shadow_)
422 setShadow(painter);
423 drawOuter(painter, cx, cy, r, 0, -180, h);
424 if (shadow_)
425 painter.setShadow(WShadow());
426 } else {
427 if (shadow_) {
428 setShadow(painter);
429 painter.setBrush(WBrush(StandardColor::Black));
430 drawSlices(painter, cx, cy + h, r, total, true);
431 painter.setShadow(WShadow());
432 }
433
434 /*
435 * Pre-processing: determine start and mid angles of each pie,
436 * and get the index of the one that contains 90 degrees (which is
437 * the one at the back
438 */
439 #ifndef WT_TARGET_JAVA
440 std::vector<double> startAngles(model()->rowCount());
441 std::vector<double> midAngles(model()->rowCount());
442 #else
443 std::vector<double> startAngles, midAngles;
444 startAngles.insert(startAngles.end(), model()->rowCount(), 0.0);
445 midAngles.insert(midAngles.end(), model()->rowCount(), 0.0);
446 #endif // WT_TARGET_JAVA
447
448 int index90 = 0;
449
450 double currentAngle = startAngle_;
451 for (int i = 0; i < model()->rowCount(); ++i) {
452 startAngles[i] = currentAngle;
453
454 double v = model()->data(i, dataColumn_);
455 if (Utils::isNaN(v))
456 continue;
457
458 double spanAngle = -v / total * 360;
459 midAngles[i] = currentAngle + spanAngle / 2.0;
460
461 double endAngle = currentAngle + spanAngle;
462
463 double to90 = currentAngle - 90;
464 if (to90 < 0)
465 to90 += 360;
466
467 if (spanAngle <= -to90)
468 index90 = i;
469
470 if (endAngle < 0)
471 endAngle += 360;
472
473 currentAngle = endAngle;
474 }
475
476 /*
477 * Draw clock wise side
478 */
479 for (int j = 0; j < model()->rowCount(); ++j) {
480 int i = (index90 + j) % model()->rowCount();
481
482 double v = model()->data(i, dataColumn_);
483 if (Utils::isNaN(v))
484 continue;
485
486 double midAngle = midAngles[i];
487 double endAngle = startAngles[(i + 1) % model()->rowCount()];
488
489 int n = nextIndex(i);
490
491 bool visible = (endAngle <= 90) || (endAngle >= 270);
492
493 bool drawS2 = visible
494 && ((pie_[i].explode > 0.0) || (pie_[n].explode > 0.0));
495
496 if (drawS2) {
497 double pcx = cx + r * pie_[i].explode * std::cos(-midAngle / 180.0 * M_PI);
498 double pcy = cy + r * pie_[i].explode * std::sin(-midAngle / 180.0 * M_PI);
499
500 painter.setBrush(darken(brush(i)));
501
502 drawSide(painter, pcx, pcy, r, endAngle, h);
503 }
504
505 if (!visible)
506 break;
507 }
508
509 /*
510 * Draw counter-clock wise side
511 */
512 for (int j = model()->rowCount(); j > 0; --j) {
513 int i = (index90 + j) % model()->rowCount();
514
515 double v = model()->data(i, dataColumn_);
516 if (Utils::isNaN(v))
517 continue;
518
519 double startAngle = startAngles[i];
520 double midAngle = midAngles[i];
521
522 int p = prevIndex(i);
523
524 bool visible = (startAngle >= 90) && (startAngle <= 270);
525
526 bool drawS1 = visible
527 && ((pie_[i].explode > 0.0) || (pie_[p].explode > 0.0));
528
529 if (drawS1) {
530 double pcx = cx + r * pie_[i].explode * std::cos(-midAngle / 180.0 * M_PI);
531 double pcy = cy + r * pie_[i].explode * std::sin(-midAngle / 180.0 * M_PI);
532
533 painter.setBrush(darken(brush(i)));
534 drawSide(painter, pcx, pcy, r, startAngle, h);
535 }
536
537 if (!visible)
538 break;
539 }
540
541 /*
542 * LabelOption::Outside
543 */
544 for (int j = 0; j < model()->rowCount(); ++j) {
545 int i = (index90 + j) % model()->rowCount();
546
547 double v = model()->data(i, dataColumn_);
548 if (Utils::isNaN(v))
549 continue;
550
551 double startAngle = startAngles[i];
552 double midAngle = midAngles[i];
553 double endAngle = startAngles[(i + 1) % model()->rowCount()];
554
555 double spanAngle = endAngle - startAngle;
556
557 if (spanAngle > 0)
558 spanAngle -= 360;
559
560 bool drawBorder = startAngle > 180 || endAngle > 180
561 || spanAngle < -180 || model()->rowCount() == 1;
562
563 if (drawBorder) {
564 painter.setBrush(darken(brush(i)));
565
566 double pcx = cx + r * pie_[i].explode * std::cos(-midAngle / 180.0 * M_PI);
567 double pcy = cy + r * pie_[i].explode * std::sin(-midAngle / 180.0 * M_PI);
568
569 double a1 = (startAngle < 180 ? 360 : startAngle);
570 double a2 = (endAngle < 180 ? 180 : endAngle);
571
572 drawOuter(painter, pcx, pcy, r, a1, a2, h);
573 }
574 }
575 }
576 }
577
578 /*
579 * Draw top
580 */
581 if (total == 0)
582 painter.drawArc(cx - r, cy - r, r*2, r*2, 0, 16*360);
583 else
584 drawSlices(painter, cx, cy, r, total, false);
585 }
586
drawSlices(WPainter & painter,double cx,double cy,double r,double total,bool shadow)587 void WPieChart::drawSlices(WPainter& painter,
588 double cx, double cy, double r, double total,
589 bool shadow) const
590 {
591 double currentAngle = startAngle_;
592
593 for (int i = 0; i < model()->rowCount(); ++i) {
594 double v = model()->data(i, dataColumn_);
595 if (Utils::isNaN(v))
596 continue;
597
598 double spanAngle = -v / total * 360;
599 double midAngle = currentAngle + spanAngle / 2.0;
600
601 double pcx = cx + r * pie_[i].explode * std::cos(-midAngle / 180.0 * M_PI);
602 double pcy = cy + r * pie_[i].explode * std::sin(-midAngle / 180.0 * M_PI);
603
604 if (!shadow)
605 painter.setBrush(brush(i));
606
607 if (v/total != 1.0)
608 painter.drawPie(pcx - r, pcy - r, r*2, r*2,
609 static_cast<int>(currentAngle * 16),
610 static_cast<int>(spanAngle * 16));
611 else
612 painter.drawEllipse(pcx - r, pcy - r, r*2, r*2);
613
614 /*
615 * See if we need to add an interactive area
616 */
617 if (!shadow) {
618 WString toolTip = model()->toolTip(i, dataColumn_);
619 WLink *link = model()->link(i, dataColumn_);
620 if (!toolTip.empty() || link) {
621 const int SEGMENT_ANGLE = 20;
622
623 std::unique_ptr<WPolygonArea> area(new WPolygonArea());
624 WTransform t = painter.worldTransform();
625
626 area->addPoint(t.map(WPointF(pcx, pcy)));
627
628 double sa = std::fabs(spanAngle);
629
630 for (double d = 0; d < sa; d += SEGMENT_ANGLE) {
631 double a;
632 if (spanAngle < 0)
633 a = currentAngle - d;
634 else
635 a = currentAngle + d;
636 area->addPoint(t.map(WPointF(pcx + r * std::cos(-a / 180.0 * M_PI),
637 pcy + r * std::sin(-a / 180.0 * M_PI))));
638 }
639
640 double a = currentAngle + spanAngle;
641 area->addPoint(t.map(WPointF(pcx + r * std::cos(-a / 180.0 * M_PI),
642 pcy + r * std::sin(-a / 180.0 * M_PI))));
643
644 area->setToolTip(toolTip);
645 if (link)
646 area->setLink(*link);
647
648 addDataPointArea(i, dataColumn_, std::move(area));
649 }
650 }
651
652 double endAngle = currentAngle + spanAngle;
653 if (endAngle < 0)
654 endAngle += 360;
655
656 currentAngle = endAngle;
657 }
658 }
659
addDataPointArea(int row,int column,std::unique_ptr<WAbstractArea> area)660 void WPieChart::addDataPointArea(int row, int column,
661 std::unique_ptr<WAbstractArea> area) const
662 {
663 (const_cast<WPieChart *>(this))->addArea(std::move(area));
664 }
665
darken(const WBrush & brush)666 WBrush WPieChart::darken(const WBrush& brush)
667 {
668 WBrush result = brush;
669 WColor c = result.color();
670
671 c.setRgb(c.red() * 3/4, c.green() * 3/4, c.blue() * 3/4, c.alpha());
672
673 result.setColor(c);
674
675 return result;
676 }
677
drawSide(WPainter & painter,double pcx,double pcy,double r,double angle,double h)678 void WPieChart::drawSide(WPainter& painter, double pcx, double pcy, double r,
679 double angle, double h) const
680 {
681 WPainterPath path;
682 path.arcMoveTo(pcx - r, pcy - r, 2 * r, 2 * r, angle);
683 path.lineTo(path.currentPosition().x(), path.currentPosition().y() + h);
684 path.lineTo(pcx, pcy + h);
685 path.lineTo(pcx, pcy);
686 path.closeSubPath();
687
688 painter.drawPath(path);
689 }
690
drawOuter(WPainter & painter,double pcx,double pcy,double r,double a1,double a2,double h)691 void WPieChart::drawOuter(WPainter& painter, double pcx, double pcy, double r,
692 double a1, double a2, double h) const
693 {
694 WPainterPath path;
695 path.arcMoveTo(pcx - r, pcy - r, 2 * r, 2 * r, a1);
696 path.lineTo(path.currentPosition().x(), path.currentPosition().y() + h);
697 path.arcTo(pcx, pcy + h, r, a1, a2 - a1);
698 path.arcTo(pcx, pcy, r, a2, a1 - a2);
699 path.closeSubPath();
700
701 painter.drawPath(path);
702 }
703
paintEvent(WPaintDevice * paintDevice)704 void WPieChart::paintEvent(WPaintDevice *paintDevice)
705 {
706 const std::vector<WAbstractArea *> allAreas = areas();
707 for (auto area : allAreas) {
708 removeArea(area);
709 }
710
711 WPainter painter(paintDevice);
712 painter.setRenderHint(RenderHint::Antialiasing, true);
713 paint(painter);
714 }
715
nextIndex(int i)716 int WPieChart::nextIndex(int i) const
717 {
718 int r = model()->rowCount();
719 for (int n = (i + 1) % r; n != i; n = (n + 1) % r) {
720 double v = model()->data(n, dataColumn_);
721 if (!Utils::isNaN(v))
722 return n;
723 }
724
725 return i;
726 }
727
prevIndex(int i)728 int WPieChart::prevIndex(int i) const
729 {
730 int r = model()->rowCount();
731 for (int p = i - 1; p != i; --p) {
732 if (p < 0)
733 p += r;
734 double v = model()->data(p, dataColumn_);
735 if (!Utils::isNaN(v))
736 return p;
737 }
738
739 return i;
740 }
741
modelReset()742 void WPieChart::modelReset()
743 {
744 if (model()->rowCount() != (int)pie_.size())
745 modelChanged();
746 else
747 update();
748 }
749
modelChanged()750 void WPieChart::modelChanged()
751 {
752 pie_.clear();
753 pie_.insert(pie_.begin(), model()->rowCount(), PieData());
754
755 update();
756 }
757
758 }
759 }
760