2011-04-27 10:05:43 +00:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
2013-01-02 11:13:29 +00:00
|
|
|
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
2012-09-19 12:28:29 +00:00
|
|
|
** Contact: http://www.qt-project.org/legal
|
2011-04-27 10:05:43 +00:00
|
|
|
**
|
|
|
|
** This file is part of the QtGui module of the Qt Toolkit.
|
|
|
|
**
|
|
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
2012-09-19 12:28:29 +00:00
|
|
|
** Commercial License Usage
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
** a written agreement between you and Digia. For licensing terms and
|
|
|
|
** conditions see http://qt.digia.com/licensing. For further information
|
|
|
|
** use the contact form at http://qt.digia.com/contact-us.
|
|
|
|
**
|
2011-04-27 10:05:43 +00:00
|
|
|
** GNU Lesser General Public License Usage
|
2012-09-19 12:28:29 +00:00
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
|
|
** General Public License version 2.1 as published by the Free Software
|
|
|
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
|
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
|
**
|
|
|
|
** In addition, as a special exception, Digia gives you certain additional
|
|
|
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
2011-04-27 10:05:43 +00:00
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
**
|
2011-05-24 09:34:08 +00:00
|
|
|
** GNU General Public License Usage
|
2012-09-19 12:28:29 +00:00
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
** General Public License version 3.0 as published by the Free Software
|
|
|
|
** Foundation and appearing in the file LICENSE.GPL included in the
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
** ensure the GNU General Public License version 3.0 requirements will be
|
|
|
|
** met: http://www.gnu.org/copyleft/gpl.html.
|
2011-04-27 10:05:43 +00:00
|
|
|
**
|
2012-01-24 06:17:24 +00:00
|
|
|
**
|
2011-04-27 10:05:43 +00:00
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
#ifndef QT_NO_ICON
|
|
|
|
#include <private/qiconloader_p.h>
|
|
|
|
|
2011-05-03 18:20:44 +00:00
|
|
|
#include <private/qguiapplication_p.h>
|
2011-04-27 10:05:43 +00:00
|
|
|
#include <private/qicon_p.h>
|
|
|
|
|
2012-05-14 16:02:16 +00:00
|
|
|
#include <QtGui/QIconEnginePlugin>
|
2011-04-27 10:05:43 +00:00
|
|
|
#include <QtGui/QPixmapCache>
|
Expose QPA API under qpa/*
The main reasons for doing this are:
1. _qpa.h end up in the master QtGui include file. QtGui is meant for
userland applications. qpa code is neither binary nor source compatible.
Inadvertant use of QPA api makes the user code binary-incompatible.
2. syncqt creates forwarding headers for non-private header files. This
gives people the impression that this is public API.
As discussed on the mailing list, even though QPA api is internal and subject
to change, it needs to treated differently from private headers since they
will be used by in-qtbase and out-of-qtbase plugins.
This commit does the following:
1. The _qpa in QPA header files is dropped.
2. syncqt now treats any file with qplatform prefix as a special file and
moves it to qpa/ directory. The recommended way of using QPA API in plugins
is: #include <qpa/qplatformfoo.h>. This allows the user include QPA API
from multiple modules (for example, qplatformfoo might be in QtPrintSupport)
3. The user needs to explicitly add QT += <module>-private to get access to
the qpa api.
4. Creates compat headers for the olden style qplatformfoo_qpa.h and QPlatformFoo
includes.
This commit does not change the cpp filenames. This requires a more careful
merging of existing non qpa cpp files and existing cpp files on a case by
case basis. This can be done at anytime.
The following files are not renamed as part of this changed but will be fixed
as part of a future change:
src/gui/kernel/qgenericpluginfactory_qpa.h
src/gui/kernel/qgenericplugin_qpa.h
src/gui/kernel/qwindowsysteminterface_qpa.h
files were renamed using
for x in `find . -name "qplatform*_qpa.h"`; do git mv $x "${x/_qpa.h/.h}"; done
for x in `find . -name "qplatform*_qpa_p.h"`; do git mv $x "${x/_qpa_p.h/_p.h}"; done
includes were renamed using script
for file in `find . -name "*.h" -or -name "*.cpp" -or -name "*.mm"`; do
sed -i -e 's,.*#.*include.*<\(Qt.*/\)\?\(QPlatform.*\)>,#include <qpa/\L\2.h>,g' \
-e 's,.*#.*include.*"\(Qt.*/\)\?\(QPlatform.*\)",#include <qpa/\L\2.h>,g' \
-e 's,.*#.*include.* "\(qplatform.*\)_qpa.h",#include <qpa/\L\1.h>,g' \
-e 's,.*#.*include.*"\(qplatform.*\)_qpa_p.h",#include <qpa/\L\1_p.h>,g' \
-e 's,.*#.*include.*<\(Qt.*/\|Qt.*/private/\|private/\)\?\(qplatform.*\)_qpa\(.*\)>,#include <qpa/\2\3>,g' \
-e 's,.*#.*include.*"\(Qt.*/\|Qt.*/private/\|private/\)\?\(qplatform.*\)_qpa\(.*\)",#include <qpa/\2\3>,g' \
$file
done
Change-Id: I04a350314a45746e3911f54b3b21ad03315afb67
Reviewed-by: Morten Johan Sørvig <morten.sorvig@nokia.com>
Reviewed-by: Samuel Rødal <samuel.rodal@nokia.com>
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@nokia.com>
Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
Reviewed-by: Lars Knoll <lars.knoll@nokia.com>
Reviewed-by: Gunnar Sletta <gunnar.sletta@nokia.com>
2012-04-26 23:33:35 +00:00
|
|
|
#include <qpa/qplatformtheme.h>
|
2012-05-14 16:02:16 +00:00
|
|
|
#include <QtGui/QIconEngine>
|
|
|
|
#include <QtGui/QPalette>
|
2011-04-27 10:05:43 +00:00
|
|
|
#include <QtCore/QList>
|
|
|
|
#include <QtCore/QHash>
|
|
|
|
#include <QtCore/QDir>
|
|
|
|
#include <QtCore/QSettings>
|
|
|
|
#include <QtGui/QPainter>
|
|
|
|
|
|
|
|
#ifdef Q_WS_MAC
|
|
|
|
#include <private/qt_cocoa_helpers_mac_p.h>
|
|
|
|
#endif
|
|
|
|
|
2011-05-03 08:40:58 +00:00
|
|
|
#include <private/qhexstring_p.h>
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
|
|
|
Q_GLOBAL_STATIC(QIconLoader, iconLoaderInstance)
|
|
|
|
|
|
|
|
/* Theme to use in last resort, if the theme does not have the icon, neither the parents */
|
|
|
|
static QString fallbackTheme()
|
|
|
|
{
|
2012-02-10 16:17:36 +00:00
|
|
|
if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) {
|
2014-09-03 07:45:16 +00:00
|
|
|
const QVariant themeHint = theme->themeHint(QPlatformTheme::SystemIconFallbackThemeName);
|
2012-02-10 16:17:36 +00:00
|
|
|
if (themeHint.isValid())
|
|
|
|
return themeHint.toString();
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
|
|
|
QIconLoader::QIconLoader() :
|
|
|
|
m_themeKey(1), m_supportsSvg(false), m_initialized(false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// We lazily initialize the loader to make static icons
|
|
|
|
// work. Though we do not officially support this.
|
2012-02-10 16:17:36 +00:00
|
|
|
|
|
|
|
static inline QString systemThemeName()
|
|
|
|
{
|
|
|
|
if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) {
|
|
|
|
const QVariant themeHint = theme->themeHint(QPlatformTheme::SystemIconThemeName);
|
|
|
|
if (themeHint.isValid())
|
|
|
|
return themeHint.toString();
|
|
|
|
}
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline QStringList systemIconSearchPaths()
|
|
|
|
{
|
|
|
|
if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) {
|
|
|
|
const QVariant themeHint = theme->themeHint(QPlatformTheme::IconThemeSearchPaths);
|
|
|
|
if (themeHint.isValid())
|
|
|
|
return themeHint.toStringList();
|
|
|
|
}
|
|
|
|
return QStringList();
|
|
|
|
}
|
|
|
|
|
2013-04-23 06:41:08 +00:00
|
|
|
#ifndef QT_NO_LIBRARY
|
|
|
|
extern QFactoryLoader *qt_iconEngineFactoryLoader(); // qicon.cpp
|
|
|
|
#endif
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
void QIconLoader::ensureInitialized()
|
|
|
|
{
|
|
|
|
if (!m_initialized) {
|
|
|
|
m_initialized = true;
|
|
|
|
|
|
|
|
Q_ASSERT(qApp);
|
|
|
|
|
2012-02-10 16:17:36 +00:00
|
|
|
m_systemTheme = systemThemeName();
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
if (m_systemTheme.isEmpty())
|
|
|
|
m_systemTheme = fallbackTheme();
|
|
|
|
#ifndef QT_NO_LIBRARY
|
2013-04-23 06:41:08 +00:00
|
|
|
if (qt_iconEngineFactoryLoader()->keyMap().key(QLatin1String("svg"), -1) != -1)
|
2011-04-27 10:05:43 +00:00
|
|
|
m_supportsSvg = true;
|
|
|
|
#endif //QT_NO_LIBRARY
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QIconLoader *QIconLoader::instance()
|
|
|
|
{
|
2013-03-22 07:47:51 +00:00
|
|
|
iconLoaderInstance()->ensureInitialized();
|
2011-04-27 10:05:43 +00:00
|
|
|
return iconLoaderInstance();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Queries the system theme and invalidates existing
|
|
|
|
// icons if the theme has changed.
|
|
|
|
void QIconLoader::updateSystemTheme()
|
|
|
|
{
|
|
|
|
// Only change if this is not explicitly set by the user
|
|
|
|
if (m_userTheme.isEmpty()) {
|
2012-02-10 16:17:36 +00:00
|
|
|
QString theme = systemThemeName();
|
2011-04-27 10:05:43 +00:00
|
|
|
if (theme.isEmpty())
|
|
|
|
theme = fallbackTheme();
|
|
|
|
if (theme != m_systemTheme) {
|
|
|
|
m_systemTheme = theme;
|
|
|
|
invalidateKey();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QIconLoader::setThemeName(const QString &themeName)
|
|
|
|
{
|
|
|
|
m_userTheme = themeName;
|
|
|
|
invalidateKey();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QIconLoader::setThemeSearchPath(const QStringList &searchPaths)
|
|
|
|
{
|
|
|
|
m_iconDirs = searchPaths;
|
|
|
|
themeList.clear();
|
|
|
|
invalidateKey();
|
|
|
|
}
|
|
|
|
|
|
|
|
QStringList QIconLoader::themeSearchPaths() const
|
|
|
|
{
|
|
|
|
if (m_iconDirs.isEmpty()) {
|
2012-02-10 16:17:36 +00:00
|
|
|
m_iconDirs = systemIconSearchPaths();
|
2011-04-27 10:05:43 +00:00
|
|
|
// Always add resource directory as search path
|
|
|
|
m_iconDirs.append(QLatin1String(":/icons"));
|
|
|
|
}
|
|
|
|
return m_iconDirs;
|
|
|
|
}
|
|
|
|
|
|
|
|
QIconTheme::QIconTheme(const QString &themeName)
|
|
|
|
: m_valid(false)
|
|
|
|
{
|
|
|
|
QFile themeIndex;
|
|
|
|
|
|
|
|
QStringList iconDirs = QIcon::themeSearchPaths();
|
|
|
|
for ( int i = 0 ; i < iconDirs.size() ; ++i) {
|
|
|
|
QDir iconDir(iconDirs[i]);
|
|
|
|
QString themeDir = iconDir.path() + QLatin1Char('/') + themeName;
|
|
|
|
themeIndex.setFileName(themeDir + QLatin1String("/index.theme"));
|
|
|
|
if (themeIndex.exists()) {
|
|
|
|
m_contentDir = themeDir;
|
|
|
|
m_valid = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifndef QT_NO_SETTINGS
|
|
|
|
if (themeIndex.exists()) {
|
|
|
|
const QSettings indexReader(themeIndex.fileName(), QSettings::IniFormat);
|
|
|
|
QStringListIterator keyIterator(indexReader.allKeys());
|
|
|
|
while (keyIterator.hasNext()) {
|
|
|
|
|
|
|
|
const QString key = keyIterator.next();
|
|
|
|
if (key.endsWith(QLatin1String("/Size"))) {
|
|
|
|
// Note the QSettings ini-format does not accept
|
|
|
|
// slashes in key names, hence we have to cheat
|
|
|
|
if (int size = indexReader.value(key).toInt()) {
|
|
|
|
QString directoryKey = key.left(key.size() - 5);
|
|
|
|
QIconDirInfo dirInfo(directoryKey);
|
|
|
|
dirInfo.size = size;
|
|
|
|
QString type = indexReader.value(directoryKey +
|
|
|
|
QLatin1String("/Type")
|
|
|
|
).toString();
|
|
|
|
|
|
|
|
if (type == QLatin1String("Fixed"))
|
|
|
|
dirInfo.type = QIconDirInfo::Fixed;
|
|
|
|
else if (type == QLatin1String("Scalable"))
|
|
|
|
dirInfo.type = QIconDirInfo::Scalable;
|
|
|
|
else
|
|
|
|
dirInfo.type = QIconDirInfo::Threshold;
|
|
|
|
|
|
|
|
dirInfo.threshold = indexReader.value(directoryKey +
|
|
|
|
QLatin1String("/Threshold"),
|
|
|
|
2).toInt();
|
|
|
|
|
|
|
|
dirInfo.minSize = indexReader.value(directoryKey +
|
|
|
|
QLatin1String("/MinSize"),
|
|
|
|
size).toInt();
|
|
|
|
|
|
|
|
dirInfo.maxSize = indexReader.value(directoryKey +
|
|
|
|
QLatin1String("/MaxSize"),
|
|
|
|
size).toInt();
|
|
|
|
m_keyList.append(dirInfo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parent themes provide fallbacks for missing icons
|
|
|
|
m_parents = indexReader.value(
|
|
|
|
QLatin1String("Icon Theme/Inherits")).toStringList();
|
|
|
|
|
|
|
|
// Ensure a default platform fallback for all themes
|
2012-02-10 16:17:36 +00:00
|
|
|
if (m_parents.isEmpty()) {
|
|
|
|
const QString fallback = fallbackTheme();
|
|
|
|
if (!fallback.isEmpty())
|
|
|
|
m_parents.append(fallback);
|
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// Ensure that all themes fall back to hicolor
|
|
|
|
if (!m_parents.contains(QLatin1String("hicolor")))
|
|
|
|
m_parents.append(QLatin1String("hicolor"));
|
|
|
|
}
|
|
|
|
#endif //QT_NO_SETTINGS
|
|
|
|
}
|
|
|
|
|
|
|
|
QThemeIconEntries QIconLoader::findIconHelper(const QString &themeName,
|
|
|
|
const QString &iconName,
|
|
|
|
QStringList &visited) const
|
|
|
|
{
|
|
|
|
QThemeIconEntries entries;
|
|
|
|
Q_ASSERT(!themeName.isEmpty());
|
|
|
|
|
|
|
|
QPixmap pixmap;
|
|
|
|
|
|
|
|
// Used to protect against potential recursions
|
|
|
|
visited << themeName;
|
|
|
|
|
|
|
|
QIconTheme theme = themeList.value(themeName);
|
|
|
|
if (!theme.isValid()) {
|
|
|
|
theme = QIconTheme(themeName);
|
|
|
|
if (!theme.isValid())
|
|
|
|
theme = QIconTheme(fallbackTheme());
|
|
|
|
|
|
|
|
themeList.insert(themeName, theme);
|
|
|
|
}
|
|
|
|
|
|
|
|
QString contentDir = theme.contentDir() + QLatin1Char('/');
|
2014-08-09 00:27:31 +00:00
|
|
|
const QVector<QIconDirInfo> subDirs = theme.keyList();
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
const QString svgext(QLatin1String(".svg"));
|
|
|
|
const QString pngext(QLatin1String(".png"));
|
|
|
|
|
|
|
|
// Add all relevant files
|
|
|
|
for (int i = 0; i < subDirs.size() ; ++i) {
|
|
|
|
const QIconDirInfo &dirInfo = subDirs.at(i);
|
|
|
|
QString subdir = dirInfo.path;
|
|
|
|
QDir currentDir(contentDir + subdir);
|
|
|
|
if (currentDir.exists(iconName + pngext)) {
|
|
|
|
PixmapEntry *iconEntry = new PixmapEntry;
|
|
|
|
iconEntry->dir = dirInfo;
|
|
|
|
iconEntry->filename = currentDir.filePath(iconName + pngext);
|
|
|
|
// Notice we ensure that pixmap entries always come before
|
|
|
|
// scalable to preserve search order afterwards
|
|
|
|
entries.prepend(iconEntry);
|
|
|
|
} else if (m_supportsSvg &&
|
|
|
|
currentDir.exists(iconName + svgext)) {
|
|
|
|
ScalableEntry *iconEntry = new ScalableEntry;
|
|
|
|
iconEntry->dir = dirInfo;
|
|
|
|
iconEntry->filename = currentDir.filePath(iconName + svgext);
|
|
|
|
entries.append(iconEntry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entries.isEmpty()) {
|
|
|
|
const QStringList parents = theme.parents();
|
|
|
|
// Search recursively through inherited themes
|
|
|
|
for (int i = 0 ; i < parents.size() ; ++i) {
|
|
|
|
|
|
|
|
const QString parentTheme = parents.at(i).trimmed();
|
|
|
|
|
|
|
|
if (!visited.contains(parentTheme)) // guard against recursion
|
|
|
|
entries = findIconHelper(parentTheme, iconName, visited);
|
|
|
|
|
|
|
|
if (!entries.isEmpty()) // success
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return entries;
|
|
|
|
}
|
|
|
|
|
|
|
|
QThemeIconEntries QIconLoader::loadIcon(const QString &name) const
|
|
|
|
{
|
|
|
|
if (!themeName().isEmpty()) {
|
|
|
|
QStringList visited;
|
|
|
|
return findIconHelper(themeName(), name, visited);
|
|
|
|
}
|
|
|
|
|
|
|
|
return QThemeIconEntries();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// -------- Icon Loader Engine -------- //
|
|
|
|
|
|
|
|
|
|
|
|
QIconLoaderEngine::QIconLoaderEngine(const QString& iconName)
|
|
|
|
: m_iconName(iconName), m_key(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QIconLoaderEngine::~QIconLoaderEngine()
|
|
|
|
{
|
2014-08-22 19:07:32 +00:00
|
|
|
qDeleteAll(m_entries);
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QIconLoaderEngine::QIconLoaderEngine(const QIconLoaderEngine &other)
|
2012-01-25 12:58:17 +00:00
|
|
|
: QIconEngine(other),
|
2011-04-27 10:05:43 +00:00
|
|
|
m_iconName(other.m_iconName),
|
|
|
|
m_key(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2012-01-25 12:58:17 +00:00
|
|
|
QIconEngine *QIconLoaderEngine::clone() const
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
|
|
|
return new QIconLoaderEngine(*this);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QIconLoaderEngine::read(QDataStream &in) {
|
|
|
|
in >> m_iconName;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QIconLoaderEngine::write(QDataStream &out) const
|
|
|
|
{
|
|
|
|
out << m_iconName;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QIconLoaderEngine::hasIcon() const
|
|
|
|
{
|
|
|
|
return !(m_entries.isEmpty());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lazily load the icon
|
|
|
|
void QIconLoaderEngine::ensureLoaded()
|
|
|
|
{
|
2013-03-22 07:47:51 +00:00
|
|
|
if (!(QIconLoader::instance()->themeKey() == m_key)) {
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2014-08-22 19:07:32 +00:00
|
|
|
qDeleteAll(m_entries);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2013-03-22 07:47:51 +00:00
|
|
|
m_entries = QIconLoader::instance()->loadIcon(m_iconName);
|
|
|
|
m_key = QIconLoader::instance()->themeKey();
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QIconLoaderEngine::paint(QPainter *painter, const QRect &rect,
|
|
|
|
QIcon::Mode mode, QIcon::State state)
|
|
|
|
{
|
|
|
|
QSize pixmapSize = rect.size();
|
|
|
|
#if defined(Q_WS_MAC)
|
|
|
|
pixmapSize *= qt_mac_get_scalefactor();
|
|
|
|
#endif
|
|
|
|
painter->drawPixmap(rect, pixmap(pixmapSize, mode, state));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This algorithm is defined by the freedesktop spec:
|
|
|
|
* http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
|
|
|
|
*/
|
|
|
|
static bool directoryMatchesSize(const QIconDirInfo &dir, int iconsize)
|
|
|
|
{
|
|
|
|
if (dir.type == QIconDirInfo::Fixed) {
|
|
|
|
return dir.size == iconsize;
|
|
|
|
|
|
|
|
} else if (dir.type == QIconDirInfo::Scalable) {
|
|
|
|
return dir.size <= dir.maxSize &&
|
|
|
|
iconsize >= dir.minSize;
|
|
|
|
|
|
|
|
} else if (dir.type == QIconDirInfo::Threshold) {
|
|
|
|
return iconsize >= dir.size - dir.threshold &&
|
|
|
|
iconsize <= dir.size + dir.threshold;
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_ASSERT(1); // Not a valid value
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This algorithm is defined by the freedesktop spec:
|
|
|
|
* http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
|
|
|
|
*/
|
|
|
|
static int directorySizeDistance(const QIconDirInfo &dir, int iconsize)
|
|
|
|
{
|
|
|
|
if (dir.type == QIconDirInfo::Fixed) {
|
|
|
|
return qAbs(dir.size - iconsize);
|
|
|
|
|
|
|
|
} else if (dir.type == QIconDirInfo::Scalable) {
|
|
|
|
if (iconsize < dir.minSize)
|
|
|
|
return dir.minSize - iconsize;
|
|
|
|
else if (iconsize > dir.maxSize)
|
|
|
|
return iconsize - dir.maxSize;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
} else if (dir.type == QIconDirInfo::Threshold) {
|
|
|
|
if (iconsize < dir.size - dir.threshold)
|
|
|
|
return dir.minSize - iconsize;
|
|
|
|
else if (iconsize > dir.size + dir.threshold)
|
|
|
|
return iconsize - dir.maxSize;
|
|
|
|
else return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_ASSERT(1); // Not a valid value
|
|
|
|
return INT_MAX;
|
|
|
|
}
|
|
|
|
|
|
|
|
QIconLoaderEngineEntry *QIconLoaderEngine::entryForSize(const QSize &size)
|
|
|
|
{
|
|
|
|
int iconsize = qMin(size.width(), size.height());
|
|
|
|
|
|
|
|
// Note that m_entries are sorted so that png-files
|
|
|
|
// come first
|
|
|
|
|
2014-08-22 19:03:34 +00:00
|
|
|
const int numEntries = m_entries.size();
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
// Search for exact matches first
|
2014-08-22 19:03:34 +00:00
|
|
|
for (int i = 0; i < numEntries; ++i) {
|
2011-04-27 10:05:43 +00:00
|
|
|
QIconLoaderEngineEntry *entry = m_entries.at(i);
|
|
|
|
if (directoryMatchesSize(entry->dir, iconsize)) {
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find the minimum distance icon
|
|
|
|
int minimalSize = INT_MAX;
|
|
|
|
QIconLoaderEngineEntry *closestMatch = 0;
|
2014-08-22 19:03:34 +00:00
|
|
|
for (int i = 0; i < numEntries; ++i) {
|
2011-04-27 10:05:43 +00:00
|
|
|
QIconLoaderEngineEntry *entry = m_entries.at(i);
|
|
|
|
int distance = directorySizeDistance(entry->dir, iconsize);
|
|
|
|
if (distance < minimalSize) {
|
|
|
|
minimalSize = distance;
|
|
|
|
closestMatch = entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return closestMatch;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns the actual icon size. For scalable svg's this is equivalent
|
|
|
|
* to the requested size. Otherwise the closest match is returned but
|
|
|
|
* we can never return a bigger size than the requested size.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
QSize QIconLoaderEngine::actualSize(const QSize &size, QIcon::Mode mode,
|
|
|
|
QIcon::State state)
|
|
|
|
{
|
|
|
|
ensureLoaded();
|
|
|
|
|
|
|
|
QIconLoaderEngineEntry *entry = entryForSize(size);
|
|
|
|
if (entry) {
|
|
|
|
const QIconDirInfo &dir = entry->dir;
|
|
|
|
if (dir.type == QIconDirInfo::Scalable)
|
|
|
|
return size;
|
|
|
|
else {
|
|
|
|
int result = qMin<int>(dir.size, qMin(size.width(), size.height()));
|
|
|
|
return QSize(result, result);
|
|
|
|
}
|
|
|
|
}
|
2012-01-25 12:58:17 +00:00
|
|
|
return QIconEngine::actualSize(size, mode, state);
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QPixmap PixmapEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
|
|
|
|
{
|
|
|
|
Q_UNUSED(state);
|
|
|
|
|
|
|
|
// Ensure that basePixmap is lazily initialized before generating the
|
|
|
|
// key, otherwise the cache key is not unique
|
|
|
|
if (basePixmap.isNull())
|
|
|
|
basePixmap.load(filename);
|
|
|
|
|
2012-03-22 11:28:29 +00:00
|
|
|
QSize actualSize = basePixmap.size();
|
|
|
|
if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height()))
|
|
|
|
actualSize.scale(size, Qt::KeepAspectRatio);
|
|
|
|
|
2012-04-16 20:16:11 +00:00
|
|
|
QString key = QLatin1String("$qt_theme_")
|
2011-04-27 10:05:43 +00:00
|
|
|
% HexString<qint64>(basePixmap.cacheKey())
|
|
|
|
% HexString<int>(mode)
|
2012-05-14 16:02:16 +00:00
|
|
|
% HexString<qint64>(QGuiApplication::palette().cacheKey())
|
2012-03-22 11:28:29 +00:00
|
|
|
% HexString<int>(actualSize.width())
|
|
|
|
% HexString<int>(actualSize.height());
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QPixmap cachedPixmap;
|
|
|
|
if (QPixmapCache::find(key, &cachedPixmap)) {
|
|
|
|
return cachedPixmap;
|
|
|
|
} else {
|
2012-03-22 11:28:29 +00:00
|
|
|
if (basePixmap.size() != actualSize)
|
2014-01-22 10:01:14 +00:00
|
|
|
cachedPixmap = basePixmap.scaled(actualSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
|
|
|
else
|
|
|
|
cachedPixmap = basePixmap;
|
2012-05-14 16:02:16 +00:00
|
|
|
if (QGuiApplication *guiApp = qobject_cast<QGuiApplication *>(qApp))
|
2014-01-22 10:01:14 +00:00
|
|
|
cachedPixmap = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(guiApp))->applyQIconStyleHelper(mode, cachedPixmap);
|
2011-04-27 10:05:43 +00:00
|
|
|
QPixmapCache::insert(key, cachedPixmap);
|
|
|
|
}
|
|
|
|
return cachedPixmap;
|
|
|
|
}
|
|
|
|
|
|
|
|
QPixmap ScalableEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
|
|
|
|
{
|
|
|
|
if (svgIcon.isNull())
|
|
|
|
svgIcon = QIcon(filename);
|
|
|
|
|
|
|
|
// Simply reuse svg icon engine
|
|
|
|
return svgIcon.pixmap(size, mode, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
QPixmap QIconLoaderEngine::pixmap(const QSize &size, QIcon::Mode mode,
|
|
|
|
QIcon::State state)
|
|
|
|
{
|
|
|
|
ensureLoaded();
|
|
|
|
|
|
|
|
QIconLoaderEngineEntry *entry = entryForSize(size);
|
|
|
|
if (entry)
|
|
|
|
return entry->pixmap(size, mode, state);
|
|
|
|
|
|
|
|
return QPixmap();
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QIconLoaderEngine::key() const
|
|
|
|
{
|
|
|
|
return QLatin1String("QIconLoaderEngine");
|
|
|
|
}
|
|
|
|
|
|
|
|
void QIconLoaderEngine::virtual_hook(int id, void *data)
|
|
|
|
{
|
|
|
|
ensureLoaded();
|
|
|
|
|
|
|
|
switch (id) {
|
2012-01-25 12:58:17 +00:00
|
|
|
case QIconEngine::AvailableSizesHook:
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2012-01-25 12:58:17 +00:00
|
|
|
QIconEngine::AvailableSizesArgument &arg
|
|
|
|
= *reinterpret_cast<QIconEngine::AvailableSizesArgument*>(data);
|
2014-08-22 18:55:24 +00:00
|
|
|
const int N = m_entries.size();
|
2014-08-22 19:13:40 +00:00
|
|
|
QList<QSize> sizes;
|
|
|
|
sizes.reserve(N);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// Gets all sizes from the DirectoryInfo entries
|
2014-08-22 18:55:24 +00:00
|
|
|
for (int i = 0; i < N; ++i) {
|
2011-04-27 10:05:43 +00:00
|
|
|
int size = m_entries.at(i)->dir.size;
|
2014-08-22 19:13:40 +00:00
|
|
|
sizes.append(QSize(size, size));
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
2014-08-22 19:13:40 +00:00
|
|
|
arg.sizes.swap(sizes); // commit
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
break;
|
2012-01-25 12:58:17 +00:00
|
|
|
case QIconEngine::IconNameHook:
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
|
|
|
QString &name = *reinterpret_cast<QString*>(data);
|
|
|
|
name = m_iconName;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2012-01-25 12:58:17 +00:00
|
|
|
QIconEngine::virtual_hook(id, data);
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
|
|
|
|
#endif //QT_NO_ICON
|