File indexing completed on 2024-03-24 04:43:39
0001 /* This file is part of the KDE project 0002 Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> 0003 Copyright (C) 2004 Alexander Dymo <cloudtemple@mskat.net> 0004 Copyright (C) 2008-2017 Jarosław Staniek <staniek@kde.org> 0005 0006 This library is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU Library General Public 0008 License as published by the Free Software Foundation; either 0009 version 2 of the License, or (at your option) any later version. 0010 0011 This library is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 Library General Public License for more details. 0015 0016 You should have received a copy of the GNU Library General Public License 0017 along with this library; see the file COPYING.LIB. If not, write to 0018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0019 * Boston, MA 02110-1301, USA. 0020 */ 0021 0022 #include "spinbox.h" 0023 #include "KProperty.h" 0024 #include "KProperty_p.h" 0025 #include "KPropertyEditorView.h" 0026 #include "KPropertyUtils.h" 0027 #include "KPropertyUtils_p.h" 0028 #include "KPropertyWidgetsFactory.h" 0029 #include "kproperty_debug.h" 0030 0031 #include <QVariant> 0032 #include <QLineEdit> 0033 #include <QLocale> 0034 0035 //! @return font size expressed in points (pt) 0036 //! or if points are not available - in pixels (px) for @a font 0037 static QString fontSizeForCSS(const QFont& font) 0038 { 0039 return font.pointSize() > 0 0040 ? QString::fromLatin1("%1pt").arg(font.pointSize()) 0041 : QString::fromLatin1("%1px").arg(font.pixelSize()); 0042 } 0043 0044 static QString cssForSpinBox(const char *_class, const QFont& font, int itemHeight) 0045 { 0046 return QString::fromLatin1( 0047 "%5 { border-left: 0; border-right: 0; font-size: %3; } " 0048 "%5::down-button { height: %1px; %4 } " 0049 "%5::up-button { height: %2px; } " 0050 "QLineEdit { border-width:0px; } " 0051 ) 0052 .arg(itemHeight/2 - 1).arg(itemHeight - itemHeight/2 - 1) 0053 .arg(fontSizeForCSS(font)) 0054 .arg(QLatin1String((itemHeight/2 <= 9) ? "bottom: 2px;" : "bottom: 0px;")) 0055 .arg(QLatin1String(_class)); 0056 } 0057 0058 namespace { 0059 0060 void intRangeValue(const KProperty &property, QVariant *min, QVariant *max) 0061 { 0062 Q_ASSERT(min); 0063 Q_ASSERT(max); 0064 *min = property.option("min"); 0065 *max = property.option("max"); 0066 if (!min->canConvert(QMetaType::Int) || min->toInt() < -INT_MAX) { 0067 min->clear(); 0068 } 0069 if (!max->canConvert(QMetaType::Int) || max->toInt() > INT_MAX) { 0070 max->clear(); 0071 } 0072 if (min->canConvert(QMetaType::Int) && max->canConvert(QMetaType::Int) 0073 && min->toInt() > max->toInt()) 0074 { 0075 min->clear(); 0076 max->clear(); 0077 } 0078 if (min->isNull()) { 0079 switch (property.type()) { 0080 case KProperty::UInt: 0081 *min = 0; 0082 break; 0083 default: 0084 *min = -INT_MAX; 0085 } 0086 } 0087 if (max->isNull()) { 0088 *max = INT_MAX; 0089 } 0090 } 0091 0092 //! Fixes @a value to fit in range @a min to @a max. 0093 //! Displays qWarning if @a warn is @c true. 0094 int fixIntValue(const QVariant &value, int min, int max, bool warn) 0095 { 0096 if (value.toInt() < min) { 0097 if (warn) { 0098 kprWarning() << "Could not assign value" << value.toInt() << "smaller than minimum" << min 0099 << "-- setting to" << min; 0100 } 0101 return min; 0102 } 0103 if (value.toInt() > max) { 0104 if (warn) { 0105 kprWarning() << "Could not assign value" << value.toInt() << "larger than maximum" << max 0106 << "-- setting to" << max; 0107 } 0108 return max; 0109 } 0110 return value.toInt(); 0111 } 0112 0113 } // namespace 0114 0115 class Q_DECL_HIDDEN KPropertyIntSpinBox::Private 0116 { 0117 public: 0118 explicit Private(const KProperty& prop) : property(&prop) 0119 { 0120 } 0121 0122 const KProperty * const property; 0123 }; 0124 0125 KPropertyIntSpinBox::KPropertyIntSpinBox(const KProperty& prop, QWidget *parent, int itemHeight) 0126 : QSpinBox(parent) 0127 , d(new Private(prop)) 0128 { 0129 QLineEdit* le = findChild<QLineEdit*>(); 0130 setContentsMargins(0,0,0,0); 0131 if (le) { 0132 le->setAlignment(Qt::AlignLeft); 0133 le->setContentsMargins(0,0,0,0); 0134 } 0135 setFrame(true); 0136 QString css = cssForSpinBox("QSpinBox", font(), itemHeight); 0137 KPropertyWidgetsFactory::setTopAndBottomBordersUsingStyleSheet(this, css); 0138 setStyleSheet(css); 0139 0140 QVariant minVal; 0141 QVariant maxVal; 0142 intRangeValue(prop, &minVal, &maxVal); 0143 setRange(minVal.toInt(), maxVal.toInt()); 0144 const KPropertyUtilsPrivate::ValueOptionsHandler options(prop); 0145 if (!options.minValueText.isNull()) { 0146 setSpecialValueText(options.minValueText.toString()); 0147 } 0148 if (!options.prefix.isEmpty()) { 0149 setPrefix(options.prefix + QLatin1Char(' ')); 0150 } 0151 if (!options.suffix.isEmpty()) { 0152 setSuffix(QLatin1Char(' ') + options.suffix); 0153 } 0154 connect(this, SIGNAL(valueChanged(int)), this, SLOT(slotValueChanged(int))); 0155 } 0156 0157 KPropertyIntSpinBox::~KPropertyIntSpinBox() 0158 { 0159 delete d; 0160 } 0161 0162 QVariant KPropertyIntSpinBox::value() const 0163 { 0164 if (d->property->type() == KProperty::UInt) { 0165 return uint(QSpinBox::value()); 0166 } 0167 return QSpinBox::value(); 0168 } 0169 0170 void KPropertyIntSpinBox::setValue(const QVariant& value) 0171 { 0172 QVariant minVal; 0173 QVariant maxVal; 0174 intRangeValue(*d->property, &minVal, &maxVal); 0175 QSpinBox::setValue(fixIntValue(value, minVal.toInt(), maxVal.toInt(), true)); 0176 } 0177 0178 void KPropertyIntSpinBox::slotValueChanged(int value) 0179 { 0180 Q_UNUSED(value); 0181 emit commitData(this); 0182 } 0183 0184 //----------------------- 0185 0186 class Q_DECL_HIDDEN KPropertyDoubleSpinBox::Private 0187 { 0188 public: 0189 explicit Private(const KProperty& prop) : property(&prop) 0190 { 0191 } 0192 0193 const KProperty * const property; 0194 }; 0195 0196 namespace { 0197 0198 void doubleRangeValue(const KProperty &property, QVariant *min, QVariant *max) 0199 { 0200 Q_ASSERT(min); 0201 Q_ASSERT(max); 0202 *min = property.option("min"); 0203 *max = property.option("max"); 0204 if (!min->canConvert(QMetaType::Double) || min->toDouble() < KPROPERTY_MIN_PRECISE_DOUBLE) { 0205 min->clear(); 0206 } 0207 if (!max->canConvert(QMetaType::Double) || max->toDouble() > KPROPERTY_MAX_PRECISE_DOUBLE) { 0208 max->clear(); 0209 } 0210 if (min->canConvert(QMetaType::Double) && max->canConvert(QMetaType::Double) 0211 && min->toDouble() > max->toDouble()) 0212 { 0213 min->clear(); 0214 max->clear(); 0215 } 0216 if (min->isNull()) { 0217 *min = 0.0; 0218 } 0219 if (max->isNull()) { 0220 *max = KPROPERTY_MAX_PRECISE_DOUBLE; 0221 } 0222 } 0223 0224 //! Fixes @a value to fit in range @a min to @a max. 0225 //! Displays qWarning if @a warn is @c true. 0226 double fixDoubleValue(const QVariant &value, double min, double max, bool warn) 0227 { 0228 if (value.toDouble() < min) { 0229 if (warn) { 0230 kprWarning() << "Could not assign value" << value.toDouble() << "smaller than minimum" << min 0231 << "-- setting to" << min; 0232 } 0233 return min; 0234 } 0235 if (value.toDouble() > max) { 0236 if (warn) { 0237 kprWarning() << "Could not assign value" << value.toDouble() << "larger than maximum" << max 0238 << "-- setting to" << max; 0239 } 0240 return max; 0241 } 0242 return value.toDouble(); 0243 } 0244 0245 QVariant precisionValue(const KProperty &property) 0246 { 0247 QVariant result = property.option("precision", KPROPERTY_DEFAULT_DOUBLE_VALUE_PRECISION); 0248 if (result.canConvert(QMetaType::Int) && result.toInt() >= 0) { 0249 return result; 0250 } 0251 return QVariant(); 0252 } 0253 0254 } // namespace 0255 0256 KPropertyDoubleSpinBox::KPropertyDoubleSpinBox(const KProperty &prop, QWidget *parent, int itemHeight) 0257 : QDoubleSpinBox(parent) 0258 , d(new Private(prop)) 0259 { 0260 setFrame(false); 0261 QLineEdit* le = findChild<QLineEdit*>(); 0262 if (le) { 0263 le->setAlignment(Qt::AlignLeft); 0264 le->setContentsMargins(0,0,0,0); 0265 le->setFrame(false); 0266 } 0267 /* KPropertyFactory::setTopAndBottomBordersUsingStyleSheet(sb, 0268 QString::fromLatin1( 0269 "QDoubleSpinBox { border-left: 0; border-right: 0; } " 0270 "QDoubleSpinBox::down-button { height: %1px; } " 0271 "QDoubleSpinBox::up-button { height: %2px; }" 0272 ).arg(itemHeight/2).arg(itemHeight - itemHeight/2) 0273 );*/ 0274 QString css = cssForSpinBox("QDoubleSpinBox", font(), itemHeight); 0275 KPropertyWidgetsFactory::setTopAndBottomBordersUsingStyleSheet(this, css); 0276 setStyleSheet(css); 0277 0278 QVariant minVal; 0279 QVariant maxVal; 0280 doubleRangeValue(prop, &minVal, &maxVal); 0281 setRange(minVal.toDouble(), maxVal.toDouble()); 0282 QVariant step = prop.option("step", KPROPERTY_DEFAULT_DOUBLE_VALUE_STEP); 0283 if (step.canConvert(QMetaType::Double) && step.toDouble() > 0.0) { 0284 setSingleStep(step.toDouble()); 0285 } 0286 const QVariant precision = precisionValue(prop); 0287 if (precision.isValid()) { 0288 setDecimals(precision.toInt()); 0289 } 0290 //! @todo implement slider 0291 // bool slider = prop->option("slider", false).toBool(); 0292 const KPropertyUtilsPrivate::ValueOptionsHandler options(prop); 0293 if (!options.minValueText.isNull()) { 0294 setSpecialValueText(options.minValueText.toString()); 0295 } 0296 if (!options.prefix.isEmpty()) { 0297 setPrefix(options.prefix + QLatin1Char(' ')); 0298 } 0299 if (!options.suffix.isEmpty()) { 0300 setSuffix(QLatin1Char(' ') + options.suffix); 0301 } 0302 connect(this, SIGNAL(valueChanged(double)), this, SLOT(slotValueChanged(double))); 0303 } 0304 0305 KPropertyDoubleSpinBox::~KPropertyDoubleSpinBox() 0306 { 0307 delete d; 0308 } 0309 0310 void KPropertyDoubleSpinBox::resizeEvent( QResizeEvent * event ) 0311 { 0312 QDoubleSpinBox::resizeEvent(event); 0313 } 0314 0315 void KPropertyDoubleSpinBox::setValue(const QVariant& value) 0316 { 0317 QVariant minVal; 0318 QVariant maxVal; 0319 doubleRangeValue(*d->property, &minVal, &maxVal); 0320 QDoubleSpinBox::setValue(fixDoubleValue(value, minVal.toDouble(), maxVal.toDouble(), true)); 0321 } 0322 0323 void KPropertyDoubleSpinBox::slotValueChanged(double value) 0324 { 0325 Q_UNUSED(value); 0326 emit commitData(this); 0327 } 0328 0329 //----------------------- 0330 0331 KPropertyIntSpinBoxDelegate::KPropertyIntSpinBoxDelegate() 0332 { 0333 } 0334 0335 QString KPropertyIntSpinBoxDelegate::propertyValueToString(const KProperty* prop, 0336 const QLocale &locale) const 0337 { 0338 //replace min value with minValueText if defined 0339 const KPropertyUtilsPrivate::ValueOptionsHandler options(*prop); 0340 QVariant minVal; 0341 QVariant maxVal; 0342 intRangeValue(*prop, &minVal, &maxVal); 0343 const int fixedValue = fixIntValue(prop->value(), minVal.toInt(), maxVal.toInt(), false); 0344 if (minVal.isValid() && minVal.toInt() == fixedValue && !options.minValueText.isNull()) 0345 { 0346 return options.minValueText.toString(); 0347 } 0348 return options.valueWithPrefixAndSuffix(valueToString(fixedValue, locale), locale); 0349 } 0350 0351 QString KPropertyIntSpinBoxDelegate::valueToString(const QVariant& value, const QLocale &locale) const 0352 { 0353 return locale.toString(value.toReal(), 'f', 0); 0354 } 0355 0356 QWidget* KPropertyIntSpinBoxDelegate::createEditor( int type, QWidget *parent, 0357 const QStyleOptionViewItem & option, const QModelIndex & index ) const 0358 { 0359 Q_UNUSED(type); 0360 0361 KProperty *prop = KPropertyUtils::propertyForIndex(index); 0362 if (!prop) { 0363 return nullptr; 0364 } 0365 return new KPropertyIntSpinBox(*prop, parent, option.rect.height() - 2); 0366 } 0367 0368 //----------------------- 0369 0370 KPropertyDoubleSpinBoxDelegate::KPropertyDoubleSpinBoxDelegate() 0371 { 0372 } 0373 0374 QString KPropertyDoubleSpinBoxDelegate::propertyValueToString(const KProperty* prop, 0375 const QLocale &locale) const 0376 { 0377 //replace min value with minValueText if defined 0378 QVariant minVal; 0379 QVariant maxVal; 0380 const KPropertyUtilsPrivate::ValueOptionsHandler options(*prop); 0381 doubleRangeValue(*prop, &minVal, &maxVal); 0382 const double fixedValue = fixDoubleValue(prop->value(), minVal.toDouble(), maxVal.toDouble(), false); 0383 if (minVal.isValid() && minVal.toDouble() == fixedValue && !options.minValueText.isNull()) 0384 { 0385 return options.minValueText.toString(); 0386 } 0387 QString valueString; 0388 const QVariant precision = precisionValue(*prop); 0389 if (precision.isValid()) { 0390 valueString = locale.toString(fixedValue, 'f', precision.toInt()); 0391 } else { 0392 valueString = valueToString(fixedValue, locale); 0393 } 0394 return options.valueWithPrefixAndSuffix(valueString, locale); 0395 } 0396 0397 QString KPropertyDoubleSpinBoxDelegate::valueToString(const QVariant& value, const QLocale &locale) const 0398 { 0399 return locale.toString(value.toReal()); 0400 } 0401 0402 QWidget* KPropertyDoubleSpinBoxDelegate::createEditor( int type, QWidget *parent, 0403 const QStyleOptionViewItem & option, const QModelIndex & index ) const 0404 { 0405 Q_UNUSED(type); 0406 0407 KProperty *prop = KPropertyUtils::propertyForIndex(index); 0408 if (!prop) { 0409 return nullptr; 0410 } 0411 return new KPropertyDoubleSpinBox(*prop, parent, option.rect.height() - 2 - 1); 0412 }