/*
* Copyright (C) 2008 Emweb bv, Herent, Belgium.
*
* See the LICENSE file for terms of use.
*/
#include "ChartConfig.h"
#include "PanelList.h"
#include <iostream>
#include <Wt/WAbstractItemModel.h>
#include <Wt/WApplication.h>
#include <Wt/WCheckBox.h>
#include <Wt/WComboBox.h>
#include <Wt/WDoubleValidator.h>
#include <Wt/WDate.h>
#include <Wt/WEnvironment.h>
#include <Wt/WIntValidator.h>
#include <Wt/WLineEdit.h>
#include <Wt/WLocale.h>
#include <Wt/WPanel.h>
#include <Wt/WPushButton.h>
#include <Wt/WStandardItemModel.h>
#include <Wt/WTable.h>
#include <Wt/WText.h>
#include <Wt/WPainterPath.h>
#include <Wt/Chart/WCartesianChart.h>
using namespace Wt;
using namespace Wt::Chart;
namespace {
void addHeader(WTable *t, const char *value) {
t->elementAt(0, t->columnCount())->addWidget(std::make_unique<WText>(value));
}
void addEntry(const std::shared_ptr<WAbstractItemModel>& model, const char *value) {
model->insertRows(model->rowCount(), 1);
model->setData(model->rowCount()-1, 0, cpp17::any(std::string(value)));
}
void addEntry(const std::shared_ptr<WAbstractItemModel>& model, const WString &value) {
model->insertRows(model->rowCount(), 1);
model->setData(model->rowCount()-1, 0, cpp17::any(value));
}
bool getDouble(WLineEdit *edit, double& value) {
try {
value = WLocale::currentLocale().toDouble(edit->text());
return true;
} catch (...) {
return false;
}
}
int seriesIndexOf(WCartesianChart* chart, int modelColumn) {
for (unsigned i = 0; i < chart->series().size(); ++i)
if (chart->series()[i]->modelColumn() == modelColumn)
return i;
return -1;
}
WString axisName(Axis axis, int axisId)
{
if (axis == Axis::X) {
return Wt::utf8("X Axis {1}").arg(axisId + 1);
} else {
return Wt::utf8("Y axis {1}").arg(axisId + 1);
}
}
}
ChartConfig::ChartConfig(WCartesianChart *chart)
: WContainerWidget(),
chart_(chart),
fill_(FillRangeType::MinimumValue)
{
chart_->setLegendStyle(chart_->legendFont(), WPen(WColor("black")),
WBrush(WColor(0xFF, 0xFA, 0xE5)));
PanelList *list = this->addWidget(std::make_unique<PanelList>());
std::shared_ptr<WIntValidator> sizeValidator
= std::make_shared<WIntValidator>(200,2000);
sizeValidator->setMandatory(true);
anyNumberValidator_ = std::make_shared<WDoubleValidator>();
anyNumberValidator_->setMandatory(true);
angleValidator_ = std::make_shared<WDoubleValidator>(-90, 90);
angleValidator_->setMandatory(true);
// ---- Chart properties ----
std::shared_ptr<WStandardItemModel> orientation
= std::make_shared<WStandardItemModel>(0,1);
addEntry(orientation, "Vertical");
addEntry(orientation, "Horizontal");
std::shared_ptr<WStandardItemModel> legendLocation
= std::make_shared<WStandardItemModel>(0,1);
addEntry(legendLocation, "Outside");
addEntry(legendLocation, "Inside");
std::shared_ptr<WStandardItemModel> legendSide
= std::make_shared<WStandardItemModel>(0,1);
addEntry(legendSide, "Top");
addEntry(legendSide, "Right");
addEntry(legendSide, "Bottom");
addEntry(legendSide, "Left");
std::shared_ptr<WStandardItemModel> legendAlignment
= std::make_shared<WStandardItemModel>(0,1);
addEntry(legendAlignment, "AlignLeft");
addEntry(legendAlignment, "AlignCenter");
addEntry(legendAlignment, "AlignRight");
addEntry(legendAlignment, "AlignTop");
addEntry(legendAlignment, "AlignMiddle");
addEntry(legendAlignment, "AlignBottom");
std::unique_ptr<WTable> chartConfig
= std::make_unique<WTable>();
chartConfig->setMargin(WLength::Auto, Side::Left | Side::Right);
int row = 0;
chartConfig->elementAt(row, 0)->addWidget(std::make_unique<WText>("Title:"));
titleEdit_ = chartConfig->elementAt(row,1)->addWidget(std::make_unique<WLineEdit>());
connectSignals(titleEdit_);
++row;
chartConfig->elementAt(row, 0)->addWidget(std::make_unique<WText>("Width:"));
chartWidthEdit_ = chartConfig->elementAt(row,1)->addWidget(std::make_unique<WLineEdit>());
chartWidthEdit_
->setText(WLocale::currentLocale().toString(chart_->width().value()));
chartWidthEdit_->setValidator(sizeValidator);
chartWidthEdit_->setMaxLength(4);
connectSignals(chartWidthEdit_);
++row;
chartConfig->elementAt(row, 0)->addWidget(std::make_unique<WText>("Height:"));
chartHeightEdit_ = chartConfig->elementAt(row,1)->addWidget(std::make_unique<WLineEdit>());
chartHeightEdit_
->setText(WLocale::currentLocale().toString(chart_->height().value()));
chartHeightEdit_->setValidator(sizeValidator);
chartHeightEdit_->setMaxLength(4);
connectSignals(chartHeightEdit_);
++row;
chartConfig->elementAt(row, 0)->addWidget(std::make_unique<WText>("Orientation:"));
chartOrientationEdit_ = chartConfig->elementAt(row,1)->addWidget(std::make_unique<WComboBox>());
chartOrientationEdit_->setModel(orientation);
chartOrientationEdit_->setCurrentIndex(0);
connectSignals(chartOrientationEdit_);
++row;
chartConfig->elementAt(row, 0)->addWidget(std::make_unique<WText>("Legend location:"));
legendLocationEdit_ = chartConfig->elementAt(row,1)->addWidget(std::make_unique<WComboBox>());
legendLocationEdit_->setModel(legendLocation);
legendLocationEdit_->setCurrentIndex(0);
connectSignals(legendLocationEdit_);
++row;
chartConfig->elementAt(row, 0)->addWidget(std::make_unique<WText>("Legend side:"));
legendSideEdit_ = chartConfig->elementAt(row,1)->addWidget(std::make_unique<WComboBox>());
legendSideEdit_->setModel(legendSide);
legendSideEdit_->setCurrentIndex(1);
connectSignals(legendSideEdit_);
++row;
chartConfig->elementAt(row, 0)->addWidget(std::make_unique<WText>("Legend alignment:"));
legendAlignmentEdit_ = chartConfig->elementAt(row,1)->addWidget(std::make_unique<WComboBox>());
legendAlignmentEdit_->setModel(legendAlignment);
legendAlignmentEdit_->setCurrentIndex(4);
connectSignals(legendAlignmentEdit_);
++row;
chartConfig->elementAt(row, 0)->addWidget(std::make_unique<WText>("Border:"));
borderEdit_ = chartConfig->elementAt(row,1)->addWidget(std::make_unique<WCheckBox>());
borderEdit_->setChecked(false);
connectSignals(borderEdit_);
++row;
for (int i = 0; i < chartConfig->rowCount(); ++i) {
chartConfig->elementAt(i, 0)->setStyleClass("tdhead");
chartConfig->elementAt(i, 1)->setStyleClass("tddata");
}
WPanel *p = list->addWidget("Chart properties", std::move(chartConfig));
p->setMargin(WLength::Auto, Side::Left | Side::Right);
p->resize(1160, WLength::Auto);
p->setMargin(20, Side::Top | Side::Bottom);
// ---- Series properties ----
std::shared_ptr<WStandardItemModel> types
= std::make_shared<WStandardItemModel>(0,1);
addEntry(types, "Points");
addEntry(types, "Line");
addEntry(types, "Curve");
addEntry(types, "Bar");
addEntry(types, "Line Area");
addEntry(types, "Curve Area");
addEntry(types, "Stacked Bar");
addEntry(types, "Stacked Line Area");
addEntry(types, "Stacked Curve Area");
std::shared_ptr<WStandardItemModel> markers
= std::make_shared<WStandardItemModel>(0,1);
addEntry(markers, "None");
addEntry(markers, "Square");
addEntry(markers, "Circle");
addEntry(markers, "Cross");
addEntry(markers, "X cross");
addEntry(markers, "Triangle");
addEntry(markers, "Pipe");
addEntry(markers, "Star");
addEntry(markers, "Inverted triangle");
addEntry(markers, "Asterisk");
addEntry(markers, "Diamond");
xAxesModel_ = std::make_shared<WStandardItemModel>(0, 1);
addEntry(xAxesModel_, axisName(Axis::X, 0));
yAxesModel_ = std::make_shared<WStandardItemModel>(0, 1);
addEntry(yAxesModel_, axisName(Axis::Y, 0));
addEntry(yAxesModel_, axisName(Axis::Y, 1));
std::shared_ptr<WStandardItemModel> labels
= std::make_shared<WStandardItemModel>(0,1);
addEntry(labels, "None");
addEntry(labels, "X");
addEntry(labels, "Y");
addEntry(labels, "X: Y");
std::unique_ptr<WTable> seriesConfig
= std::make_unique<WTable>();
WTable *seriesConfigPtr = seriesConfig.get();
seriesConfig->setMargin(WLength::Auto, Side::Left | Side::Right);
::addHeader(seriesConfigPtr, "Name");
::addHeader(seriesConfigPtr, "Enabled");
::addHeader(seriesConfigPtr, "Type");
::addHeader(seriesConfigPtr, "Marker");
::addHeader(seriesConfigPtr, "X axis");
::addHeader(seriesConfigPtr, "Y axis");
::addHeader(seriesConfigPtr, "Legend");
::addHeader(seriesConfigPtr, "Shadow");
::addHeader(seriesConfigPtr, "Value labels");
seriesConfig->rowAt(0)->setStyleClass("trhead");
for (int j = 1; j < chart->model()->columnCount(); ++j) {
SeriesControl sc;
seriesConfig->elementAt(j,0)->addWidget(std::make_unique<WText>(chart->model()->headerData(j)));
sc.enabledEdit = seriesConfig->elementAt(j,1)->addWidget(std::make_unique<WCheckBox>());
connectSignals(sc.enabledEdit);
sc.typeEdit = seriesConfig->elementAt(j,2)->addWidget(std::make_unique<WComboBox>());
sc.typeEdit->setModel(types);
sc.typeEdit->setCurrentIndex(0);
connectSignals(sc.typeEdit);
sc.markerEdit = seriesConfig->elementAt(j,3)->addWidget(std::make_unique<WComboBox>());
sc.markerEdit->setModel(markers);
sc.markerEdit->setCurrentIndex(0);
connectSignals(sc.markerEdit);
sc.xAxisEdit = seriesConfig->elementAt(j, 4)->addNew<WComboBox>();
sc.xAxisEdit->setModel(xAxesModel_);
sc.xAxisEdit->setCurrentIndex(0);
connectSignals(sc.xAxisEdit);
sc.yAxisEdit = seriesConfig->elementAt(j, 5)->addNew<WComboBox>();
sc.yAxisEdit->setModel(yAxesModel_);
sc.yAxisEdit->setCurrentIndex(0);
connectSignals(sc.yAxisEdit);
sc.legendEdit = seriesConfig->elementAt(j, 6)->addWidget(std::make_unique<WCheckBox>());
connectSignals(sc.legendEdit);
sc.shadowEdit = seriesConfig->elementAt(j, 7)->addWidget(std::make_unique<WCheckBox>());
connectSignals(sc.shadowEdit);
sc.labelsEdit = seriesConfig->elementAt(j, 8)->addWidget(std::make_unique<WComboBox>());
sc.labelsEdit->setModel(labels);
sc.labelsEdit->setCurrentIndex(0);
connectSignals(sc.labelsEdit);
int si = seriesIndexOf(chart, j);
if (si != -1) {
sc.enabledEdit->setChecked();
const WDataSeries& s = chart_->series(j);
switch (s.type()) {
case SeriesType::Point:
sc.typeEdit->setCurrentIndex(0); break;
case SeriesType::Line:
sc.typeEdit->setCurrentIndex(s.fillRange() != FillRangeType::None ?
(s.isStacked() ? 7 : 4) : 1); break;
case SeriesType::Curve:
sc.typeEdit->setCurrentIndex(s.fillRange() != FillRangeType::None ?
(s.isStacked() ? 8 : 5) : 2); break;
case SeriesType::Bar:
sc.typeEdit->setCurrentIndex(s.isStacked() ? 6 : 3);
}
sc.markerEdit->setCurrentIndex((int)s.marker());
sc.legendEdit->setChecked(s.isLegendEnabled());
sc.shadowEdit->setChecked(s.shadow() != WShadow());
}
seriesControls_.push_back(sc);
seriesConfig->rowAt(j)->setStyleClass("trdata");
}
p = list->addWidget("Series properties", std::move(seriesConfig));
p->expand();
p->setMargin(WLength::Auto, Side::Left | Side::Right);
p->resize(1160, WLength::Auto);
p->setMargin(20, Side::Top | Side::Bottom);
// ---- Axis properties ----
yScales_ = std::make_shared<WStandardItemModel>(0, 1);
addEntry(yScales_, "Linear scale");
addEntry(yScales_, "Log scale");
xScales_ = std::make_shared<WStandardItemModel>(0, 1);
addEntry(xScales_, "Categories");
addEntry(xScales_, "Linear scale");
addEntry(xScales_, "Log scale");
addEntry(xScales_, "Date scale");
auto axisConfig = std::make_unique<WContainerWidget>();
axisConfig_ = axisConfig->addNew<WTable>();
axisConfig_->setMargin(WLength::Auto, Side::Left | Side::Right);
::addHeader(axisConfig_, "Axis");
::addHeader(axisConfig_, "Visible");
::addHeader(axisConfig_, "Scale");
::addHeader(axisConfig_, "Automatic");
::addHeader(axisConfig_, "Minimum");
::addHeader(axisConfig_, "Maximum");
::addHeader(axisConfig_, "Gridlines");
::addHeader(axisConfig_, "Label angle");
::addHeader(axisConfig_, "Title");
::addHeader(axisConfig_, "Title orientation");
::addHeader(axisConfig_, "Tick direction");
::addHeader(axisConfig_, "Location");
axisConfig_->rowAt(0)->setStyleClass("trhead");
addAxis(Axis::X, 0);
addAxis(Axis::Y, 0);
addAxis(Axis::Y, 1);
WPushButton *addXAxisBtn =
axisConfig->addNew<WPushButton>(Wt::utf8("Add X axis"));
addXAxisBtn->clicked().connect(this, &ChartConfig::addXAxis);
WPushButton *clearXAxesBtn =
axisConfig->addNew<WPushButton>(Wt::utf8("Clear X axes"));
clearXAxesBtn->clicked().connect(this, &ChartConfig::clearXAxes);
WPushButton *addYAxisBtn =
axisConfig->addNew<WPushButton>(utf8("Add Y axis"));
addYAxisBtn->clicked().connect(this, &ChartConfig::addYAxis);
WPushButton *clearYAxesBtn =
axisConfig->addNew<WPushButton>(utf8("Clear Y axes"));
clearYAxesBtn->clicked().connect(this, &ChartConfig::clearYAxes);
p = list->addWidget("Axis properties", std::move(axisConfig));
p->setMargin(WLength::Auto, Side::Left | Side::Right);
p->resize(1160, WLength::Auto);
p->setMargin(20, Side::Top | Side::Bottom);
/*
* If we do not have JavaScript, then add a button to reflect changes to
* the chart.
*/
if (!WApplication::instance()->environment().javaScript()) {
auto *b = this->addWidget(std::make_unique<WPushButton>());
b->setText("Update chart");
b->setInline(false); // so we can add margin to center horizontally
b->setMargin(WLength::Auto, Side::Left | Side::Right);
b->clicked().connect(this, &ChartConfig::update);
}
}
void ChartConfig::setValueFill(FillRangeType fill)
{
fill_ = fill;
}
void ChartConfig::update()
{
bool haveLegend = false;
std::vector<std::unique_ptr<WDataSeries>> series;
for (int i = 1; i < chart_->model()->columnCount(); ++i) {
SeriesControl& sc = seriesControls_[i-1];
if (sc.enabledEdit->isChecked()) {
std::unique_ptr<WDataSeries> s
= std::make_unique<WDataSeries>(i);
switch (sc.typeEdit->currentIndex()) {
case 0:
s->setType(SeriesType::Point);
if (sc.markerEdit->currentIndex() == 0)
sc.markerEdit->setCurrentIndex(1);
break;
case 1:
s->setType(SeriesType::Line);
break;
case 2:
s->setType(SeriesType::Curve);
break;
case 3:
s->setType(SeriesType::Bar);
break;
case 4:
s->setType(SeriesType::Line);
s->setFillRange(fill_);
break;
case 5:
s->setType(SeriesType::Curve);
s->setFillRange(fill_);
break;
case 6:
s->setType(SeriesType::Bar);
s->setStacked(true);
break;
case 7:
s->setType(SeriesType::Line);
s->setFillRange(fill_);
s->setStacked(true);
break;
case 8:
s->setType(SeriesType::Curve);
s->setFillRange(fill_);
s->setStacked(true);
}
//set WPainterPath to draw a pipe
if(sc.markerEdit->currentIndex() == static_cast<int>(MarkerType::Custom)){ //was customMarker before
WPainterPath pp = WPainterPath();
pp.moveTo(0, -6);
pp.lineTo(0, 6);
s->setCustomMarker(pp);
}
s->setMarker(static_cast<MarkerType>(sc.markerEdit->currentIndex()));
s->bindToXAxis(sc.xAxisEdit->currentIndex());
s->bindToYAxis(sc.yAxisEdit->currentIndex());
if (sc.legendEdit->isChecked()) {
s->setLegendEnabled(true);
haveLegend = true;
} else
s->setLegendEnabled(false);
if (sc.shadowEdit->isChecked()) {
s->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3));
} else
s->setShadow(WShadow());
switch (sc.labelsEdit->currentIndex()) {
case 1:
s->setLabelsEnabled(Axis::X);
break;
case 2:
s->setLabelsEnabled(Axis::Y);
break;
case 3:
s->setLabelsEnabled(Axis::X);
s->setLabelsEnabled(Axis::Y);
break;
}
series.push_back(std::move(s));
}
}
chart_->setSeries(std::move(series));
for (std::size_t i = 0; i < axisControls_.size(); ++i) {
AxisControl& sc = axisControls_[i];
WAxis& axis = static_cast<int>(i) < chart_->xAxisCount() ? chart_->xAxis(i) : chart_->yAxis(i - chart_->xAxisCount());
axis.setVisible(sc.visibleEdit->isChecked());
if (sc.scaleEdit->count() != 1) {
int k = sc.scaleEdit->currentIndex();
if (axis.id() != Axis::X)
k += 1;
else {
if (k == 0)
chart_->setType(ChartType::Category);
else
chart_->setType(ChartType::Scatter);
}
switch (k) {
case 1:
axis.setScale(AxisScale::Linear); break;
case 2:
axis.setScale(AxisScale::Log); break;
case 3:
axis.setScale(AxisScale::Date); break;
}
}
if (sc.autoEdit->isChecked())
axis.setAutoLimits(AxisValue::Minimum | AxisValue::Maximum);
else {
if (!(axis.autoLimits() & (AxisValue::Minimum | AxisValue::Maximum)).empty()) {
sc.minimumEdit->setText(WLocale::currentLocale()
.toString(axis.minimum()));
sc.maximumEdit->setText(WLocale::currentLocale()
.toString(axis.maximum()));
}
if (validate(sc.minimumEdit) && validate(sc.maximumEdit)) {
double min, max;
getDouble(sc.minimumEdit, min);
getDouble(sc.maximumEdit, max);
if (axis.scale() == AxisScale::Log)
if (min <= 0)
min = 0.0001;
if (axis.scale() == AxisScale::Date){
//the number of julian days until year 1986
WDate dMin = WDate(1900,1,1);
double gregDaysMin = (double)dMin.toJulianDay();
//the number of julian days until year 1988
WDate dMax = WDate(3000,1,1);
double gregDaysMax = (double)dMax.toJulianDay();
bool greg_year_validation =
(min > gregDaysMin &&
min < gregDaysMax &&
max > gregDaysMin &&
max < gregDaysMax);
if(!greg_year_validation){
min = gregDaysMin;
max = gregDaysMax;
}
}
axis.setRange(min, max);
}
}
if (validate(sc.labelAngleEdit)) {
double angle;
getDouble(sc.labelAngleEdit, angle);
axis.setLabelAngle(angle);
}
axis.setGridLinesEnabled(sc.gridLinesEdit->isChecked());
axis.setTitle(sc.titleEdit->text());
axis.setTitleOrientation(sc.titleOrientationEdit->currentIndex() == 0 ? Orientation::Horizontal : Orientation::Vertical);
axis.setTickDirection(sc.tickDirectionEdit->currentIndex() == 0 ? TickDirection::Outwards : TickDirection::Inwards);
switch (sc.locationEdit->currentIndex()) {
case 0:
axis.setLocation(AxisValue::Minimum);
break;
case 1:
axis.setLocation(AxisValue::Maximum);
break;
case 2:
axis.setLocation(AxisValue::Zero);
break;
case 3:
axis.setLocation(AxisValue::Both);
break;
}
}
chart_->setTitle(titleEdit_->text());
if (validate(chartWidthEdit_) && validate(chartHeightEdit_)) {
double width, height;
getDouble(chartWidthEdit_, width);
getDouble(chartHeightEdit_, height);
chart_->resize(width, height);
}
switch (chartOrientationEdit_->currentIndex()) {
case 0:
chart_->setOrientation(Orientation::Vertical); break;
case 1:
chart_->setOrientation(Orientation::Horizontal); break;
}
chart_->setLegendEnabled(haveLegend);
if (haveLegend) {
LegendLocation location = LegendLocation::Outside;
Side side = Side::Right;
AlignmentFlag alignment = AlignmentFlag::Middle;
switch (legendLocationEdit_->currentIndex()) {
case 0: location = LegendLocation::Outside; break;
case 1: location = LegendLocation::Inside; break;
}
switch (legendSideEdit_->currentIndex()) {
case 0: side = Side::Top; break;
case 1: side = Side::Right; break;
case 2: side = Side::Bottom; break;
case 3: side = Side::Left; break;
}
if (side == Side::Left || side == Side::Right) {
if (legendAlignmentEdit_->currentIndex() < 3)
legendAlignmentEdit_->setCurrentIndex(4);
} else {
if (legendAlignmentEdit_->currentIndex() >= 3)
legendAlignmentEdit_->setCurrentIndex(2);
}
switch (legendAlignmentEdit_->currentIndex()) {
case 0: alignment = AlignmentFlag::Left; break;
case 1: alignment = AlignmentFlag::Center; break;
case 2: alignment = AlignmentFlag::Right; break;
case 3: alignment = AlignmentFlag::Top; break;
case 4: alignment = AlignmentFlag::Middle; break;
case 5: alignment = AlignmentFlag::Bottom; break;
}
chart_->setLegendLocation(location, side, alignment);
chart_->setLegendColumns((side == Side::Top || side == Side::Bottom ) ? 2 : 1,
WLength(100));
}
if (borderEdit_->isChecked()) {
chart_->setBorderPen(WPen());
} else {
chart_->setBorderPen(PenStyle::None);
}
}
bool ChartConfig::validate(WFormWidget *w)
{
bool valid = w->validate() == ValidationState::Valid;
if (!WApplication::instance()->environment().javaScript()) {
w->setStyleClass(valid ? "" : "Wt-invalid");
w->setToolTip(valid ? "" : "Invalid value");
}
return valid;
}
void ChartConfig::connectSignals(WFormWidget *w)
{
w->changed().connect(this, &ChartConfig::update);
if (dynamic_cast<WLineEdit *>(w))
w->enterPressed().connect(this, &ChartConfig::update);
}
void ChartConfig::addXAxis()
{
int xAxis = chart_->addXAxis(std::make_unique<WAxis>());
addAxis(Axis::X, xAxis);
addEntry(xAxesModel_, axisName(Axis::X, xAxis));
if (xAxis == 0)
update();
}
void ChartConfig::addYAxis()
{
int yAxis = chart_->addYAxis(std::make_unique<WAxis>());
addAxis(Axis::Y, yAxis);
addEntry(yAxesModel_, axisName(Axis::Y, yAxis));
if (yAxis == 0)
update();
}
void ChartConfig::addAxis(Axis ax, int axisId)
{
int j = ax == Axis::X ? 1 + axisId : 1 + chart_->xAxisCount() + axisId;
const WAxis& axis = ax == Axis::X ? chart_->xAxis(axisId) : chart_->yAxis(axisId);
AxisControl sc;
axisConfig_->insertRow(j);
axisConfig_->elementAt(j, 0)->addNew<WText>(axisName(axis.id(), axis.yAxisId()));
sc.visibleEdit = axisConfig_->elementAt(j, 1)->addNew<WCheckBox>();
sc.visibleEdit->setChecked(axis.isVisible());
connectSignals(sc.visibleEdit);
sc.scaleEdit = axisConfig_->elementAt(j, 2)->addNew<WComboBox>();
if (axis.scale() == AxisScale::Discrete)
sc.scaleEdit->addItem("Discrete scale");
else {
if (axis.id() == Axis::X) {
sc.scaleEdit->setModel(xScales_);
sc.scaleEdit->setCurrentIndex(static_cast<int>(axis.scale()));
} else {
sc.scaleEdit->setModel(yScales_);
sc.scaleEdit->setCurrentIndex(static_cast<int>(axis.scale()) - 1);
}
}
connectSignals(sc.scaleEdit);
bool autoValues = axis.autoLimits() == (AxisValue::Minimum | AxisValue::Maximum);
sc.minimumEdit = axisConfig_->elementAt(j, 4)->addNew<WLineEdit>();
sc.minimumEdit->setText(WLocale::currentLocale()
.toString(axis.minimum()));
sc.minimumEdit->setValidator(anyNumberValidator_);
sc.minimumEdit->setEnabled(!autoValues);
connectSignals(sc.minimumEdit);
sc.maximumEdit = axisConfig_->elementAt(j, 5)->addNew<WLineEdit>();
sc.maximumEdit->setText(WLocale::currentLocale()
.toString(axis.maximum()));
sc.maximumEdit->setValidator(anyNumberValidator_);
sc.maximumEdit->setEnabled(!autoValues);
connectSignals(sc.maximumEdit);
sc.autoEdit = axisConfig_->elementAt(j, 3)->addNew<WCheckBox>();
sc.autoEdit->setChecked(autoValues);
connectSignals(sc.autoEdit);
sc.autoEdit->checked().connect(sc.maximumEdit, &WLineEdit::disable);
sc.autoEdit->unChecked().connect(sc.maximumEdit, &WLineEdit::enable);
sc.autoEdit->checked().connect(sc.minimumEdit, &WLineEdit::disable);
sc.autoEdit->unChecked().connect(sc.minimumEdit, &WLineEdit::enable);
sc.gridLinesEdit = axisConfig_->elementAt(j, 6)->addNew<WCheckBox>();
connectSignals(sc.gridLinesEdit);
sc.labelAngleEdit = axisConfig_->elementAt(j, 7)->addNew<WLineEdit>();
sc.labelAngleEdit->setText("0");
sc.labelAngleEdit->setValidator(angleValidator_);
connectSignals(sc.labelAngleEdit);
sc.titleEdit = axisConfig_->elementAt(j, 8)->addNew<WLineEdit>();
sc.titleEdit->setText("");
connectSignals(sc.titleEdit);
sc.titleOrientationEdit = axisConfig_->elementAt(j, 9)->addNew<WComboBox>();
sc.titleOrientationEdit->addItem("Horizontal");
sc.titleOrientationEdit->addItem("Vertical");
sc.titleOrientationEdit->setCurrentIndex(0);
connectSignals(sc.titleOrientationEdit);
sc.tickDirectionEdit = axisConfig_->elementAt(j, 10)->addNew<WComboBox>();
sc.tickDirectionEdit->addItem("Outwards");
sc.tickDirectionEdit->addItem("Inwards");
sc.tickDirectionEdit->setCurrentIndex(0);
connectSignals(sc.tickDirectionEdit);
sc.locationEdit = axisConfig_->elementAt(j, 11)->addNew<WComboBox>();
sc.locationEdit->addItem("Minimum value");
sc.locationEdit->addItem("Maximum value");
sc.locationEdit->addItem("Zero value");
sc.locationEdit->addItem("Both sides");
sc.locationEdit->setCurrentIndex(0);
if (axis.location() == AxisValue::Maximum) {
sc.locationEdit->setCurrentIndex(1);
} else if (axis.location() == AxisValue::Zero) {
sc.locationEdit->setCurrentIndex(2);
}
connectSignals(sc.locationEdit);
WPushButton *removeAxisButton =
axisConfig_->elementAt(j, 12)->addNew<WPushButton>(utf8("x"));
if (ax == Axis::X) {
removeAxisButton->clicked().connect(std::bind(&ChartConfig::removeXAxis, this, &axis));
} else {
removeAxisButton->clicked().connect(std::bind(&ChartConfig::removeYAxis, this, &axis));
}
axisConfig_->rowAt(j)->setStyleClass("trdata");
axisControls_.insert(axisControls_.begin() + j - 1, sc);
}
void ChartConfig::removeXAxis(const WAxis *axis)
{
int xAxis = axis->xAxisId();
for (std::size_t i = 0; i < chart_->series().size(); ++i) {
if (chart_->series()[i]->xAxis() == xAxis)
chart_->series()[i]->bindToXAxis(-1);
}
chart_->removeXAxis(xAxis);
axisConfig_->removeRow(1 + xAxis);
xAxesModel_->removeRow(xAxis);
axisControls_.erase(axisControls_.begin() + xAxis);
update();
}
void ChartConfig::removeYAxis(const WAxis *axis)
{
int yAxis = axis->yAxisId();
for (std::size_t i = 0; i < chart_->series().size(); ++i) {
if (chart_->series()[i]->yAxis() == yAxis)
chart_->series()[i]->bindToYAxis(-1);
}
chart_->removeYAxis(yAxis);
axisConfig_->removeRow(1 + chart_->xAxisCount() + yAxis);
yAxesModel_->removeRow(yAxis);
axisControls_.erase(axisControls_.begin() + chart_->xAxisCount() + yAxis);
update();
}
void ChartConfig::clearXAxes()
{
if (chart_->xAxisCount() == 0)
return;
for (std::size_t i = 0; i < chart_->series().size(); ++i) {
chart_->series()[i]->bindToXAxis(-1);
}
const int xAxisCount = chart_->xAxisCount();
chart_->clearXAxes();
for (int i = 0; i < xAxisCount; ++i) {
axisConfig_->removeRow(1);
}
xAxesModel_->clear();
axisControls_.erase(axisControls_.begin(), axisControls_.begin() + xAxisCount);
}
void ChartConfig::clearYAxes()
{
if (chart_->yAxisCount() == 0)
return;
for (std::size_t i = 0; i < chart_->series().size(); ++i) {
chart_->series()[i]->bindToYAxis(-1);
}
const int yAxisCount = chart_->yAxisCount();
chart_->clearYAxes();
for (int i = 0; i < yAxisCount; ++i) {
axisConfig_->removeRow(axisConfig_->rowCount() - 1);
}
yAxesModel_->clear();
axisControls_.resize(chart_->xAxisCount());
}