4

I'm currently trying to get the icon based on a file extension, but it seems like QFileIconProvider will only return an icon if it can actually read an existing file. Is there any way I can get a QIcon based off of a file extension? One alternative would be to write a temporary file with the desired extension, but that is very inefficient so I'm looking for a way around.

Any help would be appreciated!

Brad
  • 10,015
  • 17
  • 54
  • 77

1 Answers1

3

Here's my solution for Windows:

iconprovider.h:

class IconProvider
{
public:
    static IconProvider * instance();
    static QIcon fileIcon(const QString &filename);
    static QIcon dirIcon();

private:
    IconProvider() {}

private:
    static IconProvider *self;
    QPixmapCache iconCache;
    QFileIconProvider iconProvider;
};

iconprovider.cpp:

IconProvider *IconProvider::self = 0;

IconProvider *IconProvider::instance()
{
    if(!self)
        self = new IconProvider();
    return self;
}

QIcon IconProvider::fileIcon(const QString &filename)
{
    QFileInfo fileInfo(filename);
    QPixmap pixmap;

#ifdef Q_OS_WIN32

    if (fileInfo.suffix().isEmpty() || fileInfo.suffix() == "exe" && fileInfo.exists())
    {
        return instance()->iconProvider.icon(fileInfo);
    }

    if (!instance()->iconCache.find(fileInfo.suffix(), &pixmap))
    {
        // Support for nonexistent file type icons, will reimplement it as custom icon provider later
        /* We don't use the variable, but by storing it statically, we
         * ensure CoInitialize is only called once. */
        static HRESULT comInit = CoInitialize(NULL);
        Q_UNUSED(comInit);

        SHFILEINFO shFileInfo;
        unsigned long val = 0;

        val = SHGetFileInfo((const wchar_t *)("foo." + fileInfo.suffix()).utf16(), 0, &shFileInfo,
                            sizeof(SHFILEINFO), SHGFI_ICON | SHGFI_USEFILEATTRIBUTES);

        // Even if GetFileInfo returns a valid result, hIcon can be empty in some cases
        if (val && shFileInfo.hIcon)
        {
            pixmap = QPixmap::fromWinHICON(shFileInfo.hIcon);
            if (!pixmap.isNull())
            {
                instance()->iconCache.insert(fileInfo.suffix(), pixmap);
            }
            DestroyIcon(shFileInfo.hIcon);
        }
        else
        {
            // TODO: Return default icon if nothing else found
        }
    }

#else
    // Default icon for Linux and Mac OS X for now
    return instance()->iconProvider.icon(fileInfo);
#endif

    return QIcon(pixmap);
}

QIcon IconProvider::dirIcon()
{
    return instance()->iconProvider.icon(QFileIconProvider::Folder);
}
Oleg Shparber
  • 2,732
  • 1
  • 18
  • 19
  • Why do you use WinApi functions, if you have Qt? – Blood Aug 14 '12 at 10:07
  • @Blood Qt doesn't have an API to get icons for non-existent files (i.e. file type). – Oleg Shparber Aug 14 '12 at 10:22
  • 1
    I've just used a QTemporaryFile and then used the QFileIconProvider on the temporary file. This way it can stay platform independent. Sort of inefficient but probably the only way to do it. – Brad Aug 14 '12 at 16:42
  • @Brad Yep, this is a possible solution, but beware of performance issues in case of large data set. – Oleg Shparber Aug 14 '12 at 18:51
  • @Brad, Oleg: How about keeping around a hash map of file extensions to QIcons, so that you only have to create a temporary file once for every extension that your application cares about? – nullstellensatz Feb 18 '15 at 20:37
  • @nullstellensatz, obviously that is a possible optimisation for an application, which needs to retrieve file type icons quite often. – Oleg Shparber Feb 19 '15 at 06:19