KOffice – TDE office suite
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.

4526 lines
211KB

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