transmission/qt/IconCache.cc

245 lines
5.7 KiB
C++

// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
#include "IconCache.h"
#ifdef _WIN32
#include <windows.h>
#include <shellapi.h>
#endif
#include <QApplication>
#include <QFile>
#include <QFileIconProvider>
#include <QFileInfo>
#include <QIcon>
#include <QMimeDatabase>
#include <QObject>
#include <QPainter>
#include <QPixmap>
#include <QStyle>
#ifdef _WIN32
#include <QPixmapCache>
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#include <QImage>
#else
#include <QtWin>
#endif
#endif
#include <optional>
#include <utility>
/***
****
***/
IconCache& IconCache::get()
{
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
static auto& singleton = *new IconCache();
return singleton;
}
QIcon IconCache::guessMimeIcon(QString const& filename, QIcon fallback) const
{
QIcon icon;
#ifdef _WIN32
if (!filename.isEmpty())
{
QFileInfo const file_info(filename);
addAssociatedFileIcon(file_info, SHGFI_SMALLICON, icon);
addAssociatedFileIcon(file_info, 0, icon);
addAssociatedFileIcon(file_info, SHGFI_LARGEICON, icon);
}
#else
icon = getMimeIcon(filename);
#endif
if (icon.isNull())
{
icon = std::move(fallback);
}
return icon;
}
QIcon IconCache::getMimeTypeIcon(QString const& mime_type_name, bool multifile) const
{
auto& icon = (multifile ? name_to_emblem_icon_ : name_to_icon_)[mime_type_name];
if (!icon.isNull())
{
return icon;
}
if (!multifile)
{
QMimeDatabase const mime_db;
auto const type = mime_db.mimeTypeForName(mime_type_name);
icon = getThemeIcon(type.iconName());
if (icon.isNull())
{
icon = getThemeIcon(type.genericIconName());
}
if (icon.isNull())
{
icon = file_icon_;
}
return icon;
}
auto const mime_icon = getMimeTypeIcon(mime_type_name, false);
for (auto const& size : { QSize{ 24, 24 }, QSize{ 32, 32 }, QSize{ 48, 48 } })
{
// upper left corner
auto const folder_size = size / 2;
auto const folder_rect = QRect{ QPoint{}, folder_size };
// fullsize
auto const mime_size = size;
auto const mime_rect = QRect{ QPoint{}, mime_size };
// build the icon
auto pixmap = QPixmap{ size };
pixmap.fill(Qt::transparent);
auto painter = QPainter{ &pixmap };
painter.setRenderHints(QPainter::SmoothPixmapTransform);
painter.drawPixmap(folder_rect, folder_icon_.pixmap(folder_size));
painter.drawPixmap(mime_rect, mime_icon.pixmap(mime_size));
icon.addPixmap(pixmap);
}
return icon;
}
QIcon IconCache::getThemeIcon(QString const& name, std::optional<QStyle::StandardPixmap> const& fallback) const
{
return getThemeIcon(name, name + QStringLiteral("-symbolic"), fallback);
}
/***
****
***/
#ifdef _WIN32
void IconCache::addAssociatedFileIcon(QFileInfo const& file_info, unsigned int icon_size, QIcon& icon) const
{
QString const pixmap_cache_key = QStringLiteral("tr_file_ext_") + QString::number(icon_size) + QLatin1Char('_') +
file_info.suffix();
QPixmap pixmap;
if (!QPixmapCache::find(pixmap_cache_key, &pixmap))
{
auto const filename = file_info.fileName().toStdWString();
SHFILEINFO shell_file_info;
if (::SHGetFileInfoW(
filename.data(),
FILE_ATTRIBUTE_NORMAL,
&shell_file_info,
sizeof(shell_file_info),
SHGFI_ICON | icon_size | SHGFI_USEFILEATTRIBUTES) != 0)
{
if (shell_file_info.hIcon != nullptr)
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
pixmap = QPixmap::fromImage(QImage::fromHICON(shell_file_info.hIcon));
#else
pixmap = QtWin::fromHICON(shell_file_info.hIcon);
#endif
::DestroyIcon(shell_file_info.hIcon);
}
}
QPixmapCache::insert(pixmap_cache_key, pixmap);
}
if (!pixmap.isNull())
{
icon.addPixmap(pixmap);
}
}
#else
QIcon IconCache::getMimeIcon(QString const& filename) const
{
if (suffixes_.empty())
{
for (auto const& type : QMimeDatabase{}.allMimeTypes())
{
auto const tmp = type.suffixes();
suffixes_.insert(tmp.begin(), tmp.end());
}
}
auto const ext = QFileInfo{ filename }.suffix();
if (suffixes_.count(ext) == 0)
{
return {};
}
QIcon& icon = ext_to_icon_[ext];
if (icon.isNull()) // cache miss
{
QMimeDatabase const mime_db;
auto const type = mime_db.mimeTypeForFile(filename, QMimeDatabase::MatchExtension);
if (icon.isNull())
{
icon = getThemeIcon(type.iconName());
}
if (icon.isNull())
{
icon = getThemeIcon(type.genericIconName());
}
if (icon.isNull())
{
icon = {};
}
}
return icon;
}
#endif
QIcon IconCache::getThemeIcon(
QString const& name,
QString const& fallbackName,
std::optional<QStyle::StandardPixmap> const& fallbackPixmap) const
{
auto const rtl_suffix = QApplication::layoutDirection() == Qt::RightToLeft ? QStringLiteral("-rtl") : QString{};
auto icon = QIcon::fromTheme(name + rtl_suffix);
if (icon.isNull())
{
icon = QIcon::fromTheme(fallbackName + rtl_suffix);
}
if (icon.isNull() && fallbackPixmap.has_value())
{
icon = QApplication::style()->standardIcon(*fallbackPixmap, nullptr);
}
return icon;
}