diff --git a/src/KPropertyUtils_p.cpp b/src/KPropertyUtils_p.cpp index 980b594..40c6742 100644 --- a/src/KPropertyUtils_p.cpp +++ b/src/KPropertyUtils_p.cpp @@ -1,425 +1,425 @@ /* This file is part of the KDE project Copyright (C) 2010-2017 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KPropertyUtils_p.h" #include "KPropertyEditorView.h" #include "KProperty.h" #include #if defined KPropertyCore_EXPORTS || defined KPropertyWidgets_EXPORTS #include "kproperty_debug.h" #else # define kprDebug qDebug # define kprWarning qWarning # define kprCritical qCritical #endif #include #include #include #include #include #include #include #include #ifdef KPROPERTY_KF #include #include #include #else #include #endif #ifdef QT_GUI_LIB #include #endif class QColor; class QWidget; namespace KPropertyUtilsPrivate { void showMessageBox(QWidget *parent, const QString &errorMessage, const QString &detailedErrorMessage) { if (detailedErrorMessage.isEmpty()) { #ifdef KPROPERTY_KF KMessageBox::error(parent, errorMessage); #else QMessageBox::warning(parent, QString(), errorMessage); #endif } else { #ifdef KPROPERTY_KF KMessageBox::detailedError(parent, errorMessage, detailedErrorMessage); #else QMessageBox::warning(parent, QString(), errorMessage + QLatin1Char('\n') + detailedErrorMessage); #endif } } // -- icon support QString supportedIconTheme() { return QLatin1String("breeze"); } //! @brief @return true if @a path is readable bool fileReadable(const QString &path) { return !path.isEmpty() && QFileInfo(path).isReadable(); } //! @brief Used for a workaround: locations for QStandardPaths::AppDataLocation end with app name. //! If this is not an expected app but for example a test app, replace //! the subdir name with app name so we can find resource file(s). QStringList correctStandardLocations(const QString &privateName, QStandardPaths::StandardLocation location, const QString &extraLocation) { QSet result; if (!privateName.isEmpty()) { QRegularExpression re(QLatin1Char('/') + QCoreApplication::applicationName() + QLatin1Char('$')); QStringList standardLocations(QStandardPaths::standardLocations(location)); if (!extraLocation.isEmpty()) { standardLocations.append(extraLocation); } for(const QString &dir : standardLocations) { if (dir.indexOf(re) != -1) { QString realDir(dir); realDir.replace(re, QLatin1Char('/') + privateName); result.insert(realDir); } } } return result.toList(); } #ifdef Q_OS_WIN #define KPATH_SEPARATOR ';' #else #define KPATH_SEPARATOR ':' #endif /*! @brief Locates a file path for specified parameters * @param privateName Name to be used instead of application name for resource lookup * @param path Relative path to the resource file * @param location Standard file location to use for file lookup * @param extraLocation Extra directory path for file lookup * @return Empty string on failure */ QString locateFile(const QString &privateName, const QString& path, QStandardPaths::StandardLocation location, const QString &extraLocation) { // let QStandardPaths handle this, it will look for app local stuff QString fullPath = QFileInfo( QStandardPaths::locate(location, path)).canonicalFilePath(); if (fileReadable(fullPath)) { return fullPath; } // Try extra location fullPath = QFileInfo(extraLocation + QLatin1Char('/') + path).canonicalFilePath(); if (fileReadable(fullPath)) { return fullPath; } // Try in PATH subdirs, useful for running apps from the build dir, without installing for(const QByteArray &pathDir : qgetenv("PATH").split(KPATH_SEPARATOR)) { const QString dataDirFromPath = QFileInfo(QFile::decodeName(pathDir) + QStringLiteral("/data/") + path).canonicalFilePath(); if (fileReadable(dataDirFromPath)) { return dataDirFromPath; } } const QStringList correctedStandardLocations(correctStandardLocations(privateName, location, extraLocation)); for(const QString &dir : correctedStandardLocations) { fullPath = QFileInfo(dir + QLatin1Char('/') + path).canonicalFilePath(); if (fileReadable(fullPath)) { return fullPath; } } return QString(); } /*! @brief Registers icons resource file * @param privateName Name to be used instead of application name for resource lookup * @param path Relative path to the resource file * @param location Standard file location to use for file lookup * @param resourceRoot A resource root for QResource::registerResource() * @param errorMessage On failure it is set to a brief error message. * @param errorDescription On failure it is set to a detailed error message. * other for warning */ bool registerIconsResource(const QString &privateName, const QString& path, QStandardPaths::StandardLocation location, const QString &resourceRoot, const QString &extraLocation, QString *errorMessage, QString *detailedErrorMessage) { const QString fullPath = locateFile(privateName, path, location, extraLocation); if (fullPath.isEmpty() || !QFileInfo(fullPath).isReadable() || !QResource::registerResource(fullPath, resourceRoot)) { QStringList triedLocations(QStandardPaths::standardLocations(location)); if (!extraLocation.isEmpty()) { triedLocations.append(extraLocation); } triedLocations.append(correctStandardLocations(privateName, location, extraLocation)); const QString triedLocationsString = QLocale().createSeparatedList(triedLocations); #ifdef QT_ONLY *errorMessage = QString("Could not open icon resource file %1.").arg(path); *detailedErrorMessage = QString("Tried to find in %1.").arg(triedLocationsString); #else *errorMessage = QObject::tr( "Could not open icon resource file \"%1\". " "Application will not start. " "Please check if it is properly installed.") .arg(QFileInfo(path).fileName()); *detailedErrorMessage = QObject::tr("Tried to find in %1.").arg(triedLocationsString); #endif return false; } *errorMessage = QString(); *detailedErrorMessage = QString(); return true; } /*! @brief Registers a global icon resource file * @param themeName A name of icon theme to use. * @param errorMessage On failure it is set to a brief error message. * @param errorDescription On failure it is set to a detailed error message. * other for warning */ bool registerGlobalIconsResource(const QString &themeName, QString *errorMessage, QString *detailedErrorMessage) { QString extraLocation; #ifdef CMAKE_INSTALL_FULL_ICONDIR extraLocation = QDir::fromNativeSeparators(QFile::decodeName(CMAKE_INSTALL_FULL_ICONDIR)); if (extraLocation.endsWith("/icons")) { extraLocation.chop(QLatin1String("/icons").size()); } #elif defined(Q_OS_WIN) extraLocation = QCoreApplication::applicationDirPath() + QStringLiteral("/data"); #endif return registerIconsResource(QString(), QString::fromLatin1("icons/%1/%1-icons.rcc").arg(themeName), QStandardPaths::GenericDataLocation, QStringLiteral("/icons/") + themeName, extraLocation, errorMessage, detailedErrorMessage); } /*! @brief Registers a global icon resource file * @param themeName A name of icon theme to use. */ bool registerGlobalIconsResource(const QString &themeName) { QString errorMessage; QString detailedErrorMessage; if (!registerGlobalIconsResource(themeName, &errorMessage, &detailedErrorMessage)) { showMessageBox(nullptr, errorMessage, detailedErrorMessage); kprWarning() << qPrintable(errorMessage); return false; } return true; } /*! @brief Registers a global icon resource file for default theme name. */ bool registerGlobalIconsResource() { return registerGlobalIconsResource(supportedIconTheme()); } /*! @brief Sets up a private icon resource file * @return @c false on failure and sets error message. Does not warn or exit on failure. * @param privateName Name to be used instead of application name for resource lookup * @param path Relative path to the resource file * @param themeName Icon theme to use. It affects filename. * @param errorMessage On failure it is set to a brief error message * @param errorDescription On failure it is set to a detailed error message * other for warning * @param prefix Resource path prefix. The default is useful for library-global resource, * other values is useful for plugins. */ bool setupPrivateIconsResource(const QString &privateName, const QString& path, const QString &themeName, QString *errorMessage, QString *detailedErrorMessage, const QString &prefix = QLatin1String(":/icons")) { // Register application's resource first to have priority over the theme. // Some icons may exists in both resources. QString extraLocation = QCoreApplication::applicationDirPath() + QStringLiteral("/data"); if (!registerIconsResource(privateName, path, QStandardPaths::AppDataLocation, QString(), QString(), errorMessage, detailedErrorMessage)) { return false; } bool changeTheme = false; #ifdef QT_GUI_LIB QIcon::setThemeSearchPaths(QStringList() << prefix << QIcon::themeSearchPaths()); changeTheme = 0 != QIcon::themeName().compare(themeName, Qt::CaseInsensitive); if (changeTheme) { QIcon::setThemeName(themeName); } #endif #ifdef KPROPERTY_KF KConfigGroup cg(KSharedConfig::openConfig(), "Icons"); changeTheme = changeTheme || 0 != cg.readEntry("Theme", QString()).compare(themeName, Qt::CaseInsensitive); // tell KIconLoader an co. about the theme if (changeTheme) { cg.writeEntry("Theme", themeName); cg.sync(); } #endif return true; } /*! @brief Sets up a private icon resource file * @return @c false on failure and sets error message. * @param privateName Name to be used instead of application name for resource lookup * @param path Relative path to the resource file * @param themeName Icon theme to use. It affects filename. * @param errorMessage On failure it is set to a brief error message. * @param errorDescription On failure it is set to a detailed error message. * other for warning * @param prefix Resource path prefix. The default is useful for library-global resource, * other values is useful for plugins. */ bool setupPrivateIconsResourceWithMessage(const QString &privateName, const QString& path, const QString &themeName, QString *errorMessage, QString *detailedErrorMessage, const QString &prefix = QLatin1String(":/icons")) { if (!setupPrivateIconsResource(privateName, path, themeName, errorMessage, detailedErrorMessage, prefix)) { showMessageBox(nullptr, *errorMessage, *detailedErrorMessage); return false; } return true; } /*! @overload setupPrivateIconsResourceWithMessage(QString &privateName, const QString& path, const QString &themeName, QString *errorMessage, QString *detailedErrorMessage, const QString &prefix = QLatin1String(":/icons")) Uses default theme name. */ bool setupPrivateIconsResourceWithMessage(const QString &privateName, const QString& path, QString *errorMessage, QString *detailedErrorMessage, const QString &prefix = QLatin1String(":/icons")) { return setupPrivateIconsResourceWithMessage(privateName, path, supportedIconTheme(), errorMessage, detailedErrorMessage, prefix); } bool setupPrivateIconsResourceWithMessage(const QString &privateName, const QString& path, QtMsgType messageType, const QString &prefix) { QString errorMessage; QString detailedErrorMessage; if (!setupPrivateIconsResourceWithMessage(privateName, path, &errorMessage, &detailedErrorMessage, prefix)) { if (messageType == QtFatalMsg) { kprCritical() << qPrintable(errorMessage) << qPrintable(detailedErrorMessage); } else { kprWarning() << qPrintable(errorMessage) << qPrintable(detailedErrorMessage); } return false; } return true; } bool setupGlobalIconTheme() { if (0 != QIcon::themeName().compare(supportedIconTheme(), Qt::CaseInsensitive)) { const QString message = QObject::tr( "\"%1\" supports only \"%2\" icon theme but current system theme is \"%3\". " "Application's icon theme will be changed to \"%2\". " "Please consider adding support for other themes to %4.") .arg(QLatin1String(KPROPERTYWIDGETS_BASE_NAME)).arg(supportedIconTheme()).arg(QIcon::themeName()) .arg(QCoreApplication::applicationName()); kprDebug() << qPrintable(message); if (!registerGlobalIconsResource()) { // don't fail, just warn const QString message = QObject::tr( "Failed to set icon theme to \"%1\". Icons in the application will be inconsistent. " "Please install .rcc file(s) for the system theme.") .arg(supportedIconTheme()); kprDebug() << qPrintable(message); return false; } } return true; } // ---- ValueOptionsHandler::ValueOptionsHandler(const KProperty &property) { - minValueText = property.option("minValueText").toString(); + minValueText = property.option("minValueText"); prefix = property.option("prefix").toString().trimmed(); suffix = property.option("suffix").toString().trimmed(); } QString ValueOptionsHandler::valueWithPrefixAndSuffix(const QString &valueString, const QLocale &locale) const { QString result = valueString; if (!suffix.isEmpty()) { if (locale.language() == QLocale::C) { result = QString::fromLatin1("%1 %2").arg(result).arg(suffix); } else { result = QObject::tr("%1 %2", " ").arg(result).arg(suffix); } } if (!prefix.isEmpty()) { if (locale.language() == QLocale::C) { result = QString::fromLatin1("%1 %2").arg(prefix).arg(result); } else { result = QObject::tr("%1 %2", " ").arg(prefix).arg(result); } } return result; } // ---- PainterSaver::PainterSaver(QPainter *p) : m_painter(p) { if (m_painter) { m_painter->save(); } } PainterSaver::~PainterSaver() { if (m_painter) { m_painter->restore(); } } } // namespace KPropertyUtilsPrivate diff --git a/src/KPropertyUtils_p.h b/src/KPropertyUtils_p.h index 464f6a8..5e69cdd 100644 --- a/src/KPropertyUtils_p.h +++ b/src/KPropertyUtils_p.h @@ -1,117 +1,118 @@ /* This file is part of the KDE project Copyright (C) 2010-2017 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPROPERTY_UTILS_P_H #define KPROPERTY_UTILS_P_H #include #include +#include class KProperty; namespace KPropertyUtilsPrivate { //! @return contrast color for @a c color. QColor contrastColor(const QColor& c); //! @return grid line color defined by a KPropertyEditorView widget contains @a widget //! Invalid color is returned if no grid is defined or KPropertyEditorView was not found. QColor gridLineColor(const QWidget *widget); //! @return supported icon theme //! @todo Support other themes QString supportedIconTheme(); /*! @brief Sets up a private icon resource file * Warns on failure and returns @c false. * @param privateName Name to be used instead of application name for resource lookup * @param path Relative path to the resource file * @param messageType Type of message to use on error, QtFatalMsg for fatal exit and any * other for warning * @param prefix Resource path prefix. The default is useful for library-global resource, * other values is useful for plugins. */ bool setupPrivateIconsResourceWithMessage(const QString &privateName, const QString& path, QtMsgType messageType, const QString &prefix = QLatin1String(":/icons")); //! Sets up a global icon theme if it is different from supported. //! Warns on failure and returns @c false. bool setupGlobalIconTheme(); //! Helper for handling minValueText, prefix and suffix property options class ValueOptionsHandler { public: explicit ValueOptionsHandler(const KProperty &property); //! @return @a valueString value with prefix and suffix, if present QString valueWithPrefixAndSuffix(const QString &valueString, const QLocale &locale) const; - QString minValueText; + QVariant minValueText; QString prefix; QString suffix; }; //! @short Manages the QPainter::save()/QPainter::restore() block using RAII /*! The PainterSaver class makes sure that restore() is called when exiting from the block of code. Instead of: @code painter.save(); // (code) painter.restore(); @endcode Use this: @code const PainterSaver saver(&painter); // (code) @endcode */ class PainterSaver { public: explicit PainterSaver(QPainter *p); ~PainterSaver(); private: QPainter* const m_painter; }; /** * @brief Returns @c true if native dialog should be used * * If @c false Qt's standard dialog should be used instead of the operating system native dialog. * Can be used with QColorDialog, QFileDialog and QFontDialog. * Depends on the curent desktop in use: * - on Unix (other than macOS) returns @c true if the XDG_CURRENT_DESKTOP environment variable is * empty or equal to "KDE", @c false for other values of XDG_CURRENT_DESKTOP (i.e. @c false for * XFCE, GNOME and other desktops) * - @c true for all other operating systems, i.e. for MS Windows, macOS, etc. * * @todo Share this code with KReport and Kexi */ bool shouldUseNativeDialogs(); } // namespace KPropertyUtilsPrivate #endif diff --git a/src/KProperty_p.h b/src/KProperty_p.h index 5314adb..b43380b 100644 --- a/src/KProperty_p.h +++ b/src/KProperty_p.h @@ -1,106 +1,108 @@ /* This file is part of the KDE project Copyright (C) 2009-2017 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPROPERTY_PROPERTY_P_H #define KPROPERTY_PROPERTY_P_H #include "KPropertySet.h" #include //! Default value for "step" option. Used for spin boxes, etc. #define KPROPERTY_DEFAULT_DOUBLE_VALUE_STEP 0.01 //! Default value for "precision" option. Used for spin boxes, etc. #define KPROPERTY_DEFAULT_DOUBLE_VALUE_PRECISION 2 //! @internal class Q_DECL_HIDDEN KProperty::Private { public: explicit Private(KProperty *prop); void setCaptionForDisplaying(const QString& captionForDisplaying); ~Private(); //! @return a value for option @a name or null value if there is no such option set. inline QVariant option(const char* name, const QVariant& defaultValue) const { - if (options.contains(name)) - return options[name]; - return parent ? parent->option(name, defaultValue) : defaultValue; + QVariant result = options.value(name); + if (result.isNull()) { + result = parent ? parent->option(name, defaultValue) : defaultValue; + } + return result; } //! @return true if value of this property differs from @a otherValue bool valueDiffersInternal(const QVariant &otherValue, KProperty::ValueOptions options); //! Sets value of the property to @a newValue bool setValueInternal(const QVariant &newValue, KProperty::ValueOptions valueOptions); /*! Adds @a prop as a child of this property. The children will be owned by this property. */ void addChild(KProperty *prop); /*! Adds @a set to this property. */ void addSet(KPropertySet *newSet); /*! Adds related property for this property. */ void addRelatedProperty(KProperty *property); /*! This method emits the @a KPropertySet::propertyChanged() signal. KProperty::setValue() calls this method if the value has been changed. */ void emitPropertyChanged(); void childValueChanged(KProperty *child, const QVariant &value, KProperty::ValueOptions valueOptions); KProperty * const q; int type; QByteArray name; QString captionForDisplaying; QString caption; QString description; QVariant value; QVariant oldValue; /*! The string-to-value correspondence list of the property.*/ KPropertyListData* listData; QString iconName; bool changed; bool storable; bool readOnly; bool visible; KProperty::ValueSyncPolicy valueSyncPolicy = KProperty::ValueSyncPolicy::Editor; QMap options; KComposedPropertyInterface *composed; //! Flag used to allow composed property to use setValue() without causing recursion bool useComposedProperty; //! Used when a single set is assigned for the property QPointer set; //! Used when multiple sets are assigned for the property QList< QPointer > *sets; KProperty *parent; QList *children; //! List of properties with the same name (when intersecting buffers) QList *relatedProperties; }; #endif diff --git a/src/editors/spinbox.cpp b/src/editors/spinbox.cpp index 4f017bf..a428d6b 100644 --- a/src/editors/spinbox.cpp +++ b/src/editors/spinbox.cpp @@ -1,341 +1,345 @@ /* This file is part of the KDE project Copyright (C) 2004 Cedric Pasteur Copyright (C) 2004 Alexander Dymo Copyright (C) 2008-2017 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "spinbox.h" #include "KProperty.h" #include "KProperty_p.h" #include "KPropertyEditorView.h" #include "KPropertyUtils.h" #include "KPropertyUtils_p.h" #include "KPropertyWidgetsFactory.h" #include "kproperty_debug.h" #include #include #include #include /** * Maximum double value working precisely. The spin box has localized contents and its code * supports just this maximum. For a 64-bit machine it's 2**53. * See also https://phabricator.kde.org/D5419#inline-22329 */ #define MAX_PRECISE_DOUBLE (pow(2, std::numeric_limits::digits)) //! @return font size expressed in points (pt) //! or if points are not available - in pixels (px) for @a font static QString fontSizeForCSS(const QFont& font) { return font.pointSize() > 0 ? QString::fromLatin1("%1pt").arg(font.pointSize()) : QString::fromLatin1("%1px").arg(font.pixelSize()); } static QString cssForSpinBox(const char *_class, const QFont& font, int itemHeight) { return QString::fromLatin1( "%5 { border-left: 0; border-right: 0; font-size: %3; } " "%5::down-button { height: %1px; %4 } " "%5::up-button { height: %2px; } " "QLineEdit { border-width:0px; } " ) .arg(itemHeight/2 - 1).arg(itemHeight - itemHeight/2 - 1) .arg(fontSizeForCSS(font)) .arg(QLatin1String((itemHeight/2 <= 9) ? "bottom: 2px;" : "bottom: 0px;")) .arg(QLatin1String(_class)); } namespace { bool intRangeValue(const KProperty &property, QVariant *min, QVariant *max) { Q_ASSERT(min); Q_ASSERT(max); *min = property.option("min", (property.type() == KProperty::UInt) ? 0 : -INT_MAX); *max = property.option("max", INT_MAX); if (!min->canConvert(QMetaType::Int)) { min->clear(); } if (!max->canConvert(QMetaType::Int)) { max->clear(); } return min->isValid() && max->isValid() && min->toInt() <= max->toInt(); } } // namespace class Q_DECL_HIDDEN KPropertyIntSpinBox::Private { public: explicit Private(const KProperty& prop) : isUnsigned(prop.type() == KProperty::UInt) { } const bool isUnsigned; }; KPropertyIntSpinBox::KPropertyIntSpinBox(const KProperty& prop, QWidget *parent, int itemHeight) : QSpinBox(parent) , d(new Private(prop)) { QLineEdit* le = findChild(); setContentsMargins(0,0,0,0); if (le) { le->setAlignment(Qt::AlignLeft); le->setContentsMargins(0,0,0,0); } setFrame(true); QString css = cssForSpinBox("QSpinBox", font(), itemHeight); KPropertyWidgetsFactory::setTopAndBottomBordersUsingStyleSheet(this, css); setStyleSheet(css); QVariant minVal; QVariant maxVal; if (intRangeValue(prop, &minVal, &maxVal)) { setRange(minVal.toInt(), maxVal.toInt()); } const KPropertyUtilsPrivate::ValueOptionsHandler options(prop); - if (!options.minValueText.isEmpty()) { - setSpecialValueText(options.minValueText); + if (!options.minValueText.isNull()) { + setSpecialValueText(options.minValueText.toString()); } if (!options.prefix.isEmpty()) { setPrefix(options.prefix + QLatin1Char(' ')); } if (!options.suffix.isEmpty()) { setSuffix(QLatin1Char(' ') + options.suffix); } connect(this, SIGNAL(valueChanged(int)), this, SLOT(slotValueChanged(int))); } KPropertyIntSpinBox::~KPropertyIntSpinBox() { delete d; } QVariant KPropertyIntSpinBox::value() const { if (d->isUnsigned) { return uint(QSpinBox::value()); } return QSpinBox::value(); } void KPropertyIntSpinBox::setValue(const QVariant& value) { int v( value.toInt() ); if (d->isUnsigned && v < 0) { kprWarning() << "could not assign negative value" << v << "- assigning 0"; v = 0; } QSpinBox::setValue(v); } void KPropertyIntSpinBox::slotValueChanged(int value) { Q_UNUSED(value); emit commitData(this); } //----------------------- class Q_DECL_HIDDEN KPropertyDoubleSpinBox::Private { public: bool dummy = false; }; namespace { bool doubleRangeValue(const KProperty &property, QVariant *min, QVariant *max) { Q_ASSERT(min); Q_ASSERT(max); *min = property.option("min", 0.0); *max = property.option("max", MAX_PRECISE_DOUBLE); if (!min->canConvert(QMetaType::Double)) { min->clear(); } if (!max->canConvert(QMetaType::Double)) { max->clear(); } return min->isValid() && max->isValid() && min->toDouble() <= max->toDouble(); } QVariant precisionValue(const KProperty &property) { QVariant result = property.option("precision", KPROPERTY_DEFAULT_DOUBLE_VALUE_PRECISION); if (result.canConvert(QMetaType::Int) && result.toInt() >= 0) { return result; } return QVariant(); } } // namespace KPropertyDoubleSpinBox::KPropertyDoubleSpinBox(const KProperty* prop, QWidget *parent, int itemHeight) : QDoubleSpinBox(parent) , d(new Private) { setFrame(false); QLineEdit* le = findChild(); if (le) { le->setAlignment(Qt::AlignLeft); le->setContentsMargins(0,0,0,0); le->setFrame(false); } /* KPropertyFactory::setTopAndBottomBordersUsingStyleSheet(sb, QString::fromLatin1( "QDoubleSpinBox { border-left: 0; border-right: 0; } " "QDoubleSpinBox::down-button { height: %1px; } " "QDoubleSpinBox::up-button { height: %2px; }" ).arg(itemHeight/2).arg(itemHeight - itemHeight/2) );*/ QString css = cssForSpinBox("QDoubleSpinBox", font(), itemHeight); KPropertyWidgetsFactory::setTopAndBottomBordersUsingStyleSheet(this, css); setStyleSheet(css); QVariant minVal; QVariant maxVal; if (doubleRangeValue(*prop, &minVal, &maxVal)) { setRange(minVal.toDouble(), maxVal.toDouble()); } QVariant step = prop->option("step", KPROPERTY_DEFAULT_DOUBLE_VALUE_STEP); if (step.canConvert(QMetaType::Double) && step.toDouble() > 0.0) { setSingleStep(step.toDouble()); } const QVariant precision = precisionValue(*prop); if (precision.isValid()) { setDecimals(precision.toInt()); } //! @todo implement slider // bool slider = prop->option("slider", false).toBool(); const KPropertyUtilsPrivate::ValueOptionsHandler options(*prop); - if (!options.minValueText.isEmpty()) { - setSpecialValueText(options.minValueText); + if (!options.minValueText.isNull()) { + setSpecialValueText(options.minValueText.toString()); } if (!options.prefix.isEmpty()) { setPrefix(options.prefix + QLatin1Char(' ')); } if (!options.suffix.isEmpty()) { setSuffix(QLatin1Char(' ') + options.suffix); } connect(this, SIGNAL(valueChanged(double)), this, SLOT(slotValueChanged(double))); } KPropertyDoubleSpinBox::~KPropertyDoubleSpinBox() { delete d; } void KPropertyDoubleSpinBox::resizeEvent( QResizeEvent * event ) { setFixedHeight(height() + 1); QDoubleSpinBox::resizeEvent(event); } void KPropertyDoubleSpinBox::slotValueChanged(double value) { Q_UNUSED(value); emit commitData(this); } //----------------------- KPropertyIntSpinBoxDelegate::KPropertyIntSpinBoxDelegate() { } QString KPropertyIntSpinBoxDelegate::propertyValueToString(const KProperty* prop, const QLocale &locale) const { //replace min value with minValueText if defined QVariant minVal; QVariant maxVal; const KPropertyUtilsPrivate::ValueOptionsHandler options(*prop); (void)intRangeValue(*prop, &minVal, &maxVal); - if (minVal.isValid() && minVal.toInt() == prop->value().toInt()) { - return options.minValueText; + if (minVal.isValid() && minVal.toInt() == prop->value().toInt() + && !options.minValueText.isNull()) + { + return options.minValueText.toString(); } return options.valueWithPrefixAndSuffix(valueToString(prop->value(), locale), locale); } QString KPropertyIntSpinBoxDelegate::valueToString(const QVariant& value, const QLocale &locale) const { return locale.toString(value.toReal(), 'f', 0); } QWidget* KPropertyIntSpinBoxDelegate::createEditor( int type, QWidget *parent, const QStyleOptionViewItem & option, const QModelIndex & index ) const { Q_UNUSED(type); KProperty *prop = KPropertyUtils::propertyForIndex(index); if (!prop) { return nullptr; } return new KPropertyIntSpinBox(*prop, parent, option.rect.height() - 2); } //----------------------- KPropertyDoubleSpinBoxDelegate::KPropertyDoubleSpinBoxDelegate() { } QString KPropertyDoubleSpinBoxDelegate::propertyValueToString(const KProperty* prop, const QLocale &locale) const { //replace min value with minValueText if defined QVariant minVal; QVariant maxVal; const KPropertyUtilsPrivate::ValueOptionsHandler options(*prop); (void)doubleRangeValue(*prop, &minVal, &maxVal); - if (minVal.isValid() && minVal.toDouble() == prop->value().toDouble()) { - return options.minValueText; + if (minVal.isValid() && minVal.toDouble() == prop->value().toDouble() + && !options.minValueText.isNull()) + { + return options.minValueText.toString(); } QString valueString; const QVariant precision = precisionValue(*prop); if (precision.isValid()) { valueString = locale.toString(prop->value().toReal(), 'f', precision.toInt()); } else { valueString = valueToString(prop->value().toReal(), locale); } return options.valueWithPrefixAndSuffix(valueString, locale); } QString KPropertyDoubleSpinBoxDelegate::valueToString(const QVariant& value, const QLocale &locale) const { return locale.toString(value.toReal()); } QWidget* KPropertyDoubleSpinBoxDelegate::createEditor( int type, QWidget *parent, const QStyleOptionViewItem & option, const QModelIndex & index ) const { Q_UNUSED(type); KProperty *prop = KPropertyUtils::propertyForIndex(index); if (!prop) { return nullptr; } return new KPropertyDoubleSpinBox(prop, parent, option.rect.height() - 2 - 1); }