diff --git a/src/KPropertyEditorDataModel.cpp b/src/KPropertyEditorDataModel.cpp index 525e2f7..8f2e2f9 100644 --- a/src/KPropertyEditorDataModel.cpp +++ b/src/KPropertyEditorDataModel.cpp @@ -1,296 +1,296 @@ /* This file is part of the KDE project Copyright (C) 2008-2009 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 "KPropertyEditorDataModel.h" #include "KPropertyWidgetsFactory.h" #include "KProperty.h" #include "kproperty_debug.h" #include class Q_DECL_HIDDEN KPropertyEditorDataModel::Private { public: explicit Private(KPropertySet *_set, KPropertySetIterator::Order _order = KPropertySetIterator::InsertionOrder) : set(_set), order(_order) { Q_ASSERT(set); if (!set) { - qFatal("KPropertyEditorDataModel requires a KPropertySet object"); + kprCritical() << "KPropertyEditorDataModel requires a KPropertySet object"; } } KPropertySet *set; KProperty rootItem; QHash indicesForNames; KPropertySetIterator::Order order; //!< order of properties }; // ------------------- //! A property selector offering functor selecting only visible properties. /*! Used e.g. in EditorDataModel::index(). */ class VisiblePropertySelector : public KPropertySelector { public: VisiblePropertySelector() {} virtual bool operator()(const KProperty& prop) const { return prop.isVisible(); } KPropertySelector* clone() const { return new VisiblePropertySelector(); } }; // ------------------- KPropertyEditorDataModel::KPropertyEditorDataModel(KPropertySet *propertySet, QObject *parent, KPropertySetIterator::Order order) : QAbstractItemModel(parent) , d(new Private(propertySet, order)) { collectIndices(); } KPropertyEditorDataModel::~KPropertyEditorDataModel() { delete d; } typedef QPair NameAndCaption; #if 0 static inline bool nameAndCaptionLessThan(const NameAndCaption &n1, const NameAndCaption &n2) { return QString::compare(n1.second, n2.second, Qt::CaseInsensitive) < 0; } #endif void KPropertyEditorDataModel::collectIndices() const { KPropertySetIterator it(*d->set, VisiblePropertySelector()); if (d->order == KPropertySetIterator::AlphabeticalOrder) { it.setOrder(KPropertySetIterator::AlphabeticalOrder); } d->indicesForNames.clear(); for (int row = 0; it.current(); row++, ++it) { // kprDebug() << it.current()->name() << "->" << row; d->indicesForNames.insert( it.current()->name(), QPersistentModelIndex( createIndex(row, 0, it.current()) ) ); } } QModelIndex KPropertyEditorDataModel::indexForPropertyName(const QByteArray& propertyName) const { return (const QModelIndex &)d->indicesForNames.value(propertyName); } QModelIndex KPropertyEditorDataModel::indexForColumn(const QModelIndex& index, int column) const { if (column == 0) return index; return createIndex(index.row(), column, propertyForIndex(index)); } int KPropertyEditorDataModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 2; } QVariant KPropertyEditorDataModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); const int col = index.column(); if (col == 0) { KProperty *prop = propertyForIndex(index); if (role == Qt::DisplayRole) { if (!prop->caption().isEmpty()) return prop->caption(); return prop->name(); } else if (role == PropertyModifiedRole) { return prop->isModified(); } } else if (col == 1) { KProperty *prop = propertyForIndex(index); if (role == Qt::EditRole) { return prop->value(); } else if (role == Qt::DisplayRole) { return KPropertyFactoryManager::self()->propertyValueToLocalizedString(prop); } } return QVariant(); } Qt::ItemFlags KPropertyEditorDataModel::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::ItemIsEnabled; const int col = index.column(); Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable; KProperty *prop = propertyForIndex(index); if (prop) { if (col == 1 && !prop->isReadOnly() && !d->set->isReadOnly()) { f |= Qt::ItemIsEditable; } } return f; } KProperty *KPropertyEditorDataModel::propertyForIndex(const QModelIndex &index) const { if (index.isValid()) { KProperty *item = static_cast(index.internalPointer()); if (item) return item; } return &d->rootItem; } QVariant KPropertyEditorDataModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (section == 0) { return tr("Name", "Property name"); } else { return tr("Value", "Property value"); } } return QVariant(); } QModelIndex KPropertyEditorDataModel::index(int row, int column, const QModelIndex &parent) const { if (parent.isValid() && parent.column() != 0) return QModelIndex(); KProperty *parentItem = propertyForIndex(parent); KProperty *childItem; if (parentItem == &d->rootItem) { // special case: top level int visibleRows = 0; KPropertySetIterator it(*d->set, VisiblePropertySelector()); if (d->order == KPropertySetIterator::AlphabeticalOrder) { it.setOrder(KPropertySetIterator::AlphabeticalOrder); } //! @todo use qBinaryFind()? for (; visibleRows < row && it.current(); visibleRows++, ++it) ; childItem = it.current(); } else { const QList* children = parentItem->children(); if (!children) return QModelIndex(); childItem = children->value(row); } if (!childItem) return QModelIndex(); return createIndex(row, column, childItem); } QModelIndex KPropertyEditorDataModel::parent(const QModelIndex &index) const { if (!index.isValid()) return QModelIndex(); KProperty *childItem = propertyForIndex(index); KProperty *parentItem = childItem->parent(); if (!parentItem) return QModelIndex(); const QList* children = parentItem->children(); Q_ASSERT(children); const int indexOfItem = children->indexOf(childItem); Q_ASSERT(indexOfItem != -1); return createIndex(indexOfItem, 0, parentItem); } int KPropertyEditorDataModel::rowCount(const QModelIndex &parent) const { KProperty *parentItem = propertyForIndex(parent); if (!parentItem || parentItem == &d->rootItem) { // top level return d->set->count(VisiblePropertySelector()); } const QList* children = parentItem->children(); return children ? children->count() : 0; } bool KPropertyEditorDataModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (role != Qt::EditRole) return false; KProperty *item = propertyForIndex(index); if (item == &d->rootItem) return false; item->setValue(value); //don't do that or cursor position and editor state will be reset: //emit dataChanged(index, index, {Qt::EditRole}); return true; } bool KPropertyEditorDataModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role) { Q_UNUSED(section); Q_UNUSED(orientation); Q_UNUSED(value); Q_UNUSED(role); return false; } QModelIndex KPropertyEditorDataModel::buddy(const QModelIndex & idx) const { if (idx.column() == 0) return index( idx.row(), 1, parent(idx)); return idx; } KPropertySet* KPropertyEditorDataModel::propertySet() const { return d->set; } void KPropertyEditorDataModel::setOrder(KPropertySetIterator::Order order) { if (d->order != order) { d->order = order; collectIndices(); } } KPropertySetIterator::Order KPropertyEditorDataModel::order() const { return d->order; } bool KPropertyEditorDataModel::hasChildren(const QModelIndex & parent) const { KProperty *parentItem = propertyForIndex(parent); if (!parentItem || parentItem == &d->rootItem) { // top level return d->set->hasVisibleProperties(); } const QList* children = parentItem->children(); return children && !children->isEmpty(); } diff --git a/src/KPropertyUtils_p.h b/src/KPropertyUtils_p.h index 06eb400..059c8d3 100644 --- a/src/KPropertyUtils_p.h +++ b/src/KPropertyUtils_p.h @@ -1,418 +1,425 @@ /* This file is part of the KDE project Copyright (C) 2010-2016 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 +#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 KPropertyUtils { //! @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); inline 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 //! @todo Support other themes const QString supportedIconTheme = QLatin1String("breeze"); //! @brief @return true if @a path is readable inline 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). inline 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 */ inline 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 */ inline 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 //! @todo 3.1 Re-add translation *errorMessage = /*QObject::tr*/ QString::fromLatin1( "Could not open icon resource file \"%1\". " "Application will not start. " "Please check if it is properly installed.") .arg(QFileInfo(path).fileName()); //! @todo 3.1 Re-add translation *detailedErrorMessage = QString::fromLatin1("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 */ inline 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. */ inline bool registerGlobalIconsResource(const QString &themeName) { QString errorMessage; QString detailedErrorMessage; if (!registerGlobalIconsResource(themeName, &errorMessage, &detailedErrorMessage)) { showMessageBox(nullptr, errorMessage, detailedErrorMessage); - qWarning() << qPrintable(errorMessage); + kprWarning() << qPrintable(errorMessage); return false; } return true; } /*! @brief Registers a global icon resource file for default theme name. */ inline 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. */ inline 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. */ inline 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. */ inline 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); } /*! @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. */ inline bool setupPrivateIconsResourceWithMessage(const QString &privateName, const QString& path, QtMsgType messageType, const QString &prefix = QLatin1String(":/icons")) { QString errorMessage; QString detailedErrorMessage; if (!setupPrivateIconsResourceWithMessage(privateName, path, &errorMessage, &detailedErrorMessage, prefix)) { if (messageType == QtFatalMsg) { - qFatal("%s %s", qPrintable(errorMessage), qPrintable(detailedErrorMessage)); + kprCritical() << qPrintable(errorMessage) << qPrintable(detailedErrorMessage); } else { - qWarning() << qPrintable(errorMessage) << qPrintable(detailedErrorMessage); + kprWarning() << qPrintable(errorMessage) << qPrintable(detailedErrorMessage); } return false; } return true; } //! Sets up a global icon theme if it is different from supported. //! Warns on failure and returns @c false. inline bool setupGlobalIconTheme() { if (0 != QIcon::themeName().compare(supportedIconTheme, Qt::CaseInsensitive)) { const QString message = QString::fromLatin1( "\"%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()); - qDebug() << qPrintable(message); + kprDebug() << qPrintable(message); if (!registerGlobalIconsResource()) { // don't fail, just warn const QString message = QString::fromLatin1( "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); - qDebug() << qPrintable(message); + kprDebug() << qPrintable(message); return false; } } return true; } //! @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) : m_painter(p) { if (m_painter) { m_painter->save(); } } ~PainterSaver() { if (m_painter) { m_painter->restore(); } } private: QPainter* const m_painter; }; } #endif