File indexing completed on 2024-05-19 15:27:52

0001 /* This file is part of KGraphViewer.
0002    Copyright (C) 2005-2007 Gael de Chalendar <kleag@free.fr>
0003 
0004    KGraphViewer is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU General Public
0006    License as published by the Free Software Foundation, version 2.
0007 
0008    This program is distributed in the hope that it will be useful,
0009    but WITHOUT ANY WARRANTY; without even the implied warranty of
0010    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0011    General Public License for more details.
0012 
0013    You should have received a copy of the GNU General Public License
0014    along with this program; if not, write to the Free Software
0015    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
0016    02110-1301, USA
0017 */
0018 
0019 /* This file was part of the KDE project
0020    Copyright (C) 2005 Jarosław Staniek <staniek@kde.org>
0021 
0022    This program is free software; you can redistribute it and/or
0023    modify it under the terms of the GNU Library General Public
0024    License as published by the Free Software Foundation; either
0025    version 2 of the License, or (at your option) any later version.
0026  */
0027 
0028 #include "KgvUnitWidgets.h"
0029 #include "kgraphviewerlib_debug.h"
0030 
0031 #include <QDebug>
0032 #include <QEvent>
0033 #include <QGridLayout>
0034 #include <QLocale>
0035 #include <qlayout.h>
0036 #include <qpushbutton.h>
0037 
0038 // ----------------------------------------------------------------
0039 //                          Support classes
0040 
0041 KgvUnitDoubleValidator::KgvUnitDoubleValidator(KgvUnitDoubleBase *base, QObject *parent)
0042     : QDoubleValidator(parent)
0043     , m_base(base)
0044 {
0045 }
0046 
0047 QValidator::State KgvUnitDoubleValidator::validate(QString &s, int &pos) const
0048 {
0049     qCDebug(KGRAPHVIEWERLIB_LOG) << "KgvUnitDoubleValidator::validate : " << s << " at " << pos;
0050     QValidator::State result = Acceptable;
0051 
0052     QRegExp regexp("([ a-zA-Z]+)$"); // Letters or spaces at end
0053     const int res = regexp.indexIn(s);
0054 
0055     if (res == -1) {
0056         // Nothing like an unit? The user is probably editing the unit
0057         qCDebug(KGRAPHVIEWERLIB_LOG) << "Intermediate (no unit)";
0058         return Intermediate;
0059     }
0060 
0061     // ### TODO: are all the QString::trimmed really necessary?
0062     const QString number(s.left(res).trimmed());
0063     const QString unitName(regexp.cap(1).trimmed().toLower());
0064 
0065     qCDebug(KGRAPHVIEWERLIB_LOG) << "Split:" << number << ":" << unitName << ":";
0066 
0067     bool ok = false;
0068     const double value = m_base->toDouble(number, &ok);
0069     double newVal = 0.0;
0070     if (ok) {
0071         KgvUnit::Unit unit = KgvUnit::unit(unitName, &ok);
0072         if (ok)
0073             newVal = KgvUnit::fromUserValue(value, unit);
0074         else {
0075             // Probably the user is trying to edit the unit
0076             qCDebug(KGRAPHVIEWERLIB_LOG) << "Intermediate (unknown unit)";
0077             return Intermediate;
0078         }
0079     } else {
0080         qCWarning(KGRAPHVIEWERLIB_LOG) << "Not a number:" << number;
0081         return Invalid;
0082     }
0083 
0084     newVal = KgvUnit::ptToUnit(newVal, m_base->m_unit);
0085 
0086     s = m_base->getVisibleText(newVal);
0087 
0088     return result;
0089 }
0090 
0091 QString KgvUnitDoubleBase::getVisibleText(double value) const
0092 {
0093     const QString num(QString("%1%2").arg(QLocale().toString(value, m_precision), KgvUnit::unitName(m_unit)));
0094     qCDebug(KGRAPHVIEWERLIB_LOG) << "getVisibleText: " << QString::number(value, 'f', 12) << " => " << num;
0095     return num;
0096 }
0097 
0098 double KgvUnitDoubleBase::toDouble(const QString &str, bool *ok) const
0099 {
0100     QString str2(str);
0101     /* KLocale::readNumber wants the thousand separator exactly at 1000.
0102        But when editing, it might be anywhere. So we need to remove it. */
0103     const QString sep(QLocale().groupSeparator());
0104     if (!sep.isEmpty())
0105         str2.remove(sep);
0106     str2.remove(KgvUnit::unitName(m_unit));
0107     const double dbl = QLocale().toDouble(str2, ok);
0108     if (ok)
0109         qCDebug(KGRAPHVIEWERLIB_LOG) << "toDouble:" << str << ": => :" << str2 << ": => " << QString::number(dbl, 'f', 12);
0110     else
0111         qCWarning(KGRAPHVIEWERLIB_LOG) << "error:" << str << ": => :" << str2 << ":";
0112     return dbl;
0113 }
0114 
0115 // ----------------------------------------------------------------
0116 //                          Widget classes
0117 
0118 KgvUnitDoubleSpinBox::KgvUnitDoubleSpinBox(QWidget *parent)
0119     : QDoubleSpinBox(parent)
0120     , KgvUnitDoubleBase(KgvUnit::U_PT, 2)
0121     , m_lowerInPoints(-9999)
0122     , m_upperInPoints(9999)
0123     , m_stepInPoints(1)
0124 {
0125     QDoubleSpinBox::setDecimals(2);
0126     m_validator = new KgvUnitDoubleValidator(this, this);
0127     //     QSpinBox::setValidator( m_validator );
0128     // setAcceptLocalizedNumbers( true );
0129     setUnit(KgvUnit::U_PT);
0130 
0131     connect(this, static_cast<void (KgvUnitDoubleSpinBox::*)(double)>(&KgvUnitDoubleSpinBox::valueChanged), this, &KgvUnitDoubleSpinBox::privateValueChanged);
0132 }
0133 
0134 KgvUnitDoubleSpinBox::KgvUnitDoubleSpinBox(QWidget *parent, double lower, double upper, double step, double value, KgvUnit::Unit unit, unsigned int precision)
0135     : QDoubleSpinBox(parent)
0136     , KgvUnitDoubleBase(unit, precision)
0137     , m_lowerInPoints(lower)
0138     , m_upperInPoints(upper)
0139     , m_stepInPoints(step)
0140 {
0141     setMinimum(lower);
0142     setMaximum(upper);
0143     setSingleStep(step);
0144     setValue(value);
0145     setDecimals(precision);
0146     QDoubleSpinBox::setMinimum(lower);
0147     QDoubleSpinBox::setMaximum(upper);
0148     QDoubleSpinBox::setSingleStep(step);
0149     QDoubleSpinBox::setValue(value);
0150     m_unit = KgvUnit::U_PT;
0151     m_validator = new KgvUnitDoubleValidator(this, this);
0152     //     QSpinBox::setValidator( m_validator );
0153     // setAcceptLocalizedNumbers( true );
0154     setUnit(unit);
0155     changeValue(value);
0156     setLineStep(0.5);
0157 
0158     connect(this, static_cast<void (KgvUnitDoubleSpinBox::*)(double)>(&KgvUnitDoubleSpinBox::valueChanged), this, &KgvUnitDoubleSpinBox::privateValueChanged);
0159 }
0160 
0161 void KgvUnitDoubleSpinBox::changeValue(double val)
0162 {
0163     QDoubleSpinBox::setValue(KgvUnit::toUserValue(val, m_unit));
0164     // TODO: emit valueChanged ONLY if the value was out-of-bounds
0165     // This will allow the 'user' dialog to set a dirty bool and ensure
0166     // a proper value is getting saved.
0167 }
0168 
0169 void KgvUnitDoubleSpinBox::privateValueChanged()
0170 {
0171     emit valueChangedPt(value());
0172 }
0173 
0174 void KgvUnitDoubleSpinBox::setUnit(KgvUnit::Unit unit)
0175 {
0176     double oldvalue = KgvUnit::fromUserValue(QDoubleSpinBox::value(), m_unit);
0177     QDoubleSpinBox::setMinimum(KgvUnit::toUserValue(m_lowerInPoints, unit));
0178     QDoubleSpinBox::setMaximum(KgvUnit::toUserValue(m_upperInPoints, unit));
0179     QDoubleSpinBox::setSingleStep(KgvUnit::toUserValue(m_stepInPoints, unit));
0180     QDoubleSpinBox::setValue(KgvUnit::ptToUnit(oldvalue, unit));
0181     m_unit = unit;
0182     setSuffix(KgvUnit::unitName(unit).prepend(' '));
0183 }
0184 
0185 double KgvUnitDoubleSpinBox::value(void) const
0186 {
0187     return KgvUnit::fromUserValue(QDoubleSpinBox::value(), m_unit);
0188 }
0189 
0190 void KgvUnitDoubleSpinBox::setMinValue(double min)
0191 {
0192     m_lowerInPoints = min;
0193     QDoubleSpinBox::setMinimum(KgvUnit::toUserValue(m_lowerInPoints, m_unit));
0194 }
0195 
0196 void KgvUnitDoubleSpinBox::setMaxValue(double max)
0197 {
0198     m_upperInPoints = max;
0199     QDoubleSpinBox::setMaximum(KgvUnit::toUserValue(m_upperInPoints, m_unit));
0200 }
0201 
0202 void KgvUnitDoubleSpinBox::setLineStep(double step)
0203 {
0204     m_stepInPoints = KgvUnit::toUserValue(step, KgvUnit::U_PT);
0205     QDoubleSpinBox::setSingleStep(step);
0206 }
0207 
0208 void KgvUnitDoubleSpinBox::setLineStepPt(double step)
0209 {
0210     m_stepInPoints = step;
0211     QDoubleSpinBox::setSingleStep(KgvUnit::toUserValue(m_stepInPoints, m_unit));
0212 }
0213 
0214 void KgvUnitDoubleSpinBox::setMinMaxStep(double min, double max, double step)
0215 {
0216     setMinimum(min);
0217     setMaximum(max);
0218     setLineStepPt(step);
0219 }
0220 
0221 // ----------------------------------------------------------------
0222 
0223 KgvUnitDoubleLineEdit::KgvUnitDoubleLineEdit(QWidget *parent)
0224     : QLineEdit(parent)
0225     , KgvUnitDoubleBase(KgvUnit::U_PT, 2)
0226     , m_value(0.0)
0227     , m_lower(0.0)
0228     , m_upper(9999.99)
0229     , m_lowerInPoints(0.0)
0230     , m_upperInPoints(9999.99)
0231 {
0232     setAlignment(Qt::AlignRight);
0233     m_validator = new KgvUnitDoubleValidator(this, this);
0234     setValidator(m_validator);
0235     setUnit(KgvUnit::U_PT);
0236     changeValue(KgvUnit::ptToUnit(0.0, KgvUnit::U_PT));
0237 }
0238 
0239 KgvUnitDoubleLineEdit::KgvUnitDoubleLineEdit(QWidget *parent, double lower, double upper, double value, KgvUnit::Unit unit, unsigned int precision)
0240     : QLineEdit(parent)
0241     , KgvUnitDoubleBase(unit, precision)
0242     , m_value(value)
0243     , m_lower(lower)
0244     , m_upper(upper)
0245     , m_lowerInPoints(lower)
0246     , m_upperInPoints(upper)
0247 {
0248     setAlignment(Qt::AlignRight);
0249     m_validator = new KgvUnitDoubleValidator(this, this);
0250     setValidator(m_validator);
0251     setUnit(unit);
0252     changeValue(KgvUnit::ptToUnit(value, unit));
0253 }
0254 
0255 void KgvUnitDoubleLineEdit::changeValue(double value)
0256 {
0257     m_value = value < m_lower ? m_lower : (value > m_upper ? m_upper : value);
0258     setText(getVisibleText(m_value));
0259 }
0260 
0261 void KgvUnitDoubleLineEdit::setUnit(KgvUnit::Unit unit)
0262 {
0263     KgvUnit::Unit old = m_unit;
0264     m_unit = unit;
0265     m_lower = KgvUnit::ptToUnit(m_lowerInPoints, unit);
0266     m_upper = KgvUnit::ptToUnit(m_upperInPoints, unit);
0267     changeValue(KgvUnit::ptToUnit(KgvUnit::fromUserValue(m_value, old), unit));
0268 }
0269 
0270 bool KgvUnitDoubleLineEdit::eventFilter(QObject *o, QEvent *ev)
0271 {
0272 #if 0
0273     if( ev->type() == QEvent::FocusOut || ev->type() == QEvent::Leave || ev->type() == QEvent::Hide )
0274     {
0275         bool ok;
0276         double value = toDouble( text(), &ok );
0277         changeValue( value );
0278         return false;
0279     }
0280     else
0281 #endif
0282     return QLineEdit::eventFilter(o, ev);
0283 }
0284 
0285 double KgvUnitDoubleLineEdit::value(void) const
0286 {
0287     return KgvUnit::fromUserValue(m_value, m_unit);
0288 }
0289 
0290 // ----------------------------------------------------------------
0291 
0292 KgvUnitDoubleComboBox::KgvUnitDoubleComboBox(QWidget *parent)
0293     : QComboBox(parent)
0294     , KgvUnitDoubleBase(KgvUnit::U_PT, 2)
0295     , m_value(0.0)
0296     , m_lower(0.0)
0297     , m_upper(9999.99)
0298     , m_lowerInPoints(0.0)
0299     , m_upperInPoints(9999.99)
0300 {
0301     lineEdit()->setAlignment(Qt::AlignRight);
0302     m_validator = new KgvUnitDoubleValidator(this, this);
0303     lineEdit()->setValidator(m_validator);
0304     setUnit(KgvUnit::U_PT);
0305     changeValue(KgvUnit::ptToUnit(0.0, KgvUnit::U_PT));
0306     connect(this, static_cast<void (KgvUnitDoubleComboBox::*)(int)>(&KgvUnitDoubleComboBox::activated), this, &KgvUnitDoubleComboBox::slotActivated);
0307 }
0308 
0309 KgvUnitDoubleComboBox::KgvUnitDoubleComboBox(QWidget *parent, double lower, double upper, double value, KgvUnit::Unit unit, unsigned int precision)
0310     : QComboBox(parent)
0311     , KgvUnitDoubleBase(unit, precision)
0312     , m_value(value)
0313     , m_lower(lower)
0314     , m_upper(upper)
0315     , m_lowerInPoints(lower)
0316     , m_upperInPoints(upper)
0317 {
0318     lineEdit()->setAlignment(Qt::AlignRight);
0319     m_validator = new KgvUnitDoubleValidator(this, this);
0320     lineEdit()->setValidator(m_validator);
0321     setUnit(unit);
0322     changeValue(KgvUnit::ptToUnit(value, unit));
0323     connect(this, static_cast<void (KgvUnitDoubleComboBox::*)(int)>(&KgvUnitDoubleComboBox::activated), this, &KgvUnitDoubleComboBox::slotActivated);
0324 }
0325 
0326 void KgvUnitDoubleComboBox::changeValue(double value)
0327 {
0328     QString oldLabel = lineEdit()->text();
0329     updateValue(value);
0330     if (lineEdit()->text() != oldLabel)
0331         emit valueChanged(m_value);
0332 }
0333 
0334 void KgvUnitDoubleComboBox::updateValue(double value)
0335 {
0336     m_value = value < m_lower ? m_lower : (value > m_upper ? m_upper : value);
0337     lineEdit()->setText(getVisibleText(m_value));
0338 }
0339 
0340 void KgvUnitDoubleComboBox::insertItem(double value, int index)
0341 {
0342     QComboBox::insertItem(index, getVisibleText(value));
0343 }
0344 
0345 void KgvUnitDoubleComboBox::slotActivated(int index)
0346 {
0347     double oldvalue = m_value;
0348     bool ok;
0349     double value = toDouble(itemText(index), &ok);
0350     m_value = value < m_lower ? m_lower : (value > m_upper ? m_upper : value);
0351     if (m_value != oldvalue)
0352         emit valueChanged(m_value);
0353 }
0354 
0355 void KgvUnitDoubleComboBox::setUnit(KgvUnit::Unit unit)
0356 {
0357     KgvUnit::Unit old = m_unit;
0358     m_unit = unit;
0359     m_lower = KgvUnit::ptToUnit(m_lowerInPoints, unit);
0360     m_upper = KgvUnit::ptToUnit(m_upperInPoints, unit);
0361     changeValue(KgvUnit::ptToUnit(KgvUnit::fromUserValue(m_value, old), unit));
0362 }
0363 
0364 bool KgvUnitDoubleComboBox::eventFilter(QObject *o, QEvent *ev)
0365 {
0366 #if 0
0367     if( ev->type() == QEvent::FocusOut || ev->type() == QEvent::Leave || ev->type() == QEvent::Hide )
0368     {
0369         bool ok;
0370         double value = toDouble( lineEdit()->text(), &ok );
0371         changeValue( value );
0372         return false;
0373     }
0374     else
0375 #endif
0376     return QComboBox::eventFilter(o, ev);
0377 }
0378 
0379 double KgvUnitDoubleComboBox::value(void) const
0380 {
0381     return KgvUnit::fromUserValue(m_value, m_unit);
0382 }
0383 
0384 // ----------------------------------------------------------------
0385 
0386 KgvUnitDoubleSpinComboBox::KgvUnitDoubleSpinComboBox(QWidget *parent)
0387     : QWidget(parent)
0388     , m_step(1.0)
0389 {
0390     QGridLayout *layout = new QGridLayout(this);
0391     // layout->setMargin( 2 );
0392     QPushButton *up = new QPushButton("+", this);
0393     // up->setFlat( true );
0394     up->setMaximumHeight(15);
0395     up->setMaximumWidth(15);
0396     layout->addWidget(up, 0, 0);
0397     connect(up, &QPushButton::clicked, this, &KgvUnitDoubleSpinComboBox::slotUpClicked);
0398 
0399     QPushButton *down = new QPushButton("-", this);
0400     down->setMaximumHeight(15);
0401     down->setMaximumWidth(15);
0402     layout->addWidget(down, 1, 0);
0403     connect(down, &QPushButton::clicked, this, &KgvUnitDoubleSpinComboBox::slotDownClicked);
0404 
0405     m_combo = new KgvUnitDoubleComboBox(this, KgvUnit::ptToUnit(0.0, KgvUnit::U_PT), KgvUnit::ptToUnit(9999.99, KgvUnit::U_PT), 0.0, KgvUnit::U_PT, 2);
0406     connect(m_combo, &KgvUnitDoubleComboBox::valueChanged, this, &KgvUnitDoubleSpinComboBox::valueChanged);
0407     layout->addWidget(m_combo, 0, 2, 2, 1);
0408 }
0409 
0410 KgvUnitDoubleSpinComboBox::KgvUnitDoubleSpinComboBox(QWidget *parent, double lower, double upper, double step, double value, KgvUnit::Unit unit, unsigned int precision)
0411     : QWidget(parent)
0412     , m_step(step) //, m_lowerInPoints( lower ), m_upperInPoints( upper )
0413 {
0414     QGridLayout *layout = new QGridLayout(this);
0415     // layout->setMargin( 2 );
0416     QPushButton *up = new QPushButton("+", this);
0417     // up->setFlat( true );
0418     up->setMaximumHeight(15);
0419     up->setMaximumWidth(15);
0420     layout->addWidget(up, 0, 0);
0421     connect(up, &QPushButton::clicked, this, &KgvUnitDoubleSpinComboBox::slotUpClicked);
0422 
0423     QPushButton *down = new QPushButton("-", this);
0424     down->setMaximumHeight(15);
0425     down->setMaximumWidth(15);
0426     layout->addWidget(down, 1, 0);
0427     connect(down, &QPushButton::clicked, this, &KgvUnitDoubleSpinComboBox::slotDownClicked);
0428 
0429     m_combo = new KgvUnitDoubleComboBox(this, KgvUnit::ptToUnit(lower, unit), KgvUnit::ptToUnit(upper, unit), value, unit, precision);
0430     connect(m_combo, &KgvUnitDoubleComboBox::valueChanged, this, &KgvUnitDoubleSpinComboBox::valueChanged);
0431     layout->addWidget(m_combo, 0, 2, 2, 1);
0432 }
0433 
0434 void KgvUnitDoubleSpinComboBox::slotUpClicked()
0435 {
0436     m_combo->changeValue(m_combo->value() + m_step);
0437 }
0438 
0439 void KgvUnitDoubleSpinComboBox::slotDownClicked()
0440 {
0441     m_combo->changeValue(m_combo->value() - m_step);
0442 }
0443 
0444 void KgvUnitDoubleSpinComboBox::insertItem(double value, int index)
0445 {
0446     m_combo->insertItem(value, index);
0447 }
0448 
0449 void KgvUnitDoubleSpinComboBox::updateValue(double value)
0450 {
0451     m_combo->updateValue(value);
0452 }
0453 
0454 double KgvUnitDoubleSpinComboBox::value() const
0455 {
0456     return m_combo->value();
0457 }
0458 
0459 #include "moc_KgvUnitWidgets.cpp"