kchart

KDDrawText.cpp
1/*
2 KDChart - a multi-platform charting engine
3 */
4
5/****************************************************************************
6 ** Copyright (C) 2001-2003 Klarälvdalens Datakonsult AB. All rights reserved.
7 **
8 ** This file is part of the KDChart library.
9 **
10 ** This file may be distributed and/or modified under the terms of the
11 ** GNU General Public License version 2 as published by the Free Software
12 ** Foundation and appearing in the file LICENSE.GPL included in the
13 ** packaging of this file.
14 **
15 ** Licensees holding valid commercial KDChart licenses may use this file in
16 ** accordance with the KDChart Commercial License Agreement provided with
17 ** the Software.
18 **
19 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
20 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
21 **
22 ** See http://www.klaralvdalens-datakonsult.se/?page=products for
23 ** information about KDChart Commercial License Agreements.
24 **
25 ** Contact info@klaralvdalens-datakonsult.se if any conditions of this
26 ** licensing are not clear to you.
27 **
28 **********************************************************************/
29#include <tqpainter.h>
30#include <tqbitmap.h>
31#include <tqpixmap.h>
32#include <math.h>
33#include <limits.h>
34
35#include <KDDrawText.h>
36
37#ifndef M_PI
38#define M_PI 3.14159265358979323846
39#endif
40
41void KDDrawText::drawRotatedText( TQPainter* painter,
42 float degrees,
43 TQPoint anchor,
44 const TQString& text,
45 const TQFont* font,
46 int align,
47 bool showAnchor,
48 const TQFontMetrics* fontMet,
49 bool noFirstrotate,
50 bool noBackrotate,
51 KDDrawTextRegionAndTrueRect* infos,
52 bool optimizeOutputForScreen )
53{
54 drawRotatedTxt( painter,
55 optimizeOutputForScreen,
56 degrees,
57 anchor,
58 text,
59 font,
60 align,
61 showAnchor,
62 INT_MAX,
63 INT_MAX,
64 fontMet,
65 false,
66 0 != infos,
67 noFirstrotate,
68 noBackrotate,
69 infos );
70}
71
72
73KDDrawTextRegionAndTrueRect KDDrawText::measureRotatedText(
74 TQPainter* painter,
75 float degrees,
76 TQPoint anchor,
77 const TQString& text,
78 const TQFont* font,
79 int align,
80 const TQFontMetrics* fontMet,
81 bool noFirstrotate,
82 bool noBackrotate,
83 int addPercentOfHeightToRegion )
84{
85 KDDrawTextRegionAndTrueRect infos;
86 drawRotatedTxt( painter,
87 false,
88 degrees,
89 anchor,
90 text,
91 font,
92 align,
93 false,
94 INT_MAX,
95 INT_MAX,
96 fontMet,
97 true,
98 false,
99 noFirstrotate,
100 noBackrotate,
101 &infos,
102 addPercentOfHeightToRegion );
103 return infos;
104}
105
106
107void KDDrawText::drawRotatedTxt( TQPainter* painter,
108 bool optimizeOutputForScreen,
109 float degrees,
110 TQPoint anchor,
111 const TQString& text,
112 const TQFont* font,
113 int align,
114 bool showAnchor,
115 int txtWidth,
116 int txtHeight,
117 const TQFontMetrics* fontMet,
118 bool calculateOnly,
119 bool doNotCalculate,
120 bool noFirstrotate,
121 bool noBackrotate,
122 KDDrawTextRegionAndTrueRect* infos,
123 int addPercentOfHeightToRegion )
124{
125
126// showAnchor=true;
127 //tqDebug("\nanchor: "+ text + " / "+TQString::number(anchor.x())
128 // +" / "+TQString::number(anchor.y()));
129 bool useInfos = doNotCalculate && infos;
130 bool fontChanged = ( 0 != font );
131 TQFont oldFont;
132 if( fontChanged ) {
133 oldFont = painter->font();
134 painter->setFont( *font );
135 }
136 else
137 font = static_cast<const TQFont*>(&painter->font());
138
139 bool mustBackrotate = false;
140 if( !optimizeOutputForScreen && !noFirstrotate ){
141 painter->rotate( degrees );
142 if( !noBackrotate )
143 mustBackrotate = true;
144 }
145
146 TQPoint pos = useInfos ? infos->pos : painter->xFormDev( anchor );
147
148 if( useInfos )
149 {
150 txtWidth = infos->width;
151 txtHeight = infos->height;
152 }
153 else
154 {
155 int newHeight=0;
156
157 // a bug in the AIX 5.2 compiler means using (?:) syntax doesn't work here
158 // therefor we do it the following way:
159 TQFontMetrics* pFM=0;
160 if( ! pFM ) {
161 pFM = new TQFontMetrics( painter->fontMetrics() );
162 } else {
163 pFM = const_cast<TQFontMetrics*>(fontMet);
164 }
165
166 int nLF = text.contains('\n');
167 if( INT_MAX == txtWidth ) {
168 if( nLF ){
169 int tw;
170 txtWidth = 0;
171 int i0 = 0;
172 int iLF = text.find('\n');
173 while( -1 != iLF ){
174 const TQRect r(pFM->boundingRect( text.mid(i0, iLF-i0) ));
175 tw = r.width()+ 2;
176 newHeight = r.height();
177 if( tw > txtWidth )
178 txtWidth = tw;
179 i0 = iLF+1;
180 iLF = text.find('\n', i0);
181 }
182 if( iLF < (int)text.length() ){
183 const TQRect r(pFM->boundingRect( text.mid( i0 ) ));
184 tw = r.width()+2;
185 newHeight = r.height();
186 if( tw > txtWidth )
187 txtWidth = tw;
188 i0 = iLF+1;
189 }
190 }else{
191 const TQRect r(painter->boundingRect( 0,0,1,1, TQt::AlignAuto, text ));
192 // correct width and height before painting with 2 unit to avoid truncating.
193 // PENDING Michel - improve
194 txtWidth = r.width()+2;
195 newHeight = r.height()+2;
196 }
197 }
198 if( INT_MAX == txtWidth || INT_MAX == txtHeight ) {
199 txtHeight = newHeight ? newHeight : pFM->height() * (1+nLF);
200 }
201 if( pFM != fontMet )
202 delete pFM;
203 if( infos ) {
204 infos->pos = pos;
205 // PENDING infos
206 infos->width = txtWidth;
207 infos->height = txtHeight;
208 }
209 }
210 if( showAnchor ) {
211 int d = txtHeight/4;
212 TQPen savePen = painter->pen();
213 painter->setPen( TQColor( TQt::darkRed ) );
214 painter->drawLine( pos.x(), pos.y()-d,
215 pos.x(), pos.y()+d );
216 painter->drawLine( pos.x()-d, pos.y(),
217 pos.x()+d, pos.y() );
218 painter->setPen( savePen );
219 }
220 int x = useInfos ? infos->x : pos.x();
221 int y = useInfos ? infos->y : pos.y();
222 //tqDebug("1.: (x / y) :" + text + " / "+TQString::number(x)
223 // +" / "+TQString::number(y));
224 //tqDebug("2.: (posx / posy) :" + text );
225 // tqDebug ( "%d", pos.x() ); tqDebug ( "%d", pos.y() );
226 //tqDebug("3.: (infosx / infosy) :" + text + " / "+TQString::number(infos->x)
227 // +" / "+TQString::number(infos->y));
228
229 if( !useInfos && !optimizeOutputForScreen ) {
230 switch( align & ( TQt::AlignLeft | TQt::AlignRight | TQt::AlignHCenter ) ) {
231 case TQt::AlignLeft:
232 break;
233 case TQt::AlignRight:
234//tqDebug( TQPaintDeviceMetrics::logicalDpiX() );
235 x -= txtWidth;
236 break;
237 case TQt::AlignHCenter:
238 x -= txtWidth - txtWidth/2;
239 break;
240 }
241 switch( align & ( TQt::AlignTop | TQt::AlignBottom | TQt::AlignVCenter ) ) {
242 case TQt::AlignTop:
243 break;
244 case TQt::AlignBottom:
245 y -= txtHeight;
246 break;
247 case TQt::AlignVCenter:
248 y -= txtHeight/2;
249 break;
250 }
251 }
252 if( infos && !useInfos ) {
253 painter->xForm( pos );
254 infos->x = x - 4;
255 infos->y = y - 4;
256 //PENDING Michel updating info using x , y from pos
257 //tqDebug("4.: (infosx / infosy) :" + text + " / "+TQString::number(infos->x)
258 //+" / "+TQString::number(infos->y));
259 //tqDebug("5.: (x / y) :" + text + " / "+TQString::number(x)
260 // +" / "+TQString::number(y));
261 //tqDebug("6.: (anchorx /anchory) :" + text + " / "+TQString::number(x)
262 // +" / "+TQString::number(y));
263 TQRect rect( painter->boundingRect( x, y,
264 txtWidth, txtHeight,
265 TQt::AlignLeft + TQt::AlignTop,
266 text ) );
267 //painter->fillRect (rect, TQt::blue );
268
269 TQPoint topLeft( painter->xForm( rect.topLeft() ) );
270 TQPoint topRight( painter->xForm( rect.topRight() ) );
271 TQPoint bottomRight( painter->xForm( rect.bottomRight() ) );
272 TQPoint bottomLeft( painter->xForm( rect.bottomLeft() ) );
273
274 int additor = addPercentOfHeightToRegion * txtHeight / 100;
275 TQPointArray points;
276 points.setPoints( 4, topLeft.x()-additor, topLeft.y()-additor,
277 topRight.x()+additor, topRight.y()-additor,
278 bottomRight.x()+additor, bottomRight.y()+additor,
279 bottomLeft.x()-additor, bottomLeft.y()+additor );
280 infos->region = TQRegion( points );
281 }
282
283 // When the TQt initialization bug is fixed the following scope
284 // will be put into an "if( showAnchor )" entirely.
285 {
286 int d = txtHeight/4;
287 TQPen savePen = painter->pen();
288 if( showAnchor ) {
289 painter->setPen( TQColor( TQt::blue ) );
290 painter->drawLine( x, y-d,
291 x, y+d );
292 painter->drawLine( x-d, y,
293 x+d, y );
294 painter->setPen( TQColor( TQt::darkGreen ) );
295 painter->drawRect(x,y,txtWidth,txtHeight);
296 //painter->drawText( x, y-d, text);
297
298/*
299 }else{
300 // Working around a strange TQt bug: Rotated painter must be
301 // initialized by drawing before text can be painted there.
302 painter->setPen( TQColor( TQt::white ) );
303 painter->drawLine( 30000,0,30001,0 );
304*/
305 }
306 painter->setPen( savePen );
307 }
308
309 if( mustBackrotate && optimizeOutputForScreen ){
310 painter->rotate( -degrees );
311 mustBackrotate = false;
312 }
313
314 if( !calculateOnly ){
315 //tqDebug("txtWidth: %i txtHeight: %i", txtWidth, txtHeight);
316 if( !optimizeOutputForScreen ){
317/*
318 painter->drawText( x, y,
319 txtWidth, txtHeight,
320 TQt::AlignLeft + TQt::AlignTop,
321 text );
322*/
323 painter->drawText( x, y,
324 txtWidth, txtHeight,
325 TQt::AlignLeft + TQt::AlignTop,
326 text );
327/*
328 painter->drawText( x, y,
329 text,
330 -1,
331 TQt::AlignRight + TQt::AlignTop );
332*/
333 }else{
334 // new code (rotating the text ourselves for better quality on screens)
335 TQPixmap pm( txtWidth+2, txtHeight+2, 1 );
336 // note: When using colored axis labels it will be necessary
337 // to change this code and use a 256 color pixmap instead
338 // of a monochrome one. (khz, 2002/08/15)
339 pm.fill(TQt::color0);
340 TQPainter p;
341 p.begin( &pm );
342 if( showAnchor ){
343 p.drawRect(0,0, txtWidth,txtHeight);
344 p.drawLine(0,0, txtWidth,txtHeight);
345 p.drawLine(0,txtHeight, txtWidth,0);
346 }
347 p.setFont(painter->font());
348
349 p.drawText( 0, 0, txtWidth, txtHeight,
350 TQt::AlignLeft + TQt::AlignTop,
351 text );
352/*
353 p.drawText( 0,0,
354 text,
355 -1,
356 TQt::AlignLeft + TQt::AlignTop );
357*/
358
359 TQBitmap mask;
360 mask = pm;
361 pm.setMask( mask );
362 TQWMatrix m;
363 m.rotate( degrees );
364 TQPixmap theRotatedPixmap = pm.xForm(m);
365
366 // where are our four corner points now:
367 double degreesRad = degrees;
368 while( degreesRad > 360 )
369 degreesRad -= 360;
370 degreesRad *= M_PI / 180.0;
371 double cosA = cos( degreesRad );
372 double sinA = sin( degreesRad );
373 TQPoint pTopLeft( 0,
374 0 );
375 TQPoint pBotLeft( static_cast < int > ( 0 * cosA - txtHeight * sinA ),
376 static_cast < int > ( txtHeight * cosA + 0 * sinA ) );
377 TQPoint pTopRight( static_cast < int > ( txtWidth * cosA - 0 * sinA ),
378 static_cast < int > ( 0 * cosA + txtWidth * sinA ) );
379 TQPoint pBotRight( static_cast < int > ( txtWidth * cosA - txtHeight * sinA ),
380 static_cast < int > ( txtHeight * cosA + txtWidth * sinA ) );
381
382 // make our four corner points relative
383 // to the bounding rect of the rotated pixmap
384 {
385 TQPoint pDeltaTL( TQMIN(0, TQMIN(pBotLeft.x(), TQMIN(pTopRight.x(), pBotRight.x()))),
386 TQMIN(0, TQMIN(pBotLeft.y(), TQMIN(pTopRight.y(), pBotRight.y()))) );
387 pTopLeft -= pDeltaTL;
388 pBotLeft -= pDeltaTL;
389 pTopRight -= pDeltaTL;
390 pBotRight -= pDeltaTL;
391 }
392
393 /*
394 painter->setPen( TQColor( TQt::black ) );
395 painter->drawLine( x-13, y, x+13, y );
396 painter->drawLine( x, y-13, x, y+13 );
397 painter->setPen( TQColor( TQt::blue ) );
398 painter->drawLine( x+pTopLeft.x()-3, y+pTopLeft.y(), x+pTopLeft.x()+3, y+pTopLeft.y() );
399 painter->drawLine( x+pTopLeft.x(), y+pTopLeft.y()-3, x+pTopLeft.x(), y+pTopLeft.y()+3 );
400 painter->setPen( TQColor( TQt::red ) );
401 painter->drawLine( x+pTopRight.x()-3, y+pTopRight.y(), x+pTopRight.x()+3, y+pTopRight.y() );
402 painter->drawLine( x+pTopRight.x(), y+pTopRight.y()-3, x+pTopRight.x(), y+pTopRight.y()+3 );
403 painter->setPen( TQColor( TQt::green ) );
404 painter->drawLine( x+pBotLeft.x()-3, y+pBotLeft.y(), x+pBotLeft.x()+3, y+pBotLeft.y() );
405 painter->drawLine( x+pBotLeft.x(), y+pBotLeft.y()-3, x+pBotLeft.x(), y+pBotLeft.y()+3 );
406 painter->setPen( TQColor( TQt::yellow ) );
407 painter->drawLine( x+pBotRight.x()-3, y+pBotRight.y(), x+pBotRight.x()+3, y+pBotRight.y() );
408 painter->drawLine( x+pBotRight.x(), y+pBotRight.y()-3, x+pBotRight.x(), y+pBotRight.y()+3 );
409 */
410
411 // The horizontal and vertical alignment together define one of
412 // NINE possible points: this point must be moved on the anchor.
413 int hAlign = align & ( TQt::AlignLeft | TQt::AlignRight | TQt::AlignHCenter );
414 int vAlign = align & ( TQt::AlignTop | TQt::AlignBottom | TQt::AlignVCenter );
415
416 TQPoint pixPoint;
417 switch( hAlign ) {
418 case TQt::AlignLeft:
419 switch( vAlign ) {
420 case TQt::AlignTop:
421 pixPoint = pTopLeft;
422 break;
423 case TQt::AlignBottom:
424 pixPoint = pBotLeft;
425 break;
426 case TQt::AlignVCenter:
427 default:
428 pixPoint = TQPoint( (pTopLeft.x() + pBotLeft.x()) / 2,
429 (pTopLeft.y() + pBotLeft.y()) / 2 );
430 break;
431 }
432 break;
433 case TQt::AlignRight:
434 switch( vAlign ) {
435 case TQt::AlignTop:
436 pixPoint = pTopRight;
437 break;
438 case TQt::AlignBottom:
439 pixPoint = pBotRight;
440 break;
441 case TQt::AlignVCenter:
442 default:
443 pixPoint = TQPoint( (pTopRight.x() + pBotRight.x()) / 2,
444 (pTopRight.y() + pBotRight.y()) / 2 );
445 break;
446 }
447 break;
448 case TQt::AlignHCenter:
449 default:
450 switch( vAlign ) {
451 case TQt::AlignTop:
452 pixPoint = TQPoint( (pTopLeft.x() + pTopRight.x()) / 2,
453 (pTopLeft.y() + pTopRight.y()) / 2 );
454 break;
455 case TQt::AlignBottom:
456 pixPoint = TQPoint( (pBotLeft.x() + pBotRight.x()) / 2,
457 (pBotLeft.y() + pBotRight.y()) / 2 );
458 break;
459 case TQt::AlignVCenter:
460 default:
461 pixPoint = TQPoint( (pTopLeft.x() + pBotRight.x()) / 2,
462 (pTopLeft.y() + pBotRight.y()) / 2 );
463 break;
464 }
465 break;
466 }
467 //tqDebug("2.: (x / y) : "+TQString::number(x)
468 // +" / "+TQString::number(y));
469 painter->drawPixmap( TQPoint( x - pixPoint.x(),
470 y - pixPoint.y() ),
471 theRotatedPixmap );
472 p.end();
473 }
474 }
475
476 if( mustBackrotate )
477 painter->rotate( -degrees );
478
479 if( fontChanged )
480 painter->setFont( oldFont );
481}
482
483