KissCount/lib/libkdchart/src/KDChartLayoutItems.cpp

1157 lines
36 KiB
C++

/****************************************************************************
** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved.
**
** This file is part of the KD Chart library.
**
** Licensees holding valid commercial KD Chart licenses may use this file in
** accordance with the KD Chart Commercial License Agreement provided with
** the Software.
**
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 and version 3 as published by the
** Free Software Foundation and appearing in the file LICENSE.GPL.txt included.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** Contact info@kdab.com if any conditions of this licensing are not
** clear to you.
**
**********************************************************************/
#include "KDChartLayoutItems.h"
#include "KDTextDocument.h"
#include "KDChartAbstractArea.h"
#include "KDChartAbstractDiagram.h"
#include "KDChartBackgroundAttributes.h"
#include "KDChartFrameAttributes.h"
#include "KDChartPaintContext.h"
#include "KDChartPainterSaver_p.h"
#include "KDChartPrintingParameters.h"
#include <QTextCursor>
#include <QTextBlockFormat>
#include <QTextDocumentFragment>
#include <QAbstractTextDocumentLayout>
#include <QLayout>
#include <QPainter>
#include <QDebug>
#include <QCoreApplication>
#include <QApplication>
#include <QStringList>
#include <QStyle>
#include <KDABLibFakes>
#include <math.h>
#define PI 3.141592653589793
//#define DEBUG_ITEMS_PAINT
/**
Inform the item about its widget: This enables the item,
to trigger that widget's update, whenever the size of the item's
contents has changed.
Thus, you need to call setParentWidget on every item, that
has a non-fixed size.
*/
void KDChart::AbstractLayoutItem::setParentWidget( QWidget* widget )
{
mParent = widget;
}
void KDChart::AbstractLayoutItem::paintAll( QPainter& painter )
{
paint( &painter );
}
/**
* Default impl: Paint the complete item using its layouted position and size.
*/
void KDChart::AbstractLayoutItem::paintCtx( PaintContext* context )
{
if( context )
paint( context->painter() );
}
/**
Report changed size hint: ask the parent widget to recalculate the layout.
*/
void KDChart::AbstractLayoutItem::sizeHintChanged()const
{
// This is exactly like what QWidget::updateGeometry does.
// qDebug("KDChart::AbstractLayoutItem::sizeHintChanged() called");
if( mParent ) {
if ( mParent->layout() )
mParent->layout()->invalidate();
else
QApplication::postEvent( mParent, new QEvent( QEvent::LayoutRequest ) );
}
}
KDChart::TextBubbleLayoutItem::TextBubbleLayoutItem( const QString& text,
const KDChart::TextAttributes& attributes,
const QObject* area,
KDChartEnums::MeasureOrientation orientation,
Qt::Alignment alignment )
: AbstractLayoutItem( alignment ),
m_text( new TextLayoutItem( text, attributes, area, orientation, alignment ) )
{
}
KDChart::TextBubbleLayoutItem::TextBubbleLayoutItem()
: AbstractLayoutItem( Qt::AlignLeft ),
m_text( new TextLayoutItem() )
{
}
KDChart::TextBubbleLayoutItem::~TextBubbleLayoutItem()
{
delete m_text;
}
void KDChart::TextBubbleLayoutItem::setAutoReferenceArea( const QObject* area )
{
m_text->setAutoReferenceArea( area );
}
const QObject* KDChart::TextBubbleLayoutItem::autoReferenceArea() const
{
return m_text->autoReferenceArea();
}
void KDChart::TextBubbleLayoutItem::setText( const QString& text )
{
m_text->setText( text );
}
QString KDChart::TextBubbleLayoutItem::text() const
{
return m_text->text();
}
void KDChart::TextBubbleLayoutItem::setTextAttributes( const TextAttributes& a )
{
m_text->setTextAttributes( a );
}
KDChart::TextAttributes KDChart::TextBubbleLayoutItem::textAttributes() const
{
return m_text->textAttributes();
}
bool KDChart::TextBubbleLayoutItem::isEmpty() const
{
return m_text->isEmpty();
}
Qt::Orientations KDChart::TextBubbleLayoutItem::expandingDirections() const
{
return m_text->expandingDirections();
}
QSize KDChart::TextBubbleLayoutItem::maximumSize() const
{
const int border = borderWidth();
return m_text->maximumSize() + QSize( 2 * border, 2 * border );
}
QSize KDChart::TextBubbleLayoutItem::minimumSize() const
{
const int border = borderWidth();
return m_text->minimumSize() + QSize( 2 * border, 2 * border );
}
QSize KDChart::TextBubbleLayoutItem::sizeHint() const
{
const int border = borderWidth();
return m_text->sizeHint() + QSize( 2 * border, 2 * border );
}
void KDChart::TextBubbleLayoutItem::setGeometry( const QRect& r )
{
const int border = borderWidth();
m_text->setGeometry( r.adjusted( border, border, -border, -border ) );
}
QRect KDChart::TextBubbleLayoutItem::geometry() const
{
const int border = borderWidth();
return m_text->geometry().adjusted( -border, -border, border, border );
}
void KDChart::TextBubbleLayoutItem::paint( QPainter* painter )
{
const QPen oldPen = painter->pen();
const QBrush oldBrush = painter->brush();
painter->setPen( Qt::black );
painter->setBrush( QColor( 255, 255, 220 ) );
painter->drawRoundRect( geometry(), 10 );
painter->setPen( oldPen );
painter->setBrush( oldBrush );
m_text->paint( painter );
}
int KDChart::TextBubbleLayoutItem::borderWidth() const
{
return 1;
}
KDChart::TextLayoutItem::TextLayoutItem( const QString& text,
const KDChart::TextAttributes& attributes,
const QObject* area,
KDChartEnums::MeasureOrientation orientation,
Qt::Alignment alignment )
: AbstractLayoutItem( alignment )
, mText( text )
, mTextAlignment( alignment )
, mAttributes( attributes )
, mAutoReferenceArea( area )
, mAutoReferenceOrientation( orientation )
, cachedSizeHint() // default this to invalid to force just-in-time calculation before first use of sizeHint()
, cachedFontSize( 0.0 )
, cachedFont( mAttributes.font() )
{
}
KDChart::TextLayoutItem::TextLayoutItem()
: AbstractLayoutItem( Qt::AlignLeft )
, mText()
, mTextAlignment( Qt::AlignLeft )
, mAttributes()
, mAutoReferenceArea( 0 )
, mAutoReferenceOrientation( KDChartEnums::MeasureOrientationHorizontal )
, cachedSizeHint() // default this to invalid to force just-in-time calculation before first use of sizeHint()
, cachedFontSize( 0.0 )
, cachedFont( mAttributes.font() )
{
}
void KDChart::TextLayoutItem::setAutoReferenceArea( const QObject* area )
{
mAutoReferenceArea = area;
cachedSizeHint = QSize();
sizeHint();
}
const QObject* KDChart::TextLayoutItem::autoReferenceArea() const
{
return mAutoReferenceArea;
}
void KDChart::TextLayoutItem::setText(const QString & text)
{
mText = text;
cachedSizeHint = QSize();
sizeHint();
if( mParent )
mParent->update();
}
QString KDChart::TextLayoutItem::text() const
{
return mText;
}
void KDChart::TextLayoutItem::setTextAlignment( Qt::Alignment alignment)
{
if( mTextAlignment == alignment )
return;
mTextAlignment = alignment;
if( mParent )
mParent->update();
}
Qt::Alignment KDChart::TextLayoutItem::textAlignment() const
{
return mTextAlignment;
}
/**
\brief Use this to specify the text attributes to be used for this item.
\sa textAttributes
*/
void KDChart::TextLayoutItem::setTextAttributes( const TextAttributes &a )
{
mAttributes = a;
cachedFont = a.font();
cachedSizeHint = QSize(); // invalidate size hint
sizeHint();
if( mParent )
mParent->update();
}
/**
Returns the text attributes to be used for this item.
\sa setTextAttributes
*/
KDChart::TextAttributes KDChart::TextLayoutItem::textAttributes() const
{
return mAttributes;
}
Qt::Orientations KDChart::TextLayoutItem::expandingDirections() const
{
return 0; // Grow neither vertically nor horizontally
}
QRect KDChart::TextLayoutItem::geometry() const
{
return mRect;
}
bool KDChart::TextLayoutItem::isEmpty() const
{
return false; // never empty, otherwise the layout item would not exist
}
QSize KDChart::TextLayoutItem::maximumSize() const
{
return sizeHint(); // PENDING(kalle) Review, quite inflexible
}
QSize KDChart::TextLayoutItem::minimumSize() const
{
return sizeHint(); // PENDING(kalle) Review, quite inflexible
}
void KDChart::TextLayoutItem::setGeometry( const QRect& r )
{
mRect = r;
}
QPointF rotatedPoint( const QPointF& pt, qreal rotation, const QPointF& center )
{
const qreal angle = PI * rotation / 180.0;
const qreal cosAngle = cos( angle );
const qreal sinAngle = -sin( angle );
return QPointF(
(cosAngle * ( pt.x() - center.x() ) + sinAngle * ( pt.y() - center.y() ) ),
(cosAngle * ( pt.y() - center.y() ) - sinAngle * ( pt.x() - center.x() ) ) ) + center;
}
QRectF rotatedRect( const QRectF& oldRect, qreal angleInt, const QPointF& center )
{
const QRect rect( oldRect.translated( center ).toRect() );
const qreal angle = PI * angleInt / 180.0;
const qreal cosAngle = cos( angle );
const qreal sinAngle = sin( angle );
QMatrix rotationMatrix(cosAngle, sinAngle, -sinAngle, cosAngle, 0, 0);
QPolygon rotPts;
rotPts << rotationMatrix.map(rect.topLeft()) //QPoint(0,0)
<< rotationMatrix.map(rect.topRight())
<< rotationMatrix.map(rect.bottomRight())
<< rotationMatrix.map(rect.bottomLeft());
//<< rotatedPoint(rect.topRight(), angleInt, center).toPoint()
//<< rotatedPoint(rect.bottomRight(), angleInt, center).toPoint()
//<< rotatedPoint(rect.bottomLeft(), angleInt, center).toPoint();
return rotPts.boundingRect();
/*
const QPointF topLeft( rotatedPoint( oldRect.topLeft(), angle, center ) );
const QPointF topRight( rotatedPoint( oldRect.topRight(), angle, center ) );
const QPointF bottomLeft( rotatedPoint( oldRect.bottomLeft(), angle, center ) );
const QPointF bottomRight( rotatedPoint( oldRect.bottomRight(), angle, center ) );
const qreal x = qMin( qMin( topLeft.x(), topRight.x() ), qMin( bottomLeft.x(), topLeft.x() ) );
const qreal y = qMin( qMin( topLeft.y(), topRight.y() ), qMin( bottomLeft.y(), topLeft.y() ) );
const qreal width = qMax( qMax( topLeft.x(), topRight.x() ), qMax( bottomLeft.x(), topLeft.x() ) ) - x;
const qreal height = qMax( qMax( topLeft.y(), topRight.y() ), qMax( bottomLeft.y(), topLeft.y() ) ) - y;
return QRectF( x, y, width, height );
*/
}
/*
QRectF rotatedRect( const QRectF& rect, qreal angle )
{
const QPointF topLeft( rotatedPoint( rect.topLeft(), angle ) );
//const QPointF topRight( rotatedPoint( rect.topRight(), angle ) );
//const QPointF bottomLeft( rotatedPoint( rect.bottomLeft(), angle ) );
#if 1
const QPointF bottomRight( rotatedPoint( rect.bottomRight(), angle ) );
const QRectF result( topLeft, QSizeF( bottomRight.x() - topLeft.x(), bottomRight.y() - topLeft.y() ) );
#else
const QPointF siz( rotatedPoint( QPointF( rect.size().width(), rect.size().height() ), angle ) );
const QRectF result(
topLeft,
QSizeF( siz.x(), //bottomRight.x() - topLeft.x(),
siz.y() ) ); //bottomRight.y() - topLeft.y() ) );
//qDebug() << "angle" << angle << "\nbefore:" << rect << "\n after:" << result;
#endif
return result;
}*/
qreal KDChart::TextLayoutItem::fitFontSizeToGeometry() const
{
QFont f = realFont();
const qreal origResult = f.pointSizeF();
qreal result = origResult;
const qreal minSize = mAttributes.minimalFontSize().value();
const QSize mySize = geometry().size();
if( mySize.isNull() )
return result;
const QString t = text();
QFontMetrics fm( f );
while( true )
{
const QSizeF textSize = rotatedRect( fm.boundingRect( t ), mAttributes.rotation() ).normalized().size();
if( textSize.height() <= mySize.height() && textSize.width() <= mySize.width() )
return result;
result -= 0.5;
if ( minSize > 0 && result < minSize )
return result += 0.5;
if( result <= 0.0 )
return origResult;
f.setPointSizeF( result );
fm = QFontMetrics( f );
}
}
qreal KDChart::TextLayoutItem::realFontSize() const
{
return mAttributes.calculatedFontSize( mAutoReferenceArea, mAutoReferenceOrientation );
}
bool KDChart::TextLayoutItem::realFontWasRecalculated() const
{
const qreal fntSiz = realFontSize();
const bool bRecalcDone =
( ( ! cachedSizeHint.isValid() ) || ( cachedFontSize != fntSiz ) );
if( bRecalcDone && fntSiz > 0.0 ){
cachedFontSize = fntSiz;
cachedFont.setPointSizeF( fntSiz );
}
return bRecalcDone;
}
QFont KDChart::TextLayoutItem::realFont() const
{
realFontWasRecalculated(); // we can safely ignore the boolean return value
return cachedFont;
}
QPolygon KDChart::TextLayoutItem::rotatedCorners() const
{
// the angle in rad
const qreal angle = mAttributes.rotation() * PI / 180.0;
QSize size = unrotatedSizeHint();
// my P1 - P4 (the four points of the rotated area)
QPointF P1( size.height() * sin( angle ), 0 );
QPointF P2( size.height() * sin( angle ) + size.width() * cos( angle ), size.width() * sin( angle ) );
QPointF P3( size.width() * cos( angle ), size.width() * sin( angle ) + size.height() * cos( angle ) );
QPointF P4( 0, size.height() * cos( angle ) );
QPolygon result;
result << P1.toPoint() << P2.toPoint() << P3.toPoint() << P4.toPoint();
return result;
}
bool KDChart::TextLayoutItem::intersects( const TextLayoutItem& other, const QPointF& myPos, const QPointF& otherPos ) const
{
return intersects( other, myPos.toPoint(), otherPos.toPoint() );
}
bool KDChart::TextLayoutItem::intersects( const TextLayoutItem& other, const QPoint& myPos, const QPoint& otherPos ) const
{
if ( mAttributes.rotation() != other.mAttributes.rotation() )
{
// that's the code for the common case: the rotation angles don't need to match here
QPolygon myPolygon( rotatedCorners() );
QPolygon otherPolygon( other.rotatedCorners() );
// move the polygons to their positions
myPolygon.translate( myPos );
otherPolygon.translate( otherPos );
// create regions out of it
QRegion myRegion( myPolygon );
QRegion otherRegion( otherPolygon );
// now the question - do they intersect or not?
return ! myRegion.intersect( otherRegion ).isEmpty();
} else {
// and that's the code for the special case: the rotation angles match, which is less time consuming in calculation
const qreal angle = mAttributes.rotation() * PI / 180.0;
// both sizes
const QSizeF mySize( unrotatedSizeHint() );
const QSizeF otherSize( other.unrotatedSizeHint() );
// that's myP1 relative to myPos
QPointF myP1( mySize.height() * sin( angle ), 0.0 );
// that's otherP1 to myPos
QPointF otherP1 = QPointF( otherSize.height() * sin( angle ), 0.0 ) + otherPos - myPos;
// now rotate both points the negative angle around myPos
myP1 = QPointF( myP1.x() * cos( -angle ), myP1.x() * sin( -angle ) );
qreal r = sqrt( otherP1.x() * otherP1.x() + otherP1.y() * otherP1.y() );
otherP1 = QPointF( r * cos( -angle ), r * sin( -angle ) );
// finally we look, whether both rectangles intersect or even not
const bool res = QRectF( myP1, mySize ).intersects( QRectF( otherP1, otherSize ) );
//qDebug() << res << QRectF( myP1, mySize ) << QRectF( otherP1, otherSize );
return res;
}
}
QSize KDChart::TextLayoutItem::sizeHint() const
{
QPoint dummy;
return sizeHintAndRotatedCorners(dummy,dummy,dummy,dummy);
}
QSize KDChart::TextLayoutItem::sizeHintAndRotatedCorners(
QPoint& topLeftPt, QPoint& topRightPt, QPoint& bottomRightPt, QPoint& bottomLeftPt) const
{
if( realFontWasRecalculated() || mAttributes.rotation() )
{
const QSize newSizeHint( calcSizeHint( cachedFont,
topLeftPt, topRightPt, bottomRightPt, bottomLeftPt ) );
if( newSizeHint != cachedSizeHint ){
cachedSizeHint = newSizeHint;
sizeHintChanged();
}
cachedTopLeft = topLeftPt;
cachedTopRight = topRightPt;
cachedBottomRight = bottomRightPt;
cachedBottomLeft = bottomLeftPt;
}else{
topLeftPt = cachedTopLeft;
topRightPt = cachedTopRight;
bottomRightPt = cachedBottomRight;
bottomLeftPt = cachedBottomLeft;
}
//qDebug() << "-------- KDChart::TextLayoutItem::sizeHint() returns:"<<cachedSizeHint<<" ----------";
return cachedSizeHint;
}
QSize KDChart::TextLayoutItem::sizeHintUnrotated() const
{
realFontWasRecalculated(); // make sure the cached font is updated if needed
return unrotatedSizeHint( cachedFont );
}
// PENDING(kalle) Support auto shrink
QSize KDChart::TextLayoutItem::unrotatedSizeHint( QFont fnt ) const
{
if ( fnt == QFont() )
fnt = realFont(); // this *is* the chached font in most of the time
const QFontMetricsF met( fnt, GlobalMeasureScaling::paintDevice() );
QSize ret(0, 0);
// note: boundingRect() does NOT take any newlines into account
// so we need to calculate the size by combining several
// rectangles: one per line. This fixes bugz issue #3720.
// (khz, 2007 04 14)
QStringList lines = mText.split(QString::fromAscii("\n"));
for (int i = 0; i < lines.size(); ++i) {
const QSize lSize = met.boundingRect(lines.at(i) ).toRect().size();
ret.setWidth(qMax( ret.width(), lSize.width() ));
ret.rheight() += lSize.height();
}
int frame = QApplication::style()->pixelMetric( QStyle::PM_ButtonMargin, 0, 0 );
// fine-tuning for small font sizes: the frame must not be so big, if the font is tiny
frame = qMin( frame, ret.height() * 2 / 3 );
//qDebug() << "frame:"<< frame;
ret += QSize( frame, frame );
return ret;
//const QFontMetricsF met( fnt, GlobalMeasureScaling::paintDevice() );
//const int frame = QApplication::style()->pixelMetric( QStyle::PM_ButtonMargin, 0, 0 );
//return
// met.boundingRect( mText ).size().toSize() + QSize( frame, frame );
}
QSize KDChart::TextLayoutItem::calcSizeHint(
QFont fnt, QPoint& topLeftPt, QPoint& topRightPt, QPoint& bottomRightPt, QPoint& bottomLeftPt ) const
{
const QSize siz( unrotatedSizeHint( fnt ));
//qDebug() << "-------- siz: "<<siz;
if( ! mAttributes.rotation() ){
topLeftPt = QPoint(0,0);
topRightPt = QPoint(siz.width(),0);
bottomRightPt = QPoint(siz.width(),siz.height());
bottomLeftPt = QPoint(0,siz.height());
return siz;
}
const QRect rect(QPoint(0, 0), siz + QSize(4,4));
const qreal angle = PI * mAttributes.rotation() / 180.0;
const qreal cosAngle = cos( angle );
const qreal sinAngle = sin( angle );
QMatrix rotationMatrix(cosAngle, sinAngle, -sinAngle, cosAngle, 0, 0);
QPolygon rotPts;
rotPts << rotationMatrix.map(rect.topLeft())
<< rotationMatrix.map(rect.topRight())
<< rotationMatrix.map(rect.bottomRight())
<< rotationMatrix.map(rect.bottomLeft());
QSize rotSiz( rotPts.boundingRect().size() );
//qDebug() << "-------- KDChart::TextLayoutItem::calcSizeHint() returns:"<<rotSiz<<rotPts;
topLeftPt = rotPts[0];
topRightPt = rotPts[1];
bottomRightPt = rotPts[2];
bottomLeftPt = rotPts[3];
return rotSiz;
}
void KDChart::TextLayoutItem::paint( QPainter* painter )
{
// make sure, cached font is updated, if needed:
// sizeHint();
if( !mRect.isValid() )
return;
const PainterSaver painterSaver( painter );
QFont f = realFont();
if ( mAttributes.autoShrink() )
f.setPointSizeF( fitFontSizeToGeometry() );
painter->setFont( f );
QRectF rect( geometry() );
// #ifdef DEBUG_ITEMS_PAINT
// painter->setPen( Qt::black );
// painter->drawRect( rect );
// #endif
painter->translate( rect.center() );
rect.moveTopLeft( QPointF( - rect.width() / 2, - rect.height() / 2 ) );
#ifdef DEBUG_ITEMS_PAINT
painter->setPen( Qt::blue );
painter->drawRect( rect );
painter->drawRect( QRect(QPoint((rect.topLeft().toPoint() + rect.bottomLeft().toPoint()) / 2 - QPoint(2,2)), QSize(3,3)) );
//painter->drawRect( QRect(QPoint((rect.topRight().toPoint() + rect.bottomRight().toPoint()) / 2 - QPoint(2,2)), QSize(3,3)) );
#endif
painter->rotate( mAttributes.rotation() );
rect = rotatedRect( rect, mAttributes.rotation() );
#ifdef DEBUG_ITEMS_PAINT
painter->setPen( Qt::red );
painter->drawRect( rect );
painter->drawRect( QRect(QPoint((rect.topLeft().toPoint() + rect.bottomLeft().toPoint()) / 2 - QPoint(2,2)), QSize(3,3)) );
//painter->drawRect( QRect(QPoint((rect.topRight().toPoint() + rect.bottomRight().toPoint()) / 2 - QPoint(2,2)), QSize(3,3)) );
#endif
painter->setPen( PrintingParameters::scalePen( mAttributes.pen() ) );
QFontMetrics fontMetrics( f );
const int AHight = fontMetrics.boundingRect( QChar::fromAscii( 'A' ) ).height();
const qreal AVCenter = fontMetrics.ascent() - AHight / 2.0;
// Make sure that capital letters are vertically centered. This looks much
// better than just centering the text's bounding rect.
rect.translate( 0.0, rect.height() / 2.0 - AVCenter );
//painter->drawText( rect, Qt::AlignHCenter | Qt::AlignTop, mText );
painter->drawText( rect, mTextAlignment, mText );
// if ( calcSizeHint( realFont() ).width() > rect.width() )
// qDebug() << "rect.width()" << rect.width() << "text.width()" << calcSizeHint( realFont() ).width();
//
// //painter->drawText( rect, Qt::AlignHCenter | Qt::AlignVCenter, mText );
}
KDChart::HorizontalLineLayoutItem::HorizontalLineLayoutItem()
: AbstractLayoutItem( Qt::AlignCenter )
{
}
Qt::Orientations KDChart::HorizontalLineLayoutItem::expandingDirections() const
{
return Qt::Vertical|Qt::Horizontal; // Grow both vertically, and horizontally
}
QRect KDChart::HorizontalLineLayoutItem::geometry() const
{
return mRect;
}
bool KDChart::HorizontalLineLayoutItem::isEmpty() const
{
return false; // never empty, otherwise the layout item would not exist
}
QSize KDChart::HorizontalLineLayoutItem::maximumSize() const
{
return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX );
}
QSize KDChart::HorizontalLineLayoutItem::minimumSize() const
{
return QSize( 0, 0 );
}
void KDChart::HorizontalLineLayoutItem::setGeometry( const QRect& r )
{
mRect = r;
}
QSize KDChart::HorizontalLineLayoutItem::sizeHint() const
{
return QSize( -1, 3 ); // see qframe.cpp
}
void KDChart::HorizontalLineLayoutItem::paint( QPainter* painter )
{
if( !mRect.isValid() )
return;
painter->drawLine( QPointF( mRect.left(), mRect.center().y() ),
QPointF( mRect.right(), mRect.center().y() ) );
}
KDChart::VerticalLineLayoutItem::VerticalLineLayoutItem()
: AbstractLayoutItem( Qt::AlignCenter )
{
}
Qt::Orientations KDChart::VerticalLineLayoutItem::expandingDirections() const
{
return Qt::Vertical|Qt::Vertical; // Grow both vertically, and horizontally
}
QRect KDChart::VerticalLineLayoutItem::geometry() const
{
return mRect;
}
bool KDChart::VerticalLineLayoutItem::isEmpty() const
{
return false; // never empty, otherwise the layout item would not exist
}
QSize KDChart::VerticalLineLayoutItem::maximumSize() const
{
return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX );
}
QSize KDChart::VerticalLineLayoutItem::minimumSize() const
{
return QSize( 0, 0 );
}
void KDChart::VerticalLineLayoutItem::setGeometry( const QRect& r )
{
mRect = r;
}
QSize KDChart::VerticalLineLayoutItem::sizeHint() const
{
return QSize( 3, -1 ); // see qframe.cpp
}
void KDChart::VerticalLineLayoutItem::paint( QPainter* painter )
{
if( !mRect.isValid() )
return;
painter->drawLine( QPointF( mRect.center().x(), mRect.top() ),
QPointF( mRect.center().x(), mRect.bottom() ) );
}
KDChart::MarkerLayoutItem::MarkerLayoutItem( KDChart::AbstractDiagram* diagram,
const MarkerAttributes& marker,
const QBrush& brush, const QPen& pen,
Qt::Alignment alignment )
: AbstractLayoutItem( alignment )
, mDiagram( diagram )
, mMarker( marker )
, mBrush( brush )
, mPen( pen )
{
}
Qt::Orientations KDChart::MarkerLayoutItem::expandingDirections() const
{
return 0; // Grow neither vertically nor horizontally
}
QRect KDChart::MarkerLayoutItem::geometry() const
{
return mRect;
}
bool KDChart::MarkerLayoutItem::isEmpty() const
{
return false; // never empty, otherwise the layout item would not exist
}
QSize KDChart::MarkerLayoutItem::maximumSize() const
{
return sizeHint(); // PENDING(kalle) Review, quite inflexible
}
QSize KDChart::MarkerLayoutItem::minimumSize() const
{
return sizeHint(); // PENDING(kalle) Review, quite inflexible
}
void KDChart::MarkerLayoutItem::setGeometry( const QRect& r )
{
mRect = r;
}
QSize KDChart::MarkerLayoutItem::sizeHint() const
{
//qDebug() << "KDChart::MarkerLayoutItem::sizeHint() returns:"<<mMarker.markerSize().toSize();
return mMarker.markerSize().toSize();
}
void KDChart::MarkerLayoutItem::paint( QPainter* painter )
{
paintIntoRect( painter, mRect, mDiagram, mMarker, mBrush, mPen );
}
void KDChart::MarkerLayoutItem::paintIntoRect(
QPainter* painter,
const QRect& rect,
AbstractDiagram* diagram,
const MarkerAttributes& marker,
const QBrush& brush,
const QPen& pen )
{
if( ! rect.isValid() )
return;
// The layout management may assign a larger rect than what we
// wanted. We need to adjust the position.
const QSize siz = marker.markerSize().toSize();
QPointF pos = rect.topLeft();
pos += QPointF( static_cast<qreal>(( rect.width() - siz.width()) / 2.0 ),
static_cast<qreal>(( rect.height() - siz.height()) / 2.0 ) );
#ifdef DEBUG_ITEMS_PAINT
QPointF oldPos = pos;
#endif
// And finally, drawMarker() assumes the position to be the center
// of the marker, adjust again.
pos += QPointF( static_cast<qreal>( siz.width() ) / 2.0,
static_cast<qreal>( siz.height() )/ 2.0 );
diagram->paintMarker( painter, marker, brush, pen, pos.toPoint(), siz );
#ifdef DEBUG_ITEMS_PAINT
const QPen oldPen( painter->pen() );
painter->setPen( Qt::red );
painter->drawRect( QRect(oldPos.toPoint(), siz) );
painter->setPen( oldPen );
#endif
}
KDChart::LineLayoutItem::LineLayoutItem( KDChart::AbstractDiagram* diagram,
int length,
const QPen& pen,
Qt::Alignment alignment )
: AbstractLayoutItem( alignment )
, mDiagram( diagram )
, mLength( length )
, mPen( pen )
{
//have a mini pen width
if ( pen.width() < 2 )
mPen.setWidth( 2 );
}
Qt::Orientations KDChart::LineLayoutItem::expandingDirections() const
{
return 0; // Grow neither vertically nor horizontally
}
QRect KDChart::LineLayoutItem::geometry() const
{
return mRect;
}
bool KDChart::LineLayoutItem::isEmpty() const
{
return false; // never empty, otherwise the layout item would not exist
}
QSize KDChart::LineLayoutItem::maximumSize() const
{
return sizeHint(); // PENDING(kalle) Review, quite inflexible
}
QSize KDChart::LineLayoutItem::minimumSize() const
{
return sizeHint(); // PENDING(kalle) Review, quite inflexible
}
void KDChart::LineLayoutItem::setGeometry( const QRect& r )
{
mRect = r;
}
QSize KDChart::LineLayoutItem::sizeHint() const
{
return QSize( mLength, mPen.width()+2 );
}
void KDChart::LineLayoutItem::paint( QPainter* painter )
{
paintIntoRect( painter, mRect, mPen );
}
void KDChart::LineLayoutItem::paintIntoRect(
QPainter* painter,
const QRect& rect,
const QPen& pen )
{
if( ! rect.isValid() )
return;
const QPen oldPen = painter->pen();
painter->setPen( PrintingParameters::scalePen( pen ) );
const qreal y = rect.center().y();
painter->drawLine( QPointF( rect.left(), y ),
QPointF( rect.right(), y ) );
painter->setPen( oldPen );
}
KDChart::LineWithMarkerLayoutItem::LineWithMarkerLayoutItem(
KDChart::AbstractDiagram* diagram,
int lineLength,
const QPen& linePen,
int markerOffs,
const MarkerAttributes& marker,
const QBrush& markerBrush,
const QPen& markerPen,
Qt::Alignment alignment )
: AbstractLayoutItem( alignment )
, mDiagram( diagram )
, mLineLength( lineLength )
, mLinePen( linePen )
, mMarkerOffs( markerOffs )
, mMarker( marker )
, mMarkerBrush( markerBrush )
, mMarkerPen( markerPen )
{
}
Qt::Orientations KDChart::LineWithMarkerLayoutItem::expandingDirections() const
{
return 0; // Grow neither vertically nor horizontally
}
QRect KDChart::LineWithMarkerLayoutItem::geometry() const
{
return mRect;
}
bool KDChart::LineWithMarkerLayoutItem::isEmpty() const
{
return false; // never empty, otherwise the layout item would not exist
}
QSize KDChart::LineWithMarkerLayoutItem::maximumSize() const
{
return sizeHint(); // PENDING(kalle) Review, quite inflexible
}
QSize KDChart::LineWithMarkerLayoutItem::minimumSize() const
{
return sizeHint(); // PENDING(kalle) Review, quite inflexible
}
void KDChart::LineWithMarkerLayoutItem::setGeometry( const QRect& r )
{
mRect = r;
}
QSize KDChart::LineWithMarkerLayoutItem::sizeHint() const
{
const QSize sizeM = mMarker.markerSize().toSize();
const QSize sizeL = QSize( mLineLength, mLinePen.width()+2 );
return QSize( qMax(sizeM.width(), sizeL.width()),
qMax(sizeM.height(), sizeL.height()) );
}
void KDChart::LineWithMarkerLayoutItem::paint( QPainter* painter )
{
// paint the line over the full width, into the vertical middle of the rect
LineLayoutItem::paintIntoRect( painter, mRect, mLinePen );
// paint the marker with the given offset from the left side of the line
const QRect r(
QPoint( mRect.x()+mMarkerOffs, mRect.y() ),
QSize( mMarker.markerSize().toSize().width(), mRect.height() ) );
MarkerLayoutItem::paintIntoRect(
painter, r, mDiagram, mMarker, mMarkerBrush, mMarkerPen );
}
KDChart::AutoSpacerLayoutItem::AutoSpacerLayoutItem(
bool layoutIsAtTopPosition, QHBoxLayout *rightLeftLayout,
bool layoutIsAtLeftPosition, QVBoxLayout *topBottomLayout )
: AbstractLayoutItem( Qt::AlignCenter )
, mLayoutIsAtTopPosition( layoutIsAtTopPosition )
, mRightLeftLayout( rightLeftLayout )
, mLayoutIsAtLeftPosition( layoutIsAtLeftPosition )
, mTopBottomLayout( topBottomLayout )
{
}
Qt::Orientations KDChart::AutoSpacerLayoutItem::expandingDirections() const
{
return 0; // Grow neither vertically nor horizontally
}
QRect KDChart::AutoSpacerLayoutItem::geometry() const
{
return mRect;
}
bool KDChart::AutoSpacerLayoutItem::isEmpty() const
{
return true; // never empty, otherwise the layout item would not exist
}
QSize KDChart::AutoSpacerLayoutItem::maximumSize() const
{
return sizeHint();
}
QSize KDChart::AutoSpacerLayoutItem::minimumSize() const
{
return sizeHint();
}
void KDChart::AutoSpacerLayoutItem::setGeometry( const QRect& r )
{
mRect = r;
}
static void updateCommonBrush( QBrush& commonBrush, bool& bStart, const KDChart::AbstractArea& area )
{
const KDChart::BackgroundAttributes ba( area.backgroundAttributes() );
const bool hasSimpleBrush = (
! area.frameAttributes().isVisible() &&
ba.isVisible() &&
ba.pixmapMode() == KDChart::BackgroundAttributes::BackgroundPixmapModeNone &&
ba.brush().gradient() == 0 );
if( bStart ){
bStart = false;
commonBrush = hasSimpleBrush ? ba.brush() : QBrush();
}else{
if( ! hasSimpleBrush || ba.brush() != commonBrush )
{
commonBrush = QBrush();
}
}
}
QSize KDChart::AutoSpacerLayoutItem::sizeHint() const
{
QBrush commonBrush;
bool bStart=true;
// calculate the maximal overlap of the top/bottom axes:
int topBottomOverlap = 0;
if( mTopBottomLayout ){
for (int i = 0; i < mTopBottomLayout->count(); ++i){
AbstractArea* area = dynamic_cast<AbstractArea*>(mTopBottomLayout->itemAt(i));
if( area ){
//qDebug() << "AutoSpacerLayoutItem testing" << area;
topBottomOverlap =
mLayoutIsAtLeftPosition
? qMax( topBottomOverlap, area->rightOverlap() )
: qMax( topBottomOverlap, area->leftOverlap() );
updateCommonBrush( commonBrush, bStart, *area );
}
}
}
// calculate the maximal overlap of the left/right axes:
int leftRightOverlap = 0;
if( mRightLeftLayout ){
for (int i = 0; i < mRightLeftLayout->count(); ++i){
AbstractArea* area = dynamic_cast<AbstractArea*>(mRightLeftLayout->itemAt(i));
if( area ){
//qDebug() << "AutoSpacerLayoutItem testing" << area;
leftRightOverlap =
mLayoutIsAtTopPosition
? qMax( leftRightOverlap, area->bottomOverlap() )
: qMax( leftRightOverlap, area->topOverlap() );
updateCommonBrush( commonBrush, bStart, *area );
}
}
}
if( topBottomOverlap > 0 && leftRightOverlap > 0 )
mCommonBrush = commonBrush;
else
mCommonBrush = QBrush();
mCachedSize = QSize( topBottomOverlap, leftRightOverlap );
//qDebug() << mCachedSize;
return mCachedSize;
}
void KDChart::AutoSpacerLayoutItem::paint( QPainter* painter )
{
if( mParentLayout && mRect.isValid() && mCachedSize.isValid() &&
mCommonBrush.style() != Qt::NoBrush )
{
QPoint p1( mRect.topLeft() );
QPoint p2( mRect.bottomRight() );
if( mLayoutIsAtLeftPosition )
p1.rx() += mCachedSize.width() - mParentLayout->spacing();
else
p2.rx() -= mCachedSize.width() - mParentLayout->spacing();
if( mLayoutIsAtTopPosition ){
p1.ry() += mCachedSize.height() - mParentLayout->spacing() - 1;
p2.ry() -= 1;
}else
p2.ry() -= mCachedSize.height() - mParentLayout->spacing() - 1;
//qDebug() << mLayoutIsAtTopPosition << mLayoutIsAtLeftPosition;
//qDebug() << mRect;
//qDebug() << mParentLayout->margin();
//qDebug() << QRect( p1, p2 );
const QPoint oldBrushOrigin( painter->brushOrigin() );
const QBrush oldBrush( painter->brush() );
const QPen oldPen( painter->pen() );
const QPointF newTopLeft( painter->deviceMatrix().map( p1 ) );
painter->setBrushOrigin( newTopLeft );
painter->setBrush( mCommonBrush );
painter->setPen( Qt::NoPen );
painter->drawRect( QRect( p1, p2 ) );
painter->setBrushOrigin( oldBrushOrigin );
painter->setBrush( oldBrush );
painter->setPen( oldPen );
}
// debug code:
#if 0
//qDebug() << "KDChart::AutoSpacerLayoutItem::paint()";
if( !mRect.isValid() )
return;
painter->drawRect( mRect );
painter->drawLine( QPointF( mRect.x(), mRect.top() ),
QPointF( mRect.right(), mRect.bottom() ) );
painter->drawLine( QPointF( mRect.right(), mRect.top() ),
QPointF( mRect.x(), mRect.bottom() ) );
#endif
}