Salome HOME
sources v1.2
[modules/yacs.git] / src / SALOMEGUI / QAD_SpinBoxDbl.cxx
1 //  SALOME SALOMEGUI : implementation of desktop and GUI kernel
2 //
3 //  Copyright (C) 2003  CEA/DEN, EDF R&D
4 //
5 //
6 //
7 //  File   : QAD_SpinBoxDbl.cxx
8 //  Author : Vadim SANDLER
9 //  Module : SALOME
10 //  $Header$
11
12 using namespace std;
13 #include "QAD_SpinBoxDbl.h"
14 #include <qpushbutton.h>
15 #include <qpainter.h>
16 #include <qbitmap.h>
17 #include <qlineedit.h>
18 #include <qvalidator.h>
19 #include <qpixmapcache.h>
20 #include <qapplication.h>
21 #include <limits.h>
22
23 //******************************************************************************
24 // QDblRangeControl class
25 //******************************************************************************
26
27 /*!
28   Constructs a range control with min value 0.00, max value 99.99,
29   line step 0.1, page step 1.00, precision 6, double precision 10e-6,
30   convertion flag 'g' and initial value 0.00.
31 */
32 QDblRangeControl::QDblRangeControl()
33 {
34   prec     = 6;
35   dblPrec  = 10e-6;
36   convFlag = 'g';
37   minVal   = roundPrecision(  0.00 );
38   maxVal   = roundPrecision( 99.99 );
39   line     = roundPrecision(  0.10 );
40   page     = roundPrecision(  1.00 );
41   prevVal  = roundPrecision( -0.10 );
42   val      = bound( 0.00 );
43 }
44
45 /*!
46   Constructs a range control whose value can never be smaller than
47   <minValue> or greater than <maxValue>, whose line step size is
48   <lineStep> and page step size is <pageStep> and whose value is
49   initially <value> (which is guaranteed to be in range using bound()),
50   precision is <precision>, double precision is <dblPrecision> and
51   convertion flag is <cFlag>
52 */
53 QDblRangeControl::QDblRangeControl( double minValue, double maxValue,
54                                     double lineStep, double pageStep,
55                                     double value, 
56                                     int    precision, 
57                                     double dblPrecision,
58                                     char   cFlag ) 
59 {
60   prec     = precision;
61   dblPrec  = dblPrecision;
62   convFlag = cFlag;
63   minVal   = roundPrecision( minValue );
64   maxVal   = roundPrecision( maxValue );
65   line     = roundPrecision( QABS( lineStep ) );
66   page     = roundPrecision( QABS( pageStep ) );
67   prevVal  = roundPrecision( minVal - 0.1 );
68   val      = bound( value );
69 }
70
71 /*!
72   Destroys the range control
73 */
74 QDblRangeControl::~QDblRangeControl()
75 {
76 }
77
78 /*!
79   Returns the current range control value.  This is guaranteed to be
80   within the range [minValue(), maxValue()].
81 */
82 inline double QDblRangeControl::value() const
83 { return val; }
84
85 /*!
86   Sets the range control's value to <value> and forces it to be within
87   the legal range.
88   Calls the virtual valueChange() function if the new value is
89   different from the previous value. The old value can still be
90   retrieved using prevValue().
91 */
92 void QDblRangeControl::setValue( double value )
93 {
94   directSetValue( value );
95   if ( !equal(prevVal, val ) )
96     valueChange();
97 }
98
99 /*!
100   Equivalent to {setValue( value() + pageStep() )}.
101   If the value is changed, then valueChange() is called.
102 */
103 void QDblRangeControl::addPage()
104 {
105   setValue( value() + pageStep() );
106 }
107
108 /*!
109   Equivalent to {setValue( value() - pageStep() )}.
110   If the value is changed, then valueChange() is called.
111 */
112 void QDblRangeControl::subtractPage()
113 {
114   setValue( value() - pageStep() );
115 }
116
117 /*!
118   Equivalent to {setValue( value() + lineStep() )}.
119   If the value is changed, then valueChange() is called.
120 */
121 void QDblRangeControl::addLine()
122 {
123   setValue( value() + lineStep() );
124 }
125
126 /*!
127   Equivalent to {setValue( value() - lineStep() )}.
128   If the value is changed, then valueChange() is called.
129 */
130 void QDblRangeControl::subtractLine()
131 {
132   setValue( value() - lineStep() );
133 }
134
135 /*!
136   Returns the current minimum value of the range.
137 */
138 inline double QDblRangeControl::minValue() const
139 { return minVal; }
140
141 /*!
142   Returns the current maximum value of the range.
143 */
144 inline double QDblRangeControl::maxValue() const
145 { return maxVal; }
146
147 /*!
148   Sets the range control's min value to <minValue> and its max value
149   to <maxValue>.
150
151   Calls the virtual rangeChange() function if one or both of the new
152   min and max values are different from the previous setting.  Calls
153   the virtual valueChange() function if the current value is adjusted
154   because it was outside the new range.
155
156   If <maxValue> is smaller than <minValue>, <minValue> becomes the
157   only legal value.
158 */
159 void QDblRangeControl::setRange( double minValue, double maxValue )
160 {
161   minValue = roundPrecision( minValue );
162   maxValue = roundPrecision( maxValue );
163   if ( minValue > maxValue ) {
164     maxValue = minValue;
165   }
166   if ( equal( minValue, minVal ) && equal( maxValue, maxVal ) )
167     return;
168   minVal = minValue;
169   maxVal = maxValue;
170   double tmp = bound( val );
171   rangeChange();
172   if ( !equal( tmp, val ) ) {
173     prevVal = val;
174     val = tmp;
175     valueChange();
176   }
177 }
178
179 /*!
180   Sets the current minimum value of the range to <minVal>.
181   If necessary, the maxValue() is adjusted so that the range remains
182   valid.
183 */
184 void QDblRangeControl::setMinValue( double minVal )
185 {
186   double maxVal = maxValue();
187   if ( maxVal < minVal )
188     maxVal = minVal;
189   setRange( minVal, maxVal );
190 }
191
192 /*!
193   Sets the current maximum value of the range to <maxVal>.
194   If necessary, the minValue() is adjusted so that the range remains
195   valid.
196 */
197 void QDblRangeControl::setMaxValue( double maxVal )
198 {
199   double minVal = minValue();
200   if ( minVal > maxVal )
201     minVal = maxVal;
202   setRange( minVal, maxVal );
203 }
204
205 /*!
206   Returns the current line step.
207 */
208 inline double QDblRangeControl::lineStep() const
209 { return line; }
210
211 /*!
212   Returns the current page step.
213 */
214 inline double QDblRangeControl::pageStep() const
215 { return page; }
216
217 /*!
218   Sets the range line step to <lineStep> and page step to <pageStep>.
219   Calls the virtual stepChange() function if the new line step and/or
220   page step are different from the previous settings.
221 */
222 void QDblRangeControl::setSteps( double lineStep, double pageStep )
223 {
224   lineStep = roundPrecision( QABS( lineStep ) );
225   pageStep = roundPrecision( QABS( pageStep ) );
226   if ( !equal( lineStep, line ) || !equal( pageStep, page ) ) {
227     line = lineStep;
228     page = pageStep;
229     stepChange();
230   }
231 }
232
233 /*!
234   Returs precision ( see QString::setNum() for details )
235 */
236 int QDblRangeControl::precision() const 
237 { return prec; }
238
239 /*!
240   Sets precision ( see QString::setNum() for details )
241 */
242 void QDblRangeControl::setPrecision( int precision )
243 {
244   if ( precision > 0 && prec != precision ) {
245     prec = precision;
246     setRange( minValue(), maxValue() );
247     setSteps( lineStep(), pageStep() );
248   }
249 }
250
251 /*!
252   Returns double precision which is used for rounding amd comparing 
253   of double numbers
254 */
255 double QDblRangeControl::dblPrecision() const
256 { return dblPrec; }
257
258 /*!
259   Sets double precision which is used for rounding amd comparing 
260   of double numbers
261 */
262 void QDblRangeControl::setDblPrecision( double dblPrecision )
263 {
264   dblPrecision = QABS( dblPrecision );
265   if ( dblPrecision > 0 && dblPrecision != dblPrec ) {
266     dblPrec = dblPrecision;
267     setRange( minValue(), maxValue() );
268     setSteps( lineStep(), pageStep() );
269   }
270 }
271
272 /*!
273   Returns convertion flag ( see QString::setNum() for details )
274 */
275 char QDblRangeControl::convertFlag() const
276 { return convFlag; }
277
278 /*!
279   Sets convertion flag ( see QString::setNum() for details )
280 */
281 void QDblRangeControl::setConvertFlag( char cFlag )
282 {
283   if ( ( cFlag == 'f' || cFlag == 'F' || cFlag == 'e' || 
284          cFlag == 'E' || cFlag == 'g' || cFlag == 'G' ) &&
285        ( cFlag != convFlag ) ) {
286     convFlag = cFlag;
287     setRange( minValue(), maxValue() );
288     setSteps( lineStep(), pageStep() );
289   }
290 }
291
292 /*!  
293   Forces the value <v> to be within the range from minValue() to
294   maxValue() inclusive, and returns the result.
295
296   This function is provided so that you can easily force other numbers
297   than value() into the allowed range.  You do not need to call it in
298   order to use QDblRangeControl itself.
299 */
300 double QDblRangeControl::bound( double v ) const
301 {
302   if ( v < minVal )
303     return minVal;
304   if ( v > maxVal )
305     return maxVal;
306   return roundPrecision( v );
307 }
308
309 /*!
310   Sets the range control value directly without calling valueChange().
311   Forces the new value to be within the legal range.
312
313   You will rarely have to call this function. However, if you want to
314   change the range control's value inside the overloaded method
315   valueChange(), setValue() would call the function valueChange()
316   again. To avoid this recursion you must use directSetValue()
317   instead.
318 */
319 void QDblRangeControl::directSetValue( double value )
320 {
321   prevVal = val;
322   val = bound( value );
323 }
324
325 /*!
326   Returns the previous value of the range control. "Previous value"
327   means the value before the last change occurred. Setting a new range
328   may affect the value, too, because the value is forced to be inside
329   the specified range. When the range control is initially created,
330   this is the same as value().
331
332   prevValue() can be outside the current legal range if a call to
333   setRange() causes the current value to change.  For example, if the
334   range was [0, 1000] and the current value is 500, setRange(0, 400)
335   makes value() return 400 and prevValue() return 500.
336 */
337 inline double QDblRangeControl::prevValue() const
338 { return prevVal; }
339
340 /*!
341   This virtual function is called whenever the range control value
342   changes.  You can reimplement it if you want to be notified when the
343   value changes.  The default implementation does nothing.
344
345   Note that this method is called after the value changed. The previous
346   value can be retrieved using prevValue().
347 */
348 void QDblRangeControl::valueChange()
349 {
350 }
351
352 /*!
353   This virtual function is called whenever the range control's range
354   changes.  You can reimplement it if you want to be notified when the range
355   changes.  The default implementation does nothing.
356
357   Note that this method is called after the range changed.
358 */
359 void QDblRangeControl::rangeChange()
360 {
361 }
362
363 /*!
364   This virtual function is called whenever the range control's
365   line/page step settings change.  You can reimplement it if you want
366   to be notified when the step changes.  The default implementation
367   does nothing.
368
369   Note that this method is called after the step settings change.
370 */
371 void QDblRangeControl::stepChange()
372 {
373 }
374
375 /*!
376   returns true if values qre equal ( with tolerance = dblPrecision() )
377 */
378 bool QDblRangeControl::equal( double first, double second ) const
379 {
380   return QABS( first - second ) < dblPrecision();
381 }
382
383 /*!
384   Retuns rounded value
385 */
386 double QDblRangeControl::roundPrecision( double value) const
387 {
388   bool bOk;
389   QString convertor;
390   convertor.setNum( value, convertFlag(), precision() );
391   double newValue = convertor.toDouble(&bOk);
392   if ( bOk ) {
393     if ( QABS( newValue ) < dblPrecision() )
394       newValue = 0.0;
395   }
396   else {
397     newValue = value;
398   }
399   return newValue;
400 }
401
402 //******************************************************************************
403 // QAD_SpinBox class
404 //******************************************************************************
405
406 /*! 
407   Returns true if <str> ends by <substr> [ static ]
408 */
409 static bool endsWith(const QString& str, const QString& substr)
410 {
411 #if QT_VERSION < 0x030000
412   if ( str.length() < substr.length() )
413     return false;
414   return ( str.right( substr.length() ) == substr );
415 #else
416   return str.endsWith(substr);
417 #endif
418 }
419
420 /*!
421   Validator class for double value spin box
422 */
423 class QAD_SpinBoxDblValidator: public QDoubleValidator 
424 {
425 public:
426   QAD_SpinBoxDblValidator( QAD_SpinBoxDbl* sb, const char* name ) 
427   : QDoubleValidator( sb, name ),
428     spinBox( sb ) {}
429
430   State validate( QString& str, int& pos ) const;
431
432 private:
433   QAD_SpinBoxDbl* spinBox;
434 };
435
436 /*!
437   Validates data entered
438 */
439 QValidator::State QAD_SpinBoxDblValidator::validate( QString& str, int& pos ) const 
440 {
441   QString pref = spinBox->prefix();
442   QString suff = spinBox->suffix();
443   uint overhead = pref.length() + suff.length();
444   State state = Invalid;
445
446   ((QDoubleValidator *) this)->setRange( spinBox->minValue(),
447                                          spinBox->maxValue() );
448   if ( overhead == 0 ) {
449     state = QDoubleValidator::validate( str, pos );
450   } else {
451     if ( str.length() >= overhead && 
452          str.startsWith(pref) &&
453          endsWith(str, suff) ) {
454       QString core = str.mid( pref.length(), str.length() - overhead );
455       int corePos = pos - pref.length();
456       state = QDoubleValidator::validate( core, corePos );
457       pos = corePos + pref.length();
458       str.replace( pref.length(), str.length() - overhead, core );
459     } else {
460       state = QDoubleValidator::validate( str, pos );
461       if ( state == Invalid ) {
462         // stripWhiteSpace(), cf. QAD_SpinBoxDbl::interpretText()
463         QString special = spinBox->specialValueText().stripWhiteSpace();
464         QString candidate = str.stripWhiteSpace();
465
466         if ( special.startsWith(candidate) ) {
467           if ( candidate.length() == special.length() ) {
468             state = Acceptable;
469           } else {
470             state = Intermediate;
471           }
472         }
473       }
474     }
475   }
476   return state;
477 }
478
479 /*!
480   Creates a spin box with min value 0.00, max value 99.99,
481   line step 0.1, precision 6, double precision 10e-6, 
482   convertion flag 'g' and initial value 0.00.
483 */
484 QAD_SpinBoxDbl::QAD_SpinBoxDbl( QWidget* parent, const char* name )
485 : QFrame(parent, name),
486   QDblRangeControl() 
487 {
488   initSpinBox();
489 }
490
491 /*!
492   Constructor
493   Creates a spin box with min value <minValue>, max value <maxValue>,
494   line step <step>, precision <precision>, double precision <dblPrecision>, 
495   convertion flag <cFlag> and initial value <minValue>
496 */
497 QAD_SpinBoxDbl::QAD_SpinBoxDbl( QWidget*    parent, 
498                                 double      minValue, 
499                                 double      maxValue, 
500                                 double      step, 
501                                 int         precision, 
502                                 double      dblPrecision,
503                                 char        cFlag,
504                                 const char* name )  
505 : QFrame( parent, name ),
506   QDblRangeControl( minValue, maxValue, step, step, minValue, precision, dblPrecision, cFlag ) 
507 {
508   initSpinBox();
509 }
510
511 /*!
512   Destructor
513 */
514 QAD_SpinBoxDbl::~QAD_SpinBoxDbl() 
515 {}
516
517 /*!
518   Internal initialization.
519 */
520 void QAD_SpinBoxDbl::initSpinBox()
521 {
522   wrap   = FALSE;
523   edited = FALSE;
524   butSymbols = UpDownArrows;
525   selreq = FALSE;
526
527   up = new QPushButton( this, "up" );
528   up->setFocusPolicy( QWidget::NoFocus );
529   up->setAutoDefault( FALSE );
530   up->setAutoRepeat( TRUE );
531   
532   down = new QPushButton( this, "down" );
533   down->setFocusPolicy( QWidget::NoFocus );
534   down->setAutoDefault( FALSE );
535   down->setAutoRepeat( TRUE );
536   
537   validate = new QAD_SpinBoxDblValidator( this, "validator" ); 
538   vi = new QLineEdit( this, "line editor" );
539   vi->setFrame( FALSE );
540   setFocusProxy( vi );
541   setFocusPolicy( StrongFocus );
542   vi->setValidator( validate );
543   vi->installEventFilter( this );
544   
545   setFrameStyle( Panel | Sunken );
546   setLineWidth( 2 );
547   
548 //  setPalettePropagation( AllChildren );
549 //  setFontPropagation( AllChildren );
550   
551   setSizePolicy( QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed ) );
552   updateDisplay();
553   
554   connect( up,   SIGNAL(pressed()), SLOT(stepUp()) );  
555   connect( down, SIGNAL(pressed()), SLOT(stepDown()) );
556   connect( vi,   SIGNAL(textChanged(const QString&)), SLOT(textChanged()) );
557 }
558
559 /*!
560   Returns the current text of the spin box, including any prefix() and suffix().
561 */
562 QString QAD_SpinBoxDbl::text() const
563 {
564   return vi->text();
565 }
566
567 /*!
568   Returns a copy of the current text of the spin box with any prefix
569   and/or suffix and white space at the start and end removed.
570 */
571 QString QAD_SpinBoxDbl::cleanText() const
572 {
573   QString s = QString(text()).stripWhiteSpace();
574   if ( !prefix().isEmpty() ) {
575     QString px = QString(prefix()).stripWhiteSpace();
576     int len = px.length();
577     if ( len && s.left(len) == px )  // Remove _only_ if it is the prefix
578       s.remove( 0, len );
579   }
580   if ( !suffix().isEmpty() ) {
581     QString sx = QString(suffix()).stripWhiteSpace();
582     int len = sx.length();
583     if ( len && s.right(len) == sx )  // Remove _only_ if it is the suffix
584       s.truncate( s.length() - len );
585   }
586   return s.stripWhiteSpace();
587 }
588
589 /*!
590   Sets the special-value text to text.  If set, the spin box will
591   display this text instead of a numeric value whenever the current
592   value is equal to minVal().  Typically used for indicating that this
593   choice has a special (default) meaning.
594 */
595 void QAD_SpinBoxDbl::setSpecialValueText( const QString &text )
596 {
597   specText = text;
598   updateDisplay();
599 }
600
601 /*!
602   Returns the currently special-value text, or a null string if no
603   special-value text is currently set.
604 */
605 QString QAD_SpinBoxDbl::specialValueText() const
606 {
607   if ( specText.isEmpty() )
608     return QString::null;
609   else
610     return specText;
611 }
612
613 /*!
614   Sets the prefix to <text>.  The prefix is prepended to the start of
615   the displayed value.  Typical use is to indicate the unit of
616   measurement to the user.
617 */
618 void QAD_SpinBoxDbl::setPrefix( const QString &text )
619 {
620   pfix = text;
621   updateDisplay();
622 }
623
624 /*!
625   Sets the suffix to <text>.  The suffix is appended to the end of the
626   displayed value.  Typical use is to indicate the unit of measurement
627   to the user.
628 */
629 void QAD_SpinBoxDbl::setSuffix( const QString &text )
630 {
631   sfix = text;
632   updateDisplay();
633 }
634
635 /*!
636   Returns the currently set prefix, or a null string if no prefix is set.
637 */
638 QString QAD_SpinBoxDbl::prefix() const
639 {
640   if ( pfix.isEmpty() )
641     return QString::null;
642   else
643     return pfix;
644 }
645
646 /*!
647   Returns the currently set suffix, or a null string if no suffix is set.
648 */
649 QString QAD_SpinBoxDbl::suffix() const
650 {
651   if ( sfix.isEmpty() )
652     return QString::null;
653   else
654     return sfix;
655 }
656
657 /*!
658   Setting wrapping to TRUE will allow the value to be stepped from the
659   highest value to the lowest, and vice versa.  By default, wrapping is
660   turned off.
661 */
662 void QAD_SpinBoxDbl::setWrapping( bool on )
663 {
664   wrap = on;
665   updateDisplay();
666 }
667
668 /*!
669   Returns the current setWrapping() value.
670 */
671 bool QAD_SpinBoxDbl::wrapping() const
672 {
673   return wrap;
674 }
675
676 /*!
677   Reimplementation
678 */
679 QSize QAD_SpinBoxDbl::sizeHint() const
680 {
681   constPolish();
682   QFontMetrics fm = fontMetrics();
683   int h = vi->sizeHint().height();
684   if ( h < 12 )              // ensure enough space for the button pixmaps
685     h = 12;
686   int w = 35;                // minimum width for the value
687   int wx = fm.width( ' ' )*2;
688   QString s;
689
690   QAD_SpinBoxDbl* that = (QAD_SpinBoxDbl*)this;
691   s = prefix() + that->mapValueToText( that->minValue() ) + suffix();
692   w = QMAX( w, fm.width( s ) + wx);
693   s = prefix() + that->mapValueToText( that->maxValue() ) + suffix();
694   w = QMAX(w, fm.width( s ) + wx );
695   if ( !specialValueText().isEmpty() ) {
696     s = specialValueText();
697     w = QMAX( w, fm.width( s ) + wx );
698   }
699   QSize r( h * 8/5              // ~ buttons width 
700            + w                  // widest value
701            + frameWidth() * 2,  // left/right frame
702            frameWidth() * 2     // top/bottom frame
703            + h                  // font height
704          );
705   return r.expandedTo( QApplication::globalStrut() );
706 }
707
708 /*!
709   Does the layout of the lineedit and the buttons
710 */
711 void QAD_SpinBoxDbl::arrangeWidgets()
712 {
713   if ( !up || !down ) // may happen if the application has a pointer error
714     return;
715   
716   QSize bs; // no, it's short for 'button size'
717   bs.setHeight( height()/2 - frameWidth() );
718   if ( bs.height() < 8 )
719     bs.setHeight( 8 );
720   bs.setWidth( bs.height() * 8 / 5 ); // 1.6 - approximate golden mean
721   setFrameRect( QRect( 0, 0, 0, 0 ) );
722   
723   if ( up->size() != bs || down->size() != bs ) {
724     up->resize( bs );
725     down->resize( bs );
726     updateButtonSymbols();
727   }
728   
729   int y = frameWidth();
730   int x = width() - y - bs.width();
731   up->move( x, y );
732   down->move( x, height() - y - up->height() );
733   vi->setGeometry( frameWidth(), frameWidth(),
734                    x - frameWidth(), height() - 2*frameWidth() );
735 }
736
737 /*!
738   Sets the current value of the spin box to <value>.  This is
739   QRangeControl::setValue() made available as a slot.
740 */
741 void QAD_SpinBoxDbl::setValue( double value )
742 {
743   QDblRangeControl::setValue( value );
744 }
745
746 /*!
747   Increases the current value one step, wrapping as necessary.  This is
748   the same as clicking on the pointing-up button, and can be used for
749   e.g.  keyboard accelerators.
750 */
751 void QAD_SpinBoxDbl::stepUp()
752 {
753   if ( edited )
754     interpretText();
755   if ( wrapping() && ( value()+lineStep() > maxValue() ) )
756     setValue( minValue() );
757   else
758     addLine();
759 }
760
761 /*!
762   Decreases the current value one step, wrapping as necessary.  This is
763   the same as clicking on the pointing-down button, and can be used
764   for e.g.  keyboard accelerators.
765 */
766 void QAD_SpinBoxDbl::stepDown()
767 {
768   if ( edited )
769     interpretText();
770   if ( wrapping() && ( value()-lineStep() < minValue() ) )
771     setValue( maxValue() );
772   else
773     subtractLine();
774 }
775
776 /*!
777   Intercepts and handles those events coming to the embedded QLineEdit
778   which have special meaning for the QAD_SpinBoxDbl.
779 */
780 bool QAD_SpinBoxDbl::eventFilter( QObject* obj, QEvent* ev )
781 {
782   if ( obj != vi )
783     return FALSE;
784   
785   if ( ev->type() == QEvent::KeyPress ) {
786     QKeyEvent* k = (QKeyEvent*)ev;
787     
788     bool retval = FALSE; // workaround for MSVC++ optimization bug
789     if( (k->key() == Key_Tab) || (k->key() == Key_BackTab) ){
790       if ( edited )
791         interpretText();
792       qApp->sendEvent( this, ev );
793       retval = TRUE;
794     } if ( k->key() == Key_Up ) {
795       stepUp();
796       retval = TRUE;
797     } else if ( k->key() == Key_Down ) {
798       stepDown();
799       retval = TRUE;
800     } else if ( k->key() == Key_Return ) {
801       interpretText();
802       return FALSE;
803     }
804     if ( retval )
805       return retval;
806   } else if ( ev->type() == QEvent::FocusOut || ev->type() == QEvent::Leave || ev->type() == QEvent::Hide ) {
807     if ( edited ) {
808       interpretText();
809     }
810     return FALSE;
811   }
812   return FALSE;
813 }
814
815 /*!
816   Reimplementation
817 */
818 void QAD_SpinBoxDbl::leaveEvent( QEvent* )
819 {
820   if ( edited )
821     interpretText();
822 }
823
824 /*!
825   Reimplementation
826 */
827 void QAD_SpinBoxDbl::resizeEvent( QResizeEvent* )
828 {
829   arrangeWidgets();
830 }
831
832 /*!
833   Reimplementation
834 */
835 void QAD_SpinBoxDbl::wheelEvent( QWheelEvent * e )
836 {
837   e->accept();
838   static float offset = 0;
839   static QAD_SpinBoxDbl* offset_owner = 0;
840   if (offset_owner != this) {
841     offset_owner = this;
842     offset = 0;
843   }
844   offset += -e->delta()/120;
845   if (QABS(offset) < 1)
846     return;
847   int ioff = int(offset);
848   int i;
849   for (i=0; i<QABS(ioff); i++)
850     offset > 0 ? stepDown() : stepUp();
851   offset -= ioff;
852 }
853
854 /*!
855   This method gets called by QRangeControl whenever the value has changed.
856   Updates the display and emits the valueChanged() signals.
857 */
858 void QAD_SpinBoxDbl::valueChange()
859 {
860   selreq = hasFocus();
861   updateDisplay();
862   selreq = FALSE;
863   emit valueChanged( value() );
864   emit valueChanged( currentValueText() );
865 }
866
867 /*!
868   This method gets called by QRangeControl whenever the range has
869   changed.  It adjusts the default validator and updates the display.
870 */
871 void QAD_SpinBoxDbl::rangeChange()
872 {
873   updateDisplay();
874 }
875
876 /*!
877   Sets the validator to <v>.  The validator controls what keyboard
878   input is accepted when the user is editing in the value field.  The
879   default is to use a suitable QIntValidator.
880 */
881 void QAD_SpinBoxDbl::setValidator( const QValidator* v )
882 {
883   if ( vi )
884     vi->setValidator( v );
885 }
886 /*!
887   Returns the validator which constrains editing for this spin box if
888   there is any, or else 0.
889 */
890 const QValidator* QAD_SpinBoxDbl::validator() const
891 {
892   return vi ? vi->validator() : 0;
893 }
894
895 /*!
896   Updates the contents of the embedded QLineEdit to reflect current
897   value, using mapValueToText().  Also enables/disables the push
898   buttons accordingly.
899 */
900 void QAD_SpinBoxDbl::updateDisplay()
901 {
902   vi->setUpdatesEnabled( FALSE );
903   vi->setText( currentValueText() );
904   if ( selreq && isVisible() && ( hasFocus() || vi->hasFocus() ) ) {
905     selectAll();
906   } else {
907     if ( !suffix().isEmpty() && 
908          endsWith(vi->text(), suffix()) )
909       vi->setCursorPosition( vi->text().length() - suffix().length() );
910   }
911   vi->setUpdatesEnabled( TRUE );
912   vi->repaint( FALSE ); // immediate repaint needed for some reason
913   edited = FALSE;
914  
915   up->setEnabled( isEnabled() && (wrapping() || value() < maxValue()) );
916   down->setEnabled( isEnabled() && (wrapping() || value() > minValue()) );
917   vi->setEnabled( isEnabled() );
918 }
919
920 /*!
921   QAD_SpinBoxDbl calls this after the user has manually edited the contents
922   of the spin box (not using the up/down buttons/keys).
923   The default implementation of this function interprets the new text
924   using mapTextToValue().  If mapTextToValue() is successful, it
925   changes the spin box' value.  If not the value if left unchanged.
926 */
927 void QAD_SpinBoxDbl::interpretText()
928 {
929   bool ok = TRUE;
930   bool done = FALSE;
931   double newVal = 0;
932   if ( !specialValueText().isEmpty() ) {
933     QString s = QString(text()).stripWhiteSpace();
934     QString t = QString(specialValueText()).stripWhiteSpace();
935     if ( s == t ) {
936       newVal = minValue();
937       done = TRUE;
938     }
939   }
940   if ( !done )
941     newVal = mapTextToValue( &ok );
942   if ( ok )
943     setValue( newVal );
944   updateDisplay();    // Sometimes redundant
945 }
946
947 /*!
948   Returns a pointer to the embedded 'up' button.
949 */
950
951 QPushButton* QAD_SpinBoxDbl::upButton() const
952 {
953   return up;
954 }
955
956 /*!
957   Returns a pointer to the embedded 'down' button.
958 */
959 QPushButton* QAD_SpinBoxDbl::downButton() const
960 {
961   return down;
962 }
963
964 /*!
965   Returns a pointer to the embedded QLineEdit.
966 */
967 QLineEdit* QAD_SpinBoxDbl::editor() const
968 {
969   return vi;
970 }
971
972 /*!
973   This slot gets called whenever the user edits the text of the spin box.
974 */
975 void QAD_SpinBoxDbl::textChanged()
976 {
977   edited = TRUE;  // This flag is cleared in updateDisplay()
978 };
979
980 /*!
981   This virtual function is used by the spin box whenever it needs to
982   display value <v>.  The default implementation returns a string
983   containing <v> printed in the standard way.
984 */
985
986 QString QAD_SpinBoxDbl::mapValueToText( double v )
987 {
988   QString s;
989   s.setNum( v, convertFlag(), precision() );
990   return s;
991 }
992
993 /*!
994   This virtual function is used by the spin box whenever it needs to
995   interpret the text entered by the user as a value.  The default
996   implementation tries to interpret it as an integer in the standard
997   way, and returns the double value.
998 */
999 double QAD_SpinBoxDbl::mapTextToValue( bool* ok )
1000 {
1001   QString s = text();
1002   double newVal = s.toDouble( ok );
1003   if ( !(*ok) && !( !prefix() && !suffix() ) ) {// Try removing any pre/suffix
1004     s = cleanText();
1005     newVal = s.toDouble( ok );
1006   }
1007   return newVal;
1008 }
1009
1010 /*!
1011   Returns the full text calculated from the current value, including any
1012   prefix, suffix or special-value text.
1013 */
1014 QString QAD_SpinBoxDbl::currentValueText()
1015 {
1016   QString s;
1017   if ( (value() <= minValue()) && !specialValueText().isEmpty() ) {
1018     s = specialValueText();
1019   } else {
1020     s = prefix();
1021     s.append( mapValueToText( value() ) );
1022     s.append( suffix() );
1023   }
1024   return s;
1025 }
1026
1027 /*!
1028   Reimplementation
1029 */
1030 void QAD_SpinBoxDbl::setEnabled( bool on )
1031 {
1032   bool b = isEnabled();
1033   QFrame::setEnabled( on );
1034   if ( isEnabled() != b ) {
1035     // ## enabledChange() might have been a better choice
1036     updateDisplay();
1037   }
1038 }
1039
1040 /*!
1041   Reimplementation
1042 */
1043 void QAD_SpinBoxDbl::styleChange( QStyle& old )
1044 {
1045   setFrameStyle( Panel | Sunken );
1046   arrangeWidgets();
1047   QWidget::styleChange( old );
1048 }
1049
1050 /*!  
1051   Sets the spin box to display <newSymbols> on its buttons. 
1052   <newSymbols> can be either <UpDownArrows> (the default) or <PlusMinus>.
1053 */
1054 void QAD_SpinBoxDbl::setButtonSymbols( ButtonSymbols newSymbols )
1055 {
1056   if ( buttonSymbols() == newSymbols )
1057     return;
1058   butSymbols = newSymbols;
1059   updateButtonSymbols();
1060 }
1061
1062 /*!  
1063   Returns the current button symbol mode.  The default is <UpDownArrows>
1064 */
1065 QAD_SpinBoxDbl::ButtonSymbols QAD_SpinBoxDbl::buttonSymbols() const
1066 {
1067   return butSymbols;
1068 }
1069
1070 /*!
1071   This function uses the pixmap cache for a Different Reason: the
1072   pixmap cache also preserves QPixmap::serialNumber().  by doing
1073   this, QButton::setPixmap() is able to avoid flicker e.g. when the
1074   spin box is resized in such a way that the height of the buttons
1075   does not change (common the default size policy).
1076 */
1077 void QAD_SpinBoxDbl::updateButtonSymbols()
1078 {
1079   QString key( QString::fromLatin1( "$qt$QAD_SpinBoxDbl$" ) );
1080   bool pmSym = buttonSymbols() == PlusMinus;
1081   key += QString::fromLatin1( pmSym ? "+-" : "^v" );
1082   key += QString::number( down->height() );
1083   QString upKey = key + QString::fromLatin1( "$up" );
1084   QString dnKey = key + QString::fromLatin1( "$down" );
1085   QBitmap upBm;
1086   QBitmap dnBm;
1087   
1088   bool found = QPixmapCache::find( dnKey, dnBm )
1089     && QPixmapCache::find( upKey, upBm );
1090   
1091   if ( !found ) {
1092     QPainter p;
1093     if ( pmSym ) {
1094       int h = down->height()-4;
1095       if ( h < 3 )
1096         return;
1097       else if ( h == 4 )
1098         h = 3;
1099       else if ( (h > 6) && (h & 1) )
1100         h--;
1101       h -= ( h / 8 ) * 2;    // Empty border
1102       dnBm.resize( h, h );
1103       p.begin( &dnBm );
1104       p.eraseRect( 0, 0, h, h );
1105       p.setBrush( color1 );
1106       int c = h/2;
1107       p.drawLine( 0, c, h, c );
1108       if ( !(h & 1) )
1109         p.drawLine( 0, c-1, h, c-1 );
1110       p.end();
1111       upBm = dnBm;
1112       p.begin( &upBm );
1113       p.drawLine( c, 0, c, h );
1114       if ( !(h & 1) )
1115         p.drawLine( c-1, 0, c-1, h );
1116       p.end();
1117     }
1118     else {
1119       int w = down->width()-4;
1120       if ( w < 3 )
1121         return;
1122       else if ( !(w & 1) )
1123         w--;
1124       w -= ( w / 7 ) * 2;    // Empty border
1125       int h = w/2 + 2;        // Must have empty row at foot of arrow
1126       dnBm.resize( w, h );
1127       p.begin( &dnBm );
1128       p.eraseRect( 0, 0, w, h );
1129       QPointArray a;
1130       a.setPoints( 3,  0, 1,  w-1, 1,  h-2, h-1 );
1131       p.setBrush( color1 );
1132       p.drawPolygon( a );
1133       p.end();
1134 #ifndef QT_NO_TRANSFORMATIONS
1135       QWMatrix wm;
1136       wm.scale( 1, -1 );
1137       upBm = dnBm.xForm( wm );
1138 #else
1139       upBm.resize( w, h );
1140       p.begin( &upBm );
1141       p.eraseRect( 0, 0, w, h );
1142       a.setPoints( 3,  0, h-2,  w-1, h-2,  h-2, 0 );
1143       p.setBrush( color1 );
1144       p.drawPolygon( a );
1145       p.end();
1146 #endif
1147     }
1148     QPixmapCache::insert( dnKey, dnBm );
1149     QPixmapCache::insert( upKey, upBm );
1150   }
1151   down->setPixmap( dnBm );
1152   up->setPixmap( upBm );
1153 }
1154
1155 /*!
1156   Returns minimum value, reimplementaion
1157 */
1158 double QAD_SpinBoxDbl::minValue() 
1159 {
1160   return QDblRangeControl::minValue();  
1161 }
1162
1163 /*!
1164   Returns maximum value, reimplementaion
1165 */
1166 double QAD_SpinBoxDbl::maxValue() 
1167 {
1168   return QDblRangeControl::maxValue();  
1169 }
1170
1171 /*!
1172   Sets minimum value, reimplementaion
1173 */
1174 void QAD_SpinBoxDbl::setMinValue( double minValue ) 
1175 {
1176   QDblRangeControl::setMinValue( minValue );
1177 }
1178
1179 /*!
1180   Sets maximum value, reimplementaion
1181 */
1182 void QAD_SpinBoxDbl::setMaxValue( double maxValue ) 
1183 {
1184   QDblRangeControl::setMaxValue( maxValue );
1185 }
1186
1187 /*!
1188   Returns step size, reimplementaion
1189 */
1190 double QAD_SpinBoxDbl::lineStep() 
1191 {
1192   return QDblRangeControl::lineStep();  
1193 }
1194
1195 /*!
1196   Sets step size
1197 */
1198 void QAD_SpinBoxDbl::setLineStep( double step ) 
1199 {
1200   setSteps( step, pageStep() );
1201 }
1202
1203 /*!
1204   Returns value of the spin box, reimplementaion
1205 */
1206 double QAD_SpinBoxDbl::value()
1207 {
1208   QAD_SpinBoxDbl* that = ( QAD_SpinBoxDbl* ) this;
1209   if ( edited ) {
1210     that->edited = FALSE;  // avoid recursion
1211     that->interpretText();
1212   }
1213   return QDblRangeControl::value();
1214 }
1215
1216 /*! 
1217   Selects all the text in the editor of the spinbox. 
1218 */
1219 void QAD_SpinBoxDbl::selectAll()
1220 {
1221   int overhead = prefix().length() + suffix().length();
1222   if ( !overhead || currentValueText() == specialValueText() ) {
1223     vi->selectAll();
1224   } else {
1225     vi->setSelection( prefix().length(), vi->text().length() - overhead );
1226   }
1227 }