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.
4466 lines
209 KiB
4466 lines
209 KiB
/*
|
|
KDChart - a multi-platform charting engine
|
|
*/
|
|
|
|
/****************************************************************************
|
|
** Copyright (C) 2001-2003 Klarälvdalens Datakonsult AB. All rights reserved.
|
|
**
|
|
** This file is part of the KDChart library.
|
|
**
|
|
** This file may be distributed and/or modified under the terms of the
|
|
** GNU General Public License version 2 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.GPL included in the
|
|
** packaging of this file.
|
|
**
|
|
** Licensees holding valid commercial KDChart licenses may use this file in
|
|
** accordance with the KDChart Commercial License Agreement provided with
|
|
** the Software.
|
|
**
|
|
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
|
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
**
|
|
** See http://www.klaralvdalens-datakonsult.se/?page=products for
|
|
** information about KDChart Commercial License Agreements.
|
|
**
|
|
** Contact info@klaralvdalens-datakonsult.se if any conditions of this
|
|
** licensing are not clear to you.
|
|
**
|
|
**********************************************************************/
|
|
#include <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
|
|
}
|
|
|
|
|
|
/**
|
|
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() )
|
|
+ ". "
|
|
+ TQDate::longMonthName( dLow.month() )
|
|
+ ' '
|
|
+ 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 )
|
|
commonDtHeader = TQDate::longMonthName( dLow.month() )
|
|
+ ' '
|
|
+ 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 ){
|
|
TQDateTime dt( TQDateTime::fromString( *it,
|
|
TQt::ISODate ) );
|
|
text = dt.toString( formatDT );
|
|
}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( TQt::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 ){
|
|
dt = TQDateTime::fromString( *labelIter,
|
|
TQt::ISODate );
|
|
label = dt.toString( formatDT );
|
|
}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( TQt::ISODate ));
|
|
tqDebug("dtHigh: ");
|
|
tqDebug(dtHigh.toString( TQt::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
|
|
labelTexts.append( dt.toString( TQt::ISODate ) );
|
|
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 = ¶ms.axisParams( KDChartAxisParams::AxisPosBottom );
|
|
else
|
|
if( params.axisParams( KDChartAxisParams::AxisPosBottom2 ).axisVisible()
|
|
&& ( KDChartAxisParams::AxisTypeUnknown
|
|
!= params.axisParams( KDChartAxisParams::AxisPosBottom2 ).axisType() ) )
|
|
infos.abscissaPara = ¶ms.axisParams( KDChartAxisParams::AxisPosBottom2 );
|
|
else
|
|
if( params.axisParams( KDChartAxisParams::AxisPosTop ).axisVisible()
|
|
&& ( KDChartAxisParams::AxisTypeUnknown
|
|
!= params.axisParams( KDChartAxisParams::AxisPosTop ).axisType() ) )
|
|
infos.abscissaPara = ¶ms.axisParams( KDChartAxisParams::AxisPosTop );
|
|
else
|
|
if( params.axisParams( KDChartAxisParams::AxisPosTop2 ).axisVisible()
|
|
&& ( KDChartAxisParams::AxisTypeUnknown
|
|
!= params.axisParams( KDChartAxisParams::AxisPosTop2 ).axisType() ) )
|
|
infos.abscissaPara = ¶ms.axisParams( KDChartAxisParams::AxisPosTop2 );
|
|
else
|
|
// default is bottom axis:
|
|
infos.abscissaPara = ¶ms.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 = world.mapRect( ourClipRect );
|
|
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 = ¶ms()->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() );
|
|
}
|