Wt examples: /home/koen/project/wt/public-git/wt/examples/charts/ChartConfig.C Source File

Wt examples 3.3.0

- Main Page
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

/home/koen/project/wt/public-git/wt/examples/charts/ChartConfig.C
Go to the documentation of this file.

    00001 /*
    00002  * Copyright (C) 2008 Emweb bvba, Kessel-Lo, Belgium.
    00003  *
    00004  * See the LICENSE file for terms of use.
    00005  */
    00006
    00007 #include "ChartConfig.h"
    00008 #include "PanelList.h"
    00009
    00010 #include <iostream>
    00011 #include <boost/date_time/gregorian/greg_year.hpp>
    00012
    00013 #include <Wt/WAbstractItemModel>
    00014 #include <Wt/WApplication>
    00015 #include <Wt/WCheckBox>
    00016 #include <Wt/WComboBox>
    00017 #include <Wt/WDoubleValidator>
    00018 #include <Wt/WDate>
    00019 #include <Wt/WEnvironment>
    00020 #include <Wt/WIntValidator>
    00021 #include <Wt/WLineEdit>
    00022 #include <Wt/WLocale>
    00023 #include <Wt/WPanel>
    00024 #include <Wt/WPushButton>
    00025 #include <Wt/WStandardItemModel>
    00026 #include <Wt/WTable>
    00027 #include <Wt/WText>
    00028 #include <Wt/WPainterPath>
    00029
    00030 #include <Wt/Chart/WCartesianChart>
    00031
    00032 using namespace Wt;
    00033 using namespace Wt::Chart;
    00034
    00035 namespace {
    00036   void addHeader(WTable *t, const char *value) {
    00037     t->elementAt(0, t->columnCount())->addWidget(new WText(value));
    00038   }
    00039
    00040   void addEntry(WAbstractItemModel *model, const char *value) {
    00041     model->insertRows(model->rowCount(), 1);
    00042     model->setData(model->rowCount()-1, 0, boost::any(std::string(value)));
    00043   }
    00044
    00045   bool getDouble(WLineEdit *edit, double& value) {
    00046     try {
    00047       value = WLocale::currentLocale().toDouble(edit->text());
    00048       return true;
    00049     } catch (...) {
    00050       return false;
    00051     }
    00052   }
    00053
    00054   int seriesIndexOf(WCartesianChart* chart, int modelColumn) {
    00055     for (unsigned i = 0; i < chart->series().size(); ++i)
    00056       if (chart->series()[i].modelColumn() == modelColumn)
    00057         return i;
    00058
    00059     return -1;
    00060   }
    00061 }
    00062
    00063 ChartConfig::ChartConfig(WCartesianChart *chart, WContainerWidget *parent)
    00064   : WContainerWidget(parent),
    00065     chart_(chart),
    00066     fill_(MinimumValueFill)
    00067 {
    00068   chart_->setLegendStyle(chart_->legendFont(), WPen(black),
    00069                          WBrush(WColor(0xFF, 0xFA, 0xE5)));
    00070   chart->initLayout();
    00071
    00072   PanelList *list = new PanelList(this);
    00073
    00074   WIntValidator *sizeValidator = new WIntValidator(200, 2000, this);
    00075   sizeValidator->setMandatory(true);
    00076
    00077   WDoubleValidator *anyNumberValidator = new WDoubleValidator(this);
    00078   anyNumberValidator->setMandatory(true);
    00079
    00080   WDoubleValidator *angleValidator = new WDoubleValidator(-90, 90, this);
    00081   angleValidator->setMandatory(true);
    00082
    00083   // ---- Chart properties ----
    00084
    00085   WStandardItemModel *orientation = new WStandardItemModel(0, 1, this);
    00086   addEntry(orientation, "Vertical");
    00087   addEntry(orientation, "Horizontal");
    00088
    00089   WStandardItemModel *legendLocation = new WStandardItemModel(0, 1, this);
    00090   addEntry(legendLocation, "Outside");
    00091   addEntry(legendLocation, "Inside");
    00092
    00093   WStandardItemModel *legendSide = new WStandardItemModel(0, 1, this);
    00094   addEntry(legendSide, "Top");
    00095   addEntry(legendSide, "Right");
    00096   addEntry(legendSide, "Bottom");
    00097   addEntry(legendSide, "Left");
    00098
    00099   WStandardItemModel *legendAlignment = new WStandardItemModel(0, 1, this);
    00100   addEntry(legendAlignment, "AlignLeft");
    00101   addEntry(legendAlignment, "AlignCenter");
    00102   addEntry(legendAlignment, "AlignRight");
    00103   addEntry(legendAlignment, "AlignTop");
    00104   addEntry(legendAlignment, "AlignMiddle");
    00105   addEntry(legendAlignment, "AlignBottom");
    00106
    00107   WTable *chartConfig = new WTable();
    00108   chartConfig->setMargin(WLength::Auto, Left | Right);
    00109
    00110   int row = 0;
    00111   chartConfig->elementAt(row, 0)->addWidget(new WText("Title:"));
    00112   titleEdit_ = new WLineEdit(chartConfig->elementAt(row, 1));
    00113   connectSignals(titleEdit_);
    00114   ++row;
    00115
    00116   chartConfig->elementAt(row, 0)->addWidget(new WText("Width:"));
    00117   chartWidthEdit_ = new WLineEdit(chartConfig->elementAt(row, 1));
    00118   chartWidthEdit_
    00119     ->setText(WLocale::currentLocale().toString(chart_->width().value()));
    00120   chartWidthEdit_->setValidator(sizeValidator);
    00121   chartWidthEdit_->setMaxLength(4);
    00122   connectSignals(chartWidthEdit_);
    00123   ++row;
    00124
    00125   chartConfig->elementAt(row, 0)->addWidget(new WText("Height:"));
    00126   chartHeightEdit_ = new WLineEdit(chartConfig->elementAt(row, 1));
    00127   chartHeightEdit_
    00128     ->setText(WLocale::currentLocale().toString(chart_->height().value()));
    00129   chartHeightEdit_->setValidator(sizeValidator);
    00130   chartHeightEdit_->setMaxLength(4);
    00131   connectSignals(chartHeightEdit_);
    00132   ++row;
    00133
    00134   chartConfig->elementAt(row, 0)->addWidget(new WText("Orientation:"));
    00135   chartOrientationEdit_ = new WComboBox(chartConfig->elementAt(row, 1));
    00136   chartOrientationEdit_->setModel(orientation);
    00137   connectSignals(chartOrientationEdit_);
    00138   ++row;
    00139
    00140   chartConfig->elementAt(row, 0)->addWidget(new WText("Legend location:"));
    00141   legendLocationEdit_ = new WComboBox(chartConfig->elementAt(row, 1));
    00142   legendLocationEdit_->setModel(legendLocation);
    00143   connectSignals(legendLocationEdit_);
    00144   ++row;
    00145
    00146   chartConfig->elementAt(row, 0)->addWidget(new WText("Legend side:"));
    00147   legendSideEdit_ = new WComboBox(chartConfig->elementAt(row, 1));
    00148   legendSideEdit_->setModel(legendSide);
    00149   legendSideEdit_->setCurrentIndex(1);
    00150   connectSignals(legendSideEdit_);
    00151   ++row;
    00152
    00153   chartConfig->elementAt(row, 0)->addWidget(new WText("Legend alignment:"));
    00154   legendAlignmentEdit_ = new WComboBox(chartConfig->elementAt(row, 1));
    00155   legendAlignmentEdit_->setModel(legendAlignment);
    00156   legendAlignmentEdit_->setCurrentIndex(4);
    00157   connectSignals(legendAlignmentEdit_);
    00158   ++row;
    00159
    00160   for (int i = 0; i < chartConfig->rowCount(); ++i) {
    00161     chartConfig->elementAt(i, 0)->setStyleClass("tdhead");
    00162     chartConfig->elementAt(i, 1)->setStyleClass("tddata");
    00163   }
    00164
    00165   WPanel *p = list->addWidget("Chart properties", chartConfig);
    00166   p->setMargin(WLength::Auto, Left | Right);
    00167   p->resize(880, WLength::Auto);
    00168   p->setMargin(20, Top | Bottom);
    00169
    00170   if (chart_->isLegendEnabled())
    00171     chart_->setPlotAreaPadding(200, Right);
    00172
    00173   // ---- Series properties ----
    00174
    00175   WStandardItemModel *types = new WStandardItemModel(0, 1, this);
    00176   addEntry(types, "Points");
    00177   addEntry(types, "Line");
    00178   addEntry(types, "Curve");
    00179   addEntry(types, "Bar");
    00180   addEntry(types, "Line Area");
    00181   addEntry(types, "Curve Area");
    00182   addEntry(types, "Stacked Bar");
    00183   addEntry(types, "Stacked Line Area");
    00184   addEntry(types, "Stacked Curve Area");
    00185
    00186   WStandardItemModel *markers = new WStandardItemModel(0, 1, this);
    00187   addEntry(markers, "None");
    00188   addEntry(markers, "Square");
    00189   addEntry(markers, "Circle");
    00190   addEntry(markers, "Cross");
    00191   addEntry(markers, "X cross");
    00192   addEntry(markers, "Triangle");
    00193   addEntry(markers, "Diamond");
    00194
    00195   WStandardItemModel *axes = new WStandardItemModel(0, 1, this);
    00196   addEntry(axes, "1st Y axis");
    00197   addEntry(axes, "2nd Y axis");
    00198
    00199   WStandardItemModel *labels = new WStandardItemModel(0, 1, this);
    00200   addEntry(labels, "None");
    00201   addEntry(labels, "X");
    00202   addEntry(labels, "Y");
    00203   addEntry(labels, "X: Y");
    00204
    00205   WTable *seriesConfig = new WTable();
    00206   seriesConfig->setMargin(WLength::Auto, Left | Right);
    00207
    00208   ::addHeader(seriesConfig, "Name");
    00209   ::addHeader(seriesConfig, "Enabled");
    00210   ::addHeader(seriesConfig, "Type");
    00211   ::addHeader(seriesConfig, "Marker");
    00212   ::addHeader(seriesConfig, "Y axis");
    00213   ::addHeader(seriesConfig, "Legend");
    00214   ::addHeader(seriesConfig, "Shadow");
    00215   ::addHeader(seriesConfig, "Value labels");
    00216
    00217   seriesConfig->rowAt(0)->setStyleClass("trhead");
    00218
    00219   for (int j = 1; j < chart->model()->columnCount(); ++j) {
    00220     SeriesControl sc;
    00221
    00222     new WText(asString(chart->model()->headerData(j)),
    00223               seriesConfig->elementAt(j, 0));
    00224
    00225     sc.enabledEdit = new WCheckBox(seriesConfig->elementAt(j, 1));
    00226     connectSignals(sc.enabledEdit);
    00227
    00228     sc.typeEdit = new WComboBox(seriesConfig->elementAt(j, 2));
    00229     sc.typeEdit->setModel(types);
    00230     connectSignals(sc.typeEdit);
    00231
    00232     sc.markerEdit = new WComboBox(seriesConfig->elementAt(j, 3));
    00233     sc.markerEdit->setModel(markers);
    00234     connectSignals(sc.markerEdit);
    00235
    00236     sc.axisEdit = new WComboBox(seriesConfig->elementAt(j, 4));
    00237     sc.axisEdit->setModel(axes);
    00238     connectSignals(sc.axisEdit);
    00239
    00240     sc.legendEdit = new WCheckBox(seriesConfig->elementAt(j, 5));
    00241     connectSignals(sc.legendEdit);
    00242
    00243     sc.shadowEdit = new WCheckBox(seriesConfig->elementAt(j, 6));
    00244     connectSignals(sc.shadowEdit);
    00245
    00246     sc.labelsEdit = new WComboBox(seriesConfig->elementAt(j, 7));
    00247     sc.labelsEdit->setModel(labels);
    00248     connectSignals(sc.labelsEdit);
    00249
    00250     int si = seriesIndexOf(chart, j);
    00251
    00252     if (si != -1) {
    00253       sc.enabledEdit->setChecked();
    00254       const WDataSeries& s = chart_->series(j);
    00255       switch (s.type()) {
    00256       case PointSeries:
    00257         sc.typeEdit->setCurrentIndex(0); break;
    00258       case LineSeries:
    00259         sc.typeEdit->setCurrentIndex(s.fillRange() != NoFill ?
    00260                                      (s.isStacked() ? 7 : 4) : 1); break;
    00261       case CurveSeries:
    00262         sc.typeEdit->setCurrentIndex(s.fillRange() != NoFill ?
    00263                                      (s.isStacked() ? 8 : 5) : 2); break;
    00264       case BarSeries:
    00265         sc.typeEdit->setCurrentIndex(s.isStacked() ? 6 : 3);
    00266       }
    00267
    00268       sc.markerEdit->setCurrentIndex((int)s.marker());
    00269       sc.legendEdit->setChecked(s.isLegendEnabled());
    00270       sc.shadowEdit->setChecked(s.shadow() != WShadow());
    00271     }
    00272
    00273     seriesControls_.push_back(sc);
    00274
    00275     seriesConfig->rowAt(j)->setStyleClass("trdata");
    00276   }
    00277
    00278   p = list->addWidget("Series properties", seriesConfig);
    00279   p->expand();
    00280   p->setMargin(WLength::Auto, Left | Right);
    00281   p->resize(880, WLength::Auto);
    00282   p->setMargin(20, Top | Bottom);
    00283
    00284   // ---- Axis properties ----
    00285
    00286   WStandardItemModel *yScales = new WStandardItemModel(0, 1, this);
    00287   addEntry(yScales, "Linear scale");
    00288   addEntry(yScales, "Log scale");
    00289
    00290   WStandardItemModel *xScales = new WStandardItemModel(0, 1, this);
    00291   addEntry(xScales, "Categories");
    00292   addEntry(xScales, "Linear scale");
    00293   addEntry(xScales, "Log scale");
    00294   addEntry(xScales, "Date scale");
    00295
    00296   WTable *axisConfig = new WTable();
    00297   axisConfig->setMargin(WLength::Auto, Left | Right);
    00298
    00299   ::addHeader(axisConfig, "Axis");
    00300   ::addHeader(axisConfig, "Visible");
    00301   ::addHeader(axisConfig, "Scale");
    00302   ::addHeader(axisConfig, "Automatic");
    00303   ::addHeader(axisConfig, "Minimum");
    00304   ::addHeader(axisConfig, "Maximum");
    00305   ::addHeader(axisConfig, "Gridlines");
    00306   ::addHeader(axisConfig, "Label angle");
    00307
    00308   axisConfig->rowAt(0)->setStyleClass("trhead");
    00309
    00310   for (int i = 0; i < 3; ++i) {
    00311     const char *axisName[] = { "X axis", "1st Y axis", "2nd Y axis" };
    00312     int j = i + 1;
    00313
    00314     const WAxis& axis = chart_->axis(static_cast<Axis>(i));
    00315     AxisControl sc;
    00316
    00317     new WText(WString(axisName[i], UTF8), axisConfig->elementAt(j, 0));
    00318
    00319     sc.visibleEdit = new WCheckBox(axisConfig->elementAt(j, 1));
    00320     sc.visibleEdit->setChecked(axis.isVisible());
    00321     connectSignals(sc.visibleEdit);
    00322
    00323     sc.scaleEdit = new WComboBox(axisConfig->elementAt(j, 2));
    00324     if (axis.scale() == CategoryScale)
    00325       sc.scaleEdit->addItem("Category scale");
    00326     else {
    00327       if (axis.id() == XAxis) {
    00328         sc.scaleEdit->setModel(xScales);
    00329         sc.scaleEdit->setCurrentIndex(axis.scale());
    00330       } else {
    00331         sc.scaleEdit->setModel(yScales);
    00332         sc.scaleEdit->setCurrentIndex(axis.scale() - 1);
    00333       }
    00334     }
    00335     connectSignals(sc.scaleEdit);
    00336
    00337     bool autoValues = axis.autoLimits() == (MinimumValue | MaximumValue);
    00338
    00339     sc.minimumEdit = new WLineEdit(axisConfig->elementAt(j, 4));
    00340     sc.minimumEdit->setText(WLocale::currentLocale()
    00341                             .toString(axis.minimum()));
    00342     sc.minimumEdit->setValidator(anyNumberValidator);
    00343     sc.minimumEdit->setEnabled(!autoValues);
    00344     connectSignals(sc.minimumEdit);
    00345
    00346     sc.maximumEdit = new WLineEdit(axisConfig->elementAt(j, 5));
    00347     sc.maximumEdit->setText(WLocale::currentLocale()
    00348                             .toString(axis.maximum()));
    00349     sc.maximumEdit->setValidator(anyNumberValidator);
    00350     sc.maximumEdit->setEnabled(!autoValues);
    00351     connectSignals(sc.maximumEdit);
    00352
    00353     sc.autoEdit = new WCheckBox(axisConfig->elementAt(j, 3));
    00354     sc.autoEdit->setChecked(autoValues);
    00355     connectSignals(sc.autoEdit);
    00356     sc.autoEdit->checked().connect(sc.maximumEdit, &WLineEdit::disable);
    00357     sc.autoEdit->unChecked().connect(sc.maximumEdit, &WLineEdit::enable);
    00358     sc.autoEdit->checked().connect(sc.minimumEdit, &WLineEdit::disable);
    00359     sc.autoEdit->unChecked().connect(sc.minimumEdit, &WLineEdit::enable);
    00360
    00361     sc.gridLinesEdit = new WCheckBox(axisConfig->elementAt(j, 6));
    00362     connectSignals(sc.gridLinesEdit);
    00363
    00364     sc.labelAngleEdit = new WLineEdit(axisConfig->elementAt(j, 7));
    00365     sc.labelAngleEdit->setText("0");
    00366     sc.labelAngleEdit->setValidator(angleValidator);
    00367     connectSignals(sc.labelAngleEdit);
    00368
    00369     axisConfig->rowAt(j)->setStyleClass("trdata");
    00370
    00371     axisControls_.push_back(sc);
    00372   }
    00373
    00374   p = list->addWidget("Axis properties", axisConfig);
    00375   p->setMargin(WLength::Auto, Left | Right);
    00376   p->resize(880, WLength::Auto);
    00377   p->setMargin(20, Top | Bottom);
    00378
    00379   /*
    00380    * If we do not have JavaScript, then add a button to reflect changes to
    00381    * the chart.
    00382    */
    00383   if (!WApplication::instance()->environment().javaScript()) {
    00384     WPushButton *b = new WPushButton(this);
    00385     b->setText("Update chart");
    00386     b->setInline(false); // so we can add margin to center horizontally
    00387     b->setMargin(WLength::Auto, Left | Right);
    00388     b->clicked().connect(this, &ChartConfig::update);
    00389   }
    00390 }
    00391
    00392 void ChartConfig::setValueFill(FillRangeType fill)
    00393 {
    00394   fill_ = fill;
    00395 }
    00396
    00397 void ChartConfig::update()
    00398 {
    00399   bool haveLegend = false;
    00400   std::vector<WDataSeries> series;
    00401
    00402   for (int i = 1; i < chart_->model()->columnCount(); ++i) {
    00403     SeriesControl& sc = seriesControls_[i-1];
    00404
    00405     if (sc.enabledEdit->isChecked()) {
    00406       WDataSeries s(i);
    00407
    00408       switch (sc.typeEdit->currentIndex()) {
    00409       case 0:
    00410         s.setType(PointSeries);
    00411         if (sc.markerEdit->currentIndex() == 0)
    00412           sc.markerEdit->setCurrentIndex(1);
    00413         break;
    00414       case 1:
    00415         s.setType(LineSeries);
    00416         sc.markerEdit->setCurrentIndex(0);
    00417         break;
    00418       case 2:
    00419         s.setType(CurveSeries);
    00420         sc.markerEdit->setCurrentIndex(0);
    00421         break;
    00422       case 3:
    00423         s.setType(BarSeries);
    00424         sc.markerEdit->setCurrentIndex(0);
    00425         break;
    00426       case 4:
    00427         s.setType(LineSeries);
    00428         s.setFillRange(fill_);
    00429         sc.markerEdit->setCurrentIndex(0);
    00430         break;
    00431       case 5:
    00432         s.setType(CurveSeries);
    00433         s.setFillRange(fill_);
    00434         sc.markerEdit->setCurrentIndex(0);
    00435         break;
    00436       case 6:
    00437         s.setType(BarSeries);
    00438         s.setStacked(true);
    00439         sc.markerEdit->setCurrentIndex(0);
    00440         break;
    00441       case 7:
    00442         s.setType(LineSeries);
    00443         s.setFillRange(fill_);
    00444         s.setStacked(true);
    00445         sc.markerEdit->setCurrentIndex(0);
    00446         break;
    00447       case 8:
    00448         s.setType(CurveSeries);
    00449         s.setFillRange(fill_);
    00450         s.setStacked(true);
    00451         sc.markerEdit->setCurrentIndex(0);
    00452       }
    00453
    00454       //set WPainterPath to draw a diamond
    00455       if(sc.markerEdit->currentIndex() == CustomMarker){
    00456         WPainterPath pp = WPainterPath();
    00457         pp.moveTo(-6, 0);
    00458         pp.lineTo(0, 6);
    00459         pp.lineTo(6, 0);
    00460         pp.lineTo(0, -6);
    00461         pp.lineTo(-6, 0);
    00462         s.setCustomMarker(pp);
    00463       }
    00464
    00465       s.setMarker(static_cast<MarkerType>(sc.markerEdit->currentIndex()));
    00466
    00467       if (sc.axisEdit->currentIndex() == 1) {
    00468         s.bindToAxis(Y2Axis);
    00469       }
    00470
    00471       if (sc.legendEdit->isChecked()) {
    00472         s.setLegendEnabled(true);
    00473         haveLegend = true;
    00474       } else
    00475         s.setLegendEnabled(false);
    00476
    00477       if (sc.shadowEdit->isChecked()) {
    00478         s.setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3));
    00479       } else
    00480         s.setShadow(WShadow());
    00481
    00482       switch (sc.labelsEdit->currentIndex()) {
    00483       case 1:
    00484         s.setLabelsEnabled(XAxis);
    00485         break;
    00486       case 2:
    00487         s.setLabelsEnabled(YAxis);
    00488         break;
    00489       case 3:
    00490         s.setLabelsEnabled(XAxis);
    00491         s.setLabelsEnabled(YAxis);
    00492         break;
    00493       }
    00494
    00495       series.push_back(s);
    00496     }
    00497   }
    00498
    00499   chart_->setSeries(series);
    00500
    00501   for (int i = 0; i < 3; ++i) {
    00502     AxisControl& sc = axisControls_[i];
    00503     WAxis& axis = chart_->axis(static_cast<Axis>(i));
    00504
    00505     axis.setVisible(sc.visibleEdit->isChecked());
    00506
    00507     if (sc.scaleEdit->count() != 1) {
    00508       int k = sc.scaleEdit->currentIndex();
    00509       if (axis.id() != XAxis)
    00510         k += 1;
    00511       else {
    00512         if (k == 0)
    00513           chart_->setType(CategoryChart);
    00514         else
    00515           chart_->setType(ScatterPlot);
    00516       }
    00517
    00518       switch (k) {
    00519       case 1:
    00520         axis.setScale(LinearScale); break;
    00521       case 2:
    00522         axis.setScale(LogScale); break;
    00523       case 3:
    00524         axis.setScale(DateScale); break;
    00525       }
    00526     }
    00527
    00528     if (sc.autoEdit->isChecked())
    00529       axis.setAutoLimits(MinimumValue | MaximumValue);
    00530     else {
    00531       if (validate(sc.minimumEdit) && validate(sc.maximumEdit)) {
    00532           double min, max;
    00533           getDouble(sc.minimumEdit, min);
    00534           getDouble(sc.maximumEdit, max);
    00535
    00536           if (axis.scale() == LogScale)
    00537               if (min <= 0)
    00538                   min = 0.0001;
    00539
    00540           if (axis.scale() == DateScale){
    00541               //the number of julian days until year 1986
    00542               WDate dMin = WDate(1900,1,1);
    00543               double gregDaysMin = (double)dMin.toJulianDay();
    00544               //the number of julian days until year 1988
    00545               WDate dMax = WDate(3000,1,1);
    00546               double gregDaysMax = (double)dMax.toJulianDay();
    00547
    00548               bool greg_year_validation =
    00549                       (min > gregDaysMin &&
    00550                        min < gregDaysMax &&
    00551                        max > gregDaysMin &&
    00552                        max < gregDaysMax);
    00553
    00554               if(!greg_year_validation){
    00555                   min = gregDaysMin;
    00556                   max = gregDaysMax;
    00557               }
    00558           }
    00559
    00560           axis.setRange(min, max);
    00561       }
    00562
    00563     }
    00564
    00565     if (validate(sc.labelAngleEdit)) {
    00566       double angle;
    00567       getDouble(sc.labelAngleEdit, angle);
    00568       axis.setLabelAngle(angle);
    00569     }
    00570
    00571     axis.setGridLinesEnabled(sc.gridLinesEdit->isChecked());
    00572   }
    00573
    00574   chart_->setTitle(titleEdit_->text());
    00575
    00576   if (validate(chartWidthEdit_) && validate(chartHeightEdit_)) {
    00577     double width, height;
    00578     getDouble(chartWidthEdit_, width);
    00579     getDouble(chartHeightEdit_, height);
    00580     chart_->resize(width, height);
    00581   }
    00582
    00583   switch (chartOrientationEdit_->currentIndex()) {
    00584   case 0:
    00585     chart_->setOrientation(Vertical); break;
    00586   case 1:
    00587     chart_->setOrientation(Horizontal); break;
    00588   }
    00589
    00590   chart_->setLegendEnabled(haveLegend);
    00591
    00592   if (haveLegend) {
    00593     LegendLocation location = LegendOutside;
    00594     Side side = Right;
    00595     AlignmentFlag alignment = AlignMiddle;
    00596
    00597     switch (legendLocationEdit_->currentIndex()) {
    00598     case 0: location = LegendOutside; break;
    00599     case 1: location = LegendInside; break;
    00600     }
    00601
    00602     switch (legendSideEdit_->currentIndex()) {
    00603     case 0: side = Top; break;
    00604     case 1: side = Right; break;
    00605     case 2: side = Bottom; break;
    00606     case 3: side = Left; break;
    00607     }
    00608
    00609     if (side == Left || side == Right) {
    00610       if (legendAlignmentEdit_->currentIndex() < 3)
    00611         legendAlignmentEdit_->setCurrentIndex(4);
    00612     } else {
    00613       if (legendAlignmentEdit_->currentIndex() >= 3)
    00614         legendAlignmentEdit_->setCurrentIndex(2);
    00615     }
    00616
    00617     switch (legendAlignmentEdit_->currentIndex()) {
    00618     case 0: alignment = AlignLeft; break;
    00619     case 1: alignment = AlignCenter; break;
    00620     case 2: alignment = AlignRight; break;
    00621     case 3: alignment = AlignTop; break;
    00622     case 4: alignment = AlignMiddle; break;
    00623     case 5: alignment = AlignBottom; break;
    00624     }
    00625
    00626     chart_->setLegendLocation(location, side, alignment);
    00627
    00628     chart_->setLegendColumns((side == Top || side == Bottom ) ? 2 : 1,
    00629                              WLength(100));
    00630   }
    00631
    00632   for (unsigned i = 0; i < 4; ++i) {
    00633     Side sides[] = { Top, Right, Bottom, Left };
    00634
    00635     bool legendRoom =
    00636       haveLegend
    00637       && chart_->legendLocation() == LegendOutside
    00638       && chart_->legendSide() == sides[i];
    00639
    00640     int padding;
    00641
    00642     if (i % 2 == 0)
    00643       padding = legendRoom ? 80 : 40;
    00644     else
    00645       padding = legendRoom ? 200 : 80;
    00646
    00647     chart_->setPlotAreaPadding(padding, sides[i]);
    00648   }
    00649 }
    00650
    00651 bool ChartConfig::validate(WFormWidget *w)
    00652 {
    00653   bool valid = w->validate() == WValidator::Valid;
    00654
    00655   if (!WApplication::instance()->environment().javaScript()) {
    00656     w->setStyleClass(valid ? "" : "Wt-invalid");
    00657     w->setToolTip(valid ? "" : "Invalid value");
    00658   }
    00659
    00660   return valid;
    00661 }
    00662
    00663 void ChartConfig::connectSignals(WFormWidget *w)
    00664 {
    00665   w->changed().connect(this, &ChartConfig::update);
    00666   if (dynamic_cast<WLineEdit *>(w))
    00667     w->enterPressed().connect(this, &ChartConfig::update);
    00668 }