Wt examples  4.10.4
Loading...
Searching...
No Matches
ChartConfig.C
Go to the documentation of this file.
1/*
2 * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3 *
4 * See the LICENSE file for terms of use.
5 */
6
7#include "ChartConfig.h"
8#include "PanelList.h"
9
10#include <iostream>
11
12#include <Wt/WAbstractItemModel.h>
13#include <Wt/WApplication.h>
14#include <Wt/WCheckBox.h>
15#include <Wt/WComboBox.h>
16#include <Wt/WDoubleValidator.h>
17#include <Wt/WDate.h>
18#include <Wt/WEnvironment.h>
19#include <Wt/WIntValidator.h>
20#include <Wt/WLineEdit.h>
21#include <Wt/WLocale.h>
22#include <Wt/WPanel.h>
23#include <Wt/WPushButton.h>
24#include <Wt/WStandardItemModel.h>
25#include <Wt/WTable.h>
26#include <Wt/WText.h>
27#include <Wt/WPainterPath.h>
28
29#include <Wt/Chart/WCartesianChart.h>
30
31using namespace Wt;
32using namespace Wt::Chart;
33
34namespace {
35 void addHeader(WTable *t, const char *value) {
36 t->elementAt(0, t->columnCount())->addWidget(std::make_unique<WText>(value));
37 }
38
39 void addEntry(const std::shared_ptr<WAbstractItemModel>& model, const char *value) {
40 model->insertRows(model->rowCount(), 1);
41 model->setData(model->rowCount()-1, 0, cpp17::any(std::string(value)));
42 }
43
44 void addEntry(const std::shared_ptr<WAbstractItemModel>& model, const WString &value) {
45 model->insertRows(model->rowCount(), 1);
46 model->setData(model->rowCount()-1, 0, cpp17::any(value));
47 }
48
49 bool getDouble(WLineEdit *edit, double& value) {
50 try {
51 value = WLocale::currentLocale().toDouble(edit->text());
52 return true;
53 } catch (...) {
54 return false;
55 }
56 }
57
58 int seriesIndexOf(WCartesianChart* chart, int modelColumn) {
59 for (unsigned i = 0; i < chart->series().size(); ++i)
60 if (chart->series()[i]->modelColumn() == modelColumn)
61 return i;
62
63 return -1;
64 }
65
66 WString axisName(Axis axis, int axisId)
67 {
68 if (axis == Axis::X) {
69 return Wt::utf8("X Axis {1}").arg(axisId + 1);
70 } else {
71 return Wt::utf8("Y axis {1}").arg(axisId + 1);
72 }
73 }
74}
75
76ChartConfig::ChartConfig(WCartesianChart *chart)
77 : WContainerWidget(),
78 chart_(chart),
79 fill_(FillRangeType::MinimumValue)
80{
81 chart_->setLegendStyle(chart_->legendFont(), WPen(WColor("black")),
82 WBrush(WColor(0xFF, 0xFA, 0xE5)));
83
84 PanelList *list = this->addWidget(std::make_unique<PanelList>());
85
86 std::shared_ptr<WIntValidator> sizeValidator
87 = std::make_shared<WIntValidator>(200,2000);
88 sizeValidator->setMandatory(true);
89
90 anyNumberValidator_ = std::make_shared<WDoubleValidator>();
91 anyNumberValidator_->setMandatory(true);
92
93 angleValidator_ = std::make_shared<WDoubleValidator>(-90, 90);
94 angleValidator_->setMandatory(true);
95
96 // ---- Chart properties ----
97
98 std::shared_ptr<WStandardItemModel> orientation
99 = std::make_shared<WStandardItemModel>(0,1);
100 addEntry(orientation, "Vertical");
101 addEntry(orientation, "Horizontal");
102
103 std::shared_ptr<WStandardItemModel> legendLocation
104 = std::make_shared<WStandardItemModel>(0,1);
105 addEntry(legendLocation, "Outside");
106 addEntry(legendLocation, "Inside");
107
108 std::shared_ptr<WStandardItemModel> legendSide
109 = std::make_shared<WStandardItemModel>(0,1);
110 addEntry(legendSide, "Top");
111 addEntry(legendSide, "Right");
112 addEntry(legendSide, "Bottom");
113 addEntry(legendSide, "Left");
114
115 std::shared_ptr<WStandardItemModel> legendAlignment
116 = std::make_shared<WStandardItemModel>(0,1);
117 addEntry(legendAlignment, "AlignLeft");
118 addEntry(legendAlignment, "AlignCenter");
119 addEntry(legendAlignment, "AlignRight");
120 addEntry(legendAlignment, "AlignTop");
121 addEntry(legendAlignment, "AlignMiddle");
122 addEntry(legendAlignment, "AlignBottom");
123
124 std::unique_ptr<WTable> chartConfig
125 = std::make_unique<WTable>();
126 chartConfig->setMargin(WLength::Auto, Side::Left | Side::Right);
127
128 int row = 0;
129 chartConfig->elementAt(row, 0)->addWidget(std::make_unique<WText>("Title:"));
130 titleEdit_ = chartConfig->elementAt(row,1)->addWidget(std::make_unique<WLineEdit>());
132 ++row;
133
134 chartConfig->elementAt(row, 0)->addWidget(std::make_unique<WText>("Width:"));
135 chartWidthEdit_ = chartConfig->elementAt(row,1)->addWidget(std::make_unique<WLineEdit>());
137 ->setText(WLocale::currentLocale().toString(chart_->width().value()));
138 chartWidthEdit_->setValidator(sizeValidator);
139 chartWidthEdit_->setMaxLength(4);
141 ++row;
142
143 chartConfig->elementAt(row, 0)->addWidget(std::make_unique<WText>("Height:"));
144 chartHeightEdit_ = chartConfig->elementAt(row,1)->addWidget(std::make_unique<WLineEdit>());
146 ->setText(WLocale::currentLocale().toString(chart_->height().value()));
147 chartHeightEdit_->setValidator(sizeValidator);
148 chartHeightEdit_->setMaxLength(4);
150 ++row;
151
152 chartConfig->elementAt(row, 0)->addWidget(std::make_unique<WText>("Orientation:"));
153 chartOrientationEdit_ = chartConfig->elementAt(row,1)->addWidget(std::make_unique<WComboBox>());
154 chartOrientationEdit_->setModel(orientation);
155 chartOrientationEdit_->setCurrentIndex(0);
157 ++row;
158
159 chartConfig->elementAt(row, 0)->addWidget(std::make_unique<WText>("Legend location:"));
160 legendLocationEdit_ = chartConfig->elementAt(row,1)->addWidget(std::make_unique<WComboBox>());
161 legendLocationEdit_->setModel(legendLocation);
162 legendLocationEdit_->setCurrentIndex(0);
164 ++row;
165
166 chartConfig->elementAt(row, 0)->addWidget(std::make_unique<WText>("Legend side:"));
167 legendSideEdit_ = chartConfig->elementAt(row,1)->addWidget(std::make_unique<WComboBox>());
168 legendSideEdit_->setModel(legendSide);
169 legendSideEdit_->setCurrentIndex(1);
171 ++row;
172
173 chartConfig->elementAt(row, 0)->addWidget(std::make_unique<WText>("Legend alignment:"));
174 legendAlignmentEdit_ = chartConfig->elementAt(row,1)->addWidget(std::make_unique<WComboBox>());
175 legendAlignmentEdit_->setModel(legendAlignment);
176 legendAlignmentEdit_->setCurrentIndex(4);
178 ++row;
179
180 chartConfig->elementAt(row, 0)->addWidget(std::make_unique<WText>("Border:"));
181 borderEdit_ = chartConfig->elementAt(row,1)->addWidget(std::make_unique<WCheckBox>());
182 borderEdit_->setChecked(false);
184 ++row;
185
186 for (int i = 0; i < chartConfig->rowCount(); ++i) {
187 chartConfig->elementAt(i, 0)->setStyleClass("tdhead");
188 chartConfig->elementAt(i, 1)->setStyleClass("tddata");
189 }
190
191 WPanel *p = list->addWidget("Chart properties", std::move(chartConfig));
192 p->setMargin(WLength::Auto, Side::Left | Side::Right);
193 p->resize(1160, WLength::Auto);
194 p->setMargin(20, Side::Top | Side::Bottom);
195
196 // ---- Series properties ----
197
198 std::shared_ptr<WStandardItemModel> types
199 = std::make_shared<WStandardItemModel>(0,1);
200 addEntry(types, "Points");
201 addEntry(types, "Line");
202 addEntry(types, "Curve");
203 addEntry(types, "Bar");
204 addEntry(types, "Line Area");
205 addEntry(types, "Curve Area");
206 addEntry(types, "Stacked Bar");
207 addEntry(types, "Stacked Line Area");
208 addEntry(types, "Stacked Curve Area");
209
210 std::shared_ptr<WStandardItemModel> markers
211 = std::make_shared<WStandardItemModel>(0,1);
212 addEntry(markers, "None");
213 addEntry(markers, "Square");
214 addEntry(markers, "Circle");
215 addEntry(markers, "Cross");
216 addEntry(markers, "X cross");
217 addEntry(markers, "Triangle");
218 addEntry(markers, "Pipe");
219 addEntry(markers, "Star");
220 addEntry(markers, "Inverted triangle");
221 addEntry(markers, "Asterisk");
222 addEntry(markers, "Diamond");
223
224 xAxesModel_ = std::make_shared<WStandardItemModel>(0, 1);
225 addEntry(xAxesModel_, axisName(Axis::X, 0));
226
227 yAxesModel_ = std::make_shared<WStandardItemModel>(0, 1);
228 addEntry(yAxesModel_, axisName(Axis::Y, 0));
229 addEntry(yAxesModel_, axisName(Axis::Y, 1));
230
231 std::shared_ptr<WStandardItemModel> labels
232 = std::make_shared<WStandardItemModel>(0,1);
233 addEntry(labels, "None");
234 addEntry(labels, "X");
235 addEntry(labels, "Y");
236 addEntry(labels, "X: Y");
237
238 std::unique_ptr<WTable> seriesConfig
239 = std::make_unique<WTable>();
240 WTable *seriesConfigPtr = seriesConfig.get();
241 seriesConfig->setMargin(WLength::Auto, Side::Left | Side::Right);
242 ::addHeader(seriesConfigPtr, "Name");
243 ::addHeader(seriesConfigPtr, "Enabled");
244 ::addHeader(seriesConfigPtr, "Type");
245 ::addHeader(seriesConfigPtr, "Marker");
246 ::addHeader(seriesConfigPtr, "X axis");
247 ::addHeader(seriesConfigPtr, "Y axis");
248 ::addHeader(seriesConfigPtr, "Legend");
249 ::addHeader(seriesConfigPtr, "Shadow");
250 ::addHeader(seriesConfigPtr, "Value labels");
251
252 seriesConfig->rowAt(0)->setStyleClass("trhead");
253
254 for (int j = 1; j < chart->model()->columnCount(); ++j) {
255 SeriesControl sc;
256
257 seriesConfig->elementAt(j,0)->addWidget(std::make_unique<WText>(chart->model()->headerData(j)));
258
259 sc.enabledEdit = seriesConfig->elementAt(j,1)->addWidget(std::make_unique<WCheckBox>());
261
262 sc.typeEdit = seriesConfig->elementAt(j,2)->addWidget(std::make_unique<WComboBox>());
263 sc.typeEdit->setModel(types);
264 sc.typeEdit->setCurrentIndex(0);
266
267 sc.markerEdit = seriesConfig->elementAt(j,3)->addWidget(std::make_unique<WComboBox>());
268 sc.markerEdit->setModel(markers);
269 sc.markerEdit->setCurrentIndex(0);
271
272 sc.xAxisEdit = seriesConfig->elementAt(j, 4)->addNew<WComboBox>();
273 sc.xAxisEdit->setModel(xAxesModel_);
274 sc.xAxisEdit->setCurrentIndex(0);
276
277 sc.yAxisEdit = seriesConfig->elementAt(j, 5)->addNew<WComboBox>();
278 sc.yAxisEdit->setModel(yAxesModel_);
279 sc.yAxisEdit->setCurrentIndex(0);
281
282 sc.legendEdit = seriesConfig->elementAt(j, 6)->addWidget(std::make_unique<WCheckBox>());
284
285 sc.shadowEdit = seriesConfig->elementAt(j, 7)->addWidget(std::make_unique<WCheckBox>());
287
288 sc.labelsEdit = seriesConfig->elementAt(j, 8)->addWidget(std::make_unique<WComboBox>());
289 sc.labelsEdit->setModel(labels);
290 sc.labelsEdit->setCurrentIndex(0);
292
293 int si = seriesIndexOf(chart, j);
294
295 if (si != -1) {
296 sc.enabledEdit->setChecked();
297 const WDataSeries& s = chart_->series(j);
298 switch (s.type()) {
299 case SeriesType::Point:
300 sc.typeEdit->setCurrentIndex(0); break;
301 case SeriesType::Line:
302 sc.typeEdit->setCurrentIndex(s.fillRange() != FillRangeType::None ?
303 (s.isStacked() ? 7 : 4) : 1); break;
304 case SeriesType::Curve:
305 sc.typeEdit->setCurrentIndex(s.fillRange() != FillRangeType::None ?
306 (s.isStacked() ? 8 : 5) : 2); break;
307 case SeriesType::Bar:
308 sc.typeEdit->setCurrentIndex(s.isStacked() ? 6 : 3);
309 }
310
311 sc.markerEdit->setCurrentIndex((int)s.marker());
312 sc.legendEdit->setChecked(s.isLegendEnabled());
313 sc.shadowEdit->setChecked(s.shadow() != WShadow());
314 }
315
316 seriesControls_.push_back(sc);
317
318 seriesConfig->rowAt(j)->setStyleClass("trdata");
319 }
320
321 p = list->addWidget("Series properties", std::move(seriesConfig));
322 p->expand();
323 p->setMargin(WLength::Auto, Side::Left | Side::Right);
324 p->resize(1160, WLength::Auto);
325 p->setMargin(20, Side::Top | Side::Bottom);
326
327 // ---- Axis properties ----
328
329 yScales_ = std::make_shared<WStandardItemModel>(0, 1);
330 addEntry(yScales_, "Linear scale");
331 addEntry(yScales_, "Log scale");
332
333 xScales_ = std::make_shared<WStandardItemModel>(0, 1);
334 addEntry(xScales_, "Categories");
335 addEntry(xScales_, "Linear scale");
336 addEntry(xScales_, "Log scale");
337 addEntry(xScales_, "Date scale");
338
339 auto axisConfig = std::make_unique<WContainerWidget>();
340 axisConfig_ = axisConfig->addNew<WTable>();
341 axisConfig_->setMargin(WLength::Auto, Side::Left | Side::Right);
342
343 ::addHeader(axisConfig_, "Axis");
344 ::addHeader(axisConfig_, "Visible");
345 ::addHeader(axisConfig_, "Scale");
346 ::addHeader(axisConfig_, "Automatic");
347 ::addHeader(axisConfig_, "Minimum");
348 ::addHeader(axisConfig_, "Maximum");
349 ::addHeader(axisConfig_, "Gridlines");
350 ::addHeader(axisConfig_, "Label angle");
351 ::addHeader(axisConfig_, "Title");
352 ::addHeader(axisConfig_, "Title orientation");
353 ::addHeader(axisConfig_, "Tick direction");
354 ::addHeader(axisConfig_, "Location");
355
356 axisConfig_->rowAt(0)->setStyleClass("trhead");
357
358 addAxis(Axis::X, 0);
359 addAxis(Axis::Y, 0);
360 addAxis(Axis::Y, 1);
361
362 WPushButton *addXAxisBtn =
363 axisConfig->addNew<WPushButton>(Wt::utf8("Add X axis"));
364 addXAxisBtn->clicked().connect(this, &ChartConfig::addXAxis);
365 WPushButton *clearXAxesBtn =
366 axisConfig->addNew<WPushButton>(Wt::utf8("Clear X axes"));
367 clearXAxesBtn->clicked().connect(this, &ChartConfig::clearXAxes);
368 WPushButton *addYAxisBtn =
369 axisConfig->addNew<WPushButton>(utf8("Add Y axis"));
370 addYAxisBtn->clicked().connect(this, &ChartConfig::addYAxis);
371 WPushButton *clearYAxesBtn =
372 axisConfig->addNew<WPushButton>(utf8("Clear Y axes"));
373 clearYAxesBtn->clicked().connect(this, &ChartConfig::clearYAxes);
374
375 p = list->addWidget("Axis properties", std::move(axisConfig));
376 p->setMargin(WLength::Auto, Side::Left | Side::Right);
377 p->resize(1160, WLength::Auto);
378 p->setMargin(20, Side::Top | Side::Bottom);
379
380 /*
381 * If we do not have JavaScript, then add a button to reflect changes to
382 * the chart.
383 */
384 if (!WApplication::instance()->environment().javaScript()) {
385 auto *b = this->addWidget(std::make_unique<WPushButton>());
386 b->setText("Update chart");
387 b->setInline(false); // so we can add margin to center horizontally
388 b->setMargin(WLength::Auto, Side::Left | Side::Right);
389 b->clicked().connect(this, &ChartConfig::update);
390 }
391}
392
393void ChartConfig::setValueFill(FillRangeType fill)
394{
395 fill_ = fill;
396}
397
399{
400 bool haveLegend = false;
401 std::vector<std::unique_ptr<WDataSeries>> series;
402
403 for (int i = 1; i < chart_->model()->columnCount(); ++i) {
405
406 if (sc.enabledEdit->isChecked()) {
407 std::unique_ptr<WDataSeries> s
408 = std::make_unique<WDataSeries>(i);
409
410 switch (sc.typeEdit->currentIndex()) {
411 case 0:
412 s->setType(SeriesType::Point);
413 if (sc.markerEdit->currentIndex() == 0)
414 sc.markerEdit->setCurrentIndex(1);
415 break;
416 case 1:
417 s->setType(SeriesType::Line);
418 break;
419 case 2:
420 s->setType(SeriesType::Curve);
421 break;
422 case 3:
423 s->setType(SeriesType::Bar);
424 break;
425 case 4:
426 s->setType(SeriesType::Line);
427 s->setFillRange(fill_);
428 break;
429 case 5:
430 s->setType(SeriesType::Curve);
431 s->setFillRange(fill_);
432 break;
433 case 6:
434 s->setType(SeriesType::Bar);
435 s->setStacked(true);
436 break;
437 case 7:
438 s->setType(SeriesType::Line);
439 s->setFillRange(fill_);
440 s->setStacked(true);
441 break;
442 case 8:
443 s->setType(SeriesType::Curve);
444 s->setFillRange(fill_);
445 s->setStacked(true);
446 }
447
448 //set WPainterPath to draw a pipe
449 if(sc.markerEdit->currentIndex() == static_cast<int>(MarkerType::Custom)){ //was customMarker before
450 WPainterPath pp = WPainterPath();
451 pp.moveTo(0, -6);
452 pp.lineTo(0, 6);
453 s->setCustomMarker(pp);
454 }
455
456 s->setMarker(static_cast<MarkerType>(sc.markerEdit->currentIndex()));
457
458 s->bindToXAxis(sc.xAxisEdit->currentIndex());
459 s->bindToYAxis(sc.yAxisEdit->currentIndex());
460
461 if (sc.legendEdit->isChecked()) {
462 s->setLegendEnabled(true);
463 haveLegend = true;
464 } else
465 s->setLegendEnabled(false);
466
467 if (sc.shadowEdit->isChecked()) {
468 s->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3));
469 } else
470 s->setShadow(WShadow());
471
472 switch (sc.labelsEdit->currentIndex()) {
473 case 1:
474 s->setLabelsEnabled(Axis::X);
475 break;
476 case 2:
477 s->setLabelsEnabled(Axis::Y);
478 break;
479 case 3:
480 s->setLabelsEnabled(Axis::X);
481 s->setLabelsEnabled(Axis::Y);
482 break;
483 }
484
485 series.push_back(std::move(s));
486 }
487 }
488
489 chart_->setSeries(std::move(series));
490
491 for (std::size_t i = 0; i < axisControls_.size(); ++i) {
492 AxisControl& sc = axisControls_[i];
493 WAxis& axis = static_cast<int>(i) < chart_->xAxisCount() ? chart_->xAxis(i) : chart_->yAxis(i - chart_->xAxisCount());
494
495 axis.setVisible(sc.visibleEdit->isChecked());
496
497 if (sc.scaleEdit->count() != 1) {
498 int k = sc.scaleEdit->currentIndex();
499 if (axis.id() != Axis::X)
500 k += 1;
501 else {
502 if (k == 0)
503 chart_->setType(ChartType::Category);
504 else
505 chart_->setType(ChartType::Scatter);
506 }
507
508 switch (k) {
509 case 1:
510 axis.setScale(AxisScale::Linear); break;
511 case 2:
512 axis.setScale(AxisScale::Log); break;
513 case 3:
514 axis.setScale(AxisScale::Date); break;
515 }
516 }
517
518 if (sc.autoEdit->isChecked())
519 axis.setAutoLimits(AxisValue::Minimum | AxisValue::Maximum);
520 else {
521 if (!(axis.autoLimits() & (AxisValue::Minimum | AxisValue::Maximum)).empty()) {
522 sc.minimumEdit->setText(WLocale::currentLocale()
523 .toString(axis.minimum()));
524 sc.maximumEdit->setText(WLocale::currentLocale()
525 .toString(axis.maximum()));
526 }
527 if (validate(sc.minimumEdit) && validate(sc.maximumEdit)) {
528 double min, max;
529 getDouble(sc.minimumEdit, min);
530 getDouble(sc.maximumEdit, max);
531
532 if (axis.scale() == AxisScale::Log)
533 if (min <= 0)
534 min = 0.0001;
535
536 if (axis.scale() == AxisScale::Date){
537 //the number of julian days until year 1986
538 WDate dMin = WDate(1900,1,1);
539 double gregDaysMin = (double)dMin.toJulianDay();
540 //the number of julian days until year 1988
541 WDate dMax = WDate(3000,1,1);
542 double gregDaysMax = (double)dMax.toJulianDay();
543
544 bool greg_year_validation =
545 (min > gregDaysMin &&
546 min < gregDaysMax &&
547 max > gregDaysMin &&
548 max < gregDaysMax);
549
550 if(!greg_year_validation){
551 min = gregDaysMin;
552 max = gregDaysMax;
553 }
554 }
555
556 axis.setRange(min, max);
557 }
558
559 }
560
561 if (validate(sc.labelAngleEdit)) {
562 double angle;
563 getDouble(sc.labelAngleEdit, angle);
564 axis.setLabelAngle(angle);
565 }
566
567 axis.setGridLinesEnabled(sc.gridLinesEdit->isChecked());
568
569 axis.setTitle(sc.titleEdit->text());
570
571 axis.setTitleOrientation(sc.titleOrientationEdit->currentIndex() == 0 ? Orientation::Horizontal : Orientation::Vertical);
572
573 axis.setTickDirection(sc.tickDirectionEdit->currentIndex() == 0 ? TickDirection::Outwards : TickDirection::Inwards);
574
575 switch (sc.locationEdit->currentIndex()) {
576 case 0:
577 axis.setLocation(AxisValue::Minimum);
578 break;
579 case 1:
580 axis.setLocation(AxisValue::Maximum);
581 break;
582 case 2:
583 axis.setLocation(AxisValue::Zero);
584 break;
585 case 3:
586 axis.setLocation(AxisValue::Both);
587 break;
588 }
589 }
590
591 chart_->setTitle(titleEdit_->text());
592
594 double width, height;
595 getDouble(chartWidthEdit_, width);
596 getDouble(chartHeightEdit_, height);
597 chart_->resize(width, height);
598 }
599
600 switch (chartOrientationEdit_->currentIndex()) {
601 case 0:
602 chart_->setOrientation(Orientation::Vertical); break;
603 case 1:
604 chart_->setOrientation(Orientation::Horizontal); break;
605 }
606
607 chart_->setLegendEnabled(haveLegend);
608
609 if (haveLegend) {
610 LegendLocation location = LegendLocation::Outside;
611 Side side = Side::Right;
612 AlignmentFlag alignment = AlignmentFlag::Middle;
613 switch (legendLocationEdit_->currentIndex()) {
614 case 0: location = LegendLocation::Outside; break;
615 case 1: location = LegendLocation::Inside; break;
616 }
617
618 switch (legendSideEdit_->currentIndex()) {
619 case 0: side = Side::Top; break;
620 case 1: side = Side::Right; break;
621 case 2: side = Side::Bottom; break;
622 case 3: side = Side::Left; break;
623 }
624
625 if (side == Side::Left || side == Side::Right) {
626 if (legendAlignmentEdit_->currentIndex() < 3)
627 legendAlignmentEdit_->setCurrentIndex(4);
628 } else {
629 if (legendAlignmentEdit_->currentIndex() >= 3)
630 legendAlignmentEdit_->setCurrentIndex(2);
631 }
632
633 switch (legendAlignmentEdit_->currentIndex()) {
634 case 0: alignment = AlignmentFlag::Left; break;
635 case 1: alignment = AlignmentFlag::Center; break;
636 case 2: alignment = AlignmentFlag::Right; break;
637 case 3: alignment = AlignmentFlag::Top; break;
638 case 4: alignment = AlignmentFlag::Middle; break;
639 case 5: alignment = AlignmentFlag::Bottom; break;
640 }
641
642 chart_->setLegendLocation(location, side, alignment);
643
644 chart_->setLegendColumns((side == Side::Top || side == Side::Bottom ) ? 2 : 1,
645 WLength(100));
646 }
647
648 if (borderEdit_->isChecked()) {
649 chart_->setBorderPen(WPen());
650 } else {
651 chart_->setBorderPen(PenStyle::None);
652 }
653}
654
655bool ChartConfig::validate(WFormWidget *w)
656{
657 bool valid = w->validate() == ValidationState::Valid;
658
659 if (!WApplication::instance()->environment().javaScript()) {
660 w->setStyleClass(valid ? "" : "Wt-invalid");
661 w->setToolTip(valid ? "" : "Invalid value");
662 }
663
664 return valid;
665}
666
667void ChartConfig::connectSignals(WFormWidget *w)
668{
669 w->changed().connect(this, &ChartConfig::update);
670 if (dynamic_cast<WLineEdit *>(w))
671 w->enterPressed().connect(this, &ChartConfig::update);
672}
673
675{
676 int xAxis = chart_->addXAxis(std::make_unique<WAxis>());
677 addAxis(Axis::X, xAxis);
678 addEntry(xAxesModel_, axisName(Axis::X, xAxis));
679 if (xAxis == 0)
680 update();
681}
682
684{
685 int yAxis = chart_->addYAxis(std::make_unique<WAxis>());
686 addAxis(Axis::Y, yAxis);
687 addEntry(yAxesModel_, axisName(Axis::Y, yAxis));
688 if (yAxis == 0)
689 update();
690}
691
692void ChartConfig::addAxis(Axis ax, int axisId)
693{
694 int j = ax == Axis::X ? 1 + axisId : 1 + chart_->xAxisCount() + axisId;
695
696 const WAxis& axis = ax == Axis::X ? chart_->xAxis(axisId) : chart_->yAxis(axisId);
697 AxisControl sc;
698
699 axisConfig_->insertRow(j);
700 axisConfig_->elementAt(j, 0)->addNew<WText>(axisName(axis.id(), axis.yAxisId()));
701
702 sc.visibleEdit = axisConfig_->elementAt(j, 1)->addNew<WCheckBox>();
703 sc.visibleEdit->setChecked(axis.isVisible());
705
706 sc.scaleEdit = axisConfig_->elementAt(j, 2)->addNew<WComboBox>();
707 if (axis.scale() == AxisScale::Discrete)
708 sc.scaleEdit->addItem("Discrete scale");
709 else {
710 if (axis.id() == Axis::X) {
711 sc.scaleEdit->setModel(xScales_);
712 sc.scaleEdit->setCurrentIndex(static_cast<int>(axis.scale()));
713 } else {
714 sc.scaleEdit->setModel(yScales_);
715 sc.scaleEdit->setCurrentIndex(static_cast<int>(axis.scale()) - 1);
716 }
717 }
719
720 bool autoValues = axis.autoLimits() == (AxisValue::Minimum | AxisValue::Maximum);
721
722 sc.minimumEdit = axisConfig_->elementAt(j, 4)->addNew<WLineEdit>();
723 sc.minimumEdit->setText(WLocale::currentLocale()
724 .toString(axis.minimum()));
725 sc.minimumEdit->setValidator(anyNumberValidator_);
726 sc.minimumEdit->setEnabled(!autoValues);
728
729 sc.maximumEdit = axisConfig_->elementAt(j, 5)->addNew<WLineEdit>();
730 sc.maximumEdit->setText(WLocale::currentLocale()
731 .toString(axis.maximum()));
732 sc.maximumEdit->setValidator(anyNumberValidator_);
733 sc.maximumEdit->setEnabled(!autoValues);
735
736 sc.autoEdit = axisConfig_->elementAt(j, 3)->addNew<WCheckBox>();
737 sc.autoEdit->setChecked(autoValues);
739 sc.autoEdit->checked().connect(sc.maximumEdit, &WLineEdit::disable);
740 sc.autoEdit->unChecked().connect(sc.maximumEdit, &WLineEdit::enable);
741 sc.autoEdit->checked().connect(sc.minimumEdit, &WLineEdit::disable);
742 sc.autoEdit->unChecked().connect(sc.minimumEdit, &WLineEdit::enable);
743
744 sc.gridLinesEdit = axisConfig_->elementAt(j, 6)->addNew<WCheckBox>();
746
747 sc.labelAngleEdit = axisConfig_->elementAt(j, 7)->addNew<WLineEdit>();
748 sc.labelAngleEdit->setText("0");
749 sc.labelAngleEdit->setValidator(angleValidator_);
751
752 sc.titleEdit = axisConfig_->elementAt(j, 8)->addNew<WLineEdit>();
753 sc.titleEdit->setText("");
755
756 sc.titleOrientationEdit = axisConfig_->elementAt(j, 9)->addNew<WComboBox>();
757 sc.titleOrientationEdit->addItem("Horizontal");
758 sc.titleOrientationEdit->addItem("Vertical");
759 sc.titleOrientationEdit->setCurrentIndex(0);
761
762 sc.tickDirectionEdit = axisConfig_->elementAt(j, 10)->addNew<WComboBox>();
763 sc.tickDirectionEdit->addItem("Outwards");
764 sc.tickDirectionEdit->addItem("Inwards");
765 sc.tickDirectionEdit->setCurrentIndex(0);
767
768 sc.locationEdit = axisConfig_->elementAt(j, 11)->addNew<WComboBox>();
769 sc.locationEdit->addItem("Minimum value");
770 sc.locationEdit->addItem("Maximum value");
771 sc.locationEdit->addItem("Zero value");
772 sc.locationEdit->addItem("Both sides");
773 sc.locationEdit->setCurrentIndex(0);
774 if (axis.location() == AxisValue::Maximum) {
775 sc.locationEdit->setCurrentIndex(1);
776 } else if (axis.location() == AxisValue::Zero) {
777 sc.locationEdit->setCurrentIndex(2);
778 }
780
781 WPushButton *removeAxisButton =
782 axisConfig_->elementAt(j, 12)->addNew<WPushButton>(utf8("x"));
783 if (ax == Axis::X) {
784 removeAxisButton->clicked().connect(std::bind(&ChartConfig::removeXAxis, this, &axis));
785 } else {
786 removeAxisButton->clicked().connect(std::bind(&ChartConfig::removeYAxis, this, &axis));
787 }
788
789 axisConfig_->rowAt(j)->setStyleClass("trdata");
790
791 axisControls_.insert(axisControls_.begin() + j - 1, sc);
792}
793
794void ChartConfig::removeXAxis(const WAxis *axis)
795{
796 int xAxis = axis->xAxisId();
797 for (std::size_t i = 0; i < chart_->series().size(); ++i) {
798 if (chart_->series()[i]->xAxis() == xAxis)
799 chart_->series()[i]->bindToXAxis(-1);
800 }
801 chart_->removeXAxis(xAxis);
802 axisConfig_->removeRow(1 + xAxis);
803 xAxesModel_->removeRow(xAxis);
804 axisControls_.erase(axisControls_.begin() + xAxis);
805 update();
806}
807
808void ChartConfig::removeYAxis(const WAxis *axis)
809{
810 int yAxis = axis->yAxisId();
811 for (std::size_t i = 0; i < chart_->series().size(); ++i) {
812 if (chart_->series()[i]->yAxis() == yAxis)
813 chart_->series()[i]->bindToYAxis(-1);
814 }
815 chart_->removeYAxis(yAxis);
816 axisConfig_->removeRow(1 + chart_->xAxisCount() + yAxis);
817 yAxesModel_->removeRow(yAxis);
818 axisControls_.erase(axisControls_.begin() + chart_->xAxisCount() + yAxis);
819 update();
820}
821
823{
824 if (chart_->xAxisCount() == 0)
825 return;
826
827 for (std::size_t i = 0; i < chart_->series().size(); ++i) {
828 chart_->series()[i]->bindToXAxis(-1);
829 }
830 const int xAxisCount = chart_->xAxisCount();
831 chart_->clearXAxes();
832 for (int i = 0; i < xAxisCount; ++i) {
833 axisConfig_->removeRow(1);
834 }
835 xAxesModel_->clear();
836 axisControls_.erase(axisControls_.begin(), axisControls_.begin() + xAxisCount);
837}
838
840{
841 if (chart_->yAxisCount() == 0)
842 return;
843
844 for (std::size_t i = 0; i < chart_->series().size(); ++i) {
845 chart_->series()[i]->bindToYAxis(-1);
846 }
847 const int yAxisCount = chart_->yAxisCount();
848 chart_->clearYAxes();
849 for (int i = 0; i < yAxisCount; ++i) {
850 axisConfig_->removeRow(axisConfig_->rowCount() - 1);
851 }
852 yAxesModel_->clear();
853 axisControls_.resize(chart_->xAxisCount());
854}
std::vector< SeriesControl > seriesControls_
Controls for series.
Definition ChartConfig.h:66
void addAxis(Wt::Chart::Axis axis, int axisId)
Wt::WComboBox * legendAlignmentEdit_
Definition ChartConfig.h:92
Wt::WComboBox * legendLocationEdit_
Definition ChartConfig.h:90
std::shared_ptr< Wt::WValidator > angleValidator_
Definition ChartConfig.h:97
Wt::WTable * axisConfig_
Definition ChartConfig.h:96
Wt::WComboBox * legendSideEdit_
Definition ChartConfig.h:91
void removeXAxis(const Wt::Chart::WAxis *axis)
std::shared_ptr< Wt::WValidator > anyNumberValidator_
Definition ChartConfig.h:97
void setValueFill(Wt::Chart::FillRangeType fill)
Wt::WLineEdit * chartWidthEdit_
Definition ChartConfig.h:87
Wt::WLineEdit * titleEdit_
Definition ChartConfig.h:86
void removeYAxis(const Wt::Chart::WAxis *axis)
void addYAxis()
std::shared_ptr< Wt::WStandardItemModel > xAxesModel_
Definition ChartConfig.h:95
static bool validate(Wt::WFormWidget *w)
void connectSignals(Wt::WFormWidget *w)
Wt::Chart::WCartesianChart * chart_
Definition ChartConfig.h:50
std::vector< AxisControl > axisControls_
Controls for axes.
Definition ChartConfig.h:84
void clearYAxes()
ChartConfig(Wt::Chart::WCartesianChart *chart)
Constructor.
Definition ChartConfig.C:76
std::shared_ptr< Wt::WStandardItemModel > xScales_
Definition ChartConfig.h:95
void clearXAxes()
Wt::Chart::FillRangeType fill_
Definition ChartConfig.h:51
void addXAxis()
void update()
Wt::WCheckBox * borderEdit_
Definition ChartConfig.h:93
Wt::WLineEdit * chartHeightEdit_
Definition ChartConfig.h:88
std::shared_ptr< Wt::WStandardItemModel > yAxesModel_
Definition ChartConfig.h:95
Wt::WComboBox * chartOrientationEdit_
Definition ChartConfig.h:89
std::shared_ptr< Wt::WStandardItemModel > yScales_
Definition ChartConfig.h:95
Wt::WPanel * addWidget(const Wt::WString &text, std::unique_ptr< Wt::WWidget > w)
Definition PanelList.C:17
Struct that holds the controls for one axis.
Definition ChartConfig.h:69
Wt::WLineEdit * maximumEdit
Definition ChartConfig.h:74
Wt::WComboBox * tickDirectionEdit
Definition ChartConfig.h:79
Wt::WLineEdit * titleEdit
Definition ChartConfig.h:77
Wt::WLineEdit * labelAngleEdit
Definition ChartConfig.h:76
Wt::WComboBox * titleOrientationEdit
Definition ChartConfig.h:78
Wt::WCheckBox * gridLinesEdit
Definition ChartConfig.h:75
Wt::WLineEdit * minimumEdit
Definition ChartConfig.h:73
Wt::WComboBox * locationEdit
Definition ChartConfig.h:80
Wt::WComboBox * scaleEdit
Definition ChartConfig.h:71
Wt::WCheckBox * visibleEdit
Definition ChartConfig.h:70
Wt::WCheckBox * autoEdit
Definition ChartConfig.h:72
Struct that holds the controls for one series.
Definition ChartConfig.h:54
Wt::WComboBox * markerEdit
Definition ChartConfig.h:57
Wt::WComboBox * labelsEdit
Definition ChartConfig.h:62
Wt::WCheckBox * enabledEdit
Definition ChartConfig.h:55
Wt::WCheckBox * legendEdit
Definition ChartConfig.h:60
Wt::WCheckBox * shadowEdit
Definition ChartConfig.h:61
Wt::WComboBox * typeEdit
Definition ChartConfig.h:56
Wt::WComboBox * xAxisEdit
Definition ChartConfig.h:58
Wt::WComboBox * yAxisEdit
Definition ChartConfig.h:59