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:"));
155 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Legend alignment:"));
163 chartConfig->elementAt(row, 0)->addWidget(
new WText(
"Border:"));
164 borderEdit_ =
new WCheckBox(chartConfig->elementAt(row, 1));
169 for (
int i = 0; i < chartConfig->rowCount(); ++i) {
170 chartConfig->elementAt(i, 0)->setStyleClass(
"tdhead");
171 chartConfig->elementAt(i, 1)->setStyleClass(
"tddata");
174 WPanel *p = list->
addWidget(
"Chart properties", chartConfig);
175 p->setMargin(WLength::Auto, Left | Right);
176 p->resize(1080, WLength::Auto);
177 p->setMargin(20, Top | Bottom);
181 WStandardItemModel *types =
new WStandardItemModel(0, 1,
this);
182 addEntry(types,
"Points");
183 addEntry(types,
"Line");
184 addEntry(types,
"Curve");
185 addEntry(types,
"Bar");
186 addEntry(types,
"Line Area");
187 addEntry(types,
"Curve Area");
188 addEntry(types,
"Stacked Bar");
189 addEntry(types,
"Stacked Line Area");
190 addEntry(types,
"Stacked Curve Area");
192 WStandardItemModel *markers =
new WStandardItemModel(0, 1,
this);
193 addEntry(markers,
"None");
194 addEntry(markers,
"Square");
195 addEntry(markers,
"Circle");
196 addEntry(markers,
"Cross");
197 addEntry(markers,
"X cross");
198 addEntry(markers,
"Triangle");
199 addEntry(markers,
"Diamond");
201 WStandardItemModel *axes =
new WStandardItemModel(0, 1,
this);
202 addEntry(axes,
"1st Y axis");
203 addEntry(axes,
"2nd Y axis");
205 WStandardItemModel *labels =
new WStandardItemModel(0, 1,
this);
206 addEntry(labels,
"None");
207 addEntry(labels,
"X");
208 addEntry(labels,
"Y");
209 addEntry(labels,
"X: Y");
211 WTable *seriesConfig =
new WTable();
212 seriesConfig->setMargin(WLength::Auto, Left | Right);
214 ::addHeader(seriesConfig,
"Name");
215 ::addHeader(seriesConfig,
"Enabled");
216 ::addHeader(seriesConfig,
"Type");
217 ::addHeader(seriesConfig,
"Marker");
218 ::addHeader(seriesConfig,
"Y axis");
219 ::addHeader(seriesConfig,
"Legend");
220 ::addHeader(seriesConfig,
"Shadow");
221 ::addHeader(seriesConfig,
"Value labels");
223 seriesConfig->rowAt(0)->setStyleClass(
"trhead");
225 for (
int j = 1; j < chart->model()->columnCount(); ++j) {
228 new WText(asString(chart->model()->headerData(j)),
229 seriesConfig->elementAt(j, 0));
231 sc.
enabledEdit =
new WCheckBox(seriesConfig->elementAt(j, 1));
234 sc.
typeEdit =
new WComboBox(seriesConfig->elementAt(j, 2));
239 sc.
markerEdit =
new WComboBox(seriesConfig->elementAt(j, 3));
244 sc.
axisEdit =
new WComboBox(seriesConfig->elementAt(j, 4));
249 sc.
legendEdit =
new WCheckBox(seriesConfig->elementAt(j, 5));
252 sc.
shadowEdit =
new WCheckBox(seriesConfig->elementAt(j, 6));
255 sc.
labelsEdit =
new WComboBox(seriesConfig->elementAt(j, 7));
260 int si = seriesIndexOf(chart, j);
264 const WDataSeries& s =
chart_->series(j);
267 sc.
typeEdit->setCurrentIndex(0);
break;
269 sc.
typeEdit->setCurrentIndex(s.fillRange() != NoFill ?
270 (s.isStacked() ? 7 : 4) : 1);
break;
272 sc.
typeEdit->setCurrentIndex(s.fillRange() != NoFill ?
273 (s.isStacked() ? 8 : 5) : 2);
break;
275 sc.
typeEdit->setCurrentIndex(s.isStacked() ? 6 : 3);
278 sc.
markerEdit->setCurrentIndex((
int)s.marker());
279 sc.
legendEdit->setChecked(s.isLegendEnabled());
280 sc.
shadowEdit->setChecked(s.shadow() != WShadow());
285 seriesConfig->rowAt(j)->setStyleClass(
"trdata");
288 p = list->
addWidget(
"Series properties", seriesConfig);
290 p->setMargin(WLength::Auto, Left | Right);
291 p->resize(1080, WLength::Auto);
292 p->setMargin(20, Top | Bottom);
296 WStandardItemModel *yScales =
new WStandardItemModel(0, 1,
this);
297 addEntry(yScales,
"Linear scale");
298 addEntry(yScales,
"Log scale");
300 WStandardItemModel *xScales =
new WStandardItemModel(0, 1,
this);
301 addEntry(xScales,
"Categories");
302 addEntry(xScales,
"Linear scale");
303 addEntry(xScales,
"Log scale");
304 addEntry(xScales,
"Date scale");
306 WTable *axisConfig =
new WTable();
307 axisConfig->setMargin(WLength::Auto, Left | Right);
309 ::addHeader(axisConfig,
"Axis");
310 ::addHeader(axisConfig,
"Visible");
311 ::addHeader(axisConfig,
"Scale");
312 ::addHeader(axisConfig,
"Automatic");
313 ::addHeader(axisConfig,
"Minimum");
314 ::addHeader(axisConfig,
"Maximum");
315 ::addHeader(axisConfig,
"Gridlines");
316 ::addHeader(axisConfig,
"Label angle");
317 ::addHeader(axisConfig,
"Title");
318 ::addHeader(axisConfig,
"Title orientation");
319 ::addHeader(axisConfig,
"Tick direction");
320 ::addHeader(axisConfig,
"Location");
322 axisConfig->rowAt(0)->setStyleClass(
"trhead");
324 for (
int i = 0; i < 3; ++i) {
325 const char *axisName[] = {
"X axis",
"1st Y axis",
"2nd Y axis" };
328 const WAxis& axis =
chart_->axis(static_cast<Axis>(i));
331 new WText(WString(axisName[i], UTF8), axisConfig->elementAt(j, 0));
333 sc.
visibleEdit =
new WCheckBox(axisConfig->elementAt(j, 1));
337 sc.
scaleEdit =
new WComboBox(axisConfig->elementAt(j, 2));
338 if (axis.scale() == CategoryScale)
341 if (axis.id() == XAxis) {
343 sc.
scaleEdit->setCurrentIndex(axis.scale());
346 sc.
scaleEdit->setCurrentIndex(axis.scale() - 1);
351 bool autoValues = axis.autoLimits() == (MinimumValue | MaximumValue);
353 sc.
minimumEdit =
new WLineEdit(axisConfig->elementAt(j, 4));
355 .toString(axis.minimum()));
360 sc.
maximumEdit =
new WLineEdit(axisConfig->elementAt(j, 5));
362 .toString(axis.maximum()));
367 sc.
autoEdit =
new WCheckBox(axisConfig->elementAt(j, 3));
368 sc.
autoEdit->setChecked(autoValues);
375 sc.
gridLinesEdit =
new WCheckBox(axisConfig->elementAt(j, 6));
383 sc.
titleEdit =
new WLineEdit(axisConfig->elementAt(j, 8));
399 sc.
locationEdit =
new WComboBox(axisConfig->elementAt(j, 11));
405 if (axis.location() == ZeroValue) {
410 axisConfig->rowAt(j)->setStyleClass(
"trdata");
415 p = list->
addWidget(
"Axis properties", axisConfig);
416 p->setMargin(WLength::Auto, Left | Right);
417 p->resize(1080, WLength::Auto);
418 p->setMargin(20, Top | Bottom);
424 if (!WApplication::instance()->environment().javaScript()) {
425 WPushButton *b =
new WPushButton(
this);
426 b->setText(
"Update chart");
428 b->setMargin(WLength::Auto, Left | Right);
440 bool haveLegend =
false;
441 std::vector<WDataSeries> series;
443 for (
int i = 1; i <
chart_->model()->columnCount(); ++i) {
449 switch (sc.
typeEdit->currentIndex()) {
451 s.setType(PointSeries);
456 s.setType(LineSeries);
460 s.setType(CurveSeries);
464 s.setType(BarSeries);
468 s.setType(LineSeries);
469 s.setFillRange(
fill_);
473 s.setType(CurveSeries);
474 s.setFillRange(
fill_);
478 s.setType(BarSeries);
483 s.setType(LineSeries);
484 s.setFillRange(
fill_);
489 s.setType(CurveSeries);
490 s.setFillRange(
fill_);
496 if(sc.
markerEdit->currentIndex() == CustomMarker){
497 WPainterPath pp = WPainterPath();
503 s.setCustomMarker(pp);
506 s.setMarker(static_cast<MarkerType>(sc.
markerEdit->currentIndex()));
508 if (sc.
axisEdit->currentIndex() == 1) {
509 s.bindToAxis(Y2Axis);
513 s.setLegendEnabled(
true);
516 s.setLegendEnabled(
false);
519 s.setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3));
521 s.setShadow(WShadow());
525 s.setLabelsEnabled(XAxis);
528 s.setLabelsEnabled(YAxis);
531 s.setLabelsEnabled(XAxis);
532 s.setLabelsEnabled(YAxis);
540 chart_->setSeries(series);
542 for (
int i = 0; i < 3; ++i) {
544 WAxis& axis =
chart_->axis(static_cast<Axis>(i));
550 if (axis.id() != XAxis)
554 chart_->setType(CategoryChart);
556 chart_->setType(ScatterPlot);
561 axis.setScale(LinearScale);
break;
563 axis.setScale(LogScale);
break;
565 axis.setScale(DateScale);
break;
570 axis.setAutoLimits(MinimumValue | MaximumValue);
572 if (axis.autoLimits() & (MinimumValue | MaximumValue)) {
574 .toString(axis.minimum()));
576 .toString(axis.maximum()));
583 if (axis.scale() == LogScale)
587 if (axis.scale() == DateScale){
589 WDate dMin = WDate(1900,1,1);
590 double gregDaysMin = (double)dMin.toJulianDay();
592 WDate dMax = WDate(3000,1,1);
593 double gregDaysMax = (double)dMax.toJulianDay();
595 bool greg_year_validation =
596 (min > gregDaysMin &&
601 if(!greg_year_validation){
607 axis.setRange(min, max);
615 axis.setLabelAngle(angle);
624 axis.setTickDirection(sc.
tickDirectionEdit->currentIndex() == 0 ? Outwards : Inwards);
628 axis.setLocation(MinimumValue);
631 axis.setLocation(MaximumValue);
634 axis.setLocation(ZeroValue);
637 axis.setLocation(BothSides);
645 double width, height;
648 chart_->resize(width, height);
653 chart_->setOrientation(Vertical);
break;
655 chart_->setOrientation(Horizontal);
break;
658 chart_->setLegendEnabled(haveLegend);
661 LegendLocation location = LegendOutside;
663 AlignmentFlag alignment = AlignMiddle;
665 case 0: location = LegendOutside;
break;
666 case 1: location = LegendInside;
break;
670 case 0: side = Top;
break;
671 case 1: side = Right;
break;
672 case 2: side = Bottom;
break;
673 case 3: side = Left;
break;
676 if (side == Left || side == Right) {
685 case 0: alignment = AlignLeft;
break;
686 case 1: alignment = AlignCenter;
break;
687 case 2: alignment = AlignRight;
break;
688 case 3: alignment = AlignTop;
break;
689 case 4: alignment = AlignMiddle;
break;
690 case 5: alignment = AlignBottom;
break;
693 chart_->setLegendLocation(location, side, alignment);
695 chart_->setLegendColumns((side == Top || side == Bottom ) ? 2 : 1,
700 chart_->setBorderPen(WPen());
702 chart_->setBorderPen(NoPen);
708 bool valid = w->validate() == WValidator::Valid;
710 if (!WApplication::instance()->environment().javaScript()) {
711 w->setStyleClass(valid ?
"" :
"Wt-invalid");
712 w->setToolTip(valid ?
"" :
"Invalid value");
721 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_