11 #include <boost/date_time/gregorian/greg_year.hpp> 13 #include <Wt/WAbstractItemModel> 14 #include <Wt/WApplication> 15 #include <Wt/WCheckBox> 16 #include <Wt/WComboBox> 17 #include <Wt/WDoubleValidator> 19 #include <Wt/WEnvironment> 20 #include <Wt/WIntValidator> 21 #include <Wt/WLineEdit> 24 #include <Wt/WPushButton> 25 #include <Wt/WStandardItemModel> 28 #include <Wt/WPainterPath> 30 #include <Wt/Chart/WCartesianChart> 36 void addHeader(WTable *t,
const char *value) {
37 t->elementAt(0, t->columnCount())->addWidget(
new WText(value));
40 void addEntry(WAbstractItemModel *model,
const char *value) {
41 model->insertRows(model->rowCount(), 1);
42 model->setData(model->rowCount()-1, 0, boost::any(std::string(value)));
45 bool getDouble(WLineEdit *edit,
double& value) {
47 value = WLocale::currentLocale().toDouble(edit->text());
54 int seriesIndexOf(WCartesianChart* chart,
int modelColumn) {
55 for (
unsigned i = 0; i < chart->series().size(); ++i)
56 if (chart->series()[i]->modelColumn() == modelColumn)
64 : WContainerWidget(parent),
66 fill_(MinimumValueFill)
68 chart_->setLegendStyle(
chart_->legendFont(), WPen(black),
69 WBrush(WColor(0xFF, 0xFA, 0xE5)));
73 WIntValidator *sizeValidator =
new WIntValidator(200, 2000,
this);
74 sizeValidator->setMandatory(
true);
76 WDoubleValidator *anyNumberValidator =
new WDoubleValidator(
this);
77 anyNumberValidator->setMandatory(
true);
79 WDoubleValidator *angleValidator =
new WDoubleValidator(-90, 90,
this);
80 angleValidator->setMandatory(
true);
84 WStandardItemModel *orientation =
new WStandardItemModel(0, 1,
this);
85 addEntry(orientation,
"Vertical");
86 addEntry(orientation,
"Horizontal");
88 WStandardItemModel *legendLocation =
new WStandardItemModel(0, 1,
this);
89 addEntry(legendLocation,
"Outside");
90 addEntry(legendLocation,
"Inside");
92 WStandardItemModel *legendSide =
new WStandardItemModel(0, 1,
this);
93 addEntry(legendSide,
"Top");
94 addEntry(legendSide,
"Right");
95 addEntry(legendSide,
"Bottom");
96 addEntry(legendSide,
"Left");
98 WStandardItemModel *legendAlignment =
new WStandardItemModel(0, 1,
this);
99 addEntry(legendAlignment,
"AlignLeft");
100 addEntry(legendAlignment,
"AlignCenter");
101 addEntry(legendAlignment,
"AlignRight");
102 addEntry(legendAlignment,
"AlignTop");
103 addEntry(legendAlignment,
"AlignMiddle");
104 addEntry(legendAlignment,
"AlignBottom");
106 WTable *chartConfig =
new WTable();
107 chartConfig->setMargin(WLength::Auto, Left | Right);
110 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Title:"));
111 titleEdit_ =
new WLineEdit(chartConfig->elementAt(row, 1));
115 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Width:"));
118 ->setText(WLocale::currentLocale().toString(
chart_->width().value()));
124 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Height:"));
127 ->setText(WLocale::currentLocale().toString(
chart_->height().value()));
133 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Orientation:"));
140 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Legend location:"));
147 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Legend side:"));
154 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Legend alignment:"));
161 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Border:"));
162 borderEdit_ =
new WCheckBox(chartConfig->elementAt(row, 1));
167 for (
int i = 0; i < chartConfig->rowCount(); ++i) {
168 chartConfig->elementAt(i, 0)->setStyleClass(
"tdhead");
169 chartConfig->elementAt(i, 1)->setStyleClass(
"tddata");
172 WPanel *p = list->
addWidget(
"Chart properties", chartConfig);
173 p->setMargin(WLength::Auto, Left | Right);
174 p->resize(1080, WLength::Auto);
175 p->setMargin(20, Top | Bottom);
179 WStandardItemModel *types =
new WStandardItemModel(0, 1,
this);
180 addEntry(types,
"Points");
181 addEntry(types,
"Line");
182 addEntry(types,
"Curve");
183 addEntry(types,
"Bar");
184 addEntry(types,
"Line Area");
185 addEntry(types,
"Curve Area");
186 addEntry(types,
"Stacked Bar");
187 addEntry(types,
"Stacked Line Area");
188 addEntry(types,
"Stacked Curve Area");
190 WStandardItemModel *markers =
new WStandardItemModel(0, 1,
this);
191 addEntry(markers,
"None");
192 addEntry(markers,
"Square");
193 addEntry(markers,
"Circle");
194 addEntry(markers,
"Cross");
195 addEntry(markers,
"X cross");
196 addEntry(markers,
"Triangle");
197 addEntry(markers,
"Pipe");
198 addEntry(markers,
"Star");
199 addEntry(markers,
"Inverted triangle");
200 addEntry(markers,
"Asterisk");
201 addEntry(markers,
"Diamond");
203 WStandardItemModel *axes =
new WStandardItemModel(0, 1,
this);
204 addEntry(axes,
"1st Y axis");
205 addEntry(axes,
"2nd Y axis");
207 WStandardItemModel *labels =
new WStandardItemModel(0, 1,
this);
208 addEntry(labels,
"None");
209 addEntry(labels,
"X");
210 addEntry(labels,
"Y");
211 addEntry(labels,
"X: Y");
213 WTable *seriesConfig =
new WTable();
214 seriesConfig->setMargin(WLength::Auto, Left | Right);
216 ::addHeader(seriesConfig,
"Name");
217 ::addHeader(seriesConfig,
"Enabled");
218 ::addHeader(seriesConfig,
"Type");
219 ::addHeader(seriesConfig,
"Marker");
220 ::addHeader(seriesConfig,
"Y axis");
221 ::addHeader(seriesConfig,
"Legend");
222 ::addHeader(seriesConfig,
"Shadow");
223 ::addHeader(seriesConfig,
"Value labels");
225 seriesConfig->rowAt(0)->setStyleClass(
"trhead");
227 for (
int j = 1; j < chart->model()->columnCount(); ++j) {
230 new WText(chart->model()->headerData(j),
231 seriesConfig->elementAt(j, 0));
233 sc.
enabledEdit =
new WCheckBox(seriesConfig->elementAt(j, 1));
236 sc.
typeEdit =
new WComboBox(seriesConfig->elementAt(j, 2));
241 sc.
markerEdit =
new WComboBox(seriesConfig->elementAt(j, 3));
246 sc.
axisEdit =
new WComboBox(seriesConfig->elementAt(j, 4));
251 sc.
legendEdit =
new WCheckBox(seriesConfig->elementAt(j, 5));
254 sc.
shadowEdit =
new WCheckBox(seriesConfig->elementAt(j, 6));
257 sc.
labelsEdit =
new WComboBox(seriesConfig->elementAt(j, 7));
262 int si = seriesIndexOf(chart, j);
266 const WDataSeries& s =
chart_->series(j);
269 sc.
typeEdit->setCurrentIndex(0);
break;
271 sc.
typeEdit->setCurrentIndex(s.fillRange() != NoFill ?
272 (s.isStacked() ? 7 : 4) : 1);
break;
274 sc.
typeEdit->setCurrentIndex(s.fillRange() != NoFill ?
275 (s.isStacked() ? 8 : 5) : 2);
break;
277 sc.
typeEdit->setCurrentIndex(s.isStacked() ? 6 : 3);
280 sc.
markerEdit->setCurrentIndex((
int)s.marker());
281 sc.
legendEdit->setChecked(s.isLegendEnabled());
282 sc.
shadowEdit->setChecked(s.shadow() != WShadow());
287 seriesConfig->rowAt(j)->setStyleClass(
"trdata");
290 p = list->
addWidget(
"Series properties", seriesConfig);
292 p->setMargin(WLength::Auto, Left | Right);
293 p->resize(1080, WLength::Auto);
294 p->setMargin(20, Top | Bottom);
298 WStandardItemModel *yScales =
new WStandardItemModel(0, 1,
this);
299 addEntry(yScales,
"Linear scale");
300 addEntry(yScales,
"Log scale");
302 WStandardItemModel *xScales =
new WStandardItemModel(0, 1,
this);
303 addEntry(xScales,
"Categories");
304 addEntry(xScales,
"Linear scale");
305 addEntry(xScales,
"Log scale");
306 addEntry(xScales,
"Date scale");
308 WTable *axisConfig =
new WTable();
309 axisConfig->setMargin(WLength::Auto, Left | Right);
311 ::addHeader(axisConfig,
"Axis");
312 ::addHeader(axisConfig,
"Visible");
313 ::addHeader(axisConfig,
"Scale");
314 ::addHeader(axisConfig,
"Automatic");
315 ::addHeader(axisConfig,
"Minimum");
316 ::addHeader(axisConfig,
"Maximum");
317 ::addHeader(axisConfig,
"Gridlines");
318 ::addHeader(axisConfig,
"Label angle");
319 ::addHeader(axisConfig,
"Title");
320 ::addHeader(axisConfig,
"Title orientation");
321 ::addHeader(axisConfig,
"Tick direction");
322 ::addHeader(axisConfig,
"Location");
324 axisConfig->rowAt(0)->setStyleClass(
"trhead");
326 for (
int i = 0; i < 3; ++i) {
327 const char *axisName[] = {
"X axis",
"1st Y axis",
"2nd Y axis" };
330 const WAxis& axis =
chart_->axis(static_cast<Axis>(i));
333 new WText(WString(axisName[i], UTF8), axisConfig->elementAt(j, 0));
335 sc.
visibleEdit =
new WCheckBox(axisConfig->elementAt(j, 1));
339 sc.
scaleEdit =
new WComboBox(axisConfig->elementAt(j, 2));
340 if (axis.scale() == CategoryScale)
343 if (axis.id() == XAxis) {
345 sc.
scaleEdit->setCurrentIndex(axis.scale());
348 sc.
scaleEdit->setCurrentIndex(axis.scale() - 1);
353 bool autoValues = axis.autoLimits() == (MinimumValue | MaximumValue);
355 sc.
minimumEdit =
new WLineEdit(axisConfig->elementAt(j, 4));
357 .toString(axis.minimum()));
362 sc.
maximumEdit =
new WLineEdit(axisConfig->elementAt(j, 5));
364 .toString(axis.maximum()));
369 sc.
autoEdit =
new WCheckBox(axisConfig->elementAt(j, 3));
370 sc.
autoEdit->setChecked(autoValues);
377 sc.
gridLinesEdit =
new WCheckBox(axisConfig->elementAt(j, 6));
385 sc.
titleEdit =
new WLineEdit(axisConfig->elementAt(j, 8));
401 sc.
locationEdit =
new WComboBox(axisConfig->elementAt(j, 11));
407 if (axis.location() == ZeroValue) {
412 axisConfig->rowAt(j)->setStyleClass(
"trdata");
417 p = list->
addWidget(
"Axis properties", axisConfig);
418 p->setMargin(WLength::Auto, Left | Right);
419 p->resize(1080, WLength::Auto);
420 p->setMargin(20, Top | Bottom);
426 if (!WApplication::instance()->environment().javaScript()) {
427 WPushButton *b =
new WPushButton(
this);
428 b->setText(
"Update chart");
430 b->setMargin(WLength::Auto, Left | Right);
442 bool haveLegend =
false;
443 std::vector<WDataSeries *> series;
445 for (
int i = 1; i <
chart_->model()->columnCount(); ++i) {
449 WDataSeries *s =
new WDataSeries(i);
451 switch (sc.
typeEdit->currentIndex()) {
453 s->setType(PointSeries);
458 s->setType(LineSeries);
461 s->setType(CurveSeries);
464 s->setType(BarSeries);
467 s->setType(LineSeries);
468 s->setFillRange(
fill_);
471 s->setType(CurveSeries);
472 s->setFillRange(
fill_);
475 s->setType(BarSeries);
479 s->setType(LineSeries);
480 s->setFillRange(
fill_);
484 s->setType(CurveSeries);
485 s->setFillRange(
fill_);
490 if(sc.
markerEdit->currentIndex() == CustomMarker){
491 WPainterPath pp = WPainterPath();
494 s->setCustomMarker(pp);
497 s->setMarker(static_cast<MarkerType>(sc.
markerEdit->currentIndex()));
499 if (sc.
axisEdit->currentIndex() == 1) {
500 s->bindToAxis(Y2Axis);
504 s->setLegendEnabled(
true);
507 s->setLegendEnabled(
false);
510 s->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3));
512 s->setShadow(WShadow());
516 s->setLabelsEnabled(XAxis);
519 s->setLabelsEnabled(YAxis);
522 s->setLabelsEnabled(XAxis);
523 s->setLabelsEnabled(YAxis);
531 chart_->setSeries(series);
533 for (
int i = 0; i < 3; ++i) {
535 WAxis& axis =
chart_->axis(static_cast<Axis>(i));
541 if (axis.id() != XAxis)
545 chart_->setType(CategoryChart);
547 chart_->setType(ScatterPlot);
552 axis.setScale(LinearScale);
break;
554 axis.setScale(LogScale);
break;
556 axis.setScale(DateScale);
break;
561 axis.setAutoLimits(MinimumValue | MaximumValue);
563 if (axis.autoLimits() & (MinimumValue | MaximumValue)) {
565 .toString(axis.minimum()));
567 .toString(axis.maximum()));
574 if (axis.scale() == LogScale)
578 if (axis.scale() == DateScale){
580 WDate dMin = WDate(1900,1,1);
581 double gregDaysMin = (double)dMin.toJulianDay();
583 WDate dMax = WDate(3000,1,1);
584 double gregDaysMax = (double)dMax.toJulianDay();
586 bool greg_year_validation =
587 (min > gregDaysMin &&
592 if(!greg_year_validation){
598 axis.setRange(min, max);
606 axis.setLabelAngle(angle);
615 axis.setTickDirection(sc.
tickDirectionEdit->currentIndex() == 0 ? Outwards : Inwards);
619 axis.setLocation(MinimumValue);
622 axis.setLocation(MaximumValue);
625 axis.setLocation(ZeroValue);
628 axis.setLocation(BothSides);
636 double width, height;
639 chart_->resize(width, height);
644 chart_->setOrientation(Vertical);
break;
646 chart_->setOrientation(Horizontal);
break;
649 chart_->setLegendEnabled(haveLegend);
652 LegendLocation location = LegendOutside;
654 AlignmentFlag alignment = AlignMiddle;
656 case 0: location = LegendOutside;
break;
657 case 1: location = LegendInside;
break;
661 case 0: side = Top;
break;
662 case 1: side = Right;
break;
663 case 2: side = Bottom;
break;
664 case 3: side = Left;
break;
667 if (side == Left || side == Right) {
676 case 0: alignment = AlignLeft;
break;
677 case 1: alignment = AlignCenter;
break;
678 case 2: alignment = AlignRight;
break;
679 case 3: alignment = AlignTop;
break;
680 case 4: alignment = AlignMiddle;
break;
681 case 5: alignment = AlignBottom;
break;
684 chart_->setLegendLocation(location, side, alignment);
686 chart_->setLegendColumns((side == Top || side == Bottom ) ? 2 : 1,
691 chart_->setBorderPen(WPen());
693 chart_->setBorderPen(NoPen);
699 bool valid = w->validate() == WValidator::Valid;
701 if (!WApplication::instance()->environment().javaScript()) {
702 w->setStyleClass(valid ?
"" :
"Wt-invalid");
703 w->setToolTip(valid ?
"" :
"Invalid value");
712 if (dynamic_cast<WLineEdit *>(w))
Wt::WCheckBox * borderEdit_
ChartConfig(Wt::Chart::WCartesianChart *chart, Wt::WContainerWidget *parent)
Constructor.
Wt::Chart::FillRangeType fill_
Wt::WComboBox * scaleEdit
Wt::WLineEdit * titleEdit_
Wt::Chart::WCartesianChart * chart_
void connectSignals(Wt::WFormWidget *w)
Wt::WCheckBox * enabledEdit
Wt::WComboBox * locationEdit
Struct that holds the controls for one series.
Wt::WLineEdit * titleEdit
Wt::WCheckBox * visibleEdit
Wt::WComboBox * markerEdit
Wt::WLineEdit * chartWidthEdit_
Struct that holds the controls for one axis.
Wt::WPanel * addWidget(const Wt::WString &text, Wt::WWidget *w)
Wt::WComboBox * chartOrientationEdit_
Wt::WComboBox * legendLocationEdit_
std::vector< SeriesControl > seriesControls_
Controls for series.
Wt::WLineEdit * minimumEdit
Wt::WComboBox * legendSideEdit_
std::vector< AxisControl > axisControls_
Controls for axes.
Wt::WCheckBox * shadowEdit
Wt::WComboBox * labelsEdit
void setValueFill(Wt::Chart::FillRangeType fill)
Wt::WComboBox * tickDirectionEdit
Wt::WLineEdit * labelAngleEdit
Wt::WCheckBox * legendEdit
Wt::WCheckBox * gridLinesEdit
Wt::WComboBox * titleOrientationEdit
Wt::WComboBox * legendAlignmentEdit_
Wt::WLineEdit * maximumEdit
static bool validate(Wt::WFormWidget *w)
Wt::WLineEdit * chartHeightEdit_