Wt examples  4.0.5
ChartConfig.C
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2008 Emweb bvba, Kessel-Lo, 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(cpp14::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 yAxis)
67  {
68  if (axis == Axis::X)
69  return Wt::utf8("X Axis");
70  else {
71  return Wt::utf8("Y axis {1}").arg(yAxis + 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(cpp14::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  = cpp14::make_unique<WTable>();
126  chartConfig->setMargin(WLength::Auto, Side::Left | Side::Right);
127 
128  int row = 0;
129  chartConfig->elementAt(row, 0)->addWidget(cpp14::make_unique<WText>("Title:"));
130  titleEdit_ = chartConfig->elementAt(row,1)->addWidget(cpp14::make_unique<WLineEdit>());
132  ++row;
133 
134  chartConfig->elementAt(row, 0)->addWidget(cpp14::make_unique<WText>("Width:"));
135  chartWidthEdit_ = chartConfig->elementAt(row,1)->addWidget(cpp14::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(cpp14::make_unique<WText>("Height:"));
144  chartHeightEdit_ = chartConfig->elementAt(row,1)->addWidget(cpp14::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(cpp14::make_unique<WText>("Orientation:"));
153  chartOrientationEdit_ = chartConfig->elementAt(row,1)->addWidget(cpp14::make_unique<WComboBox>());
154  chartOrientationEdit_->setModel(orientation);
155  chartOrientationEdit_->setCurrentIndex(0);
157  ++row;
158 
159  chartConfig->elementAt(row, 0)->addWidget(cpp14::make_unique<WText>("Legend location:"));
160  legendLocationEdit_ = chartConfig->elementAt(row,1)->addWidget(cpp14::make_unique<WComboBox>());
161  legendLocationEdit_->setModel(legendLocation);
162  legendLocationEdit_->setCurrentIndex(0);
164  ++row;
165 
166  chartConfig->elementAt(row, 0)->addWidget(cpp14::make_unique<WText>("Legend side:"));
167  legendSideEdit_ = chartConfig->elementAt(row,1)->addWidget(cpp14::make_unique<WComboBox>());
168  legendSideEdit_->setModel(legendSide);
169  legendSideEdit_->setCurrentIndex(1);
171  ++row;
172 
173  chartConfig->elementAt(row, 0)->addWidget(cpp14::make_unique<WText>("Legend alignment:"));
174  legendAlignmentEdit_ = chartConfig->elementAt(row,1)->addWidget(cpp14::make_unique<WComboBox>());
175  legendAlignmentEdit_->setModel(legendAlignment);
176  legendAlignmentEdit_->setCurrentIndex(4);
178  ++row;
179 
180  chartConfig->elementAt(row, 0)->addWidget(cpp14::make_unique<WText>("Border:"));
181  borderEdit_ = chartConfig->elementAt(row,1)->addWidget(cpp14::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  yAxesModel_ = std::make_shared<WStandardItemModel>(0, 1);
225  addEntry(yAxesModel_, axisName(Axis::Y, 0));
226  addEntry(yAxesModel_, axisName(Axis::Y, 1));
227 
228  std::shared_ptr<WStandardItemModel> labels
229  = std::make_shared<WStandardItemModel>(0,1);
230  addEntry(labels, "None");
231  addEntry(labels, "X");
232  addEntry(labels, "Y");
233  addEntry(labels, "X: Y");
234 
235  std::unique_ptr<WTable> seriesConfig
236  = cpp14::make_unique<WTable>();
237  WTable *seriesConfigPtr = seriesConfig.get();
238  seriesConfig->setMargin(WLength::Auto, Side::Left | Side::Right);
239  ::addHeader(seriesConfigPtr, "Name");
240  ::addHeader(seriesConfigPtr, "Enabled");
241  ::addHeader(seriesConfigPtr, "Type");
242  ::addHeader(seriesConfigPtr, "Marker");
243  ::addHeader(seriesConfigPtr, "Y axis");
244  ::addHeader(seriesConfigPtr, "Legend");
245  ::addHeader(seriesConfigPtr, "Shadow");
246  ::addHeader(seriesConfigPtr, "Value labels");
247 
248  seriesConfig->rowAt(0)->setStyleClass("trhead");
249 
250  for (int j = 1; j < chart->model()->columnCount(); ++j) {
251  SeriesControl sc;
252 
253  seriesConfig->elementAt(j,0)->addWidget(cpp14::make_unique<WText>(chart->model()->headerData(j)));
254 
255  sc.enabledEdit = seriesConfig->elementAt(j,1)->addWidget(cpp14::make_unique<WCheckBox>());
257 
258  sc.typeEdit = seriesConfig->elementAt(j,2)->addWidget(cpp14::make_unique<WComboBox>());
259  sc.typeEdit->setModel(types);
260  sc.typeEdit->setCurrentIndex(0);
262 
263  sc.markerEdit = seriesConfig->elementAt(j,3)->addWidget(cpp14::make_unique<WComboBox>());
264  sc.markerEdit->setModel(markers);
265  sc.markerEdit->setCurrentIndex(0);
267 
268  sc.axisEdit = seriesConfig->elementAt(j,4)->addNew<WComboBox>();
269  sc.axisEdit->setModel(yAxesModel_);
270  sc.axisEdit->setCurrentIndex(0);
272 
273  sc.legendEdit = seriesConfig->elementAt(j, 5)->addWidget(cpp14::make_unique<WCheckBox>());
275 
276  sc.shadowEdit = seriesConfig->elementAt(j,6)->addWidget(cpp14::make_unique<WCheckBox>());
278 
279  sc.labelsEdit = seriesConfig->elementAt(j,7)->addWidget(cpp14::make_unique<WComboBox>());
280  sc.labelsEdit->setModel(labels);
281  sc.labelsEdit->setCurrentIndex(0);
283 
284  int si = seriesIndexOf(chart, j);
285 
286  if (si != -1) {
287  sc.enabledEdit->setChecked();
288  const WDataSeries& s = chart_->series(j);
289  switch (s.type()) {
290  case SeriesType::Point:
291  sc.typeEdit->setCurrentIndex(0); break;
292  case SeriesType::Line:
293  sc.typeEdit->setCurrentIndex(s.fillRange() != FillRangeType::None ?
294  (s.isStacked() ? 7 : 4) : 1); break;
295  case SeriesType::Curve:
296  sc.typeEdit->setCurrentIndex(s.fillRange() != FillRangeType::None ?
297  (s.isStacked() ? 8 : 5) : 2); break;
298  case SeriesType::Bar:
299  sc.typeEdit->setCurrentIndex(s.isStacked() ? 6 : 3);
300  }
301 
302  sc.markerEdit->setCurrentIndex((int)s.marker());
303  sc.legendEdit->setChecked(s.isLegendEnabled());
304  sc.shadowEdit->setChecked(s.shadow() != WShadow());
305  }
306 
307  seriesControls_.push_back(sc);
308 
309  seriesConfig->rowAt(j)->setStyleClass("trdata");
310  }
311 
312  p = list->addWidget("Series properties", std::move(seriesConfig));
313  p->expand();
314  p->setMargin(WLength::Auto, Side::Left | Side::Right);
315  p->resize(1160, WLength::Auto);
316  p->setMargin(20, Side::Top | Side::Bottom);
317 
318  // ---- Axis properties ----
319 
320  yScales_ = std::make_shared<WStandardItemModel>(0, 1);
321  addEntry(yScales_, "Linear scale");
322  addEntry(yScales_, "Log scale");
323 
324  xScales_ = std::make_shared<WStandardItemModel>(0, 1);
325  addEntry(xScales_, "Categories");
326  addEntry(xScales_, "Linear scale");
327  addEntry(xScales_, "Log scale");
328  addEntry(xScales_, "Date scale");
329 
330  auto axisConfig = cpp14::make_unique<WContainerWidget>();
331  axisConfig_ = axisConfig->addNew<WTable>();
332  axisConfig_->setMargin(WLength::Auto, Side::Left | Side::Right);
333 
334  ::addHeader(axisConfig_, "Axis");
335  ::addHeader(axisConfig_, "Visible");
336  ::addHeader(axisConfig_, "Scale");
337  ::addHeader(axisConfig_, "Automatic");
338  ::addHeader(axisConfig_, "Minimum");
339  ::addHeader(axisConfig_, "Maximum");
340  ::addHeader(axisConfig_, "Gridlines");
341  ::addHeader(axisConfig_, "Label angle");
342  ::addHeader(axisConfig_, "Title");
343  ::addHeader(axisConfig_, "Title orientation");
344  ::addHeader(axisConfig_, "Tick direction");
345  ::addHeader(axisConfig_, "Location");
346 
347  axisConfig_->rowAt(0)->setStyleClass("trhead");
348 
349  addAxis(Axis::X, 0);
350  addAxis(Axis::Y, 0);
351  addAxis(Axis::Y, 1);
352 
353  WPushButton *addAxisBtn =
354  axisConfig->addNew<WPushButton>(utf8("Add Y axis"));
355  addAxisBtn->clicked().connect(this, &ChartConfig::addYAxis);
356  WPushButton *clearAxesBtn =
357  axisConfig->addNew<WPushButton>(utf8("Clear Y axes"));
358  clearAxesBtn->clicked().connect(this, &ChartConfig::clearYAxes);
359 
360  p = list->addWidget("Axis properties", std::move(axisConfig));
361  p->setMargin(WLength::Auto, Side::Left | Side::Right);
362  p->resize(1160, WLength::Auto);
363  p->setMargin(20, Side::Top | Side::Bottom);
364 
365  /*
366  * If we do not have JavaScript, then add a button to reflect changes to
367  * the chart.
368  */
369  if (!WApplication::instance()->environment().javaScript()) {
370  auto *b = this->addWidget(cpp14::make_unique<WPushButton>());
371  b->setText("Update chart");
372  b->setInline(false); // so we can add margin to center horizontally
373  b->setMargin(WLength::Auto, Side::Left | Side::Right);
374  b->clicked().connect(this, &ChartConfig::update);
375  }
376 }
377 
378 void ChartConfig::setValueFill(FillRangeType fill)
379 {
380  fill_ = fill;
381 }
382 
384 {
385  bool haveLegend = false;
386  std::vector<std::unique_ptr<WDataSeries>> series;
387 
388  for (int i = 1; i < chart_->model()->columnCount(); ++i) {
389  SeriesControl& sc = seriesControls_[i-1];
390 
391  if (sc.enabledEdit->isChecked()) {
392  std::unique_ptr<WDataSeries> s
393  = cpp14::make_unique<WDataSeries>(i);
394 
395  switch (sc.typeEdit->currentIndex()) {
396  case 0:
397  s->setType(SeriesType::Point);
398  if (sc.markerEdit->currentIndex() == 0)
399  sc.markerEdit->setCurrentIndex(1);
400  break;
401  case 1:
402  s->setType(SeriesType::Line);
403  break;
404  case 2:
405  s->setType(SeriesType::Curve);
406  break;
407  case 3:
408  s->setType(SeriesType::Bar);
409  break;
410  case 4:
411  s->setType(SeriesType::Line);
412  s->setFillRange(fill_);
413  break;
414  case 5:
415  s->setType(SeriesType::Curve);
416  s->setFillRange(fill_);
417  break;
418  case 6:
419  s->setType(SeriesType::Bar);
420  s->setStacked(true);
421  break;
422  case 7:
423  s->setType(SeriesType::Line);
424  s->setFillRange(fill_);
425  s->setStacked(true);
426  break;
427  case 8:
428  s->setType(SeriesType::Curve);
429  s->setFillRange(fill_);
430  s->setStacked(true);
431  }
432 
433  //set WPainterPath to draw a pipe
434  if(sc.markerEdit->currentIndex() == static_cast<int>(MarkerType::Custom)){ //was customMarker before
435  WPainterPath pp = WPainterPath();
436  pp.moveTo(0, -6);
437  pp.lineTo(0, 6);
438  s->setCustomMarker(pp);
439  }
440 
441  s->setMarker(static_cast<MarkerType>(sc.markerEdit->currentIndex()));
442 
443  s->bindToYAxis(sc.axisEdit->currentIndex());
444 
445  if (sc.legendEdit->isChecked()) {
446  s->setLegendEnabled(true);
447  haveLegend = true;
448  } else
449  s->setLegendEnabled(false);
450 
451  if (sc.shadowEdit->isChecked()) {
452  s->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3));
453  } else
454  s->setShadow(WShadow());
455 
456  switch (sc.labelsEdit->currentIndex()) {
457  case 1:
458  s->setLabelsEnabled(Axis::X);
459  break;
460  case 2:
461  s->setLabelsEnabled(Axis::Y);
462  break;
463  case 3:
464  s->setLabelsEnabled(Axis::X);
465  s->setLabelsEnabled(Axis::Y);
466  break;
467  }
468 
469  series.push_back(std::move(s));
470  }
471  }
472 
473  chart_->setSeries(std::move(series));
474 
475  for (std::size_t i = 0; i < axisControls_.size(); ++i) {
476  AxisControl& sc = axisControls_[i];
477  WAxis& axis = i == 0 ? chart_->axis(Axis::X) : chart_->yAxis(i - 1);
478 
479  axis.setVisible(sc.visibleEdit->isChecked());
480 
481  if (sc.scaleEdit->count() != 1) {
482  int k = sc.scaleEdit->currentIndex();
483  if (axis.id() != Axis::X)
484  k += 1;
485  else {
486  if (k == 0)
487  chart_->setType(ChartType::Category);
488  else
489  chart_->setType(ChartType::Scatter);
490  }
491 
492  switch (k) {
493  case 1:
494  axis.setScale(AxisScale::Linear); break;
495  case 2:
496  axis.setScale(AxisScale::Log); break;
497  case 3:
498  axis.setScale(AxisScale::Date); break;
499  }
500  }
501 
502  if (sc.autoEdit->isChecked())
503  axis.setAutoLimits(AxisValue::Minimum | AxisValue::Maximum);
504  else {
505  if (!(axis.autoLimits() & (AxisValue::Minimum | AxisValue::Maximum)).empty()) {
506  sc.minimumEdit->setText(WLocale::currentLocale()
507  .toString(axis.minimum()));
508  sc.maximumEdit->setText(WLocale::currentLocale()
509  .toString(axis.maximum()));
510  }
511  if (validate(sc.minimumEdit) && validate(sc.maximumEdit)) {
512  double min, max;
513  getDouble(sc.minimumEdit, min);
514  getDouble(sc.maximumEdit, max);
515 
516  if (axis.scale() == AxisScale::Log)
517  if (min <= 0)
518  min = 0.0001;
519 
520  if (axis.scale() == AxisScale::Date){
521  //the number of julian days until year 1986
522  WDate dMin = WDate(1900,1,1);
523  double gregDaysMin = (double)dMin.toJulianDay();
524  //the number of julian days until year 1988
525  WDate dMax = WDate(3000,1,1);
526  double gregDaysMax = (double)dMax.toJulianDay();
527 
528  bool greg_year_validation =
529  (min > gregDaysMin &&
530  min < gregDaysMax &&
531  max > gregDaysMin &&
532  max < gregDaysMax);
533 
534  if(!greg_year_validation){
535  min = gregDaysMin;
536  max = gregDaysMax;
537  }
538  }
539 
540  axis.setRange(min, max);
541  }
542 
543  }
544 
545  if (validate(sc.labelAngleEdit)) {
546  double angle;
547  getDouble(sc.labelAngleEdit, angle);
548  axis.setLabelAngle(angle);
549  }
550 
551  axis.setGridLinesEnabled(sc.gridLinesEdit->isChecked());
552 
553  axis.setTitle(sc.titleEdit->text());
554 
555  axis.setTitleOrientation(sc.titleOrientationEdit->currentIndex() == 0 ? Orientation::Horizontal : Orientation::Vertical);
556 
557  axis.setTickDirection(sc.tickDirectionEdit->currentIndex() == 0 ? TickDirection::Outwards : TickDirection::Inwards);
558 
559  switch (sc.locationEdit->currentIndex()) {
560  case 0:
561  axis.setLocation(AxisValue::Minimum);
562  break;
563  case 1:
564  axis.setLocation(AxisValue::Maximum);
565  break;
566  case 2:
567  axis.setLocation(AxisValue::Zero);
568  break;
569  case 3:
570  axis.setLocation(AxisValue::Both);
571  break;
572  }
573  }
574 
575  chart_->setTitle(titleEdit_->text());
576 
578  double width, height;
579  getDouble(chartWidthEdit_, width);
580  getDouble(chartHeightEdit_, height);
581  chart_->resize(width, height);
582  }
583 
584  switch (chartOrientationEdit_->currentIndex()) {
585  case 0:
586  chart_->setOrientation(Orientation::Vertical); break;
587  case 1:
588  chart_->setOrientation(Orientation::Horizontal); break;
589  }
590 
591  chart_->setLegendEnabled(haveLegend);
592 
593  if (haveLegend) {
594  LegendLocation location = LegendLocation::Outside;
595  Side side = Side::Right;
596  AlignmentFlag alignment = AlignmentFlag::Middle;
597  switch (legendLocationEdit_->currentIndex()) {
598  case 0: location = LegendLocation::Outside; break;
599  case 1: location = LegendLocation::Inside; break;
600  }
601 
602  switch (legendSideEdit_->currentIndex()) {
603  case 0: side = Side::Top; break;
604  case 1: side = Side::Right; break;
605  case 2: side = Side::Bottom; break;
606  case 3: side = Side::Left; break;
607  }
608 
609  if (side == Side::Left || side == Side::Right) {
610  if (legendAlignmentEdit_->currentIndex() < 3)
611  legendAlignmentEdit_->setCurrentIndex(4);
612  } else {
613  if (legendAlignmentEdit_->currentIndex() >= 3)
614  legendAlignmentEdit_->setCurrentIndex(2);
615  }
616 
617  switch (legendAlignmentEdit_->currentIndex()) {
618  case 0: alignment = AlignmentFlag::Left; break;
619  case 1: alignment = AlignmentFlag::Center; break;
620  case 2: alignment = AlignmentFlag::Right; break;
621  case 3: alignment = AlignmentFlag::Top; break;
622  case 4: alignment = AlignmentFlag::Middle; break;
623  case 5: alignment = AlignmentFlag::Bottom; break;
624  }
625 
626  chart_->setLegendLocation(location, side, alignment);
627 
628  chart_->setLegendColumns((side == Side::Top || side == Side::Bottom ) ? 2 : 1,
629  WLength(100));
630  }
631 
632  if (borderEdit_->isChecked()) {
633  chart_->setBorderPen(WPen());
634  } else {
635  chart_->setBorderPen(PenStyle::None);
636  }
637 }
638 
639 bool ChartConfig::validate(WFormWidget *w)
640 {
641  bool valid = w->validate() == ValidationState::Valid;
642 
643  if (!WApplication::instance()->environment().javaScript()) {
644  w->setStyleClass(valid ? "" : "Wt-invalid");
645  w->setToolTip(valid ? "" : "Invalid value");
646  }
647 
648  return valid;
649 }
650 
651 void ChartConfig::connectSignals(WFormWidget *w)
652 {
653  w->changed().connect(this, &ChartConfig::update);
654  if (dynamic_cast<WLineEdit *>(w))
655  w->enterPressed().connect(this, &ChartConfig::update);
656 }
657 
659 {
660  int yAxis = chart_->addYAxis(cpp14::make_unique<WAxis>());
661  addAxis(Axis::Y, yAxis);
662  addEntry(yAxesModel_, axisName(Axis::Y, yAxis));
663  if (yAxis == 0)
664  update();
665 }
666 
667 void ChartConfig::addAxis(Axis ax, int yAxis)
668 {
669  int j = ax == Axis::X ? 1 : yAxis + 2;
670 
671  const WAxis& axis = ax == Axis::X ? chart_->axis(Axis::X) : chart_->yAxis(yAxis);
672  AxisControl sc;
673 
674  axisConfig_->elementAt(j, 0)->addNew<WText>(axisName(axis.id(), axis.yAxisId()));
675 
676  sc.visibleEdit = axisConfig_->elementAt(j, 1)->addNew<WCheckBox>();
677  sc.visibleEdit->setChecked(axis.isVisible());
679 
680  sc.scaleEdit = axisConfig_->elementAt(j, 2)->addNew<WComboBox>();
681  if (axis.scale() == AxisScale::Discrete)
682  sc.scaleEdit->addItem("Discrete scale");
683  else {
684  if (axis.id() == Axis::X) {
685  sc.scaleEdit->setModel(xScales_);
686  sc.scaleEdit->setCurrentIndex(static_cast<int>(axis.scale()));
687  } else {
688  sc.scaleEdit->setModel(yScales_);
689  sc.scaleEdit->setCurrentIndex(static_cast<int>(axis.scale()) - 1);
690  }
691  }
693 
694  bool autoValues = axis.autoLimits() == (AxisValue::Minimum | AxisValue::Maximum);
695 
696  sc.minimumEdit = axisConfig_->elementAt(j, 4)->addNew<WLineEdit>();
697  sc.minimumEdit->setText(WLocale::currentLocale()
698  .toString(axis.minimum()));
699  sc.minimumEdit->setValidator(anyNumberValidator_);
700  sc.minimumEdit->setEnabled(!autoValues);
702 
703  sc.maximumEdit = axisConfig_->elementAt(j, 5)->addNew<WLineEdit>();
704  sc.maximumEdit->setText(WLocale::currentLocale()
705  .toString(axis.maximum()));
706  sc.maximumEdit->setValidator(anyNumberValidator_);
707  sc.maximumEdit->setEnabled(!autoValues);
709 
710  sc.autoEdit = axisConfig_->elementAt(j, 3)->addNew<WCheckBox>();
711  sc.autoEdit->setChecked(autoValues);
713  sc.autoEdit->checked().connect(sc.maximumEdit, &WLineEdit::disable);
714  sc.autoEdit->unChecked().connect(sc.maximumEdit, &WLineEdit::enable);
715  sc.autoEdit->checked().connect(sc.minimumEdit, &WLineEdit::disable);
716  sc.autoEdit->unChecked().connect(sc.minimumEdit, &WLineEdit::enable);
717 
718  sc.gridLinesEdit = axisConfig_->elementAt(j, 6)->addNew<WCheckBox>();
720 
721  sc.labelAngleEdit = axisConfig_->elementAt(j, 7)->addNew<WLineEdit>();
722  sc.labelAngleEdit->setText("0");
723  sc.labelAngleEdit->setValidator(angleValidator_);
725 
726  sc.titleEdit = axisConfig_->elementAt(j, 8)->addNew<WLineEdit>();
727  sc.titleEdit->setText("");
729 
730  sc.titleOrientationEdit = axisConfig_->elementAt(j, 9)->addNew<WComboBox>();
731  sc.titleOrientationEdit->addItem("Horizontal");
732  sc.titleOrientationEdit->addItem("Vertical");
733  sc.titleOrientationEdit->setCurrentIndex(0);
735 
736  sc.tickDirectionEdit = axisConfig_->elementAt(j, 10)->addNew<WComboBox>();
737  sc.tickDirectionEdit->addItem("Outwards");
738  sc.tickDirectionEdit->addItem("Inwards");
739  sc.tickDirectionEdit->setCurrentIndex(0);
741 
742  sc.locationEdit = axisConfig_->elementAt(j, 11)->addNew<WComboBox>();
743  sc.locationEdit->addItem("Minimum value");
744  sc.locationEdit->addItem("Maximum value");
745  sc.locationEdit->addItem("Zero value");
746  sc.locationEdit->addItem("Both sides");
747  sc.locationEdit->setCurrentIndex(0);
748  if (axis.location() == AxisValue::Maximum) {
749  sc.locationEdit->setCurrentIndex(1);
750  } else if (axis.location() == AxisValue::Zero) {
751  sc.locationEdit->setCurrentIndex(2);
752  }
754 
755  if (ax != Axis::X) {
756  WPushButton *removeAxisButton =
757  axisConfig_->elementAt(j, 12)->addNew<WPushButton>(utf8("x"));
758  removeAxisButton->clicked().connect(std::bind(&ChartConfig::removeYAxis, this, &axis));
759  }
760 
761  axisConfig_->rowAt(j)->setStyleClass("trdata");
762 
763  axisControls_.push_back(sc);
764 }
765 
766 void ChartConfig::removeYAxis(const WAxis *axis)
767 {
768  int yAxis = axis->yAxisId();
769  for (std::size_t i = 0; i < chart_->series().size(); ++i) {
770  if (chart_->series()[i]->yAxis() == yAxis)
771  chart_->series()[i]->bindToYAxis(-1);
772  }
773  chart_->removeYAxis(yAxis);
774  axisConfig_->removeRow(yAxis + 2);
775  yAxesModel_->removeRow(yAxis);
776  axisControls_.erase(axisControls_.begin() + yAxis + 1);
777  update();
778 }
779 
781 {
782  if (chart_->yAxisCount() == 0)
783  return;
784 
785  for (std::size_t i = 0; i < chart_->series().size(); ++i) {
786  chart_->series()[i]->bindToYAxis(-1);
787  }
788  chart_->clearYAxes();
789  while (axisConfig_->rowCount() > 2)
790  axisConfig_->removeRow(2);
791  yAxesModel_->clear();
792  axisControls_.resize(1);
793 }
ChartConfig(Wt::Chart::WCartesianChart *chart)
Constructor.
Definition: ChartConfig.C:76
Wt::WCheckBox * borderEdit_
Definition: ChartConfig.h:92
Wt::WComboBox * axisEdit
Definition: ChartConfig.h:58
std::shared_ptr< Wt::WStandardItemModel > xScales_
Definition: ChartConfig.h:94
std::shared_ptr< Wt::WValidator > angleValidator_
Definition: ChartConfig.h:96
Wt::Chart::FillRangeType fill_
Definition: ChartConfig.h:51
Wt::WComboBox * scaleEdit
Definition: ChartConfig.h:70
Wt::WLineEdit * titleEdit_
Definition: ChartConfig.h:85
Wt::Chart::WCartesianChart * chart_
Definition: ChartConfig.h:50
Wt::WTable * axisConfig_
Definition: ChartConfig.h:95
void connectSignals(Wt::WFormWidget *w)
Definition: ChartConfig.C:651
Wt::WCheckBox * enabledEdit
Definition: ChartConfig.h:55
Wt::WComboBox * locationEdit
Definition: ChartConfig.h:79
Struct that holds the controls for one series.
Definition: ChartConfig.h:54
Wt::WLineEdit * titleEdit
Definition: ChartConfig.h:76
Wt::WComboBox * typeEdit
Definition: ChartConfig.h:56
Wt::WCheckBox * visibleEdit
Definition: ChartConfig.h:69
void removeYAxis(const Wt::Chart::WAxis *axis)
Definition: ChartConfig.C:766
Wt::WComboBox * markerEdit
Definition: ChartConfig.h:57
Wt::WLineEdit * chartWidthEdit_
Definition: ChartConfig.h:86
Struct that holds the controls for one axis.
Definition: ChartConfig.h:68
Wt::WPanel * addWidget(const Wt::WString &text, std::unique_ptr< Wt::WWidget > w)
Definition: PanelList.C:17
Wt::WComboBox * chartOrientationEdit_
Definition: ChartConfig.h:88
Wt::WComboBox * legendLocationEdit_
Definition: ChartConfig.h:89
std::vector< SeriesControl > seriesControls_
Controls for series.
Definition: ChartConfig.h:65
Wt::WLineEdit * minimumEdit
Definition: ChartConfig.h:72
Wt::WComboBox * legendSideEdit_
Definition: ChartConfig.h:90
std::vector< AxisControl > axisControls_
Controls for axes.
Definition: ChartConfig.h:83
void update()
Definition: ChartConfig.C:383
Wt::WCheckBox * shadowEdit
Definition: ChartConfig.h:60
Wt::WComboBox * labelsEdit
Definition: ChartConfig.h:61
void setValueFill(Wt::Chart::FillRangeType fill)
Definition: ChartConfig.C:378
Wt::WComboBox * tickDirectionEdit
Definition: ChartConfig.h:78
Wt::WLineEdit * labelAngleEdit
Definition: ChartConfig.h:75
Wt::WCheckBox * legendEdit
Definition: ChartConfig.h:59
std::shared_ptr< Wt::WStandardItemModel > yScales_
Definition: ChartConfig.h:94
Wt::WCheckBox * gridLinesEdit
Definition: ChartConfig.h:74
std::shared_ptr< Wt::WValidator > anyNumberValidator_
Definition: ChartConfig.h:96
Wt::WComboBox * titleOrientationEdit
Definition: ChartConfig.h:77
void addYAxis()
Definition: ChartConfig.C:658
Wt::WComboBox * legendAlignmentEdit_
Definition: ChartConfig.h:91
Wt::WCheckBox * autoEdit
Definition: ChartConfig.h:71
void clearYAxes()
Definition: ChartConfig.C:780
Wt::WLineEdit * maximumEdit
Definition: ChartConfig.h:73
void addAxis(Wt::Chart::Axis axis, int yAxis)
Definition: ChartConfig.C:667
std::shared_ptr< Wt::WStandardItemModel > yAxesModel_
Definition: ChartConfig.h:94
static bool validate(Wt::WFormWidget *w)
Definition: ChartConfig.C:639
Wt::WLineEdit * chartHeightEdit_
Definition: ChartConfig.h:87

Generated on Mon May 20 2019 for the C++ Web Toolkit (Wt) by doxygen 1.8.14