Compare commits

...

6 Commits

Author SHA1 Message Date
H5117 29dc11c3d4
Merge 17cb035a44 into 821a6816ef 2024-04-25 11:12:04 +02:00
Pooyan Khanjankhani 821a6816ef
doc: fix typo (#6790) 2024-04-21 18:21:17 -05:00
Dzmitry Neviadomski ef18816b7f
Fix code style script path in CONTRIBUTING.md (#6787)
Signed-off-by: Dzmitry Neviadomski <nevack.d@gmail.com>
2024-04-21 07:36:13 -05:00
Dzmitry Neviadomski 0e25584e78
Make std::hash specialization for tr_socket_address a struct (#6788)
To be in line with std::hash declaration

See https://en.cppreference.com/w/cpp/utility/hash

Signed-off-by: Dzmitry Neviadomski <nevack.d@gmail.com>
2024-04-20 21:01:47 -05:00
Dzmitry Neviadomski bd0b74fccb
Use std::declval instead of nullptr cast trick (#6785)
Signed-off-by: Dzmitry Neviadomski <nevack.d@gmail.com>
2024-04-15 15:18:43 -05:00
Vladimir Stoiakin 17cb035a44 qt: call exec() in the application's constructor 2024-02-20 17:38:04 +03:00
6 changed files with 280 additions and 270 deletions

View File

@ -41,7 +41,7 @@ On macOS, Transmission is usually built with Xcode. Everywhere else, it's CMake
- Prefer `enum class` over `enum` - Prefer `enum class` over `enum`
- Prefer new-style headers, e.g. `<cstring>` over `<string.h>` - Prefer new-style headers, e.g. `<cstring>` over `<string.h>`
- Fix any warnings in new code before merging - Fix any warnings in new code before merging
- Run `./code-style.sh` on your code to ensure the whole codebase has consistent indentation. - Run `./code_style.sh` on your code to ensure the whole codebase has consistent indentation.
Note that Transmission existed in C for over a decade and those idioms don't change overnight. "Follow the C++ core guidelines" can be difficult when working with older code, and the maintainers will understand that when reviewing your PRs. :smiley: Note that Transmission existed in C for over a decade and those idioms don't change overnight. "Follow the C++ core guidelines" can be difficult when working with older code, and the maintainers will understand that when reviewing your PRs. :smiley:

View File

@ -404,7 +404,7 @@ struct tr_socket_address
}; };
template<> template<>
class std::hash<tr_socket_address> struct std::hash<tr_socket_address>
{ {
public: public:
std::size_t operator()(tr_socket_address const& socket_address) const noexcept std::size_t operator()(tr_socket_address const& socket_address) const noexcept

View File

@ -74,7 +74,7 @@ auto constexpr TrUnixSocketPrefix = "unix:"sv;
#ifdef _WIN32 #ifdef _WIN32
auto inline constexpr TrUnixAddrStrLen = size_t{ INET6_ADDRSTRLEN }; auto inline constexpr TrUnixAddrStrLen = size_t{ INET6_ADDRSTRLEN };
#else #else
auto inline constexpr TrUnixAddrStrLen = size_t{ sizeof(((struct sockaddr_un*)nullptr)->sun_path) + auto inline constexpr TrUnixAddrStrLen = size_t{ sizeof(std::declval<struct sockaddr_un>().sun_path) +
std::size(TrUnixSocketPrefix) }; std::size(TrUnixSocketPrefix) };
#endif #endif

View File

@ -46,9 +46,22 @@
#include "TorrentModel.h" #include "TorrentModel.h"
#include "WatchDir.h" #include "WatchDir.h"
#define APP_DISPLAY_NAME "transmission-qt"
#define APP_CONFIG_NAME "transmission"
#ifdef QT_DBUS_LIB
#define FDO_NOTIFICATIONS_SERVICE_NAME "org.freedesktop.Notifications"
#define FDO_NOTIFICATIONS_PATH "/org/freedesktop/Notifications"
#define FDO_NOTIFICATIONS_INTERFACE_NAME "org.freedesktop.Notifications"
#endif
namespace namespace
{ {
auto constexpr StatsRefreshIntervalMsec = 3000;
auto constexpr SessionRefreshIntervalMsec = 3000;
auto constexpr ModelRefreshIntervalMsec = 3000;
std::array<tr_option, 8> const Opts = { std::array<tr_option, 8> const Opts = {
tr_option{ 'g', "config-dir", "Where to look for configuration files", "g", true, "<path>" }, tr_option{ 'g', "config-dir", "Where to look for configuration files", "g", true, "<path>" },
{ 'm', "minimized", "Start minimized in system tray", "m", false, nullptr }, { 'm', "minimized", "Start minimized in system tray", "m", false, nullptr },
@ -66,10 +79,6 @@ char const* getUsage()
" transmission [OPTIONS...] [torrent files]"; " transmission [OPTIONS...] [torrent files]";
} }
auto constexpr StatsRefreshIntervalMsec = 3000;
auto constexpr SessionRefreshIntervalMsec = 3000;
auto constexpr ModelRefreshIntervalMsec = 3000;
bool loadTranslation(QTranslator& translator, QString const& name, QLocale const& locale, QStringList const& search_directories) bool loadTranslation(QTranslator& translator, QString const& name, QLocale const& locale, QStringList const& search_directories)
{ {
for (QString const& directory : search_directories) for (QString const& directory : search_directories)
@ -83,7 +92,33 @@ bool loadTranslation(QTranslator& translator, QString const& name, QLocale const
return false; return false;
} }
[[nodiscard]] auto makeWindowIcon() void initUnits()
{
using Config = libtransmission::Values::Config;
Config::Speed = { Config::Base::Kilo,
QObject::tr("B/s").toStdString(),
QObject::tr("kB/s").toStdString(),
QObject::tr("MB/s").toStdString(),
QObject::tr("GB/s").toStdString(),
QObject::tr("TB/s").toStdString() };
Config::Memory = { Config::Base::Kibi,
QObject::tr("B").toStdString(),
QObject::tr("KiB").toStdString(),
QObject::tr("MiB").toStdString(),
QObject::tr("GiB").toStdString(),
QObject::tr("TiB").toStdString() };
Config::Storage = { Config::Base::Kilo,
QObject::tr("B").toStdString(),
QObject::tr("kB").toStdString(),
QObject::tr("MB").toStdString(),
QObject::tr("GB").toStdString(),
QObject::tr("TB").toStdString() };
}
auto makeWindowIcon()
{ {
// first, try to load it from the system theme // first, try to load it from the system theme
if (auto icon = QIcon::fromTheme(QStringLiteral("transmission")); !icon.isNull()) if (auto icon = QIcon::fromTheme(QStringLiteral("transmission")); !icon.isNull())
@ -116,283 +151,290 @@ QAccessibleInterface* accessibleFactory(QString const& className, QObject* objec
} // namespace } // namespace
Application::Application(int& argc, char** argv) Application::Application(int argc, char** argv, int& exit_code)
: QApplication{ argc, argv } : QApplication{ argc, argv }
, config_name_{ QStringLiteral("transmission") }
, display_name_{ QStringLiteral("transmission-qt") }
{ {
setApplicationName(config_name_); {
loadTranslations(); setApplicationName(QStringLiteral(APP_CONFIG_NAME));
initUnits(); loadTranslations();
initUnits();
#if defined(_WIN32) || defined(__APPLE__) #if defined(_WIN32) || defined(__APPLE__)
if (QIcon::themeName().isEmpty()) if (QIcon::themeName().isEmpty())
{ {
QIcon::setThemeName(QStringLiteral("Faenza")); QIcon::setThemeName(QStringLiteral("Faenza"));
} }
#endif #endif
setWindowIcon(makeWindowIcon()); setWindowIcon(makeWindowIcon());
#ifdef __APPLE__ #ifdef __APPLE__
setAttribute(Qt::AA_DontShowIconsInMenus); setAttribute(Qt::AA_DontShowIconsInMenus);
#endif #endif
// parse the command-line arguments tr_locale_set_global("");
int c = 0;
bool minimized = false;
char const* optarg = nullptr;
QString host;
QString port;
QString username;
QString password;
QString config_dir;
QStringList filenames;
while ((c = tr_getopt(getUsage(), argc, const_cast<char const**>(argv), Opts.data(), &optarg)) != TR_OPT_DONE) // parse the command-line arguments
{ bool minimized = false;
switch (c) QString host;
QString port;
QString username;
QString password;
QString config_dir;
QStringList filenames;
int c = 0;
char const* optarg = nullptr;
while ((c = tr_getopt(getUsage(), argc, const_cast<char const**>(argv), Opts.data(), &optarg)) != TR_OPT_DONE)
{ {
case 'g': switch (c)
config_dir = QString::fromUtf8(optarg);
break;
case 'p':
port = QString::fromUtf8(optarg);
break;
case 'r':
host = QString::fromUtf8(optarg);
break;
case 'u':
username = QString::fromUtf8(optarg);
break;
case 'w':
password = QString::fromUtf8(optarg);
break;
case 'm':
minimized = true;
break;
case 'v':
qInfo() << qPrintable(display_name_) << LONG_VERSION_STRING;
quitLater();
return;
case TR_OPT_ERR:
qWarning() << qPrintable(QObject::tr("Invalid option"));
tr_getopt_usage(qPrintable(display_name_), getUsage(), Opts.data());
quitLater();
return;
default:
filenames.append(QString::fromUtf8(optarg));
break;
}
}
// try to delegate the work to an existing copy of Transmission
// before starting ourselves...
InteropHelper const interop_client;
if (interop_client.isConnected())
{
bool delegated = false;
for (QString const& filename : filenames)
{
auto const a = AddData(filename);
QString metainfo;
switch (a.type)
{ {
case AddData::URL: case 'g':
metainfo = a.url.toString(); config_dir = QString::fromUtf8(optarg);
break; break;
case AddData::MAGNET: case 'p':
metainfo = a.magnet; port = QString::fromUtf8(optarg);
break; break;
case AddData::FILENAME: case 'r':
case AddData::METAINFO: host = QString::fromUtf8(optarg);
metainfo = QString::fromUtf8(a.toBase64());
break; break;
case 'u':
username = QString::fromUtf8(optarg);
break;
case 'w':
password = QString::fromUtf8(optarg);
break;
case 'm':
minimized = true;
break;
case 'v':
qInfo() << APP_DISPLAY_NAME << LONG_VERSION_STRING;
return;
case TR_OPT_ERR:
qWarning() << qUtf8Printable(QObject::tr("Invalid option"));
tr_getopt_usage(APP_DISPLAY_NAME, getUsage(), Opts.data());
exit_code = 1;
return;
default: default:
filenames.append(QString::fromUtf8(optarg));
break; break;
} }
}
if (!metainfo.isEmpty() && interop_client.addMetainfo(metainfo)) // try to delegate the work to an existing copy of Transmission
// before starting ourselves...
InteropHelper::initialize();
InteropHelper const interop_client;
if (interop_client.isConnected())
{
bool delegated = false;
for (QString const& filename : filenames)
{ {
delegated = true; auto const a = AddData(filename);
QString metainfo;
switch (a.type)
{
case AddData::URL:
metainfo = a.url.toString();
break;
case AddData::MAGNET:
metainfo = a.magnet;
break;
case AddData::FILENAME:
case AddData::METAINFO:
metainfo = QString::fromUtf8(a.toBase64());
break;
default:
break;
}
if (!metainfo.isEmpty() && interop_client.addMetainfo(metainfo))
{
delegated = true;
}
}
if (delegated)
{
return;
} }
} }
if (delegated) // set the fallback config dir
if (config_dir.isNull())
{ {
quitLater(); config_dir = QString::fromStdString(tr_getDefaultConfigDir("transmission"));
return;
} }
}
// set the fallback config dir // ensure our config directory exists
if (config_dir.isNull()) QDir const dir(config_dir);
{
config_dir = QString::fromStdString(tr_getDefaultConfigDir("transmission"));
}
// ensure our config directory exists if (!dir.exists())
QDir const dir(config_dir); {
dir.mkpath(config_dir);
}
if (!dir.exists()) // is this the first time we've run transmission?
{ bool const first_time = !dir.exists(QStringLiteral("settings.json"));
dir.mkpath(config_dir);
}
// is this the first time we've run transmission? // initialize the prefs
bool const first_time = !dir.exists(QStringLiteral("settings.json")); prefs_ = std::make_unique<Prefs>(config_dir);
// initialize the prefs if (!host.isNull())
prefs_ = std::make_unique<Prefs>(config_dir); {
prefs_->set(Prefs::SESSION_REMOTE_HOST, host);
}
if (!host.isNull()) if (!port.isNull())
{ {
prefs_->set(Prefs::SESSION_REMOTE_HOST, host); prefs_->set(Prefs::SESSION_REMOTE_PORT, port.toUInt());
} }
if (!port.isNull()) if (!username.isNull())
{ {
prefs_->set(Prefs::SESSION_REMOTE_PORT, port.toUInt()); prefs_->set(Prefs::SESSION_REMOTE_USERNAME, username);
} }
if (!username.isNull()) if (!password.isNull())
{ {
prefs_->set(Prefs::SESSION_REMOTE_USERNAME, username); prefs_->set(Prefs::SESSION_REMOTE_PASSWORD, password);
} }
if (!password.isNull()) if (!host.isNull() || !port.isNull() || !username.isNull() || !password.isNull())
{ {
prefs_->set(Prefs::SESSION_REMOTE_PASSWORD, password); prefs_->set(Prefs::SESSION_IS_REMOTE, true);
} }
if (!host.isNull() || !port.isNull() || !username.isNull() || !password.isNull()) if (prefs_->getBool(Prefs::START_MINIMIZED))
{ {
prefs_->set(Prefs::SESSION_IS_REMOTE, true); minimized = true;
} }
if (prefs_->getBool(Prefs::START_MINIMIZED)) // start as minimized only if the system tray present
{ if (!prefs_->getBool(Prefs::SHOW_TRAY_ICON))
minimized = true; {
} minimized = false;
}
// start as minimized only if the system tray present
if (!prefs_->getBool(Prefs::SHOW_TRAY_ICON))
{
minimized = false;
}
#if QT_CONFIG(accessibility) #if QT_CONFIG(accessibility)
QAccessible::installFactory(&accessibleFactory); QAccessible::installFactory(&accessibleFactory);
#endif #endif
session_ = std::make_unique<Session>(config_dir, *prefs_); session_ = std::make_unique<Session>(config_dir, *prefs_);
model_ = std::make_unique<TorrentModel>(*prefs_); model_ = std::make_unique<TorrentModel>(*prefs_);
window_ = std::make_unique<MainWindow>(*session_, *prefs_, *model_, minimized); window_ = std::make_unique<MainWindow>(*session_, *prefs_, *model_, minimized);
watch_dir_ = std::make_unique<WatchDir>(*model_); watch_dir_ = std::make_unique<WatchDir>(*model_);
connect(this, &QCoreApplication::aboutToQuit, this, &Application::saveGeometry); connect(this, &QCoreApplication::aboutToQuit, this, &Application::saveGeometry);
connect(model_.get(), &TorrentModel::torrentsAdded, this, &Application::onTorrentsAdded); connect(model_.get(), &TorrentModel::torrentsAdded, this, &Application::onTorrentsAdded);
connect(model_.get(), &TorrentModel::torrentsCompleted, this, &Application::onTorrentsCompleted); connect(model_.get(), &TorrentModel::torrentsCompleted, this, &Application::onTorrentsCompleted);
connect(model_.get(), &TorrentModel::torrentsEdited, this, &Application::onTorrentsEdited); connect(model_.get(), &TorrentModel::torrentsEdited, this, &Application::onTorrentsEdited);
connect(model_.get(), &TorrentModel::torrentsNeedInfo, this, &Application::onTorrentsNeedInfo); connect(model_.get(), &TorrentModel::torrentsNeedInfo, this, &Application::onTorrentsNeedInfo);
connect(prefs_.get(), &Prefs::changed, this, &Application::refreshPref); connect(prefs_.get(), &Prefs::changed, this, &Application::refreshPref);
connect(session_.get(), &Session::sourceChanged, this, &Application::onSessionSourceChanged); connect(session_.get(), &Session::sourceChanged, this, &Application::onSessionSourceChanged);
connect(session_.get(), &Session::torrentsRemoved, model_.get(), &TorrentModel::removeTorrents); connect(session_.get(), &Session::torrentsRemoved, model_.get(), &TorrentModel::removeTorrents);
connect(session_.get(), &Session::torrentsUpdated, model_.get(), &TorrentModel::updateTorrents); connect(session_.get(), &Session::torrentsUpdated, model_.get(), &TorrentModel::updateTorrents);
connect(watch_dir_.get(), &WatchDir::torrentFileAdded, this, qOverload<QString const&>(&Application::addWatchdirTorrent)); connect(
watch_dir_.get(),
&WatchDir::torrentFileAdded,
this,
qOverload<QString const&>(&Application::addWatchdirTorrent));
// init from preferences // init from preferences
for (auto const key : { Prefs::DIR_WATCH }) for (auto const key : { Prefs::DIR_WATCH })
{ {
refreshPref(key); refreshPref(key);
} }
QTimer* timer = &model_timer_; QTimer* timer = &model_timer_;
connect(timer, &QTimer::timeout, this, &Application::refreshTorrents); connect(timer, &QTimer::timeout, this, &Application::refreshTorrents);
timer->setSingleShot(false); timer->setSingleShot(false);
timer->setInterval(ModelRefreshIntervalMsec); timer->setInterval(ModelRefreshIntervalMsec);
timer->start(); timer->start();
timer = &stats_timer_; timer = &stats_timer_;
connect(timer, &QTimer::timeout, session_.get(), &Session::refreshSessionStats); connect(timer, &QTimer::timeout, session_.get(), &Session::refreshSessionStats);
timer->setSingleShot(false); timer->setSingleShot(false);
timer->setInterval(StatsRefreshIntervalMsec); timer->setInterval(StatsRefreshIntervalMsec);
timer->start(); timer->start();
timer = &session_timer_; timer = &session_timer_;
connect(timer, &QTimer::timeout, session_.get(), &Session::refreshSessionInfo); connect(timer, &QTimer::timeout, session_.get(), &Session::refreshSessionInfo);
timer->setSingleShot(false); timer->setSingleShot(false);
timer->setInterval(SessionRefreshIntervalMsec); timer->setInterval(SessionRefreshIntervalMsec);
timer->start(); timer->start();
maybeUpdateBlocklist(); maybeUpdateBlocklist();
if (!first_time) if (!first_time)
{ {
session_->restart(); session_->restart();
} }
else else
{ {
window_->openSession(); window_->openSession();
} }
if (!prefs_->getBool(Prefs::USER_HAS_GIVEN_INFORMED_CONSENT)) if (!prefs_->getBool(Prefs::USER_HAS_GIVEN_INFORMED_CONSENT))
{ {
auto* dialog = new QMessageBox{ QMessageBox::Information, auto* dialog = new QMessageBox{ QMessageBox::Information,
QString{}, QString{},
tr("<b>Transmission is a file sharing program.</b>"), tr("<b>Transmission is a file sharing program.</b>"),
QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok | QMessageBox::Cancel,
window_.get() }; window_.get() };
dialog->setInformativeText( dialog->setInformativeText(
tr("When you run a torrent, its data will be made available to others by means of upload. " tr("When you run a torrent, its data will be made available to others by means of upload. "
"Any content you share is your sole responsibility.")); "Any content you share is your sole responsibility."));
dialog->button(QMessageBox::Ok)->setText(tr("I &Agree")); dialog->button(QMessageBox::Ok)->setText(tr("I &Agree"));
dialog->setDefaultButton(QMessageBox::Ok); dialog->setDefaultButton(QMessageBox::Ok);
dialog->setModal(true); dialog->setModal(true);
connect(dialog, &QDialog::finished, this, &Application::consentGiven); connect(dialog, &QDialog::finished, this, &Application::consentGiven);
dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->show(); dialog->show();
} }
// torrent files passed in on the command line // torrent files passed in on the command line
for (QString const& filename : filenames) for (QString const& filename : filenames)
{ {
addTorrent(AddData{ filename }); addTorrent(AddData{ filename });
} }
InteropHelper::registerObject(this); InteropHelper::registerObject(this);
#ifdef QT_DBUS_LIB #ifdef QT_DBUS_LIB
if (auto bus = QDBusConnection::sessionBus(); bus.isConnected()) if (auto bus = QDBusConnection::sessionBus(); bus.isConnected())
{ {
bus.connect( bus.connect(
fdo_notifications_service_name_, QStringLiteral(FDO_NOTIFICATIONS_SERVICE_NAME),
fdo_notifications_path_, QStringLiteral(FDO_NOTIFICATIONS_PATH),
fdo_notifications_interface_name_, QStringLiteral(FDO_NOTIFICATIONS_INTERFACE_NAME),
QLatin1String("ActionInvoked"), QLatin1String("ActionInvoked"),
this, this,
SLOT(onNotificationActionInvoked(quint32, QString))); SLOT(onNotificationActionInvoked(quint32, QString)));
} }
#endif #endif
}
exit_code = exec();
} }
void Application::loadTranslations() void Application::loadTranslations()
@ -425,32 +467,13 @@ void Application::loadTranslations()
installTranslator(&qt_translator_); installTranslator(&qt_translator_);
} }
if (loadTranslation(app_translator_, config_name_, locale, app_qm_dirs) || if (loadTranslation(app_translator_, QStringLiteral(APP_CONFIG_NAME), locale, app_qm_dirs) ||
loadTranslation(app_translator_, config_name_, english_locale, app_qm_dirs)) loadTranslation(app_translator_, QStringLiteral(APP_CONFIG_NAME), english_locale, app_qm_dirs))
{ {
installTranslator(&app_translator_); installTranslator(&app_translator_);
} }
} }
void Application::initUnits()
{
using Config = libtransmission::Values::Config;
Config::Speed = { Config::Base::Kilo, tr("B/s").toStdString(), tr("kB/s").toStdString(),
tr("MB/s").toStdString(), tr("GB/s").toStdString(), tr("TB/s").toStdString() };
Config::Memory = { Config::Base::Kibi, tr("B").toStdString(), tr("KiB").toStdString(),
tr("MiB").toStdString(), tr("GiB").toStdString(), tr("TiB").toStdString() };
Config::Storage = { Config::Base::Kilo, tr("B").toStdString(), tr("kB").toStdString(),
tr("MB").toStdString(), tr("GB").toStdString(), tr("TB").toStdString() };
}
void Application::quitLater() const
{
QTimer::singleShot(0, this, SLOT(quit()));
}
void Application::onTorrentsEdited(torrent_ids_t const& torrent_ids) const void Application::onTorrentsEdited(torrent_ids_t const& torrent_ids) const
{ {
// the backend's tr_info has changed, so reload those fields // the backend's tr_info has changed, so reload those fields
@ -666,9 +689,9 @@ bool Application::notifyApp(QString const& title, QString const& body, QStringLi
if (auto bus = QDBusConnection::sessionBus(); bus.isConnected()) if (auto bus = QDBusConnection::sessionBus(); bus.isConnected())
{ {
QDBusMessage m = QDBusMessage::createMethodCall( QDBusMessage m = QDBusMessage::createMethodCall(
fdo_notifications_service_name_, QStringLiteral(FDO_NOTIFICATIONS_SERVICE_NAME),
fdo_notifications_path_, QStringLiteral(FDO_NOTIFICATIONS_PATH),
fdo_notifications_interface_name_, QStringLiteral(FDO_NOTIFICATIONS_INTERFACE_NAME),
QStringLiteral("Notify")); QStringLiteral("Notify"));
QVariantList args; QVariantList args;
args.append(QStringLiteral("Transmission")); // app_name args.append(QStringLiteral("Transmission")); // app_name
@ -699,7 +722,9 @@ bool Application::notifyApp(QString const& title, QString const& body, QStringLi
#ifdef QT_DBUS_LIB #ifdef QT_DBUS_LIB
void Application::onNotificationActionInvoked(quint32 /* notification_id */, QString action_key) void Application::onNotificationActionInvoked(quint32 /* notification_id */, QString action_key)
{ {
auto const match = start_now_regex_.match(action_key); static const QRegularExpression start_now_regex{ QStringLiteral(R"rgx(start-now\((\d+)\))rgx") };
auto const match = start_now_regex.match(action_key);
if (match.hasMatch()) if (match.hasMatch())
{ {
int const torrent_id = match.captured(1).toInt(); int const torrent_id = match.captured(1).toInt();
@ -716,10 +741,7 @@ int tr_main(int argc, char** argv)
{ {
auto const init_mgr = tr_lib_init(); auto const init_mgr = tr_lib_init();
tr_locale_set_global(""); int exit_code = 0;
Application const app(argc, argv, exit_code);
InteropHelper::initialize(); return exit_code;
Application const app(argc, argv);
return QApplication::exec();
} }

View File

@ -10,6 +10,8 @@
#include <unordered_set> #include <unordered_set>
#include <QApplication> #include <QApplication>
#include <QPixmap>
#include <QPointer>
#include <QRegularExpression> #include <QRegularExpression>
#include <QTimer> #include <QTimer>
#include <QTranslator> #include <QTranslator>
@ -20,7 +22,6 @@
#include "AddData.h" #include "AddData.h"
#include "Typedefs.h" #include "Typedefs.h"
#include "Utils.h" // std::hash<QString>
class AddData; class AddData;
class MainWindow; class MainWindow;
@ -36,7 +37,7 @@ class Application : public QApplication
TR_DISABLE_COPY_MOVE(Application) TR_DISABLE_COPY_MOVE(Application)
public: public:
Application(int& argc, char** argv); Application(int argc, char** argv, int& exit_code);
void raise() const; void raise() const;
bool notifyApp(QString const& title, QString const& body, QStringList const& actions = {}) const; bool notifyApp(QString const& title, QString const& body, QStringList const& actions = {}) const;
@ -92,9 +93,7 @@ private slots:
private: private:
void maybeUpdateBlocklist() const; void maybeUpdateBlocklist() const;
void loadTranslations(); void loadTranslations();
void initUnits();
QStringList getNames(torrent_ids_t const& ids) const; QStringList getNames(torrent_ids_t const& ids) const;
void quitLater() const;
void notifyTorrentAdded(Torrent const*) const; void notifyTorrentAdded(Torrent const*) const;
std::unordered_set<QString> interned_strings_; std::unordered_set<QString> interned_strings_;
@ -112,17 +111,6 @@ private:
QTranslator app_translator_; QTranslator app_translator_;
FaviconCache<QPixmap> favicon_cache_; FaviconCache<QPixmap> favicon_cache_;
QString const config_name_ = QStringLiteral("transmission");
QString const display_name_ = QStringLiteral("transmission-qt");
#ifdef QT_DBUS_LIB
QString const fdo_notifications_service_name_ = QStringLiteral("org.freedesktop.Notifications");
QString const fdo_notifications_path_ = QStringLiteral("/org/freedesktop/Notifications");
QString const fdo_notifications_interface_name_ = QStringLiteral("org.freedesktop.Notifications");
#endif
QRegularExpression const start_now_regex_{ QStringLiteral(R"rgx(start-now\((\d+)\))rgx") };
}; };
#define trApp dynamic_cast<Application*>(Application::instance()) #define trApp dynamic_cast<Application*>(Application::instance())

View File

@ -150,7 +150,7 @@ Get a file list for the current torrent(s)
.It Fl g Fl -get Ar all | file-index | files .It Fl g Fl -get Ar all | file-index | files
Mark file(s) for download. Mark file(s) for download.
.Ar all .Ar all
marks all all of the torrent's files for downloading, marks all of the torrent's files for downloading,
.Ar file-index .Ar file-index
adds a single file to the download list, and adds a single file to the download list, and
.Ar files .Ar files