File indexing completed on 2024-05-12 16:02:12

0001 /* This file is part of the KDE project
0002    SPDX-FileCopyrightText: 2002 Rob Buis (buis@kde.org)
0003    SPDX-FileCopyrightText: 2004 Nicolas GOUTTE <goutte@kde.org>
0004    SPDX-FileCopyrightText: 2007 Thomas Zander <zander@kde.org>
0005 
0006    SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "KoUnitDoubleSpinBox.h"
0010 
0011 #include <KoUnit.h>
0012 
0013 #include <WidgetsDebug.h>
0014 
0015 #include <klocalizedstring.h>
0016 #include <qnumeric.h>
0017 
0018 // #define DEBUG_VALIDATOR
0019 // #define DEBUG_VALUEFROMTEXT
0020 
0021 class Q_DECL_HIDDEN KoUnitDoubleSpinBox::Private
0022 {
0023 public:
0024     Private(double low, double up, double step)
0025         : lowerInPoints(low),
0026         upperInPoints(up),
0027         stepInPoints(step),
0028         unit(KoUnit(KoUnit::Point))
0029     {
0030     }
0031 
0032     double lowerInPoints; ///< lowest value in points
0033     double upperInPoints; ///< highest value in points
0034     double stepInPoints;  ///< step in points
0035     KoUnit unit;
0036 };
0037 
0038 KoUnitDoubleSpinBox::KoUnitDoubleSpinBox( QWidget *parent)
0039     : QDoubleSpinBox( parent ),
0040     d( new Private(-9999, 9999, 1))
0041 {
0042     QDoubleSpinBox::setDecimals( 2 );
0043     //setAcceptLocalizedNumbers( true );
0044     setUnit( KoUnit(KoUnit::Point) );
0045     setAlignment( Qt::AlignRight );
0046 
0047     connect(this, SIGNAL(valueChanged(double)), SLOT(privateValueChanged()));
0048 }
0049 
0050 KoUnitDoubleSpinBox::~KoUnitDoubleSpinBox()
0051 {
0052     delete d;
0053 }
0054 
0055 QValidator::State KoUnitDoubleSpinBox::validate(QString &input, int &pos) const
0056 {
0057 #ifdef DEBUG_VALIDATOR
0058     debugWidgets <<"KoUnitDoubleSpinBox::validate :" << input <<" at" << pos;
0059 #else
0060     Q_UNUSED(pos);
0061 #endif
0062 
0063     QRegExp regexp ("([ a-zA-Z]+)$"); // Letters or spaces at end
0064     const int res = input.indexOf( regexp );
0065 
0066     if ( res == -1 )
0067     {
0068         // Nothing like an unit? The user is probably editing the unit
0069 #ifdef DEBUG_VALIDATOR
0070         debugWidgets <<"Intermediate (no unit)";
0071 #endif
0072         return QValidator::Intermediate;
0073     }
0074 
0075     // ### TODO: are all the QString::trimmed really necessary?
0076     const QString number ( input.left( res ).trimmed() );
0077     const QString unitName ( regexp.cap( 1 ).trimmed().toLower() );
0078 
0079 #ifdef DEBUG_VALIDATOR
0080     debugWidgets <<"Split:" << number <<":" << unitName <<":";
0081 #endif
0082 
0083     const double value = valueFromText( number );
0084     double newVal = 0.0;
0085     if (!qIsNaN(value)) {
0086         bool ok;
0087         const KoUnit unit = KoUnit::fromSymbol(unitName, &ok);
0088         if ( ok )
0089             newVal = unit.fromUserValue( value );
0090         else
0091         {
0092             // Probably the user is trying to edit the unit
0093 #ifdef DEBUG_VALIDATOR
0094             debugWidgets <<"Intermediate (unknown unit)";
0095 #endif
0096             return QValidator::Intermediate;
0097         }
0098     }
0099     else
0100     {
0101         warnWidgets << "Not a number: " << number;
0102         return QValidator::Invalid;
0103     }
0104     newVal = d->unit.toUserValuePrecise(newVal);
0105     //input = textFromValue( newVal ); // don't overwrite for now; the effect is not exactly what I expect...
0106 
0107     return QValidator::Acceptable;
0108 }
0109 
0110 void KoUnitDoubleSpinBox::changeValue( double val )
0111 {
0112     QDoubleSpinBox::setValue( d->unit.toUserValue( val ) );
0113     // TODO: emit valueChanged ONLY if the value was out-of-bounds
0114     // This will allow the 'user' dialog to set a dirty bool and ensure
0115     // a proper value is getting saved.
0116 }
0117 
0118 void KoUnitDoubleSpinBox::privateValueChanged() {
0119     emit valueChangedPt( value () );
0120 }
0121 
0122 void KoUnitDoubleSpinBox::setUnit( const KoUnit &unit )
0123 {
0124     if (unit == d->unit) return;
0125 
0126     double oldvalue = d->unit.fromUserValue( QDoubleSpinBox::value() );
0127     QDoubleSpinBox::setMinimum( unit.toUserValue( d->lowerInPoints ) );
0128     QDoubleSpinBox::setMaximum( unit.toUserValue( d->upperInPoints ) );
0129 
0130     qreal step = unit.toUserValue( d->stepInPoints );
0131 
0132     if (unit.type() == KoUnit::Pixel) {
0133         // limit the pixel step by 1.0
0134         step = qMax(qreal(1.0), step);
0135     }
0136 
0137     QDoubleSpinBox::setSingleStep( step );
0138     d->unit = unit;
0139     QDoubleSpinBox::setValue(unit.toUserValuePrecise(oldvalue));
0140     setSuffix(unit.symbol().prepend(QLatin1Char(' ')));
0141 }
0142 
0143 double KoUnitDoubleSpinBox::value( ) const
0144 {
0145     return d->unit.fromUserValue( QDoubleSpinBox::value() );
0146 }
0147 
0148 void KoUnitDoubleSpinBox::setMinimum( double min )
0149 {
0150   d->lowerInPoints = min;
0151   QDoubleSpinBox::setMinimum( d->unit.toUserValue( min ) );
0152 }
0153 
0154 void KoUnitDoubleSpinBox::setMaximum( double max )
0155 {
0156   d->upperInPoints = max;
0157   QDoubleSpinBox::setMaximum( d->unit.toUserValue( max ) );
0158 }
0159 
0160 void KoUnitDoubleSpinBox::setLineStep( double step )
0161 {
0162   d->stepInPoints = KoUnit(KoUnit::Point).toUserValue(step);
0163   QDoubleSpinBox::setSingleStep( step );
0164 }
0165 
0166 void KoUnitDoubleSpinBox::setLineStepPt( double step )
0167 {
0168   d->stepInPoints = step;
0169   QDoubleSpinBox::setSingleStep( d->unit.toUserValue( step ) );
0170 }
0171 
0172 void KoUnitDoubleSpinBox::setMinMaxStep( double min, double max, double step )
0173 {
0174   setMinimum( min );
0175   setMaximum( max );
0176   setLineStepPt( step );
0177 }
0178 
0179 QString KoUnitDoubleSpinBox::textFromValue( double value ) const
0180 {
0181     //debugWidgets <<"textFromValue:" << QString::number( value, 'f', 12 ) <<" =>" << num;
0182     //const QString num(QString("%1%2").arg(QLocale().toString(value, d->precision ), m_unit.symbol()));
0183     //const QString num ( QString( "%1").arg( QLocale().toString( value, d->precision )) );
0184     return QLocale().toString( value, 'f', decimals() );
0185 }
0186 
0187 double KoUnitDoubleSpinBox::valueFromText( const QString& str ) const
0188 {
0189     QString str2( str );
0190     str2.remove(d->unit.symbol());
0191     return QLocale().toDouble(str2);
0192 //    QString str2( str );
0193 //    /* KLocale::readNumber wants the thousand separator exactly at 1000.
0194 //       But when editing, it might be anywhere. So we need to remove it. */
0195 //    const QString sep( KGlobal::locale()->thousandsSeparator() );
0196 //    if ( !sep.isEmpty() )
0197 //        str2.remove( sep );
0198 //    str2.remove(d->unit.symbol());
0199 //    bool ok;
0200 //    const double dbl = KGlobal::locale()->readNumber( str2, &ok );
0201 //#ifdef DEBUG_VALUEFROMTEXT
0202 //    if ( ok )
0203 //      debugWidgets <<"valueFromText:" << str <<": => :" << str2 <<": =>" << QString::number( dbl, 'f', 12 );
0204 //    else
0205 //        warnWidgets << "valueFromText error:" << str << ": => :" << str2 << ":";
0206 //#endif
0207 //    return dbl;
0208 }