qlegend.cpp
720 lines
| 21.6 KiB
| text/x-c
|
CppLexer
/ src / qlegend.cpp
|
r794 | /**************************************************************************** | ||
** | ||||
** 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$ | ||||
** | ||||
****************************************************************************/ | ||||
|
r524 | #include "qlegend.h" | ||
|
r790 | #include "qchart_p.h" | ||
|
r524 | #include "qseries.h" | ||
|
r547 | #include "legendmarker_p.h" | ||
|
r716 | #include "legendscrollbutton_p.h" | ||
|
r565 | #include "qxyseries.h" | ||
#include "qlineseries.h" | ||||
#include "qareaseries.h" | ||||
#include "qscatterseries.h" | ||||
#include "qsplineseries.h" | ||||
#include "qbarseries.h" | ||||
#include "qstackedbarseries.h" | ||||
#include "qpercentbarseries.h" | ||||
#include "qbarset.h" | ||||
#include "qpieseries.h" | ||||
#include "qpieslice.h" | ||||
|
r626 | #include "chartpresenter_p.h" | ||
|
r529 | #include <QPainter> | ||
#include <QPen> | ||||
|
r524 | |||
|
r547 | #include <QGraphicsSceneEvent> | ||
|
r529 | |||
|
r547 | QTCOMMERCIALCHART_BEGIN_NAMESPACE | ||
|
r529 | |||
|
r728 | /*! | ||
\class QLegend | ||||
\brief part of QtCommercial chart API. | ||||
QLegend is a graphical object, whics displays legend of the chart. Legend state is updated by QChart, when | ||||
series have been changed. By default, legend is drawn by QChart, but user can set a new parent to legend and | ||||
handle the drawing manually. | ||||
User isn't supposed to create or delete legend objects, but can reference it via QChart class. | ||||
\mainclass | ||||
\sa QChart, QSeries | ||||
*/ | ||||
|
r724 | /*! | ||
|
r766 | \enum QLegend::Layout | ||
|
r724 | |||
This enum describes the possible position for legend inside chart. | ||||
|
r766 | \value LayoutTop | ||
\value LayoutBottom | ||||
\value LayoutLeft | ||||
\value LayoutRight | ||||
|
r724 | */ | ||
/*! | ||||
|
r728 | \fn void QLegend::clicked(QSeries* series, Qt::MouseButton button) | ||
|
r724 | \brief Notifies when series has been clicked on legend \a series \a button | ||
*/ | ||||
/*! | ||||
|
r728 | \fn void QLegend::clicked(QBarSet* barset, Qt::MouseButton button) | ||
|
r724 | \brief Notifies when barset has been clicked on legend \a barset \a button | ||
*/ | ||||
/*! | ||||
|
r728 | \fn void QLegend::clicked(QPieSlice* slice, Qt::MouseButton button) | ||
|
r724 | \brief Notifies when pie slice has been clicked on legend \a slice \a button | ||
*/ | ||||
/*! | ||||
Constructs the legend object and sets the parent to \a parent | ||||
*/ | ||||
|
r791 | QLegend::QLegend(QChart *chart):QGraphicsWidget(chart), | ||
|
r783 | m_margin(5), | ||
|
r766 | m_pos(0,0), | ||
m_minimumSize(50,20), // TODO: magic numbers | ||||
m_maximumSize(150,100), | ||||
|
r783 | m_size(m_minimumSize), | ||
|
r785 | m_brush(Qt::darkGray), // TODO: default should come from theme | ||
|
r800 | m_alignment(QLegend::AlignmentTop), | ||
|
r799 | mFirstMarker(0), | ||
m_attachedToChart(true) | ||||
|
r762 | { | ||
|
r766 | m_scrollButtonLeft = new LegendScrollButton(LegendScrollButton::ScrollButtonIdLeft, this); | ||
m_scrollButtonRight = new LegendScrollButton(LegendScrollButton::ScrollButtonIdRight, this); | ||||
m_scrollButtonUp = new LegendScrollButton(LegendScrollButton::ScrollButtonIdUp, this); | ||||
m_scrollButtonDown = new LegendScrollButton(LegendScrollButton::ScrollButtonIdDown, this); | ||||
|
r626 | setZValue(ChartPresenter::LegendZValue); | ||
|
r524 | } | ||
|
r724 | /*! | ||
Paints the legend to given \a painter. Paremeters \a option and \a widget arent used. | ||||
*/ | ||||
|
r524 | void QLegend::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) | ||
{ | ||||
|
r611 | Q_UNUSED(option) | ||
Q_UNUSED(widget) | ||||
|
r724 | painter->setOpacity(opacity()); | ||
|
r645 | painter->setPen(m_pen); | ||
painter->setBrush(m_brush); | ||||
|
r785 | // painter->drawRect(boundingRect()); | ||
|
r524 | } | ||
|
r724 | /*! | ||
Bounding rect of legend. | ||||
*/ | ||||
|
r524 | QRectF QLegend::boundingRect() const | ||
{ | ||||
|
r766 | return QRectF(m_pos,m_size); | ||
|
r524 | } | ||
|
r724 | /*! | ||
Sets the \a brush of legend. Brush affects the background of legend. | ||||
*/ | ||||
|
r737 | void QLegend::setBrush(const QBrush &brush) | ||
|
r540 | { | ||
|
r737 | if (m_brush != brush) { | ||
|
r645 | m_brush = brush; | ||
update(); | ||||
} | ||||
} | ||||
|
r724 | /*! | ||
Returns the brush used by legend. | ||||
*/ | ||||
|
r645 | QBrush QLegend::brush() const | ||
{ | ||||
return m_brush; | ||||
} | ||||
|
r724 | /*! | ||
Sets the \a pen of legend. Pen affects the legend borders. | ||||
*/ | ||||
|
r737 | void QLegend::setPen(const QPen &pen) | ||
|
r645 | { | ||
|
r737 | if (m_pen != pen) { | ||
|
r645 | m_pen = pen; | ||
update(); | ||||
} | ||||
|
r540 | } | ||
|
r724 | /*! | ||
Returns the pen used by legend | ||||
*/ | ||||
|
r645 | QPen QLegend::pen() const | ||
|
r540 | { | ||
|
r645 | return m_pen; | ||
|
r540 | } | ||
|
r724 | /*! | ||
Sets the \a preferred layout for legend. Legend tries to paint itself on the defined position in chart. | ||||
|
r766 | \sa QLegend::Layout | ||
|
r724 | */ | ||
|
r799 | void QLegend::setAlignmnent(QLegend::Alignment alignment) | ||
|
r616 | { | ||
|
r766 | m_alignment = alignment; | ||
|
r716 | updateLayout(); | ||
} | ||||
|
r724 | /*! | ||
Returns the preferred layout for legend | ||||
*/ | ||||
|
r799 | QLegend::Alignment QLegend::alignment() const | ||
|
r716 | { | ||
|
r766 | return m_alignment; | ||
|
r616 | } | ||
|
r724 | /*! | ||
Returns the maximum size of legend. | ||||
*/ | ||||
|
r626 | QSizeF QLegend::maximumSize() const | ||
|
r582 | { | ||
|
r766 | return m_maximumSize; | ||
|
r582 | } | ||
|
r724 | /*! | ||
Sets the maximum \a size for legend. The legend can't grow bigger than this size. If there are | ||||
more series than legend can fit to this size, scroll buttons are displayed. | ||||
*/ | ||||
|
r626 | void QLegend::setMaximumSize(const QSizeF size) | ||
|
r582 | { | ||
|
r766 | m_maximumSize = size; | ||
|
r716 | updateLayout(); | ||
|
r626 | } | ||
|
r724 | /*! | ||
Returns the current size of legend. | ||||
*/ | ||||
QSizeF QLegend::size() const | ||||
{ | ||||
|
r766 | return m_size; | ||
|
r724 | } | ||
/*! | ||||
Sets the \a size of legend. If size is bigger than maximum size of legend, the legend is resized to the maximum size. | ||||
\sa setMmaximumSize() | ||||
*/ | ||||
|
r626 | void QLegend::setSize(const QSizeF size) | ||
{ | ||||
|
r766 | m_size = size; | ||
if (m_size.width() > m_maximumSize.width()) { | ||||
m_size.setWidth(m_maximumSize.width()); | ||||
|
r626 | } | ||
|
r766 | if (m_size.height() > m_maximumSize.height()) { | ||
m_size.setHeight(m_maximumSize.height()); | ||||
|
r626 | } | ||
} | ||||
|
r724 | /*! | ||
Sets position of legend to \a pos | ||||
*/ | ||||
|
r626 | void QLegend::setPos(const QPointF &pos) | ||
{ | ||||
|
r766 | m_pos = pos; | ||
|
r716 | updateLayout(); | ||
|
r582 | } | ||
|
r724 | /*! | ||
\internal \a series \a domain Should be called when series is added to chart. | ||||
*/ | ||||
|
r737 | void QLegend::handleSeriesAdded(QSeries *series, Domain *domain) | ||
|
r524 | { | ||
|
r611 | Q_UNUSED(domain) | ||
|
r792 | switch (series->type()) | ||
{ | ||||
case QSeries::SeriesTypeLine: { | ||||
QLineSeries *lineSeries = static_cast<QLineSeries *>(series); | ||||
appendMarkers(lineSeries); | ||||
break; | ||||
} | ||||
case QSeries::SeriesTypeArea: { | ||||
QAreaSeries *areaSeries = static_cast<QAreaSeries *>(series); | ||||
appendMarkers(areaSeries); | ||||
break; | ||||
} | ||||
case QSeries::SeriesTypeBar: { | ||||
QBarSeries *barSeries = static_cast<QBarSeries *>(series); | ||||
appendMarkers(barSeries); | ||||
break; | ||||
} | ||||
case QSeries::SeriesTypeStackedBar: { | ||||
QStackedBarSeries *stackedBarSeries = static_cast<QStackedBarSeries *>(series); | ||||
appendMarkers(stackedBarSeries); | ||||
break; | ||||
} | ||||
case QSeries::SeriesTypePercentBar: { | ||||
QPercentBarSeries *percentBarSeries = static_cast<QPercentBarSeries *>(series); | ||||
appendMarkers(percentBarSeries); | ||||
break; | ||||
} | ||||
case QSeries::SeriesTypeScatter: { | ||||
QScatterSeries *scatterSeries = static_cast<QScatterSeries *>(series); | ||||
appendMarkers(scatterSeries); | ||||
break; | ||||
} | ||||
case QSeries::SeriesTypePie: { | ||||
QPieSeries *pieSeries = static_cast<QPieSeries *>(series); | ||||
appendMarkers(pieSeries); | ||||
connect(pieSeries,SIGNAL(added(QList<QPieSlice*>)),this,SLOT(handleAdded(QList<QPieSlice*>))); | ||||
break; | ||||
} | ||||
case QSeries::SeriesTypeSpline: { | ||||
QSplineSeries *splineSeries = static_cast<QSplineSeries *>(series); | ||||
appendMarkers(splineSeries); | ||||
break; | ||||
} | ||||
default: { | ||||
qWarning()<< "QLegend::handleSeriesAdded" << series->type() << "unknown series type."; | ||||
break; | ||||
} | ||||
} | ||||
|
r716 | updateLayout(); | ||
|
r586 | } | ||
|
r724 | /*! | ||
\internal \a series Should be called when series is removed from chart. | ||||
*/ | ||||
|
r737 | void QLegend::handleSeriesRemoved(QSeries *series) | ||
|
r586 | { | ||
|
r792 | switch (series->type()) | ||
{ | ||||
case QSeries::SeriesTypeArea: { | ||||
QAreaSeries *areaSeries = static_cast<QAreaSeries *>(series); | ||||
deleteMarkers(areaSeries); | ||||
break; | ||||
} | ||||
case QSeries::SeriesTypePie: { | ||||
QPieSeries *pieSeries = static_cast<QPieSeries *>(series); | ||||
disconnect(pieSeries, SIGNAL(added(QList<QPieSlice *>)), this, SLOT(handleAdded(QList<QPieSlice *>))); | ||||
deleteMarkers(series); | ||||
break; | ||||
} | ||||
default: { | ||||
// All other types | ||||
|
r586 | deleteMarkers(series); | ||
|
r792 | break; | ||
} | ||||
|
r586 | } | ||
|
r716 | updateLayout(); | ||
|
r586 | } | ||
|
r724 | /*! | ||
\internal \a slices Should be called when slices are added to pie chart. | ||||
*/ | ||||
|
r737 | void QLegend::handleAdded(QList<QPieSlice *> slices) | ||
|
r637 | { | ||
|
r737 | QPieSeries* series = static_cast<QPieSeries *> (sender()); | ||
|
r637 | foreach(QPieSlice* s, slices) { | ||
|
r737 | LegendMarker* marker = new LegendMarker(series, s, this); | ||
|
r637 | marker->setName(s->label()); | ||
|
r756 | marker->setBrush(s->brush()); | ||
|
r737 | connect(marker, SIGNAL(clicked(QPieSlice*,Qt::MouseButton)), | ||
this, SIGNAL(clicked(QPieSlice*,Qt::MouseButton))); | ||||
connect(s, SIGNAL(changed()), marker, SLOT(changed())); | ||||
connect(s, SIGNAL(destroyed()), marker, SLOT(deleteLater())); | ||||
connect(marker, SIGNAL(destroyed()), this, SLOT(handleMarkerDestroyed())); | ||||
|
r766 | m_markers.append(marker); | ||
|
r637 | childItems().append(marker); | ||
} | ||||
|
r716 | updateLayout(); | ||
|
r637 | } | ||
|
r724 | /*! | ||
\internal \a slices Should be called when slices are removed from pie chart. Currently unused, | ||||
because removed slices are also deleted and we listen destroyed signal | ||||
*/ | ||||
|
r643 | void QLegend::handleRemoved(QList<QPieSlice *> slices) | ||
{ | ||||
|
r647 | Q_UNUSED(slices) | ||
|
r643 | } | ||
|
r724 | /*! | ||
\internal Notifies legend that some marker has been removed. Sent by legend markers when destroyed | ||||
*/ | ||||
|
r637 | void QLegend::handleMarkerDestroyed() | ||
{ | ||||
|
r737 | LegendMarker* m = static_cast<LegendMarker *> (sender()); | ||
|
r766 | m_markers.removeOne(m); | ||
|
r716 | updateLayout(); | ||
} | ||||
|
r724 | /*! | ||
\internal \a event Handles clicked signals from scroll buttons | ||||
*/ | ||||
|
r790 | void QLegend::scrollButtonClicked(LegendScrollButton *scrollButton) | ||
|
r716 | { | ||
|
r790 | Q_ASSERT(scrollButton); | ||
|
r716 | |||
switch (scrollButton->id()) { | ||||
case LegendScrollButton::ScrollButtonIdLeft: | ||||
case LegendScrollButton::ScrollButtonIdUp: { | ||||
// Lower limit is same in these cases | ||||
mFirstMarker--; | ||||
|
r724 | checkFirstMarkerBounds(); | ||
|
r716 | break; | ||
} | ||||
case LegendScrollButton::ScrollButtonIdRight: | ||||
case LegendScrollButton::ScrollButtonIdDown: { | ||||
mFirstMarker++; | ||||
|
r724 | checkFirstMarkerBounds(); | ||
|
r716 | break; | ||
} | ||||
default: { | ||||
break; | ||||
} | ||||
} | ||||
updateLayout(); | ||||
|
r637 | } | ||
|
r799 | /*! | ||
Detaches the legend from chart. Chart won't change layout of the legend. | ||||
*/ | ||||
void QLegend::detachFromChart() | ||||
{ | ||||
m_attachedToChart = false; | ||||
} | ||||
/*! | ||||
Attaches the legend to chart. Chart may change layout of the legend. | ||||
*/ | ||||
void QLegend::attachToChart() | ||||
{ | ||||
m_attachedToChart = true; | ||||
} | ||||
/*! | ||||
Returns true, if legend is attached to chart. | ||||
*/ | ||||
bool QLegend::attachedToChart() | ||||
{ | ||||
return m_attachedToChart; | ||||
} | ||||
|
r724 | /*! | ||
|
r792 | \internal Helper function. Appends markers from \a series to legend. | ||
|
r724 | */ | ||
|
r792 | void QLegend::appendMarkers(QAreaSeries* series) | ||
|
r586 | { | ||
|
r792 | LegendMarker* marker = new LegendMarker(series,this); | ||
connect(marker, SIGNAL(clicked(QSeries *, Qt::MouseButton)), this, SIGNAL(clicked(QSeries *, Qt::MouseButton))); | ||||
connect(marker, SIGNAL(destroyed()), this, SLOT(handleMarkerDestroyed())); | ||||
connect(series,SIGNAL(updated()),marker,SLOT(changed())); | ||||
|
r800 | marker->changed(); | ||
|
r792 | m_markers.append(marker); | ||
childItems().append(marker); | ||||
|
r576 | } | ||
|
r724 | /*! | ||
\internal Helper function. Appends markers from \a series to legend. | ||||
*/ | ||||
|
r586 | void QLegend::appendMarkers(QXYSeries* series) | ||
|
r529 | { | ||
|
r565 | LegendMarker* marker = new LegendMarker(series,this); | ||
|
r737 | connect(marker, SIGNAL(clicked(QSeries *, Qt::MouseButton)), this, SIGNAL(clicked(QSeries *, Qt::MouseButton))); | ||
connect(marker, SIGNAL(destroyed()), this, SLOT(handleMarkerDestroyed())); | ||||
|
r792 | connect(series,SIGNAL(updated()),marker,SLOT(changed())); | ||
|
r800 | marker->changed(); | ||
|
r766 | m_markers.append(marker); | ||
|
r565 | childItems().append(marker); | ||
} | ||||
|
r724 | /*! | ||
|
r792 | \internal Helper function. Appends markers from \a series to legend. | ||
|
r724 | */ | ||
|
r586 | void QLegend::appendMarkers(QBarSeries *series) | ||
|
r565 | { | ||
|
r786 | foreach(QBarSet* set, series->barSets()) { | ||
LegendMarker* marker = new LegendMarker(series, set, this); | ||||
|
r737 | connect(marker, SIGNAL(clicked(QBarSet *, Qt::MouseButton)), | ||
this, SIGNAL(clicked(QBarSet *, Qt::MouseButton))); | ||||
|
r786 | connect(set, SIGNAL(valueChanged()), marker, SLOT(changed())); | ||
|
r737 | connect(marker, SIGNAL(destroyed()), this, SLOT(handleMarkerDestroyed())); | ||
|
r800 | marker->changed(); | ||
|
r766 | m_markers.append(marker); | ||
|
r565 | childItems().append(marker); | ||
|
r529 | } | ||
|
r565 | } | ||
|
r529 | |||
|
r724 | /*! | ||
|
r792 | \internal Helper function. Appends markers from \a series to legend. | ||
|
r724 | */ | ||
|
r586 | void QLegend::appendMarkers(QPieSeries *series) | ||
|
r565 | { | ||
|
r786 | foreach(QPieSlice* slice, series->slices()) { | ||
LegendMarker* marker = new LegendMarker(series, slice, this); | ||||
|
r737 | connect(marker, SIGNAL(clicked(QPieSlice *, Qt::MouseButton)), | ||
this, SIGNAL(clicked(QPieSlice *, Qt::MouseButton))); | ||||
|
r786 | connect(slice, SIGNAL(changed()), marker, SLOT(changed())); | ||
connect(slice, SIGNAL(destroyed()), marker, SLOT(deleteLater())); | ||||
|
r737 | connect(marker, SIGNAL(destroyed()), this, SLOT(handleMarkerDestroyed())); | ||
|
r800 | marker->changed(); | ||
|
r766 | m_markers.append(marker); | ||
|
r565 | childItems().append(marker); | ||
|
r529 | } | ||
} | ||||
|
r724 | /*! | ||
\internal Deletes all markers that are created from \a series | ||||
*/ | ||||
|
r586 | void QLegend::deleteMarkers(QSeries *series) | ||
{ | ||||
// Search all markers that belong to given series and delete them. | ||||
|
r766 | foreach (LegendMarker *m, m_markers) { | ||
|
r586 | if (m->series() == series) { | ||
|
r766 | m_markers.removeOne(m); | ||
|
r586 | delete m; | ||
} | ||||
} | ||||
} | ||||
|
r724 | /*! | ||
\internal Updates layout of legend. Tries to fit as many markers as possible up to the maximum size of legend. | ||||
If items don't fit, sets the visibility of scroll buttons accordingly. | ||||
Causes legend to be resized. | ||||
*/ | ||||
|
r716 | void QLegend::updateLayout() | ||
|
r529 | { | ||
// Calculate layout for markers and text | ||||
|
r766 | if (m_markers.count() <= 0) { | ||
|
r529 | // Nothing to do | ||
return; | ||||
} | ||||
|
r626 | // Find out widest item. | ||
|
r716 | QSizeF markerMaxSize = maximumMarkerSize(); | ||
|
r724 | checkFirstMarkerBounds(); | ||
|
r626 | |||
|
r716 | // Use max height as scroll button size | ||
rescaleScrollButtons(QSize(markerMaxSize.height() ,markerMaxSize.height())); | ||||
|
r616 | |||
|
r626 | qreal totalWidth = 0; | ||
qreal totalHeight = 0; | ||||
|
r766 | switch (m_alignment) | ||
|
r616 | { | ||
|
r716 | // Both cases organise items horizontally | ||
|
r799 | case QLegend::AlignmentBottom: | ||
case QLegend::AlignmentTop: { | ||||
|
r716 | |||
qreal xStep = markerMaxSize.width(); | ||||
|
r766 | qreal x = m_pos.x() + m_margin; | ||
qreal y = m_pos.y() + m_margin; | ||||
|
r626 | int column = 0; | ||
int maxColumns = 1; | ||||
|
r724 | qreal scrollButtonWidth = 0; | ||
|
r716 | |||
// Set correct visibility for scroll scrollbuttons | ||||
if (scrollButtonsVisible()) { | ||||
|
r766 | m_scrollButtonLeft->setVisible(true); | ||
m_scrollButtonRight->setVisible(true); | ||||
|
r737 | // scrollbuttons visible, so add their width to total width | ||
|
r766 | totalWidth += (m_scrollButtonLeft->boundingRect().width() + m_margin) * 2; | ||
scrollButtonWidth = m_scrollButtonLeft->boundingRect().width() + m_margin; | ||||
|
r737 | // start position changes by scrollbutton width | ||
x += scrollButtonWidth; | ||||
|
r716 | } else { | ||
|
r766 | m_scrollButtonLeft->setVisible(false); | ||
m_scrollButtonRight->setVisible(false); | ||||
|
r716 | } | ||
|
r766 | m_scrollButtonUp->setVisible(false); | ||
m_scrollButtonDown->setVisible(false); | ||||
|
r716 | |||
|
r766 | for (int i=0; i < m_markers.count(); i++) { | ||
LegendMarker *m = m_markers.at(i); | ||||
|
r737 | if (i < mFirstMarker) { | ||
|
r716 | // Markers before first are not visible. | ||
m->setVisible(false); | ||||
} else { | ||||
|
r766 | if ((x + xStep + scrollButtonWidth + m_margin) > (m_pos.x() + m_maximumSize.width())) { | ||
|
r716 | // This marker would go outside legend rect. | ||
m->setVisible(false); | ||||
} else { | ||||
// This marker is ok | ||||
m->setVisible(true); | ||||
|
r737 | m->setPos(x, y); | ||
|
r716 | x += xStep; | ||
column++; | ||||
} | ||||
|
r626 | } | ||
|
r716 | maxColumns = column; | ||
|
r616 | } | ||
|
r716 | |||
|
r766 | m_scrollButtonLeft->setPos(m_pos.x() + m_margin, y); | ||
m_scrollButtonRight->setPos(x + m_margin, y); | ||||
|
r716 | |||
|
r766 | totalWidth += maxColumns * markerMaxSize.width() + m_margin * 2; | ||
totalHeight = markerMaxSize.height() + m_margin * 2; | ||||
|
r716 | |||
|
r616 | break; | ||
|
r529 | } | ||
|
r716 | // Both cases organize items vertically | ||
|
r799 | case QLegend::AlignmentLeft: | ||
case QLegend::AlignmentRight: { | ||||
|
r716 | qreal yStep = markerMaxSize.height(); | ||
|
r766 | qreal x = m_pos.x() + m_margin; | ||
qreal y = m_pos.y() + m_margin; | ||||
|
r716 | int row = 1; | ||
int maxRows = 1; | ||||
|
r724 | qreal scrollButtonHeight = 0; | ||
|
r716 | |||
// Set correct visibility for scroll scrollbuttons | ||||
if (scrollButtonsVisible()) { | ||||
|
r766 | m_scrollButtonUp->setVisible(true); | ||
m_scrollButtonDown->setVisible(true); | ||||
totalHeight += (m_scrollButtonUp->boundingRect().height() + m_margin) * 2; // scrollbuttons visible, so add their height to total height | ||||
scrollButtonHeight = m_scrollButtonUp->boundingRect().height(); | ||||
y += scrollButtonHeight + m_margin; // start position changes by scrollbutton height | ||||
|
r716 | } else { | ||
|
r766 | m_scrollButtonUp->setVisible(false); | ||
m_scrollButtonDown->setVisible(false); | ||||
|
r616 | } | ||
|
r766 | m_scrollButtonLeft->setVisible(false); | ||
m_scrollButtonRight->setVisible(false); | ||||
|
r716 | |||
|
r766 | for (int i=0; i < m_markers.count(); i++) { | ||
LegendMarker* m = m_markers.at(i); | ||||
|
r737 | if (i < mFirstMarker) { | ||
|
r716 | // Markers before first are not visible. | ||
m->setVisible(false); | ||||
} else { | ||||
|
r766 | if ((y + yStep + scrollButtonHeight) > (m_pos.y() + m_maximumSize.height())) { | ||
|
r716 | // This marker would go outside legend rect. | ||
m->setVisible(false); | ||||
} else { | ||||
// This marker is ok | ||||
m->setVisible(true); | ||||
|
r737 | m->setPos(x, y); | ||
|
r716 | y += yStep; | ||
row++; | ||||
} | ||||
} | ||||
maxRows = row; | ||||
} | ||||
|
r766 | m_scrollButtonUp->setPos(m_pos.x() + m_margin, m_pos.y() + m_margin); | ||
m_scrollButtonDown->setPos(m_pos.x() + m_margin, y + m_margin); | ||||
|
r716 | |||
|
r766 | totalWidth += markerMaxSize.width() + m_margin * 2; | ||
totalHeight = maxRows * markerMaxSize.height() + m_margin * 4 + scrollButtonHeight; // TODO: check this | ||||
|
r616 | break; | ||
} | ||||
default: { | ||||
break; | ||||
} | ||||
} | ||||
|
r766 | m_size.setWidth(totalWidth); | ||
m_size.setHeight(totalHeight); | ||||
|
r716 | |||
update(); | ||||
} | ||||
|
r724 | /*! | ||
\internal Sets the size of scroll buttons to \a size | ||||
*/ | ||||
|
r716 | void QLegend::rescaleScrollButtons(const QSize &size) | ||
{ | ||||
QPolygonF left; | ||||
|
r737 | left << QPointF(size.width(), 0) << QPointF(0, size.height() / 2) << QPointF(size.width(), size.height()); | ||
|
r716 | QPolygonF right; | ||
|
r737 | right << QPointF(0, 0) << QPointF(size.width(), size.height() / 2) << QPointF(0, size.height()); | ||
|
r716 | QPolygonF up; | ||
|
r737 | up << QPointF(0, size.height()) << QPointF(size.width() / 2,0) << QPointF(size.width(), size.height()); | ||
|
r716 | QPolygonF down; | ||
|
r737 | down << QPointF(0, 0) << QPointF(size.width() / 2, size.height()) << QPointF(size.width(), 0); | ||
|
r716 | |||
|
r766 | m_scrollButtonLeft->setPolygon(left); | ||
m_scrollButtonRight->setPolygon(right); | ||||
m_scrollButtonUp->setPolygon(up); | ||||
m_scrollButtonDown->setPolygon(down); | ||||
|
r716 | } | ||
|
r724 | /*! | ||
\internal Finds out maximum size of single marker. Marker sizes depend on series names. | ||||
*/ | ||||
|
r716 | QSizeF QLegend::maximumMarkerSize() | ||
{ | ||||
QSizeF max(0,0); | ||||
|
r766 | foreach (LegendMarker* m, m_markers) { | ||
|
r737 | if (m->boundingRect().width() > max.width()) | ||
|
r716 | max.setWidth(m->boundingRect().width()); | ||
|
r737 | if (m->boundingRect().height() > max.height()) | ||
|
r716 | max.setHeight(m->boundingRect().height()); | ||
} | ||||
return max; | ||||
} | ||||
|
r724 | /*! | ||
\internal Checks that first marker is in acceptable bounds. Bounds range from 0 to (maximum number of markers - visible markers) | ||||
If scrollbuttons are visible, they affect the number of visible markers. | ||||
*/ | ||||
void QLegend::checkFirstMarkerBounds() | ||||
|
r716 | { | ||
|
r799 | if ((m_alignment == QLegend::AlignmentLeft) || (m_alignment == QLegend::AlignmentRight)) { | ||
|
r724 | // Bounds limited by height. | ||
|
r716 | int max; | ||
if (scrollButtonsVisible()) { | ||||
|
r766 | max = (m_maximumSize.height() - m_scrollButtonLeft->boundingRect().height() * 2 - m_margin * 4) / maximumMarkerSize().height(); | ||
|
r716 | } else { | ||
|
r766 | max = m_maximumSize.height() / maximumMarkerSize().height(); | ||
|
r716 | } | ||
|
r766 | if (mFirstMarker > m_markers.count() - max) | ||
mFirstMarker = m_markers.count() - max; | ||||
|
r716 | } else { | ||
// Bounds limited by width | ||||
int max; | ||||
if (scrollButtonsVisible()) { | ||||
|
r766 | max = (m_maximumSize.width() - m_scrollButtonLeft->boundingRect().width() * 2 - m_margin*4) / maximumMarkerSize().width(); | ||
|
r716 | } else { | ||
|
r766 | max = m_maximumSize.width() / maximumMarkerSize().width(); | ||
|
r716 | } | ||
|
r766 | if (mFirstMarker > m_markers.count() - max) | ||
mFirstMarker = m_markers.count() - max; | ||||
|
r716 | } | ||
|
r737 | if (mFirstMarker < 0) | ||
|
r716 | mFirstMarker = 0; | ||
} | ||||
|
r724 | /*! | ||
\internal Helper function. Visibility of scroll buttons isn't quite obvious, so helper function clarifies the logic. | ||||
*/ | ||||
|
r716 | bool QLegend::scrollButtonsVisible() | ||
{ | ||||
// Just a helper to clarify, what the magic below means :) | ||||
|
r799 | if ((m_alignment == QLegend::AlignmentTop) || (m_alignment == QLegend::AlignmentBottom)) { | ||
|
r766 | return (maximumMarkerSize().width() * m_markers.count() + m_margin * 2 > m_maximumSize.width()); | ||
|
r799 | } else if ((m_alignment == QLegend::AlignmentLeft) || (m_alignment == QLegend::AlignmentRight)) { | ||
|
r766 | return (maximumMarkerSize().height() * m_markers.count() + m_margin * 2 > m_maximumSize.height()); | ||
|
r716 | } | ||
|
r724 | |||
|
r766 | return (maximumMarkerSize().height() * m_markers.count() + m_margin * 2 > m_maximumSize.height()); | ||
|
r529 | } | ||
|
r524 | #include "moc_qlegend.cpp" | ||
|
r737 | |||
|
r524 | QTCOMMERCIALCHART_END_NAMESPACE | ||