refactor: don't load the same stock more than once (#1041)
* refactor: don't load the same stock more than once Some actions share an icon -- for example, "start all", "start now", and "start" each use "media-playback-start". When this happens, get the icon once and cache it to avoid hitting the disk more often than necessary. In addition, the statusbar's network transfer icon was being reloaded in a periodic upkeep timer, reloading with QIcon::fromTheme each time. Only give icons are needed, so load them once and cache them. * refactor: better lookup of torrent mime-type icons filename-to-mime-type and mime-type-to-icon lookups are both expensive, so do a better job of detecting top-level folders and caching the icons based on file suffixes. This also lets find a good mime icon even if the torrent doesn't have its 'files' property populated yet from RPC.
This commit is contained in:
parent
edbdeae623
commit
aa9b752cd9
|
@ -89,10 +89,8 @@ QIcon MainWindow::getStockIcon(QString const& name, int fallback)
|
|||
return icon;
|
||||
}
|
||||
|
||||
QIcon MainWindow::getStockIcon(QString const& name, int fallback, QStringList const& emblemNames)
|
||||
QIcon MainWindow::addEmblem(QIcon baseIcon, QStringList const& emblemNames)
|
||||
{
|
||||
QIcon baseIcon = getStockIcon(name, fallback);
|
||||
|
||||
if (baseIcon.isNull())
|
||||
{
|
||||
return baseIcon;
|
||||
|
@ -168,20 +166,23 @@ MainWindow::MainWindow(Session& session, Prefs& prefs, TorrentModel& model, bool
|
|||
ui.listView->setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
|
||||
// icons
|
||||
ui.action_OpenFile->setIcon(getStockIcon(QLatin1String("document-open"), QStyle::SP_DialogOpenButton));
|
||||
ui.action_AddURL->setIcon(getStockIcon(QLatin1String("document-open"), QStyle::SP_DialogOpenButton,
|
||||
QIcon const iconPlay = getStockIcon(QLatin1String("media-playback-start"), QStyle::SP_MediaPlay);
|
||||
QIcon const iconPause = getStockIcon(QLatin1String("media-playback-pause"), QStyle::SP_MediaPause);
|
||||
QIcon const iconOpen = getStockIcon(QLatin1String("document-open"), QStyle::SP_DialogOpenButton);
|
||||
ui.action_OpenFile->setIcon(iconOpen);
|
||||
ui.action_AddURL->setIcon(addEmblem(iconOpen,
|
||||
QStringList() << QLatin1String("emblem-web") << QLatin1String("applications-internet")));
|
||||
ui.action_New->setIcon(getStockIcon(QLatin1String("document-new"), QStyle::SP_DesktopIcon));
|
||||
ui.action_Properties->setIcon(getStockIcon(QLatin1String("document-properties"), QStyle::SP_DesktopIcon));
|
||||
ui.action_OpenFolder->setIcon(getStockIcon(QLatin1String("folder-open"), QStyle::SP_DirOpenIcon));
|
||||
ui.action_Start->setIcon(getStockIcon(QLatin1String("media-playback-start"), QStyle::SP_MediaPlay));
|
||||
ui.action_StartNow->setIcon(getStockIcon(QLatin1String("media-playback-start"), QStyle::SP_MediaPlay));
|
||||
ui.action_Start->setIcon(iconPlay);
|
||||
ui.action_StartNow->setIcon(iconPlay);
|
||||
ui.action_Announce->setIcon(getStockIcon(QLatin1String("network-transmit-receive")));
|
||||
ui.action_Pause->setIcon(getStockIcon(QLatin1String("media-playback-pause"), QStyle::SP_MediaPause));
|
||||
ui.action_Pause->setIcon(iconPause);
|
||||
ui.action_Remove->setIcon(getStockIcon(QLatin1String("list-remove"), QStyle::SP_TrashIcon));
|
||||
ui.action_Delete->setIcon(getStockIcon(QLatin1String("edit-delete"), QStyle::SP_TrashIcon));
|
||||
ui.action_StartAll->setIcon(getStockIcon(QLatin1String("media-playback-start"), QStyle::SP_MediaPlay));
|
||||
ui.action_PauseAll->setIcon(getStockIcon(QLatin1String("media-playback-pause"), QStyle::SP_MediaPause));
|
||||
ui.action_StartAll->setIcon(iconPlay);
|
||||
ui.action_PauseAll->setIcon(iconPause);
|
||||
ui.action_Quit->setIcon(getStockIcon(QLatin1String("application-exit")));
|
||||
ui.action_SelectAll->setIcon(getStockIcon(QLatin1String("edit-select-all")));
|
||||
ui.action_ReverseSortOrder->setIcon(getStockIcon(QLatin1String("view-sort-ascending"), QStyle::SP_ArrowDown));
|
||||
|
@ -193,6 +194,18 @@ MainWindow::MainWindow(Session& session, Prefs& prefs, TorrentModel& model, bool
|
|||
ui.action_QueueMoveDown->setIcon(getStockIcon(QLatin1String("go-down"), QStyle::SP_ArrowDown));
|
||||
ui.action_QueueMoveBottom->setIcon(getStockIcon(QLatin1String("go-bottom")));
|
||||
|
||||
auto makeNetworkPixmap = [this](char const* nameIn, QSize size = QSize(16, 16))
|
||||
{
|
||||
QString const name = QLatin1String(nameIn);
|
||||
QIcon const icon = getStockIcon(name, QStyle::SP_DriveNetIcon);
|
||||
return icon.pixmap(size);
|
||||
};
|
||||
myPixmapNetworkError = makeNetworkPixmap("network-error");
|
||||
myPixmapNetworkIdle = makeNetworkPixmap("network-idle");
|
||||
myPixmapNetworkReceive = makeNetworkPixmap("network-receive");
|
||||
myPixmapNetworkTransmit = makeNetworkPixmap("network-transmit");
|
||||
myPixmapNetworkTransmitReceive = makeNetworkPixmap("network-transmit-receive");
|
||||
|
||||
// ui signals
|
||||
connect(ui.action_Toolbar, SIGNAL(toggled(bool)), this, SLOT(setToolbarVisible(bool)));
|
||||
connect(ui.action_Filterbar, SIGNAL(toggled(bool)), this, SLOT(setFilterbarVisible(bool)));
|
||||
|
@ -1433,32 +1446,29 @@ void MainWindow::updateNetworkIcon()
|
|||
time_t const secondsSinceLastRead = now - myLastReadTime;
|
||||
bool const isSending = secondsSinceLastSend <= period;
|
||||
bool const isReading = secondsSinceLastRead <= period;
|
||||
char const* key;
|
||||
QPixmap pixmap;
|
||||
|
||||
if (myNetworkError)
|
||||
{
|
||||
key = "network-error";
|
||||
pixmap = myPixmapNetworkError;
|
||||
}
|
||||
else if (isSending && isReading)
|
||||
{
|
||||
key = "network-transmit-receive";
|
||||
pixmap = myPixmapNetworkTransmitReceive;
|
||||
}
|
||||
else if (isSending)
|
||||
{
|
||||
key = "network-transmit";
|
||||
pixmap = myPixmapNetworkTransmit;
|
||||
}
|
||||
else if (isReading)
|
||||
{
|
||||
key = "network-receive";
|
||||
pixmap = myPixmapNetworkReceive;
|
||||
}
|
||||
else
|
||||
{
|
||||
key = "network-idle";
|
||||
pixmap = myPixmapNetworkIdle;
|
||||
}
|
||||
|
||||
QIcon const icon = getStockIcon(QLatin1String(key), QStyle::SP_DriveNetIcon);
|
||||
QPixmap const pixmap = icon.pixmap(16, 16);
|
||||
|
||||
QString tip;
|
||||
QString const url = mySession.getRemoteUrl().host();
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ protected:
|
|||
|
||||
private:
|
||||
QIcon getStockIcon(QString const&, int fallback = -1);
|
||||
QIcon getStockIcon(QString const&, int fallback, QStringList const& emblemNames);
|
||||
QIcon addEmblem(QIcon icon, QStringList const& emblemNames);
|
||||
|
||||
QSet<int> getSelectedTorrents(bool withMetadataOnly = false) const;
|
||||
void updateNetworkIcon();
|
||||
|
@ -149,6 +149,12 @@ private:
|
|||
Prefs& myPrefs;
|
||||
TorrentModel& myModel;
|
||||
|
||||
QPixmap myPixmapNetworkError;
|
||||
QPixmap myPixmapNetworkIdle;
|
||||
QPixmap myPixmapNetworkReceive;
|
||||
QPixmap myPixmapNetworkTransmit;
|
||||
QPixmap myPixmapNetworkTransmitReceive;
|
||||
|
||||
Ui_MainWindow ui;
|
||||
|
||||
time_t myLastFullUpdateTime;
|
||||
|
|
|
@ -10,11 +10,7 @@
|
|||
#include <iostream>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QFileIconProvider>
|
||||
#include <QFileInfo>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <QStyle>
|
||||
#include <QUrl>
|
||||
#include <QVariant>
|
||||
|
||||
|
@ -41,7 +37,7 @@ Torrent::Torrent(Prefs const& prefs, int id) :
|
|||
#endif
|
||||
|
||||
setInt(ID, id);
|
||||
setIcon(MIME_ICON, qApp->style()->standardIcon(QStyle::SP_FileIcon));
|
||||
setIcon(MIME_ICON, Utils::getFileIcon());
|
||||
}
|
||||
|
||||
Torrent::~Torrent()
|
||||
|
@ -484,7 +480,7 @@ void Torrent::updateMimeIcon()
|
|||
|
||||
if (files.size() > 1)
|
||||
{
|
||||
icon = QFileIconProvider().icon(QFileIconProvider::Folder);
|
||||
icon = Utils::getFolderIcon();
|
||||
}
|
||||
else if (files.size() == 1)
|
||||
{
|
||||
|
@ -492,7 +488,7 @@ void Torrent::updateMimeIcon()
|
|||
}
|
||||
else
|
||||
{
|
||||
icon = QIcon();
|
||||
icon = Utils::guessMimeIcon(name());
|
||||
}
|
||||
|
||||
setIcon(MIME_ICON, icon);
|
||||
|
@ -581,7 +577,13 @@ void Torrent::update(tr_variant* d)
|
|||
|
||||
if (tr_variantGetStr(child, &val, nullptr))
|
||||
{
|
||||
changed |= setString(property_index, val);
|
||||
bool const field_changed = setString(property_index, val);
|
||||
changed |= field_changed;
|
||||
|
||||
if (field_changed && key == TR_KEY_name)
|
||||
{
|
||||
updateMimeIcon();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
99
qt/Utils.cc
99
qt/Utils.cc
|
@ -6,6 +6,9 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
|
@ -16,7 +19,7 @@
|
|||
#include <QColor>
|
||||
#include <QDataStream>
|
||||
#include <QFile>
|
||||
#include <QFileDialog>
|
||||
#include <QFileIconProvider>
|
||||
#include <QFileInfo>
|
||||
#include <QHeaderView>
|
||||
#include <QIcon>
|
||||
|
@ -25,7 +28,6 @@
|
|||
#include <QMimeType>
|
||||
#include <QObject>
|
||||
#include <QPixmapCache>
|
||||
#include <QSet>
|
||||
#include <QStyle>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -85,12 +87,87 @@ bool isSlashChar(QChar const& c)
|
|||
return c == QLatin1Char('/') || c == QLatin1Char('\\');
|
||||
}
|
||||
|
||||
QIcon folderIcon()
|
||||
{
|
||||
static QIcon icon;
|
||||
if (icon.isNull())
|
||||
{
|
||||
icon = QFileIconProvider().icon(QFileIconProvider::Folder);
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
QIcon fileIcon()
|
||||
{
|
||||
static QIcon icon;
|
||||
if (icon.isNull())
|
||||
{
|
||||
icon = QFileIconProvider().icon(QFileIconProvider::File);
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
std::map<QString, QIcon> iconCache;
|
||||
QIcon const getMimeIcon(QString const& filename)
|
||||
{
|
||||
// If the suffix doesn't match a mime type, treat it as a folder.
|
||||
// This heuristic is fast and yields good results for torrent names.
|
||||
static std::set<QString> suffixes;
|
||||
if (suffixes.empty())
|
||||
{
|
||||
for (auto const& type : QMimeDatabase().allMimeTypes())
|
||||
{
|
||||
auto const tmp = type.suffixes();
|
||||
suffixes.insert(tmp.begin(), tmp.end());
|
||||
}
|
||||
}
|
||||
|
||||
QString const ext = QFileInfo(filename).suffix();
|
||||
if (suffixes.count(ext) == 0)
|
||||
{
|
||||
return folderIcon();
|
||||
}
|
||||
|
||||
QIcon& icon = iconCache[ext];
|
||||
if (icon.isNull()) // cache miss
|
||||
{
|
||||
QMimeDatabase mimeDb;
|
||||
QMimeType type = mimeDb.mimeTypeForFile(filename, QMimeDatabase::MatchExtension);
|
||||
if (icon.isNull())
|
||||
{
|
||||
icon = QIcon::fromTheme(type.iconName());
|
||||
}
|
||||
|
||||
if (icon.isNull())
|
||||
{
|
||||
icon = QIcon::fromTheme(type.genericIconName());
|
||||
}
|
||||
|
||||
if (icon.isNull())
|
||||
{
|
||||
icon = fileIcon();
|
||||
}
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QIcon Utils::getFolderIcon()
|
||||
{
|
||||
return folderIcon();
|
||||
}
|
||||
|
||||
QIcon Utils::getFileIcon()
|
||||
{
|
||||
return fileIcon();
|
||||
}
|
||||
|
||||
QIcon Utils::guessMimeIcon(QString const& filename)
|
||||
{
|
||||
static QIcon const fallback = qApp->style()->standardIcon(QStyle::SP_FileIcon);
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
QIcon icon;
|
||||
|
@ -109,17 +186,11 @@ QIcon Utils::guessMimeIcon(QString const& filename)
|
|||
return icon;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
return getMimeIcon(filename);
|
||||
|
||||
#endif
|
||||
|
||||
QMimeDatabase mimeDb;
|
||||
QMimeType mimeType = mimeDb.mimeTypeForFile(filename, QMimeDatabase::MatchExtension);
|
||||
|
||||
if (mimeType.isValid())
|
||||
{
|
||||
return QIcon::fromTheme(mimeType.iconName(), QIcon::fromTheme(mimeType.genericIconName(), fallback));
|
||||
}
|
||||
|
||||
return fallback;
|
||||
}
|
||||
|
||||
QIcon Utils::getIconFromIndex(QModelIndex const& index)
|
||||
|
|
|
@ -23,6 +23,8 @@ class QModelIndex;
|
|||
class Utils
|
||||
{
|
||||
public:
|
||||
static QIcon getFileIcon();
|
||||
static QIcon getFolderIcon();
|
||||
static QIcon guessMimeIcon(QString const& filename);
|
||||
static QIcon getIconFromIndex(QModelIndex const& index);
|
||||
|
||||
|
|
Loading…
Reference in New Issue