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.
484 lines
18 KiB
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 );
|
|
}
|
|
|
|
|