]> SALOME platform Git repositories - modules/gui.git/blob - src/Qtx/QtxDblSpinBox.cxx
Salome HOME
6c3655240cd0f028138c9ed2b12943bb4cb4f046
[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 #include <float.h>
29
30 /*!
31   \class  QtxDblSpinBox::Validator [internal]
32   Validator for QtxDblSpinBox (getted from Trolltech Qt - SpinBoxValidator)
33 */
34 class QtxDblSpinBox::Validator : public QDoubleValidator
35 {
36 public:
37     Validator( QtxDblSpinBox* sb, const char* name )
38         : QDoubleValidator( sb, name ), spinBox( sb ) {}
39
40     virtual State validate( QString& str, int& pos ) const;
41
42 private:
43     QtxDblSpinBox* spinBox;
44 };
45
46
47 /*!
48   Checks string and \return QValidator::State
49 */
50 QValidator::State QtxDblSpinBox::Validator::validate( QString& str, int& pos ) const
51 {
52   QString pref = spinBox->prefix();
53   QString suff = spinBox->suffix();
54   uint overhead = pref.length() + suff.length();
55   State state = Invalid;
56
57   if ( overhead == 0 )
58           state = QDoubleValidator::validate( str, pos );
59   else
60         {
61                 if ( str.length() >= overhead && str.startsWith( pref ) &&
62          str.right( suff.length() ) == suff )
63                 {
64                         QString core = str.mid( pref.length(), str.length() - overhead );
65                         int corePos = pos - pref.length();
66                         state = QDoubleValidator::validate( core, corePos );
67                         pos = corePos + pref.length();
68                         str.replace( pref.length(), str.length() - overhead, core );
69                 }
70                 else
71                 {
72                         state = QDoubleValidator::validate( str, pos );
73                         if ( state == Invalid )
74                         {
75                                 QString special = spinBox->specialValueText().stripWhiteSpace();
76                                 QString candidate = str.stripWhiteSpace();
77                                 if ( special.startsWith( candidate ) )
78                                 {
79                                         if ( candidate.length() == special.length() )
80                                                 state = Acceptable;
81                                         else
82                                                 state = Intermediate;
83                                 }
84                         }
85                 }
86   }
87   return state;
88 }
89
90 /*!
91   Constructor
92 */
93 QtxDblSpinBox::QtxDblSpinBox( QWidget* parent, const char* name )
94 : QSpinBox( parent, name ),
95 myCleared( false ),
96 myBlocked( false ),
97 myPrecision( 0 )
98 {
99   myMin = -DBL_MAX;
100   myMax = DBL_MAX;
101   myStep = QRangeControl::lineStep();
102         myValue = 0;
103   setValidator( new Validator( this, "double_spinbox_validator" ) );
104   rangeChange();
105   updateDisplay();
106
107   connect( editor(), SIGNAL( textChanged( const QString& ) ), this, SLOT( onTextChanged( const QString& ) ) );
108 }
109
110 /*!
111   Constructor
112 */
113 QtxDblSpinBox::QtxDblSpinBox( double min, double max, double step, QWidget* parent, const char* name )
114 : QSpinBox( parent, name ),
115 myMin( min ),
116 myMax( max ),
117 myStep( step ),
118 myCleared( false ),
119 myBlocked( false ),
120 myPrecision( 0 )
121 {
122         myValue = myMin;
123   setValidator( new Validator( this, "double_spinbox_validator" ) );
124   rangeChange();
125   updateDisplay();
126
127   connect( editor(), SIGNAL( textChanged( const QString& ) ), this, SLOT( onTextChanged( const QString& ) ) );
128 }
129
130 /*!
131   Destructor
132 */
133 QtxDblSpinBox::~QtxDblSpinBox()
134 {
135 }
136
137 /*!
138   \return min value of spin box
139 */
140 double QtxDblSpinBox::minValue() const
141 {
142   return myMin;
143 }
144
145 /*!
146   \return max value of spin box
147 */
148 double QtxDblSpinBox::maxValue() const
149 {
150   return myMax;
151 }
152
153 /*!
154   Changes min value of spin box
155   \param min - new min value
156 */
157 void QtxDblSpinBox::setMinValue( int min )
158 {
159         setMinValue( (double)min );
160 }
161
162 /*!
163   Changes min value of spin box
164   \param min - new min value
165 */
166 void QtxDblSpinBox::setMinValue( double min )
167 {
168   if ( myMin != min )
169   {
170     myMin = min;
171     rangeChange();
172   }
173 }
174
175 /*!
176   Changes max value of spin box
177   \param max - new max value
178 */
179 void QtxDblSpinBox::setMaxValue( int max )
180 {
181         setMaxValue( (double)max );
182 }
183
184 /*!
185   Changes max value of spin box
186   \param max - new max value
187 */
188 void QtxDblSpinBox::setMaxValue( double max )
189 {
190   if ( myMax != max )
191   {
192     myMax = max;
193     rangeChange();
194   }
195 }
196
197 /*!
198   Changes min and max value of spin box
199   \param min - new min value
200   \param max - new max value
201 */
202 void QtxDblSpinBox::setRange( int min, int max )
203 {
204         setRange( (double)min, (double)max );
205 }
206
207 /*!
208   Changes min and max value of spin box
209   \param min - new min value
210   \param max - new max value
211 */
212 void QtxDblSpinBox::setRange( double min, double max )
213 {
214   if ( myMin != min || myMax != max )
215   {
216     myMin = min;
217     myMax = max;
218     rangeChange();
219   }
220 }
221
222 /*!
223   \return step of spin box
224 */
225 double QtxDblSpinBox::lineStep() const
226 {
227   return myStep;
228 }
229
230 /*!
231   Changes step of spin box
232   \param step - new step
233 */
234 void QtxDblSpinBox::setLineStep( int step )
235 {
236   setLineStep( (double)step );
237 }
238
239 /*!
240   Changes step of spin box
241   \param step - new step
242 */
243 void QtxDblSpinBox::setLineStep( double step )
244 {
245   myStep = step;
246 }
247
248 /*!
249   \return value of spin box
250 */
251 double QtxDblSpinBox::value() const
252 {
253   QSpinBox::value();
254
255   return myValue;
256 }
257
258 /*!
259   Changes value of spin box
260   \param val - new value of spin box
261 */
262 void QtxDblSpinBox::setValue( int val )
263 {
264         setValue( (double)val );
265 }
266
267 /*!
268   Changes value of spin box
269   \param val - new value of spin box
270 */
271 void QtxDblSpinBox::setValue( double val )
272 {
273         myCleared = false;
274   double prevVal = myValue;
275   myValue = bound( val );
276   if ( prevVal != myValue )
277     valueChange();
278 }
279
280 /*!
281   Adds step to value
282 */
283 void QtxDblSpinBox::stepUp()
284 {
285         interpretText();
286         if ( wrapping() && myValue + myStep > myMax )
287                 setValue( myMin );
288         else
289                 setValue( myValue + myStep );
290 }
291
292 /*!
293   Subtracks step from value
294 */
295 void QtxDblSpinBox::stepDown()
296 {
297         interpretText();
298         if ( wrapping() && myValue - myStep < myMin )
299                 setValue( myMax );
300         else
301                 setValue( myValue - myStep );
302 }
303
304 /*!
305   \return number of digit after comma
306 */
307 int QtxDblSpinBox::precision() const
308 {
309         return myPrecision;
310 }
311
312 /*!
313   Changes number of digit after comma
314   \param prec - new digit number
315 */
316 void QtxDblSpinBox::setPrecision( const int prec )
317 {
318         int newPrec = QMAX( prec, 0 );
319         int oldPrec = QMAX( myPrecision, 0 );
320         myPrecision = prec;
321         if ( newPrec != oldPrec )
322                 updateDisplay();
323 }
324
325 /*!
326   \return true if spin box is cleared
327 */
328 bool QtxDblSpinBox::isCleared() const
329 {
330         return myCleared;
331 }
332
333 /*!
334   Changes cleared status of spin box
335   \param on - new status
336 */
337 void QtxDblSpinBox::setCleared( const bool on )
338 {
339         if ( myCleared == on )
340                 return;
341
342         myCleared = on;
343         updateDisplay();
344 }
345
346 /*!
347   Selects all content of spin box editor
348 */
349 void QtxDblSpinBox::selectAll()
350 {
351 #if QT_VER >= 3
352         QSpinBox::selectAll();
353 #else
354   editor()->selectAll();
355 #endif
356 }
357
358 /*!
359   Custom event filter, updates text of spin box editor
360 */
361 bool QtxDblSpinBox::eventFilter( QObject* o, QEvent* e )
362 {
363   if ( !myCleared || o != editor() || !editor()->text().stripWhiteSpace().isEmpty() )
364   {
365     bool state = QSpinBox::eventFilter( o, e );
366     if ( e->type() == QEvent::FocusOut && o == editor() )
367       updateDisplay();
368     return state;
369   }
370
371   if ( e->type() == QEvent::FocusOut || e->type() == QEvent::Leave || e->type() == QEvent::Hide )
372     return false;
373
374   if ( e->type() == QEvent::KeyPress &&
375           ( ((QKeyEvent*)e)->key() == Key_Tab || ((QKeyEvent*)e)->key() == Key_BackTab ) )
376   {
377     QApplication::sendEvent( this, e );
378     return true;
379   }
380
381   return QSpinBox::eventFilter( o, e );
382 }
383
384 /*!
385   Updates text of editor
386 */
387 void QtxDblSpinBox::updateDisplay()
388 {
389   if ( myBlocked )
390     return;
391
392   bool upd = editor()->isUpdatesEnabled();
393   editor()->setUpdatesEnabled( false );
394
395   bool isBlock = myBlocked;
396   myBlocked = true;
397     
398   QString txt = currentValueText();
399     
400   if ( myValue >= myMax )
401     QSpinBox::setValue( QSpinBox::maxValue() );
402   else if ( myValue <= myMin )
403     QSpinBox::setValue( QSpinBox::minValue() );
404   else
405     QSpinBox::setValue( ( QSpinBox::minValue() + QSpinBox::maxValue() ) / 2 );
406   
407   QSpinBox::updateDisplay();
408
409   editor()->setUpdatesEnabled( upd );
410
411   editor()->setText( myCleared ? QString::null : txt );
412   if ( !myCleared && editor()->hasFocus() )
413   {
414     if ( editor()->text() == specialValueText() )
415       editor()->selectAll();
416     else
417       editor()->setSelection( prefix().length(), editor()->text().length() - prefix().length() - suffix().length() );
418   }
419   else
420     editor()->setCursorPosition( 0 );
421
422   myBlocked = isBlock;
423 }
424
425 /*!
426   Sets double value by text in editor
427 */
428 void QtxDblSpinBox::interpretText()
429 {
430   myCleared = false;
431
432   bool ok = true;
433   bool done = false;
434   double newVal = 0;
435   if ( !specialValueText().isEmpty() )
436   {
437           QString s = QString( text() ).stripWhiteSpace();
438           QString t = QString( specialValueText() ).stripWhiteSpace();
439           if ( s == t )
440     {
441       newVal = minValue();
442             done = true;
443     }
444   }
445   if ( !done )
446           newVal = mapTextToDoubleValue( &ok );
447   if ( ok )
448           setValue( newVal );
449   updateDisplay();
450 }
451
452 /*!
453   Emits signal "valueChanged"
454 */
455 void QtxDblSpinBox::valueChange()
456 {
457   updateDisplay();
458   emit valueChanged( myValue );
459   emit valueChanged( currentValueText() );
460 }
461
462 /*!
463   Attune parameters on range changing
464 */
465 void QtxDblSpinBox::rangeChange()
466 {
467   double min = QMIN( myMin, myMax );
468   double max = QMAX( myMin, myMax );
469   myMin = min;
470   myMax = max;
471   QDoubleValidator* v = ::qt_cast<QDoubleValidator*>( validator() );
472   if ( v )
473     v->setRange( myMin, myMax );
474
475         if ( myMin == myMax )
476                 QSpinBox::setRange( 0, 0 );
477         else
478                 QSpinBox::setRange( 0, 2 );
479
480   setValue( myValue );
481   updateDisplay();
482 }
483
484 /*!
485   \return text of editor
486 */
487 QString QtxDblSpinBox::currentValueText()
488 {
489   QString s;
490   if ( (myValue == minValue()) && !specialValueText().isEmpty() )
491           s = specialValueText();
492   else
493         {
494           s = prefix();
495                 s.append( mapValueToText( myValue ) );
496                 s.append( suffix() );
497         }
498   return s;
499 }
500
501 /*!
502   Converts number to string
503   \param v - number to be converted
504 */
505 QString QtxDblSpinBox::mapValueToText( double v )
506 {
507         QString s;
508   s.setNum( v, myPrecision >= 0 ? 'f' : 'g', myPrecision == 0 ? 6 : QABS( myPrecision ) );
509   return removeTrailingZeroes( s );
510 }
511
512 /*!
513   Converts value to string
514 */
515 QString QtxDblSpinBox::mapValueToText( int )
516 {
517   QString s;
518   s.setNum( myValue, myPrecision >= 0 ? 'f' : 'g', myPrecision == 0 ? 6 : QABS( myPrecision ) );
519   return removeTrailingZeroes( s );
520 }
521
522 /*!
523   Converts current text of editor to double
524 */
525 double QtxDblSpinBox::mapTextToDoubleValue( bool* ok )
526 {
527   QString s = text();
528   double newVal = s.toDouble( ok );
529   if ( !(*ok) && !( !prefix() && !suffix() ) )
530   {
531           s = cleanText();
532           newVal = s.toDouble( ok );
533   }
534   return newVal;
535 }
536
537 /*!
538   \return value corrected in accordance with borders
539   \param val - value to be corrected
540 */
541 double QtxDblSpinBox::bound( double val )
542 {
543   double newVal = val;
544   if ( newVal > myMax )
545     newVal = myMax;
546   if ( newVal < myMin )
547     newVal = myMin;
548   return newVal;
549 }
550
551 /*!
552   Custom handler for leave event
553 */
554 void QtxDblSpinBox::leaveEvent( QEvent* e )
555 {
556         if ( !myCleared )
557                 QSpinBox::leaveEvent( e );
558 }
559
560 /*!
561   Custom handler for wheel event
562 */
563 void QtxDblSpinBox::wheelEvent( QWheelEvent* e )
564 {
565   if ( !isEnabled() )
566     return;
567
568   QSpinBox::wheelEvent( e );
569   updateDisplay();
570 }
571
572 /*!
573   SLOT: called if text is changed
574 */
575 void QtxDblSpinBox::onTextChanged( const QString& str )
576 {
577   if ( !myBlocked )
578     myCleared = false;
579 }
580
581 /*!
582   \return string without excess zeros in start and in end
583 */
584 QString QtxDblSpinBox::removeTrailingZeroes( const QString& src ) const
585 {
586   QString delim( "." );
587
588   int idx = src.findRev( delim );
589   if ( idx == -1 )
590     return src;
591
592   QString iPart = src.left( idx );
593   QString fPart = src.mid( idx + 1 );
594
595   while ( !fPart.isEmpty() && fPart.at( fPart.length() - 1 ) == '0' )
596     fPart.remove( fPart.length() - 1, 1 );
597
598   QString res = iPart;
599   if ( !fPart.isEmpty() )
600     res += delim + fPart;
601
602   return res;
603 }