KNemo – network interfaces monitor for systray
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.
 
 
 
 
 
 

707 lines
20 KiB

  1. /*
  2. KSysGuard, the KDE System Guard
  3. Copyright (c) 1999 - 2002 Chris Schlaeger <cs@kde.org>
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of version 2 of the GNU General Public
  6. License as published by the Free Software Foundation
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  14. KSysGuard is currently maintained by Chris Schlaeger <cs@kde.org>.
  15. Please do not commit any changes without consulting me first. Thanks!
  16. $Id: SignalPlotter.cc,v 1.7 2004/01/04 13:43:48 waba Exp $
  17. */
  18. #include <math.h>
  19. #include <string.h>
  20. #include <tqpainter.h>
  21. #include <tqpixmap.h>
  22. #include <kdebug.h>
  23. #include <kconfig.h>
  24. #include "signalplotter.h"
  25. static inline int min( int a, int b )
  26. {
  27. return ( a < b ? a : b );
  28. }
  29. SignalPlotter::SignalPlotter( TQWidget *parent, const char *name )
  30. : TQDialog( parent, name ),
  31. mPosInitialized( false ),
  32. mName( name )
  33. {
  34. // Auto deletion does not work for pointer to arrays.
  35. mBeamData.setAutoDelete( false );
  36. setBackgroundMode( NoBackground );
  37. mSamples = 0;
  38. mMinValue = mMaxValue = 0.0;
  39. mUseAutoRange = true;
  40. mGraphStyle = GRAPH_POLYGON;
  41. // Anything smaller than this does not make sense.
  42. setMinimumSize( 16, 16 );
  43. setSizePolicy( TQSizePolicy( TQSizePolicy::Expanding,
  44. TQSizePolicy::Expanding, false ) );
  45. mShowVerticalLines = true;
  46. mVerticalLinesColor = TQColor( 0x04FB1D );
  47. mVerticalLinesDistance = 30;
  48. mVerticalLinesScroll = true;
  49. mVerticalLinesOffset = 0;
  50. mHorizontalScale = 1;
  51. mShowHorizontalLines = true;
  52. mHorizontalLinesColor = TQColor( 0x04FB1D );
  53. mHorizontalLinesCount = 5;
  54. mShowLabels = true;
  55. mShowTopBar = false;
  56. mFontSize = 8;
  57. mBackgroundColor = TQColor( 0x313031 );
  58. // Restore window size and position.
  59. KConfig* config = new KConfig( "knemorc", false );
  60. if ( config->hasGroup( "Interface_" + mName ) )
  61. {
  62. config->setGroup( "Interface_" + mName );
  63. if ( config->hasKey( "PlotterX" ) && config->hasKey( "PlotterY" ) )
  64. {
  65. mPos.setX( config->readNumEntry( "PlotterX" ) );
  66. mPos.setY( config->readNumEntry( "PlotterY" ) );
  67. mPosInitialized = true;
  68. }
  69. if ( config->hasKey( "PlotterWidth" ) && config->hasKey( "PlotterHeight" ) )
  70. resize( config->readNumEntry( "PlotterWidth" ),
  71. config->readNumEntry( "PlotterHeight" ) );
  72. }
  73. delete config;
  74. }
  75. SignalPlotter::~SignalPlotter()
  76. {
  77. for ( double* p = mBeamData.first(); p; p = mBeamData.next() )
  78. delete [] p;
  79. // Store window size and position.
  80. KConfig* config = new KConfig( "knemorc", false );
  81. if ( config->hasGroup( "Interface_" + mName ) )
  82. {
  83. config->setGroup( "Interface_" + mName );
  84. config->writeEntry( "PlotterX", x() );
  85. config->writeEntry( "PlotterY", y() );
  86. config->writeEntry( "PlotterWidth", width() );
  87. config->writeEntry( "PlotterHeight", height() );
  88. config->sync();
  89. }
  90. delete config;
  91. }
  92. void SignalPlotter::hide()
  93. {
  94. mPos = pos();
  95. mPosInitialized = true;
  96. TQDialog::hide();
  97. }
  98. void SignalPlotter::show()
  99. {
  100. TQDialog::show();
  101. /**
  102. * mPosInitialized should always be true, except when
  103. * starting KNemo for the very first time.
  104. */
  105. if ( mPosInitialized )
  106. move( mPos );
  107. }
  108. bool SignalPlotter::addBeam( const TQColor &color )
  109. {
  110. double* d = new double[ mSamples ];
  111. memset( d, 0, sizeof(double) * mSamples );
  112. mBeamData.append( d );
  113. mBeamColor.append( color );
  114. return true;
  115. }
  116. void SignalPlotter::addSample( const TQValueList<double>& sampleBuf )
  117. {
  118. if ( mBeamData.count() != sampleBuf.count() )
  119. return;
  120. double* d;
  121. if ( mUseAutoRange ) {
  122. double sum = 0;
  123. for ( d = mBeamData.first(); d; d = mBeamData.next() ) {
  124. sum += d[ 0 ];
  125. if ( sum < mMinValue )
  126. mMinValue = sum;
  127. if ( sum > mMaxValue )
  128. mMaxValue = sum;
  129. }
  130. }
  131. /* If the vertical lines are scrolling, increment the offset
  132. * so they move with the data. The vOffset / hScale confusion
  133. * is because v refers toQt::Vertical Lines, and h to the horizontal
  134. * distance between the vertical lines. */
  135. if ( mVerticalLinesScroll ) {
  136. mVerticalLinesOffset = ( mVerticalLinesOffset + mHorizontalScale)
  137. % mVerticalLinesDistance;
  138. }
  139. // Shift data buffers one sample down and insert new samples.
  140. TQValueList<double>::ConstIterator s;
  141. for ( d = mBeamData.first(), s = sampleBuf.begin(); d; d = mBeamData.next(), ++s ) {
  142. memmove( d, d + 1, ( mSamples - 1 ) * sizeof( double ) );
  143. d[ mSamples - 1 ] = *s;
  144. }
  145. update();
  146. }
  147. void SignalPlotter::changeRange( int beam, double min, double max )
  148. {
  149. // Only the first beam affects range calculation.
  150. if ( beam > 1 )
  151. return;
  152. mMinValue = min;
  153. mMaxValue = max;
  154. }
  155. TQValueList<TQColor> &SignalPlotter::beamColors()
  156. {
  157. return mBeamColor;
  158. }
  159. void SignalPlotter::removeBeam( uint pos )
  160. {
  161. mBeamColor.remove( mBeamColor.at( pos ) );
  162. mBeamData.remove( pos );
  163. }
  164. void SignalPlotter::setTitle( const TQString &title )
  165. {
  166. mTitle = title;
  167. }
  168. TQString SignalPlotter::title() const
  169. {
  170. return mTitle;
  171. }
  172. void SignalPlotter::setUseAutoRange( bool value )
  173. {
  174. mUseAutoRange = value;
  175. }
  176. bool SignalPlotter::useAutoRange() const
  177. {
  178. return mUseAutoRange;
  179. }
  180. void SignalPlotter::setMinValue( double min )
  181. {
  182. mMinValue = min;
  183. }
  184. double SignalPlotter::minValue() const
  185. {
  186. return ( mUseAutoRange ? 0 : mMinValue );
  187. }
  188. void SignalPlotter::setMaxValue( double max )
  189. {
  190. mMaxValue = max;
  191. }
  192. double SignalPlotter::maxValue() const
  193. {
  194. return ( mUseAutoRange ? 0 : mMaxValue );
  195. }
  196. void SignalPlotter::setGraphStyle( uint style )
  197. {
  198. mGraphStyle = style;
  199. }
  200. uint SignalPlotter::graphStyle() const
  201. {
  202. return mGraphStyle;
  203. }
  204. void SignalPlotter::setHorizontalScale( uint scale )
  205. {
  206. if (scale == mHorizontalScale)
  207. return;
  208. mHorizontalScale = scale;
  209. if (isVisible())
  210. updateDataBuffers();
  211. }
  212. uint SignalPlotter::horizontalScale() const
  213. {
  214. return mHorizontalScale;
  215. }
  216. void SignalPlotter::setShowVerticalLines( bool value )
  217. {
  218. mShowVerticalLines = value;
  219. }
  220. bool SignalPlotter::showVerticalLines() const
  221. {
  222. return mShowVerticalLines;
  223. }
  224. void SignalPlotter::setVerticalLinesColor( const TQColor &color )
  225. {
  226. mVerticalLinesColor = color;
  227. }
  228. TQColor SignalPlotter::verticalLinesColor() const
  229. {
  230. return mVerticalLinesColor;
  231. }
  232. void SignalPlotter::setVerticalLinesDistance( int distance )
  233. {
  234. mVerticalLinesDistance = distance;
  235. }
  236. int SignalPlotter::verticalLinesDistance() const
  237. {
  238. return mVerticalLinesDistance;
  239. }
  240. void SignalPlotter::setVerticalLinesScroll( bool value )
  241. {
  242. mVerticalLinesScroll = value;
  243. }
  244. bool SignalPlotter::verticalLinesScroll() const
  245. {
  246. return mVerticalLinesScroll;
  247. }
  248. void SignalPlotter::setShowHorizontalLines( bool value )
  249. {
  250. mShowHorizontalLines = value;
  251. }
  252. bool SignalPlotter::showHorizontalLines() const
  253. {
  254. return mShowHorizontalLines;
  255. }
  256. void SignalPlotter::setHorizontalLinesColor( const TQColor &color )
  257. {
  258. mHorizontalLinesColor = color;
  259. }
  260. TQColor SignalPlotter::horizontalLinesColor() const
  261. {
  262. return mHorizontalLinesColor;
  263. }
  264. void SignalPlotter::setHorizontalLinesCount( int count )
  265. {
  266. mHorizontalLinesCount = count;
  267. }
  268. int SignalPlotter::horizontalLinesCount() const
  269. {
  270. return mHorizontalLinesCount;
  271. }
  272. void SignalPlotter::setShowLabels( bool value )
  273. {
  274. mShowLabels = value;
  275. }
  276. bool SignalPlotter::showLabels() const
  277. {
  278. return mShowLabels;
  279. }
  280. void SignalPlotter::setShowTopBar( bool value )
  281. {
  282. mShowTopBar = value;
  283. }
  284. bool SignalPlotter::showTopBar() const
  285. {
  286. return mShowTopBar;
  287. }
  288. void SignalPlotter::setFontSize( int size )
  289. {
  290. mFontSize = size;
  291. }
  292. int SignalPlotter::fontSize() const
  293. {
  294. return mFontSize;
  295. }
  296. void SignalPlotter::setBackgroundColor( const TQColor &color )
  297. {
  298. mBackgroundColor = color;
  299. }
  300. TQColor SignalPlotter::backgroundColor() const
  301. {
  302. return mBackgroundColor;
  303. }
  304. void SignalPlotter::resizeEvent( TQResizeEvent* )
  305. {
  306. Q_ASSERT( width() > 2 );
  307. updateDataBuffers();
  308. }
  309. void SignalPlotter::updateDataBuffers()
  310. {
  311. /* Since the data buffers for the beams are equal in size to the
  312. * width of the widget minus 2 we have to enlarge or shrink the
  313. * buffers accordingly when a resize occures. To have a nicer
  314. * display we try to keep as much data as possible. Data that is
  315. * lost due to shrinking the buffers cannot be recovered on
  316. * enlarging though. */
  317. /* Determine new number of samples first.
  318. * +0.5 to ensure rounding up
  319. * +2 for extra data points so there is
  320. * 1) no wasted space and
  321. * 2) no loss of precision when drawing the first data point. */
  322. uint newSampleNum = static_cast<uint>( ( ( width() - 2 ) /
  323. mHorizontalScale ) + 2.5 );
  324. // overlap between the old and the new buffers.
  325. int overlap = min( mSamples, newSampleNum );
  326. for ( uint i = 0; i < mBeamData.count(); ++i ) {
  327. double* nd = new double[ newSampleNum ];
  328. // initialize new part of the new buffer
  329. if ( newSampleNum > (uint)overlap )
  330. memset( nd, 0, sizeof( double ) * ( newSampleNum - overlap ) );
  331. // copy overlap from old buffer to new buffer
  332. memcpy( nd + ( newSampleNum - overlap ), mBeamData.at( i ) +
  333. ( mSamples - overlap ), overlap * sizeof( double ) );
  334. mBeamData.remove( i );
  335. mBeamData.insert( i, nd );
  336. }
  337. mSamples = newSampleNum;
  338. }
  339. void SignalPlotter::paintEvent( TQPaintEvent* )
  340. {
  341. uint w = width();
  342. uint h = height();
  343. /* Do not do repaints when the widget is not yet setup properly. */
  344. if ( w <= 2 )
  345. return;
  346. TQPixmap pm( w, h );
  347. TQPainter p;
  348. p.begin( TQT_TQPAINTDEVICE(&pm), this );
  349. pm.fill( mBackgroundColor );
  350. /* Draw white line along the bottom and the right side of the
  351. * widget to create a 3D like look. */
  352. p.setPen( TQColor( colorGroup().light() ) );
  353. p.drawLine( 0, h - 1, w - 1, h - 1 );
  354. p.drawLine( w - 1, 0, w - 1, h - 1 );
  355. p.setClipRect( 1, 1, w - 2, h - 2 );
  356. double range = mMaxValue - mMinValue;
  357. /* If the range is too small we will force it to 1.0 since it
  358. * looks a lot nicer. */
  359. if ( range < 0.000001 )
  360. range = 1.0;
  361. double minValue = mMinValue;
  362. if ( mUseAutoRange ) {
  363. if ( mMinValue != 0.0 ) {
  364. double dim = pow( 10, floor( log10( fabs( mMinValue ) ) ) ) / 2;
  365. if ( mMinValue < 0.0 )
  366. minValue = dim * floor( mMinValue / dim );
  367. else
  368. minValue = dim * ceil( mMinValue / dim );
  369. range = mMaxValue - minValue;
  370. if ( range < 0.000001 )
  371. range = 1.0;
  372. }
  373. // Massage the range so that the grid shows some nice values.
  374. double step = range / mHorizontalLinesCount;
  375. double dim = pow( 10, floor( log10( step ) ) ) / 2;
  376. range = dim * ceil( step / dim ) * mHorizontalLinesCount;
  377. }
  378. double maxValue = minValue + range;
  379. int top = 0;
  380. if ( mShowTopBar && h > ( mFontSize + 2 + mHorizontalLinesCount * 10 ) ) {
  381. /* Draw horizontal bar with current sensor values at top of display. */
  382. p.setPen( mHorizontalLinesColor );
  383. int x0 = w / 2;
  384. p.setFont( TQFont( p.font().family(), mFontSize ) );
  385. top = p.fontMetrics().height();
  386. h -= top;
  387. int h0 = top - 2;
  388. // JJ 2005-07-18: show numerical in/out values in the top bar --->
  389. double *d1 = mBeamData.first();
  390. double UploadSpeed = 0;
  391. if(d1)
  392. UploadSpeed = d1[ w - 3 ]; // read value from graph data
  393. double *d2 = mBeamData.next();
  394. double DownloadSpeed = 0;
  395. if(d2)
  396. DownloadSpeed = d2[ w - 3 ]; // read value from graph data
  397. // The left side of the top bar is now divided into three sections:
  398. // - name of interface (original title)
  399. // - download speed (numerically, from the value shown by the bar on the right)
  400. // - upload speed (numerically, from the value shown by the bar on the right)
  401. // title
  402. p.drawText(0, 0, x0/3, top - 2, TQt::AlignCenter, mTitle );
  403. TQValueList<TQColor>::Iterator col_speeds;
  404. col_speeds = mBeamColor.begin();
  405. TQColor UploadColor = *(col_speeds++);
  406. TQColor DownloadColor = *(col_speeds);
  407. // download speed
  408. TQString DownloadSpeedText;
  409. DownloadSpeedText.sprintf("in: %0.2f KB/s", DownloadSpeed);
  410. p.setPen( DownloadColor );
  411. p.drawText(x0/3, 0, x0/3, top - 2, TQt::AlignCenter, DownloadSpeedText );
  412. // upload speed
  413. TQString UploadSpeedText;
  414. UploadSpeedText.sprintf("out: %0.2f KB/s", UploadSpeed);
  415. p.setPen( UploadColor );
  416. p.drawText(2*x0/3, 0, x0/3, top - 2, TQt::AlignCenter, UploadSpeedText );
  417. // restore correct pen color for the separator lines
  418. p.setPen( mHorizontalLinesColor );
  419. // <--- JJ 2005-07-18
  420. p.drawLine( x0 - 1, 1, x0 - 1, h0 );
  421. p.drawLine( 0, top - 1, w - 2, top - 1 );
  422. double bias = -minValue;
  423. double scaleFac = ( w - x0 - 2 ) / range;
  424. TQValueList<TQColor>::Iterator col;
  425. col = mBeamColor.begin();
  426. for ( double* d = mBeamData.first(); d; d = mBeamData.next(), ++col ) {
  427. int start = x0 + (int)( bias * scaleFac );
  428. int end = x0 + (int)( ( bias += d[ w - 3 ] ) * scaleFac );
  429. /* If the rect is wider than 2 pixels we draw only the last
  430. * pixels with the bright color. The rest is painted with
  431. * a 50% darker color. */
  432. if ( end - start > 1 ) {
  433. p.setPen( (*col).dark( 150 ) );
  434. p.setBrush( (*col).dark( 150 ) );
  435. p.drawRect( start, 1, end - start, h0 );
  436. p.setPen( *col );
  437. p.drawLine( end, 1, end, h0 );
  438. } else if ( start - end > 1 ) {
  439. p.setPen( (*col).dark( 150 ) );
  440. p.setBrush( (*col).dark( 150 ) );
  441. p.drawRect( end, 1, start - end, h0 );
  442. p.setPen( *col );
  443. p.drawLine( end, 1, end, h0 );
  444. } else {
  445. p.setPen( *col );
  446. p.drawLine( start, 1, start, h0 );
  447. }
  448. }
  449. }
  450. /* Draw scope-like grid vertical lines */
  451. if ( mShowVerticalLines && w > 60 ) {
  452. p.setPen( mVerticalLinesColor );
  453. for ( uint x = mVerticalLinesOffset; x < ( w - 2 ); x += mVerticalLinesDistance )
  454. p.drawLine( w - x, top, w - x, h + top - 2 );
  455. }
  456. /* In autoRange mode we determine the range and plot the values in
  457. * one go. This is more efficiently than running through the
  458. * buffers twice but we do react on recently discarded samples as
  459. * well as new samples one plot too late. So the range is not
  460. * correct if the recently discarded samples are larger or smaller
  461. * than the current extreme values. But we can probably live with
  462. * this. */
  463. if ( mUseAutoRange )
  464. mMinValue = mMaxValue = 0.0;
  465. /* Plot stacked values */
  466. double scaleFac = ( h - 2 ) / range;
  467. if ( mGraphStyle == GRAPH_ORIGINAL ) {
  468. int xPos = 0;
  469. for ( int i = 0; i < mSamples; i++, xPos += mHorizontalScale ) {
  470. double bias = -minValue;
  471. TQValueList<TQColor>::Iterator col;
  472. col = mBeamColor.begin();
  473. double sum = 0.0;
  474. for ( double* d = mBeamData.first(); d; d = mBeamData.next(), ++col ) {
  475. if ( mUseAutoRange ) {
  476. sum += d[ i ];
  477. if ( sum < mMinValue )
  478. mMinValue = sum;
  479. if ( sum > mMaxValue )
  480. mMaxValue = sum;
  481. }
  482. int start = top + h - 2 - (int)( bias * scaleFac );
  483. int end = top + h - 2 - (int)( ( bias + d[ i ] ) * scaleFac );
  484. bias += d[ i ];
  485. /* If the line is longer than 2 pixels we draw only the last
  486. * 2 pixels with the bright color. The rest is painted with
  487. * a 50% darker color. */
  488. if ( end - start > 2 ) {
  489. p.fillRect( xPos, start, mHorizontalScale, end - start - 1, (*col).dark( 150 ) );
  490. p.fillRect( xPos, end - 1, mHorizontalScale, 2, *col );
  491. } else if ( start - end > 2 ) {
  492. p.fillRect( xPos, start, mHorizontalScale, end - start + 1, (*col).dark( 150 ) );
  493. p.fillRect( xPos, end + 1, mHorizontalScale, 2, *col );
  494. } else
  495. p.fillRect( xPos, start, mHorizontalScale, end - start, *col );
  496. }
  497. }
  498. } else if ( mGraphStyle == GRAPH_POLYGON ) {
  499. int *prevVals = new int[ mBeamData.count() ];
  500. int hack[ 4 ] = { 0, 0, 0, 0 };
  501. int x1 = w - ( ( mSamples + 1 ) * mHorizontalScale );
  502. for ( int i = 0; i < mSamples; i++ ) {
  503. TQValueList<TQColor>::Iterator col;
  504. col = mBeamColor.begin();
  505. double sum = 0.0;
  506. int y = top + h - 2;
  507. int oldY = top + h;
  508. int oldPrevY = oldY;
  509. int height = 0;
  510. int j = 0;
  511. int jMax = mBeamData.count() - 1;
  512. x1 += mHorizontalScale;
  513. int x2 = x1 + mHorizontalScale;
  514. for ( double* d = mBeamData.first(); d; d = mBeamData.next(), ++col, j++ ) {
  515. if ( mUseAutoRange ) {
  516. sum += d[ i ];
  517. if ( sum < mMinValue )
  518. mMinValue = sum;
  519. if ( sum > mMaxValue )
  520. mMaxValue = sum;
  521. }
  522. height = (int)( ( d[ i ] - minValue ) * scaleFac );
  523. y -= height;
  524. /* If the line is longer than 2 pixels we draw only the last
  525. * 2 pixels with the bright color. The rest is painted with
  526. * a 50% darker color. */
  527. TQPen lastPen = TQPen( p.pen() );
  528. p.setPen( (*col).dark( 150 ) );
  529. p.setBrush( (*col).dark( 150 ) );
  530. TQPointArray pa( 4 );
  531. int prevY = ( i == 0 ) ? y : prevVals[ j ];
  532. pa.putPoints( 0, 1, x1, prevY );
  533. pa.putPoints( 1, 1, x2, y );
  534. pa.putPoints( 2, 1, x2, oldY );
  535. pa.putPoints( 3, 1, x1, oldPrevY );
  536. p.drawPolygon( pa );
  537. p.setPen( lastPen );
  538. if ( jMax == 0 ) {
  539. // draw as normal, no deferred drawing req'd.
  540. p.setPen( *col );
  541. p.drawLine( x1, prevY, x2, y );
  542. } else if ( j == jMax ) {
  543. // draw previous values and current values
  544. p.drawLine( hack[ 0 ], hack[ 1 ], hack[ 2 ], hack[ 3 ] );
  545. p.setPen( *col );
  546. p.drawLine( x1, prevY, x2, y );
  547. } else if ( j == 0 ) {
  548. // save values only
  549. hack[ 0 ] = x1;
  550. hack[ 1 ] = prevY;
  551. hack[ 2 ] = x2;
  552. hack[ 3 ] = y;
  553. p.setPen( *col );
  554. } else {
  555. p.drawLine( hack[ 0 ], hack[ 1 ], hack[ 2 ], hack[ 3 ] );
  556. hack[ 0 ] = x1;
  557. hack[ 1 ] = prevY;
  558. hack[ 2 ] = x2;
  559. hack[ 3 ] = y;
  560. p.setPen( *col );
  561. }
  562. prevVals[ j ] = y;
  563. oldY = y;
  564. oldPrevY = prevY;
  565. }
  566. }
  567. delete[] prevVals;
  568. }
  569. /* Draw horizontal lines and values. Lines are drawn when the
  570. * height is greater than 10 times hCount + 1, values are shown
  571. * when width is greater than 60 */
  572. if ( mShowHorizontalLines && h > ( 10 * ( mHorizontalLinesCount + 1 ) ) ) {
  573. p.setPen( mHorizontalLinesColor );
  574. p.setFont( TQFont( p.font().family(), mFontSize ) );
  575. TQString val;
  576. for ( uint y = 1; y < mHorizontalLinesCount; y++ ) {
  577. p.drawLine( 0, top + y * ( h / mHorizontalLinesCount ), w - 2,
  578. top + y * ( h / mHorizontalLinesCount ) );
  579. if ( mShowLabels && h > ( mFontSize + 1 ) * ( mHorizontalLinesCount + 1 )
  580. && w > 60 ) {
  581. val = TQString( "%1" ).arg( maxValue - y * ( range / mHorizontalLinesCount ) );
  582. p.drawText( 6, top + y * ( h / mHorizontalLinesCount ) - 1, val );
  583. }
  584. }
  585. if ( mShowLabels && h > ( mFontSize + 1 ) * ( mHorizontalLinesCount + 1 )
  586. && w > 60 ) {
  587. val = TQString( "%1" ).arg( minValue );
  588. p.drawText( 6, top + h - 2, val );
  589. }
  590. }
  591. p.end();
  592. bitBlt( this, 0, 0, &pm );
  593. }
  594. #include "signalplotter.moc"