/* 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 #include #include #include #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif void KDDrawText::drawRotatedText( TQPainter* painter, float degrees, TQPoint anchor, const TQString& text, const TQFont* font, int align, bool showAnchor, const TQFontMetrics* 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( TQPainter* painter, float degrees, TQPoint anchor, const TQString& text, const TQFont* font, int align, const TQFontMetrics* 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( TQPainter* painter, bool optimizeOutputForScreen, float degrees, TQPoint anchor, const TQString& text, const TQFont* font, int align, bool showAnchor, int txtWidth, int txtHeight, const TQFontMetrics* fontMet, bool calculateOnly, bool doNotCalculate, bool noFirstrotate, bool noBackrotate, KDDrawTextRegionAndTrueRect* infos, int addPercentOfHeightToRegion ) { // showAnchor=true; //tqDebug("\nanchor: "+ text + " / "+TQString::number(anchor.x()) // +" / "+TQString::number(anchor.y())); bool useInfos = doNotCalculate && infos; bool fontChanged = ( 0 != font ); TQFont oldFont; if( fontChanged ) { oldFont = painter->font(); painter->setFont( *font ); } else font = static_cast(&painter->font()); bool mustBackrotate = false; if( !optimizeOutputForScreen && !noFirstrotate ){ painter->rotate( degrees ); if( !noBackrotate ) mustBackrotate = true; } TQPoint 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: TQFontMetrics* pFM=0; if( ! pFM ) { pFM = new TQFontMetrics( painter->fontMetrics() ); } else { pFM = const_cast(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 TQRect 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 TQRect r(pFM->boundingRect( text.mid( i0 ) )); tw = r.width()+2; newHeight = r.height(); if( tw > txtWidth ) txtWidth = tw; i0 = iLF+1; } }else{ const TQRect r(painter->boundingRect( 0,0,1,1, TQt::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; TQPen savePen = painter->pen(); painter->setPen( TQColor( TQt::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(); //tqDebug("1.: (x / y) :" + text + " / "+TQString::number(x) // +" / "+TQString::number(y)); //tqDebug("2.: (posx / posy) :" + text ); // tqDebug ( "%d", pos.x() ); tqDebug ( "%d", pos.y() ); //tqDebug("3.: (infosx / infosy) :" + text + " / "+TQString::number(infos->x) // +" / "+TQString::number(infos->y)); if( !useInfos && !optimizeOutputForScreen ) { switch( align & ( TQt::AlignLeft | TQt::AlignRight | TQt::AlignHCenter ) ) { case TQt::AlignLeft: break; case TQt::AlignRight: //tqDebug( TQPaintDeviceMetrics::logicalDpiX() ); x -= txtWidth; break; case TQt::AlignHCenter: x -= txtWidth - txtWidth/2; break; } switch( align & ( TQt::AlignTop | TQt::AlignBottom | TQt::AlignVCenter ) ) { case TQt::AlignTop: break; case TQt::AlignBottom: y -= txtHeight; break; case TQt::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 //tqDebug("4.: (infosx / infosy) :" + text + " / "+TQString::number(infos->x) //+" / "+TQString::number(infos->y)); //tqDebug("5.: (x / y) :" + text + " / "+TQString::number(x) // +" / "+TQString::number(y)); //tqDebug("6.: (anchorx /anchory) :" + text + " / "+TQString::number(x) // +" / "+TQString::number(y)); TQRect rect( painter->boundingRect( x, y, txtWidth, txtHeight, TQt::AlignLeft + TQt::AlignTop, text ) ); //painter->fillRect (rect, TQt::blue ); TQPoint topLeft( painter->xForm( rect.topLeft() ) ); TQPoint topRight( painter->xForm( rect.topRight() ) ); TQPoint bottomRight( painter->xForm( rect.bottomRight() ) ); TQPoint bottomLeft( painter->xForm( rect.bottomLeft() ) ); int additor = addPercentOfHeightToRegion * txtHeight / 100; TQPointArray 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 = TQRegion( points ); } // When the TQt initialization bug is fixed the following scope // will be put into an "if( showAnchor )" entirely. { int d = txtHeight/4; TQPen savePen = painter->pen(); if( showAnchor ) { painter->setPen( TQColor( TQt::blue ) ); painter->drawLine( x, y-d, x, y+d ); painter->drawLine( x-d, y, x+d, y ); painter->setPen( TQColor( TQt::darkGreen ) ); painter->drawRect(x,y,txtWidth,txtHeight); //painter->drawText( x, y-d, text); /* }else{ // Working around a strange TQt bug: Rotated painter must be // initialized by drawing before text can be painted there. painter->setPen( TQColor( TQt::white ) ); painter->drawLine( 30000,0,30001,0 ); */ } painter->setPen( savePen ); } if( mustBackrotate && optimizeOutputForScreen ){ painter->rotate( -degrees ); mustBackrotate = false; } if( !calculateOnly ){ //tqDebug("txtWidth: %i txtHeight: %i", txtWidth, txtHeight); if( !optimizeOutputForScreen ){ /* painter->drawText( x, y, txtWidth, txtHeight, TQt::AlignLeft + TQt::AlignTop, text ); */ painter->drawText( x, y, txtWidth, txtHeight, TQt::AlignLeft + TQt::AlignTop, text ); /* painter->drawText( x, y, text, -1, TQt::AlignRight + TQt::AlignTop ); */ }else{ // new code (rotating the text ourselves for better quality on screens) TQPixmap 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(TQt::color0); TQPainter 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, TQt::AlignLeft + TQt::AlignTop, text ); /* p.drawText( 0,0, text, -1, TQt::AlignLeft + TQt::AlignTop ); */ TQBitmap mask; mask = pm; pm.setMask( mask ); TQWMatrix m; m.rotate( degrees ); TQPixmap 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 ); TQPoint pTopLeft( 0, 0 ); TQPoint pBotLeft( static_cast < int > ( 0 * cosA - txtHeight * sinA ), static_cast < int > ( txtHeight * cosA + 0 * sinA ) ); TQPoint pTopRight( static_cast < int > ( txtWidth * cosA - 0 * sinA ), static_cast < int > ( 0 * cosA + txtWidth * sinA ) ); TQPoint 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 { TQPoint pDeltaTL( TQMIN(0, TQMIN(pBotLeft.x(), TQMIN(pTopRight.x(), pBotRight.x()))), TQMIN(0, TQMIN(pBotLeft.y(), TQMIN(pTopRight.y(), pBotRight.y()))) ); pTopLeft -= pDeltaTL; pBotLeft -= pDeltaTL; pTopRight -= pDeltaTL; pBotRight -= pDeltaTL; } /* painter->setPen( TQColor( TQt::black ) ); painter->drawLine( x-13, y, x+13, y ); painter->drawLine( x, y-13, x, y+13 ); painter->setPen( TQColor( TQt::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( TQColor( TQt::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( TQColor( TQt::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( TQColor( TQt::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 & ( TQt::AlignLeft | TQt::AlignRight | TQt::AlignHCenter ); int vAlign = align & ( TQt::AlignTop | TQt::AlignBottom | TQt::AlignVCenter ); TQPoint pixPoint; switch( hAlign ) { case TQt::AlignLeft: switch( vAlign ) { case TQt::AlignTop: pixPoint = pTopLeft; break; case TQt::AlignBottom: pixPoint = pBotLeft; break; case TQt::AlignVCenter: default: pixPoint = TQPoint( (pTopLeft.x() + pBotLeft.x()) / 2, (pTopLeft.y() + pBotLeft.y()) / 2 ); break; } break; case TQt::AlignRight: switch( vAlign ) { case TQt::AlignTop: pixPoint = pTopRight; break; case TQt::AlignBottom: pixPoint = pBotRight; break; case TQt::AlignVCenter: default: pixPoint = TQPoint( (pTopRight.x() + pBotRight.x()) / 2, (pTopRight.y() + pBotRight.y()) / 2 ); break; } break; case TQt::AlignHCenter: default: switch( vAlign ) { case TQt::AlignTop: pixPoint = TQPoint( (pTopLeft.x() + pTopRight.x()) / 2, (pTopLeft.y() + pTopRight.y()) / 2 ); break; case TQt::AlignBottom: pixPoint = TQPoint( (pBotLeft.x() + pBotRight.x()) / 2, (pBotLeft.y() + pBotRight.y()) / 2 ); break; case TQt::AlignVCenter: default: pixPoint = TQPoint( (pTopLeft.x() + pBotRight.x()) / 2, (pTopLeft.y() + pBotRight.y()) / 2 ); break; } break; } //tqDebug("2.: (x / y) : "+TQString::number(x) // +" / "+TQString::number(y)); painter->drawPixmap( TQPoint( x - pixPoint.x(), y - pixPoint.y() ), theRotatedPixmap ); p.end(); } } if( mustBackrotate ) painter->rotate( -degrees ); if( fontChanged ) painter->setFont( oldFont ); }