/**************************************************************************** ** ** Copyright (C) 2012 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 Commercial Charts Add-on. ** ** $QT_BEGIN_LICENSE$ ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial 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 "chartaxis_p.h" #include "qabstractaxis.h" #include "qabstractaxis_p.h" #include "chartpresenter_p.h" #include "chartlayout_p.h" #include "domain_p.h" #include #include #include #include #include QTCOMMERCIALCHART_BEGIN_NAMESPACE ChartAxis::ChartAxis(QAbstractAxis *axis, ChartPresenter *presenter, bool intervalAxis) : ChartElement(presenter), m_chartAxis(axis), m_labelsAngle(0), m_grid(new QGraphicsItemGroup(presenter->rootItem())), m_shades(new QGraphicsItemGroup(presenter->rootItem())), m_labels(new QGraphicsItemGroup(presenter->rootItem())), m_arrow(new QGraphicsItemGroup(presenter->rootItem())), m_title(new QGraphicsSimpleTextItem(presenter->rootItem())), m_min(0), m_max(0), m_animation(0), m_labelPadding(5), m_intervalAxis(intervalAxis) { //initial initialization m_arrow->setZValue(ChartPresenter::AxisZValue); m_arrow->setHandlesChildEvents(false); m_labels->setZValue(ChartPresenter::AxisZValue); m_shades->setZValue(ChartPresenter::ShadesZValue); m_grid->setZValue(ChartPresenter::GridZValue); QObject::connect(m_chartAxis->d_ptr.data(), SIGNAL(updated()), this, SLOT(handleAxisUpdated())); QGraphicsSimpleTextItem item; m_font = item.font(); } ChartAxis::~ChartAxis() { } void ChartAxis::setAnimation(AxisAnimation *animation) { m_animation = animation; } void ChartAxis::setLayout(QVector &layout) { m_layoutVector = layout; } void ChartAxis::createItems(int count) { if (m_arrow->children().size() == 0) m_arrow->addToGroup(new AxisItem(this, presenter()->rootItem())); if (m_intervalAxis && m_grid->children().size() == 0) { for (int i = 0 ; i < 2 ; i ++) m_grid->addToGroup(new QGraphicsLineItem(presenter()->rootItem())); } for (int i = 0; i < count; ++i) { m_grid->addToGroup(new QGraphicsLineItem(presenter()->rootItem())); m_labels->addToGroup(new QGraphicsSimpleTextItem(presenter()->rootItem())); m_arrow->addToGroup(new QGraphicsLineItem(presenter()->rootItem())); if ((m_grid->childItems().size()) % 2 && m_grid->childItems().size() > 2) m_shades->addToGroup(new QGraphicsRectItem(presenter()->rootItem())); } } void ChartAxis::deleteItems(int count) { QList lines = m_grid->childItems(); QList labels = m_labels->childItems(); QList shades = m_shades->childItems(); QList axis = m_arrow->childItems(); for (int i = 0; i < count; ++i) { if (lines.size() % 2 && lines.size() > 1) delete(shades.takeLast()); delete(lines.takeLast()); delete(labels.takeLast()); delete(axis.takeLast()); } } void ChartAxis::updateLayout(QVector &layout) { int diff = m_layoutVector.size() - layout.size(); if (diff > 0) deleteItems(diff); else if (diff < 0) createItems(-diff); if (diff < 0) handleAxisUpdated(); if (m_animation) { switch (presenter()->state()) { case ChartPresenter::ZoomInState: m_animation->setAnimationType(AxisAnimation::ZoomInAnimation); m_animation->setAnimationPoint(presenter()->statePoint()); break; case ChartPresenter::ZoomOutState: m_animation->setAnimationType(AxisAnimation::ZoomOutAnimation); m_animation->setAnimationPoint(presenter()->statePoint()); break; case ChartPresenter::ScrollUpState: case ChartPresenter::ScrollLeftState: m_animation->setAnimationType(AxisAnimation::MoveBackwordAnimation); break; case ChartPresenter::ScrollDownState: case ChartPresenter::ScrollRightState: m_animation->setAnimationType(AxisAnimation::MoveForwardAnimation); break; case ChartPresenter::ShowState: m_animation->setAnimationType(AxisAnimation::DefaultAnimation); break; } m_animation->setValues(m_layoutVector, layout); presenter()->startAnimation(m_animation); } else { setLayout(layout); updateGeometry(); } } void ChartAxis::setArrowOpacity(qreal opacity) { m_arrow->setOpacity(opacity); } qreal ChartAxis::arrowOpacity() const { return m_arrow->opacity(); } void ChartAxis::setArrowVisibility(bool visible) { m_arrow->setOpacity(visible); } void ChartAxis::setGridOpacity(qreal opacity) { m_grid->setOpacity(opacity); } qreal ChartAxis::gridOpacity() const { return m_grid->opacity(); } void ChartAxis::setGridVisibility(bool visible) { m_grid->setOpacity(visible); } void ChartAxis::setLabelsOpacity(qreal opacity) { m_labels->setOpacity(opacity); } qreal ChartAxis::labelsOpacity() const { return m_labels->opacity(); } void ChartAxis::setLabelsVisibility(bool visible) { m_labels->setOpacity(visible); } void ChartAxis::setShadesOpacity(qreal opacity) { m_shades->setOpacity(opacity); } qreal ChartAxis::shadesOpacity() const { return m_shades->opacity(); } void ChartAxis::setShadesVisibility(bool visible) { m_shades->setVisible(visible); } void ChartAxis::setLabelsAngle(int angle) { foreach (QGraphicsItem *item, m_labels->childItems()) item->setRotation(angle); m_labelsAngle = angle; } void ChartAxis::setLabelsPen(const QPen &pen) { foreach (QGraphicsItem *item , m_labels->childItems()) static_cast(item)->setPen(pen); } void ChartAxis::setLabelsBrush(const QBrush &brush) { foreach (QGraphicsItem *item , m_labels->childItems()) static_cast(item)->setBrush(brush); } void ChartAxis::setLabelsFont(const QFont &font) { foreach (QGraphicsItem *item , m_labels->childItems()) static_cast(item)->setFont(font); if (m_font != font) { m_font = font; foreach (QGraphicsItem *item , m_labels->childItems()) static_cast(item)->setFont(font); QGraphicsLayoutItem::updateGeometry(); presenter()->layout()->invalidate(); } } void ChartAxis::setShadesBrush(const QBrush &brush) { foreach (QGraphicsItem *item , m_shades->childItems()) static_cast(item)->setBrush(brush); } void ChartAxis::setShadesPen(const QPen &pen) { foreach (QGraphicsItem *item , m_shades->childItems()) static_cast(item)->setPen(pen); } void ChartAxis::setArrowPen(const QPen &pen) { foreach (QGraphicsItem *item , m_arrow->childItems()) static_cast(item)->setPen(pen); } void ChartAxis::setGridPen(const QPen &pen) { foreach (QGraphicsItem *item , m_grid->childItems()) static_cast(item)->setPen(pen); } void ChartAxis::setLabelPadding(int padding) { m_labelPadding = padding; } bool ChartAxis::isEmpty() { return !m_axisRect.isValid() || m_gridRect.isEmpty() || qFuzzyIsNull(m_min - m_max); } void ChartAxis::handleDomainUpdated() { Domain *domain = qobject_cast(sender()); qreal min(0); qreal max(0); if (m_chartAxis->orientation() == Qt::Horizontal) { min = domain->minX(); max = domain->maxX(); } else if (m_chartAxis->orientation() == Qt::Vertical) { min = domain->minY(); max = domain->maxY(); } if (!qFuzzyIsNull(m_min - min) || !qFuzzyIsNull(m_max - max)) { m_min = min; m_max = max; if (!isEmpty()) { QVector layout = calculateLayout(); updateLayout(layout); QSizeF before = effectiveSizeHint(Qt::MinimumSize); QSizeF after = sizeHint(Qt::MinimumSize); 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 ChartAxis::handleAxisUpdated() { if (isEmpty()) return; bool visible = m_chartAxis->isVisible(); setArrowVisibility(visible && m_chartAxis->isLineVisible()); setGridVisibility(visible && m_chartAxis->isGridLineVisible()); setLabelsVisibility(visible && m_chartAxis->labelsVisible()); setShadesVisibility(visible && m_chartAxis->shadesVisible()); setLabelsAngle(m_chartAxis->labelsAngle()); setArrowPen(m_chartAxis->linePen()); setLabelsPen(m_chartAxis->labelsPen()); setLabelsBrush(m_chartAxis->labelsBrush()); setLabelsFont(m_chartAxis->labelsFont()); setGridPen(m_chartAxis->gridLinePen()); setShadesPen(m_chartAxis->shadesPen()); setShadesBrush(m_chartAxis->shadesBrush()); setTitleText(m_chartAxis->title()); } void ChartAxis::setTitleText(const QString &title) { if (m_titleText != title) { m_titleText = title; m_axisRect = QRect(); QGraphicsLayoutItem::updateGeometry(); presenter()->layout()->invalidate(); } } void ChartAxis::hide() { setArrowVisibility(false); setGridVisibility(false); setLabelsVisibility(false); setShadesVisibility(false); } void ChartAxis::setGeometry(const QRectF &axis, const QRectF &grid) { m_gridRect = grid; m_axisRect = axis; if (isEmpty()) return; if (!m_titleText.isNull()) { QFontMetrics fn(m_title->font()); int size(0); if (orientation() == Qt::Horizontal) size = grid.width(); else if (orientation() == Qt::Vertical) size = grid.height(); if (fn.boundingRect(m_titleText).width() > size) { QString string = m_titleText + "..."; while (fn.boundingRect(string).width() > size && string.length() > 3) string.remove(string.length() - 4, 1); m_title->setText(string); } else { m_title->setText(m_titleText); } QPointF center = grid.center() - m_title->boundingRect().center(); if (orientation() == Qt::Horizontal) { m_title->setPos(center.x(), m_axisRect.bottom() - m_title->boundingRect().height()); } else if (orientation() == Qt::Vertical) { m_title->setTransformOriginPoint(m_title->boundingRect().center()); m_title->setRotation(270); m_title->setPos(m_axisRect.left() - m_title->boundingRect().width() / 2 + m_title->boundingRect().height() / 2, center.y()); } } QVector layout = calculateLayout(); updateLayout(layout); } void ChartAxis::axisSelected() { //TODO: axis clicked; } Qt::Orientation ChartAxis::orientation() const { return m_chartAxis->orientation(); } Qt::Alignment ChartAxis::alignment() const { return m_chartAxis->alignment(); } bool ChartAxis::isVisible() { return m_chartAxis->isVisible(); } void ChartAxis::setLabels(const QStringList &labels) { m_labelsList = labels; } QSizeF ChartAxis::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const { Q_UNUSED(constraint); QFontMetrics fn(m_title->font()); QSizeF sh; if (m_titleText.isNull()) return sh; switch (which) { case Qt::MinimumSize: if (orientation() == Qt::Horizontal) sh = QSizeF(fn.boundingRect("...").width(), fn.height()); else if (orientation() == Qt::Vertical) sh = QSizeF(fn.height(), fn.boundingRect("...").width()); break; case Qt::MaximumSize: case Qt::PreferredSize: if (orientation() == Qt::Horizontal) sh = QSizeF(fn.boundingRect(m_chartAxis->title()).width(), fn.height()); else if (orientation() == Qt::Vertical) sh = QSizeF(fn.height(), fn.boundingRect(m_chartAxis->title()).width()); break; default: break; } return sh; } QStringList ChartAxis::createValueLabels(int ticks) const { Q_ASSERT(m_max > m_min); Q_ASSERT(ticks > 1); QStringList labels; int n = qMax(int(-qFloor(log10((m_max - m_min) / (ticks - 1)))), 0); n++; QValueAxis *axis = qobject_cast(m_chartAxis); QString format = axis->labelFormat(); if (format.isNull()) { for (int i = 0; i < ticks; i++) { qreal value = m_min + (i * (m_max - m_min) / (ticks - 1)); labels << QString::number(value, 'f', n); } } else { QByteArray array = format.toAscii(); for (int i = 0; i < ticks; i++) { qreal value = m_min + (i * (m_max - m_min) / (ticks - 1)); labels << QString().sprintf(array, value); } } return labels; } QStringList ChartAxis::createDateTimeLabels(const QString &format, int ticks) const { Q_ASSERT(m_max > m_min); Q_ASSERT(ticks > 1); QStringList labels; int n = qMax(int(-floor(log10((m_max - m_min) / (ticks - 1)))), 0); n++; for (int i = 0; i < ticks; i++) { qreal value = m_min + (i * (m_max - m_min) / (ticks - 1)); labels << QDateTime::fromMSecsSinceEpoch(value).toString(format); } return labels; } #include "moc_chartaxis_p.cpp" QTCOMMERCIALCHART_END_NAMESPACE