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)));
74 WIntValidator *sizeValidator =
new WIntValidator(200, 2000,
this);
75 sizeValidator->setMandatory(
true);
77 WDoubleValidator *anyNumberValidator =
new WDoubleValidator(
this);
78 anyNumberValidator->setMandatory(
true);
80 WDoubleValidator *angleValidator =
new WDoubleValidator(-90, 90,
this);
81 angleValidator->setMandatory(
true);
85 WStandardItemModel *orientation =
new WStandardItemModel(0, 1,
this);
86 addEntry(orientation,
"Vertical");
87 addEntry(orientation,
"Horizontal");
89 WStandardItemModel *legendLocation =
new WStandardItemModel(0, 1,
this);
90 addEntry(legendLocation,
"Outside");
91 addEntry(legendLocation,
"Inside");
93 WStandardItemModel *legendSide =
new WStandardItemModel(0, 1,
this);
94 addEntry(legendSide,
"Top");
95 addEntry(legendSide,
"Right");
96 addEntry(legendSide,
"Bottom");
97 addEntry(legendSide,
"Left");
99 WStandardItemModel *legendAlignment =
new WStandardItemModel(0, 1,
this);
100 addEntry(legendAlignment,
"AlignLeft");
101 addEntry(legendAlignment,
"AlignCenter");
102 addEntry(legendAlignment,
"AlignRight");
103 addEntry(legendAlignment,
"AlignTop");
104 addEntry(legendAlignment,
"AlignMiddle");
105 addEntry(legendAlignment,
"AlignBottom");
107 WTable *chartConfig =
new WTable();
108 chartConfig->setMargin(WLength::Auto, Left | Right);
111 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Title:"));
112 titleEdit_ =
new WLineEdit(chartConfig->elementAt(row, 1));
116 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Width:"));
119 ->setText(WLocale::currentLocale().toString(
chart_->width().value()));
125 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Height:"));
128 ->setText(WLocale::currentLocale().toString(
chart_->height().value()));
134 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Orientation:"));
140 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Legend location:"));
146 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Legend side:"));
153 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Legend alignment:"));
160 for (
int i = 0; i < chartConfig->rowCount(); ++i) {
161 chartConfig->elementAt(i, 0)->setStyleClass(
"tdhead");
162 chartConfig->elementAt(i, 1)->setStyleClass(
"tddata");
165 WPanel *p = list->
addWidget(
"Chart properties", chartConfig);
166 p->setMargin(WLength::Auto, Left | Right);
167 p->resize(880, WLength::Auto);
168 p->setMargin(20, Top | Bottom);
170 if (
chart_->isLegendEnabled())
171 chart_->setPlotAreaPadding(200, Right);
175 WStandardItemModel *types =
new WStandardItemModel(0, 1,
this);
176 addEntry(types,
"Points");
177 addEntry(types,
"Line");
178 addEntry(types,
"Curve");
179 addEntry(types,
"Bar");
180 addEntry(types,
"Line Area");
181 addEntry(types,
"Curve Area");
182 addEntry(types,
"Stacked Bar");
183 addEntry(types,
"Stacked Line Area");
184 addEntry(types,
"Stacked Curve Area");
186 WStandardItemModel *markers =
new WStandardItemModel(0, 1,
this);
187 addEntry(markers,
"None");
188 addEntry(markers,
"Square");
189 addEntry(markers,
"Circle");
190 addEntry(markers,
"Cross");
191 addEntry(markers,
"X cross");
192 addEntry(markers,
"Triangle");
193 addEntry(markers,
"Diamond");
195 WStandardItemModel *axes =
new WStandardItemModel(0, 1,
this);
196 addEntry(axes,
"1st Y axis");
197 addEntry(axes,
"2nd Y axis");
199 WStandardItemModel *labels =
new WStandardItemModel(0, 1,
this);
200 addEntry(labels,
"None");
201 addEntry(labels,
"X");
202 addEntry(labels,
"Y");
203 addEntry(labels,
"X: Y");
205 WTable *seriesConfig =
new WTable();
206 seriesConfig->setMargin(WLength::Auto, Left | Right);
208 ::addHeader(seriesConfig,
"Name");
209 ::addHeader(seriesConfig,
"Enabled");
210 ::addHeader(seriesConfig,
"Type");
211 ::addHeader(seriesConfig,
"Marker");
212 ::addHeader(seriesConfig,
"Y axis");
213 ::addHeader(seriesConfig,
"Legend");
214 ::addHeader(seriesConfig,
"Shadow");
215 ::addHeader(seriesConfig,
"Value labels");
217 seriesConfig->rowAt(0)->setStyleClass(
"trhead");
219 for (
int j = 1; j < chart->model()->columnCount(); ++j) {
222 new WText(asString(chart->model()->headerData(j)),
223 seriesConfig->elementAt(j, 0));
225 sc.
enabledEdit =
new WCheckBox(seriesConfig->elementAt(j, 1));
228 sc.
typeEdit =
new WComboBox(seriesConfig->elementAt(j, 2));
232 sc.
markerEdit =
new WComboBox(seriesConfig->elementAt(j, 3));
236 sc.
axisEdit =
new WComboBox(seriesConfig->elementAt(j, 4));
240 sc.
legendEdit =
new WCheckBox(seriesConfig->elementAt(j, 5));
243 sc.
shadowEdit =
new WCheckBox(seriesConfig->elementAt(j, 6));
246 sc.
labelsEdit =
new WComboBox(seriesConfig->elementAt(j, 7));
250 int si = seriesIndexOf(chart, j);
254 const WDataSeries& s =
chart_->series(j);
257 sc.
typeEdit->setCurrentIndex(0);
break;
259 sc.
typeEdit->setCurrentIndex(s.fillRange() != NoFill ?
260 (s.isStacked() ? 7 : 4) : 1);
break;
262 sc.
typeEdit->setCurrentIndex(s.fillRange() != NoFill ?
263 (s.isStacked() ? 8 : 5) : 2);
break;
265 sc.
typeEdit->setCurrentIndex(s.isStacked() ? 6 : 3);
268 sc.
markerEdit->setCurrentIndex((
int)s.marker());
269 sc.
legendEdit->setChecked(s.isLegendEnabled());
270 sc.
shadowEdit->setChecked(s.shadow() != WShadow());
275 seriesConfig->rowAt(j)->setStyleClass(
"trdata");
278 p = list->
addWidget(
"Series properties", seriesConfig);
280 p->setMargin(WLength::Auto, Left | Right);
281 p->resize(880, WLength::Auto);
282 p->setMargin(20, Top | Bottom);
286 WStandardItemModel *yScales =
new WStandardItemModel(0, 1,
this);
287 addEntry(yScales,
"Linear scale");
288 addEntry(yScales,
"Log scale");
290 WStandardItemModel *xScales =
new WStandardItemModel(0, 1,
this);
291 addEntry(xScales,
"Categories");
292 addEntry(xScales,
"Linear scale");
293 addEntry(xScales,
"Log scale");
294 addEntry(xScales,
"Date scale");
296 WTable *axisConfig =
new WTable();
297 axisConfig->setMargin(WLength::Auto, Left | Right);
299 ::addHeader(axisConfig,
"Axis");
300 ::addHeader(axisConfig,
"Visible");
301 ::addHeader(axisConfig,
"Scale");
302 ::addHeader(axisConfig,
"Automatic");
303 ::addHeader(axisConfig,
"Minimum");
304 ::addHeader(axisConfig,
"Maximum");
305 ::addHeader(axisConfig,
"Gridlines");
306 ::addHeader(axisConfig,
"Label angle");
308 axisConfig->rowAt(0)->setStyleClass(
"trhead");
310 for (
int i = 0; i < 3; ++i) {
311 const char *axisName[] = {
"X axis",
"1st Y axis",
"2nd Y axis" };
314 const WAxis& axis =
chart_->axis(static_cast<Axis>(i));
317 new WText(WString(axisName[i], UTF8), axisConfig->elementAt(j, 0));
319 sc.
visibleEdit =
new WCheckBox(axisConfig->elementAt(j, 1));
323 sc.
scaleEdit =
new WComboBox(axisConfig->elementAt(j, 2));
324 if (axis.scale() == CategoryScale)
327 if (axis.id() == XAxis) {
329 sc.
scaleEdit->setCurrentIndex(axis.scale());
332 sc.
scaleEdit->setCurrentIndex(axis.scale() - 1);
337 bool autoValues = axis.autoLimits() == (MinimumValue | MaximumValue);
339 sc.
minimumEdit =
new WLineEdit(axisConfig->elementAt(j, 4));
341 .toString(axis.minimum()));
346 sc.
maximumEdit =
new WLineEdit(axisConfig->elementAt(j, 5));
348 .toString(axis.maximum()));
353 sc.
autoEdit =
new WCheckBox(axisConfig->elementAt(j, 3));
354 sc.
autoEdit->setChecked(autoValues);
361 sc.
gridLinesEdit =
new WCheckBox(axisConfig->elementAt(j, 6));
369 axisConfig->rowAt(j)->setStyleClass(
"trdata");
374 p = list->
addWidget(
"Axis properties", axisConfig);
375 p->setMargin(WLength::Auto, Left | Right);
376 p->resize(880, WLength::Auto);
377 p->setMargin(20, Top | Bottom);
383 if (!WApplication::instance()->environment().javaScript()) {
384 WPushButton *b =
new WPushButton(
this);
385 b->setText(
"Update chart");
387 b->setMargin(WLength::Auto, Left | Right);
399 bool haveLegend =
false;
400 std::vector<WDataSeries> series;
402 for (
int i = 1; i <
chart_->model()->columnCount(); ++i) {
408 switch (sc.
typeEdit->currentIndex()) {
410 s.setType(PointSeries);
415 s.setType(LineSeries);
419 s.setType(CurveSeries);
423 s.setType(BarSeries);
427 s.setType(LineSeries);
428 s.setFillRange(
fill_);
432 s.setType(CurveSeries);
433 s.setFillRange(
fill_);
437 s.setType(BarSeries);
442 s.setType(LineSeries);
443 s.setFillRange(
fill_);
448 s.setType(CurveSeries);
449 s.setFillRange(
fill_);
455 if(sc.
markerEdit->currentIndex() == CustomMarker){
456 WPainterPath pp = WPainterPath();
462 s.setCustomMarker(pp);
465 s.setMarker(static_cast<MarkerType>(sc.
markerEdit->currentIndex()));
467 if (sc.
axisEdit->currentIndex() == 1) {
468 s.bindToAxis(Y2Axis);
472 s.setLegendEnabled(
true);
475 s.setLegendEnabled(
false);
478 s.setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3));
480 s.setShadow(WShadow());
484 s.setLabelsEnabled(XAxis);
487 s.setLabelsEnabled(YAxis);
490 s.setLabelsEnabled(XAxis);
491 s.setLabelsEnabled(YAxis);
499 chart_->setSeries(series);
501 for (
int i = 0; i < 3; ++i) {
503 WAxis& axis =
chart_->axis(static_cast<Axis>(i));
509 if (axis.id() != XAxis)
513 chart_->setType(CategoryChart);
515 chart_->setType(ScatterPlot);
520 axis.setScale(LinearScale);
break;
522 axis.setScale(LogScale);
break;
524 axis.setScale(DateScale);
break;
529 axis.setAutoLimits(MinimumValue | MaximumValue);
536 if (axis.scale() == LogScale)
540 if (axis.scale() == DateScale){
542 WDate dMin = WDate(1900,1,1);
543 double gregDaysMin = (double)dMin.toJulianDay();
545 WDate dMax = WDate(3000,1,1);
546 double gregDaysMax = (double)dMax.toJulianDay();
548 bool greg_year_validation =
549 (min > gregDaysMin &&
554 if(!greg_year_validation){
560 axis.setRange(min, max);
568 axis.setLabelAngle(angle);
577 double width, height;
580 chart_->resize(width, height);
585 chart_->setOrientation(Vertical);
break;
587 chart_->setOrientation(Horizontal);
break;
590 chart_->setLegendEnabled(haveLegend);
593 LegendLocation location = LegendOutside;
595 AlignmentFlag alignment = AlignMiddle;
598 case 0: location = LegendOutside;
break;
599 case 1: location = LegendInside;
break;
603 case 0: side = Top;
break;
604 case 1: side = Right;
break;
605 case 2: side = Bottom;
break;
606 case 3: side = Left;
break;
609 if (side == Left || side == Right) {
618 case 0: alignment = AlignLeft;
break;
619 case 1: alignment = AlignCenter;
break;
620 case 2: alignment = AlignRight;
break;
621 case 3: alignment = AlignTop;
break;
622 case 4: alignment = AlignMiddle;
break;
623 case 5: alignment = AlignBottom;
break;
626 chart_->setLegendLocation(location, side, alignment);
628 chart_->setLegendColumns((side == Top || side == Bottom ) ? 2 : 1,
632 for (
unsigned i = 0; i < 4; ++i) {
633 Side sides[] = { Top, Right, Bottom, Left };
637 &&
chart_->legendLocation() == LegendOutside
638 &&
chart_->legendSide() == sides[i];
643 padding = legendRoom ? 80 : 40;
645 padding = legendRoom ? 200 : 80;
647 chart_->setPlotAreaPadding(padding, sides[i]);
653 bool valid = w->validate() == WValidator::Valid;
655 if (!WApplication::instance()->environment().javaScript()) {
656 w->setStyleClass(valid ?
"" :
"Wt-invalid");
657 w->setToolTip(valid ?
"" :
"Invalid value");
666 if (dynamic_cast<WLineEdit *>(w))
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
Struct that holds the controls for one series.
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::WLineEdit * labelAngleEdit
Wt::WCheckBox * legendEdit
Wt::WCheckBox * gridLinesEdit
Wt::WComboBox * legendAlignmentEdit_
Wt::WLineEdit * maximumEdit
static bool validate(Wt::WFormWidget *w)
Wt::WLineEdit * chartHeightEdit_