File indexing completed on 2024-05-12 16:02:27
0001 /* 0002 * SPDX-FileCopyrightText: 2016 Laurent Valentin Jospin <laurent.valentin@famillejospin.ch> 0003 * SPDX-FileCopyrightText: 2021 Deif Lou <ginoba@gmail.com> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "kis_double_parse_unit_spin_box.h" 0009 #include "kis_spin_box_unit_manager.h" 0010 0011 #include <QLineEdit> 0012 0013 class Q_DECL_HIDDEN KisDoubleParseUnitSpinBox::Private 0014 { 0015 public: 0016 Private(double low, double up, double step, KisSpinBoxUnitManager* unitManager) 0017 : lowerInPoints(low), 0018 upperInPoints(up), 0019 stepInPoints(step), 0020 unit(KoUnit(KoUnit::Point)), 0021 outPutSymbol(""), 0022 unitManager(unitManager), 0023 defaultUnitManager(unitManager) 0024 { 0025 } 0026 0027 double lowerInPoints {0.0}; ///< lowest value in points 0028 double upperInPoints {0.0}; ///< highest value in points 0029 double stepInPoints {0.0}; ///< step in points 0030 KoUnit unit; 0031 0032 double previousValueInPoint {0.0}; ///< allow to store the previous value in point, useful in some cases, even if, usually, we prefer to refer to the actual value (in selected unit) and convert it, since this is not always updated. 0033 QString previousSymbol; 0034 QString outPutSymbol; 0035 0036 KisSpinBoxUnitManager* unitManager {0}; //manage more units than permitted by KoUnit. 0037 KisSpinBoxUnitManager* defaultUnitManager {0}; //the default unit manager is the one the spinbox rely on and go back to if a connected unit manager is destroyed before the spinbox. 0038 0039 bool isDeleting {false}; 0040 0041 bool unitHasBeenChangedFromOutSideOnce {false}; //in some part of the code the unit is reset. We want to prevent this overriding the unit defined by the user. We use this switch to do so. 0042 bool letUnitBeChangedFromOutsideMoreThanOnce {true}; 0043 0044 bool displayUnit {true}; 0045 0046 bool allowResetDecimals {true}; 0047 0048 bool mustUsePreviousText {false}; 0049 }; 0050 0051 KisDoubleParseUnitSpinBox::KisDoubleParseUnitSpinBox(QWidget *parent) : 0052 KisDoubleParseSpinBox(parent), 0053 d(new Private(-9999, 9999, 1, KisSpinBoxUnitManagerFactory::buildDefaultUnitManager(this))) 0054 { 0055 setUnit( KoUnit(KoUnit::Point) ); 0056 setAlignment( Qt::AlignRight ); 0057 0058 connect(this, SIGNAL(valueChanged(double)), this, SLOT(privateValueChanged())); 0059 connect(lineEdit(), SIGNAL(textChanged(QString)), 0060 this, SLOT(detectUnitChanges()) ); 0061 0062 connect(d->unitManager, (void (KisSpinBoxUnitManager::*)()) &KisSpinBoxUnitManager::unitAboutToChange, this, (void (KisDoubleParseUnitSpinBox::*)()) &KisDoubleParseUnitSpinBox::prepareUnitChange); 0063 connect(d->unitManager, (void (KisSpinBoxUnitManager::*)( QString )) &KisSpinBoxUnitManager::unitChanged, this, (void (KisDoubleParseUnitSpinBox::*)( QString const& )) &KisDoubleParseUnitSpinBox::internalUnitChange); 0064 0065 setDecimals(d->unitManager->getApparentUnitRecommandedDecimals()); 0066 0067 } 0068 0069 KisDoubleParseUnitSpinBox::~KisDoubleParseUnitSpinBox() 0070 { 0071 d->isDeleting = true; 0072 delete d->defaultUnitManager; 0073 delete d; 0074 } 0075 0076 void KisDoubleParseUnitSpinBox::setUnitManager(KisSpinBoxUnitManager* unitManager) 0077 { 0078 qreal oldVal = d->unitManager->getReferenceValue(KisDoubleParseSpinBox::value()); 0079 QString oldSymbol = d->unitManager->getApparentUnitSymbol(); 0080 0081 qreal newVal = 0.0; 0082 0083 double newMin; 0084 double newMax; 0085 double newStep; 0086 0087 if (oldSymbol == unitManager->getApparentUnitSymbol() && 0088 d->unitManager->getUnitDimensionType() == unitManager->getUnitDimensionType()) 0089 { 0090 d->unitManager = unitManager; //set the new unitmanager anyway, since it may be a subclass, so change the behavior anyway. 0091 goto connect_signals; 0092 } 0093 0094 if (d->unitManager->getUnitDimensionType() == unitManager->getUnitDimensionType()) { 0095 //dimension is the same, calculate the new value 0096 newVal = unitManager->getApparentValue(oldVal); 0097 } else { 0098 newVal = unitManager->getApparentValue(d->lowerInPoints); 0099 } 0100 0101 newMin = unitManager->getApparentValue(d->lowerInPoints); 0102 newMax = unitManager->getApparentValue(d->upperInPoints); 0103 newStep = unitManager->getApparentValue(d->stepInPoints); 0104 0105 if (unitManager->getApparentUnitSymbol() == KoUnit(KoUnit::Pixel).symbol()) { 0106 // limit the pixel step by 1.0 0107 newStep = qMax(qreal(1.0), newStep); 0108 } 0109 0110 KisDoubleParseSpinBox::setMinimum(newMin); 0111 KisDoubleParseSpinBox::setMaximum(newMax); 0112 KisDoubleParseSpinBox::setSingleStep(newStep); 0113 0114 connect_signals: 0115 0116 if (d->unitManager != d->defaultUnitManager) { 0117 disconnect(d->unitManager, &QObject::destroyed, 0118 this, &KisDoubleParseUnitSpinBox::disconnectExternalUnitManager); //there's no dependence anymore. 0119 } 0120 disconnect(d->unitManager, (void (KisSpinBoxUnitManager::*)()) &KisSpinBoxUnitManager::unitAboutToChange, this, (void (KisDoubleParseUnitSpinBox::*)()) &KisDoubleParseUnitSpinBox::prepareUnitChange); 0121 disconnect(d->unitManager, (void (KisSpinBoxUnitManager::*)( QString )) &KisSpinBoxUnitManager::unitChanged, this, (void (KisDoubleParseUnitSpinBox::*)( QString const& )) &KisDoubleParseUnitSpinBox::internalUnitChange); 0122 0123 d->unitManager = unitManager; 0124 0125 connect(d->unitManager, &QObject::destroyed, 0126 this, &KisDoubleParseUnitSpinBox::disconnectExternalUnitManager); 0127 0128 0129 connect(d->unitManager, (void (KisSpinBoxUnitManager::*)()) &KisSpinBoxUnitManager::unitAboutToChange, this, (void (KisDoubleParseUnitSpinBox::*)()) &KisDoubleParseUnitSpinBox::prepareUnitChange); 0130 connect(d->unitManager, (void (KisSpinBoxUnitManager::*)( QString )) &KisSpinBoxUnitManager::unitChanged, this, (void (KisDoubleParseUnitSpinBox::*)( QString const& )) &KisDoubleParseUnitSpinBox::internalUnitChange); 0131 0132 KisDoubleParseSpinBox::setValue(newVal); 0133 0134 if (d->allowResetDecimals) { //if the user has not fixed the number of decimals. 0135 setDecimals(d->unitManager->getApparentUnitRecommandedDecimals()); 0136 } 0137 } 0138 0139 void KisDoubleParseUnitSpinBox::changeValue( double newValue ) 0140 { 0141 double apparentValue; 0142 double fact = 0.0; 0143 double cons = 0.0; 0144 0145 if (d->outPutSymbol.isEmpty()) { 0146 apparentValue = d->unitManager->getApparentValue(newValue); 0147 } else { 0148 0149 fact = d->unitManager->getConversionFactor(d->unitManager->getUnitDimensionType(), d->outPutSymbol); 0150 cons = d->unitManager->getConversionConstant(d->unitManager->getUnitDimensionType(), d->outPutSymbol); 0151 0152 apparentValue = fact*newValue + cons; 0153 } 0154 0155 if (apparentValue == KisDoubleParseSpinBox::value()) { 0156 return; 0157 } 0158 0159 if (d->outPutSymbol.isEmpty()) { 0160 KisDoubleParseSpinBox::setValue( d->unitManager->getApparentValue(newValue) ); 0161 } else { 0162 0163 KisDoubleParseSpinBox::setValue( d->unitManager->getApparentValue((newValue - cons)/fact) ); 0164 } 0165 } 0166 0167 void KisDoubleParseUnitSpinBox::setUnit( const KoUnit & unit) 0168 { 0169 if (d->unitHasBeenChangedFromOutSideOnce && !d->letUnitBeChangedFromOutsideMoreThanOnce) { 0170 return; 0171 } 0172 0173 if (d->unitManager->getUnitDimensionType() != KisSpinBoxUnitManager::LENGTH) { 0174 d->unitManager->setUnitDimension(KisSpinBoxUnitManager::LENGTH); //setting the unit using a KoUnit mean you want to use a length. 0175 } 0176 0177 setUnit(unit.symbol()); 0178 d->unit = unit; 0179 } 0180 0181 void KisDoubleParseUnitSpinBox::setUnit(const QString &symbol) 0182 { 0183 d->unitManager->setApparentUnitFromSymbol(symbol); //via signals and slots, the correct functions should be called. 0184 } 0185 0186 void KisDoubleParseUnitSpinBox::setReturnUnit(const QString & symbol) 0187 { 0188 d->outPutSymbol = symbol; 0189 } 0190 0191 void KisDoubleParseUnitSpinBox::prepareUnitChange() { 0192 0193 d->previousValueInPoint = d->unitManager->getReferenceValue(KisDoubleParseSpinBox::value()); 0194 d->previousSymbol = d->unitManager->getApparentUnitSymbol(); 0195 } 0196 0197 void KisDoubleParseUnitSpinBox::internalUnitChange(const QString &symbol) { 0198 0199 //d->unitManager->setApparentUnitFromSymbol(symbol); 0200 0201 if (d->unitManager->getApparentUnitSymbol() == d->previousSymbol) { //the setApparentUnitFromSymbol is a bit clever, for example in regard of Casesensitivity. So better check like this. 0202 return; 0203 } 0204 0205 KisDoubleParseSpinBox::setMinimum( d->unitManager->getApparentValue( d->lowerInPoints ) ); 0206 KisDoubleParseSpinBox::setMaximum( d->unitManager->getApparentValue( d->upperInPoints ) ); 0207 0208 qreal step = d->unitManager->getApparentValue( d->stepInPoints ); 0209 0210 if (symbol == KoUnit(KoUnit::Pixel).symbol()) { 0211 // limit the pixel step by 1.0 0212 step = qMax(qreal(1.0), step); 0213 } 0214 0215 KisDoubleParseSpinBox::setSingleStep( step ); 0216 KisDoubleParseSpinBox::setValue( d->unitManager->getApparentValue( d->previousValueInPoint ) ); 0217 0218 if (d->allowResetDecimals) { 0219 setDecimals(d->unitManager->getApparentUnitRecommandedDecimals()); 0220 } 0221 0222 d->unitHasBeenChangedFromOutSideOnce = true; 0223 } 0224 0225 void KisDoubleParseUnitSpinBox::setDimensionType(int dim) 0226 { 0227 if (!KisSpinBoxUnitManager::isUnitId(dim)) { 0228 return; 0229 } 0230 0231 d->unitManager->setUnitDimension((KisSpinBoxUnitManager::UnitDimension) dim); 0232 } 0233 0234 double KisDoubleParseUnitSpinBox::value( ) const 0235 { 0236 if (d->outPutSymbol.isEmpty()) { 0237 return d->unitManager->getReferenceValue( KisDoubleParseSpinBox::value() ); 0238 } 0239 0240 double ref = d->unitManager->getReferenceValue( KisDoubleParseSpinBox::value() ); 0241 double fact = d->unitManager->getConversionFactor(d->unitManager->getUnitDimensionType(), d->outPutSymbol); 0242 double cons = d->unitManager->getConversionConstant(d->unitManager->getUnitDimensionType(), d->outPutSymbol); 0243 0244 return fact*ref + cons; 0245 } 0246 0247 void KisDoubleParseUnitSpinBox::setMinimum(double min) 0248 { 0249 d->lowerInPoints = min; 0250 KisDoubleParseSpinBox::setMinimum( d->unitManager->getApparentValue( min ) ); 0251 } 0252 0253 void KisDoubleParseUnitSpinBox::setMaximum(double max) 0254 { 0255 d->upperInPoints = max; 0256 KisDoubleParseSpinBox::setMaximum( d->unitManager->getApparentValue( max ) ); 0257 } 0258 0259 void KisDoubleParseUnitSpinBox::setLineStep(double step) 0260 { 0261 d->stepInPoints = d->unitManager->getReferenceValue(step); 0262 KisDoubleParseSpinBox::setSingleStep( step ); 0263 } 0264 0265 void KisDoubleParseUnitSpinBox::setLineStepPt(double step) 0266 { 0267 d->stepInPoints = step; 0268 KisDoubleParseSpinBox::setSingleStep( d->unitManager->getApparentValue( step ) ); 0269 } 0270 0271 void KisDoubleParseUnitSpinBox::setMinMaxStep( double min, double max, double step ) 0272 { 0273 setMinimum( min ); 0274 setMaximum( max ); 0275 setLineStepPt( step ); 0276 } 0277 0278 QString KisDoubleParseUnitSpinBox::textFromValue( double value ) const 0279 { 0280 // Just return the current value (for example when the user is editing) 0281 if (d->mustUsePreviousText) { 0282 return cleanText(); 0283 } 0284 // Construct a new value 0285 QString txt = KisDoubleParseSpinBox::textFromValue(value); 0286 if (d->displayUnit) { 0287 if (!txt.endsWith(d->unitManager->getApparentUnitSymbol())) { 0288 txt += " " + d->unitManager->getApparentUnitSymbol(); 0289 } 0290 } 0291 return txt; 0292 } 0293 0294 QString KisDoubleParseUnitSpinBox::veryCleanText() const 0295 { 0296 return makeTextClean(cleanText()); 0297 } 0298 0299 double KisDoubleParseUnitSpinBox::valueFromText( const QString& str ) const 0300 { 0301 QString txt = makeTextClean(str); 0302 //this function will take care of prefix (and don't mind if suffix has been removed. 0303 return KisDoubleParseSpinBox::valueFromText(txt); 0304 } 0305 0306 void KisDoubleParseUnitSpinBox::setUnitChangeFromOutsideBehavior(bool toggle) 0307 { 0308 d->letUnitBeChangedFromOutsideMoreThanOnce = toggle; 0309 } 0310 0311 void KisDoubleParseUnitSpinBox::setDisplayUnit(bool toggle) 0312 { 0313 d->displayUnit = toggle; 0314 } 0315 0316 void KisDoubleParseUnitSpinBox::preventDecimalsChangeFromUnitManager(bool prevent) 0317 { 0318 d->allowResetDecimals = !prevent; 0319 } 0320 0321 void KisDoubleParseUnitSpinBox::privateValueChanged() 0322 { 0323 emit valueChangedPt( value() ); 0324 } 0325 0326 QString KisDoubleParseUnitSpinBox::detectUnit() 0327 { 0328 QString str = veryCleanText().trimmed(); //text with the new unit but not the old one. 0329 0330 QRegExp regexp ("([ ]*[a-zA-Z]+[ ]*)$"); // Letters or spaces at end 0331 int res = str.indexOf( regexp ); 0332 0333 if (res > -1) { 0334 QString expr ( str.right( str.size() - res ) ); 0335 expr = expr.trimmed(); 0336 return expr; 0337 } 0338 0339 return ""; 0340 } 0341 0342 void KisDoubleParseUnitSpinBox::detectUnitChanges() 0343 { 0344 QString unitSymb = detectUnit(); 0345 0346 if (unitSymb.isEmpty()) { 0347 return; 0348 } 0349 0350 QString oldUnitSymb = d->unitManager->getApparentUnitSymbol(); 0351 0352 setUnit(unitSymb); 0353 // Quick hack 0354 // This function is called when the user changed the text and the call to 0355 // setValue will provoke a call to textFromValue which will return a new 0356 // text different from the current one. Since the following setValue is 0357 // called because of a user change, we use a flag to prevent the text from 0358 // changing 0359 d->mustUsePreviousText = true; 0360 // Change value keep the old value, but converted to new unit... which is 0361 // different from the value the user entered in the new unit. So we need 0362 // to set the new value. 0363 setValue(valueFromText(cleanText())); 0364 d->mustUsePreviousText = false; 0365 0366 if (oldUnitSymb != d->unitManager->getApparentUnitSymbol()) { 0367 // the user has changed the unit, so we block changes from outside. 0368 setUnitChangeFromOutsideBehavior(false); 0369 } 0370 } 0371 0372 QString KisDoubleParseUnitSpinBox::makeTextClean(QString const& txt) const 0373 { 0374 QString expr = txt; 0375 QString symbol = d->unitManager->getApparentUnitSymbol(); 0376 0377 if ( expr.endsWith(suffix()) ) { 0378 expr.remove(expr.size()-suffix().size(), suffix().size()); 0379 } 0380 0381 expr = expr.trimmed(); 0382 0383 if ( expr.endsWith(symbol) ) { 0384 expr.remove(expr.size()-symbol.size(), symbol.size()); 0385 } 0386 0387 return expr.trimmed(); 0388 } 0389 0390 void KisDoubleParseUnitSpinBox::disconnectExternalUnitManager() 0391 { 0392 if (!d->isDeleting) 0393 { 0394 setUnitManager(d->defaultUnitManager); //go back to default unit manager. 0395 } 0396 }