You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kmymoney/libkdchart/KDChartAxesPainter.cpp

4526 lines
211 KiB

/*
KDChart - a multi-platform charting engine
*/
/****************************************************************************
** Copyright (C) 2001-2003 Klar<EFBFBD>vdalens Datakonsult AB. All rights reserved.
**
** This file is part of the KDChart library.
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.
**
** Licensees holding valid commercial KDChart licenses may use this file in
** accordance with the KDChart Commercial License Agreement provided with
** the Software.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** See http://www.klaralvdalens-datakonsult.se/?page=products for
** information about KDChart Commercial License Agreements.
**
** Contact info@klaralvdalens-datakonsult.se if any conditions of this
** licensing are not clear to you.
**
**********************************************************************/
#include <tqpainter.h>
#include <tqlabel.h>
#include <KDDrawText.h>
#include "KDChartAxesPainter.h"
#include "KDChartAxisParams.h"
#include "KDChartParams.h"
#include <stdlib.h>
/**
Little helper function returning the number of seconds
between UTC start date 1970/01/01 00:00 and a given date \c dt.
The return value is negative for \c dt < 1970/01/01.
*/
int secondsSinceUTCStart( const TQDateTime& dt )
{
TQDateTime dtStart( TQDate( 1970, 1, 1 ) );
return dtStart.secsTo( dt );
}
/**
\class KDChartAxesPainter KDChartAxesPainter.h
\brief A common base class for classes that implement chart
painters for chart types ith axes.
*/
/**
Constructor. Sets up internal data structures as necessary.
\param params the KDChartParams structure that defines the chart
*/
KDChartAxesPainter::KDChartAxesPainter( KDChartParams* params ) :
KDChartPainter( params )
{
// Intentionally left blank.
// We cannot setup the geometry yet
// since we do not know the size of the painter.
}
/**
Destructor.
*/
KDChartAxesPainter::~KDChartAxesPainter()
{
// intentionally left blank
}
#if COMPAT_TQT_VERSION < 0x030000
TQDateTime dateTimeFromString( const TQString& s ) // only ISODate is allowed
{
int year( s.mid( 0, 4 ).toInt() );
int month( s.mid( 5, 2 ).toInt() );
int day( s.mid( 8, 2 ).toInt() );
TQString t( s.mid( 11 ) );
int hour( t.mid( 0, 2 ).toInt() );
int minute( t.mid( 3, 2 ).toInt() );
int second( t.mid( 6, 2 ).toInt() );
int msec( t.mid( 9, 3 ).toInt() );
if ( year && month && day )
return TQDateTime( TQDate( year, month, day ),
TQTime( hour, minute, second, msec ) );
else
return TQDateTime();
}
TQString dateTimeToString( const TQDateTime& dt ) // ISODate is returned
{
TQString date;
TQString month(
TQString::number( dt.date().month() ).rightJustify( 2, '0' ) );
TQString day(
TQString::number( dt.date().day() ).rightJustify( 2, '0' ) );
date = TQString::number( dt.date().year() ) + "-" + month + "-" + day;
TQString time;
time.sprintf( "%.2d:%.2d:%.2d",
dt.time().hour(), dt.time().minute(), dt.time().second() );
return date + "T" + time;
}
#endif
/**
ReCalculate the labels based upon given nDelta and nDeltaPix.
This is necessary to build isometric axes.
*/
void reCalculateLabelTexts(
TQPainter* painter,
const KDChartTableDataBase& data,
const KDChartParams& params,
uint axisNumber,
double averageValueP1000,
double delimLen,
internal__KDChart__CalcValues& cv )
{
KDChartAxesPainter::calculateLabelTexts(
painter,
data,
params,
axisNumber,
averageValueP1000,
delimLen,
// start of reference parameters
cv.basicPos,
cv.orig,
cv.dest,
cv.pXDeltaFactor,
cv.pYDeltaFactor,
cv.pXDelimDeltaFaktor,
cv.pYDelimDeltaFaktor,
cv.nSubDelimFactor,
cv.pDelimDelta,
cv.nTxtHeight,
cv.pTextsX,
cv.pTextsY,
cv.pTextsW,
cv.pTextsH,
cv.textAlign,
cv.bLogarithmic,
cv.isDateTime,
cv.autoDtLabels,
cv.dtLow,
cv.dtHigh,
cv.dtDeltaScale,
true,
cv.nDelta,
cv.nDeltaPix );
const KDChartAxisParams & para = params.axisParams( axisNumber );
cv.bSteadyCalc = para.axisSteadyValueCalc();
cv.bDecreasing = para.axisValuesDecreasing();
cv.nLow = para.trueAxisLow();
cv.nHigh = para.trueAxisHigh();
}
bool KDChartAxesPainter::calculateAllAxesLabelTextsAndCalcValues(
TQPainter* painter,
KDChartTableDataBase* data,
double areaWidthP1000,
double areaHeightP1000,
double& delimLen)
{
uint iAxis;
double averageValueP1000 = TQMIN(areaWidthP1000, areaHeightP1000);//( areaWidthP1000 + areaHeightP1000 ) / 2.0;
//tqDebug("KChart::KDChartAxesPainter::calculateAllAxesLabelTextsAndCalcValues() averageValueP1000: %f", averageValueP1000);
// length of little delimiter-marks indicating axis scaling
delimLen = 20.0 * averageValueP1000; // per mille of area
// Determine axes calculation values and labels before drawing the axes.
// step #1: calculate all values independendly from the other axes' values
for( iAxis = 0; iAxis < KDCHART_MAX_AXES; ++iAxis )
{
internal__KDChart__CalcValues& cv = calcVal[iAxis];
cv.processThisAxis = ( params()->axisParams( iAxis ).axisVisible()
&& KDChartAxisParams::AxisTypeUnknown
!= params()->axisParams( iAxis ).axisType() );
if( cv.processThisAxis ){
cv.nSubDelimFactor = 0.0;
cv.pDelimDelta = 0.0;
cv.nTxtHeight = 0.0;
cv.pTextsX = 0.0;
cv.pTextsY = 0.0;
cv.pTextsW = 0.0;
cv.pTextsH = 0.0;
cv.textAlign = TQt::AlignHCenter | TQt::AlignVCenter;
cv.isDateTime = false;
cv.autoDtLabels = false;
calculateLabelTexts( painter,
*data,
*params(),
iAxis,
averageValueP1000,
delimLen,
// start of reference parameters
cv.basicPos,
cv.orig,
cv.dest,
cv.pXDeltaFactor,
cv.pYDeltaFactor,
cv.pXDelimDeltaFaktor,
cv.pYDelimDeltaFaktor,
cv.nSubDelimFactor,
cv.pDelimDelta,
cv.nTxtHeight,
cv.pTextsX,
cv.pTextsY,
cv.pTextsW,
cv.pTextsH,
cv.textAlign,
cv.bLogarithmic,
cv.isDateTime,
cv.autoDtLabels,
cv.dtLow,
cv.dtHigh,
cv.dtDeltaScale );
const KDChartAxisParams & para = params()->axisParams( iAxis );
cv.bSteadyCalc = para.axisSteadyValueCalc();
cv.bDecreasing = para.axisValuesDecreasing();
cv.nLow = para.trueAxisLow();
cv.nHigh = para.trueAxisHigh();
cv.nDelta = para.trueAxisDelta();
cv.nDeltaPix = para.trueAxisDeltaPixels();
cv.pLastX = cv.dest.x();
cv.pLastY = cv.dest.y();
}
}
// step #2: if isometric axes are desired adjust/re-calculate some values
for ( iAxis = 0; iAxis < KDCHART_MAX_AXES; ++iAxis ){
internal__KDChart__CalcValues& cv = calcVal[iAxis];
if( cv.processThisAxis
&& cv.bSteadyCalc ){
const KDChartAxisParams & para = params()->axisParams( iAxis );
const uint isoRef = para.isometricReferenceAxis();
if( KDCHART_NO_AXIS != isoRef
&& iAxis != isoRef
&& ( KDCHART_MAX_AXES > isoRef
|| KDCHART_ALL_AXES == isoRef ) ){
if( KDCHART_ALL_AXES == isoRef ){
uint iAxis2;
// first find the axis values to be taken as reference
double nDelta = cv.nDelta;
double nDeltaPix = cv.nDeltaPix;
double nSubDelimFactor = cv.nSubDelimFactor;
for ( iAxis2 = 0;
iAxis2 < KDCHART_MAX_AXES;
++iAxis2 ){
internal__KDChart__CalcValues& cv2 = calcVal[iAxis2];
if( cv2.processThisAxis
&& cv2.bSteadyCalc
&& (0.0 != cv2.nDelta)
&& (fabs(cv2.nDeltaPix / cv2.nDelta) < fabs(nDeltaPix / nDelta)) ){
if( (nDelta >= 0.0) == (cv2.nDelta >= 0.0) )
nDelta = cv2.nDelta;
else
nDelta = cv2.nDelta * -1.0;
if( (nDeltaPix >= 0.0) == (cv2.nDeltaPix >= 0.0) )
nDeltaPix = cv2.nDeltaPix;
else
nDeltaPix = cv2.nDeltaPix * -1.0;
if( (nSubDelimFactor >= 0.0) == (cv2.nSubDelimFactor >= 0.0) )
nSubDelimFactor = cv2.nSubDelimFactor;
else
nSubDelimFactor = cv2.nSubDelimFactor * -1.0;
}
}
// now adjust all axes (if necessary)
for ( iAxis2 = 0;
iAxis2 < KDCHART_MAX_AXES;
++iAxis2 ){
internal__KDChart__CalcValues& cv2 = calcVal[iAxis2];
if( cv2.processThisAxis
&& cv2.bSteadyCalc
&& ( fabs(cv2.nDelta) != fabs(nDelta)
|| fabs(cv2.nDeltaPix) != fabs(nDeltaPix) ) ){
//tqDebug("\nrecalculating scale for axis %x", iAxis2);
//tqDebug("cv2.nDelta %f cv2.nDeltaPix %f nDelta %f nDeltaPix %f\n",
// cv2.nDelta,cv2.nDeltaPix,nDelta,nDeltaPix);
if( (cv2.nDelta >= 0.0) == (nDelta >= 0.0) )
cv2.nDelta = nDelta;
else
cv2.nDelta = nDelta * -1.0;
if( (cv2.nDeltaPix >= 0.0) == (nDeltaPix >= 0.0) )
cv2.nDeltaPix = nDeltaPix;
else
cv2.nDeltaPix = nDeltaPix * -1.0;
reCalculateLabelTexts( painter,
*data,
*params(),
iAxis2,
averageValueP1000,
delimLen,
cv2 );
if( (cv2.nSubDelimFactor >= 0.0) == (nSubDelimFactor >= 0.0) )
cv2.nSubDelimFactor = nSubDelimFactor;
else
cv2.nSubDelimFactor = nSubDelimFactor * -1.0;
}
}
}else{
internal__KDChart__CalcValues& cv2 = calcVal[isoRef];
// adjust this axis or the other axis (if necessary)
if( cv2.processThisAxis
&& cv2.bSteadyCalc
&& ( cv2.nDelta != cv.nDelta
|| cv2.nDeltaPix != cv.nDeltaPix ) ){
if( cv2.nDelta > cv.nDelta
|| ( cv2.nDelta == cv.nDelta
&& cv2.nDeltaPix < cv.nDeltaPix ) ){
// adjust this axis
//tqDebug("recalculating scale for this axis %x", iAxis);
cv.nDelta = cv2.nDelta;
cv.nDeltaPix = cv2.nDeltaPix;
reCalculateLabelTexts(
painter,
*data,
*params(),
iAxis,
averageValueP1000,
delimLen,
cv );
cv.nSubDelimFactor = cv2.nSubDelimFactor;
}else{
// adjust the other axis
//tqDebug("\nrecalculating scale for other axis %x", isoRef);
//tqDebug("cv2.nDelta %f cv2.nDeltaPix %f cv.nDelta %f cv.nDeltaPix %f",
// cv2.nDelta,cv2.nDeltaPix,cv.nDelta,cv.nDeltaPix);
cv2.nDelta = cv.nDelta;
cv2.nDeltaPix = cv.nDeltaPix;
reCalculateLabelTexts(
painter,
*data,
*params(),
isoRef,
averageValueP1000,
delimLen,
cv2 );
cv2.nSubDelimFactor = cv.nSubDelimFactor;
}
}
}
}
}
}
return true;
}
/**
Paints the actual axes areas.
\param painter the TQPainter onto which the chart should be painted
\param data the data that will be displayed as a chart
*/
void KDChartAxesPainter::paintAxes( TQPainter* painter,
KDChartTableDataBase* data )
{
if ( !painter || !data || 0 == params() )
return ;
const bool bMultiRowBarChart = KDChartParams::Bar == params()->chartType() &&
KDChartParams::BarMultiRows == params()->barChartSubType();
double areaWidthP1000 = _logicalWidth / 1000.0;
double areaHeightP1000 = _logicalHeight / 1000.0;
double averageValueP1000 = TQMIN(areaWidthP1000, areaHeightP1000);//( areaWidthP1000 + areaHeightP1000 ) / 2.0;
// length of little delimiter-marks indicating axis scaling
double delimLen;
//tqDebug("-------------------------------------------------------------------------------------");
calculateAllAxesLabelTextsAndCalcValues( painter, data, areaWidthP1000, areaHeightP1000, delimLen );
// Now the labels are known, so let us paint the axes...
painter->save();
painter->setPen( TQt::NoPen );
bool screenOutput = params()->optimizeOutputForScreen();
uint iAxis;
for ( iAxis = 0; iAxis < KDCHART_MAX_AXES; ++iAxis ){
internal__KDChart__CalcValues& cv = calcVal[iAxis];
if( cv.processThisAxis ){
const KDChartAxisParams & para = params()->axisParams( iAxis );
internal__KDChart__CalcValues& cv = calcVal[iAxis];
const TQColor labelsColor( para.axisLabelsColor() );
// Debugging axis areas:
//painter->fillRect(para.axisTrueAreaRect(), TQt::yellow);
uint lineWidth = 0 <= para.axisLineWidth()
? para.axisLineWidth()
: -1 * static_cast < int > ( para.axisLineWidth()
* averageValueP1000 );
( ( KDChartAxisParams& ) para ).setAxisTrueLineWidth( lineWidth );
uint gridLineWidth
= ( KDCHART_AXIS_GRID_AUTO_LINEWIDTH
== para.axisGridLineWidth() )
? lineWidth
: ( ( 0 <= para.axisGridLineWidth() )
? para.axisGridLineWidth()
: -1 * static_cast < int > ( para.axisGridLineWidth()
* averageValueP1000 ) );
uint gridSubLineWidth
= ( KDCHART_AXIS_GRID_AUTO_LINEWIDTH
== para.axisGridSubLineWidth() )
? lineWidth
: ( ( 0 <= para.axisGridSubLineWidth() )
? para.axisGridSubLineWidth()
: -1 * static_cast < int > ( para.axisGridSubLineWidth()
* averageValueP1000 ) );
// Magic to find out axis scaling factors and labels text height
// =============================================================
// - khz, 02/24/2001
//
// 1st Calculate the axis label texts height regarding to
// user-defined per-axis settings.
//
// 2nd This height is given to calculateLabelTexts() to
// calculate the delimiter and sub-delimiter distances as
// well as the axis scaling factors.
// If neccessary and possible the short replacement strings
// are taken that might have been specified by the user.
// - see KDChartAxisParams::setAxisLabelStringLists() -
//
// 3rd Before displaying the texts we make sure they fit into
// their space, if needed we will do the following
// in order to avoid clipping of text parts:
//
// (a) ABSCISSA axes only: rotate the texts in 5 steps
// until they are drawn vertically
//
// (b) further reduce the texts' font height down to 6pt
// .
//
// If the texts *still* don't fit into their space, we are lost
// and they will be clipped. Such is live.
//
// Why all this?
//
// Because I do not believe in axis areas growing and shrinking
// regarding to long or short label texts: start such behaviour
// and become mad.
//
// Better plan: ask the user to specify a way how to abbreviate
// label texts (e.g. by writing "200" instead
// of that wide and unreadable "200,000.00")
//
//
// F E A T U R E P L A N N E D F O R F U T U R E . . .
//
//
// Note: The labels-touch-edges flag may have been set to true
// inside the calculateLabelTexts() function.
bool bTouchEdges = para.axisLabelsTouchEdges();
// NOTE: The steady-value-calc flag may have been set to true
// inside the calculateLabelTexts() function
// by a special setAxisLabelTextParams() call,
// therefor we do not store its value before calling that function.
if( cv.bLogarithmic )
cv.nSubDelimFactor = 0.1;
const double nUsableAxisHeight = cv.pTextsH;
const double nUsableAxisWidth = cv.pTextsW;
const bool isHorizontalAxis
= (KDChartAxisParams::AxisPosBottom == cv.basicPos) ||
(KDChartAxisParams::AxisPosTop == cv.basicPos);
TQStringList* labelTexts = ( TQStringList* ) para.axisLabelTexts();
uint nLabels = ( 0 != labelTexts )
? labelTexts->count()
: 0;
// start point of 1st delimiter on the axis-line == grid-start
TQPoint p1( cv.orig );
// end point of 1st delimiter near the label text
TQPoint p2( cv.orig );
// end point of small sub-delimiter
TQPoint p2a( cv.orig );
// start point of 1st grid-line (beginnig at the axis)
TQPoint pGA( cv.orig );
// end point of 1st grid-line at the other side of the chart
TQPoint pGZ( cv.orig );
// start point of zero-line, this is often identical with p1
// but will be different in case of shifted zero-line
double axisZeroLineStartX = p1.x();
double axisZeroLineStartY = p1.y();
p2.setX( p2.x() + static_cast < int > ( cv.pXDelimDeltaFaktor * delimLen ) );
p2.setY( p2.y() + static_cast < int > ( cv.pYDelimDeltaFaktor * delimLen ) );
p2a.setX( p2a.x() + static_cast < int > ( cv.pXDelimDeltaFaktor * delimLen * 2.0 / 3.0 ) );
p2a.setY( p2a.y() + static_cast < int > ( cv.pYDelimDeltaFaktor * delimLen * 2.0 / 3.0 ) );
pGZ.setX( pGZ.x() - static_cast < int > ( cv.pXDelimDeltaFaktor * (_dataRect.width() - 1) ) );
pGZ.setY( pGZ.y() - static_cast < int > ( cv.pYDelimDeltaFaktor * (_dataRect.height() - 1) ) );
if ( nLabels ) {
// Sometimes the first or last labels partially reach out of
// their axis area: we allow this
const bool oldClippingFlag = painter->hasClipping();
painter->setClipping( false );
if( para.hasAxisFirstLabelText() )
labelTexts->first() = para.axisFirstLabelText();
if( para.hasAxisLastLabelText() )
labelTexts->last() = para.axisLastLabelText();
const double pXDelta = cv.pXDeltaFactor * cv.pDelimDelta;
const double pYDelta = cv.pYDeltaFactor * cv.pDelimDelta;
// draw label texts and delimiters and grid
painter->setPen( TQPen( para.axisLineColor(),
lineWidth ) );
const TQString formatDT = cv.isDateTime
? para.axisLabelsDateTimeFormat()
: TQString();
// calculate font size
const double minTextHeight = para.axisLabelsFontMinSize();
//tqDebug("KChart::KDChartAxesPainter::paintAxes() cv.nTxtHeight: %f minTextHeight: %f", cv.nTxtHeight, minTextHeight);
if ( minTextHeight > cv.nTxtHeight )
cv.nTxtHeight = minTextHeight;
TQFont actFont( para.axisLabelsFont() );
if ( para.axisLabelsFontUseRelSize() ) {
actFont.setPixelSize( static_cast < int > ( cv.nTxtHeight ) );
}
painter->setFont( actFont );
TQFontMetrics fm( painter->fontMetrics() );
int nLeaveOut = 0;
int nRotation = 0;
// Draw simple string labels
// or calculate and draw nice Date/Time ruler?
TQString commonDtHeader;
if( cv.autoDtLabels ){
cv.textAlign = TQt::AlignCenter;
//tqDebug(dtLow.toString("\nd.MM.yyyy - h:mm:ss" ));
//tqDebug(dtHigh.toString( "d.MM.yyyy - h:mm:ss" ));
const TQDate& dLow = cv.dtLow.date();
const TQTime& tLow = cv.dtLow.time();
const TQDate& dHigh = cv.dtHigh.date();
const TQTime& tHigh = cv.dtHigh.time();
bool sameYear = dLow.year() == dHigh.year();
bool sameMonth = sameYear && (dLow.month() == dHigh.month() );
bool sameDay = sameMonth && (dLow.day() == dHigh.day() );
bool sameHour = sameDay && (tLow.hour() == tHigh.hour() );
bool sameMinute = sameHour && (tLow.minute() == tHigh.minute());
bool sameSecond = sameMinute && (tLow.second() == tHigh.second());
if( sameDay ){
commonDtHeader = TQString::number( dLow.day() )
+ ". "
#if COMPAT_TQT_VERSION >= 0x030000
+ TQDate::longMonthName( dLow.month() )
#else
+ dLow.monthName( dLow.month() )
#endif
+ ' '
+ TQString::number( dLow.year() );
if( sameHour ){
commonDtHeader += " / "
+ TQString::number( tLow.hour() )
+ ':';
if( sameMinute ){
if( 10 > tLow.minute() )
commonDtHeader += '0';
commonDtHeader += TQString::number( tLow.minute() )
+ ':';
if( sameSecond ){
if( 10 > tLow.second() )
commonDtHeader += '0';
commonDtHeader += TQString::number( tLow.second() );
//
// " Huston, we have a problem! "
//
// Currently we don't support milli secs
// since they will not fit into a double
// when looking at years...
//
// This will be improved in release 2.0.
// (khz, 2002/07/12)
}
else
commonDtHeader += "00";
}
else
commonDtHeader += "00";
}
}else if( sameMonth )
#if COMPAT_TQT_VERSION >= 0x030000
commonDtHeader = TQDate::longMonthName( dLow.month() )
#else
commonDtHeader = dLow.monthName( dLow.month() )
#endif
+ ' '
+ TQString::number( dLow.year() );
else if( sameYear )
commonDtHeader = TQString::number( dLow.year() );
//if( !commonDtHeader.isEmpty() )
// tqDebug(commonDtHeader);
}else{
// make sure all label texts fit into their space
// by rotating and/or shrinking the texts
// or by leaving out some of the labels
TQRegion unitedRegions;
const bool tryLeavingOut =
( para.axisValueLeaveOut() == KDCHART_AXIS_LABELS_AUTO_LEAVEOUT )
|| ( 0 < para.axisValueLeaveOut() );
if( tryLeavingOut ) {
if( para.axisValueLeaveOut()
== KDCHART_AXIS_LABELS_AUTO_LEAVEOUT )
nLeaveOut = 0;
else
nLeaveOut = para.axisValueLeaveOut();
}
else
nLeaveOut = 0;
int stepWidthLeaveOut = nLeaveOut+1;
int iStepsLeaveOut = 0;
const bool tryShrinking = !para.axisLabelsDontShrinkFont();
const double nInitialTxtHeight = cv.nTxtHeight;
const bool tryRotating = isHorizontalAxis
&& !para.axisLabelsDontAutoRotate();
const int nInitialRotation = ( (360 > para.axisLabelsRotation())
&& (270 <= para.axisLabelsRotation()) )
? para.axisLabelsRotation()
: 0;
nRotation = nInitialRotation;
bool textsDontFitIntoArea;
bool textsOverlapping;
bool textsMatching;
do {
textsDontFitIntoArea = false;
textsOverlapping = false;
textsMatching = true;
// test if all texts match without mutually overlapping
unitedRegions = TQRegion();
int align = nRotation
? (TQt::AlignRight | TQt::AlignVCenter) // adjusting for rotation
: cv.textAlign;
TQPoint anchor(200,200);
int iLeaveOut = 0;
double iLabel=0.0;
for ( TQStringList::Iterator it = labelTexts->begin();
it != labelTexts->end();
++it ) {
iLabel += 1.0;
if( iLeaveOut < nLeaveOut ) {
++iLeaveOut;
} else {
iLeaveOut = 0;
anchor.setX( p2.x() + static_cast < int > ( pXDelta * (iLabel - 0.5) ) );
anchor.setY( p2.y() + static_cast < int > ( pYDelta * (iLabel - 0.5) ) );
// allow for shearing and/or scaling of the painter
anchor = painter->worldMatrix().map( anchor );
TQString text;
if( cv.isDateTime ){
#if COMPAT_TQT_VERSION >= 0x030000
TQDateTime dt( TQDateTime::fromString( *it,
Qt::ISODate ) );
text = dt.toString( formatDT );
#else
TQDateTime dt( dateTimeFromString( *it ) );
text = dt.toString();
#endif
}else{
text = *it;
}
KDDrawTextRegionAndTrueRect infosKDD =
KDDrawText::measureRotatedText( painter,
nRotation,
anchor,
text,
0,
align,
&fm,
false,
false,
15 );
if( infosKDD.region.boundingRect().left()
< params()->globalLeadingLeft()+1 ){
textsMatching = false;
textsDontFitIntoArea = true;
//tqDebug("too wide");
}
//tqDebug("nRotation: %i",nRotation);
TQRegion sectReg;
if( nRotation ){
//tqDebug("test 1");
sectReg = infosKDD.region.intersect( unitedRegions );
}else{
//tqDebug("test 2");
TQRect rect( infosKDD.region.boundingRect() );
rect.addCoords(-2,-2,2,2);
TQRegion biggerRegion( rect );
sectReg = biggerRegion.intersect( unitedRegions );
}
if ( sectReg.isEmpty() )
unitedRegions = unitedRegions.unite( infosKDD.region );
else {
textsMatching = false;
textsOverlapping = true;
//tqDebug("label regions are intersecting");
break;
}
}
}
/*
if(!iAxis){
tqDebug("nTxtHeight: "+TQString::number(cv.nTxtHeight)+" nRotation: "+TQString::number(nRotation)+
" matching: "+TQString(textsMatching ? "TRUE":"FALSE"));
tqDebug("nUsableAxisHeight: %f, unitedRegions.boundingRect().height(): %i ",
nUsableAxisHeight, unitedRegions.boundingRect().height());
}
*/
if( isHorizontalAxis ) {
if( nUsableAxisHeight < unitedRegions.boundingRect().height() ){
//textsMatching = false;
textsDontFitIntoArea = true;
}
} else {
if( nUsableAxisWidth < unitedRegions.boundingRect().width() ){
//tqDebug("textsMatching: %s",textsMatching ? "TRUE" : "FALSE");
textsMatching = false;
textsDontFitIntoArea = true;
//tqDebug("too wide");
}
//else tqDebug("not too wide");
}
/*
if(textsMatching && !iAxis){
tqDebug("--------------------------");
tqDebug("nTxtHeight: "+TQString::number(cv.nTxtHeight)+" nRotation: "+TQString::number(nRotation));
tqDebug("matching");
}
*/
if( !textsMatching ) {
bool rotatingDoesNotHelp = false;
// step 1: In case of labels being too wide
// to fit into the available space
// we try to rotate the texts in 5 steps.
// This is done for Abscissa axes only.
if ( tryRotating ) {
//tqDebug("try rotating");
// The following is designed for horizontal axes
// since we currently don't support label rotating
// on vertical axes. (khz, 2002/08/15)
if( textsDontFitIntoArea ){
if( nRotation != nInitialRotation ){
//textsDontFitIntoArea = false;
nRotation = nInitialRotation;
}
rotatingDoesNotHelp = true;
//tqDebug("rotating does not help (a)");
}
else{
if( nRotation ) {
if( 270 < nRotation ) {
nRotation -= 5;
if( 270 > nRotation )
nRotation = 270; // drawing vertically now
} else {
if( nInitialRotation )
nRotation = nInitialRotation;
else
nRotation = 0; // reset rotation to ZERO
rotatingDoesNotHelp = true;
//tqDebug("rotating does not help (b)");
}
} else {
if( nInitialRotation )
nRotation = nInitialRotation;
else
nRotation = 350; // (re-)start rotating with -10
}
}
}
if ( !tryRotating || rotatingDoesNotHelp ) {
// step 2: In case of labels being too wide and
// rotating them did not help or is forbidden
// we try to reduce the font size.
if ( tryShrinking && (minTextHeight < cv.nTxtHeight) ) {
//tqDebug("try shrinking");
cv.nTxtHeight -= 1.0;
if ( minTextHeight > cv.nTxtHeight )
cv.nTxtHeight = minTextHeight;
} else {
// step 3: In case reducing the font size is not possible
// any further (or is not allowed at all) we try
// to leave out some of the labels.
if( tryLeavingOut
&& textsOverlapping
&& (nLeaveOut+1 < static_cast < int > ( nLabels ) ) ) {
//tqDebug("try leaving out");
++iStepsLeaveOut;
//if(!iAxis)tqDebug("iStepsLeaveOut: %i", iStepsLeaveOut);
nLeaveOut =
iStepsLeaveOut*stepWidthLeaveOut - 1;
if( tryShrinking )
cv.nTxtHeight = nInitialTxtHeight;
}
else
break;
}
if( tryShrinking ) {
actFont.setPixelSize( static_cast < int > ( cv.nTxtHeight ) );
//tqDebug("axis: cv.nTxtHeight: %f", iAxis, cv.nTxtHeight);
painter->setFont( actFont );
fm = painter->fontMetrics();
}
}
}
//tqDebug("nLeaveOut: %i",nLeaveOut);
} while( !textsMatching );
if( nRotation ){
// The following is designed for horizontal axes
// since we currently don't support label rotating
// on vertical axes. (khz, 2002/08/15)
//int oldVert = textAlign & (TQt::AlignTop | TQt::AlignBottom);
//int steepness = abs(270-nRotation);
//bool steep = (30 > steepness);
cv.textAlign = TQt::AlignRight | TQt::AlignVCenter; // adjusting for rotation
//cv.textAlign = TQt::AlignRight | TQt::AlignVCenter;
/* ( steep ? TQt::AlignVCenter : oldVert);*/
//int dx = pXDelta / 2 - steep ? (nTxtHeight / 4) : 0;
double dx = (pXDelta / 2) - (cv.nTxtHeight / 4);
double dy = /*steep ? 0 : */(cv.nTxtHeight / 2.0);
cv.pTextsX += dx;
cv.pTextsY += dy;
}
/*
TQBrush oldBrush = painter->brush();
TQRegion oldReg = painter->clipRegion();//TQPainter::CoordPainter);
painter->setBrush(TQt::Dense4Pattern);
painter->setClipRegion(unitedRegions);//,TQPainter::CoordPainter);
painter->drawRect(0,0,2000,1500);
painter->setClipRegion(oldReg);//,TQPainter::CoordPainter);
painter->setBrush(oldBrush);
*/
/*if(!iAxis){
tqDebug("==========================");
tqDebug("nTxtHeight: "+TQString::number(nTxtHeight)+" nRotation: "+TQString::number(nRotation));
tqDebug(textsMatching ? "matching":"not matching");
}*/
}
painter->setFont( actFont );
fm = TQFontMetrics( painter->fontMetrics() );
// set colour of grid pen
TQPen gridPen, leaveOutGridPen;
if( para.axisShowGrid() && !bMultiRowBarChart )
gridPen.setColor( para.axisGridColor() );
const int pXDeltaDiv2 = static_cast < int > ( pXDelta / 2.0 );
const int pYDeltaDiv2 = static_cast < int > ( pYDelta / 2.0 );
bool bDrawAdditionalSubGridLine = false;
double pGXMicroAdjust = 0.0;
double pGYMicroAdjust = 0.0;
if ( !bTouchEdges ) {
// adjust the data values pos
p1.setX( p1.x() + pXDeltaDiv2 );
p1.setY( p1.y() + pYDeltaDiv2 );
p2.setX( p2.x() + pXDeltaDiv2 );
p2.setY( p2.y() + pYDeltaDiv2 );
// adjust the short delimiter lines pos
p2a.setX( p2a.x() + pXDeltaDiv2 );
p2a.setY( p2a.y() + pYDeltaDiv2 );
// adjust grid lines pos
bDrawAdditionalSubGridLine =
isHorizontalAxis && !
params()->axisParams(
KDChartAxisParams::AxisPosRight ).axisVisible() &&
!bMultiRowBarChart;
pGA.setX( pGA.x() + pXDeltaDiv2 );
pGA.setY( pGA.y() + pYDeltaDiv2 );
pGZ.setX( pGZ.x() + pXDeltaDiv2 );
pGZ.setY( pGZ.y() + pYDeltaDiv2 );
// fine-tune grid line pos for grid of vertical axis
if( KDChartAxisParams::AxisTypeNORTH == para.axisType() ) {
pGXMicroAdjust = cv.pXDeltaFactor * lineWidth / 2.0;
pGYMicroAdjust = cv.pYDeltaFactor * lineWidth / 2.0;
}
}
double x1, y1, x2, y2, xGA, yGA, xGZ, yGZ,
p1X, p1Y, p2X, p2Y, pGAX, pGAY, pGZX, pGZY, xT, yT;
double pXSubDelimDelta = pXDelta * cv.nSubDelimFactor;
double pYSubDelimDelta = pYDelta * cv.nSubDelimFactor;
if ( !cv.autoDtLabels
&& 0.0 != cv.nSubDelimFactor
&& para.axisShowSubDelimiters()
&& para.axisLabelsVisible()
&& !nLeaveOut ) {
TQPen pen( para.axisLineColor(), static_cast < int > ( 0.5 * lineWidth ) );
uint penWidth = pen.width();
bool bOk = true;
if( cv.bLogarithmic )
cv.nSubDelimFactor = 0.1;
while ( fabs( ( pXDelta + pYDelta ) * cv.nSubDelimFactor / 6.0 )
<= 1.0 + penWidth
&& bOk ) {
if ( 0 < penWidth ) {
--penWidth;
pen.setWidth( penWidth );
}else{
if( cv.bLogarithmic ){
break; // there is nothing we can do: we allways
// want 10 sub-delims per logarithmic step
}else{
if ( 0.5 != cv.nSubDelimFactor ) {
// emercency: reduce number of sub-scaling
cv.nSubDelimFactor = 0.5;
pXSubDelimDelta = pXDelta * cv.nSubDelimFactor;
pYSubDelimDelta = pYDelta * cv.nSubDelimFactor;
} else
bOk = false;
}
}
}
if ( bOk ) {
x1 = p1.x();
y1 = p1.y();
x2 = p2a.x();
y2 = p2a.y();
xGA = pGA.x();
yGA = pGA.y();
xGZ = pGZ.x();
yGZ = pGZ.y();
p1X = x1;
p1Y = y1;
p2X = x2;
p2Y = y2;
pGAX = xGA;
pGAY = yGA;
pGZX = xGZ;
pGZY = yGZ;
// set up grid pen for drawing the sub-grid lines
const TQPen oldGridPen( gridPen );
if ( para.axisShowGrid() ) {
gridPen.setColor( para.axisGridSubColor() );
gridPen.setWidth( gridSubLineWidth );
gridPen.setStyle( para.axisGridSubStyle() );
}
const TQPen oldPen( painter->pen() );
painter->setPen( pen );
double nSubDelim = ( labelTexts->count() - 1 )
/ cv.nSubDelimFactor;
//tqDebug("subDelim: %f",
modf( nSubDelim, &nSubDelim );
int logarithCnt = 1;
double xLogarithOffs = 0;
double yLogarithOffs = 0;
double dDummy;
double mainDelim = 0.0;
bool paint = true;
for ( double iDelim = 1.0;
iDelim <= nSubDelim + 1.0;
iDelim += 1.0, logarithCnt++ ) {
// test if it is a sub or a main delimiter
if ( mainDelim > 0.0 )
paint = true;
else
paint = false;
if ( cv.bLogarithmic )
{
if ( logarithCnt == 11 )
{
xLogarithOffs +=
pXDelta * log10( 10*cv.nSubDelimFactor*10 );
yLogarithOffs +=
pYDelta * log10( 10*cv.nSubDelimFactor*10 );
logarithCnt=1;
}
pXSubDelimDelta =
pXDelta * log10( 10*cv.nSubDelimFactor*logarithCnt );
pYSubDelimDelta =
pYDelta * log10( 10*cv.nSubDelimFactor*logarithCnt );
}
if ( para.axisShowGrid() && !bMultiRowBarChart) {
// draw the sub grid line
if( 0.0 != modf((iDelim-1.0) * cv.nSubDelimFactor, &dDummy) )
saveDrawLine( *painter,
TQPoint( static_cast<int>( pGAX - pGXMicroAdjust ),
static_cast<int>( pGAY - pGYMicroAdjust ) ),
TQPoint( static_cast<int>( pGZX - pGXMicroAdjust ),
static_cast<int>( pGZY - pGYMicroAdjust ) ),
gridPen );
if( cv.bLogarithmic ){
pGAX = xGA + pXSubDelimDelta + xLogarithOffs;
pGAY = yGA + pYSubDelimDelta + yLogarithOffs;
pGZX = xGZ + pXSubDelimDelta + xLogarithOffs;
pGZY = yGZ + pYSubDelimDelta + yLogarithOffs;
}else{
pGAX = xGA + iDelim * pXSubDelimDelta;
pGAY = yGA + iDelim * pYSubDelimDelta;
pGZX = xGZ + iDelim * pXSubDelimDelta;
pGZY = yGZ + iDelim * pYSubDelimDelta;
/*
if( !modf(iDelim * cv.nSubDelimFactor, &dDummy) ){
pGAX = xGA + (iDelim * cv.nSubDelimFactor) * pXDelta;
pGAY = yGA + (iDelim * cv.nSubDelimFactor) * pYDelta;
pGZX = xGZ + (iDelim * cv.nSubDelimFactor) * pXDelta;
pGZY = yGZ + (iDelim * cv.nSubDelimFactor) * pYDelta;
}
*/
}
}
// draw the short delimiter line
// PENDING: Michel - make sure not to draw the sub-delimiters over the main ones.
// by testing if it is a sub delimiter or a main one
if ( paint )
painter->drawLine( TQPoint( static_cast<int>( p1X ), static_cast<int>( p1Y ) ),
TQPoint( static_cast<int>( p2X ), static_cast<int>( p2Y ) ) );
mainDelim += 1.0;
if( cv.bLogarithmic ){
p1X = x1 + pXSubDelimDelta + xLogarithOffs;
p1Y = y1 + pYSubDelimDelta + yLogarithOffs;
p2X = x2 + pXSubDelimDelta + xLogarithOffs;
p2Y = y2 + pYSubDelimDelta + yLogarithOffs;
}else{
p1X = x1 + iDelim * pXSubDelimDelta;
p1Y = y1 + iDelim * pYSubDelimDelta;
p2X = x2 + iDelim * pXSubDelimDelta;
p2Y = y2 + iDelim * pYSubDelimDelta;
}
if ( mainDelim >= nSubDelim/(labelTexts->count() -1) )
mainDelim = 0.0;
} // for
// draw additional sub grid line
if( bDrawAdditionalSubGridLine
&& para.axisShowGrid() ) {
saveDrawLine( *painter,
TQPoint( static_cast<int>( pGAX - pGXMicroAdjust ),
static_cast<int>( pGAY - pGYMicroAdjust ) ),
TQPoint( static_cast<int>( pGZX - pGXMicroAdjust ),
static_cast<int>( pGZY - pGYMicroAdjust ) ),
gridPen );
}
painter->setPen( oldPen );
gridPen = oldGridPen;
}
}
x1 = p1.x();
y1 = p1.y();
x2 = p2.x();
y2 = p2.y();
xGA = pGA.x();
yGA = pGA.y();
xGZ = pGZ.x();
yGZ = pGZ.y();
p1X = x1;
p1Y = y1;
p2X = x2;
p2Y = y2;
pGAX = xGA;
pGAY = yGA;
pGZX = xGZ;
pGZY = yGZ;
xT = cv.pTextsX;
yT = cv.pTextsY;
// set up grid pen for drawing the normal grid lines
if ( para.axisShowGrid() ) {
gridPen.setWidth( gridLineWidth );
gridPen.setStyle( para.axisGridStyle() );
// if axis not visible draw the 1st grid line too
if( !para.axisLineVisible() )
saveDrawLine( *painter, cv.orig, cv.dest, gridPen );
}
if( nLeaveOut ) {
leaveOutGridPen = gridPen;
leaveOutGridPen.setWidth( gridLineWidth / 2 );
leaveOutGridPen.setStyle( Qt::DotLine );
}
// =========================================================
// || The labels and delimiters and grid printing loops ||
// =========================================================
//
double iLabel = 0.0;
if( cv.autoDtLabels )
{
/*
tqDebug("\ndtLow: %i %i %i %i:%i:%i",
dtLow.date().year(),
dtLow.date().month(),
dtLow.date().day(),
dtLow.time().hour(),
dtLow.time().minute(),
dtLow.time().second());
tqDebug("dtHigh: %i %i %i %i:%i:%i",
dtHigh.date().year(),
dtHigh.date().month(),
dtHigh.date().day(),
dtHigh.time().hour(),
dtHigh.time().minute(),
dtHigh.time().second());
*/
int pXD = static_cast <int> (cv.pXDelimDeltaFaktor * 1.25 * (cv.nTxtHeight+4));
int pYD = static_cast <int> (cv.pYDelimDeltaFaktor * 1.25 * (cv.nTxtHeight+4));
int orgXD = pXD;
int orgYD = pYD;
cv.pTextsW = fabs( (0.0 == pXDelta) ? pXD : pXDelta );
cv.pTextsH = fabs( (0.0 == pYDelta) ? pYD : pYDelta );
double pSecX = x1;
double pSecY = y1;
bool secPaint= false;
double pMinX = x1;
double pMinY = y1;
bool minPaint= false;
double pHourX = x1;
double pHourY = y1;
bool hourPaint= false;
double pDayX = x1;
double pDayY = y1;
bool dayPaint= false;
/* khz: currently not used
double pWeekX = x1;
double pWeekY = y1;
bool weekPaint= false;
*/
double pMonthX = x1;
double pMonthY = y1;
bool monthPaint= false;
/*double pQuarterX = x1;
double pQuarterY = y1;
bool minPaint= false;
*/
double pYearX = x1;
double pYearY = y1;
bool yearPaint= false;
double pXYDelta = fabs( pXDelta ) + fabs( pYDelta );
if( 0.0 == para.trueAxisDeltaPixels() )
( ( KDChartAxisParams& ) para ).setTrueAxisDeltaPixels( TQMIN(_logicalWidth, _logicalHeight) / 150 );
bool dtGoDown = cv.dtLow > cv.dtHigh;
int mult = dtGoDown ? -1 : 1;
const TQDateTime& startDt = dtGoDown ? cv.dtHigh : cv.dtLow;
( ( KDChartAxisParams& ) para ).setAxisDtLowPos( x1, y1 );
// adjust stored dt-low and scale settings
( ( KDChartAxisParams& ) para ).setTrueAxisDtLow( startDt );
( ( KDChartAxisParams& ) para ).setTrueAxisDtScale( cv.dtDeltaScale );
int gridDX = pGZ.x() - pGA.x();
int gridDY = pGZ.y() - pGA.y();
if ( para.axisShowGrid() ) {
gridPen.setColor( para.axisGridColor() );
gridPen.setWidth( gridLineWidth );
gridPen.setStyle( para.axisGridStyle() );
}
TQPen subGridPen( gridPen.color(), 1, para.axisGridStyle() );
TQPen subSubGridPen( gridPen.color(), 1, para.axisGridSubStyle() );
TQPen pen = subGridPen;
TQDateTime dt( startDt );
TQDateTime newDt( startDt );
for( uint i=1; i <= nLabels; ++i ){
switch( cv.dtDeltaScale ) {
case KDChartAxisParams::ValueScaleSecond:
dtAddSecs( dt, 1 * mult, newDt );
break;
case KDChartAxisParams::ValueScaleMinute:
dtAddSecs( dt, 60 * mult, newDt );
break;
case KDChartAxisParams::ValueScaleHour:
dtAddSecs( dt, 3600 * mult, newDt );
break;
case KDChartAxisParams::ValueScaleDay:
dtAddDays( dt, 1 * mult, newDt );
break;
case KDChartAxisParams::ValueScaleWeek:
dtAddDays( dt, 7 * mult, newDt );
break;
case KDChartAxisParams::ValueScaleMonth:
dtAddMonths( dt,1 * mult, newDt );
break;
case KDChartAxisParams::ValueScaleQuarter:
dtAddMonths( dt,3 * mult, newDt );
break;
case KDChartAxisParams::ValueScaleYear:
dtAddYears( dt, 1 * mult, newDt );
break;
default:
dtAddDays( dt, 1 * mult, newDt );
break;
}
const TQDateTime& testDt
= dtGoDown
? ( ( newDt < cv.dtLow )
? cv.dtLow
: newDt )
: ( ( newDt > cv.dtHigh )
? cv.dtHigh
: newDt );
/*
tqDebug(" dt: %i %i %i %i:%i:%i",
newDt.date().year(),newDt.date().month(),newDt.date().day(),
newDt.time().hour(),newDt.time().minute(),newDt.time().second());
tqDebug("testDt: %i %i %i %i:%i:%i",
testDt.date().year(),testDt.date().month(),testDt.date().day(),
testDt.time().hour(),testDt.time().minute(),testDt.time().second());
*/
bool endLoop = (i == nLabels) || (&testDt != &newDt);
secPaint = ( KDChartAxisParams::ValueScaleSecond >= cv.dtDeltaScale ) &&
( testDt.time().second() != dt.time().second() ||
( endLoop && ((pSecX != x1) || (pSecY != y1))));
minPaint = ( KDChartAxisParams::ValueScaleMinute >= cv.dtDeltaScale ) &&
( testDt.time().minute() != dt.time().minute() ||
( endLoop && ((pMinX != x1) || (pMinY != y1))));
hourPaint = ( KDChartAxisParams::ValueScaleHour >= cv.dtDeltaScale ) &&
( testDt.time().hour() != dt.time().hour() ||
( endLoop && ((pHourX != x1) || (pHourY != y1))));
dayPaint = ( KDChartAxisParams::ValueScaleDay >= cv.dtDeltaScale ) &&
( testDt.date().day() != dt.date().day() ||
( endLoop && ((pDayX != x1) || (pDayY != y1))));
/* khz: currently not used
weekPaint = ( KDChartAxisParams::ValueScaleWeek >= cv.dtDeltaScale ) &&
( testDt.date().week() != dt.date().week() ||
( endLoop && ((pWeekX != x1) || (pWeekY != y1))));
*/
monthPaint = ( KDChartAxisParams::ValueScaleMonth >= cv.dtDeltaScale ) &&
( testDt.date().month() != dt.date().month() ||
( endLoop && ((pMonthX != x1) || (pMonthY != y1))));
yearPaint = ( KDChartAxisParams::ValueScaleYear >= cv.dtDeltaScale ) &&
( testDt.date().year() != dt.date().year() ||
( endLoop && ((pYearX != x1) || (pYearY != y1))));
p1X = x1 + iLabel * pXDelta;
p1Y = y1 + iLabel * pYDelta;
p2X = p1X + pXDelta;
p2Y = p1Y + pYDelta;
pXD = orgXD;
pYD = orgYD;
if( endLoop ){
( ( KDChartAxisParams& ) para ).setAxisDtHighPos( p1X, p1Y );
// adjust stored dt-high settings
( ( KDChartAxisParams& ) para ).setTrueAxisDtHigh( dt );
}
pen = subGridPen;
/*
// old code: just draw the seconds without any tests
// (not wise to do that when supporting sec1000
// and the like some day...)
if( newDt.time().second() != dt.time().second() ){
painter->drawLine( TQPoint( p1X, p1Y ), TQPoint( p1X+pXD, p1Y+pYD ) );
painter->drawLine( TQPoint( p1X+pXD, p1Y+pYD ),
TQPoint( p1X+pXD + pXDelta, p1Y+pYD + pYDelta ) );
painter->drawText( p1X+pXD-orgXD, p1Y+pYD-orgYD,
pTextsW, pTextsH,
textAlign | TQt::DontClip,
TQString::number( dt.time().second() ) );
pXD += orgXD;
pYD += orgYD;
}
*/
if( secPaint ){
painter->drawLine( TQPoint( static_cast<int>( pSecX+pXD ),
static_cast<int>( pSecY+pYD ) ),
TQPoint( static_cast<int>( p2X + pXD ),
static_cast<int>( p2Y + pYD ) ) );
if( (pXDelta/2.0 < p2X - pSecX) || (pYDelta/2.0 < p2Y - pSecY) ){
TQPen oldPen( painter->pen() );
painter->setPen( TQPen( labelsColor ) );
painter->drawText( static_cast<int>( pSecX+pXD-orgXD ),
static_cast<int>( pSecY+pYD-orgYD ),
static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pSecX))),
static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pSecY))),
cv.textAlign | TQt::DontClip,
TQString::number( dt.time().second() ) );
painter->setPen( oldPen );
if ( para.axisShowGrid() ){
saveDrawLine( *painter,
TQPoint( static_cast<int>( pSecX ),
static_cast<int>( pSecY ) ),
TQPoint( static_cast<int>( pSecX + gridDX ),
static_cast<int>( pSecY + gridDY ) ),
pen );
pen = gridPen;
}
if( !minPaint || pMinX != pSecX || pMinY != pSecY ){
painter->drawLine( TQPoint( static_cast<int>( pSecX ),
static_cast<int>( pSecY ) ),
TQPoint( static_cast<int>( pSecX+pXD ),
static_cast<int>( pSecY+pYD ) ) );
}
}
if( endLoop && !minPaint )
painter->drawLine( TQPoint( static_cast<int>( p2X ),
static_cast<int>( p2Y ) ),
TQPoint( static_cast<int>( p2X+pXD ),
static_cast<int>( p2Y+pYD ) ) );
pSecX = p1X + pXDelta;
pSecY = p1Y + pYDelta;
pXD += orgXD;
pYD += orgYD;
}
if( minPaint ){
painter->drawLine( TQPoint( static_cast<int>( pMinX+pXD ),
static_cast<int>( pMinY+pYD ) ),
TQPoint( static_cast<int>( p2X + pXD ),
static_cast<int>( p2Y + pYD ) ) );
if( (pXDelta/2.0 < p2X - pMinX) || (pYDelta/2.0 < p2Y - pMinY) ){
TQPen oldPen( painter->pen() );
painter->setPen( TQPen( labelsColor ) );
painter->drawText( static_cast<int>( pMinX+pXD-orgXD ),
static_cast<int>( pMinY+pYD-orgYD ),
static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pMinX)) ),
static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pMinY)) ),
cv.textAlign | TQt::DontClip,
TQString::number( dt.time().minute() ) );
painter->setPen( oldPen );
if ( para.axisShowGrid() ){
if( !secPaint && 10 < pXYDelta ){
saveDrawLine( *painter,
TQPoint( static_cast<int>( pMinX+pXDelta/2 ),
static_cast<int>( pMinY+pYDelta/2 ) ),
TQPoint( static_cast<int>( pMinX+pXDelta/2 + gridDX ),
static_cast<int>( pMinY+pYDelta/2 + gridDY ) ),
subSubGridPen );
}
saveDrawLine( *painter,
TQPoint( static_cast<int>( pMinX ),
static_cast<int>( pMinY ) ),
TQPoint( static_cast<int>( pMinX + gridDX ),
static_cast<int>( pMinY + gridDY ) ),
pen );
pen = gridPen;
}
if( !hourPaint || pHourX != pMinX || pHourY != pMinY ){
painter->drawLine( TQPoint( static_cast<int>( pMinX ),
static_cast<int>( pMinY ) ),
TQPoint( static_cast<int>( pMinX+pXD ),
static_cast<int>( pMinY+pYD ) ) );
}
}
if( endLoop && !hourPaint )
painter->drawLine( TQPoint( static_cast<int>( p2X ),
static_cast<int>( p2Y ) ),
TQPoint( static_cast<int>( p2X+pXD ),
static_cast<int>( p2Y+pYD ) ) );
pMinX = p1X + pXDelta;
pMinY = p1Y + pYDelta;
pXD += orgXD;
pYD += orgYD;
}
if( hourPaint ){
painter->drawLine( TQPoint( static_cast<int>( pHourX+pXD ),
static_cast<int>( pHourY+pYD ) ),
TQPoint( static_cast<int>( p2X + pXD ),
static_cast<int>( p2Y + pYD ) ) );
/*
tqDebug("line");
tqDebug("pXDelta / 2.0 : %f", pXDelta/2.0);
tqDebug("p2X - pHourX : %f", p2X - pHourX);
*/
if( (pXDelta/2.0 < p2X - pHourX) || (pYDelta/2.0 < p2Y - pHourY) ){
/*
tqDebug("pHourX %f", pHourX );
tqDebug(" +pXD %i", pXD );
tqDebug(" -orgXD %i", orgXD);
tqDebug("pHourY %f", pHourY );
tqDebug(" +pYD %i", pYD );
tqDebug(" -orgYD %i", orgYD);
*/
TQPen oldPen( painter->pen() );
painter->setPen( TQPen( labelsColor ) );
painter->drawText( static_cast<int>( pHourX+pXD-orgXD ),
static_cast<int>( pHourY+pYD-orgYD ),
static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pHourX))),
static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pHourY))),
cv.textAlign | TQt::DontClip,
TQString::number( dt.time().hour() ) );
painter->setPen( oldPen );
if ( para.axisShowGrid() ){
if( !minPaint && 10 < pXYDelta ){
saveDrawLine( *painter,
TQPoint( static_cast<int>( pHourX+pXDelta/2 ),
static_cast<int>( pHourY+pYDelta/2 ) ),
TQPoint( static_cast<int>( pHourX+pXDelta/2 + gridDX ),
static_cast<int>( pHourY+pYDelta/2 + gridDY ) ),
subSubGridPen );
}
saveDrawLine( *painter,
TQPoint( static_cast<int>( pHourX ),
static_cast<int>( pHourY ) ),
TQPoint( static_cast<int>( pHourX + gridDX ),
static_cast<int>( pHourY + gridDY ) ),
pen );
pen = gridPen;
}
if( !dayPaint || pDayX != pHourX || pDayY != pHourY ){
painter->drawLine( TQPoint( static_cast<int>( pHourX ),
static_cast<int>( pHourY ) ),
TQPoint( static_cast<int>( pHourX+pXD ),
static_cast<int>( pHourY+pYD ) ) );
}
}
if( endLoop && !dayPaint )
painter->drawLine( TQPoint( static_cast<int>( p2X ),
static_cast<int>( p2Y ) ),
TQPoint( static_cast<int>( p2X+pXD ),
static_cast<int>( p2Y+pYD ) ) );
pHourX = p1X + pXDelta;
pHourY = p1Y + pYDelta;
pXD += orgXD;
pYD += orgYD;
}
if( dayPaint ){
painter->drawLine( TQPoint( static_cast<int>( pDayX+pXD ),
static_cast<int>( pDayY+pYD ) ),
TQPoint( static_cast<int>( p2X + pXD ),
static_cast<int>( p2Y + pYD ) ) );
if( (pXDelta/2.0 < p2X - pDayX) || (pYDelta/2.0 < p2Y - pDayY) ){
TQPen oldPen( painter->pen() );
painter->setPen( TQPen( labelsColor ) );
painter->drawText( static_cast<int>( pDayX+pXD-orgXD ),
static_cast<int>( pDayY+pYD-orgYD ),
static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pDayX)) ),
static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pDayY)) ),
cv.textAlign | TQt::DontClip,
TQString::number( dt.date().day() ) );
painter->setPen( oldPen );
/* khz: currently not used
if( !weekPaint || pWeekX != pDayX || pWeekY != pDayY )
*/
if ( para.axisShowGrid() ){
if( !hourPaint && 10 < pXYDelta ){
saveDrawLine( *painter,
TQPoint( static_cast<int>( pDayX+pXDelta/2 ),
static_cast<int>( pDayY+pYDelta/2 ) ),
TQPoint( static_cast<int>( pDayX+pXDelta/2 + gridDX ),
static_cast<int>( pDayY+pYDelta/2 + gridDY ) ),
subSubGridPen );
}
saveDrawLine( *painter,
TQPoint( static_cast<int>( pDayX ),
static_cast<int>( pDayY ) ),
TQPoint( static_cast<int>( pDayX + gridDX ),
static_cast<int>( pDayY + gridDY ) ),
pen );
pen = gridPen;
}
if( !monthPaint || pMonthX != pDayX || pMonthY != pDayY ){
painter->drawLine( TQPoint( static_cast<int>( pDayX ),
static_cast<int>( pDayY ) ),
TQPoint( static_cast<int>( pDayX+pXD ),
static_cast<int>( pDayY+pYD ) ) );
}
}
/* khz: currently not used
if( endLoop && !weekPaint )
*/
if( endLoop && !monthPaint )
painter->drawLine( TQPoint( static_cast<int>( p2X ),
static_cast<int>( p2Y ) ),
TQPoint( static_cast<int>( p2X+pXD ),
static_cast<int>( p2Y+pYD ) ) );
pDayX = p1X + pXDelta;
pDayY = p1Y + pYDelta;
pXD += orgXD;
pYD += orgYD;
}
/* khz: currently unused
if( weekPaint ){
painter->drawLine( TQPoint( pWeekX+pXD, pWeekY+pYD ),
TQPoint( p2X + pXD, p2Y + pYD ) );
if( (pXDelta/2.0 < p2X - pWeekX) || (pYDelta/2.0 < p2Y - pWeekY) ){
TQPen oldPen( painter->pen() );
painter->setPen( TQPen( labelsColor ) );
painter->drawText( pWeekX+pXD-orgXD, pWeekY+pYD-orgYD,
painter->setPen( oldPen );
fabs((0.0 == pXDelta) ? pTextsW : (p2X - pWeekX)),
fabs((0.0 == pYDelta) ? pTextsH : (p2Y - pWeekY)),
textAlign | TQt::DontClip,
TQString::number( dt.date().week() ) );
if ( para.axisShowGrid() ){
if( !dayPaint && 40 < pXYDelta ){
// draw 7 lines:
//saveDrawLine( *painter,
// TQPoint( pWeekX+pXDelta/2,
// pWeekY+pYDelta/2 ),
// TQPoint( pWeekX+pXDelta/2 + gridDX,
// pWeekY+pYDelta/2 + gridDY ),
// subSubGridPen );
}
saveDrawLine( *painter,
TQPoint( pWeekX,
pWeekY ),
TQPoint( pWeekX + gridDX,
pWeekY + gridDY ),
pen );
pen = gridPen;
}
if( !monthPaint || pMonthX != pDayX || pMonthY != pDayY ){
painter->drawLine( TQPoint( pWeekX, pWeekY ), TQPoint( pWeekX+pXD, pWeekY+pYD ) );
}
}
if( endLoop && !monthPaint )
painter->drawLine( TQPoint( p2X, p2Y ), TQPoint( p2X+pXD, p2Y+pYD ) );
pWeekX = p1X + pXDelta;
pWeekY = p1Y + pYDelta;
pXD += orgXD;
pYD += orgYD;
}
*/
if( monthPaint ){
painter->drawLine( TQPoint( static_cast<int>( pMonthX+pXD ),
static_cast<int>( pMonthY+pYD ) ),
TQPoint( static_cast<int>( p2X + pXD ),
static_cast<int>( p2Y + pYD ) ) );
if( (pXDelta/2.0 < p2X - pMonthX) || (pYDelta/2.0 < p2Y - pMonthY) ){
TQPen oldPen( painter->pen() );
painter->setPen( TQPen( labelsColor ) );
painter->drawText( static_cast<int>( pMonthX+pXD-orgXD ),
static_cast<int>( pMonthY+pYD-orgYD ),
static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pMonthX)) ),
static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pMonthY)) ),
cv.textAlign | TQt::DontClip,
TQString::number( dt.date().month() ) );
painter->setPen( oldPen );
if ( para.axisShowGrid() ){
/* khz: currently unused
if( !weekPaint &&
&& 10 < pXYDelta ){
saveDrawLine( *painter,
TQPoint( pMonthX+pXDelta/2,
pMonthY+pYDelta/2 ),
TQPoint( pMonthX+pXDelta/2 + gridDX,
pMonthY+pYDelta/2 + gridDY ),
subSubGridPen );
}
*/
saveDrawLine( *painter,
TQPoint( static_cast<int>( pMonthX ),
static_cast<int>( pMonthY ) ),
TQPoint( static_cast<int>( pMonthX + gridDX ),
static_cast<int>( pMonthY + gridDY ) ),
pen );
pen = gridPen;
}
if( !yearPaint || pYearX != pMonthX || pYearY != pMonthY ){
painter->drawLine( TQPoint( static_cast<int>( pMonthX ),
static_cast<int>( pMonthY ) ),
TQPoint( static_cast<int>( pMonthX+pXD ),
static_cast<int>( pMonthY+pYD ) ) );
}
}
if( endLoop && !yearPaint )
painter->drawLine( TQPoint( static_cast<int>( p2X ),
static_cast<int>( p2Y ) ),
TQPoint( static_cast<int>( p2X+pXD ),
static_cast<int>( p2Y+pYD ) ) );
pMonthX = p1X + pXDelta;
pMonthY = p1Y + pYDelta;
pXD += orgXD;
pYD += orgYD;
}
if( yearPaint ){
painter->drawLine( TQPoint( static_cast<int>( pYearX+pXD ),
static_cast<int>( pYearY+pYD ) ),
TQPoint( static_cast<int>( p2X + pXD ),
static_cast<int>( p2Y + pYD ) ) );
if( (pXDelta/2.0 < p2X - pYearX) || (pYDelta/2.0 < p2Y - pYearY) ){
TQPen oldPen( painter->pen() );
painter->setPen( TQPen( labelsColor ) );
painter->drawText( static_cast<int>( pYearX+pXD-orgXD ),
static_cast<int>( pYearY+pYD-orgYD ),
static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pYearX)) ),
static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pYearY)) ),
cv.textAlign | TQt::DontClip,
TQString::number( dt.date().year() ) );
painter->setPen( oldPen );
if ( para.axisShowGrid() ){
if( !monthPaint && 10 < pXYDelta ){
saveDrawLine( *painter,
TQPoint( static_cast<int>( pYearX+pXDelta/2 ),
static_cast<int>( pYearY+pYDelta/2 ) ),
TQPoint( static_cast<int>( pYearX+pXDelta/2 + gridDX ),
static_cast<int>( pYearY+pYDelta/2 + gridDY ) ),
subSubGridPen );
}
saveDrawLine( *painter,
TQPoint( static_cast<int>( pYearX ),
static_cast<int>( pYearY ) ),
TQPoint( static_cast<int>( pYearX + gridDX ),
static_cast<int>( pYearY + gridDY ) ),
pen );
pen = gridPen;
}
painter->drawLine( TQPoint( static_cast<int>( pYearX ),
static_cast<int>( pYearY ) ),
TQPoint( static_cast<int>( pYearX+pXD ),
static_cast<int>( pYearY+pYD ) ) );
}
if( endLoop )
painter->drawLine( TQPoint( static_cast<int>( p2X ),
static_cast<int>( p2Y ) ),
TQPoint( static_cast<int>( p2X+pXD ),
static_cast<int>( p2Y+pYD ) ) );
pYearX = p1X + pXDelta;
pYearY = p1Y + pYDelta;
pXD += orgXD;
pYD += orgYD;
}
if( &testDt != &newDt )
break;
dt = newDt;
iLabel += 1.0;
}
if( !commonDtHeader.isEmpty() ){
TQPen oldPen( painter->pen() );
painter->setPen( TQPen( labelsColor ) );
painter->drawText( static_cast<int>( x1 + pXD ), static_cast<int>( y1 + pYD ),
commonDtHeader );
painter->setPen( oldPen );
}
}else{
int iLeaveOut = nLeaveOut;
TQString label;
for ( TQStringList::Iterator labelIter = labelTexts->begin();
labelIter != labelTexts->end();
++labelIter ) {
TQDateTime dt;
if( cv.isDateTime ){
#if COMPAT_TQT_VERSION >= 0x030000
dt = TQDateTime::fromString( *labelIter,
Qt::ISODate );
label = dt.toString( formatDT );
#else
dt = dateTimeFromString( *labelIter );
label = dt.toString();
#endif
}else{
label = *labelIter;
}
if( iLeaveOut < nLeaveOut )
++iLeaveOut;
else
iLeaveOut = 0;
//Pending Michel: test if the user implicitely wants to get rid
//of the non fractional values delimiters and grid lines.
// axisDigitsBehindComma == 0 and the user implicitely
// setAxisShowFractionalValuesDelimiters() to false
bool showDelim = para.axisShowFractionalValuesDelimiters();
if ( para.axisShowGrid() && !bMultiRowBarChart ) {
if ( !label.isNull() || showDelim ){
if( !iLeaveOut )
// draw the main grid line
saveDrawLine( *painter,
TQPoint( static_cast<int>( pGAX - pGXMicroAdjust ),
static_cast<int>( pGAY - pGYMicroAdjust ) ),
TQPoint( static_cast<int>( pGZX - pGXMicroAdjust ),
static_cast<int>( pGZY - pGYMicroAdjust ) ),
gridPen );
else if( para.axisShowSubDelimiters() )
// draw a thin sub grid line instead of main line
saveDrawLine( *painter,
TQPoint( static_cast<int>( pGAX - pGXMicroAdjust ),
static_cast<int>( pGAY - pGYMicroAdjust ) ),
TQPoint( static_cast<int>( pGZX - pGXMicroAdjust ),
static_cast<int>( pGZY - pGYMicroAdjust ) ),
leaveOutGridPen );
}
}
if ( para.axisLabelsVisible() ) {
if( !iLeaveOut ) {
/*PENDING Michel: those points should not be redrawn if sub-delimiters are drawn
*drawing the submarkers
* make it visible or not
*In the case we have a null label - axisDigitsBehindComma is implicitely set to 0 -
*also paint or dont paint the delimiter corresponding to this label - default is paint.
*/
if ( !label.isNull() || showDelim )
painter->drawLine( TQPoint( static_cast<int>( p1X ),
static_cast<int>( p1Y ) ),
TQPoint( static_cast<int>( p2X ),
static_cast<int>( p2Y ) ) );
cv.pLastX = p1X;
cv.pLastY = p1Y;
TQPen oldPen( painter->pen() );
painter->setPen( TQPen( labelsColor ) );
if( para.axisLabelsDontShrinkFont()
&& isHorizontalAxis
&& (TQt::AlignHCenter == (cv.textAlign & TQt::AlignHCenter)) ) {
double w = fm.width( label ) + 4.0;
double x0 = cv.pTextsX + cv.pTextsW / 2.0;
painter->drawText( static_cast<int>( x0 - w / 2.0 ),
static_cast<int>( cv.pTextsY ),
static_cast<int>( w ),
static_cast<int>( cv.pTextsH ),
cv.textAlign, label );
} else {
if( nRotation ){
KDDrawText::drawRotatedText(
painter,
nRotation,
painter->worldMatrix().map(
TQPoint( static_cast<int>( cv.pTextsX ),
static_cast<int>( cv.pTextsY ) ) ),
label,
0,
cv.textAlign,
false,
&fm,
screenOutput,screenOutput,0,
screenOutput );
} else {
// Pending Michel draw the axis labels
painter->drawText( static_cast<int>( cv.pTextsX ),
static_cast<int>( cv.pTextsY ),
static_cast<int>( cv.pTextsW ),
static_cast<int>( cv.pTextsH ),
cv.textAlign | TQt::DontClip,
label );
// debugging text rect
/*
painter->drawRect(static_cast <int>(cv.pTextsX),
static_cast <int>(cv.pTextsY),
static_cast <int> (nUsableAxisWidth),
static_cast <int> (nUsableAxisHeight));
*/
}
}
painter->setPen( oldPen );
}
}
if( cv.isDateTime ){
if( labelTexts->begin() == labelIter ){
((KDChartAxisParams&)para).setAxisDtLowPos(
pGAX - pGXMicroAdjust,
pGAY - pGYMicroAdjust );
// adjust stored dt-low settings
( ( KDChartAxisParams& ) para ).setTrueAxisDtLow( dt );
}else{
((KDChartAxisParams&)para).setAxisDtHighPos(
pGAX - pGXMicroAdjust,
pGAY - pGYMicroAdjust );
// adjust stored dt-high settings
( ( KDChartAxisParams& ) para ).setTrueAxisDtHigh( dt );
}
}
iLabel += 1.0;
p1X = x1 + iLabel * pXDelta;
p1Y = y1 + iLabel * pYDelta;
p2X = x2 + iLabel * pXDelta;
p2Y = y2 + iLabel * pYDelta;
cv.pTextsX = xT + iLabel * pXDelta;
cv.pTextsY = yT + iLabel * pYDelta;
pGAX = xGA + iLabel * pXDelta;
pGAY = yGA + iLabel * pYDelta;
pGZX = xGZ + iLabel * pXDelta;
pGZY = yGZ + iLabel * pYDelta;
/*
pGAX = xGA + iLabel * pXSubDelimDelta / cv.nSubDelimFactor;
pGAY = yGA + iLabel * pYSubDelimDelta / cv.nSubDelimFactor;
pGZX = xGZ + iLabel * pXSubDelimDelta / cv.nSubDelimFactor;
pGZY = yGZ + iLabel * pYSubDelimDelta / cv.nSubDelimFactor;
*/
}
}
// adjust zero-line start, if not starting at origin
if ( cv.bSteadyCalc &&
( para.axisValuesDecreasing() ||
(0.0 != para.trueAxisLow()) ) ) {
//double x = p1.x();
double x = 0.0;
/* we have to find the *real* X axis position,
this is NOT always the p1.x() as it is the
case for left2 or right2 axes. [cmw, 12/01/2005] */
if (cv.basicPos==KDChartAxisParams::AxisPosRight)
x = static_cast<double>(_dataRect.right());
else
x = static_cast<double>(_dataRect.left());
double y = p1.y();
double mult = para.trueAxisLow() / para.trueAxisDelta();
x -= mult * pXDelta;
y -= mult * pYDelta;
axisZeroLineStartX = x;
axisZeroLineStartY = y;
//tqDebug( "axisZeroLineStartX %f, axisZeroLineStartY %f",
// axisZeroLineStartX, axisZeroLineStartY );
}
painter->setClipping( oldClippingFlag );
} // if( nLabels )
// draw zero-line (Ok, this might be overwritten by axes
// cause those are drawn after all labels and grid and
// zero-line(s) has been painted, see code below, starting
// with "// draw all the axes".
if ( cv.bSteadyCalc && !cv.isDateTime ) {
( ( KDChartAxisParams& ) para ).setAxisZeroLineStart( axisZeroLineStartX, axisZeroLineStartY );
double axisZeroLineStart;
int minCoord, maxCoord;
double xFactor, yFactor;
switch( cv.basicPos ){
case KDChartAxisParams::AxisPosLeft:
xFactor = 1.0;
yFactor = 0.0;
axisZeroLineStart = axisZeroLineStartY;
minCoord = TQMIN( cv.orig.y(), cv.dest.y() );
maxCoord = TQMAX( cv.orig.y(), cv.dest.y() );
break;
case KDChartAxisParams::AxisPosRight:
xFactor = -1.0;
yFactor = 0.0;
axisZeroLineStart = axisZeroLineStartY;
minCoord = TQMIN( cv.orig.y(), cv.dest.y() );
maxCoord = TQMAX( cv.orig.y(), cv.dest.y() );
break;
case KDChartAxisParams::AxisPosTop:
xFactor = 0.0;
yFactor = 1.0;
axisZeroLineStart = axisZeroLineStartX;
minCoord = TQMIN( cv.orig.x(), cv.dest.x() );
maxCoord = TQMAX( cv.orig.x(), cv.dest.x() );
break;
case KDChartAxisParams::AxisPosBottom:
xFactor = 0.0;
yFactor = -1.0;
axisZeroLineStart = axisZeroLineStartX;
minCoord = TQMIN( cv.orig.x(), cv.dest.x() );
maxCoord = TQMAX( cv.orig.x(), cv.dest.x() );
break;
default:
xFactor = 0.0;
yFactor = 0.0;
axisZeroLineStart = 0.0;
minCoord = 0;
maxCoord = 0;
}
if( axisZeroLineStart >= minCoord &&
axisZeroLineStart <= maxCoord ){
TQPoint pZ0( static_cast<int>( para.axisZeroLineStartX() ),
static_cast<int>( para.axisZeroLineStartY() ) );
TQPoint pZ( static_cast<int>( para.axisZeroLineStartX()
+ xFactor * _dataRect.width() ),
static_cast<int>( para.axisZeroLineStartY()
+ yFactor * _dataRect.height() ) );
//tqDebug("------");
saveDrawLine( *painter,
pZ0,
pZ,
TQPen( para.axisZeroLineColor(),
lineWidth ) );
}
}
}
}
// Drawing all the axes lines:
/*
// 1. test if the standard axes are share one or several corner points
// if yes, we first draw a polyline using a "TQt::MiterJoin" PenJoinStyle
// to make sure the corners are filled
internal__KDChart__CalcValues& cv1 = calcVal[ KDChartAxisParams::AxisPosLeft ];
internal__KDChart__CalcValues& cv2 = calcVal[ KDChartAxisParams::AxisPosBottom ];
const KDChartAxisParams& pa1 = params()->axisParams( KDChartAxisParams::AxisPosLeft );
const KDChartAxisParams& pa2 = params()->axisParams( KDChartAxisParams::AxisPosBottom );
tqDebug("\n\nx1: %i y1: %i w1: %i\nx2: %i y2: %i w2: %i",
cv1.orig.x(), cv1.orig.y(), pa1.axisTrueLineWidth(),
cv2.orig.x(), cv2.orig.y(), pa2.axisTrueLineWidth() );
if( cv1.orig == cv2.orig ){
const TQColor c1( pa1.axisLineColor() );
const TQColor c2( pa2.axisLineColor() );
const TQPoint pA( cv1.dest );
const TQPoint pB( cv1.orig );
const TQPoint pC( cv2.dest );
TQPen pen( TQColor( (c1.red() + c2.red()) /2,
(c1.green() + c2.green())/2,
(c1.blue() + c2.blue()) /2 ),
TQMIN(pa1.axisTrueLineWidth(), pa2.axisTrueLineWidth()) );
pen.setJoinStyle( TQt::MiterJoin );
painter->setPen( pen );
TQPointArray a;
a.putPoints( 0, 3, pA.x(),pA.y(), pB.x(),pB.y(), pC.x(),pC.y() );
painter->drawPolyline( a );
tqDebug("done\n" );
}
*/
// 2. draw the axes using their normal color
for( iAxis = 0; iAxis < KDCHART_MAX_AXES; ++iAxis ){
internal__KDChart__CalcValues& cv = calcVal[iAxis];
const KDChartAxisParams & para = params()->axisParams( iAxis );
if( cv.processThisAxis && para.axisLineVisible() ){
painter->setPen( TQPen( para.axisLineColor(),
para.axisTrueLineWidth() ) );
int x = cv.dest.x();
if( 2.0 >= TQABS(cv.pLastX - x) )
x = static_cast < int > ( cv.pLastX );
int y = cv.dest.y();
if( 2.0 >= TQABS(cv.pLastY - y) )
y = static_cast < int > ( cv.pLastY );
painter->drawLine( cv.orig, TQPoint(x,y) );
}
}
painter->restore();
}
double fastPow10( int x )
{
double res = 1.0;
if( 0 <= x ){
for( int i = 1; i <= x; ++i )
res *= 10.0;
}else{
for( int i = -1; i >= x; --i )
res /= 10.0;
}
return res;
}
double fastPow10( double x )
{
return pow(10.0, x);
}
/**
Calculates the actual label texts for one axis.
\note When calling this function the actual area size for this
axis must be set, this means you may only call it when
\c KDChartPainter::setupGeometry() has been called before.
\param painter the TQPainter onto which the chart should be painted
\param data the data that will be displayed as a chart
\param params the KDChartParams that were specified globally
\param axisNumber the number of this axis (used in some params structures)
\param averageValueP1000 (average height+width of the prtbl. area) / 1000
\param basicPos the basic axis position returned by
KDChartAxisParams::basicAxisPos()
\param orig the axis start point
\param delimLen the length of one delimiter mark
\param (all others) the reference parameters to be returned
by this function
*/
/**** static ****/
void KDChartAxesPainter::calculateLabelTexts(
TQPainter* painter,
const KDChartTableDataBase& data,
const KDChartParams& params,
uint axisNumber,
double averageValueP1000,
double delimLen,
// start of return parameters
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,
double trueDelta,
double trueDeltaPix )
{
//tqDebug("\nentering KDChartAxesPainter::calculateLabelTexts() : nTxtHeight: "+TQString::number(nTxtHeight));
const KDChartAxisParams & para = params.axisParams( axisNumber );
// store whether the labels are to be drawn in reverted order
const bool bDecreasing = para.axisValuesDecreasing();
basicPos = KDChartAxisParams::basicAxisPos( axisNumber );
pXDeltaFactor = 0.0;
pYDeltaFactor = 0.0;
pXDelimDeltaFaktor = 0.0;
pYDelimDeltaFaktor = 0.0;
int axisLength;
switch ( basicPos ) {
case KDChartAxisParams::AxisPosBottom: {
axisLength = para.axisTrueAreaRect().width();
orig = bDecreasing
? para.axisTrueAreaRect().topRight()
: para.axisTrueAreaRect().topLeft();
dest = bDecreasing
? para.axisTrueAreaRect().topLeft()
: para.axisTrueAreaRect().topRight();
pYDelimDeltaFaktor = 1.0;
pXDeltaFactor = bDecreasing ? -1.0 : 1.0;
//tqDebug("\nsetting pXDeltaFactor for axis %x", axisNumber);
//tqDebug(bDecreasing ? "bDecreasing = TRUE" : "bDecreasing = FALSE");
//tqDebug("pXDeltaFactor = %f\n",pXDeltaFactor);
}
break;
case KDChartAxisParams::AxisPosLeft: {
axisLength = para.axisTrueAreaRect().height();
orig = bDecreasing
? para.axisTrueAreaRect().topRight()
: para.axisTrueAreaRect().bottomRight();
dest = bDecreasing
? para.axisTrueAreaRect().bottomRight()
: para.axisTrueAreaRect().topRight();
pXDelimDeltaFaktor = -1.0;
pYDeltaFactor = bDecreasing ? 1.0 : -1.0;
}
break;
case KDChartAxisParams::AxisPosTop: {
axisLength = para.axisTrueAreaRect().width();
orig = bDecreasing
? para.axisTrueAreaRect().bottomRight()
: para.axisTrueAreaRect().bottomLeft();
dest = bDecreasing
? para.axisTrueAreaRect().bottomLeft()
: para.axisTrueAreaRect().bottomRight();
pYDelimDeltaFaktor = -1.0;
pXDeltaFactor = bDecreasing ? -1.0 : 1.0;
}
break;
case KDChartAxisParams::AxisPosRight: {
axisLength = para.axisTrueAreaRect().height();
orig = bDecreasing
? para.axisTrueAreaRect().topLeft()
: para.axisTrueAreaRect().bottomLeft();
dest = bDecreasing
? para.axisTrueAreaRect().bottomLeft()
: para.axisTrueAreaRect().topLeft();
pXDelimDeltaFaktor = 1.0;
pYDeltaFactor = bDecreasing ? 1.0 : -1.0;
}
break;
default: {
axisLength = 0;
tqDebug( "IMPLEMENTATION ERROR: KDChartAxesPainter::paintAxes() unhandled enum value." );
}
break;
}
// which dataset(s) is/are represented by this axis?
uint dataset, dataset2, chart;
if ( !params.axisDatasets( axisNumber, dataset, dataset2, chart ) ) {
dataset = KDCHART_ALL_DATASETS;
dataset2 = KDCHART_ALL_DATASETS;
chart = 0;
//tqDebug("\nautomatic set values: chart: %u,\ndataset: %u, dataset2: %u",
//chart, dataset, dataset2);
}
// which dataset(s) with mode DataEntry (or mode ExtraLinesAnchor, resp.)
// is/are represented by this axis?
uint dataDataset, dataDataset2;
if( params.findDatasets( KDChartParams::DataEntry,
KDChartParams::ExtraLinesAnchor,
dataDataset,
dataDataset2,
chart ) ) {
// adjust dataDataset in case MORE THAN ONE AXIS
// is representing THIS CHART
if( ( KDCHART_ALL_DATASETS != dataset
&& KDCHART_NO_DATASET != dataset )
|| ( KDCHART_ALL_DATASETS != dataDataset
&& KDCHART_NO_DATASET != dataDataset ) ){
int ds = (KDCHART_ALL_DATASETS != dataset)
? dataset
: 0;
int dds = (KDCHART_ALL_DATASETS != dataDataset)
? dataDataset
: 0;
dataDataset = TQMAX( ds, dds );
}
if( ( KDCHART_ALL_DATASETS != dataset2
&& KDCHART_NO_DATASET != dataset2 )
|| ( KDCHART_ALL_DATASETS != dataDataset2
&& KDCHART_NO_DATASET != dataDataset2 ) ){
int ds2 = (KDCHART_ALL_DATASETS != dataset2)
? dataset2
: KDCHART_MAX_AXES-1;
int dds2 = (KDCHART_ALL_DATASETS != dataDataset2)
? dataDataset2
: KDCHART_MAX_AXES-1;
dataDataset2 = TQMIN( ds2, dds2 );
}
}
else {
// Should not happen
tqDebug( "IMPLEMENTATION ERROR: findDatasets( DataEntry, ExtraLinesAnchor ) should *always* return true. (b)" );
dataDataset = KDCHART_ALL_DATASETS;
}
//tqDebug("\naxisNumber: %x\nchart: %x\ndataset: %x, dataset2: %x,\ndataDataset: %x, dataDataset2: %x",
//axisNumber, chart, dataset, dataset2, dataDataset, dataDataset2);
if ( para.axisLabelsFontUseRelSize() ){
nTxtHeight = para.axisLabelsFontRelSize()
* averageValueP1000;
//tqDebug("using rel. size in KDChartAxesPainter::calculateLabelTexts() : nTxtHeight: "+TQString::number(nTxtHeight));
}else {
TQFontInfo info( para.axisLabelsFont() );
nTxtHeight = info.pointSize();
//tqDebug("using FIXED size in KDChartAxesPainter::calculateLabelTexts() : nTxtHeight: "+TQString::number(nTxtHeight));
}
const KDChartEnums::NumberNotation notation = para.axisLabelsNotation();
const int behindComma = para.axisDigitsBehindComma();
const int divPow10 = para.axisLabelsDivPow10();
const TQString decimalPoint = para.axisLabelsDecimalPoint();
const TQString thousandsPoint = para.axisLabelsThousandsPoint();
const TQString prefix = para.axisLabelsPrefix();
const TQString postfix = para.axisLabelsPostfix();
const int totalLen = para.axisLabelsTotalLen();
const TQChar padFill = para.axisLabelsPadFill();
const bool blockAlign = para.axisLabelsBlockAlign();
TQStringList labelTexts;
int colNum = para.labelTextsDataRow();
bool bDone = true;
switch ( para.axisLabelTextsFormDataRow() ) {
case KDChartAxisParams::LabelsFromDataRowYes: {
// Take whatever is in the specified column (even if not a string)
int trueBehindComma = -1;
TQVariant value;
for ( uint iDataset = 0; iDataset < data.usedRows(); iDataset++ ) {
if( data.cellCoord( iDataset, colNum, value, 1 ) ){
if( TQVariant::String == value.type() )
labelTexts.append( value.toString() );
else {
labelTexts.append( applyLabelsFormat( value.toDouble(),
divPow10,
behindComma,
para.axisValueDelta(),
trueBehindComma,
notation,
decimalPoint,
thousandsPoint,
prefix,
postfix,
totalLen,
padFill,
blockAlign ) );
}
}
}
break;
}
case KDChartAxisParams::LabelsFromDataRowGuess: {
TQVariant value;
for ( uint iDataset = 0; iDataset < data.usedRows(); iDataset++ ) {
if( data.cellCoord( iDataset, colNum, value, 1 ) ){
if( TQVariant::String == value.type() ){
const TQString sVal( value.toString() );
if( !sVal.isEmpty() && !sVal.isNull() )
labelTexts.append( sVal );
}
}else{
labelTexts.clear();
bDone = false;
break;
}
}
break;
}
case KDChartAxisParams::LabelsFromDataRowNo: {
bDone = false;
break;
}
default:
// Should not happen
tqDebug( "KDChart: Unknown label texts source" );
}
// if necessary adjust text params *including* the steady value calc setting
const bool dataCellsHaveSeveralCoordinates =
(KDCHART_ALL_DATASETS == dataDataset)
? data.cellsHaveSeveralCoordinates()
: data.cellsHaveSeveralCoordinates( dataDataset, dataDataset2 );
if( dataCellsHaveSeveralCoordinates && !para.axisSteadyValueCalc() )
((KDChartParams&)params).setAxisLabelTextParams(
axisNumber,
true,
KDCHART_AXIS_LABELS_AUTO_LIMIT,
KDCHART_AXIS_LABELS_AUTO_LIMIT,
KDCHART_AXIS_LABELS_AUTO_DELTA,
para.axisLabelsDigitsBehindComma() );// NOTE: This sets MANY other params to default values too!
const KDChartParams::ChartType params_chartType
= ( 0 == chart )
? params.chartType()
: params.additionalChartType();
// store whether we are calculating Ordinate-like axis values
const bool bSteadyCalc = para.axisSteadyValueCalc();
// store whether logarithmic calculation is wanted
isLogarithmic = bSteadyCalc &&
(KDChartParams::Line == params_chartType) &&
(KDChartAxisParams::AxisCalcLogarithmic == para.axisCalcMode());
//tqDebug(bSteadyCalc ? "bSteadyCalc":"NOT bSteadyCalc");
//tqDebug(isLogarithmic? "isLogarithmic":"NOT isLogarithmic");
// store whether this is a vertical axis or a horizontal axis
const bool bVertAxis = (KDChartAxisParams::AxisPosLeft == basicPos) ||
(KDChartAxisParams::AxisPosRight == basicPos);
// store the coordinate number to be used for this axis
const int coordinate = bVertAxis ? 1 : 2;
// store whether our coordinates are double or TQDateTime values
const TQVariant::Type valueType =
(KDCHART_ALL_DATASETS == dataDataset)
? data.cellsValueType( coordinate )
: data.cellsValueType( dataDataset, dataDataset2, coordinate );
isDateTime = valueType == TQVariant::DateTime;
bool bIsDouble = valueType == TQVariant::Double;
autoDtLabels = isDateTime && ( KDCHART_AXIS_LABELS_AUTO_DATETIME_FORMAT
== para.axisLabelsDateTimeFormat() );
if( autoDtLabels || bSteadyCalc )
( ( KDChartAxisParams& ) para ).setAxisLabelsTouchEdges( true );
bool bStatistical = KDChartParams::HiLo == params_chartType
|| KDChartParams::BoxWhisker == params_chartType;
if ( !bVertAxis && KDChartParams::BoxWhisker == params_chartType
&& ! para.axisLabelStringCount() ) {
uint ds1 = (KDCHART_ALL_DATASETS == dataDataset)
? 0
: dataDataset;
uint ds2 = (KDCHART_ALL_DATASETS == dataDataset)
? data.usedRows() - 1
: dataDataset2;
for (uint i = ds1; i <= ds2; ++i)
labelTexts.append(
TQObject::tr( "Series " ) + TQString::number( i + 1 ) );
bDone = true;
}
double nLow = 1.0 + bSteadyCalc;// ? 0.0 : data.colsScrolledBy();
double nHigh = 10.0;
double nDelta = 1.0;
if ( !bDone ) {
bDone = true;
// look if exact label specification was made via limits and delta
if ( ! isLogarithmic
&& ! para.axisLabelStringCount()
&& ! ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueStart() )
&& ! ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd() )
&& ! ( para.axisValueStart() == para.axisValueEnd() )
&& ! ( KDCHART_AXIS_LABELS_AUTO_DELTA == para.axisValueDelta() )
&& ! ( 0.0 == para.axisValueDelta() ) ) {
nLow = para.axisValueStart().toDouble();
nHigh = para.axisValueEnd().toDouble();
nDelta = para.axisValueDelta();
int behindComma = para.axisDigitsBehindComma();
int trueBehindComma = -1;
bool upwards = (nLow < nHigh);
if( upwards != (0.0 < nDelta) )
nDelta *= -1.0;
double nVal = nLow;
bDone = false;
bool bShowVeryLastLabel = false;
//tqDebug("\n nLow: %f, nHigh: %f, nDelta: %f", nLow, nHigh, nDelta );
while( bShowVeryLastLabel || (upwards ? (nVal <= nHigh) : (nVal >= nHigh)) ){
//tqDebug("nVal : %f", nVal );
labelTexts.append( applyLabelsFormat( nVal,
divPow10,
behindComma,
nDelta,
trueBehindComma,
notation,
decimalPoint,
thousandsPoint,
prefix,
postfix,
totalLen,
padFill,
blockAlign ) );
nVal += nDelta;
//tqDebug("nVal-neu: %f", nVal );
if( ! (upwards ? (nVal <= nHigh) : (nVal >= nHigh)) ){
// work around a rounding error: show the last label, even if not nVal == nHigh is not matching exactly
if( bShowVeryLastLabel )
bShowVeryLastLabel = false;
else{
TQString sHigh( applyLabelsFormat( nHigh,
divPow10,
behindComma,
nDelta,
trueBehindComma,
notation,
decimalPoint,
thousandsPoint,
prefix,
postfix,
totalLen,
padFill,
blockAlign ) );
TQString sValue( applyLabelsFormat( nVal,
divPow10,
behindComma,
nDelta,
trueBehindComma,
notation,
decimalPoint,
thousandsPoint,
prefix,
postfix,
totalLen,
padFill,
blockAlign ) );
bShowVeryLastLabel = (sValue == sHigh);
//tqDebug("test: sHigh: "+sHigh+" sValue: "+sValue );
}
}
bDone = true;
}
( ( KDChartAxisParams& ) para ).setTrueAxisLowHighDelta( nLow, nHigh, nDelta );
//tqDebug("\n[Z-0] nLow: %f, nHigh: %f, nDelta: %f", nLow, nHigh, nDelta );
} // look if a string list was specified
else if ( para.axisLabelStringCount() ) {
int nLabels = bSteadyCalc
? para.axisLabelStringCount()
: bStatistical ? data.usedRows() : data.usedCols();
calculateBasicTextFactors( nTxtHeight, para, averageValueP1000,
basicPos, orig, delimLen, nLabels,
// start of return parameters
pDelimDelta,
pTextsX, pTextsY, pTextsW, pTextsH,
textAlign );
bool useShortLabels = false;
TQStringList tmpList( para.axisLabelStringList() );
// find start- and/or end-entry
int iStart = 0;
int iEnd = para.axisLabelStringCount() - 1;
if( ! ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueStart() )
|| ! ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd() ) ) {
const bool testStart = !( TQVariant::String == para.axisValueStart().type() );
const bool testEnd = !( TQVariant::String == para.axisValueEnd().type() );
TQString sStart = testStart
? para.axisValueStart().toString().upper()
: TQString();
TQString sEnd = testEnd
? para.axisValueEnd().toString().upper()
: TQString();
uint i = 0;
for ( TQStringList::Iterator it = tmpList.begin();
it != tmpList.end(); ++it, ++i ) {
if ( 0 == iStart &&
0 == TQString::compare( sStart, ( *it ).upper() ) ) {
iStart = i;
}
if ( 0 == TQString::compare( sEnd, ( *it ).upper() ) ) {
iEnd = i;
}
}
}
// check text widths to ensure all the entries will fit
// into the available space
if ( para.axisLabelStringCount()
&& para.axisShortLabelsStringCount()
&& para.axisLabelStringList() != para.axisShortLabelsStringList() ) {
TQFont font( para.axisLabelsFont() );
if ( para.axisLabelsFontUseRelSize() )
font.setPixelSize( static_cast < int > ( nTxtHeight ) );
painter->setFont( font );
TQFontMetrics fm( painter->fontMetrics() );
TQStringList::Iterator it = tmpList.begin();
for ( int i = 0; i < nLabels; ++i ) {
if ( it != tmpList.end() ) {
if ( fm.width( *it ) > pTextsW ) {
useShortLabels = true;
break;
}
++it;
}
}
}
if ( useShortLabels )
tmpList = para.axisShortLabelsStringList();
else
tmpList = para.axisLabelStringList();
// prepare transfering the strings into the labelTexts list
double ddelta
= ( KDCHART_AXIS_LABELS_AUTO_DELTA == para.axisValueDelta() )
? 1.0
: para.axisValueDelta();
modf( ddelta, &ddelta );
bool positive = ( 0.0 <= ddelta );
int delta = static_cast < int > ( fabs( ddelta ) );
// find 1st significant entry
TQStringList::Iterator it = positive
? tmpList.begin()
: tmpList.fromLast();
if ( positive )
for ( int i = 0; i < (int)tmpList.count(); ++i ) {
if ( i >= iStart )
break;
++it;
}
else
for ( int i = tmpList.count() - 1; i >= 0; --i ) {
if ( i <= iEnd )
break;
--it;
}
// transfer the strings
int meter = delta;
int i2 = positive ? iStart : iEnd;
for ( int iLabel = 0; iLabel < nLabels; ) {
if ( positive ) {
if ( it == tmpList.end() ) {
it = tmpList.begin();
i2 = 0;
}
} else {
if ( it == tmpList.begin() ) {
it = tmpList.end();
i2 = tmpList.count();
}
}
if ( ( positive && i2 >= iStart )
|| ( !positive && i2 <= iEnd ) ) {
if ( meter >= delta ) {
labelTexts << *it;
++iLabel;
meter = 1;
} else {
meter += 1;
}
}
if ( positive ) {
if ( i2 == iEnd ) {
it = tmpList.begin();
i2 = 0;
} else {
++it;
++i2;
}
} else {
if ( i2 == iStart ) {
it = tmpList.end();
i2 = tmpList.count();
} else {
--it;
--i2;
}
}
}
} else {
// find out if the associated dataset contains only strings
// if yes, we will take these as label texts
uint dset = ( dataset == KDCHART_ALL_DATASETS ) ? 0 : dataset;
//tqDebug("\ndset: %u", dset);
bDone = false;
TQVariant value;
for ( uint col = 0; col < data.usedCols(); ++col ) {
if( data.cellCoord( dset, col, value, coordinate ) ){
if( TQVariant::String == value.type() ){
const TQString sVal = value.toString();
if( !sVal.isEmpty() && !sVal.isNull() ){
labelTexts.append( sVal );
bDone = true;
}
}else{
labelTexts.clear();
bDone = false;
break;
}
}
}
}
}
if ( bDone ) {
// Some strings were found, now let us see which of them are
// actually to be taken right now.
//
//
// F E A T U R E P L A N N E D F O R F U T U R E . . .
//
//
}
else {
// No strings were found, so let us either calculate the texts
// based upon the numerical values of the associated dataset(s)
// or just compose some default texts...
if ( data.usedCols() && bSteadyCalc ) {
// double values for numerical coordinates
double nLow = 0.01;
double nHigh = 0.0;
double orgLow = 0.0;
double orgHigh = 0.0;
double nDelta = 0.0;
double nDist = 0.0;
// VERTICAL axes support three modes:
enum { Normal, Stacked, Percent } mode;
if( bVertAxis ){
switch ( params_chartType ) {
case KDChartParams::Bar:
if ( KDChartParams::BarStacked
== params.barChartSubType() )
mode = Stacked;
else if ( KDChartParams::BarPercent
== params.barChartSubType() )
mode = Percent;
else
mode = Normal;
break;
case KDChartParams::Line:
if ( KDChartParams::LineStacked
== params.lineChartSubType() )
mode = Stacked;
else if ( KDChartParams::LinePercent
== params.lineChartSubType() )
mode = Percent;
else
mode = Normal;
break;
case KDChartParams::Area:
if ( KDChartParams::AreaStacked
== params.areaChartSubType() )
mode = Stacked;
else if ( KDChartParams::AreaPercent
== params.areaChartSubType() )
mode = Percent;
else
mode = Normal;
break;
case KDChartParams::HiLo:
case KDChartParams::BoxWhisker:
mode = Normal;
break;
case KDChartParams::Polar:
if ( KDChartParams::PolarStacked
== params.polarChartSubType() )
mode = Stacked;
else if ( KDChartParams::PolarPercent
== params.polarChartSubType() )
mode = Percent;
else
mode = Normal;
break;
default: {
// Should not happen
tqDebug( "IMPLEMENTATION ERROR: Unknown params_chartType in calculateLabelTexts()" );
mode = Normal;
}
}
}else
mode = Normal; // this axis is not a vertical axis
uint nLabels = 200;
// find highest and lowest value of associated dataset(s)
bool bOrdFactorsOk = false;
if( adjustTheValues ){
nDelta = fabs( trueDelta );
pDelimDelta = trueDeltaPix;
nLow = TQMIN( para.trueAxisLow(), para.trueAxisHigh() );
//tqDebug("\nsearching: para.trueAxisLow() %f para.trueAxisHigh() %f",para.trueAxisLow(),para.trueAxisHigh());
double orgLow( nLow );
modf( nLow / nDelta, &nLow );
nLow *= nDelta;
if ( nLow > orgLow )
nLow -= nDelta;
if ( 0.0 < nLow && 0.0 >= orgLow )
nLow = 0.0;
nHigh = nLow;
double dx = fabs( pXDeltaFactor * pDelimDelta );
double dy = fabs( pYDeltaFactor * pDelimDelta );
double x = 0.0;
double y = 0.0;
nLabels = 1;
if( axisLength ){
do{
++nLabels;
nHigh += nDelta;
x += dx;
y += dy;
}while( x < axisLength && y < axisLength );
nHigh -= nDelta;
--nLabels;
}
nDist = nHigh - nLow;
bOrdFactorsOk = true;
}
if( !bOrdFactorsOk ){
const bool bAutoCalcStart =
( Percent != mode )
&& ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueStart() );
const bool bAutoCalcEnd =
( Percent != mode )
&& ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd() );
if( !bIsDouble && !isDateTime ){
// no data at all: let us use our default 0..10 range
nLow = 0.0;
nHigh = 10.0;
nDist = 10.0;
nDelta = 1.0;
nSubDelimFactor = 0.5;
bIsDouble = true;
bOrdFactorsOk = true;
}else if( mode == Percent ){
// precentage mode: we use a 0..100 range
nLow = 0.0;
nHigh = 100.0;
nDist = 100.0;
nDelta = 10.0;
nSubDelimFactor = 0.25;
bOrdFactorsOk = true;
}else{
//tqDebug("\ngo: nLow: %f nHigh: %f", nLow, nHigh );
// get the raw start value
const bool bStackedMode = (mode == Stacked);
if( bAutoCalcStart ){
if ( dataDataset == KDCHART_ALL_DATASETS ) {
if( bIsDouble ){
nLow = bStackedMode
? TQMIN( data.minColSum(), 0.0 )
: data.minValue( coordinate,
isLogarithmic );
//tqDebug("\n1: nLow: %f", nLow );
}else{
dtLow = data.minDtValue( coordinate );
}
} else {
if( bIsDouble ){
nLow = bStackedMode
? TQMIN( data.minColSum( dataDataset, dataDataset2 ),
0.0 )
: data.minInRows( dataDataset,dataDataset2,
coordinate,
isLogarithmic );
}else{
dtLow = data.minDtInRows( dataDataset,dataDataset2,
coordinate );
}
}
}else{
if( bIsDouble ){
if( TQVariant::Double == para.axisValueStart().type() )
nLow = para.axisValueStart().toDouble();
}else{
if( TQVariant::DateTime == para.axisValueStart().type() )
dtLow = para.axisValueStart().toDateTime();
}
}
// get the raw end value
if( bAutoCalcEnd ){
if ( dataDataset == KDCHART_ALL_DATASETS ) {
if( bIsDouble ){
nHigh = bStackedMode
? TQMAX( data.maxColSum(), 0.0 )
: data.maxValue( coordinate );
}else{
dtHigh = data.maxDtValue( coordinate );
}
} else {
if( bIsDouble )
nHigh = bStackedMode
? TQMAX( data.maxColSum( dataDataset, dataDataset2 ),
0.0 )
: data.maxInRows( dataDataset,dataDataset2,
coordinate );
else
dtHigh = data.maxDtInRows( dataDataset,dataDataset2,
coordinate );
}
//tqDebug("\nbAutoCalcEnd:\n nLow: %f\n nHigh: %f", nLow, nHigh );
}else{
if( bIsDouble ){
if( TQVariant::Double == para.axisValueEnd().type() )
nHigh = para.axisValueEnd().toDouble();
}else{
if( TQVariant::DateTime == para.axisValueEnd().type() )
dtHigh = para.axisValueEnd().toDateTime();
}
}
}
//tqDebug("\nnew: nLow: %f nHigh: %f", nLow, nHigh );
if( bIsDouble ) {
if( DBL_MAX == nLow
|| ( ( 0.0 == nHigh || 0 == nHigh )
&& ( 0.0 == nLow || 0 == nLow ) ) ) {
// tqDebug("NO values or all values have ZERO value, showing 0.0 - 10.0 span");
nLow = 0.0;
nHigh = 10.0;
nDist = 10.0;
nDelta = 1.0;
nSubDelimFactor = 0.5;
bOrdFactorsOk = true;
//tqDebug("nLow: %f, nHigh: %f", nLow, nHigh);
}else if( nLow == nHigh ){
// if both values are equal, but NOT Zero
// -> move the appropriate one to Zero
if( nLow < 0.0 )
nHigh = 0.0;
else
nLow = 0.0;
//tqDebug("nLow: %f, nHigh: %f", nLow, nHigh);
}else if( nHigh < nLow ){
// make sure nLow is <= nHigh
double nTmp = nLow;
nLow = nHigh;
nHigh = nTmp;
}
} else if( isDateTime ){
bool toggleDts = dtLow > dtHigh;
if( toggleDts ) {
TQDateTime dt( dtLow );
dtLow = dtHigh;
dtHigh = dt;
}
double secDist = dtLow.secsTo( dtHigh );
secDist += (dtHigh.time().msec() - dtLow.time().msec()) / 1000.0;
const double aDist = fabs( secDist );
const double secMin = 60.0;
const double secHour = 60.0 * secMin;
const double secDay = 24.0 * secHour;
//
// we temporarily disable week alignment until bug
// is fixed (1st week of year must not start in the
// preceeding year but rather be shown incompletely)
//
// (khz, 2003/10/10)
//
//const int secWeek = 7 * secDay;
const double secMonth = 30 * secDay; // approx.
const double secYear = 12 * secMonth; // approx.
if( 2.0*secMin > aDist )
dtDeltaScale = KDChartAxisParams::ValueScaleSecond;
else if( 2.0*secHour > aDist )
dtDeltaScale = KDChartAxisParams::ValueScaleMinute;
else if( 2.0*secDay > aDist )
dtDeltaScale = KDChartAxisParams::ValueScaleHour;
// khz: else if( 2*secWeek > aDist )
// khz: dtDeltaScale = KDChartAxisParams::ValueScaleDay;
else if( 2.0*secMonth > aDist )
dtDeltaScale = KDChartAxisParams::ValueScaleDay;
// khz: dtDeltaScale = KDChartAxisParams::ValueScaleWeek;
else if( 2.0*secYear > aDist )
dtDeltaScale = KDChartAxisParams::ValueScaleMonth;
else if( 10.0*secYear > aDist )
dtDeltaScale = KDChartAxisParams::ValueScaleQuarter;
else
dtDeltaScale = KDChartAxisParams::ValueScaleYear;
//const int yearLow = dtLow.date().year();
const int monthLow = dtLow.date().month();
// khz: currently unused: const int dowLow = dtLow.date().dayOfWeek();
const int dayLow = dtLow.date().day();
const int hourLow = dtLow.time().hour();
const int minuteLow = dtLow.time().minute();
const int secondLow = dtLow.time().second();
//const int yearHigh = dtHigh.date().year();
const int monthHigh = dtHigh.date().month();
// khz: currently unused: const int dowHigh = dtHigh.date().dayOfWeek();
const int hourHigh = dtHigh.time().hour();
const int minuteHigh = dtHigh.time().minute();
const int secondHigh = dtHigh.time().second();
int yearLowD = 0;
int monthLowD = 0;
int dayLowD = 0;
int hourLowD = 0;
int minuteLowD = 0;
int secondLowD = 0;
int yearHighD = 0;
int monthHighD = 0;
int dayHighD = 0;
int hourHighD = 0;
int minuteHighD = 0;
int secondHighD = 0;
bool gotoEndOfMonth = false;
switch( dtDeltaScale ) {
case KDChartAxisParams::ValueScaleSecond:
//tqDebug("\nKDChartAxisParams::ValueScaleSecond");
if( 5.0 < aDist ){
secondLowD = secondLow % 5;
if( secondHigh % 5 )
secondHighD = 5 - secondHigh % 5;
}
break;
case KDChartAxisParams::ValueScaleMinute:
//tqDebug("\nKDChartAxisParams::ValueScaleMinute");
secondLowD = secondLow;
secondHighD = 59-secondHigh;
break;
case KDChartAxisParams::ValueScaleHour:
//tqDebug("\nKDChartAxisParams::ValueScaleHour");
minuteLowD = minuteLow;
secondLowD = secondLow;
minuteHighD = 59-minuteHigh;
secondHighD = 59-secondHigh;
break;
case KDChartAxisParams::ValueScaleDay:
//tqDebug("\nKDChartAxisParams::ValueScaleDay");
hourLowD = hourLow;
minuteLowD = minuteLow;
secondLowD = secondLow;
hourHighD = 23-hourHigh;
minuteHighD = 59-minuteHigh;
secondHighD = 59-secondHigh;
break;
case KDChartAxisParams::ValueScaleWeek:
//tqDebug("\nKDChartAxisParams::ValueScaleWeek");
// khz: week scaling is disabled at the moment
/*
dayLowD = dowLow - 1;
hourLowD = hourLow;
minuteLowD = minuteLow;
secondLowD = secondLow;
if( 7 > dowHigh )
dayHighD = 7 - dowHigh + 1;
*/
break;
case KDChartAxisParams::ValueScaleMonth:
//tqDebug("\nKDChartAxisParams::ValueScaleMonth");
if( 1 < dayLow )
dayLowD = dayLow - 1;
hourLowD = hourLow;
minuteLowD = minuteLow;
secondLowD = secondLow;
gotoEndOfMonth = true;
break;
case KDChartAxisParams::ValueScaleQuarter:
//tqDebug("\nKDChartAxisParams::ValueScaleQuarter");
monthLowD = ( monthLow - 1 ) % 3;
dayLowD = dayLow;
hourLowD = hourLow;
minuteLowD = minuteLow;
secondLowD = secondLow;
if( ( monthHigh - 1 ) % 3 )
monthHighD = 3 - ( monthHigh - 1 ) % 3;
gotoEndOfMonth = true;
break;
case KDChartAxisParams::ValueScaleYear:
//tqDebug("\nKDChartAxisParams::ValueScaleYear");
monthLowD = monthLow;
dayLowD = dayLow;
hourLowD = hourLow;
minuteLowD = minuteLow;
secondLowD = secondLow;
if( 12 > monthHigh )
monthHighD = 12 - monthHigh;
gotoEndOfMonth = true;
break;
default:
/* NOOP */
break;
}
dtLow = dtLow.addSecs( -1 * (secondLowD + 60*minuteLowD + 3600*hourLowD) );
dtLow = dtLow.addDays( -1 * dayLowD );
dtAddMonths( dtLow, -1 * monthLowD, dtLow );
dtAddYears( dtLow, -1 * yearLowD, dtLow );
dtHigh = dtHigh.addSecs( secondHighD + 60*minuteHighD + 3600* hourHighD );
dtHigh = dtHigh.addDays( dayHighD );
dtAddMonths( dtHigh, monthHighD, dtHigh );
dtAddYears( dtHigh, yearHighD, dtHigh );
if( gotoEndOfMonth ){
dtHigh.setDate( TQDate( dtHigh.date().year(),
dtHigh.date().month(),
dtHigh.date().daysInMonth() ) );
dtHigh.setTime( TQTime( 23, 59, 59 ) );
}
if( toggleDts ) {
TQDateTime dt( dtLow );
dtLow = dtHigh;
dtHigh = dt;
}
// secDist = dtLow.secsTo( dtHigh );
// NOTE: nSubDelimFactor is not set here since it
// cannot be used for TQDateTime values.
nSubDelimFactor = 0.0;
bOrdFactorsOk = true;
}
if( !bOrdFactorsOk ) {
// adjust one or both of our limit values
// according to max-empty-inner-span settings
nDist = nHigh - nLow;
if( !isLogarithmic ){
// replace nLow (or nHigh, resp.) by zero if NOT ALL OF
// our values are located outside of the 'max. empty
// inner space' (i.e. percentage of the y-axis range
// that may to contain NO data entries)
int maxEmpty = para.axisMaxEmptyInnerSpan();
if( bAutoCalcStart ) {
//tqDebug("\nbAutoCalcStart:\n nLow: %f\n nHigh: %f", nLow, nHigh );
if( 0.0 < nLow ) {
if( maxEmpty == KDCHART_AXIS_IGNORE_EMPTY_INNER_SPAN
|| maxEmpty > ( nLow / nHigh * 100.0 ) )
nLow = 0.0;
else if( nDist / 100.0 < nLow )
nLow -= nDist / 100.0; // shift lowest value
}
else if( nDist / 100.0 < fabs( nLow ) )
nLow -= nDist / 100.0; // shift lowest value
nDist = nHigh - nLow;
//tqDebug("* nLow: %f\n nHigh: %f", nLow, nHigh );
}
if( bAutoCalcEnd ) {
//tqDebug("\nbAutoCalcEnd:\n nLow: %f\n nHigh: %f", nLow, nHigh );
if( 0.0 > nHigh ) {
if( maxEmpty == KDCHART_AXIS_IGNORE_EMPTY_INNER_SPAN
|| maxEmpty > ( nHigh / nLow * 100.0 ) )
nHigh = 0.0;
else if( nDist / 100.0 > nHigh )
nHigh += nDist / 100.0; // shift highest value
}
else if( nDist / 100.0 < fabs( nHigh ) )
nHigh += nDist / 100.0; // shift highest value
nDist = nHigh - nLow;
//tqDebug("* nLow: %f\n nHigh: %f\n\n", nLow, nHigh );
}
}
}
if( isLogarithmic ){
if( bIsDouble ) {
//tqDebug("\n[L--] nLow: %f, nHigh: %f, nDelta: %f", nLow, nHigh, nDelta );
if( 0.0 == TQABS( nLow ) )
nLow = -5;
else{
// find the Low / High values for the log. axis
nLow = log10( TQABS( nLow ) );
//if( 0.0 >= nLow ){
//nLow = fastPow10( -nLow );
//}
}
nHigh = log10( TQABS( nHigh ) );
//tqDebug("[L-0] nLow: %f, nHigh: %f", nLow, nHigh );
double intPart=0.0; // initialization necessary for Borland C++
double fractPart = modf( nLow, &intPart );
//tqDebug(" intPart: %f\nfractPart: %f", intPart, fractPart );
if( 0.0 > nLow && 0.0 != fractPart )
nLow = intPart - 1.0;
else
nLow = intPart;
fractPart = modf( nHigh, &intPart );
if( 0.0 != fractPart )
nHigh = intPart + 1.0;
nDist = nHigh - nLow;
nDelta = 1.0;
nSubDelimFactor = 0.1;
//tqDebug("\n[L-1] nLow: %f, nHigh: %f, nDelta: %f", nLow, nHigh, nDelta );
bOrdFactorsOk = true;
}
}
if ( !bOrdFactorsOk ) {
// adjust one or both of our limit values
// according to first two digits of (nHigh - nLow) delta
double nDivisor;
double nRound;
nDist = nHigh - nLow;
//tqDebug("* nLow: %f\n nHigh: %f nDist: %f\n\n", nLow, nHigh, nDist );
// find out factors and adjust nLow and nHigh
orgLow = nLow;
orgHigh = nHigh;
calculateOrdinateFactors( para, isLogarithmic,
nDist, nDivisor, nRound,
nDelta, nSubDelimFactor,
nLow, nHigh );
nLabels = params.roundVal( nDist / nDelta );
//tqDebug("\n0. nOrgHigh: %f\n nOrgLow: %f",
// orgHigh, orgLow);
//tqDebug("\n nDist: %f\n nHigh: %f\n nLow: %f",
// nDist, nHigh, nLow);
//tqDebug(" nDelta: %f", nDelta);
//tqDebug(" nRound: %f", nRound);
//tqDebug(" nLabels: %u", nLabels);
if( para.axisSteadyValueCalc() ) {
++nLabels;
//tqDebug("* nLabels: %u", nLabels );
}
}
// calculate the amount of nLabels to be written we could take
// based on the space we have for writing the label texts
if( ! ( KDCHART_AXIS_LABELS_AUTO_DELTA
== para.axisValueDelta() ) ){
nDist = nHigh - nLow;
nDelta = para.axisValueDelta();
nLabels = params.roundVal( nDist / nDelta );
//tqDebug("\nI nLow: %f\n nHigh: %f\n nDelta: %f\n nLabels: %u",
// nLow, nHigh, nDelta, nLabels );
if( para.axisSteadyValueCalc() ) {
++nLabels;
//tqDebug("* nLabels: %u", nLabels );
}
}
// make sure labels fit into available height, if vertical axis
if( bVertAxis ) {
//Pending Michel
//find out the width
const KDChartAxisParams & xpara = params.axisParams( KDChartAxisParams::AxisPosBottom );
double areaWidth = xpara.axisTrueAreaRect().width();
//make sure to avoid inf
double areaHeight = para.axisTrueAreaRect().height()>0?para.axisTrueAreaRect().height():1.0;
double widthHeight = areaWidth / areaHeight;
//tqDebug( "widthHeight %f, nDelta %f", widthHeight, nDelta);
//tqDebug( "maxValue %f", data.maxValue());
//tqDebug( "maxColSum %f", data.maxColSum());
//tqDebug( "axisValueEnd() %f", para.axisValueEnd().toDouble());
double nDivisor;
double nRound;
orgLow = nLow;
orgHigh = nHigh;
//check if there are axis limitation - if not (auto calculation):
//adjust the axis for 3dbars in order to display the whole top of the bar
//in relation to the with and the height of the area.
// add conditions for multirows here
if ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd().toDouble()) {
if (params.threeDBars() ) {
if ( KDChartParams::BarPercent != params.barChartSubType()) {
if ( widthHeight > 1.5 )
orgHigh += nDelta * widthHeight;
else
orgHigh += widthHeight * 0.5;
}
}
} else {
orgHigh = nHigh = para.axisValueEnd().toDouble();
}
//tqDebug("\ncalc ordinate 0. nDist: %f\n nLow: %f\n nHigh: %f\n nDelta: %f\n nLabels: %u", nDist, nLow, nHigh, nDelta, nLabels );
bool bTryNext = false;
uint minLabels = para.axisSteadyValueCalc() ? 3 : 2;
// the following must be processed at least twice - to avoid rounding errors
int pass = 0;
do{
nDist = nHigh - nLow;
nLow = orgLow;
nHigh = orgHigh;
/*
tqDebug("\n=============================================================================\ncalc ordinate 1. nDist: %f\n nLow: %f\n nHigh: %f\n nDelta: %f\n nLabels: %u",
nDist, nLow, nHigh, nDelta, nLabels );
*/
calculateOrdinateFactors( para, isLogarithmic,
nDist, nDivisor, nRound,
nDelta,
nSubDelimFactor, nLow, nHigh,
bTryNext );
nLabels = params.roundVal( nDist / nDelta );
//tqDebug("\ncalc ordinate 2. nDist: %f\n+ nLow: %f\n nHigh: %f\n nDelta: %f\n nLabels: %u",
//nDist, nLow, nHigh, nDelta, nLabels );
//TQString sDelta;sDelta.setNum( nDelta, 'f', 24 );
//TQString sLow; sLow.setNum( nLow, 'f', 24 );
//tqDebug("nLow: %f, sLow: %s, sDelta: %s", nLow, sLow.latin1(), sDelta.latin1());
// special case: End values was set by the user, but no Detla values was set.
if( !bAutoCalcEnd && orgHigh > nLow + nLabels * nDelta ) {
++nLabels;
//tqDebug("\nnLabels: %u\n", nLabels );
}
if( para.axisSteadyValueCalc() ) {
++nLabels;
//tqDebug("\nnLabels: %u\n", nLabels );
}
//tqDebug("calc ordinate n. nDist = nHigh - nLow: %f = %f - %f",nDist, nHigh, nLow);
//tqDebug(" nRound: %f\n", nRound);
bTryNext = true;
++pass;
}while ( ( pass < 2 )
|| ( ( minLabels < nLabels )
&& ( areaHeight < ( nTxtHeight * 1.5 ) * nLabels ) ) );
}
}
// finally we can build the texts
if( bIsDouble ) {
int trueBehindComma = -1;
double nVal = nLow;
for ( uint i = 0; i < nLabels; ++i ) {
if( isLogarithmic ) {
labelTexts.append( applyLabelsFormat(
fastPow10( static_cast < int > ( nVal ) ),
divPow10,
behindComma,
1.0 == nDelta ? KDCHART_AXIS_LABELS_AUTO_DELTA : nDelta,
trueBehindComma,
notation,
decimalPoint,
thousandsPoint,
prefix,
postfix,
totalLen,
padFill,
blockAlign ) );
} else {
labelTexts.append( applyLabelsFormat( nVal,
divPow10,
behindComma,
nDelta,
trueBehindComma,
notation,
decimalPoint,
thousandsPoint,
prefix,
postfix,
totalLen,
padFill,
blockAlign ) );
}
nVal += nDelta;
}
// save our true Low and High value
//tqDebug(para.axisSteadyValueCalc()?"\ntrue " : "\nfalse");
//tqDebug("nVal: %f, nDelta: %f", nVal, nDelta );
if ( para.axisSteadyValueCalc() ) {
nHigh = nVal - nDelta;
}
( ( KDChartAxisParams& ) para ).setTrueAxisLowHighDelta( nLow, nHigh, nDelta );
//tqDebug("[Z] nLow: %f, nHigh: %f, nDelta: %f", nLow, nHigh, nDelta );
} else {
bool goDown = dtLow > dtHigh;
int mult = goDown ? -1 : 1;
TQDateTime dt( dtLow );
nLabels = 0;
/*
tqDebug("dtLow: ");
tqDebug(dtLow.toString( Qt::ISODate ));
tqDebug("dtHigh: ");
tqDebug(dtHigh.toString( Qt::ISODate ));
*/
bool bDone=false;
while( !bDone ) {
/*
tqDebug("dtLow: %i %i %i %i:%i:%i",
dtLow.date().year(),
dtLow.date().month(),
dtLow.date().day(),
dtLow.time().hour(),
dtLow.time().minute(),
dtLow.time().second());
tqDebug("dtHigh: %i %i %i %i:%i:%i",
dtHigh.date().year(),
dtHigh.date().month(),
dtHigh.date().day(),
dtHigh.time().hour(),
dtHigh.time().minute(),
dtHigh.time().second());
tqDebug("dt: %i %i %i %i:%i:%i",
dt.date().year(),
dt.date().month(),
dt.date().day(),
dt.time().hour(),
dt.time().minute(),
dt.time().second());
*/
++nLabels;
if( autoDtLabels )
labelTexts.append( "x" );
else
#if COMPAT_TQT_VERSION >= 0x030000
labelTexts.append( dt.toString( Qt::ISODate ) );
#else
labelTexts.append( dateTimeToString( dt ) );
#endif
bDone = (goDown ? (dt < dtLow ) : (dt > dtHigh));
/*if( bDone ){
dtHigh = dt;
}else*/{
switch( dtDeltaScale ) {
case KDChartAxisParams::ValueScaleSecond:
dtAddSecs( dt, 1 * mult, dt );
break;
case KDChartAxisParams::ValueScaleMinute:
dtAddSecs( dt, 60 * mult, dt );
break;
case KDChartAxisParams::ValueScaleHour:
dtAddSecs( dt, 3600 * mult, dt );
break;
case KDChartAxisParams::ValueScaleDay:
dtAddDays( dt, 1 * mult, dt );
break;
case KDChartAxisParams::ValueScaleWeek:
dtAddDays( dt, 7 * mult, dt );
break;
case KDChartAxisParams::ValueScaleMonth:
dtAddMonths( dt,1 * mult, dt );
break;
case KDChartAxisParams::ValueScaleQuarter:
dtAddMonths( dt,3 * mult, dt );
break;
case KDChartAxisParams::ValueScaleYear:
dtAddYears( dt, 1 * mult, dt );
break;
default:
dtAddDays( dt, 1 * mult, dt );
break;
}
}
}
//if( autoDtLabels )
// labelTexts.append( "x" );
( ( KDChartAxisParams& ) para ).setTrueAxisDtLowHighDeltaScale(
dtLow, dtHigh,
dtDeltaScale );
// note: pDelimDelta will be calculated below,
// look for "COMMOM CALC OF NLABELS, DELIM DELTA..."
}
bDone = true;
}
// let's generate some strings
if ( !bDone ) {
// default scenario for abscissa axes
uint count = bStatistical
? (data.usedRows() ? data.usedRows() : 1)
: (data.usedCols() ? data.usedCols() : 1);
//double start( 1.0 );
double start( 1.0 + (bSteadyCalc ? 0.0 : static_cast < double >(data.colsScrolledBy())) );
//tqDebug("colsScrolledBy: %i", data.colsScrolledBy());
//if(bVertAxis)
//tqDebug("vert nVal starting: %f",start);
//else
//tqDebug("horz nVal starting: %f",start);
//if(bSteadyCalc)
//tqDebug("bSteadyCalc");
//else
//tqDebug("not bSteadyCalc");
double delta( 1.0 );
double finis( start + delta * ( count - 1 ) );
const bool startIsDouble = TQVariant::Double == para.axisValueStart().type();
const bool endIsDouble = TQVariant::Double == para.axisValueEnd().type();
bool deltaIsAuto = true;
if ( !( KDCHART_AXIS_LABELS_AUTO_DELTA == para.axisValueDelta() ) ) {
delta = para.axisValueDelta();
deltaIsAuto = false;
}
if ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueStart() ) {
if ( ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd() ) ) {
finis = start + delta * ( count - 1 );
} else {
if( endIsDouble ){
finis = para.axisValueEnd().toDouble();
start = finis - delta * ( count - 1 );
//tqDebug("1 changing: start: %f",start);
} else {
//
//
// F E A T U R E P L A N N E D F O R F U T U R E . . .
//
//
}
}
}else{
if ( startIsDouble ) {
start = para.axisValueStart().toDouble() + (bSteadyCalc ? 0.0 : static_cast < double >(data.colsScrolledBy()));
//tqDebug("2 changing: start: %f",start);
} else {
//
//
// F E A T U R E P L A N N E D F O R F U T U R E . . .
//
//
}
if ( !( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd() ) ) {
if (endIsDouble ) {
finis = para.axisValueEnd().toDouble();
if ( deltaIsAuto ) {
delta = ( finis - start ) / count;
} else {
count = static_cast < uint > (
( finis - start ) / delta );
}
} else {
// auto-rows like
// sunday, monday, tuesday, ...
//
//
// F E A T U R E P L A N N E D F O R F U T U R E . . .
//
//
}
}
else {
finis = start + delta * ( count - 1 );
}
}
TQString prefix( TQObject::tr( "Item " ) );
TQString postfix;
if ( startIsDouble && endIsDouble ) {
int precis =
KDCHART_AXIS_LABELS_AUTO_DIGITS == para.axisDigitsBehindComma()
? 0
: para.axisDigitsBehindComma();
double s = start;
double f = finis;
//tqDebug("label loop: s: %f f: %f",s,f);
bool up = ( 0.0 < delta );
// check the text widths of one large(?) entry
// and hope all the entries will
// fit into the available space
double value = up ? s : f;
uint nLabels = 0;
while ( up ? ( value <= f ) : ( value >= s ) ) {
++nLabels;
value += delta * up ? 1.0 : -1.0;
}
calculateBasicTextFactors( nTxtHeight, para,
averageValueP1000,
basicPos, orig, delimLen, nLabels,
// start of return parameters
pDelimDelta,
pTextsX, pTextsY, pTextsW, pTextsH,
textAlign );
TQFont font( para.axisLabelsFont() );
if ( para.axisLabelsFontUseRelSize() )
font.setPixelSize( static_cast < int > ( nTxtHeight ) );
painter->setFont( font );
TQFontMetrics fm( painter->fontMetrics() );
if ( fm.width( prefix +
TQString::number( -fabs( ( s + f ) / 2.0 + delta ),
'f', precis ) )
> pTextsW ) {
prefix = "";
postfix = "";
}
// now transfer the strings into labelTexts
value = up ? s : f;
while ( up ? ( value <= f ) : ( value >= s ) ) {
labelTexts.append(
prefix + TQString::number( value, 'f', precis )
+ postfix );
value += delta * up ? 1.0 : -1.0;
}
} else {
// pending(KHZ): make sure this branch will ever be reached
// check the text widths largest entry
// to make sure it will fit into the available space
calculateBasicTextFactors( nTxtHeight, para,
averageValueP1000,
basicPos, orig, delimLen,
count,
// start of return parameters
pDelimDelta,
pTextsX, pTextsY, pTextsW, pTextsH,
textAlign );
TQFont font( para.axisLabelsFont() );
if ( para.axisLabelsFontUseRelSize() )
font.setPixelSize( static_cast < int > ( nTxtHeight ) );
painter->setFont( font );
TQFontMetrics fm( painter->fontMetrics() );
if ( fm.width( prefix + TQString::number( count - 1 ) )
> pTextsW ) {
prefix = "";
postfix = "";
}
// now transfer the strings into labelTexts
for ( uint i = 1; i <= count; ++i )
labelTexts.append(
prefix + TQString::number( i ) + postfix );
}
}
}
/*
finishing: COMMOM CALC OF NLABELS, DELIM DELTA...
*/
uint nLabels = labelTexts.count()
? labelTexts.count()
: 0;
( ( KDChartAxisParams& ) para ).setAxisLabelTexts( &labelTexts );
if( !adjustTheValues ){
calculateBasicTextFactors( nTxtHeight, para, averageValueP1000,
basicPos, orig, delimLen, nLabels,
// start of return parameters
pDelimDelta,
pTextsX, pTextsY, pTextsW, pTextsH,
textAlign );
}
( ( KDChartAxisParams& ) para ).setTrueAxisDeltaPixels( pDelimDelta );
//tqDebug("\nsetting: para.trueAxisLow() %f para.trueAxisHigh() %f",para.trueAxisLow(),para.trueAxisHigh());
//tqDebug("\npDelimDelta: %f", pDelimDelta );
/*
tqDebug( "Found label texts:" );
for ( TQStringList::Iterator it = labelTexts.begin();
it != labelTexts.end(); ++it )
tqDebug( ">>> %s", (*it).latin1() );
tqDebug( "\n" );
*/
//tqDebug("\nleaving KDChartAxesPainter::calculateLabelTexts() : nTxtHeight: "+TQString::number(nTxtHeight));
}
/**
Calculates some label text factors needed
by function \c calculateLabelTexts()
\note When calling this function the actual area size for this
axis must be set, this means you may only call it when
\c KDChartPainter::setupGeometry() has been called before.
\param nTxtHeight the text height to be used for calculating
the return values
\param para the KDChartAxisParams that were specified for this axis
\param averageValueP1000 (average height+width of the prtbl. area) / 1000
\param basicPos the basic axis position returned by
KDChartAxisParams::basicAxisPos()
\param orig the axis start point
\param delimLen the length of one delimiter mark
\param nLabels the number of labels to be shown at this axis
\param (all others) the reference parameters to be returned
by this function
*/
/**** static ****/
void KDChartAxesPainter::calculateBasicTextFactors( double nTxtHeight,
const KDChartAxisParams& para,
double /*averageValueP1000*/,
KDChartAxisParams::AxisPos basicPos,
const TQPoint& orig,
double delimLen,
uint nLabels,
// start of return params
double& pDelimDelta,
double& pTextsX,
double& pTextsY,
double& pTextsW,
double& pTextsH,
int& textAlign )
{
switch ( basicPos ) {
case KDChartAxisParams::AxisPosBottom: {
bool bTouchEdges = para.axisLabelsTouchEdges();
double wid = para.axisTrueAreaRect().width();
double divi = bTouchEdges
? ( 1 < nLabels ? nLabels - 1 : 1 )
: ( nLabels ? nLabels : 10 );
pDelimDelta = wid / divi;
pTextsW = pDelimDelta - 4.0;
pTextsX = orig.x() + 2.0
- ( bTouchEdges
? pDelimDelta / 2.0
: 0.0 );
pTextsH = para.axisTrueAreaRect().height() - delimLen * 1.33;
pTextsY = orig.y()
+ delimLen * 1.33;
textAlign = TQt::AlignHCenter | TQt::AlignTop;
/*
tqDebug("pTextsW %f wid %f nLabels %u", pTextsW, wid, nLabels );
*/
}
break;
case KDChartAxisParams::AxisPosLeft: {
double hig = para.axisTrueAreaRect().height();
pDelimDelta = hig / ( 1 < nLabels ? nLabels - 1 : 1 );
pTextsX = para.axisTrueAreaRect().bottomLeft().x()
+ 2.0;
pTextsY = orig.y() - nTxtHeight / 2;
pTextsW = para.axisTrueAreaRect().width()
- delimLen * 1.33 - 2.0;
pTextsH = nTxtHeight;
textAlign = TQt::AlignRight | TQt::AlignVCenter;
}
break;
case KDChartAxisParams::AxisPosTop: {
bool bTouchEdges = para.axisLabelsTouchEdges();
double wid = para.axisTrueAreaRect().width();
double divi = bTouchEdges
? ( 1 < nLabels ? nLabels - 1 : 1 )
: ( nLabels ? nLabels : 10 );
pDelimDelta = wid / divi;
pTextsW = pDelimDelta - 4.0;
pDelimDelta = wid / divi;
pTextsX = orig.x() + 2.0
- ( bTouchEdges
? pDelimDelta / 2.0
: 0.0 );
pTextsH = para.axisTrueAreaRect().height() - delimLen * 1.33;
pTextsY = para.axisTrueAreaRect().topLeft().y();
textAlign = TQt::AlignHCenter | TQt::AlignBottom;
}
break;
case KDChartAxisParams::AxisPosRight: {
double hig = para.axisTrueAreaRect().height();
pDelimDelta = hig / ( 1 < nLabels ? nLabels - 1 : 1 );
pTextsX = para.axisTrueAreaRect().bottomLeft().x()
+ delimLen * 1.33;
pTextsY = orig.y() - nTxtHeight / 2;
pTextsW = para.axisTrueAreaRect().width()
- delimLen * 1.33 - 2.0;
pTextsH = nTxtHeight;
textAlign = TQt::AlignLeft | TQt::AlignVCenter;
}
break;
default: {
tqDebug( "IMPLEMENTATION ERROR: KDChartAxesPainter::calculateBasicTextFactors() unhandled enum value." );
// NOOP since the 'basicPos' does not support more that these four values.
}
break;
}
}
/**
Takes double \c nVal and returns a TQString showing the amount of digits
behind the comma that was specified by \c behindComma (or calculated
automatically by removing trailing zeroes, resp.).
To make sure the resulting string looks fine together with other strings
of the same label row please specify \c nDelta indicating the step width
from one label text to the other.
To prevent the function from having to re-calculate the number of
digits to keep behind the comma, provide it with a temporary helper
variable "trueBehindComma" that has to be initialized with a value
smaller than zero.
\note This function is reserved for internal use.
*/
TQString KDChartAxesPainter::truncateBehindComma( const double nVal,
const int behindComma,
const double nDelta,
int& trueBehindComma )
{
const int nTrustedPrecision = 6; // when using 15 we got 1.850000 rounded to 1.849999999999999
const bool bUseAutoDigits = KDCHART_AXIS_LABELS_AUTO_DIGITS == behindComma;
const bool bAutoDelta = KDCHART_AXIS_LABELS_AUTO_DELTA == nDelta;
TQString sVal;
sVal.setNum( nVal, 'f', bUseAutoDigits ? nTrustedPrecision
: TQMIN(behindComma, nTrustedPrecision) );
//tqDebug("nVal: %f sVal: "+sVal, nVal );
//tqDebug( TQString(" %1").arg(sVal));
if ( bUseAutoDigits ) {
int comma = sVal.find( '.' );
if ( -1 < comma ) {
if ( bAutoDelta ) {
int i = sVal.length();
while ( 1 < i
&& '0' == sVal[ i - 1 ] )
--i;
sVal.truncate( i );
if ( '.' == sVal[ i - 1 ] )
sVal.truncate( i - 1 );
} else {
if ( 0 > trueBehindComma ) {
TQString sDelta = TQString::number( nDelta, 'f', nTrustedPrecision );
int i = sDelta.length();
while ( 1 < i
&& '0' == sDelta[ i - 1 ] )
--i;
sDelta.truncate( i );
if ( '.' == sDelta[ i - 1 ] )
trueBehindComma = 0;
else {
int deltaComma = sDelta.find( '.' );
if ( -1 < deltaComma )
trueBehindComma = sDelta.length() - deltaComma - 1;
else
trueBehindComma = 0;
}
}
// now we cut off the too-many digits behind the comma
int nPos = comma + ( trueBehindComma ? trueBehindComma + 1 : 0 );
sVal.truncate( nPos );
}
}
}
//tqDebug( TQString(" - %1").arg(trueBehindComma));
return sVal;
}
#if 0
#if defined ( TQ_WS_WIN)
#define trunc(x) ((int)(x))
#endif
#else
// Ugly hack because Solaris (among others?) doesn't have trunc(),
// since the libs are not C99. I could do a complex #if expression,
// but instead I will just define trunc() here.
double trunc(double x)
{
return x >= 0.0 ? floor(x) : -floor(-x);
}
#endif
TQString KDChartAxesPainter::applyLabelsFormat( const double nVal_,
int divPow10,
int behindComma,
double nDelta_,
int& trueBehindComma,
KDChartEnums::NumberNotation notation,
const TQString& decimalPoint,
const TQString& thousandsPoint,
const TQString& prefix,
const TQString& postfix,
int totalLen,
const TQChar& padFill,
bool blockAlign )
{
double nVal = nVal_ / fastPow10( divPow10 );
double nDelta = nDelta_;
double valLog10 = 0.0;
if( notation == KDChartEnums::NumberNotationScientific ||
notation == KDChartEnums::NumberNotationScientificBig ){
valLog10 = (nVal != 0.0) ? trunc( log10(TQABS(nVal)) ) : 0.0;
//tqDebug("nVal old: %f valLog10: %f",nVal,valLog10);
nVal /= fastPow10( valLog10 );
nDelta /= fastPow10( valLog10 );
//tqDebug("nVal new: %f",nVal);
}
TQString sVal = truncateBehindComma( nVal,
behindComma,
nDelta,
trueBehindComma );
//tqDebug("sVal : "+sVal+" behindComma: %i",behindComma);
int posComma = sVal.find( '.' );
if( 0 <= posComma ){
sVal.replace( posComma, 1, decimalPoint);
}else{
posComma = sVal.length();
}
if( notation == KDChartEnums::NumberNotationScientific ||
notation == KDChartEnums::NumberNotationScientificBig ){
if( notation == KDChartEnums::NumberNotationScientific )
sVal.append( "e" );
else
sVal.append( "E" );
sVal.append( TQString::number( valLog10, 'f', 0 ) );
} else {
if( thousandsPoint.length() ){
const int minLen = (0 < sVal.length() && '-' == sVal[0])
? 4
: 3;
int n = posComma; // number of digits at the left side of the comma
while( minLen < n ){
n -= 3;
sVal.insert(n, thousandsPoint);
}
}
}
sVal.append( postfix );
int nFill = totalLen - (sVal.length() + prefix.length());
if( 0 > nFill )
nFill = 0;
if( !blockAlign )
sVal.prepend( prefix );
for(int i=0; i < nFill; ++i)
sVal.prepend( padFill );
if( blockAlign )
sVal.prepend( prefix );
if ( totalLen > 0 )
sVal.truncate( totalLen );
/*Pending Michel: Force non fractional values
*In case it is a fractional value
*and the user has set axisLabelsDigitsBehindComma() == 0
*return an empty string
*/
if ( behindComma == 0 && TQString::number(nVal).find('.') > 0 )
sVal = TQString();//sVal = "";
return sVal;
}
/**
Calculates the factors to be used for calculating ordinate labels texts.
\note This function is reserved for internal use.
*/
void KDChartAxesPainter::calculateOrdinateFactors(
const KDChartAxisParams& para,
bool isLogarithmic,
double& nDist,
double& nDivisor,
double& nRound,
double& nDelta,
double& nSubDelimFactor,
double& nLow,
double& nHigh,
bool findNextRound )
{
if ( findNextRound ) {
if ( 1.0 > nRound )
nRound = 1.0;
else
if ( 2.0 > nRound )
nRound = 2.0;
else
if ( 2.5 > nRound )
nRound = 2.5;
else
if ( 5.0 > nRound )
nRound = 5.0;
else {
nDivisor *= 10.0;
nRound = 1.0;
}
} else {
nDivisor = 1.0;
TQString sDistDigis2;
sDistDigis2.setNum( nDist, 'f', 24);
if ( 1.0 > nDist ) {
sDistDigis2.remove( 0, 2 );
nDivisor = 0.01;
while ( 0 < sDistDigis2.length()
&& '0' == sDistDigis2[ 0 ] ) {
nDivisor *= 0.1;
sDistDigis2.remove( 0, 1 );
}
} else {
if ( 10.0 > nDist ) {
nDivisor = 0.1;
// remove comma, if present
sDistDigis2.remove( 1, 1 );
} else
if ( 100.0 > nDist )
nDivisor = 1.0;
else {
int comma = sDistDigis2.find( '.' );
if ( -1 < comma )
sDistDigis2.truncate( comma );
nDivisor = fastPow10( (int)sDistDigis2.length() - 2 );
}
}
sDistDigis2.truncate( 2 );
bool bOk;
double nDistDigis2( sDistDigis2.toDouble( &bOk ) );
if ( !bOk )
nDistDigis2 = 10.0;
if ( 75.0 <= nDistDigis2 )
nRound = 5.0;
else
if ( 40.0 <= nDistDigis2 )
nRound = 2.5;
else
if ( 20.0 <= nDistDigis2 )
nRound = 2.0;
else
nRound = 1.0;
}
nDelta = nRound * nDivisor;
// make sure its a whole number > 0 if its a log axis. Just round up.
if( isLogarithmic )
nDelta = static_cast < int > ( nDelta ) < nDelta
? static_cast < int > ( nDelta ) + 1
: static_cast < int > ( nDelta );
bool bInvertedAxis = ( 0.0 > nDist );
if( bInvertedAxis )
nDelta *= -1.0;
/*
tqDebug(" n D i s t : %f", nDist );
tqDebug(" n D i v i s o r : %f", nDivisor);
tqDebug(" n R o u n d : %f", nRound );
tqDebug(" n D e l t a : %f", nDelta );
tqDebug(" nHigh : %f", nHigh );
tqDebug(" nLow : %f", nLow );
*/
if( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueStart()
|| !para.axisValueStartIsExact() ) {
double orgLow( nLow );
modf( nLow / nDelta, &nLow );
nLow *= nDelta;
if( bInvertedAxis ){
if ( nLow < orgLow )
nLow += nDelta;
if ( 0.0 > nLow && 0.0 <= orgLow )
nLow = 0.0;
}else{
if ( nLow > orgLow )
nLow -= nDelta;
if ( 0.0 < nLow && 0.0 >= orgLow )
nLow = 0.0;
}
}
if ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd() ) {
double orgHigh( nHigh );
modf( nHigh / nDelta, &nHigh );
nHigh *= nDelta;
if( bInvertedAxis ){
if ( nHigh > orgHigh )
nHigh -= nDelta;
if ( 0.0 < nHigh && 0.0 >= orgHigh )
nHigh = 0.0;
}else{
if ( nHigh < orgHigh )
nHigh += nDelta;
if ( 0.0 > nHigh && 0.0 <= orgHigh )
nHigh = 0.0;
}
}
//tqDebug(" n H i g h : %f", nHigh );
//tqDebug(" n L o w : %f\n\n", nLow );
if ( 1.0 == nRound )
nSubDelimFactor = 0.5;
else
if ( 2.0 == nRound )
nSubDelimFactor = 0.25;
else
if ( 2.5 == nRound )
nSubDelimFactor = 0.2;
else
if ( 5.0 == nRound )
nSubDelimFactor = 0.2;
else {
// Should not happen
tqDebug( "IMPLEMENTATION ERROR: Unknown nRound in calculateOrdinateFactors()" );
nSubDelimFactor = 1.0;
}
nDist = nHigh - nLow;
}
/**** static ****/
void KDChartAxesPainter::saveDrawLine( TQPainter& painter,
TQPoint pA,
TQPoint pZ,
TQPen pen )
{
const TQPen oldPen( painter.pen() );
bool bNice = ( pen.color() == oldPen.color() )
&& ( pen.width() == oldPen.width() )
&& ( pen.style() == oldPen.style() );
if ( !bNice )
painter.setPen( pen );
painter.drawLine( pA, pZ );
if ( !bNice )
painter.setPen( oldPen );
}
/**** static ****/
void KDChartAxesPainter::dtAddSecs( const TQDateTime& org, const int secs, TQDateTime& dest )
{
//tqDebug("entering KDChartAxesPainter::dtAddSecs() ..");
int s = org.time().second();
int m = org.time().minute();
int h = org.time().hour();
int days = 0;
if( -1 < secs ){
int mins = (s + secs) / 60;
if( 0 == mins )
s += secs;
else{
s = (s + secs) % 60;
int hours = (m + mins) / 60;
if( 0 == hours )
m += mins;
else{
m = (m + mins) % 60;
days = (h + hours) / 24;
if( 0 == days )
h += hours;
else{
h = (h + hours) % 24;
}
}
}
}
dest.setTime( TQTime(h,m,s) );
dest.setDate( org.date() );
if( days )
dtAddDays( dest, days, dest );
//tqDebug(".. KDChartAxesPainter::dtAddSecs() done.");
}
/**** static ****/
void KDChartAxesPainter::dtAddDays( const TQDateTime& org, const int days, TQDateTime& dest )
{
//tqDebug("entering KDChartAxesPainter::dtAddDays() ..");
int d = org.date().day();
int m = org.date().month();
int y = org.date().year();
int dd = (-1 < days) ? 1 : -1;
int di = 0;
while( di != days ){
d += dd;
// underrunning day?
if( 1 > d ){
if( 1 < m ){
--m;
d = TQDate( y,m,1 ).daysInMonth();
}
else{
--y;
m = 12;
d = 31;
}
// overrunning day?
}else if( TQDate( y,m,1 ).daysInMonth() < d ){
if( 12 > m )
++m;
else{
++y;
m = 1;
}
d = 1;
}
di += dd;
}
dest = TQDateTime( TQDate( y,m,d ), org.time() );
//tqDebug(".. KDChartAxesPainter::dtAddDays() done.");
}
/**** static ****/
void KDChartAxesPainter::dtAddMonths( const TQDateTime& org, const int months, TQDateTime& dest )
{
//tqDebug("entering KDChartAxesPainter::dtAddMonths() ..");
int d = org.date().day();
int m = org.date().month();
int y = org.date().year();
int md = (-1 < months) ? 1 : -1;
int mi = 0;
while( mi != months ){
m += md;
if( 1 > m ){
--y;
m = 12;
}else if( 12 < m ){
++y;
m = 1;
}
mi += md;
}
// TQMIN takes care for intercalary day
dest = TQDateTime( TQDate( y,m,TQMIN( d, TQDate( y,m,1 ).daysInMonth() ) ),
org.time() );
//tqDebug(".. KDChartAxesPainter::dtAddMonths() done.");
}
/**** static ****/
void KDChartAxesPainter::dtAddYears( const TQDateTime& org, const int years, TQDateTime& dest )
{
//tqDebug("entering KDChartAxesPainter::dtAddYears() ..");
int d = org.date().day();
int m = org.date().month();
int y = org.date().year() + years;
dest.setTime( org.time() );
// TQMIN takes care for intercalary day
dest = TQDateTime( TQDate( y,m,TQMIN( d, TQDate( y,m,d ).daysInMonth() ) ),
org.time() );
//tqDebug(".. KDChartAxesPainter::dtAddYears() done.");
}
void KDChartAxesPainter::calculateAbscissaInfos( const KDChartParams& params,
const KDChartTableDataBase& data,
uint datasetStart,
uint datasetEnd,
double logWidth,
const TQRect& dataRect,
abscissaInfos& infos )
{
if( params.axisParams( KDChartAxisParams::AxisPosBottom ).axisVisible()
&& ( KDChartAxisParams::AxisTypeUnknown
!= params.axisParams( KDChartAxisParams::AxisPosBottom ).axisType() ) )
infos.abscissaPara = &params.axisParams( KDChartAxisParams::AxisPosBottom );
else
if( params.axisParams( KDChartAxisParams::AxisPosBottom2 ).axisVisible()
&& ( KDChartAxisParams::AxisTypeUnknown
!= params.axisParams( KDChartAxisParams::AxisPosBottom2 ).axisType() ) )
infos.abscissaPara = &params.axisParams( KDChartAxisParams::AxisPosBottom2 );
else
if( params.axisParams( KDChartAxisParams::AxisPosTop ).axisVisible()
&& ( KDChartAxisParams::AxisTypeUnknown
!= params.axisParams( KDChartAxisParams::AxisPosTop ).axisType() ) )
infos.abscissaPara = &params.axisParams( KDChartAxisParams::AxisPosTop );
else
if( params.axisParams( KDChartAxisParams::AxisPosTop2 ).axisVisible()
&& ( KDChartAxisParams::AxisTypeUnknown
!= params.axisParams( KDChartAxisParams::AxisPosTop2 ).axisType() ) )
infos.abscissaPara = &params.axisParams( KDChartAxisParams::AxisPosTop2 );
else
// default is bottom axis:
infos.abscissaPara = &params.axisParams( KDChartAxisParams::AxisPosBottom );
if( infos.abscissaPara->axisLabelsTouchEdges() )
infos.bCenterThePoints = false;
infos.bAbscissaDecreasing = infos.abscissaPara->axisValuesDecreasing();
infos.bAbscissaIsLogarithmic
= KDChartAxisParams::AxisCalcLogarithmic == infos.abscissaPara->axisCalcMode();
// Number of values: If -1, use all values, otherwise use the
// specified number of values.
infos.numValues = 0;
if ( params.numValues() > -1 )
infos.numValues = params.numValues();
else
infos.numValues = data.usedCols();
TQVariant::Type type2Ref = TQVariant::Invalid;
infos.bCellsHaveSeveralCoordinates =
data.cellsHaveSeveralCoordinates( datasetStart, datasetEnd,
&type2Ref );
infos.numLabels = (infos.abscissaPara &&
infos.abscissaPara->axisLabelTexts())
? infos.abscissaPara->axisLabelTexts()->count()
: infos.numValues;
if( 0 >= infos.numLabels )
infos.numLabels = 1;
infos.bAbscissaHasTrueAxisValues =
infos.abscissaPara && (0.0 != infos.abscissaPara->trueAxisDelta());
infos.abscissaStart = infos.bAbscissaHasTrueAxisValues
? infos.abscissaPara->trueAxisLow()
: 0.0;
infos.abscissaEnd = infos.bAbscissaHasTrueAxisValues
? infos.abscissaPara->trueAxisHigh()
: 1.0 * (infos.numLabels - 1);
infos.abscissaSpan = fabs( infos.abscissaEnd - infos.abscissaStart );
infos.abscissaDelta = infos.bAbscissaHasTrueAxisValues
? infos.abscissaPara->trueAxisDelta()
: ( ( 0.0 != infos.abscissaSpan )
? ( infos.abscissaSpan / infos.numLabels )
: infos.abscissaSpan );
//tqDebug( bAbscissaDecreasing ? "bAbscissaDecreasing = TRUE" : "bAbscissaDecreasing = FALSE");
//tqDebug( abscissaHasTrueAxisValues ? "abscissaHasTrueAxisValues = TRUE" : "abscissaHasTrueAxisValues = FALSE");
//tqDebug( "abscissaDelta = %f", abscissaDelta);
infos.bAbscissaHasTrueAxisDtValues =
(TQVariant::DateTime == type2Ref) &&
infos.abscissaPara &&
infos.abscissaPara->trueAxisDtLow().isValid();
if( infos.bAbscissaHasTrueAxisDtValues ){
infos.numLabels = 200;
infos.bCenterThePoints = false;
}
infos.dtLowPos = infos.bAbscissaHasTrueAxisDtValues
? infos.abscissaPara->axisDtLowPosX() - dataRect.x()
: 0.0;
infos.dtHighPos = infos.bAbscissaHasTrueAxisDtValues
? infos.abscissaPara->axisDtHighPosX() - dataRect.x()
: logWidth;
infos.abscissaDtStart = infos.bAbscissaHasTrueAxisDtValues
? infos.abscissaPara->trueAxisDtLow()
: TQDateTime();
infos.abscissaDtEnd = infos.bAbscissaHasTrueAxisDtValues
? infos.abscissaPara->trueAxisDtHigh()
: TQDateTime();
//Pending Michel case when same hh:mm:ss but different msecs
infos.bScaleLessThanDay = ( infos.bAbscissaHasTrueAxisDtValues
? infos.abscissaPara->trueAxisDtDeltaScale()
: KDChartAxisParams::ValueScaleDay )
< KDChartAxisParams::ValueScaleDay;
if ( infos.abscissaDtStart.time() == infos.abscissaDtEnd.time() && infos.bScaleLessThanDay )
infos.dtHighPos = logWidth;
// adjust the milli seconds:
infos.abscissaDtStart.setTime(
TQTime( infos.abscissaDtStart.time().hour(),
infos.abscissaDtStart.time().minute(),
infos.abscissaDtStart.time().second(),
0 ) );
infos.abscissaDtEnd.setTime(
TQTime( infos.abscissaDtEnd.time().hour(),
infos.abscissaDtEnd.time().minute(),
infos.abscissaDtEnd.time().second(),
999 ) );
//tqDebug( infos.abscissaPara->trueAxisDtLow().toString("yyyy-MM-dd-hh:mm:ss.zzz"));
//tqDebug( infos.abscissaPara->trueAxisDtHigh().toString("yyyy-MM-dd-hh:mm:ss.zzz"));
//tqDebug(infos.abscissaDtStart.toString("yyyy-MM-dd-hh:mm:ss.zzz"));
//tqDebug(infos.abscissaDtEnd.toString("yyyy-MM-dd-hh:mm:ss.zzz"));
/*
infos.bScaleLessThanDay = ( infos.bAbscissaHasTrueAxisDtValues
? infos.abscissaPara->trueAxisDtDeltaScale()
: KDChartAxisParams::ValueScaleDay )
< KDChartAxisParams::ValueScaleDay;
*/
if( infos.bAbscissaHasTrueAxisDtValues ){
if( infos.bScaleLessThanDay ){
infos.abscissaDtSpan = infos.abscissaDtStart.secsTo( infos.abscissaDtEnd );
/* NOTE: We do *not* add the milli seconds because they aren't covered
by the span indicated by infos.dtHighPos - infos.dtLowPos.
if( infos.abscissaDtStart.time().msec() || infos.abscissaDtEnd.time().msec() )
infos.abscissaDtSpan =
( infos.abscissaDtEnd.time().msec() -
infos.abscissaDtStart.time().msec() ) / 1000.0;
*/
}
else{
infos.abscissaDtSpan = infos.abscissaDtStart.daysTo( infos.abscissaDtEnd );
if( infos.abscissaDtStart.time().msec() || infos.abscissaDtEnd.time().msec() )
infos.abscissaDtSpan +=
( infos.abscissaDtEnd.time().msec() -
infos.abscissaDtStart.time().msec() ) / (86400.0 * 1000.0);
if( infos.abscissaDtEnd.time().second() )
infos.abscissaDtSpan += infos.abscissaDtEnd.time().second() / 86400.0;
if( infos.abscissaDtEnd.time().minute() )
infos.abscissaDtSpan += infos.abscissaDtEnd.time().minute() / 1440.0;
if( infos.abscissaDtEnd.time().hour() )
infos.abscissaDtSpan += infos.abscissaDtEnd.time().hour() / 24.0;
}
}else
infos.abscissaDtSpan = 10.0;
if( 0 == infos.abscissaDtSpan || 0.0 == infos.abscissaDtSpan )
infos.abscissaDtSpan = 1.0;
infos.abscissaDtPixelsPerScaleUnit = (infos.dtHighPos - infos.dtLowPos)/ infos.abscissaDtSpan;
if( infos.bAbscissaHasTrueAxisDtValues )
infos.abscissaDelta = 20.0;
infos.pointDist
= ( infos.abscissaPara && (0.0 != infos.abscissaPara->trueAxisDeltaPixels()) )
? infos.abscissaPara->trueAxisDeltaPixels()
: ( logWidth /
(
(1 > ((double)(infos.numLabels) - (infos.bCenterThePoints ? 0.0 : 1.0)))
? ((double)(infos.numLabels) - (infos.bCenterThePoints ? 0.0 : 1.0))
: 1 ) );
infos.abscissaPixelsPerUnit = ( 0.0 != infos.abscissaDelta )
? ( infos.pointDist / infos.abscissaDelta )
: infos.pointDist;
//const double infos.abscissaZeroPos2 = -1.0 * infos.abscissaPixelsPerUnit * infos.abscissaStart;
infos.abscissaZeroPos = infos.abscissaPara->axisZeroLineStartX() - dataRect.x();
//tqDebug("abscissaZeroPos %f abscissaZeroPos2 %f",abscissaZeroPos,abscissaZeroPos2);
/*
tqDebug(abscissaPara ?
"\nabscissaPara: OK" :
"\nabscissaPara: leer");
tqDebug(abscissaHasTrueAxisValues ?
"abscissaHasTrueAxisValues: TRUE" :
"abscissaHasTrueAxisValues: FALSE");
tqDebug("abscissaStart: %f", abscissaStart);
tqDebug("abscissaEnd : %f", abscissaEnd);
tqDebug("abscissaPara->trueAxisDelta(): %f", abscissaPara->trueAxisDelta());
tqDebug("numValues : %u, numLabels : %u", numValues, numLabels);
*/
}
bool KDChartAxesPainter::calculateAbscissaAxisValue( const TQVariant& value,
abscissaInfos& ai,
int colNumber,
double& xValue )
{
if( ai.bCellsHaveSeveralCoordinates ) {
if( TQVariant::Double == value.type() ) {
double dVal = value.toDouble();
if( ai.bAbscissaIsLogarithmic ){
if( 0.0 < dVal )
xValue = ai.abscissaPixelsPerUnit * log10( dVal );
else
xValue = -10250.0;
}else{
xValue = ai.abscissaPixelsPerUnit * dVal;
}
xValue *= ai.bAbscissaDecreasing ? -1.0 : 1.0;
xValue += ai.abscissaZeroPos;
}
else if( ai.bAbscissaHasTrueAxisDtValues &&
TQVariant::DateTime == value.type() ) {
const TQDateTime dtVal = value.toDateTime();
double dT = ( ai.bScaleLessThanDay )
? ai.abscissaDtStart.secsTo( dtVal )
: ai.abscissaDtStart.daysTo( dtVal );
/*
tqDebug("abscissaDtStart: %i %i %i %i:%i:%i.%i",
ai.abscissaDtStart.date().year(),
ai.abscissaDtStart.date().month(),
ai.abscissaDtStart.date().day(),
ai.abscissaDtStart.time().hour(),
ai.abscissaDtStart.time().minute(),
ai.abscissaDtStart.time().second(),
ai.abscissaDtStart.time().msec());
*/
//tqDebug("days to = %f",dT);
/*
tqDebug(" dtVal: %i %i %i %i:%i:%i.%i",
dtVal.date().year(),
dtVal.date().month(),
dtVal.date().day(),
dtVal.time().hour(),
dtVal.time().minute(),
dtVal.time().second(),
dtVal.time().msec());
*/
xValue = ai.abscissaDtPixelsPerScaleUnit * dT;
if( dtVal.time().msec() )
xValue += (ai.abscissaDtPixelsPerScaleUnit * dtVal.time().msec())
/ ( ai.bScaleLessThanDay
? 1000.0
: (1000.0 * 86400.0) );
//tqDebug("xValue: %f",xValue);
if( !ai.bScaleLessThanDay ){
if( dtVal.time().second() )
xValue += (ai.abscissaDtPixelsPerScaleUnit * dtVal.time().second())
/ 86400.0;
if( dtVal.time().minute() )
xValue += (ai.abscissaDtPixelsPerScaleUnit * dtVal.time().minute())
/ 1440.0;
if( dtVal.time().hour() )
xValue += (ai.abscissaDtPixelsPerScaleUnit * dtVal.time().hour())
/ 24.0;
}
xValue *= ai.bAbscissaDecreasing ? -1.0 : 1.0;
xValue += ai.dtLowPos;
// tqDebug("xValue = dtLowPos + abscissaDtPixelsPerScaleUnit * dT\n%f = %f + %f * %f",
// xValue, dtLowPos, abscissaDtPixelsPerScaleUnit, dT);
}
else
return false;
} else
xValue = ai.pointDist * ( double ) colNumber;
return true;
}
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/*** ***/
/*** Framework for data drawing using cartesian axes (Bar, Line, ...) ***/
/*** ***/
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/**
Paints the actual data area and registers the region for the data
points if \a regions is not 0.
\param painter the TQPainter onto which the chart should be painted
\param data the data that will be displayed as a chart
\param paint2nd specifies whether the main chart or the additional chart is to be drawn now
\param regions a pointer to a list of regions that will be filled
with regions representing the data segments, if not null
*/
void KDChartAxesPainter::paintData( TQPainter* painter,
KDChartTableDataBase* data,
bool paint2nd,
KDChartDataRegionList* regions )
{
bool bNormalMode = isNormalMode();
uint chart = paint2nd ? 1 : 0;
// find out the ordinate axis (or axes, resp.) belonging to this chart
// (up to 4 ordinates might be in use: 2 left ones and 2 right ones)
uint axesCount;
KDChartParams::AxesArray ordinateAxes;
ordinateAxes.resize( KDCHART_CNT_ORDINATES );
if( !params()->chartAxes( chart, axesCount, ordinateAxes ) ) {
// no axis - no fun!
return;
// We cannot draw data without an axis having calculated high/low
// values and position of the zero line before.
// PENDING(khz) Allow drawing without having a visible axis!
}
//const KDChartParams::ChartType params_chartType
// = paint2nd ? params()->additionalChartType() : params()->chartType();
double logWidth = _dataRect.width();
double areaWidthP1000 = logWidth / 1000.0;
int nClipShiftUp = clipShiftUp(bNormalMode, areaWidthP1000);
TQRect ourClipRect( _dataRect );
if ( 0 < ourClipRect.top() ) {
ourClipRect.setTop( ourClipRect.top() - nClipShiftUp );
ourClipRect.setHeight( ourClipRect.height() + nClipShiftUp - 1 );
} else
ourClipRect.setHeight( ourClipRect.height() + nClipShiftUp / 2 - 1 );
// protect axes ?
//ourClipRect.setBottom( ourClipRect.bottom() - 1 );
//ourClipRect.setLeft( ourClipRect.left() + 1 );
//ourClipRect.setRight( ourClipRect.right() - 1 );
const TQWMatrix & world = painter->worldMatrix();
ourClipRect =
#if COMPAT_TQT_VERSION >= 0x030000
world.mapRect( ourClipRect );
#else
world.map( ourClipRect );
#endif
painter->setClipRect( ourClipRect );
painter->translate( _dataRect.x(), _dataRect.y() );
painter->setPen( params()->outlineDataColor() );
// find out which datasets are to be represented by this chart
uint chartDatasetStart, chartDatasetEnd;
findChartDatasets( data, paint2nd, chart, chartDatasetStart, chartDatasetEnd );
// Note: 'aI' is *not* the axis number!
for( uint aI = 0; aI < axesCount; ++aI ) {
// 'axis' is the REAL axis number!
uint axis = ordinateAxes.at( aI );
const KDChartAxisParams* axisPara = &params()->axisParams( axis );
uint datasetStart, datasetEnd;
uint axisDatasetStart, axisDatasetEnd;
uint dummy;
if( params()->axisDatasets( axis,
axisDatasetStart,
axisDatasetEnd, dummy )
&& ( KDCHART_ALL_DATASETS != axisDatasetStart ) ) {
if( KDCHART_NO_DATASET == axisDatasetStart ){
//==========
continue; // NO DATASETS --> STOP PROCESSING THIS AXIS
//==========
}
if( axisDatasetStart >= chartDatasetStart
&& axisDatasetStart <= chartDatasetEnd )
datasetStart = TQMAX( axisDatasetStart, chartDatasetStart );
else if( axisDatasetStart <= chartDatasetStart
&& axisDatasetEnd >= chartDatasetStart )
datasetStart = chartDatasetStart;
else
datasetStart = 20;
if( axisDatasetEnd >= chartDatasetStart
&& axisDatasetEnd <= chartDatasetEnd )
datasetEnd = TQMIN( axisDatasetEnd, chartDatasetEnd );
else if( axisDatasetEnd >= chartDatasetEnd
&& axisDatasetStart <= chartDatasetEnd )
datasetEnd = chartDatasetEnd;
else
datasetEnd = 0;
} else {
datasetStart = chartDatasetStart;
datasetEnd = chartDatasetEnd;
}
//tqDebug("\n=========================================================="
// "\naxis %u axisDatasetStart %u axisDatasetEnd %u / chartDatasetStart %u chartDatasetEnd %u",
//axis, axisDatasetStart, axisDatasetEnd, chartDatasetStart, chartDatasetEnd );
double logHeight = axisPara->axisTrueAreaRect().height();
double axisYOffset = axisPara->axisTrueAreaRect().y() - _dataRect.y();
//tqDebug("\n==========================================================\naxis %u logHeight %f axisDatasetStart %u chartDatasetStart %u axisDatasetEnd %u chartDatasetEnd %u",
//axis, logHeight, axisDatasetStart, chartDatasetStart, axisDatasetEnd, chartDatasetEnd );
//if( KDCHART_ALL_DATASETS == axisDatasetStart )
// tqDebug(" ALL DATASETS");
//if( KDCHART_NO_DATASET == axisDatasetStart )
// tqDebug(" N O DATESETS");
double maxColumnValue = axisPara->trueAxisHigh();
double minColumnValue = axisPara->trueAxisLow();
double columnValueDistance = maxColumnValue - minColumnValue;
// call the chart type specific data painter:
specificPaintData( painter,
ourClipRect,
data,
regions,
axisPara,
bNormalMode,
chart,
logWidth,
areaWidthP1000,
logHeight,
axisYOffset,
minColumnValue,
maxColumnValue,
columnValueDistance,
chartDatasetStart,
chartDatasetEnd,
datasetStart,
datasetEnd );
}
painter->translate( - _dataRect.x(), - _dataRect.y() );
}