File indexing completed on 2024-12-08 08:10:25

0001 /***************************************************************************
0002  *   Copyright (C) 2003-2006 by David Saxton                               *
0003  *   david@bluehaze.org                                                    *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  ***************************************************************************/
0010 
0011 #include "doublespinbox.h"
0012 #include "cnitem.h"
0013 
0014 #include <KLocalizedString>
0015 
0016 #include <QDebug>
0017 #include <QLineEdit>
0018 #include <QLocale>
0019 #include <QLoggingCategory>
0020 #include <QRegExp>
0021 #include <QTimer>
0022 
0023 #include <algorithm>
0024 #include <cmath>
0025 
0026 using namespace std;
0027 
0028 Q_LOGGING_CATEGORY(KTL_DOUBLESPINBOX_LOG, "org.kde.ktechlab.doublespinbox", QtWarningMsg)
0029 
0030 inline int roundDouble(double val)
0031 {
0032     return (val > 0) ? int(val + 0.5) : int(val - 0.5);
0033 }
0034 
0035 DoubleSpinBox::DoubleSpinBox(double lower, double upper, double minAbs, double value, const QString &unit, QWidget *parent)
0036     : QDoubleSpinBox(parent)
0037 {
0038     qCDebug(KTL_DOUBLESPINBOX_LOG) << " lower=" << lower << " upper=" << upper << " minAbs=" << minAbs << " value=" << value << " unit=" << unit << " parent=" << parent;
0039 
0040     setDecimals(20); // should be enough
0041 
0042     //  m_lastEmittedValue = value;
0043     m_unit = unit;
0044     //  m_minValue = lower;
0045     setMinimum(lower);
0046     //  m_maxValue = upper;
0047     setMaximum(upper);
0048     m_minAbsValue = minAbs;
0049     //  m_queuedSuffix = QString();
0050 
0051     init();
0052     setValue(value);
0053 }
0054 
0055 DoubleSpinBox::DoubleSpinBox(QWidget *parent)
0056     : QDoubleSpinBox(parent)
0057 {
0058     setDecimals(20); // should be enough
0059 
0060     m_lastEmittedValue = 0;
0061     //  m_minValue = 0;
0062     setMinimum(0);
0063     //  m_maxValue = 1e9;
0064     setMaximum(1e9);
0065     m_minAbsValue = 1e-9;
0066     //  m_queuedSuffix = QString();
0067 
0068     init();
0069     setValue(0);
0070 }
0071 
0072 void DoubleSpinBox::init()
0073 {
0074     lineEdit()->setAlignment(Qt::AlignRight);
0075 
0076     //  connect( this, SIGNAL(valueChanged(double)), this, SLOT(checkIfChanged()) );
0077     //  QSpinBox::setMinValue( -(1<<30) );
0078     //  QSpinBox::setMaxValue( +(1<<30) );
0079 
0080     // setValidator( 0 ); // apparently in Qt4 there is no validator
0081 }
0082 
0083 DoubleSpinBox::~DoubleSpinBox()
0084 {
0085 }
0086 
0087 QValidator::State DoubleSpinBox::validate(QString &text, int &pos) const
0088 {
0089     qCDebug(KTL_DOUBLESPINBOX_LOG) << "text = |" << text << "| pos= " << pos;
0090     return QValidator::Acceptable; // QValidator::Intermediate; // don't bother
0091 }
0092 
0093 // double DoubleSpinBox::value()
0094 // {
0095 //     const double mult = getMult();
0096 //     const double displatedNumber = getDisplayedNumber( 0 );
0097 //     const double toRet = displatedNumber * mult ;
0098 //     qCDebug(KTL_DOUBLESPINBOX_LOG) << "value() mult = " << mult;
0099 //     qCDebug(KTL_DOUBLESPINBOX_LOG) << "value() displatedNumber = " << displatedNumber;
0100 //     qCDebug(KTL_DOUBLESPINBOX_LOG) << "value() toRet = " << toRet;
0101 //  return toRet;
0102 // }
0103 
0104 // void DoubleSpinBox::setValue( double value )
0105 // {
0106 //  if ( this->value() == value )
0107 //      return;
0108 //
0109 //  if ( value > maxValue() )
0110 //      value = maxValue();
0111 //
0112 //  else if ( value < minValue() )
0113 //      value = minValue();
0114 //
0115 //  if ( std::abs(value) < m_minAbsValue*0.9999 )
0116 //      value = 0.0;
0117 //
0118 //  updateSuffix( value );
0119 //
0120 //     const int toBeStoredValue = roundDouble( (value / Item::getMultiplier( value )) /* * 100 */ );
0121 //
0122 //     qCDebug(KTL_DOUBLESPINBOX_LOG) << "value = " << value;
0123 //     qCDebug(KTL_DOUBLESPINBOX_LOG) << "value() = " << QSpinBox::value();
0124 //     qCDebug(KTL_DOUBLESPINBOX_LOG) << "to be stored = " << toBeStoredValue;
0125 //
0126 //  QSpinBox::setValue( toBeStoredValue );
0127 // }
0128 
0129 // void DoubleSpinBox::setUnit( const QString & unit )
0130 // {
0131 //  updateSuffix( value() );
0132 //  m_unit = unit;
0133 // }
0134 
0135 void DoubleSpinBox::updateSuffix(double value)
0136 {
0137     QString nextSuffix = " " + CNItem::getNumberMag(value) + m_unit;
0138     setSuffix(nextSuffix);
0139     //  m_queuedSuffix = " " + CNItem::getNumberMag( value ) + m_unit;
0140     //
0141     //  Set suffix to be empty if it is nothing but white space
0142     //  if ( m_queuedSuffix.trimmed().isEmpty() )
0143     //      m_queuedSuffix = "";
0144     //
0145     //  QTimer::singleShot( 0, this, SLOT(setQueuedSuffix()) );
0146 }
0147 
0148 // void DoubleSpinBox::setQueuedSuffix()
0149 // {
0150 //  bool changed = false;
0151 //  if ( !m_queuedSuffix.isNull() && suffix().simplifyWhiteSpace() != m_queuedSuffix.simplifyWhiteSpace() )
0152 //  {
0153 //      setSuffix( m_queuedSuffix );
0154 //      changed = true;
0155 //  }
0156 //  m_queuedSuffix = QString();
0157 //
0158 //  if ( changed )
0159 //      emit valueChanged( value() );
0160 // }
0161 
0162 DoubleSpinBox::StepEnabled DoubleSpinBox::stepEnabled() const
0163 {
0164     return QDoubleSpinBox::StepDownEnabled | QDoubleSpinBox::StepUpEnabled;
0165 }
0166 
0167 void DoubleSpinBox::stepBy(int steps)
0168 {
0169     double workVal = value();
0170     while (steps != 0) {
0171         if (steps > 0) {
0172             workVal = getNextUpStepValue(workVal);
0173             steps--;
0174         } else {
0175             workVal = getNextDownStepValue(workVal);
0176             steps++;
0177         }
0178     }
0179     setValue(workVal);
0180 }
0181 
0182 double DoubleSpinBox::getNextUpStepValue(double in)
0183 {
0184     qCDebug(KTL_DOUBLESPINBOX_LOG) << " in = " << in;
0185 
0186     double value = roundToOneSF(in);
0187 
0188     if (value == 0) {
0189         value = m_minAbsValue;
0190     } else if (value > 0) {
0191         value += std::pow(10., std::floor(std::log10(value)));
0192     } else {
0193         double sub = std::pow(10., std::floor(std::log10(std::abs(value)) - 1));
0194         value += std::pow(10., std::floor(std::log10(std::abs(value) - sub)));
0195     }
0196 
0197     value *= 1.00001;
0198 
0199     if (std::abs(value) < m_minAbsValue) {
0200         value = 0.;
0201     }
0202 
0203     qCDebug(KTL_DOUBLESPINBOX_LOG) << " out = " << value;
0204     return value;
0205 }
0206 
0207 double DoubleSpinBox::getNextDownStepValue(double in)
0208 {
0209     qCDebug(KTL_DOUBLESPINBOX_LOG) << " in = " << in;
0210 
0211     double value = roundToOneSF(in);
0212 
0213     if (value == 0) {
0214         value = -m_minAbsValue;
0215     } else if (value > 0) {
0216         double sub = std::pow(10., std::floor(std::log10(value) - 1));
0217         value -= std::pow(10., std::floor(std::log10(value - sub)));
0218     } else {
0219         double add = std::pow(10., std::floor(std::log10(std::abs(value)) - 1));
0220         value -= std::pow(10., std::floor(std::log10(std::abs(value) + add)));
0221     }
0222 
0223     value *= 1.00001;
0224 
0225     if (std::abs(value) < m_minAbsValue) {
0226         value = 0.;
0227     }
0228 
0229     qCDebug(KTL_DOUBLESPINBOX_LOG) << " out = " << value;
0230 
0231     return value;
0232 }
0233 
0234 // double DoubleSpinBox::getMult()
0235 // {
0236 //  QString text = this->text().trimmed();
0237 //  if ( !m_queuedSuffix.isNull() )
0238 //  {
0239 //      QString nsSuffix = suffix().trimmed();
0240 //
0241 //      if ( nsSuffix.isEmpty() )
0242 //          text.append( m_queuedSuffix );
0243 //      else
0244 //          text.replace( nsSuffix, m_queuedSuffix );
0245 //  }
0246 //
0247 //  if ( text.length() == 0 )
0248 //      return 1.0;
0249 //
0250 //  if ( text.endsWith( m_unit, false ) )
0251 //      text = text.remove( text.length() - m_unit.length(), m_unit.length() );
0252 //
0253 //  text.trimmed();
0254 //
0255 //  QChar siExp = text[ text.length()-1 ];
0256 //
0257 //  if ( siExp.isLetter() || siExp.isSymbol() )
0258 //      return CNItem::getMultiplier((QString)siExp);
0259 //
0260 //  else
0261 //      return 1;
0262 // }
0263 
0264 // double DoubleSpinBox::getDisplayedNumber( bool * ok )
0265 // {
0266 //  KLocale * locale = KGlobal::locale();
0267 //
0268 //  //Fetch the characters that we don't want to discard
0269 //  const QString exclude = locale->decimalSymbol()
0270 //          + locale->thousandsSeparator()
0271 //          + locale->positiveSign()
0272 //          + locale->negativeSign();
0273 //
0274 //  QString number = cleanText().remove( QRegExp("[^"+exclude+"\\d]") );
0275 //
0276 //  return locale->readNumber( number, ok );
0277 // }
0278 
0279 // int DoubleSpinBox::mapTextToValue( bool * ok )
0280 // {
0281 //  (void)ok;
0282 //
0283 //  double value = this->value();
0284 //
0285 //  if ( value > maxValue() )
0286 //      value = maxValue();
0287 //
0288 //  else if ( value < minValue() )
0289 //      value = minValue();
0290 //
0291 //  if ( std::abs(value) < m_minAbsValue*0.9999 )
0292 //      value = 0.0;
0293 //
0294 //  updateSuffix( value );
0295 //
0296 //  value /= Item::getMultiplier( value );
0297 //
0298 //  Precision of 2 extra digits
0299 //  return int( value /* * 100 */ );
0300 // }
0301 //
0302 
0303 double DoubleSpinBox::valueFromText(const QString &text) const
0304 {
0305     qCDebug(KTL_DOUBLESPINBOX_LOG) << "text = " << text;
0306 
0307     QLocale locale;
0308 
0309     // Fetch the characters that we don't want to discard
0310     const QString exclude = QString(locale.decimalPoint()) + locale.groupSeparator() + locale.positiveSign() + locale.negativeSign();
0311 
0312     QString textToStrip(text);
0313     QString numberToRead = textToStrip.remove(QRegExp("[^" + exclude + "\\d]"));
0314 
0315     bool ok;
0316     double value = locale.toDouble(numberToRead, &ok);
0317     if (!ok) {
0318         qCDebug(KTL_DOUBLESPINBOX_LOG) << "numberToRead = |" << numberToRead << "| NOT OK";
0319         value = 0;
0320     }
0321     qCDebug(KTL_DOUBLESPINBOX_LOG) << "numberToRead = " << numberToRead << ", value = " << value;
0322 
0323     if (value > maximum()) {
0324         value = maximum();
0325     } else if (value < minimum()) {
0326         value = minimum();
0327     }
0328 
0329     if (std::abs(value) < m_minAbsValue * 0.9999) {
0330         value = 0.0;
0331     }
0332 
0333     double multiplier = 1.0;
0334     // updateSuffix( value );
0335     QString textForSuffix(text);
0336 
0337     if (textForSuffix.length() != 0) {
0338         if (textForSuffix.endsWith(m_unit, Qt::CaseInsensitive)) {
0339             textForSuffix = textForSuffix.remove(textForSuffix.length() - m_unit.length(), m_unit.length());
0340         }
0341 
0342         textForSuffix = textForSuffix.trimmed();
0343 
0344         QChar siExp;
0345         if (textForSuffix.length() > 0) {
0346             siExp = textForSuffix[textForSuffix.length() - 1];
0347         } else {
0348             siExp = '1';
0349         }
0350 
0351         qCDebug(KTL_DOUBLESPINBOX_LOG) << "SI exp = " << siExp;
0352 
0353         if (siExp.isLetter() || siExp.isSymbol()) {
0354             multiplier = CNItem::getMultiplier(QString(siExp));
0355         } else {
0356             multiplier = 1;
0357         }
0358     }
0359     qCDebug(KTL_DOUBLESPINBOX_LOG) << "multiplier = " << multiplier;
0360 
0361     // value /= Item::getMultiplier( value );
0362     value *= multiplier;
0363 
0364     qCDebug(KTL_DOUBLESPINBOX_LOG) << "value = " << value;
0365 
0366     return value;
0367 }
0368 
0369 //
0370 // QString DoubleSpinBox::mapValueToText( int v )
0371 // {
0372 //  double val = double(v) /* /100.0 */;
0373 //
0374 //  int leftDigits = (int)floor( log10( abs(val) ) ) + 1;
0375 //  if ( leftDigits < 0 )
0376 //      leftDigits = 0;
0377 //  else if ( leftDigits > 3 )
0378 //      leftDigits = 3;
0379 //
0380 //  KLocale * locale = KGlobal::locale();
0381 //  return locale->formatNumber( val, 3-leftDigits );
0382 // }
0383 
0384 QString DoubleSpinBox::textFromValue(double value) const
0385 {
0386     qCDebug(KTL_DOUBLESPINBOX_LOG) << " value = " << value;
0387 
0388     //     int leftDigits = (int)floor( log10( abs( value ) ) ) + 1;
0389     //     if ( leftDigits < 0 ) {
0390     //         leftDigits = 0;
0391     //     } else if ( leftDigits > 3 ) {
0392     //         leftDigits = 3;
0393     //     }
0394     double multiplier = Item::getMultiplier(value);
0395 
0396     double toDisplayNr = value / multiplier;
0397 
0398     qCDebug(KTL_DOUBLESPINBOX_LOG) << "toDisplayNr = " << toDisplayNr;
0399 
0400     QString numberStr = QLocale().toString(toDisplayNr, 'f', 0 /* 3-leftDigits */);
0401 
0402     QString magStr = Item::getNumberMag(value);
0403 
0404     QString toRet = numberStr + " " + magStr + m_unit;
0405 
0406     qCDebug(KTL_DOUBLESPINBOX_LOG) << " text = " << toRet;
0407     return toRet;
0408 }
0409 
0410 // void DoubleSpinBox::checkIfChanged()
0411 // {
0412 //  double newValue = value();
0413 //
0414 //  if ( m_lastEmittedValue == newValue )
0415 //      return;
0416 //
0417 //  m_lastEmittedValue = newValue;
0418 //  emit valueChanged( m_lastEmittedValue );
0419 // }
0420 
0421 double DoubleSpinBox::roundToOneSF(double value)
0422 {
0423     if (value == 0.0)
0424         return 0.0;
0425 
0426     value *= 1.000001;
0427     double tens = pow(10.0, floor(log10(abs(value))));
0428 
0429     return int(value / tens) * tens;
0430 }
0431 
0432 // void DoubleSpinBox::stepUp()
0433 // {
0434 //  double value = roundToOneSF( this->value() );
0435 //
0436 //  if ( value == 0 )
0437 //      value = m_minAbsValue;
0438 //
0439 //  else if ( value > 0 )
0440 //      value += std::pow( 10., std::floor( std::log10(value) ) );
0441 //
0442 //  else
0443 //  {
0444 //      double sub = std::pow(10., std::floor( std::log10(std::abs(value))-1) );
0445 //      value += std::pow( 10., std::floor( std::log10(std::abs(value)-sub) ) );
0446 //  }
0447 //
0448 //  value *= 1.00001;
0449 //
0450 //  if ( std::abs(value) < m_minAbsValue )
0451 //      value = 0.;
0452 //
0453 //  setValue( value );
0454 // }
0455 
0456 // void DoubleSpinBox::stepDown()
0457 // {
0458 //  double value = roundToOneSF( this->value() );
0459 //
0460 //  if ( value == 0 )
0461 //      value = -m_minAbsValue;
0462 //
0463 //  else if ( value > 0 )
0464 //  {
0465 //      double sub = std::pow(10., std::floor( std::log10(value)-1) );
0466 //      value -= std::pow( 10., std::floor( std::log10(value-sub) ) );
0467 //  }
0468 //  else
0469 //  {
0470 //      double add = std::pow(10., std::floor( std::log10(std::abs(value))-1) );
0471 //      value -= std::pow( 10., std::floor( std::log10(std::abs(value)+add) ) );
0472 //  }
0473 //
0474 //  value *= 1.00001;
0475 //
0476 //  if ( std::abs(value) < m_minAbsValue )
0477 //      value = 0.;
0478 //
0479 //  setValue( value );
0480 // }
0481 
0482 #include "moc_doublespinbox.cpp"