diff --git a/limereport/designer.pri b/limereport/designer.pri index 13bf6ea..ab07ed6 100644 --- a/limereport/designer.pri +++ b/limereport/designer.pri @@ -30,6 +30,7 @@ SOURCES += \ $$REPORT_PATH/objectinspector/propertyItems/lrcontentpropitem.cpp \ $$REPORT_PATH/objectinspector/propertyItems/lrmarginpropitem.cpp \ $$REPORT_PATH/objectinspector/propertyItems/lrseriespropitem.cpp \ + $$REPORT_PATH/objectinspector/propertyItems/lraxispropitem.cpp \ $$REPORT_PATH/objectinspector/editors/lrtextitempropertyeditor.cpp \ $$REPORT_PATH/objectinspector/editors/lrcomboboxeditor.cpp \ $$REPORT_PATH/objectinspector/editors/lrcheckboxeditor.cpp \ @@ -80,6 +81,7 @@ HEADERS += \ $$REPORT_PATH/objectinspector/propertyItems/lrcolorpropitem.h \ $$REPORT_PATH/objectinspector/propertyItems/lrmarginpropitem.h \ $$REPORT_PATH/objectinspector/propertyItems/lrseriespropitem.h \ + $$REPORT_PATH/objectinspector/propertyItems/lraxispropitem.h \ $$REPORT_PATH/objectinspector/editors/lrtextitempropertyeditor.h \ $$REPORT_PATH/objectinspector/editors/lrcomboboxeditor.h \ $$REPORT_PATH/objectinspector/editors/lrcheckboxeditor.h \ diff --git a/limereport/items/charts/lrlineschart.cpp b/limereport/items/charts/lrlineschart.cpp index 36aabc6..18c8692 100644 --- a/limereport/items/charts/lrlineschart.cpp +++ b/limereport/items/charts/lrlineschart.cpp @@ -70,7 +70,12 @@ void LinesChart::drawDesignMode(QPainter* painter, qreal hStep, qreal vStep, qre qreal LinesChart::calculatePos(const AxisData &data, qreal value, qreal rectSize) const { - return (data.rangeMax() - value) / data.delta() * rectSize; + if (data.type() == AxisData::XAxis || (data.reverseDirection() && data.rangeMin() >= 0)) { + // Not flipping for minimum less than 0 because lower number is at the bottom. + return (1 - (data.rangeMax() - value) / data.delta()) * rectSize; + } else { + return (data.rangeMax() - value) / data.delta() * rectSize; + } } void LinesChart::paintSeries(QPainter *painter, SeriesItem *series, QRectF barsRect) diff --git a/limereport/items/charts/lrverticalbarchart.cpp b/limereport/items/charts/lrverticalbarchart.cpp index 36109b0..bccc244 100644 --- a/limereport/items/charts/lrverticalbarchart.cpp +++ b/limereport/items/charts/lrverticalbarchart.cpp @@ -71,8 +71,6 @@ void VerticalBarChart::paintVerticalBars(QPainter *painter, QRectF barsRect) qreal hStep = (barsRect.width() / valuesCount()) / (barSeriesCount == 0 ? 1 : barSeriesCount); qreal topShift = (delta - (maxValue() - minValue())) * vStep + barsRect.top(); - qDebug() << "vStep" << vStep << "hStep" << hStep; - if (!m_chartItem->series().isEmpty() && (m_chartItem->itemMode() != DesignMode)){ int curSeries = 0; foreach (SeriesItem* series, m_chartItem->series()) { diff --git a/limereport/items/lrchartaxiseditor.cpp b/limereport/items/lrchartaxiseditor.cpp new file mode 100644 index 0000000..548a0f5 --- /dev/null +++ b/limereport/items/lrchartaxiseditor.cpp @@ -0,0 +1,142 @@ +#include "lrchartaxiseditor.h" + +#include "ui_lrchartaxiseditor.h" +#include "lraxisdata.h" + +#include "lrbasedesignintf.h" + +ChartAxisEditor::ChartAxisEditor(LimeReport::ChartItem *item, LimeReport::PageDesignIntf *page, bool isXAxis, QSettings *settings, QWidget *parent): + QWidget(parent), ui(new Ui::ChartAxisEditor), m_chartItem(item), m_page(page), + m_settings(settings), m_isXAxis(isXAxis) +{ + ui->setupUi(this); + readSetting(); + init(); +} + +ChartAxisEditor::~ChartAxisEditor() +{ +#if defined(Q_OS_WIN) || defined(Q_OS_MAC) + writeSetting(); +#endif + delete ui; +} + +QSettings* ChartAxisEditor::settings() +{ + if (m_settings){ + return m_settings; + } + m_settings = new QSettings("LimeReport",QCoreApplication::applicationName()); + return m_settings; +} + +void ChartAxisEditor::readSetting() +{ + if (settings() == 0) return; + + settings()->beginGroup("ChartAxisEditor"); + QVariant v = settings()->value("Geometry"); + if (v.isValid()) { + restoreGeometry(v.toByteArray()); + } + + settings()->endGroup(); +} + +void ChartAxisEditor::writeSetting() +{ + if (settings() == 0) { + return; + } + settings()->beginGroup("ChartAxisEditor"); + settings()->setValue("Geometry",saveGeometry()); + settings()->endGroup(); +} + +void ChartAxisEditor::init() +{ + ui->gbAxis->setTitle(m_isXAxis ? QObject::tr("X Axis") : QObject::tr("Y Axis")); + ui->direction_checkbox->setVisible(!m_isXAxis); + + LimeReport::AxisData *axisData = m_isXAxis ? m_chartItem->xAxisData() : m_chartItem->yAxisData(); + + ui->minimumSpinBox->setValue(axisData->manualMinimum()); + ui->maximumSpinBox->setValue(axisData->manualMaximum()); + ui->stepSpinBox->setValue(axisData->manualStep()); + + ui->minimumCheckBox->setChecked(axisData->isMinimumAutomatic()); + ui->maximumCheckBox->setChecked(axisData->isMaximumAutomatic()); + ui->stepCheckBox->setChecked(axisData->isStepAutomatic()); + + ui->direction_checkbox->setChecked(axisData->reverseDirection()); + + const bool isScaleCalcEnabled = axisData->calculateAxisScale(); + ui->enableScaleCalculation_checkbox->setChecked(isScaleCalcEnabled); + on_enableScaleCalculation_checkbox_stateChanged(isScaleCalcEnabled); +} + +void ChartAxisEditor::on_minimumCheckBox_stateChanged(int arg1) +{ + const bool isAutomatic = (bool)arg1; + ui->minimumSpinBox->setEnabled(!isAutomatic); +} + +void ChartAxisEditor::on_maximumCheckBox_stateChanged(int arg1) +{ + const bool isAutomatic = (bool)arg1; + ui->maximumSpinBox->setEnabled(!isAutomatic); +} + +void ChartAxisEditor::on_stepCheckBox_stateChanged(int arg1) +{ + const bool isAutomatic = (bool)arg1; + ui->stepSpinBox->setEnabled(!isAutomatic); +} + +void ChartAxisEditor::on_pushButtonOk_clicked() +{ + LimeReport::AxisData *axisData = m_isXAxis ? m_chartItem->xAxisData() : m_chartItem->yAxisData(); + if (!m_isXAxis) { + axisData->setReverseDirection(ui->direction_checkbox->isChecked()); + } + + axisData->setIsStepAutomatic(ui->stepCheckBox->isChecked()); + axisData->setManualStep(ui->stepSpinBox->value()); + axisData->setIsMinimumAutomatic(ui->minimumCheckBox->isChecked()); + axisData->setManualMinimum(ui->minimumSpinBox->value()); + axisData->setIsMaximumAutomatic(ui->maximumCheckBox->isChecked()); + axisData->setManualMaximum(ui->maximumSpinBox->value()); + + axisData->setCalculateAxisScale(ui->enableScaleCalculation_checkbox->isChecked()); + + if (m_chartItem->itemMode() == LimeReport::DesignMode) { + axisData->updateForDesignMode(); + } else { + axisData->update(); + } + m_chartItem->update(); + close(); +} + +void ChartAxisEditor::on_enableScaleCalculation_checkbox_stateChanged(int arg1) +{ + const bool isEnabled = (bool)arg1; + ui->minimumCheckBox->setEnabled(isEnabled); + ui->maximumCheckBox->setEnabled(isEnabled); + ui->stepCheckBox->setEnabled(isEnabled); + + ui->minimumSpinBox->setEnabled(!ui->minimumCheckBox->isChecked() && isEnabled); + ui->maximumSpinBox->setEnabled(!ui->maximumCheckBox->isChecked() && isEnabled); + ui->stepSpinBox->setEnabled(!ui->stepCheckBox->isChecked() && isEnabled); + + ui->minimumCheckBox->setEnabled(isEnabled); + ui->maximumCheckBox->setEnabled(isEnabled); + ui->stepCheckBox->setEnabled(isEnabled); +} + +void ChartAxisEditor::on_cancelButton_clicked() +{ + close(); +} + diff --git a/limereport/items/lrchartaxiseditor.h b/limereport/items/lrchartaxiseditor.h new file mode 100644 index 0000000..5eeb58b --- /dev/null +++ b/limereport/items/lrchartaxiseditor.h @@ -0,0 +1,41 @@ +#ifndef CHARTAXISEDITOR_H +#define CHARTAXISEDITOR_H + +#include +#include "lrchartitem.h" + +namespace Ui { +class ChartAxisEditor; +} + +class ChartAxisEditor : public QWidget +{ + Q_OBJECT + public: + ChartAxisEditor(LimeReport::ChartItem* item, LimeReport::PageDesignIntf* page, bool isXAxis, + QSettings* settings=0, QWidget *parent = 0); + ~ChartAxisEditor(); + + QSettings *settings(); +private slots: + void on_minimumCheckBox_stateChanged(int arg1); + void on_maximumCheckBox_stateChanged(int arg1); + void on_stepCheckBox_stateChanged(int arg1); + void on_pushButtonOk_clicked(); + void on_enableScaleCalculation_checkbox_stateChanged(int arg1); + + void on_cancelButton_clicked(); + +private: + void readSetting(); + void writeSetting(); + void init(); + + Ui::ChartAxisEditor *ui; + LimeReport::ChartItem* m_chartItem; + LimeReport::PageDesignIntf* m_page; + QSettings* m_settings; + bool m_isXAxis; +}; + +#endif // CHARTAXISEDITOR_H diff --git a/limereport/items/lrchartaxiseditor.ui b/limereport/items/lrchartaxiseditor.ui new file mode 100755 index 0000000..35dda3c --- /dev/null +++ b/limereport/items/lrchartaxiseditor.ui @@ -0,0 +1,212 @@ + + + ChartAxisEditor + + + + 0 + 0 + 380 + 268 + + + + Axis editor + + + + + + + 0 + 0 + + + + Axis + + + + + + true + + + Reverse direction + + + + + + + true + + + Enable scale calculation + + + + + + + QLayout::SetFixedSize + + + + + Step + + + + + + + Maximum + + + + + + + false + + + -99999999.000000000000000 + + + 99999999.000000000000000 + + + + + + + Minimum + + + + + + + Automatic + + + true + + + + + + + Automatic + + + true + + + + + + + Automatic + + + true + + + + + + + 0.000000000000000 + + + 9999999.990000000223517 + + + + + + + -9999999.000000000000000 + + + 99999999.000000000000000 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Cancel + + + + + + + Ok + + + + + + + + + + + + + slotAddSeries() + slotDeleteSeries() + + diff --git a/limereport/items/lrchartitem.cpp b/limereport/items/lrchartitem.cpp index 357b8cb..d78aaf2 100644 --- a/limereport/items/lrchartitem.cpp +++ b/limereport/items/lrchartitem.cpp @@ -8,6 +8,7 @@ #include "lrpagedesignintf.h" #include "lrreportengine_p.h" #include "lrdatadesignintf.h" +#include "lrchartaxiseditor.h" #include "charts/lrpiechart.h" #include "charts/lrverticalbarchart.h" @@ -147,6 +148,9 @@ ChartItem::ChartItem(QObject *owner, QGraphicsItem *parent) m_horizontalAxisOnTop(false), m_gridChartLines(AllLines), m_legendStyle(LegendPoints) { + m_xAxisData = new AxisData(AxisData::XAxis, this); + m_xAxisData->setReverseDirection(true); + m_yAxisData = new AxisData(AxisData::YAxis, this); m_labels<<"First"<<"Second"<<"Thrid"; m_chart = new PieChart(this); m_chart->setTitleFont(font()); @@ -224,6 +228,47 @@ void ChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, ItemDesignIntf::paint(painter,option,widget); } +QObject *ChartItem::xAxisSettings() +{ + return m_xAxisData; +} + +void ChartItem::setYAxisSettings(QObject *axis) +{ + AxisData *data = dynamic_cast(axis); + if (data) { + m_yAxisData->copy(data); + } +} + +QObject *ChartItem::yAxisSettings() +{ + return m_yAxisData; +} + +void ChartItem::setXAxisSettings(QObject *axis) +{ + AxisData *data = static_cast(axis); + if (data) { + m_xAxisData->copy(data); + } +} + +AxisData *ChartItem::xAxisData() +{ + return m_xAxisData; +} + +AxisData *ChartItem::yAxisData() +{ + return m_yAxisData; +} + +void ChartItem::showAxisEditorDialog(bool isXAxis) +{ + showDialog(new ChartAxisEditor(this, page(), isXAxis, settings())); +} + BaseDesignIntf *ChartItem::createSameTypeItem(QObject *owner, QGraphicsItem *parent) { ChartItem* result = new ChartItem(owner,parent); @@ -289,10 +334,7 @@ void ChartItem::fillLabels(IDataSource *dataSource) QWidget *ChartItem::defaultEditor() { - QSettings* l_settings = (page()->settings() != 0) ? - page()->settings() : - (page()->reportEditor()!=0) ? page()->reportEditor()->settings() : 0; - QWidget* editor = new ChartItemEditor(this, page(), l_settings); + QWidget* editor = new ChartItemEditor(this, page(), settings()); editor->setAttribute(Qt::WA_DeleteOnClose); return editor; } @@ -302,6 +344,18 @@ bool ChartItem::isNeedUpdateSize(RenderPass pass) const return pass == FirstPass && m_isEmpty; } +QSettings *ChartItem::settings() +{ + PageDesignIntf *page = this->page(); + if (page->settings()) { + return page->settings(); + } + if (page->reportEditor()) { + return page->reportEditor()->settings(); + } + return 0; +} + bool ChartItem::showLegend() const { return m_showLegend; @@ -724,56 +778,58 @@ AbstractSeriesChart::AbstractSeriesChart(ChartItem *chartItem) qreal AbstractSeriesChart::maxValue() { - return m_yAxisData.maxValue(); + return m_chartItem->yAxisData()->maxValue(); } qreal AbstractSeriesChart::minValue() { - return m_yAxisData.minValue(); + return m_chartItem->yAxisData()->minValue(); } -AxisData AbstractSeriesChart::yAxisData() +AxisData &AbstractSeriesChart::xAxisData() const { - return m_yAxisData; + return *m_chartItem->xAxisData(); } -AxisData AbstractSeriesChart::xAxisData() +AxisData &AbstractSeriesChart::yAxisData() const { - return m_xAxisData; + return *m_chartItem->yAxisData(); } void AbstractSeriesChart::updateMinAndMaxValues() { - qreal maxYValue = 0; - qreal minYValue = 0; - qreal maxXValue = 0; - qreal minXValue = 0; if (m_chartItem->itemMode() == DesignMode) { - maxYValue = 40; - maxXValue = 40; - } else { - for (SeriesItem* series : m_chartItem->series()){ - for (qreal value : series->data()->values()){ - minYValue = std::min(minYValue, value); - maxYValue = std::max(maxYValue, value); - } - if (series->data()->xAxisValues().isEmpty()) { - // Grid plot starts from 0 on x axis so x range must be decresed by 1 - const bool startingFromZero = m_chartItem->chartType() == ChartItem::GridLines; - const qreal valuesCount = this->valuesCount() - (startingFromZero ? 1 : 0); - minXValue = std::min(0.0, minXValue); - maxXValue = std::max(valuesCount, maxXValue); - } else { - for (qreal value : series->data()->xAxisValues()){ - minXValue = std::min(value, minXValue); - maxXValue = std::max(value, maxXValue); - } + m_chartItem->xAxisData()->updateForDesignMode(); + m_chartItem->yAxisData()->updateForDesignMode(); + return; + } + + qreal maxYValue = 0; + qreal minYValue = std::numeric_limits::max(); + qreal maxXValue = 0; + qreal minXValue = std::numeric_limits::max(); + + for (SeriesItem* series : m_chartItem->series()){ + for (qreal value : series->data()->values()){ + minYValue = std::min(minYValue, value); + maxYValue = std::max(maxYValue, value); + } + if (series->data()->xAxisValues().isEmpty()) { + // Grid plot starts from 0 on x axis so x range must be decresed by 1 + const bool startingFromZero = m_chartItem->chartType() == ChartItem::GridLines; + const qreal valuesCount = this->valuesCount() - (startingFromZero ? 1 : 0); + minXValue = std::min(0.0, minXValue); + maxXValue = std::max(valuesCount, maxXValue); + } else { + for (qreal value : series->data()->xAxisValues()){ + minXValue = std::min(value, minXValue); + maxXValue = std::max(value, maxXValue); } } } - m_yAxisData = AxisData(minYValue, maxYValue); - m_xAxisData = AxisData(minXValue, maxXValue); + m_chartItem->xAxisData()->update(minXValue, maxXValue); + m_chartItem->yAxisData()->update(minYValue, maxYValue); } qreal AbstractSeriesChart::hPadding(QRectF chartRect) @@ -977,7 +1033,7 @@ void AbstractSeriesChart::paintGrid(QPainter *painter, QRectF gridRect) painter->save(); const AxisData &yAxisData = this->yAxisData(); - const AxisData &xAxisData = this->xAxisData(); + AxisData &xAxisData = this->xAxisData(); painter->setRenderHint(QPainter::Antialiasing,false); @@ -1035,7 +1091,6 @@ void AbstractSeriesChart::paintGrid(QPainter *painter, QRectF gridRect) text); } } - painter->restore(); } @@ -1121,7 +1176,12 @@ QString AbstractSeriesChart::axisLabel(int i, const AxisData &axisData) { const qreal min = axisData.rangeMin(); const qreal step = axisData.step(); - qreal value = min + i * step; + qreal value = 0; + if (axisData.type() == AxisData::YAxis && axisData.reverseDirection() && min >= 0) { + value = min + (axisData.segmentCount() - i) * step; + } else { + value = min + i * step; + } if (std::floor(step) == step) { return QString::number(value); } diff --git a/limereport/items/lrchartitem.h b/limereport/items/lrchartitem.h index 9c0b05f..8194f64 100644 --- a/limereport/items/lrchartitem.h +++ b/limereport/items/lrchartitem.h @@ -97,8 +97,8 @@ class AbstractSeriesChart: public AbstractChart{ public: AbstractSeriesChart(ChartItem* chartItem); protected: - AxisData yAxisData(); - AxisData xAxisData(); + AxisData &xAxisData() const; + AxisData &yAxisData() const; qreal maxValue(); qreal minValue(); void updateMinAndMaxValues(); @@ -125,7 +125,6 @@ private: bool calculateLegendColumnWidths(qreal indicatorWidth, qreal maxWidth, const QFontMetrics &fm); bool calculateLegendSingleColumnWidth(qreal ¤tRowWidth, int ¤tColumn, int &maxColumnCount, const qreal itemWidth, const qreal maxRowWidth); - AxisData m_yAxisData, m_xAxisData; qreal m_designValues [9]; }; @@ -146,6 +145,8 @@ private: class ChartItem : public LimeReport::ItemDesignIntf { Q_OBJECT + Q_PROPERTY(QObject* xAxisSettings READ xAxisSettings WRITE setXAxisSettings) + Q_PROPERTY(QObject* yAxisSettings READ yAxisSettings WRITE setYAxisSettings) Q_PROPERTY(ACollectionProperty series READ fakeCollectionReader WRITE setSeries) Q_PROPERTY(QString datasource READ datasource WRITE setDatasource) Q_PROPERTY(QString chartTitle READ chartTitle WRITE setChartTitle) @@ -201,6 +202,16 @@ public: ~ChartItem(); virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + QObject* xAxisSettings(); + void setYAxisSettings(QObject *axis); + QObject* yAxisSettings(); + void setXAxisSettings(QObject *axis); + + AxisData *xAxisData(); + AxisData *yAxisData(); + + void showAxisEditorDialog(bool isXAxis); + QList &series(); void setSeries(const QList &series); bool isSeriesExists(const QString& name); @@ -255,6 +266,8 @@ public: void setTitleFont(QFont value); void setCharItemFont(QFont value); + QSettings *settings(); + protected: void paintChartTitle(QPainter* painter, QRectF titleRect); virtual BaseDesignIntf* createSameTypeItem(QObject *owner, QGraphicsItem *parent); @@ -287,6 +300,7 @@ private: bool m_horizontalAxisOnTop; GridChartLines m_gridChartLines; LegendStyle m_legendStyle; + AxisData *m_xAxisData, *m_yAxisData; }; } //namespace LimeReport #endif // LRCHARTITEM_H diff --git a/limereport/limereport.pri b/limereport/limereport.pri index 4ed78f6..aca7167 100644 --- a/limereport/limereport.pri +++ b/limereport/limereport.pri @@ -47,6 +47,7 @@ SOURCES += \ $$REPORT_PATH/items/lrabstractlayout.cpp \ $$REPORT_PATH/items/lrchartitem.cpp \ $$REPORT_PATH/items/lrchartitemeditor.cpp \ + $$REPORT_PATH/items/lrchartaxiseditor.cpp \ $$REPORT_PATH/items/charts/lrhorizontalbarchart.cpp \ $$REPORT_PATH/items/charts/lrlineschart.cpp \ $$REPORT_PATH/items/charts/lrgridlineschart.cpp \ @@ -131,6 +132,7 @@ HEADERS += \ $$REPORT_PATH/items/lrabstractlayout.h \ $$REPORT_PATH/items/lrchartitem.h \ $$REPORT_PATH/items/lrchartitemeditor.h \ + $$REPORT_PATH/items/lrchartaxiseditor.h \ $$REPORT_PATH/items/charts/lrhorizontalbarchart.h \ $$REPORT_PATH/items/charts/lrlineschart.h \ $$REPORT_PATH/items/charts/lrgridlineschart.h \ @@ -195,6 +197,7 @@ FORMS += \ $$REPORT_PATH/lraboutdialog.ui \ $$REPORT_PATH/lrsettingdialog.ui \ $$REPORT_PATH/items/lrchartitemeditor.ui \ + $$REPORT_PATH/items/lrchartaxiseditor.ui \ $$REPORT_PATH/items/lrimageitemeditor.ui \ $$REPORT_PATH/scripteditor/lrscripteditor.ui diff --git a/limereport/lraxisdata.cpp b/limereport/lraxisdata.cpp index fbe7f2a..692003c 100644 --- a/limereport/lraxisdata.cpp +++ b/limereport/lraxisdata.cpp @@ -1,21 +1,84 @@ #include "lraxisdata.h" -namespace LimeReport { -AxisData::AxisData() - : m_rangeMin(0), m_rangeMax(0), - m_minValue(0), m_maxValue(0), m_step(0), - m_delta(0), m_segmentCount(4) -{ +#include +#include +namespace LimeReport { +AxisData::AxisData(AxisType type, QObject *parent) + : QObject(parent), m_rangeMin(0), m_rangeMax(0), + m_minValue(0), m_maxValue(0), m_step(0), + m_delta(0), m_segmentCount(4), m_calculateAxisScale(false), + m_reverseDirection(false), m_manualMaximum(0), + m_manualMinimum(0), m_manualStep(0), m_isMaximumAutomatic(true), + m_isMinimumAutomatic(true), m_isStepAutomatic(true), + m_type(type) +{ } -AxisData::AxisData(qreal minValue, qreal maxValue) - : AxisData() +QString AxisData::toString() const +{ + // Just for debug purposes + QString str; + QTextStream stream(&str); + stream << "{ " + << "min: " << m_minValue << ", max: " << m_maxValue << ", step: " << m_step + << ", range min: " << m_rangeMin << ", range max: " << m_rangeMax << ", segments: " << m_segmentCount + << ", reverseDiection: " << m_reverseDirection << ", calculateAxisScale: " << m_calculateAxisScale + << ", manualMaxEnabled: " << !m_isMaximumAutomatic << ", manualMinEnabled: " << !m_isMinimumAutomatic + << ", manualStepEnabled: " << !m_isStepAutomatic << ", manualMax: " << m_manualMaximum + << ", manualMin: " << m_manualMinimum << ", manualStep: " << m_manualStep + << " }"; + return str; +} + +void AxisData::copy(AxisData *other) +{ + m_calculateAxisScale = other->calculateAxisScale(); + m_reverseDirection = other->reverseDirection(); + m_manualMaximum = other->manualMaximum(); + m_manualMinimum = other->manualMinimum(); + m_manualStep = other->manualStep(); + m_isMaximumAutomatic = other->isMaximumAutomatic(); + m_isMinimumAutomatic = other->isMinimumAutomatic(); + m_isStepAutomatic = other->isStepAutomatic(); +} + +void AxisData::update() +{ + if (m_calculateAxisScale) { + calculateRoundedAxisScale(); + } else { + calculateSimpleAxisScale(); + } + m_delta = m_step * m_segmentCount; + + // Update manual values if they are automatic + if (m_isStepAutomatic) { + m_manualStep = m_step; + } + if (m_isMinimumAutomatic) { + m_manualMinimum = m_rangeMin; + } + if (m_isMaximumAutomatic) { + m_manualMaximum = m_rangeMax; + } +} + +void AxisData::update(qreal minValue, qreal maxValue) { m_minValue = minValue; m_maxValue = maxValue; - calculateValuesAboveMax(minValue, maxValue, 4); - m_delta = m_step * m_segmentCount; + update(); +} + +void AxisData::updateForDesignMode() +{ + m_minValue = 0; + m_maxValue = 40; + const bool tmp = m_calculateAxisScale; + m_calculateAxisScale = false; + update(); + m_calculateAxisScale = tmp; } int AxisData::segmentCount() const @@ -23,6 +86,11 @@ int AxisData::segmentCount() const return m_segmentCount; } +bool AxisData::calculateAxisScale() const +{ + return m_calculateAxisScale; +} + qreal AxisData::rangeMin() const { return m_rangeMin; @@ -53,16 +121,229 @@ qreal AxisData::delta() const return m_delta; } -void AxisData::calculateValuesAboveMax(qreal minValue, qreal maxValue, int segments) +void AxisData::calculateRoundedAxisScale() { - const int delta = maxValue - minValue; + const int maximumSegmentCount = 10; + + bool calculateStep = isStepAutomatic(); + const bool calculateMinimum = isMinimumAutomatic(); + const bool calculateMaximum = isMaximumAutomatic(); + + qreal temporaryMin = 0; + qreal temporaryMax = 0; + if (calculateMinimum) { + temporaryMin = qMin(0.0, minValue()); + } else { + temporaryMin = qMin(manualMinimum(), minValue()); + } + if (calculateMaximum) { + temporaryMax = maxValue(); + } else { + temporaryMax = qMax(manualMaximum(), maxValue()); + } + m_step = calculateStep ? 0 : manualStep(); + + if (temporaryMax == temporaryMin) { + if (temporaryMax == 0) { + temporaryMax = 1; + } else { + temporaryMax *= 2; + } + } + + const qreal minAndMaxSpacingOffset = 0.95; + + qreal stepMagnitude = 0.0; + qreal normalizedStep = 0.0; + bool isStepNormalized = false; + bool isLoopFinished = false; + + // Calculate until segment count is below maximum + while( !isLoopFinished ) { + if (calculateStep) { + if(isStepNormalized) { + if( normalizedStep == 1.0 ) { + normalizedStep = 2.0; + } else if( normalizedStep == 2.0 ) { + normalizedStep = 5.0; + } else { + normalizedStep = 1.0; + stepMagnitude *= 10; + } + } else { + const double startingStep = (temporaryMax - temporaryMin) / maximumSegmentCount; + const int exponent = static_cast< int >( floor( log10( startingStep ) ) ); + stepMagnitude = pow(10.0, static_cast(exponent)); + normalizedStep = startingStep / stepMagnitude; + if( normalizedStep <= 1.0 ) { + normalizedStep = 1.0; + } else if( normalizedStep <= 2.0 ) { + normalizedStep = 2.0; + } else if( normalizedStep <= 5.0 ) { + normalizedStep = 5.0; + } else { + normalizedStep = 1.0; + stepMagnitude *= 10; + } + isStepNormalized = true; + } + m_step = normalizedStep * stepMagnitude; + } + + qreal currentAxisMinimum = temporaryMin; + qreal currentAxisMaximum = temporaryMax; + + if (calculateMinimum) { + currentAxisMinimum = calculateNewMinimum(currentAxisMinimum, m_step); + const qreal currentDelta = currentAxisMaximum - currentAxisMinimum; + const qreal actualDelta = currentAxisMaximum - minValue(); + if ((currentAxisMinimum != 0.0) && ((actualDelta / currentDelta) > minAndMaxSpacingOffset)) { + currentAxisMinimum -= m_step; + } + } + + if (calculateMaximum) { + currentAxisMaximum = calculateNewMaximum(currentAxisMaximum, m_step); + const qreal currentDelta = currentAxisMaximum - currentAxisMinimum; + const qreal actualDelta = maxValue() - currentAxisMinimum; + if ((currentAxisMaximum != 0.0) && ((actualDelta / currentDelta) > minAndMaxSpacingOffset)) { + currentAxisMaximum += m_step; + } + } + + m_segmentCount = static_cast(round((currentAxisMaximum - currentAxisMinimum) / m_step)); + m_rangeMin = currentAxisMinimum; + m_rangeMax = currentAxisMaximum; + // Check also if step is correctly calucalted. It is possible for float steps that + // there might be a difference. Recalculate the step in that case. + const qreal tmpStep = (m_rangeMax - m_rangeMin) / m_segmentCount; + isLoopFinished = m_segmentCount <= maximumSegmentCount && qFuzzyCompare(tmpStep, m_step); + if (!isLoopFinished) { + // Configured step may be invalid, calculating it automatically + calculateStep = true; + } + } +} + +void AxisData::calculateSimpleAxisScale() +{ + qreal min = 0; + if (m_minValue < 0) { + min = minValue(); + } + m_segmentCount = 4; + const int delta = maxValue() - min; int max = delta; - while (max % segments != 0){ + while (max % m_segmentCount != 0){ max++; } - m_rangeMax = max; - m_step = max / segments; - m_rangeMin = minValue; - m_segmentCount = segments; + m_rangeMax = minValue() + max; + m_step = max / m_segmentCount; + m_rangeMin = minValue(); } + +double AxisData::calculateNewMinimum(qreal min, qreal step) const +{ + if (step <= 0.0) + return min; + + double ret = floor(min / step) * step; + if (ret > min && !qFuzzyCompare(ret, min)) { + ret -= step; + } + return ret; +} + +double AxisData::calculateNewMaximum(qreal max, qreal step) const +{ + if (step <= 0.0) + return max; + + double ret = floor(max / step) * step; + if (ret < max && !qFuzzyCompare(ret, max)) { + ret += step; + } + return ret; +} + +void AxisData::setCalculateAxisScale(bool newCalculateAxisScale) +{ + m_calculateAxisScale = newCalculateAxisScale; +} + +bool AxisData::reverseDirection() const +{ + return m_reverseDirection; +} + +void AxisData::setReverseDirection(bool reverseDirection) +{ + m_reverseDirection = reverseDirection; +} + +qreal AxisData::manualMaximum() const +{ + return m_manualMaximum; +} + +void AxisData::setManualMaximum(qreal newManualMaximum) +{ + m_manualMaximum = newManualMaximum; +} + +qreal AxisData::manualMinimum() const +{ + return m_manualMinimum; +} + +void AxisData::setManualMinimum(qreal newManualMinimum) +{ + m_manualMinimum = newManualMinimum; +} + +qreal AxisData::manualStep() const +{ + return m_manualStep; +} + +void AxisData::setManualStep(qreal newManualStep) +{ + m_manualStep = newManualStep; +} + +bool AxisData::isMaximumAutomatic() const +{ + return m_isMaximumAutomatic; +} + +void AxisData::setIsMaximumAutomatic(bool newIsMaximumAutomatic) +{ + m_isMaximumAutomatic = newIsMaximumAutomatic; +} + +bool AxisData::isMinimumAutomatic() const +{ + return m_isMinimumAutomatic; +} + +void AxisData::setIsMinimumAutomatic(bool newIsMinimumAutomatic) +{ + m_isMinimumAutomatic = newIsMinimumAutomatic; +} + +bool AxisData::isStepAutomatic() const +{ + return m_isStepAutomatic; +} + +void AxisData::setIsStepAutomatic(bool newIsStepAutomatic) +{ + m_isStepAutomatic = newIsStepAutomatic; +} + +AxisData::AxisType AxisData::type() const +{ + return m_type; +} + } diff --git a/limereport/lraxisdata.h b/limereport/lraxisdata.h index 820eeb1..d7342a4 100644 --- a/limereport/lraxisdata.h +++ b/limereport/lraxisdata.h @@ -1,14 +1,33 @@ #ifndef AXISDATA_H #define AXISDATA_H -#include +#include namespace LimeReport { -class AxisData +class AxisData : public QObject { + Q_OBJECT + Q_PROPERTY(bool reverseDirection READ reverseDirection WRITE setReverseDirection) + Q_PROPERTY(bool calculateAxisScale READ calculateAxisScale WRITE setCalculateAxisScale) + Q_PROPERTY(bool isStepAutomatic READ isStepAutomatic WRITE setIsStepAutomatic) + Q_PROPERTY(bool isMinimumAutomatic READ isMinimumAutomatic WRITE setIsMinimumAutomatic) + Q_PROPERTY(bool isMaximumAutomatic READ isMaximumAutomatic WRITE setIsMaximumAutomatic) + Q_PROPERTY(qreal manualStep READ manualStep WRITE setManualStep) + Q_PROPERTY(qreal manualMinimum READ manualMinimum WRITE setManualMinimum) + Q_PROPERTY(qreal manualMaximum READ manualMaximum WRITE setManualMaximum) public: - AxisData(); - AxisData(qreal minValue, qreal maxValue); + enum AxisType { + YAxis = 0, + XAxis = 1 + }; + + AxisData(AxisType type, QObject *parent = nullptr); + + QString toString() const; + void copy(AxisData *other); + void update(); + void update(qreal minValue, qreal maxValue); + void updateForDesignMode(); int segmentCount() const; @@ -21,8 +40,36 @@ public: qreal delta() const; + bool reverseDirection() const; + void setReverseDirection(bool newReverseDirection); + bool calculateAxisScale() const; + void setCalculateAxisScale(bool newCalculateAxisScale); + + qreal manualMaximum() const; + void setManualMaximum(qreal newManualMaximum); + + qreal manualMinimum() const; + void setManualMinimum(qreal newManualMinimum); + + qreal manualStep() const; + void setManualStep(qreal newManualStep); + + bool isMaximumAutomatic() const; + void setIsMaximumAutomatic(bool newIsMaximumAutomatic); + + bool isMinimumAutomatic() const; + void setIsMinimumAutomatic(bool newIsMinimumAutomatic); + + bool isStepAutomatic() const; + void setIsStepAutomatic(bool newIsStepAutomatic); + + AxisType type() const; + private: - void calculateValuesAboveMax(qreal minValue, qreal maxValue, int segments); + void calculateRoundedAxisScale(); + void calculateSimpleAxisScale(); + qreal calculateNewMinimum(qreal min, qreal step) const; + qreal calculateNewMaximum(qreal max, qreal step) const; qreal m_rangeMin; qreal m_rangeMax; @@ -31,6 +78,15 @@ private: qreal m_step; qreal m_delta; int m_segmentCount; + bool m_calculateAxisScale; + bool m_reverseDirection; + qreal m_manualMaximum; + qreal m_manualMinimum; + qreal m_manualStep; + bool m_isMaximumAutomatic; + bool m_isMinimumAutomatic; + bool m_isStepAutomatic; + const AxisType m_type; }; }; diff --git a/limereport/lrbasedesignintf.cpp b/limereport/lrbasedesignintf.cpp index 8d35fab..9b93395 100644 --- a/limereport/lrbasedesignintf.cpp +++ b/limereport/lrbasedesignintf.cpp @@ -1290,32 +1290,41 @@ void BaseDesignIntf::setItemPos(const QPointF &newPos) } -QWidget* findRootWidget(QWidget* widget){ +QWidget* BaseDesignIntf::findRootWidget(QWidget* widget) +{ while (widget->parentWidget()) { widget = widget->parentWidget(); } return widget; } -void BaseDesignIntf::showEditorDialog(){ - QWidget *editor = defaultEditor(); - if (editor) { - editor->setStyleSheet(findRootWidget(scene()->views().at(0))->styleSheet()); - QDialog* dialog = new QDialog(QApplication::activeWindow()); - dialog->setAttribute(Qt::WA_DeleteOnClose); -#ifdef Q_OS_MAC - dialog->setWindowModality(Qt::WindowModal); -#else - dialog->setWindowModality(Qt::ApplicationModal); -#endif - dialog->setLayout(new QVBoxLayout()); - dialog->resize(editor->size()); - dialog->layout()->setContentsMargins(2,2,2,2); - dialog->layout()->addWidget(editor); - connect(editor,SIGNAL(destroyed()),dialog,SLOT(close())); - dialog->setWindowTitle(editor->windowTitle()); - dialog->exec(); +void BaseDesignIntf::showDialog(QWidget *widget) +{ + if (!widget) { + return; } + widget->setStyleSheet(findRootWidget(scene()->views().at(0))->styleSheet()); + QDialog *dialog = new QDialog(QApplication::activeWindow()); + widget->setParent(dialog); + widget->setAttribute(Qt::WA_DeleteOnClose); +#ifdef Q_OS_MAC + dialog->setWindowModality(Qt::WindowModal); +#else + dialog->setWindowModality(Qt::ApplicationModal); +#endif + dialog->setLayout(new QVBoxLayout()); + dialog->resize(widget->size()); + dialog->layout()->setContentsMargins(2,2,2,2); + dialog->layout()->addWidget(widget); + connect(widget,SIGNAL(destroyed()),dialog,SLOT(close())); + dialog->setWindowTitle(widget->windowTitle()); + dialog->exec(); + dialog->deleteLater(); +} + +void BaseDesignIntf::showEditorDialog() +{ + showDialog(defaultEditor()); } void BaseDesignIntf::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) diff --git a/limereport/lrbasedesignintf.h b/limereport/lrbasedesignintf.h index 511afce..c311f16 100644 --- a/limereport/lrbasedesignintf.h +++ b/limereport/lrbasedesignintf.h @@ -397,6 +397,9 @@ protected: qreal calcAbsolutePosY(qreal currentOffset, BaseDesignIntf* item); qreal calcAbsolutePosX(qreal currentOffset, BaseDesignIntf* item); + QWidget* findRootWidget(QWidget* widget); + void showDialog(QWidget *widget); + private: int resizeDirectionFlags(QPointF position); void moveSelectedItems(QPointF delta); diff --git a/limereport/objectinspector/propertyItems/lraxispropitem.cpp b/limereport/objectinspector/propertyItems/lraxispropitem.cpp new file mode 100644 index 0000000..40466c4 --- /dev/null +++ b/limereport/objectinspector/propertyItems/lraxispropitem.cpp @@ -0,0 +1,57 @@ +#include "lraxispropitem.h" + +#include + +#include +#include +#include + +namespace { +LimeReport::ObjectPropItem * createYAxisPropItem( + QObject *object, LimeReport::ObjectPropItem::ObjectsList* objects, const QString& name, const QString& displayName, const QVariant& data, LimeReport::ObjectPropItem* parent, bool readonly) +{ + return new LimeReport::AxisPropItem(object, objects, name, displayName, data, parent, readonly, false); +} + +LimeReport::ObjectPropItem * createXAxisPropItem( + QObject *object, LimeReport::ObjectPropItem::ObjectsList* objects, const QString& name, const QString& displayName, const QVariant& data, LimeReport::ObjectPropItem* parent, bool readonly) +{ + return new LimeReport::AxisPropItem(object, objects, name, displayName, data, parent, readonly, true); +} +bool VARIABLE_IS_NOT_USED registredXAxisProp = LimeReport::ObjectPropFactory::instance().registerCreator(LimeReport::APropIdent("xAxisSettings", "LimeReport::ChartItem"), QObject::tr("X axis"), createXAxisPropItem); +bool VARIABLE_IS_NOT_USED registredYAxisProp = LimeReport::ObjectPropFactory::instance().registerCreator(LimeReport::APropIdent("yAxisSettings", "LimeReport::ChartItem"), QObject::tr("Y axis"), createYAxisPropItem); +} + +namespace LimeReport { + +QWidget *AxisPropItem::createProperyEditor(QWidget *parent) const +{ + return new AxisPropEditor(qobject_cast(object()), m_isXAxis, parent); +} + +QString AxisPropItem::displayValue() const +{ + return QObject::tr("Axis"); +} + +AxisPropEditor::AxisPropEditor(ChartItem *chart, bool isXAxis, QWidget *parent) + : QWidget(parent), m_button(new QPushButton(this)), m_chart(chart), m_isXAxis(isXAxis) +{ + m_button->setText("..."); + QHBoxLayout* layout = new QHBoxLayout(this); + layout->addWidget(m_button); + layout->setSpacing(1); + layout->setContentsMargins(1,0,1,1); + setLayout(layout); + setFocusProxy(m_button); + setAutoFillBackground(true); + connect(m_button,SIGNAL(clicked()),this,SLOT(slotButtonClicked())); +} + +void AxisPropEditor::slotButtonClicked() +{ + m_chart->showAxisEditorDialog(m_isXAxis); + emit editingFinished(); +} + +} diff --git a/limereport/objectinspector/propertyItems/lraxispropitem.h b/limereport/objectinspector/propertyItems/lraxispropitem.h new file mode 100644 index 0000000..93fc08d --- /dev/null +++ b/limereport/objectinspector/propertyItems/lraxispropitem.h @@ -0,0 +1,43 @@ +#ifndef AXISPROPITEM_H +#define AXISPROPITEM_H + +#include +#include +#include +#include + + +namespace LimeReport { + +class AxisPropEditor : public QWidget +{ + Q_OBJECT +public: + AxisPropEditor(ChartItem* chart, bool isXAxis, QWidget *parent = 0); +signals: + void editingFinished(); +private slots: + void slotButtonClicked(); +private: + QPushButton* m_button; + ChartItem* m_chart; + bool m_isXAxis; +}; + +class AxisPropItem: public LimeReport::ObjectPropItem +{ + Q_OBJECT +public: + AxisPropItem():ObjectPropItem(){} + AxisPropItem(QObject* object, ObjectsList* objects, const QString& name, const QString& displayName, const QVariant& value, ObjectPropItem* parent, bool readonly, bool isXAxis) + :ObjectPropItem(object, objects, name, displayName, value, parent, readonly), m_isXAxis(isXAxis){} + QWidget* createProperyEditor(QWidget *parent) const; + QString displayValue() const; + +private: + bool m_isXAxis = false; +}; + +} // namespace LimeReport + +#endif // AXISPROPITEM_H