Merge pull request #20498 from sledgehammer999/backport

Backport changes to v4.6.x branch
This commit is contained in:
sledgehammer999 2024-03-24 01:31:22 +02:00 committed by GitHub
commit 4e30e6cb8e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 149 additions and 89 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
.vscode/
src/gui/geoip/GeoIP.dat
src/gui/geoip/GeoIP.dat.gz
src/qbittorrent

View file

@ -3,9 +3,9 @@
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
LangString inst_qbt_req ${LANG_FRENCH} "qBittorrent (requis)"
;LangString inst_desktop ${LANG_ENGLISH} "Create Desktop Shortcut"
LangString inst_desktop ${LANG_FRENCH} "Créer un Raccourci sur le Bureau"
LangString inst_desktop ${LANG_FRENCH} "Créer un raccourci sur le Bureau"
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_FRENCH} "Créer un Raccourci dans le Menu Démarrer"
LangString inst_startmenu ${LANG_FRENCH} "Créer un raccourci dans le Menu Démarrer"
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_FRENCH} "Démarrer qBittorrent au démarrage de Windows"
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
@ -57,6 +57,6 @@ LangString remove_cache ${LANG_FRENCH} "Supprimer les torrents et données en ca
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_FRENCH} "qBittorrent est en cours d'exécution. Fermez l'application avant de la désinstaller."
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_FRENCH} "Ne peut pas supprimer l'association du .torrent. Elle est associée avec :"
LangString uninst_tor_warn ${LANG_FRENCH} "Impossible de supprimer l'association .torrent. Elle est associée avec :"
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
LangString uninst_mag_warn ${LANG_FRENCH} "Ne peut pas supprimer l'association du magnet. Elle est associée avec :"
LangString uninst_mag_warn ${LANG_FRENCH} "Impossible de supprimer l'association magnet. Elle est associée avec :"

View file

@ -31,7 +31,7 @@ LangString inst_requires_64bit ${LANG_HUNGARIAN} "A telepítő csak 64-bites Win
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_HUNGARIAN} "A qBittorrent ezen verziójához minimum Windows 7 szükséges."
;LangString inst_requires_win10 ${LANG_ENGLISH} "This installer requires at least Windows 10 (1809) / Windows Server 2019."
LangString inst_requires_win10 ${LANG_HUNGARIAN} "This installer requires at least Windows 10 (1809) / Windows Server 2019."
LangString inst_requires_win10 ${LANG_HUNGARIAN} "A telepítéshez minimum Windows 10 (1809) / Windows Server 2019 szükséges."
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_HUNGARIAN} "qBittorrent eltávolítása"

View file

@ -264,11 +264,8 @@ Application::Application(int &argc, char **argv)
Logger::initInstance();
const auto portableProfilePath = Path(QCoreApplication::applicationDirPath()) / DEFAULT_PORTABLE_MODE_PROFILE_DIR;
const bool portableModeEnabled = m_commandLineArgs.profileDir.isEmpty() && portableProfilePath.exists();
const Path profileDir = portableModeEnabled
? portableProfilePath
: m_commandLineArgs.profileDir;
const bool portableModeEnabled = m_commandLineArgs.profileDir.isEmpty() && Utils::Fs::isDir(portableProfilePath);
const Path profileDir = portableModeEnabled ? portableProfilePath : m_commandLineArgs.profileDir;
Profile::initInstance(profileDir, m_commandLineArgs.configurationName,
(m_commandLineArgs.relativeFastresumePaths || portableModeEnabled));

View file

@ -33,6 +33,7 @@
#include <functional>
#include <QAction>
#include <QByteArray>
#include <QDateTime>
#include <QDebug>
#include <QDir>
@ -41,6 +42,7 @@
#include <QPushButton>
#include <QScreen>
#include <QShortcut>
#include <QSize>
#include <QString>
#include <QUrl>
#include <QVector>

View file

@ -39,6 +39,9 @@
#include "base/path.h"
#include "base/settingvalue.h"
class LineEdit;
class TorrentFileGuard;
namespace BitTorrent
{
class InfoHash;
@ -54,9 +57,6 @@ namespace Ui
class AddNewTorrentDialog;
}
class LineEdit;
class TorrentFileGuard;
class AddNewTorrentDialog final : public QDialog
{
Q_OBJECT

View file

@ -31,6 +31,7 @@
#include <chrono>
#include <QtGlobal>
#include <QMenu>
#include <QTimer>
@ -300,11 +301,11 @@ QIcon DesktopIntegration::getSystrayIcon() const
icon = UIThemeManager::instance()->getIcon(u"qbittorrent-tray-light"_s);
break;
}
#if ((QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
#ifdef Q_OS_UNIX
// Workaround for invisible tray icon in KDE, https://bugreports.qt.io/browse/QTBUG-53550
return {icon.pixmap(32)};
#else
return icon;
if (qEnvironmentVariable("XDG_CURRENT_DESKTOP").compare(u"KDE", Qt::CaseInsensitive) == 0)
return icon.pixmap(32);
#endif
return icon;
}
#endif // Q_OS_MACOS

View file

@ -1,5 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2024 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2022 Mike Tzou (Chocobo1)
* Copyright (C) 2016 Eugene Shalygin
*
@ -32,6 +33,7 @@
#include <QCompleter>
#include <QContextMenuEvent>
#include <QDir>
#include <QFileIconProvider>
#include <QFileInfo>
#include <QFileSystemModel>
#include <QMenu>
@ -160,36 +162,33 @@ QValidator::State Private::FileSystemPathValidator::validate(QString &input, int
}
Private::FileLineEdit::FileLineEdit(QWidget *parent)
: QLineEdit {parent}
, m_completerModel {new QFileSystemModel(this)}
, m_completer {new QCompleter(this)}
: QLineEdit(parent)
{
m_iconProvider.setOptions(QFileIconProvider::DontUseCustomDirectoryIcons);
m_completerModel->setIconProvider(&m_iconProvider);
m_completerModel->setOptions(QFileSystemModel::DontWatchForChanges);
m_completer->setModel(m_completerModel);
setCompleter(m_completer);
connect(this, &QLineEdit::textChanged, this, &FileLineEdit::validateText);
}
Private::FileLineEdit::~FileLineEdit()
{
delete m_completerModel; // has to be deleted before deleting the m_iconProvider object
delete m_iconProvider;
}
void Private::FileLineEdit::completeDirectoriesOnly(const bool completeDirsOnly)
{
m_completeDirectoriesOnly = completeDirsOnly;
if (m_completerModel)
{
const QDir::Filters filters = QDir::NoDotAndDotDot
| (completeDirsOnly ? QDir::Dirs : QDir::AllEntries);
m_completerModel->setFilter(filters);
}
}
void Private::FileLineEdit::setFilenameFilters(const QStringList &filters)
{
m_completerModel->setNameFilters(filters);
m_filenameFilters = filters;
if (m_completerModel)
m_completerModel->setNameFilters(m_filenameFilters);
}
void Private::FileLineEdit::setBrowseAction(QAction *action)
@ -223,6 +222,24 @@ void Private::FileLineEdit::keyPressEvent(QKeyEvent *e)
if ((e->key() == Qt::Key_Space) && (e->modifiers() == Qt::CTRL))
{
if (!m_completer)
{
m_iconProvider = new QFileIconProvider;
m_iconProvider->setOptions(QFileIconProvider::DontUseCustomDirectoryIcons);
m_completerModel = new QFileSystemModel(this);
m_completerModel->setIconProvider(m_iconProvider);
m_completerModel->setOptions(QFileSystemModel::DontWatchForChanges);
m_completerModel->setNameFilters(m_filenameFilters);
const QDir::Filters filters = QDir::NoDotAndDotDot
| (m_completeDirectoriesOnly ? QDir::Dirs : QDir::AllEntries);
m_completerModel->setFilter(filters);
m_completer = new QCompleter(this);
m_completer->setModel(m_completerModel);
setCompleter(m_completer);
}
m_completerModel->setRootPath(Path(text()).data());
showCompletionPopup();
}

View file

@ -1,5 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2024 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2016 Eugene Shalygin
*
* This program is free software; you can redistribute it and/or
@ -29,7 +30,6 @@
#pragma once
#include <QComboBox>
#include <QFileIconProvider>
#include <QLineEdit>
#include <QtContainerFwd>
#include <QValidator>
@ -39,6 +39,7 @@
class QAction;
class QCompleter;
class QContextMenuEvent;
class QFileIconProvider;
class QFileSystemModel;
class QKeyEvent;
@ -143,7 +144,9 @@ namespace Private
QCompleter *m_completer = nullptr;
QAction *m_browseAction = nullptr;
QAction *m_warningAction = nullptr;
QFileIconProvider m_iconProvider;
QFileIconProvider *m_iconProvider = nullptr;
bool m_completeDirectoriesOnly = false;
QStringList m_filenameFilters;
};
class FileComboEdit final : public QComboBox, public IFileEditorWithCompletion

View file

@ -29,20 +29,41 @@
#include "lineedit.h"
#include <chrono>
#include <QAction>
#include <QKeyEvent>
#include <QTimer>
#include "base/global.h"
#include "uithememanager.h"
using namespace std::chrono_literals;
namespace
{
const std::chrono::milliseconds FILTER_INPUT_DELAY {400};
}
LineEdit::LineEdit(QWidget *parent)
: QLineEdit(parent)
, m_delayedTextChangedTimer {new QTimer(this)}
{
auto *action = new QAction(UIThemeManager::instance()->getIcon(u"edit-find"_s), QString(), this);
addAction(action, QLineEdit::LeadingPosition);
setClearButtonEnabled(true);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
m_delayedTextChangedTimer->setSingleShot(true);
connect(m_delayedTextChangedTimer, &QTimer::timeout, this, [this]
{
emit textChanged(text());
});
connect(this, &QLineEdit::textChanged, this, [this]
{
m_delayedTextChangedTimer->start(FILTER_INPUT_DELAY);
});
}
void LineEdit::keyPressEvent(QKeyEvent *event)

View file

@ -31,6 +31,9 @@
#include <QLineEdit>
class QKeyEvent;
class QTimer;
class LineEdit final : public QLineEdit
{
Q_OBJECT
@ -39,6 +42,11 @@ class LineEdit final : public QLineEdit
public:
explicit LineEdit(QWidget *parent = nullptr);
signals:
void textChanged(const QString &text);
private:
void keyPressEvent(QKeyEvent *event) override;
QTimer *m_delayedTextChangedTimer = nullptr;
};

View file

@ -57,6 +57,7 @@
#include <QShortcut>
#include <QSplitter>
#include <QStatusBar>
#include <QString>
#include <QtGlobal>
#include <QTimer>

View file

@ -42,6 +42,7 @@ class QCloseEvent;
class QComboBox;
class QFileSystemWatcher;
class QSplitter;
class QString;
class QTabWidget;
class QTimer;

View file

@ -104,7 +104,7 @@ void ArticleListWidget::handleArticleRead(RSS::Article *rssArticle)
const QBrush foregroundBrush {UIThemeManager::instance()->getColor(u"RSS.ReadArticle"_s)};
item->setData(Qt::ForegroundRole, foregroundBrush);
item->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"loading"_s, u"sphere"_s));
item->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"rss_read_article"_s, u"sphere"_s));
checkInvariant();
}
@ -131,13 +131,13 @@ QListWidgetItem *ArticleListWidget::createItem(RSS::Article *article) const
{
const QBrush foregroundBrush {UIThemeManager::instance()->getColor(u"RSS.ReadArticle"_s)};
item->setData(Qt::ForegroundRole, foregroundBrush);
item->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"loading"_s, u"sphere"_s));
item->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"rss_read_article"_s, u"sphere"_s));
}
else
{
const QBrush foregroundBrush {UIThemeManager::instance()->getColor(u"RSS.UnreadArticle"_s)};
item->setData(Qt::ForegroundRole, foregroundBrush);
item->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"loading"_s, u"sphere"_s));
item->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"rss_unread_article"_s, u"sphere"_s));
}
return item;

View file

@ -128,9 +128,9 @@ SearchJobWidget::SearchJobWidget(SearchHandler *searchHandler, QWidget *parent)
m_lineEditSearchResultsFilter->setPlaceholderText(tr("Filter search results..."));
m_lineEditSearchResultsFilter->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_lineEditSearchResultsFilter, &QWidget::customContextMenuRequested, this, &SearchJobWidget::showFilterContextMenu);
connect(m_lineEditSearchResultsFilter, &LineEdit::textChanged, this, &SearchJobWidget::filterSearchResults);
m_ui->horizontalLayout->insertWidget(0, m_lineEditSearchResultsFilter);
connect(m_lineEditSearchResultsFilter, &LineEdit::textChanged, this, &SearchJobWidget::filterSearchResults);
connect(m_ui->filterMode, qOverload<int>(&QComboBox::currentIndexChanged)
, this, &SearchJobWidget::updateFilter);
connect(m_ui->minSeeds, &QAbstractSpinBox::editingFinished, this, &SearchJobWidget::updateFilter);

View file

@ -149,6 +149,8 @@ inline QSet<QString> defaultUIThemeIcons()
u"queued"_s,
u"ratio"_s,
u"reannounce"_s,
u"rss_read_article"_s,
u"rss_unread_article"_s,
u"security-high"_s,
u"security-low"_s,
u"set-location"_s,

View file

@ -331,6 +331,8 @@
<file>queued.svg</file>
<file>ratio.svg</file>
<file>reannounce.svg</file>
<file>rss_read_article.png</file>
<file>rss_unread_article.png</file>
<file>security-high.svg</file>
<file>security-low.svg</file>
<file>set-location.svg</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

View file

@ -7,7 +7,7 @@ win32: include(../winconf.pri)
macx: include(../macxconf.pri)
unix:!macx: include(../unixconf.pri)
QT += network sql xml
QT += core-private network sql xml
macx|*-clang*: QMAKE_CXXFLAGS_WARN_ON += -Wno-range-loop-analysis

View file

@ -658,6 +658,8 @@ window.addEvent('load', function() {
$('error_div').set('html', '');
if (response) {
clearTimeout(torrentsFilterInputTimer);
torrentsFilterInputTimer = -1;
let torrentsTableSelectedRows;
let update_categories = false;
let updateTags = false;
@ -1357,18 +1359,14 @@ window.addEvent('load', function() {
$('torrentFilesFilterToolbar').addClass("invisible");
};
let prevTorrentsFilterValue;
let torrentsFilterInputTimer = null;
// listen for changes to torrentsFilterInput
$('torrentsFilterInput').addEvent('input', function() {
const value = $('torrentsFilterInput').get("value");
if (value !== prevTorrentsFilterValue) {
prevTorrentsFilterValue = value;
let torrentsFilterInputTimer = -1;
$('torrentsFilterInput').addEvent('input', () => {
clearTimeout(torrentsFilterInputTimer);
torrentsFilterInputTimer = setTimeout(function() {
torrentsTable.updateTable(false);
}, 400);
}
torrentsFilterInputTimer = setTimeout(() => {
torrentsFilterInputTimer = -1;
torrentsTable.updateTable();
}, window.qBittorrent.Misc.FILTER_INPUT_DELAY);
});
$('transfersTabLink').addEvent('click', showTransfersTab);

View file

@ -175,12 +175,14 @@ window.qBittorrent.ContextMenu = (function() {
const touchstartEvent = e;
this.touchstartTimer = setTimeout(function() {
this.touchstartTimer = -1;
this.triggerMenu(touchstartEvent, elem);
}.bind(this), this.options.touchTimer);
}.bind(this));
elem.addEvent('touchend', function(e) {
e.preventDefault();
clearTimeout(this.touchstartTimer);
this.touchstartTimer = -1;
}.bind(this));
},

View file

@ -701,10 +701,7 @@ window.qBittorrent.DynamicTable = (function() {
return null;
},
updateTable: function(fullUpdate) {
if (fullUpdate === undefined)
fullUpdate = false;
updateTable: function(fullUpdate = false) {
const rows = this.getFilteredAndSortedRows();
for (let i = 0; i < this.selectedRows.length; ++i)

View file

@ -46,6 +46,8 @@ window.qBittorrent.Misc = (function() {
toFixedPointString: toFixedPointString,
containsAllTerms: containsAllTerms,
sleep: sleep,
// variables
FILTER_INPUT_DELAY: 400,
MAX_ETA: 8640000
};
};

View file

@ -366,6 +366,7 @@ window.qBittorrent.PropFiles = (function() {
},
onSuccess: function(files) {
clearTimeout(torrentFilesFilterInputTimer);
torrentFilesFilterInputTimer = -1;
if (files.length === 0) {
torrentFilesTable.clear();
@ -640,26 +641,27 @@ window.qBittorrent.PropFiles = (function() {
if (torrentFilesTable.getSortedColumn() === null)
torrentFilesTable.setSortedColumn('name');
let prevTorrentFilesFilterValue;
let torrentFilesFilterInputTimer = null;
// listen for changes to torrentFilesFilterInput
$('torrentFilesFilterInput').addEvent('input', function() {
const value = $('torrentFilesFilterInput').get("value");
if (value !== prevTorrentFilesFilterValue) {
prevTorrentFilesFilterValue = value;
torrentFilesTable.setFilter(value);
let torrentFilesFilterInputTimer = -1;
$('torrentFilesFilterInput').addEvent('input', () => {
clearTimeout(torrentFilesFilterInputTimer);
torrentFilesFilterInputTimer = setTimeout(function() {
const value = $('torrentFilesFilterInput').get("value");
torrentFilesTable.setFilter(value);
torrentFilesFilterInputTimer = setTimeout(() => {
torrentFilesFilterInputTimer = -1;
if (current_hash === "")
return;
torrentFilesTable.updateTable(false);
torrentFilesTable.updateTable();
if (value.trim() === "")
collapseAllNodes();
else
expandAllNodes();
}, 400);
}
}, window.qBittorrent.Misc.FILTER_INPUT_DELAY);
});
/**

View file

@ -183,7 +183,7 @@
};
let customSyncLogDataInterval = null;
let logFilterTimer;
let logFilterTimer = -1;
let inputtedFilterText = "";
let selectBox;
let selectedLogLevels = JSON.parse(LocalPreferences.get('qbt_selected_log_levels')) || ['1', '2', '4', '8'];
@ -297,9 +297,11 @@
const logFilterChanged = () => {
clearTimeout(logFilterTimer);
logFilterTimer = setTimeout((curTab) => {
logFilterTimer = -1;
tableInfo[curTab].instance.updateTable(false);
updateLabelCount(curTab);
}, 400, currentSelectedTab);
}, window.qBittorrent.Misc.FILTER_INPUT_DELAY, currentSelectedTab);
};
const setCurrentTab = (tab) => {
@ -321,6 +323,7 @@
}
clearTimeout(logFilterTimer);
logFilterTimer = -1;
load();
if (tableInfo[currentSelectedTab].instance.filterText !== getFilterText()) {
@ -377,6 +380,8 @@
if (response.length > 0) {
clearTimeout(logFilterTimer);
logFilterTimer = -1;
for (let i = 0; i < response.length; ++i) {
let row;
if (curTab === 'main') {

View file

@ -233,7 +233,6 @@
max: 0.00,
maxUnit: 3
};
let prevNameFilterValue;
let selectedCategory = "QBT_TR(All categories)QBT_TR[CONTEXT=SearchEngineWidget]";
let selectedPlugin = "all";
let prevSelectedPlugin;
@ -258,18 +257,17 @@
searchResultsTable.setup('searchResultsTableDiv', 'searchResultsTableFixedHeaderDiv', searchResultsTableContextMenu);
getPlugins();
let searchInNameFilterTimer = null;
// listen for changes to searchInNameFilter
$('searchInNameFilter').addEvent('input', function() {
const value = $('searchInNameFilter').get("value");
if (value !== prevNameFilterValue) {
prevNameFilterValue = value;
let searchInNameFilterTimer = -1;
$('searchInNameFilter').addEvent('input', () => {
clearTimeout(searchInNameFilterTimer);
searchInNameFilterTimer = setTimeout(function() {
searchInNameFilterTimer = setTimeout(() => {
searchInNameFilterTimer = -1;
const value = $('searchInNameFilter').get("value");
searchText.filterPattern = value;
searchFilterChanged();
}, 400);
}
}, window.qBittorrent.Misc.FILTER_INPUT_DELAY);
});
new Keyboard({