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 void addEntry(WAbstractItemModel *model,
const WString &value) {
46 model->insertRows(model->rowCount(), 1);
47 model->setData(model->rowCount()-1, 0, boost::any(value));
50 bool getDouble(WLineEdit *edit,
double& value) {
52 value = WLocale::currentLocale().toDouble(edit->text());
59 int seriesIndexOf(WCartesianChart* chart,
int modelColumn) {
60 for (
unsigned i = 0; i < chart->series().size(); ++i)
61 if (chart->series()[i]->modelColumn() == modelColumn)
67 WString axisName(Axis axis,
int yAxis)
70 return Wt::utf8(
"X Axis");
72 return Wt::utf8(
"Y axis {1}").arg(yAxis + 1);
78 : WContainerWidget(parent),
80 fill_(MinimumValueFill)
82 chart_->setLegendStyle(
chart_->legendFont(), WPen(black),
83 WBrush(WColor(0xFF, 0xFA, 0xE5)));
87 WIntValidator *sizeValidator =
new WIntValidator(200, 2000,
this);
88 sizeValidator->setMandatory(
true);
98 WStandardItemModel *orientation =
new WStandardItemModel(0, 1,
this);
99 addEntry(orientation,
"Vertical");
100 addEntry(orientation,
"Horizontal");
102 WStandardItemModel *legendLocation =
new WStandardItemModel(0, 1,
this);
103 addEntry(legendLocation,
"Outside");
104 addEntry(legendLocation,
"Inside");
106 WStandardItemModel *legendSide =
new WStandardItemModel(0, 1,
this);
107 addEntry(legendSide,
"Top");
108 addEntry(legendSide,
"Right");
109 addEntry(legendSide,
"Bottom");
110 addEntry(legendSide,
"Left");
112 WStandardItemModel *legendAlignment =
new WStandardItemModel(0, 1,
this);
113 addEntry(legendAlignment,
"AlignLeft");
114 addEntry(legendAlignment,
"AlignCenter");
115 addEntry(legendAlignment,
"AlignRight");
116 addEntry(legendAlignment,
"AlignTop");
117 addEntry(legendAlignment,
"AlignMiddle");
118 addEntry(legendAlignment,
"AlignBottom");
120 WTable *chartConfig =
new WTable();
121 chartConfig->setMargin(WLength::Auto, Left | Right);
124 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Title:"));
125 titleEdit_ =
new WLineEdit(chartConfig->elementAt(row, 1));
129 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Width:"));
132 ->setText(WLocale::currentLocale().toString(
chart_->width().value()));
138 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Height:"));
141 ->setText(WLocale::currentLocale().toString(
chart_->height().value()));
147 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Orientation:"));
154 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Legend location:"));
161 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Legend side:"));
168 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Legend alignment:"));
175 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Border:"));
176 borderEdit_ =
new WCheckBox(chartConfig->elementAt(row, 1));
181 for (
int i = 0; i < chartConfig->rowCount(); ++i) {
182 chartConfig->elementAt(i, 0)->setStyleClass(
"tdhead");
183 chartConfig->elementAt(i, 1)->setStyleClass(
"tddata");
186 WPanel *p = list->
addWidget(
"Chart properties", chartConfig);
187 p->setMargin(WLength::Auto, Left | Right);
188 p->resize(1160, WLength::Auto);
189 p->setMargin(20, Top | Bottom);
193 WStandardItemModel *types =
new WStandardItemModel(0, 1,
this);
194 addEntry(types,
"Points");
195 addEntry(types,
"Line");
196 addEntry(types,
"Curve");
197 addEntry(types,
"Bar");
198 addEntry(types,
"Line Area");
199 addEntry(types,
"Curve Area");
200 addEntry(types,
"Stacked Bar");
201 addEntry(types,
"Stacked Line Area");
202 addEntry(types,
"Stacked Curve Area");
204 WStandardItemModel *markers =
new WStandardItemModel(0, 1,
this);
205 addEntry(markers,
"None");
206 addEntry(markers,
"Square");
207 addEntry(markers,
"Circle");
208 addEntry(markers,
"Cross");
209 addEntry(markers,
"X cross");
210 addEntry(markers,
"Triangle");
211 addEntry(markers,
"Pipe");
212 addEntry(markers,
"Star");
213 addEntry(markers,
"Inverted triangle");
214 addEntry(markers,
"Asterisk");
215 addEntry(markers,
"Diamond");
221 WStandardItemModel *labels =
new WStandardItemModel(0, 1,
this);
222 addEntry(labels,
"None");
223 addEntry(labels,
"X");
224 addEntry(labels,
"Y");
225 addEntry(labels,
"X: Y");
227 WTable *seriesConfig =
new WTable();
228 seriesConfig->setMargin(WLength::Auto, Left | Right);
230 ::addHeader(seriesConfig,
"Name");
231 ::addHeader(seriesConfig,
"Enabled");
232 ::addHeader(seriesConfig,
"Type");
233 ::addHeader(seriesConfig,
"Marker");
234 ::addHeader(seriesConfig,
"Y axis");
235 ::addHeader(seriesConfig,
"Legend");
236 ::addHeader(seriesConfig,
"Shadow");
237 ::addHeader(seriesConfig,
"Value labels");
239 seriesConfig->rowAt(0)->setStyleClass(
"trhead");
241 for (
int j = 1; j < chart->model()->columnCount(); ++j) {
244 new WText(chart->model()->headerData(j),
245 seriesConfig->elementAt(j, 0));
247 sc.
enabledEdit =
new WCheckBox(seriesConfig->elementAt(j, 1));
250 sc.
typeEdit =
new WComboBox(seriesConfig->elementAt(j, 2));
255 sc.
markerEdit =
new WComboBox(seriesConfig->elementAt(j, 3));
260 sc.
axisEdit =
new WComboBox(seriesConfig->elementAt(j, 4));
265 sc.
legendEdit =
new WCheckBox(seriesConfig->elementAt(j, 5));
268 sc.
shadowEdit =
new WCheckBox(seriesConfig->elementAt(j, 6));
271 sc.
labelsEdit =
new WComboBox(seriesConfig->elementAt(j, 7));
276 int si = seriesIndexOf(chart, j);
280 const WDataSeries& s =
chart_->series(j);
283 sc.
typeEdit->setCurrentIndex(0);
break;
285 sc.
typeEdit->setCurrentIndex(s.fillRange() != NoFill ?
286 (s.isStacked() ? 7 : 4) : 1);
break;
288 sc.
typeEdit->setCurrentIndex(s.fillRange() != NoFill ?
289 (s.isStacked() ? 8 : 5) : 2);
break;
291 sc.
typeEdit->setCurrentIndex(s.isStacked() ? 6 : 3);
294 sc.
markerEdit->setCurrentIndex((
int)s.marker());
295 sc.
legendEdit->setChecked(s.isLegendEnabled());
296 sc.
shadowEdit->setChecked(s.shadow() != WShadow());
301 seriesConfig->rowAt(j)->setStyleClass(
"trdata");
304 p = list->
addWidget(
"Series properties", seriesConfig);
306 p->setMargin(WLength::Auto, Left | Right);
307 p->resize(1160, WLength::Auto);
308 p->setMargin(20, Top | Bottom);
312 yScales_ =
new WStandardItemModel(0, 1,
this);
316 xScales_ =
new WStandardItemModel(0, 1,
this);
322 WContainerWidget *axisConfig =
new WContainerWidget();
324 axisConfig_->setMargin(WLength::Auto, Left | Right);
345 WPushButton *addAxisBtn =
346 new WPushButton(Wt::utf8(
"Add Y axis"), axisConfig);
348 WPushButton *clearAxesBtn =
349 new WPushButton(Wt::utf8(
"Clear Y axes"), axisConfig);
352 p = list->
addWidget(
"Axis properties", axisConfig);
353 p->setMargin(WLength::Auto, Left | Right);
354 p->resize(1160, WLength::Auto);
355 p->setMargin(20, Top | Bottom);
361 if (!WApplication::instance()->environment().javaScript()) {
362 WPushButton *b =
new WPushButton(
this);
363 b->setText(
"Update chart");
365 b->setMargin(WLength::Auto, Left | Right);
377 bool haveLegend =
false;
378 std::vector<WDataSeries *> series;
380 for (
int i = 1; i <
chart_->model()->columnCount(); ++i) {
384 WDataSeries *s =
new WDataSeries(i);
386 switch (sc.
typeEdit->currentIndex()) {
388 s->setType(PointSeries);
393 s->setType(LineSeries);
396 s->setType(CurveSeries);
399 s->setType(BarSeries);
402 s->setType(LineSeries);
403 s->setFillRange(
fill_);
406 s->setType(CurveSeries);
407 s->setFillRange(
fill_);
410 s->setType(BarSeries);
414 s->setType(LineSeries);
415 s->setFillRange(
fill_);
419 s->setType(CurveSeries);
420 s->setFillRange(
fill_);
425 if(sc.
markerEdit->currentIndex() == CustomMarker){
426 WPainterPath pp = WPainterPath();
429 s->setCustomMarker(pp);
432 s->setMarker(
static_cast<MarkerType
>(sc.
markerEdit->currentIndex()));
434 s->bindToYAxis(sc.
axisEdit->currentIndex());
437 s->setLegendEnabled(
true);
440 s->setLegendEnabled(
false);
443 s->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3));
445 s->setShadow(WShadow());
449 s->setLabelsEnabled(XAxis);
452 s->setLabelsEnabled(YAxis);
455 s->setLabelsEnabled(XAxis);
456 s->setLabelsEnabled(YAxis);
464 chart_->setSeries(series);
468 WAxis& axis = i == 0 ?
chart_->axis(XAxis) :
chart_->yAxis(i - 1);
474 if (axis.id() != XAxis)
478 chart_->setType(CategoryChart);
480 chart_->setType(ScatterPlot);
485 axis.setScale(LinearScale);
break;
487 axis.setScale(LogScale);
break;
489 axis.setScale(DateScale);
break;
494 axis.setAutoLimits(MinimumValue | MaximumValue);
496 if (axis.autoLimits() & (MinimumValue | MaximumValue)) {
498 .toString(axis.minimum()));
500 .toString(axis.maximum()));
507 if (axis.scale() == LogScale)
511 if (axis.scale() == DateScale){
513 WDate dMin = WDate(1900,1,1);
514 double gregDaysMin = (double)dMin.toJulianDay();
516 WDate dMax = WDate(3000,1,1);
517 double gregDaysMax = (double)dMax.toJulianDay();
519 bool greg_year_validation =
520 (min > gregDaysMin &&
525 if(!greg_year_validation){
531 axis.setRange(min, max);
539 axis.setLabelAngle(angle);
548 axis.setTickDirection(sc.
tickDirectionEdit->currentIndex() == 0 ? Outwards : Inwards);
552 axis.setLocation(MinimumValue);
555 axis.setLocation(MaximumValue);
558 axis.setLocation(ZeroValue);
561 axis.setLocation(BothSides);
569 double width, height;
572 chart_->resize(width, height);
577 chart_->setOrientation(Vertical);
break;
579 chart_->setOrientation(Horizontal);
break;
582 chart_->setLegendEnabled(haveLegend);
585 LegendLocation location = LegendOutside;
587 AlignmentFlag alignment = AlignMiddle;
589 case 0: location = LegendOutside;
break;
590 case 1: location = LegendInside;
break;
594 case 0: side = Top;
break;
595 case 1: side = Right;
break;
596 case 2: side = Bottom;
break;
597 case 3: side = Left;
break;
600 if (side == Left || side == Right) {
609 case 0: alignment = AlignLeft;
break;
610 case 1: alignment = AlignCenter;
break;
611 case 2: alignment = AlignRight;
break;
612 case 3: alignment = AlignTop;
break;
613 case 4: alignment = AlignMiddle;
break;
614 case 5: alignment = AlignBottom;
break;
617 chart_->setLegendLocation(location, side, alignment);
619 chart_->setLegendColumns((side == Top || side == Bottom ) ? 2 : 1,
624 chart_->setBorderPen(WPen());
626 chart_->setBorderPen(NoPen);
632 bool valid = w->validate() == WValidator::Valid;
634 if (!WApplication::instance()->environment().javaScript()) {
635 w->setStyleClass(valid ?
"" :
"Wt-invalid");
636 w->setToolTip(valid ?
"" :
"Invalid value");
645 if (
dynamic_cast<WLineEdit *
>(w))
651 int yAxis =
chart_->addYAxis(
new WAxis());
660 int j = ax == XAxis ? 1 : yAxis + 2;
662 const WAxis& axis = ax == XAxis ?
chart_->axis(XAxis) :
chart_->yAxis(yAxis);
665 new WText(axisName(axis.id(), axis.yAxisId()),
axisConfig_->elementAt(j, 0));
672 if (axis.scale() == CategoryScale)
675 if (axis.id() == XAxis) {
677 sc.
scaleEdit->setCurrentIndex(axis.scale());
680 sc.
scaleEdit->setCurrentIndex(axis.scale() - 1);
685 bool autoValues = axis.autoLimits() == (MinimumValue | MaximumValue);
689 .toString(axis.minimum()));
696 .toString(axis.maximum()));
702 sc.
autoEdit->setChecked(autoValues);
739 if (axis.location() == MaximumValue) {
741 }
else if (axis.location() == ZeroValue) {
747 WPushButton *removeAxisButton =
748 new WPushButton(Wt::utf8(
"x"),
axisConfig_->elementAt(j, 12));
759 int yAxis = axis->yAxisId();
760 for (std::size_t i = 0; i <
chart_->series().size(); ++i) {
761 if (
chart_->series()[i]->yAxis() == yAxis)
762 chart_->series()[i]->bindToYAxis(-1);
764 chart_->removeYAxis(yAxis);
773 if (
chart_->yAxisCount() == 0)
776 for (std::size_t i = 0; i <
chart_->series().size(); ++i) {
777 chart_->series()[i]->bindToYAxis(-1);