Salome HOME
e00944da675f69148f34649aba0e513d8f64f60c
[modules/gui.git] / src / Qtx / QtxDblSpinBox.cxx
1 // File:      QtxDblSpinBox.cxx
2 // Author:    Sergey TELKOV
3
4 #include "QtxDblSpinBox.h"
5
6 #include <qlineedit.h>
7 #include <qvalidator.h>
8 #include <qapplication.h>
9
10 /*
11         Class: QtxDblSpinBox::Validator [internal]
12         Descr: Validator for QtxDblSpinBox (getted from Trolltech Qt - SpinBoxValidator)
13 */
14
15 class QtxDblSpinBox::Validator : public QDoubleValidator
16 {
17 public:
18     Validator( QtxDblSpinBox* sb, const char* name )
19         : QDoubleValidator( sb, name ), spinBox( sb ) {}
20
21     virtual State validate( QString& str, int& pos ) const;
22
23 private:
24     QtxDblSpinBox* spinBox;
25 };
26
27 QValidator::State QtxDblSpinBox::Validator::validate( QString& str, int& pos ) const
28 {
29   QString pref = spinBox->prefix();
30   QString suff = spinBox->suffix();
31   uint overhead = pref.length() + suff.length();
32   State state = Invalid;
33
34   if ( overhead == 0 )
35           state = QDoubleValidator::validate( str, pos );
36   else
37         {
38                 if ( str.length() >= overhead && str.startsWith( pref ) &&
39          str.right( suff.length() ) == suff )
40                 {
41                         QString core = str.mid( pref.length(), str.length() - overhead );
42                         int corePos = pos - pref.length();
43                         state = QDoubleValidator::validate( core, corePos );
44                         pos = corePos + pref.length();
45                         str.replace( pref.length(), str.length() - overhead, core );
46                 }
47                 else
48                 {
49                         state = QDoubleValidator::validate( str, pos );
50                         if ( state == Invalid )
51                         {
52                                 QString special = spinBox->specialValueText().stripWhiteSpace();
53                                 QString candidate = str.stripWhiteSpace();
54                                 if ( special.startsWith( candidate ) )
55                                 {
56                                         if ( candidate.length() == special.length() )
57                                                 state = Acceptable;
58                                         else
59                                                 state = Intermediate;
60                                 }
61                         }
62                 }
63   }
64   return state;
65 }
66
67 /*
68         Class: QtxDblSpinBox
69         Descr: Spin box for real numbers.
70 */
71
72 QtxDblSpinBox::QtxDblSpinBox( QWidget* parent, const char* name )
73 : QSpinBox( parent, name ),
74 myCleared( false ),
75 myBlocked( false ),
76 myPrecision( 0 )
77 {
78   myMin = QRangeControl::minValue();
79   myMax = QRangeControl::maxValue();
80   myStep = QRangeControl::lineStep();
81         myValue = myMin;
82   setValidator( new Validator( this, "double_spinbox_validator" ) );
83   rangeChange();
84   updateDisplay();
85
86   connect( editor(), SIGNAL( textChanged( const QString& ) ), this, SLOT( onTextChanged( const QString& ) ) );
87 }
88
89 QtxDblSpinBox::QtxDblSpinBox( double min, double max, double step, QWidget* parent, const char* name )
90 : QSpinBox( parent, name ),
91 myMin( min ),
92 myMax( max ),
93 myStep( step ),
94 myCleared( false ),
95 myBlocked( false ),
96 myPrecision( 0 )
97 {
98         myValue = myMin;
99   setValidator( new Validator( this, "double_spinbox_validator" ) );
100   rangeChange();
101   updateDisplay();
102
103   connect( editor(), SIGNAL( textChanged( const QString& ) ), this, SLOT( onTextChanged( const QString& ) ) );
104 }
105
106 QtxDblSpinBox::~QtxDblSpinBox()
107 {
108 }
109
110 double QtxDblSpinBox::minValue() const
111 {
112   return myMin;
113 }
114
115 double QtxDblSpinBox::maxValue() const
116 {
117   return myMax;
118 }
119
120 void QtxDblSpinBox::setMinValue( int min )
121 {
122         setMinValue( (double)min );
123 }
124
125 void QtxDblSpinBox::setMinValue( double min )
126 {
127   if ( myMin != min )
128   {
129     myMin = min;
130     rangeChange();
131   }
132 }
133
134 void QtxDblSpinBox::setMaxValue( int max )
135 {
136         setMaxValue( (double)max );
137 }
138
139 void QtxDblSpinBox::setMaxValue( double max )
140 {
141   if ( myMax != max )
142   {
143     myMax = max;
144     rangeChange();
145   }
146 }
147
148 void QtxDblSpinBox::setRange( int min, int max )
149 {
150         setRange( (double)min, (double)max );
151 }
152
153 void QtxDblSpinBox::setRange( double min, double max )
154 {
155   if ( myMin != min || myMax != max )
156   {
157     myMin = min;
158     myMax = max;
159     rangeChange();
160   }
161 }
162
163 double QtxDblSpinBox::lineStep() const
164 {
165   return myStep;
166 }
167
168 void QtxDblSpinBox::setLineStep( int step )
169 {
170   setLineStep( (double)step );
171 }
172
173 void QtxDblSpinBox::setLineStep( double step )
174 {
175   myStep = step;
176 }
177
178 double QtxDblSpinBox::value() const
179 {
180   QSpinBox::value();
181
182   return myValue;
183 }
184
185 void QtxDblSpinBox::setValue( int val )
186 {
187         setValue( (double)val );
188 }
189
190 void QtxDblSpinBox::setValue( double val )
191 {
192         myCleared = false;
193   double prevVal = myValue;
194   myValue = bound( val );
195   if ( prevVal != myValue )
196     valueChange();
197 }
198
199 void QtxDblSpinBox::stepUp()
200 {
201         interpretText();
202         if ( wrapping() && myValue + myStep > myMax )
203                 setValue( myMin );
204         else
205                 setValue( myValue + myStep );
206 }
207
208 void QtxDblSpinBox::stepDown()
209 {
210         interpretText();
211         if ( wrapping() && myValue - myStep < myMin )
212                 setValue( myMax );
213         else
214                 setValue( myValue - myStep );
215 }
216
217 int QtxDblSpinBox::precision() const
218 {
219         return myPrecision;
220 }
221
222 void QtxDblSpinBox::setPrecision( const int prec )
223 {
224         int newPrec = QMAX( prec, 0 );
225         int oldPrec = QMAX( myPrecision, 0 );
226         myPrecision = prec;
227         if ( newPrec != oldPrec )
228                 updateDisplay();
229 }
230
231 bool QtxDblSpinBox::isCleared() const
232 {
233         return myCleared;
234 }
235
236 void QtxDblSpinBox::setCleared( const bool on )
237 {
238         if ( myCleared == on )
239                 return;
240
241         myCleared = on;
242         updateDisplay();
243 }
244
245 void QtxDblSpinBox::selectAll()
246 {
247 #if QT_VER >= 3
248         QSpinBox::selectAll();
249 #else
250   editor()->selectAll();
251 #endif
252 }
253
254 bool QtxDblSpinBox::eventFilter( QObject* o, QEvent* e )
255 {
256   if ( !myCleared || o != editor() || !editor()->text().stripWhiteSpace().isEmpty() )
257     return QSpinBox::eventFilter( o, e );
258
259   if ( e->type() == QEvent::FocusOut || e->type() == QEvent::Leave || e->type() == QEvent::Hide )
260     return false;
261
262   if ( e->type() == QEvent::KeyPress &&
263           ( ((QKeyEvent*)e)->key() == Key_Tab || ((QKeyEvent*)e)->key() == Key_BackTab ) )
264   {
265     QApplication::sendEvent( this, e );
266     return true;
267   }
268
269   return QSpinBox::eventFilter( o, e );
270 }
271
272 void QtxDblSpinBox::updateDisplay()
273 {
274   if ( myBlocked )
275     return;
276
277   bool upd = editor()->isUpdatesEnabled();
278   editor()->setUpdatesEnabled( false );
279
280   bool isBlock = myBlocked;
281   myBlocked = true;
282     
283   QString txt = currentValueText();
284     
285   if ( myValue >= myMax )
286     QSpinBox::setValue( QSpinBox::maxValue() );
287   else if ( myValue <= myMin )
288     QSpinBox::setValue( QSpinBox::minValue() );
289   else
290     QSpinBox::setValue( ( QSpinBox::minValue() + QSpinBox::maxValue() ) / 2 );
291   
292   QSpinBox::updateDisplay();
293
294   editor()->setUpdatesEnabled( upd );
295
296   editor()->setText( myCleared ? QString::null : txt );
297   if ( !myCleared && editor()->hasFocus() )
298   {
299     if ( editor()->text() == specialValueText() )
300       editor()->selectAll();
301     else
302       editor()->setSelection( prefix().length(), editor()->text().length() - prefix().length() - suffix().length() );
303   }
304
305   myBlocked = isBlock;
306 }
307
308 void QtxDblSpinBox::interpretText()
309 {
310   myCleared = false;
311
312   bool ok = true;
313   bool done = false;
314   double newVal = 0;
315   if ( !specialValueText().isEmpty() )
316   {
317           QString s = QString( text() ).stripWhiteSpace();
318           QString t = QString( specialValueText() ).stripWhiteSpace();
319           if ( s == t )
320     {
321       newVal = minValue();
322             done = true;
323     }
324   }
325   if ( !done )
326           newVal = mapTextToDoubleValue( &ok );
327   if ( ok )
328           setValue( newVal );
329   updateDisplay();
330 }
331
332 void QtxDblSpinBox::valueChange()
333 {
334   updateDisplay();
335   emit valueChanged( myValue );
336   emit valueChanged( currentValueText() );
337 }
338
339 void QtxDblSpinBox::rangeChange()
340 {
341   double min = QMIN( myMin, myMax );
342   double max = QMAX( myMin, myMax );
343   myMin = min;
344   myMax = max;
345   if ( validator()->inherits( "QDoubleValidator" ) )
346     ((QDoubleValidator*)validator())->setRange( myMin, myMax );
347
348         if ( myMin == myMax )
349                 QSpinBox::setRange( 0, 0 );
350         else
351                 QSpinBox::setRange( 0, 2 );
352
353   setValue( myValue );
354   updateDisplay();
355 }
356
357 QString QtxDblSpinBox::currentValueText()
358 {
359   QString s;
360   if ( (myValue == minValue()) && !specialValueText().isEmpty() )
361           s = specialValueText();
362   else
363         {
364           s = prefix();
365                 s.append( mapValueToText( myValue ) );
366                 s.append( suffix() );
367         }
368   return s;
369 }
370
371 QString QtxDblSpinBox::mapValueToText( double v )
372 {
373         QString s;
374   s.setNum( v, myPrecision >= 0 ? 'f' : 'g', myPrecision == 0 ? 6 : QABS( myPrecision ) );
375   return removeTrailingZeroes( s );
376 }
377
378 QString QtxDblSpinBox::mapValueToText( int )
379 {
380   QString s;
381   s.setNum( myValue, myPrecision >= 0 ? 'f' : 'g', myPrecision == 0 ? 6 : QABS( myPrecision ) );
382   return removeTrailingZeroes( s );
383 }
384
385 double QtxDblSpinBox::mapTextToDoubleValue( bool* ok )
386 {
387   QString s = text();
388   double newVal = s.toDouble( ok );
389   if ( !(*ok) && !( !prefix() && !suffix() ) )
390   {
391           s = cleanText();
392           newVal = s.toDouble( ok );
393   }
394   return newVal;
395 }
396
397 double QtxDblSpinBox::bound( double val )
398 {
399   double newVal = val;
400   if ( newVal > myMax )
401     newVal = myMax;
402   if ( newVal < myMin )
403     newVal = myMin;
404   return newVal;
405 }
406
407 void QtxDblSpinBox::leaveEvent( QEvent* e )
408 {
409         if ( !myCleared )
410                 QSpinBox::leaveEvent( e );
411 }
412
413 void QtxDblSpinBox::wheelEvent( QWheelEvent* e )
414 {
415   if ( !isEnabled() )
416     return;
417
418   QSpinBox::wheelEvent( e );
419   updateDisplay();
420 }
421
422 void QtxDblSpinBox::onTextChanged( const QString& str )
423 {
424   if ( !myBlocked )
425     myCleared = false;
426 }
427
428 QString QtxDblSpinBox::removeTrailingZeroes( const QString& src ) const
429 {
430   QString delim( "." );
431
432   int idx = src.findRev( delim );
433   if ( idx == -1 )
434     return src;
435
436   QString iPart = src.left( idx );
437   QString fPart = src.mid( idx + 1 );
438
439   while ( !fPart.isEmpty() && fPart.at( fPart.length() - 1 ) == '0' )
440     fPart.remove( fPart.length() - 1, 1 );
441
442   QString res = iPart;
443   if ( !fPart.isEmpty() )
444     res += delim + fPart;
445
446   return res;
447 }