diff options
Diffstat (limited to 'src/charts/axis/chartaxiselement.cpp')
-rw-r--r-- | src/charts/axis/chartaxiselement.cpp | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/src/charts/axis/chartaxiselement.cpp b/src/charts/axis/chartaxiselement.cpp new file mode 100644 index 00000000..b7a60d27 --- /dev/null +++ b/src/charts/axis/chartaxiselement.cpp @@ -0,0 +1,404 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Enterprise Charts Add-on. +** +** $QT_BEGIN_LICENSE$ +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "chartaxiselement_p.h" +#include "qabstractaxis_p.h" +#include "chartpresenter_p.h" +#include "abstractchartlayout_p.h" +#include <qmath.h> +#include <QDateTime> +#include <QTextDocument> + +QT_CHARTS_BEGIN_NAMESPACE + +static const char *labelFormatMatchString = "%[\\-\\+#\\s\\d\\.lhjztL]*([dicuoxfegXFEG])"; +static const char *labelFormatMatchLocalizedString = "^([^%]*)%\\.(\\d+)([defgiEG])(.*)$"; +static QRegExp *labelFormatMatcher = 0; +static QRegExp *labelFormatMatcherLocalized = 0; +class StaticLabelFormatMatcherDeleter +{ +public: + StaticLabelFormatMatcherDeleter() {} + ~StaticLabelFormatMatcherDeleter() { + delete labelFormatMatcher; + delete labelFormatMatcherLocalized; + } +}; +static StaticLabelFormatMatcherDeleter staticLabelFormatMatcherDeleter; + +ChartAxisElement::ChartAxisElement(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis) + : ChartElement(item), + m_axis(axis), + m_animation(0), + m_grid(new QGraphicsItemGroup(item)), + m_arrow(new QGraphicsItemGroup(item)), + m_shades(new QGraphicsItemGroup(item)), + m_labels(new QGraphicsItemGroup(item)), + m_title(new QGraphicsTextItem(item)), + m_intervalAxis(intervalAxis) + +{ + //initial initialization + m_arrow->setHandlesChildEvents(false); + m_arrow->setZValue(ChartPresenter::AxisZValue); + m_labels->setZValue(ChartPresenter::AxisZValue); + m_shades->setZValue(ChartPresenter::ShadesZValue); + m_grid->setZValue(ChartPresenter::GridZValue); + m_title->setZValue(ChartPresenter::GridZValue); + m_title->document()->setDocumentMargin(ChartPresenter::textMargin()); + handleVisibleChanged(axis->isVisible()); + connectSlots(); + + setFlag(QGraphicsItem::ItemHasNoContents, true); +} + +ChartAxisElement::~ChartAxisElement() +{ +} + +void ChartAxisElement::connectSlots() +{ + QObject::connect(axis(), SIGNAL(visibleChanged(bool)), this, SLOT(handleVisibleChanged(bool))); + QObject::connect(axis(), SIGNAL(lineVisibleChanged(bool)), this, SLOT(handleArrowVisibleChanged(bool))); + QObject::connect(axis(), SIGNAL(gridVisibleChanged(bool)), this, SLOT(handleGridVisibleChanged(bool))); + QObject::connect(axis(), SIGNAL(labelsVisibleChanged(bool)), this, SLOT(handleLabelsVisibleChanged(bool))); + QObject::connect(axis(), SIGNAL(shadesVisibleChanged(bool)), this, SLOT(handleShadesVisibleChanged(bool))); + QObject::connect(axis(), SIGNAL(labelsAngleChanged(int)), this, SLOT(handleLabelsAngleChanged(int))); + QObject::connect(axis(), SIGNAL(linePenChanged(const QPen&)), this, SLOT(handleArrowPenChanged(const QPen&))); + QObject::connect(axis(), SIGNAL(labelsPenChanged(const QPen&)), this, SLOT(handleLabelsPenChanged(const QPen&))); + QObject::connect(axis(), SIGNAL(labelsBrushChanged(const QBrush&)), this, SLOT(handleLabelsBrushChanged(const QBrush&))); + QObject::connect(axis(), SIGNAL(labelsFontChanged(const QFont&)), this, SLOT(handleLabelsFontChanged(const QFont&))); + QObject::connect(axis(), SIGNAL(gridLinePenChanged(const QPen&)), this, SLOT(handleGridPenChanged(const QPen&))); + QObject::connect(axis(), SIGNAL(shadesPenChanged(const QPen&)), this, SLOT(handleShadesPenChanged(const QPen&))); + QObject::connect(axis(), SIGNAL(shadesBrushChanged(const QBrush&)), this, SLOT(handleShadesBrushChanged(const QBrush&))); + QObject::connect(axis(), SIGNAL(titleTextChanged(const QString&)), this, SLOT(handleTitleTextChanged(const QString&))); + QObject::connect(axis(), SIGNAL(titleFontChanged(const QFont&)), this, SLOT(handleTitleFontChanged(const QFont&))); + QObject::connect(axis(), SIGNAL(titlePenChanged(const QPen&)), this, SLOT(handleTitlePenChanged(const QPen&))); + QObject::connect(axis(), SIGNAL(titleBrushChanged(const QBrush&)), this, SLOT(handleTitleBrushChanged(const QBrush&))); + QObject::connect(axis(), SIGNAL(titleVisibleChanged(bool)), this, SLOT(handleTitleVisibleChanged(bool))); + QObject::connect(axis()->d_ptr.data(), SIGNAL(rangeChanged(qreal, qreal)), this, SLOT(handleRangeChanged(qreal, qreal))); +} + +void ChartAxisElement::handleArrowVisibleChanged(bool visible) +{ + m_arrow->setVisible(visible); +} + +void ChartAxisElement::handleGridVisibleChanged(bool visible) +{ + m_grid->setVisible(visible); +} + +void ChartAxisElement::handleLabelsVisibleChanged(bool visible) +{ + QGraphicsLayoutItem::updateGeometry(); + presenter()->layout()->invalidate(); + m_labels->setVisible(visible); +} + +void ChartAxisElement::handleShadesVisibleChanged(bool visible) +{ + m_shades->setVisible(visible); +} + +void ChartAxisElement::handleTitleVisibleChanged(bool visible) +{ + QGraphicsLayoutItem::updateGeometry(); + presenter()->layout()->invalidate(); + m_title->setVisible(visible); +} + +void ChartAxisElement::handleLabelsAngleChanged(int angle) +{ + foreach (QGraphicsItem *item, m_labels->childItems()) + item->setRotation(angle); + + QGraphicsLayoutItem::updateGeometry(); + presenter()->layout()->invalidate(); +} + +void ChartAxisElement::handleLabelsPenChanged(const QPen &pen) +{ + Q_UNUSED(pen) +} + +void ChartAxisElement::handleLabelsBrushChanged(const QBrush &brush) +{ + foreach (QGraphicsItem *item, m_labels->childItems()) + static_cast<QGraphicsTextItem *>(item)->setDefaultTextColor(brush.color()); +} + +void ChartAxisElement::handleLabelsFontChanged(const QFont &font) +{ + foreach (QGraphicsItem *item, m_labels->childItems()) + static_cast<QGraphicsTextItem *>(item)->setFont(font); + QGraphicsLayoutItem::updateGeometry(); + presenter()->layout()->invalidate(); +} + +void ChartAxisElement::handleTitleTextChanged(const QString &title) +{ + QGraphicsLayoutItem::updateGeometry(); + presenter()->layout()->invalidate(); + if (title.isEmpty() || !m_title->isVisible()) + m_title->setHtml(title); +} + +void ChartAxisElement::handleTitlePenChanged(const QPen &pen) +{ + Q_UNUSED(pen) +} + +void ChartAxisElement::handleTitleBrushChanged(const QBrush &brush) +{ + m_title->setDefaultTextColor(brush.color()); +} + +void ChartAxisElement::handleTitleFontChanged(const QFont &font) +{ + if (m_title->font() != font) { + m_title->setFont(font); + QGraphicsLayoutItem::updateGeometry(); + presenter()->layout()->invalidate(); + } +} + +void ChartAxisElement::handleVisibleChanged(bool visible) +{ + setVisible(visible); + if (!visible) { + m_grid->setVisible(visible); + m_arrow->setVisible(visible); + m_shades->setVisible(visible); + m_labels->setVisible(visible); + m_title->setVisible(visible); + } else { + m_grid->setVisible(axis()->isGridLineVisible()); + m_arrow->setVisible(axis()->isLineVisible()); + m_shades->setVisible(axis()->shadesVisible()); + m_labels->setVisible(axis()->labelsVisible()); + m_title->setVisible(axis()->isTitleVisible()); + } + + if (presenter()) presenter()->layout()->invalidate(); +} + +void ChartAxisElement::handleRangeChanged(qreal min, qreal max) +{ + Q_UNUSED(min); + Q_UNUSED(max); + + if (!isEmpty()) { + QVector<qreal> layout = calculateLayout(); + updateLayout(layout); + QSizeF before = effectiveSizeHint(Qt::PreferredSize); + QSizeF after = sizeHint(Qt::PreferredSize); + + if (before != after) { + QGraphicsLayoutItem::updateGeometry(); + // We don't want to call invalidate on layout, since it will change minimum size of + // component, which we would like to avoid since it causes nasty flips when scrolling + // or zooming, instead recalculate layout and use plotArea for extra space. + presenter()->layout()->setGeometry(presenter()->layout()->geometry()); + } + } +} + +bool ChartAxisElement::isEmpty() +{ + return axisGeometry().isEmpty() + || gridGeometry().isEmpty() + || qFuzzyCompare(min(), max()); +} + +qreal ChartAxisElement::min() const +{ + return m_axis->d_ptr->min(); +} + +qreal ChartAxisElement::max() const +{ + return m_axis->d_ptr->max(); +} + +QString ChartAxisElement::formatLabel(const QString &formatSpec, const QByteArray &array, + qreal value, int precision, const QString &preStr, + const QString &postStr) const +{ + QString retVal; + if (!formatSpec.isEmpty()) { + if (formatSpec.at(0) == QLatin1Char('d') + || formatSpec.at(0) == QLatin1Char('i') + || formatSpec.at(0) == QLatin1Char('c')) { + if (presenter()->localizeNumbers()) + retVal = preStr + presenter()->locale().toString(qint64(value)) + postStr; + else + retVal = QString().sprintf(array, qint64(value)); + } else if (formatSpec.at(0) == QLatin1Char('u') + || formatSpec.at(0) == QLatin1Char('o') + || formatSpec.at(0) == QLatin1Char('x') + || formatSpec.at(0) == QLatin1Char('X')) { + // These formats are not supported by localized numbers + retVal = QString().sprintf(array, quint64(value)); + } else if (formatSpec.at(0) == QLatin1Char('f') + || formatSpec.at(0) == QLatin1Char('F') + || formatSpec.at(0) == QLatin1Char('e') + || formatSpec.at(0) == QLatin1Char('E') + || formatSpec.at(0) == QLatin1Char('g') + || formatSpec.at(0) == QLatin1Char('G')) { + if (presenter()->localizeNumbers()) { + retVal = preStr + + presenter()->locale().toString(value, formatSpec.at(0).toLatin1(), + precision) + + postStr; + } else { + retVal = QString().sprintf(array, value); + } + } + } + return retVal; +} + +QStringList ChartAxisElement::createValueLabels(qreal min, qreal max, int ticks, + const QString &format) const +{ + QStringList labels; + + if (max <= min || ticks < 1) + return labels; + + if (format.isNull()) { + int n = qMax(int(-qFloor(log10((max - min) / (ticks - 1)))), 0) + 1; + for (int i = 0; i < ticks; i++) { + qreal value = min + (i * (max - min) / (ticks - 1)); + labels << presenter()->numberToString(value, 'f', n); + } + } else { + QByteArray array = format.toLatin1(); + QString formatSpec; + QString preStr; + QString postStr; + int precision = 6; // Six is the default precision in Qt API + if (presenter()->localizeNumbers()) { + if (!labelFormatMatcherLocalized) + labelFormatMatcherLocalized + = new QRegExp(QString::fromLatin1(labelFormatMatchLocalizedString)); + if (labelFormatMatcherLocalized->indexIn(format, 0) != -1) { + preStr = labelFormatMatcherLocalized->cap(1); + if (!labelFormatMatcherLocalized->cap(2).isEmpty()) + precision = labelFormatMatcherLocalized->cap(2).toInt(); + formatSpec = labelFormatMatcherLocalized->cap(3); + postStr = labelFormatMatcherLocalized->cap(4); + } + } else { + if (!labelFormatMatcher) + labelFormatMatcher = new QRegExp(QString::fromLatin1(labelFormatMatchString)); + if (labelFormatMatcher->indexIn(format, 0) != -1) + formatSpec = labelFormatMatcher->cap(1); + } + for (int i = 0; i < ticks; i++) { + qreal value = min + (i * (max - min) / (ticks - 1)); + labels << formatLabel(formatSpec, array, value, precision, preStr, postStr); + } + } + + return labels; +} + +QStringList ChartAxisElement::createLogValueLabels(qreal min, qreal max, qreal base, int ticks, + const QString &format) const +{ + QStringList labels; + + if (max <= min || ticks < 1) + return labels; + + int firstTick; + if (base > 1) + firstTick = ceil(log10(min) / log10(base)); + else + firstTick = ceil(log10(max) / log10(base)); + + if (format.isNull()) { + int n = 0; + if (ticks > 1) + n = qMax(int(-qFloor(log10((max - min) / (ticks - 1)))), 0); + n++; + for (int i = firstTick; i < ticks + firstTick; i++) { + qreal value = qPow(base, i); + labels << presenter()->numberToString(value, 'f', n); + } + } else { + QByteArray array = format.toLatin1(); + QString formatSpec; + QString preStr; + QString postStr; + int precision = 6; // Six is the default precision in Qt API + if (presenter()->localizeNumbers()) { + if (!labelFormatMatcherLocalized) + labelFormatMatcherLocalized = + new QRegExp(QString::fromLatin1(labelFormatMatchLocalizedString)); + if (labelFormatMatcherLocalized->indexIn(format, 0) != -1) { + preStr = labelFormatMatcherLocalized->cap(1); + if (!labelFormatMatcherLocalized->cap(2).isEmpty()) + precision = labelFormatMatcherLocalized->cap(2).toInt(); + formatSpec = labelFormatMatcherLocalized->cap(3); + postStr = labelFormatMatcherLocalized->cap(4); + } + } else { + if (!labelFormatMatcher) + labelFormatMatcher = new QRegExp(QString::fromLatin1(labelFormatMatchString)); + if (labelFormatMatcher->indexIn(format, 0) != -1) + formatSpec = labelFormatMatcher->cap(1); + } + for (int i = firstTick; i < ticks + firstTick; i++) { + qreal value = qPow(base, i); + labels << formatLabel(formatSpec, array, value, precision, preStr, postStr); + } + } + + return labels; +} + +QStringList ChartAxisElement::createDateTimeLabels(qreal min, qreal max,int ticks, + const QString &format) const +{ + QStringList labels; + + if (max <= min || ticks < 1) + return labels; + + int n = qMax(int(-floor(log10((max - min) / (ticks - 1)))), 0); + n++; + for (int i = 0; i < ticks; i++) { + qreal value = min + (i * (max - min) / (ticks - 1)); + labels << presenter()->locale().toString(QDateTime::fromMSecsSinceEpoch(value), format); + } + return labels; +} + +void ChartAxisElement::axisSelected() +{ + emit clicked(); +} + +#include "moc_chartaxiselement_p.cpp" + +QT_CHARTS_END_NAMESPACE |