Browse Source

Merge branch 'ctkIconEnginePlugin'

* ctkIconEnginePlugin:
  ENH: Add more comments to ctkIconEnginePlugin
  ENH:Add ctkIconEngine/ctkIconEnginePlugin to support multiple files per icon
Julien Finet 15 years ago
parent
commit
4200abf586

+ 5 - 0
Libs/Widgets/CMakeLists.txt

@@ -46,10 +46,14 @@ SET(KIT_SRCS
   ctkFileDialog.h
   ctkFittedTextBrowser.cpp
   ctkFittedTextBrowser.h
+  ctkIconEnginePlugin.cpp
+  ctkIconEnginePlugin.h
   ctkMatrixWidget.cpp
   ctkMatrixWidget.h
   ctkMenuButton.cpp
   ctkMenuButton.h
+  ctkPixmapIconEngine.cpp
+  ctkPixmapIconEngine.h
   ctkRangeSlider.cpp
   ctkRangeSlider.h
   ctkRangeWidget.cpp
@@ -95,6 +99,7 @@ SET(KIT_MOC_SRCS
   ctkDynamicSpacer.h
   ctkFileDialog.h
   ctkFittedTextBrowser.h
+  ctkIconEnginePlugin.h
   ctkMatrixWidget.h
   ctkMenuButton.h
   ctkRangeSlider.h

+ 146 - 0
Libs/Widgets/ctkIconEnginePlugin.cpp

@@ -0,0 +1,146 @@
+/*=========================================================================
+
+  Library:   CTK
+ 
+  Copyright (c) 2010  Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ 
+=========================================================================*/
+
+// Qt includes
+#include <QDebug>
+#include <QDir>
+#include <QFileInfo>
+#include <QImageReader>
+#include <QPainter>
+
+#include "ctkIconEnginePlugin.h"
+
+class ctkIconEnginePluginPrivate:public ctkPrivate<ctkIconEnginePlugin>
+{
+public:
+  QStringList SizeDirectories;
+};
+
+//------------------------------------------------------------------------------
+ctkIconEnginePlugin::ctkIconEnginePlugin(QObject* parentObject)
+ :QIconEnginePluginV2(parentObject)
+{
+  CTK_INIT_PRIVATE(ctkIconEnginePlugin);
+}
+
+//------------------------------------------------------------------------------
+QIconEngineV2* ctkIconEnginePlugin::create(const QString& fileName)
+{
+  CTK_D(ctkIconEnginePlugin);
+  Q_UNUSED(fileName);
+  ctkIconEngine* iconEngine = new ctkIconEngine;
+  iconEngine->setSizeDirectories(d->SizeDirectories);
+  return iconEngine;
+}
+
+//------------------------------------------------------------------------------
+QStringList ctkIconEnginePlugin::keys()const
+{
+  QStringList supportedKeys;
+  foreach(QByteArray byteArray, QImageReader::supportedImageFormats())
+    {
+    supportedKeys << QString(byteArray);
+    }
+  return supportedKeys;
+}
+
+//------------------------------------------------------------------------------
+void ctkIconEnginePlugin::setSizeDirectories(const QStringList& sizeDirectories)
+{
+  CTK_D(ctkIconEnginePlugin);
+  d->SizeDirectories = sizeDirectories;
+}
+
+//------------------------------------------------------------------------------
+QStringList ctkIconEnginePlugin::sizeDirectories()const
+{
+  CTK_D(const ctkIconEnginePlugin);
+  return d->SizeDirectories;
+}
+
+//------------------------------------------------------------------------------
+class ctkIconEnginePrivate:public ctkPrivate<ctkIconEngine>
+{
+public:
+  QStringList SizeDirectories;
+};
+
+//------------------------------------------------------------------------------
+ctkIconEngine::ctkIconEngine()
+{
+  CTK_INIT_PRIVATE(ctkIconEngine);
+}
+
+//------------------------------------------------------------------------------
+void ctkIconEngine::addFile(const QString& fileName, const QSize& size,
+                            QIcon::Mode mode, QIcon::State state)
+{
+  CTK_D(ctkIconEngine);
+  this->Superclass::addFile(fileName, size, mode, state);
+  QString sizeDirectory;
+  foreach(QString directory, d->SizeDirectories)
+    {
+    if (fileName.contains(directory))
+      {
+      sizeDirectory = directory;
+      break;
+      }
+    }
+  if (sizeDirectory.isEmpty())
+    {
+    return;
+    }
+  foreach(QString directory, d->SizeDirectories)
+    {
+    QString otherFileName = fileName;
+    otherFileName.replace(sizeDirectory, directory);
+    if (otherFileName != fileName)
+      {
+      this->Superclass::addFile(otherFileName, QSize(), mode, state);
+      }
+    }
+  /*
+  QFileInfo file(fileName);
+  QDir dir = file.dir();
+  foreach(QString subdirPath, dir.entryList(QDir::NoDotAndDotDot | QDir::Dirs))
+    {
+    QDir subdir(subdirPath);
+    QStringList matchingFiles = subdir.entryList(QStringList() <<file.fileName());
+    if (matchingFiles.size() && matchingFiles[0] != fileName)
+      {
+      this->Superclass::addFile(matchingFiles[0], QSize(), mode, state);
+      }
+    }
+  */
+}
+
+//------------------------------------------------------------------------------
+void ctkIconEngine::setSizeDirectories(const QStringList& sizeDirectories)
+{
+  CTK_D(ctkIconEngine);
+  d->SizeDirectories = sizeDirectories;
+}
+
+//------------------------------------------------------------------------------
+QStringList ctkIconEngine::sizeDirectories()const
+{
+  CTK_D(const ctkIconEngine);
+  return d->SizeDirectories;
+}

+ 109 - 0
Libs/Widgets/ctkIconEnginePlugin.h

@@ -0,0 +1,109 @@
+/*=========================================================================
+
+  Library:   CTK
+ 
+  Copyright (c) 2010  Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ 
+=========================================================================*/
+
+#ifndef __ctkIconEnginePlugin_h
+#define __ctkIconEnginePlugin_h
+
+// Qt includes
+#include <QIconEngineV2>
+#include <QIconEnginePluginV2>
+
+// CTK includes
+#include "ctkPimpl.h"
+#include "ctkPixmapIconEngine.h"
+#include "CTKWidgetsExport.h"
+
+class ctkIconEnginePluginPrivate;
+class ctkIconEnginePrivate;
+
+/// ctkIconEnginePlugin must be loaded when starting the application.
+/// \code
+/// QApplication myApp;
+/// QCoreApplication::addLibraryPath("MyApp-build/plugins");
+/// \endcode
+/// where the plugin must be located in "MyApp-build/plugins/iconengines"
+/// don't forget to declare in the cpp file:
+///   Q_EXPORT_PLUGIN2(yourpluginName, ctkIconEnginePlugin)
+class CTK_WIDGETS_EXPORT ctkIconEnginePlugin: public QIconEnginePluginV2
+{
+  Q_OBJECT;
+public:
+  ctkIconEnginePlugin(QObject* parent = 0);
+
+  virtual QIconEngineV2* create(const QString& filename=QString());
+  /// Support all the Qt image formats by default
+  virtual QStringList keys()const;
+
+  /// Directory list given to the created icon engines
+  /// Subdirectories where the icons should be searched, typically:
+  /// "Small", "Medium", "Large", "XLarge" or
+  /// "16x16", "32x32", "64x64", "128x128" or
+  /// "LowDef", "HighDef"
+  /// \sa ctkIconEnginePlugin::setSizeDirectories
+  void setSizeDirectories(const QStringList& sizeDirectories);
+  QStringList sizeDirectories()const;
+private:
+  CTK_DECLARE_PRIVATE(ctkIconEnginePlugin);
+};
+
+//------------------------------------------------------------------------------
+/// ctkIconEngine is an icon engine that behaves like the default Qt icon engine
+/// QPixmapIconEngine(ctkPixmapIconEngine)), but can automatically support icons
+/// in multiple size. When adding a file to an icon, it will automatically check
+/// if the same file name exists in a different directory. This allows the
+/// application to contains icons in different size,e.g. :/Icons/Small/edit.png
+/// and :/Icons/Large/edit.png.
+/// Without ctkIconEngine, QIcon already support mutltiple files:
+/// \code
+///  QIcon editIcon;
+///  editIcon.addFile(":/Icons/Small/edit.png");
+///  editIcon.addFile(":/Icons/Large/edit.png");
+/// \endcode
+/// Using ctkIconEngine, adding a file to an icon will automatically search for
+/// any icon in a different directory:
+/// \code
+///  ctkIconEngine* autoIconEngine;
+///  autoIconEngine->setSizeDirectories(QStringList() << "Large" << "Small"; 
+///  QIcon editIcon(autoIconEngine);
+///  editIcon.addFile(":/Icons/Small/edit.png");
+/// \endcode
+/// where the large version of the icon is automatically added.
+/// It is mostly useful when using the designer, where only 1 icon file can
+/// be specified. It must be used with ctkIconEnginePlugin
+/// TODO: support more than just files in ressources.
+class CTK_WIDGETS_EXPORT ctkIconEngine: public ctkPixmapIconEngine
+{
+public:
+  typedef ctkPixmapIconEngine Superclass;
+  ctkIconEngine();
+  virtual void addFile(const QString& fileName, const QSize& size,
+                       QIcon::Mode mode, QIcon::State state);
+  /// Subdirectories where the icons should be searched, typically:
+  /// "Small", "Medium", "Large", "XLarge" or
+  /// "16x16", "32x32", "64x64", "128x128" or
+  /// "LowDef", "HighDef"
+  void setSizeDirectories(const QStringList& sizeDirectories);
+  QStringList sizeDirectories()const;
+
+private:
+  CTK_DECLARE_PRIVATE(ctkIconEngine);
+};
+
+#endif

+ 347 - 0
Libs/Widgets/ctkPixmapIconEngine.cpp

@@ -0,0 +1,347 @@
+/*=========================================================================
+
+  Library:   CTK
+ 
+  Copyright (c) 2010  Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ 
+=========================================================================*/
+
+// Qt includes
+#include <QApplication>
+#include <QFileInfo>
+#include <QPainter>
+#include <QPixmapCache>
+#include <QStyleOption>
+
+#include "ctkPixmapIconEngine.h"
+
+ctkPixmapIconEngine::ctkPixmapIconEngine()
+{
+}
+
+ctkPixmapIconEngine::ctkPixmapIconEngine(const ctkPixmapIconEngine &other)
+    : QIconEngineV2(other), pixmaps(other.pixmaps)
+{
+}
+
+ctkPixmapIconEngine::~ctkPixmapIconEngine()
+{
+}
+
+void ctkPixmapIconEngine::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 ctkPixmapIconEngineEntry *bestSizeMatch( const QSize &size, ctkPixmapIconEngineEntry *pa, ctkPixmapIconEngineEntry *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;
+}
+
+ctkPixmapIconEngineEntry *ctkPixmapIconEngine::tryMatch(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+    ctkPixmapIconEngineEntry *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;
+}
+
+
+ctkPixmapIconEngineEntry *ctkPixmapIconEngine::bestMatch(const QSize &size, QIcon::Mode mode, QIcon::State state, bool sizeOnly)
+{
+    ctkPixmapIconEngineEntry *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 ctkPixmapIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+    QPixmap pm;
+    ctkPixmapIconEngineEntry *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_icon_")
+                  + QString::number(pm.cacheKey())
+                  + QString::number(static_cast<int>(pe->mode))
+                  + QString::number(QApplication::palette().cacheKey())
+                  + QLatin1Char('_')
+                  + QString::number(actualSize.width())
+                  + QLatin1Char('_')
+                  + QString::number(actualSize.height())
+                  + QLatin1Char('_');
+
+
+    if (mode == QIcon::Active) {
+        if (QPixmapCache::find(key + QString::number(static_cast<int>(mode)), pm))
+            return pm; // horray
+        if (QPixmapCache::find(key + QString::number(static_cast<int>(QIcon::Normal)), pm)) {
+            QStyleOption opt(0);
+            opt.palette = QApplication::palette();
+            QPixmap active = QApplication::style()->generatedIconPixmap(QIcon::Active, pm, &opt);
+            if (pm.cacheKey() == active.cacheKey())
+                return pm;
+        }
+    }
+
+    if (!QPixmapCache::find(key + QString::number(static_cast<int>(mode)), pm)) {
+        if (pm.size() != actualSize)
+            pm = pm.scaled(actualSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+        if (pe->mode != mode && mode != QIcon::Normal) {
+            QStyleOption opt(0);
+            opt.palette = QApplication::palette();
+            QPixmap generated = QApplication::style()->generatedIconPixmap(mode, pm, &opt);
+            if (!generated.isNull())
+                pm = generated;
+        }
+        QPixmapCache::insert(key + QString::number(static_cast<int>(mode)), pm);
+    }
+    return pm;
+}
+
+QSize ctkPixmapIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+    QSize actualSize;
+    if (ctkPixmapIconEngineEntry *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 ctkPixmapIconEngine::addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state)
+{
+    if (!pixmap.isNull()) {
+        ctkPixmapIconEngineEntry *pe = tryMatch(pixmap.size(), mode, state);
+        if(pe && pe->size == pixmap.size()) {
+            pe->pixmap = pixmap;
+            pe->fileName.clear();
+        } else {
+            pixmaps += ctkPixmapIconEngineEntry(pixmap, mode, state);
+        }
+    }
+}
+
+void ctkPixmapIconEngine::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) {
+                ctkPixmapIconEngineEntry *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;
+                }
+            }
+        }
+        ctkPixmapIconEngineEntry e(abs, size, mode, state);
+        e.pixmap = pixmap;
+        pixmaps += e;
+    }
+}
+
+QString ctkPixmapIconEngine::key() const
+{
+    return QLatin1String("ctkPixmapIconEngine");
+}
+
+QIconEngineV2 *ctkPixmapIconEngine::clone() const
+{
+    return new ctkPixmapIconEngine(*this);
+}
+
+bool ctkPixmapIconEngine::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 {
+            ctkPixmapIconEngineEntry pe(fileName, sz, QIcon::Mode(mode), QIcon::State(state));
+            pe.pixmap = pm;
+            pixmaps += pe;
+        }
+    }
+    return true;
+}
+
+bool ctkPixmapIconEngine::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 << static_cast<uint>(pixmaps.at(i).mode);
+        out << static_cast<uint>(pixmaps.at(i).state);
+    }
+    return true;
+}
+
+void ctkPixmapIconEngine::virtual_hook(int id, void *data)
+{
+    switch (id) {
+    case QIconEngineV2::AvailableSizesHook: {
+        QIconEngineV2::AvailableSizesArgument &arg =
+            *reinterpret_cast<QIconEngineV2::AvailableSizesArgument*>(data);
+        arg.sizes.clear();
+        for (int i = 0; i < pixmaps.size(); ++i) {
+            ctkPixmapIconEngineEntry &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:
+        QIconEngineV2::virtual_hook(id, data);
+    }
+}

+ 73 - 0
Libs/Widgets/ctkPixmapIconEngine.h

@@ -0,0 +1,73 @@
+/*=========================================================================
+
+  Library:   CTK
+ 
+  Copyright (c) 2010  Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ 
+=========================================================================*/
+
+#ifndef __ctkPixmapIconEngine_h
+#define __ctkPixmapIconEngine_h
+
+#include <QIconEngineV2>
+#include <QPixmap>
+#include <QVector>
+
+struct ctkPixmapIconEngineEntry
+{
+    ctkPixmapIconEngineEntry():mode(QIcon::Normal), state(QIcon::Off){}
+    ctkPixmapIconEngineEntry(const QPixmap &pm, QIcon::Mode m = QIcon::Normal, QIcon::State s = QIcon::Off)
+        :pixmap(pm), size(pm.size()), mode(m), state(s){}
+    ctkPixmapIconEngineEntry(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 ctkPixmapIconEngine : public QIconEngineV2 {
+public:
+    ctkPixmapIconEngine();
+    ctkPixmapIconEngine(const ctkPixmapIconEngine &);
+    ~ctkPixmapIconEngine();
+    void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state);
+    QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state);
+    ctkPixmapIconEngineEntry *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);
+
+    // v2 functions
+    QString key() const;
+    QIconEngineV2 *clone() const;
+    bool read(QDataStream &in);
+    bool write(QDataStream &out) const;
+    void virtual_hook(int id, void *data);
+
+private:
+    ctkPixmapIconEngineEntry *tryMatch(const QSize &size, QIcon::Mode mode, QIcon::State state);
+    QVector<ctkPixmapIconEngineEntry> pixmaps;
+
+    friend QDataStream &operator<<(QDataStream &s, const QIcon &icon);
+    friend class QIconThemeEngine;
+};
+
+
+#endif