From 6a1c465d85c64f88652798f426c7c84015c475b6 Mon Sep 17 00:00:00 2001 From: Vladimir Golovnev Date: Wed, 12 Feb 2025 09:33:41 +0300 Subject: [PATCH 01/10] WebAPI: Don't trim string parameters PR #22266. Closes #19485. Closes #22254. --- src/webui/api/rsscontroller.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/webui/api/rsscontroller.cpp b/src/webui/api/rsscontroller.cpp index d103d3198..0dd5d1dc3 100644 --- a/src/webui/api/rsscontroller.cpp +++ b/src/webui/api/rsscontroller.cpp @@ -49,7 +49,7 @@ void RSSController::addFolderAction() { requireParams({u"path"_s}); - const QString path = params()[u"path"_s].trimmed(); + const QString path = params()[u"path"_s]; const nonstd::expected result = RSS::Session::instance()->addFolder(path); if (!result) throw APIError(APIErrorType::Conflict, result.error()); @@ -59,8 +59,8 @@ void RSSController::addFeedAction() { requireParams({u"url"_s, u"path"_s}); - const QString url = params()[u"url"_s].trimmed(); - const QString path = params()[u"path"_s].trimmed(); + const QString url = params()[u"url"_s]; + const QString path = params()[u"path"_s]; const nonstd::expected result = RSS::Session::instance()->addFeed(url, (path.isEmpty() ? url : path)); if (!result) throw APIError(APIErrorType::Conflict, result.error()); @@ -70,8 +70,8 @@ void RSSController::setFeedURLAction() { requireParams({u"path"_s, u"url"_s}); - const QString path = params()[u"path"_s].trimmed(); - const QString url = params()[u"url"_s].trimmed(); + const QString path = params()[u"path"_s]; + const QString url = params()[u"url"_s]; const nonstd::expected result = RSS::Session::instance()->setFeedURL(path, url); if (!result) throw APIError(APIErrorType::Conflict, result.error()); @@ -81,7 +81,7 @@ void RSSController::removeItemAction() { requireParams({u"path"_s}); - const QString path = params()[u"path"_s].trimmed(); + const QString path = params()[u"path"_s]; const nonstd::expected result = RSS::Session::instance()->removeItem(path); if (!result) throw APIError(APIErrorType::Conflict, result.error()); @@ -91,8 +91,8 @@ void RSSController::moveItemAction() { requireParams({u"itemPath"_s, u"destPath"_s}); - const QString itemPath = params()[u"itemPath"_s].trimmed(); - const QString destPath = params()[u"destPath"_s].trimmed(); + const QString itemPath = params()[u"itemPath"_s]; + const QString destPath = params()[u"destPath"_s]; const nonstd::expected result = RSS::Session::instance()->moveItem(itemPath, destPath); if (!result) throw APIError(APIErrorType::Conflict, result.error()); @@ -146,8 +146,8 @@ void RSSController::setRuleAction() { requireParams({u"ruleName"_s, u"ruleDef"_s}); - const QString ruleName {params()[u"ruleName"_s].trimmed()}; - const QByteArray ruleDef {params()[u"ruleDef"_s].trimmed().toUtf8()}; + const QString ruleName {params()[u"ruleName"_s]}; + const QByteArray ruleDef {params()[u"ruleDef"_s].toUtf8()}; const auto jsonObj = QJsonDocument::fromJson(ruleDef).object(); RSS::AutoDownloader::instance()->setRule(RSS::AutoDownloadRule::fromJsonObject(jsonObj, ruleName)); @@ -157,8 +157,8 @@ void RSSController::renameRuleAction() { requireParams({u"ruleName"_s, u"newRuleName"_s}); - const QString ruleName {params()[u"ruleName"_s].trimmed()}; - const QString newRuleName {params()[u"newRuleName"_s].trimmed()}; + const QString ruleName {params()[u"ruleName"_s]}; + const QString newRuleName {params()[u"newRuleName"_s]}; RSS::AutoDownloader::instance()->renameRule(ruleName, newRuleName); } @@ -167,7 +167,7 @@ void RSSController::removeRuleAction() { requireParams({u"ruleName"_s}); - const QString ruleName {params()[u"ruleName"_s].trimmed()}; + const QString ruleName {params()[u"ruleName"_s]}; RSS::AutoDownloader::instance()->removeRule(ruleName); } From 269dfe87e0a29f8c6a86423a8e61cc6515fdb2df Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Sun, 16 Feb 2025 05:08:39 +0800 Subject: [PATCH 02/10] GHA CI: fix AppImage building Upstream now defaults to static runtime and the previous URL is invalid now. Upstream commits: * https://github.com/linuxdeploy/linuxdeploy/commit/c28054bab6623e72e0e27ffec88c41df5ce46667 * https://github.com/linuxdeploy/linuxdeploy-plugin-qt/commit/ce5291e25979d7d6417551d787f308b88c1b8d76 Also fuse2 is not needed now as stated on: https://github.com/AppImage/type2-runtime?tab=readme-ov-file#type2-runtime- PR #22286. --- .github/workflows/ci_ubuntu.yaml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci_ubuntu.yaml b/.github/workflows/ci_ubuntu.yaml index c07e7f2d5..5643d7cce 100644 --- a/.github/workflows/ci_ubuntu.yaml +++ b/.github/workflows/ci_ubuntu.yaml @@ -138,16 +138,15 @@ jobs: - name: Install AppImage run: | - sudo apt install libfuse2 curl \ -L \ -Z \ - -O https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-static-x86_64.AppImage \ - -O https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-static-x86_64.AppImage \ + -O https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage \ + -O https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage \ -O https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage chmod +x \ - linuxdeploy-static-x86_64.AppImage \ - linuxdeploy-plugin-qt-static-x86_64.AppImage \ + linuxdeploy-x86_64.AppImage \ + linuxdeploy-plugin-qt-x86_64.AppImage \ linuxdeploy-plugin-appimage-x86_64.AppImage - name: Prepare files for AppImage @@ -160,12 +159,12 @@ jobs: - name: Package AppImage run: | - ./linuxdeploy-static-x86_64.AppImage --appdir qbittorrent --plugin qt + ./linuxdeploy-x86_64.AppImage --appdir qbittorrent --plugin qt rm qbittorrent/apprun-hooks/* cp .github/workflows/helper/appimage/export_vars.sh qbittorrent/apprun-hooks/export_vars.sh NO_APPSTREAM=1 \ OUTPUT=upload/qbittorrent-CI_Ubuntu_x86_64.AppImage \ - ./linuxdeploy-static-x86_64.AppImage --appdir qbittorrent --output appimage + ./linuxdeploy-x86_64.AppImage --appdir qbittorrent --output appimage - name: Upload build artifacts uses: actions/upload-artifact@v4 From 57b24a200e9abe38b705c6a09260a5194b49fefe Mon Sep 17 00:00:00 2001 From: Daniel Nylander Date: Wed, 19 Feb 2025 11:45:59 +0100 Subject: [PATCH 03/10] NSIS: Update Swedish translation PR #22046. --- dist/windows/installer-translations/swedish.nsh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dist/windows/installer-translations/swedish.nsh b/dist/windows/installer-translations/swedish.nsh index 77db60683..51912d112 100644 --- a/dist/windows/installer-translations/swedish.nsh +++ b/dist/windows/installer-translations/swedish.nsh @@ -7,21 +7,21 @@ LangString inst_desktop ${LANG_SWEDISH} "Skapa skrivbordsgenväg" ;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut" LangString inst_startmenu ${LANG_SWEDISH} "Skapa startmenygenväg" ;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up" -LangString inst_startup ${LANG_SWEDISH} "Starta qBittorrent vid Windows start" +LangString inst_startup ${LANG_SWEDISH} "Starta qBittorrent vid Windows-uppstart" ;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent" LangString inst_torrent ${LANG_SWEDISH} "Öppna .torrent-filer med qBittorrent" ;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent" LangString inst_magnet ${LANG_SWEDISH} "Öppna magnetlänkar med qBittorrent" ;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule" -LangString inst_firewall ${LANG_SWEDISH} "Lägg till Windows-brandväggregel" +LangString inst_firewall ${LANG_SWEDISH} "Lägg till Windows-brandväggsregel" ;LangString inst_pathlimit ${LANG_ENGLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)" LangString inst_pathlimit ${LANG_SWEDISH} "Inaktivera gränsen för Windows-sökvägslängd (260 tecken MAX_PATH-begränsning, kräver Windows 10 1607 eller senare)" ;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule" -LangString inst_firewallinfo ${LANG_SWEDISH} "Lägger till Windows-brandväggregel" +LangString inst_firewallinfo ${LANG_SWEDISH} "Lägger till Windows-brandväggsregel" ;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing." -LangString inst_warning ${LANG_SWEDISH} "qBittorrent körs. Vänligen stäng programmet innan du installerar." +LangString inst_warning ${LANG_SWEDISH} "qBittorrent körs. Stäng programmet innan du installerar." ;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact." -LangString inst_uninstall_question ${LANG_SWEDISH} "Nuvarande version avinstalleras. Användarinställningar och torrenter kommer att förbli intakta." +LangString inst_uninstall_question ${LANG_SWEDISH} "Aktuell version avinstalleras. Användarinställningar och torrenter kommer att förbli intakta." ;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version." LangString inst_unist ${LANG_SWEDISH} "Avinstallerar tidigare version." ;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent." @@ -53,7 +53,7 @@ LangString remove_firewallinfo ${LANG_SWEDISH} "Tar bort Windows-brandväggsrege ;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data" LangString remove_cache ${LANG_SWEDISH} "Ta bort torrenter och cachade data" ;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling." -LangString uninst_warning ${LANG_SWEDISH} "qBittorrent körs. Vänligen stäng programmet innan du avinstallerar." +LangString uninst_warning ${LANG_SWEDISH} "qBittorrent körs. Stäng programmet innan du avinstallerar." ;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:" LangString uninst_tor_warn ${LANG_SWEDISH} "Tar inte bort .torrent-association. Den är associerad med:" ;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:" From d028f46fab755d3b0699853f9ad5d09372f7faf7 Mon Sep 17 00:00:00 2001 From: Luke Memet <1598289+lukemmtt@users.noreply.github.com> Date: Wed, 19 Feb 2025 05:52:51 -0500 Subject: [PATCH 04/10] Fix shift-click selection on macOS PR #22284. Closes #16818. --- src/gui/CMakeLists.txt | 2 + src/gui/macosshiftclickhandler.cpp | 73 ++++++++++++++++++++++++++++++ src/gui/macosshiftclickhandler.h | 50 ++++++++++++++++++++ src/gui/transferlistwidget.cpp | 2 + 4 files changed, 127 insertions(+) create mode 100644 src/gui/macosshiftclickhandler.cpp create mode 100644 src/gui/macosshiftclickhandler.h diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 69c9ae1a2..e25d02945 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -287,6 +287,8 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") macosdockbadge/badger.mm macosdockbadge/badgeview.h macosdockbadge/badgeview.mm + macosshiftclickhandler.h + macosshiftclickhandler.cpp macutilities.h macutilities.mm ) diff --git a/src/gui/macosshiftclickhandler.cpp b/src/gui/macosshiftclickhandler.cpp new file mode 100644 index 000000000..81ba81926 --- /dev/null +++ b/src/gui/macosshiftclickhandler.cpp @@ -0,0 +1,73 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2025 Luke Memet (lukemmtt) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "macosshiftclickhandler.h" + +#include +#include + +MacOSShiftClickHandler::MacOSShiftClickHandler(QTreeView *treeView) + : QObject(treeView) + , m_treeView {treeView} +{ + treeView->installEventFilter(this); +} + +bool MacOSShiftClickHandler::eventFilter(QObject *watched, QEvent *event) +{ + if ((watched == m_treeView) && (event->type() == QEvent::MouseButtonPress)) + { + const auto *mouseEvent = static_cast(event); + if (mouseEvent->button() != Qt::LeftButton) + return false; + + const QModelIndex clickedIndex = m_treeView->indexAt(mouseEvent->position().toPoint()); + if (!clickedIndex.isValid()) + return false; + + const Qt::KeyboardModifiers modifiers = mouseEvent->modifiers(); + const bool shiftPressed = modifiers.testFlag(Qt::ShiftModifier); + + if (shiftPressed && m_lastClickedIndex.isValid()) + { + const QItemSelection selection(m_lastClickedIndex, clickedIndex); + const bool commandPressed = modifiers.testFlag(Qt::ControlModifier); + if (commandPressed) + m_treeView->selectionModel()->select(selection, (QItemSelectionModel::Select | QItemSelectionModel::Rows)); + else + m_treeView->selectionModel()->select(selection, (QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows)); + m_treeView->selectionModel()->setCurrentIndex(clickedIndex, QItemSelectionModel::NoUpdate); + return true; + } + + if (!modifiers.testFlags(Qt::AltModifier | Qt::MetaModifier)) + m_lastClickedIndex = clickedIndex; + } + + return QObject::eventFilter(watched, event); +} diff --git a/src/gui/macosshiftclickhandler.h b/src/gui/macosshiftclickhandler.h new file mode 100644 index 000000000..43d6fda16 --- /dev/null +++ b/src/gui/macosshiftclickhandler.h @@ -0,0 +1,50 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2025 Luke Memet (lukemmtt) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#pragma once + +#include +#include + +class QTreeView; + +// Workaround for QTBUG-115838: Shift-click range selection not working properly on macOS +class MacOSShiftClickHandler final : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(MacOSShiftClickHandler) + +public: + explicit MacOSShiftClickHandler(QTreeView *treeView); + +private: + bool eventFilter(QObject *watched, QEvent *event) override; + + QTreeView *m_treeView = nullptr; + QPersistentModelIndex m_lastClickedIndex; +}; diff --git a/src/gui/transferlistwidget.cpp b/src/gui/transferlistwidget.cpp index 9435b05be..cd6b44653 100644 --- a/src/gui/transferlistwidget.cpp +++ b/src/gui/transferlistwidget.cpp @@ -74,6 +74,7 @@ #include "utils.h" #ifdef Q_OS_MACOS +#include "macosshiftclickhandler.h" #include "macutilities.h" #endif @@ -158,6 +159,7 @@ TransferListWidget::TransferListWidget(IGUIApplication *app, QWidget *parent) setDropIndicatorShown(true); #if defined(Q_OS_MACOS) setAttribute(Qt::WA_MacShowFocusRect, false); + new MacOSShiftClickHandler(this); #endif header()->setFirstSectionMovable(true); header()->setStretchLastSection(false); From 49cfbd9a49e14134380ed43d0e35730885c10fbb Mon Sep 17 00:00:00 2001 From: Vladimir Golovnev Date: Tue, 25 Feb 2025 09:11:03 +0300 Subject: [PATCH 05/10] Improve command line parameters serialization PR #22319. Closes #22306. --- src/app/application.cpp | 67 +++++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/src/app/application.cpp b/src/app/application.cpp index a415950d0..e82df1168 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2015-2024 Vladimir Golovnev + * Copyright (C) 2015-2025 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -124,6 +124,28 @@ namespace const int PIXMAP_CACHE_SIZE = 64 * 1024 * 1024; // 64MiB #endif + const QString PARAM_ADDSTOPPED = u"@addStopped"_s; + const QString PARAM_CATEGORY = u"@category"_s; + const QString PARAM_FIRSTLASTPIECEPRIORITY = u"@firstLastPiecePriority"_s; + const QString PARAM_SAVEPATH = u"@savePath"_s; + const QString PARAM_SEQUENTIAL = u"@sequential"_s; + const QString PARAM_SKIPCHECKING = u"@skipChecking"_s; + const QString PARAM_SKIPDIALOG = u"@skipDialog"_s; + + QString bindParamValue(const QStringView paramName, const QStringView paramValue) + { + return paramName + u'=' + paramValue; + } + + std::pair parseParam(const QStringView param) + { + const qsizetype sepIndex = param.indexOf(u'='); + if (sepIndex >= 0) + return {param.first(sepIndex), param.sliced(sepIndex + 1)}; + + return {param, {}}; + } + QString serializeParams(const QBtCommandLineParameters ¶ms) { QStringList result; @@ -138,85 +160,86 @@ namespace const BitTorrent::AddTorrentParams &addTorrentParams = params.addTorrentParams; if (!addTorrentParams.savePath.isEmpty()) - result.append(u"@savePath=" + addTorrentParams.savePath.data()); + result.append(bindParamValue(PARAM_SAVEPATH, addTorrentParams.savePath.data())); if (addTorrentParams.addStopped.has_value()) - result.append(*addTorrentParams.addStopped ? u"@addStopped=1"_s : u"@addStopped=0"_s); + result.append(bindParamValue(PARAM_ADDSTOPPED, (*addTorrentParams.addStopped ? u"1" : u"0"))); if (addTorrentParams.skipChecking) - result.append(u"@skipChecking"_s); + result.append(PARAM_SKIPCHECKING); if (!addTorrentParams.category.isEmpty()) - result.append(u"@category=" + addTorrentParams.category); + result.append(bindParamValue(PARAM_CATEGORY, addTorrentParams.category)); if (addTorrentParams.sequential) - result.append(u"@sequential"_s); + result.append(PARAM_SEQUENTIAL); if (addTorrentParams.firstLastPiecePriority) - result.append(u"@firstLastPiecePriority"_s); + result.append(PARAM_FIRSTLASTPIECEPRIORITY); if (params.skipDialog.has_value()) - result.append(*params.skipDialog ? u"@skipDialog=1"_s : u"@skipDialog=0"_s); + result.append(bindParamValue(PARAM_SKIPDIALOG, (*params.skipDialog ? u"1" : u"0"))); result += params.torrentSources; return result.join(PARAMS_SEPARATOR); } - QBtCommandLineParameters parseParams(const QString &str) + QBtCommandLineParameters parseParams(const QStringView str) { QBtCommandLineParameters parsedParams; BitTorrent::AddTorrentParams &addTorrentParams = parsedParams.addTorrentParams; - for (QString param : asConst(str.split(PARAMS_SEPARATOR, Qt::SkipEmptyParts))) + for (QStringView param : asConst(str.split(PARAMS_SEPARATOR, Qt::SkipEmptyParts))) { param = param.trimmed(); + const auto [paramName, paramValue] = parseParam(param); // Process strings indicating options specified by the user. - if (param.startsWith(u"@savePath=")) + if (paramName == PARAM_SAVEPATH) { - addTorrentParams.savePath = Path(param.mid(10)); + addTorrentParams.savePath = Path(paramValue.toString()); continue; } - if (param.startsWith(u"@addStopped=")) + if (paramName == PARAM_ADDSTOPPED) { - addTorrentParams.addStopped = (QStringView(param).mid(11).toInt() != 0); + addTorrentParams.addStopped = (paramValue.toInt() != 0); continue; } - if (param == u"@skipChecking") + if (paramName == PARAM_SKIPCHECKING) { addTorrentParams.skipChecking = true; continue; } - if (param.startsWith(u"@category=")) + if (paramName == PARAM_CATEGORY) { - addTorrentParams.category = param.mid(10); + addTorrentParams.category = paramValue.toString(); continue; } - if (param == u"@sequential") + if (paramName == PARAM_SEQUENTIAL) { addTorrentParams.sequential = true; continue; } - if (param == u"@firstLastPiecePriority") + if (paramName == PARAM_FIRSTLASTPIECEPRIORITY) { addTorrentParams.firstLastPiecePriority = true; continue; } - if (param.startsWith(u"@skipDialog=")) + if (paramName == PARAM_SKIPDIALOG) { - parsedParams.skipDialog = (QStringView(param).mid(12).toInt() != 0); + parsedParams.skipDialog = (paramValue.toInt() != 0); continue; } - parsedParams.torrentSources.append(param); + parsedParams.torrentSources.append(param.toString()); } return parsedParams; From 478c2d5b12e7df79fbe517865bc30f7ec1b30c2f Mon Sep 17 00:00:00 2001 From: Vladimir Golovnev Date: Tue, 25 Feb 2025 18:56:15 +0300 Subject: [PATCH 06/10] Don't miss to declare some of the color IDs PR #22330. Closes #22326. --- src/gui/uithemecommon.h | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/gui/uithemecommon.h b/src/gui/uithemecommon.h index 31ae5a456..9f1545fe6 100644 --- a/src/gui/uithemecommon.h +++ b/src/gui/uithemecommon.h @@ -80,7 +80,33 @@ inline QHash defaultUIThemeColors() {u"TransferList.StoppedUploading"_s, {Color::Primer::Light::doneFg, Color::Primer::Dark::doneFg}}, {u"TransferList.Moving"_s, {Color::Primer::Light::successFg, Color::Primer::Dark::successFg}}, {u"TransferList.MissingFiles"_s, {Color::Primer::Light::dangerFg, Color::Primer::Dark::dangerFg}}, - {u"TransferList.Error"_s, {Color::Primer::Light::dangerFg, Color::Primer::Dark::dangerFg}} + {u"TransferList.Error"_s, {Color::Primer::Light::dangerFg, Color::Primer::Dark::dangerFg}}, + + {u"Palette.Window"_s, {{}, {}}}, + {u"Palette.WindowText"_s, {{}, {}}}, + {u"Palette.Base"_s, {{}, {}}}, + {u"Palette.AlternateBase"_s, {{}, {}}}, + {u"Palette.Text"_s, {{}, {}}}, + {u"Palette.ToolTipBase"_s, {{}, {}}}, + {u"Palette.ToolTipText"_s, {{}, {}}}, + {u"Palette.BrightText"_s, {{}, {}}}, + {u"Palette.Highlight"_s, {{}, {}}}, + {u"Palette.HighlightedText"_s, {{}, {}}}, + {u"Palette.Button"_s, {{}, {}}}, + {u"Palette.ButtonText"_s, {{}, {}}}, + {u"Palette.Link"_s, {{}, {}}}, + {u"Palette.LinkVisited"_s, {{}, {}}}, + {u"Palette.Light"_s, {{}, {}}}, + {u"Palette.Midlight"_s, {{}, {}}}, + {u"Palette.Mid"_s, {{}, {}}}, + {u"Palette.Dark"_s, {{}, {}}}, + {u"Palette.Shadow"_s, {{}, {}}}, + {u"Palette.WindowTextDisabled"_s, {{}, {}}}, + {u"Palette.TextDisabled"_s, {{}, {}}}, + {u"Palette.ToolTipTextDisabled"_s, {{}, {}}}, + {u"Palette.BrightTextDisabled"_s, {{}, {}}}, + {u"Palette.HighlightedTextDisabled"_s, {{}, {}}}, + {u"Palette.ButtonTextDisabled"_s, {{}, {}}} }; } From 260394623d94da9f967af217c20237d9c28511d5 Mon Sep 17 00:00:00 2001 From: Vladimir Golovnev Date: Wed, 5 Mar 2025 09:03:00 +0300 Subject: [PATCH 07/10] Add missing includes PR #22362. --- src/base/http/types.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/base/http/types.h b/src/base/http/types.h index dcc839744..ebe342a6f 100644 --- a/src/base/http/types.h +++ b/src/base/http/types.h @@ -30,8 +30,10 @@ #pragma once #include +#include #include #include +#include #include #include "base/global.h" From c1defceccfa77b1854f4c867e1780d320032f763 Mon Sep 17 00:00:00 2001 From: skomerko <168652295+skomerko@users.noreply.github.com> Date: Sun, 2 Mar 2025 10:08:04 +0100 Subject: [PATCH 08/10] WebUI: Fix bug where the 'Tracker editing' dialog displays incorrect data In Trackers table, moving the 'URL' column from its default (2) position caused the 'Tracker editing' dialog to display incorrect data. Steps to reproduce: 1. Move 'URL' column in Trackers table to any position from default 2. Choose tracker URL and click 'Edit tracker URL' PR #22338. --- src/webui/www/private/scripts/prop-trackers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webui/www/private/scripts/prop-trackers.js b/src/webui/www/private/scripts/prop-trackers.js index 796b090c1..3f151d5c8 100644 --- a/src/webui/www/private/scripts/prop-trackers.js +++ b/src/webui/www/private/scripts/prop-trackers.js @@ -196,7 +196,7 @@ window.qBittorrent.PropTrackers ??= (() => { if (current_hash.length === 0) return; - const trackerUrl = encodeURIComponent(element.childNodes[1].textContent); + const trackerUrl = encodeURIComponent(torrentTrackersTable.selectedRowsIds()[0]); new MochaUI.Window({ id: "trackersPage", icon: "images/qbittorrent-tray.svg", From 964be0fa1c4263858e1cf65bc3e894ad3795e02b Mon Sep 17 00:00:00 2001 From: skomerko <168652295+skomerko@users.noreply.github.com> Date: Sun, 2 Mar 2025 10:15:21 +0100 Subject: [PATCH 09/10] WebUI: Maintain row highlight after rearranging table columns This PR fixes a bug where row highlight effect would be lost after reordering columns. PR #22339. --- src/webui/www/private/scripts/dynamicTable.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js index ee9bf554f..123b24e88 100644 --- a/src/webui/www/private/scripts/dynamicTable.js +++ b/src/webui/www/private/scripts/dynamicTable.js @@ -294,6 +294,7 @@ window.qBittorrent.DynamicTable ??= (() => { this.updateTableHeaders(); this.tableBody.replaceChildren(); this.updateTable(true); + this.reselectRows(this.selectedRowsIds()); } if (this.currentHeaderAction === "drag") { resetElementBorderStyle(el); @@ -750,10 +751,7 @@ window.qBittorrent.DynamicTable ??= (() => { reselectRows: function(rowIds) { this.deselectAll(); this.selectedRows = rowIds.slice(); - for (const tr of this.getTrs()) { - if (rowIds.includes(tr.rowId)) - tr.classList.add("selected"); - } + this.setRowClass(); }, setRowClass: function() { From 7003ac3f4dce33d4f6f0b93a721fa417baedc979 Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Fri, 21 Feb 2025 20:44:42 +0800 Subject: [PATCH 10/10] WebUI v5.1 fixes PR #22282. --- src/webui/www/private/css/style.css | 1 + src/webui/www/private/rename_files.html | 2 +- src/webui/www/private/scripts/dynamicTable.js | 8 ++++++-- src/webui/www/private/scripts/prop-files.js | 2 +- src/webui/www/private/scripts/search.js | 11 +++++++---- src/webui/www/private/views/filters.html | 2 +- src/webui/www/private/views/log.html | 2 +- src/webui/www/private/views/rss.html | 4 ++-- src/webui/www/private/views/searchplugins.html | 2 +- src/webui/www/private/views/transferlist.html | 2 +- 10 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/webui/www/private/css/style.css b/src/webui/www/private/css/style.css index 665300d91..f28e0a5cf 100644 --- a/src/webui/www/private/css/style.css +++ b/src/webui/www/private/css/style.css @@ -100,6 +100,7 @@ ol { .dynamicTableDiv, .mochaContentWrapper, .panel, + .scrollableMenu, #rssDetailsView { scrollbar-width: thin; } diff --git a/src/webui/www/private/rename_files.html b/src/webui/www/private/rename_files.html index 27310e0de..f8eefa009 100644 --- a/src/webui/www/private/rename_files.html +++ b/src/webui/www/private/rename_files.html @@ -21,7 +21,7 @@ } = window.MUI.Windows.instances["multiRenamePage"]; const bulkRenameFilesContextMenu = new window.qBittorrent.ContextMenu.ContextMenu({ - targets: "#bulkRenameFilesTableDiv tr", + targets: "#bulkRenameFilesTableDiv tbody tr", menu: "multiRenameFilesMenu", actions: { ToggleSelection: (element, ref) => { diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js index 123b24e88..21cbce2bc 100644 --- a/src/webui/www/private/scripts/dynamicTable.js +++ b/src/webui/www/private/scripts/dynamicTable.js @@ -1750,7 +1750,7 @@ window.qBittorrent.DynamicTable ??= (() => { td.append(span); } - span.style.backgroundImage = `url('images/flags/${country_code ?? "xx"}.svg')`; + span.style.backgroundImage = `url('images/flags/${country_code || "xx"}.svg')`; span.textContent = country; td.title = country; }; @@ -2056,7 +2056,11 @@ window.qBittorrent.DynamicTable ??= (() => { break; } - td.className = statusClass; + for (const c of [...td.classList]) { + if (c.startsWith("tracker")) + td.classList.remove(c); + } + td.classList.add(statusClass); td.textContent = status; td.title = status; }; diff --git a/src/webui/www/private/scripts/prop-files.js b/src/webui/www/private/scripts/prop-files.js index 69875fa92..39c9db083 100644 --- a/src/webui/www/private/scripts/prop-files.js +++ b/src/webui/www/private/scripts/prop-files.js @@ -575,7 +575,7 @@ window.qBittorrent.PropFiles ??= (() => { }; const torrentFilesContextMenu = new window.qBittorrent.ContextMenu.ContextMenu({ - targets: "#torrentFilesTableDiv tr", + targets: "#torrentFilesTableDiv tbody tr", menu: "torrentFilesMenu", actions: { Rename: (element, ref) => { diff --git a/src/webui/www/private/scripts/search.js b/src/webui/www/private/scripts/search.js index cd97bde5d..3dc0b454b 100644 --- a/src/webui/www/private/scripts/search.js +++ b/src/webui/www/private/scripts/search.js @@ -97,8 +97,8 @@ window.qBittorrent.Search ??= (() => { } }, offsets: { - x: -15, - y: -53 + x: 2, + y: -60 }, onShow: function() { setActiveTab(this.options.element); @@ -109,7 +109,7 @@ window.qBittorrent.Search ??= (() => { // load "Search in" preference from local storage $("searchInTorrentName").value = (LocalPreferences.get("search_in_filter") === "names") ? "names" : "everywhere"; const searchResultsTableContextMenu = new window.qBittorrent.ContextMenu.ContextMenu({ - targets: "#searchResultsTableDiv tr", + targets: "#searchResultsTableDiv tbody tr", menu: "searchResultsTableMenu", actions: { Download: downloadSearchTorrent, @@ -184,7 +184,10 @@ window.qBittorrent.Search ??= (() => { closeTabElem.src = "images/application-exit.svg"; closeTabElem.width = "10"; closeTabElem.height = "10"; - closeTabElem.addEventListener("click", function(e) { qBittorrent.Search.closeSearchTab(this); }); + closeTabElem.addEventListener("click", function(e) { + e.stopPropagation(); + closeSearchTab(this); + }); tabElem.prepend(closeTabElem); tabElem.appendChild(getStatusIconElement("QBT_TR(Searching...)QBT_TR[CONTEXT=SearchJobWidget]", "images/queued.svg")); diff --git a/src/webui/www/private/views/filters.html b/src/webui/www/private/views/filters.html index 60eeb5c88..ef7bc88dc 100644 --- a/src/webui/www/private/views/filters.html +++ b/src/webui/www/private/views/filters.html @@ -98,7 +98,7 @@ } }, offsets: { - x: -15, + x: 0, y: 2 }, onShow: function() { diff --git a/src/webui/www/private/views/log.html b/src/webui/www/private/views/log.html index a988e5ba5..0790bc0bb 100644 --- a/src/webui/www/private/views/log.html +++ b/src/webui/www/private/views/log.html @@ -206,7 +206,7 @@ }); const logTableContextMenu = new window.qBittorrent.ContextMenu.ContextMenu({ - targets: ":is(#logMessageView, #logPeerView) tr", + targets: ":is(#logMessageTableDiv, #logPeerTableDiv) tbody tr", menu: "logTableMenu", actions: { Clear: () => { diff --git a/src/webui/www/private/views/rss.html b/src/webui/www/private/views/rss.html index e1aa96bf0..31d0b2561 100644 --- a/src/webui/www/private/views/rss.html +++ b/src/webui/www/private/views/rss.html @@ -218,7 +218,7 @@ $("rssFetchingDisabled").classList.remove("invisible"); const rssFeedContextMenu = new window.qBittorrent.ContextMenu.RssFeedContextMenu({ - targets: "#rssFeedTableDiv tr", + targets: "#rssFeedTableDiv tbody tr", menu: "rssFeedMenu", actions: { update: (el) => { @@ -288,7 +288,7 @@ rssFeedTable.setup("rssFeedTableDiv", "rssFeedFixedHeaderDiv", rssFeedContextMenu); const rssArticleContextMenu = new window.qBittorrent.ContextMenu.RssArticleContextMenu({ - targets: "#rssArticleTableDiv tr", + targets: "#rssArticleTableDiv tbody tr", menu: "rssArticleMenu", actions: { Download: (el) => { diff --git a/src/webui/www/private/views/searchplugins.html b/src/webui/www/private/views/searchplugins.html index e6a6b5bea..768b605fc 100644 --- a/src/webui/www/private/views/searchplugins.html +++ b/src/webui/www/private/views/searchplugins.html @@ -94,7 +94,7 @@ const setup = () => { searchPluginsTable = new window.qBittorrent.DynamicTable.SearchPluginsTable(); searchPluginsTableContextMenu = new window.qBittorrent.ContextMenu.SearchPluginsTableContextMenu({ - targets: "#searchPluginsTableDiv tr", + targets: "#searchPluginsTableDiv tbody tr", menu: "searchPluginsTableMenu", actions: { Enabled: enablePlugin, diff --git a/src/webui/www/private/views/transferlist.html b/src/webui/www/private/views/transferlist.html index 40dfdac7a..0692a06db 100644 --- a/src/webui/www/private/views/transferlist.html +++ b/src/webui/www/private/views/transferlist.html @@ -29,7 +29,7 @@ // create a context menu const contextMenu = new window.qBittorrent.ContextMenu.TorrentsTableContextMenu({ - targets: "#torrentsTableDiv tr", + targets: "#torrentsTableDiv tbody tr", menu: "torrentsTableMenu", actions: { start: (element, ref) => {