You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/kchart/kdchart/KDDrawText.cpp

484 lines
18 KiB

/* -*- Mode: C++ -*-
KDChart - a multi-platform charting engine
*/
/****************************************************************************
** Copyright (C) 2001-2003 Klarälvdalens Datakonsult AB. All rights reserved.
**
** This file is part of the KDChart library.
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.
**
** Licensees holding valid commercial KDChart licenses may use this file in
** accordance with the KDChart Commercial License Agreement provided with
** the Software.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** See http://www.klaralvdalens-datakonsult.se/?page=products for
** information about KDChart Commercial License Agreements.
**
** Contact info@klaralvdalens-datakonsult.se if any conditions of this
** licensing are not clear to you.
**
**********************************************************************/
#include <qpainter.h>
#include <qbitmap.h>
#include <qpixmap.h>
#include <math.h>
#include <limits.h>
#include <KDDrawText.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
void KDDrawText::drawRotatedText( QPainter* painter,
float degrees,
QPoint anchor,
const QString& text,
const QFont* font,
int align,
bool showAnchor,
const QFontMetrics* fontMet,
bool noFirstrotate,
bool noBackrotate,
KDDrawTextRegionAndTrueRect* infos,
bool optimizeOutputForScreen )
{
drawRotatedTxt( painter,
optimizeOutputForScreen,
degrees,
anchor,
text,
font,
align,
showAnchor,
INT_MAX,
INT_MAX,
fontMet,
false,
0 != infos,
noFirstrotate,
noBackrotate,
infos );
}
KDDrawTextRegionAndTrueRect KDDrawText::measureRotatedText(
QPainter* painter,
float degrees,
QPoint anchor,
const QString& text,
const QFont* font,
int align,
const QFontMetrics* fontMet,
bool noFirstrotate,
bool noBackrotate,
int addPercentOfHeightToRegion )
{
KDDrawTextRegionAndTrueRect infos;
drawRotatedTxt( painter,
false,
degrees,
anchor,
text,
font,
align,
false,
INT_MAX,
INT_MAX,
fontMet,
true,
false,
noFirstrotate,
noBackrotate,
&infos,
addPercentOfHeightToRegion );
return infos;
}
void KDDrawText::drawRotatedTxt( QPainter* painter,
bool optimizeOutputForScreen,
float degrees,
QPoint anchor,
const QString& text,
const QFont* font,
int align,
bool showAnchor,
int txtWidth,
int txtHeight,
const QFontMetrics* fontMet,
bool calculateOnly,
bool doNotCalculate,
bool noFirstrotate,
bool noBackrotate,
KDDrawTextRegionAndTrueRect* infos,
int addPercentOfHeightToRegion )
{
// showAnchor=true;
//qDebug("\nanchor: "+ text + " / "+QString::number(anchor.x())
// +" / "+QString::number(anchor.y()));
bool useInfos = doNotCalculate && infos;
bool fontChanged = ( 0 != font );
QFont oldFont;
if( fontChanged ) {
oldFont = painter->font();
painter->setFont( *font );
}
else
font = &painter->font();
bool mustBackrotate = false;
if( !optimizeOutputForScreen && !noFirstrotate ){
painter->rotate( degrees );
if( !noBackrotate )
mustBackrotate = true;
}
QPoint pos = useInfos ? infos->pos : painter->xFormDev( anchor );
if( useInfos )
{
txtWidth = infos->width;
txtHeight = infos->height;
}
else
{
int newHeight=0;
// a bug in the AIX 5.2 compiler means using (?:) syntax doesn't work here
// therefor we do it the following way:
QFontMetrics* pFM=0;
if( ! pFM ) {
pFM = new QFontMetrics( painter->fontMetrics() );
} else {
pFM = const_cast<QFontMetrics*>(fontMet);
}
int nLF = text.contains('\n');
if( INT_MAX == txtWidth ) {
if( nLF ){
int tw;
txtWidth = 0;
int i0 = 0;
int iLF = text.find('\n');
while( -1 != iLF ){
const QRect r(pFM->boundingRect( text.mid(i0, iLF-i0) ));
tw = r.width()+ 2;
newHeight = r.height();
if( tw > txtWidth )
txtWidth = tw;
i0 = iLF+1;
iLF = text.find('\n', i0);
}
if( iLF < (int)text.length() ){
const QRect r(pFM->boundingRect( text.mid( i0 ) ));
tw = r.width()+2;
newHeight = r.height();
if( tw > txtWidth )
txtWidth = tw;
i0 = iLF+1;
}
}else{
const QRect r(painter->boundingRect( 0,0,1,1, Qt::AlignAuto, text ));
// correct width and height before painting with 2 unit to avoid truncating.
// PENDING Michel - improve
txtWidth = r.width()+2;
newHeight = r.height()+2;
}
}
if( INT_MAX == txtWidth || INT_MAX == txtHeight ) {
txtHeight = newHeight ? newHeight : pFM->height() * (1+nLF);
}
if( pFM != fontMet )
delete pFM;
if( infos ) {
infos->pos = pos;
// PENDING infos
infos->width = txtWidth;
infos->height = txtHeight;
}
}
if( showAnchor ) {
int d = txtHeight/4;
QPen savePen = painter->pen();
painter->setPen( QColor( Qt::darkRed ) );
painter->drawLine( pos.x(), pos.y()-d,
pos.x(), pos.y()+d );
painter->drawLine( pos.x()-d, pos.y(),
pos.x()+d, pos.y() );
painter->setPen( savePen );
}
int x = useInfos ? infos->x : pos.x();
int y = useInfos ? infos->y : pos.y();
//qDebug("1.: (x / y) :" + text + " / "+QString::number(x)
// +" / "+QString::number(y));
//qDebug("2.: (posx / posy) :" + text );
// qDebug ( "%d", pos.x() ); qDebug ( "%d", pos.y() );
//qDebug("3.: (infosx / infosy) :" + text + " / "+QString::number(infos->x)
// +" / "+QString::number(infos->y));
if( !useInfos && !optimizeOutputForScreen ) {
switch( align & ( Qt::AlignLeft | Qt::AlignRight | Qt::AlignHCenter ) ) {
case Qt::AlignLeft:
break;
case Qt::AlignRight:
//qDebug( QPaintDeviceMetrics::logicalDpiX() );
x -= txtWidth;
break;
case Qt::AlignHCenter:
x -= txtWidth - txtWidth/2;
break;
}
switch( align & ( Qt::AlignTop | Qt::AlignBottom | Qt::AlignVCenter ) ) {
case Qt::AlignTop:
break;
case Qt::AlignBottom:
y -= txtHeight;
break;
case Qt::AlignVCenter:
y -= txtHeight/2;
break;
}
}
if( infos && !useInfos ) {
painter->xForm( pos );
infos->x = x - 4;
infos->y = y - 4;
//PENDING Michel updating info using x , y from pos
//qDebug("4.: (infosx / infosy) :" + text + " / "+QString::number(infos->x)
//+" / "+QString::number(infos->y));
//qDebug("5.: (x / y) :" + text + " / "+QString::number(x)
// +" / "+QString::number(y));
//qDebug("6.: (anchorx /anchory) :" + text + " / "+QString::number(x)
// +" / "+QString::number(y));
QRect rect( painter->boundingRect( x, y,
txtWidth, txtHeight,
Qt::AlignLeft + Qt::AlignTop,
text ) );
//painter->fillRect (rect, Qt::blue );
QPoint topLeft( painter->xForm( rect.topLeft() ) );
QPoint topRight( painter->xForm( rect.topRight() ) );
QPoint bottomRight( painter->xForm( rect.bottomRight() ) );
QPoint bottomLeft( painter->xForm( rect.bottomLeft() ) );
int additor = addPercentOfHeightToRegion * txtHeight / 100;
QPointArray points;
points.setPoints( 4, topLeft.x()-additor, topLeft.y()-additor,
topRight.x()+additor, topRight.y()-additor,
bottomRight.x()+additor, bottomRight.y()+additor,
bottomLeft.x()-additor, bottomLeft.y()+additor );
infos->region = QRegion( points );
}
// When the Qt initialization bug is fixed the following scope
// will be put into an "if( showAnchor )" entirely.
{
int d = txtHeight/4;
QPen savePen = painter->pen();
if( showAnchor ) {
painter->setPen( QColor( Qt::blue ) );
painter->drawLine( x, y-d,
x, y+d );
painter->drawLine( x-d, y,
x+d, y );
painter->setPen( QColor( Qt::darkGreen ) );
painter->drawRect(x,y,txtWidth,txtHeight);
//painter->drawText( x, y-d, text);
/*
}else{
// Working around a strange Qt bug: Rotated painter must be
// initialized by drawing before text can be painted there.
painter->setPen( QColor( Qt::white ) );
painter->drawLine( 30000,0,30001,0 );
*/
}
painter->setPen( savePen );
}
if( mustBackrotate && optimizeOutputForScreen ){
painter->rotate( -degrees );
mustBackrotate = false;
}
if( !calculateOnly ){
//qDebug("txtWidth: %i txtHeight: %i", txtWidth, txtHeight);
if( !optimizeOutputForScreen ){
/*
painter->drawText( x, y,
txtWidth, txtHeight,
Qt::AlignLeft + Qt::AlignTop,
text );
*/
painter->drawText( x, y,
txtWidth, txtHeight,
Qt::AlignLeft + Qt::AlignTop,
text );
/*
painter->drawText( x, y,
text,
-1,
Qt::AlignRight + Qt::AlignTop );
*/
}else{
// new code (rotating the text ourselves for better quality on screens)
QPixmap pm( txtWidth+2, txtHeight+2, 1 );
// note: When using colored axis labels it will be necessary
// to change this code and use a 256 color pixmap instead
// of a monochrome one. (khz, 2002/08/15)
pm.fill(Qt::color0);
QPainter p;
p.begin( &pm );
if( showAnchor ){
p.drawRect(0,0, txtWidth,txtHeight);
p.drawLine(0,0, txtWidth,txtHeight);
p.drawLine(0,txtHeight, txtWidth,0);
}
p.setFont(painter->font());
p.drawText( 0, 0, txtWidth, txtHeight,
Qt::AlignLeft + Qt::AlignTop,
text );
/*
p.drawText( 0,0,
text,
-1,
Qt::AlignLeft + Qt::AlignTop );
*/
QBitmap mask;
mask = pm;
pm.setMask( mask );
QWMatrix m;
m.rotate( degrees );
QPixmap theRotatedPixmap = pm.xForm(m);
// where are our four corner points now:
double degreesRad = degrees;
while( degreesRad > 360 )
degreesRad -= 360;
degreesRad *= M_PI / 180.0;
double cosA = cos( degreesRad );
double sinA = sin( degreesRad );
QPoint pTopLeft( 0,
0 );
QPoint pBotLeft( static_cast < int > ( 0 * cosA - txtHeight * sinA ),
static_cast < int > ( txtHeight * cosA + 0 * sinA ) );
QPoint pTopRight( static_cast < int > ( txtWidth * cosA - 0 * sinA ),
static_cast < int > ( 0 * cosA + txtWidth * sinA ) );
QPoint pBotRight( static_cast < int > ( txtWidth * cosA - txtHeight * sinA ),
static_cast < int > ( txtHeight * cosA + txtWidth * sinA ) );
// make our four corner points relative
// to the bounding rect of the rotated pixmap
{
QPoint pDeltaTL( QMIN(0, QMIN(pBotLeft.x(), QMIN(pTopRight.x(), pBotRight.x()))),
QMIN(0, QMIN(pBotLeft.y(), QMIN(pTopRight.y(), pBotRight.y()))) );
pTopLeft -= pDeltaTL;
pBotLeft -= pDeltaTL;
pTopRight -= pDeltaTL;
pBotRight -= pDeltaTL;
}
/*
painter->setPen( QColor( Qt::black ) );
painter->drawLine( x-13, y, x+13, y );
painter->drawLine( x, y-13, x, y+13 );
painter->setPen( QColor( Qt::blue ) );
painter->drawLine( x+pTopLeft.x()-3, y+pTopLeft.y(), x+pTopLeft.x()+3, y+pTopLeft.y() );
painter->drawLine( x+pTopLeft.x(), y+pTopLeft.y()-3, x+pTopLeft.x(), y+pTopLeft.y()+3 );
painter->setPen( QColor( Qt::red ) );
painter->drawLine( x+pTopRight.x()-3, y+pTopRight.y(), x+pTopRight.x()+3, y+pTopRight.y() );
painter->drawLine( x+pTopRight.x(), y+pTopRight.y()-3, x+pTopRight.x(), y+pTopRight.y()+3 );
painter->setPen( QColor( Qt::green ) );
painter->drawLine( x+pBotLeft.x()-3, y+pBotLeft.y(), x+pBotLeft.x()+3, y+pBotLeft.y() );
painter->drawLine( x+pBotLeft.x(), y+pBotLeft.y()-3, x+pBotLeft.x(), y+pBotLeft.y()+3 );
painter->setPen( QColor( Qt::yellow ) );
painter->drawLine( x+pBotRight.x()-3, y+pBotRight.y(), x+pBotRight.x()+3, y+pBotRight.y() );
painter->drawLine( x+pBotRight.x(), y+pBotRight.y()-3, x+pBotRight.x(), y+pBotRight.y()+3 );
*/
// The horizontal and vertical alignment together define one of
// NINE possible points: this point must be moved on the anchor.
int hAlign = align & ( Qt::AlignLeft | Qt::AlignRight | Qt::AlignHCenter );
int vAlign = align & ( Qt::AlignTop | Qt::AlignBottom | Qt::AlignVCenter );
QPoint pixPoint;
switch( hAlign ) {
case Qt::AlignLeft:
switch( vAlign ) {
case Qt::AlignTop:
pixPoint = pTopLeft;
break;
case Qt::AlignBottom:
pixPoint = pBotLeft;
break;
case Qt::AlignVCenter:
default:
pixPoint = QPoint( (pTopLeft.x() + pBotLeft.x()) / 2,
(pTopLeft.y() + pBotLeft.y()) / 2 );
break;
}
break;
case Qt::AlignRight:
switch( vAlign ) {
case Qt::AlignTop:
pixPoint = pTopRight;
break;
case Qt::AlignBottom:
pixPoint = pBotRight;
break;
case Qt::AlignVCenter:
default:
pixPoint = QPoint( (pTopRight.x() + pBotRight.x()) / 2,
(pTopRight.y() + pBotRight.y()) / 2 );
break;
}
break;
case Qt::AlignHCenter:
default:
switch( vAlign ) {
case Qt::AlignTop:
pixPoint = QPoint( (pTopLeft.x() + pTopRight.x()) / 2,
(pTopLeft.y() + pTopRight.y()) / 2 );
break;
case Qt::AlignBottom:
pixPoint = QPoint( (pBotLeft.x() + pBotRight.x()) / 2,
(pBotLeft.y() + pBotRight.y()) / 2 );
break;
case Qt::AlignVCenter:
default:
pixPoint = QPoint( (pTopLeft.x() + pBotRight.x()) / 2,
(pTopLeft.y() + pBotRight.y()) / 2 );
break;
}
break;
}
//qDebug("2.: (x / y) : "+QString::number(x)
// +" / "+QString::number(y));
painter->drawPixmap( QPoint( x - pixPoint.x(),
y - pixPoint.y() ),
theRotatedPixmap );
p.end();
}
}
if( mustBackrotate )
painter->rotate( -degrees );
if( fontChanged )
painter->setFont( oldFont );
}