##// END OF EJS Templates
Use qFuzzyIsNull to compare (in)equality of real values
Jani Honkonen -
r768:dd9d32f835df
parent child
Show More
@@ -1,611 +1,611
1 1 #include <QtGui/QApplication>
2 2 #include <QMainWindow>
3 3 #include <qchartglobal.h>
4 4 #include <qchartview.h>
5 5 #include <qpieseries.h>
6 6 #include <qpieslice.h>
7 7 #include <QGridLayout>
8 8 #include <QFormLayout>
9 9 #include <QComboBox>
10 10 #include <QSpinBox>
11 11 #include <QCheckBox>
12 12 #include <QGroupBox>
13 13 #include <QLabel>
14 14 #include <QPushButton>
15 15 #include <QColorDialog>
16 16 #include <QFontDialog>
17 17
18 18 QTCOMMERCIALCHART_USE_NAMESPACE
19 19
20 20 class PenTool : public QWidget
21 21 {
22 22 Q_OBJECT
23 23
24 24 public:
25 25 explicit PenTool(QString title, QWidget *parent = 0)
26 26 :QWidget(parent)
27 27 {
28 28 setWindowTitle(title);
29 29 setWindowFlags(Qt::Tool);
30 30
31 31 m_colorButton = new QPushButton();
32 32
33 33 m_widthSpinBox = new QDoubleSpinBox();
34 34
35 35 m_styleCombo = new QComboBox();
36 36 m_styleCombo->addItem("NoPen");
37 37 m_styleCombo->addItem("SolidLine");
38 38 m_styleCombo->addItem("DashLine");
39 39 m_styleCombo->addItem("DotLine");
40 40 m_styleCombo->addItem("DashDotLine");
41 41 m_styleCombo->addItem("DashDotDotLine");
42 42
43 43 m_capStyleCombo = new QComboBox();
44 44 m_capStyleCombo->addItem("FlatCap", Qt::FlatCap);
45 45 m_capStyleCombo->addItem("SquareCap", Qt::SquareCap);
46 46 m_capStyleCombo->addItem("RoundCap", Qt::RoundCap);
47 47
48 48 m_joinStyleCombo = new QComboBox();
49 49 m_joinStyleCombo->addItem("MiterJoin", Qt::MiterJoin);
50 50 m_joinStyleCombo->addItem("BevelJoin", Qt::BevelJoin);
51 51 m_joinStyleCombo->addItem("RoundJoin", Qt::RoundJoin);
52 52 m_joinStyleCombo->addItem("SvgMiterJoin", Qt::SvgMiterJoin);
53 53
54 54 QFormLayout *layout = new QFormLayout();
55 55 layout->addRow("Color", m_colorButton);
56 56 layout->addRow("Width", m_widthSpinBox);
57 57 layout->addRow("Style", m_styleCombo);
58 58 layout->addRow("Cap style", m_capStyleCombo);
59 59 layout->addRow("Join style", m_joinStyleCombo);
60 60 setLayout(layout);
61 61
62 62 connect(m_colorButton, SIGNAL(clicked()), this, SLOT(showColorDialog()));
63 63 connect(m_widthSpinBox, SIGNAL(valueChanged(double)), this, SLOT(updateWidth(double)));
64 64 connect(m_styleCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateStyle(int)));
65 65 connect(m_capStyleCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateCapStyle(int)));
66 66 connect(m_joinStyleCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateJoinStyle(int)));
67 67 }
68 68
69 69 void setPen(QPen pen)
70 70 {
71 71 m_pen = pen;
72 72 m_colorButton->setText(m_pen.color().name());
73 73 m_widthSpinBox->setValue(m_pen.widthF());
74 74 m_styleCombo->setCurrentIndex(m_pen.style()); // index matches the enum
75 75 m_capStyleCombo->setCurrentIndex(m_capStyleCombo->findData(m_pen.capStyle()));
76 76 m_joinStyleCombo->setCurrentIndex(m_joinStyleCombo->findData(m_pen.joinStyle()));
77 77 }
78 78
79 79 QPen pen() const
80 80 {
81 81 return m_pen;
82 82 }
83 83
84 84 QString name()
85 85 {
86 86 return name(m_pen);
87 87 }
88 88
89 89 static QString name(const QPen &pen)
90 90 {
91 91 return pen.color().name() + ":" + QString::number(pen.widthF());
92 92 }
93 93
94 94 Q_SIGNALS:
95 95 void changed();
96 96
97 97 public Q_SLOTS:
98 98
99 99 void showColorDialog()
100 100 {
101 101 QColorDialog dialog(m_pen.color());
102 102 dialog.show();
103 103 dialog.exec();
104 104 m_pen.setColor(dialog.selectedColor());
105 105 m_colorButton->setText(m_pen.color().name());
106 106 emit changed();
107 107 }
108 108
109 109 void updateWidth(double width)
110 110 {
111 if (width != m_pen.widthF()) {
111 if (!qFuzzyIsNull(width - m_pen.widthF())) {
112 112 m_pen.setWidthF(width);
113 113 emit changed();
114 114 }
115 115 }
116 116
117 117 void updateStyle(int style)
118 118 {
119 119 if (m_pen.style() != style) {
120 120 m_pen.setStyle((Qt::PenStyle) style);
121 121 emit changed();
122 122 }
123 123 }
124 124
125 125 void updateCapStyle(int index)
126 126 {
127 127 Qt::PenCapStyle capStyle = (Qt::PenCapStyle) m_capStyleCombo->itemData(index).toInt();
128 128 if (m_pen.capStyle() != capStyle) {
129 129 m_pen.setCapStyle(capStyle);
130 130 emit changed();
131 131 }
132 132 }
133 133
134 134 void updateJoinStyle(int index)
135 135 {
136 136 Qt::PenJoinStyle joinStyle = (Qt::PenJoinStyle) m_joinStyleCombo->itemData(index).toInt();
137 137 if (m_pen.joinStyle() != joinStyle) {
138 138 m_pen.setJoinStyle(joinStyle);
139 139 emit changed();
140 140 }
141 141 }
142 142
143 143 private:
144 144 QPen m_pen;
145 145 QPushButton *m_colorButton;
146 146 QDoubleSpinBox *m_widthSpinBox;
147 147 QComboBox *m_styleCombo;
148 148 QComboBox *m_capStyleCombo;
149 149 QComboBox *m_joinStyleCombo;
150 150 };
151 151
152 152 class BrushTool : public QWidget
153 153 {
154 154 Q_OBJECT
155 155
156 156 public:
157 157 explicit BrushTool(QString title, QWidget *parent = 0)
158 158 :QWidget(parent)
159 159 {
160 160 setWindowTitle(title);
161 161 setWindowFlags(Qt::Tool);
162 162
163 163 m_colorButton = new QPushButton();
164 164 m_styleCombo = new QComboBox();
165 165 m_styleCombo->addItem("Nobrush", Qt::NoBrush);
166 166 m_styleCombo->addItem("Solidpattern", Qt::SolidPattern);
167 167 m_styleCombo->addItem("Dense1pattern", Qt::Dense1Pattern);
168 168 m_styleCombo->addItem("Dense2attern", Qt::Dense2Pattern);
169 169 m_styleCombo->addItem("Dense3Pattern", Qt::Dense3Pattern);
170 170 m_styleCombo->addItem("Dense4Pattern", Qt::Dense4Pattern);
171 171 m_styleCombo->addItem("Dense5Pattern", Qt::Dense5Pattern);
172 172 m_styleCombo->addItem("Dense6Pattern", Qt::Dense6Pattern);
173 173 m_styleCombo->addItem("Dense7Pattern", Qt::Dense7Pattern);
174 174 m_styleCombo->addItem("HorPattern", Qt::HorPattern);
175 175 m_styleCombo->addItem("VerPattern", Qt::VerPattern);
176 176 m_styleCombo->addItem("CrossPattern", Qt::CrossPattern);
177 177 m_styleCombo->addItem("BDiagPattern", Qt::BDiagPattern);
178 178 m_styleCombo->addItem("FDiagPattern", Qt::FDiagPattern);
179 179 m_styleCombo->addItem("DiagCrossPattern", Qt::DiagCrossPattern);
180 180
181 181 QFormLayout *layout = new QFormLayout();
182 182 layout->addRow("Color", m_colorButton);
183 183 layout->addRow("Style", m_styleCombo);
184 184 setLayout(layout);
185 185
186 186 connect(m_colorButton, SIGNAL(clicked()), this, SLOT(showColorDialog()));
187 187 connect(m_styleCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateStyle()));
188 188 }
189 189
190 190 void setBrush(QBrush brush)
191 191 {
192 192 m_brush = brush;
193 193 m_colorButton->setText(m_brush.color().name());
194 194 m_styleCombo->setCurrentIndex(m_brush.style()); // index matches the enum
195 195 }
196 196
197 197 QBrush brush() const
198 198 {
199 199 return m_brush;
200 200 }
201 201
202 202 QString name()
203 203 {
204 204 return name(m_brush);
205 205 }
206 206
207 207 static QString name(const QBrush &brush)
208 208 {
209 209 return brush.color().name();
210 210 }
211 211
212 212 Q_SIGNALS:
213 213 void changed();
214 214
215 215 public Q_SLOTS:
216 216
217 217 void showColorDialog()
218 218 {
219 219 QColorDialog dialog(m_brush.color());
220 220 dialog.show();
221 221 dialog.exec();
222 222 m_brush.setColor(dialog.selectedColor());
223 223 m_colorButton->setText(m_brush.color().name());
224 224 emit changed();
225 225 }
226 226
227 227 void updateStyle()
228 228 {
229 229 Qt::BrushStyle style = (Qt::BrushStyle) m_styleCombo->itemData(m_styleCombo->currentIndex()).toInt();
230 230 if (m_brush.style() != style) {
231 231 m_brush.setStyle(style);
232 232 emit changed();
233 233 }
234 234 }
235 235
236 236 private:
237 237 QBrush m_brush;
238 238 QPushButton *m_colorButton;
239 239 QComboBox *m_styleCombo;
240 240 };
241 241
242 242 class CustomSlice : public QPieSlice
243 243 {
244 244 Q_OBJECT
245 245 public:
246 246 CustomSlice(qreal value, QString label)
247 247 :QPieSlice(value, label)
248 248 {
249 249 connect(this, SIGNAL(hoverEnter()), this, SLOT(handleHoverEnter()));
250 250 connect(this, SIGNAL(hoverLeave()), this, SLOT(handleHoverLeave()));
251 251 }
252 252
253 253 public:
254 254 QBrush originalBrush()
255 255 {
256 256 return m_originalBrush;
257 257 }
258 258
259 259 public Q_SLOTS:
260 260
261 261 void handleHoverEnter()
262 262 {
263 263 QBrush brush = this->brush();
264 264 m_originalBrush = brush;
265 265 brush.setColor(brush.color().lighter());
266 266 setBrush(brush);
267 267 }
268 268
269 269 void handleHoverLeave()
270 270 {
271 271 setBrush(m_originalBrush);
272 272 }
273 273
274 274 private:
275 275 QBrush m_originalBrush;
276 276 };
277 277
278 278 class MainWidget : public QWidget
279 279 {
280 280 Q_OBJECT
281 281
282 282 public:
283 283 explicit MainWidget(QWidget* parent = 0)
284 284 :QWidget(parent),
285 285 m_slice(0)
286 286 {
287 287 // create chart
288 288 m_chartView = new QChartView(new QChart());
289 289 m_chartView->chart()->setTitle("Piechart customization");
290 290 m_chartView->chart()->setAnimationOptions(QChart::AllAnimations);
291 291
292 292 // create series
293 293 m_series = new QPieSeries();
294 294 *m_series << new CustomSlice(10.0, "Slice 1");
295 295 *m_series << new CustomSlice(20.0, "Slice 2");
296 296 *m_series << new CustomSlice(30.0, "Slice 3");
297 297 *m_series << new CustomSlice(40.0, "Slice 4");
298 298 *m_series << new CustomSlice(50.0, "Slice 5");
299 299 m_series->setLabelsVisible();
300 300 m_chartView->chart()->addSeries(m_series);
301 301
302 302 connect(m_series, SIGNAL(clicked(QPieSlice*, Qt::MouseButtons)), this, SLOT(handleSliceClicked(QPieSlice*, Qt::MouseButtons)));
303 303
304 304 // chart settings
305 305 m_themeComboBox = new QComboBox();
306 306 m_themeComboBox->addItem("Default", QChart::ChartThemeDefault);
307 307 m_themeComboBox->addItem("Light", QChart::ChartThemeLight);
308 308 m_themeComboBox->addItem("BlueCerulean", QChart::ChartThemeBlueCerulean);
309 309 m_themeComboBox->addItem("Dark", QChart::ChartThemeDark);
310 310 m_themeComboBox->addItem("BrownSand", QChart::ChartThemeBrownSand);
311 311 m_themeComboBox->addItem("BlueNcs", QChart::ChartThemeBlueNcs);
312 312 m_themeComboBox->addItem("High Contrast", QChart::ChartThemeHighContrast);
313 313 m_themeComboBox->addItem("Blue Icy", QChart::ChartThemeBlueIcy);
314 314
315 315 m_aaCheckBox = new QCheckBox();
316 316 m_animationsCheckBox = new QCheckBox();
317 317 m_animationsCheckBox->setCheckState(Qt::Checked);
318 318
319 319 QFormLayout* chartSettingsLayout = new QFormLayout();
320 320 chartSettingsLayout->addRow("Theme", m_themeComboBox);
321 321 chartSettingsLayout->addRow("Antialiasing", m_aaCheckBox);
322 322 chartSettingsLayout->addRow("Animations", m_animationsCheckBox);
323 323 QGroupBox* chartSettings = new QGroupBox("Chart");
324 324 chartSettings->setLayout(chartSettingsLayout);
325 325
326 326 connect(m_themeComboBox, SIGNAL(currentIndexChanged(int)), this ,SLOT(updateChartSettings()));
327 327 connect(m_aaCheckBox, SIGNAL(toggled(bool)), this ,SLOT(updateChartSettings()));
328 328 connect(m_animationsCheckBox, SIGNAL(toggled(bool)), this ,SLOT(updateChartSettings()));
329 329
330 330 // series settings
331 331 m_hPosition = new QDoubleSpinBox();
332 332 m_hPosition->setMinimum(0.0);
333 333 m_hPosition->setMaximum(1.0);
334 334 m_hPosition->setSingleStep(0.1);
335 335 m_hPosition->setValue(m_series->pieHorizontalPosition());
336 336
337 337 m_vPosition = new QDoubleSpinBox();
338 338 m_vPosition->setMinimum(0.0);
339 339 m_vPosition->setMaximum(1.0);
340 340 m_vPosition->setSingleStep(0.1);
341 341 m_vPosition->setValue(m_series->pieVerticalPosition());
342 342
343 343 m_sizeFactor = new QDoubleSpinBox();
344 344 m_sizeFactor->setMinimum(0.0);
345 345 m_sizeFactor->setMaximum(1.0);
346 346 m_sizeFactor->setSingleStep(0.1);
347 347 m_sizeFactor->setValue(m_series->pieSize());
348 348
349 349 m_startAngle = new QDoubleSpinBox();
350 350 m_startAngle->setMinimum(0.0);
351 351 m_startAngle->setMaximum(360);
352 352 m_startAngle->setValue(m_series->pieStartAngle());
353 353 m_startAngle->setSingleStep(1);
354 354
355 355 m_endAngle = new QDoubleSpinBox();
356 356 m_endAngle->setMinimum(0.0);
357 357 m_endAngle->setMaximum(360);
358 358 m_endAngle->setValue(m_series->pieEndAngle());
359 359 m_endAngle->setSingleStep(1);
360 360
361 361 QPushButton *addSlice = new QPushButton("Add slice");
362 362 QPushButton *insertSlice = new QPushButton("Insert slice");
363 363
364 364 QFormLayout* seriesSettingsLayout = new QFormLayout();
365 365 seriesSettingsLayout->addRow("Horizontal position", m_hPosition);
366 366 seriesSettingsLayout->addRow("Vertical position", m_vPosition);
367 367 seriesSettingsLayout->addRow("Size factor", m_sizeFactor);
368 368 seriesSettingsLayout->addRow("Start angle", m_startAngle);
369 369 seriesSettingsLayout->addRow("End angle", m_endAngle);
370 370 seriesSettingsLayout->addRow(addSlice);
371 371 seriesSettingsLayout->addRow(insertSlice);
372 372 QGroupBox* seriesSettings = new QGroupBox("Series");
373 373 seriesSettings->setLayout(seriesSettingsLayout);
374 374
375 375 connect(m_vPosition, SIGNAL(valueChanged(double)), this, SLOT(updateSerieSettings()));
376 376 connect(m_hPosition, SIGNAL(valueChanged(double)), this, SLOT(updateSerieSettings()));
377 377 connect(m_sizeFactor, SIGNAL(valueChanged(double)), this, SLOT(updateSerieSettings()));
378 378 connect(m_startAngle, SIGNAL(valueChanged(double)), this, SLOT(updateSerieSettings()));
379 379 connect(m_endAngle, SIGNAL(valueChanged(double)), this, SLOT(updateSerieSettings()));
380 380 connect(addSlice, SIGNAL(clicked()), this, SLOT(addSlice()));
381 381 connect(insertSlice, SIGNAL(clicked()), this, SLOT(insertSlice()));
382 382
383 383 // slice settings
384 384 m_sliceName = new QLabel("<click a slice>");
385 385 m_sliceValue = new QDoubleSpinBox();
386 386 m_sliceValue->setMaximum(1000);
387 387 m_sliceLabelVisible = new QCheckBox();
388 388 m_sliceLabelArmFactor = new QDoubleSpinBox();
389 389 m_sliceLabelArmFactor->setSingleStep(0.01);
390 390 m_sliceExploded = new QCheckBox();
391 391 m_sliceExplodedFactor = new QDoubleSpinBox();
392 392 m_sliceExplodedFactor->setSingleStep(0.01);
393 393 m_pen = new QPushButton();
394 394 m_penTool = new PenTool("Slice pen", this);
395 395 m_brush = new QPushButton();
396 396 m_brushTool = new BrushTool("Slice brush", this);
397 397 m_font = new QPushButton();
398 398 m_labelPen = new QPushButton();
399 399 m_labelPenTool = new PenTool("Label pen", this);
400 400 QPushButton *removeSlice = new QPushButton("Remove slice");
401 401
402 402 QFormLayout* sliceSettingsLayout = new QFormLayout();
403 403 sliceSettingsLayout->addRow("Selected", m_sliceName);
404 404 sliceSettingsLayout->addRow("Value", m_sliceValue);
405 405 sliceSettingsLayout->addRow("Pen", m_pen);
406 406 sliceSettingsLayout->addRow("Brush", m_brush);
407 407 sliceSettingsLayout->addRow("Label visible", m_sliceLabelVisible);
408 408 sliceSettingsLayout->addRow("Label font", m_font);
409 409 sliceSettingsLayout->addRow("Label pen", m_labelPen);
410 410 sliceSettingsLayout->addRow("Label arm length", m_sliceLabelArmFactor);
411 411 sliceSettingsLayout->addRow("Exploded", m_sliceExploded);
412 412 sliceSettingsLayout->addRow("Explode distance", m_sliceExplodedFactor);
413 413 sliceSettingsLayout->addRow(removeSlice);
414 414 QGroupBox* sliceSettings = new QGroupBox("Slice");
415 415 sliceSettings->setLayout(sliceSettingsLayout);
416 416
417 417 connect(m_sliceValue, SIGNAL(valueChanged(double)), this, SLOT(updateSliceSettings()));
418 418 connect(m_pen, SIGNAL(clicked()), m_penTool, SLOT(show()));
419 419 connect(m_penTool, SIGNAL(changed()), this, SLOT(updateSliceSettings()));
420 420 connect(m_brush, SIGNAL(clicked()), m_brushTool, SLOT(show()));
421 421 connect(m_brushTool, SIGNAL(changed()), this, SLOT(updateSliceSettings()));
422 422 connect(m_font, SIGNAL(clicked()), this, SLOT(showFontDialog()));
423 423 connect(m_labelPen, SIGNAL(clicked()), m_labelPenTool, SLOT(show()));
424 424 connect(m_labelPenTool, SIGNAL(changed()), this, SLOT(updateSliceSettings()));
425 425 connect(m_sliceLabelVisible, SIGNAL(toggled(bool)), this, SLOT(updateSliceSettings()));
426 426 connect(m_sliceLabelVisible, SIGNAL(toggled(bool)), this, SLOT(updateSliceSettings()));
427 427 connect(m_sliceLabelArmFactor, SIGNAL(valueChanged(double)), this, SLOT(updateSliceSettings()));
428 428 connect(m_sliceExploded, SIGNAL(toggled(bool)), this, SLOT(updateSliceSettings()));
429 429 connect(m_sliceExplodedFactor, SIGNAL(valueChanged(double)), this, SLOT(updateSliceSettings()));
430 430 connect(removeSlice, SIGNAL(clicked()), this, SLOT(removeSlice()));
431 431
432 432 // create main layout
433 433 QVBoxLayout *settingsLayout = new QVBoxLayout();
434 434 settingsLayout->addWidget(chartSettings);
435 435 settingsLayout->addWidget(seriesSettings);
436 436 settingsLayout->addWidget(sliceSettings);
437 437 settingsLayout->addStretch();
438 438
439 439 QGridLayout* baseLayout = new QGridLayout();
440 440 baseLayout->addLayout(settingsLayout, 0, 0);
441 441 baseLayout->addWidget(m_chartView, 0, 1);
442 442 setLayout(baseLayout);
443 443
444 444 updateSerieSettings();
445 445 }
446 446
447 447 public Q_SLOTS:
448 448
449 449 void updateChartSettings()
450 450 {
451 451 QChart::ChartTheme theme = (QChart::ChartTheme) m_themeComboBox->itemData(m_themeComboBox->currentIndex()).toInt();
452 452 m_chartView->chart()->setTheme(theme);
453 453 m_chartView->setRenderHint(QPainter::Antialiasing, m_aaCheckBox->isChecked());
454 454
455 455 if (m_animationsCheckBox->checkState() == Qt::Checked)
456 456 m_chartView->chart()->setAnimationOptions(QChart::AllAnimations);
457 457 else
458 458 m_chartView->chart()->setAnimationOptions(QChart::NoAnimation);
459 459 }
460 460
461 461 void updateSerieSettings()
462 462 {
463 463 m_series->setPiePosition(m_hPosition->value(), m_vPosition->value());
464 464 m_series->setPieSize(m_sizeFactor->value());
465 465 m_series->setPieStartAngle(m_startAngle->value());
466 466 m_series->setPieEndAngle(m_endAngle->value());
467 467 }
468 468
469 469 void updateSliceSettings()
470 470 {
471 471 if (!m_slice)
472 472 return;
473 473
474 474 m_slice->setValue(m_sliceValue->value());
475 475
476 476 m_slice->setPen(m_penTool->pen());
477 477 m_slice->setBrush(m_brushTool->brush());
478 478
479 479 m_slice->setLabelPen(m_labelPenTool->pen());
480 480 m_slice->setLabelVisible(m_sliceLabelVisible->isChecked());
481 481 m_slice->setLabelArmLengthFactor(m_sliceLabelArmFactor->value());
482 482
483 483 m_slice->setExploded(m_sliceExploded->isChecked());
484 484 m_slice->setExplodeDistanceFactor(m_sliceExplodedFactor->value());
485 485 }
486 486
487 487 void handleSliceClicked(QPieSlice* slice, Qt::MouseButtons buttons)
488 488 {
489 489 Q_UNUSED(buttons);
490 490
491 491 m_slice = static_cast<CustomSlice*>(slice);
492 492
493 493 // name
494 494 m_sliceName->setText(slice->label());
495 495
496 496 // value
497 497 m_sliceValue->blockSignals(true);
498 498 m_sliceValue->setValue(slice->value());
499 499 m_sliceValue->blockSignals(false);
500 500
501 501 // pen
502 502 m_pen->setText(PenTool::name(m_slice->pen()));
503 503 m_penTool->setPen(m_slice->pen());
504 504
505 505 // brush
506 506 m_brush->setText(m_slice->originalBrush().color().name());
507 507 m_brushTool->setBrush(m_slice->originalBrush());
508 508
509 509 // label
510 510 m_labelPen->setText(PenTool::name(m_slice->labelPen()));
511 511 m_labelPenTool->setPen(m_slice->labelPen());
512 512 m_font->setText(slice->labelFont().toString());
513 513 m_sliceLabelVisible->blockSignals(true);
514 514 m_sliceLabelVisible->setChecked(slice->isLabelVisible());
515 515 m_sliceLabelVisible->blockSignals(false);
516 516 m_sliceLabelArmFactor->blockSignals(true);
517 517 m_sliceLabelArmFactor->setValue(slice->labelArmLengthFactor());
518 518 m_sliceLabelArmFactor->blockSignals(false);
519 519
520 520 // exploded
521 521 m_sliceExploded->blockSignals(true);
522 522 m_sliceExploded->setChecked(slice->isExploded());
523 523 m_sliceExploded->blockSignals(false);
524 524 m_sliceExplodedFactor->blockSignals(true);
525 525 m_sliceExplodedFactor->setValue(slice->explodeDistanceFactor());
526 526 m_sliceExplodedFactor->blockSignals(false);
527 527 }
528 528
529 529 void showFontDialog()
530 530 {
531 531 if (!m_slice)
532 532 return;
533 533
534 534 QFontDialog dialog(m_slice->labelFont());
535 535 dialog.show();
536 536 dialog.exec();
537 537
538 538 m_slice->setLabelFont(dialog.currentFont());
539 539 m_font->setText(dialog.currentFont().toString());
540 540 }
541 541
542 542 void addSlice()
543 543 {
544 544 *m_series << new CustomSlice(10.0, "Slice " + QString::number(m_series->count()+1));
545 545 }
546 546
547 547 void insertSlice()
548 548 {
549 549 if (!m_slice)
550 550 return;
551 551
552 552 int i = m_series->slices().indexOf(m_slice);
553 553
554 554 m_series->insert(i, new CustomSlice(10.0, "Slice " + QString::number(m_series->count()+1)));
555 555 }
556 556
557 557 void removeSlice()
558 558 {
559 559 if (!m_slice)
560 560 return;
561 561
562 562 m_series->remove(m_slice);
563 563 m_slice = 0;
564 564 }
565 565
566 566 private:
567 567 QComboBox *m_themeComboBox;
568 568 QCheckBox *m_aaCheckBox;
569 569 QCheckBox *m_animationsCheckBox;
570 570
571 571 QChartView* m_chartView;
572 572 QPieSeries* m_series;
573 573 CustomSlice* m_slice;
574 574
575 575 QDoubleSpinBox* m_hPosition;
576 576 QDoubleSpinBox* m_vPosition;
577 577 QDoubleSpinBox* m_sizeFactor;
578 578 QDoubleSpinBox* m_startAngle;
579 579 QDoubleSpinBox* m_endAngle;
580 580
581 581 QLabel* m_sliceName;
582 582 QDoubleSpinBox* m_sliceValue;
583 583 QCheckBox* m_sliceLabelVisible;
584 584 QDoubleSpinBox* m_sliceLabelArmFactor;
585 585 QCheckBox* m_sliceExploded;
586 586 QDoubleSpinBox* m_sliceExplodedFactor;
587 587 QPushButton *m_brush;
588 588 BrushTool *m_brushTool;
589 589 QPushButton *m_pen;
590 590 PenTool *m_penTool;
591 591 QPushButton *m_font;
592 592 QPushButton *m_labelPen;
593 593 PenTool *m_labelPenTool;
594 594 };
595 595
596 596 int main(int argc, char *argv[])
597 597 {
598 598 QApplication a(argc, argv);
599 599
600 600 QMainWindow window;
601 601
602 602 MainWidget* widget = new MainWidget();
603 603
604 604 window.setCentralWidget(widget);
605 605 window.resize(900, 600);
606 606 window.show();
607 607
608 608 return a.exec();
609 609 }
610 610
611 611 #include "main.moc"
@@ -1,421 +1,422
1 1 #include "axisitem_p.h"
2 2 #include "qchartaxis.h"
3 3 #include "chartpresenter_p.h"
4 4 #include "chartanimator_p.h"
5 5 #include <QPainter>
6 6 #include <QDebug>
7 7 #include <cmath>
8 8
9 9 static int label_padding = 5;
10 10
11 11 QTCOMMERCIALCHART_BEGIN_NAMESPACE
12 12
13 13 Axis::Axis(QChartAxis *axis,ChartPresenter *presenter,AxisType type) : Chart(presenter),
14 14 m_chartAxis(axis),
15 15 m_type(type),
16 16 m_labelsAngle(0),
17 17 m_grid(presenter->rootItem()),
18 18 m_shades(presenter->rootItem()),
19 19 m_labels(presenter->rootItem()),
20 20 m_axis(presenter->rootItem()),
21 21 m_min(0),
22 22 m_max(0),
23 23 m_ticksCount(0)
24 24 {
25 25 //initial initialization
26 26 m_axis.setZValue(ChartPresenter::AxisZValue);
27 27 m_axis.setHandlesChildEvents(false);
28 28
29 29 m_shades.setZValue(ChartPresenter::ShadesZValue);
30 30 m_grid.setZValue(ChartPresenter::GridZValue);
31 31
32 32 connect(m_chartAxis,SIGNAL(updated()),this,SLOT(handleAxisUpdated()));
33 33 connect(m_chartAxis->categories(),SIGNAL(updated()),this,SLOT(handleAxisCategoriesUpdated()));
34 34
35 35 handleAxisUpdated();
36 36 }
37 37
38 38 Axis::~Axis()
39 39 {
40 40 }
41 41
42 42 void Axis::createItems(int count)
43 43 {
44 44
45 45 if (m_axis.children().size() == 0)
46 46 m_axis.addToGroup(new AxisItem(this));
47 47 for (int i = 0; i < count; ++i) {
48 48 m_grid.addToGroup(new QGraphicsLineItem());
49 49 m_labels.addToGroup(new QGraphicsSimpleTextItem());
50 50 m_axis.addToGroup(new QGraphicsLineItem());
51 51 if ((m_grid.childItems().size())%2 && m_grid.childItems().size()>2) m_shades.addToGroup(new QGraphicsRectItem());
52 52 }
53 53 }
54 54
55 55 void Axis::deleteItems(int count)
56 56 {
57 57 QList<QGraphicsItem *> lines = m_grid.childItems();
58 58 QList<QGraphicsItem *> labels = m_labels.childItems();
59 59 QList<QGraphicsItem *> shades = m_shades.childItems();
60 60 QList<QGraphicsItem *> axis = m_axis.childItems();
61 61
62 62 for (int i = 0; i < count; ++i) {
63 63 if (lines.size()%2 && lines.size() > 1) delete(shades.takeLast());
64 64 delete(lines.takeLast());
65 65 delete(labels.takeLast());
66 66 delete(axis.takeLast());
67 67 }
68 68 }
69 69
70 70 void Axis::updateLayout(QVector<qreal> &layout)
71 71 {
72 72 if (animator()) {
73 73 animator()->updateLayout(this,layout);
74 74 } else {
75 75 setLayout(layout);
76 76 }
77 77 }
78 78
79 79 bool Axis::createLabels(QStringList &labels,qreal min, qreal max,int ticks) const
80 80 {
81 81 Q_ASSERT(max>=min);
82 82 Q_ASSERT(ticks>1);
83 83
84 84 QChartAxisCategories* categories = m_chartAxis->categories();
85 85
86 86 bool category = categories->count()>0;
87 87
88 88 if (!category) {
89 89 int n = qMax(int(-floor(log10((max-min)/(ticks-1)))),0);
90 90 for (int i=0; i< ticks; i++) {
91 91 qreal value = min + (i * (max - min)/ (ticks-1));
92 92 labels << QString::number(value,'f',n);
93 93 }
94 94 } else {
95 95 QList<qreal> values = categories->values();
96 96 for (int i=0; i< ticks; i++) {
97 97 qreal value = (min + (i * (max - min)/ (ticks-1)));
98 98 int j=0;
99 99 for (; j<values.count(); j++) {
100 100 if (values.at(j) > value) break;
101 101 }
102 102 if (j!=0) value=values.at(j-1);
103 103
104 104 QString label = categories->label(value);
105 105 labels << label;
106 106 }
107 107 }
108 108
109 109 return category;
110 110 }
111 111
112 112 void Axis::setAxisOpacity(qreal opacity)
113 113 {
114 114 m_axis.setOpacity(opacity);
115 115 }
116 116
117 117 qreal Axis::axisOpacity() const
118 118 {
119 119 return m_axis.opacity();
120 120 }
121 121
122 122 void Axis::setGridOpacity(qreal opacity)
123 123 {
124 124 m_grid.setOpacity(opacity);
125 125 }
126 126
127 127 qreal Axis::gridOpacity() const
128 128 {
129 129 return m_grid.opacity();
130 130 }
131 131
132 132 void Axis::setLabelsOpacity(qreal opacity)
133 133 {
134 134 m_labels.setOpacity(opacity);
135 135 }
136 136
137 137 qreal Axis::labelsOpacity() const
138 138 {
139 139 return m_labels.opacity();
140 140 }
141 141
142 142 void Axis::setShadesOpacity(qreal opacity)
143 143 {
144 144 m_shades.setOpacity(opacity);
145 145 }
146 146
147 147 qreal Axis::shadesOpacity() const
148 148 {
149 149 return m_shades.opacity();
150 150 }
151 151
152 152 void Axis::setLabelsAngle(int angle)
153 153 {
154 154 foreach(QGraphicsItem* item , m_labels.childItems()) {
155 155 QPointF center = item->boundingRect().center();
156 156 item->setRotation(angle);
157 157 }
158 158
159 159 m_labelsAngle=angle;
160 160 }
161 161
162 162 void Axis::setLabelsPen(const QPen &pen)
163 163 {
164 164 foreach(QGraphicsItem* item , m_labels.childItems()) {
165 165 static_cast<QGraphicsSimpleTextItem*>(item)->setPen(pen);
166 166 }
167 167 }
168 168
169 169 void Axis::setLabelsBrush(const QBrush &brush)
170 170 {
171 171 foreach(QGraphicsItem* item , m_labels.childItems()) {
172 172 static_cast<QGraphicsSimpleTextItem*>(item)->setBrush(brush);
173 173 }
174 174 }
175 175
176 176 void Axis::setLabelsFont(const QFont &font)
177 177 {
178 178 foreach(QGraphicsItem* item , m_labels.childItems()) {
179 179 static_cast<QGraphicsSimpleTextItem*>(item)->setFont(font);
180 180 }
181 181 }
182 182
183 183 void Axis::setShadesBrush(const QBrush &brush)
184 184 {
185 185 foreach(QGraphicsItem* item , m_shades.childItems()) {
186 186 static_cast<QGraphicsRectItem*>(item)->setBrush(brush);
187 187 }
188 188 }
189 189
190 190 void Axis::setShadesPen(const QPen &pen)
191 191 {
192 192 foreach(QGraphicsItem* item , m_shades.childItems()) {
193 193 static_cast<QGraphicsRectItem*>(item)->setPen(pen);
194 194 }
195 195 }
196 196
197 197 void Axis::setAxisPen(const QPen &pen)
198 198 {
199 199 foreach(QGraphicsItem* item , m_axis.childItems()) {
200 200 static_cast<QGraphicsLineItem*>(item)->setPen(pen);
201 201 }
202 202 }
203 203
204 204 void Axis::setGridPen(const QPen &pen)
205 205 {
206 206 foreach(QGraphicsItem* item , m_grid.childItems()) {
207 207 static_cast<QGraphicsLineItem*>(item)->setPen(pen);
208 208 }
209 209 }
210 210
211 211 QVector<qreal> Axis::calculateLayout() const
212 212 {
213 213 Q_ASSERT(m_ticksCount>=2);
214 214
215 215 QVector<qreal> points;
216 216 points.resize(m_ticksCount);
217 217
218 218 switch (m_type)
219 219 {
220 220 case X_AXIS:
221 221 {
222 222 const qreal deltaX = m_rect.width()/(m_ticksCount-1);
223 223 for (int i = 0; i < m_ticksCount; ++i) {
224 224 int x = i * deltaX + m_rect.left();
225 225 points[i] = x;
226 226 }
227 227 }
228 228 break;
229 229 case Y_AXIS:
230 230 {
231 231 const qreal deltaY = m_rect.height()/(m_ticksCount-1);
232 232 for (int i = 0; i < m_ticksCount; ++i) {
233 233 int y = i * -deltaY + m_rect.bottom();
234 234 points[i] = y;
235 235 }
236 236 }
237 237 break;
238 238 }
239 239 return points;
240 240 }
241 241
242 242 void Axis::setLayout(QVector<qreal> &layout)
243 243 {
244 244 int diff = m_layoutVector.size() - layout.size();
245 245
246 246 if (diff>0) {
247 247 deleteItems(diff);
248 248 } else if (diff<0) {
249 249 createItems(-diff);
250 250 }
251 251
252 252 if( diff!=0) handleAxisUpdated();
253 253
254 254 QStringList ticksList;
255 255
256 256 bool categories = createLabels(ticksList,m_min,m_max,layout.size());
257 257
258 258 QList<QGraphicsItem *> lines = m_grid.childItems();
259 259 QList<QGraphicsItem *> labels = m_labels.childItems();
260 260 QList<QGraphicsItem *> shades = m_shades.childItems();
261 261 QList<QGraphicsItem *> axis = m_axis.childItems();
262 262
263 263 Q_ASSERT(labels.size() == ticksList.size());
264 264 Q_ASSERT(layout.size() == ticksList.size());
265 265
266 266 switch (m_type)
267 267 {
268 268 case X_AXIS:
269 269 {
270 270 QGraphicsLineItem *lineItem = static_cast<QGraphicsLineItem*>(axis.at(0));
271 271 lineItem->setLine(m_rect.left(), m_rect.bottom(), m_rect.right(), m_rect.bottom());
272 272
273 273 for (int i = 0; i < layout.size(); ++i) {
274 274 QGraphicsLineItem *lineItem = static_cast<QGraphicsLineItem*>(lines.at(i));
275 275 lineItem->setLine(layout[i], m_rect.top(), layout[i], m_rect.bottom());
276 276 QGraphicsSimpleTextItem *labelItem = static_cast<QGraphicsSimpleTextItem*>(labels.at(i));
277 277 if (!categories) {
278 278 labelItem->setText(ticksList.at(i));
279 279 QPointF center = labelItem->boundingRect().center();
280 280 labelItem->setTransformOriginPoint(center.x(), center.y());
281 281 labelItem->setPos(layout[i] - center.x(), m_rect.bottom() + label_padding);
282 282 } else if(i>0) {
283 283 labelItem->setText(ticksList.at(i));
284 284 QPointF center = labelItem->boundingRect().center();
285 285 labelItem->setTransformOriginPoint(center.x(), center.y());
286 286 labelItem->setPos(layout[i] - (layout[i] - layout[i-1])/2 - center.x(), m_rect.bottom() + label_padding);
287 287 }
288 288
289 289 if ((i+1)%2 && i>1) {
290 290 QGraphicsRectItem *rectItem = static_cast<QGraphicsRectItem*>(shades.at(i/2-1));
291 291 rectItem->setRect(layout[i-1],m_rect.top(),layout[i]-layout[i-1],m_rect.height());
292 292 }
293 293 lineItem = static_cast<QGraphicsLineItem*>(axis.at(i+1));
294 294 lineItem->setLine(layout[i],m_rect.bottom(),layout[i],m_rect.bottom()+5);
295 295 }
296 296 }
297 297 break;
298 298
299 299 case Y_AXIS:
300 300 {
301 301 QGraphicsLineItem *lineItem = static_cast<QGraphicsLineItem*>(axis.at(0));
302 302 lineItem->setLine(m_rect.left() , m_rect.top(), m_rect.left(), m_rect.bottom());
303 303
304 304 for (int i = 0; i < layout.size(); ++i) {
305 305 QGraphicsLineItem *lineItem = static_cast<QGraphicsLineItem*>(lines.at(i));
306 306 lineItem->setLine(m_rect.left() , layout[i], m_rect.right(), layout[i]);
307 307 QGraphicsSimpleTextItem *labelItem = static_cast<QGraphicsSimpleTextItem*>(labels.at(i));
308 308
309 309 if (!categories) {
310 310 labelItem->setText(ticksList.at(i));
311 311 QPointF center = labelItem->boundingRect().center();
312 312 labelItem->setTransformOriginPoint(center.x(), center.y());
313 313 labelItem->setPos(m_rect.left() - labelItem->boundingRect().width() - label_padding , layout[i]-center.y());
314 314 } else if (i>0) {
315 315 labelItem->setText(ticksList.at(i));
316 316 QPointF center = labelItem->boundingRect().center();
317 317 labelItem->setTransformOriginPoint(center.x(), center.y());
318 318 labelItem->setPos(m_rect.left() - labelItem->boundingRect().width() - label_padding , layout[i] - (layout[i] - layout[i-1])/2 -center.y());
319 319 }
320 320
321 321 if ((i+1)%2 && i>1) {
322 322 QGraphicsRectItem *rectItem = static_cast<QGraphicsRectItem*>(shades.at(i/2-1));
323 323 rectItem->setRect(m_rect.left(),layout[i],m_rect.width(),layout[i-1]-layout[i]);
324 324 }
325 325 lineItem = static_cast<QGraphicsLineItem*>(axis.at(i+1));
326 326 lineItem->setLine(m_rect.left()-5,layout[i],m_rect.left(),layout[i]);
327 327 }
328 328 }
329 329 break;
330 330 default:
331 331 qDebug()<<"Unknown axis type";
332 332 break;
333 333 }
334 334
335 335 m_layoutVector=layout;
336 336 }
337 337
338 338 bool Axis::isEmpty()
339 339 {
340 return m_rect.isEmpty() || m_min==m_max || m_ticksCount==0;
340 return m_rect.isEmpty() || qFuzzyIsNull(m_min - m_max) || m_ticksCount==0;
341 341 }
342 342
343 343 //handlers
344 344
345 345 void Axis::handleAxisCategoriesUpdated()
346 346 {
347 347 if (isEmpty()) return;
348 348 updateLayout(m_layoutVector);
349 349 }
350 350
351 351 void Axis::handleAxisUpdated()
352 352 {
353 353
354 354 if (isEmpty()) return;
355 355
356 356 if (m_chartAxis->isAxisVisible()) {
357 357 setAxisOpacity(100);
358 358 } else {
359 359 setAxisOpacity(0);
360 360 }
361 361
362 362 if (m_chartAxis->isGridLineVisible()) {
363 363 setGridOpacity(100);
364 364 } else {
365 365 setGridOpacity(0);
366 366 }
367 367
368 368 if (m_chartAxis->labelsVisible()) {
369 369 setLabelsOpacity(100);
370 370 } else {
371 371 setLabelsOpacity(0);
372 372 }
373 373
374 374 if (m_chartAxis->shadesVisible()) {
375 375 setShadesOpacity(m_chartAxis->shadesOpacity());
376 376 } else {
377 377 setShadesOpacity(0);
378 378 }
379 379
380 380 setLabelsAngle(m_chartAxis->labelsAngle());
381 381 setAxisPen(m_chartAxis->axisPen());
382 382 setLabelsPen(m_chartAxis->labelsPen());
383 383 setLabelsBrush(m_chartAxis->labelsBrush());
384 384 setLabelsFont(m_chartAxis->labelsFont());
385 385 setGridPen(m_chartAxis->gridLinePen());
386 386 setShadesPen(m_chartAxis->shadesPen());
387 387 setShadesBrush(m_chartAxis->shadesBrush());
388 388
389 389 }
390 390
391 391 void Axis::handleRangeChanged(qreal min, qreal max,int tickCount)
392 392 {
393 if (min==max || tickCount<2) return;
393 if (qFuzzyIsNull(min - max) || tickCount < 2)
394 return;
394 395
395 396 m_min = min;
396 397 m_max = max;
397 398 m_ticksCount= tickCount;
398 399
399 400 if (isEmpty()) return;
400 401 QVector<qreal> layout = calculateLayout();
401 402 updateLayout(layout);
402 403
403 404 }
404 405
405 406 void Axis::handleGeometryChanged(const QRectF &rect)
406 407 {
407 408 m_rect = rect;
408 409 if (isEmpty()) return;
409 410 QVector<qreal> layout = calculateLayout();
410 411 updateLayout(layout);
411 412 }
412 413
413 414 void Axis::axisSelected()
414 415 {
415 416 qDebug()<<"TODO axis clicked";
416 417 }
417 418
418 419 //TODO "nice numbers algorithm"
419 420 #include "moc_axisitem_p.cpp"
420 421
421 422 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,401 +1,401
1 1 #include "qchartaxis.h"
2 2
3 3 QTCOMMERCIALCHART_BEGIN_NAMESPACE
4 4
5 5 /*!
6 6 \class QChartAxis
7 7 \brief The QChartAxis class is used for manipulating chart's axis
8 8 and for adding optional axes to the chart.
9 9 \mainclass
10 10
11 11 There is only one x Axis, however there can be multiple y axes.
12 12 Each chart series can be bound to exactly one Y axis and the share common X axis.
13 13 Axis can be setup to show axis line with ticks, gird lines and shades.
14 14
15 15 */
16 16
17 17 /*!
18 18 \fn bool QChartAxis::isAxisVisible() const
19 19 \brief Returns if axis is visible
20 20 \sa setAxisVisible()
21 21 */
22 22
23 23 /*!
24 24 \fn QPen QChartAxis::axisPen() const
25 25 \brief Returns pen used to draw axis and ticks.
26 26 \sa setAxisPen()
27 27 */
28 28
29 29
30 30 /*!
31 31 \fn bool QChartAxis::isGridLineVisible() const
32 32 \brief Returns if grid is visible
33 33 \sa setGridLineVisible()
34 34 */
35 35
36 36 /*!
37 37 \fn QPen QChartAxis::gridLinePen() const
38 38 \brief Returns pen used to draw grid.
39 39 \sa setGridLinePen()
40 40 */
41 41
42 42 /*!
43 43 \fn bool QChartAxis::labelsVisible() const
44 44 \brief Returns if grid is visible
45 45 \sa setLabelsVisible()
46 46 */
47 47
48 48 /*!
49 49 \fn QPen QChartAxis::labelsPen() const
50 50 \brief Returns the pen used to labels.
51 51 \sa setLabelsPen()
52 52 */
53 53
54 54 /*!
55 55 \fn QBrush QChartAxis::labelsBrush() const
56 56 \brief Returns brush used to draw labels.
57 57 \sa setLabelsBrush()
58 58 */
59 59
60 60 /*!
61 61 \fn QFont QChartAxis::labelsFont() const
62 62 \brief Returns font used to draw labels.
63 63 \sa setLabelsFont()
64 64 */
65 65
66 66 /*!
67 67 \fn QFont QChartAxis::labelsAngle() const
68 68 \brief Returns angle used to draw labels.
69 69 \sa setLabelsAngle()
70 70 */
71 71
72 72 /*!
73 73 \fn bool QChartAxis::shadesVisible() const
74 74 \brief Returns if shades are visible.
75 75 \sa setShadesVisible()
76 76 */
77 77
78 78 /*!
79 79 \fn qreal QChartAxis::shadesOpacity() const
80 80 \brief Returns opacity of shades.
81 81 */
82 82
83 83 /*!
84 84 \fn QPen QChartAxis::shadesPen() const
85 85 \brief Returns pen used to draw shades.
86 86 \sa setShadesPen()
87 87 */
88 88
89 89 /*!
90 90 \fn QBrush QChartAxis::shadesBrush() const
91 91 \brief Returns brush used to draw shades.
92 92 \sa setShadesBrush()
93 93 */
94 94
95 95 /*!
96 96 \fn qreal QChartAxis::min() const
97 97 \brief Returns minimum value on the axis.
98 98 \sa setMin()
99 99 */
100 100
101 101 /*!
102 102 \fn qreal QChartAxis::max() const
103 103 \brief Returns maximim value on the axis.
104 104 \sa setMax()
105 105 */
106 106
107 107 /*!
108 108 \fn void QChartAxis::minChanged(qreal min)
109 109 \brief Axis emits signal when \a min of axis has changed.
110 110 */
111 111
112 112 /*!
113 113 \fn void QChartAxis::maxChanged(qreal max)
114 114 \brief Axis emits signal when \a max of axis has changed.
115 115 */
116 116
117 117 /*!
118 118 \fn void QChartAxis::rangeChanged(qreal min, qreal max)
119 119 \brief Axis emits signal when \a min or \a max of axis has changed.
120 120 */
121 121
122 122 /*!
123 123 \fn int QChartAxis::ticksCount() const
124 124 \brief Return number of ticks on the axis
125 125 \sa setTicksCount()
126 126 */
127 127
128 128 /*!
129 129 \fn void QChartAxis::updated()
130 130 \brief \internal
131 131 */
132 132
133 133 /*!
134 134 \fn void QChartAxis::handleAxisRangeChanged(qreal min, qreal max)
135 135 \brief \internal \a min \a max
136 136 */
137 137
138 138 /*!
139 139 Constructs new axis object which is a child of \a parent. Ownership is taken by
140 140 QChatView or QChart when axis added.
141 141 */
142 142
143 143 QChartAxis::QChartAxis(QObject *parent) : QObject(parent),
144 144 m_axisVisible(true),
145 145 m_gridLineVisible(true),
146 146 m_labelsVisible(true),
147 147 m_labelsAngle(0),
148 148 m_shadesVisible(false),
149 149 m_shadesOpacity(1.0),
150 150 m_min(0),
151 151 m_max(0),
152 152 m_ticksCount(5),
153 153 m_niceNumbers(false)
154 154 {
155 155
156 156 }
157 157
158 158 /*!
159 159 Destructor of the axis object. When axis is added to chart, chart object takes ownership.
160 160 */
161 161
162 162 QChartAxis::~QChartAxis()
163 163 {
164 164 }
165 165
166 166 /*!
167 167 Sets \a pen used to draw axis line and ticks.
168 168 */
169 169 void QChartAxis::setAxisPen(const QPen &pen)
170 170 {
171 171 if (pen != m_axisPen) {
172 172 m_axisPen = pen;
173 173 emit updated();
174 174 }
175 175 }
176 176
177 177 /*!
178 178 Sets if axis and ticks are \a visible.
179 179 */
180 180 void QChartAxis::setAxisVisible(bool visible)
181 181 {
182 182 if (m_axisVisible != visible) {
183 183 m_axisVisible = visible;
184 184 emit updated();
185 185 }
186 186 }
187 187
188 188 /*!
189 189 Sets if grid line is \a visible.
190 190 */
191 191 void QChartAxis::setGridLineVisible(bool visible)
192 192 {
193 193 if (m_gridLineVisible != visible) {
194 194 m_gridLineVisible = visible;
195 195 emit updated();
196 196 }
197 197 }
198 198
199 199 /*!
200 200 Sets \a pen used to draw grid line.
201 201 */
202 202 void QChartAxis::setGridLinePen(const QPen &pen)
203 203 {
204 204 if (m_gridLinePen != pen) {
205 205 m_gridLinePen = pen;
206 206 emit updated();
207 207 }
208 208 }
209 209
210 210 /*!
211 211 Sets if axis' labels are \a visible.
212 212 */
213 213 void QChartAxis::setLabelsVisible(bool visible)
214 214 {
215 215 if (m_labelsVisible != visible) {
216 216 m_labelsVisible = visible;
217 217 emit updated();
218 218 }
219 219 }
220 220
221 221 /*!
222 222 Sets \a pen used to draw labels.
223 223 */
224 224 void QChartAxis::setLabelsPen(const QPen &pen)
225 225 {
226 226 if (m_labelsPen != pen) {
227 227 m_labelsPen = pen;
228 228 emit updated();
229 229 }
230 230 }
231 231
232 232 /*!
233 233 Sets \a brush used to draw labels.
234 234 */
235 235 void QChartAxis::setLabelsBrush(const QBrush &brush)
236 236 {
237 237 if (m_labelsBrush != brush) {
238 238 m_labelsBrush = brush;
239 239 emit updated();
240 240 }
241 241 }
242 242
243 243 /*!
244 244 Sets \a font used to draw labels.
245 245 */
246 246 void QChartAxis::setLabelsFont(const QFont &font)
247 247 {
248 248 if (m_labelsFont != font) {
249 249 m_labelsFont = font;
250 250 emit updated();
251 251 }
252 252 }
253 253
254 254 /*!
255 255 Sets \a angle for all the labels on given axis.
256 256 */
257 257 void QChartAxis::setLabelsAngle(int angle)
258 258 {
259 259 if (m_labelsAngle != angle) {
260 260 m_labelsAngle = angle;
261 261 emit updated();
262 262 }
263 263 }
264 264
265 265 /*!
266 266 Sets if shades are \a visible.
267 267 */
268 268 void QChartAxis::setShadesVisible(bool visible)
269 269 {
270 270 if (m_shadesVisible != visible) {
271 271 m_shadesVisible = visible;
272 272 emit updated();
273 273 }
274 274 }
275 275
276 276 /*!
277 277 Sets \a pen used to draw shades.
278 278 */
279 279 void QChartAxis::setShadesPen(const QPen &pen)
280 280 {
281 281 if (m_shadesPen != pen) {
282 282 m_shadesPen = pen;
283 283 emit updated();
284 284 }
285 285 }
286 286
287 287 /*!
288 288 Sets \a brush used to draw shades.
289 289 */
290 290 void QChartAxis::setShadesBrush(const QBrush &brush)
291 291 {
292 292 if (m_shadesBrush != brush) {
293 293 m_shadesBrush = brush;
294 294 emit updated();
295 295 }
296 296 }
297 297
298 298 /*!
299 299 Sets \a opacity of the shades.
300 300 */
301 301 void QChartAxis::setShadesOpacity(qreal opacity)
302 302 {
303 303 if (m_shadesOpacity != opacity) {
304 304 m_shadesOpacity=opacity;
305 305 emit updated();
306 306 }
307 307 }
308 308
309 309 /*!
310 310 Sets \a min value on the axis.
311 311 */
312 312 void QChartAxis::setMin(qreal min)
313 313 {
314 314 setRange(min,m_max);
315 315 }
316 316
317 317 /*!
318 318 Sets \a max value on the axis.
319 319 */
320 320 void QChartAxis::setMax(qreal max)
321 321 {
322 322 setRange(m_min,max);
323 323 }
324 324
325 325 /*!
326 326 Sets range from \a min to \a max on the axis.
327 327 */
328 328 void QChartAxis::setRange(qreal min, qreal max)
329 329 {
330 330 bool changed = false;
331 if (m_min != min) {
331 if (!qFuzzyIsNull(m_min - min)) {
332 332 m_min = min;
333 333 changed = true;
334 334 emit minChanged(min);
335 335 }
336 336
337 if (m_max != max) {
337 if (!qFuzzyIsNull(m_max - max)) {
338 338 m_max = max;
339 339 changed = true;
340 340 emit maxChanged(max);
341 341 }
342 342
343 343 if (changed) {
344 344 emit rangeChanged(m_min,m_max);
345 345 emit this->changed(m_min, m_max, m_ticksCount, m_niceNumbers);
346 346 }
347 347 }
348 348
349 349 /*!
350 350 Sets \a count for ticks on the axis.
351 351 */
352 352 void QChartAxis::setTicksCount(int count)
353 353 {
354 354 if (m_ticksCount != count) {
355 355 m_ticksCount = count;
356 356 emit ticksCountChanged(count);
357 357 emit changed(m_min, m_max, m_ticksCount, m_niceNumbers);
358 358 }
359 359 }
360 360
361 361 /*!
362 362 Sets axis, shades, labels and grid lines to be visible.
363 363 */
364 364 void QChartAxis::show()
365 365 {
366 366 m_axisVisible=true;
367 367 m_gridLineVisible=true;
368 368 m_labelsVisible=true;
369 369 m_shadesVisible=true;
370 370 emit updated();
371 371 }
372 372
373 373 /*!
374 374 Sets axis, shades, labels and grid lines to not be visible.
375 375 */
376 376 void QChartAxis::hide()
377 377 {
378 378 m_axisVisible = false;
379 379 m_gridLineVisible = false;
380 380 m_labelsVisible = false;
381 381 m_shadesVisible = false;
382 382 emit updated();
383 383 }
384 384
385 385 void QChartAxis::handleAxisRangeChanged(qreal min, qreal max,int count)
386 386 {
387 387 setRange(min,max);
388 388 setTicksCount(count);
389 389 }
390 390
391 391 void QChartAxis::setNiceNumbers(bool enabled)
392 392 {
393 393 if (m_niceNumbers != enabled){
394 394 m_niceNumbers = enabled;
395 395 emit changed(m_min, m_max, m_ticksCount, m_niceNumbers);
396 396 }
397 397 }
398 398
399 399 #include "moc_qchartaxis.cpp"
400 400
401 401 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,215 +1,215
1 1 #include "barchartitem_p.h"
2 2 #include "bar_p.h"
3 3 #include "barvalue_p.h"
4 4 #include "qbarset.h"
5 5 #include "qbarseries.h"
6 6 #include "qchart.h"
7 7 #include "qchartaxis.h"
8 8 #include "qchartaxiscategories.h"
9 9 #include "chartpresenter_p.h"
10 10 #include "chartanimator_p.h"
11 11 #include "chartdataset_p.h"
12 12 #include <QDebug>
13 13 #include <QToolTip>
14 14
15 15 QTCOMMERCIALCHART_BEGIN_NAMESPACE
16 16
17 17 BarChartItem::BarChartItem(QBarSeries *series, ChartPresenter *presenter) :
18 18 ChartItem(presenter),
19 19 m_layoutSet(false),
20 20 m_series(series)
21 21 {
22 22 connect(series, SIGNAL(showToolTip(QPoint,QString)), this, SLOT(showToolTip(QPoint,QString)));
23 23 connect(series, SIGNAL(updatedBars()), this, SLOT(handleLayoutChanged()));
24 24 //TODO: connect(series,SIGNAL("position or size has changed"), this, SLOT(handleLayoutChanged()));
25 25 connect(series, SIGNAL(restructuredBar(int)), this, SLOT(handleModelChanged(int)));
26 26 setZValue(ChartPresenter::BarSeriesZValue);
27 27 dataChanged();
28 28 }
29 29
30 30 BarChartItem::~BarChartItem()
31 31 {
32 32 disconnect(this,SLOT(showToolTip(QPoint,QString)));
33 33 }
34 34
35 35 void BarChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
36 36 {
37 37 if (!m_layoutSet) {
38 38 qWarning() << "BarChartItem::paint called without layout set. Aborting.";
39 39 return;
40 40 }
41 41
42 42 foreach(QGraphicsItem* i, childItems())
43 43 i->paint(painter,option,widget);
44 44 }
45 45
46 46 QRectF BarChartItem::boundingRect() const
47 47 {
48 48 return m_rect;
49 49 }
50 50
51 51 void BarChartItem::dataChanged()
52 52 {
53 53 // TODO: performance optimizations. Do we really need to delete and create items every time data is changed or can we reuse them?
54 54 // Delete old bars
55 55 foreach (QGraphicsItem *item, childItems())
56 56 delete item;
57 57
58 58 m_bars.clear();
59 59 m_floatingValues.clear();
60 60 m_layout.clear();
61 61
62 62 // Create new graphic items for bars
63 63 for (int c = 0; c < m_series->categoryCount(); c++) {
64 64 QString category = m_series->categoryName(c);
65 65 for (int s = 0; s < m_series->barsetCount(); s++) {
66 66 QBarSet *set = m_series->barsetAt(s);
67 67 Bar *bar = new Bar(category,this);
68 68 childItems().append(bar);
69 69 m_bars.append(bar);
70 70 connect(bar, SIGNAL(clicked(QString)), set, SIGNAL(clicked(QString)));
71 71 connect(bar, SIGNAL(rightClicked(QString)), set, SIGNAL(rightClicked(QString)));
72 72 connect(bar, SIGNAL(hoverEntered(QPoint)), set, SLOT(barHoverEnterEvent(QPoint)));
73 73 connect(bar, SIGNAL(hoverLeaved()), set, SLOT(barHoverLeaveEvent()));
74 74 m_layout.append(QRectF(0, 0, 0, 0));
75 75 }
76 76 }
77 77
78 78 // Create floating values
79 79 for (int category = 0; category < m_series->categoryCount(); category++) {
80 80 for (int s = 0; s < m_series->barsetCount(); s++) {
81 81 QBarSet *set = m_series->barsetAt(s);
82 82 BarValue *value = new BarValue(*set, this);
83 83 childItems().append(value);
84 84 m_floatingValues.append(value);
85 85 connect(set, SIGNAL(toggleFloatingValues()), value, SLOT(toggleVisible()));
86 86 }
87 87 }
88 88 }
89 89 QVector<QRectF> BarChartItem::calculateLayout()
90 90 {
91 91 QVector<QRectF> layout;
92 92
93 93 // Use temporary qreals for accurancy (we might get some compiler warnings... :)
94 94 qreal categoryCount = m_series->categoryCount();
95 95 qreal setCount = m_series->barsetCount();
96 96
97 97 qreal width = geometry().width();
98 98 qreal height = geometry().height();
99 99
100 100 qreal max = m_series->max();
101 101
102 102 // Domain:
103 103 if (m_domainMaxY > max) {
104 104 max = m_domainMaxY;
105 105 }
106 106
107 107 qreal scale = (height / max);
108 108 qreal categoryWidth = width / categoryCount;
109 109 qreal barWidth = categoryWidth / (setCount+1);
110 110
111 111 int itemIndex(0);
112 112 for (int category = 0; category < categoryCount; category++) {
113 113 qreal xPos = categoryWidth * category + barWidth / 2;
114 114 qreal yPos = height;
115 115 for (int set = 0; set < setCount; set++) {
116 116 qreal barHeight = m_series->valueAt(set, category) * scale;
117 117 Bar* bar = m_bars.at(itemIndex);
118 118
119 119 QRectF rect(xPos, yPos - barHeight, barWidth, barHeight);
120 120 layout.append(rect);
121 121 bar->setPen(m_series->barsetAt(set)->pen());
122 122 bar->setBrush(m_series->barsetAt(set)->brush());
123 123 itemIndex++;
124 124 xPos += barWidth;
125 125 }
126 126 }
127 127
128 128 // Position floating values
129 129 itemIndex = 0;
130 130 for (int category = 0; category < m_series->categoryCount(); category++) {
131 131 qreal xPos = categoryWidth * category + barWidth;
132 132 qreal yPos = height;
133 133 for (int set=0; set < m_series->barsetCount(); set++) {
134 134 qreal barHeight = m_series->valueAt(set, category) * scale;
135 135 BarValue* value = m_floatingValues.at(itemIndex);
136 136
137 137 QBarSet* barSet = m_series->barsetAt(set);
138 138 value->resize(100, 50); // TODO: proper layout for this.
139 139 value->setPos(xPos, yPos-barHeight / 2);
140 140 value->setPen(barSet->floatingValuePen());
141 141
142 if (m_series->valueAt(set,category) != 0) {
142 if (!qFuzzyIsNull(m_series->valueAt(set,category))) {
143 143 value->setValueString(QString::number(m_series->valueAt(set, category)));
144 144 } else {
145 145 value->setValueString(QString(""));
146 146 }
147 147
148 148 itemIndex++;
149 149 xPos += barWidth;
150 150 }
151 151 }
152 152
153 153 return layout;
154 154 }
155 155
156 156 void BarChartItem::applyLayout(const QVector<QRectF> &layout)
157 157 {
158 158 if (animator())
159 159 animator()->updateLayout(this, m_layout, layout);
160 160 else
161 161 setLayout(layout);
162 162 }
163 163
164 164 void BarChartItem::setLayout(const QVector<QRectF> &layout)
165 165 {
166 166 m_layout = layout;
167 167
168 168 for (int i=0; i < m_bars.count(); i++)
169 169 m_bars.at(i)->setRect(layout.at(i));
170 170
171 171 update();
172 172 }
173 173
174 174 //handlers
175 175
176 176 void BarChartItem::handleModelChanged(int index)
177 177 {
178 178 Q_UNUSED(index)
179 179 dataChanged();
180 180 }
181 181
182 182 void BarChartItem::handleDomainChanged(qreal minX, qreal maxX, qreal minY, qreal maxY)
183 183 {
184 184 m_domainMinX = minX;
185 185 m_domainMaxX = maxX;
186 186 m_domainMinY = minY;
187 187 m_domainMaxY = maxY;
188 188 handleLayoutChanged();
189 189 }
190 190
191 191 void BarChartItem::handleGeometryChanged(const QRectF &rect)
192 192 {
193 193 m_rect = rect;
194 194 handleLayoutChanged();
195 195 m_layoutSet = true;
196 196 setPos(rect.topLeft());
197 197 }
198 198
199 199 void BarChartItem::handleLayoutChanged()
200 200 {
201 201 QVector<QRectF> layout = calculateLayout();
202 202 applyLayout(layout);
203 203 update();
204 204 }
205 205
206 206
207 207 void BarChartItem::showToolTip(QPoint pos, QString tip)
208 208 {
209 209 // TODO: cool tooltip instead of default
210 210 QToolTip::showText(pos, tip);
211 211 }
212 212
213 213 #include "moc_barchartitem_p.cpp"
214 214
215 215 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,81 +1,81
1 1 #include "percentbarchartitem_p.h"
2 2 #include "bar_p.h"
3 3 #include "barvalue_p.h"
4 4 #include "qbarset.h"
5 5 #include <QDebug>
6 6
7 7 QTCOMMERCIALCHART_BEGIN_NAMESPACE
8 8
9 9 PercentBarChartItem::PercentBarChartItem(QBarSeries *series, ChartPresenter *presenter) :
10 10 BarChartItem(series, presenter)
11 11 {
12 12 }
13 13
14 14 QVector<QRectF> PercentBarChartItem::calculateLayout()
15 15 {
16 16 QVector<QRectF> layout;
17 17
18 18 // Use temporary qreals for accurancy (we might get some compiler warnings... :)
19 19 qreal width = geometry().width();
20 20 qreal height = geometry().height();
21 21
22 22 qreal categoryCount = m_series->categoryCount();
23 23 qreal barWidth = width / (m_series->categoryCount() * 2);
24 24 qreal xStep = width / categoryCount;
25 25 qreal xPos = xStep / 2 - barWidth / 2;
26 26
27 27 int itemIndex(0);
28 28 for (int category = 0; category < categoryCount; category++) {
29 29 qreal colSum = m_series->categorySum(category);
30 30 qreal scale = (height / colSum);
31 31 qreal yPos = height;
32 32 for (int set=0; set < m_series->barsetCount(); set++) {
33 33 qreal barHeight = m_series->valueAt(set, category) * scale;
34 34 Bar* bar = m_bars.at(itemIndex);
35 35 bar->setPen(m_series->barsetAt(set)->pen());
36 36 bar->setBrush(m_series->barsetAt(set)->brush());
37 37 QRectF rect(xPos, yPos-barHeight, barWidth, barHeight);
38 38 layout.append(rect);
39 39 itemIndex++;
40 40 yPos -= barHeight;
41 41 }
42 42 xPos += xStep;
43 43 }
44 44
45 45 // Position floating values
46 46 itemIndex = 0;
47 47 xPos = (width/categoryCount);
48 48 for (int category=0; category < m_series->categoryCount(); category++) {
49 49 qreal yPos = height;
50 50 qreal colSum = m_series->categorySum(category);
51 51 qreal scale = (height / colSum);
52 52 for (int set=0; set < m_series->barsetCount(); set++) {
53 53 qreal barHeight = m_series->valueAt(set,category) * scale;
54 54 BarValue* value = m_floatingValues.at(itemIndex);
55 55
56 56 QBarSet* barSet = m_series->barsetAt(set);
57 57 value->resize(100, 50); // TODO: proper layout for this.
58 58 value->setPos(xPos, yPos-barHeight / 2);
59 59 value->setPen(barSet->floatingValuePen());
60 60
61 if (m_series->valueAt(set,category) != 0) {
61 if (!qFuzzyIsNull(m_series->valueAt(set,category))) {
62 62 int p = m_series->percentageAt(set,category) * 100;
63 63 QString vString(QString::number(p));
64 64 vString.truncate(3);
65 65 vString.append("%");
66 66 value->setValueString(vString);
67 67 } else {
68 68 value->setValueString(QString(""));
69 69 }
70 70
71 71 itemIndex++;
72 72 yPos -= barHeight;
73 73 }
74 74 xPos += xStep;
75 75 }
76 76 return layout;
77 77 }
78 78
79 79 #include "moc_percentbarchartitem_p.cpp"
80 80
81 81 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,83 +1,83
1 1 #include "stackedbarchartitem_p.h"
2 2 #include "bar_p.h"
3 3 #include "barvalue_p.h"
4 4 #include "qbarset.h"
5 5 #include <QDebug>
6 6
7 7 QTCOMMERCIALCHART_BEGIN_NAMESPACE
8 8
9 9 StackedBarChartItem::StackedBarChartItem(QBarSeries *series, ChartPresenter *presenter) :
10 10 BarChartItem(series, presenter)
11 11 {
12 12 }
13 13
14 14 StackedBarChartItem::~StackedBarChartItem()
15 15 {
16 16 }
17 17
18 18 QVector<QRectF> StackedBarChartItem::calculateLayout()
19 19 {
20 20 QVector<QRectF> layout;
21 21 // Use temporary qreals for accurancy (we might get some compiler warnings... :)
22 22
23 23 qreal maxSum = m_series->maxCategorySum();
24 24 // Domain:
25 25 if (m_domainMaxY > maxSum) {
26 26 maxSum = m_domainMaxY;
27 27 }
28 28
29 29 qreal height = geometry().height();
30 30 qreal width = geometry().width();
31 31 qreal scale = (height / m_series->maxCategorySum());
32 32 qreal categotyCount = m_series->categoryCount();
33 33 qreal barWidth = width / (categotyCount * 2);
34 34 qreal xStep = width / categotyCount;
35 35 qreal xPos = xStep / 2 - barWidth / 2;
36 36
37 37 int itemIndex(0);
38 38 for (int category = 0; category < categotyCount; category++) {
39 39 qreal yPos = height;
40 40 for (int set=0; set < m_series->barsetCount(); set++) {
41 41 qreal barHeight = m_series->valueAt(set, category) * scale;
42 42 Bar* bar = m_bars.at(itemIndex);
43 43 bar->setPen(m_series->barsetAt(set)->pen());
44 44 bar->setBrush(m_series->barsetAt(set)->brush());
45 45 QRectF rect(xPos, yPos-barHeight, barWidth, barHeight);
46 46 layout.append(rect);
47 47 itemIndex++;
48 48 yPos -= barHeight;
49 49 }
50 50 xPos += xStep;
51 51 }
52 52
53 53 // Position floating values
54 54 itemIndex = 0;
55 55 xPos = (width/categotyCount);
56 56 for (int category=0; category < m_series->categoryCount(); category++) {
57 57 qreal yPos = height;
58 58 for (int set=0; set < m_series->barsetCount(); set++) {
59 59 qreal barHeight = m_series->valueAt(set, category) * scale;
60 60 BarValue* value = m_floatingValues.at(itemIndex);
61 61
62 62 QBarSet* barSet = m_series->barsetAt(set);
63 63 value->resize(100, 50); // TODO: proper layout for this.
64 64 value->setPos(xPos, yPos-barHeight / 2);
65 65 value->setPen(barSet->floatingValuePen());
66 66
67 if (m_series->valueAt(set, category) != 0) {
67 if (!qFuzzyIsNull(m_series->valueAt(set, category))) {
68 68 value->setValueString(QString::number(m_series->valueAt(set,category)));
69 69 } else {
70 70 value->setValueString(QString(""));
71 71 }
72 72
73 73 itemIndex++;
74 74 yPos -= barHeight;
75 75 }
76 76 xPos += xStep;
77 77 }
78 78 return layout;
79 79 }
80 80
81 81 #include "moc_stackedbarchartitem_p.cpp"
82 82
83 83 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,257 +1,257
1 1 #include "domain_p.h"
2 2 #include <cmath>
3 3
4 4 QTCOMMERCIALCHART_BEGIN_NAMESPACE
5 5
6 6 Domain::Domain(QObject* parent):QObject(parent),
7 7 m_minX(0),
8 8 m_maxX(0),
9 9 m_minY(0),
10 10 m_maxY(0),
11 11 m_tickXCount(5),
12 12 m_tickYCount(5),
13 13 m_niceNumbers(false)
14 14 {
15 15 }
16 16
17 17 Domain::~Domain()
18 18 {
19 19 }
20 20
21 21 void Domain::setRange(qreal minX, qreal maxX, qreal minY, qreal maxY)
22 22 {
23 23 setRange(minX, maxX, minY, maxY,m_tickXCount,m_tickYCount);
24 24 }
25 25
26 26 void Domain::setRange(qreal minX, qreal maxX, qreal minY, qreal maxY,int tickXCount,int tickYCount)
27 27 {
28 28 bool domainChanged = false;
29 29 bool tickXChanged = false;
30 30 bool tickYChanged = false;
31 31
32 32 if(m_tickXCount!=tickXCount) {
33 33 m_tickXCount=tickXCount;
34 34 tickXChanged=true;
35 35 }
36 36
37 37 if(m_tickYCount!=tickYCount) {
38 38 m_tickYCount=tickYCount;
39 39 tickYChanged=true;
40 40 }
41 41
42 if(m_minX!=minX || m_maxX!=maxX) {
42 if (!qFuzzyIsNull(m_minX - minX) || !qFuzzyIsNull(m_maxX - maxX)) {
43 43 if(m_niceNumbers) looseNiceNumbers(minX, maxX, m_tickXCount);
44 44 m_minX=minX;
45 45 m_maxX=maxX;
46 46 domainChanged=true;
47 47 tickXChanged=false;
48 48 emit rangeXChanged(minX,maxX, m_tickXCount);
49 49 }
50 50
51 if(m_minY!=minY || m_maxY!=maxY) {
51 if (!qFuzzyIsNull(m_minY - minY) || !qFuzzyIsNull(m_maxY - maxY)) {
52 52 if(m_niceNumbers) looseNiceNumbers(minY, maxY, m_tickYCount);
53 53 m_minY=minY;
54 54 m_maxY=maxY;
55 55 domainChanged=true;
56 56 tickYChanged=false;
57 57 emit rangeYChanged(minY,maxY, m_tickYCount);
58 58 }
59 59
60 60 if(domainChanged) {
61 61 emit this->domainChanged(m_minX, m_maxX, m_minY, m_maxY);
62 62 }
63 63
64 64 if(tickXChanged) {
65 65 emit rangeXChanged(minX,maxX, m_tickXCount);
66 66 }
67 67
68 68 if(tickYChanged) {
69 69 emit rangeYChanged(minY,maxY, m_tickYCount);
70 70 }
71 71 }
72 72
73 73 void Domain::setRangeX(qreal min, qreal max)
74 74 {
75 75 setRange(min,max,m_minY, m_maxY);
76 76 }
77 77
78 78 void Domain::setRangeX(qreal min, qreal max, int tickCount)
79 79 {
80 80 setRange(min,max,m_minY, m_maxY,tickCount,m_tickYCount);
81 81 }
82 82
83 83 void Domain::setRangeY(qreal min, qreal max)
84 84 {
85 85 setRange(m_minX, m_maxX, min, max);
86 86 }
87 87
88 88 void Domain::setRangeY(qreal min, qreal max,int tickCount)
89 89 {
90 90 setRange(m_minX, m_maxX, min, max,m_tickXCount,tickCount);
91 91 }
92 92
93 93 void Domain::setMinX(qreal min)
94 94 {
95 95 setRange(min, m_maxX, m_minY, m_maxY);
96 96 }
97 97
98 98 void Domain::setMaxX(qreal max)
99 99 {
100 100 setRange(m_minX, max, m_minY, m_maxY);
101 101 }
102 102
103 103 void Domain::setMinY(qreal min)
104 104 {
105 105 setRange(m_minX, m_maxX, min, m_maxY);
106 106 }
107 107
108 108 void Domain::setMaxY(qreal max)
109 109 {
110 110 setRange(m_minX, m_maxX, m_minY, max);
111 111 }
112 112
113 113 qreal Domain::spanX() const
114 114 {
115 115 Q_ASSERT(m_maxX >= m_minX);
116 116 return m_maxX - m_minX;
117 117 }
118 118
119 119 qreal Domain::spanY() const
120 120 {
121 121 Q_ASSERT(m_maxY >= m_minY);
122 122 return m_maxY - m_minY;
123 123 }
124 124
125 125 bool Domain::isEmpty() const
126 126 {
127 return spanX()==0 || spanY()==0;
127 return qFuzzyIsNull(spanX()) || qFuzzyIsNull(spanY());
128 128 }
129 129
130 130 void Domain::zoomIn(const QRectF& rect, const QSizeF& size)
131 131 {
132 132 qreal dx = spanX() / size.width();
133 133 qreal dy = spanY() / size.height();
134 134
135 135 m_maxX = m_minX + dx * rect.right();
136 136 m_minX = m_minX + dx * rect.left();
137 137 m_minY = m_maxY - dy * rect.bottom();
138 138 m_maxY = m_maxY - dy * rect.top();
139 139
140 140 if(m_niceNumbers) {
141 141 looseNiceNumbers(m_minX, m_maxX, m_tickXCount);
142 142 looseNiceNumbers(m_minY, m_maxY, m_tickYCount);
143 143 }
144 144
145 145 emit domainChanged(m_minX, m_maxX, m_minY, m_maxY);
146 146 emit rangeXChanged(m_minX, m_maxX, m_tickXCount);
147 147 emit rangeYChanged(m_minY, m_maxY, m_tickYCount);
148 148 }
149 149
150 150 void Domain::zoomOut(const QRectF& rect, const QSizeF& size)
151 151 {
152 152 qreal dx = spanX() / rect.width();
153 153 qreal dy = spanY() / rect.height();
154 154
155 155 m_minX = m_maxX - dx * rect.right();
156 156 m_maxX = m_minX + dx * size.width();
157 157 m_maxY = m_minY + dy * rect.bottom();
158 158 m_minY = m_maxY - dy * size.height();
159 159
160 160 if(m_niceNumbers) {
161 161 looseNiceNumbers(m_minX, m_maxX, m_tickXCount);
162 162 looseNiceNumbers(m_minY, m_maxY, m_tickYCount);
163 163 }
164 164
165 165 emit domainChanged(m_minX, m_maxX, m_minY, m_maxY);
166 166 emit rangeXChanged(m_minX, m_maxX, m_tickXCount);
167 167 emit rangeYChanged(m_minY, m_maxY, m_tickYCount);
168 168 }
169 169
170 170 void Domain::move(int dx,int dy,const QSizeF& size)
171 171 {
172 172 qreal x = spanX() / size.width();
173 173 qreal y = spanY() / size.height();
174 174
175 175 if(dx!=0) {
176 176 m_minX = m_minX + x * dx;
177 177 m_maxX = m_maxX + x * dx;
178 178 emit rangeXChanged(m_minX, m_maxX, m_tickXCount);
179 179 }
180 180 if(dy!=0) {
181 181 m_minY = m_minY + y * dy;
182 182 m_maxY = m_maxY + y * dy;
183 183 emit rangeYChanged(m_minY, m_maxY, m_tickYCount);
184 184 }
185 185
186 186 emit domainChanged(m_minX, m_maxX, m_minY, m_maxY);
187 187 }
188 188
189 189 void Domain::handleAxisXChanged(qreal min,qreal max,int tickXCount,bool niceNumbers)
190 190 {
191 191 m_niceNumbers=niceNumbers;
192 192 setRange(min,max,m_minY, m_maxY,tickXCount,m_tickYCount);
193 193 }
194 194
195 195 void Domain::handleAxisYChanged(qreal min,qreal max,int tickYCount,bool niceNumbers)
196 196 {
197 197 m_niceNumbers=niceNumbers;
198 198 setRange(m_minX, m_maxX, min, max,m_tickXCount,tickYCount);
199 199 }
200 200
201 201 //algorithm defined by Paul S.Heckbert GraphicalGems I
202 202
203 203 void Domain::looseNiceNumbers(qreal &min, qreal &max, int &ticksCount)
204 204 {
205 205 qreal range = niceNumber(max-min,true); //range with ceiling
206 206 qreal step = niceNumber(range/(ticksCount-1),false);
207 207 min = floor(min/step);
208 208 max = ceil(max/step);
209 209 ticksCount = int(max-min) +1;
210 210 min*=step;
211 211 max*=step;
212 212 }
213 213
214 214 //nice numbers can be expressed as form of 1*10^n, 2* 10^n or 5*10^n
215 215
216 216 qreal Domain::niceNumber(qreal x,bool ceiling)
217 217 {
218 218 qreal z = pow(10,floor(log10(x))); //find corresponding number of the form of 10^n than is smaller than x
219 219 qreal q = x/z;//q<10 && q>=1;
220 220
221 221 if(ceiling) {
222 222 if(q <= 1.0) q=1;
223 223 else if(q <= 2.0) q=2;
224 224 else if(q <= 5.0) q=5;
225 225 else q=10;
226 226 }
227 227 else {
228 228 if(q < 1.5) q=1;
229 229 else if(q < 3.0) q=2;
230 230 else if(q < 7.0) q=5;
231 231 else q=10;
232 232 }
233 233 return q*z;
234 234 }
235 235
236 236 bool operator== (const Domain &domain1, const Domain &domain2)
237 237 {
238 return (domain1.m_maxX == domain2.m_maxX &&
239 domain1.m_maxY == domain2.m_maxY &&
240 domain1.m_minX == domain2.m_minX &&
241 domain1.m_minY == domain2.m_minY);
238 return (qFuzzyIsNull(domain1.m_maxX - domain2.m_maxX) &&
239 qFuzzyIsNull(domain1.m_maxY - domain2.m_maxY) &&
240 qFuzzyIsNull(domain1.m_minX - domain2.m_minX) &&
241 qFuzzyIsNull(domain1.m_minY - domain2.m_minY));
242 242 }
243 243
244 244 bool operator!= (const Domain &domain1, const Domain &domain2)
245 245 {
246 246 return !(domain1 == domain2);
247 247 }
248 248
249 249 QDebug operator<<(QDebug dbg, const Domain &domain)
250 250 {
251 251 dbg.nospace() << "Domain("<<domain.m_minX<<','<<domain.m_maxX<<','<<domain.m_minY<<','<<domain.m_maxY<<')' << domain.m_tickXCount << "," << domain.m_tickYCount ;
252 252 return dbg.maybeSpace();
253 253 }
254 254
255 255 #include "moc_domain_p.cpp"
256 256
257 257 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,110 +1,110
1 1 #ifndef PIESLICEDATA_P_H
2 2 #define PIESLICEDATA_P_H
3 3
4 4 #include <qchartglobal.h>
5 5 #include <QPen>
6 6 #include <QBrush>
7 7 #include <QDebug>
8 8
9 9 QTCOMMERCIALCHART_BEGIN_NAMESPACE
10 10
11 11 template <class T>
12 12 class Themed : public T
13 13 {
14 14 public:
15 15 Themed():m_isThemed(true) {}
16 16
17 17 inline T &operator=(const T &other) { return T::operator =(other); }
18 18
19 19 inline bool operator!=(const T &other) { return T::operator !=(other); }
20 20 inline bool operator!=(const Themed &other)
21 21 {
22 22 if (T::operator !=(other))
23 23 return true;
24 24
25 25 if (m_isThemed != other.m_isThemed)
26 26 return true;
27 27
28 28 return false;
29 29 }
30 30
31 31 inline void setThemed(bool state) { m_isThemed = state; }
32 32 inline bool isThemed() const { return m_isThemed; }
33 33
34 34 private:
35 35 bool m_isThemed;
36 36 };
37 37
38 38 class PieSliceData
39 39 {
40 40 public:
41 41 PieSliceData()
42 42 {
43 43 m_value = 0;
44 44
45 45 m_isExploded = false;
46 46 m_explodeDistanceFactor = 0.15;
47 47
48 48 m_isLabelVisible = false;
49 49 m_labelArmLengthFactor = 0.15;
50 50
51 51 m_percentage = 0;
52 52 m_radius = 0;
53 53 m_startAngle = 0;
54 54 m_angleSpan = 0;
55 55 }
56 56
57 57 bool operator!=(const PieSliceData &other)
58 58 {
59 if (m_value != other.m_value)
59 if (!qFuzzyIsNull(m_value - other.m_value))
60 60 return true;
61 61
62 62 if (m_slicePen != other.m_slicePen ||
63 63 m_sliceBrush != other.m_sliceBrush)
64 64 return true;
65 65
66 66 if (m_isExploded != other.m_isExploded ||
67 67 !qFuzzyIsNull(m_explodeDistanceFactor - other.m_explodeDistanceFactor))
68 68 return true;
69 69
70 70 if (m_isLabelVisible != other.m_isLabelVisible ||
71 71 m_labelText != other.m_labelText ||
72 72 m_labelFont != other.m_labelFont ||
73 73 !qFuzzyIsNull(m_labelArmLengthFactor - other.m_labelArmLengthFactor) ||
74 74 m_labelPen != other.m_labelPen)
75 75 return true;
76 76
77 77 if (!qFuzzyIsNull(m_percentage - other.m_percentage) ||
78 78 m_center != other.m_center ||
79 79 !qFuzzyIsNull(m_radius - other.m_radius) ||
80 80 !qFuzzyIsNull(m_startAngle - other.m_startAngle) ||
81 81 !qFuzzyIsNull(m_angleSpan - other.m_angleSpan))
82 82 return true;
83 83
84 84 return false;
85 85 }
86 86
87 87 qreal m_value;
88 88
89 89 Themed<QPen> m_slicePen;
90 90 Themed<QBrush> m_sliceBrush;
91 91
92 92 bool m_isExploded;
93 93 qreal m_explodeDistanceFactor;
94 94
95 95 bool m_isLabelVisible;
96 96 QString m_labelText;
97 97 Themed<QFont> m_labelFont;
98 98 qreal m_labelArmLengthFactor;
99 99 Themed<QPen> m_labelPen;
100 100
101 101 qreal m_percentage;
102 102 QPointF m_center;
103 103 qreal m_radius;
104 104 qreal m_startAngle;
105 105 qreal m_angleSpan;
106 106 };
107 107
108 108 QTCOMMERCIALCHART_END_NAMESPACE
109 109
110 110 #endif // PIESLICEDATA_P_H
@@ -1,98 +1,98
1 1 #include "qscatterseries.h"
2 2 #include "qchart.h"
3 3
4 4 /*!
5 5 \class QScatterSeries
6 6 \brief The QScatterSeries class is used for making scatter charts.
7 7
8 8 \mainclass
9 9
10 10 The scatter data is displayed as a collection of points on the chart. Each point determines the position on the horizontal axis
11 11 and the vertical axis.
12 12
13 13 \image scatterchart.png
14 14
15 15 Creating basic scatter chart is simple:
16 16 \code
17 17 QScatterSeries* series = new QScatterSeries();
18 18 series->add(0, 6);
19 19 series->add(2, 4);
20 20 ...
21 21 chartView->addSeries(series);
22 22 \endcode
23 23 */
24 24
25 25 /*!
26 26 \enum QScatterSeries::MarkerShape
27 27
28 28 This enum describes the shape used when rendering marker items.
29 29
30 30 \value MarkerShapeCircle
31 31 \value MarkerShapeRectangle
32 32 */
33 33
34 34 /*!
35 35 \fn QChartSeriesType QScatterSeries::type() const
36 36 \brief Returns QChartSeries::SeriesTypeScatter.
37 37 \sa QSeries, QSeriesType
38 38 */
39 39
40 40 QTCOMMERCIALCHART_BEGIN_NAMESPACE
41 41
42 42 /*!
43 43 Constructs a series object which is a child of \a parent.
44 44 */
45 45 QScatterSeries::QScatterSeries(QObject *parent) :
46 46 QXYSeries(parent),
47 47 m_shape(QScatterSeries::MarkerShapeCircle),
48 48 m_size(15.0)
49 49 {
50 50 }
51 51
52 52 /*!
53 53 Destroys the object. Note that adding series to QChart transfers the ownership to the chart.
54 54 */
55 55 QScatterSeries::~QScatterSeries()
56 56 {
57 57 }
58 58
59 59 /*!
60 60 Returns the shape used for drawing markers.
61 61 */
62 62 QScatterSeries::MarkerShape QScatterSeries::shape() const
63 63 {
64 64 return m_shape;
65 65 }
66 66
67 67 /*!
68 68 Overrides the default shape of the marker items with a user defined \a shape. The default shape
69 69 is defined by chart theme setting.
70 70 */
71 71 void QScatterSeries::setShape(MarkerShape shape)
72 72 {
73 73 if (m_shape != shape) {
74 74 m_shape = shape;
75 75 emit QXYSeries::updated();
76 76 }
77 77 }
78 78
79 79 /*!
80 80 Returns the size of the marker items.
81 81 */
82 82 qreal QScatterSeries::size() const
83 83 {
84 84 return m_size;
85 85 }
86 86
87 87 /*!
88 88 Set the \a size of the marker items. The default size is 9.0.
89 89 */
90 90 void QScatterSeries::setSize(qreal size)
91 91 {
92 if (m_size != size) {
92 if (!qFuzzyIsNull(m_size - size)) {
93 93 m_size = size;
94 94 emit updated();
95 95 }
96 96 }
97 97
98 98 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,156 +1,156
1 1 #include "xychartitem_p.h"
2 2 #include "qxyseries.h"
3 3 #include "chartpresenter_p.h"
4 4 #include "chartanimator_p.h"
5 5 #include <QPainter>
6 6 #include <QGraphicsSceneMouseEvent>
7 7
8 8
9 9 QTCOMMERCIALCHART_BEGIN_NAMESPACE
10 10
11 11 //TODO: optimize : remove points which are not visible
12 12
13 13 XYChartItem::XYChartItem(QXYSeries *series, ChartPresenter *presenter):ChartItem(presenter),
14 14 m_minX(0),
15 15 m_maxX(0),
16 16 m_minY(0),
17 17 m_maxY(0),
18 18 m_series(series)
19 19 {
20 20 connect(series,SIGNAL(pointReplaced(int)),this,SLOT(handlePointReplaced(int)));
21 21 connect(series,SIGNAL(pointAdded(int)),this,SLOT(handlePointAdded(int)));
22 22 connect(series,SIGNAL(pointRemoved(int)),this,SLOT(handlePointRemoved(int)));
23 23 connect(this,SIGNAL(clicked(const QPointF&)),series,SIGNAL(clicked(const QPointF&)));
24 24 }
25 25
26 26 QPointF XYChartItem::calculateGeometryPoint(const QPointF &point) const
27 27 {
28 28 const qreal deltaX = m_size.width()/(m_maxX-m_minX);
29 29 const qreal deltaY = m_size.height()/(m_maxY-m_minY);
30 30 qreal x = (point.x() - m_minX)* deltaX;
31 31 qreal y = (point.y() - m_minY)*-deltaY + m_size.height();
32 32 return QPointF(x,y);
33 33 }
34 34
35 35
36 36 QPointF XYChartItem::calculateGeometryPoint(int index) const
37 37 {
38 38 const qreal deltaX = m_size.width()/(m_maxX-m_minX);
39 39 const qreal deltaY = m_size.height()/(m_maxY-m_minY);
40 40 qreal x = (m_series->x(index) - m_minX)* deltaX;
41 41 qreal y = (m_series->y(index) - m_minY)*-deltaY + m_size.height();
42 42 return QPointF(x,y);
43 43 }
44 44
45 45 QVector<QPointF> XYChartItem::calculateGeometryPoints() const
46 46 {
47 47 const qreal deltaX = m_size.width()/(m_maxX-m_minX);
48 48 const qreal deltaY = m_size.height()/(m_maxY-m_minY);
49 49
50 50 QVector<QPointF> points;
51 51 points.reserve(m_series->count());
52 52 for (int i = 0; i < m_series->count(); ++i) {
53 53 qreal x = (m_series->x(i) - m_minX)* deltaX;
54 54 qreal y = (m_series->y(i) - m_minY)*-deltaY + m_size.height();
55 55 points << QPointF(x,y);
56 56 }
57 57 return points;
58 58 }
59 59
60 60 QPointF XYChartItem::calculateDomainPoint(const QPointF &point) const
61 61 {
62 62 const qreal deltaX = m_size.width()/(m_maxX-m_minX);
63 63 const qreal deltaY = m_size.height()/(m_maxY-m_minY);
64 64 qreal x = point.x()/deltaX +m_minX;
65 65 qreal y = (point.y()-m_size.height())/(-deltaY)+ m_minY;
66 66 return QPointF(x,y);
67 67 }
68 68
69 69 void XYChartItem::updateLayout(QVector<QPointF> &oldPoints, QVector<QPointF> &newPoints,int index)
70 70 {
71 71 if (animator()) {
72 72 animator()->updateLayout(this,oldPoints,newPoints,index);
73 73 } else {
74 74 setLayout(newPoints);
75 75 }
76 76 }
77 77
78 78 void XYChartItem::setLayout(QVector<QPointF> &points)
79 79 {
80 80 m_points = points;
81 81 update();
82 82 }
83 83
84 84 //handlers
85 85
86 86 void XYChartItem::handlePointAdded(int index)
87 87 {
88 88 Q_ASSERT(index<m_series->count());
89 89 Q_ASSERT(index>=0);
90 90 QPointF point = calculateGeometryPoint(index);
91 91 QVector<QPointF> points = m_points;
92 92 points.insert(index,point);
93 93 updateLayout(m_points,points,index);
94 94 update();
95 95 }
96 96 void XYChartItem::handlePointRemoved(int index)
97 97 {
98 98 Q_ASSERT(index<m_series->count() + 1);
99 99 Q_ASSERT(index>=0);
100 100 QVector<QPointF> points = m_points;
101 101 points.remove(index);
102 102 updateLayout(m_points,points,index);
103 103 update();
104 104 }
105 105
106 106 void XYChartItem::handlePointReplaced(int index)
107 107 {
108 108 Q_ASSERT(index<m_series->count());
109 109 Q_ASSERT(index>=0);
110 110 QPointF point = calculateGeometryPoint(index);
111 111 QVector<QPointF> points = m_points;
112 112 points.replace(index,point);
113 113 updateLayout(m_points,points,index);
114 114 update();
115 115 }
116 116
117 117 void XYChartItem::handleDomainChanged(qreal minX, qreal maxX, qreal minY, qreal maxY)
118 118 {
119 119 m_minX=minX;
120 120 m_maxX=maxX;
121 121 m_minY=minY;
122 122 m_maxY=maxY;
123 123
124 124 if (isEmpty()) return;
125 125 QVector<QPointF> points = calculateGeometryPoints();
126 126 updateLayout(m_points,points);
127 127 update();
128 128 }
129 129
130 130 void XYChartItem::handleGeometryChanged(const QRectF &rect)
131 131 {
132 132 Q_ASSERT(rect.isValid());
133 133 m_size=rect.size();
134 134 m_clipRect=rect.translated(-rect.topLeft());
135 135 setPos(rect.topLeft());
136 136
137 137 if (isEmpty()) return;
138 138 QVector<QPointF> points = calculateGeometryPoints();
139 139 updateLayout(m_points,points);
140 140 update();
141 141 }
142 142
143 143
144 144 bool XYChartItem::isEmpty()
145 145 {
146 return !m_clipRect.isValid() || m_maxX - m_minX == 0 || m_maxY - m_minY ==0 ;
146 return !m_clipRect.isValid() || qFuzzyIsNull(m_maxX - m_minX) || qFuzzyIsNull(m_maxY - m_minY);
147 147 }
148 148
149 149 void XYChartItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
150 150 {
151 151 emit clicked(calculateDomainPoint(event->pos()));
152 152 }
153 153
154 154 #include "moc_xychartitem_p.cpp"
155 155
156 156 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,579 +1,579
1 1 #include <QtTest/QtTest>
2 2 #include <private/domain_p.h>
3 3 #include <qchartaxis.h>
4 4
5 5 QTCOMMERCIALCHART_USE_NAMESPACE
6 6
7 7 Q_DECLARE_METATYPE(Domain*)
8 8 Q_DECLARE_METATYPE(QSizeF)
9 9
10 10 class tst_Domain : public QObject
11 11 {
12 12 Q_OBJECT
13 13
14 14 public slots:
15 15 void initTestCase();
16 16 void cleanupTestCase();
17 17 void init();
18 18 void cleanup();
19 19
20 20 private slots:
21 21 void domain();
22 22 void handleAxisRangeXChanged_data();
23 23 void handleAxisRangeXChanged();
24 24 void handleAxisRangeYChanged_data();
25 25 void handleAxisRangeYChanged();
26 26 void isEmpty_data();
27 27 void isEmpty();
28 28 void maxX_data();
29 29 void maxX();
30 30 void maxY_data();
31 31 void maxY();
32 32 void minX_data();
33 33 void minX();
34 34 void minY_data();
35 35 void minY();
36 36 void operatorEquals_data();
37 37 void operatorEquals();
38 38 void setRange_data();
39 39 void setRange();
40 40 void setRangeX_data();
41 41 void setRangeX();
42 42 void setRangeY_data();
43 43 void setRangeY();
44 44 void spanX_data();
45 45 void spanX();
46 46 void spanY_data();
47 47 void spanY();
48 48 void zoom_data();
49 49 void zoom();
50 50 };
51 51
52 52 void tst_Domain::initTestCase()
53 53 {
54 54 }
55 55
56 56 void tst_Domain::cleanupTestCase()
57 57 {
58 58 }
59 59
60 60 void tst_Domain::init()
61 61 {
62 62 }
63 63
64 64 void tst_Domain::cleanup()
65 65 {
66 66 }
67 67
68 68 void tst_Domain::domain()
69 69 {
70 70 Domain domain;
71 71
72 72 QCOMPARE(domain.isEmpty(), true);
73 73 QCOMPARE(domain.maxX(), 0.0);
74 74 QCOMPARE(domain.maxY(), 0.0);
75 75 QCOMPARE(domain.minX(), 0.0);
76 76 QCOMPARE(domain.minY(), 0.0);
77 77 }
78 78
79 79 void tst_Domain::handleAxisRangeXChanged_data()
80 80 {
81 81 QTest::addColumn<qreal>("min");
82 82 QTest::addColumn<qreal>("max");
83 83 QTest::newRow("-1 1") << -1.0 << 1.0;
84 84 QTest::newRow("0 1") << 0.0 << 1.0;
85 85 QTest::newRow("-1 0") << -1.0 << 0.0;
86 86 }
87 87
88 88 void tst_Domain::handleAxisRangeXChanged()
89 89 {
90 90 QFETCH(qreal, min);
91 91 QFETCH(qreal, max);
92 92
93 93 Domain domain;
94 94
95 95 QSignalSpy spy0(&domain, SIGNAL(domainChanged(qreal, qreal, qreal, qreal)));
96 96 QSignalSpy spy1(&domain, SIGNAL(rangeXChanged(qreal, qreal)));
97 97 QSignalSpy spy2(&domain, SIGNAL(rangeYChanged(qreal, qreal)));
98 98
99 99 domain.handleAxisXChanged(min, max);
100 100
101 101 QList<QVariant> arg0 = spy0.first();
102 QVERIFY(arg0.at(0).toReal() == min);
103 QVERIFY(arg0.at(1).toReal() == max);
102 QVERIFY(qFuzzyIsNull(arg0.at(0).toReal() - min));
103 QVERIFY(qFuzzyIsNull(arg0.at(1).toReal() - max));
104 104
105 105 QList<QVariant> arg1 = spy1.first();
106 QVERIFY(arg1.at(0).toReal() == min);
107 QVERIFY(arg1.at(1).toReal() == max);
106 QVERIFY(qFuzzyIsNull(arg1.at(0).toReal() - min));
107 QVERIFY(qFuzzyIsNull(arg1.at(1).toReal() - max));
108 108
109 109 QCOMPARE(spy0.count(), 1);
110 110 QCOMPARE(spy1.count(), 1);
111 111 QCOMPARE(spy2.count(), 0);
112 112
113 113 }
114 114
115 115 void tst_Domain::handleAxisRangeYChanged_data()
116 116 {
117 117 QTest::addColumn<qreal>("min");
118 118 QTest::addColumn<qreal>("max");
119 119 QTest::newRow("-1 1") << -1.0 << 1.0;
120 120 QTest::newRow("0 1") << 0.0 << 1.0;
121 121 QTest::newRow("-1 0") << -1.0 << 0.0;
122 122 }
123 123
124 124
125 125 void tst_Domain::handleAxisRangeYChanged()
126 126 {
127 127 QFETCH(qreal, min);
128 128 QFETCH(qreal, max);
129 129
130 130 Domain domain;
131 131
132 132 QSignalSpy spy0(&domain, SIGNAL(domainChanged(qreal, qreal, qreal, qreal)));
133 133 QSignalSpy spy1(&domain, SIGNAL(rangeXChanged(qreal, qreal)));
134 134 QSignalSpy spy2(&domain, SIGNAL(rangeYChanged(qreal, qreal)));
135 135
136 136 domain.handleAxisYChanged(min, max,5);
137 137
138 138 QList<QVariant> arg0 = spy0.first();
139 QVERIFY(arg0.at(2).toReal() == min);
140 QVERIFY(arg0.at(3).toReal() == max);
139 QVERIFY(qFuzzyIsNull(arg0.at(2).toReal() - min));
140 QVERIFY(qFuzzyIsNull(arg0.at(3).toReal() - max));
141 141
142 142 QList<QVariant> arg1 = spy2.first();
143 QVERIFY(arg1.at(0).toReal() == min);
144 QVERIFY(arg1.at(1).toReal() == max);
143 QVERIFY(qFuzzyIsNull(arg1.at(0).toReal() - min));
144 QVERIFY(qFuzzyIsNull(arg1.at(1).toReal() - max));
145 145
146 146 QCOMPARE(spy0.count(), 1);
147 147 QCOMPARE(spy1.count(), 0);
148 148 QCOMPARE(spy2.count(), 1);
149 149 }
150 150
151 151 void tst_Domain::isEmpty_data()
152 152 {
153 153 QTest::addColumn<qreal>("minX");
154 154 QTest::addColumn<qreal>("maxX");
155 155 QTest::addColumn<qreal>("minY");
156 156 QTest::addColumn<qreal>("maxY");
157 157 QTest::addColumn<bool>("isEmpty");
158 158 QTest::newRow("0 0 0 0") << 0.0 << 0.0 << 0.0 << 0.0 << true;
159 159 QTest::newRow("0 1 0 0") << 0.0 << 1.0 << 0.0 << 0.0 << true;
160 160 QTest::newRow("0 0 0 1") << 0.0 << 1.0 << 0.0 << 0.0 << true;
161 161 QTest::newRow("0 1 0 1") << 0.0 << 1.0 << 0.0 << 1.0 << false;
162 162 }
163 163
164 164 void tst_Domain::isEmpty()
165 165 {
166 166 QFETCH(qreal, minX);
167 167 QFETCH(qreal, maxX);
168 168 QFETCH(qreal, minY);
169 169 QFETCH(qreal, maxY);
170 170 QFETCH(bool, isEmpty);
171 171
172 172 Domain domain;
173 173 domain.setRange(minX,maxX,minY,maxY);
174 174 QCOMPARE(domain.isEmpty(), isEmpty);
175 175 }
176 176
177 177 void tst_Domain::maxX_data()
178 178 {
179 179 QTest::addColumn<qreal>("maxX1");
180 180 QTest::addColumn<qreal>("maxX2");
181 181 QTest::addColumn<int>("count");
182 182 QTest::newRow("1") << 0.0 << 1.0 << 1;
183 183 QTest::newRow("1.0") << 1.0 << 1.0 << 1;
184 184 QTest::newRow("2.0") << 1.0 << 0.0 << 2;
185 185 }
186 186
187 187 void tst_Domain::maxX()
188 188 {
189 189 QFETCH(qreal, maxX1);
190 190 QFETCH(qreal, maxX2);
191 191 QFETCH(int, count);
192 192
193 193 Domain domain;
194 194
195 195 QSignalSpy spy0(&domain, SIGNAL(domainChanged(qreal, qreal, qreal, qreal)));
196 196 QSignalSpy spy1(&domain, SIGNAL(rangeXChanged(qreal, qreal)));
197 197 QSignalSpy spy2(&domain, SIGNAL(rangeYChanged(qreal, qreal)));
198 198
199 199 domain.setMaxX(maxX1);
200 200 QCOMPARE(domain.maxX(), maxX1);
201 201 domain.setMaxX(maxX2);
202 202 QCOMPARE(domain.maxX(), maxX2);
203 203
204 204
205 205 QCOMPARE(spy0.count(), count);
206 206 QCOMPARE(spy1.count(), count);
207 207 QCOMPARE(spy2.count(), 0);
208 208
209 209 }
210 210
211 211 void tst_Domain::maxY_data()
212 212 {
213 213 QTest::addColumn<qreal>("maxY1");
214 214 QTest::addColumn<qreal>("maxY2");
215 215 QTest::addColumn<int>("count");
216 216 QTest::newRow("1") << 0.0 << 1.0 << 1;
217 217 QTest::newRow("1.0") << 1.0 << 1.0 << 1;
218 218 QTest::newRow("2.0") << 1.0 << 0.0 << 2;
219 219 }
220 220
221 221
222 222 void tst_Domain::maxY()
223 223 {
224 224 QFETCH(qreal, maxY1);
225 225 QFETCH(qreal, maxY2);
226 226 QFETCH(int, count);
227 227
228 228 Domain domain;
229 229
230 230 QSignalSpy spy0(&domain, SIGNAL(domainChanged(qreal, qreal, qreal, qreal)));
231 231 QSignalSpy spy1(&domain, SIGNAL(rangeXChanged(qreal, qreal)));
232 232 QSignalSpy spy2(&domain, SIGNAL(rangeYChanged(qreal, qreal)));
233 233
234 234 domain.setMaxY(maxY1);
235 235 QCOMPARE(domain.maxY(), maxY1);
236 236 domain.setMaxY(maxY2);
237 237 QCOMPARE(domain.maxY(), maxY2);
238 238
239 239 QCOMPARE(spy0.count(), count);
240 240 QCOMPARE(spy1.count(), 0);
241 241 QCOMPARE(spy2.count(), count);
242 242 }
243 243
244 244 void tst_Domain::minX_data()
245 245 {
246 246 QTest::addColumn<qreal>("minX1");
247 247 QTest::addColumn<qreal>("minX2");
248 248 QTest::addColumn<int>("count");
249 249 QTest::newRow("1") << 0.0 << 1.0 << 1;
250 250 QTest::newRow("1.0") << 1.0 << 1.0 << 1;
251 251 QTest::newRow("2.0") << 1.0 << 0.0 << 2;
252 252 }
253 253
254 254
255 255 void tst_Domain::minX()
256 256 {
257 257 QFETCH(qreal, minX1);
258 258 QFETCH(qreal, minX2);
259 259 QFETCH(int, count);
260 260
261 261 Domain domain;
262 262
263 263 QSignalSpy spy0(&domain, SIGNAL(domainChanged(qreal, qreal, qreal, qreal)));
264 264 QSignalSpy spy1(&domain, SIGNAL(rangeXChanged(qreal, qreal)));
265 265 QSignalSpy spy2(&domain, SIGNAL(rangeYChanged(qreal, qreal)));
266 266
267 267 domain.setMinX(minX1);
268 268 QCOMPARE(domain.minX(), minX1);
269 269 domain.setMinX(minX2);
270 270 QCOMPARE(domain.minX(), minX2);
271 271
272 272 QCOMPARE(spy0.count(), count);
273 273 QCOMPARE(spy1.count(), count);
274 274 QCOMPARE(spy2.count(), 0);
275 275 }
276 276
277 277 void tst_Domain::minY_data()
278 278 {
279 279 QTest::addColumn<qreal>("minY1");
280 280 QTest::addColumn<qreal>("minY2");
281 281 QTest::addColumn<int>("count");
282 282 QTest::newRow("1") << 0.0 << 1.0 << 1;
283 283 QTest::newRow("1.0") << 1.0 << 1.0 << 1;
284 284 QTest::newRow("2.0") << 1.0 << 0.0 << 2;
285 285 }
286 286
287 287 void tst_Domain::minY()
288 288 {
289 289 QFETCH(qreal, minY1);
290 290 QFETCH(qreal, minY2);
291 291 QFETCH(int, count);
292 292
293 293 Domain domain;
294 294
295 295 QSignalSpy spy0(&domain, SIGNAL(domainChanged(qreal, qreal, qreal, qreal)));
296 296 QSignalSpy spy1(&domain, SIGNAL(rangeXChanged(qreal, qreal)));
297 297 QSignalSpy spy2(&domain, SIGNAL(rangeYChanged(qreal, qreal)));
298 298
299 299 domain.setMinY(minY1);
300 300 QCOMPARE(domain.minY(), minY1);
301 301 domain.setMinY(minY2);
302 302 QCOMPARE(domain.minY(), minY2);
303 303
304 304 QCOMPARE(spy0.count(), count);
305 305 QCOMPARE(spy1.count(), 0);
306 306 QCOMPARE(spy2.count(), count);
307 307 }
308 308
309 309 void tst_Domain::operatorEquals_data()
310 310 {
311 311
312 312 QTest::addColumn<Domain*>("domain1");
313 313 QTest::addColumn<Domain*>("domain2");
314 314 QTest::addColumn<bool>("equals");
315 315 QTest::addColumn<bool>("notEquals");
316 316 Domain* a;
317 317 Domain* b;
318 318 a = new Domain();
319 319 a->setRange(0,100,0,100);
320 320 b = new Domain();
321 321 b->setRange(0,100,0,100);
322 322 QTest::newRow("equals") << a << b << true <<false;
323 323 a = new Domain();
324 324 a->setRange(0,100,0,100);
325 325 b = new Domain();
326 326 b->setRange(0,100,0,1);
327 327 QTest::newRow("equals") << a << b << false << true;
328 328 a = new Domain();
329 329 a->setRange(0,100,0,100);
330 330 b = new Domain();
331 331 b->setRange(0,1,0,100);
332 332 QTest::newRow("equals") << a << b << false << true;
333 333
334 334 }
335 335
336 336 void tst_Domain::operatorEquals()
337 337 {
338 338 QFETCH(Domain*, domain1);
339 339 QFETCH(Domain*, domain2);
340 340 QFETCH(bool, equals);
341 341 QFETCH(bool, notEquals);
342 342
343 343 Domain domain;
344 344
345 345 QSignalSpy spy0(&domain, SIGNAL(domainChanged(qreal, qreal, qreal, qreal)));
346 346 QSignalSpy spy1(&domain, SIGNAL(rangeXChanged(qreal, qreal)));
347 347 QSignalSpy spy2(&domain, SIGNAL(rangeYChanged(qreal, qreal)));
348 348
349 349 QCOMPARE(*domain1==*domain2, equals);
350 350 QCOMPARE(*domain1!=*domain2, notEquals);
351 351
352 352 QCOMPARE(spy0.count(), 0);
353 353 QCOMPARE(spy1.count(), 0);
354 354 QCOMPARE(spy2.count(), 0);
355 355 }
356 356
357 357 void tst_Domain::setRange_data()
358 358 {
359 359 QTest::addColumn<qreal>("minX");
360 360 QTest::addColumn<qreal>("maxX");
361 361 QTest::addColumn<qreal>("minY");
362 362 QTest::addColumn<qreal>("maxY");
363 363 QTest::newRow("1,2,1,2") << 1.0 << 2.0 << 1.0 << 2.0;
364 364 QTest::newRow("1,3,1,3") << 1.0 << 3.0 << 1.0 << 3.0;
365 365 QTest::newRow("-1,5,-2,-1") << -1.0 << 5.0 << -2.0 << -1.0;
366 366 }
367 367
368 368 void tst_Domain::setRange()
369 369 {
370 370 QFETCH(qreal, minX);
371 371 QFETCH(qreal, maxX);
372 372 QFETCH(qreal, minY);
373 373 QFETCH(qreal, maxY);
374 374
375 375 Domain domain;
376 376
377 377 QSignalSpy spy0(&domain, SIGNAL(domainChanged(qreal, qreal, qreal, qreal)));
378 378 QSignalSpy spy1(&domain, SIGNAL(rangeXChanged(qreal, qreal)));
379 379 QSignalSpy spy2(&domain, SIGNAL(rangeYChanged(qreal, qreal)));
380 380
381 381 domain.setRange(minX, maxX, minY, maxY);
382 382
383 383 QCOMPARE(domain.minX(), minX);
384 384 QCOMPARE(domain.maxX(), maxX);
385 385 QCOMPARE(domain.minY(), minY);
386 386 QCOMPARE(domain.maxY(), maxY);
387 387
388 388 QCOMPARE(spy0.count(), 1);
389 389 QCOMPARE(spy1.count(), 1);
390 390 QCOMPARE(spy2.count(), 1);
391 391
392 392
393 393 }
394 394
395 395 void tst_Domain::setRangeX_data()
396 396 {
397 397 QTest::addColumn<qreal>("min");
398 398 QTest::addColumn<qreal>("max");
399 399 QTest::newRow("-1 1") << -1.0 << 1.0;
400 400 QTest::newRow("0 1") << 0.0 << 1.0;
401 401 QTest::newRow("-1 0") << -1.0 << 0.0;
402 402 }
403 403
404 404 void tst_Domain::setRangeX()
405 405 {
406 406 QFETCH(qreal, min);
407 407 QFETCH(qreal, max);
408 408
409 409 Domain domain;
410 410
411 411 QSignalSpy spy0(&domain, SIGNAL(domainChanged(qreal, qreal, qreal, qreal)));
412 412 QSignalSpy spy1(&domain, SIGNAL(rangeXChanged(qreal, qreal)));
413 413 QSignalSpy spy2(&domain, SIGNAL(rangeYChanged(qreal, qreal)));
414 414
415 415 domain.setRangeX(min, max);
416 416
417 417 QList<QVariant> arg0 = spy0.first();
418 QVERIFY(arg0.at(0).toReal() == min);
419 QVERIFY(arg0.at(1).toReal() == max);
418 QVERIFY(qFuzzyIsNull(arg0.at(0).toReal() - min));
419 QVERIFY(qFuzzyIsNull(arg0.at(1).toReal() - max));
420 420
421 421 QList<QVariant> arg1 = spy1.first();
422 QVERIFY(arg1.at(0).toReal() == min);
423 QVERIFY(arg1.at(1).toReal() == max);
422 QVERIFY(qFuzzyIsNull(arg1.at(0).toReal() - min));
423 QVERIFY(qFuzzyIsNull(arg1.at(1).toReal() - max));
424 424
425 425 QCOMPARE(spy0.count(), 1);
426 426 QCOMPARE(spy1.count(), 1);
427 427 QCOMPARE(spy2.count(), 0);
428 428 }
429 429
430 430 void tst_Domain::setRangeY_data()
431 431 {
432 432 QTest::addColumn<qreal>("min");
433 433 QTest::addColumn<qreal>("max");
434 434 QTest::newRow("-1 1") << -1.0 << 1.0;
435 435 QTest::newRow("0 1") << 0.0 << 1.0;
436 436 QTest::newRow("-1 0") << -1.0 << 0.0;
437 437 }
438 438
439 439 void tst_Domain::setRangeY()
440 440 {
441 441 QFETCH(qreal, min);
442 442 QFETCH(qreal, max);
443 443
444 444 Domain domain;
445 445
446 446 QSignalSpy spy0(&domain, SIGNAL(domainChanged(qreal, qreal, qreal, qreal)));
447 447 QSignalSpy spy1(&domain, SIGNAL(rangeXChanged(qreal, qreal)));
448 448 QSignalSpy spy2(&domain, SIGNAL(rangeYChanged(qreal, qreal)));
449 449
450 450 domain.setRangeY(min, max);
451 451
452 452 QList<QVariant> arg0 = spy0.first();
453 QVERIFY(arg0.at(2).toReal() == min);
454 QVERIFY(arg0.at(3).toReal() == max);
453 QVERIFY(qFuzzyIsNull(arg0.at(2).toReal() - min));
454 QVERIFY(qFuzzyIsNull(arg0.at(3).toReal() - max));
455 455
456 456 QList<QVariant> arg1 = spy2.first();
457 QVERIFY(arg1.at(0).toReal() == min);
458 QVERIFY(arg1.at(1).toReal() == max);
457 QVERIFY(qFuzzyIsNull(arg1.at(0).toReal() - min));
458 QVERIFY(qFuzzyIsNull(arg1.at(1).toReal() - max));
459 459
460 460 QCOMPARE(spy0.count(), 1);
461 461 QCOMPARE(spy1.count(), 0);
462 462 QCOMPARE(spy2.count(), 1);
463 463 }
464 464
465 465 void tst_Domain::spanX_data()
466 466 {
467 467 QTest::addColumn<qreal>("minX");
468 468 QTest::addColumn<qreal>("maxX");
469 469 QTest::addColumn<qreal>("spanX");
470 470 QTest::newRow("1 2 1") << 1.0 << 2.0 << 1.0;
471 471 QTest::newRow("0 2 2") << 1.0 << 2.0 << 1.0;
472 472 }
473 473
474 474 void tst_Domain::spanX()
475 475 {
476 476 QFETCH(qreal, minX);
477 477 QFETCH(qreal, maxX);
478 478 QFETCH(qreal, spanX);
479 479
480 480 Domain domain;
481 481
482 482 domain.setRangeX(minX,maxX);
483 483
484 484 QSignalSpy spy0(&domain, SIGNAL(domainChanged(qreal, qreal, qreal, qreal)));
485 485 QSignalSpy spy1(&domain, SIGNAL(rangeXChanged(qreal, qreal)));
486 486 QSignalSpy spy2(&domain, SIGNAL(rangeYChanged(qreal, qreal)));
487 487
488 488 QCOMPARE(domain.spanX(), spanX);
489 489
490 490 QCOMPARE(spy0.count(), 0);
491 491 QCOMPARE(spy1.count(), 0);
492 492 QCOMPARE(spy2.count(), 0);
493 493 }
494 494
495 495 void tst_Domain::spanY_data()
496 496 {
497 497 QTest::addColumn<qreal>("minY");
498 498 QTest::addColumn<qreal>("maxY");
499 499 QTest::addColumn<qreal>("spanY");
500 500 QTest::newRow("1 2 1") << 1.0 << 2.0 << 1.0;
501 501 QTest::newRow("0 2 2") << 1.0 << 2.0 << 1.0;
502 502 }
503 503
504 504 void tst_Domain::spanY()
505 505 {
506 506 QFETCH(qreal, minY);
507 507 QFETCH(qreal, maxY);
508 508 QFETCH(qreal, spanY);
509 509
510 510 Domain domain;
511 511
512 512 domain.setRangeY(minY,maxY);
513 513
514 514 QSignalSpy spy0(&domain, SIGNAL(domainChanged(qreal, qreal, qreal, qreal)));
515 515 QSignalSpy spy1(&domain, SIGNAL(rangeXChanged(qreal, qreal)));
516 516 QSignalSpy spy2(&domain, SIGNAL(rangeYChanged(qreal, qreal)));
517 517
518 518 QCOMPARE(domain.spanY(), spanY);
519 519
520 520 QCOMPARE(spy0.count(), 0);
521 521 QCOMPARE(spy1.count(), 0);
522 522 QCOMPARE(spy2.count(), 0);
523 523 }
524 524
525 525
526 526 void tst_Domain::zoom_data()
527 527 {
528 528 QTest::addColumn<QRectF>("rect0");
529 529 QTest::addColumn<QSizeF>("size0");
530 530 QTest::addColumn<QRectF>("rect1");
531 531 QTest::addColumn<QSizeF>("size1");
532 532 QTest::addColumn<QRectF>("rect2");
533 533 QTest::addColumn<QSizeF>("size2");
534 534 QTest::newRow("first") << QRectF(10,10,100,100) << QSizeF(1000,1000) << QRectF(20,20,100,100) << QSizeF(1000,1000) << QRectF(50,50,100,100) << QSizeF(1000,1000);
535 535 QTest::newRow("scound") << QRectF(10,10,50,50) << QSizeF(1000,1000) << QRectF(20,20,100,100) << QSizeF(1000,1000) << QRectF(50,50,100,100) << QSizeF(1000,1000);
536 536 QTest::newRow("third") << QRectF(10,10,10,10) << QSizeF(100,100) << QRectF(20,20,20,20) << QSizeF(100,100) << QRectF(50,50,50,50) << QSizeF(100,100);
537 537 }
538 538
539 539 void tst_Domain::zoom()
540 540 {
541 541 QFETCH(QRectF, rect0);
542 542 QFETCH(QSizeF, size0);
543 543 QFETCH(QRectF, rect1);
544 544 QFETCH(QSizeF, size1);
545 545 QFETCH(QRectF, rect2);
546 546 QFETCH(QSizeF, size2);
547 547
548 548 Domain domain;
549 549
550 550 domain.setRange(0,1000,0,1000);
551 551
552 552 QSignalSpy spy0(&domain, SIGNAL(domainChanged(qreal, qreal, qreal, qreal)));
553 553 QSignalSpy spy1(&domain, SIGNAL(rangeXChanged(qreal, qreal)));
554 554 QSignalSpy spy2(&domain, SIGNAL(rangeYChanged(qreal, qreal)));
555 555
556 556 Domain domain0;
557 557 domain0.setRange(domain.minX(),domain.maxX(),domain.minY(),domain.maxY());
558 558 domain.zoomIn(rect0, size0);
559 559 Domain domain1;
560 560 domain1.setRange(domain.minX(),domain.maxX(),domain.minY(),domain.maxY());
561 561 domain.zoomIn(rect1, size1);
562 562 Domain domain2;
563 563 domain2.setRange(domain.minX(),domain.maxX(),domain.minY(),domain.maxY());
564 564 domain.zoomIn(rect2, size2);
565 565 domain.zoomOut(rect2, size2);
566 566 QCOMPARE(domain == domain2,true);
567 567 domain.zoomOut(rect1, size1);
568 568 QCOMPARE(domain == domain1,true);
569 569 domain.zoomOut(rect0, size0);
570 570 QCOMPARE(domain == domain0,true);
571 571 QCOMPARE(spy0.count(), 6);
572 572 QCOMPARE(spy1.count(), 6);
573 573 QCOMPARE(spy2.count(), 6);
574 574
575 575 }
576 576
577 577 QTEST_MAIN(tst_Domain)
578 578 #include "tst_domain.moc"
579 579
General Comments 0
You need to be logged in to leave comments. Login now