Wt examples  4.5.0
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 
31 using namespace Wt;
32 using namespace Wt::Chart;
33 
34 namespace {
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 
76 ChartConfig::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 
393 void 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) {
404  SeriesControl& sc = seriesControls_[i-1];
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 
655 bool 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 
667 void 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 
692 void 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 
794 void 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 
808 void 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)
Definition: ChartConfig.C:692
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)
Definition: ChartConfig.C:794
std::shared_ptr< Wt::WValidator > anyNumberValidator_
Definition: ChartConfig.h:97
void setValueFill(Wt::Chart::FillRangeType fill)
Definition: ChartConfig.C:393
Wt::WLineEdit * chartWidthEdit_
Definition: ChartConfig.h:87
Wt::WLineEdit * titleEdit_
Definition: ChartConfig.h:86
void removeYAxis(const Wt::Chart::WAxis *axis)
Definition: ChartConfig.C:808
void addYAxis()
Definition: ChartConfig.C:683
std::shared_ptr< Wt::WStandardItemModel > xAxesModel_
Definition: ChartConfig.h:95
static bool validate(Wt::WFormWidget *w)
Definition: ChartConfig.C:655
void connectSignals(Wt::WFormWidget *w)
Definition: ChartConfig.C:667
Wt::Chart::WCartesianChart * chart_
Definition: ChartConfig.h:50
std::vector< AxisControl > axisControls_
Controls for axes.
Definition: ChartConfig.h:84
void clearYAxes()
Definition: ChartConfig.C:839
ChartConfig(Wt::Chart::WCartesianChart *chart)
Constructor.
Definition: ChartConfig.C:76
std::shared_ptr< Wt::WStandardItemModel > xScales_
Definition: ChartConfig.h:95
void clearXAxes()
Definition: ChartConfig.C:822
Wt::Chart::FillRangeType fill_
Definition: ChartConfig.h:51
void addXAxis()
Definition: ChartConfig.C:674
void update()
Definition: ChartConfig.C:398
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

Generated on Sat Aug 14 2021 for the C++ Web Toolkit (Wt) by doxygen 1.9.1