diff options
author | Olivier Goffart <ogoffart@woboq.com> | 2012-05-14 18:02:16 +0200 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2012-05-18 21:42:38 +0200 |
commit | 6c06e14a49773ce5572935864ed6b9be219c6103 (patch) | |
tree | 14bb88db68657527cabe6590b6cf5044aed65307 /src/gui/image | |
parent | 83645377e98fb1312b6f80a1c996a013b62f0b46 (diff) | |
download | qtbase-6c06e14a49773ce5572935864ed6b9be219c6103.tar.gz |
QIcon: move back to QtGui
- Move the files and tests
git mv src/widgets/kernel/qicon* qrc/gui/image/
git mv tests/auto/widgets/kernel/qicon/ tests/auto/gui/image/
- update the include of QIcon
git grep -O"sed -i s,QtWidgets/qicon,QtGui/qicon," "QtWidgets/qicon"
git grep -O"sed -i s,QtWidgets/QIcon,QtGui/QIcon," "QtWidgets/QIcon"
- Adapt QIcon \ingroup documentation
sed -i s/QtWidgets/QtGui/ src/gui/images/qicon*
- Adapt export macro
sed -i s/Q_WIDGETS_EXPORT/Q_GUI_EXPORT/g src/gui/image/qicon*
- Update .pri and .pro files
- Remove the use of QStyle::alignedRect by copying its content (and
adapt slightly
- Use QGuiApplication::palette() instead of QApplication::palette()
- Add a hook in QGuiApplicationPrivate to call the
QStyle::generatedIconPixmap() from QtWidgets
Another commit follows to adjust QMetaType::Icon and move the QVariant
and QMetaType icon handler back in QtGui
Change-Id: I1b63759f892ebc02dfc30f41bb6e76e0b7451182
Reviewed-by: Lars Knoll <lars.knoll@nokia.com>
Reviewed-by: Girish Ramakrishnan <girish.1.ramakrishnan@nokia.com>
Diffstat (limited to 'src/gui/image')
-rw-r--r-- | src/gui/image/image.pri | 12 | ||||
-rw-r--r-- | src/gui/image/qicon.cpp | 1152 | ||||
-rw-r--r-- | src/gui/image/qicon.h | 146 | ||||
-rw-r--r-- | src/gui/image/qicon_p.h | 128 | ||||
-rw-r--r-- | src/gui/image/qiconengine.cpp | 287 | ||||
-rw-r--r-- | src/gui/image/qiconengine.h | 95 | ||||
-rw-r--r-- | src/gui/image/qiconengineplugin.cpp | 107 | ||||
-rw-r--r-- | src/gui/image/qiconengineplugin.h | 80 | ||||
-rw-r--r-- | src/gui/image/qiconloader.cpp | 584 | ||||
-rw-r--r-- | src/gui/image/qiconloader_p.h | 192 |
10 files changed, 2782 insertions, 1 deletions
diff --git a/src/gui/image/image.pri b/src/gui/image/image.pri index 737b7fcbd4..6b306ddaad 100644 --- a/src/gui/image/image.pri +++ b/src/gui/image/image.pri @@ -23,6 +23,11 @@ HEADERS += \ image/qpixmapcache_p.h \ image/qplatformpixmap.h \ image/qimagepixmapcleanuphooks_p.h \ + image/qicon.h \ + image/qicon_p.h \ + image/qiconloader_p.h \ + image/qiconengine.h \ + image/qiconengineplugin.h \ SOURCES += \ image/qbitmap.cpp \ @@ -40,7 +45,12 @@ SOURCES += \ image/qpixmap_raster.cpp \ image/qpixmap_blitter.cpp \ image/qnativeimage.cpp \ - image/qimagepixmapcleanuphooks.cpp + image/qimagepixmapcleanuphooks.cpp \ + image/qicon.cpp \ + image/qiconloader.cpp \ + image/qiconengine.cpp \ + image/qiconengineplugin.cpp \ + win32: SOURCES += image/qpixmap_win.cpp diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp new file mode 100644 index 0000000000..c2139c3336 --- /dev/null +++ b/src/gui/image/qicon.cpp @@ -0,0 +1,1152 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qicon.h" +#include "qicon_p.h" +#include "qiconengine.h" +#include "qiconengineplugin.h" +#include "private/qfactoryloader_p.h" +#include "private/qiconloader_p.h" +#include "qpainter.h" +#include "qfileinfo.h" +#include "qpixmapcache.h" +#include "qvariant.h" +#include "qcache.h" +#include "qdebug.h" +#include "qpalette.h" + +#ifdef Q_WS_MAC +#include <private/qt_mac_p.h> +#include <private/qt_cocoa_helpers_mac_p.h> +#endif + +#include "private/qhexstring_p.h" +#include "private/qguiapplication_p.h" + +#ifndef QT_NO_ICON +QT_BEGIN_NAMESPACE + +/*! + \enum QIcon::Mode + + This enum type describes the mode for which a pixmap is intended + to be used. The currently defined modes are: + + \value Normal + Display the pixmap when the user is + not interacting with the icon, but the + functionality represented by the icon is available. + \value Disabled + Display the pixmap when the + functionality represented by the icon is not available. + \value Active + Display the pixmap when the + functionality represented by the icon is available and + the user is interacting with the icon, for example, moving the + mouse over it or clicking it. + \value Selected + Display the pixmap when the item represented by the icon is + selected. +*/ + +/*! + \enum QIcon::State + + This enum describes the state for which a pixmap is intended to be + used. The \e state can be: + + \value Off Display the pixmap when the widget is in an "off" state + \value On Display the pixmap when the widget is in an "on" state +*/ + +static QBasicAtomicInt serialNumCounter = Q_BASIC_ATOMIC_INITIALIZER(1); + +static void qt_cleanup_icon_cache(); +namespace { + struct IconCache : public QCache<QString, QIcon> + { + IconCache() + { + // ### note: won't readd if QApplication is re-created! + qAddPostRoutine(qt_cleanup_icon_cache); + } + }; +} + +Q_GLOBAL_STATIC(IconCache, qtIconCache) + +static void qt_cleanup_icon_cache() +{ + qtIconCache()->clear(); +} + +QIconPrivate::QIconPrivate() + : engine(0), ref(1), + serialNum(serialNumCounter.fetchAndAddRelaxed(1)), + detach_no(0) +{ +} + +QPixmapIconEngine::QPixmapIconEngine() +{ +} + +QPixmapIconEngine::QPixmapIconEngine(const QPixmapIconEngine &other) + : QIconEngine(other), pixmaps(other.pixmaps) +{ +} + +QPixmapIconEngine::~QPixmapIconEngine() +{ +} + +void QPixmapIconEngine::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)); +} + +static inline int area(const QSize &s) { return s.width() * s.height(); } + +// returns the smallest of the two that is still larger than or equal to size. +static QPixmapIconEngineEntry *bestSizeMatch( const QSize &size, QPixmapIconEngineEntry *pa, QPixmapIconEngineEntry *pb) +{ + int s = area(size); + if (pa->size == QSize() && pa->pixmap.isNull()) { + pa->pixmap = QPixmap(pa->fileName); + pa->size = pa->pixmap.size(); + } + int a = area(pa->size); + if (pb->size == QSize() && pb->pixmap.isNull()) { + pb->pixmap = QPixmap(pb->fileName); + pb->size = pb->pixmap.size(); + } + int b = area(pb->size); + int res = a; + if (qMin(a,b) >= s) + res = qMin(a,b); + else + res = qMax(a,b); + if (res == a) + return pa; + return pb; +} + +QPixmapIconEngineEntry *QPixmapIconEngine::tryMatch(const QSize &size, QIcon::Mode mode, QIcon::State state) +{ + QPixmapIconEngineEntry *pe = 0; + for (int i = 0; i < pixmaps.count(); ++i) + if (pixmaps.at(i).mode == mode && pixmaps.at(i).state == state) { + if (pe) + pe = bestSizeMatch(size, &pixmaps[i], pe); + else + pe = &pixmaps[i]; + } + return pe; +} + + +QPixmapIconEngineEntry *QPixmapIconEngine::bestMatch(const QSize &size, QIcon::Mode mode, QIcon::State state, bool sizeOnly) +{ + QPixmapIconEngineEntry *pe = tryMatch(size, mode, state); + while (!pe){ + QIcon::State oppositeState = (state == QIcon::On) ? QIcon::Off : QIcon::On; + if (mode == QIcon::Disabled || mode == QIcon::Selected) { + QIcon::Mode oppositeMode = (mode == QIcon::Disabled) ? QIcon::Selected : QIcon::Disabled; + if ((pe = tryMatch(size, QIcon::Normal, state))) + break; + if ((pe = tryMatch(size, QIcon::Active, state))) + break; + if ((pe = tryMatch(size, mode, oppositeState))) + break; + if ((pe = tryMatch(size, QIcon::Normal, oppositeState))) + break; + if ((pe = tryMatch(size, QIcon::Active, oppositeState))) + break; + if ((pe = tryMatch(size, oppositeMode, state))) + break; + if ((pe = tryMatch(size, oppositeMode, oppositeState))) + break; + } else { + QIcon::Mode oppositeMode = (mode == QIcon::Normal) ? QIcon::Active : QIcon::Normal; + if ((pe = tryMatch(size, oppositeMode, state))) + break; + if ((pe = tryMatch(size, mode, oppositeState))) + break; + if ((pe = tryMatch(size, oppositeMode, oppositeState))) + break; + if ((pe = tryMatch(size, QIcon::Disabled, state))) + break; + if ((pe = tryMatch(size, QIcon::Selected, state))) + break; + if ((pe = tryMatch(size, QIcon::Disabled, oppositeState))) + break; + if ((pe = tryMatch(size, QIcon::Selected, oppositeState))) + break; + } + + if (!pe) + return pe; + } + + if (sizeOnly ? (pe->size.isNull() || !pe->size.isValid()) : pe->pixmap.isNull()) { + pe->pixmap = QPixmap(pe->fileName); + if (!pe->pixmap.isNull()) + pe->size = pe->pixmap.size(); + } + + return pe; +} + +QPixmap QPixmapIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) +{ + QPixmap pm; + QPixmapIconEngineEntry *pe = bestMatch(size, mode, state, false); + if (pe) + pm = pe->pixmap; + + if (pm.isNull()) { + int idx = pixmaps.count(); + while (--idx >= 0) { + if (pe == &pixmaps[idx]) { + pixmaps.remove(idx); + break; + } + } + if (pixmaps.isEmpty()) + return pm; + else + return pixmap(size, mode, state); + } + + QSize actualSize = pm.size(); + if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height())) + actualSize.scale(size, Qt::KeepAspectRatio); + + QString key = QLatin1String("qt_") + % HexString<quint64>(pm.cacheKey()) + % HexString<uint>(pe->mode) + % HexString<quint64>(QGuiApplication::palette().cacheKey()) + % HexString<uint>(actualSize.width()) + % HexString<uint>(actualSize.height()); + + if (mode == QIcon::Active) { + if (QPixmapCache::find(key % HexString<uint>(mode), pm)) + return pm; // horray + if (QPixmapCache::find(key % HexString<uint>(QIcon::Normal), pm)) { + QPixmap active = pm; + if (QGuiApplication *guiApp = qobject_cast<QGuiApplication *>(qApp)) + active = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(guiApp))->applyQIconStyleHelper(QIcon::Active, pm); + if (pm.cacheKey() == active.cacheKey()) + return pm; + } + } + + if (!QPixmapCache::find(key % HexString<uint>(mode), pm)) { + if (pm.size() != actualSize) + pm = pm.scaled(actualSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + if (pe->mode != mode && mode != QIcon::Normal) { + QPixmap generated = pm; + if (QGuiApplication *guiApp = qobject_cast<QGuiApplication *>(qApp)) + generated = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(guiApp))->applyQIconStyleHelper(mode, pm); + if (!generated.isNull()) + pm = generated; + } + QPixmapCache::insert(key % HexString<uint>(mode), pm); + } + return pm; +} + +QSize QPixmapIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) +{ + QSize actualSize; + if (QPixmapIconEngineEntry *pe = bestMatch(size, mode, state, true)) + actualSize = pe->size; + + if (actualSize.isNull()) + return actualSize; + + if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height())) + actualSize.scale(size, Qt::KeepAspectRatio); + return actualSize; +} + +void QPixmapIconEngine::addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state) +{ + if (!pixmap.isNull()) { + QPixmapIconEngineEntry *pe = tryMatch(pixmap.size(), mode, state); + if(pe && pe->size == pixmap.size()) { + pe->pixmap = pixmap; + pe->fileName.clear(); + } else { + pixmaps += QPixmapIconEngineEntry(pixmap, mode, state); + } + } +} + +void QPixmapIconEngine::addFile(const QString &fileName, const QSize &_size, QIcon::Mode mode, QIcon::State state) +{ + if (!fileName.isEmpty()) { + QSize size = _size; + QPixmap pixmap; + + QString abs = fileName; + if (fileName.at(0) != QLatin1Char(':')) + abs = QFileInfo(fileName).absoluteFilePath(); + + for (int i = 0; i < pixmaps.count(); ++i) { + if (pixmaps.at(i).mode == mode && pixmaps.at(i).state == state) { + QPixmapIconEngineEntry *pe = &pixmaps[i]; + if(size == QSize()) { + pixmap = QPixmap(abs); + size = pixmap.size(); + } + if (pe->size == QSize() && pe->pixmap.isNull()) { + pe->pixmap = QPixmap(pe->fileName); + pe->size = pe->pixmap.size(); + } + if(pe->size == size) { + pe->pixmap = pixmap; + pe->fileName = abs; + return; + } + } + } + QPixmapIconEngineEntry e(abs, size, mode, state); + e.pixmap = pixmap; + pixmaps += e; + } +} + +QString QPixmapIconEngine::key() const +{ + return QLatin1String("QPixmapIconEngine"); +} + +QIconEngine *QPixmapIconEngine::clone() const +{ + return new QPixmapIconEngine(*this); +} + +bool QPixmapIconEngine::read(QDataStream &in) +{ + int num_entries; + QPixmap pm; + QString fileName; + QSize sz; + uint mode; + uint state; + + in >> num_entries; + for (int i=0; i < num_entries; ++i) { + if (in.atEnd()) { + pixmaps.clear(); + return false; + } + in >> pm; + in >> fileName; + in >> sz; + in >> mode; + in >> state; + if (pm.isNull()) { + addFile(fileName, sz, QIcon::Mode(mode), QIcon::State(state)); + } else { + QPixmapIconEngineEntry pe(fileName, sz, QIcon::Mode(mode), QIcon::State(state)); + pe.pixmap = pm; + pixmaps += pe; + } + } + return true; +} + +bool QPixmapIconEngine::write(QDataStream &out) const +{ + int num_entries = pixmaps.size(); + out << num_entries; + for (int i=0; i < num_entries; ++i) { + if (pixmaps.at(i).pixmap.isNull()) + out << QPixmap(pixmaps.at(i).fileName); + else + out << pixmaps.at(i).pixmap; + out << pixmaps.at(i).fileName; + out << pixmaps.at(i).size; + out << (uint) pixmaps.at(i).mode; + out << (uint) pixmaps.at(i).state; + } + return true; +} + +void QPixmapIconEngine::virtual_hook(int id, void *data) +{ + switch (id) { + case QIconEngine::AvailableSizesHook: { + QIconEngine::AvailableSizesArgument &arg = + *reinterpret_cast<QIconEngine::AvailableSizesArgument*>(data); + arg.sizes.clear(); + for (int i = 0; i < pixmaps.size(); ++i) { + QPixmapIconEngineEntry &pe = pixmaps[i]; + if (pe.size == QSize() && pe.pixmap.isNull()) { + pe.pixmap = QPixmap(pe.fileName); + pe.size = pe.pixmap.size(); + } + if (pe.mode == arg.mode && pe.state == arg.state && !pe.size.isEmpty()) + arg.sizes.push_back(pe.size); + } + break; + } + default: + QIconEngine::virtual_hook(id, data); + } +} + +#ifndef QT_NO_LIBRARY +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QIconEngineFactoryInterface_iid, QLatin1String("/iconengines"), Qt::CaseInsensitive)) +#endif + + + +/*! + \class QIcon + + \brief The QIcon class provides scalable icons in different modes + and states. + + \ingroup painting + \ingroup shared + \inmodule QtGui + + A QIcon can generate smaller, larger, active, and disabled pixmaps + from the set of pixmaps it is given. Such pixmaps are used by Qt + widgets to show an icon representing a particular action. + + The simplest use of QIcon is to create one from a QPixmap file or + resource, and then use it, allowing Qt to work out all the required + icon styles and sizes. For example: + + \snippet code/src_gui_image_qicon.cpp 0 + + To undo a QIcon, simply set a null icon in its place: + + \snippet code/src_gui_image_qicon.cpp 1 + + Use the QImageReader::supportedImageFormats() and + QImageWriter::supportedImageFormats() functions to retrieve a + complete list of the supported file formats. + + When you retrieve a pixmap using pixmap(QSize, Mode, State), and no + pixmap for this given size, mode and state has been added with + addFile() or addPixmap(), then QIcon will generate one on the + fly. This pixmap generation happens in a QIconEngineV2. The default + engine scales pixmaps down if required, but never up, and it uses + the current style to calculate a disabled appearance. By using + custom icon engines, you can customize every aspect of generated + icons. With QIconEnginePluginV2 it is possible to register different + icon engines for different file suffixes, making it possible for + third parties to provide additional icon engines to those included + with Qt. + + \note Since Qt 4.2, an icon engine that supports SVG is included. + + \section1 Making Classes that Use QIcon + + If you write your own widgets that have an option to set a small + pixmap, consider allowing a QIcon to be set for that pixmap. The + Qt class QToolButton is an example of such a widget. + + Provide a method to set a QIcon, and when you draw the icon, choose + whichever pixmap is appropriate for the current state of your widget. + For example: + \snippet code/src_gui_image_qicon.cpp 2 + + You might also make use of the \c Active mode, perhaps making your + widget \c Active when the mouse is over the widget (see \l + QWidget::enterEvent()), while the mouse is pressed pending the + release that will activate the function, or when it is the currently + selected item. If the widget can be toggled, the "On" mode might be + used to draw a different icon. + + \img icon.png QIcon + + \sa {fowler}{GUI Design Handbook: Iconic Label}, {Icons Example} +*/ + + +/*! + Constructs a null icon. +*/ +QIcon::QIcon() + : d(0) +{ +} + +/*! + Constructs an icon from a \a pixmap. + */ +QIcon::QIcon(const QPixmap &pixmap) + :d(0) +{ + addPixmap(pixmap); +} + +/*! + Constructs a copy of \a other. This is very fast. +*/ +QIcon::QIcon(const QIcon &other) + :d(other.d) +{ + if (d) + d->ref.ref(); +} + +/*! + Constructs an icon from the file with the given \a fileName. The + file will be loaded on demand. + + If \a fileName contains a relative path (e.g. the filename only) + the relevant file must be found relative to the runtime working + directory. + + The file name can be either refer to an actual file on disk or to + one of the application's embedded resources. See the + \l{resources.html}{Resource System} overview for details on how to + embed images and other resource files in the application's + executable. + + Use the QImageReader::supportedImageFormats() and + QImageWriter::supportedImageFormats() functions to retrieve a + complete list of the supported file formats. +*/ +QIcon::QIcon(const QString &fileName) + : d(0) +{ + addFile(fileName); +} + + +/*! + Creates an icon with a specific icon \a engine. The icon takes + ownership of the engine. +*/ +QIcon::QIcon(QIconEngine *engine) + :d(new QIconPrivate) +{ + d->engine = engine; +} + +/*! + Destroys the icon. +*/ +QIcon::~QIcon() +{ + if (d && !d->ref.deref()) + delete d; +} + +/*! + Assigns the \a other icon to this icon and returns a reference to + this icon. +*/ +QIcon &QIcon::operator=(const QIcon &other) +{ + if (other.d) + other.d->ref.ref(); + if (d && !d->ref.deref()) + delete d; + d = other.d; + return *this; +} + +/*! + \fn void QIcon::swap(QIcon &other) + \since 4.8 + + Swaps icon \a other with this icon. This operation is very + fast and never fails. +*/ + +/*! + Returns the icon as a QVariant. +*/ +QIcon::operator QVariant() const +{ + return QVariant(QVariant::Icon, this); +} + +/*! \fn int QIcon::serialNumber() const + \obsolete + + Returns a number that identifies the contents of this + QIcon object. Distinct QIcon objects can have + the same serial number if they refer to the same contents + (but they don't have to). Also, the serial number of + a QIcon object may change during its lifetime. + + Use cacheKey() instead. + + A null icon always has a serial number of 0. + + Serial numbers are mostly useful in conjunction with caching. + + \sa QPixmap::serialNumber() +*/ + +/*! + Returns a number that identifies the contents of this QIcon + object. Distinct QIcon objects can have the same key if + they refer to the same contents. + \since 4.3 + + The cacheKey() will change when the icon is altered via + addPixmap() or addFile(). + + Cache keys are mostly useful in conjunction with caching. + + \sa QPixmap::cacheKey() +*/ +qint64 QIcon::cacheKey() const +{ + if (!d) + return 0; + return (((qint64) d->serialNum) << 32) | ((qint64) (d->detach_no)); +} + +/*! + Returns a pixmap with the requested \a size, \a mode, and \a + state, generating one if necessary. The pixmap might be smaller than + requested, but never larger. + + \sa actualSize(), paint() +*/ +QPixmap QIcon::pixmap(const QSize &size, Mode mode, State state) const +{ + if (!d) + return QPixmap(); + return d->engine->pixmap(size, mode, state); +} + +/*! + \fn QPixmap QIcon::pixmap(int w, int h, Mode mode = Normal, State state = Off) const + + \overload + + Returns a pixmap of size QSize(\a w, \a h). The pixmap might be smaller than + requested, but never larger. +*/ + +/*! + \fn QPixmap QIcon::pixmap(int extent, Mode mode = Normal, State state = Off) const + + \overload + + Returns a pixmap of size QSize(\a extent, \a extent). The pixmap might be smaller + than requested, but never larger. +*/ + +/*! Returns the actual size of the icon for the requested \a size, \a + mode, and \a state. The result might be smaller than requested, but + never larger. + + \sa pixmap(), paint() +*/ +QSize QIcon::actualSize(const QSize &size, Mode mode, State state) const +{ + if (!d) + return QSize(); + return d->engine->actualSize(size, mode, state); +} + + +/*! + Uses the \a painter to paint the icon with specified \a alignment, + required \a mode, and \a state into the rectangle \a rect. + + \sa actualSize(), pixmap() +*/ +void QIcon::paint(QPainter *painter, const QRect &rect, Qt::Alignment alignment, Mode mode, State state) const +{ + if (!d || !painter) + return; + + // Copy of QStyle::alignedRect + const QSize size = d->engine->actualSize(rect.size(), mode, state); + alignment = QGuiApplicationPrivate::visualAlignment(painter->layoutDirection(), alignment); + int x = rect.x(); + int y = rect.y(); + int w = size.width(); + int h = size.height(); + if ((alignment & Qt::AlignVCenter) == Qt::AlignVCenter) + y += rect.size().height()/2 - h/2; + else if ((alignment & Qt::AlignBottom) == Qt::AlignBottom) + y += rect.size().height() - h; + if ((alignment & Qt::AlignRight) == Qt::AlignRight) + x += rect.size().width() - w; + else if ((alignment & Qt::AlignHCenter) == Qt::AlignHCenter) + x += rect.size().width()/2 - w/2; + QRect alignedRect(x, y, w, h); + + d->engine->paint(painter, alignedRect, mode, state); +} + +/*! + \fn void QIcon::paint(QPainter *painter, int x, int y, int w, int h, Qt::Alignment alignment, + Mode mode, State state) const + + \overload + + Paints the icon into the rectangle QRect(\a x, \a y, \a w, \a h). +*/ + +/*! + Returns true if the icon is empty; otherwise returns false. + + An icon is empty if it has neither a pixmap nor a filename. + + Note: Even a non-null icon might not be able to create valid + pixmaps, eg. if the file does not exist or cannot be read. +*/ +bool QIcon::isNull() const +{ + return !d; +} + +/*!\internal + */ +bool QIcon::isDetached() const +{ + return !d || d->ref.load() == 1; +} + +/*! \internal + */ +void QIcon::detach() +{ + if (d) { + if (d->ref.load() != 1) { + QIconPrivate *x = new QIconPrivate; + x->engine = d->engine->clone(); + if (!d->ref.deref()) + delete d; + d = x; + } + ++d->detach_no; + } +} + +/*! + Adds \a pixmap to the icon, as a specialization for \a mode and + \a state. + + Custom icon engines are free to ignore additionally added + pixmaps. + + \sa addFile() +*/ +void QIcon::addPixmap(const QPixmap &pixmap, Mode mode, State state) +{ + if (pixmap.isNull()) + return; + if (!d) { + d = new QIconPrivate; + d->engine = new QPixmapIconEngine; + } else { + detach(); + } + d->engine->addPixmap(pixmap, mode, state); +} + + +/*! Adds an image from the file with the given \a fileName to the + icon, as a specialization for \a size, \a mode and \a state. The + file will be loaded on demand. Note: custom icon engines are free + to ignore additionally added pixmaps. + + If \a fileName contains a relative path (e.g. the filename only) + the relevant file must be found relative to the runtime working + directory. + + The file name can be either refer to an actual file on disk or to + one of the application's embedded resources. See the + \l{resources.html}{Resource System} overview for details on how to + embed images and other resource files in the application's + executable. + + Use the QImageReader::supportedImageFormats() and + QImageWriter::supportedImageFormats() functions to retrieve a + complete list of the supported file formats. + + Note: When you add a non-empty filename to a QIcon, the icon becomes + non-null, even if the file doesn't exist or points to a corrupt file. + + \sa addPixmap() + */ +void QIcon::addFile(const QString &fileName, const QSize &size, Mode mode, State state) +{ + if (fileName.isEmpty()) + return; + if (!d) { +#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + QFileInfo info(fileName); + QString suffix = info.suffix(); + if (!suffix.isEmpty()) { + // first try version 2 engines.. + const int index = loader()->indexOf(suffix); + if (index != -1) { + if (QIconEngineFactoryInterface *factory = qobject_cast<QIconEngineFactoryInterface*>(loader()->instance(index))) { + if (QIconEngine *engine = factory->create(fileName)) { + d = new QIconPrivate; + d->engine = engine; + } + } + } + } +#endif + // ...then fall back to the default engine + if (!d) { + d = new QIconPrivate; + d->engine = new QPixmapIconEngine; + } + } else { + detach(); + } + d->engine->addFile(fileName, size, mode, state); +} + +/*! + \since 4.5 + + Returns a list of available icon sizes for the specified \a mode and + \a state. +*/ +QList<QSize> QIcon::availableSizes(Mode mode, State state) const +{ + if (!d || !d->engine) + return QList<QSize>(); + return d->engine->availableSizes(mode, state); +} + +/*! + \since 4.7 + + Returns the name used to create the icon, if available. + + Depending on the way the icon was created, it may have an associated + name. This is the case for icons created with fromTheme() or icons + using a QIconEngine which supports the QIconEngineV2::IconNameHook. + + \sa fromTheme(), QIconEngine +*/ +QString QIcon::name() const +{ + if (!d || !d->engine) + return QString(); + return d->engine->iconName(); +} + +/*! + \since 4.6 + + Sets the search paths for icon themes to \a paths. + \sa themeSearchPaths(), fromTheme(), setThemeName() +*/ +void QIcon::setThemeSearchPaths(const QStringList &paths) +{ + QIconLoader::instance()->setThemeSearchPath(paths); +} + +/*! + \since 4.6 + + Returns the search paths for icon themes. + + The default value will depend on the platform: + + On X11, the search path will use the XDG_DATA_DIRS environment + variable if available. + + By default all platforms will have the resource directory + \c{:\icons} as a fallback. You can use "rcc -project" to generate a + resource file from your icon theme. + + \sa setThemeSearchPaths(), fromTheme(), setThemeName() +*/ +QStringList QIcon::themeSearchPaths() +{ + return QIconLoader::instance()->themeSearchPaths(); +} + +/*! + \since 4.6 + + Sets the current icon theme to \a name. + + The \a name should correspond to a directory name in the + themeSearchPath() containing an index.theme + file describing it's contents. + + \sa themeSearchPaths(), themeName() +*/ +void QIcon::setThemeName(const QString &name) +{ + QIconLoader::instance()->setThemeName(name); +} + +/*! + \since 4.6 + + Returns the name of the current icon theme. + + On X11, the current icon theme depends on your desktop + settings. On other platforms it is not set by default. + + \sa setThemeName(), themeSearchPaths(), fromTheme(), + hasThemeIcon() +*/ +QString QIcon::themeName() +{ + return QIconLoader::instance()->themeName(); +} + +/*! + \since 4.6 + + Returns the QIcon corresponding to \a name in the current + icon theme. If no such icon is found in the current theme + \a fallback is returned instead. + + The latest version of the freedesktop icon specification and naming + specification can be obtained here: + + \list + \li \l{http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html} + \li \l{http://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html} + \endlist + + To fetch an icon from the current icon theme: + + \snippet code/src_gui_image_qicon.cpp 3 + + Or if you want to provide a guaranteed fallback for platforms that + do not support theme icons, you can use the second argument: + + \snippet code/src_gui_image_qicon.cpp 4 + + \note By default, only X11 will support themed icons. In order to + use themed icons on Mac and Windows, you will have to bundle a + compliant theme in one of your themeSearchPaths() and set the + appropriate themeName(). + + \sa themeName(), setThemeName(), themeSearchPaths() +*/ +QIcon QIcon::fromTheme(const QString &name, const QIcon &fallback) +{ + QIcon icon; + + if (qtIconCache()->contains(name)) { + icon = *qtIconCache()->object(name); + } else { + QIcon *cachedIcon = new QIcon(new QIconLoaderEngine(name)); + qtIconCache()->insert(name, cachedIcon); + icon = *cachedIcon; + } + + // Note the qapp check is to allow lazy loading of static icons + // Supporting fallbacks will not work for this case. + if (qApp && icon.availableSizes().isEmpty()) + return fallback; + + return icon; +} + +/*! + \since 4.6 + + Returns true if there is an icon available for \a name in the + current icon theme, otherwise returns false. + + \sa themeSearchPaths(), fromTheme(), setThemeName() +*/ +bool QIcon::hasThemeIcon(const QString &name) +{ + QIcon icon = fromTheme(name); + + return !icon.isNull(); +} + + +/***************************************************************************** + QIcon stream functions + *****************************************************************************/ +#if !defined(QT_NO_DATASTREAM) +/*! + \fn QDataStream &operator<<(QDataStream &stream, const QIcon &icon) + \relates QIcon + \since 4.2 + + Writes the given \a icon to the given \a stream as a PNG + image. If the icon contains more than one image, all images will + be written to the stream. Note that writing the stream to a file + will not produce a valid image file. +*/ + +QDataStream &operator<<(QDataStream &s, const QIcon &icon) +{ + if (s.version() >= QDataStream::Qt_4_3) { + if (icon.isNull()) { + s << QString(); + } else { + s << icon.d->engine->key(); + icon.d->engine->write(s); + } + } else if (s.version() == QDataStream::Qt_4_2) { + if (icon.isNull()) { + s << 0; + } else { + QPixmapIconEngine *engine = static_cast<QPixmapIconEngine *>(icon.d->engine); + int num_entries = engine->pixmaps.size(); + s << num_entries; + for (int i=0; i < num_entries; ++i) { + s << engine->pixmaps.at(i).pixmap; + s << engine->pixmaps.at(i).fileName; + s << engine->pixmaps.at(i).size; + s << (uint) engine->pixmaps.at(i).mode; + s << (uint) engine->pixmaps.at(i).state; + } + } + } else { + s << QPixmap(icon.pixmap(22,22)); + } + return s; +} + +/*! + \fn QDataStream &operator>>(QDataStream &stream, QIcon &icon) + \relates QIcon + \since 4.2 + + Reads an image, or a set of images, from the given \a stream into + the given \a icon. +*/ + +QDataStream &operator>>(QDataStream &s, QIcon &icon) +{ + if (s.version() >= QDataStream::Qt_4_3) { + icon = QIcon(); + QString key; + s >> key; + if (key == QLatin1String("QPixmapIconEngine")) { + icon.d = new QIconPrivate; + QIconEngine *engine = new QPixmapIconEngine; + icon.d->engine = engine; + engine->read(s); + } else if (key == QLatin1String("QIconLoaderEngine")) { + icon.d = new QIconPrivate; + QIconEngine *engine = new QIconLoaderEngine(); + icon.d->engine = engine; + engine->read(s); +#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + } else { + const int index = loader()->indexOf(key); + if (index != -1) { + if (QIconEngineFactoryInterface *factory = qobject_cast<QIconEngineFactoryInterface*>(loader()->instance(index))) { + if (QIconEngine *engine= factory->create()) { + icon.d = new QIconPrivate; + icon.d->engine = engine; + engine->read(s); + } // factory + } // instance + } // index +#endif + } + } else if (s.version() == QDataStream::Qt_4_2) { + icon = QIcon(); + int num_entries; + QPixmap pm; + QString fileName; + QSize sz; + uint mode; + uint state; + + s >> num_entries; + for (int i=0; i < num_entries; ++i) { + s >> pm; + s >> fileName; + s >> sz; + s >> mode; + s >> state; + if (pm.isNull()) + icon.addFile(fileName, sz, QIcon::Mode(mode), QIcon::State(state)); + else + icon.addPixmap(pm, QIcon::Mode(mode), QIcon::State(state)); + } + } else { + QPixmap pm; + s >> pm; + icon.addPixmap(pm); + } + return s; +} + +#endif //QT_NO_DATASTREAM + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QIcon &i) +{ + dbg.nospace() << "QIcon(" << i.name() << ')'; + return dbg.space(); +} +#endif + +/*! + \fn DataPtr &QIcon::data_ptr() + \internal +*/ + +/*! + \typedef QIcon::DataPtr + \internal +*/ + +QT_END_NAMESPACE +#endif //QT_NO_ICON diff --git a/src/gui/image/qicon.h b/src/gui/image/qicon.h new file mode 100644 index 0000000000..264b672c8c --- /dev/null +++ b/src/gui/image/qicon.h @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QICON_H +#define QICON_H + +#include <QtCore/qglobal.h> +#include <QtCore/qsize.h> +#include <QtCore/qlist.h> +#include <QtGui/qpixmap.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QIconPrivate; +class QIconEngine; + +class Q_GUI_EXPORT QIcon +{ +public: + enum Mode { Normal, Disabled, Active, Selected }; + enum State { On, Off }; + + QIcon(); + QIcon(const QPixmap &pixmap); + QIcon(const QIcon &other); + explicit QIcon(const QString &fileName); // file or resource name + explicit QIcon(QIconEngine *engine); + ~QIcon(); + QIcon &operator=(const QIcon &other); +#ifdef Q_COMPILER_RVALUE_REFS + inline QIcon &operator=(QIcon &&other) + { qSwap(d, other.d); return *this; } +#endif + inline void swap(QIcon &other) { qSwap(d, other.d); } + + operator QVariant() const; + + QPixmap pixmap(const QSize &size, Mode mode = Normal, State state = Off) const; + inline QPixmap pixmap(int w, int h, Mode mode = Normal, State state = Off) const + { return pixmap(QSize(w, h), mode, state); } + inline QPixmap pixmap(int extent, Mode mode = Normal, State state = Off) const + { return pixmap(QSize(extent, extent), mode, state); } + + QSize actualSize(const QSize &size, Mode mode = Normal, State state = Off) const; + + QString name() const; + + void paint(QPainter *painter, const QRect &rect, Qt::Alignment alignment = Qt::AlignCenter, Mode mode = Normal, State state = Off) const; + inline void paint(QPainter *painter, int x, int y, int w, int h, Qt::Alignment alignment = Qt::AlignCenter, Mode mode = Normal, State state = Off) const + { paint(painter, QRect(x, y, w, h), alignment, mode, state); } + + bool isNull() const; + bool isDetached() const; + void detach(); + +#if QT_DEPRECATED_SINCE(5, 0) + QT_DEPRECATED inline int serialNumber() const { return cacheKey() >> 32; } +#endif + qint64 cacheKey() const; + + void addPixmap(const QPixmap &pixmap, Mode mode = Normal, State state = Off); + void addFile(const QString &fileName, const QSize &size = QSize(), Mode mode = Normal, State state = Off); + + QList<QSize> availableSizes(Mode mode = Normal, State state = Off) const; + + static QIcon fromTheme(const QString &name, const QIcon &fallback = QIcon()); + static bool hasThemeIcon(const QString &name); + + static QStringList themeSearchPaths(); + static void setThemeSearchPaths(const QStringList &searchpath); + + static QString themeName(); + static void setThemeName(const QString &path); + + Q_DUMMY_COMPARISON_OPERATOR(QIcon) + +private: + QIconPrivate *d; +#if !defined(QT_NO_DATASTREAM) + friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QIcon &); + friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QIcon &); +#endif + +public: + typedef QIconPrivate * DataPtr; + inline DataPtr &data_ptr() { return d; } +}; + +Q_DECLARE_SHARED(QIcon) +Q_DECLARE_TYPEINFO(QIcon, Q_MOVABLE_TYPE); + +#if !defined(QT_NO_DATASTREAM) +Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QIcon &); +Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QIcon &); +#endif + +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QIcon &); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QICON_H diff --git a/src/gui/image/qicon_p.h b/src/gui/image/qicon_p.h new file mode 100644 index 0000000000..b8b1f001fe --- /dev/null +++ b/src/gui/image/qicon_p.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QICON_P_H +#define QICON_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> +#include <QtCore/qsize.h> +#include <QtCore/qlist.h> +#include <QtGui/qpixmap.h> +#include <QtGui/qicon.h> +#include <QtGui/qiconengine.h> + +#ifndef QT_NO_ICON +QT_BEGIN_NAMESPACE + +class QIconPrivate +{ +public: + QIconPrivate(); + + ~QIconPrivate() { + delete engine; + } + + QIconEngine *engine; + + QAtomicInt ref; + int serialNum; + int detach_no; +}; + + +struct QPixmapIconEngineEntry +{ + QPixmapIconEngineEntry():mode(QIcon::Normal), state(QIcon::Off){} + QPixmapIconEngineEntry(const QPixmap &pm, QIcon::Mode m = QIcon::Normal, QIcon::State s = QIcon::Off) + :pixmap(pm), size(pm.size()), mode(m), state(s){} + QPixmapIconEngineEntry(const QString &file, const QSize &sz = QSize(), QIcon::Mode m = QIcon::Normal, QIcon::State s = QIcon::Off) + :fileName(file), size(sz), mode(m), state(s){} + QPixmap pixmap; + QString fileName; + QSize size; + QIcon::Mode mode; + QIcon::State state; + bool isNull() const {return (fileName.isEmpty() && pixmap.isNull()); } +}; + + + +class QPixmapIconEngine : public QIconEngine { +public: + QPixmapIconEngine(); + QPixmapIconEngine(const QPixmapIconEngine &); + ~QPixmapIconEngine(); + void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state); + QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state); + QPixmapIconEngineEntry *bestMatch(const QSize &size, QIcon::Mode mode, QIcon::State state, bool sizeOnly); + QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state); + void addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state); + void addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state); + + QString key() const; + QIconEngine *clone() const; + bool read(QDataStream &in); + bool write(QDataStream &out) const; + void virtual_hook(int id, void *data); + +private: + QPixmapIconEngineEntry *tryMatch(const QSize &size, QIcon::Mode mode, QIcon::State state); + QVector<QPixmapIconEngineEntry> pixmaps; + + friend QDataStream &operator<<(QDataStream &s, const QIcon &icon); + friend class QIconThemeEngine; +}; + +QT_END_NAMESPACE +#endif //QT_NO_ICON +#endif // QICON_P_H diff --git a/src/gui/image/qiconengine.cpp b/src/gui/image/qiconengine.cpp new file mode 100644 index 0000000000..2b7fdb9bdc --- /dev/null +++ b/src/gui/image/qiconengine.cpp @@ -0,0 +1,287 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qiconengine.h" +#include "qpainter.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QIconEngine + + \brief The QIconEngine class provides an abstract base class for QIcon renderers. + + \ingroup painting + \inmodule QtGui + + An icon engine provides the rendering functions for a QIcon. Each icon has a + corresponding icon engine that is responsible for drawing the icon with a + requested size, mode and state. + + The icon is rendered by the paint() function, and the icon can additionally be + obtained as a pixmap with the pixmap() function (the default implementation + simply uses paint() to achieve this). The addPixmap() function can be used to + add new pixmaps to the icon engine, and is used by QIcon to add specialized + custom pixmaps. + + The paint(), pixmap(), and addPixmap() functions are all virtual, and can + therefore be reimplemented in subclasses of QIconEngine. + + \sa QIconEnginePlugin + +*/ + +/*! + \fn virtual void QIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) = 0; + + Uses the given \a painter to paint the icon with the required \a mode and + \a state into the rectangle \a rect. +*/ + +/*! Returns the actual size of the icon the engine provides for the + requested \a size, \a mode and \a state. The default implementation + returns the given \a size. + */ +QSize QIconEngine::actualSize(const QSize &size, QIcon::Mode /*mode*/, QIcon::State /*state*/) +{ + return size; +} + + +/*! + Destroys the icon engine. + */ +QIconEngine::~QIconEngine() +{ +} + + +/*! + Returns the icon as a pixmap with the required \a size, \a mode, + and \a state. The default implementation creates a new pixmap and + calls paint() to fill it. +*/ +QPixmap QIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) +{ + QPixmap pm(size); + { + QPainter p(&pm); + paint(&p, QRect(QPoint(0,0),size), mode, state); + } + return pm; +} + +/*! + Called by QIcon::addPixmap(). Adds a specialized \a pixmap for the given + \a mode and \a state. The default pixmap-based engine stores any supplied + pixmaps, and it uses them instead of scaled pixmaps if the size of a pixmap + matches the size of icon requested. Custom icon engines that implement + scalable vector formats are free to ignores any extra pixmaps. + */ +void QIconEngine::addPixmap(const QPixmap &/*pixmap*/, QIcon::Mode /*mode*/, QIcon::State /*state*/) +{ +} + + +/*! Called by QIcon::addFile(). Adds a specialized pixmap from the + file with the given \a fileName, \a size, \a mode and \a state. The + default pixmap-based engine stores any supplied file names, and it + loads the pixmaps on demand instead of using scaled pixmaps if the + size of a pixmap matches the size of icon requested. Custom icon + engines that implement scalable vector formats are free to ignores + any extra files. + */ +void QIconEngine::addFile(const QString &/*fileName*/, const QSize &/*size*/, QIcon::Mode /*mode*/, QIcon::State /*state*/) +{ +} + + +/*! + \enum QIconEngine::IconEngineHook + \since 4.5 + + These enum values are used for virtual_hook() to allow additional + queries to icon engine without breaking binary compatibility. + + \value AvailableSizesHook Allows to query the sizes of the + contained pixmaps for pixmap-based engines. The \a data argument + of the virtual_hook() function is a AvailableSizesArgument pointer + that should be filled with icon sizes. Engines that work in terms + of a scalable, vectorial format normally return an empty list. + + \value IconNameHook Allows to query the name used to create the + icon, for example when instantiating an icon using + QIcon::fromTheme(). + + \sa virtual_hook() + */ + +/*! + \class QIconEngine::AvailableSizesArgument + \since 4.5 + + \inmodule QtGui + + This struct represents arguments to virtual_hook() function when + \a id parameter is QIconEngine::AvailableSizesHook. + + \sa virtual_hook(), QIconEngine::IconEngineHook + */ + +/*! + \variable QIconEngine::AvailableSizesArgument::mode + \brief the requested mode of an image. + + \sa QIcon::Mode +*/ + +/*! + \variable QIconEngine::AvailableSizesArgument::state + \brief the requested state of an image. + + \sa QIcon::State +*/ + +/*! + \variable QIconEngine::AvailableSizesArgument::sizes + + \brief image sizes that are available with specified \a mode and + \a state. This is an output parameter and is filled after call to + virtual_hook(). Engines that work in terms of a scalable, + vectorial format normally return an empty list. +*/ + + +/*! + Returns a key that identifies this icon engine. + */ +QString QIconEngine::key() const +{ + return QString(); +} + +/*! \fn QIconEngine *QIconEngine::clone() const + + Reimplement this method to return a clone of this icon engine. + */ + +/*! + Reads icon engine contents from the QDataStream \a in. Returns + true if the contents were read; otherwise returns false. + + QIconEngine's default implementation always return false. + */ +bool QIconEngine::read(QDataStream &) +{ + return false; +} + +/*! + Writes the contents of this engine to the QDataStream \a out. + Returns true if the contents were written; otherwise returns false. + + QIconEngine's default implementation always return false. + */ +bool QIconEngine::write(QDataStream &) const +{ + return false; +} + +/*! + \since 4.5 + + Additional method to allow extending QIconEngine without + adding new virtual methods (and without breaking binary compatibility). + The actual action and format of \a data depends on \a id argument + which is in fact a constant from IconEngineHook enum. + + \sa IconEngineHook +*/ +void QIconEngine::virtual_hook(int id, void *data) +{ + switch (id) { + case QIconEngine::AvailableSizesHook: { + QIconEngine::AvailableSizesArgument &arg = + *reinterpret_cast<QIconEngine::AvailableSizesArgument*>(data); + arg.sizes.clear(); + break; + } + default: + break; + } +} + +/*! + \since 4.5 + + Returns sizes of all images that are contained in the engine for the + specific \a mode and \a state. + + \note This is a helper method and the actual work is done by + virtual_hook() method, hence this method depends on icon engine support + and may not work with all icon engines. + */ +QList<QSize> QIconEngine::availableSizes(QIcon::Mode mode, QIcon::State state) const +{ + AvailableSizesArgument arg; + arg.mode = mode; + arg.state = state; + const_cast<QIconEngine *>(this)->virtual_hook(QIconEngine::AvailableSizesHook, reinterpret_cast<void*>(&arg)); + return arg.sizes; +} + +/*! + \since 4.7 + + Returns the name used to create the engine, if available. + + \note This is a helper method and the actual work is done by + virtual_hook() method, hence this method depends on icon engine support + and may not work with all icon engines. + */ +QString QIconEngine::iconName() const +{ + QString name; + const_cast<QIconEngine *>(this)->virtual_hook(QIconEngine::IconNameHook, reinterpret_cast<void*>(&name)); + return name; +} + +QT_END_NAMESPACE diff --git a/src/gui/image/qiconengine.h b/src/gui/image/qiconengine.h new file mode 100644 index 0000000000..e47f86713a --- /dev/null +++ b/src/gui/image/qiconengine.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QICONENGINE_H +#define QICONENGINE_H + +#include <QtCore/qglobal.h> +#include <QtCore/qlist.h> +#include <QtGui/qicon.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class Q_GUI_EXPORT QIconEngine +{ +public: + virtual ~QIconEngine(); + virtual void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) = 0; + virtual QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state); + virtual QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state); + + virtual void addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state); + virtual void addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state); + + virtual QString key() const; + virtual QIconEngine *clone() const = 0; + virtual bool read(QDataStream &in); + virtual bool write(QDataStream &out) const; + + enum IconEngineHook { AvailableSizesHook = 1, IconNameHook }; + + struct AvailableSizesArgument + { + QIcon::Mode mode; + QIcon::State state; + QList<QSize> sizes; + }; + + virtual QList<QSize> availableSizes(QIcon::Mode mode = QIcon::Normal, + QIcon::State state = QIcon::Off) const; + + virtual QString iconName() const; + + virtual void virtual_hook(int id, void *data); +}; + +#if QT_DEPRECATED_SINCE(5, 0) +typedef QIconEngine QIconEngineV2; +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QICONENGINE_H diff --git a/src/gui/image/qiconengineplugin.cpp b/src/gui/image/qiconengineplugin.cpp new file mode 100644 index 0000000000..29953a468e --- /dev/null +++ b/src/gui/image/qiconengineplugin.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qiconengineplugin.h" +#include "qiconengine.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QIconEnginePlugin + \brief The QIconEnginePlugin class provides an abstract base for custom QIconEngine plugins. + + \ingroup plugins + \inmodule QtGui + + \b {Use QIconEnginePluginV2 instead.} + + The icon engine plugin is a simple plugin interface that makes it easy to + create custom icon engines that can be loaded dynamically into applications + through QIcon. QIcon uses the file or resource name's suffix to determine + what icon engine to use. + + Writing a icon engine plugin is achieved by subclassing this base class, + reimplementing the pure virtual functions keys() and create(), and + exporting the class with the Q_EXPORT_PLUGIN2() macro. + + \sa {How to Create Qt Plugins} +*/ + +/*! + \fn QStringList QIconEnginePlugin::keys() const + + Returns a list of icon engine keys that this plugin supports. The keys correspond + to the suffix of the file or resource name used when the plugin was created. + Keys are case insensitive. + + \sa create() +*/ + +/*! + \fn QIconEngine* QIconEnginePlugin::create(const QString& filename) + + Creates and returns a QIconEngine object for the icon with the given + \a filename. + + \sa keys() +*/ + +/*! + Constructs a icon engine plugin with the given \a parent. This is invoked + automatically by the Q_EXPORT_PLUGIN2() macro. +*/ +QIconEnginePlugin::QIconEnginePlugin(QObject *parent) + : QObject(parent) +{ +} + +/*! + Destroys the icon engine plugin. + + You never have to call this explicitly. Qt destroys a plugin + automatically when it is no longer used. +*/ +QIconEnginePlugin::~QIconEnginePlugin() +{ +} + + +QT_END_NAMESPACE diff --git a/src/gui/image/qiconengineplugin.h b/src/gui/image/qiconengineplugin.h new file mode 100644 index 0000000000..9333400609 --- /dev/null +++ b/src/gui/image/qiconengineplugin.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QICONENGINEPLUGIN_H +#define QICONENGINEPLUGIN_H + +#include <QtCore/qplugin.h> +#include <QtCore/qfactoryinterface.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QIconEngine; + +struct Q_GUI_EXPORT QIconEngineFactoryInterface : public QFactoryInterface +{ + virtual QIconEngine *create(const QString &filename = QString()) = 0; +}; + +#define QIconEngineFactoryInterface_iid \ + "org.qt-project.Qt.QIconEngineFactoryInterface" +Q_DECLARE_INTERFACE(QIconEngineFactoryInterface, QIconEngineFactoryInterface_iid) + +class Q_GUI_EXPORT QIconEnginePlugin : public QObject, public QIconEngineFactoryInterface +{ + Q_OBJECT + Q_INTERFACES(QIconEngineFactoryInterface:QFactoryInterface) +public: + QIconEnginePlugin(QObject *parent = 0); + ~QIconEnginePlugin(); + + virtual QStringList keys() const = 0; + virtual QIconEngine *create(const QString &filename = QString()) = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QICONENGINEPLUGIN_H diff --git a/src/gui/image/qiconloader.cpp b/src/gui/image/qiconloader.cpp new file mode 100644 index 0000000000..71b286b2ff --- /dev/null +++ b/src/gui/image/qiconloader.cpp @@ -0,0 +1,584 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QT_NO_ICON +#include <private/qiconloader_p.h> + +#include <private/qguiapplication_p.h> +#include <private/qicon_p.h> + +#include <QtGui/QIconEnginePlugin> +#include <QtGui/QPixmapCache> +#include <qpa/qplatformtheme.h> +#include <QtGui/QIconEngine> +#include <QtGui/QPalette> +#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 + +#include <private/qhexstring_p.h> + +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() +{ + if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) { + const QVariant themeHint = theme->themeHint(QPlatformTheme::SystemIconThemeName); + if (themeHint.isValid()) + return themeHint.toString(); + } + 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. + +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(); +} + +void QIconLoader::ensureInitialized() +{ + if (!m_initialized) { + m_initialized = true; + + Q_ASSERT(qApp); + + m_systemTheme = systemThemeName(); + + if (m_systemTheme.isEmpty()) + m_systemTheme = fallbackTheme(); +#ifndef QT_NO_LIBRARY + QFactoryLoader iconFactoryLoader(QIconEngineFactoryInterface_iid, + QLatin1String("/iconengines"), + Qt::CaseInsensitive); + if (iconFactoryLoader.keyMap().key(QLatin1String("svg"), -1) != -1) + m_supportsSvg = true; +#endif //QT_NO_LIBRARY + } +} + +QIconLoader *QIconLoader::instance() +{ + 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()) { + QString theme = systemThemeName(); + 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()) { + m_iconDirs = systemIconSearchPaths(); + // 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; + + QList <QIconDirInfo> keyList; + 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 + if (m_parents.isEmpty()) { + const QString fallback = fallbackTheme(); + if (!fallback.isEmpty()) + m_parents.append(fallback); + } + + // 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('/'); + QList<QIconDirInfo> subDirs = theme.keyList(); + + 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() +{ + while (!m_entries.isEmpty()) + delete m_entries.takeLast(); + Q_ASSERT(m_entries.size() == 0); +} + +QIconLoaderEngine::QIconLoaderEngine(const QIconLoaderEngine &other) + : QIconEngine(other), + m_iconName(other.m_iconName), + m_key(0) +{ +} + +QIconEngine *QIconLoaderEngine::clone() const +{ + 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() +{ + + iconLoaderInstance()->ensureInitialized(); + + if (!(iconLoaderInstance()->themeKey() == m_key)) { + + while (!m_entries.isEmpty()) + delete m_entries.takeLast(); + + Q_ASSERT(m_entries.size() == 0); + m_entries = iconLoaderInstance()->loadIcon(m_iconName); + m_key = iconLoaderInstance()->themeKey(); + } +} + +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 + + // Search for exact matches first + for (int i = 0; i < m_entries.count(); ++i) { + 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; + for (int i = 0; i < m_entries.count(); ++i) { + 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); + } + } + return QIconEngine::actualSize(size, mode, state); +} + +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); + + int actualSize = qMin(size.width(), size.height()); + QString key = QLatin1String("$qt_theme_") + % HexString<qint64>(basePixmap.cacheKey()) + % HexString<int>(mode) + % HexString<qint64>(QGuiApplication::palette().cacheKey()) + % HexString<int>(actualSize); + + QPixmap cachedPixmap; + if (QPixmapCache::find(key, &cachedPixmap)) { + return cachedPixmap; + } else { + cachedPixmap = basePixmap; + if (QGuiApplication *guiApp = qobject_cast<QGuiApplication *>(qApp)) + cachedPixmap = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(guiApp))->applyQIconStyleHelper(mode, basePixmap); + 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) { + case QIconEngine::AvailableSizesHook: + { + QIconEngine::AvailableSizesArgument &arg + = *reinterpret_cast<QIconEngine::AvailableSizesArgument*>(data); + const QList<QIconDirInfo> directoryKey = iconLoaderInstance()->theme().keyList(); + arg.sizes.clear(); + + // Gets all sizes from the DirectoryInfo entries + for (int i = 0 ; i < m_entries.size() ; ++i) { + int size = m_entries.at(i)->dir.size; + arg.sizes.append(QSize(size, size)); + } + } + break; + case QIconEngine::IconNameHook: + { + QString &name = *reinterpret_cast<QString*>(data); + name = m_iconName; + } + break; + default: + QIconEngine::virtual_hook(id, data); + } +} + +QT_END_NAMESPACE + +#endif //QT_NO_ICON diff --git a/src/gui/image/qiconloader_p.h b/src/gui/image/qiconloader_p.h new file mode 100644 index 0000000000..bd990d0d53 --- /dev/null +++ b/src/gui/image/qiconloader_p.h @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDESKTOPICON_P_H +#define QDESKTOPICON_P_H + +#ifndef QT_NO_ICON +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/QIcon> +#include <QtGui/QIconEngine> +#include <QtGui/QPixmapCache> +#include <private/qicon_p.h> +#include <private/qfactoryloader_p.h> +#include <QtCore/QHash> + +QT_BEGIN_NAMESPACE + +class QIconLoader; + +struct QIconDirInfo +{ + enum Type { Fixed, Scalable, Threshold }; + QIconDirInfo(const QString &_path = QString()) : + path(_path), + size(0), + maxSize(0), + minSize(0), + threshold(0), + type(Threshold) {} + QString path; + short size; + short maxSize; + short minSize; + short threshold; + Type type : 4; +}; + +class QIconLoaderEngineEntry + { +public: + virtual ~QIconLoaderEngineEntry() {} + virtual QPixmap pixmap(const QSize &size, + QIcon::Mode mode, + QIcon::State state) = 0; + QString filename; + QIconDirInfo dir; + static int count; +}; + +struct ScalableEntry : public QIconLoaderEngineEntry +{ + QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state); + QIcon svgIcon; +}; + +struct PixmapEntry : public QIconLoaderEngineEntry +{ + QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state); + QPixmap basePixmap; +}; + +typedef QList<QIconLoaderEngineEntry*> QThemeIconEntries; + +class QIconLoaderEngine : public QIconEngine +{ +public: + QIconLoaderEngine(const QString& iconName = QString()); + ~QIconLoaderEngine(); + + void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state); + QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state); + QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state); + QIconEngine *clone() const; + bool read(QDataStream &in); + bool write(QDataStream &out) const; + +private: + QString key() const; + bool hasIcon() const; + void ensureLoaded(); + void virtual_hook(int id, void *data); + QIconLoaderEngineEntry *entryForSize(const QSize &size); + QIconLoaderEngine(const QIconLoaderEngine &other); + QThemeIconEntries m_entries; + QString m_iconName; + uint m_key; + + friend class QIconLoader; +}; + +class QIconTheme +{ +public: + QIconTheme(const QString &name); + QIconTheme() : m_valid(false) {} + QStringList parents() { return m_parents; } + QList <QIconDirInfo> keyList() { return m_keyList; } + QString contentDir() { return m_contentDir; } + bool isValid() { return m_valid; } + +private: + QString m_contentDir; + QList <QIconDirInfo> m_keyList; + QStringList m_parents; + bool m_valid; +}; + +class Q_GUI_EXPORT QIconLoader : public QObject +{ +public: + QIconLoader(); + QThemeIconEntries loadIcon(const QString &iconName) const; + uint themeKey() const { return m_themeKey; } + + QString themeName() const { return m_userTheme.isEmpty() ? m_systemTheme : m_userTheme; } + void setThemeName(const QString &themeName); + QIconTheme theme() { return themeList.value(themeName()); } + void setThemeSearchPath(const QStringList &searchPaths); + QStringList themeSearchPaths() const; + QIconDirInfo dirInfo(int dirindex); + static QIconLoader *instance(); + void updateSystemTheme(); + void invalidateKey() { m_themeKey++; } + void ensureInitialized(); + +private: + QThemeIconEntries findIconHelper(const QString &themeName, + const QString &iconName, + QStringList &visited) const; + uint m_themeKey; + bool m_supportsSvg; + bool m_initialized; + + mutable QString m_userTheme; + mutable QString m_systemTheme; + mutable QStringList m_iconDirs; + mutable QHash <QString, QIconTheme> themeList; +}; + +QT_END_NAMESPACE + +#endif // QDESKTOPICON_P_H + +#endif //QT_NO_ICON |