ChartConfig.C 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715
  1. /*
  2. * Copyright (C) 2008 Emweb bvba, Kessel-Lo, Belgium.
  3. *
  4. * See the LICENSE file for terms of use.
  5. */
  6. #include "ChartConfig.h"
  7. #include "PanelList.h"
  8. #include <iostream>
  9. #include <boost/date_time/gregorian/greg_year.hpp>
  10. #include <Wt/WAbstractItemModel>
  11. #include <Wt/WApplication>
  12. #include <Wt/WCheckBox>
  13. #include <Wt/WComboBox>
  14. #include <Wt/WDoubleValidator>
  15. #include <Wt/WDate>
  16. #include <Wt/WEnvironment>
  17. #include <Wt/WIntValidator>
  18. #include <Wt/WLineEdit>
  19. #include <Wt/WLocale>
  20. #include <Wt/WPanel>
  21. #include <Wt/WPushButton>
  22. #include <Wt/WStandardItemModel>
  23. #include <Wt/WTable>
  24. #include <Wt/WText>
  25. #include <Wt/WPainterPath>
  26. #include <Wt/Chart/WCartesianChart>
  27. using namespace Wt;
  28. using namespace Wt::Chart;
  29. namespace {
  30. void addHeader(WTable *t, const char *value) {
  31. t->elementAt(0, t->columnCount())->addWidget(new WText(value));
  32. }
  33. void addEntry(WAbstractItemModel *model, const char *value) {
  34. model->insertRows(model->rowCount(), 1);
  35. model->setData(model->rowCount()-1, 0, boost::any(std::string(value)));
  36. }
  37. bool getDouble(WLineEdit *edit, double& value) {
  38. try {
  39. value = WLocale::currentLocale().toDouble(edit->text());
  40. return true;
  41. } catch (...) {
  42. return false;
  43. }
  44. }
  45. int seriesIndexOf(WCartesianChart* chart, int modelColumn) {
  46. for (unsigned i = 0; i < chart->series().size(); ++i)
  47. if (chart->series()[i]->modelColumn() == modelColumn)
  48. return i;
  49. return -1;
  50. }
  51. }
  52. ChartConfig::ChartConfig(WCartesianChart *chart, WContainerWidget *parent)
  53. : WContainerWidget(parent),
  54. chart_(chart),
  55. fill_(MinimumValueFill)
  56. {
  57. chart_->setLegendStyle(chart_->legendFont(), WPen(black),
  58. WBrush(WColor(0xFF, 0xFA, 0xE5)));
  59. PanelList *list = new PanelList(this);
  60. WIntValidator *sizeValidator = new WIntValidator(200, 2000, this);
  61. sizeValidator->setMandatory(true);
  62. WDoubleValidator *anyNumberValidator = new WDoubleValidator(this);
  63. anyNumberValidator->setMandatory(true);
  64. WDoubleValidator *angleValidator = new WDoubleValidator(-90, 90, this);
  65. angleValidator->setMandatory(true);
  66. // ---- Chart properties ----
  67. WStandardItemModel *orientation = new WStandardItemModel(0, 1, this);
  68. addEntry(orientation, "Vertical");
  69. addEntry(orientation, "Horizontal");
  70. WStandardItemModel *legendLocation = new WStandardItemModel(0, 1, this);
  71. addEntry(legendLocation, "Outside");
  72. addEntry(legendLocation, "Inside");
  73. WStandardItemModel *legendSide = new WStandardItemModel(0, 1, this);
  74. addEntry(legendSide, "Top");
  75. addEntry(legendSide, "Right");
  76. addEntry(legendSide, "Bottom");
  77. addEntry(legendSide, "Left");
  78. WStandardItemModel *legendAlignment = new WStandardItemModel(0, 1, this);
  79. addEntry(legendAlignment, "AlignLeft");
  80. addEntry(legendAlignment, "AlignCenter");
  81. addEntry(legendAlignment, "AlignRight");
  82. addEntry(legendAlignment, "AlignTop");
  83. addEntry(legendAlignment, "AlignMiddle");
  84. addEntry(legendAlignment, "AlignBottom");
  85. WTable *chartConfig = new WTable();
  86. chartConfig->setMargin(WLength::Auto, Left | Right);
  87. int row = 0;
  88. chartConfig->elementAt(row, 0)->addWidget(new WText("Title:"));
  89. titleEdit_ = new WLineEdit(chartConfig->elementAt(row, 1));
  90. connectSignals(titleEdit_);
  91. ++row;
  92. chartConfig->elementAt(row, 0)->addWidget(new WText("Width:"));
  93. chartWidthEdit_ = new WLineEdit(chartConfig->elementAt(row, 1));
  94. chartWidthEdit_
  95. ->setText(WLocale::currentLocale().toString(chart_->width().value()));
  96. chartWidthEdit_->setValidator(sizeValidator);
  97. chartWidthEdit_->setMaxLength(4);
  98. connectSignals(chartWidthEdit_);
  99. ++row;
  100. chartConfig->elementAt(row, 0)->addWidget(new WText("Height:"));
  101. chartHeightEdit_ = new WLineEdit(chartConfig->elementAt(row, 1));
  102. chartHeightEdit_
  103. ->setText(WLocale::currentLocale().toString(chart_->height().value()));
  104. chartHeightEdit_->setValidator(sizeValidator);
  105. chartHeightEdit_->setMaxLength(4);
  106. connectSignals(chartHeightEdit_);
  107. ++row;
  108. chartConfig->elementAt(row, 0)->addWidget(new WText("Orientation:"));
  109. chartOrientationEdit_ = new WComboBox(chartConfig->elementAt(row, 1));
  110. chartOrientationEdit_->setModel(orientation);
  111. chartOrientationEdit_->setCurrentIndex(0);
  112. connectSignals(chartOrientationEdit_);
  113. ++row;
  114. chartConfig->elementAt(row, 0)->addWidget(new WText("Legend location:"));
  115. legendLocationEdit_ = new WComboBox(chartConfig->elementAt(row, 1));
  116. legendLocationEdit_->setModel(legendLocation);
  117. legendLocationEdit_->setCurrentIndex(0);
  118. connectSignals(legendLocationEdit_);
  119. ++row;
  120. chartConfig->elementAt(row, 0)->addWidget(new WText("Legend side:"));
  121. legendSideEdit_ = new WComboBox(chartConfig->elementAt(row, 1));
  122. legendSideEdit_->setModel(legendSide);
  123. legendSideEdit_->setCurrentIndex(1);
  124. connectSignals(legendSideEdit_);
  125. ++row;
  126. chartConfig->elementAt(row, 0)->addWidget(new WText("Legend alignment:"));
  127. legendAlignmentEdit_ = new WComboBox(chartConfig->elementAt(row, 1));
  128. legendAlignmentEdit_->setModel(legendAlignment);
  129. legendAlignmentEdit_->setCurrentIndex(4);
  130. connectSignals(legendAlignmentEdit_);
  131. ++row;
  132. chartConfig->elementAt(row, 0)->addWidget(new WText("Border:"));
  133. borderEdit_ = new WCheckBox(chartConfig->elementAt(row, 1));
  134. borderEdit_->setChecked(false);
  135. connectSignals(borderEdit_);
  136. ++row;
  137. for (int i = 0; i < chartConfig->rowCount(); ++i) {
  138. chartConfig->elementAt(i, 0)->setStyleClass("tdhead");
  139. chartConfig->elementAt(i, 1)->setStyleClass("tddata");
  140. }
  141. WPanel *p = list->addWidget("Chart properties", chartConfig);
  142. p->setMargin(WLength::Auto, Left | Right);
  143. p->resize(1080, WLength::Auto);
  144. p->setMargin(20, Top | Bottom);
  145. // ---- Series properties ----
  146. WStandardItemModel *types = new WStandardItemModel(0, 1, this);
  147. addEntry(types, "Points");
  148. addEntry(types, "Line");
  149. addEntry(types, "Curve");
  150. addEntry(types, "Bar");
  151. addEntry(types, "Line Area");
  152. addEntry(types, "Curve Area");
  153. addEntry(types, "Stacked Bar");
  154. addEntry(types, "Stacked Line Area");
  155. addEntry(types, "Stacked Curve Area");
  156. WStandardItemModel *markers = new WStandardItemModel(0, 1, this);
  157. addEntry(markers, "None");
  158. addEntry(markers, "Square");
  159. addEntry(markers, "Circle");
  160. addEntry(markers, "Cross");
  161. addEntry(markers, "X cross");
  162. addEntry(markers, "Triangle");
  163. addEntry(markers, "Pipe");
  164. addEntry(markers, "Star");
  165. addEntry(markers, "Inverted triangle");
  166. addEntry(markers, "Asterisk");
  167. addEntry(markers, "Diamond");
  168. WStandardItemModel *axes = new WStandardItemModel(0, 1, this);
  169. addEntry(axes, "1st Y axis");
  170. addEntry(axes, "2nd Y axis");
  171. WStandardItemModel *labels = new WStandardItemModel(0, 1, this);
  172. addEntry(labels, "None");
  173. addEntry(labels, "X");
  174. addEntry(labels, "Y");
  175. addEntry(labels, "X: Y");
  176. WTable *seriesConfig = new WTable();
  177. seriesConfig->setMargin(WLength::Auto, Left | Right);
  178. ::addHeader(seriesConfig, "Name");
  179. ::addHeader(seriesConfig, "Enabled");
  180. ::addHeader(seriesConfig, "Type");
  181. ::addHeader(seriesConfig, "Marker");
  182. ::addHeader(seriesConfig, "Y axis");
  183. ::addHeader(seriesConfig, "Legend");
  184. ::addHeader(seriesConfig, "Shadow");
  185. ::addHeader(seriesConfig, "Value labels");
  186. seriesConfig->rowAt(0)->setStyleClass("trhead");
  187. for (int j = 1; j < chart->model()->columnCount(); ++j) {
  188. SeriesControl sc;
  189. new WText(chart->model()->headerData(j),
  190. seriesConfig->elementAt(j, 0));
  191. sc.enabledEdit = new WCheckBox(seriesConfig->elementAt(j, 1));
  192. connectSignals(sc.enabledEdit);
  193. sc.typeEdit = new WComboBox(seriesConfig->elementAt(j, 2));
  194. sc.typeEdit->setModel(types);
  195. sc.typeEdit->setCurrentIndex(0);
  196. connectSignals(sc.typeEdit);
  197. sc.markerEdit = new WComboBox(seriesConfig->elementAt(j, 3));
  198. sc.markerEdit->setModel(markers);
  199. sc.markerEdit->setCurrentIndex(0);
  200. connectSignals(sc.markerEdit);
  201. sc.axisEdit = new WComboBox(seriesConfig->elementAt(j, 4));
  202. sc.axisEdit->setModel(axes);
  203. sc.axisEdit->setCurrentIndex(0);
  204. connectSignals(sc.axisEdit);
  205. sc.legendEdit = new WCheckBox(seriesConfig->elementAt(j, 5));
  206. connectSignals(sc.legendEdit);
  207. sc.shadowEdit = new WCheckBox(seriesConfig->elementAt(j, 6));
  208. connectSignals(sc.shadowEdit);
  209. sc.labelsEdit = new WComboBox(seriesConfig->elementAt(j, 7));
  210. sc.labelsEdit->setModel(labels);
  211. sc.labelsEdit->setCurrentIndex(0);
  212. connectSignals(sc.labelsEdit);
  213. int si = seriesIndexOf(chart, j);
  214. if (si != -1) {
  215. sc.enabledEdit->setChecked();
  216. const WDataSeries& s = chart_->series(j);
  217. switch (s.type()) {
  218. case PointSeries:
  219. sc.typeEdit->setCurrentIndex(0); break;
  220. case LineSeries:
  221. sc.typeEdit->setCurrentIndex(s.fillRange() != NoFill ?
  222. (s.isStacked() ? 7 : 4) : 1); break;
  223. case CurveSeries:
  224. sc.typeEdit->setCurrentIndex(s.fillRange() != NoFill ?
  225. (s.isStacked() ? 8 : 5) : 2); break;
  226. case BarSeries:
  227. sc.typeEdit->setCurrentIndex(s.isStacked() ? 6 : 3);
  228. }
  229. sc.markerEdit->setCurrentIndex((int)s.marker());
  230. sc.legendEdit->setChecked(s.isLegendEnabled());
  231. sc.shadowEdit->setChecked(s.shadow() != WShadow());
  232. }
  233. seriesControls_.push_back(sc);
  234. seriesConfig->rowAt(j)->setStyleClass("trdata");
  235. }
  236. p = list->addWidget("Series properties", seriesConfig);
  237. p->expand();
  238. p->setMargin(WLength::Auto, Left | Right);
  239. p->resize(1080, WLength::Auto);
  240. p->setMargin(20, Top | Bottom);
  241. // ---- Axis properties ----
  242. WStandardItemModel *yScales = new WStandardItemModel(0, 1, this);
  243. addEntry(yScales, "Linear scale");
  244. addEntry(yScales, "Log scale");
  245. WStandardItemModel *xScales = new WStandardItemModel(0, 1, this);
  246. addEntry(xScales, "Categories");
  247. addEntry(xScales, "Linear scale");
  248. addEntry(xScales, "Log scale");
  249. addEntry(xScales, "Date scale");
  250. WTable *axisConfig = new WTable();
  251. axisConfig->setMargin(WLength::Auto, Left | Right);
  252. ::addHeader(axisConfig, "Axis");
  253. ::addHeader(axisConfig, "Visible");
  254. ::addHeader(axisConfig, "Scale");
  255. ::addHeader(axisConfig, "Automatic");
  256. ::addHeader(axisConfig, "Minimum");
  257. ::addHeader(axisConfig, "Maximum");
  258. ::addHeader(axisConfig, "Gridlines");
  259. ::addHeader(axisConfig, "Label angle");
  260. ::addHeader(axisConfig, "Title");
  261. ::addHeader(axisConfig, "Title orientation");
  262. ::addHeader(axisConfig, "Tick direction");
  263. ::addHeader(axisConfig, "Location");
  264. axisConfig->rowAt(0)->setStyleClass("trhead");
  265. for (int i = 0; i < 3; ++i) {
  266. const char *axisName[] = { "X axis", "1st Y axis", "2nd Y axis" };
  267. int j = i + 1;
  268. const WAxis& axis = chart_->axis(static_cast<Axis>(i));
  269. AxisControl sc;
  270. new WText(WString(axisName[i], UTF8), axisConfig->elementAt(j, 0));
  271. sc.visibleEdit = new WCheckBox(axisConfig->elementAt(j, 1));
  272. sc.visibleEdit->setChecked(axis.isVisible());
  273. connectSignals(sc.visibleEdit);
  274. sc.scaleEdit = new WComboBox(axisConfig->elementAt(j, 2));
  275. if (axis.scale() == CategoryScale)
  276. sc.scaleEdit->addItem("Category scale");
  277. else {
  278. if (axis.id() == XAxis) {
  279. sc.scaleEdit->setModel(xScales);
  280. sc.scaleEdit->setCurrentIndex(axis.scale());
  281. } else {
  282. sc.scaleEdit->setModel(yScales);
  283. sc.scaleEdit->setCurrentIndex(axis.scale() - 1);
  284. }
  285. }
  286. connectSignals(sc.scaleEdit);
  287. bool autoValues = axis.autoLimits() == (MinimumValue | MaximumValue);
  288. sc.minimumEdit = new WLineEdit(axisConfig->elementAt(j, 4));
  289. sc.minimumEdit->setText(WLocale::currentLocale()
  290. .toString(axis.minimum()));
  291. sc.minimumEdit->setValidator(anyNumberValidator);
  292. sc.minimumEdit->setEnabled(!autoValues);
  293. connectSignals(sc.minimumEdit);
  294. sc.maximumEdit = new WLineEdit(axisConfig->elementAt(j, 5));
  295. sc.maximumEdit->setText(WLocale::currentLocale()
  296. .toString(axis.maximum()));
  297. sc.maximumEdit->setValidator(anyNumberValidator);
  298. sc.maximumEdit->setEnabled(!autoValues);
  299. connectSignals(sc.maximumEdit);
  300. sc.autoEdit = new WCheckBox(axisConfig->elementAt(j, 3));
  301. sc.autoEdit->setChecked(autoValues);
  302. connectSignals(sc.autoEdit);
  303. sc.autoEdit->checked().connect(sc.maximumEdit, &WLineEdit::disable);
  304. sc.autoEdit->unChecked().connect(sc.maximumEdit, &WLineEdit::enable);
  305. sc.autoEdit->checked().connect(sc.minimumEdit, &WLineEdit::disable);
  306. sc.autoEdit->unChecked().connect(sc.minimumEdit, &WLineEdit::enable);
  307. sc.gridLinesEdit = new WCheckBox(axisConfig->elementAt(j, 6));
  308. connectSignals(sc.gridLinesEdit);
  309. sc.labelAngleEdit = new WLineEdit(axisConfig->elementAt(j, 7));
  310. sc.labelAngleEdit->setText("0");
  311. sc.labelAngleEdit->setValidator(angleValidator);
  312. connectSignals(sc.labelAngleEdit);
  313. sc.titleEdit = new WLineEdit(axisConfig->elementAt(j, 8));
  314. sc.titleEdit->setText("");
  315. connectSignals(sc.titleEdit);
  316. sc.titleOrientationEdit = new WComboBox(axisConfig->elementAt(j, 9));
  317. sc.titleOrientationEdit->addItem("Horizontal");
  318. sc.titleOrientationEdit->addItem("Vertical");
  319. sc.titleOrientationEdit->setCurrentIndex(0);
  320. connectSignals(sc.titleOrientationEdit);
  321. sc.tickDirectionEdit = new WComboBox(axisConfig->elementAt(j, 10));
  322. sc.tickDirectionEdit->addItem("Outwards");
  323. sc.tickDirectionEdit->addItem("Inwards");
  324. sc.tickDirectionEdit->setCurrentIndex(0);
  325. connectSignals(sc.tickDirectionEdit);
  326. sc.locationEdit = new WComboBox(axisConfig->elementAt(j, 11));
  327. sc.locationEdit->addItem("Minimum value");
  328. sc.locationEdit->addItem("Maximum value");
  329. sc.locationEdit->addItem("Zero value");
  330. sc.locationEdit->addItem("Both sides");
  331. sc.locationEdit->setCurrentIndex(0);
  332. if (axis.location() == ZeroValue) {
  333. sc.locationEdit->setCurrentIndex(2);
  334. }
  335. connectSignals(sc.locationEdit);
  336. axisConfig->rowAt(j)->setStyleClass("trdata");
  337. axisControls_.push_back(sc);
  338. }
  339. p = list->addWidget("Axis properties", axisConfig);
  340. p->setMargin(WLength::Auto, Left | Right);
  341. p->resize(1080, WLength::Auto);
  342. p->setMargin(20, Top | Bottom);
  343. /*
  344. * If we do not have JavaScript, then add a button to reflect changes to
  345. * the chart.
  346. */
  347. if (!WApplication::instance()->environment().javaScript()) {
  348. WPushButton *b = new WPushButton(this);
  349. b->setText("Update chart");
  350. b->setInline(false); // so we can add margin to center horizontally
  351. b->setMargin(WLength::Auto, Left | Right);
  352. b->clicked().connect(this, &ChartConfig::update);
  353. }
  354. }
  355. void ChartConfig::setValueFill(FillRangeType fill)
  356. {
  357. fill_ = fill;
  358. }
  359. void ChartConfig::update()
  360. {
  361. bool haveLegend = false;
  362. std::vector<WDataSeries *> series;
  363. for (int i = 1; i < chart_->model()->columnCount(); ++i) {
  364. SeriesControl& sc = seriesControls_[i-1];
  365. if (sc.enabledEdit->isChecked()) {
  366. WDataSeries *s = new WDataSeries(i);
  367. switch (sc.typeEdit->currentIndex()) {
  368. case 0:
  369. s->setType(PointSeries);
  370. if (sc.markerEdit->currentIndex() == 0)
  371. sc.markerEdit->setCurrentIndex(1);
  372. break;
  373. case 1:
  374. s->setType(LineSeries);
  375. break;
  376. case 2:
  377. s->setType(CurveSeries);
  378. break;
  379. case 3:
  380. s->setType(BarSeries);
  381. break;
  382. case 4:
  383. s->setType(LineSeries);
  384. s->setFillRange(fill_);
  385. break;
  386. case 5:
  387. s->setType(CurveSeries);
  388. s->setFillRange(fill_);
  389. break;
  390. case 6:
  391. s->setType(BarSeries);
  392. s->setStacked(true);
  393. break;
  394. case 7:
  395. s->setType(LineSeries);
  396. s->setFillRange(fill_);
  397. s->setStacked(true);
  398. break;
  399. case 8:
  400. s->setType(CurveSeries);
  401. s->setFillRange(fill_);
  402. s->setStacked(true);
  403. }
  404. //set WPainterPath to draw a pipe
  405. if(sc.markerEdit->currentIndex() == CustomMarker){
  406. WPainterPath pp = WPainterPath();
  407. pp.moveTo(0, -6);
  408. pp.lineTo(0, 6);
  409. s->setCustomMarker(pp);
  410. }
  411. s->setMarker(static_cast<MarkerType>(sc.markerEdit->currentIndex()));
  412. if (sc.axisEdit->currentIndex() == 1) {
  413. s->bindToAxis(Y2Axis);
  414. }
  415. if (sc.legendEdit->isChecked()) {
  416. s->setLegendEnabled(true);
  417. haveLegend = true;
  418. } else
  419. s->setLegendEnabled(false);
  420. if (sc.shadowEdit->isChecked()) {
  421. s->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3));
  422. } else
  423. s->setShadow(WShadow());
  424. switch (sc.labelsEdit->currentIndex()) {
  425. case 1:
  426. s->setLabelsEnabled(XAxis);
  427. break;
  428. case 2:
  429. s->setLabelsEnabled(YAxis);
  430. break;
  431. case 3:
  432. s->setLabelsEnabled(XAxis);
  433. s->setLabelsEnabled(YAxis);
  434. break;
  435. }
  436. series.push_back(s);
  437. }
  438. }
  439. chart_->setSeries(series);
  440. for (int i = 0; i < 3; ++i) {
  441. AxisControl& sc = axisControls_[i];
  442. WAxis& axis = chart_->axis(static_cast<Axis>(i));
  443. axis.setVisible(sc.visibleEdit->isChecked());
  444. if (sc.scaleEdit->count() != 1) {
  445. int k = sc.scaleEdit->currentIndex();
  446. if (axis.id() != XAxis)
  447. k += 1;
  448. else {
  449. if (k == 0)
  450. chart_->setType(CategoryChart);
  451. else
  452. chart_->setType(ScatterPlot);
  453. }
  454. switch (k) {
  455. case 1:
  456. axis.setScale(LinearScale); break;
  457. case 2:
  458. axis.setScale(LogScale); break;
  459. case 3:
  460. axis.setScale(DateScale); break;
  461. }
  462. }
  463. if (sc.autoEdit->isChecked())
  464. axis.setAutoLimits(MinimumValue | MaximumValue);
  465. else {
  466. if (axis.autoLimits() & (MinimumValue | MaximumValue)) {
  467. sc.minimumEdit->setText(WLocale::currentLocale()
  468. .toString(axis.minimum()));
  469. sc.maximumEdit->setText(WLocale::currentLocale()
  470. .toString(axis.maximum()));
  471. }
  472. if (validate(sc.minimumEdit) && validate(sc.maximumEdit)) {
  473. double min, max;
  474. getDouble(sc.minimumEdit, min);
  475. getDouble(sc.maximumEdit, max);
  476. if (axis.scale() == LogScale)
  477. if (min <= 0)
  478. min = 0.0001;
  479. if (axis.scale() == DateScale){
  480. //the number of julian days until year 1986
  481. WDate dMin = WDate(1900,1,1);
  482. double gregDaysMin = (double)dMin.toJulianDay();
  483. //the number of julian days until year 1988
  484. WDate dMax = WDate(3000,1,1);
  485. double gregDaysMax = (double)dMax.toJulianDay();
  486. bool greg_year_validation =
  487. (min > gregDaysMin &&
  488. min < gregDaysMax &&
  489. max > gregDaysMin &&
  490. max < gregDaysMax);
  491. if(!greg_year_validation){
  492. min = gregDaysMin;
  493. max = gregDaysMax;
  494. }
  495. }
  496. axis.setRange(min, max);
  497. }
  498. }
  499. if (validate(sc.labelAngleEdit)) {
  500. double angle;
  501. getDouble(sc.labelAngleEdit, angle);
  502. axis.setLabelAngle(angle);
  503. }
  504. axis.setGridLinesEnabled(sc.gridLinesEdit->isChecked());
  505. axis.setTitle(sc.titleEdit->text());
  506. axis.setTitleOrientation(sc.titleOrientationEdit->currentIndex() == 0 ? Horizontal : Vertical);
  507. axis.setTickDirection(sc.tickDirectionEdit->currentIndex() == 0 ? Outwards : Inwards);
  508. switch (sc.locationEdit->currentIndex()) {
  509. case 0:
  510. axis.setLocation(MinimumValue);
  511. break;
  512. case 1:
  513. axis.setLocation(MaximumValue);
  514. break;
  515. case 2:
  516. axis.setLocation(ZeroValue);
  517. break;
  518. case 3:
  519. axis.setLocation(BothSides);
  520. break;
  521. }
  522. }
  523. chart_->setTitle(titleEdit_->text());
  524. if (validate(chartWidthEdit_) && validate(chartHeightEdit_)) {
  525. double width, height;
  526. getDouble(chartWidthEdit_, width);
  527. getDouble(chartHeightEdit_, height);
  528. chart_->resize(width, height);
  529. }
  530. switch (chartOrientationEdit_->currentIndex()) {
  531. case 0:
  532. chart_->setOrientation(Vertical); break;
  533. case 1:
  534. chart_->setOrientation(Horizontal); break;
  535. }
  536. chart_->setLegendEnabled(haveLegend);
  537. if (haveLegend) {
  538. LegendLocation location = LegendOutside;
  539. Side side = Right;
  540. AlignmentFlag alignment = AlignMiddle;
  541. switch (legendLocationEdit_->currentIndex()) {
  542. case 0: location = LegendOutside; break;
  543. case 1: location = LegendInside; break;
  544. }
  545. switch (legendSideEdit_->currentIndex()) {
  546. case 0: side = Top; break;
  547. case 1: side = Right; break;
  548. case 2: side = Bottom; break;
  549. case 3: side = Left; break;
  550. }
  551. if (side == Left || side == Right) {
  552. if (legendAlignmentEdit_->currentIndex() < 3)
  553. legendAlignmentEdit_->setCurrentIndex(4);
  554. } else {
  555. if (legendAlignmentEdit_->currentIndex() >= 3)
  556. legendAlignmentEdit_->setCurrentIndex(2);
  557. }
  558. switch (legendAlignmentEdit_->currentIndex()) {
  559. case 0: alignment = AlignLeft; break;
  560. case 1: alignment = AlignCenter; break;
  561. case 2: alignment = AlignRight; break;
  562. case 3: alignment = AlignTop; break;
  563. case 4: alignment = AlignMiddle; break;
  564. case 5: alignment = AlignBottom; break;
  565. }
  566. chart_->setLegendLocation(location, side, alignment);
  567. chart_->setLegendColumns((side == Top || side == Bottom ) ? 2 : 1,
  568. WLength(100));
  569. }
  570. if (borderEdit_->isChecked()) {
  571. chart_->setBorderPen(WPen());
  572. } else {
  573. chart_->setBorderPen(NoPen);
  574. }
  575. }
  576. bool ChartConfig::validate(WFormWidget *w)
  577. {
  578. bool valid = w->validate() == WValidator::Valid;
  579. if (!WApplication::instance()->environment().javaScript()) {
  580. w->setStyleClass(valid ? "" : "Wt-invalid");
  581. w->setToolTip(valid ? "" : "Invalid value");
  582. }
  583. return valid;
  584. }
  585. void ChartConfig::connectSignals(WFormWidget *w)
  586. {
  587. w->changed().connect(this, &ChartConfig::update);
  588. if (dynamic_cast<WLineEdit *>(w))
  589. w->enterPressed().connect(this, &ChartConfig::update);
  590. }