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