##// END OF EJS Templates
Fix crash when using linuxfb and 2D renderer...
Fix crash when using linuxfb and 2D renderer No longer should call any OpenGL functions on QML charts applications if we don't have a valid OpenGL context. Task-number: QTBUG-51387 Change-Id: Ic393f69921e300f19dc42c83fc07677ce7869273 Reviewed-by: Mika Salmela <mika.salmela@theqtcompany.com> Reviewed-by: Miikka Heikkinen <miikka.heikkinen@theqtcompany.com>

File last commit:

r2845:ae12522d475c
r2868:f4ba2fb98402
Show More
chartaxiselement.cpp
448 lines | 16.9 KiB | text/x-c | CppLexer
/ src / charts / axis / chartaxiselement.cpp
/******************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Charts module.
**
** $QT_BEGIN_LICENSE:COMM$
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** $QT_END_LICENSE$
**
******************************************************************************/
#include <private/chartaxiselement_p.h>
#include <private/qabstractaxis_p.h>
#include <private/chartpresenter_p.h>
#include <private/abstractchartlayout_p.h>
#include <QtCharts/QCategoryAxis>
#include <QtCore/QtMath>
#include <QtCore/QDateTime>
#include <QtGui/QTextDocument>
#include <cmath>
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_minorGrid(new QGraphicsItemGroup(item)),
m_minorArrow(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_minorArrow->setHandlesChildEvents(false);
m_minorArrow->setZValue(ChartPresenter::AxisZValue);
m_labels->setZValue(ChartPresenter::AxisZValue);
m_shades->setZValue(ChartPresenter::ShadesZValue);
m_grid->setZValue(ChartPresenter::GridZValue);
m_minorGrid->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(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(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)));
QObject::connect(axis(), SIGNAL(reverseChanged(bool)), this, SLOT(handleReverseChanged(bool)));
QObject::connect(axis(), SIGNAL(lineVisibleChanged(bool)),
this, SLOT(handleMinorArrowVisibleChanged(bool)));
QObject::connect(axis(), SIGNAL(linePenChanged(const QPen&)), this,
SLOT(handleMinorArrowPenChanged(const QPen&)));
QObject::connect(axis(), SIGNAL(minorGridVisibleChanged(bool)),
this, SLOT(handleMinorGridVisibleChanged(bool)));
QObject::connect(axis(), SIGNAL(minorGridLinePenChanged(const QPen&)),
this, SLOT(handleMinorGridPenChanged(const QPen&)));
QObject::connect(axis(), SIGNAL(gridLineColorChanged(const QColor&)),
this, SLOT(handleGridLineColorChanged(const QColor&)));
QObject::connect(axis(), SIGNAL(minorGridLineColorChanged(const QColor&)),
this, SLOT(handleMinorGridLineColorChanged(const QColor&)));
if (axis()->type() == QAbstractAxis::AxisTypeCategory) {
QCategoryAxis *categoryAxis = static_cast<QCategoryAxis *>(axis());
QObject::connect(categoryAxis,
SIGNAL(labelsPositionChanged(QCategoryAxis::AxisLabelsPosition)),
this, SLOT(handleLabelsPositionChanged()));
}
}
void ChartAxisElement::handleArrowVisibleChanged(bool visible)
{
m_arrow->setVisible(visible);
}
void ChartAxisElement::handleMinorArrowVisibleChanged(bool visible)
{
m_minorArrow->setVisible(visible);
}
void ChartAxisElement::handleGridVisibleChanged(bool visible)
{
m_grid->setVisible(visible);
}
void ChartAxisElement::handleMinorGridVisibleChanged(bool visible)
{
m_minorGrid->setVisible(visible);
}
void ChartAxisElement::handleLabelsPositionChanged()
{
QGraphicsLayoutItem::updateGeometry();
presenter()->layout()->invalidate();
}
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::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::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_minorGrid->setVisible(visible);
m_minorArrow->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_minorGrid->setVisible(axis()->isMinorGridLineVisible());
m_minorArrow->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());
}
}
}
void ChartAxisElement::handleReverseChanged(bool reverse)
{
Q_UNUSED(reverse);
QGraphicsLayoutItem::updateGeometry();
presenter()->layout()->invalidate();
}
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(std::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 = qCeil(std::log10(min) / std::log10(base));
else
firstTick = qCeil(std::log10(max) / std::log10(base));
if (format.isNull()) {
int n = 0;
if (ticks > 1)
n = qMax(int(-qFloor(std::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(-qFloor(std::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