kchart

KDChartAxesPainter.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 <tqlabel.h>
31
32#include <KDDrawText.h>
33#include "KDChartAxesPainter.h"
34#include "KDChartAxisParams.h"
35#include "KDChartParams.h"
36
37#include <stdlib.h>
38
39
45int secondsSinceUTCStart( const TQDateTime& dt )
46{
47 TQDateTime dtStart( TQDate( 1970, 1, 1 ) );
48 return dtStart.secsTo( dt );
49}
50
51
65KDChartPainter( params )
66{
67 // Intentionally left blank.
68 // We cannot setup the geometry yet
69 // since we do not know the size of the painter.
70}
71
76{
77 // intentionally left blank
78}
79
80
86void reCalculateLabelTexts(
87 TQPainter* painter,
88 const KDChartTableDataBase& data,
89 const KDChartParams& params,
90 uint axisNumber,
91 double averageValueP1000,
92 double delimLen,
93 internal__KDChart__CalcValues& cv )
94{
96 painter,
97 data,
98 params,
99 axisNumber,
100 averageValueP1000,
101 delimLen,
102 // start of reference parameters
103 cv.basicPos,
104 cv.orig,
105 cv.dest,
106 cv.pXDeltaFactor,
107 cv.pYDeltaFactor,
108 cv.pXDelimDeltaFaktor,
109 cv.pYDelimDeltaFaktor,
110 cv.nSubDelimFactor,
111 cv.pDelimDelta,
112 cv.nTxtHeight,
113 cv.pTextsX,
114 cv.pTextsY,
115 cv.pTextsW,
116 cv.pTextsH,
117 cv.textAlign,
118 cv.bLogarithmic,
119 cv.isDateTime,
120 cv.autoDtLabels,
121 cv.dtLow,
122 cv.dtHigh,
123 cv.dtDeltaScale,
124 true,
125 cv.nDelta,
126 cv.nDeltaPix );
127 const KDChartAxisParams & para = params.axisParams( axisNumber );
128 cv.bSteadyCalc = para.axisSteadyValueCalc();
129 cv.bDecreasing = para.axisValuesDecreasing();
130 cv.nLow = para.trueAxisLow();
131 cv.nHigh = para.trueAxisHigh();
132}
133
134
135bool KDChartAxesPainter::calculateAllAxesLabelTextsAndCalcValues(
136 TQPainter* painter,
138 double areaWidthP1000,
139 double areaHeightP1000,
140 double& delimLen)
141{
142 uint iAxis;
143 double averageValueP1000 = TQMIN(areaWidthP1000, areaHeightP1000);//( areaWidthP1000 + areaHeightP1000 ) / 2.0;
144 //tqDebug("KChart::KDChartAxesPainter::calculateAllAxesLabelTextsAndCalcValues() averageValueP1000: %f", averageValueP1000);
145 // length of little delimiter-marks indicating axis scaling
146 delimLen = 20.0 * averageValueP1000; // per mille of area
147
148 // Determine axes calculation values and labels before drawing the axes.
149
150 // step #1: calculate all values independendly from the other axes' values
151 for( iAxis = 0; iAxis < KDCHART_MAX_AXES; ++iAxis )
152 {
153 internal__KDChart__CalcValues& cv = calcVal[iAxis];
154 cv.processThisAxis = ( params()->axisParams( iAxis ).axisVisible()
155 && KDChartAxisParams::AxisTypeUnknown
156 != params()->axisParams( iAxis ).axisType() );
157 if( cv.processThisAxis ){
158 cv.nSubDelimFactor = 0.0;
159 cv.pDelimDelta = 0.0;
160 cv.nTxtHeight = 0.0;
161 cv.pTextsX = 0.0;
162 cv.pTextsY = 0.0;
163 cv.pTextsW = 0.0;
164 cv.pTextsH = 0.0;
165 cv.textAlign = TQt::AlignHCenter | TQt::AlignVCenter;
166 cv.isDateTime = false;
167 cv.autoDtLabels = false;
168 calculateLabelTexts( painter,
169 *data,
170 *params(),
171 iAxis,
172 averageValueP1000,
173 delimLen,
174 // start of reference parameters
175 cv.basicPos,
176 cv.orig,
177 cv.dest,
178 cv.pXDeltaFactor,
179 cv.pYDeltaFactor,
180 cv.pXDelimDeltaFaktor,
181 cv.pYDelimDeltaFaktor,
182 cv.nSubDelimFactor,
183 cv.pDelimDelta,
184 cv.nTxtHeight,
185 cv.pTextsX,
186 cv.pTextsY,
187 cv.pTextsW,
188 cv.pTextsH,
189 cv.textAlign,
190 cv.bLogarithmic,
191 cv.isDateTime,
192 cv.autoDtLabels,
193 cv.dtLow,
194 cv.dtHigh,
195 cv.dtDeltaScale );
196 const KDChartAxisParams & para = params()->axisParams( iAxis );
197 cv.bSteadyCalc = para.axisSteadyValueCalc();
198 cv.bDecreasing = para.axisValuesDecreasing();
199 cv.nLow = para.trueAxisLow();
200 cv.nHigh = para.trueAxisHigh();
201 cv.nDelta = para.trueAxisDelta();
202 cv.nDeltaPix = para.trueAxisDeltaPixels();
203 cv.pLastX = cv.dest.x();
204 cv.pLastY = cv.dest.y();
205 }
206 }
207
208 // step #2: if isometric axes are desired adjust/re-calculate some values
209 for ( iAxis = 0; iAxis < KDCHART_MAX_AXES; ++iAxis ){
210 internal__KDChart__CalcValues& cv = calcVal[iAxis];
211 if( cv.processThisAxis
212 && cv.bSteadyCalc ){
213 const KDChartAxisParams & para = params()->axisParams( iAxis );
214 const uint isoRef = para.isometricReferenceAxis();
215 if( KDCHART_NO_AXIS != isoRef
216 && iAxis != isoRef
217 && ( KDCHART_MAX_AXES > isoRef
218 || KDCHART_ALL_AXES == isoRef ) ){
219 if( KDCHART_ALL_AXES == isoRef ){
220 uint iAxis2;
221 // first find the axis values to be taken as reference
222 double nDelta = cv.nDelta;
223 double nDeltaPix = cv.nDeltaPix;
224 double nSubDelimFactor = cv.nSubDelimFactor;
225 for ( iAxis2 = 0;
226 iAxis2 < KDCHART_MAX_AXES;
227 ++iAxis2 ){
228 internal__KDChart__CalcValues& cv2 = calcVal[iAxis2];
229 if( cv2.processThisAxis
230 && cv2.bSteadyCalc
231 && (0.0 != cv2.nDelta)
232 && (fabs(cv2.nDeltaPix / cv2.nDelta) < fabs(nDeltaPix / nDelta)) ){
233 if( (nDelta >= 0.0) == (cv2.nDelta >= 0.0) )
234 nDelta = cv2.nDelta;
235 else
236 nDelta = cv2.nDelta * -1.0;
237 if( (nDeltaPix >= 0.0) == (cv2.nDeltaPix >= 0.0) )
238 nDeltaPix = cv2.nDeltaPix;
239 else
240 nDeltaPix = cv2.nDeltaPix * -1.0;
241 if( (nSubDelimFactor >= 0.0) == (cv2.nSubDelimFactor >= 0.0) )
242 nSubDelimFactor = cv2.nSubDelimFactor;
243 else
244 nSubDelimFactor = cv2.nSubDelimFactor * -1.0;
245 }
246 }
247 // now adjust all axes (if necessary)
248 for ( iAxis2 = 0;
249 iAxis2 < KDCHART_MAX_AXES;
250 ++iAxis2 ){
251 internal__KDChart__CalcValues& cv2 = calcVal[iAxis2];
252 if( cv2.processThisAxis
253 && cv2.bSteadyCalc
254 && ( fabs(cv2.nDelta) != fabs(nDelta)
255 || fabs(cv2.nDeltaPix) != fabs(nDeltaPix) ) ){
256 //tqDebug("\nrecalculating scale for axis %x", iAxis2);
257 //tqDebug("cv2.nDelta %f cv2.nDeltaPix %f nDelta %f nDeltaPix %f\n",
258 // cv2.nDelta,cv2.nDeltaPix,nDelta,nDeltaPix);
259 if( (cv2.nDelta >= 0.0) == (nDelta >= 0.0) )
260 cv2.nDelta = nDelta;
261 else
262 cv2.nDelta = nDelta * -1.0;
263 if( (cv2.nDeltaPix >= 0.0) == (nDeltaPix >= 0.0) )
264 cv2.nDeltaPix = nDeltaPix;
265 else
266 cv2.nDeltaPix = nDeltaPix * -1.0;
267 reCalculateLabelTexts( painter,
268 *data,
269 *params(),
270 iAxis2,
271 averageValueP1000,
272 delimLen,
273 cv2 );
274 if( (cv2.nSubDelimFactor >= 0.0) == (nSubDelimFactor >= 0.0) )
275 cv2.nSubDelimFactor = nSubDelimFactor;
276 else
277 cv2.nSubDelimFactor = nSubDelimFactor * -1.0;
278 }
279 }
280 }else{
281 internal__KDChart__CalcValues& cv2 = calcVal[isoRef];
282 // adjust this axis or the other axis (if necessary)
283 if( cv2.processThisAxis
284 && cv2.bSteadyCalc
285 && ( cv2.nDelta != cv.nDelta
286 || cv2.nDeltaPix != cv.nDeltaPix ) ){
287 if( cv2.nDelta > cv.nDelta
288 || ( cv2.nDelta == cv.nDelta
289 && cv2.nDeltaPix < cv.nDeltaPix ) ){
290 // adjust this axis
291 //tqDebug("recalculating scale for this axis %x", iAxis);
292 cv.nDelta = cv2.nDelta;
293 cv.nDeltaPix = cv2.nDeltaPix;
294 reCalculateLabelTexts(
295 painter,
296 *data,
297 *params(),
298 iAxis,
299 averageValueP1000,
300 delimLen,
301 cv );
302 cv.nSubDelimFactor = cv2.nSubDelimFactor;
303 }else{
304 // adjust the other axis
305 //tqDebug("\nrecalculating scale for other axis %x", isoRef);
306 //tqDebug("cv2.nDelta %f cv2.nDeltaPix %f cv.nDelta %f cv.nDeltaPix %f",
307 // cv2.nDelta,cv2.nDeltaPix,cv.nDelta,cv.nDeltaPix);
308 cv2.nDelta = cv.nDelta;
309 cv2.nDeltaPix = cv.nDeltaPix;
310 reCalculateLabelTexts(
311 painter,
312 *data,
313 *params(),
314 isoRef,
315 averageValueP1000,
316 delimLen,
317 cv2 );
318 cv2.nSubDelimFactor = cv.nSubDelimFactor;
319 }
320 }
321 }
322 }
323 }
324 }
325 return true;
326}
327
328
335void KDChartAxesPainter::paintAxes( TQPainter* painter,
337{
338 if ( !painter || !data || 0 == params() )
339 return ;
340
341 const bool bMultiRowBarChart = KDChartParams::Bar == params()->chartType() &&
342 KDChartParams::BarMultiRows == params()->barChartSubType();
343
344 double areaWidthP1000 = _logicalWidth / 1000.0;
345 double areaHeightP1000 = _logicalHeight / 1000.0;
346 double averageValueP1000 = TQMIN(areaWidthP1000, areaHeightP1000);//( areaWidthP1000 + areaHeightP1000 ) / 2.0;
347 // length of little delimiter-marks indicating axis scaling
348 double delimLen;
349
350//tqDebug("-------------------------------------------------------------------------------------");
351
352 calculateAllAxesLabelTextsAndCalcValues( painter, data, areaWidthP1000, areaHeightP1000, delimLen );
353
354
355 // Now the labels are known, so let us paint the axes...
356 painter->save();
357 painter->setPen( TQt::NoPen );
358
359 bool screenOutput = params()->optimizeOutputForScreen();
360 uint iAxis;
361
362 for ( iAxis = 0; iAxis < KDCHART_MAX_AXES; ++iAxis ){
363 internal__KDChart__CalcValues& cv = calcVal[iAxis];
364 if( cv.processThisAxis ){
365
366 const KDChartAxisParams & para = params()->axisParams( iAxis );
367
368 internal__KDChart__CalcValues& cv = calcVal[iAxis];
369
370 const TQColor labelsColor( para.axisLabelsColor() );
371
372 // Debugging axis areas:
373 //painter->fillRect(para.axisTrueAreaRect(), TQt::yellow);
374
375 uint lineWidth = 0 <= para.axisLineWidth()
376 ? para.axisLineWidth()
377 : -1 * static_cast < int > ( para.axisLineWidth()
378 * averageValueP1000 );
379 ( ( KDChartAxisParams& ) para ).setAxisTrueLineWidth( lineWidth );
380
381 uint gridLineWidth
383 == para.axisGridLineWidth() )
384 ? lineWidth
385 : ( ( 0 <= para.axisGridLineWidth() )
386 ? para.axisGridLineWidth()
387 : -1 * static_cast < int > ( para.axisGridLineWidth()
388 * averageValueP1000 ) );
389
390 uint gridSubLineWidth
392 == para.axisGridSubLineWidth() )
393 ? lineWidth
394 : ( ( 0 <= para.axisGridSubLineWidth() )
395 ? para.axisGridSubLineWidth()
396 : -1 * static_cast < int > ( para.axisGridSubLineWidth()
397 * averageValueP1000 ) );
398
399 // Magic to find out axis scaling factors and labels text height
400 // =============================================================
401 // - khz, 02/24/2001
402 //
403 // 1st Calculate the axis label texts height regarding to
404 // user-defined per-axis settings.
405 //
406 // 2nd This height is given to calculateLabelTexts() to
407 // calculate the delimiter and sub-delimiter distances as
408 // well as the axis scaling factors.
409 // If neccessary and possible the short replacement strings
410 // are taken that might have been specified by the user.
411 // - see KDChartAxisParams::setAxisLabelStringLists() -
412 //
413 // 3rd Before displaying the texts we make sure they fit into
414 // their space, if needed we will do the following
415 // in order to avoid clipping of text parts:
416 //
417 // (a) ABSCISSA axes only: rotate the texts in 5 steps
418 // until they are drawn vertically
419 //
420 // (b) further reduce the texts' font height down to 6pt
421 // .
422 //
423 // If the texts *still* don't fit into their space, we are lost
424 // and they will be clipped. Such is live.
425 //
426 // Why all this?
427 //
428 // Because I do not believe in axis areas growing and shrinking
429 // regarding to long or short label texts: start such behaviour
430 // and become mad.
431 //
432 // Better plan: ask the user to specify a way how to abbreviate
433 // label texts (e.g. by writing "200" instead
434 // of that wide and unreadable "200,000.00")
435 //
436 //
437 // F E A T U R E P L A N N E D F O R F U T U R E . . .
438 //
439 //
440
441 // Note: The labels-touch-edges flag may have been set to true
442 // inside the calculateLabelTexts() function.
443 bool bTouchEdges = para.axisLabelsTouchEdges();
444
445 // NOTE: The steady-value-calc flag may have been set to true
446 // inside the calculateLabelTexts() function
447 // by a special setAxisLabelTextParams() call,
448 // therefor we do not store its value before calling that function.
449 if( cv.bLogarithmic )
450 cv.nSubDelimFactor = 0.1;
451
452 const double nUsableAxisHeight = cv.pTextsH;
453 const double nUsableAxisWidth = cv.pTextsW;
454
455 const bool isHorizontalAxis
456 = (KDChartAxisParams::AxisPosBottom == cv.basicPos) ||
457 (KDChartAxisParams::AxisPosTop == cv.basicPos);
458
459 TQStringList* labelTexts = ( TQStringList* ) para.axisLabelTexts();
460 uint nLabels = ( 0 != labelTexts )
461 ? labelTexts->count()
462 : 0;
463 // start point of 1st delimiter on the axis-line == grid-start
464 TQPoint p1( cv.orig );
465 // end point of 1st delimiter near the label text
466 TQPoint p2( cv.orig );
467 // end point of small sub-delimiter
468 TQPoint p2a( cv.orig );
469 // start point of 1st grid-line (beginnig at the axis)
470 TQPoint pGA( cv.orig );
471 // end point of 1st grid-line at the other side of the chart
472 TQPoint pGZ( cv.orig );
473 // start point of zero-line, this is often identical with p1
474 // but will be different in case of shifted zero-line
475 double axisZeroLineStartX = p1.x();
476 double axisZeroLineStartY = p1.y();
477
478 p2.setX( p2.x() + static_cast < int > ( cv.pXDelimDeltaFaktor * delimLen ) );
479 p2.setY( p2.y() + static_cast < int > ( cv.pYDelimDeltaFaktor * delimLen ) );
480 p2a.setX( p2a.x() + static_cast < int > ( cv.pXDelimDeltaFaktor * delimLen * 2.0 / 3.0 ) );
481 p2a.setY( p2a.y() + static_cast < int > ( cv.pYDelimDeltaFaktor * delimLen * 2.0 / 3.0 ) );
482 pGZ.setX( pGZ.x() - static_cast < int > ( cv.pXDelimDeltaFaktor * (_dataRect.width() - 1) ) );
483 pGZ.setY( pGZ.y() - static_cast < int > ( cv.pYDelimDeltaFaktor * (_dataRect.height() - 1) ) );
484
485 if ( nLabels ) {
486 // Sometimes the first or last labels partially reach out of
487 // their axis area: we allow this
488 const bool oldClippingFlag = painter->hasClipping();
489 painter->setClipping( false );
490
491 if( para.hasAxisFirstLabelText() )
492 labelTexts->first() = para.axisFirstLabelText();
493 if( para.hasAxisLastLabelText() )
494 labelTexts->last() = para.axisLastLabelText();
495
496 const double pXDelta = cv.pXDeltaFactor * cv.pDelimDelta;
497 const double pYDelta = cv.pYDeltaFactor * cv.pDelimDelta;
498
499 // draw label texts and delimiters and grid
500 painter->setPen( TQPen( para.axisLineColor(),
501 lineWidth ) );
502
503 const TQString formatDT = cv.isDateTime
505 : TQString();
506
507 // calculate font size
508 const double minTextHeight = para.axisLabelsFontMinSize();
509 //tqDebug("KChart::KDChartAxesPainter::paintAxes() cv.nTxtHeight: %f minTextHeight: %f", cv.nTxtHeight, minTextHeight);
510 if ( minTextHeight > cv.nTxtHeight )
511 cv.nTxtHeight = minTextHeight;
512 TQFont actFont( para.axisLabelsFont() );
513 if ( para.axisLabelsFontUseRelSize() ) {
514 actFont.setPixelSize( static_cast < int > ( cv.nTxtHeight ) );
515 }
516 painter->setFont( actFont );
517 TQFontMetrics fm( painter->fontMetrics() );
518
519 int nLeaveOut = 0;
520 int nRotation = 0;
521
522 // Draw simple string labels
523 // or calculate and draw nice Date/Time ruler?
524 TQString commonDtHeader;
525 if( cv.autoDtLabels ){
526 cv.textAlign = TQt::AlignCenter;
527 //tqDebug(dtLow.toString("\nd.MM.yyyy - h:mm:ss" ));
528 //tqDebug(dtHigh.toString( "d.MM.yyyy - h:mm:ss" ));
529 const TQDate& dLow = cv.dtLow.date();
530 const TQTime& tLow = cv.dtLow.time();
531 const TQDate& dHigh = cv.dtHigh.date();
532 const TQTime& tHigh = cv.dtHigh.time();
533 bool sameYear = dLow.year() == dHigh.year();
534 bool sameMonth = sameYear && (dLow.month() == dHigh.month() );
535 bool sameDay = sameMonth && (dLow.day() == dHigh.day() );
536 bool sameHour = sameDay && (tLow.hour() == tHigh.hour() );
537 bool sameMinute = sameHour && (tLow.minute() == tHigh.minute());
538 bool sameSecond = sameMinute && (tLow.second() == tHigh.second());
539 if( sameDay ){
540 commonDtHeader = TQString::number( dLow.day() )
541 + ". "
542 + TQDate::longMonthName( dLow.month() )
543 + ' '
544 + TQString::number( dLow.year() );
545 if( sameHour ){
546 commonDtHeader += " / "
547 + TQString::number( tLow.hour() )
548 + ':';
549 if( sameMinute ){
550 if( 10 > tLow.minute() )
551 commonDtHeader += '0';
552 commonDtHeader += TQString::number( tLow.minute() )
553 + ':';
554 if( sameSecond ){
555 if( 10 > tLow.second() )
556 commonDtHeader += '0';
557 commonDtHeader += TQString::number( tLow.second() );
558 //
559 // " Huston, we have a problem! "
560 //
561 // Currently we don't support milli secs
562 // since they will not fit into a double
563 // when looking at years...
564 //
565 // This will be improved in release 2.0.
566 // (khz, 2002/07/12)
567 }
568 else
569 commonDtHeader += "00";
570 }
571 else
572 commonDtHeader += "00";
573 }
574 }else if( sameMonth )
575 commonDtHeader = TQDate::longMonthName( dLow.month() )
576 + ' '
577 + TQString::number( dLow.year() );
578 else if( sameYear )
579 commonDtHeader = TQString::number( dLow.year() );
580 //if( !commonDtHeader.isEmpty() )
581 // tqDebug(commonDtHeader);
582 }else{
583 // make sure all label texts fit into their space
584 // by rotating and/or shrinking the texts
585 // or by leaving out some of the labels
586 TQRegion unitedRegions;
587
588 const bool tryLeavingOut =
590 || ( 0 < para.axisValueLeaveOut() );
591 if( tryLeavingOut ) {
592 if( para.axisValueLeaveOut()
594 nLeaveOut = 0;
595 else
596 nLeaveOut = para.axisValueLeaveOut();
597
598 }
599 else
600 nLeaveOut = 0;
601 int stepWidthLeaveOut = nLeaveOut+1;
602 int iStepsLeaveOut = 0;
603
604 const bool tryShrinking = !para.axisLabelsDontShrinkFont();
605 const double nInitialTxtHeight = cv.nTxtHeight;
606
607 const bool tryRotating = isHorizontalAxis
608 && !para.axisLabelsDontAutoRotate();
609 const int nInitialRotation = ( (360 > para.axisLabelsRotation())
610 && (270 <= para.axisLabelsRotation()) )
611 ? para.axisLabelsRotation()
612 : 0;
613 nRotation = nInitialRotation;
614
615 bool textsDontFitIntoArea;
616 bool textsOverlapping;
617 bool textsMatching;
618 do {
619 textsDontFitIntoArea = false;
620 textsOverlapping = false;
621 textsMatching = true;
622 // test if all texts match without mutually overlapping
623 unitedRegions = TQRegion();
624 int align = nRotation
625 ? (TQt::AlignRight | TQt::AlignVCenter) // adjusting for rotation
626 : cv.textAlign;
627 TQPoint anchor(200,200);
628 int iLeaveOut = 0;
629 double iLabel=0.0;
630 for ( TQStringList::Iterator it = labelTexts->begin();
631 it != labelTexts->end();
632 ++it ) {
633 iLabel += 1.0;
634 if( iLeaveOut < nLeaveOut ) {
635 ++iLeaveOut;
636 } else {
637 iLeaveOut = 0;
638 anchor.setX( p2.x() + static_cast < int > ( pXDelta * (iLabel - 0.5) ) );
639 anchor.setY( p2.y() + static_cast < int > ( pYDelta * (iLabel - 0.5) ) );
640
641 // allow for shearing and/or scaling of the painter
642 anchor = painter->worldMatrix().map( anchor );
643
644 TQString text;
645 if( cv.isDateTime ){
646 TQDateTime dt( TQDateTime::fromString( *it,
647 TQt::ISODate ) );
648 text = dt.toString( formatDT );
649 }else{
650 text = *it;
651 }
652 KDDrawTextRegionAndTrueRect infosKDD =
653 KDDrawText::measureRotatedText( painter,
654 nRotation,
655 anchor,
656 text,
657 0,
658 align,
659 &fm,
660 false,
661 false,
662 15 );
663 if( infosKDD.region.boundingRect().left()
664 < params()->globalLeadingLeft()+1 ){
665 textsMatching = false;
666 textsDontFitIntoArea = true;
667 //tqDebug("too wide");
668 }
669 //tqDebug("nRotation: %i",nRotation);
670 TQRegion sectReg;
671 if( nRotation ){
672 //tqDebug("test 1");
673 sectReg = infosKDD.region.intersect( unitedRegions );
674 }else{
675 //tqDebug("test 2");
676 TQRect rect( infosKDD.region.boundingRect() );
677 rect.addCoords(-2,-2,2,2);
678 TQRegion biggerRegion( rect );
679 sectReg = biggerRegion.intersect( unitedRegions );
680 }
681 if ( sectReg.isEmpty() )
682 unitedRegions = unitedRegions.unite( infosKDD.region );
683 else {
684 textsMatching = false;
685 textsOverlapping = true;
686 //tqDebug("label regions are intersecting");
687 break;
688 }
689 }
690 }
691/*
692 if(!iAxis){
693
694 tqDebug("nTxtHeight: "+TQString::number(cv.nTxtHeight)+" nRotation: "+TQString::number(nRotation)+
695 " matching: "+TQString(textsMatching ? "true":"false"));
696 tqDebug("nUsableAxisHeight: %f, unitedRegions.boundingRect().height(): %i ",
697 nUsableAxisHeight, unitedRegions.boundingRect().height());
698 }
699*/
700 if( isHorizontalAxis ) {
701 if( nUsableAxisHeight < unitedRegions.boundingRect().height() ){
702 //textsMatching = false;
703 textsDontFitIntoArea = true;
704 }
705 } else {
706 if( nUsableAxisWidth < unitedRegions.boundingRect().width() ){
707 //tqDebug("textsMatching: %s",textsMatching ? "true" : "false");
708 textsMatching = false;
709 textsDontFitIntoArea = true;
710 //tqDebug("too wide");
711 }
712 //else tqDebug("not too wide");
713 }
714 /*
715 if(textsMatching && !iAxis){
716 tqDebug("--------------------------");
717 tqDebug("nTxtHeight: "+TQString::number(cv.nTxtHeight)+" nRotation: "+TQString::number(nRotation));
718 tqDebug("matching");
719 }
720 */
721 if( !textsMatching ) {
722 bool rotatingDoesNotHelp = false;
723 // step 1: In case of labels being too wide
724 // to fit into the available space
725 // we try to rotate the texts in 5 steps.
726 // This is done for Abscissa axes only.
727 if ( tryRotating ) {
728 //tqDebug("try rotating");
729 // The following is designed for horizontal axes
730 // since we currently don't support label rotating
731 // on vertical axes. (khz, 2002/08/15)
732 if( textsDontFitIntoArea ){
733 if( nRotation != nInitialRotation ){
734 //textsDontFitIntoArea = false;
735 nRotation = nInitialRotation;
736 }
737 rotatingDoesNotHelp = true;
738 //tqDebug("rotating does not help (a)");
739 }
740 else{
741 if( nRotation ) {
742 if( 270 < nRotation ) {
743 nRotation -= 5;
744 if( 270 > nRotation )
745 nRotation = 270; // drawing vertically now
746 } else {
747 if( nInitialRotation )
748 nRotation = nInitialRotation;
749 else
750 nRotation = 0; // reset rotation to ZERO
751 rotatingDoesNotHelp = true;
752 //tqDebug("rotating does not help (b)");
753 }
754 } else {
755 if( nInitialRotation )
756 nRotation = nInitialRotation;
757 else
758 nRotation = 350; // (re-)start rotating with -10
759 }
760 }
761 }
762 if ( !tryRotating || rotatingDoesNotHelp ) {
763
764 // step 2: In case of labels being too wide and
765 // rotating them did not help or is forbidden
766 // we try to reduce the font size.
767 if ( tryShrinking && (minTextHeight < cv.nTxtHeight) ) {
768 //tqDebug("try shrinking");
769 cv.nTxtHeight -= 1.0;
770 if ( minTextHeight > cv.nTxtHeight )
771 cv.nTxtHeight = minTextHeight;
772 } else {
773
774 // step 3: In case reducing the font size is not possible
775 // any further (or is not allowed at all) we try
776 // to leave out some of the labels.
777 if( tryLeavingOut
778 && textsOverlapping
779 && (nLeaveOut+1 < static_cast < int > ( nLabels ) ) ) {
780 //tqDebug("try leaving out");
781 ++iStepsLeaveOut;
782 //if(!iAxis)tqDebug("iStepsLeaveOut: %i", iStepsLeaveOut);
783 nLeaveOut =
784 iStepsLeaveOut*stepWidthLeaveOut - 1;
785 if( tryShrinking )
786 cv.nTxtHeight = nInitialTxtHeight;
787 }
788 else
789 break;
790 }
791 if( tryShrinking ) {
792 actFont.setPixelSize( static_cast < int > ( cv.nTxtHeight ) );
793 //tqDebug("axis: cv.nTxtHeight: %f", iAxis, cv.nTxtHeight);
794 painter->setFont( actFont );
795 fm = painter->fontMetrics();
796 }
797 }
798 }
799//tqDebug("nLeaveOut: %i",nLeaveOut);
800 } while( !textsMatching );
801
802 if( nRotation ){
803 // The following is designed for horizontal axes
804 // since we currently don't support label rotating
805 // on vertical axes. (khz, 2002/08/15)
806 //int oldVert = textAlign & (TQt::AlignTop | TQt::AlignBottom);
807 //int steepness = abs(270-nRotation);
808 //bool steep = (30 > steepness);
809 cv.textAlign = TQt::AlignRight | TQt::AlignVCenter; // adjusting for rotation
810 //cv.textAlign = TQt::AlignRight | TQt::AlignVCenter;
811 /* ( steep ? TQt::AlignVCenter : oldVert);*/
812 //int dx = pXDelta / 2 - steep ? (nTxtHeight / 4) : 0;
813 double dx = (pXDelta / 2) - (cv.nTxtHeight / 4);
814 double dy = /*steep ? 0 : */(cv.nTxtHeight / 2.0);
815 cv.pTextsX += dx;
816 cv.pTextsY += dy;
817 }
818 /*
819 TQBrush oldBrush = painter->brush();
820 TQRegion oldReg = painter->clipRegion();//TQPainter::CoordPainter);
821 painter->setBrush(TQt::Dense4Pattern);
822 painter->setClipRegion(unitedRegions);//,TQPainter::CoordPainter);
823 painter->drawRect(0,0,2000,1500);
824 painter->setClipRegion(oldReg);//,TQPainter::CoordPainter);
825 painter->setBrush(oldBrush);
826 */
827 /*if(!iAxis){
828 tqDebug("==========================");
829 tqDebug("nTxtHeight: "+TQString::number(nTxtHeight)+" nRotation: "+TQString::number(nRotation));
830 tqDebug(textsMatching ? "matching":"not matching");
831 }*/
832 }
833
834 painter->setFont( actFont );
835 fm = TQFontMetrics( painter->fontMetrics() );
836
837 // set colour of grid pen
838 TQPen gridPen, leaveOutGridPen;
839 if( para.axisShowGrid() && !bMultiRowBarChart )
840 gridPen.setColor( para.axisGridColor() );
841
842 const int pXDeltaDiv2 = static_cast < int > ( pXDelta / 2.0 );
843 const int pYDeltaDiv2 = static_cast < int > ( pYDelta / 2.0 );
844
845 bool bDrawAdditionalSubGridLine = false;
846 double pGXMicroAdjust = 0.0;
847 double pGYMicroAdjust = 0.0;
848 if ( !bTouchEdges ) {
849 // adjust the data values pos
850 p1.setX( p1.x() + pXDeltaDiv2 );
851 p1.setY( p1.y() + pYDeltaDiv2 );
852 p2.setX( p2.x() + pXDeltaDiv2 );
853 p2.setY( p2.y() + pYDeltaDiv2 );
854 // adjust the short delimiter lines pos
855 p2a.setX( p2a.x() + pXDeltaDiv2 );
856 p2a.setY( p2a.y() + pYDeltaDiv2 );
857 // adjust grid lines pos
858 bDrawAdditionalSubGridLine =
859 isHorizontalAxis && !
860 params()->axisParams(
861 KDChartAxisParams::AxisPosRight ).axisVisible() &&
862 !bMultiRowBarChart;
863 pGA.setX( pGA.x() + pXDeltaDiv2 );
864 pGA.setY( pGA.y() + pYDeltaDiv2 );
865 pGZ.setX( pGZ.x() + pXDeltaDiv2 );
866 pGZ.setY( pGZ.y() + pYDeltaDiv2 );
867 // fine-tune grid line pos for grid of vertical axis
868 if( KDChartAxisParams::AxisTypeNORTH == para.axisType() ) {
869 pGXMicroAdjust = cv.pXDeltaFactor * lineWidth / 2.0;
870 pGYMicroAdjust = cv.pYDeltaFactor * lineWidth / 2.0;
871 }
872 }
873 double x1, y1, x2, y2, xGA, yGA, xGZ, yGZ,
874 p1X, p1Y, p2X, p2Y, pGAX, pGAY, pGZX, pGZY, xT, yT;
875
876 double pXSubDelimDelta = pXDelta * cv.nSubDelimFactor;
877 double pYSubDelimDelta = pYDelta * cv.nSubDelimFactor;
878
879 if ( !cv.autoDtLabels
880 && 0.0 != cv.nSubDelimFactor
881 && para.axisShowSubDelimiters()
882 && para.axisLabelsVisible()
883 && !nLeaveOut ) {
884 TQPen pen( para.axisLineColor(), static_cast < int > ( 0.5 * lineWidth ) );
885 uint penWidth = pen.width();
886 bool bOk = true;
887
888 if( cv.bLogarithmic )
889 cv.nSubDelimFactor = 0.1;
890
891 while ( fabs( ( pXDelta + pYDelta ) * cv.nSubDelimFactor / 6.0 )
892 <= 1.0 + penWidth
893 && bOk ) {
894 if ( 0 < penWidth ) {
895 --penWidth;
896 pen.setWidth( penWidth );
897 }else{
898 if( cv.bLogarithmic ){
899 break; // there is nothing we can do: we allways
900 // want 10 sub-delims per logarithmic step
901 }else{
902 if ( 0.5 != cv.nSubDelimFactor ) {
903 // emercency: reduce number of sub-scaling
904 cv.nSubDelimFactor = 0.5;
905
906 pXSubDelimDelta = pXDelta * cv.nSubDelimFactor;
907 pYSubDelimDelta = pYDelta * cv.nSubDelimFactor;
908 } else
909 bOk = false;
910 }
911 }
912 }
913 if ( bOk ) {
914 x1 = p1.x();
915 y1 = p1.y();
916 x2 = p2a.x();
917 y2 = p2a.y();
918 xGA = pGA.x();
919 yGA = pGA.y();
920 xGZ = pGZ.x();
921 yGZ = pGZ.y();
922 p1X = x1;
923 p1Y = y1;
924 p2X = x2;
925 p2Y = y2;
926 pGAX = xGA;
927 pGAY = yGA;
928 pGZX = xGZ;
929 pGZY = yGZ;
930
931 // set up grid pen for drawing the sub-grid lines
932 const TQPen oldGridPen( gridPen );
933 if ( para.axisShowGrid() ) {
934 gridPen.setColor( para.axisGridSubColor() );
935 gridPen.setWidth( gridSubLineWidth );
936 gridPen.setStyle( para.axisGridSubStyle() );
937 }
938 const TQPen oldPen( painter->pen() );
939 painter->setPen( pen );
940 double nSubDelim = ( labelTexts->count() - 1 )
941 / cv.nSubDelimFactor;
942
943 //tqDebug("subDelim: %f",
944 modf( nSubDelim, &nSubDelim );
945
946 int logarithCnt = 1;
947 double xLogarithOffs = 0;
948 double yLogarithOffs = 0;
949 double dDummy;
950 double mainDelim = 0.0;
951 bool paint = true;
952
953 for ( double iDelim = 1.0;
954 iDelim <= nSubDelim + 1.0;
955 iDelim += 1.0, logarithCnt++ ) {
956 // test if it is a sub or a main delimiter
957 if ( mainDelim > 0.0 )
958 paint = true;
959 else
960 paint = false;
961
962 if ( cv.bLogarithmic )
963 {
964 if ( logarithCnt == 11 )
965 {
966 xLogarithOffs +=
967 pXDelta * log10( 10*cv.nSubDelimFactor*10 );
968 yLogarithOffs +=
969 pYDelta * log10( 10*cv.nSubDelimFactor*10 );
970 logarithCnt=1;
971 }
972
973 pXSubDelimDelta =
974 pXDelta * log10( 10*cv.nSubDelimFactor*logarithCnt );
975 pYSubDelimDelta =
976 pYDelta * log10( 10*cv.nSubDelimFactor*logarithCnt );
977 }
978
979 if ( para.axisShowGrid() && !bMultiRowBarChart) {
980 // draw the sub grid line
981 if( 0.0 != modf((iDelim-1.0) * cv.nSubDelimFactor, &dDummy) )
982
983 saveDrawLine( *painter,
984 TQPoint( static_cast<int>( pGAX - pGXMicroAdjust ),
985 static_cast<int>( pGAY - pGYMicroAdjust ) ),
986 TQPoint( static_cast<int>( pGZX - pGXMicroAdjust ),
987 static_cast<int>( pGZY - pGYMicroAdjust ) ),
988 gridPen );
989
990 if( cv.bLogarithmic ){
991 pGAX = xGA + pXSubDelimDelta + xLogarithOffs;
992 pGAY = yGA + pYSubDelimDelta + yLogarithOffs;
993 pGZX = xGZ + pXSubDelimDelta + xLogarithOffs;
994 pGZY = yGZ + pYSubDelimDelta + yLogarithOffs;
995 }else{
996 pGAX = xGA + iDelim * pXSubDelimDelta;
997 pGAY = yGA + iDelim * pYSubDelimDelta;
998 pGZX = xGZ + iDelim * pXSubDelimDelta;
999 pGZY = yGZ + iDelim * pYSubDelimDelta;
1000 /*
1001 if( !modf(iDelim * cv.nSubDelimFactor, &dDummy) ){
1002 pGAX = xGA + (iDelim * cv.nSubDelimFactor) * pXDelta;
1003 pGAY = yGA + (iDelim * cv.nSubDelimFactor) * pYDelta;
1004 pGZX = xGZ + (iDelim * cv.nSubDelimFactor) * pXDelta;
1005 pGZY = yGZ + (iDelim * cv.nSubDelimFactor) * pYDelta;
1006 }
1007 */
1008 }
1009 }
1010
1011
1012 // draw the short delimiter line
1013 // PENDING: Michel - make sure not to draw the sub-delimiters over the main ones.
1014 // by testing if it is a sub delimiter or a main one
1015 if ( paint )
1016 painter->drawLine( TQPoint( static_cast<int>( p1X ), static_cast<int>( p1Y ) ),
1017 TQPoint( static_cast<int>( p2X ), static_cast<int>( p2Y ) ) );
1018
1019 mainDelim += 1.0;
1020
1021
1022 if( cv.bLogarithmic ){
1023 p1X = x1 + pXSubDelimDelta + xLogarithOffs;
1024 p1Y = y1 + pYSubDelimDelta + yLogarithOffs;
1025 p2X = x2 + pXSubDelimDelta + xLogarithOffs;
1026 p2Y = y2 + pYSubDelimDelta + yLogarithOffs;
1027 }else{
1028 p1X = x1 + iDelim * pXSubDelimDelta;
1029 p1Y = y1 + iDelim * pYSubDelimDelta;
1030 p2X = x2 + iDelim * pXSubDelimDelta;
1031 p2Y = y2 + iDelim * pYSubDelimDelta;
1032 }
1033
1034 if ( mainDelim >= nSubDelim/(labelTexts->count() -1) )
1035 mainDelim = 0.0;
1036
1037
1038 } // for
1039 // draw additional sub grid line
1040 if( bDrawAdditionalSubGridLine
1041 && para.axisShowGrid() ) {
1042
1043 saveDrawLine( *painter,
1044 TQPoint( static_cast<int>( pGAX - pGXMicroAdjust ),
1045 static_cast<int>( pGAY - pGYMicroAdjust ) ),
1046 TQPoint( static_cast<int>( pGZX - pGXMicroAdjust ),
1047 static_cast<int>( pGZY - pGYMicroAdjust ) ),
1048 gridPen );
1049
1050 }
1051 painter->setPen( oldPen );
1052 gridPen = oldGridPen;
1053 }
1054 }
1055 x1 = p1.x();
1056 y1 = p1.y();
1057 x2 = p2.x();
1058 y2 = p2.y();
1059 xGA = pGA.x();
1060 yGA = pGA.y();
1061 xGZ = pGZ.x();
1062 yGZ = pGZ.y();
1063 p1X = x1;
1064 p1Y = y1;
1065 p2X = x2;
1066 p2Y = y2;
1067 pGAX = xGA;
1068 pGAY = yGA;
1069 pGZX = xGZ;
1070 pGZY = yGZ;
1071 xT = cv.pTextsX;
1072 yT = cv.pTextsY;
1073 // set up grid pen for drawing the normal grid lines
1074 if ( para.axisShowGrid() ) {
1075 gridPen.setWidth( gridLineWidth );
1076 gridPen.setStyle( para.axisGridStyle() );
1077 // if axis not visible draw the 1st grid line too
1078 if( !para.axisLineVisible() )
1079 saveDrawLine( *painter, cv.orig, cv.dest, gridPen );
1080 }
1081 if( nLeaveOut ) {
1082 leaveOutGridPen = gridPen;
1083 leaveOutGridPen.setWidth( gridLineWidth / 2 );
1084 leaveOutGridPen.setStyle( TQt::DotLine );
1085 }
1086 // =========================================================
1087 // || The labels and delimiters and grid printing loops ||
1088 // =========================================================
1089 //
1090 double iLabel = 0.0;
1091 if( cv.autoDtLabels )
1092 {
1093 /*
1094 tqDebug("\ndtLow: %i %i %i %i:%i:%i",
1095 dtLow.date().year(),
1096 dtLow.date().month(),
1097 dtLow.date().day(),
1098 dtLow.time().hour(),
1099 dtLow.time().minute(),
1100 dtLow.time().second());
1101 tqDebug("dtHigh: %i %i %i %i:%i:%i",
1102 dtHigh.date().year(),
1103 dtHigh.date().month(),
1104 dtHigh.date().day(),
1105 dtHigh.time().hour(),
1106 dtHigh.time().minute(),
1107 dtHigh.time().second());
1108 */
1109 int pXD = static_cast <int> (cv.pXDelimDeltaFaktor * 1.25 * (cv.nTxtHeight+4));
1110 int pYD = static_cast <int> (cv.pYDelimDeltaFaktor * 1.25 * (cv.nTxtHeight+4));
1111 int orgXD = pXD;
1112 int orgYD = pYD;
1113 cv.pTextsW = fabs( (0.0 == pXDelta) ? pXD : pXDelta );
1114 cv.pTextsH = fabs( (0.0 == pYDelta) ? pYD : pYDelta );
1115
1116 double pSecX = x1;
1117 double pSecY = y1;
1118 bool secPaint= false;
1119 double pMinX = x1;
1120 double pMinY = y1;
1121 bool minPaint= false;
1122 double pHourX = x1;
1123 double pHourY = y1;
1124 bool hourPaint= false;
1125 double pDayX = x1;
1126 double pDayY = y1;
1127 bool dayPaint= false;
1128 /* khz: currently not used
1129 double pWeekX = x1;
1130 double pWeekY = y1;
1131 bool weekPaint= false;
1132 */
1133 double pMonthX = x1;
1134 double pMonthY = y1;
1135 bool monthPaint= false;
1136 /*double pQuarterX = x1;
1137 double pQuarterY = y1;
1138 bool minPaint= false;
1139 */
1140 double pYearX = x1;
1141 double pYearY = y1;
1142 bool yearPaint= false;
1143
1144 double pXYDelta = fabs( pXDelta ) + fabs( pYDelta );
1145
1146 if( 0.0 == para.trueAxisDeltaPixels() )
1147 ( ( KDChartAxisParams& ) para ).setTrueAxisDeltaPixels( TQMIN(_logicalWidth, _logicalHeight) / 150 );
1148
1149 bool dtGoDown = cv.dtLow > cv.dtHigh;
1150 int mult = dtGoDown ? -1 : 1;
1151 const TQDateTime& startDt = dtGoDown ? cv.dtHigh : cv.dtLow;
1152
1153 ( ( KDChartAxisParams& ) para ).setAxisDtLowPos( x1, y1 );
1154 // adjust stored dt-low and scale settings
1155 ( ( KDChartAxisParams& ) para ).setTrueAxisDtLow( startDt );
1156 ( ( KDChartAxisParams& ) para ).setTrueAxisDtScale( cv.dtDeltaScale );
1157
1158 int gridDX = pGZ.x() - pGA.x();
1159 int gridDY = pGZ.y() - pGA.y();
1160 if ( para.axisShowGrid() ) {
1161 gridPen.setColor( para.axisGridColor() );
1162 gridPen.setWidth( gridLineWidth );
1163 gridPen.setStyle( para.axisGridStyle() );
1164 }
1165 TQPen subGridPen( gridPen.color(), 1, para.axisGridStyle() );
1166 TQPen subSubGridPen( gridPen.color(), 1, para.axisGridSubStyle() );
1167 TQPen pen = subGridPen;
1168
1169 TQDateTime dt( startDt );
1170 TQDateTime newDt( startDt );
1171 for( uint i=1; i <= nLabels; ++i ){
1172 switch( cv.dtDeltaScale ) {
1173 case KDChartAxisParams::ValueScaleSecond:
1174 dtAddSecs( dt, 1 * mult, newDt );
1175 break;
1176 case KDChartAxisParams::ValueScaleMinute:
1177 dtAddSecs( dt, 60 * mult, newDt );
1178 break;
1179 case KDChartAxisParams::ValueScaleHour:
1180 dtAddSecs( dt, 3600 * mult, newDt );
1181 break;
1182 case KDChartAxisParams::ValueScaleDay:
1183 dtAddDays( dt, 1 * mult, newDt );
1184 break;
1185 case KDChartAxisParams::ValueScaleWeek:
1186 dtAddDays( dt, 7 * mult, newDt );
1187 break;
1188 case KDChartAxisParams::ValueScaleMonth:
1189 dtAddMonths( dt,1 * mult, newDt );
1190 break;
1191 case KDChartAxisParams::ValueScaleQuarter:
1192 dtAddMonths( dt,3 * mult, newDt );
1193 break;
1194 case KDChartAxisParams::ValueScaleYear:
1195 dtAddYears( dt, 1 * mult, newDt );
1196 break;
1197 default:
1198 dtAddDays( dt, 1 * mult, newDt );
1199 break;
1200 }
1201 const TQDateTime& testDt
1202 = dtGoDown
1203 ? ( ( newDt < cv.dtLow )
1204 ? cv.dtLow
1205 : newDt )
1206 : ( ( newDt > cv.dtHigh )
1207 ? cv.dtHigh
1208 : newDt );
1209 /*
1210 tqDebug(" dt: %i %i %i %i:%i:%i",
1211 newDt.date().year(),newDt.date().month(),newDt.date().day(),
1212 newDt.time().hour(),newDt.time().minute(),newDt.time().second());
1213 tqDebug("testDt: %i %i %i %i:%i:%i",
1214 testDt.date().year(),testDt.date().month(),testDt.date().day(),
1215 testDt.time().hour(),testDt.time().minute(),testDt.time().second());
1216 */
1217 bool endLoop = (i == nLabels) || (&testDt != &newDt);
1218
1219 secPaint = ( KDChartAxisParams::ValueScaleSecond >= cv.dtDeltaScale ) &&
1220 ( testDt.time().second() != dt.time().second() ||
1221 ( endLoop && ((pSecX != x1) || (pSecY != y1))));
1222 minPaint = ( KDChartAxisParams::ValueScaleMinute >= cv.dtDeltaScale ) &&
1223 ( testDt.time().minute() != dt.time().minute() ||
1224 ( endLoop && ((pMinX != x1) || (pMinY != y1))));
1225 hourPaint = ( KDChartAxisParams::ValueScaleHour >= cv.dtDeltaScale ) &&
1226 ( testDt.time().hour() != dt.time().hour() ||
1227 ( endLoop && ((pHourX != x1) || (pHourY != y1))));
1228 dayPaint = ( KDChartAxisParams::ValueScaleDay >= cv.dtDeltaScale ) &&
1229 ( testDt.date().day() != dt.date().day() ||
1230 ( endLoop && ((pDayX != x1) || (pDayY != y1))));
1231 /* khz: currently not used
1232 weekPaint = ( KDChartAxisParams::ValueScaleWeek >= cv.dtDeltaScale ) &&
1233 ( testDt.date().week() != dt.date().week() ||
1234 ( endLoop && ((pWeekX != x1) || (pWeekY != y1))));
1235 */
1236 monthPaint = ( KDChartAxisParams::ValueScaleMonth >= cv.dtDeltaScale ) &&
1237 ( testDt.date().month() != dt.date().month() ||
1238 ( endLoop && ((pMonthX != x1) || (pMonthY != y1))));
1239 yearPaint = ( KDChartAxisParams::ValueScaleYear >= cv.dtDeltaScale ) &&
1240 ( testDt.date().year() != dt.date().year() ||
1241 ( endLoop && ((pYearX != x1) || (pYearY != y1))));
1242
1243 p1X = x1 + iLabel * pXDelta;
1244 p1Y = y1 + iLabel * pYDelta;
1245 p2X = p1X + pXDelta;
1246 p2Y = p1Y + pYDelta;
1247 pXD = orgXD;
1248 pYD = orgYD;
1249
1250 if( endLoop ){
1251 ( ( KDChartAxisParams& ) para ).setAxisDtHighPos( p1X, p1Y );
1252 // adjust stored dt-high settings
1253 ( ( KDChartAxisParams& ) para ).setTrueAxisDtHigh( dt );
1254 }
1255 pen = subGridPen;
1256 /*
1257 // old code: just draw the seconds without any tests
1258 // (not wise to do that when supporting sec1000
1259 // and the like some day...)
1260 if( newDt.time().second() != dt.time().second() ){
1261 painter->drawLine( TQPoint( p1X, p1Y ), TQPoint( p1X+pXD, p1Y+pYD ) );
1262 painter->drawLine( TQPoint( p1X+pXD, p1Y+pYD ),
1263 TQPoint( p1X+pXD + pXDelta, p1Y+pYD + pYDelta ) );
1264 painter->drawText( p1X+pXD-orgXD, p1Y+pYD-orgYD,
1265 pTextsW, pTextsH,
1266 textAlign | TQt::DontClip,
1267 TQString::number( dt.time().second() ) );
1268 pXD += orgXD;
1269 pYD += orgYD;
1270 }
1271 */
1272 if( secPaint ){
1273 painter->drawLine( TQPoint( static_cast<int>( pSecX+pXD ),
1274 static_cast<int>( pSecY+pYD ) ),
1275 TQPoint( static_cast<int>( p2X + pXD ),
1276 static_cast<int>( p2Y + pYD ) ) );
1277 if( (pXDelta/2.0 < p2X - pSecX) || (pYDelta/2.0 < p2Y - pSecY) ){
1278 TQPen oldPen( painter->pen() );
1279 painter->setPen( TQPen( labelsColor ) );
1280 painter->drawText( static_cast<int>( pSecX+pXD-orgXD ),
1281 static_cast<int>( pSecY+pYD-orgYD ),
1282 static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pSecX))),
1283 static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pSecY))),
1284 cv.textAlign | TQt::DontClip,
1285 TQString::number( dt.time().second() ) );
1286 painter->setPen( oldPen );
1287 if ( para.axisShowGrid() ){
1288
1289 saveDrawLine( *painter,
1290 TQPoint( static_cast<int>( pSecX ),
1291 static_cast<int>( pSecY ) ),
1292 TQPoint( static_cast<int>( pSecX + gridDX ),
1293 static_cast<int>( pSecY + gridDY ) ),
1294 pen );
1295 pen = gridPen;
1296 }
1297 if( !minPaint || pMinX != pSecX || pMinY != pSecY ){
1298 painter->drawLine( TQPoint( static_cast<int>( pSecX ),
1299 static_cast<int>( pSecY ) ),
1300 TQPoint( static_cast<int>( pSecX+pXD ),
1301 static_cast<int>( pSecY+pYD ) ) );
1302 }
1303 }
1304 if( endLoop && !minPaint )
1305 painter->drawLine( TQPoint( static_cast<int>( p2X ),
1306 static_cast<int>( p2Y ) ),
1307 TQPoint( static_cast<int>( p2X+pXD ),
1308 static_cast<int>( p2Y+pYD ) ) );
1309 pSecX = p1X + pXDelta;
1310 pSecY = p1Y + pYDelta;
1311 pXD += orgXD;
1312 pYD += orgYD;
1313 }
1314 if( minPaint ){
1315 painter->drawLine( TQPoint( static_cast<int>( pMinX+pXD ),
1316 static_cast<int>( pMinY+pYD ) ),
1317 TQPoint( static_cast<int>( p2X + pXD ),
1318 static_cast<int>( p2Y + pYD ) ) );
1319 if( (pXDelta/2.0 < p2X - pMinX) || (pYDelta/2.0 < p2Y - pMinY) ){
1320 TQPen oldPen( painter->pen() );
1321 painter->setPen( TQPen( labelsColor ) );
1322 painter->drawText( static_cast<int>( pMinX+pXD-orgXD ),
1323 static_cast<int>( pMinY+pYD-orgYD ),
1324 static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pMinX)) ),
1325 static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pMinY)) ),
1326 cv.textAlign | TQt::DontClip,
1327 TQString::number( dt.time().minute() ) );
1328 painter->setPen( oldPen );
1329 if ( para.axisShowGrid() ){
1330 if( !secPaint && 10 < pXYDelta ){
1331 saveDrawLine( *painter,
1332 TQPoint( static_cast<int>( pMinX+pXDelta/2 ),
1333 static_cast<int>( pMinY+pYDelta/2 ) ),
1334 TQPoint( static_cast<int>( pMinX+pXDelta/2 + gridDX ),
1335 static_cast<int>( pMinY+pYDelta/2 + gridDY ) ),
1336 subSubGridPen );
1337 }
1338 saveDrawLine( *painter,
1339 TQPoint( static_cast<int>( pMinX ),
1340 static_cast<int>( pMinY ) ),
1341 TQPoint( static_cast<int>( pMinX + gridDX ),
1342 static_cast<int>( pMinY + gridDY ) ),
1343 pen );
1344 pen = gridPen;
1345 }
1346 if( !hourPaint || pHourX != pMinX || pHourY != pMinY ){
1347 painter->drawLine( TQPoint( static_cast<int>( pMinX ),
1348 static_cast<int>( pMinY ) ),
1349 TQPoint( static_cast<int>( pMinX+pXD ),
1350 static_cast<int>( pMinY+pYD ) ) );
1351 }
1352 }
1353 if( endLoop && !hourPaint )
1354 painter->drawLine( TQPoint( static_cast<int>( p2X ),
1355 static_cast<int>( p2Y ) ),
1356 TQPoint( static_cast<int>( p2X+pXD ),
1357 static_cast<int>( p2Y+pYD ) ) );
1358 pMinX = p1X + pXDelta;
1359 pMinY = p1Y + pYDelta;
1360 pXD += orgXD;
1361 pYD += orgYD;
1362 }
1363 if( hourPaint ){
1364 painter->drawLine( TQPoint( static_cast<int>( pHourX+pXD ),
1365 static_cast<int>( pHourY+pYD ) ),
1366 TQPoint( static_cast<int>( p2X + pXD ),
1367 static_cast<int>( p2Y + pYD ) ) );
1368 /*
1369 tqDebug("line");
1370 tqDebug("pXDelta / 2.0 : %f", pXDelta/2.0);
1371 tqDebug("p2X - pHourX : %f", p2X - pHourX);
1372 */
1373 if( (pXDelta/2.0 < p2X - pHourX) || (pYDelta/2.0 < p2Y - pHourY) ){
1374 /*
1375 tqDebug("pHourX %f", pHourX );
1376 tqDebug(" +pXD %i", pXD );
1377 tqDebug(" -orgXD %i", orgXD);
1378 tqDebug("pHourY %f", pHourY );
1379 tqDebug(" +pYD %i", pYD );
1380 tqDebug(" -orgYD %i", orgYD);
1381 */
1382 TQPen oldPen( painter->pen() );
1383 painter->setPen( TQPen( labelsColor ) );
1384 painter->drawText( static_cast<int>( pHourX+pXD-orgXD ),
1385 static_cast<int>( pHourY+pYD-orgYD ),
1386 static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pHourX))),
1387 static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pHourY))),
1388 cv.textAlign | TQt::DontClip,
1389 TQString::number( dt.time().hour() ) );
1390 painter->setPen( oldPen );
1391 if ( para.axisShowGrid() ){
1392 if( !minPaint && 10 < pXYDelta ){
1393 saveDrawLine( *painter,
1394 TQPoint( static_cast<int>( pHourX+pXDelta/2 ),
1395 static_cast<int>( pHourY+pYDelta/2 ) ),
1396 TQPoint( static_cast<int>( pHourX+pXDelta/2 + gridDX ),
1397 static_cast<int>( pHourY+pYDelta/2 + gridDY ) ),
1398 subSubGridPen );
1399 }
1400 saveDrawLine( *painter,
1401 TQPoint( static_cast<int>( pHourX ),
1402 static_cast<int>( pHourY ) ),
1403 TQPoint( static_cast<int>( pHourX + gridDX ),
1404 static_cast<int>( pHourY + gridDY ) ),
1405 pen );
1406 pen = gridPen;
1407 }
1408 if( !dayPaint || pDayX != pHourX || pDayY != pHourY ){
1409 painter->drawLine( TQPoint( static_cast<int>( pHourX ),
1410 static_cast<int>( pHourY ) ),
1411 TQPoint( static_cast<int>( pHourX+pXD ),
1412 static_cast<int>( pHourY+pYD ) ) );
1413 }
1414 }
1415 if( endLoop && !dayPaint )
1416 painter->drawLine( TQPoint( static_cast<int>( p2X ),
1417 static_cast<int>( p2Y ) ),
1418 TQPoint( static_cast<int>( p2X+pXD ),
1419 static_cast<int>( p2Y+pYD ) ) );
1420 pHourX = p1X + pXDelta;
1421 pHourY = p1Y + pYDelta;
1422 pXD += orgXD;
1423 pYD += orgYD;
1424 }
1425 if( dayPaint ){
1426 painter->drawLine( TQPoint( static_cast<int>( pDayX+pXD ),
1427 static_cast<int>( pDayY+pYD ) ),
1428 TQPoint( static_cast<int>( p2X + pXD ),
1429 static_cast<int>( p2Y + pYD ) ) );
1430 if( (pXDelta/2.0 < p2X - pDayX) || (pYDelta/2.0 < p2Y - pDayY) ){
1431 TQPen oldPen( painter->pen() );
1432 painter->setPen( TQPen( labelsColor ) );
1433 painter->drawText( static_cast<int>( pDayX+pXD-orgXD ),
1434 static_cast<int>( pDayY+pYD-orgYD ),
1435 static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pDayX)) ),
1436 static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pDayY)) ),
1437 cv.textAlign | TQt::DontClip,
1438 TQString::number( dt.date().day() ) );
1439 painter->setPen( oldPen );
1440 /* khz: currently not used
1441 if( !weekPaint || pWeekX != pDayX || pWeekY != pDayY )
1442 */
1443 if ( para.axisShowGrid() ){
1444 if( !hourPaint && 10 < pXYDelta ){
1445 saveDrawLine( *painter,
1446 TQPoint( static_cast<int>( pDayX+pXDelta/2 ),
1447 static_cast<int>( pDayY+pYDelta/2 ) ),
1448 TQPoint( static_cast<int>( pDayX+pXDelta/2 + gridDX ),
1449 static_cast<int>( pDayY+pYDelta/2 + gridDY ) ),
1450 subSubGridPen );
1451 }
1452 saveDrawLine( *painter,
1453 TQPoint( static_cast<int>( pDayX ),
1454 static_cast<int>( pDayY ) ),
1455 TQPoint( static_cast<int>( pDayX + gridDX ),
1456 static_cast<int>( pDayY + gridDY ) ),
1457 pen );
1458 pen = gridPen;
1459 }
1460 if( !monthPaint || pMonthX != pDayX || pMonthY != pDayY ){
1461 painter->drawLine( TQPoint( static_cast<int>( pDayX ),
1462 static_cast<int>( pDayY ) ),
1463 TQPoint( static_cast<int>( pDayX+pXD ),
1464 static_cast<int>( pDayY+pYD ) ) );
1465 }
1466 }
1467 /* khz: currently not used
1468 if( endLoop && !weekPaint )
1469 */
1470 if( endLoop && !monthPaint )
1471 painter->drawLine( TQPoint( static_cast<int>( p2X ),
1472 static_cast<int>( p2Y ) ),
1473 TQPoint( static_cast<int>( p2X+pXD ),
1474 static_cast<int>( p2Y+pYD ) ) );
1475 pDayX = p1X + pXDelta;
1476 pDayY = p1Y + pYDelta;
1477 pXD += orgXD;
1478 pYD += orgYD;
1479 }
1480 /* khz: currently unused
1481 if( weekPaint ){
1482 painter->drawLine( TQPoint( pWeekX+pXD, pWeekY+pYD ),
1483 TQPoint( p2X + pXD, p2Y + pYD ) );
1484 if( (pXDelta/2.0 < p2X - pWeekX) || (pYDelta/2.0 < p2Y - pWeekY) ){
1485 TQPen oldPen( painter->pen() );
1486 painter->setPen( TQPen( labelsColor ) );
1487 painter->drawText( pWeekX+pXD-orgXD, pWeekY+pYD-orgYD,
1488 painter->setPen( oldPen );
1489 fabs((0.0 == pXDelta) ? pTextsW : (p2X - pWeekX)),
1490 fabs((0.0 == pYDelta) ? pTextsH : (p2Y - pWeekY)),
1491 textAlign | TQt::DontClip,
1492 TQString::number( dt.date().week() ) );
1493 if ( para.axisShowGrid() ){
1494 if( !dayPaint && 40 < pXYDelta ){
1495 // draw 7 lines:
1496 //saveDrawLine( *painter,
1497 // TQPoint( pWeekX+pXDelta/2,
1498 // pWeekY+pYDelta/2 ),
1499 // TQPoint( pWeekX+pXDelta/2 + gridDX,
1500 // pWeekY+pYDelta/2 + gridDY ),
1501 // subSubGridPen );
1502 }
1503 saveDrawLine( *painter,
1504 TQPoint( pWeekX,
1505 pWeekY ),
1506 TQPoint( pWeekX + gridDX,
1507 pWeekY + gridDY ),
1508 pen );
1509 pen = gridPen;
1510 }
1511 if( !monthPaint || pMonthX != pDayX || pMonthY != pDayY ){
1512 painter->drawLine( TQPoint( pWeekX, pWeekY ), TQPoint( pWeekX+pXD, pWeekY+pYD ) );
1513 }
1514 }
1515 if( endLoop && !monthPaint )
1516 painter->drawLine( TQPoint( p2X, p2Y ), TQPoint( p2X+pXD, p2Y+pYD ) );
1517 pWeekX = p1X + pXDelta;
1518 pWeekY = p1Y + pYDelta;
1519 pXD += orgXD;
1520 pYD += orgYD;
1521 }
1522 */
1523 if( monthPaint ){
1524 painter->drawLine( TQPoint( static_cast<int>( pMonthX+pXD ),
1525 static_cast<int>( pMonthY+pYD ) ),
1526 TQPoint( static_cast<int>( p2X + pXD ),
1527 static_cast<int>( p2Y + pYD ) ) );
1528 if( (pXDelta/2.0 < p2X - pMonthX) || (pYDelta/2.0 < p2Y - pMonthY) ){
1529 TQPen oldPen( painter->pen() );
1530 painter->setPen( TQPen( labelsColor ) );
1531 painter->drawText( static_cast<int>( pMonthX+pXD-orgXD ),
1532 static_cast<int>( pMonthY+pYD-orgYD ),
1533 static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pMonthX)) ),
1534 static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pMonthY)) ),
1535 cv.textAlign | TQt::DontClip,
1536 TQString::number( dt.date().month() ) );
1537 painter->setPen( oldPen );
1538 if ( para.axisShowGrid() ){
1539 /* khz: currently unused
1540 if( !weekPaint &&
1541 && 10 < pXYDelta ){
1542 saveDrawLine( *painter,
1543 TQPoint( pMonthX+pXDelta/2,
1544 pMonthY+pYDelta/2 ),
1545 TQPoint( pMonthX+pXDelta/2 + gridDX,
1546 pMonthY+pYDelta/2 + gridDY ),
1547 subSubGridPen );
1548 }
1549 */
1550 saveDrawLine( *painter,
1551 TQPoint( static_cast<int>( pMonthX ),
1552 static_cast<int>( pMonthY ) ),
1553 TQPoint( static_cast<int>( pMonthX + gridDX ),
1554 static_cast<int>( pMonthY + gridDY ) ),
1555 pen );
1556 pen = gridPen;
1557 }
1558 if( !yearPaint || pYearX != pMonthX || pYearY != pMonthY ){
1559 painter->drawLine( TQPoint( static_cast<int>( pMonthX ),
1560 static_cast<int>( pMonthY ) ),
1561 TQPoint( static_cast<int>( pMonthX+pXD ),
1562 static_cast<int>( pMonthY+pYD ) ) );
1563 }
1564 }
1565 if( endLoop && !yearPaint )
1566 painter->drawLine( TQPoint( static_cast<int>( p2X ),
1567 static_cast<int>( p2Y ) ),
1568 TQPoint( static_cast<int>( p2X+pXD ),
1569 static_cast<int>( p2Y+pYD ) ) );
1570 pMonthX = p1X + pXDelta;
1571 pMonthY = p1Y + pYDelta;
1572 pXD += orgXD;
1573 pYD += orgYD;
1574 }
1575 if( yearPaint ){
1576 painter->drawLine( TQPoint( static_cast<int>( pYearX+pXD ),
1577 static_cast<int>( pYearY+pYD ) ),
1578 TQPoint( static_cast<int>( p2X + pXD ),
1579 static_cast<int>( p2Y + pYD ) ) );
1580 if( (pXDelta/2.0 < p2X - pYearX) || (pYDelta/2.0 < p2Y - pYearY) ){
1581 TQPen oldPen( painter->pen() );
1582 painter->setPen( TQPen( labelsColor ) );
1583 painter->drawText( static_cast<int>( pYearX+pXD-orgXD ),
1584 static_cast<int>( pYearY+pYD-orgYD ),
1585 static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pYearX)) ),
1586 static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pYearY)) ),
1587 cv.textAlign | TQt::DontClip,
1588 TQString::number( dt.date().year() ) );
1589 painter->setPen( oldPen );
1590 if ( para.axisShowGrid() ){
1591 if( !monthPaint && 10 < pXYDelta ){
1592 saveDrawLine( *painter,
1593 TQPoint( static_cast<int>( pYearX+pXDelta/2 ),
1594 static_cast<int>( pYearY+pYDelta/2 ) ),
1595 TQPoint( static_cast<int>( pYearX+pXDelta/2 + gridDX ),
1596 static_cast<int>( pYearY+pYDelta/2 + gridDY ) ),
1597 subSubGridPen );
1598 }
1599 saveDrawLine( *painter,
1600 TQPoint( static_cast<int>( pYearX ),
1601 static_cast<int>( pYearY ) ),
1602 TQPoint( static_cast<int>( pYearX + gridDX ),
1603 static_cast<int>( pYearY + gridDY ) ),
1604 pen );
1605 pen = gridPen;
1606 }
1607 painter->drawLine( TQPoint( static_cast<int>( pYearX ),
1608 static_cast<int>( pYearY ) ),
1609 TQPoint( static_cast<int>( pYearX+pXD ),
1610 static_cast<int>( pYearY+pYD ) ) );
1611 }
1612 if( endLoop )
1613 painter->drawLine( TQPoint( static_cast<int>( p2X ),
1614 static_cast<int>( p2Y ) ),
1615 TQPoint( static_cast<int>( p2X+pXD ),
1616 static_cast<int>( p2Y+pYD ) ) );
1617 pYearX = p1X + pXDelta;
1618 pYearY = p1Y + pYDelta;
1619 pXD += orgXD;
1620 pYD += orgYD;
1621 }
1622 if( &testDt != &newDt )
1623 break;
1624 dt = newDt;
1625 iLabel += 1.0;
1626 }
1627 if( !commonDtHeader.isEmpty() ){
1628 TQPen oldPen( painter->pen() );
1629 painter->setPen( TQPen( labelsColor ) );
1630 painter->drawText( static_cast<int>( x1 + pXD ), static_cast<int>( y1 + pYD ),
1631 commonDtHeader );
1632 painter->setPen( oldPen );
1633 }
1634 }else{
1635 int iLeaveOut = nLeaveOut;
1636 TQString label;
1637 for ( TQStringList::Iterator labelIter = labelTexts->begin();
1638 labelIter != labelTexts->end();
1639 ++labelIter ) {
1640 TQDateTime dt;
1641 if( cv.isDateTime ){
1642 dt = TQDateTime::fromString( *labelIter,
1643 TQt::ISODate );
1644 label = dt.toString( formatDT );
1645 }else{
1646 label = *labelIter;
1647 }
1648
1649 if( iLeaveOut < nLeaveOut )
1650 ++iLeaveOut;
1651 else
1652 iLeaveOut = 0;
1653 //Pending Michel: test if the user implicitely wants to get rid
1654 //of the non fractional values delimiters and grid lines.
1655 // axisDigitsBehindComma == 0 and the user implicitely
1656 // setAxisShowFractionalValuesDelimiters() to false
1657 bool showDelim = para.axisShowFractionalValuesDelimiters();
1658 if ( para.axisShowGrid() && !bMultiRowBarChart ) {
1659 if ( !label.isNull() || showDelim ){
1660 if( !iLeaveOut )
1661 // draw the main grid line
1662
1663 saveDrawLine( *painter,
1664 TQPoint( static_cast<int>( pGAX - pGXMicroAdjust ),
1665 static_cast<int>( pGAY - pGYMicroAdjust ) ),
1666 TQPoint( static_cast<int>( pGZX - pGXMicroAdjust ),
1667 static_cast<int>( pGZY - pGYMicroAdjust ) ),
1668 gridPen );
1669
1670 else if( para.axisShowSubDelimiters() )
1671 // draw a thin sub grid line instead of main line
1672 saveDrawLine( *painter,
1673 TQPoint( static_cast<int>( pGAX - pGXMicroAdjust ),
1674 static_cast<int>( pGAY - pGYMicroAdjust ) ),
1675 TQPoint( static_cast<int>( pGZX - pGXMicroAdjust ),
1676 static_cast<int>( pGZY - pGYMicroAdjust ) ),
1677 leaveOutGridPen );
1678 }
1679 }
1680 if ( para.axisLabelsVisible() ) {
1681 if( !iLeaveOut ) {
1682 /*PENDING Michel: those points should not be redrawn if sub-delimiters are drawn
1683 *drawing the submarkers
1684 * make it visible or not
1685 *In the case we have a null label - axisDigitsBehindComma is implicitely set to 0 -
1686 *also paint or dont paint the delimiter corresponding to this label - default is paint.
1687 */
1688 if ( !label.isNull() || showDelim )
1689 painter->drawLine( TQPoint( static_cast<int>( p1X ),
1690 static_cast<int>( p1Y ) ),
1691 TQPoint( static_cast<int>( p2X ),
1692 static_cast<int>( p2Y ) ) );
1693
1694 cv.pLastX = p1X;
1695 cv.pLastY = p1Y;
1696 TQPen oldPen( painter->pen() );
1697 painter->setPen( TQPen( labelsColor ) );
1698 if( para.axisLabelsDontShrinkFont()
1699 && isHorizontalAxis
1700 && (TQt::AlignHCenter == (cv.textAlign & TQt::AlignHCenter)) ) {
1701 double w = fm.width( label ) + 4.0;
1702 double x0 = cv.pTextsX + cv.pTextsW / 2.0;
1703
1704 painter->drawText( static_cast<int>( x0 - w / 2.0 ),
1705 static_cast<int>( cv.pTextsY ),
1706 static_cast<int>( w ),
1707 static_cast<int>( cv.pTextsH ),
1708 cv.textAlign, label );
1709 } else {
1710 if( nRotation ){
1711 KDDrawText::drawRotatedText(
1712 painter,
1713 nRotation,
1714 painter->worldMatrix().map(
1715 TQPoint( static_cast<int>( cv.pTextsX ),
1716 static_cast<int>( cv.pTextsY ) ) ),
1717 label,
1718 0,
1719 cv.textAlign,
1720 false,
1721 &fm,
1722 screenOutput,screenOutput,0,
1723 screenOutput );
1724 } else {
1725 // Pending Michel draw the axis labels
1726 painter->drawText( static_cast<int>( cv.pTextsX ),
1727 static_cast<int>( cv.pTextsY ),
1728 static_cast<int>( cv.pTextsW ),
1729 static_cast<int>( cv.pTextsH ),
1730 cv.textAlign | TQt::DontClip,
1731 label );
1732
1733 // debugging text rect
1734 /*
1735 painter->drawRect(static_cast <int>(cv.pTextsX),
1736 static_cast <int>(cv.pTextsY),
1737 static_cast <int> (nUsableAxisWidth),
1738 static_cast <int> (nUsableAxisHeight));
1739 */
1740 }
1741 }
1742 painter->setPen( oldPen );
1743 }
1744 }
1745
1746
1747 if( cv.isDateTime ){
1748 if( labelTexts->begin() == labelIter ){
1749 ((KDChartAxisParams&)para).setAxisDtLowPos(
1750 pGAX - pGXMicroAdjust,
1751 pGAY - pGYMicroAdjust );
1752 // adjust stored dt-low settings
1753 ( ( KDChartAxisParams& ) para ).setTrueAxisDtLow( dt );
1754 }else{
1755 ((KDChartAxisParams&)para).setAxisDtHighPos(
1756 pGAX - pGXMicroAdjust,
1757 pGAY - pGYMicroAdjust );
1758 // adjust stored dt-high settings
1759 ( ( KDChartAxisParams& ) para ).setTrueAxisDtHigh( dt );
1760 }
1761 }
1762
1763 iLabel += 1.0;
1764 p1X = x1 + iLabel * pXDelta;
1765 p1Y = y1 + iLabel * pYDelta;
1766 p2X = x2 + iLabel * pXDelta;
1767 p2Y = y2 + iLabel * pYDelta;
1768 cv.pTextsX = xT + iLabel * pXDelta;
1769 cv.pTextsY = yT + iLabel * pYDelta;
1770
1771 pGAX = xGA + iLabel * pXDelta;
1772 pGAY = yGA + iLabel * pYDelta;
1773 pGZX = xGZ + iLabel * pXDelta;
1774 pGZY = yGZ + iLabel * pYDelta;
1775 /*
1776 pGAX = xGA + iLabel * pXSubDelimDelta / cv.nSubDelimFactor;
1777 pGAY = yGA + iLabel * pYSubDelimDelta / cv.nSubDelimFactor;
1778 pGZX = xGZ + iLabel * pXSubDelimDelta / cv.nSubDelimFactor;
1779 pGZY = yGZ + iLabel * pYSubDelimDelta / cv.nSubDelimFactor;
1780 */
1781 }
1782 }
1783
1784
1785 // adjust zero-line start, if not starting at origin
1786 if ( cv.bSteadyCalc &&
1787 ( para.axisValuesDecreasing() ||
1788 (0.0 != para.trueAxisLow()) ) ) {
1789 //double x = p1.x();
1790 double x = 0.0;
1791 /* we have to find the *real* X axis position,
1792 this is NOT always the p1.x() as it is the
1793 case for left2 or right2 axes. [cmw, 12/01/2005] */
1794 if (cv.basicPos==KDChartAxisParams::AxisPosRight)
1795 x = static_cast<double>(_dataRect.right());
1796 else
1797 x = static_cast<double>(_dataRect.left());
1798 double y = p1.y();
1799 double mult = para.trueAxisLow() / para.trueAxisDelta();
1800 x -= mult * pXDelta;
1801 y -= mult * pYDelta;
1802 axisZeroLineStartX = x;
1803 axisZeroLineStartY = y;
1804 //tqDebug( "axisZeroLineStartX %f, axisZeroLineStartY %f",
1805 // axisZeroLineStartX, axisZeroLineStartY );
1806 }
1807
1808 painter->setClipping( oldClippingFlag );
1809 } // if( nLabels )
1810
1811 // draw zero-line (Ok, this might be overwritten by axes
1812 // cause those are drawn after all labels and grid and
1813 // zero-line(s) has been painted, see code below, starting
1814 // with "// draw all the axes".
1815 if ( cv.bSteadyCalc && !cv.isDateTime ) {
1816 ( ( KDChartAxisParams& ) para ).setAxisZeroLineStart( axisZeroLineStartX, axisZeroLineStartY );
1817 double axisZeroLineStart;
1818 int minCoord, maxCoord;
1819 double xFactor, yFactor;
1820 switch( cv.basicPos ){
1821 case KDChartAxisParams::AxisPosLeft:
1822 xFactor = 1.0;
1823 yFactor = 0.0;
1824 axisZeroLineStart = axisZeroLineStartY;
1825 minCoord = TQMIN( cv.orig.y(), cv.dest.y() );
1826 maxCoord = TQMAX( cv.orig.y(), cv.dest.y() );
1827
1828 break;
1829 case KDChartAxisParams::AxisPosRight:
1830 xFactor = -1.0;
1831 yFactor = 0.0;
1832 axisZeroLineStart = axisZeroLineStartY;
1833 minCoord = TQMIN( cv.orig.y(), cv.dest.y() );
1834 maxCoord = TQMAX( cv.orig.y(), cv.dest.y() );
1835 break;
1836 case KDChartAxisParams::AxisPosTop:
1837 xFactor = 0.0;
1838 yFactor = 1.0;
1839 axisZeroLineStart = axisZeroLineStartX;
1840 minCoord = TQMIN( cv.orig.x(), cv.dest.x() );
1841 maxCoord = TQMAX( cv.orig.x(), cv.dest.x() );
1842 break;
1843 case KDChartAxisParams::AxisPosBottom:
1844 xFactor = 0.0;
1845 yFactor = -1.0;
1846 axisZeroLineStart = axisZeroLineStartX;
1847 minCoord = TQMIN( cv.orig.x(), cv.dest.x() );
1848 maxCoord = TQMAX( cv.orig.x(), cv.dest.x() );
1849 break;
1850 default:
1851 xFactor = 0.0;
1852 yFactor = 0.0;
1853 axisZeroLineStart = 0.0;
1854 minCoord = 0;
1855 maxCoord = 0;
1856 }
1857 if( axisZeroLineStart >= minCoord &&
1858 axisZeroLineStart <= maxCoord ){
1859 TQPoint pZ0( static_cast<int>( para.axisZeroLineStartX() ),
1860 static_cast<int>( para.axisZeroLineStartY() ) );
1861 TQPoint pZ( static_cast<int>( para.axisZeroLineStartX()
1862 + xFactor * _dataRect.width() ),
1863 static_cast<int>( para.axisZeroLineStartY()
1864 + yFactor * _dataRect.height() ) );
1865 //tqDebug("------");
1866 saveDrawLine( *painter,
1867 pZ0,
1868 pZ,
1869 TQPen( para.axisZeroLineColor(),
1870 lineWidth ) );
1871 }
1872 }
1873
1874 }
1875
1876 }
1877
1878 // Drawing all the axes lines:
1879/*
1880 // 1. test if the standard axes are share one or several corner points
1881 // if yes, we first draw a polyline using a "TQt::MiterJoin" PenJoinStyle
1882 // to make sure the corners are filled
1883 internal__KDChart__CalcValues& cv1 = calcVal[ KDChartAxisParams::AxisPosLeft ];
1884 internal__KDChart__CalcValues& cv2 = calcVal[ KDChartAxisParams::AxisPosBottom ];
1885 const KDChartAxisParams& pa1 = params()->axisParams( KDChartAxisParams::AxisPosLeft );
1886 const KDChartAxisParams& pa2 = params()->axisParams( KDChartAxisParams::AxisPosBottom );
1887tqDebug("\n\nx1: %i y1: %i w1: %i\nx2: %i y2: %i w2: %i",
1888cv1.orig.x(), cv1.orig.y(), pa1.axisTrueLineWidth(),
1889cv2.orig.x(), cv2.orig.y(), pa2.axisTrueLineWidth() );
1890 if( cv1.orig == cv2.orig ){
1891 const TQColor c1( pa1.axisLineColor() );
1892 const TQColor c2( pa2.axisLineColor() );
1893 const TQPoint pA( cv1.dest );
1894 const TQPoint pB( cv1.orig );
1895 const TQPoint pC( cv2.dest );
1896 TQPen pen( TQColor( (c1.red() + c2.red()) /2,
1897 (c1.green() + c2.green())/2,
1898 (c1.blue() + c2.blue()) /2 ),
1899 TQMIN(pa1.axisTrueLineWidth(), pa2.axisTrueLineWidth()) );
1900 pen.setJoinStyle( TQt::MiterJoin );
1901 painter->setPen( pen );
1902 TQPointArray a;
1903 a.putPoints( 0, 3, pA.x(),pA.y(), pB.x(),pB.y(), pC.x(),pC.y() );
1904 painter->drawPolyline( a );
1905tqDebug("done\n" );
1906 }
1907*/
1908 // 2. draw the axes using their normal color
1909 for( iAxis = 0; iAxis < KDCHART_MAX_AXES; ++iAxis ){
1910 internal__KDChart__CalcValues& cv = calcVal[iAxis];
1911 const KDChartAxisParams & para = params()->axisParams( iAxis );
1912 if( cv.processThisAxis && para.axisLineVisible() ){
1913 painter->setPen( TQPen( para.axisLineColor(),
1914 para.axisTrueLineWidth() ) );
1915 int x = cv.dest.x();
1916 if( 2.0 >= TQABS(cv.pLastX - x) )
1917 x = static_cast < int > ( cv.pLastX );
1918 int y = cv.dest.y();
1919 if( 2.0 >= TQABS(cv.pLastY - y) )
1920 y = static_cast < int > ( cv.pLastY );
1921 painter->drawLine( cv.orig, TQPoint(x,y) );
1922 }
1923 }
1924
1925 painter->restore();
1926}
1927
1928
1929double fastPow10( int x )
1930{
1931 double res = 1.0;
1932 if( 0 <= x ){
1933 for( int i = 1; i <= x; ++i )
1934 res *= 10.0;
1935 }else{
1936 for( int i = -1; i >= x; --i )
1937 res /= 10.0;
1938 }
1939 return res;
1940}
1941double fastPow10( double x )
1942{
1943 return pow(10.0, x);
1944}
1945
1946
1966/**** static ****/
1968 TQPainter* painter,
1969 const KDChartTableDataBase& data,
1970 const KDChartParams& params,
1971 uint axisNumber,
1972 double averageValueP1000,
1973 double delimLen,
1974 // start of return parameters
1975 KDChartAxisParams::AxisPos& basicPos,
1976 TQPoint& orig,
1977 TQPoint& dest,
1978 double& pXDeltaFactor,
1979 double& pYDeltaFactor,
1980 double& pXDelimDeltaFaktor,
1981 double& pYDelimDeltaFaktor,
1982 double& nSubDelimFactor,
1983 double& pDelimDelta,
1984 double& nTxtHeight,
1985 double& pTextsX,
1986 double& pTextsY,
1987 double& pTextsW,
1988 double& pTextsH,
1989 int& textAlign,
1990 bool& isLogarithmic,
1991 bool& isDateTime,
1992 bool& autoDtLabels,
1993 TQDateTime& dtLow,
1994 TQDateTime& dtHigh,
1995 KDChartAxisParams::ValueScale& dtDeltaScale,
1996 bool adjustTheValues,
1997 double trueDelta,
1998 double trueDeltaPix )
1999{
2000//tqDebug("\nentering KDChartAxesPainter::calculateLabelTexts() : nTxtHeight: "+TQString::number(nTxtHeight));
2001 const KDChartAxisParams & para = params.axisParams( axisNumber );
2002
2003 // store whether the labels are to be drawn in reverted order
2004 const bool bDecreasing = para.axisValuesDecreasing();
2005
2006 basicPos = KDChartAxisParams::basicAxisPos( axisNumber );
2007
2008 pXDeltaFactor = 0.0;
2009 pYDeltaFactor = 0.0;
2010 pXDelimDeltaFaktor = 0.0;
2011 pYDelimDeltaFaktor = 0.0;
2012 int axisLength;
2013 switch ( basicPos ) {
2014 case KDChartAxisParams::AxisPosBottom: {
2015 axisLength = para.axisTrueAreaRect().width();
2016 orig = bDecreasing
2017 ? para.axisTrueAreaRect().topRight()
2018 : para.axisTrueAreaRect().topLeft();
2019 dest = bDecreasing
2020 ? para.axisTrueAreaRect().topLeft()
2021 : para.axisTrueAreaRect().topRight();
2022 pYDelimDeltaFaktor = 1.0;
2023 pXDeltaFactor = bDecreasing ? -1.0 : 1.0;
2024 //tqDebug("\nsetting pXDeltaFactor for axis %x", axisNumber);
2025 //tqDebug(bDecreasing ? "bDecreasing = true" : "bDecreasing = false");
2026 //tqDebug("pXDeltaFactor = %f\n",pXDeltaFactor);
2027 }
2028 break;
2029 case KDChartAxisParams::AxisPosLeft: {
2030 axisLength = para.axisTrueAreaRect().height();
2031 orig = bDecreasing
2032 ? para.axisTrueAreaRect().topRight()
2033 : para.axisTrueAreaRect().bottomRight();
2034 dest = bDecreasing
2035 ? para.axisTrueAreaRect().bottomRight()
2036 : para.axisTrueAreaRect().topRight();
2037 pXDelimDeltaFaktor = -1.0;
2038 pYDeltaFactor = bDecreasing ? 1.0 : -1.0;
2039 }
2040 break;
2041 case KDChartAxisParams::AxisPosTop: {
2042 axisLength = para.axisTrueAreaRect().width();
2043 orig = bDecreasing
2044 ? para.axisTrueAreaRect().bottomRight()
2045 : para.axisTrueAreaRect().bottomLeft();
2046 dest = bDecreasing
2047 ? para.axisTrueAreaRect().bottomLeft()
2048 : para.axisTrueAreaRect().bottomRight();
2049 pYDelimDeltaFaktor = -1.0;
2050 pXDeltaFactor = bDecreasing ? -1.0 : 1.0;
2051 }
2052 break;
2053 case KDChartAxisParams::AxisPosRight: {
2054 axisLength = para.axisTrueAreaRect().height();
2055 orig = bDecreasing
2056 ? para.axisTrueAreaRect().topLeft()
2057 : para.axisTrueAreaRect().bottomLeft();
2058 dest = bDecreasing
2059 ? para.axisTrueAreaRect().bottomLeft()
2060 : para.axisTrueAreaRect().topLeft();
2061 pXDelimDeltaFaktor = 1.0;
2062 pYDeltaFactor = bDecreasing ? 1.0 : -1.0;
2063 }
2064 break;
2065 default: {
2066 axisLength = 0;
2067 tqDebug( "IMPLEMENTATION ERROR: KDChartAxesPainter::paintAxes() unhandled enum value." );
2068 }
2069 break;
2070 }
2071
2072 // which dataset(s) is/are represented by this axis?
2073 uint dataset, dataset2, chart;
2074 if ( !params.axisDatasets( axisNumber, dataset, dataset2, chart ) ) {
2075 dataset = KDCHART_ALL_DATASETS;
2076 dataset2 = KDCHART_ALL_DATASETS;
2077 chart = 0;
2078 //tqDebug("\nautomatic set values: chart: %u,\ndataset: %u, dataset2: %u",
2079 //chart, dataset, dataset2);
2080 }
2081 // which dataset(s) with mode DataEntry (or mode ExtraLinesAnchor, resp.)
2082 // is/are represented by this axis?
2083 uint dataDataset, dataDataset2;
2084 if( params.findDatasets( KDChartParams::DataEntry,
2085 KDChartParams::ExtraLinesAnchor,
2086 dataDataset,
2087 dataDataset2,
2088 chart ) ) {
2089 // adjust dataDataset in case MORE THAN ONE AXIS
2090 // is representing THIS CHART
2091 if( ( KDCHART_ALL_DATASETS != dataset
2092 && KDCHART_NO_DATASET != dataset )
2093 || ( KDCHART_ALL_DATASETS != dataDataset
2094 && KDCHART_NO_DATASET != dataDataset ) ){
2095 int ds = (KDCHART_ALL_DATASETS != dataset)
2096 ? dataset
2097 : 0;
2098 int dds = (KDCHART_ALL_DATASETS != dataDataset)
2099 ? dataDataset
2100 : 0;
2101 dataDataset = TQMAX( ds, dds );
2102 }
2103 if( ( KDCHART_ALL_DATASETS != dataset2
2104 && KDCHART_NO_DATASET != dataset2 )
2105 || ( KDCHART_ALL_DATASETS != dataDataset2
2106 && KDCHART_NO_DATASET != dataDataset2 ) ){
2107 int ds2 = (KDCHART_ALL_DATASETS != dataset2)
2108 ? dataset2
2109 : KDCHART_MAX_AXES-1;
2110 int dds2 = (KDCHART_ALL_DATASETS != dataDataset2)
2111 ? dataDataset2
2112 : KDCHART_MAX_AXES-1;
2113 dataDataset2 = TQMIN( ds2, dds2 );
2114 }
2115 }
2116 else {
2117 // Should not happen
2118 tqDebug( "IMPLEMENTATION ERROR: findDatasets( DataEntry, ExtraLinesAnchor ) should *always* return true. (b)" );
2119 dataDataset = KDCHART_ALL_DATASETS;
2120 }
2121 //tqDebug("\naxisNumber: %x\nchart: %x\ndataset: %x, dataset2: %x,\ndataDataset: %x, dataDataset2: %x",
2122 //axisNumber, chart, dataset, dataset2, dataDataset, dataDataset2);
2123
2124 if ( para.axisLabelsFontUseRelSize() ){
2125 nTxtHeight = para.axisLabelsFontRelSize()
2126 * averageValueP1000;
2127//tqDebug("using rel. size in KDChartAxesPainter::calculateLabelTexts() : nTxtHeight: "+TQString::number(nTxtHeight));
2128 }else {
2129 TQFontInfo info( para.axisLabelsFont() );
2130 nTxtHeight = info.pointSize();
2131//tqDebug("using FIXED size in KDChartAxesPainter::calculateLabelTexts() : nTxtHeight: "+TQString::number(nTxtHeight));
2132 }
2133
2134 const KDChartEnums::NumberNotation notation = para.axisLabelsNotation();
2135 const int behindComma = para.axisDigitsBehindComma();
2136 const int divPow10 = para.axisLabelsDivPow10();
2137 const TQString decimalPoint = para.axisLabelsDecimalPoint();
2138 const TQString thousandsPoint = para.axisLabelsThousandsPoint();
2139 const TQString prefix = para.axisLabelsPrefix();
2140 const TQString postfix = para.axisLabelsPostfix();
2141 const int totalLen = para.axisLabelsTotalLen();
2142 const TQChar padFill = para.axisLabelsPadFill();
2143 const bool blockAlign = para.axisLabelsBlockAlign();
2144
2145 TQStringList labelTexts;
2146 int colNum = para.labelTextsDataRow();
2147 bool bDone = true;
2148 switch ( para.axisLabelTextsFormDataRow() ) {
2149 case KDChartAxisParams::LabelsFromDataRowYes: {
2150 // Take whatever is in the specified column (even if not a string)
2151 int trueBehindComma = -1;
2152 TQVariant value;
2153 for ( uint iDataset = 0; iDataset < data.usedRows(); iDataset++ ) {
2154 if( data.cellCoord( iDataset, colNum, value, 1 ) ){
2155 if( TQVariant::String == value.type() )
2156 labelTexts.append( value.toString() );
2157 else {
2158 labelTexts.append( applyLabelsFormat( value.toDouble(),
2159 divPow10,
2160 behindComma,
2161 para.axisValueDelta(),
2162 trueBehindComma,
2163 notation,
2164 decimalPoint,
2165 thousandsPoint,
2166 prefix,
2167 postfix,
2168 totalLen,
2169 padFill,
2170 blockAlign ) );
2171
2172 }
2173 }
2174 }
2175 break;
2176 }
2177 case KDChartAxisParams::LabelsFromDataRowGuess: {
2178 TQVariant value;
2179 for ( uint iDataset = 0; iDataset < data.usedRows(); iDataset++ ) {
2180 if( data.cellCoord( iDataset, colNum, value, 1 ) ){
2181 if( TQVariant::String == value.type() ){
2182 const TQString sVal( value.toString() );
2183 if( !sVal.isEmpty() && !sVal.isNull() )
2184 labelTexts.append( sVal );
2185 }
2186 }else{
2187 labelTexts.clear();
2188 bDone = false;
2189 break;
2190 }
2191 }
2192 break;
2193 }
2194 case KDChartAxisParams::LabelsFromDataRowNo: {
2195 bDone = false;
2196 break;
2197 }
2198 default:
2199 // Should not happen
2200 tqDebug( "KDChart: Unknown label texts source" );
2201 }
2202
2203 // if necessary adjust text params *including* the steady value calc setting
2204 const bool dataCellsHaveSeveralCoordinates =
2205 (KDCHART_ALL_DATASETS == dataDataset)
2206 ? data.cellsHaveSeveralCoordinates()
2207 : data.cellsHaveSeveralCoordinates( dataDataset, dataDataset2 );
2208 if( dataCellsHaveSeveralCoordinates && !para.axisSteadyValueCalc() )
2209 ((KDChartParams&)params).setAxisLabelTextParams(
2210 axisNumber,
2211 true,
2215 para.axisLabelsDigitsBehindComma() );// NOTE: This sets MANY other params to default values too!
2216
2217
2218 const KDChartParams::ChartType params_chartType
2219 = ( 0 == chart )
2220 ? params.chartType()
2221 : params.additionalChartType();
2222
2223
2224 // store whether we are calculating Ordinate-like axis values
2225 const bool bSteadyCalc = para.axisSteadyValueCalc();
2226
2227 // store whether logarithmic calculation is wanted
2228 isLogarithmic = bSteadyCalc &&
2229 (KDChartParams::Line == params_chartType) &&
2230 (KDChartAxisParams::AxisCalcLogarithmic == para.axisCalcMode());
2231
2232 //tqDebug(bSteadyCalc ? "bSteadyCalc":"NOT bSteadyCalc");
2233 //tqDebug(isLogarithmic? "isLogarithmic":"NOT isLogarithmic");
2234
2235 // store whether this is a vertical axis or a horizontal axis
2236 const bool bVertAxis = (KDChartAxisParams::AxisPosLeft == basicPos) ||
2237 (KDChartAxisParams::AxisPosRight == basicPos);
2238
2239 // store the coordinate number to be used for this axis
2240 const int coordinate = bVertAxis ? 1 : 2;
2241
2242 // store whether our coordinates are double or TQDateTime values
2243 const TQVariant::Type valueType =
2244 (KDCHART_ALL_DATASETS == dataDataset)
2245 ? data.cellsValueType( coordinate )
2246 : data.cellsValueType( dataDataset, dataDataset2, coordinate );
2247 isDateTime = valueType == TQVariant::DateTime;
2248 bool bIsDouble = valueType == TQVariant::Double;
2249
2250 autoDtLabels = isDateTime && ( KDCHART_AXIS_LABELS_AUTO_DATETIME_FORMAT
2251 == para.axisLabelsDateTimeFormat() );
2252
2253 if( autoDtLabels || bSteadyCalc )
2254 ( ( KDChartAxisParams& ) para ).setAxisLabelsTouchEdges( true );
2255
2256 bool bStatistical = KDChartParams::HiLo == params_chartType
2257 || KDChartParams::BoxWhisker == params_chartType;
2258
2259 if ( !bVertAxis && KDChartParams::BoxWhisker == params_chartType
2260 && ! para.axisLabelStringCount() ) {
2261 uint ds1 = (KDCHART_ALL_DATASETS == dataDataset)
2262 ? 0
2263 : dataDataset;
2264 uint ds2 = (KDCHART_ALL_DATASETS == dataDataset)
2265 ? data.usedRows() - 1
2266 : dataDataset2;
2267 for (uint i = ds1; i <= ds2; ++i)
2268 labelTexts.append(
2269 TQObject::tr( "Series " ) + TQString::number( i + 1 ) );
2270 bDone = true;
2271 }
2272
2273 double nLow = 1.0 + bSteadyCalc;// ? 0.0 : data.colsScrolledBy();
2274 double nHigh = 10.0;
2275 double nDelta = 1.0;
2276 if ( !bDone ) {
2277 bDone = true;
2278
2279 // look if exact label specification was made via limits and delta
2280 if ( ! isLogarithmic
2281 && ! para.axisLabelStringCount()
2284 && ! ( para.axisValueStart() == para.axisValueEnd() )
2286 && ! ( 0.0 == para.axisValueDelta() ) ) {
2287 nLow = para.axisValueStart().toDouble();
2288 nHigh = para.axisValueEnd().toDouble();
2289 nDelta = para.axisValueDelta();
2290 int behindComma = para.axisDigitsBehindComma();
2291 int trueBehindComma = -1;
2292 bool upwards = (nLow < nHigh);
2293 if( upwards != (0.0 < nDelta) )
2294 nDelta *= -1.0;
2295 double nVal = nLow;
2296 bDone = false;
2297 bool bShowVeryLastLabel = false;
2298 //tqDebug("\n nLow: %f, nHigh: %f, nDelta: %f", nLow, nHigh, nDelta );
2299 while( bShowVeryLastLabel || (upwards ? (nVal <= nHigh) : (nVal >= nHigh)) ){
2300 //tqDebug("nVal : %f", nVal );
2301 labelTexts.append( applyLabelsFormat( nVal,
2302 divPow10,
2303 behindComma,
2304 nDelta,
2305 trueBehindComma,
2306 notation,
2307 decimalPoint,
2308 thousandsPoint,
2309 prefix,
2310 postfix,
2311 totalLen,
2312 padFill,
2313 blockAlign ) );
2314 nVal += nDelta;
2315 //tqDebug("nVal-neu: %f", nVal );
2316 if( ! (upwards ? (nVal <= nHigh) : (nVal >= nHigh)) ){
2317 // work around a rounding error: show the last label, even if not nVal == nHigh is not matching exactly
2318 if( bShowVeryLastLabel )
2319 bShowVeryLastLabel = false;
2320 else{
2321 TQString sHigh( applyLabelsFormat( nHigh,
2322 divPow10,
2323 behindComma,
2324 nDelta,
2325 trueBehindComma,
2326 notation,
2327 decimalPoint,
2328 thousandsPoint,
2329 prefix,
2330 postfix,
2331 totalLen,
2332 padFill,
2333 blockAlign ) );
2334 TQString sValue( applyLabelsFormat( nVal,
2335 divPow10,
2336 behindComma,
2337 nDelta,
2338 trueBehindComma,
2339 notation,
2340 decimalPoint,
2341 thousandsPoint,
2342 prefix,
2343 postfix,
2344 totalLen,
2345 padFill,
2346 blockAlign ) );
2347 bShowVeryLastLabel = (sValue == sHigh);
2348 //tqDebug("test: sHigh: "+sHigh+" sValue: "+sValue );
2349 }
2350 }
2351 bDone = true;
2352 }
2353 ( ( KDChartAxisParams& ) para ).setTrueAxisLowHighDelta( nLow, nHigh, nDelta );
2354 //tqDebug("\n[Z-0] nLow: %f, nHigh: %f, nDelta: %f", nLow, nHigh, nDelta );
2355 } // look if a string list was specified
2356 else if ( para.axisLabelStringCount() ) {
2357 int nLabels = bSteadyCalc
2358 ? para.axisLabelStringCount()
2359 : bStatistical ? data.usedRows() : data.usedCols();
2360 calculateBasicTextFactors( nTxtHeight, para, averageValueP1000,
2361 basicPos, orig, delimLen, nLabels,
2362 // start of return parameters
2363 pDelimDelta,
2364 pTextsX, pTextsY, pTextsW, pTextsH,
2365 textAlign );
2366 bool useShortLabels = false;
2367 TQStringList tmpList( para.axisLabelStringList() );
2368
2369 // find start- and/or end-entry
2370 int iStart = 0;
2371 int iEnd = para.axisLabelStringCount() - 1;
2373 || ! ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd() ) ) {
2374 const bool testStart = !( TQVariant::String == para.axisValueStart().type() );
2375 const bool testEnd = !( TQVariant::String == para.axisValueEnd().type() );
2376 TQString sStart = testStart
2377 ? para.axisValueStart().toString().upper()
2378 : TQString();
2379 TQString sEnd = testEnd
2380 ? para.axisValueEnd().toString().upper()
2381 : TQString();
2382
2383 uint i = 0;
2384 for ( TQStringList::Iterator it = tmpList.begin();
2385 it != tmpList.end(); ++it, ++i ) {
2386 if ( 0 == iStart &&
2387 0 == TQString::compare( sStart, ( *it ).upper() ) ) {
2388 iStart = i;
2389 }
2390 if ( 0 == TQString::compare( sEnd, ( *it ).upper() ) ) {
2391 iEnd = i;
2392 }
2393 }
2394 }
2395
2396 // check text widths to ensure all the entries will fit
2397 // into the available space
2398 if ( para.axisLabelStringCount()
2399 && para.axisShortLabelsStringCount()
2400 && para.axisLabelStringList() != para.axisShortLabelsStringList() ) {
2401 TQFont font( para.axisLabelsFont() );
2402 if ( para.axisLabelsFontUseRelSize() )
2403 font.setPixelSize( static_cast < int > ( nTxtHeight ) );
2404 painter->setFont( font );
2405 TQFontMetrics fm( painter->fontMetrics() );
2406
2407 TQStringList::Iterator it = tmpList.begin();
2408 for ( int i = 0; i < nLabels; ++i ) {
2409 if ( it != tmpList.end() ) {
2410 if ( fm.width( *it ) > pTextsW ) {
2411 useShortLabels = true;
2412 break;
2413 }
2414 ++it;
2415 }
2416 }
2417 }
2418 if ( useShortLabels )
2419 tmpList = para.axisShortLabelsStringList();
2420 else
2421 tmpList = para.axisLabelStringList();
2422
2423 // prepare transfering the strings into the labelTexts list
2424 double ddelta
2426 ? 1.0
2427 : para.axisValueDelta();
2428 modf( ddelta, &ddelta );
2429 bool positive = ( 0.0 <= ddelta );
2430 int delta = static_cast < int > ( fabs( ddelta ) );
2431 // find 1st significant entry
2432 TQStringList::Iterator it = positive
2433 ? tmpList.begin()
2434 : tmpList.fromLast();
2435 if ( positive )
2436 for ( int i = 0; i < (int)tmpList.count(); ++i ) {
2437 if ( i >= iStart )
2438 break;
2439 ++it;
2440 }
2441 else
2442 for ( int i = tmpList.count() - 1; i >= 0; --i ) {
2443 if ( i <= iEnd )
2444 break;
2445 --it;
2446 }
2447 // transfer the strings
2448 int meter = delta;
2449 int i2 = positive ? iStart : iEnd;
2450 for ( int iLabel = 0; iLabel < nLabels; ) {
2451 if ( positive ) {
2452 if ( it == tmpList.end() ) {
2453 it = tmpList.begin();
2454 i2 = 0;
2455 }
2456 } else {
2457 if ( it == tmpList.begin() ) {
2458 it = tmpList.end();
2459 i2 = tmpList.count();
2460 }
2461 }
2462 if ( ( positive && i2 >= iStart )
2463 || ( !positive && i2 <= iEnd ) ) {
2464 if ( meter >= delta ) {
2465 labelTexts << *it;
2466 ++iLabel;
2467 meter = 1;
2468 } else {
2469 meter += 1;
2470 }
2471 }
2472 if ( positive ) {
2473 if ( i2 == iEnd ) {
2474 it = tmpList.begin();
2475 i2 = 0;
2476 } else {
2477 ++it;
2478 ++i2;
2479 }
2480 } else {
2481 if ( i2 == iStart ) {
2482 it = tmpList.end();
2483 i2 = tmpList.count();
2484 } else {
2485 --it;
2486 --i2;
2487 }
2488 }
2489 }
2490 } else {
2491 // find out if the associated dataset contains only strings
2492 // if yes, we will take these as label texts
2493 uint dset = ( dataset == KDCHART_ALL_DATASETS ) ? 0 : dataset;
2494 //tqDebug("\ndset: %u", dset);
2495 bDone = false;
2496 TQVariant value;
2497 for ( uint col = 0; col < data.usedCols(); ++col ) {
2498 if( data.cellCoord( dset, col, value, coordinate ) ){
2499 if( TQVariant::String == value.type() ){
2500 const TQString sVal = value.toString();
2501 if( !sVal.isEmpty() && !sVal.isNull() ){
2502 labelTexts.append( sVal );
2503 bDone = true;
2504 }
2505 }else{
2506 labelTexts.clear();
2507 bDone = false;
2508 break;
2509 }
2510 }
2511 }
2512 }
2513 }
2514
2515
2516 if ( bDone ) {
2517 // Some strings were found, now let us see which of them are
2518 // actually to be taken right now.
2519 //
2520 //
2521 // F E A T U R E P L A N N E D F O R F U T U R E . . .
2522 //
2523 //
2524
2525 }
2526 else {
2527 // No strings were found, so let us either calculate the texts
2528 // based upon the numerical values of the associated dataset(s)
2529 // or just compose some default texts...
2530 if ( data.usedCols() && bSteadyCalc ) {
2531 // double values for numerical coordinates
2532 double nLow = 0.01;
2533 double nHigh = 0.0;
2534 double orgLow = 0.0;
2535 double orgHigh = 0.0;
2536 double nDelta = 0.0;
2537 double nDist = 0.0;
2538
2539 // VERTICAL axes support three modes:
2540 enum { Normal, Stacked, Percent } mode;
2541
2542 if( bVertAxis ){
2543 switch ( params_chartType ) {
2544 case KDChartParams::Bar:
2545 if ( KDChartParams::BarStacked
2546 == params.barChartSubType() )
2547 mode = Stacked;
2548 else if ( KDChartParams::BarPercent
2549 == params.barChartSubType() )
2550 mode = Percent;
2551 else
2552 mode = Normal;
2553 break;
2554 case KDChartParams::Line:
2555 if ( KDChartParams::LineStacked
2556 == params.lineChartSubType() )
2557 mode = Stacked;
2558 else if ( KDChartParams::LinePercent
2559 == params.lineChartSubType() )
2560 mode = Percent;
2561 else
2562 mode = Normal;
2563 break;
2564 case KDChartParams::Area:
2565 if ( KDChartParams::AreaStacked
2566 == params.areaChartSubType() )
2567 mode = Stacked;
2568 else if ( KDChartParams::AreaPercent
2569 == params.areaChartSubType() )
2570 mode = Percent;
2571 else
2572 mode = Normal;
2573 break;
2574 case KDChartParams::HiLo:
2575 case KDChartParams::BoxWhisker:
2576 mode = Normal;
2577 break;
2578 case KDChartParams::Polar:
2579 if ( KDChartParams::PolarStacked
2580 == params.polarChartSubType() )
2581 mode = Stacked;
2582 else if ( KDChartParams::PolarPercent
2583 == params.polarChartSubType() )
2584 mode = Percent;
2585 else
2586 mode = Normal;
2587 break;
2588 default: {
2589 // Should not happen
2590 tqDebug( "IMPLEMENTATION ERROR: Unknown params_chartType in calculateLabelTexts()" );
2591 mode = Normal;
2592 }
2593 }
2594 }else
2595 mode = Normal; // this axis is not a vertical axis
2596
2597 uint nLabels = 200;
2598
2599 // find highest and lowest value of associated dataset(s)
2600 bool bOrdFactorsOk = false;
2601
2602 if( adjustTheValues ){
2603 nDelta = fabs( trueDelta );
2604 pDelimDelta = trueDeltaPix;
2605 nLow = TQMIN( para.trueAxisLow(), para.trueAxisHigh() );
2606 //tqDebug("\nsearching: para.trueAxisLow() %f para.trueAxisHigh() %f",para.trueAxisLow(),para.trueAxisHigh());
2607 double orgLow( nLow );
2608 modf( nLow / nDelta, &nLow );
2609 nLow *= nDelta;
2610 if ( nLow > orgLow )
2611 nLow -= nDelta;
2612 if ( 0.0 < nLow && 0.0 >= orgLow )
2613 nLow = 0.0;
2614 nHigh = nLow;
2615 double dx = fabs( pXDeltaFactor * pDelimDelta );
2616 double dy = fabs( pYDeltaFactor * pDelimDelta );
2617 double x = 0.0;
2618 double y = 0.0;
2619 nLabels = 1;
2620 if( axisLength ){
2621 do{
2622 ++nLabels;
2623 nHigh += nDelta;
2624 x += dx;
2625 y += dy;
2626 }while( x < axisLength && y < axisLength );
2627 nHigh -= nDelta;
2628 --nLabels;
2629 }
2630 nDist = nHigh - nLow;
2631 bOrdFactorsOk = true;
2632
2633 }
2634
2635 if( !bOrdFactorsOk ){
2636 const bool bAutoCalcStart =
2637 ( Percent != mode )
2639 const bool bAutoCalcEnd =
2640 ( Percent != mode )
2642
2643 if( !bIsDouble && !isDateTime ){
2644 // no data at all: let us use our default 0..10 range
2645 nLow = 0.0;
2646 nHigh = 10.0;
2647 nDist = 10.0;
2648 nDelta = 1.0;
2649 nSubDelimFactor = 0.5;
2650 bIsDouble = true;
2651 bOrdFactorsOk = true;
2652 }else if( mode == Percent ){
2653 // precentage mode: we use a 0..100 range
2654 nLow = 0.0;
2655 nHigh = 100.0;
2656 nDist = 100.0;
2657 nDelta = 10.0;
2658 nSubDelimFactor = 0.25;
2659 bOrdFactorsOk = true;
2660 }else{
2661 //tqDebug("\ngo: nLow: %f nHigh: %f", nLow, nHigh );
2662 // get the raw start value
2663 const bool bStackedMode = (mode == Stacked);
2664 if( bAutoCalcStart ){
2665 if ( dataDataset == KDCHART_ALL_DATASETS ) {
2666 if( bIsDouble ){
2667 nLow = bStackedMode
2668 ? TQMIN( data.minColSum(), 0.0 )
2669 : data.minValue( coordinate,
2670 isLogarithmic );
2671 //tqDebug("\n1: nLow: %f", nLow );
2672
2673 }else{
2674 dtLow = data.minDtValue( coordinate );
2675 }
2676 } else {
2677 if( bIsDouble ){
2678 nLow = bStackedMode
2679 ? TQMIN( data.minColSum( dataDataset, dataDataset2 ),
2680 0.0 )
2681 : data.minInRows( dataDataset,dataDataset2,
2682 coordinate,
2683 isLogarithmic );
2684 }else{
2685 dtLow = data.minDtInRows( dataDataset,dataDataset2,
2686 coordinate );
2687 }
2688 }
2689 }else{
2690 if( bIsDouble ){
2691 if( TQVariant::Double == para.axisValueStart().type() )
2692 nLow = para.axisValueStart().toDouble();
2693 }else{
2694 if( TQVariant::DateTime == para.axisValueStart().type() )
2695 dtLow = para.axisValueStart().toDateTime();
2696 }
2697 }
2698
2699 // get the raw end value
2700 if( bAutoCalcEnd ){
2701 if ( dataDataset == KDCHART_ALL_DATASETS ) {
2702 if( bIsDouble ){
2703 nHigh = bStackedMode
2704 ? TQMAX( data.maxColSum(), 0.0 )
2705 : data.maxValue( coordinate );
2706 }else{
2707 dtHigh = data.maxDtValue( coordinate );
2708 }
2709 } else {
2710 if( bIsDouble )
2711 nHigh = bStackedMode
2712 ? TQMAX( data.maxColSum( dataDataset, dataDataset2 ),
2713 0.0 )
2714 : data.maxInRows( dataDataset,dataDataset2,
2715 coordinate );
2716 else
2717 dtHigh = data.maxDtInRows( dataDataset,dataDataset2,
2718 coordinate );
2719 }
2720 //tqDebug("\nbAutoCalcEnd:\n nLow: %f\n nHigh: %f", nLow, nHigh );
2721 }else{
2722 if( bIsDouble ){
2723 if( TQVariant::Double == para.axisValueEnd().type() )
2724 nHigh = para.axisValueEnd().toDouble();
2725 }else{
2726 if( TQVariant::DateTime == para.axisValueEnd().type() )
2727 dtHigh = para.axisValueEnd().toDateTime();
2728 }
2729 }
2730 }
2731
2732
2733 //tqDebug("\nnew: nLow: %f nHigh: %f", nLow, nHigh );
2734
2735 if( bIsDouble ) {
2736 if( DBL_MAX == nLow
2737 || ( ( 0.0 == nHigh || 0 == nHigh )
2738 && ( 0.0 == nLow || 0 == nLow ) ) ) {
2739 // tqDebug("NO values or all values have ZERO value, showing 0.0 - 10.0 span");
2740 nLow = 0.0;
2741 nHigh = 10.0;
2742 nDist = 10.0;
2743 nDelta = 1.0;
2744 nSubDelimFactor = 0.5;
2745 bOrdFactorsOk = true;
2746 //tqDebug("nLow: %f, nHigh: %f", nLow, nHigh);
2747 }else if( nLow == nHigh ){
2748 // if both values are equal, but NOT Zero
2749 // -> move the appropriate one to Zero
2750 if( nLow < 0.0 )
2751 nHigh = 0.0;
2752 else
2753 nLow = 0.0;
2754 //tqDebug("nLow: %f, nHigh: %f", nLow, nHigh);
2755 }else if( nHigh < nLow ){
2756 // make sure nLow is <= nHigh
2757 double nTmp = nLow;
2758 nLow = nHigh;
2759 nHigh = nTmp;
2760 }
2761 } else if( isDateTime ){
2762 bool toggleDts = dtLow > dtHigh;
2763 if( toggleDts ) {
2764 TQDateTime dt( dtLow );
2765 dtLow = dtHigh;
2766 dtHigh = dt;
2767 }
2768 double secDist = dtLow.secsTo( dtHigh );
2769 secDist += (dtHigh.time().msec() - dtLow.time().msec()) / 1000.0;
2770 const double aDist = fabs( secDist );
2771 const double secMin = 60.0;
2772 const double secHour = 60.0 * secMin;
2773 const double secDay = 24.0 * secHour;
2774 //
2775 // we temporarily disable week alignment until bug
2776 // is fixed (1st week of year must not start in the
2777 // preceeding year but rather be shown incompletely)
2778 //
2779 // (khz, 2003/10/10)
2780 //
2781 //const int secWeek = 7 * secDay;
2782 const double secMonth = 30 * secDay; // approx.
2783 const double secYear = 12 * secMonth; // approx.
2784 if( 2.0*secMin > aDist )
2785 dtDeltaScale = KDChartAxisParams::ValueScaleSecond;
2786 else if( 2.0*secHour > aDist )
2787 dtDeltaScale = KDChartAxisParams::ValueScaleMinute;
2788 else if( 2.0*secDay > aDist )
2789 dtDeltaScale = KDChartAxisParams::ValueScaleHour;
2790 // khz: else if( 2*secWeek > aDist )
2791 // khz: dtDeltaScale = KDChartAxisParams::ValueScaleDay;
2792 else if( 2.0*secMonth > aDist )
2793 dtDeltaScale = KDChartAxisParams::ValueScaleDay;
2794 // khz: dtDeltaScale = KDChartAxisParams::ValueScaleWeek;
2795
2796 else if( 2.0*secYear > aDist )
2797 dtDeltaScale = KDChartAxisParams::ValueScaleMonth;
2798 else if( 10.0*secYear > aDist )
2799 dtDeltaScale = KDChartAxisParams::ValueScaleQuarter;
2800 else
2801 dtDeltaScale = KDChartAxisParams::ValueScaleYear;
2802
2803
2804 //const int yearLow = dtLow.date().year();
2805 const int monthLow = dtLow.date().month();
2806 // khz: currently unused: const int dowLow = dtLow.date().dayOfWeek();
2807 const int dayLow = dtLow.date().day();
2808 const int hourLow = dtLow.time().hour();
2809 const int minuteLow = dtLow.time().minute();
2810 const int secondLow = dtLow.time().second();
2811
2812 //const int yearHigh = dtHigh.date().year();
2813 const int monthHigh = dtHigh.date().month();
2814 // khz: currently unused: const int dowHigh = dtHigh.date().dayOfWeek();
2815 const int hourHigh = dtHigh.time().hour();
2816 const int minuteHigh = dtHigh.time().minute();
2817 const int secondHigh = dtHigh.time().second();
2818 int yearLowD = 0;
2819 int monthLowD = 0;
2820 int dayLowD = 0;
2821 int hourLowD = 0;
2822 int minuteLowD = 0;
2823 int secondLowD = 0;
2824 int yearHighD = 0;
2825 int monthHighD = 0;
2826 int dayHighD = 0;
2827 int hourHighD = 0;
2828 int minuteHighD = 0;
2829 int secondHighD = 0;
2830 bool gotoEndOfMonth = false;
2831 switch( dtDeltaScale ) {
2832 case KDChartAxisParams::ValueScaleSecond:
2833 //tqDebug("\nKDChartAxisParams::ValueScaleSecond");
2834 if( 5.0 < aDist ){
2835 secondLowD = secondLow % 5;
2836 if( secondHigh % 5 )
2837 secondHighD = 5 - secondHigh % 5;
2838 }
2839 break;
2840 case KDChartAxisParams::ValueScaleMinute:
2841 //tqDebug("\nKDChartAxisParams::ValueScaleMinute");
2842 secondLowD = secondLow;
2843 secondHighD = 59-secondHigh;
2844 break;
2845 case KDChartAxisParams::ValueScaleHour:
2846 //tqDebug("\nKDChartAxisParams::ValueScaleHour");
2847 minuteLowD = minuteLow;
2848 secondLowD = secondLow;
2849 minuteHighD = 59-minuteHigh;
2850 secondHighD = 59-secondHigh;
2851 break;
2852 case KDChartAxisParams::ValueScaleDay:
2853 //tqDebug("\nKDChartAxisParams::ValueScaleDay");
2854 hourLowD = hourLow;
2855 minuteLowD = minuteLow;
2856 secondLowD = secondLow;
2857 hourHighD = 23-hourHigh;
2858 minuteHighD = 59-minuteHigh;
2859 secondHighD = 59-secondHigh;
2860 break;
2861 case KDChartAxisParams::ValueScaleWeek:
2862 //tqDebug("\nKDChartAxisParams::ValueScaleWeek");
2863 // khz: week scaling is disabled at the moment
2864 /*
2865 dayLowD = dowLow - 1;
2866 hourLowD = hourLow;
2867 minuteLowD = minuteLow;
2868 secondLowD = secondLow;
2869 if( 7 > dowHigh )
2870 dayHighD = 7 - dowHigh + 1;
2871 */
2872 break;
2873 case KDChartAxisParams::ValueScaleMonth:
2874 //tqDebug("\nKDChartAxisParams::ValueScaleMonth");
2875 if( 1 < dayLow )
2876 dayLowD = dayLow - 1;
2877 hourLowD = hourLow;
2878 minuteLowD = minuteLow;
2879 secondLowD = secondLow;
2880 gotoEndOfMonth = true;
2881 break;
2882 case KDChartAxisParams::ValueScaleQuarter:
2883 //tqDebug("\nKDChartAxisParams::ValueScaleQuarter");
2884 monthLowD = ( monthLow - 1 ) % 3;
2885 dayLowD = dayLow;
2886 hourLowD = hourLow;
2887 minuteLowD = minuteLow;
2888 secondLowD = secondLow;
2889 if( ( monthHigh - 1 ) % 3 )
2890 monthHighD = 3 - ( monthHigh - 1 ) % 3;
2891 gotoEndOfMonth = true;
2892 break;
2893 case KDChartAxisParams::ValueScaleYear:
2894 //tqDebug("\nKDChartAxisParams::ValueScaleYear");
2895 monthLowD = monthLow;
2896 dayLowD = dayLow;
2897 hourLowD = hourLow;
2898 minuteLowD = minuteLow;
2899 secondLowD = secondLow;
2900 if( 12 > monthHigh )
2901 monthHighD = 12 - monthHigh;
2902 gotoEndOfMonth = true;
2903 break;
2904 default:
2905 /* NOOP */
2906 break;
2907 }
2908 dtLow = dtLow.addSecs( -1 * (secondLowD + 60*minuteLowD + 3600*hourLowD) );
2909 dtLow = dtLow.addDays( -1 * dayLowD );
2910 dtAddMonths( dtLow, -1 * monthLowD, dtLow );
2911 dtAddYears( dtLow, -1 * yearLowD, dtLow );
2912 dtHigh = dtHigh.addSecs( secondHighD + 60*minuteHighD + 3600* hourHighD );
2913 dtHigh = dtHigh.addDays( dayHighD );
2914 dtAddMonths( dtHigh, monthHighD, dtHigh );
2915 dtAddYears( dtHigh, yearHighD, dtHigh );
2916 if( gotoEndOfMonth ){
2917 dtHigh.setDate( TQDate( dtHigh.date().year(),
2918 dtHigh.date().month(),
2919 dtHigh.date().daysInMonth() ) );
2920 dtHigh.setTime( TQTime( 23, 59, 59 ) );
2921 }
2922 if( toggleDts ) {
2923 TQDateTime dt( dtLow );
2924 dtLow = dtHigh;
2925 dtHigh = dt;
2926 }
2927 // secDist = dtLow.secsTo( dtHigh );
2928
2929 // NOTE: nSubDelimFactor is not set here since it
2930 // cannot be used for TQDateTime values.
2931 nSubDelimFactor = 0.0;
2932 bOrdFactorsOk = true;
2933 }
2934
2935
2936 if( !bOrdFactorsOk ) {
2937 // adjust one or both of our limit values
2938 // according to max-empty-inner-span settings
2939 nDist = nHigh - nLow;
2940 if( !isLogarithmic ){
2941 // replace nLow (or nHigh, resp.) by zero if NOT ALL OF
2942 // our values are located outside of the 'max. empty
2943 // inner space' (i.e. percentage of the y-axis range
2944 // that may to contain NO data entries)
2945 int maxEmpty = para.axisMaxEmptyInnerSpan();
2946 if( bAutoCalcStart ) {
2947 //tqDebug("\nbAutoCalcStart:\n nLow: %f\n nHigh: %f", nLow, nHigh );
2948 if( 0.0 < nLow ) {
2950 || maxEmpty > ( nLow / nHigh * 100.0 ) )
2951 nLow = 0.0;
2952 else if( nDist / 100.0 < nLow )
2953 nLow -= nDist / 100.0; // shift lowest value
2954 }
2955 else if( nDist / 100.0 < fabs( nLow ) )
2956 nLow -= nDist / 100.0; // shift lowest value
2957 nDist = nHigh - nLow;
2958 //tqDebug("* nLow: %f\n nHigh: %f", nLow, nHigh );
2959 }
2960 if( bAutoCalcEnd ) {
2961 //tqDebug("\nbAutoCalcEnd:\n nLow: %f\n nHigh: %f", nLow, nHigh );
2962 if( 0.0 > nHigh ) {
2964 || maxEmpty > ( nHigh / nLow * 100.0 ) )
2965 nHigh = 0.0;
2966 else if( nDist / 100.0 > nHigh )
2967 nHigh += nDist / 100.0; // shift highest value
2968 }
2969 else if( nDist / 100.0 < fabs( nHigh ) )
2970 nHigh += nDist / 100.0; // shift highest value
2971 nDist = nHigh - nLow;
2972 //tqDebug("* nLow: %f\n nHigh: %f\n\n", nLow, nHigh );
2973 }
2974 }
2975 }
2976
2977
2978 if( isLogarithmic ){
2979 if( bIsDouble ) {
2980 //tqDebug("\n[L--] nLow: %f, nHigh: %f, nDelta: %f", nLow, nHigh, nDelta );
2981 if( 0.0 == TQABS( nLow ) )
2982 nLow = -5;
2983 else{
2984 // find the Low / High values for the log. axis
2985 nLow = log10( TQABS( nLow ) );
2986 //if( 0.0 >= nLow ){
2987 //nLow = fastPow10( -nLow );
2988 //}
2989 }
2990 nHigh = log10( TQABS( nHigh ) );
2991
2992 //tqDebug("[L-0] nLow: %f, nHigh: %f", nLow, nHigh );
2993 double intPart;
2994 double fractPart = modf( nLow, &intPart );
2995 //tqDebug(" intPart: %f\nfractPart: %f", intPart, fractPart );
2996 if( 0.0 > nLow && 0.0 != fractPart )
2997 nLow = intPart - 1.0;
2998 else
2999 nLow = intPart;
3000 fractPart = modf( nHigh, &intPart );
3001 if( 0.0 != fractPart )
3002 nHigh = intPart + 1.0;
3003
3004 nDist = nHigh - nLow;
3005 nDelta = 1.0;
3006 nSubDelimFactor = 0.1;
3007 //tqDebug("\n[L-1] nLow: %f, nHigh: %f, nDelta: %f", nLow, nHigh, nDelta );
3008 bOrdFactorsOk = true;
3009 }
3010 }
3011
3012
3013 if ( !bOrdFactorsOk ) {
3014 // adjust one or both of our limit values
3015 // according to first two digits of (nHigh - nLow) delta
3016 double nDivisor;
3017 double nRound;
3018 nDist = nHigh - nLow;
3019 //tqDebug("* nLow: %f\n nHigh: %f nDist: %f\n\n", nLow, nHigh, nDist );
3020 // find out factors and adjust nLow and nHigh
3021 orgLow = nLow;
3022 orgHigh = nHigh;
3023 calculateOrdinateFactors( para, isLogarithmic,
3024 nDist, nDivisor, nRound,
3025 nDelta, nSubDelimFactor,
3026 nLow, nHigh );
3027 nLabels = params.roundVal( nDist / nDelta );
3028
3029 //tqDebug("\n0. nOrgHigh: %f\n nOrgLow: %f",
3030 // orgHigh, orgLow);
3031 //tqDebug("\n nDist: %f\n nHigh: %f\n nLow: %f",
3032 // nDist, nHigh, nLow);
3033 //tqDebug(" nDelta: %f", nDelta);
3034 //tqDebug(" nRound: %f", nRound);
3035 //tqDebug(" nLabels: %u", nLabels);
3036
3037 if( para.axisSteadyValueCalc() ) {
3038 ++nLabels;
3039 //tqDebug("* nLabels: %u", nLabels );
3040 }
3041 }
3042
3043
3044 // calculate the amount of nLabels to be written we could take
3045 // based on the space we have for writing the label texts
3047 == para.axisValueDelta() ) ){
3048 nDist = nHigh - nLow;
3049 nDelta = para.axisValueDelta();
3050 nLabels = params.roundVal( nDist / nDelta );
3051
3052 //tqDebug("\nI nLow: %f\n nHigh: %f\n nDelta: %f\n nLabels: %u",
3053 // nLow, nHigh, nDelta, nLabels );
3054
3055 if( para.axisSteadyValueCalc() ) {
3056 ++nLabels;
3057
3058 //tqDebug("* nLabels: %u", nLabels );
3059
3060 }
3061 }
3062
3063 // make sure labels fit into available height, if vertical axis
3064 if( bVertAxis ) {
3065 //Pending Michel
3066 //find out the width
3067 const KDChartAxisParams & xpara = params.axisParams( KDChartAxisParams::AxisPosBottom );
3068 double areaWidth = xpara.axisTrueAreaRect().width();
3069 //make sure to avoid inf
3070 double areaHeight = para.axisTrueAreaRect().height()>0?para.axisTrueAreaRect().height():1.0;
3071 double widthHeight = areaWidth / areaHeight;
3072 //tqDebug( "widthHeight %f, nDelta %f", widthHeight, nDelta);
3073 //tqDebug( "maxValue %f", data.maxValue());
3074 //tqDebug( "maxColSum %f", data.maxColSum());
3075 //tqDebug( "axisValueEnd() %f", para.axisValueEnd().toDouble());
3076 double nDivisor;
3077 double nRound;
3078 orgLow = nLow;
3079 orgHigh = nHigh;
3080
3081 //check if there are axis limitation - if not (auto calculation):
3082 //adjust the axis for 3dbars in order to display the whole top of the bar
3083 //in relation to the with and the height of the area.
3084 // add conditions for multirows here
3085 if ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd().toDouble()) {
3086 if (params.threeDBars() ) {
3087 if ( KDChartParams::BarPercent != params.barChartSubType()) {
3088 if ( widthHeight > 1.5 )
3089 orgHigh += nDelta * widthHeight;
3090 else
3091 orgHigh += widthHeight * 0.5;
3092 }
3093 }
3094 } else {
3095 orgHigh = nHigh = para.axisValueEnd().toDouble();
3096 }
3097 //tqDebug("\ncalc ordinate 0. nDist: %f\n nLow: %f\n nHigh: %f\n nDelta: %f\n nLabels: %u", nDist, nLow, nHigh, nDelta, nLabels );
3098 bool bTryNext = false;
3099 uint minLabels = para.axisSteadyValueCalc() ? 3 : 2;
3100 // the following must be processed at least twice - to avoid rounding errors
3101 int pass = 0;
3102 do{
3103 nDist = nHigh - nLow;
3104 nLow = orgLow;
3105 nHigh = orgHigh;
3106 /*
3107 tqDebug("\n=============================================================================\ncalc ordinate 1. nDist: %f\n nLow: %f\n nHigh: %f\n nDelta: %f\n nLabels: %u",
3108 nDist, nLow, nHigh, nDelta, nLabels );
3109 */
3110 calculateOrdinateFactors( para, isLogarithmic,
3111 nDist, nDivisor, nRound,
3112 nDelta,
3113 nSubDelimFactor, nLow, nHigh,
3114 bTryNext );
3115 nLabels = params.roundVal( nDist / nDelta );
3116
3117 //tqDebug("\ncalc ordinate 2. nDist: %f\n+ nLow: %f\n nHigh: %f\n nDelta: %f\n nLabels: %u",
3118 //nDist, nLow, nHigh, nDelta, nLabels );
3119 //TQString sDelta;sDelta.setNum( nDelta, 'f', 24 );
3120 //TQString sLow; sLow.setNum( nLow, 'f', 24 );
3121 //tqDebug("nLow: %f, sLow: %s, sDelta: %s", nLow, sLow.latin1(), sDelta.latin1());
3122
3123 // special case: End values was set by the user, but no Detla values was set.
3124 if( !bAutoCalcEnd && orgHigh > nLow + nLabels * nDelta ) {
3125 ++nLabels;
3126 //tqDebug("\nnLabels: %u\n", nLabels );
3127 }
3128 if( para.axisSteadyValueCalc() ) {
3129 ++nLabels;
3130 //tqDebug("\nnLabels: %u\n", nLabels );
3131 }
3132 //tqDebug("calc ordinate n. nDist = nHigh - nLow: %f = %f - %f",nDist, nHigh, nLow);
3133 //tqDebug(" nRound: %f\n", nRound);
3134 bTryNext = true;
3135 ++pass;
3136 }while ( ( pass < 2 )
3137 || ( ( minLabels < nLabels )
3138 && ( areaHeight < ( nTxtHeight * 1.5 ) * nLabels ) ) );
3139 }
3140 }
3141
3142 // finally we can build the texts
3143 if( bIsDouble ) {
3144 int trueBehindComma = -1;
3145 double nVal = nLow;
3146 for ( uint i = 0; i < nLabels; ++i ) {
3147 if( isLogarithmic ) {
3148 labelTexts.append( applyLabelsFormat(
3149 fastPow10( static_cast < int > ( nVal ) ),
3150 divPow10,
3151 behindComma,
3152 1.0 == nDelta ? KDCHART_AXIS_LABELS_AUTO_DELTA : nDelta,
3153 trueBehindComma,
3154 notation,
3155 decimalPoint,
3156 thousandsPoint,
3157 prefix,
3158 postfix,
3159 totalLen,
3160 padFill,
3161 blockAlign ) );
3162 } else {
3163 labelTexts.append( applyLabelsFormat( nVal,
3164 divPow10,
3165 behindComma,
3166 nDelta,
3167 trueBehindComma,
3168 notation,
3169 decimalPoint,
3170 thousandsPoint,
3171 prefix,
3172 postfix,
3173 totalLen,
3174 padFill,
3175 blockAlign ) );
3176 }
3177 nVal += nDelta;
3178 }
3179
3180 // save our true Low and High value
3181 //tqDebug(para.axisSteadyValueCalc()?"\ntrue " : "\nfalse");
3182 //tqDebug("nVal: %f, nDelta: %f", nVal, nDelta );
3183 if ( para.axisSteadyValueCalc() ) {
3184 nHigh = nVal - nDelta;
3185 }
3186 ( ( KDChartAxisParams& ) para ).setTrueAxisLowHighDelta( nLow, nHigh, nDelta );
3187 //tqDebug("[Z] nLow: %f, nHigh: %f, nDelta: %f", nLow, nHigh, nDelta );
3188
3189 } else {
3190 bool goDown = dtLow > dtHigh;
3191 int mult = goDown ? -1 : 1;
3192 TQDateTime dt( dtLow );
3193 nLabels = 0;
3194 /*
3195 tqDebug("dtLow: ");
3196 tqDebug(dtLow.toString( TQt::ISODate ));
3197 tqDebug("dtHigh: ");
3198 tqDebug(dtHigh.toString( TQt::ISODate ));
3199 */
3200 bool bDone=false;
3201 while( !bDone ) {
3202 /*
3203 tqDebug("dtLow: %i %i %i %i:%i:%i",
3204 dtLow.date().year(),
3205 dtLow.date().month(),
3206 dtLow.date().day(),
3207 dtLow.time().hour(),
3208 dtLow.time().minute(),
3209 dtLow.time().second());
3210 tqDebug("dtHigh: %i %i %i %i:%i:%i",
3211 dtHigh.date().year(),
3212 dtHigh.date().month(),
3213 dtHigh.date().day(),
3214 dtHigh.time().hour(),
3215 dtHigh.time().minute(),
3216 dtHigh.time().second());
3217 tqDebug("dt: %i %i %i %i:%i:%i",
3218 dt.date().year(),
3219 dt.date().month(),
3220 dt.date().day(),
3221 dt.time().hour(),
3222 dt.time().minute(),
3223 dt.time().second());
3224 */
3225 ++nLabels;
3226 if( autoDtLabels )
3227 labelTexts.append( "x" );
3228 else
3229 labelTexts.append( dt.toString( TQt::ISODate ) );
3230 bDone = (goDown ? (dt < dtLow ) : (dt > dtHigh));
3231 /*if( bDone ){
3232 dtHigh = dt;
3233 }else*/{
3234 switch( dtDeltaScale ) {
3235 case KDChartAxisParams::ValueScaleSecond:
3236 dtAddSecs( dt, 1 * mult, dt );
3237 break;
3238 case KDChartAxisParams::ValueScaleMinute:
3239 dtAddSecs( dt, 60 * mult, dt );
3240 break;
3241 case KDChartAxisParams::ValueScaleHour:
3242 dtAddSecs( dt, 3600 * mult, dt );
3243 break;
3244 case KDChartAxisParams::ValueScaleDay:
3245 dtAddDays( dt, 1 * mult, dt );
3246 break;
3247 case KDChartAxisParams::ValueScaleWeek:
3248 dtAddDays( dt, 7 * mult, dt );
3249 break;
3250 case KDChartAxisParams::ValueScaleMonth:
3251 dtAddMonths( dt,1 * mult, dt );
3252 break;
3253 case KDChartAxisParams::ValueScaleQuarter:
3254 dtAddMonths( dt,3 * mult, dt );
3255 break;
3256 case KDChartAxisParams::ValueScaleYear:
3257 dtAddYears( dt, 1 * mult, dt );
3258 break;
3259 default:
3260 dtAddDays( dt, 1 * mult, dt );
3261 break;
3262 }
3263 }
3264 }
3265 //if( autoDtLabels )
3266 // labelTexts.append( "x" );
3267 ( ( KDChartAxisParams& ) para ).setTrueAxisDtLowHighDeltaScale(
3268 dtLow, dtHigh,
3269 dtDeltaScale );
3270 // note: pDelimDelta will be calculated below,
3271 // look for "COMMOM CALC OF NLABELS, DELIM DELTA..."
3272 }
3273 bDone = true;
3274 }
3275
3276 // let's generate some strings
3277 if ( !bDone ) {
3278 // default scenario for abscissa axes
3279 uint count = bStatistical
3280 ? (data.usedRows() ? data.usedRows() : 1)
3281 : (data.usedCols() ? data.usedCols() : 1);
3282 //double start( 1.0 );
3283 double start( 1.0 + (bSteadyCalc ? 0.0 : static_cast < double >(data.colsScrolledBy())) );
3284//tqDebug("colsScrolledBy: %i", data.colsScrolledBy());
3285//if(bVertAxis)
3286//tqDebug("vert nVal starting: %f",start);
3287//else
3288//tqDebug("horz nVal starting: %f",start);
3289//if(bSteadyCalc)
3290//tqDebug("bSteadyCalc");
3291//else
3292//tqDebug("not bSteadyCalc");
3293 double delta( 1.0 );
3294 double finis( start + delta * ( count - 1 ) );
3295 const bool startIsDouble = TQVariant::Double == para.axisValueStart().type();
3296 const bool endIsDouble = TQVariant::Double == para.axisValueEnd().type();
3297
3298 bool deltaIsAuto = true;
3299 if ( !( KDCHART_AXIS_LABELS_AUTO_DELTA == para.axisValueDelta() ) ) {
3300 delta = para.axisValueDelta();
3301 deltaIsAuto = false;
3302 }
3304 if ( ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd() ) ) {
3305 finis = start + delta * ( count - 1 );
3306 } else {
3307 if( endIsDouble ){
3308 finis = para.axisValueEnd().toDouble();
3309 start = finis - delta * ( count - 1 );
3310//tqDebug("1 changing: start: %f",start);
3311 } else {
3312 //
3313 //
3314 // F E A T U R E P L A N N E D F O R F U T U R E . . .
3315 //
3316 //
3317 }
3318 }
3319 }else{
3320 if ( startIsDouble ) {
3321 start = para.axisValueStart().toDouble() + (bSteadyCalc ? 0.0 : static_cast < double >(data.colsScrolledBy()));
3322//tqDebug("2 changing: start: %f",start);
3323 } else {
3324 //
3325 //
3326 // F E A T U R E P L A N N E D F O R F U T U R E . . .
3327 //
3328 //
3329 }
3330 if ( !( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd() ) ) {
3331 if (endIsDouble ) {
3332 finis = para.axisValueEnd().toDouble();
3333 if ( deltaIsAuto ) {
3334 delta = ( finis - start ) / count;
3335 } else {
3336 count = static_cast < uint > (
3337 ( finis - start ) / delta );
3338 }
3339 } else {
3340 // auto-rows like
3341 // sunday, monday, tuesday, ...
3342 //
3343 //
3344 // F E A T U R E P L A N N E D F O R F U T U R E . . .
3345 //
3346 //
3347 }
3348 }
3349 else {
3350 finis = start + delta * ( count - 1 );
3351 }
3352 }
3353 TQString prefix( TQObject::tr( "Item " ) );
3354 TQString postfix;
3355
3356
3357 if ( startIsDouble && endIsDouble ) {
3358 int precis =
3360 ? 0
3361 : para.axisDigitsBehindComma();
3362 double s = start;
3363 double f = finis;
3364//tqDebug("label loop: s: %f f: %f",s,f);
3365 bool up = ( 0.0 < delta );
3366 // check the text widths of one large(?) entry
3367 // and hope all the entries will
3368 // fit into the available space
3369 double value = up ? s : f;
3370 uint nLabels = 0;
3371 while ( up ? ( value <= f ) : ( value >= s ) ) {
3372 ++nLabels;
3373 value += delta * up ? 1.0 : -1.0;
3374 }
3375 calculateBasicTextFactors( nTxtHeight, para,
3376 averageValueP1000,
3377 basicPos, orig, delimLen, nLabels,
3378 // start of return parameters
3379 pDelimDelta,
3380 pTextsX, pTextsY, pTextsW, pTextsH,
3381 textAlign );
3382 TQFont font( para.axisLabelsFont() );
3383 if ( para.axisLabelsFontUseRelSize() )
3384 font.setPixelSize( static_cast < int > ( nTxtHeight ) );
3385 painter->setFont( font );
3386 TQFontMetrics fm( painter->fontMetrics() );
3387
3388 if ( fm.width( prefix +
3389 TQString::number( -fabs( ( s + f ) / 2.0 + delta ),
3390 'f', precis ) )
3391 > pTextsW ) {
3392 prefix = "";
3393 postfix = "";
3394 }
3395 // now transfer the strings into labelTexts
3396 value = up ? s : f;
3397 while ( up ? ( value <= f ) : ( value >= s ) ) {
3398 labelTexts.append(
3399 prefix + TQString::number( value, 'f', precis )
3400 + postfix );
3401 value += delta * up ? 1.0 : -1.0;
3402 }
3403 } else {
3404 // pending(KHZ): make sure this branch will ever be reached
3405 // check the text widths largest entry
3406 // to make sure it will fit into the available space
3407 calculateBasicTextFactors( nTxtHeight, para,
3408 averageValueP1000,
3409 basicPos, orig, delimLen,
3410 count,
3411 // start of return parameters
3412 pDelimDelta,
3413 pTextsX, pTextsY, pTextsW, pTextsH,
3414 textAlign );
3415 TQFont font( para.axisLabelsFont() );
3416 if ( para.axisLabelsFontUseRelSize() )
3417 font.setPixelSize( static_cast < int > ( nTxtHeight ) );
3418 painter->setFont( font );
3419 TQFontMetrics fm( painter->fontMetrics() );
3420
3421 if ( fm.width( prefix + TQString::number( count - 1 ) )
3422 > pTextsW ) {
3423 prefix = "";
3424 postfix = "";
3425 }
3426 // now transfer the strings into labelTexts
3427 for ( uint i = 1; i <= count; ++i )
3428 labelTexts.append(
3429 prefix + TQString::number( i ) + postfix );
3430 }
3431 }
3432 }
3433
3434 /*
3435 finishing: COMMOM CALC OF NLABELS, DELIM DELTA...
3436 */
3437 uint nLabels = labelTexts.count()
3438 ? labelTexts.count()
3439 : 0;
3440 ( ( KDChartAxisParams& ) para ).setAxisLabelTexts( &labelTexts );
3441
3442 if( !adjustTheValues ){
3443
3444 calculateBasicTextFactors( nTxtHeight, para, averageValueP1000,
3445 basicPos, orig, delimLen, nLabels,
3446 // start of return parameters
3447 pDelimDelta,
3448 pTextsX, pTextsY, pTextsW, pTextsH,
3449 textAlign );
3450 }
3451
3452 ( ( KDChartAxisParams& ) para ).setTrueAxisDeltaPixels( pDelimDelta );
3453
3454 //tqDebug("\nsetting: para.trueAxisLow() %f para.trueAxisHigh() %f",para.trueAxisLow(),para.trueAxisHigh());
3455 //tqDebug("\npDelimDelta: %f", pDelimDelta );
3456
3457 /*
3458 tqDebug( "Found label texts:" );
3459 for ( TQStringList::Iterator it = labelTexts.begin();
3460 it != labelTexts.end(); ++it )
3461 tqDebug( ">>> %s", (*it).latin1() );
3462 tqDebug( "\n" );
3463 */
3464//tqDebug("\nleaving KDChartAxesPainter::calculateLabelTexts() : nTxtHeight: "+TQString::number(nTxtHeight));
3465}
3466
3467
3488/**** static ****/
3490 const KDChartAxisParams& para,
3491 double /*averageValueP1000*/,
3492 KDChartAxisParams::AxisPos basicPos,
3493 const TQPoint& orig,
3494 double delimLen,
3495 uint nLabels,
3496 // start of return params
3497 double& pDelimDelta,
3498 double& pTextsX,
3499 double& pTextsY,
3500 double& pTextsW,
3501 double& pTextsH,
3502 int& textAlign )
3503{
3504 switch ( basicPos ) {
3505 case KDChartAxisParams::AxisPosBottom: {
3506 bool bTouchEdges = para.axisLabelsTouchEdges();
3507 double wid = para.axisTrueAreaRect().width();
3508 double divi = bTouchEdges
3509 ? ( 1 < nLabels ? nLabels - 1 : 1 )
3510 : ( nLabels ? nLabels : 10 );
3511 pDelimDelta = wid / divi;
3512
3513 pTextsW = pDelimDelta - 4.0;
3514 pTextsX = orig.x() + 2.0
3515 - ( bTouchEdges
3516 ? pDelimDelta / 2.0
3517 : 0.0 );
3518 pTextsH = para.axisTrueAreaRect().height() - delimLen * 1.33;
3519 pTextsY = orig.y()
3520 + delimLen * 1.33;
3521 textAlign = TQt::AlignHCenter | TQt::AlignTop;
3522 /*
3523 tqDebug("pTextsW %f wid %f nLabels %u", pTextsW, wid, nLabels );
3524 */
3525 }
3526 break;
3527 case KDChartAxisParams::AxisPosLeft: {
3528 double hig = para.axisTrueAreaRect().height();
3529 pDelimDelta = hig / ( 1 < nLabels ? nLabels - 1 : 1 );
3530
3531 pTextsX = para.axisTrueAreaRect().bottomLeft().x()
3532 + 2.0;
3533 pTextsY = orig.y() - nTxtHeight / 2;
3534 pTextsW = para.axisTrueAreaRect().width()
3535 - delimLen * 1.33 - 2.0;
3536 pTextsH = nTxtHeight;
3537 textAlign = TQt::AlignRight | TQt::AlignVCenter;
3538 }
3539 break;
3540 case KDChartAxisParams::AxisPosTop: {
3541 bool bTouchEdges = para.axisLabelsTouchEdges();
3542 double wid = para.axisTrueAreaRect().width();
3543 double divi = bTouchEdges
3544 ? ( 1 < nLabels ? nLabels - 1 : 1 )
3545 : ( nLabels ? nLabels : 10 );
3546 pDelimDelta = wid / divi;
3547
3548 pTextsW = pDelimDelta - 4.0;
3549 pDelimDelta = wid / divi;
3550
3551 pTextsX = orig.x() + 2.0
3552 - ( bTouchEdges
3553 ? pDelimDelta / 2.0
3554 : 0.0 );
3555 pTextsH = para.axisTrueAreaRect().height() - delimLen * 1.33;
3556 pTextsY = para.axisTrueAreaRect().topLeft().y();
3557 textAlign = TQt::AlignHCenter | TQt::AlignBottom;
3558 }
3559 break;
3560 case KDChartAxisParams::AxisPosRight: {
3561 double hig = para.axisTrueAreaRect().height();
3562 pDelimDelta = hig / ( 1 < nLabels ? nLabels - 1 : 1 );
3563
3564 pTextsX = para.axisTrueAreaRect().bottomLeft().x()
3565 + delimLen * 1.33;
3566 pTextsY = orig.y() - nTxtHeight / 2;
3567 pTextsW = para.axisTrueAreaRect().width()
3568 - delimLen * 1.33 - 2.0;
3569 pTextsH = nTxtHeight;
3570 textAlign = TQt::AlignLeft | TQt::AlignVCenter;
3571 }
3572 break;
3573 default: {
3574 tqDebug( "IMPLEMENTATION ERROR: KDChartAxesPainter::calculateBasicTextFactors() unhandled enum value." );
3575 // NOOP since the 'basicPos' does not support more that these four values.
3576 }
3577 break;
3578 }
3579}
3580
3581
3597 const int behindComma,
3598 const double nDelta,
3599 int& trueBehindComma )
3600{
3601 const int nTrustedPrecision = 6; // when using 15 we got 1.850000 rounded to 1.849999999999999
3602
3603 const bool bUseAutoDigits = KDCHART_AXIS_LABELS_AUTO_DIGITS == behindComma;
3604 const bool bAutoDelta = KDCHART_AXIS_LABELS_AUTO_DELTA == nDelta;
3605 TQString sVal;
3606 sVal.setNum( nVal, 'f', bUseAutoDigits ? nTrustedPrecision
3607 : TQMIN(behindComma, nTrustedPrecision) );
3608 //tqDebug("nVal: %f sVal: "+sVal, nVal );
3609 //tqDebug( TQString(" %1").arg(sVal));
3610 if ( bUseAutoDigits ) {
3611 int comma = sVal.find( '.' );
3612 if ( -1 < comma ) {
3613 if ( bAutoDelta ) {
3614 int i = sVal.length();
3615 while ( 1 < i
3616 && '0' == sVal[ i - 1 ] )
3617 --i;
3618 sVal.truncate( i );
3619 if ( '.' == sVal[ i - 1 ] )
3620 sVal.truncate( i - 1 );
3621 } else {
3622 if ( 0 > trueBehindComma ) {
3623 TQString sDelta = TQString::number( nDelta, 'f', nTrustedPrecision );
3624 int i = sDelta.length();
3625 while ( 1 < i
3626 && '0' == sDelta[ i - 1 ] )
3627 --i;
3628 sDelta.truncate( i );
3629 if ( '.' == sDelta[ i - 1 ] )
3630 trueBehindComma = 0;
3631 else {
3632 int deltaComma = sDelta.find( '.' );
3633 if ( -1 < deltaComma )
3634 trueBehindComma = sDelta.length() - deltaComma - 1;
3635 else
3636 trueBehindComma = 0;
3637 }
3638 }
3639 // now we cut off the too-many digits behind the comma
3640 int nPos = comma + ( trueBehindComma ? trueBehindComma + 1 : 0 );
3641 sVal.truncate( nPos );
3642 }
3643 }
3644 }
3645 //tqDebug( TQString(" - %1").arg(trueBehindComma));
3646 return sVal;
3647}
3648
3649#if 0
3650
3651#if defined ( TQ_WS_WIN)
3652#define trunc(x) ((int)(x))
3653#endif
3654
3655#else
3656
3657// Ugly hack because Solaris (among others?) doesn't have trunc(),
3658// since the libs are not C99. I could do a complex #if expression,
3659// but instead I will just define trunc() here.
3660
3661double trunc(double x)
3662{
3663 return x >= 0.0 ? floor(x) : -floor(-x);
3664}
3665
3666#endif
3667
3668
3669TQString KDChartAxesPainter::applyLabelsFormat( const double nVal_,
3670 int divPow10,
3671 int behindComma,
3672 double nDelta_,
3673 int& trueBehindComma,
3675 const TQString& decimalPoint,
3676 const TQString& thousandsPoint,
3677 const TQString& prefix,
3678 const TQString& postfix,
3679 int totalLen,
3680 const TQChar& padFill,
3681 bool blockAlign )
3682{
3683 double nVal = nVal_ / fastPow10( divPow10 );
3684 double nDelta = nDelta_;
3685
3686 double valLog10 = 0.0;
3687 if( notation == KDChartEnums::NumberNotationScientific ||
3688 notation == KDChartEnums::NumberNotationScientificBig ){
3689 valLog10 = (nVal != 0.0) ? trunc( log10(TQABS(nVal)) ) : 0.0;
3690 //tqDebug("nVal old: %f valLog10: %f",nVal,valLog10);
3691 nVal /= fastPow10( valLog10 );
3692 nDelta /= fastPow10( valLog10 );
3693 //tqDebug("nVal new: %f",nVal);
3694 }
3695 TQString sVal = truncateBehindComma( nVal,
3696 behindComma,
3697 nDelta,
3698 trueBehindComma );
3699 //tqDebug("sVal : "+sVal+" behindComma: %i",behindComma);
3700
3701 int posComma = sVal.find( '.' );
3702 if( 0 <= posComma ){
3703 sVal.replace( posComma, 1, decimalPoint);
3704 }else{
3705 posComma = sVal.length();
3706 }
3707 if( notation == KDChartEnums::NumberNotationScientific ||
3708 notation == KDChartEnums::NumberNotationScientificBig ){
3709 if( notation == KDChartEnums::NumberNotationScientific )
3710 sVal.append( "e" );
3711 else
3712 sVal.append( "E" );
3713 sVal.append( TQString::number( valLog10, 'f', 0 ) );
3714 } else {
3715 if( thousandsPoint.length() ){
3716 const int minLen = (0 < sVal.length() && '-' == sVal[0])
3717 ? 4
3718 : 3;
3719 int n = posComma; // number of digits at the left side of the comma
3720 while( minLen < n ){
3721 n -= 3;
3722 sVal.insert(n, thousandsPoint);
3723 }
3724 }
3725 }
3726 sVal.append( postfix );
3727 int nFill = totalLen - (sVal.length() + prefix.length());
3728 if( 0 > nFill )
3729 nFill = 0;
3730 if( !blockAlign )
3731 sVal.prepend( prefix );
3732 for(int i=0; i < nFill; ++i)
3733 sVal.prepend( padFill );
3734 if( blockAlign )
3735 sVal.prepend( prefix );
3736 if ( totalLen > 0 )
3737 sVal.truncate( totalLen );
3738 /*Pending Michel: Force non fractional values
3739 *In case it is a fractional value
3740 *and the user has set axisLabelsDigitsBehindComma() == 0
3741 *return an empty string
3742 */
3743 if ( behindComma == 0 && TQString::number(nVal).find('.') > 0 )
3744 sVal = TQString();//sVal = "";
3745 return sVal;
3746}
3747
3754 const KDChartAxisParams& para,
3755 bool isLogarithmic,
3756 double& nDist,
3757 double& nDivisor,
3758 double& nRound,
3759 double& nDelta,
3760 double& nSubDelimFactor,
3761 double& nLow,
3762 double& nHigh,
3763 bool findNextRound )
3764{
3765 if ( findNextRound ) {
3766 if ( 1.0 > nRound )
3767 nRound = 1.0;
3768 else
3769 if ( 2.0 > nRound )
3770 nRound = 2.0;
3771 else
3772 if ( 2.5 > nRound )
3773 nRound = 2.5;
3774 else
3775 if ( 5.0 > nRound )
3776 nRound = 5.0;
3777 else {
3778 nDivisor *= 10.0;
3779 nRound = 1.0;
3780 }
3781 } else {
3782 nDivisor = 1.0;
3783 TQString sDistDigis2;
3784 sDistDigis2.setNum( nDist, 'f', 24);
3785 if ( 1.0 > nDist ) {
3786 sDistDigis2.remove( 0, 2 );
3787 nDivisor = 0.01;
3788 while ( 0 < sDistDigis2.length()
3789 && '0' == sDistDigis2[ 0 ] ) {
3790 nDivisor *= 0.1;
3791 sDistDigis2.remove( 0, 1 );
3792 }
3793 } else {
3794 if ( 10.0 > nDist ) {
3795 nDivisor = 0.1;
3796 // remove comma, if present
3797 sDistDigis2.remove( 1, 1 );
3798 } else
3799 if ( 100.0 > nDist )
3800 nDivisor = 1.0;
3801 else {
3802 int comma = sDistDigis2.find( '.' );
3803 if ( -1 < comma )
3804 sDistDigis2.truncate( comma );
3805 nDivisor = fastPow10( (int)sDistDigis2.length() - 2 );
3806 }
3807 }
3808 sDistDigis2.truncate( 2 );
3809 bool bOk;
3810 double nDistDigis2( sDistDigis2.toDouble( &bOk ) );
3811 if ( !bOk )
3812 nDistDigis2 = 10.0;
3813 if ( 75.0 <= nDistDigis2 )
3814 nRound = 5.0;
3815 else
3816 if ( 40.0 <= nDistDigis2 )
3817 nRound = 2.5;
3818 else
3819 if ( 20.0 <= nDistDigis2 )
3820 nRound = 2.0;
3821 else
3822 nRound = 1.0;
3823 }
3824
3825 nDelta = nRound * nDivisor;
3826
3827 // make sure its a whole number > 0 if its a log axis. Just round up.
3828 if( isLogarithmic )
3829 nDelta = static_cast < int > ( nDelta ) < nDelta
3830 ? static_cast < int > ( nDelta ) + 1
3831 : static_cast < int > ( nDelta );
3832
3833 bool bInvertedAxis = ( 0.0 > nDist );
3834 if( bInvertedAxis )
3835 nDelta *= -1.0;
3836
3837 /*
3838 tqDebug(" n D i s t : %f", nDist );
3839 tqDebug(" n D i v i s o r : %f", nDivisor);
3840 tqDebug(" n R o u n d : %f", nRound );
3841 tqDebug(" n D e l t a : %f", nDelta );
3842 tqDebug(" nHigh : %f", nHigh );
3843 tqDebug(" nLow : %f", nLow );
3844 */
3846 || !para.axisValueStartIsExact() ) {
3847 double orgLow( nLow );
3848 modf( nLow / nDelta, &nLow );
3849 nLow *= nDelta;
3850 if( bInvertedAxis ){
3851 if ( nLow < orgLow )
3852 nLow += nDelta;
3853 if ( 0.0 > nLow && 0.0 <= orgLow )
3854 nLow = 0.0;
3855 }else{
3856 if ( nLow > orgLow )
3857 nLow -= nDelta;
3858 if ( 0.0 < nLow && 0.0 >= orgLow )
3859 nLow = 0.0;
3860 }
3861 }
3863 double orgHigh( nHigh );
3864 modf( nHigh / nDelta, &nHigh );
3865 nHigh *= nDelta;
3866 if( bInvertedAxis ){
3867 if ( nHigh > orgHigh )
3868 nHigh -= nDelta;
3869 if ( 0.0 < nHigh && 0.0 >= orgHigh )
3870 nHigh = 0.0;
3871 }else{
3872 if ( nHigh < orgHigh )
3873 nHigh += nDelta;
3874 if ( 0.0 > nHigh && 0.0 <= orgHigh )
3875 nHigh = 0.0;
3876 }
3877 }
3878
3879 //tqDebug(" n H i g h : %f", nHigh );
3880 //tqDebug(" n L o w : %f\n\n", nLow );
3881
3882 if ( 1.0 == nRound )
3883 nSubDelimFactor = 0.5;
3884 else
3885 if ( 2.0 == nRound )
3886 nSubDelimFactor = 0.25;
3887 else
3888 if ( 2.5 == nRound )
3889 nSubDelimFactor = 0.2;
3890 else
3891 if ( 5.0 == nRound )
3892 nSubDelimFactor = 0.2;
3893 else {
3894 // Should not happen
3895 tqDebug( "IMPLEMENTATION ERROR: Unknown nRound in calculateOrdinateFactors()" );
3896 nSubDelimFactor = 1.0;
3897 }
3898
3899 nDist = nHigh - nLow;
3900}
3901
3902/**** static ****/
3903void KDChartAxesPainter::saveDrawLine( TQPainter& painter,
3904 TQPoint pA,
3905 TQPoint pZ,
3906 TQPen pen )
3907{
3908 const TQPen oldPen( painter.pen() );
3909 bool bNice = ( pen.color() == oldPen.color() )
3910 && ( pen.width() == oldPen.width() )
3911 && ( pen.style() == oldPen.style() );
3912 if ( !bNice )
3913 painter.setPen( pen );
3914 painter.drawLine( pA, pZ );
3915 if ( !bNice )
3916 painter.setPen( oldPen );
3917}
3918
3919/**** static ****/
3920void KDChartAxesPainter::dtAddSecs( const TQDateTime& org, const int secs, TQDateTime& dest )
3921{
3922 //tqDebug("entering KDChartAxesPainter::dtAddSecs() ..");
3923 int s = org.time().second();
3924 int m = org.time().minute();
3925 int h = org.time().hour();
3926 int days = 0;
3927 if( -1 < secs ){
3928 int mins = (s + secs) / 60;
3929 if( 0 == mins )
3930 s += secs;
3931 else{
3932 s = (s + secs) % 60;
3933 int hours = (m + mins) / 60;
3934 if( 0 == hours )
3935 m += mins;
3936 else{
3937 m = (m + mins) % 60;
3938 days = (h + hours) / 24;
3939 if( 0 == days )
3940 h += hours;
3941 else{
3942 h = (h + hours) % 24;
3943 }
3944 }
3945 }
3946 }
3947 dest.setTime( TQTime(h,m,s) );
3948 dest.setDate( org.date() );
3949 if( days )
3950 dtAddDays( dest, days, dest );
3951 //tqDebug(".. KDChartAxesPainter::dtAddSecs() done.");
3952}
3953
3954/**** static ****/
3955void KDChartAxesPainter::dtAddDays( const TQDateTime& org, const int days, TQDateTime& dest )
3956{
3957 //tqDebug("entering KDChartAxesPainter::dtAddDays() ..");
3958 int d = org.date().day();
3959 int m = org.date().month();
3960 int y = org.date().year();
3961 int dd = (-1 < days) ? 1 : -1;
3962 int di = 0;
3963 while( di != days ){
3964 d += dd;
3965 // underrunning day?
3966 if( 1 > d ){
3967 if( 1 < m ){
3968 --m;
3969 d = TQDate( y,m,1 ).daysInMonth();
3970 }
3971 else{
3972 --y;
3973 m = 12;
3974 d = 31;
3975 }
3976 // overrunning day?
3977 }else if( TQDate( y,m,1 ).daysInMonth() < d ){
3978 if( 12 > m )
3979 ++m;
3980 else{
3981 ++y;
3982 m = 1;
3983 }
3984 d = 1;
3985 }
3986 di += dd;
3987 }
3988 dest = TQDateTime( TQDate( y,m,d ), org.time() );
3989 //tqDebug(".. KDChartAxesPainter::dtAddDays() done.");
3990}
3991
3992/**** static ****/
3993void KDChartAxesPainter::dtAddMonths( const TQDateTime& org, const int months, TQDateTime& dest )
3994{
3995 //tqDebug("entering KDChartAxesPainter::dtAddMonths() ..");
3996 int d = org.date().day();
3997 int m = org.date().month();
3998 int y = org.date().year();
3999 int md = (-1 < months) ? 1 : -1;
4000 int mi = 0;
4001 while( mi != months ){
4002 m += md;
4003 if( 1 > m ){
4004 --y;
4005 m = 12;
4006 }else if( 12 < m ){
4007 ++y;
4008 m = 1;
4009 }
4010 mi += md;
4011 }
4012 // TQMIN takes care for intercalary day
4013 dest = TQDateTime( TQDate( y,m,TQMIN( d, TQDate( y,m,1 ).daysInMonth() ) ),
4014 org.time() );
4015 //tqDebug(".. KDChartAxesPainter::dtAddMonths() done.");
4016}
4017
4018/**** static ****/
4019void KDChartAxesPainter::dtAddYears( const TQDateTime& org, const int years, TQDateTime& dest )
4020{
4021 //tqDebug("entering KDChartAxesPainter::dtAddYears() ..");
4022 int d = org.date().day();
4023 int m = org.date().month();
4024 int y = org.date().year() + years;
4025 dest.setTime( org.time() );
4026 // TQMIN takes care for intercalary day
4027 dest = TQDateTime( TQDate( y,m,TQMIN( d, TQDate( y,m,d ).daysInMonth() ) ),
4028 org.time() );
4029 //tqDebug(".. KDChartAxesPainter::dtAddYears() done.");
4030}
4031
4032
4033
4034void KDChartAxesPainter::calculateAbscissaInfos( const KDChartParams& params,
4035 const KDChartTableDataBase& data,
4036 uint datasetStart,
4037 uint datasetEnd,
4038 double logWidth,
4039 const TQRect& dataRect,
4040 abscissaInfos& infos )
4041{
4042 if( params.axisParams( KDChartAxisParams::AxisPosBottom ).axisVisible()
4043 && ( KDChartAxisParams::AxisTypeUnknown
4044 != params.axisParams( KDChartAxisParams::AxisPosBottom ).axisType() ) )
4045 infos.abscissaPara = &params.axisParams( KDChartAxisParams::AxisPosBottom );
4046 else
4047 if( params.axisParams( KDChartAxisParams::AxisPosBottom2 ).axisVisible()
4048 && ( KDChartAxisParams::AxisTypeUnknown
4049 != params.axisParams( KDChartAxisParams::AxisPosBottom2 ).axisType() ) )
4050 infos.abscissaPara = &params.axisParams( KDChartAxisParams::AxisPosBottom2 );
4051 else
4052 if( params.axisParams( KDChartAxisParams::AxisPosTop ).axisVisible()
4053 && ( KDChartAxisParams::AxisTypeUnknown
4054 != params.axisParams( KDChartAxisParams::AxisPosTop ).axisType() ) )
4055 infos.abscissaPara = &params.axisParams( KDChartAxisParams::AxisPosTop );
4056 else
4057 if( params.axisParams( KDChartAxisParams::AxisPosTop2 ).axisVisible()
4058 && ( KDChartAxisParams::AxisTypeUnknown
4059 != params.axisParams( KDChartAxisParams::AxisPosTop2 ).axisType() ) )
4060 infos.abscissaPara = &params.axisParams( KDChartAxisParams::AxisPosTop2 );
4061 else
4062 // default is bottom axis:
4063 infos.abscissaPara = &params.axisParams( KDChartAxisParams::AxisPosBottom );
4064
4065 if( infos.abscissaPara->axisLabelsTouchEdges() )
4066 infos.bCenterThePoints = false;
4067
4068 infos.bAbscissaDecreasing = infos.abscissaPara->axisValuesDecreasing();
4069 infos.bAbscissaIsLogarithmic
4070 = KDChartAxisParams::AxisCalcLogarithmic == infos.abscissaPara->axisCalcMode();
4071
4072
4073 // Number of values: If -1, use all values, otherwise use the
4074 // specified number of values.
4075 infos.numValues = 0;
4076 if ( params.numValues() > -1 )
4077 infos.numValues = params.numValues();
4078 else
4079 infos.numValues = data.usedCols();
4080
4081 TQVariant::Type type2Ref = TQVariant::Invalid;
4082 infos.bCellsHaveSeveralCoordinates =
4083 data.cellsHaveSeveralCoordinates( datasetStart, datasetEnd,
4084 &type2Ref );
4085
4086 infos.numLabels = (infos.abscissaPara &&
4087 infos.abscissaPara->axisLabelTexts())
4088 ? infos.abscissaPara->axisLabelTexts()->count()
4089 : infos.numValues;
4090 if( 0 >= infos.numLabels )
4091 infos.numLabels = 1;
4092
4093 infos.bAbscissaHasTrueAxisValues =
4094 infos.abscissaPara && (0.0 != infos.abscissaPara->trueAxisDelta());
4095 infos.abscissaStart = infos.bAbscissaHasTrueAxisValues
4096 ? infos.abscissaPara->trueAxisLow()
4097 : 0.0;
4098 infos.abscissaEnd = infos.bAbscissaHasTrueAxisValues
4099 ? infos.abscissaPara->trueAxisHigh()
4100 : 1.0 * (infos.numLabels - 1);
4101 infos.abscissaSpan = fabs( infos.abscissaEnd - infos.abscissaStart );
4102 infos.abscissaDelta = infos.bAbscissaHasTrueAxisValues
4103 ? infos.abscissaPara->trueAxisDelta()
4104 : ( ( 0.0 != infos.abscissaSpan )
4105 ? ( infos.abscissaSpan / infos.numLabels )
4106 : infos.abscissaSpan );
4107
4108 //tqDebug( bAbscissaDecreasing ? "bAbscissaDecreasing = true" : "bAbscissaDecreasing = false");
4109 //tqDebug( abscissaHasTrueAxisValues ? "abscissaHasTrueAxisValues = true" : "abscissaHasTrueAxisValues = false");
4110 //tqDebug( "abscissaDelta = %f", abscissaDelta);
4111
4112 infos.bAbscissaHasTrueAxisDtValues =
4113 (TQVariant::DateTime == type2Ref) &&
4114 infos.abscissaPara &&
4115 infos.abscissaPara->trueAxisDtLow().isValid();
4116 if( infos.bAbscissaHasTrueAxisDtValues ){
4117 infos.numLabels = 200;
4118 infos.bCenterThePoints = false;
4119 }
4120
4121 infos.dtLowPos = infos.bAbscissaHasTrueAxisDtValues
4122 ? infos.abscissaPara->axisDtLowPosX() - dataRect.x()
4123 : 0.0;
4124 infos.dtHighPos = infos.bAbscissaHasTrueAxisDtValues
4125 ? infos.abscissaPara->axisDtHighPosX() - dataRect.x()
4126 : logWidth;
4127 infos.abscissaDtStart = infos.bAbscissaHasTrueAxisDtValues
4128 ? infos.abscissaPara->trueAxisDtLow()
4129 : TQDateTime();
4130 infos.abscissaDtEnd = infos.bAbscissaHasTrueAxisDtValues
4131 ? infos.abscissaPara->trueAxisDtHigh()
4132 : TQDateTime();
4133
4134 //Pending Michel case when same hh:mm:ss but different msecs
4135 infos.bScaleLessThanDay = ( infos.bAbscissaHasTrueAxisDtValues
4136 ? infos.abscissaPara->trueAxisDtDeltaScale()
4137 : KDChartAxisParams::ValueScaleDay )
4138 < KDChartAxisParams::ValueScaleDay;
4139 if ( infos.abscissaDtStart.time() == infos.abscissaDtEnd.time() && infos.bScaleLessThanDay )
4140 infos.dtHighPos = logWidth;
4141
4142 // adjust the milli seconds:
4143 infos.abscissaDtStart.setTime(
4144 TQTime( infos.abscissaDtStart.time().hour(),
4145 infos.abscissaDtStart.time().minute(),
4146 infos.abscissaDtStart.time().second(),
4147 0 ) );
4148 infos.abscissaDtEnd.setTime(
4149 TQTime( infos.abscissaDtEnd.time().hour(),
4150 infos.abscissaDtEnd.time().minute(),
4151 infos.abscissaDtEnd.time().second(),
4152 999 ) );
4153 //tqDebug( infos.abscissaPara->trueAxisDtLow().toString("yyyy-MM-dd-hh:mm:ss.zzz"));
4154 //tqDebug( infos.abscissaPara->trueAxisDtHigh().toString("yyyy-MM-dd-hh:mm:ss.zzz"));
4155 //tqDebug(infos.abscissaDtStart.toString("yyyy-MM-dd-hh:mm:ss.zzz"));
4156 //tqDebug(infos.abscissaDtEnd.toString("yyyy-MM-dd-hh:mm:ss.zzz"));
4157/*
4158 infos.bScaleLessThanDay = ( infos.bAbscissaHasTrueAxisDtValues
4159 ? infos.abscissaPara->trueAxisDtDeltaScale()
4160 : KDChartAxisParams::ValueScaleDay )
4161 < KDChartAxisParams::ValueScaleDay;
4162*/
4163 if( infos.bAbscissaHasTrueAxisDtValues ){
4164 if( infos.bScaleLessThanDay ){
4165 infos.abscissaDtSpan = infos.abscissaDtStart.secsTo( infos.abscissaDtEnd );
4166
4167 /* NOTE: We do *not* add the milli seconds because they aren't covered
4168 by the span indicated by infos.dtHighPos - infos.dtLowPos.
4169 if( infos.abscissaDtStart.time().msec() || infos.abscissaDtEnd.time().msec() )
4170 infos.abscissaDtSpan =
4171 ( infos.abscissaDtEnd.time().msec() -
4172 infos.abscissaDtStart.time().msec() ) / 1000.0;
4173 */
4174 }
4175 else{
4176 infos.abscissaDtSpan = infos.abscissaDtStart.daysTo( infos.abscissaDtEnd );
4177 if( infos.abscissaDtStart.time().msec() || infos.abscissaDtEnd.time().msec() )
4178 infos.abscissaDtSpan +=
4179 ( infos.abscissaDtEnd.time().msec() -
4180 infos.abscissaDtStart.time().msec() ) / (86400.0 * 1000.0);
4181 if( infos.abscissaDtEnd.time().second() )
4182 infos.abscissaDtSpan += infos.abscissaDtEnd.time().second() / 86400.0;
4183 if( infos.abscissaDtEnd.time().minute() )
4184 infos.abscissaDtSpan += infos.abscissaDtEnd.time().minute() / 1440.0;
4185 if( infos.abscissaDtEnd.time().hour() )
4186 infos.abscissaDtSpan += infos.abscissaDtEnd.time().hour() / 24.0;
4187 }
4188 }else
4189 infos.abscissaDtSpan = 10.0;
4190 if( 0 == infos.abscissaDtSpan || 0.0 == infos.abscissaDtSpan )
4191 infos.abscissaDtSpan = 1.0;
4192
4193 infos.abscissaDtPixelsPerScaleUnit = (infos.dtHighPos - infos.dtLowPos)/ infos.abscissaDtSpan;
4194
4195 if( infos.bAbscissaHasTrueAxisDtValues )
4196 infos.abscissaDelta = 20.0;
4197
4198 infos.pointDist
4199 = ( infos.abscissaPara && (0.0 != infos.abscissaPara->trueAxisDeltaPixels()) )
4200 ? infos.abscissaPara->trueAxisDeltaPixels()
4201 : ( logWidth /
4202 (
4203 (1 > ((double)(infos.numLabels) - (infos.bCenterThePoints ? 0.0 : 1.0)))
4204 ? ((double)(infos.numLabels) - (infos.bCenterThePoints ? 0.0 : 1.0))
4205 : 1 ) );
4206
4207 infos.abscissaPixelsPerUnit = ( 0.0 != infos.abscissaDelta )
4208 ? ( infos.pointDist / infos.abscissaDelta )
4209 : infos.pointDist;
4210
4211 //const double infos.abscissaZeroPos2 = -1.0 * infos.abscissaPixelsPerUnit * infos.abscissaStart;
4212 infos.abscissaZeroPos = infos.abscissaPara->axisZeroLineStartX() - dataRect.x();
4213 //tqDebug("abscissaZeroPos %f abscissaZeroPos2 %f",abscissaZeroPos,abscissaZeroPos2);
4214
4215 /*
4216 tqDebug(abscissaPara ?
4217 "\nabscissaPara: OK" :
4218 "\nabscissaPara: leer");
4219 tqDebug(abscissaHasTrueAxisValues ?
4220 "abscissaHasTrueAxisValues: true" :
4221 "abscissaHasTrueAxisValues: false");
4222 tqDebug("abscissaStart: %f", abscissaStart);
4223 tqDebug("abscissaEnd : %f", abscissaEnd);
4224 tqDebug("abscissaPara->trueAxisDelta(): %f", abscissaPara->trueAxisDelta());
4225 tqDebug("numValues : %u, numLabels : %u", numValues, numLabels);
4226 */
4227}
4228
4229
4230
4231bool KDChartAxesPainter::calculateAbscissaAxisValue( const TQVariant& value,
4232 abscissaInfos& ai,
4233 int colNumber,
4234 double& xValue )
4235{
4236 if( ai.bCellsHaveSeveralCoordinates ) {
4237 if( TQVariant::Double == value.type() ) {
4238 double dVal = value.toDouble();
4239 if( ai.bAbscissaIsLogarithmic ){
4240 if( 0.0 < dVal )
4241 xValue = ai.abscissaPixelsPerUnit * log10( dVal );
4242 else
4243 xValue = -10250.0;
4244 }else{
4245 xValue = ai.abscissaPixelsPerUnit * dVal;
4246 }
4247 xValue *= ai.bAbscissaDecreasing ? -1.0 : 1.0;
4248 xValue += ai.abscissaZeroPos;
4249 }
4250 else if( ai.bAbscissaHasTrueAxisDtValues &&
4251 TQVariant::DateTime == value.type() ) {
4252 const TQDateTime dtVal = value.toDateTime();
4253 double dT = ( ai.bScaleLessThanDay )
4254 ? ai.abscissaDtStart.secsTo( dtVal )
4255 : ai.abscissaDtStart.daysTo( dtVal );
4256 /*
4257 tqDebug("abscissaDtStart: %i %i %i %i:%i:%i.%i",
4258 ai.abscissaDtStart.date().year(),
4259 ai.abscissaDtStart.date().month(),
4260 ai.abscissaDtStart.date().day(),
4261 ai.abscissaDtStart.time().hour(),
4262 ai.abscissaDtStart.time().minute(),
4263 ai.abscissaDtStart.time().second(),
4264 ai.abscissaDtStart.time().msec());
4265 */
4266 //tqDebug("days to = %f",dT);
4267
4268 /*
4269 tqDebug(" dtVal: %i %i %i %i:%i:%i.%i",
4270 dtVal.date().year(),
4271 dtVal.date().month(),
4272 dtVal.date().day(),
4273 dtVal.time().hour(),
4274 dtVal.time().minute(),
4275 dtVal.time().second(),
4276 dtVal.time().msec());
4277 */
4278 xValue = ai.abscissaDtPixelsPerScaleUnit * dT;
4279 if( dtVal.time().msec() )
4280 xValue += (ai.abscissaDtPixelsPerScaleUnit * dtVal.time().msec())
4281 / ( ai.bScaleLessThanDay
4282 ? 1000.0
4283 : (1000.0 * 86400.0) );
4284 //tqDebug("xValue: %f",xValue);
4285 if( !ai.bScaleLessThanDay ){
4286 if( dtVal.time().second() )
4287 xValue += (ai.abscissaDtPixelsPerScaleUnit * dtVal.time().second())
4288 / 86400.0;
4289 if( dtVal.time().minute() )
4290 xValue += (ai.abscissaDtPixelsPerScaleUnit * dtVal.time().minute())
4291 / 1440.0;
4292 if( dtVal.time().hour() )
4293 xValue += (ai.abscissaDtPixelsPerScaleUnit * dtVal.time().hour())
4294 / 24.0;
4295 }
4296 xValue *= ai.bAbscissaDecreasing ? -1.0 : 1.0;
4297 xValue += ai.dtLowPos;
4298 // tqDebug("xValue = dtLowPos + abscissaDtPixelsPerScaleUnit * dT\n%f = %f + %f * %f",
4299 // xValue, dtLowPos, abscissaDtPixelsPerScaleUnit, dT);
4300 }
4301 else
4302 return false;
4303 } else
4304 xValue = ai.pointDist * ( double ) colNumber;
4305 return true;
4306}
4307
4308
4309
4310/*************************************************************************/
4311/*************************************************************************/
4312/*************************************************************************/
4313/*** ***/
4314/*** Framework for data drawing using cartesian axes (Bar, Line, ...) ***/
4315/*** ***/
4316/*************************************************************************/
4317/*************************************************************************/
4318/*************************************************************************/
4319
4330void KDChartAxesPainter::paintData( TQPainter* painter,
4332 bool paint2nd,
4333 KDChartDataRegionList* regions )
4334{
4335 bool bNormalMode = isNormalMode();
4336
4337 uint chart = paint2nd ? 1 : 0;
4338
4339 // find out the ordinate axis (or axes, resp.) belonging to this chart
4340 // (up to 4 ordinates might be in use: 2 left ones and 2 right ones)
4341 uint axesCount;
4342 KDChartParams::AxesArray ordinateAxes;
4343 ordinateAxes.resize( KDCHART_CNT_ORDINATES );
4344 if( !params()->chartAxes( chart, axesCount, ordinateAxes ) ) {
4345 // no axis - no fun!
4346 return;
4347 // We cannot draw data without an axis having calculated high/low
4348 // values and position of the zero line before.
4349
4350 // PENDING(khz) Allow drawing without having a visible axis!
4351 }
4352
4353 //const KDChartParams::ChartType params_chartType
4354 // = paint2nd ? params()->additionalChartType() : params()->chartType();
4355
4356 double logWidth = _dataRect.width();
4357 double areaWidthP1000 = logWidth / 1000.0;
4358
4359 int nClipShiftUp = clipShiftUp(bNormalMode, areaWidthP1000);
4360 TQRect ourClipRect( _dataRect );
4361 if ( 0 < ourClipRect.top() ) {
4362 ourClipRect.setTop( ourClipRect.top() - nClipShiftUp );
4363 ourClipRect.setHeight( ourClipRect.height() + nClipShiftUp - 1 );
4364 } else
4365 ourClipRect.setHeight( ourClipRect.height() + nClipShiftUp / 2 - 1 );
4366
4367 // protect axes ?
4368 //ourClipRect.setBottom( ourClipRect.bottom() - 1 );
4369 //ourClipRect.setLeft( ourClipRect.left() + 1 );
4370 //ourClipRect.setRight( ourClipRect.right() - 1 );
4371
4372 const TQWMatrix & world = painter->worldMatrix();
4373 ourClipRect = world.mapRect( ourClipRect );
4374 painter->setClipRect( ourClipRect );
4375 painter->translate( _dataRect.x(), _dataRect.y() );
4376
4377 painter->setPen( params()->outlineDataColor() );
4378
4379 // find out which datasets are to be represented by this chart
4380 uint chartDatasetStart, chartDatasetEnd;
4381 findChartDatasets( data, paint2nd, chart, chartDatasetStart, chartDatasetEnd );
4382
4383 // Note: 'aI' is *not* the axis number!
4384 for( uint aI = 0; aI < axesCount; ++aI ) {
4385 // 'axis' is the REAL axis number!
4386 uint axis = ordinateAxes.at( aI );
4387
4388 const KDChartAxisParams* axisPara = &params()->axisParams( axis );
4389
4390 uint datasetStart, datasetEnd;
4391 uint axisDatasetStart, axisDatasetEnd;
4392 uint dummy;
4393 if( params()->axisDatasets( axis,
4394 axisDatasetStart,
4395 axisDatasetEnd, dummy )
4396 && ( KDCHART_ALL_DATASETS != axisDatasetStart ) ) {
4397
4398 if( KDCHART_NO_DATASET == axisDatasetStart ){
4399 //==========
4400 continue; // NO DATASETS --> STOP PROCESSING THIS AXIS
4401 //==========
4402 }
4403
4404 if( axisDatasetStart >= chartDatasetStart
4405 && axisDatasetStart <= chartDatasetEnd )
4406 datasetStart = TQMAX( axisDatasetStart, chartDatasetStart );
4407 else if( axisDatasetStart <= chartDatasetStart
4408 && axisDatasetEnd >= chartDatasetStart )
4409 datasetStart = chartDatasetStart;
4410 else
4411 datasetStart = 20;
4412 if( axisDatasetEnd >= chartDatasetStart
4413 && axisDatasetEnd <= chartDatasetEnd )
4414 datasetEnd = TQMIN( axisDatasetEnd, chartDatasetEnd );
4415 else if( axisDatasetEnd >= chartDatasetEnd
4416 && axisDatasetStart <= chartDatasetEnd )
4417 datasetEnd = chartDatasetEnd;
4418 else
4419 datasetEnd = 0;
4420 } else {
4421 datasetStart = chartDatasetStart;
4422 datasetEnd = chartDatasetEnd;
4423 }
4424
4425 //tqDebug("\n=========================================================="
4426 // "\naxis %u axisDatasetStart %u axisDatasetEnd %u / chartDatasetStart %u chartDatasetEnd %u",
4427 //axis, axisDatasetStart, axisDatasetEnd, chartDatasetStart, chartDatasetEnd );
4428
4429 double logHeight = axisPara->axisTrueAreaRect().height();
4430 double axisYOffset = axisPara->axisTrueAreaRect().y() - _dataRect.y();
4431
4432 //tqDebug("\n==========================================================\naxis %u logHeight %f axisDatasetStart %u chartDatasetStart %u axisDatasetEnd %u chartDatasetEnd %u",
4433 //axis, logHeight, axisDatasetStart, chartDatasetStart, axisDatasetEnd, chartDatasetEnd );
4434 //if( KDCHART_ALL_DATASETS == axisDatasetStart )
4435 // tqDebug(" ALL DATASETS");
4436 //if( KDCHART_NO_DATASET == axisDatasetStart )
4437 // tqDebug(" N O DATESETS");
4438
4439 double maxColumnValue = axisPara->trueAxisHigh();
4440 double minColumnValue = axisPara->trueAxisLow();
4441 double columnValueDistance = maxColumnValue - minColumnValue;
4442
4443
4444 // call the chart type specific data painter:
4445 specificPaintData( painter,
4446 ourClipRect,
4447 data,
4448 regions,
4449 axisPara,
4450 bNormalMode,
4451 chart,
4452 logWidth,
4453 areaWidthP1000,
4454 logHeight,
4455 axisYOffset,
4456 minColumnValue,
4457 maxColumnValue,
4458 columnValueDistance,
4459 chartDatasetStart,
4460 chartDatasetEnd,
4461 datasetStart,
4462 datasetEnd );
4463 }
4464 painter->translate( - _dataRect.x(), - _dataRect.y() );
4465}
Provide access to the chart axis parameters.
#define KDCHART_AXIS_LABELS_AUTO_LIMIT
Use this to specify that the axis label limits are to be calculated automatically.
#define KDCHART_AXIS_LABELS_AUTO_DELTA
Use this to specify that the step-width from one label to the other shall be calculated automatically...
#define KDCHART_AXIS_GRID_AUTO_LINEWIDTH
Use this to specify that the width of the axis grid lines shall be equal to the width of the axis lin...
#define KDCHART_AXIS_LABELS_AUTO_DATETIME_FORMAT
Use this to specify that date/time format to be used for displaying the axis labels is to be determin...
#define KDCHART_AXIS_IGNORE_EMPTY_INNER_SPAN
Use this to specify that the zero-line shall NOT be omitted in case all of the data are grouped far a...
#define KDCHART_AXIS_LABELS_AUTO_LEAVEOUT
Use this to specify that an automatically computed amount of axis labels are to be skipped if there i...
#define KDCHART_AXIS_LABELS_AUTO_DIGITS
Use this to specify that the number of digits to be shown on the axis labels behind the comma is to b...
Header for all common chart parameters.
#define KDCHART_CNT_ORDINATES
Our charts may have up to 4 ordinate axes: 2 left ones and 2 right ones.
#define KDCHART_MAX_AXES
Charts have up to 12 axes: up to two axes on both sides for X, Y and Z (if data is 3-dimensional).
static TQString truncateBehindComma(const double nVal, const int behindComma, const double nDelta, int &trueBehindComma)
Takes double nVal and returns a TQString showing the amount of digits behind the comma that was speci...
virtual ~KDChartAxesPainter()
Destructor.
static void calculateBasicTextFactors(double nTxtHeight, const KDChartAxisParams &para, double averageValueP1000, KDChartAxisParams::AxisPos basicPos, const TQPoint &orig, double delimLen, uint nLabels, double &pDelimDelta, double &pTextsX, double &pTextsY, double &pTextsW, double &pTextsH, int &textAlign)
Calculates some label text factors needed by function calculateLabelTexts()
static void calculateLabelTexts(TQPainter *painter, const KDChartTableDataBase &data, const KDChartParams &params, uint axisNumber, double averageValueP1000, double delimLen, KDChartAxisParams::AxisPos &basicPos, TQPoint &orig, TQPoint &dest, double &pXDeltaFactor, double &pYDeltaFactor, double &pXDelimDeltaFaktor, double &pYDelimDeltaFaktor, double &nSubDelimFactor, double &pDelimDelta, double &nTxtHeight, double &pTextsX, double &pTextsY, double &pTextsW, double &pTextsH, int &textAlign, bool &isLogarithmic, bool &isDateTime, bool &autoDtLabels, TQDateTime &dtLow, TQDateTime &dtHigh, KDChartAxisParams::ValueScale &dtDeltaScale, bool adjustTheValues=false, double trueDelta=0.0, double trueDeltaPix=0.0)
Calculates the actual label texts for one axis.
virtual void paintData(TQPainter *painter, KDChartTableDataBase *data, bool paint2nd, KDChartDataRegionList *regions)
Paints the actual data area and registers the region for the data points if regions is not 0.
virtual void paintAxes(TQPainter *painter, KDChartTableDataBase *data)
Paints the actual axes areas.
KDChartAxesPainter(KDChartParams *params)
Constructor.
static void calculateOrdinateFactors(const KDChartAxisParams &para, bool bLogarithmic, double &nDist, double &nDivisor, double &nRound, double &nDelta, double &nSubDelimFactor, double &nLow, double &nHigh, bool findNextRound=false)
Calculates the factors to be used for calculating ordinate labels texts.
access the chart axis parameters.
TQColor axisLabelsColor() const
Returns the axis labels colour.
TQStringList axisShortLabelsStringList() const
Returns a TQStringList containing the label texts to be used in case the axis area size is too small ...
TQColor axisGridColor() const
Returns the axis grid color.
TQt::PenStyle axisGridStyle() const
Returns the axis grid line pattern.
double trueAxisHigh() const
Returns the true end value of the ordinate axis labels as is was calculated and set by setTrueAxisLow...
int axisTrueLineWidth() const
Returns the axis true line width, as calculated and drawn.
bool axisValuesDecreasing() const
Returns whether axis values should be printed in reverted order: starting with the highest label and ...
double trueAxisLow() const
Returns the true start value of the ordinate axis labels as is was calculated and set by setTrueAxisL...
TQRect axisTrueAreaRect() const
Returns the true axis area rectangle as calculated and drawn.
int axisLabelsRotation() const
Returns by how many degrees the axis labels will be rotated.
double trueAxisDeltaPixels() const
Returns the true delimiter delta pixel value of the axis labels as is was calculated and set by setTr...
static AxisPos basicAxisPos(uint pos)
enum KDChartAxisParams::AxisPos { AxisPosSTART = 0,
TQt::PenStyle axisGridSubStyle() const
Returns the axis grid line pattern for the thin lines showing the sub-delimiter values.
uint isometricReferenceAxis() const
Returns which axis this axis shall be isometric with, this will be either the axis position - see KDC...
int axisGridLineWidth() const
Returns the width of the axis grid lines.
TQFont axisLabelsFont() const
Returns the axis labels font.
bool axisShowSubDelimiters() const
Returns whether the axis sub-delimiters will be drawn.
TQVariant axisValueStart() const
Returns the lower limit for the axis labels: the start value.
int axisMaxEmptyInnerSpan() const
Returns the percentage of the y-axis range that may to contain NO data entries, if - and only if - ax...
double axisValueDelta() const
Returns the DELTA value for the axis labels: the distance between two labels.
int axisLabelsFontRelSize() const
Returns the axis labels relative font size.
TQString axisLabelsDateTimeFormat() const
Returns the format to be used for displaying abscissa axis TQDateTime item labels.
bool axisLabelsFontUseRelSize() const
Returns whether the fix axis font size is used.
AxisType axisType() const
int labelTextsDataRow() const
Returns the number of the data row that contains (or might contain, resp.) the texts to be taken for ...
double axisZeroLineStartX() const
Returns the not-rounded x-position where drawing of this axis zero line started.
uint axisLabelStringCount() const
Returns the number of strings stored as label texts, the texts can be retrieved by calling axisLabelS...
bool axisShowGrid() const
Returns whether a grid should be drawn at the chart data area.
int axisGridSubLineWidth() const
Returns the axis grid sub line width.
int axisDigitsBehindComma() const
Returns the number of digits to be printed behind the comma on the axis labels.
bool axisSteadyValueCalc() const
Returns whether label values shall be calculataed based upon the associated dataset values (normally ...
int axisLineWidth() const
Returns the axis line width.
bool axisLabelsDontShrinkFont() const
Specifies whether the axis labels' font size may be shrinked to avoid overwriting neighboring areas.
bool axisVisible() const
Returns weither the axis will be drawn or not.
double axisZeroLineStartY() const
Returns the not-rounded y-position where drawing of this axis zero line started.
TQStringList axisLabelStringList() const
Returns a TQStringList containing the label texts to be used.
AxisCalcMode axisCalcMode() const
Returns the axis calculation mode configured in this params object.
TQColor axisLineColor() const
Returns the axis line colour.
bool axisLabelsDontAutoRotate() const
Returns whether the axis labels may not be rotated to avoid overwriting neighboring areas.
TQColor axisGridSubColor() const
Returns the axis grid sub color.
double trueAxisDelta() const
Returns the true delta value of the ordinate axis labels as is was calculated and set by setTrueAxisL...
int axisValueLeaveOut() const
Returns how many axis labels are to be skipped if there is not enough space for displaying all of the...
TQVariant axisValueEnd() const
Returns the upper limit for the axis labels: the end value.
bool axisLabelsVisible() const
Returns whether the axis labels should be drawn.
TQColor axisZeroLineColor() const
Returns the colour used for the zero-value line that is drawn if zero is not at the lower edge of the...
bool axisLineVisible() const
Returns whether the axis line should be drawn.
bool axisLabelsTouchEdges() const
Returns whether the axis labels start and end at the edges of the charts instead being positioned in ...
The collection class used by KD Chart to store data region information.
NumberNotation
Number notation specifies the general way, how a number is to be shown.
An abstract base class that defines an interface for classes that implement chart drawing.
virtual void paint(TQPainter *painter, KDChartTableDataBase *data, bool paintFirst, bool paintLast, KDChartDataRegionList *regions=0, const TQRect *rect=0, bool mustCalculateGeometry=true)
Paints the chart for which this chart painter is configured on a TQPainter.
Bundles the display parameters of a chart.
ChartType chartType() const
Returns the chart type configured in this params object.
static int roundVal(double d)
Returns the parameter d rounded to the nearest integer.
AreaChartSubType areaChartSubType() const
Returns the area chart subtype.
int numValues() const
Returns how many of the values should be shown.
LineChartSubType lineChartSubType() const
Returns the line chart subtype.
ChartType
The chart type.
ChartType additionalChartType() const
Returns the additional chart type configured in this params object.
bool optimizeOutputForScreen() const
Returns whether drawing is optimized for (low resolution) screen output (this is the default) or whet...
PolarChartSubType polarChartSubType() const
Returns the polar chart subtype.
const KDChartAxisParams & axisParams(uint n) const
Returns the parameters that are currently defined for axis n.
bool threeDBars() const
Returns whether the engine should draw any bars in 3D.
bool axisDatasets(uint n, uint &dataset, uint &dataset2, uint &chart) const
Returns which data are to be represented by a given axis.
BarChartSubType barChartSubType() const
Returns the bar chart subtype.
bool findDatasets(SourceMode mode1, SourceMode mode2, uint &dataset, uint &dataset2, uint chart=0) const
Retrieves the dataset number or a range of datasets being used with one of two given SourceModes for ...
KD Chart's build-in table data for an easy way of storing data values.
virtual uint usedCols() const
Returns the number of cols in the table that actually contain data.
virtual bool cellCoord(uint _row, uint _col, TQVariant &_value, int coordinate=1) const =0
Returns one of the coordinate data value(s) stored in a cell.
virtual uint colsScrolledBy() const
Returns the number of cols the table has been scrolled by.
virtual uint usedRows() const
Returns the number of rows in the table that actually contain data.