kchart

KDDrawText.cpp

00001 /* -*- Mode: C++ -*-
00002    KDChart - a multi-platform charting engine
00003    */
00004 
00005 /****************************************************************************
00006  ** Copyright (C) 2001-2003 Klarälvdalens Datakonsult AB.  All rights reserved.
00007  **
00008  ** This file is part of the KDChart library.
00009  **
00010  ** This file may be distributed and/or modified under the terms of the
00011  ** GNU General Public License version 2 as published by the Free Software
00012  ** Foundation and appearing in the file LICENSE.GPL included in the
00013  ** packaging of this file.
00014  **
00015  ** Licensees holding valid commercial KDChart licenses may use this file in
00016  ** accordance with the KDChart Commercial License Agreement provided with
00017  ** the Software.
00018  **
00019  ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020  ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00021  **
00022  ** See http://www.klaralvdalens-datakonsult.se/?page=products for
00023  **   information about KDChart Commercial License Agreements.
00024  **
00025  ** Contact info@klaralvdalens-datakonsult.se if any conditions of this
00026  ** licensing are not clear to you.
00027  **
00028  **********************************************************************/
00029 #include <qpainter.h>
00030 #include <qbitmap.h>
00031 #include <qpixmap.h>
00032 #include <math.h>
00033 #include <limits.h>
00034 
00035 #include <KDDrawText.h>
00036 
00037 #ifndef M_PI
00038 #define M_PI 3.14159265358979323846
00039 #endif
00040 
00041 void KDDrawText::drawRotatedText( QPainter* painter,
00042         float  degrees,
00043         QPoint anchor,
00044         const QString& text,
00045         const QFont* font,
00046         int align,
00047         bool showAnchor,
00048         const QFontMetrics* fontMet,
00049         bool noFirstrotate,
00050         bool noBackrotate,
00051         KDDrawTextRegionAndTrueRect* infos,
00052         bool optimizeOutputForScreen )
00053 {
00054     drawRotatedTxt( painter,
00055             optimizeOutputForScreen,
00056             degrees,
00057             anchor,
00058             text,
00059             font,
00060             align,
00061             showAnchor,
00062             INT_MAX,
00063             INT_MAX,
00064             fontMet,
00065             false,
00066             0 != infos,
00067             noFirstrotate,
00068             noBackrotate,
00069             infos );
00070 }
00071 
00072 
00073 KDDrawTextRegionAndTrueRect KDDrawText::measureRotatedText(
00074         QPainter* painter,
00075         float  degrees,
00076         QPoint anchor,
00077         const QString& text,
00078         const QFont* font,
00079         int align,
00080         const QFontMetrics* fontMet,
00081         bool noFirstrotate,
00082         bool noBackrotate,
00083         int addPercentOfHeightToRegion )
00084 {
00085     KDDrawTextRegionAndTrueRect infos;
00086     drawRotatedTxt( painter,
00087             false,
00088             degrees,
00089             anchor,
00090             text,
00091             font,
00092             align,
00093             false,
00094             INT_MAX,
00095             INT_MAX,
00096             fontMet,
00097             true,
00098             false,
00099             noFirstrotate,
00100             noBackrotate,
00101             &infos,
00102             addPercentOfHeightToRegion );
00103     return infos;
00104 }
00105 
00106 
00107 void KDDrawText::drawRotatedTxt( QPainter* painter,
00108         bool optimizeOutputForScreen,
00109         float  degrees,
00110         QPoint anchor,
00111         const QString& text,
00112         const QFont* font,
00113         int align,
00114         bool showAnchor,
00115         int txtWidth,
00116         int txtHeight,
00117         const QFontMetrics* fontMet,
00118         bool calculateOnly,
00119         bool doNotCalculate,
00120         bool noFirstrotate,
00121         bool noBackrotate,
00122         KDDrawTextRegionAndTrueRect* infos,
00123         int addPercentOfHeightToRegion )
00124 {
00125  
00126 //    showAnchor=true;
00127   //qDebug("\nanchor: "+ text + " / "+QString::number(anchor.x())
00128   //         +" / "+QString::number(anchor.y()));
00129     bool useInfos = doNotCalculate && infos;
00130     bool fontChanged = ( 0 != font );
00131     QFont oldFont;
00132     if( fontChanged ) {
00133         oldFont = painter->font();
00134         painter->setFont( *font );
00135     }
00136     else
00137         font = &painter->font();
00138 
00139     bool mustBackrotate = false;
00140     if( !optimizeOutputForScreen && !noFirstrotate ){
00141         painter->rotate( degrees );
00142         if( !noBackrotate )
00143             mustBackrotate = true;
00144     }
00145 
00146     QPoint pos = useInfos ? infos->pos : painter->xFormDev( anchor );
00147 
00148     if( useInfos )
00149     {
00150         txtWidth  = infos->width;
00151         txtHeight = infos->height;
00152     }
00153     else
00154     {
00155         int newHeight=0;
00156 
00157         // a bug in the AIX 5.2 compiler means using (?:) syntax doesn't work here
00158         // therefor we do it the following way:
00159         QFontMetrics* pFM=0;
00160         if( ! pFM ) {
00161             pFM = new QFontMetrics( painter->fontMetrics() );
00162         } else {
00163             pFM = const_cast<QFontMetrics*>(fontMet);
00164         }
00165 
00166         int nLF = text.contains('\n');
00167         if( INT_MAX == txtWidth ) {
00168             if( nLF ){
00169                 int tw;
00170                 txtWidth = 0;
00171                 int i0  = 0;
00172                 int iLF = text.find('\n');
00173                 while( -1 != iLF ){
00174                     const QRect r(pFM->boundingRect( text.mid(i0, iLF-i0) ));
00175                     tw = r.width()+ 2;
00176                     newHeight = r.height();
00177                     if( tw > txtWidth )
00178                         txtWidth = tw;
00179                     i0 = iLF+1;
00180                     iLF = text.find('\n', i0);
00181                 }
00182                 if( iLF < (int)text.length() ){
00183                     const QRect r(pFM->boundingRect( text.mid( i0 ) ));
00184                     tw = r.width()+2;
00185                     newHeight = r.height();
00186                     if( tw > txtWidth )
00187                         txtWidth = tw;
00188                     i0 = iLF+1;
00189                 }
00190             }else{
00191                 const QRect r(painter->boundingRect( 0,0,1,1, Qt::AlignAuto, text ));
00192                 // correct width and height before painting with 2 unit to avoid truncating.
00193                 // PENDING Michel - improve
00194                 txtWidth  = r.width()+2;
00195                 newHeight = r.height()+2;
00196             }
00197         }
00198         if( INT_MAX == txtWidth || INT_MAX == txtHeight ) {
00199             txtHeight = newHeight ? newHeight : pFM->height() * (1+nLF);
00200         }
00201         if( pFM != fontMet )
00202             delete pFM;
00203         if( infos ) {
00204             infos->pos    = pos;
00205             // PENDING infos
00206             infos->width = txtWidth;
00207             infos->height = txtHeight;
00208         }
00209     }
00210     if( showAnchor ) {
00211         int d = txtHeight/4;
00212         QPen savePen = painter->pen();
00213         painter->setPen( QColor( Qt::darkRed ) );
00214         painter->drawLine( pos.x(),   pos.y()-d,
00215                            pos.x(),   pos.y()+d );
00216         painter->drawLine( pos.x()-d, pos.y(),
00217                            pos.x()+d, pos.y() );
00218         painter->setPen( savePen );
00219     }
00220     int x = useInfos ? infos->x : pos.x();
00221     int y = useInfos ? infos->y : pos.y();
00222     //qDebug("1.:     (x / y) :" +  text + " / "+QString::number(x)
00223     //       +" / "+QString::number(y));
00224     //qDebug("2.:     (posx / posy) :" +  text );
00225     // qDebug ( "%d", pos.x() ); qDebug ( "%d", pos.y() );
00226     //qDebug("3.:     (infosx / infosy) :" +  text + " / "+QString::number(infos->x)
00227     //           +" / "+QString::number(infos->y));
00228 
00229     if( !useInfos && !optimizeOutputForScreen ) {
00230         switch( align & ( Qt::AlignLeft | Qt::AlignRight | Qt::AlignHCenter ) ) {
00231               case Qt::AlignLeft:
00232                 break;
00233             case Qt::AlignRight:
00234 //qDebug( QPaintDeviceMetrics::logicalDpiX() );
00235                 x -= txtWidth;
00236                 break;
00237             case Qt::AlignHCenter:
00238                 x -= txtWidth - txtWidth/2;
00239                 break;
00240         }
00241         switch( align & ( Qt::AlignTop | Qt::AlignBottom | Qt::AlignVCenter ) ) {
00242             case Qt::AlignTop:
00243                 break;
00244             case Qt::AlignBottom:
00245                 y -= txtHeight;
00246                 break;
00247             case Qt::AlignVCenter:
00248                 y -= txtHeight/2;
00249                 break;
00250         }
00251     }
00252     if( infos && !useInfos ) {
00253          painter->xForm( pos );
00254         infos->x = x - 4;
00255         infos->y = y - 4;
00256         //PENDING Michel updating info using x , y from pos 
00257         //qDebug("4.:     (infosx / infosy) :" +  text + " / "+QString::number(infos->x)
00258     //+" / "+QString::number(infos->y));
00259         //qDebug("5.:  (x / y) :" +  text + " / "+QString::number(x)
00260     //   +" / "+QString::number(y));
00261     //qDebug("6.:  (anchorx /anchory) :" +  text + " / "+QString::number(x)
00262     //   +" / "+QString::number(y));
00263         QRect rect( painter->boundingRect( x, y,
00264                     txtWidth, txtHeight,
00265                     Qt::AlignLeft + Qt::AlignTop,
00266                     text ) );
00267         //painter->fillRect (rect, Qt::blue );
00268         
00269         QPoint topLeft(     painter->xForm( rect.topLeft()     ) );
00270         QPoint topRight(    painter->xForm( rect.topRight()    ) );
00271         QPoint bottomRight( painter->xForm( rect.bottomRight() ) );
00272         QPoint bottomLeft(  painter->xForm( rect.bottomLeft()  ) );
00273       
00274         int additor = addPercentOfHeightToRegion * txtHeight / 100;
00275         QPointArray points;
00276         points.setPoints( 4, topLeft.x()-additor,     topLeft.y()-additor,
00277                 topRight.x()+additor,    topRight.y()-additor,
00278                 bottomRight.x()+additor, bottomRight.y()+additor,
00279                 bottomLeft.x()-additor,  bottomLeft.y()+additor );
00280         infos->region = QRegion( points );
00281     }
00282 
00283     // When the Qt initialization bug is fixed the following scope
00284     // will be put into an "if( showAnchor )" entirely.
00285     {
00286         int d = txtHeight/4;
00287         QPen savePen = painter->pen();
00288         if( showAnchor ) {
00289             painter->setPen( QColor( Qt::blue ) );
00290             painter->drawLine( x,   y-d,
00291                                x,   y+d );
00292             painter->drawLine( x-d, y,
00293                                x+d, y );
00294             painter->setPen( QColor( Qt::darkGreen ) );
00295             painter->drawRect(x,y,txtWidth,txtHeight);
00296             //painter->drawText( x, y-d, text);           
00297         
00298 /*
00299         }else{
00300             // Working around a strange Qt bug: Rotated painter must be
00301             // initialized by drawing before text can be painted there.
00302             painter->setPen( QColor( Qt::white ) );
00303             painter->drawLine( 30000,0,30001,0 );
00304 */
00305         }
00306         painter->setPen( savePen );
00307     }
00308 
00309     if( mustBackrotate && optimizeOutputForScreen ){
00310         painter->rotate( -degrees );
00311         mustBackrotate = false;
00312     }
00313 
00314     if( !calculateOnly ){
00315         //qDebug("txtWidth: %i  txtHeight: %i", txtWidth, txtHeight);
00316         if( !optimizeOutputForScreen ){
00317 /*
00318             painter->drawText( x, y,
00319                                txtWidth, txtHeight,
00320                                Qt::AlignLeft + Qt::AlignTop,
00321                                text );
00322 */
00323             painter->drawText( x, y,
00324                                txtWidth, txtHeight,
00325                                Qt::AlignLeft + Qt::AlignTop,
00326                                text );
00327 /*
00328             painter->drawText( x, y,
00329                                text,
00330                                -1,
00331                                Qt::AlignRight + Qt::AlignTop );
00332 */
00333         }else{
00334             // new code (rotating the text ourselves for better quality on screens)
00335             QPixmap pm( txtWidth+2, txtHeight+2, 1 );
00336             // note: When using colored axis labels it will be necessary
00337             //       to change this code and use a 256 color pixmap instead
00338             //       of a monochrome one.                 (khz, 2002/08/15)
00339             pm.fill(Qt::color0);
00340             QPainter p;
00341             p.begin( &pm );
00342             if( showAnchor ){
00343                 p.drawRect(0,0, txtWidth,txtHeight);
00344                 p.drawLine(0,0, txtWidth,txtHeight);
00345                 p.drawLine(0,txtHeight, txtWidth,0);
00346             }
00347             p.setFont(painter->font());
00348 
00349             p.drawText( 0, 0, txtWidth, txtHeight,
00350                         Qt::AlignLeft + Qt::AlignTop,
00351                         text );
00352 /*
00353             p.drawText( 0,0,
00354                         text,
00355                        -1,
00356                         Qt::AlignLeft + Qt::AlignTop );
00357 */
00358 
00359             QBitmap mask;
00360             mask = pm;
00361             pm.setMask( mask );
00362             QWMatrix m;
00363             m.rotate( degrees );
00364             QPixmap theRotatedPixmap = pm.xForm(m);
00365 
00366             // where are our four corner points now:
00367             double degreesRad = degrees;
00368             while( degreesRad > 360 )
00369                 degreesRad -= 360;
00370             degreesRad *= M_PI / 180.0;
00371             double cosA = cos( degreesRad );
00372             double sinA = sin( degreesRad );
00373             QPoint pTopLeft(  0,
00374                               0 );
00375             QPoint pBotLeft(  static_cast < int > ( 0         * cosA  -  txtHeight * sinA ),
00376                               static_cast < int > ( txtHeight * cosA  +  0         * sinA  ) );
00377             QPoint pTopRight( static_cast < int > ( txtWidth  * cosA  -  0         * sinA ),
00378                               static_cast < int > ( 0         * cosA  +  txtWidth  * sinA  ) );
00379             QPoint pBotRight( static_cast < int > ( txtWidth  * cosA  -  txtHeight * sinA ),
00380                               static_cast < int > ( txtHeight * cosA  +  txtWidth  * sinA  ) );
00381 
00382             // make our four corner points relative
00383             // to the bounding rect of the rotated pixmap
00384             {
00385                 QPoint pDeltaTL( QMIN(0, QMIN(pBotLeft.x(), QMIN(pTopRight.x(), pBotRight.x()))),
00386                                  QMIN(0, QMIN(pBotLeft.y(), QMIN(pTopRight.y(), pBotRight.y()))) );
00387                 pTopLeft  -= pDeltaTL;
00388                 pBotLeft  -= pDeltaTL;
00389                 pTopRight -= pDeltaTL;
00390                 pBotRight -= pDeltaTL;
00391             }
00392 
00393             /*
00394             painter->setPen( QColor( Qt::black ) );
00395             painter->drawLine( x-13,  y,    x+13,  y    );
00396             painter->drawLine( x,     y-13, x,     y+13 );
00397             painter->setPen( QColor( Qt::blue ) );
00398             painter->drawLine( x+pTopLeft.x()-3,   y+pTopLeft.y(),   x+pTopLeft.x()+3,   y+pTopLeft.y()   );
00399             painter->drawLine( x+pTopLeft.x(),     y+pTopLeft.y()-3, x+pTopLeft.x(),     y+pTopLeft.y()+3 );
00400             painter->setPen( QColor( Qt::red ) );
00401             painter->drawLine( x+pTopRight.x()-3,   y+pTopRight.y(),   x+pTopRight.x()+3,   y+pTopRight.y()   );
00402             painter->drawLine( x+pTopRight.x(),     y+pTopRight.y()-3, x+pTopRight.x(),     y+pTopRight.y()+3 );
00403             painter->setPen( QColor( Qt::green ) );
00404             painter->drawLine( x+pBotLeft.x()-3,   y+pBotLeft.y(),   x+pBotLeft.x()+3,   y+pBotLeft.y()   );
00405             painter->drawLine( x+pBotLeft.x(),     y+pBotLeft.y()-3, x+pBotLeft.x(),     y+pBotLeft.y()+3 );
00406             painter->setPen( QColor( Qt::yellow ) );
00407             painter->drawLine( x+pBotRight.x()-3,   y+pBotRight.y(),   x+pBotRight.x()+3,   y+pBotRight.y()   );
00408             painter->drawLine( x+pBotRight.x(),     y+pBotRight.y()-3, x+pBotRight.x(),     y+pBotRight.y()+3 );
00409             */
00410 
00411             // The horizontal and vertical alignment together define one of
00412             // NINE possible points: this point must be moved on the anchor.
00413             int hAlign = align & ( Qt::AlignLeft | Qt::AlignRight  | Qt::AlignHCenter );
00414             int vAlign = align & ( Qt::AlignTop  | Qt::AlignBottom | Qt::AlignVCenter );
00415 
00416             QPoint pixPoint;
00417             switch( hAlign ) {
00418                 case Qt::AlignLeft:
00419                     switch( vAlign ) {
00420                         case Qt::AlignTop:
00421                             pixPoint = pTopLeft;
00422                             break;
00423                         case Qt::AlignBottom:
00424                             pixPoint = pBotLeft;
00425                             break;
00426                         case Qt::AlignVCenter:
00427                         default:
00428                             pixPoint = QPoint( (pTopLeft.x() + pBotLeft.x()) / 2,
00429                                                (pTopLeft.y() + pBotLeft.y()) / 2 );
00430                             break;
00431                     }
00432                     break;
00433                 case Qt::AlignRight:
00434                     switch( vAlign ) {
00435                         case Qt::AlignTop:
00436                             pixPoint = pTopRight;
00437                             break;
00438                         case Qt::AlignBottom:
00439                             pixPoint = pBotRight;
00440                             break;
00441                         case Qt::AlignVCenter:
00442                         default:
00443                             pixPoint = QPoint( (pTopRight.x() + pBotRight.x()) / 2,
00444                                                (pTopRight.y() + pBotRight.y()) / 2 );
00445                             break;
00446                     }
00447                     break;
00448                 case Qt::AlignHCenter:
00449                 default:
00450                     switch( vAlign ) {
00451                         case Qt::AlignTop:
00452                             pixPoint = QPoint( (pTopLeft.x() + pTopRight.x()) / 2,
00453                                                (pTopLeft.y() + pTopRight.y()) / 2 );
00454                             break;
00455                         case Qt::AlignBottom:
00456                             pixPoint = QPoint( (pBotLeft.x() + pBotRight.x()) / 2,
00457                                                (pBotLeft.y() + pBotRight.y()) / 2 );
00458                             break;
00459                         case Qt::AlignVCenter:
00460                         default:
00461                             pixPoint = QPoint( (pTopLeft.x() + pBotRight.x()) / 2,
00462                                                (pTopLeft.y() + pBotRight.y()) / 2 );
00463                             break;
00464                     }
00465                     break;
00466             }
00467         //qDebug("2.:     (x / y) : "+QString::number(x)
00468         //             +" / "+QString::number(y));
00469             painter->drawPixmap( QPoint( x - pixPoint.x(),
00470                                          y - pixPoint.y() ),
00471                                  theRotatedPixmap );
00472             p.end();
00473         }
00474     }
00475 
00476     if( mustBackrotate )
00477         painter->rotate( -degrees );
00478 
00479     if( fontChanged )
00480         painter->setFont( oldFont );
00481 }
00482 
00483 
KDE Home | KDE Accessibility Home | Description of Access Keys