diff --git a/src/app/application.cpp b/src/app/application.cpp index 4e4e1fc7a..988543098 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -658,8 +658,7 @@ Application::AddTorrentParams Application::parseParams(const QStringList ¶ms continue; } - parsedParams.torrentSource = param; - break; + parsedParams.torrentSources.append(param); } return parsedParams; @@ -675,10 +674,16 @@ void Application::processParams(const AddTorrentParams ¶ms) // should be overridden. const bool showDialogForThisTorrent = !params.skipTorrentDialog.value_or(!AddNewTorrentDialog::isEnabled()); if (showDialogForThisTorrent) - AddNewTorrentDialog::show(params.torrentSource, params.torrentParams, m_window); + { + for (const QString &torrentSource : params.torrentSources) + AddNewTorrentDialog::show(torrentSource, params.torrentParams, m_window); + } else #endif - BitTorrent::Session::instance()->addTorrent(params.torrentSource, params.torrentParams); + { + for (const QString &torrentSource : params.torrentSources) + BitTorrent::Session::instance()->addTorrent(torrentSource, params.torrentParams); + } } int Application::exec(const QStringList ¶ms) @@ -788,12 +793,9 @@ try }); disconnect(m_desktopIntegration, &DesktopIntegration::activationRequested, this, &Application::createStartupProgressDialog); - // we must not delete menu while it is used by DesktopIntegration - auto *oldMenu = m_desktopIntegration->menu(); const MainWindow::State windowState = (!m_startupProgressDialog || (m_startupProgressDialog->windowState() & Qt::WindowMinimized)) ? MainWindow::Minimized : MainWindow::Normal; m_window = new MainWindow(this, windowState); - delete oldMenu; delete m_startupProgressDialog; #ifdef Q_OS_WIN auto *pref = Preferences::instance(); diff --git a/src/app/application.h b/src/app/application.h index d8801face..21f1dc132 100644 --- a/src/app/application.h +++ b/src/app/application.h @@ -148,7 +148,7 @@ private slots: private: struct AddTorrentParams { - QString torrentSource; + QStringList torrentSources; BitTorrent::AddTorrentParams torrentParams; std::optional skipTorrentDialog; }; diff --git a/src/base/bittorrent/resumedatastorage.cpp b/src/base/bittorrent/resumedatastorage.cpp index 0b640a95e..6e67a5b58 100644 --- a/src/base/bittorrent/resumedatastorage.cpp +++ b/src/base/bittorrent/resumedatastorage.cpp @@ -33,6 +33,7 @@ #include #include #include +#include const int TORRENTIDLIST_TYPEID = qRegisterMetaType>(); @@ -59,11 +60,11 @@ void BitTorrent::ResumeDataStorage::loadAll() const loadingThread->start(); } -QVector BitTorrent::ResumeDataStorage::fetchLoadedResumeData() const +QList BitTorrent::ResumeDataStorage::fetchLoadedResumeData() const { const QMutexLocker locker {&m_loadedResumeDataMutex}; - const QVector loadedResumeData = m_loadedResumeData; + const QList loadedResumeData = m_loadedResumeData; m_loadedResumeData.clear(); return loadedResumeData; diff --git a/src/base/bittorrent/resumedatastorage.h b/src/base/bittorrent/resumedatastorage.h index 3faf63494..b583da60a 100644 --- a/src/base/bittorrent/resumedatastorage.h +++ b/src/base/bittorrent/resumedatastorage.h @@ -29,9 +29,9 @@ #pragma once #include +#include #include #include -#include #include "base/3rdparty/expected.hpp" #include "base/path.h" @@ -65,7 +65,7 @@ namespace BitTorrent virtual void storeQueue(const QVector &queue) const = 0; void loadAll() const; - QVector fetchLoadedResumeData() const; + QList fetchLoadedResumeData() const; signals: void loadStarted(const QVector &torrents); @@ -78,7 +78,7 @@ namespace BitTorrent virtual void doLoadAll() const = 0; const Path m_path; - mutable QVector m_loadedResumeData; + mutable QList m_loadedResumeData; mutable QMutex m_loadedResumeDataMutex; }; } diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index 981917dd4..79b3f47b4 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -403,9 +403,6 @@ namespace BitTorrent virtual Torrent *findTorrent(const InfoHash &infoHash) const = 0; virtual QVector torrents() const = 0; virtual qsizetype torrentsCount() const = 0; - virtual bool hasActiveTorrents() const = 0; - virtual bool hasUnfinishedTorrents() const = 0; - virtual bool hasRunningSeed() const = 0; virtual const SessionStatus &status() const = 0; virtual const CacheStatus &cacheStatus() const = 0; virtual bool isListening() const = 0; diff --git a/src/base/bittorrent/sessionimpl.cpp b/src/base/bittorrent/sessionimpl.cpp index 89ce7a9bd..25a8adfa5 100644 --- a/src/base/bittorrent/sessionimpl.cpp +++ b/src/base/bittorrent/sessionimpl.cpp @@ -322,7 +322,7 @@ struct BitTorrent::SessionImpl::ResumeSessionContext final : public QObject ResumeDataStorage *startupStorage = nullptr; ResumeDataStorageType currentStorageType = ResumeDataStorageType::Legacy; - QVector loadedResumeData; + QList loadedResumeData; int processingResumeDataCount = 0; int64_t totalResumeDataCount = 0; int64_t finishedResumeDataCount = 0; @@ -2190,30 +2190,6 @@ Torrent *SessionImpl::findTorrent(const InfoHash &infoHash) const return m_torrents.value(altID); } -bool SessionImpl::hasActiveTorrents() const -{ - return std::any_of(m_torrents.begin(), m_torrents.end(), [](TorrentImpl *torrent) - { - return TorrentFilter::ActiveTorrent.match(torrent); - }); -} - -bool SessionImpl::hasUnfinishedTorrents() const -{ - return std::any_of(m_torrents.begin(), m_torrents.end(), [](const TorrentImpl *torrent) - { - return (!torrent->isSeed() && !torrent->isPaused() && !torrent->isErrored() && torrent->hasMetadata()); - }); -} - -bool SessionImpl::hasRunningSeed() const -{ - return std::any_of(m_torrents.begin(), m_torrents.end(), [](const TorrentImpl *torrent) - { - return (torrent->isSeed() && !torrent->isPaused()); - }); -} - void SessionImpl::banIP(const QString &ip) { QStringList bannedIPs = m_bannedIPs; @@ -2811,6 +2787,19 @@ bool SessionImpl::downloadMetadata(const MagnetUri &magnetUri) lt::add_torrent_params p = magnetUri.addTorrentParams(); + if (isAddTrackersEnabled()) + { + // Use "additional trackers" when metadata retrieving (this can help when the DHT nodes are few) + p.trackers.reserve(p.trackers.size() + static_cast(m_additionalTrackerList.size())); + p.tracker_tiers.reserve(p.trackers.size() + static_cast(m_additionalTrackerList.size())); + p.tracker_tiers.resize(p.trackers.size(), 0); + for (const TrackerEntry &trackerEntry : asConst(m_additionalTrackerList)) + { + p.trackers.push_back(trackerEntry.url.toStdString()); + p.tracker_tiers.push_back(trackerEntry.tier); + } + } + // Flags // Preallocation mode if (isPreallocationEnabled()) @@ -4638,7 +4627,11 @@ void SessionImpl::handleTorrentFinished(TorrentImpl *const torrent) } } - if (!hasUnfinishedTorrents()) + const bool hasUnfinishedTorrents = std::any_of(m_torrents.cbegin(), m_torrents.cend(), [](const TorrentImpl *torrent) + { + return !(torrent->isSeed() || torrent->isPaused() || torrent->isErrored()); + }); + if (!hasUnfinishedTorrents) emit allTorrentsFinished(); } diff --git a/src/base/bittorrent/sessionimpl.h b/src/base/bittorrent/sessionimpl.h index d9bbc1a7c..3d0c3b08d 100644 --- a/src/base/bittorrent/sessionimpl.h +++ b/src/base/bittorrent/sessionimpl.h @@ -376,9 +376,6 @@ namespace BitTorrent Torrent *findTorrent(const InfoHash &infoHash) const override; QVector torrents() const override; qsizetype torrentsCount() const override; - bool hasActiveTorrents() const override; - bool hasUnfinishedTorrents() const override; - bool hasRunningSeed() const override; const SessionStatus &status() const override; const CacheStatus &cacheStatus() const override; bool isListening() const override; diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index 4cc99227c..101eefd77 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -996,6 +996,18 @@ void Preferences::resolvePeerHostNames(const bool resolve) setValue(u"Preferences/Connection/ResolvePeerHostNames"_qs, resolve); } +#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) +bool Preferences::useSystemIcons() const +{ + return value(u"Preferences/Advanced/useSystemIconTheme"_qs, false); +} + +void Preferences::useSystemIcons(const bool enabled) +{ + setValue(u"Preferences/Advanced/useSystemIconTheme"_qs, enabled); +} +#endif + bool Preferences::isRecursiveDownloadEnabled() const { return !value(u"Preferences/Advanced/DisableRecursiveDownload"_qs, false); diff --git a/src/base/preferences.h b/src/base/preferences.h index d22db8df5..0dde9afef 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -281,6 +281,10 @@ public: void resolvePeerCountries(bool resolve); bool resolvePeerHostNames() const; void resolvePeerHostNames(bool resolve); +#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) + bool useSystemIcons() const; + void useSystemIcons(bool enabled); +#endif bool isRecursiveDownloadEnabled() const; void setRecursiveDownloadEnabled(bool enable); #ifdef Q_OS_WIN diff --git a/src/base/utils/misc.cpp b/src/base/utils/misc.cpp index 01d81e178..e9803f36c 100644 --- a/src/base/utils/misc.cpp +++ b/src/base/utils/misc.cpp @@ -53,6 +53,8 @@ #include #include +#include +#include #include #include #include @@ -403,6 +405,94 @@ QString Utils::Misc::getUserIDString() return uid; } +QString Utils::Misc::languageToLocalizedString(const QString &localeStr) +{ + if (localeStr.startsWith(u"eo", Qt::CaseInsensitive)) + { + // QLocale doesn't work with that locale. Esperanto isn't a "real" language. + return C_LOCALE_ESPERANTO; + } + + if (localeStr.startsWith(u"ltg", Qt::CaseInsensitive)) + { + // QLocale doesn't work with that locale. + return C_LOCALE_LATGALIAN; + } + + const QLocale locale {localeStr}; + switch (locale.language()) + { + case QLocale::Arabic: return C_LOCALE_ARABIC; + case QLocale::Armenian: return C_LOCALE_ARMENIAN; + case QLocale::Azerbaijani: return C_LOCALE_AZERBAIJANI; + case QLocale::Basque: return C_LOCALE_BASQUE; + case QLocale::Bulgarian: return C_LOCALE_BULGARIAN; + case QLocale::Byelorussian: return C_LOCALE_BYELORUSSIAN; + case QLocale::Catalan: return C_LOCALE_CATALAN; + case QLocale::Chinese: + switch (locale.country()) + { + case QLocale::China: return C_LOCALE_CHINESE_SIMPLIFIED; + case QLocale::HongKong: return C_LOCALE_CHINESE_TRADITIONAL_HK; + default: return C_LOCALE_CHINESE_TRADITIONAL_TW; + } + case QLocale::Croatian: return C_LOCALE_CROATIAN; + case QLocale::Czech: return C_LOCALE_CZECH; + case QLocale::Danish: return C_LOCALE_DANISH; + case QLocale::Dutch: return C_LOCALE_DUTCH; + case QLocale::English: + switch (locale.country()) + { + case QLocale::Australia: return C_LOCALE_ENGLISH_AUSTRALIA; + case QLocale::UnitedKingdom: return C_LOCALE_ENGLISH_UNITEDKINGDOM; + default: return C_LOCALE_ENGLISH; + } + case QLocale::Estonian: return C_LOCALE_ESTONIAN; + case QLocale::Finnish: return C_LOCALE_FINNISH; + case QLocale::French: return C_LOCALE_FRENCH; + case QLocale::Galician: return C_LOCALE_GALICIAN; + case QLocale::Georgian: return C_LOCALE_GEORGIAN; + case QLocale::German: return C_LOCALE_GERMAN; + case QLocale::Greek: return C_LOCALE_GREEK; + case QLocale::Hebrew: return C_LOCALE_HEBREW; + case QLocale::Hindi: return C_LOCALE_HINDI; + case QLocale::Hungarian: return C_LOCALE_HUNGARIAN; + case QLocale::Icelandic: return C_LOCALE_ICELANDIC; + case QLocale::Indonesian: return C_LOCALE_INDONESIAN; + case QLocale::Italian: return C_LOCALE_ITALIAN; + case QLocale::Japanese: return C_LOCALE_JAPANESE; + case QLocale::Korean: return C_LOCALE_KOREAN; + case QLocale::Latvian: return C_LOCALE_LATVIAN; + case QLocale::Lithuanian: return C_LOCALE_LITHUANIAN; + case QLocale::Malay: return C_LOCALE_MALAY; + case QLocale::Mongolian: return C_LOCALE_MONGOLIAN; + case QLocale::NorwegianBokmal: return C_LOCALE_NORWEGIAN; + case QLocale::Occitan: return C_LOCALE_OCCITAN; + case QLocale::Persian: return C_LOCALE_PERSIAN; + case QLocale::Polish: return C_LOCALE_POLISH; + case QLocale::Portuguese: + if (locale.country() == QLocale::Brazil) + return C_LOCALE_PORTUGUESE_BRAZIL; + return C_LOCALE_PORTUGUESE; + case QLocale::Romanian: return C_LOCALE_ROMANIAN; + case QLocale::Russian: return C_LOCALE_RUSSIAN; + case QLocale::Serbian: return C_LOCALE_SERBIAN; + case QLocale::Slovak: return C_LOCALE_SLOVAK; + case QLocale::Slovenian: return C_LOCALE_SLOVENIAN; + case QLocale::Spanish: return C_LOCALE_SPANISH; + case QLocale::Swedish: return C_LOCALE_SWEDISH; + case QLocale::Thai: return C_LOCALE_THAI; + case QLocale::Turkish: return C_LOCALE_TURKISH; + case QLocale::Ukrainian: return C_LOCALE_UKRAINIAN; + case QLocale::Uzbek: return C_LOCALE_UZBEK; + case QLocale::Vietnamese: return C_LOCALE_VIETNAMESE; + default: + const QString lang = QLocale::languageToString(locale.language()); + qWarning() << "Unrecognized language name: " << lang; + return lang; + } +} + QString Utils::Misc::parseHtmlLinks(const QString &rawText) { QString result = rawText; diff --git a/src/base/utils/misc.h b/src/base/utils/misc.h index 95da08afd..fc1eac40d 100644 --- a/src/base/utils/misc.h +++ b/src/base/utils/misc.h @@ -85,6 +85,8 @@ namespace Utils::Misc QString userFriendlyDuration(qlonglong seconds, qlonglong maxCap = -1); QString getUserIDString(); + QString languageToLocalizedString(const QString &localeStr); + #ifdef Q_OS_WIN Path windowsSystemPath(); diff --git a/src/gui/categoryfiltermodel.cpp b/src/gui/categoryfiltermodel.cpp index c45351952..7b3844770 100644 --- a/src/gui/categoryfiltermodel.cpp +++ b/src/gui/categoryfiltermodel.cpp @@ -56,7 +56,7 @@ public: clear(); if (m_parent) { - m_parent->m_torrentsCount -= m_torrentsCount; + m_parent->decreaseTorrentsCount(m_torrentsCount); const QString uid = m_parent->m_children.key(this); m_parent->m_children.remove(uid); m_parent->m_childUids.removeOne(uid); @@ -86,18 +86,18 @@ public: return m_torrentsCount; } - void increaseTorrentsCount() + void increaseTorrentsCount(const int delta = 1) { - ++m_torrentsCount; + m_torrentsCount += delta; if (m_parent) - m_parent->increaseTorrentsCount(); + m_parent->increaseTorrentsCount(delta); } - void decreaseTorrentsCount() + void decreaseTorrentsCount(const int delta = 1) { - --m_torrentsCount; + m_torrentsCount -= delta; if (m_parent) - m_parent->decreaseTorrentsCount(); + m_parent->decreaseTorrentsCount(delta); } int pos() const @@ -139,7 +139,7 @@ public: item->m_parent = this; m_children[uid] = item; m_childUids.append(uid); - m_torrentsCount += item->torrentsCount(); + increaseTorrentsCount(item->torrentsCount()); } void clear() @@ -211,7 +211,7 @@ QVariant CategoryFilterModel::data(const QModelIndex &index, int role) const if ((index.column() == 0) && (role == Qt::DecorationRole)) { - return UIThemeManager::instance()->getIcon(u"view-categories"_qs); + return UIThemeManager::instance()->getIcon(u"view-categories"_qs, u"inode-directory"_qs); } if ((index.column() == 0) && (role == Qt::DisplayRole)) @@ -408,9 +408,9 @@ void CategoryFilterModel::populate() m_rootItem->addChild(UID_UNCATEGORIZED, new CategoryModelItem(nullptr, tr("Uncategorized"), torrentsCount)); using BitTorrent::Torrent; - for (const QString &categoryName : asConst(session->categories())) + if (m_isSubcategoriesEnabled) { - if (m_isSubcategoriesEnabled) + for (const QString &categoryName : asConst(session->categories())) { CategoryModelItem *parent = m_rootItem; for (const QString &subcat : asConst(session->expandCategory(categoryName))) @@ -419,16 +419,19 @@ void CategoryFilterModel::populate() if (!parent->hasChild(subcatName)) { const int torrentsCount = std::count_if(torrents.cbegin(), torrents.cend() - , [subcat](Torrent *torrent) { return torrent->category() == subcat; }); + , [subcat](Torrent *torrent) { return torrent->category() == subcat; }); new CategoryModelItem(parent, subcatName, torrentsCount); } parent = parent->child(subcatName); } } - else + } + else + { + for (const QString &categoryName : asConst(session->categories())) { const int torrentsCount = std::count_if(torrents.begin(), torrents.end() - , [categoryName](Torrent *torrent) { return torrent->belongsToCategory(categoryName); }); + , [categoryName](Torrent *torrent) { return torrent->belongsToCategory(categoryName); }); new CategoryModelItem(m_rootItem, categoryName, torrentsCount); } } diff --git a/src/gui/categoryfilterwidget.cpp b/src/gui/categoryfilterwidget.cpp index 61a77f049..5c22bfbd4 100644 --- a/src/gui/categoryfilterwidget.cpp +++ b/src/gui/categoryfilterwidget.cpp @@ -121,18 +121,18 @@ void CategoryFilterWidget::showMenu() , this, &CategoryFilterWidget::addSubcategory); } - menu->addAction(UIThemeManager::instance()->getIcon(u"edit-rename"_qs), tr("Edit category...") + menu->addAction(UIThemeManager::instance()->getIcon(u"edit-rename"_qs, u"document-edit"_qs), tr("Edit category...") , this, &CategoryFilterWidget::editCategory); - menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs), tr("Remove category") + menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs, u"list-remove"_qs), tr("Remove category") , this, &CategoryFilterWidget::removeCategory); } - menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs), tr("Remove unused categories") + menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs, u"list-remove"_qs), tr("Remove unused categories") , this, &CategoryFilterWidget::removeUnusedCategories); menu->addSeparator(); - menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-start"_qs), tr("Resume torrents") + menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-start"_qs, u"media-playback-start"_qs), tr("Resume torrents") , this, &CategoryFilterWidget::actionResumeTorrentsTriggered); - menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs), tr("Pause torrents") + menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs, u"media-playback-pause"_qs), tr("Pause torrents") , this, &CategoryFilterWidget::actionPauseTorrentsTriggered); menu->addAction(UIThemeManager::instance()->getIcon(u"list-remove"_qs), tr("Remove torrents") , this, &CategoryFilterWidget::actionDeleteTorrentsTriggered); diff --git a/src/gui/desktopintegration.cpp b/src/gui/desktopintegration.cpp index ee0950924..ae28c494f 100644 --- a/src/gui/desktopintegration.cpp +++ b/src/gui/desktopintegration.cpp @@ -99,6 +99,12 @@ DesktopIntegration::DesktopIntegration(QObject *parent) connect(Preferences::instance(), &Preferences::changed, this, &DesktopIntegration::onPreferencesChanged); } +DesktopIntegration::~DesktopIntegration() +{ + if (m_menu) + delete m_menu; +} + bool DesktopIntegration::isActive() const { #ifdef Q_OS_MACOS @@ -135,12 +141,36 @@ void DesktopIntegration::setMenu(QMenu *menu) if (menu == m_menu) return; +#if defined Q_OS_MACOS + if (m_menu) + delete m_menu; + m_menu = menu; -#ifdef Q_OS_MACOS if (m_menu) m_menu->setAsDockMenu(); +#elif defined Q_OS_UNIX + const bool systemTrayEnabled = m_systrayIcon; + if (m_menu) + { + if (m_systrayIcon) + { + delete m_systrayIcon; + m_systrayIcon = nullptr; + } + delete m_menu; + } + + m_menu = menu; + + if (systemTrayEnabled && !m_systrayIcon) + createTrayIcon(); #else + if (m_menu) + delete m_menu; + + m_menu = menu; + if (m_systrayIcon) m_systrayIcon->setContextMenu(m_menu); #endif diff --git a/src/gui/desktopintegration.h b/src/gui/desktopintegration.h index 2c2b7f9a3..726165c96 100644 --- a/src/gui/desktopintegration.h +++ b/src/gui/desktopintegration.h @@ -49,6 +49,7 @@ class DesktopIntegration final : public QObject public: explicit DesktopIntegration(QObject *parent = nullptr); + ~DesktopIntegration() override; bool isActive() const; diff --git a/src/gui/executionlogwidget.cpp b/src/gui/executionlogwidget.cpp index 5d12f3f50..95f1cb064 100644 --- a/src/gui/executionlogwidget.cpp +++ b/src/gui/executionlogwidget.cpp @@ -69,8 +69,8 @@ ExecutionLogWidget::ExecutionLogWidget(const Log::MsgTypes types, QWidget *paren m_ui->tabBan->layout()->addWidget(peerView); #ifndef Q_OS_MACOS - m_ui->tabConsole->setTabIcon(0, UIThemeManager::instance()->getIcon(u"help-contents"_qs)); - m_ui->tabConsole->setTabIcon(1, UIThemeManager::instance()->getIcon(u"ip-blocked"_qs)); + m_ui->tabConsole->setTabIcon(0, UIThemeManager::instance()->getIcon(u"help-contents"_qs, u"view-calendar-journal"_qs)); + m_ui->tabConsole->setTabIcon(1, UIThemeManager::instance()->getIcon(u"ip-blocked"_qs, u"view-filter"_qs)); #endif } diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index 6f73e4ce9..ad781f3a9 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -134,8 +134,7 @@ MainWindow::MainWindow(IGUIApplication *app, const State initialState) m_displaySpeedInTitle = pref->speedInTitleBar(); // Setting icons #ifndef Q_OS_MACOS - const QIcon appLogo(UIThemeManager::instance()->getIcon(u"qbittorrent"_qs, u"qbittorrent-tray"_qs)); - setWindowIcon(appLogo); + setWindowIcon(UIThemeManager::instance()->getIcon(u"qbittorrent"_qs)); #endif // Q_OS_MACOS #if (defined(Q_OS_UNIX)) @@ -147,7 +146,7 @@ MainWindow::MainWindow(IGUIApplication *app, const State initialState) m_ui->actionOpen->setIcon(UIThemeManager::instance()->getIcon(u"list-add"_qs)); m_ui->actionDownloadFromURL->setIcon(UIThemeManager::instance()->getIcon(u"insert-link"_qs)); m_ui->actionSetGlobalSpeedLimits->setIcon(UIThemeManager::instance()->getIcon(u"speedometer"_qs)); - m_ui->actionCreateTorrent->setIcon(UIThemeManager::instance()->getIcon(u"torrent-creator"_qs)); + m_ui->actionCreateTorrent->setIcon(UIThemeManager::instance()->getIcon(u"torrent-creator"_qs, u"document-edit"_qs)); m_ui->actionAbout->setIcon(UIThemeManager::instance()->getIcon(u"help-about"_qs)); m_ui->actionStatistics->setIcon(UIThemeManager::instance()->getIcon(u"view-statistics"_qs)); m_ui->actionTopQueuePos->setIcon(UIThemeManager::instance()->getIcon(u"go-top"_qs)); @@ -159,13 +158,13 @@ MainWindow::MainWindow(IGUIApplication *app, const State initialState) m_ui->actionDonateMoney->setIcon(UIThemeManager::instance()->getIcon(u"wallet-open"_qs)); m_ui->actionExit->setIcon(UIThemeManager::instance()->getIcon(u"application-exit"_qs)); m_ui->actionLock->setIcon(UIThemeManager::instance()->getIcon(u"object-locked"_qs)); - m_ui->actionOptions->setIcon(UIThemeManager::instance()->getIcon(u"configure"_qs)); - m_ui->actionPause->setIcon(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs)); - m_ui->actionPauseAll->setIcon(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs)); - m_ui->actionStart->setIcon(UIThemeManager::instance()->getIcon(u"torrent-start"_qs)); - m_ui->actionStartAll->setIcon(UIThemeManager::instance()->getIcon(u"torrent-start"_qs)); - m_ui->menuAutoShutdownOnDownloadsCompletion->setIcon(UIThemeManager::instance()->getIcon(u"task-complete"_qs)); - m_ui->actionManageCookies->setIcon(UIThemeManager::instance()->getIcon(u"browser-cookies"_qs)); + m_ui->actionOptions->setIcon(UIThemeManager::instance()->getIcon(u"configure"_qs, u"preferences-system"_qs)); + m_ui->actionPause->setIcon(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs, u"media-playback-pause"_qs)); + m_ui->actionPauseAll->setIcon(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs, u"media-playback-pause"_qs)); + m_ui->actionStart->setIcon(UIThemeManager::instance()->getIcon(u"torrent-start"_qs, u"media-playback-start"_qs)); + m_ui->actionStartAll->setIcon(UIThemeManager::instance()->getIcon(u"torrent-start"_qs, u"media-playback-start"_qs)); + m_ui->menuAutoShutdownOnDownloadsCompletion->setIcon(UIThemeManager::instance()->getIcon(u"task-complete"_qs, u"application-exit"_qs)); + m_ui->actionManageCookies->setIcon(UIThemeManager::instance()->getIcon(u"browser-cookies"_qs, u"preferences-web-browser-cookies"_qs)); m_ui->menuLog->setIcon(UIThemeManager::instance()->getIcon(u"help-contents"_qs)); m_ui->actionCheckForUpdates->setIcon(UIThemeManager::instance()->getIcon(u"view-refresh"_qs)); @@ -464,7 +463,6 @@ MainWindow::MainWindow(IGUIApplication *app, const State initialState) MainWindow::~MainWindow() { - app()->desktopIntegration()->setMenu(nullptr); delete m_ui; } @@ -1139,7 +1137,12 @@ void MainWindow::closeEvent(QCloseEvent *e) } #endif // Q_OS_MACOS - if (pref->confirmOnExit() && BitTorrent::Session::instance()->hasActiveTorrents()) + const QVector allTorrents = BitTorrent::Session::instance()->torrents(); + const bool hasActiveTorrents = std::any_of(allTorrents.cbegin(), allTorrents.cend(), [](BitTorrent::Torrent *torrent) + { + return torrent->isActive(); + }); + if (pref->confirmOnExit() && hasActiveTorrents) { if (e->spontaneous() || m_forceExit) { @@ -1569,7 +1572,7 @@ void MainWindow::downloadFromURLList(const QStringList &urlList) QMenu *MainWindow::createDesktopIntegrationMenu() { - auto *menu = new QMenu(this); + auto *menu = new QMenu; #ifndef Q_OS_MACOS connect(menu, &QMenu::aboutToShow, this, [this]() @@ -1903,8 +1906,17 @@ void MainWindow::on_actionAutoShutdown_toggled(bool enabled) void MainWindow::updatePowerManagementState() { - const bool inhibitSuspend = (Preferences::instance()->preventFromSuspendWhenDownloading() && BitTorrent::Session::instance()->hasUnfinishedTorrents()) - || (Preferences::instance()->preventFromSuspendWhenSeeding() && BitTorrent::Session::instance()->hasRunningSeed()); + const QVector allTorrents = BitTorrent::Session::instance()->torrents(); + const bool hasUnfinishedTorrents = std::any_of(allTorrents.cbegin(), allTorrents.cend(), [](const BitTorrent::Torrent *torrent) + { + return (!torrent->isSeed() && !torrent->isPaused() && !torrent->isErrored() && torrent->hasMetadata()); + }); + const bool hasRunningSeed = std::any_of(allTorrents.cbegin(), allTorrents.cend(), [](const BitTorrent::Torrent *torrent) + { + return (torrent->isSeed() && !torrent->isPaused()); + }); + const bool inhibitSuspend = (Preferences::instance()->preventFromSuspendWhenDownloading() && hasUnfinishedTorrents) + || (Preferences::instance()->preventFromSuspendWhenSeeding() && hasRunningSeed); m_pwr->setActivityState(inhibitSuspend); } diff --git a/src/gui/optionsdialog.cpp b/src/gui/optionsdialog.cpp index d4068e26a..2ae1359e1 100644 --- a/src/gui/optionsdialog.cpp +++ b/src/gui/optionsdialog.cpp @@ -52,8 +52,8 @@ #include "base/rss/rss_session.h" #include "base/torrentfileguard.h" #include "base/torrentfileswatcher.h" -#include "base/unicodestrings.h" #include "base/utils/fs.h" +#include "base/utils/misc.h" #include "base/utils/net.h" #include "base/utils/password.h" #include "base/utils/random.h" @@ -89,81 +89,6 @@ namespace return ret; } - QString languageToLocalizedString(const QLocale &locale) - { - switch (locale.language()) - { - case QLocale::Arabic: return C_LOCALE_ARABIC; - case QLocale::Armenian: return C_LOCALE_ARMENIAN; - case QLocale::Azerbaijani: return C_LOCALE_AZERBAIJANI; - case QLocale::Basque: return C_LOCALE_BASQUE; - case QLocale::Bulgarian: return C_LOCALE_BULGARIAN; - case QLocale::Byelorussian: return C_LOCALE_BYELORUSSIAN; - case QLocale::Catalan: return C_LOCALE_CATALAN; - case QLocale::Chinese: - switch (locale.country()) - { - case QLocale::China: return C_LOCALE_CHINESE_SIMPLIFIED; - case QLocale::HongKong: return C_LOCALE_CHINESE_TRADITIONAL_HK; - default: return C_LOCALE_CHINESE_TRADITIONAL_TW; - } - case QLocale::Croatian: return C_LOCALE_CROATIAN; - case QLocale::Czech: return C_LOCALE_CZECH; - case QLocale::Danish: return C_LOCALE_DANISH; - case QLocale::Dutch: return C_LOCALE_DUTCH; - case QLocale::English: - switch (locale.country()) - { - case QLocale::Australia: return C_LOCALE_ENGLISH_AUSTRALIA; - case QLocale::UnitedKingdom: return C_LOCALE_ENGLISH_UNITEDKINGDOM; - default: return C_LOCALE_ENGLISH; - } - case QLocale::Estonian: return C_LOCALE_ESTONIAN; - case QLocale::Finnish: return C_LOCALE_FINNISH; - case QLocale::French: return C_LOCALE_FRENCH; - case QLocale::Galician: return C_LOCALE_GALICIAN; - case QLocale::Georgian: return C_LOCALE_GEORGIAN; - case QLocale::German: return C_LOCALE_GERMAN; - case QLocale::Greek: return C_LOCALE_GREEK; - case QLocale::Hebrew: return C_LOCALE_HEBREW; - case QLocale::Hindi: return C_LOCALE_HINDI; - case QLocale::Hungarian: return C_LOCALE_HUNGARIAN; - case QLocale::Icelandic: return C_LOCALE_ICELANDIC; - case QLocale::Indonesian: return C_LOCALE_INDONESIAN; - case QLocale::Italian: return C_LOCALE_ITALIAN; - case QLocale::Japanese: return C_LOCALE_JAPANESE; - case QLocale::Korean: return C_LOCALE_KOREAN; - case QLocale::Latvian: return C_LOCALE_LATVIAN; - case QLocale::Lithuanian: return C_LOCALE_LITHUANIAN; - case QLocale::Malay: return C_LOCALE_MALAY; - case QLocale::Mongolian: return C_LOCALE_MONGOLIAN; - case QLocale::NorwegianBokmal: return C_LOCALE_NORWEGIAN; - case QLocale::Occitan: return C_LOCALE_OCCITAN; - case QLocale::Persian: return C_LOCALE_PERSIAN; - case QLocale::Polish: return C_LOCALE_POLISH; - case QLocale::Portuguese: - if (locale.country() == QLocale::Brazil) - return C_LOCALE_PORTUGUESE_BRAZIL; - return C_LOCALE_PORTUGUESE; - case QLocale::Romanian: return C_LOCALE_ROMANIAN; - case QLocale::Russian: return C_LOCALE_RUSSIAN; - case QLocale::Serbian: return C_LOCALE_SERBIAN; - case QLocale::Slovak: return C_LOCALE_SLOVAK; - case QLocale::Slovenian: return C_LOCALE_SLOVENIAN; - case QLocale::Spanish: return C_LOCALE_SPANISH; - case QLocale::Swedish: return C_LOCALE_SWEDISH; - case QLocale::Thai: return C_LOCALE_THAI; - case QLocale::Turkish: return C_LOCALE_TURKISH; - case QLocale::Ukrainian: return C_LOCALE_UKRAINIAN; - case QLocale::Uzbek: return C_LOCALE_UZBEK; - case QLocale::Vietnamese: return C_LOCALE_VIETNAMESE; - default: - const QString lang = QLocale::languageToString(locale.language()); - qWarning() << "Unrecognized language name: " << lang; - return lang; - } - } - class WheelEventEater final : public QObject { public: @@ -203,17 +128,17 @@ OptionsDialog::OptionsDialog(IGUIApplication *app, QWidget *parent) // Main icons m_ui->tabSelection->item(TAB_UI)->setIcon(UIThemeManager::instance()->getIcon(u"preferences-desktop"_qs)); - m_ui->tabSelection->item(TAB_BITTORRENT)->setIcon(UIThemeManager::instance()->getIcon(u"preferences-bittorrent"_qs)); - m_ui->tabSelection->item(TAB_CONNECTION)->setIcon(UIThemeManager::instance()->getIcon(u"network-connect"_qs)); - m_ui->tabSelection->item(TAB_DOWNLOADS)->setIcon(UIThemeManager::instance()->getIcon(u"download"_qs)); - m_ui->tabSelection->item(TAB_SPEED)->setIcon(UIThemeManager::instance()->getIcon(u"speedometer"_qs)); - m_ui->tabSelection->item(TAB_RSS)->setIcon(UIThemeManager::instance()->getIcon(u"application-rss"_qs)); + m_ui->tabSelection->item(TAB_BITTORRENT)->setIcon(UIThemeManager::instance()->getIcon(u"preferences-bittorrent"_qs, u"preferences-system-network"_qs)); + m_ui->tabSelection->item(TAB_CONNECTION)->setIcon(UIThemeManager::instance()->getIcon(u"network-connect"_qs, u"network-wired"_qs)); + m_ui->tabSelection->item(TAB_DOWNLOADS)->setIcon(UIThemeManager::instance()->getIcon(u"download"_qs, u"folder-download"_qs)); + m_ui->tabSelection->item(TAB_SPEED)->setIcon(UIThemeManager::instance()->getIcon(u"speedometer"_qs, u"chronometer"_qs)); + m_ui->tabSelection->item(TAB_RSS)->setIcon(UIThemeManager::instance()->getIcon(u"application-rss"_qs, u"application-rss+xml"_qs)); #ifdef DISABLE_WEBUI m_ui->tabSelection->item(TAB_WEBUI)->setHidden(true); #else - m_ui->tabSelection->item(TAB_WEBUI)->setIcon(UIThemeManager::instance()->getIcon(u"preferences-webui"_qs)); + m_ui->tabSelection->item(TAB_WEBUI)->setIcon(UIThemeManager::instance()->getIcon(u"preferences-webui"_qs, u"network-server"_qs)); #endif - m_ui->tabSelection->item(TAB_ADVANCED)->setIcon(UIThemeManager::instance()->getIcon(u"preferences-advanced"_qs)); + m_ui->tabSelection->item(TAB_ADVANCED)->setIcon(UIThemeManager::instance()->getIcon(u"preferences-advanced"_qs, u"preferences-other"_qs)); // set uniform size for all icons int maxHeight = -1; @@ -288,6 +213,11 @@ void OptionsDialog::loadBehaviorTabOptions() m_ui->customThemeFilePath->setMode(FileSystemPathEdit::Mode::FileOpen); m_ui->customThemeFilePath->setDialogCaption(tr("Select qBittorrent UI Theme file")); m_ui->customThemeFilePath->setFileNameFilter(tr("qBittorrent UI Theme file (*.qbtheme config.json)")); +#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) + m_ui->checkUseSystemIcon->setChecked(pref->useSystemIcons()); +#else + m_ui->checkUseSystemIcon->setVisible(false); +#endif m_ui->confirmDeletion->setChecked(pref->confirmTorrentDeletion()); m_ui->checkAltRowColors->setChecked(pref->useAlternatingRowColors()); @@ -382,6 +312,9 @@ void OptionsDialog::loadBehaviorTabOptions() connect(m_ui->comboI18n, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); +#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) + connect(m_ui->checkUseSystemIcon, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); +#endif connect(m_ui->checkUseCustomTheme, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->customThemeFilePath, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton); @@ -451,6 +384,9 @@ void OptionsDialog::saveBehaviorTabOptions() const } pref->setLocale(locale); +#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) + pref->useSystemIcons(m_ui->checkUseSystemIcon->isChecked()); +#endif pref->setUseCustomUITheme(m_ui->checkUseCustomTheme->isChecked()); pref->setCustomUIThemePath(m_ui->customThemeFilePath->selectedPath()); @@ -1315,25 +1251,8 @@ void OptionsDialog::initializeLanguageCombo() const QStringList langFiles = langDir.entryList(QStringList(u"qbittorrent_*.qm"_qs), QDir::Files); for (const QString &langFile : langFiles) { - QString localeStr = langFile.mid(12); // remove "qbittorrent_" - localeStr.chop(3); // Remove ".qm" - QString languageName; - if (localeStr.startsWith(u"eo", Qt::CaseInsensitive)) - { - // QLocale doesn't work with that locale. Esperanto isn't a "real" language. - languageName = C_LOCALE_ESPERANTO; - } - else if (localeStr.startsWith(u"ltg", Qt::CaseInsensitive)) - { - // QLocale doesn't work with that locale. - languageName = C_LOCALE_LATGALIAN; - } - else - { - QLocale locale(localeStr); - languageName = languageToLocalizedString(locale); - } - m_ui->comboI18n->addItem(/*QIcon(":/icons/flags/"+country+".svg"), */ languageName, localeStr); + const QString localeStr = langFile.section(u"_"_qs, 1, -1).section(u"."_qs, 0, 0); // remove "qbittorrent_" and ".qm" + m_ui->comboI18n->addItem(/*QIcon(":/icons/flags/"+country+".svg"), */ Utils::Misc::languageToLocalizedString(localeStr), localeStr); qDebug() << "Supported locale:" << localeStr; } } diff --git a/src/gui/optionsdialog.ui b/src/gui/optionsdialog.ui index ea4c73d7f..e407d79e9 100644 --- a/src/gui/optionsdialog.ui +++ b/src/gui/optionsdialog.ui @@ -133,41 +133,18 @@ Interface - - - - Use custom UI Theme + + + + + true + - - true + + Changing Interface settings requires application restart - - - - - UI Theme file: - - - - - - - - - - - Qt::Horizontal - - - - 200 - 20 - - - - @@ -191,15 +168,45 @@ - - - - - true - + + + + Qt::Horizontal + + + 200 + 20 + + + + + + + + Use custom UI Theme + + + true + + + + + + UI Theme file: + + + + + + + + + + + - Changing Interface settings requires application restart + Use icons from system theme diff --git a/src/gui/properties/propertieswidget.cpp b/src/gui/properties/propertieswidget.cpp index 8c63d048f..b8875a6e7 100644 --- a/src/gui/properties/propertieswidget.cpp +++ b/src/gui/properties/propertieswidget.cpp @@ -750,7 +750,7 @@ void PropertiesWidget::displayWebSeedListMenu() if (!rows.isEmpty()) { - menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs), tr("Remove Web seed") + menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs, u"list-remove"_qs), tr("Remove Web seed") , this, &PropertiesWidget::deleteSelectedUrlSeeds); menu->addSeparator(); menu->addAction(UIThemeManager::instance()->getIcon(u"edit-copy"_qs), tr("Copy Web seed URL") diff --git a/src/gui/properties/proptabbar.cpp b/src/gui/properties/proptabbar.cpp index 81941f9fa..d2094a658 100644 --- a/src/gui/properties/proptabbar.cpp +++ b/src/gui/properties/proptabbar.cpp @@ -45,7 +45,7 @@ PropTabBar::PropTabBar(QWidget *parent) // General tab QPushButton *mainInfosButton = new QPushButton( #ifndef Q_OS_MACOS - UIThemeManager::instance()->getIcon(u"help-about"_qs), + UIThemeManager::instance()->getIcon(u"help-about"_qs, u"document-properties"_qs), #endif tr("General"), parent); mainInfosButton->setShortcut(Qt::ALT + Qt::Key_G); @@ -54,7 +54,7 @@ PropTabBar::PropTabBar(QWidget *parent) // Trackers tab QPushButton *trackersButton = new QPushButton( #ifndef Q_OS_MACOS - UIThemeManager::instance()->getIcon(u"trackers"_qs), + UIThemeManager::instance()->getIcon(u"trackers"_qs, u"network-server"_qs), #endif tr("Trackers"), parent); trackersButton->setShortcut(Qt::ALT + Qt::Key_C); diff --git a/src/gui/properties/trackerlistwidget.cpp b/src/gui/properties/trackerlistwidget.cpp index 791700485..e38afdcaa 100644 --- a/src/gui/properties/trackerlistwidget.cpp +++ b/src/gui/properties/trackerlistwidget.cpp @@ -572,7 +572,7 @@ void TrackerListWidget::showTrackerListMenu() { menu->addAction(UIThemeManager::instance()->getIcon(u"edit-rename"_qs),tr("Edit tracker URL...") , this, &TrackerListWidget::editSelectedTracker); - menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs), tr("Remove tracker") + menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs, u"list-remove"_qs), tr("Remove tracker") , this, &TrackerListWidget::deleteSelectedTrackers); menu->addAction(UIThemeManager::instance()->getIcon(u"edit-copy"_qs), tr("Copy tracker URL") , this, &TrackerListWidget::copyTrackerUrl); @@ -580,10 +580,10 @@ void TrackerListWidget::showTrackerListMenu() if (!torrent->isPaused()) { - menu->addAction(UIThemeManager::instance()->getIcon(u"reannounce"_qs), tr("Force reannounce to selected trackers") + menu->addAction(UIThemeManager::instance()->getIcon(u"reannounce"_qs, u"view-refresh"_qs), tr("Force reannounce to selected trackers") , this, &TrackerListWidget::reannounceSelected); menu->addSeparator(); - menu->addAction(UIThemeManager::instance()->getIcon(u"reannounce"_qs), tr("Force reannounce to all trackers") + menu->addAction(UIThemeManager::instance()->getIcon(u"reannounce"_qs, u"view-refresh"_qs), tr("Force reannounce to all trackers") , this, [this]() { BitTorrent::Torrent *h = m_properties->getCurrentTorrent(); diff --git a/src/gui/properties/trackersadditiondialog.cpp b/src/gui/properties/trackersadditiondialog.cpp index 4ea931ddd..5b154c994 100644 --- a/src/gui/properties/trackersadditiondialog.cpp +++ b/src/gui/properties/trackersadditiondialog.cpp @@ -52,7 +52,7 @@ TrackersAdditionDialog::TrackersAdditionDialog(QWidget *parent, BitTorrent::Torr { m_ui->setupUi(this); - m_ui->downloadButton->setIcon(UIThemeManager::instance()->getIcon(u"downloading"_qs)); + m_ui->downloadButton->setIcon(UIThemeManager::instance()->getIcon(u"downloading"_qs, u"download"_qs)); m_ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Add")); connect(m_ui->downloadButton, &QAbstractButton::clicked, this, &TrackersAdditionDialog::onDownloadButtonClicked); diff --git a/src/gui/rss/articlelistwidget.cpp b/src/gui/rss/articlelistwidget.cpp index 8d8d48daf..3f3f3b0aa 100644 --- a/src/gui/rss/articlelistwidget.cpp +++ b/src/gui/rss/articlelistwidget.cpp @@ -105,7 +105,7 @@ void ArticleListWidget::handleArticleRead(RSS::Article *rssArticle) const QColor defaultColor {palette().color(QPalette::Inactive, QPalette::WindowText)}; const QBrush foregroundBrush {UIThemeManager::instance()->getColor(u"RSS.ReadArticle"_qs, defaultColor)}; item->setData(Qt::ForegroundRole, foregroundBrush); - item->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"loading"_qs)); + item->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"loading"_qs, u"sphere"_qs)); checkInvariant(); } @@ -133,14 +133,14 @@ QListWidgetItem *ArticleListWidget::createItem(RSS::Article *article) const const QColor defaultColor {palette().color(QPalette::Inactive, QPalette::WindowText)}; const QBrush foregroundBrush {UIThemeManager::instance()->getColor(u"RSS.ReadArticle"_qs, defaultColor)}; item->setData(Qt::ForegroundRole, foregroundBrush); - item->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"loading"_qs)); + item->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"loading"_qs, u"sphere"_qs)); } else { const QColor defaultColor {palette().color(QPalette::Active, QPalette::Link)}; const QBrush foregroundBrush {UIThemeManager::instance()->getColor(u"RSS.UnreadArticle"_qs, defaultColor)}; item->setData(Qt::ForegroundRole, foregroundBrush); - item->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"loading"_qs)); + item->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"loading"_qs, u"sphere"_qs)); } return item; diff --git a/src/gui/rss/automatedrssdownloader.cpp b/src/gui/rss/automatedrssdownloader.cpp index ff5f31f4e..a8f9204a2 100644 --- a/src/gui/rss/automatedrssdownloader.cpp +++ b/src/gui/rss/automatedrssdownloader.cpp @@ -75,7 +75,7 @@ AutomatedRssDownloader::AutomatedRssDownloader(QWidget *parent) { m_ui->setupUi(this); // Icons - m_ui->removeRuleBtn->setIcon(UIThemeManager::instance()->getIcon(u"edit-clear"_qs)); + m_ui->removeRuleBtn->setIcon(UIThemeManager::instance()->getIcon(u"edit-clear"_qs, u"list-remove"_qs)); m_ui->addRuleBtn->setIcon(UIThemeManager::instance()->getIcon(u"list-add"_qs)); m_ui->addCategoryBtn->setIcon(UIThemeManager::instance()->getIcon(u"list-add"_qs)); @@ -521,7 +521,7 @@ void AutomatedRssDownloader::displayRulesListMenu() { if (selection.count() == 1) { - menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs), tr("Delete rule") + menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs, u"list-remove"_qs), tr("Delete rule") , this, &AutomatedRssDownloader::on_removeRuleBtn_clicked); menu->addSeparator(); menu->addAction(UIThemeManager::instance()->getIcon(u"edit-rename"_qs), tr("Rename rule...") @@ -529,7 +529,7 @@ void AutomatedRssDownloader::displayRulesListMenu() } else { - menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs), tr("Delete selected rules") + menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs, u"list-remove"_qs), tr("Delete selected rules") , this, &AutomatedRssDownloader::on_removeRuleBtn_clicked); } @@ -759,7 +759,7 @@ void AutomatedRssDownloader::updateMustLineValidity() else { m_ui->lineContains->setStyleSheet(u"QLineEdit { color: #ff0000; }"_qs); - m_ui->labelMustStat->setPixmap(UIThemeManager::instance()->getIcon(u"dialog-warning"_qs).pixmap(16, 16)); + m_ui->labelMustStat->setPixmap(UIThemeManager::instance()->getIcon(u"dialog-warning"_qs, u"task-attention"_qs).pixmap(16, 16)); m_ui->labelMustStat->setToolTip(error); } } @@ -806,7 +806,7 @@ void AutomatedRssDownloader::updateMustNotLineValidity() else { m_ui->lineNotContains->setStyleSheet(u"QLineEdit { color: #ff0000; }"_qs); - m_ui->labelMustNotStat->setPixmap(UIThemeManager::instance()->getIcon(u"dialog-warning"_qs).pixmap(16, 16)); + m_ui->labelMustNotStat->setPixmap(UIThemeManager::instance()->getIcon(u"dialog-warning"_qs, u"task-attention"_qs).pixmap(16, 16)); m_ui->labelMustNotStat->setToolTip(error); } } @@ -824,7 +824,7 @@ void AutomatedRssDownloader::updateEpisodeFilterValidity() else { m_ui->lineEFilter->setStyleSheet(u"QLineEdit { color: #ff0000; }"_qs); - m_ui->labelEpFilterStat->setPixmap(UIThemeManager::instance()->getIcon(u"dialog-warning"_qs).pixmap(16, 16)); + m_ui->labelEpFilterStat->setPixmap(UIThemeManager::instance()->getIcon(u"dialog-warning"_qs, u"task-attention"_qs).pixmap(16, 16)); } } diff --git a/src/gui/rss/feedlistwidget.cpp b/src/gui/rss/feedlistwidget.cpp index 06676be1d..8657dcca8 100644 --- a/src/gui/rss/feedlistwidget.cpp +++ b/src/gui/rss/feedlistwidget.cpp @@ -81,7 +81,7 @@ namespace if (feed->isLoading()) return UIThemeManager::instance()->getIcon(u"loading"_qs); if (feed->hasError()) - return UIThemeManager::instance()->getIcon(u"task-reject"_qs); + return UIThemeManager::instance()->getIcon(u"task-reject"_qs, u"unavailable"_qs); return loadIcon(feed->iconPath(), u"application-rss"_qs); } diff --git a/src/gui/rss/rsswidget.cpp b/src/gui/rss/rsswidget.cpp index 52d76a341..e0bfec041 100644 --- a/src/gui/rss/rsswidget.cpp +++ b/src/gui/rss/rsswidget.cpp @@ -64,8 +64,8 @@ RSSWidget::RSSWidget(QWidget *parent) // Icons m_ui->actionCopyFeedURL->setIcon(UIThemeManager::instance()->getIcon(u"edit-copy"_qs)); m_ui->actionDelete->setIcon(UIThemeManager::instance()->getIcon(u"edit-clear"_qs)); - m_ui->actionDownloadTorrent->setIcon(UIThemeManager::instance()->getIcon(u"downloading"_qs)); - m_ui->actionMarkItemsRead->setIcon(UIThemeManager::instance()->getIcon(u"task-complete"_qs)); + m_ui->actionDownloadTorrent->setIcon(UIThemeManager::instance()->getIcon(u"downloading"_qs, u"download"_qs)); + m_ui->actionMarkItemsRead->setIcon(UIThemeManager::instance()->getIcon(u"task-complete"_qs, u"mail-mark-read"_qs)); m_ui->actionNewFolder->setIcon(UIThemeManager::instance()->getIcon(u"folder-new"_qs)); m_ui->actionNewSubscription->setIcon(UIThemeManager::instance()->getIcon(u"list-add"_qs)); m_ui->actionOpenNewsURL->setIcon(UIThemeManager::instance()->getIcon(u"application-url"_qs)); @@ -74,9 +74,9 @@ RSSWidget::RSSWidget(QWidget *parent) m_ui->actionUpdateAllFeeds->setIcon(UIThemeManager::instance()->getIcon(u"view-refresh"_qs)); #ifndef Q_OS_MACOS m_ui->newFeedButton->setIcon(UIThemeManager::instance()->getIcon(u"list-add"_qs)); - m_ui->markReadButton->setIcon(UIThemeManager::instance()->getIcon(u"task-complete"_qs)); + m_ui->markReadButton->setIcon(UIThemeManager::instance()->getIcon(u"task-complete"_qs, u"mail-mark-read"_qs)); m_ui->updateAllButton->setIcon(UIThemeManager::instance()->getIcon(u"view-refresh"_qs)); - m_ui->rssDownloaderBtn->setIcon(UIThemeManager::instance()->getIcon(u"downloading"_qs)); + m_ui->rssDownloaderBtn->setIcon(UIThemeManager::instance()->getIcon(u"downloading"_qs, u"download"_qs)); #endif m_articleListWidget = new ArticleListWidget(m_ui->splitterMain); diff --git a/src/gui/search/searchjobwidget.cpp b/src/gui/search/searchjobwidget.cpp index 9b907c717..3b7cb37d1 100644 --- a/src/gui/search/searchjobwidget.cpp +++ b/src/gui/search/searchjobwidget.cpp @@ -392,7 +392,7 @@ void SearchJobWidget::contextMenuEvent(QContextMenuEvent *event) menu->addAction(UIThemeManager::instance()->getIcon(u"download"_qs) , tr("Open download window"), this, [this]() { downloadTorrents(AddTorrentOption::ShowDialog); }); - menu->addAction(UIThemeManager::instance()->getIcon(u"downloading"_qs) + menu->addAction(UIThemeManager::instance()->getIcon(u"downloading"_qs, u"download"_qs) , tr("Download"), this, [this]() { downloadTorrents(AddTorrentOption::SkipDialog); }); menu->addSeparator(); menu->addAction(UIThemeManager::instance()->getIcon(u"application-url"_qs), tr("Open description page") @@ -401,11 +401,11 @@ void SearchJobWidget::contextMenuEvent(QContextMenuEvent *event) QMenu *copySubMenu = menu->addMenu( UIThemeManager::instance()->getIcon(u"edit-copy"_qs), tr("Copy")); - copySubMenu->addAction(UIThemeManager::instance()->getIcon(u"name"_qs), tr("Name") + copySubMenu->addAction(UIThemeManager::instance()->getIcon(u"name"_qs, u"edit-copy"_qs), tr("Name") , this, &SearchJobWidget::copyTorrentNames); - copySubMenu->addAction(UIThemeManager::instance()->getIcon(u"insert-link"_qs), tr("Download link") + copySubMenu->addAction(UIThemeManager::instance()->getIcon(u"insert-link"_qs, u"edit-copy"_qs), tr("Download link") , this, &SearchJobWidget::copyTorrentDownloadLinks); - copySubMenu->addAction(UIThemeManager::instance()->getIcon(u"application-url"_qs), tr("Description page URL") + copySubMenu->addAction(UIThemeManager::instance()->getIcon(u"application-url"_qs, u"edit-copy"_qs), tr("Description page URL") , this, &SearchJobWidget::copyTorrentURLs); menu->popup(event->globalPos()); diff --git a/src/gui/search/searchwidget.cpp b/src/gui/search/searchwidget.cpp index 03d4a39ff..2896f773e 100644 --- a/src/gui/search/searchwidget.cpp +++ b/src/gui/search/searchwidget.cpp @@ -112,7 +112,7 @@ SearchWidget::SearchWidget(IGUIApplication *app, MainWindow *mainWindow) #ifndef Q_OS_MACOS // Icons m_ui->searchButton->setIcon(UIThemeManager::instance()->getIcon(u"edit-find"_qs)); - m_ui->pluginsButton->setIcon(UIThemeManager::instance()->getIcon(u"plugins"_qs)); + m_ui->pluginsButton->setIcon(UIThemeManager::instance()->getIcon(u"plugins"_qs, u"preferences-system-network"_qs)); #else // On macOS the icons overlap the text otherwise QSize iconSize = m_ui->tabWidget->iconSize(); diff --git a/src/gui/statusbar.cpp b/src/gui/statusbar.cpp index 0f4bda38e..b4877b692 100644 --- a/src/gui/statusbar.cpp +++ b/src/gui/statusbar.cpp @@ -69,7 +69,7 @@ StatusBar::StatusBar(QWidget *parent) connect(m_connecStatusLblIcon, &QAbstractButton::clicked, this, &StatusBar::connectionButtonClicked); m_dlSpeedLbl = new QPushButton(this); - m_dlSpeedLbl->setIcon(UIThemeManager::instance()->getIcon(u"downloading"_qs)); + m_dlSpeedLbl->setIcon(UIThemeManager::instance()->getIcon(u"downloading"_qs, u"downloading_small"_qs)); connect(m_dlSpeedLbl, &QAbstractButton::clicked, this, &StatusBar::capSpeed); m_dlSpeedLbl->setFlat(true); m_dlSpeedLbl->setFocusPolicy(Qt::NoFocus); @@ -78,7 +78,7 @@ StatusBar::StatusBar(QWidget *parent) m_dlSpeedLbl->setMinimumWidth(200); m_upSpeedLbl = new QPushButton(this); - m_upSpeedLbl->setIcon(UIThemeManager::instance()->getIcon(u"upload"_qs)); + m_upSpeedLbl->setIcon(UIThemeManager::instance()->getIcon(u"upload"_qs, u"seeding"_qs)); connect(m_upSpeedLbl, &QAbstractButton::clicked, this, &StatusBar::capSpeed); m_upSpeedLbl->setFlat(true); m_upSpeedLbl->setFocusPolicy(Qt::NoFocus); diff --git a/src/gui/tagfiltermodel.cpp b/src/gui/tagfiltermodel.cpp index 702f9c9e7..c12029efc 100644 --- a/src/gui/tagfiltermodel.cpp +++ b/src/gui/tagfiltermodel.cpp @@ -123,7 +123,7 @@ QVariant TagFilterModel::data(const QModelIndex &index, int role) const switch (role) { case Qt::DecorationRole: - return UIThemeManager::instance()->getIcon(u"tags"_qs); + return UIThemeManager::instance()->getIcon(u"tags"_qs, u"inode-directory"_qs); case Qt::DisplayRole: return u"%1 (%2)"_qs.arg(tagDisplayName(item.tag())).arg(item.torrentsCount()); case Qt::UserRole: diff --git a/src/gui/tagfilterwidget.cpp b/src/gui/tagfilterwidget.cpp index 7d971c516..125769fc3 100644 --- a/src/gui/tagfilterwidget.cpp +++ b/src/gui/tagfilterwidget.cpp @@ -113,16 +113,16 @@ void TagFilterWidget::showMenu() const auto selectedRows = selectionModel()->selectedRows(); if (!selectedRows.empty() && !TagFilterModel::isSpecialItem(selectedRows.first())) { - menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs), tr("Remove tag") + menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs, u"list-remove"_qs), tr("Remove tag") , this, &TagFilterWidget::removeTag); } - menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs), tr("Remove unused tags") + menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs, u"list-remove"_qs), tr("Remove unused tags") , this, &TagFilterWidget::removeUnusedTags); menu->addSeparator(); - menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-start"_qs), tr("Resume torrents") + menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-start"_qs, u"media-playback-start"_qs), tr("Resume torrents") , this, &TagFilterWidget::actionResumeTorrentsTriggered); - menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs), tr("Pause torrents") + menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs, u"media-playback-pause"_qs), tr("Pause torrents") , this, &TagFilterWidget::actionPauseTorrentsTriggered); menu->addAction(UIThemeManager::instance()->getIcon(u"list-remove"_qs), tr("Remove torrents") , this, &TagFilterWidget::actionDeleteTorrentsTriggered); diff --git a/src/gui/torrentcategorydialog.cpp b/src/gui/torrentcategorydialog.cpp index 34cdce354..ccbc3bd62 100644 --- a/src/gui/torrentcategorydialog.cpp +++ b/src/gui/torrentcategorydialog.cpp @@ -133,6 +133,9 @@ QString TorrentCategoryDialog::categoryName() const void TorrentCategoryDialog::setCategoryName(const QString &categoryName) { m_ui->textCategoryName->setText(categoryName); + + const int subcategoryNameStart = categoryName.lastIndexOf(u"/") + 1; + m_ui->textCategoryName->setSelection(subcategoryNameStart, (categoryName.size() - subcategoryNameStart)); } BitTorrent::CategoryOptions TorrentCategoryDialog::categoryOptions() const diff --git a/src/gui/torrentcategorydialog.ui b/src/gui/torrentcategorydialog.ui index 7eaaa7909..2a9c785ef 100644 --- a/src/gui/torrentcategorydialog.ui +++ b/src/gui/torrentcategorydialog.ui @@ -29,13 +29,10 @@ - - - - - 0 - 0 - + + + + Name: @@ -49,10 +46,13 @@ - - - - Name: + + + + + 0 + 0 + @@ -173,6 +173,12 @@ 1 + + textCategoryName + comboSavePath + comboUseDownloadPath + comboDownloadPath + diff --git a/src/gui/torrentcontentmodel.cpp b/src/gui/torrentcontentmodel.cpp index 21bbff55d..0ae77fd57 100644 --- a/src/gui/torrentcontentmodel.cpp +++ b/src/gui/torrentcontentmodel.cpp @@ -70,7 +70,7 @@ namespace { public: UnifiedFileIconProvider() - : m_textPlainIcon {UIThemeManager::instance()->getIcon(u"help-about"_qs)} + : m_textPlainIcon {UIThemeManager::instance()->getIcon(u"help-about"_qs, u"text-plain"_qs)} { } diff --git a/src/gui/transferlistfilterswidget.cpp b/src/gui/transferlistfilterswidget.cpp index 4a7c0281d..0a62098ac 100644 --- a/src/gui/transferlistfilterswidget.cpp +++ b/src/gui/transferlistfilterswidget.cpp @@ -173,31 +173,31 @@ StatusFilterWidget::StatusFilterWidget(QWidget *parent, TransferListWidget *tran // Add status filters auto *all = new QListWidgetItem(this); all->setData(Qt::DisplayRole, tr("All (0)", "this is for the status filter")); - all->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"filter-all"_qs)); + all->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"filter-all"_qs, u"filterall"_qs)); auto *downloading = new QListWidgetItem(this); downloading->setData(Qt::DisplayRole, tr("Downloading (0)")); downloading->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"downloading"_qs)); auto *seeding = new QListWidgetItem(this); seeding->setData(Qt::DisplayRole, tr("Seeding (0)")); - seeding->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"upload"_qs)); + seeding->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"upload"_qs, u"uploading"_qs)); auto *completed = new QListWidgetItem(this); completed->setData(Qt::DisplayRole, tr("Completed (0)")); - completed->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"checked-completed"_qs)); + completed->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"checked-completed"_qs, u"completed"_qs)); auto *resumed = new QListWidgetItem(this); resumed->setData(Qt::DisplayRole, tr("Resumed (0)")); - resumed->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"torrent-start"_qs)); + resumed->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"torrent-start"_qs, u"media-playback-start"_qs)); auto *paused = new QListWidgetItem(this); paused->setData(Qt::DisplayRole, tr("Paused (0)")); - paused->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"torrent-stop"_qs)); + paused->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"torrent-stop"_qs, u"media-playback-pause"_qs)); auto *active = new QListWidgetItem(this); active->setData(Qt::DisplayRole, tr("Active (0)")); - active->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"filter-active"_qs)); + active->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"filter-active"_qs, u"filteractive"_qs)); auto *inactive = new QListWidgetItem(this); inactive->setData(Qt::DisplayRole, tr("Inactive (0)")); - inactive->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"filter-inactive"_qs)); + inactive->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"filter-inactive"_qs, u"filterinactive"_qs)); auto *stalled = new QListWidgetItem(this); stalled->setData(Qt::DisplayRole, tr("Stalled (0)")); - stalled->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"filter-stalled"_qs)); + stalled->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"filter-stalled"_qs, u"filterstalled"_qs)); auto *stalledUploading = new QListWidgetItem(this); stalledUploading->setData(Qt::DisplayRole, tr("Stalled Uploading (0)")); stalledUploading->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"stalledUP"_qs)); @@ -206,7 +206,7 @@ StatusFilterWidget::StatusFilterWidget(QWidget *parent, TransferListWidget *tran stalledDownloading->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"stalledDL"_qs)); auto *checking = new QListWidgetItem(this); checking->setData(Qt::DisplayRole, tr("Checking (0)")); - checking->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"force-recheck"_qs)); + checking->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"force-recheck"_qs, u"checking"_qs)); auto *moving = new QListWidgetItem(this); moving->setData(Qt::DisplayRole, tr("Moving (0)")); moving->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"set-location"_qs)); @@ -308,9 +308,9 @@ void StatusFilterWidget::showMenu() QMenu *menu = new QMenu(this); menu->setAttribute(Qt::WA_DeleteOnClose); - menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-start"_qs), tr("Resume torrents") + menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-start"_qs, u"media-playback-start"_qs), tr("Resume torrents") , transferList, &TransferListWidget::startVisibleTorrents); - menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs), tr("Pause torrents") + menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs, u"media-playback-pause"_qs), tr("Pause torrents") , transferList, &TransferListWidget::pauseVisibleTorrents); menu->addAction(UIThemeManager::instance()->getIcon(u"list-remove"_qs), tr("Remove torrents") , transferList, &TransferListWidget::deleteVisibleTorrents); @@ -371,16 +371,16 @@ TrackerFiltersList::TrackerFiltersList(QWidget *parent, TransferListWidget *tran { auto *allTrackers = new QListWidgetItem(this); allTrackers->setData(Qt::DisplayRole, tr("All (0)", "this is for the tracker filter")); - allTrackers->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"trackers"_qs)); + allTrackers->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"trackers"_qs, u"network-server"_qs)); auto *noTracker = new QListWidgetItem(this); noTracker->setData(Qt::DisplayRole, tr("Trackerless (0)")); - noTracker->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"trackerless"_qs)); + noTracker->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"trackerless"_qs, u"network-server"_qs)); auto *errorTracker = new QListWidgetItem(this); errorTracker->setData(Qt::DisplayRole, tr("Error (0)")); - errorTracker->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"tracker-error"_qs)); + errorTracker->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"tracker-error"_qs, u"dialog-error"_qs)); auto *warningTracker = new QListWidgetItem(this); warningTracker->setData(Qt::DisplayRole, tr("Warning (0)")); - warningTracker->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"tracker-warning"_qs)); + warningTracker->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"tracker-warning"_qs, u"dialog-warning"_qs)); m_trackers[NULL_HOST] = {{}, noTracker}; @@ -474,7 +474,7 @@ void TrackerFiltersList::addItems(const QString &trackerURL, const QVectorsetData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"trackers"_qs)); + trackerItem->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"trackers"_qs, u"network-server"_qs)); const TrackerData trackerData {{}, trackerItem}; trackersIt = m_trackers.insert(host, trackerData); @@ -712,9 +712,9 @@ void TrackerFiltersList::showMenu() QMenu *menu = new QMenu(this); menu->setAttribute(Qt::WA_DeleteOnClose); - menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-start"_qs), tr("Resume torrents") + menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-start"_qs, u"media-playback-start"_qs), tr("Resume torrents") , transferList, &TransferListWidget::startVisibleTorrents); - menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs), tr("Pause torrents") + menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs, u"media-playback-pause"_qs), tr("Pause torrents") , transferList, &TransferListWidget::pauseVisibleTorrents); menu->addAction(UIThemeManager::instance()->getIcon(u"list-remove"_qs), tr("Remove torrents") , transferList, &TransferListWidget::deleteVisibleTorrents); diff --git a/src/gui/transferlistmodel.cpp b/src/gui/transferlistmodel.cpp index 720e93691..4547935d1 100644 --- a/src/gui/transferlistmodel.cpp +++ b/src/gui/transferlistmodel.cpp @@ -158,15 +158,15 @@ TransferListModel::TransferListModel(QObject *parent) {BitTorrent::TorrentState::Error, tr("Errored", "Torrent status, the torrent has an error")} } , m_stateThemeColors {torrentStateColorsFromUITheme()} - , m_checkingIcon {UIThemeManager::instance()->getIcon(u"force-recheck"_qs)} - , m_completedIcon {UIThemeManager::instance()->getIcon(u"checked-completed"_qs)} + , m_checkingIcon {UIThemeManager::instance()->getIcon(u"force-recheck"_qs, u"checking"_qs)} + , m_completedIcon {UIThemeManager::instance()->getIcon(u"checked-completed"_qs, u"completed"_qs)} , m_downloadingIcon {UIThemeManager::instance()->getIcon(u"downloading"_qs)} , m_errorIcon {UIThemeManager::instance()->getIcon(u"error"_qs)} - , m_pausedIcon {UIThemeManager::instance()->getIcon(u"torrent-stop"_qs)} + , m_pausedIcon {UIThemeManager::instance()->getIcon(u"torrent-stop"_qs, u"media-playback-pause"_qs)} , m_queuedIcon {UIThemeManager::instance()->getIcon(u"queued"_qs)} , m_stalledDLIcon {UIThemeManager::instance()->getIcon(u"stalledDL"_qs)} , m_stalledUPIcon {UIThemeManager::instance()->getIcon(u"stalledUP"_qs)} - , m_uploadingIcon {UIThemeManager::instance()->getIcon(u"upload"_qs)} + , m_uploadingIcon {UIThemeManager::instance()->getIcon(u"upload"_qs, u"uploading"_qs)} { configure(); connect(Preferences::instance(), &Preferences::changed, this, &TransferListModel::configure); diff --git a/src/gui/transferlistwidget.cpp b/src/gui/transferlistwidget.cpp index 196334702..9bca61d45 100644 --- a/src/gui/transferlistwidget.cpp +++ b/src/gui/transferlistwidget.cpp @@ -930,11 +930,11 @@ void TransferListWidget::displayListMenu() // Create actions - auto *actionStart = new QAction(UIThemeManager::instance()->getIcon(u"torrent-start"_qs), tr("&Resume", "Resume/start the torrent"), listMenu); + auto *actionStart = new QAction(UIThemeManager::instance()->getIcon(u"torrent-start"_qs, u"media-playback-start"_qs), tr("&Resume", "Resume/start the torrent"), listMenu); connect(actionStart, &QAction::triggered, this, &TransferListWidget::startSelectedTorrents); - auto *actionPause = new QAction(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs), tr("&Pause", "Pause the torrent"), listMenu); + auto *actionPause = new QAction(UIThemeManager::instance()->getIcon(u"torrent-stop"_qs, u"media-playback-pause"_qs), tr("&Pause", "Pause the torrent"), listMenu); connect(actionPause, &QAction::triggered, this, &TransferListWidget::pauseSelectedTorrents); - auto *actionForceStart = new QAction(UIThemeManager::instance()->getIcon(u"torrent-start-forced"_qs), tr("Force Resu&me", "Force Resume/start the torrent"), listMenu); + auto *actionForceStart = new QAction(UIThemeManager::instance()->getIcon(u"torrent-start-forced"_qs, u"media-playback-start"_qs), tr("Force Resu&me", "Force Resume/start the torrent"), listMenu); connect(actionForceStart, &QAction::triggered, this, &TransferListWidget::forceStartSelectedTorrents); auto *actionDelete = new QAction(UIThemeManager::instance()->getIcon(u"list-remove"_qs), tr("&Remove", "Remove the torrent"), listMenu); connect(actionDelete, &QAction::triggered, this, &TransferListWidget::softDeleteSelectedTorrents); @@ -952,21 +952,21 @@ void TransferListWidget::displayListMenu() connect(actionTopQueuePos, &QAction::triggered, this, &TransferListWidget::topQueuePosSelectedTorrents); auto *actionBottomQueuePos = new QAction(UIThemeManager::instance()->getIcon(u"go-bottom"_qs), tr("Move to &bottom", "i.e. Move to bottom of the queue"), listMenu); connect(actionBottomQueuePos, &QAction::triggered, this, &TransferListWidget::bottomQueuePosSelectedTorrents); - auto *actionSetTorrentPath = new QAction(UIThemeManager::instance()->getIcon(u"set-location"_qs), tr("Set loc&ation..."), listMenu); + auto *actionSetTorrentPath = new QAction(UIThemeManager::instance()->getIcon(u"set-location"_qs, u"inode-directory"_qs), tr("Set loc&ation..."), listMenu); connect(actionSetTorrentPath, &QAction::triggered, this, &TransferListWidget::setSelectedTorrentsLocation); - auto *actionForceRecheck = new QAction(UIThemeManager::instance()->getIcon(u"force-recheck"_qs), tr("Force rec&heck"), listMenu); + auto *actionForceRecheck = new QAction(UIThemeManager::instance()->getIcon(u"force-recheck"_qs, u"document-edit-verify"_qs), tr("Force rec&heck"), listMenu); connect(actionForceRecheck, &QAction::triggered, this, &TransferListWidget::recheckSelectedTorrents); - auto *actionForceReannounce = new QAction(UIThemeManager::instance()->getIcon(u"reannounce"_qs), tr("Force r&eannounce"), listMenu); + auto *actionForceReannounce = new QAction(UIThemeManager::instance()->getIcon(u"reannounce"_qs, u"document-edit-verify"_qs), tr("Force r&eannounce"), listMenu); connect(actionForceReannounce, &QAction::triggered, this, &TransferListWidget::reannounceSelectedTorrents); - auto *actionCopyMagnetLink = new QAction(UIThemeManager::instance()->getIcon(u"torrent-magnet"_qs), tr("&Magnet link"), listMenu); + auto *actionCopyMagnetLink = new QAction(UIThemeManager::instance()->getIcon(u"torrent-magnet"_qs, u"kt-magnet"_qs), tr("&Magnet link"), listMenu); connect(actionCopyMagnetLink, &QAction::triggered, this, &TransferListWidget::copySelectedMagnetURIs); - auto *actionCopyID = new QAction(UIThemeManager::instance()->getIcon(u"help-about"_qs), tr("Torrent &ID"), listMenu); + auto *actionCopyID = new QAction(UIThemeManager::instance()->getIcon(u"help-about"_qs, u"edit-copy"_qs), tr("Torrent &ID"), listMenu); connect(actionCopyID, &QAction::triggered, this, &TransferListWidget::copySelectedIDs); - auto *actionCopyName = new QAction(UIThemeManager::instance()->getIcon(u"name"_qs), tr("&Name"), listMenu); + auto *actionCopyName = new QAction(UIThemeManager::instance()->getIcon(u"name"_qs, u"edit-copy"_qs), tr("&Name"), listMenu); connect(actionCopyName, &QAction::triggered, this, &TransferListWidget::copySelectedNames); - auto *actionCopyHash1 = new QAction(UIThemeManager::instance()->getIcon(u"hash"_qs), tr("Info &hash v1"), listMenu); + auto *actionCopyHash1 = new QAction(UIThemeManager::instance()->getIcon(u"hash"_qs, u"edit-copy"_qs), tr("Info &hash v1"), listMenu); connect(actionCopyHash1, &QAction::triggered, this, [this]() { copySelectedInfohashes(CopyInfohashPolicy::Version1); }); - auto *actionCopyHash2 = new QAction(UIThemeManager::instance()->getIcon(u"hash"_qs), tr("Info h&ash v2"), listMenu); + auto *actionCopyHash2 = new QAction(UIThemeManager::instance()->getIcon(u"hash"_qs, u"edit-copy"_qs), tr("Info h&ash v2"), listMenu); connect(actionCopyHash2, &QAction::triggered, this, [this]() { copySelectedInfohashes(CopyInfohashPolicy::Version2); }); auto *actionSuperSeedingMode = new TriStateAction(tr("Super seeding mode"), listMenu); connect(actionSuperSeedingMode, &QAction::triggered, this, &TransferListWidget::setSelectedTorrentsSuperSeeding); @@ -1152,7 +1152,7 @@ void TransferListWidget::displayListMenu() QStringList tags(BitTorrent::Session::instance()->tags().values()); std::sort(tags.begin(), tags.end(), Utils::Compare::NaturalLessThan()); - QMenu *tagsMenu = listMenu->addMenu(UIThemeManager::instance()->getIcon(u"tags"_qs), tr("Ta&gs")); + QMenu *tagsMenu = listMenu->addMenu(UIThemeManager::instance()->getIcon(u"tags"_qs, u"view-categories"_qs), tr("Ta&gs")); tagsMenu->addAction(UIThemeManager::instance()->getIcon(u"list-add"_qs), tr("&Add...", "Add / assign multiple tags...") , this, &TransferListWidget::askAddTagsForSelection); diff --git a/src/gui/uithememanager.cpp b/src/gui/uithememanager.cpp index 1253646b4..25412eb00 100644 --- a/src/gui/uithememanager.cpp +++ b/src/gui/uithememanager.cpp @@ -167,7 +167,10 @@ void UIThemeManager::initInstance() } UIThemeManager::UIThemeManager() - : m_useCustomTheme(Preferences::instance()->useCustomUITheme()) + : m_useCustomTheme {Preferences::instance()->useCustomUITheme()} +#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) + , m_useSystemIcons {Preferences::instance()->useSystemIcons()} +#endif { if (m_useCustomTheme) { @@ -196,14 +199,25 @@ void UIThemeManager::applyStyleSheet() const qApp->setStyleSheet(QString::fromUtf8(m_themeSource->readStyleSheet())); } -QIcon UIThemeManager::getIcon(const QString &iconId, const QString &fallback) const +QIcon UIThemeManager::getIcon(const QString &iconId, [[maybe_unused]] const QString &fallback) const { // Cache to avoid rescaling svg icons const auto iter = m_iconCache.find(iconId); if (iter != m_iconCache.end()) return *iter; - const QIcon icon {getIconPathFromResources(iconId, fallback).data()}; +#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) + // Don't cache system icons because users might change them at run time + if (m_useSystemIcons) + { + auto icon = QIcon::fromTheme(iconId); + if (icon.name() != iconId) + icon = QIcon::fromTheme(fallback, QIcon(getIconPathFromResources(iconId).data())); + return icon; + } +#endif + + const QIcon icon {getIconPathFromResources(iconId).data()}; m_iconCache[iconId] = icon; return icon; } @@ -259,23 +273,33 @@ QIcon UIThemeManager::getSystrayIcon() const Path UIThemeManager::getIconPath(const QString &iconId) const { - return getIconPathFromResources(iconId, {}); +#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) + if (m_useSystemIcons) + { + Path path = Utils::Fs::tempPath() / Path(iconId + u".png"); + if (!path.exists()) + { + const QIcon icon = QIcon::fromTheme(iconId); + if (!icon.isNull()) + icon.pixmap(32).save(path.toString()); + else + path = getIconPathFromResources(iconId); + } + + return path; + } +#endif + + return getIconPathFromResources(iconId); } -Path UIThemeManager::getIconPathFromResources(const QString &iconId, const QString &fallback) const +Path UIThemeManager::getIconPathFromResources(const QString &iconId) const { if (m_useCustomTheme && m_themeSource) { const Path customIcon = m_themeSource->iconPath(iconId); if (!customIcon.isEmpty()) return customIcon; - - if (!fallback.isEmpty()) - { - const Path fallbackIcon = m_themeSource->iconPath(fallback); - if (!fallbackIcon.isEmpty()) - return fallbackIcon; - } } return findIcon(iconId, DEFAULT_ICONS_DIR); diff --git a/src/gui/uithememanager.h b/src/gui/uithememanager.h index 3b3c18cde..fe5bc4738 100644 --- a/src/gui/uithememanager.h +++ b/src/gui/uithememanager.h @@ -70,13 +70,16 @@ public: private: UIThemeManager(); // singleton class - Path getIconPathFromResources(const QString &iconId, const QString &fallback = {}) const; + Path getIconPathFromResources(const QString &iconId) const; void loadColorsFromJSONConfig(); void applyPalette() const; void applyStyleSheet() const; static UIThemeManager *m_instance; const bool m_useCustomTheme; +#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) + const bool m_useSystemIcons; +#endif std::unique_ptr m_themeSource; QHash m_colors; mutable QHash m_iconCache; diff --git a/src/icons/browser-cookies.svg b/src/icons/browser-cookies.svg index c5fdc49d6..0873b0db0 100644 --- a/src/icons/browser-cookies.svg +++ b/src/icons/browser-cookies.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/icons/filter-stalled.svg b/src/icons/filter-stalled.svg index b376051c5..28896ec8a 100644 --- a/src/icons/filter-stalled.svg +++ b/src/icons/filter-stalled.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/icons/view-refresh.svg b/src/icons/view-refresh.svg index 3b5b6a848..bea8382f0 100644 --- a/src/icons/view-refresh.svg +++ b/src/icons/view-refresh.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/webui/api/appcontroller.cpp b/src/webui/api/appcontroller.cpp index 0b82613c7..e0ddc7d5b 100644 --- a/src/webui/api/appcontroller.cpp +++ b/src/webui/api/appcontroller.cpp @@ -299,6 +299,8 @@ void AppController::preferencesAction() // Advanced settings // qBitorrent preferences + // Resume data storage type + data[u"resume_data_storage_type"_qs] = Utils::String::fromEnum(session->resumeDataStorageType()); // Physical memory (RAM) usage limit data[u"memory_working_set_limit"_qs] = app()->memoryWorkingSetLimit(); // Current network interface @@ -776,6 +778,9 @@ void AppController::setPreferencesAction() // Advanced settings // qBittorrent preferences + // Resume data storage type + if (hasKey(u"resume_data_storage_type"_qs)) + session->setResumeDataStorageType(Utils::String::toEnum(it.value().toString(), BitTorrent::ResumeDataStorageType::Legacy)); // Physical memory (RAM) usage limit if (hasKey(u"memory_working_set_limit"_qs)) app()->setMemoryWorkingSetLimit(it.value().toInt()); diff --git a/src/webui/api/freediskspacechecker.h b/src/webui/api/freediskspacechecker.h index 092032bfc..a472f35cc 100644 --- a/src/webui/api/freediskspacechecker.h +++ b/src/webui/api/freediskspacechecker.h @@ -30,13 +30,13 @@ #include -class FreeDiskSpaceChecker : public QObject +class FreeDiskSpaceChecker final : public QObject { Q_OBJECT Q_DISABLE_COPY_MOVE(FreeDiskSpaceChecker) public: - FreeDiskSpaceChecker() = default; + using QObject::QObject; public slots: void check(); diff --git a/src/webui/api/synccontroller.cpp b/src/webui/api/synccontroller.cpp index f3b6c160b..1473d4cd6 100644 --- a/src/webui/api/synccontroller.cpp +++ b/src/webui/api/synccontroller.cpp @@ -32,7 +32,7 @@ #include #include -#include +#include #include "base/bittorrent/cachestatus.h" #include "base/bittorrent/infohash.h" @@ -49,7 +49,6 @@ #include "base/utils/string.h" #include "apierror.h" #include "freediskspacechecker.h" -#include "isessionmanager.h" #include "serialize/serialize_torrent.h" namespace @@ -177,7 +176,7 @@ namespace switch (static_cast(value.type())) { case QMetaType::QVariantMap: - { + { QVariantMap map; processMap(prevData[key].toMap(), value.toMap(), map); if (!map.isEmpty()) @@ -185,7 +184,7 @@ namespace } break; case QMetaType::QVariantHash: - { + { QVariantMap map; processHash(prevData[key].toHash(), value.toHash(), map, removedItems); if (!map.isEmpty()) @@ -195,7 +194,7 @@ namespace } break; case QMetaType::QVariantList: - { + { QVariantList list; processList(prevData[key].toList(), value.toList(), list, removedItems); if (!list.isEmpty()) @@ -372,15 +371,7 @@ namespace SyncController::SyncController(IApplication *app, QObject *parent) : APIController(app, parent) - , m_freeDiskSpaceChecker {new FreeDiskSpaceChecker} - , m_freeDiskSpaceThread {new QThread} { - m_freeDiskSpaceChecker->moveToThread(m_freeDiskSpaceThread.get()); - - connect(m_freeDiskSpaceThread.get(), &QThread::finished, m_freeDiskSpaceChecker, &QObject::deleteLater); - connect(m_freeDiskSpaceChecker, &FreeDiskSpaceChecker::checked, this, &SyncController::freeDiskSpaceSizeUpdated); - - m_freeDiskSpaceThread->start(); invokeChecker(); m_freeDiskSpaceElapsedTimer.start(); } @@ -595,20 +586,27 @@ void SyncController::torrentPeersAction() qint64 SyncController::getFreeDiskSpace() { if (m_freeDiskSpaceElapsedTimer.hasExpired(FREEDISKSPACE_CHECK_TIMEOUT)) - { invokeChecker(); - m_freeDiskSpaceElapsedTimer.restart(); - } return m_freeDiskSpace; } -void SyncController::freeDiskSpaceSizeUpdated(qint64 freeSpaceSize) +void SyncController::invokeChecker() { - m_freeDiskSpace = freeSpaceSize; -} + if (m_isFreeDiskSpaceCheckerRunning) + return; -void SyncController::invokeChecker() const -{ - QMetaObject::invokeMethod(m_freeDiskSpaceChecker, &FreeDiskSpaceChecker::check, Qt::QueuedConnection); + auto *freeDiskSpaceChecker = new FreeDiskSpaceChecker; + connect(freeDiskSpaceChecker, &FreeDiskSpaceChecker::checked, this, [this](const qint64 freeSpaceSize) + { + m_freeDiskSpace = freeSpaceSize; + m_isFreeDiskSpaceCheckerRunning = false; + m_freeDiskSpaceElapsedTimer.restart(); + }); + connect(freeDiskSpaceChecker, &FreeDiskSpaceChecker::checked, freeDiskSpaceChecker, &QObject::deleteLater); + m_isFreeDiskSpaceCheckerRunning = true; + QThreadPool::globalInstance()->start([freeDiskSpaceChecker] + { + freeDiskSpaceChecker->check(); + }); } diff --git a/src/webui/api/synccontroller.h b/src/webui/api/synccontroller.h index aae77c36f..9d9a4014f 100644 --- a/src/webui/api/synccontroller.h +++ b/src/webui/api/synccontroller.h @@ -31,7 +31,6 @@ #include #include -#include "base/utils/thread.h" #include "apicontroller.h" class QThread; @@ -51,16 +50,14 @@ public: private slots: void maindataAction(); void torrentPeersAction(); - void freeDiskSpaceSizeUpdated(qint64 freeSpaceSize); private: qint64 getFreeDiskSpace(); - void invokeChecker() const; + void invokeChecker(); qint64 m_freeDiskSpace = 0; - FreeDiskSpaceChecker *m_freeDiskSpaceChecker = nullptr; - Utils::Thread::UniquePtr m_freeDiskSpaceThread; QElapsedTimer m_freeDiskSpaceElapsedTimer; + bool m_isFreeDiskSpaceCheckerRunning = false; QVariantMap m_lastMaindataResponse; QVariantMap m_lastAcceptedMaindataResponse; diff --git a/src/webui/webapplication.cpp b/src/webui/webapplication.cpp index d35a48c87..f16e6e812 100644 --- a/src/webui/webapplication.cpp +++ b/src/webui/webapplication.cpp @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -112,6 +113,22 @@ namespace return u"no-store"_qs; } + + QString createLanguagesOptionsHtml() + { + // List language files + const QDir langDir {u":/www/translations"_qs}; + const QStringList langFiles = langDir.entryList(QStringList(u"webui_*.qm"_qs), QDir::Files); + QStringList languages; + for (const QString &langFile : langFiles) + { + const QString localeStr = langFile.section(u"_"_qs, 1, -1).section(u"."_qs, 0, 0); // remove "webui_" and ".qm" + languages << u""_qs.arg(localeStr, Utils::Misc::languageToLocalizedString(localeStr)); + qDebug() << "Supported locale:" << localeStr; + } + + return languages.join(u'\n'); + } } WebApplication::WebApplication(IApplication *app, QObject *parent) @@ -472,13 +489,17 @@ void WebApplication::sendFile(const Path &path) const QMimeType mimeType = QMimeDatabase().mimeTypeForFileNameAndData(path.data(), data); const bool isTranslatable = !m_isAltUIUsed && mimeType.inherits(u"text/plain"_qs); - // Translate the file if (isTranslatable) { auto dataStr = QString::fromUtf8(data); + // Translate the file translateDocument(dataStr); - data = dataStr.toUtf8(); + // Add the language options + if (path == (m_rootFolder / Path(PRIVATE_FOLDER) / Path(u"views/preferences.html"_qs))) + dataStr.replace(u"${LANGUAGE_OPTIONS}"_qs, createLanguagesOptionsHtml()); + + data = dataStr.toUtf8(); m_translatedFiles[path] = {data, mimeType.name(), lastModified}; // caching translated file } diff --git a/src/webui/webapplication.h b/src/webui/webapplication.h index 54a3f3c4e..bc55919b0 100644 --- a/src/webui/webapplication.h +++ b/src/webui/webapplication.h @@ -52,7 +52,7 @@ #include "base/utils/version.h" #include "api/isessionmanager.h" -inline const Utils::Version<3, 2> API_VERSION {2, 8, 18}; +inline const Utils::Version<3, 2> API_VERSION {2, 8, 19}; class APIController; class AuthController; diff --git a/src/webui/www/private/images/filter-stalled.svg b/src/webui/www/private/images/filter-stalled.svg index b376051c5..28896ec8a 100644 --- a/src/webui/www/private/images/filter-stalled.svg +++ b/src/webui/www/private/images/filter-stalled.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/webui/www/private/images/folder-documents.svg b/src/webui/www/private/images/folder-documents.svg new file mode 100644 index 000000000..0b8d29c86 --- /dev/null +++ b/src/webui/www/private/images/folder-documents.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/webui/www/private/images/task-reject.svg b/src/webui/www/private/images/task-reject.svg new file mode 100644 index 000000000..bcd6364c0 --- /dev/null +++ b/src/webui/www/private/images/task-reject.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/webui/www/private/images/view-refresh.svg b/src/webui/www/private/images/view-refresh.svg index 3b5b6a848..bea8382f0 100644 --- a/src/webui/www/private/images/view-refresh.svg +++ b/src/webui/www/private/images/view-refresh.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/webui/www/private/upload.html b/src/webui/www/private/upload.html index 32de2b00d..1a0974356 100644 --- a/src/webui/www/private/upload.html +++ b/src/webui/www/private/upload.html @@ -14,7 +14,7 @@
- +
diff --git a/src/webui/www/private/views/preferences.html b/src/webui/www/private/views/preferences.html index 78f1b42c5..3c3634431 100644 --- a/src/webui/www/private/views/preferences.html +++ b/src/webui/www/private/views/preferences.html @@ -666,62 +666,7 @@ QBT_TR(Language)QBT_TR[CONTEXT=OptionsDialog] @@ -934,6 +879,17 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
QBT_TR(qBittorrent Section)QBT_TR[CONTEXT=OptionsDialog] (QBT_TR(Open documentation)QBT_TR[CONTEXT=HttpServer])
+ + + +
+ + + +
@@ -1408,6 +1364,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD updateWebUICustomHTTPHeadersSettings: updateWebUICustomHTTPHeadersSettings, updateWebUIReverseProxySettings: updateWebUIReverseProxySettings, updateDynDnsSettings: updateDynDnsSettings, + updateWebuiLocaleSelect: updateWebuiLocaleSelect, registerDynDns: registerDynDns, applyPreferences: applyPreferences }; @@ -1737,6 +1694,18 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD }).send(); }; + const updateWebuiLocaleSelect = (selected) => { + let languages = []; + for (let i = 0; i < $('locale_select').options.length; i++) + languages.push($('locale_select').options[i].value); + + if (!languages.includes(selected)) { + const lang = selected.slice(0, selected.indexOf('_')); + selected = languages.includes(lang) ? lang : 'en'; + } + $('locale_select').setProperty('value', selected); + }; + const loadPreferences = function() { const url = 'api/v2/app/preferences'; new Request.JSON({ @@ -2013,7 +1982,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD // Web UI tab // Language - $('locale_select').setProperty('value', ((pref.locale === "en_US") ? "en" : pref.locale)); + updateWebuiLocaleSelect(pref.locale); $('performanceWarning').setProperty('checked', pref.performance_warning); // HTTP Server @@ -2068,6 +2037,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD // Advanced settings // qBittorrent section + $('resumeDataStorageType').setProperty('value', pref.resume_data_storage_type); $('memoryWorkingSetLimit').setProperty('value', pref.memory_working_set_limit); updateNetworkInterfaces(pref.current_network_interface); updateInterfaceAddresses(pref.current_network_interface, pref.current_interface_address); @@ -2495,6 +2465,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD // Update advanced settings // qBittorrent section + settings.set('resume_data_storage_type', $('resumeDataStorageType').getProperty('value')); settings.set('memory_working_set_limit', $('memoryWorkingSetLimit').getProperty('value')); settings.set('current_network_interface', $('networkInterface').getProperty('value')); settings.set('current_interface_address', $('optionalIPAddressToBind').getProperty('value')); diff --git a/src/webui/www/webui.qrc b/src/webui/www/webui.qrc index 4a6a5038d..597453128 100644 --- a/src/webui/www/webui.qrc +++ b/src/webui/www/webui.qrc @@ -309,6 +309,7 @@ private/images/flags/za.svg private/images/flags/zm.svg private/images/flags/zw.svg + private/images/folder-documents.svg private/images/folder-new.svg private/images/force-recheck.svg private/images/go-bottom.svg @@ -348,6 +349,7 @@ private/images/tabs.gif private/images/tags.svg private/images/task-complete.svg + private/images/task-reject.svg private/images/toolbox-divider.gif private/images/torrent-magnet.svg private/images/torrent-start-forced.svg