refactor: remove exit-time destructors from transmission-qt (#1395)

* refactor: remove exit-time destructors from transmission-qt
This commit is contained in:
Charles Kerr 2020-08-15 10:42:51 -05:00 committed by GitHub
parent 677dc73eac
commit 68920f5fa6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 680 additions and 477 deletions

View File

@ -406,6 +406,7 @@ set(WARNING_CANDIDATES
-Wextra
-Wcast-align
-Wduplicated-cond
-Wexit-time-destructors
-Wextra-semi
-Wextra-semi-stmt
-Wextra-tokens

View File

@ -20,7 +20,7 @@ Checks: >
-clang-diagnostic-sign-conversion,
-clang-diagnostic-switch-enum,
-clang-diagnostic-undefined-reinterpret-cast,
+ -clang-diagnostic-unused-member-function,
-clang-diagnostic-unused-member-function,
cppcoreguidelines-*,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-init-variables,

View File

@ -44,9 +44,6 @@
namespace
{
auto const MyConfigName = QStringLiteral("transmission");
auto const MyReadableName = QStringLiteral("transmission-qt");
std::array<tr_option, 8> const Opts =
{
tr_option{ 'g', "config-dir", "Where to look for configuration files", "g", true, "<path>" },
@ -88,13 +85,13 @@ bool loadTranslation(QTranslator& translator, QString const& name, QLocale const
} // namespace
Application::Application(int& argc, char** argv) :
QApplication(argc, argv)
QApplication(argc, argv),
config_name_{QStringLiteral("transmission")},
display_name_{QStringLiteral("transmission-qt")}
{
setApplicationName(MyConfigName);
setApplicationName(config_name_);
loadTranslations();
Formatter::initUnits();
#if defined(_WIN32) || defined(__APPLE__)
if (QIcon::themeName().isEmpty())
@ -162,13 +159,13 @@ Application::Application(int& argc, char** argv) :
break;
case 'v':
std::cerr << qPrintable(MyReadableName) << ' ' << LONG_VERSION_STRING << std::endl;
std::cerr << qPrintable(display_name_) << ' ' << LONG_VERSION_STRING << std::endl;
quitLater();
return;
case TR_OPT_ERR:
std::cerr << qPrintable(QObject::tr("Invalid option")) << std::endl;
tr_getopt_usage(qPrintable(MyReadableName), getUsage(), Opts.data());
tr_getopt_usage(qPrintable(display_name_), getUsage(), Opts.data());
quitLater();
return;
@ -379,8 +376,8 @@ void Application::loadTranslations()
installTranslator(&qt_translator_);
}
if (loadTranslation(app_translator_, MyConfigName, locale, app_qm_dirs) ||
loadTranslation(app_translator_, MyConfigName, english_locale, app_qm_dirs))
if (loadTranslation(app_translator_, config_name_, locale, app_qm_dirs) ||
loadTranslation(app_translator_, config_name_, english_locale, app_qm_dirs))
{
installTranslator(&app_translator_);
}

View File

@ -69,6 +69,9 @@ private:
QTranslator qt_translator_;
QTranslator app_translator_;
FaviconCache favicons_;
QString const config_name_;
QString const display_name_;
};
#undef qApp

View File

@ -19,6 +19,7 @@ set(${PROJECT_NAME}_SOURCES
Filters.cc
Formatter.cc
FreeSpaceLabel.cc
IconCache.cc
IconToolButton.cc
InteropHelper.cc
InteropObject.cc
@ -84,6 +85,7 @@ set(${PROJECT_NAME}_HEADERS
Filters.h
Formatter.h
FreeSpaceLabel.h
IconCache.h
IconToolButton.h
InteropHelper.h
InteropObject.h

View File

@ -17,15 +17,6 @@
#include "DBusInteropHelper.h"
#include "InteropObject.h"
namespace
{
auto const DBusService = QStringLiteral("com.transmissionbt.Transmission");
auto const DBusObjectPath = QStringLiteral("/com/transmissionbt/Transmission");
auto const DBusInterface = QStringLiteral("com.transmissionbt.Transmission");
} // namespace
bool DBusInteropHelper::isConnected() const
{
return QDBusConnection::sessionBus().isConnected();
@ -33,8 +24,12 @@ bool DBusInteropHelper::isConnected() const
QVariant DBusInteropHelper::addMetainfo(QString const& metainfo)
{
QDBusMessage request = QDBusMessage::createMethodCall(DBusService, DBusObjectPath, DBusInterface,
QStringLiteral("AddMetainfo"));
auto request = QDBusMessage::createMethodCall(
QStringLiteral("com.transmissionbt.Transmission"),
QStringLiteral("com/transmissionbt/Transmission"),
QStringLiteral("com.transmissionbt.Transmission"),
QStringLiteral("AddMetainfo")
);
request.setArguments(QVariantList() << metainfo);
QDBusReply<bool> const response = QDBusConnection::sessionBus().call(request);
@ -43,20 +38,21 @@ QVariant DBusInteropHelper::addMetainfo(QString const& metainfo)
void DBusInteropHelper::registerObject(QObject* parent)
{
QDBusConnection bus = QDBusConnection::sessionBus();
auto bus = QDBusConnection::sessionBus();
if (!bus.isConnected())
{
return;
}
if (!bus.registerService(DBusService))
auto const service_name = QStringLiteral("com.transmissionbt.Transmission");
if (!bus.registerService(service_name))
{
std::cerr << "couldn't register " << qPrintable(DBusService) << std::endl;
std::cerr << "couldn't register " << qPrintable(service_name) << std::endl;
}
if (!bus.registerObject(DBusObjectPath, new InteropObject(parent), QDBusConnection::ExportAllSlots))
auto const object_path = QStringLiteral("com/transmissionbt/Transmission");
if (!bus.registerObject(object_path, new InteropObject(parent), QDBusConnection::ExportAllSlots))
{
std::cerr << "couldn't register " << qPrintable(DBusObjectPath) << std::endl;
std::cerr << "couldn't register " << qPrintable(object_path) << std::endl;
}
}

View File

@ -206,7 +206,9 @@ DetailsDialog::DetailsDialog(Session& session, Prefs& prefs, TorrentModel const&
BaseDialog(parent),
session_(session),
prefs_(prefs),
model_(model)
model_(model),
icon_encrypted_(QStringLiteral(":/icons/encrypted.png")),
icon_unencrypted_()
{
ui_.setupUi(this);
@ -511,13 +513,13 @@ void DetailsDialog::refreshUI()
double const d = size_when_done == 0 ?
100.0 :
100.0 * static_cast<double>(size_when_done - left_until_done) / static_cast<double>(size_when_done);
auto const pct = Formatter::percentToString(d);
auto const pct = Formatter::get().percentToString(d);
if (have_unverified == 0 && left_until_done == 0)
{
//: Text following the "Have:" label in torrent properties dialog;
//: %1 is amount of downloaded and verified data
string = tr("%1 (100%)").arg(Formatter::sizeToString(have_verified));
string = tr("%1 (100%)").arg(Formatter::get().sizeToString(have_verified));
}
else if (have_unverified == 0)
{
@ -525,7 +527,7 @@ void DetailsDialog::refreshUI()
//: %1 is amount of downloaded and verified data,
//: %2 is overall size of torrent data,
//: %3 is percentage (%1/%2*100)
string = tr("%1 of %2 (%3%)").arg(Formatter::sizeToString(have_verified)).arg(Formatter::sizeToString(
string = tr("%1 of %2 (%3%)").arg(Formatter::get().sizeToString(have_verified)).arg(Formatter::get().sizeToString(
size_when_done)).
arg(pct);
}
@ -536,8 +538,8 @@ void DetailsDialog::refreshUI()
//: %2 is overall size of torrent data,
//: %3 is percentage (%1/%2*100),
//: %4 is amount of downloaded but not yet verified data
string = tr("%1 of %2 (%3%), %4 Unverified").arg(Formatter::sizeToString(have_verified + have_unverified)).
arg(Formatter::sizeToString(size_when_done)).arg(pct).arg(Formatter::sizeToString(have_unverified));
string = tr("%1 of %2 (%3%), %4 Unverified").arg(Formatter::get().sizeToString(have_verified + have_unverified)).
arg(Formatter::get().sizeToString(size_when_done)).arg(pct).arg(Formatter::get().sizeToString(have_unverified));
}
}
@ -551,7 +553,7 @@ void DetailsDialog::refreshUI()
else
{
auto const percent = 100.0 * static_cast<double>(available) / static_cast<double>(size_when_done);
string = QStringLiteral("%1%").arg(Formatter::percentToString(percent));
string = QStringLiteral("%1%").arg(Formatter::get().percentToString(percent));
}
ui_.availabilityValueLabel->setText(string);
@ -572,8 +574,8 @@ void DetailsDialog::refreshUI()
f += t->failedEver();
}
QString const dstr = Formatter::sizeToString(d);
QString const fstr = Formatter::sizeToString(f);
QString const dstr = Formatter::get().sizeToString(d);
QString const fstr = Formatter::get().sizeToString(f);
if (f != 0)
{
@ -603,7 +605,8 @@ void DetailsDialog::refreshUI()
d += t->downloadedEver();
}
string = tr("%1 (Ratio: %2)").arg(Formatter::sizeToString(u)).arg(Formatter::ratioToString(tr_getRatio(u, d)));
string = tr("%1 (Ratio: %2)").arg(Formatter::get().sizeToString(u)).arg(Formatter::get().ratioToString(tr_getRatio(u,
d)));
}
ui_.uploadedValueLabel->setText(string);
@ -643,7 +646,7 @@ void DetailsDialog::refreshUI()
{
auto const now = time(nullptr);
auto const seconds = int(std::difftime(now, baseline));
string = Formatter::timeToString(seconds);
string = Formatter::get().timeToString(seconds);
}
}
@ -677,7 +680,7 @@ void DetailsDialog::refreshUI()
}
else
{
string = Formatter::timeToString(baseline);
string = Formatter::get().timeToString(baseline);
}
}
}
@ -716,7 +719,7 @@ void DetailsDialog::refreshUI()
}
else
{
string = tr("%1 ago").arg(Formatter::timeToString(seconds));
string = tr("%1 ago").arg(Formatter::get().timeToString(seconds));
}
}
@ -779,12 +782,12 @@ void DetailsDialog::refreshUI()
}
else if (piece_size > 0)
{
string = tr("%1 (%Ln pieces @ %2)", "", pieces).arg(Formatter::sizeToString(size)).
arg(Formatter::memToString(piece_size));
string = tr("%1 (%Ln pieces @ %2)", "", pieces).arg(Formatter::get().sizeToString(size)).
arg(Formatter::get().memToString(piece_size));
}
else
{
string = tr("%1 (%Ln pieces)", "", pieces).arg(Formatter::sizeToString(size));
string = tr("%1 (%Ln pieces)", "", pieces).arg(Formatter::get().sizeToString(size));
}
}
@ -1072,13 +1075,11 @@ void DetailsDialog::refreshUI()
if (item == nullptr) // new peer has connected
{
static QIcon const EncryptionIcon(QStringLiteral(":/icons/encrypted.png"));
static QIcon const EmptyIcon;
item = new PeerItem(peer);
item->setTextAlignment(COL_UP, Qt::AlignRight | Qt::AlignVCenter);
item->setTextAlignment(COL_DOWN, Qt::AlignRight | Qt::AlignVCenter);
item->setTextAlignment(COL_PERCENT, Qt::AlignRight | Qt::AlignVCenter);
item->setIcon(COL_LOCK, peer.is_encrypted ? EncryptionIcon : EmptyIcon);
item->setIcon(COL_LOCK, peer.is_encrypted ? icon_encrypted_ : icon_unencrypted_);
item->setToolTip(COL_LOCK, peer.is_encrypted ? tr("Encrypted connection") : QString());
item->setText(COL_ADDRESS, peer.address);
item->setText(COL_CLIENT, peer.client_name);
@ -1157,8 +1158,9 @@ void DetailsDialog::refreshUI()
code_tip.resize(code_tip.size() - 1); // eat the trailing linefeed
}
item->setText(COL_UP, peer.rate_to_peer.isZero() ? QString() : Formatter::speedToString(peer.rate_to_peer));
item->setText(COL_DOWN, peer.rate_to_client.isZero() ? QString() : Formatter::speedToString(peer.rate_to_client));
item->setText(COL_UP, peer.rate_to_peer.isZero() ? QString() : Formatter::get().speedToString(peer.rate_to_peer));
item->setText(COL_DOWN,
peer.rate_to_client.isZero() ? QString() : Formatter::get().speedToString(peer.rate_to_client));
item->setText(COL_PERCENT, peer.progress > 0 ? QStringLiteral("%1%").arg(int(peer.progress * 100.0)) :
QString());
item->setText(COL_STATUS, code);
@ -1391,7 +1393,7 @@ void DetailsDialog::onRemoveTrackerClicked()
void DetailsDialog::initOptionsTab()
{
auto const speed_unit_str = Formatter::unitStr(Formatter::SPEED, Formatter::KB);
auto const speed_unit_str = Formatter::get().unitStr(Formatter::SPEED, Formatter::KB);
ui_.singleDownSpin->setSuffix(QStringLiteral(" %1").arg(speed_unit_str));
ui_.singleUpSpin->setSuffix(QStringLiteral(" %1").arg(speed_unit_str));

View File

@ -132,4 +132,7 @@ private:
TrackerDelegate* tracker_delegate_ = {};
QMap<QString, QTreeWidgetItem*> peers_;
QIcon const icon_encrypted_;
QIcon const icon_unencrypted_;
};

View File

@ -18,7 +18,7 @@
#include "FileTreeItem.h"
#include "FileTreeModel.h"
#include "Formatter.h"
#include "Utils.h" // mime icons
#include "IconCache.h"
QHash<QString, int> const& FileTreeItem::getMyChildRows()
{
@ -171,7 +171,7 @@ QVariant FileTreeItem::data(int column, int role) const
}
else
{
value = Utils::guessMimeIcon(name());
value = IconCache::get().guessMimeIcon(name());
}
}
@ -213,7 +213,7 @@ double FileTreeItem::progress() const
QString FileTreeItem::sizeString() const
{
return Formatter::sizeToString(size());
return Formatter::get().sizeToString(size());
}
uint64_t FileTreeItem::size() const

View File

@ -89,9 +89,10 @@ void FileTreeView::resizeEvent(QResizeEvent* event)
switch (column)
{
case FileTreeModel::COL_SIZE:
for (int s = Formatter::B; s <= Formatter::TB; ++s)
for (int s = Formatter::get().B; s <= Formatter::get().TB; ++s)
{
item_texts << QStringLiteral("999.9 ") + Formatter::unitStr(Formatter::MEM, static_cast<Formatter::Size>(s));
item_texts << QStringLiteral("999.9 ") + Formatter::get().unitStr(Formatter::MEM,
static_cast<Formatter::Size>(s));
}
break;

View File

@ -6,35 +6,8 @@
*
*/
#include <cstdint> // uint64_t
#include "Filters.h"
std::array<QString, FilterMode::NUM_MODES> const FilterMode::Names =
{
QStringLiteral("show-all"),
QStringLiteral("show-active"),
QStringLiteral("show-downloading"),
QStringLiteral("show-seeding"),
QStringLiteral("show-paused"),
QStringLiteral("show-finished"),
QStringLiteral("show-verifying"),
QStringLiteral("show-error")
};
int FilterMode::modeFromName(QString const& name)
{
for (int i = 0; i < NUM_MODES; ++i)
{
if (Names[i] == name)
{
return i;
}
}
return FilterMode().mode(); // use the default value
}
// NB: if you change this function, update TorrentFields too
bool FilterMode::test(Torrent const& tor, int mode)
{
@ -65,34 +38,3 @@ bool FilterMode::test(Torrent const& tor, int mode)
return true;
}
}
/***
****
***/
std::array<QString, SortMode::NUM_MODES> const SortMode::Names =
{
QStringLiteral("sort-by-activity"),
QStringLiteral("sort-by-age"),
QStringLiteral("sort-by-eta"),
QStringLiteral("sort-by-name"),
QStringLiteral("sort-by-progress"),
QStringLiteral("sort-by-queue"),
QStringLiteral("sort-by-ratio"),
QStringLiteral("sort-by-size"),
QStringLiteral("sort-by-state"),
QStringLiteral("sort-by-id")
};
int SortMode::modeFromName(QString const& name)
{
for (int i = 0; i < NUM_MODES; ++i)
{
if (Names[i] == name)
{
return i;
}
}
return SortMode().mode(); // use the default value
}

View File

@ -10,10 +10,6 @@
#include <array>
#include <QMetaType>
#include <QString>
#include <QVariant>
#include "Torrent.h"
class FilterMode
@ -38,28 +34,11 @@ public:
{
}
FilterMode(QString const& name) :
mode_(modeFromName(name))
{
}
int mode() const
{
return mode_;
}
QString const& name() const
{
return nameFromMode(mode_);
}
static int modeFromName(QString const& name);
static QString const& nameFromMode(int mode)
{
return Names[mode];
}
/* The Torrent properties that can affect this filter.
When one of these changes, it's time to refilter. */
static Torrent::fields_t constexpr TorrentFields = {
@ -75,8 +54,6 @@ public:
private:
int mode_;
static std::array<QString, NUM_MODES> const Names;
};
Q_DECLARE_METATYPE(FilterMode)
@ -105,28 +82,13 @@ public:
{
}
SortMode(QString const& name) :
mode_(modeFromName(name))
{
}
int mode() const
{
return mode_;
}
QString const& name() const
{
return Names[mode_];
}
static int modeFromName(QString const& name);
static QString const& nameFromMode(int mode);
private:
int mode_ = SORT_BY_ID;
static std::array<QString, NUM_MODES> const Names;
};
Q_DECLARE_METATYPE(SortMode)

View File

@ -15,59 +15,47 @@
#include <algorithm>
#include <array>
/***
**** Constants
***/
namespace
Formatter& Formatter::get()
{
auto constexpr SpeedBase = 1000;
auto constexpr SizeBase = 1000;
auto constexpr MemBase = 1024;
} // namespace
std::array<std::array<QString, Formatter::NUM_SIZES>, Formatter::NUM_TYPES> const Formatter::UnitStrings =
{{
/* SPEED */ { tr("B/s"), tr("kB/s"), tr("MB/s"), tr("GB/s"), tr("TB/s") },
/* SIZE */ { tr("B"), tr("kB"), tr("MB"), tr("GB"), tr("TB") },
/* MEM */ { tr("B"), tr("KiB"), tr("MiB"), tr("GiB"), tr("TiB") }
}};
void Formatter::initUnits()
{
tr_formatter_speed_init(SpeedBase, UnitStrings[SPEED][KB].toUtf8().constData(),
UnitStrings[SPEED][MB].toUtf8().constData(), UnitStrings[SPEED][GB].toUtf8().constData(),
UnitStrings[SPEED][TB].toUtf8().constData());
tr_formatter_size_init(SizeBase, UnitStrings[SIZE][KB].toUtf8().constData(),
UnitStrings[SIZE][MB].toUtf8().constData(),
UnitStrings[SIZE][GB].toUtf8().constData(), UnitStrings[SIZE][TB].toUtf8().constData());
tr_formatter_mem_init(MemBase, UnitStrings[MEM][KB].toUtf8().constData(), UnitStrings[MEM][MB].toUtf8().constData(),
UnitStrings[MEM][GB].toUtf8().constData(), UnitStrings[MEM][TB].toUtf8().constData());
static auto& singleton = *new Formatter();
return singleton;
}
/***
****
***/
double Speed::getKBps() const
Formatter::Formatter() :
UnitStrings{{
{ tr("B/s"), tr("kB/s"), tr("MB/s"), tr("GB/s"), tr("TB/s") }, // SPEED
{ tr("B"), tr("kB"), tr("MB"), tr("GB"), tr("TB") }, // SIZE
{ tr("B"), tr("KiB"), tr("MiB"), tr("GiB"), tr("TiB") } // MEM
}}
{
return getBps() / static_cast<double>(SpeedBase);
auto const& speed = UnitStrings[SPEED];
tr_formatter_speed_init(SpeedBase,
speed[KB].toUtf8().constData(),
speed[MB].toUtf8().constData(),
speed[GB].toUtf8().constData(),
speed[TB].toUtf8().constData());
auto const& size = UnitStrings[SIZE];
tr_formatter_size_init(SizeBase,
size[KB].toUtf8().constData(),
size[MB].toUtf8().constData(),
size[GB].toUtf8().constData(),
size[TB].toUtf8().constData());
auto const& mem = UnitStrings[MEM];
tr_formatter_mem_init(MemBase,
mem[KB].toUtf8().constData(),
mem[MB].toUtf8().constData(),
mem[GB].toUtf8().constData(),
mem[TB].toUtf8().constData());
}
Speed Speed::fromKBps(double KBps)
QString Formatter::unitStr(Type t, Size s) const
{
return Speed{ static_cast<int>(KBps * SpeedBase) };
return UnitStrings[t][s];
}
/***
****
***/
QString Formatter::memToString(int64_t bytes)
QString Formatter::memToString(int64_t bytes) const
{
if (bytes < 0)
{
@ -84,7 +72,7 @@ QString Formatter::memToString(int64_t bytes)
return QString::fromUtf8(buf.data());
}
QString Formatter::sizeToString(int64_t bytes)
QString Formatter::sizeToString(int64_t bytes) const
{
if (bytes < 0)
{
@ -101,40 +89,40 @@ QString Formatter::sizeToString(int64_t bytes)
return QString::fromUtf8(buf.data());
}
QString Formatter::speedToString(Speed const& speed)
QString Formatter::speedToString(Speed const& speed) const
{
auto buf = std::array<char, 128>{};
tr_formatter_speed_KBps(buf.data(), speed.getKBps(), buf.size());
return QString::fromUtf8(buf.data());
}
QString Formatter::uploadSpeedToString(Speed const& upload_speed)
QString Formatter::uploadSpeedToString(Speed const& upload_speed) const
{
static QChar constexpr UploadSymbol(0x25B4);
return tr("%1 %2").arg(speedToString(upload_speed)).arg(UploadSymbol);
}
QString Formatter::downloadSpeedToString(Speed const& download_speed)
QString Formatter::downloadSpeedToString(Speed const& download_speed) const
{
static QChar constexpr DownloadSymbol(0x25BE);
return tr("%1 %2").arg(speedToString(download_speed)).arg(DownloadSymbol);
}
QString Formatter::percentToString(double x)
QString Formatter::percentToString(double x) const
{
auto buf = std::array<char, 128>{};
return QString::fromUtf8(tr_strpercent(buf.data(), x, buf.size()));
}
QString Formatter::ratioToString(double ratio)
QString Formatter::ratioToString(double ratio) const
{
auto buf = std::array<char, 128>{};
return QString::fromUtf8(tr_strratio(buf.data(), buf.size(), ratio, "\xE2\x88\x9E"));
}
QString Formatter::timeToString(int seconds)
QString Formatter::timeToString(int seconds) const
{
seconds = std::max(seconds, 0);
auto const days = seconds / 86400;
@ -189,3 +177,17 @@ QString Formatter::timeToString(int seconds)
return str;
}
/***
****
***/
double Speed::getKBps() const
{
return getBps() / static_cast<double>(Formatter::SpeedBase);
}
Speed Speed::fromKBps(double KBps)
{
return Speed{ static_cast<int>(KBps * Formatter::SpeedBase) };
}

View File

@ -41,23 +41,25 @@ public:
NUM_TYPES
};
public:
static QString memToString(int64_t bytes);
static QString sizeToString(int64_t bytes);
static QString speedToString(Speed const& speed);
static QString percentToString(double x);
static QString ratioToString(double ratio);
static QString timeToString(int seconds);
static QString uploadSpeedToString(Speed const& up);
static QString downloadSpeedToString(Speed const& down);
static constexpr int SpeedBase = 1000;
static constexpr int SizeBase = 1000;
static constexpr int MemBase = 1024;
static QString unitStr(Type t, Size s)
{
return UnitStrings[t][s];
}
static Formatter& get();
static void initUnits();
QString memToString(int64_t bytes) const;
QString sizeToString(int64_t bytes) const;
QString speedToString(Speed const& speed) const;
QString percentToString(double x) const;
QString ratioToString(double ratio) const;
QString timeToString(int seconds) const;
QString uploadSpeedToString(Speed const& up) const;
QString downloadSpeedToString(Speed const& down) const;
QString unitStr(Type t, Size s) const;
protected:
Formatter();
private:
static std::array<std::array<QString, Formatter::NUM_SIZES>, Formatter::NUM_TYPES> const UnitStrings;
std::array<std::array<QString, Formatter::NUM_SIZES>, Formatter::NUM_TYPES> const UnitStrings;
};

View File

@ -84,7 +84,7 @@ void FreeSpaceLabel::onTimer()
auto const bytes = dictFind<int64_t>(r.args.get(), TR_KEY_size_bytes);
if (bytes && *bytes > 1)
{
setText(tr("%1 free").arg(Formatter::sizeToString(*bytes)));
setText(tr("%1 free").arg(Formatter::get().sizeToString(*bytes)));
}
else
{

155
qt/IconCache.cc Normal file
View File

@ -0,0 +1,155 @@
/*
* This file Copyright (C) 2009-2015 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
*
*/
#include "IconCache.h"
#ifdef _WIN32
#include <windows.h>
#include <shellapi.h>
#endif
#include <QFile>
#include <QFileIconProvider>
#include <QFileInfo>
#include <QIcon>
#include <QObject>
#ifdef _WIN32
#include <QPixmapCache>
#include <QtWin>
#else
#include <QMimeDatabase>
#include <QMimeType>
#endif
#include <libtransmission/transmission.h>
/***
****
***/
IconCache& IconCache::get()
{
static auto& singleton = *new IconCache();
return singleton;
}
IconCache::IconCache() :
folder_icon_(QFileIconProvider().icon(QFileIconProvider::Folder)),
file_icon_(QFileIconProvider().icon(QFileIconProvider::Folder))
{
}
QIcon IconCache::guessMimeIcon(QString const& filename) const
{
#ifdef _WIN32
QIcon icon;
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);
}
return icon;
#else
return getMimeIcon(filename);
#endif
}
/***
****
***/
#ifdef _WIN32
void IconCache::addAssociatedFileIcon(QFileInfo const& file_info, UINT 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)
{
pixmap = QtWin::fromHICON(shell_file_info.hIcon);
::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 the suffix doesn't match a mime type, treat it as a folder.
// This heuristic is fast and yields good results for torrent names.
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 folder_icon_;
}
QIcon& icon = icon_cache_[ext];
if (icon.isNull()) // cache miss
{
QMimeDatabase mime_db;
QMimeType type = mime_db.mimeTypeForFile(filename, QMimeDatabase::MatchExtension);
if (icon.isNull())
{
icon = QIcon::fromTheme(type.iconName());
}
if (icon.isNull())
{
icon = QIcon::fromTheme(type.genericIconName());
}
if (icon.isNull())
{
icon = file_icon_;
}
}
return icon;
}
#endif

48
qt/IconCache.h Normal file
View File

@ -0,0 +1,48 @@
/*
* This file Copyright (C) 2009-2015 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
*
*/
#pragma once
#if defined(_WIN32)
#include <windows.h> // UINT
class QFileInfo;
#else
#include <unordered_map>
#include <unordered_set>
#include "Utils.h" // std::hash<QString>()
#endif
#include <QIcon>
#include <QString>
class QModelIndex;
class IconCache
{
public:
static IconCache& get();
QIcon folderIcon() const { return folder_icon_; }
QIcon fileIcon() const { return file_icon_; }
QIcon guessMimeIcon(QString const& filename) const;
protected:
IconCache();
private:
QIcon const folder_icon_;
QIcon const file_icon_;
#if defined(_WIN32)
void addAssociatedFileIcon(QFileInfo const& file_info, UINT icon_size, QIcon& icon) const;
#else
mutable std::unordered_map<QString, QIcon> icon_cache_;
mutable std::unordered_set<QString> suffixes_;
QIcon getMimeIcon(QString const& filename) const;
#endif
};

View File

@ -50,16 +50,6 @@
#define STATS_MODE_KEY "stats-mode"
#define SORT_MODE_KEY "sort-mode"
namespace
{
auto const TotalRatioStatsModeName = QStringLiteral("total-ratio");
auto const TotalTransferStatsModeName = QStringLiteral("total-transfer");
auto const SessionRatioStatsModeName = QStringLiteral("session-ratio");
auto const SessionTransferStatsModeName = QStringLiteral("session-transfer");
} // namespace
/**
* This is a proxy-style for that forces it to be always disabled.
* We use this to make our torrent list view behave consistently on
@ -147,6 +137,11 @@ MainWindow::MainWindow(Session& session, Prefs& prefs, TorrentModel& model, bool
torrent_delegate_(new TorrentDelegate(this)),
torrent_delegate_min_(new TorrentDelegateMin(this)),
network_timer_(this),
total_ratio_stats_mode_name_(QStringLiteral("total-ratio")),
total_transfer_stats_mode_name_(QStringLiteral("total-transfer")),
session_ratio_stats_mode_name_(QStringLiteral("session-ratio")),
session_transfer_stats_mode_name_(QStringLiteral("session-transfer")),
show_options_checkbox_name_(QStringLiteral("show-options-checkbox")),
refresh_timer_(this)
{
setAcceptDrops(true);
@ -395,7 +390,7 @@ void MainWindow::initStatusBar()
ui_.optionsButton->setMenu(createOptionsMenu());
int const minimum_speed_width =
ui_.downloadSpeedLabel->fontMetrics().boundingRect(Formatter::uploadSpeedToString(Speed::fromKBps(999.99))).width();
ui_.downloadSpeedLabel->fontMetrics().boundingRect(Formatter::get().uploadSpeedToString(Speed::fromKBps(999.99))).width();
ui_.downloadSpeedLabel->setMinimumWidth(minimum_speed_width);
ui_.uploadSpeedLabel->setMinimumWidth(minimum_speed_width);
@ -420,7 +415,8 @@ QMenu* MainWindow::createOptionsMenu()
action_group->addAction(off_action);
connect(off_action, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs(bool)));
on_action = menu->addAction(tr("Limited at %1").arg(Formatter::speedToString(Speed::fromKBps(current_value))));
on_action =
menu->addAction(tr("Limited at %1").arg(Formatter::get().speedToString(Speed::fromKBps(current_value))));
on_action->setCheckable(true);
on_action->setProperty(PREF_VARIANTS_KEY, QVariantList() << pref << current_value << enabled_pref << true);
action_group->addAction(on_action);
@ -430,7 +426,7 @@ QMenu* MainWindow::createOptionsMenu()
for (int const i : stock_speeds)
{
QAction* action = menu->addAction(Formatter::speedToString(Speed::fromKBps(i)));
QAction* action = menu->addAction(Formatter::get().speedToString(Speed::fromKBps(i)));
action->setProperty(PREF_VARIANTS_KEY, QVariantList() << pref << i << enabled_pref << true);
connect(action, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs()));
}
@ -450,7 +446,7 @@ QMenu* MainWindow::createOptionsMenu()
action_group->addAction(off_action);
connect(off_action, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs(bool)));
on_action = menu->addAction(tr("Stop at Ratio (%1)").arg(Formatter::ratioToString(current_value)));
on_action = menu->addAction(tr("Stop at Ratio (%1)").arg(Formatter::get().ratioToString(current_value)));
on_action->setCheckable(true);
on_action->setProperty(PREF_VARIANTS_KEY, QVariantList() << pref << current_value << enabled_pref << true);
action_group->addAction(on_action);
@ -460,7 +456,7 @@ QMenu* MainWindow::createOptionsMenu()
for (double const i : stock_ratios)
{
QAction* action = menu->addAction(Formatter::ratioToString(i));
QAction* action = menu->addAction(Formatter::get().ratioToString(i));
action->setProperty(PREF_VARIANTS_KEY, QVariantList() << pref << i << enabled_pref << true);
connect(action, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs()));
}
@ -485,10 +481,10 @@ QMenu* MainWindow::createStatsModeMenu()
{
std::array<QPair<QAction*, QString>, 4> stats_modes =
{
qMakePair(ui_.action_TotalRatio, TotalRatioStatsModeName),
qMakePair(ui_.action_TotalTransfer, TotalTransferStatsModeName),
qMakePair(ui_.action_SessionRatio, SessionRatioStatsModeName),
qMakePair(ui_.action_SessionTransfer, SessionTransferStatsModeName)
qMakePair(ui_.action_TotalRatio, total_ratio_stats_mode_name_),
qMakePair(ui_.action_TotalTransfer, total_transfer_stats_mode_name_),
qMakePair(ui_.action_SessionRatio, session_ratio_stats_mode_name_),
qMakePair(ui_.action_SessionTransfer, session_transfer_stats_mode_name_)
};
auto* action_group = new QActionGroup(this);
@ -786,12 +782,13 @@ void MainWindow::refreshTrayIcon(TransferStats const& stats)
}
else if (stats.peers_sending != 0)
{
tip = Formatter::downloadSpeedToString(stats.speed_down) + QStringLiteral(" ") + Formatter::uploadSpeedToString(
tip = Formatter::get().downloadSpeedToString(stats.speed_down) + QStringLiteral(" ") +
Formatter::get().uploadSpeedToString(
stats.speed_up);
}
else if (stats.peers_receiving != 0)
{
tip = Formatter::uploadSpeedToString(stats.speed_up);
tip = Formatter::get().uploadSpeedToString(stats.speed_up);
}
tray_icon_.setToolTip(tip);
@ -799,9 +796,9 @@ void MainWindow::refreshTrayIcon(TransferStats const& stats)
void MainWindow::refreshStatusBar(TransferStats const& stats)
{
ui_.uploadSpeedLabel->setText(Formatter::uploadSpeedToString(stats.speed_up));
ui_.uploadSpeedLabel->setText(Formatter::get().uploadSpeedToString(stats.speed_up));
ui_.uploadSpeedLabel->setVisible(stats.peers_sending || stats.peers_receiving);
ui_.downloadSpeedLabel->setText(Formatter::downloadSpeedToString(stats.speed_down));
ui_.downloadSpeedLabel->setText(Formatter::get().downloadSpeedToString(stats.speed_down));
ui_.downloadSpeedLabel->setVisible(stats.peers_sending);
ui_.networkLabel->setVisible(!session_.isServer());
@ -809,30 +806,30 @@ void MainWindow::refreshStatusBar(TransferStats const& stats)
auto const mode = prefs_.getString(Prefs::STATUSBAR_STATS);
auto str = QString {};
if (mode == SessionRatioStatsModeName)
if (mode == session_ratio_stats_mode_name_)
{
str = tr("Ratio: %1")
.arg(Formatter::ratioToString(session_.getStats().ratio));
.arg(Formatter::get().ratioToString(session_.getStats().ratio));
}
else if (mode == SessionTransferStatsModeName)
else if (mode == session_transfer_stats_mode_name_)
{
auto const& st = session_.getStats();
str = tr("Down: %1, Up: %2")
.arg(Formatter::sizeToString(st.downloadedBytes))
.arg(Formatter::sizeToString(st.uploadedBytes));
.arg(Formatter::get().sizeToString(st.downloadedBytes))
.arg(Formatter::get().sizeToString(st.uploadedBytes));
}
else if (mode == TotalTransferStatsModeName)
else if (mode == total_transfer_stats_mode_name_)
{
auto const& st = session_.getCumulativeStats();
str = tr("Down: %1, Up: %2")
.arg(Formatter::sizeToString(st.downloadedBytes))
.arg(Formatter::sizeToString(st.uploadedBytes));
.arg(Formatter::get().sizeToString(st.downloadedBytes))
.arg(Formatter::get().sizeToString(st.uploadedBytes));
}
else // default is "total-ratio"
{
assert(mode == TotalRatioStatsModeName);
assert(mode == total_ratio_stats_mode_name_);
str = tr("Ratio: %1")
.arg(Formatter::ratioToString(session_.getCumulativeStats().ratio));
.arg(Formatter::get().ratioToString(session_.getCumulativeStats().ratio));
}
ui_.statsLabel->setText(str);
@ -1149,7 +1146,8 @@ void MainWindow::refreshPref(int key)
break;
case Prefs::DSPEED:
dlimit_on_action_->setText(tr("Limited at %1").arg(Formatter::speedToString(Speed::fromKBps(prefs_.get<int>(key)))));
dlimit_on_action_->setText(tr("Limited at %1").arg(Formatter::get().speedToString(Speed::fromKBps(prefs_.get<int>(
key)))));
break;
case Prefs::USPEED_ENABLED:
@ -1157,7 +1155,8 @@ void MainWindow::refreshPref(int key)
break;
case Prefs::USPEED:
ulimit_on_action_->setText(tr("Limited at %1").arg(Formatter::speedToString(Speed::fromKBps(prefs_.get<int>(key)))));
ulimit_on_action_->setText(tr("Limited at %1").arg(Formatter::get().speedToString(Speed::fromKBps(prefs_.get<int>(
key)))));
break;
case Prefs::RATIO_ENABLED:
@ -1165,7 +1164,7 @@ void MainWindow::refreshPref(int key)
break;
case Prefs::RATIO:
ratio_on_action_->setText(tr("Stop at Ratio (%1)").arg(Formatter::ratioToString(prefs_.get<double>(key))));
ratio_on_action_->setText(tr("Stop at Ratio (%1)").arg(Formatter::get().ratioToString(prefs_.get<double>(key))));
break;
case Prefs::FILTERBAR:
@ -1239,7 +1238,7 @@ void MainWindow::refreshPref(int key)
tr("Click to enable Temporary Speed Limits\n (%1 down, %2 up)");
Speed const d = Speed::fromKBps(prefs_.getInt(Prefs::ALT_SPEED_LIMIT_DOWN));
Speed const u = Speed::fromKBps(prefs_.getInt(Prefs::ALT_SPEED_LIMIT_UP));
ui_.altSpeedButton->setToolTip(fmt.arg(Formatter::speedToString(d)).arg(Formatter::speedToString(u)));
ui_.altSpeedButton->setToolTip(fmt.arg(Formatter::get().speedToString(d)).arg(Formatter::get().speedToString(u)));
break;
}
@ -1252,13 +1251,6 @@ void MainWindow::refreshPref(int key)
****
***/
namespace
{
auto const ShowOptionsCheckboxName = QStringLiteral("show-options-checkbox");
} // namespace
void MainWindow::newTorrent()
{
auto* dialog = new MakeDialog(session_, this);
@ -1280,7 +1272,7 @@ void MainWindow::openTorrent()
{
auto* b = new QCheckBox(tr("Show &options dialog"));
b->setChecked(prefs_.getBool(Prefs::OPTIONS_PROMPT));
b->setObjectName(ShowOptionsCheckboxName);
b->setObjectName(show_options_checkbox_name_);
l->addWidget(b, l->rowCount(), 0, 1, -1, Qt::AlignLeft);
}
@ -1314,7 +1306,7 @@ void MainWindow::addTorrents(QStringList const& filenames)
if (file_dialog != nullptr)
{
auto const* const b = file_dialog->findChild<QCheckBox const*>(ShowOptionsCheckboxName);
auto const* const b = file_dialog->findChild<QCheckBox const*>(show_options_checkbox_name_);
if (b != nullptr)
{
@ -1499,7 +1491,7 @@ void MainWindow::updateNetworkIcon()
}
else if (seconds_since_last_read < 60 * 2)
{
tip = tr("%1 last responded %2 ago").arg(url).arg(Formatter::timeToString(seconds_since_last_read));
tip = tr("%1 last responded %2 ago").arg(url).arg(Formatter::get().timeToString(seconds_since_last_read));
}
else
{

View File

@ -180,6 +180,13 @@ private:
QAction* alt_speed_action_ = {};
QString error_message_;
QString const total_ratio_stats_mode_name_;
QString const total_transfer_stats_mode_name_;
QString const session_ratio_stats_mode_name_;
QString const session_transfer_stats_mode_name_;
QString const show_options_checkbox_name_;
struct TransferStats
{
Speed speed_up;

View File

@ -221,8 +221,8 @@ void MakeDialog::onSourceChanged()
{
QString files = tr("%Ln File(s)", nullptr, builder_->fileCount);
QString pieces = tr("%Ln Piece(s)", nullptr, builder_->pieceCount);
text = tr("%1 in %2; %3 @ %4").arg(Formatter::sizeToString(builder_->totalSize)).arg(files).arg(pieces).
arg(Formatter::sizeToString(builder_->pieceSize));
text = tr("%1 in %2; %3 @ %4").arg(Formatter::get().sizeToString(builder_->totalSize)).arg(files).arg(pieces).
arg(Formatter::get().sizeToString(builder_->pieceSize));
}
ui_.sourceSizeLabel->setText(text);

View File

@ -130,12 +130,33 @@ std::array<Prefs::PrefItem, Prefs::PREFS_COUNT> const Prefs::Items
***/
Prefs::Prefs(QString config_dir) :
config_dir_(std::move(config_dir))
config_dir_(std::move(config_dir)),
FilterModes{
std::make_pair(FilterMode::SHOW_ALL, QStringLiteral("show-all")),
std::make_pair(FilterMode::SHOW_ACTIVE, QStringLiteral("show-active")),
std::make_pair(FilterMode::SHOW_DOWNLOADING, QStringLiteral("show-downloading")),
std::make_pair(FilterMode::SHOW_SEEDING, QStringLiteral("show-seeding")),
std::make_pair(FilterMode::SHOW_PAUSED, QStringLiteral("show-paused")),
std::make_pair(FilterMode::SHOW_FINISHED, QStringLiteral("show-finished")),
std::make_pair(FilterMode::SHOW_VERIFYING, QStringLiteral("show-verifying")),
std::make_pair(FilterMode::SHOW_ERROR, QStringLiteral("show-error"))
},
SortModes{
std::make_pair(SortMode::SORT_BY_NAME, QStringLiteral("sort-by-name")),
std::make_pair(SortMode::SORT_BY_ACTIVITY, QStringLiteral("sort-by-activity")),
std::make_pair(SortMode::SORT_BY_AGE, QStringLiteral("sort-by-age")),
std::make_pair(SortMode::SORT_BY_ETA, QStringLiteral("sort-by-eta")),
std::make_pair(SortMode::SORT_BY_PROGRESS, QStringLiteral("sort-by-progress")),
std::make_pair(SortMode::SORT_BY_QUEUE, QStringLiteral("sort-by-queue")),
std::make_pair(SortMode::SORT_BY_RATIO, QStringLiteral("sort-by-ratio")),
std::make_pair(SortMode::SORT_BY_SIZE, QStringLiteral("sort-by-size")),
std::make_pair(SortMode::SORT_BY_STATE, QStringLiteral("sort-by-state")),
std::make_pair(SortMode::SORT_BY_ID, QStringLiteral("sort-by-id"))
}
{
static_assert(sizeof(Items) / sizeof(Items[0]) == PREFS_COUNT);
#ifndef NDEBUG
for (int i = 0; i < PREFS_COUNT; ++i)
{
assert(Items[i].id == i);
@ -173,7 +194,10 @@ Prefs::Prefs(QString config_dir) :
auto const value = getValue<QString>(b);
if (value)
{
values_[i] = QVariant::fromValue(SortMode(*value));
auto test = [&value](auto const& item) { return item.second == *value; };
auto it = std::find_if(std::cbegin(SortModes), std::cend(SortModes), test);
auto const& pair = it == std::end(SortModes) ? SortModes[0] : *it;
values_[i] = QVariant::fromValue(SortMode(pair.first));
}
}
break;
@ -183,7 +207,10 @@ Prefs::Prefs(QString config_dir) :
auto const value = getValue<QString>(b);
if (value)
{
values_[i] = QVariant::fromValue(FilterMode(*value));
auto test = [&value](auto const& item) { return item.second == *value; };
auto it = std::find_if(std::cbegin(FilterModes), std::cend(FilterModes), test);
auto const& pair = it == std::end(FilterModes) ? FilterModes[0] : *it;
values_[i] = QVariant::fromValue(FilterMode(pair.first));
}
}
break;
@ -260,12 +287,24 @@ Prefs::~Prefs()
break;
case CustomVariantType::SortModeType:
dictAdd(&current_settings, key, val.value<SortMode>().name());
break;
{
auto const mode = val.value<SortMode>().mode();
auto test = [&mode](auto const& item) { return item.first == mode; };
auto it = std::find_if(std::cbegin(SortModes), std::cend(SortModes), test);
auto const& pair = it == std::end(SortModes) ? SortModes[0] : *it;
dictAdd(&current_settings, key, pair.second);
break;
}
case CustomVariantType::FilterModeType:
dictAdd(&current_settings, key, val.value<FilterMode>().name());
break;
{
auto const mode = val.value<FilterMode>().mode();
auto test = [&mode](auto const& item) { return item.first == mode; };
auto it = std::find_if(std::cbegin(FilterModes), std::cend(FilterModes), test);
auto const& pair = it == std::end(FilterModes) ? FilterModes[0] : *it;
dictAdd(&current_settings, key, pair.second);
break;
}
case QVariant::String:
dictAdd(&current_settings, key, val.toString());

View File

@ -204,6 +204,8 @@ private:
void set(int key, char const* value) = delete;
QString const config_dir_;
std::array<std::pair<int, QString>, FilterMode::NUM_MODES> const FilterModes;
std::array<std::pair<int, QString>, SortMode::NUM_MODES> const SortModes;
QSet<int> temporary_prefs_;
QVariant mutable values_[PREFS_COUNT];

View File

@ -324,7 +324,7 @@ void PrefsDialog::altSpeedDaysEdited(int i)
void PrefsDialog::initSpeedTab()
{
QString const speed_unit_str = Formatter::unitStr(Formatter::SPEED, Formatter::KB);
QString const speed_unit_str = Formatter::get().unitStr(Formatter::get().SPEED, Formatter::get().KB);
auto const suffix = QStringLiteral(" %1").arg(speed_unit_str);
QLocale const locale;

View File

@ -8,8 +8,11 @@
#include "Session.h"
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <string_view>
#include <QApplication>
#include <QByteArray>
@ -46,19 +49,6 @@ using ::trqt::variant_helpers::listAdd;
****
***/
namespace
{
using KeyList = Torrent::KeyList;
// If this object is passed as "ids" (compared by address), then recently active torrents are queried.
auto const RecentlyActiveIDs = torrent_ids_t{ -1 };
// If this object is passed as "ids" (compared by being empty), then all torrents are queried.
auto const AllIDs = torrent_ids_t{};
} // namespace
void Session::sessionSet(tr_quark const key, QVariant const& value)
{
tr_variant args;
@ -120,11 +110,12 @@ void Session::portTest()
void Session::copyMagnetLinkToClipboard(int torrent_id)
{
auto constexpr MagnetLinkKey = std::string_view { "magnetLink" };
auto constexpr Fields = std::array<std::string_view, 1>{ MagnetLinkKey };
tr_variant args;
tr_variantInitDict(&args, 2);
dictAdd(&args, TR_KEY_ids, std::array<int, 1>{ torrent_id });
dictAdd(&args, TR_KEY_fields, std::array<std::string_view, 1>{ MagnetLinkKey });
dictAdd(&args, TR_KEY_fields, Fields);
auto* q = new RpcQueue();
@ -263,7 +254,7 @@ void Session::updatePref(int key)
case Prefs::RPC_PORT:
if (session_ != nullptr)
{
tr_sessionSetRPCPort(session_, prefs_.getInt(key));
tr_sessionSetRPCPort(session_, static_cast<tr_port>(prefs_.getInt(key)));
}
break;
@ -405,10 +396,7 @@ bool Session::isLocal() const
****
***/
namespace
{
void addOptionalIds(tr_variant* args, torrent_ids_t const& ids)
void Session::addOptionalIds(tr_variant* args, torrent_ids_t const& ids)
{
auto constexpr RecentlyActiveKey = std::string_view { "recently-active" };
@ -422,8 +410,6 @@ void addOptionalIds(tr_variant* args, torrent_ids_t const& ids)
}
}
} // namespace
Session::Tag Session::torrentSetImpl(tr_variant* args)
{
auto* const q = new RpcQueue();
@ -541,30 +527,151 @@ void Session::torrentRenamePath(torrent_ids_t const& ids, QString const& oldpath
q->add([this, ids](RpcResponse const& /*r*/)
{
refreshTorrents(ids, { TR_KEY_fileStats, TR_KEY_files, TR_KEY_id, TR_KEY_name });
refreshTorrents(ids, TorrentProperties::Rename);
});
q->run();
}
void Session::refreshTorrents(torrent_ids_t const& ids, KeyList const& keys)
std::vector<std::string_view> const& Session::getKeyNames(TorrentProperties props)
{
auto constexpr TableKey = std::string_view { "table" };
std::vector<std::string_view>& names = names_[props];
if (names.empty())
{
// unchanging fields needed by the main window
static auto constexpr MainInfoKeys = std::array<tr_quark, 6>{
TR_KEY_addedDate,
TR_KEY_downloadDir,
TR_KEY_hashString,
TR_KEY_name,
TR_KEY_totalSize,
TR_KEY_trackers,
};
// changing fields needed by the main window
static auto constexpr MainStatKeys = std::array<tr_quark, 25>{
TR_KEY_downloadedEver,
TR_KEY_editDate,
TR_KEY_error,
TR_KEY_errorString,
TR_KEY_eta,
TR_KEY_haveUnchecked,
TR_KEY_haveValid,
TR_KEY_isFinished,
TR_KEY_leftUntilDone,
TR_KEY_manualAnnounceTime,
TR_KEY_metadataPercentComplete,
TR_KEY_peersConnected,
TR_KEY_peersGettingFromUs,
TR_KEY_peersSendingToUs,
TR_KEY_percentDone,
TR_KEY_queuePosition,
TR_KEY_rateDownload,
TR_KEY_rateUpload,
TR_KEY_recheckProgress,
TR_KEY_seedRatioLimit,
TR_KEY_seedRatioMode,
TR_KEY_sizeWhenDone,
TR_KEY_status,
TR_KEY_uploadedEver,
TR_KEY_webseedsSendingToUs
};
// unchanging fields needed by the details dialog
static auto constexpr DetailInfoKeys = std::array<tr_quark, 8>{
TR_KEY_comment,
TR_KEY_creator,
TR_KEY_dateCreated,
TR_KEY_files,
TR_KEY_isPrivate,
TR_KEY_pieceCount,
TR_KEY_pieceSize,
TR_KEY_trackers
};
// changing fields needed by the details dialog
static auto constexpr DetailStatKeys = std::array<tr_quark, 17>{
TR_KEY_activityDate,
TR_KEY_bandwidthPriority,
TR_KEY_corruptEver,
TR_KEY_desiredAvailable,
TR_KEY_downloadedEver,
TR_KEY_downloadLimit,
TR_KEY_downloadLimited,
TR_KEY_fileStats,
TR_KEY_honorsSessionLimits,
TR_KEY_peer_limit,
TR_KEY_peers,
TR_KEY_seedIdleLimit,
TR_KEY_seedIdleMode,
TR_KEY_startDate,
TR_KEY_trackerStats,
TR_KEY_uploadLimit,
TR_KEY_uploadLimited
};
// keys needed after renaming a torrent
static auto constexpr RenameKeys = std::array<tr_quark, 3>{
TR_KEY_fileStats,
TR_KEY_files,
TR_KEY_name
};
auto const append = [&names](tr_quark key)
{
size_t len = {};
char const* str = tr_quark_get_string(key, &len);
names.emplace_back(str, len);
};
switch (props)
{
case TorrentProperties::DetailInfo:
std::for_each(DetailInfoKeys.begin(), DetailInfoKeys.end(), append);
break;
case TorrentProperties::DetailStat:
std::for_each(DetailStatKeys.begin(), DetailStatKeys.end(), append);
break;
case TorrentProperties::MainAll:
std::for_each(MainInfoKeys.begin(), MainInfoKeys.end(), append);
std::for_each(MainStatKeys.begin(), MainStatKeys.end(), append);
break;
case TorrentProperties::MainInfo:
std::for_each(MainInfoKeys.begin(), MainInfoKeys.end(), append);
break;
case TorrentProperties::MainStats:
std::for_each(MainStatKeys.begin(), MainStatKeys.end(), append);
break;
case TorrentProperties::Rename:
std::for_each(RenameKeys.begin(), RenameKeys.end(), append);
break;
}
// must be in every torrent req
append(TR_KEY_id);
// sort and remove dupes
std::sort(names.begin(), names.end());
names.erase(std::unique(names.begin(), names.end()), names.end());
}
return names;
}
void Session::refreshTorrents(torrent_ids_t const& ids, TorrentProperties props)
{
auto constexpr Table = std::string_view{ "table" };
tr_variant args;
tr_variantInitDict(&args, 3);
dictAdd(&args, TR_KEY_format, TableKey);
std::vector<std::string_view> keystrs;
keystrs.reserve(keys.size());
for (auto const& key : keys)
{
auto len = size_t{};
auto const* str = tr_quark_get_string(key, &len);
keystrs.emplace_back(str, len);
}
dictAdd(&args, TR_KEY_fields, keystrs);
dictAdd(&args, TR_KEY_format, Table);
dictAdd(&args, TR_KEY_fields, getKeyNames(props));
addOptionalIds(&args, ids);
auto* q = new RpcQueue();
@ -596,12 +703,12 @@ void Session::refreshTorrents(torrent_ids_t const& ids, KeyList const& keys)
void Session::refreshDetailInfo(torrent_ids_t const& ids)
{
refreshTorrents(ids, Torrent::DetailInfoKeys);
refreshTorrents(ids, TorrentProperties::DetailInfo);
}
void Session::refreshExtraStats(torrent_ids_t const& ids)
{
refreshTorrents(ids, Torrent::MainStatKeys + Torrent::DetailStatKeys);
refreshTorrents(ids, TorrentProperties::DetailStat);
}
void Session::sendTorrentRequest(std::string_view request, torrent_ids_t const& ids)
@ -619,7 +726,7 @@ void Session::sendTorrentRequest(std::string_view request, torrent_ids_t const&
q->add([this, ids]()
{
refreshTorrents(ids, Torrent::MainStatKeys);
refreshTorrents(ids, TorrentProperties::MainStats);
});
q->run();
@ -662,17 +769,20 @@ void Session::queueMoveBottom(torrent_ids_t const& ids)
void Session::refreshActiveTorrents()
{
refreshTorrents(RecentlyActiveIDs, Torrent::MainStatKeys);
// If this object is passed as "ids" (compared by address), then recently active torrents are queried.
refreshTorrents(RecentlyActiveIDs, TorrentProperties::MainStats);
}
void Session::refreshAllTorrents()
{
refreshTorrents(AllIDs, Torrent::MainStatKeys);
// if an empty ids object is used, all torrents are queried.
torrent_ids_t const ids = {};
refreshTorrents(ids, TorrentProperties::MainStats);
}
void Session::initTorrents(torrent_ids_t const& ids)
{
refreshTorrents(ids, Torrent::AllMainKeys);
refreshTorrents(ids, TorrentProperties::MainAll);
}
void Session::refreshSessionStats()
@ -772,7 +882,7 @@ void Session::updateStats(tr_variant* d, tr_session_stats* stats)
stats->secondsActive = *value;
}
stats->ratio = tr_getRatio(stats->uploadedBytes, stats->downloadedBytes);
stats->ratio = static_cast<float>(tr_getRatio(stats->uploadedBytes, stats->downloadedBytes));
}
void Session::updateStats(tr_variant* d)

View File

@ -8,7 +8,9 @@
#pragma once
#include <map>
#include <string_view>
#include <vector>
#include <QObject>
#include <QString>
@ -105,6 +107,16 @@ public:
void reannounceTorrents(torrent_ids_t const& torrent_ids);
void refreshExtraStats(torrent_ids_t const& ids);
enum class TorrentProperties
{
MainInfo,
MainStats,
MainAll,
DetailInfo,
DetailStat,
Rename
};
public slots:
void addTorrent(AddData const& addme);
void launchWebInterface();
@ -141,14 +153,19 @@ private:
void sessionSet(tr_quark const key, QVariant const& variant);
void pumpRequests();
void sendTorrentRequest(std::string_view request, torrent_ids_t const& torrent_ids);
void refreshTorrents(torrent_ids_t const& torrent_ids, Torrent::KeyList const& keys);
void refreshTorrents(torrent_ids_t const& ids, TorrentProperties props);
std::vector<std::string_view> const& getKeyNames(TorrentProperties props);
static void updateStats(tr_variant* d, tr_session_stats* stats);
void addOptionalIds(tr_variant* args, torrent_ids_t const& ids);
private:
QString const config_dir_;
Prefs& prefs_;
std::map<TorrentProperties, std::vector<std::string_view>> names_;
int64_t blocklist_size_ = -1;
tr_session* session_ = {};
QStringList idle_json_;
@ -158,4 +175,5 @@ private:
QString session_id_;
bool is_definitely_local_session_ = true;
RpcClient rpc_;
torrent_ids_t const RecentlyActiveIDs = { -1 };
};

View File

@ -55,15 +55,15 @@ void StatsDialog::updateStats()
tr_session_stats const& current(session_.getStats());
tr_session_stats const& total(session_.getCumulativeStats());
ui_.currentUploadedValueLabel->setText(Formatter::sizeToString(current.uploadedBytes));
ui_.currentDownloadedValueLabel->setText(Formatter::sizeToString(current.downloadedBytes));
ui_.currentRatioValueLabel->setText(Formatter::ratioToString(current.ratio));
ui_.currentDurationValueLabel->setText(Formatter::timeToString(current.secondsActive));
ui_.currentUploadedValueLabel->setText(Formatter::get().sizeToString(current.uploadedBytes));
ui_.currentDownloadedValueLabel->setText(Formatter::get().sizeToString(current.downloadedBytes));
ui_.currentRatioValueLabel->setText(Formatter::get().ratioToString(current.ratio));
ui_.currentDurationValueLabel->setText(Formatter::get().timeToString(current.secondsActive));
ui_.totalUploadedValueLabel->setText(Formatter::sizeToString(total.uploadedBytes));
ui_.totalDownloadedValueLabel->setText(Formatter::sizeToString(total.downloadedBytes));
ui_.totalRatioValueLabel->setText(Formatter::ratioToString(total.ratio));
ui_.totalDurationValueLabel->setText(Formatter::timeToString(total.secondsActive));
ui_.totalUploadedValueLabel->setText(Formatter::get().sizeToString(total.uploadedBytes));
ui_.totalDownloadedValueLabel->setText(Formatter::get().sizeToString(total.downloadedBytes));
ui_.totalRatioValueLabel->setText(Formatter::get().ratioToString(total.ratio));
ui_.totalDurationValueLabel->setText(Formatter::get().timeToString(total.secondsActive));
ui_.startCountLabel->setText(tr("Started %Ln time(s)", nullptr, total.sessionCount));
}

View File

@ -19,6 +19,7 @@
#include <libtransmission/variant.h>
#include "Application.h"
#include "IconCache.h"
#include "Prefs.h"
#include "Torrent.h"
#include "Utils.h"
@ -26,95 +27,9 @@
using ::trqt::variant_helpers::change;
/***
****
***/
// unchanging fields needed by the main window
Torrent::KeyList const Torrent::MainInfoKeys{
TR_KEY_addedDate,
TR_KEY_downloadDir,
TR_KEY_hashString,
TR_KEY_id, // must be in every req
TR_KEY_name,
TR_KEY_totalSize,
TR_KEY_trackers,
};
// changing fields needed by the main window
Torrent::KeyList const Torrent::MainStatKeys{
TR_KEY_downloadedEver,
TR_KEY_editDate,
TR_KEY_error,
TR_KEY_errorString,
TR_KEY_eta,
TR_KEY_haveUnchecked,
TR_KEY_haveValid,
TR_KEY_id, // must be in every req
TR_KEY_isFinished,
TR_KEY_leftUntilDone,
TR_KEY_manualAnnounceTime,
TR_KEY_metadataPercentComplete,
TR_KEY_peersConnected,
TR_KEY_peersGettingFromUs,
TR_KEY_peersSendingToUs,
TR_KEY_percentDone,
TR_KEY_queuePosition,
TR_KEY_rateDownload,
TR_KEY_rateUpload,
TR_KEY_recheckProgress,
TR_KEY_seedRatioLimit,
TR_KEY_seedRatioMode,
TR_KEY_sizeWhenDone,
TR_KEY_status,
TR_KEY_uploadedEver,
TR_KEY_webseedsSendingToUs
};
Torrent::KeyList const Torrent::AllMainKeys = Torrent::MainInfoKeys + Torrent::MainStatKeys;
// unchanging fields needed by the details dialog
Torrent::KeyList const Torrent::DetailInfoKeys{
TR_KEY_comment,
TR_KEY_creator,
TR_KEY_dateCreated,
TR_KEY_files,
TR_KEY_id, // must be in every req
TR_KEY_isPrivate,
TR_KEY_pieceCount,
TR_KEY_pieceSize,
TR_KEY_trackers
};
// changing fields needed by the details dialog
Torrent::KeyList const Torrent::DetailStatKeys{
TR_KEY_activityDate,
TR_KEY_bandwidthPriority,
TR_KEY_corruptEver,
TR_KEY_desiredAvailable,
TR_KEY_downloadedEver,
TR_KEY_downloadLimit,
TR_KEY_downloadLimited,
TR_KEY_fileStats,
TR_KEY_honorsSessionLimits,
TR_KEY_id, // must be in every req
TR_KEY_peer_limit,
TR_KEY_peers,
TR_KEY_seedIdleLimit,
TR_KEY_seedIdleMode,
TR_KEY_startDate,
TR_KEY_trackerStats,
TR_KEY_uploadLimit,
TR_KEY_uploadLimited
};
/***
****
***/
Torrent::Torrent(Prefs const& prefs, int id) :
id_(id),
icon_(Utils::getFileIcon()),
icon_(IconCache::get().fileIcon()),
prefs_(prefs)
{
}
@ -248,20 +163,21 @@ int Torrent::compareETA(Torrent const& that) const
void Torrent::updateMimeIcon()
{
auto const& files = files_;
auto const& icon_cache = IconCache::get();
QIcon icon;
if (files.size() > 1)
{
icon = Utils::getFolderIcon();
icon = icon_cache.folderIcon();
}
else if (files.size() == 1)
{
icon = Utils::guessMimeIcon(files.at(0).filename);
icon = icon_cache.guessMimeIcon(files.at(0).filename);
}
else
{
icon = Utils::guessMimeIcon(name());
icon = icon_cache.guessMimeIcon(name());
}
icon_ = icon;

View File

@ -503,13 +503,6 @@ public:
return icon_;
}
using KeyList = QSet<tr_quark>;
static KeyList const AllMainKeys;
static KeyList const DetailInfoKeys;
static KeyList const DetailStatKeys;
static KeyList const MainInfoKeys;
static KeyList const MainStatKeys;
enum Field
{
ACTIVITY_DATE,

View File

@ -168,7 +168,7 @@ QString TorrentDelegate::progressString(Torrent const& tor)
//: First part of torrent progress string;
//: %1 is the percentage of torrent metadata downloaded
str = tr("Magnetized transfer - retrieving metadata (%1%)").
arg(Formatter::percentToString(tor.metadataPercentDone() * 100.0));
arg(Formatter::get().percentToString(tor.metadataPercentDone() * 100.0));
}
else if (!is_done) // downloading
{
@ -176,8 +176,10 @@ QString TorrentDelegate::progressString(Torrent const& tor)
//: %1 is how much we've got,
//: %2 is how much we'll have when done,
//: %3 is a percentage of the two
str = tr("%1 of %2 (%3%)").arg(Formatter::sizeToString(have_total)).arg(Formatter::sizeToString(tor.sizeWhenDone())).
arg(Formatter::percentToString(tor.percentDone() * 100.0));
str =
tr("%1 of %2 (%3%)").arg(Formatter::get().sizeToString(have_total)).arg(Formatter::get().sizeToString(
tor.sizeWhenDone())).
arg(Formatter::get().percentToString(tor.percentDone() * 100.0));
}
else if (!is_seed) // partial seed
{
@ -190,11 +192,11 @@ QString TorrentDelegate::progressString(Torrent const& tor)
//: %4 is how much we've uploaded,
//: %5 is our upload-to-download ratio,
//: %6 is the ratio we want to reach before we stop uploading
str = tr("%1 of %2 (%3%), uploaded %4 (Ratio: %5 Goal: %6)").arg(Formatter::sizeToString(have_total)).
arg(Formatter::sizeToString(tor.totalSize())).
arg(Formatter::percentToString(tor.percentComplete() * 100.0)).
arg(Formatter::sizeToString(tor.uploadedEver())).arg(Formatter::ratioToString(tor.ratio())).
arg(Formatter::ratioToString(seed_ratio));
str = tr("%1 of %2 (%3%), uploaded %4 (Ratio: %5 Goal: %6)").arg(Formatter::get().sizeToString(have_total)).
arg(Formatter::get().sizeToString(tor.totalSize())).
arg(Formatter::get().percentToString(tor.percentComplete() * 100.0)).
arg(Formatter::get().sizeToString(tor.uploadedEver())).arg(Formatter::get().ratioToString(tor.ratio())).
arg(Formatter::get().ratioToString(seed_ratio));
}
else
{
@ -204,10 +206,10 @@ QString TorrentDelegate::progressString(Torrent const& tor)
//: %3 is a percentage of the two,
//: %4 is how much we've uploaded,
//: %5 is our upload-to-download ratio
str = tr("%1 of %2 (%3%), uploaded %4 (Ratio: %5)").arg(Formatter::sizeToString(have_total)).
arg(Formatter::sizeToString(tor.totalSize())).
arg(Formatter::percentToString(tor.percentComplete() * 100.0)).
arg(Formatter::sizeToString(tor.uploadedEver())).arg(Formatter::ratioToString(tor.ratio()));
str = tr("%1 of %2 (%3%), uploaded %4 (Ratio: %5)").arg(Formatter::get().sizeToString(have_total)).
arg(Formatter::get().sizeToString(tor.totalSize())).
arg(Formatter::get().percentToString(tor.percentComplete() * 100.0)).
arg(Formatter::get().sizeToString(tor.uploadedEver())).arg(Formatter::get().ratioToString(tor.ratio()));
}
}
else // seeding
@ -219,9 +221,9 @@ QString TorrentDelegate::progressString(Torrent const& tor)
//: %2 is how much we've uploaded,
//: %3 is our upload-to-download ratio,
//: %4 is the ratio we want to reach before we stop uploading
str = tr("%1, uploaded %2 (Ratio: %3 Goal: %4)").arg(Formatter::sizeToString(have_total)).
arg(Formatter::sizeToString(tor.uploadedEver())).arg(Formatter::ratioToString(tor.ratio())).
arg(Formatter::ratioToString(seed_ratio));
str = tr("%1, uploaded %2 (Ratio: %3 Goal: %4)").arg(Formatter::get().sizeToString(have_total)).
arg(Formatter::get().sizeToString(tor.uploadedEver())).arg(Formatter::get().ratioToString(tor.ratio())).
arg(Formatter::get().ratioToString(seed_ratio));
}
else // seeding w/o a ratio
{
@ -229,8 +231,8 @@ QString TorrentDelegate::progressString(Torrent const& tor)
//: %1 is the torrent's total size,
//: %2 is how much we've uploaded,
//: %3 is our upload-to-download ratio
str = tr("%1, uploaded %2 (Ratio: %3)").arg(Formatter::sizeToString(have_total)).
arg(Formatter::sizeToString(tor.uploadedEver())).arg(Formatter::ratioToString(tor.ratio()));
str = tr("%1, uploaded %2 (Ratio: %3)").arg(Formatter::get().sizeToString(have_total)).
arg(Formatter::get().sizeToString(tor.uploadedEver())).arg(Formatter::get().ratioToString(tor.ratio()));
}
}
@ -242,7 +244,7 @@ QString TorrentDelegate::progressString(Torrent const& tor)
//: Second (optional) part of torrent progress string;
//: %1 is duration;
//: notice that leading space (before the dash) is included here
str += tr(" - %1 left").arg(Formatter::timeToString(tor.getETA()));
str += tr(" - %1 left").arg(Formatter::get().timeToString(tor.getETA()));
}
else
{
@ -264,12 +266,12 @@ QString TorrentDelegate::shortTransferString(Torrent const& tor)
if (have_down)
{
str = Formatter::downloadSpeedToString(tor.downloadSpeed()) + QStringLiteral(" ") +
Formatter::uploadSpeedToString(tor.uploadSpeed());
str = Formatter::get().downloadSpeedToString(tor.downloadSpeed()) + QStringLiteral(" ") +
Formatter::get().uploadSpeedToString(tor.uploadSpeed());
}
else if (have_up)
{
str = Formatter::uploadSpeedToString(tor.uploadSpeed());
str = Formatter::get().uploadSpeedToString(tor.uploadSpeed());
}
return str.trimmed();
@ -282,12 +284,13 @@ QString TorrentDelegate::shortStatusString(Torrent const& tor)
switch (tor.getActivity())
{
case TR_STATUS_CHECK:
str = tr("Verifying local data (%1% tested)").arg(Formatter::percentToString(tor.getVerifyProgress() * 100.0));
str = tr("Verifying local data (%1% tested)").arg(Formatter::get().percentToString(tor.getVerifyProgress() * 100.0));
break;
case TR_STATUS_DOWNLOAD:
case TR_STATUS_SEED:
str = shortTransferString(tor) + QStringLiteral(" ") + tr("Ratio: %1").arg(Formatter::ratioToString(tor.ratio()));
str = shortTransferString(tor) + QStringLiteral(" ") + tr("Ratio: %1").arg(Formatter::get().ratioToString(
tor.ratio()));
break;
default:
@ -322,7 +325,7 @@ QString TorrentDelegate::statusString(Torrent const& tor)
if (!tor.hasMetadata())
{
str = tr("Downloading metadata from %Ln peer(s) (%1% done)", nullptr, tor.peersWeAreDownloadingFrom()).
arg(Formatter::percentToString(100.0 * tor.metadataPercentDone()));
arg(Formatter::get().percentToString(100.0 * tor.metadataPercentDone()));
}
else
{

View File

@ -146,7 +146,7 @@ bool TorrentFilter::lessThan(QModelIndex const& left, QModelIndex const& right)
b->peersWeAreUploadingTo() + b->webseedsWeAreDownloadingFrom());
}
// fall through
[[fallthrough]];
case SortMode::SORT_BY_STATE:
if (val == 0)
@ -169,7 +169,7 @@ bool TorrentFilter::lessThan(QModelIndex const& left, QModelIndex const& right)
val = compare(a->hasError(), b->hasError());
}
// fall through
[[fallthrough]];
case SortMode::SORT_BY_PROGRESS:
if (val == 0)
@ -192,7 +192,7 @@ bool TorrentFilter::lessThan(QModelIndex const& left, QModelIndex const& right)
val = -compare(a->queuePosition(), b->queuePosition());
}
// fall through
[[fallthrough]];
case SortMode::SORT_BY_RATIO:
if (val == 0)

View File

@ -166,7 +166,7 @@ QString timeToStringRounded(int seconds)
seconds -= seconds % 60;
}
return Formatter::timeToString(seconds);
return Formatter::get().timeToString(seconds);
}
} // namespace

View File

@ -52,6 +52,7 @@ bool isSlashChar(QChar const& c)
return c == QLatin1Char('/') || c == QLatin1Char('\\');
}
#if 0
QIcon folderIcon()
{
static QIcon icon;
@ -156,10 +157,12 @@ QIcon getMimeIcon(QString const& filename)
return icon;
}
#endif
#endif
} // namespace
#if 0
QIcon Utils::getFolderIcon()
{
return folderIcon();
@ -194,6 +197,8 @@ QIcon Utils::guessMimeIcon(QString const& filename)
#endif
}
#endif
QIcon Utils::getIconFromIndex(QModelIndex const& index)
{
QVariant const variant = index.data(Qt::DecorationRole);

View File

@ -9,7 +9,9 @@
#pragma once
#include <cctype> // isxdigit()
#include <functional>
#include <QHash>
#include <QPointer>
#include <QRect>
#include <QString>
@ -20,12 +22,27 @@ class QHeaderView;
class QIcon;
class QModelIndex;
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
namespace std
{
template<>
struct hash<QString>
{
std::size_t operator ()(QString const& s) const
{
return qHash(s);
}
};
} // namespace std
#endif
class Utils
{
public:
static QIcon getFileIcon();
static QIcon getFolderIcon();
static QIcon guessMimeIcon(QString const& filename);
static QIcon getIconFromIndex(QModelIndex const& index);
// Test if string is UTF-8 or not
@ -98,21 +115,3 @@ public:
s.startsWith(QStringLiteral("https://"));
}
};
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
namespace std
{
template<>
struct hash<QString>
{
std::size_t operator ()(QString const& s) const
{
return qHash(s);
}
};
} // namespace std
#endif

View File

@ -8,8 +8,10 @@
#include "VariantHelpers.h"
#include <cmath>
#include <limits>
#include <QUrl>
#include <QtGlobal> // qFuzzyCompare
#include "Application.h" // qApp
#include "Filters.h"
@ -21,7 +23,7 @@ namespace trqt::variant_helpers
bool change(double& setme, double const& value)
{
bool const changed = !qFuzzyCompare(setme + 1, value + 1);
bool const changed = std::fabs(setme - value) > std::numeric_limits<double>::epsilon();
if (changed)
{

View File

@ -91,6 +91,7 @@ SOURCES += AboutDialog.cc \
Filters.cc \
Formatter.cc \
FreeSpaceLabel.cc \
IconCache.cc \
IconToolButton.cc \
InteropHelper.cc \
InteropObject.cc \