mirror of
https://github.com/qbittorrent/qBittorrent
synced 2025-08-19 21:03:30 -07:00
parent
4fc36b9e99
commit
f9f4b60b83
5 changed files with 128 additions and 66 deletions
|
@ -359,7 +359,7 @@ SearchHandler *SearchPluginManager::startSearch(const QString &pattern, const QS
|
|||
// No search pattern entered
|
||||
Q_ASSERT(!pattern.isEmpty());
|
||||
|
||||
return new SearchHandler {pattern, category, usedPlugins, this};
|
||||
return new SearchHandler(pattern, category, usedPlugins, this);
|
||||
}
|
||||
|
||||
QString SearchPluginManager::categoryFullName(const QString &categoryName)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2018-2024 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2018-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
@ -61,12 +61,30 @@ namespace
|
|||
{
|
||||
return QApplication::palette().color(QPalette::Disabled, QPalette::WindowText);
|
||||
}
|
||||
|
||||
QString statusText(SearchJobWidget::Status st)
|
||||
{
|
||||
switch (st)
|
||||
{
|
||||
case SearchJobWidget::Status::Ongoing:
|
||||
return SearchJobWidget::tr("Searching...");
|
||||
case SearchJobWidget::Status::Finished:
|
||||
return SearchJobWidget::tr("Search has finished");
|
||||
case SearchJobWidget::Status::Aborted:
|
||||
return SearchJobWidget::tr("Search aborted");
|
||||
case SearchJobWidget::Status::Error:
|
||||
return SearchJobWidget::tr("An error occurred during search...");
|
||||
case SearchJobWidget::Status::NoResults:
|
||||
return SearchJobWidget::tr("Search returned no results");
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SearchJobWidget::SearchJobWidget(SearchHandler *searchHandler, IGUIApplication *app, QWidget *parent)
|
||||
: GUIApplicationComponent(app, parent)
|
||||
, m_ui {new Ui::SearchJobWidget}
|
||||
, m_searchHandler {searchHandler}
|
||||
, m_nameFilteringMode {u"Search/FilteringMode"_s}
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
@ -94,7 +112,6 @@ SearchJobWidget::SearchJobWidget(SearchHandler *searchHandler, IGUIApplication *
|
|||
m_proxyModel = new SearchSortModel(this);
|
||||
m_proxyModel->setDynamicSortFilter(true);
|
||||
m_proxyModel->setSourceModel(m_searchListModel);
|
||||
m_proxyModel->setNameFilter(searchHandler->pattern());
|
||||
m_ui->resultsBrowser->setModel(m_proxyModel);
|
||||
|
||||
m_ui->resultsBrowser->hideColumn(SearchSortModel::DL_LINK); // Hide url column
|
||||
|
@ -134,8 +151,9 @@ SearchJobWidget::SearchJobWidget(SearchHandler *searchHandler, IGUIApplication *
|
|||
connect(header(), &QHeaderView::sortIndicatorChanged, this, &SearchJobWidget::saveSettings);
|
||||
|
||||
fillFilterComboBoxes();
|
||||
setStatusTip(statusText(m_status));
|
||||
|
||||
updateFilter();
|
||||
assignSearchHandler(searchHandler);
|
||||
|
||||
m_lineEditSearchResultsFilter = new LineEdit(this);
|
||||
m_lineEditSearchResultsFilter->setPlaceholderText(tr("Filter search results..."));
|
||||
|
@ -165,13 +183,6 @@ SearchJobWidget::SearchJobWidget(SearchHandler *searchHandler, IGUIApplication *
|
|||
|
||||
connect(m_ui->resultsBrowser, &QAbstractItemView::doubleClicked, this, &SearchJobWidget::onItemDoubleClicked);
|
||||
|
||||
connect(searchHandler, &SearchHandler::newSearchResults, this, &SearchJobWidget::appendSearchResults);
|
||||
connect(searchHandler, &SearchHandler::searchFinished, this, &SearchJobWidget::searchFinished);
|
||||
connect(searchHandler, &SearchHandler::searchFailed, this, &SearchJobWidget::searchFailed);
|
||||
connect(this, &QObject::destroyed, searchHandler, &QObject::deleteLater);
|
||||
|
||||
setStatusTip(statusText(m_status));
|
||||
|
||||
connect(UIThemeManager::instance(), &UIThemeManager::themeChanged, this, &SearchJobWidget::onUIThemeChanged);
|
||||
}
|
||||
|
||||
|
@ -181,6 +192,15 @@ SearchJobWidget::~SearchJobWidget()
|
|||
delete m_ui;
|
||||
}
|
||||
|
||||
QString SearchJobWidget::searchPattern() const
|
||||
{
|
||||
Q_ASSERT(m_searchHandler);
|
||||
if (!m_searchHandler) [[unlikely]]
|
||||
return {};
|
||||
|
||||
return m_searchHandler->pattern();
|
||||
}
|
||||
|
||||
void SearchJobWidget::onItemDoubleClicked(const QModelIndex &index)
|
||||
{
|
||||
downloadTorrent(index);
|
||||
|
@ -238,8 +258,33 @@ LineEdit *SearchJobWidget::lineEditSearchResultsFilter() const
|
|||
return m_lineEditSearchResultsFilter;
|
||||
}
|
||||
|
||||
void SearchJobWidget::assignSearchHandler(SearchHandler *searchHandler)
|
||||
{
|
||||
Q_ASSERT(searchHandler);
|
||||
if (!searchHandler) [[unlikely]]
|
||||
return;
|
||||
|
||||
m_searchListModel->removeRows(0, m_searchListModel->rowCount());
|
||||
delete m_searchHandler;
|
||||
|
||||
m_searchHandler = searchHandler;
|
||||
m_searchHandler->setParent(this);
|
||||
connect(m_searchHandler, &SearchHandler::newSearchResults, this, &SearchJobWidget::appendSearchResults);
|
||||
connect(m_searchHandler, &SearchHandler::searchFinished, this, &SearchJobWidget::searchFinished);
|
||||
connect(m_searchHandler, &SearchHandler::searchFailed, this, &SearchJobWidget::searchFailed);
|
||||
|
||||
m_proxyModel->setNameFilter(m_searchHandler->pattern());
|
||||
updateFilter();
|
||||
|
||||
setStatus(Status::Ongoing);
|
||||
}
|
||||
|
||||
void SearchJobWidget::cancelSearch()
|
||||
{
|
||||
Q_ASSERT(m_searchHandler);
|
||||
if (!m_searchHandler) [[unlikely]]
|
||||
return;
|
||||
|
||||
m_searchHandler->cancelSearch();
|
||||
}
|
||||
|
||||
|
@ -296,7 +341,8 @@ void SearchJobWidget::copyField(const int column) const
|
|||
|
||||
void SearchJobWidget::setStatus(Status value)
|
||||
{
|
||||
if (m_status == value) return;
|
||||
if (m_status == value)
|
||||
return;
|
||||
|
||||
m_status = value;
|
||||
setStatusTip(statusText(value));
|
||||
|
@ -444,25 +490,6 @@ void SearchJobWidget::contextMenuEvent(QContextMenuEvent *event)
|
|||
menu->popup(event->globalPos());
|
||||
}
|
||||
|
||||
QString SearchJobWidget::statusText(SearchJobWidget::Status st)
|
||||
{
|
||||
switch (st)
|
||||
{
|
||||
case Status::Ongoing:
|
||||
return tr("Searching...");
|
||||
case Status::Finished:
|
||||
return tr("Search has finished");
|
||||
case Status::Aborted:
|
||||
return tr("Search aborted");
|
||||
case Status::Error:
|
||||
return tr("An error occurred during search...");
|
||||
case Status::NoResults:
|
||||
return tr("Search returned no results");
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
SearchJobWidget::NameFilteringMode SearchJobWidget::filteringMode() const
|
||||
{
|
||||
return static_cast<NameFilteringMode>(m_ui->filterMode->itemData(m_ui->filterMode->currentIndex()).toInt());
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2018-2024 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2018-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
@ -79,10 +79,12 @@ public:
|
|||
SearchJobWidget(SearchHandler *searchHandler, IGUIApplication *app, QWidget *parent = nullptr);
|
||||
~SearchJobWidget() override;
|
||||
|
||||
QString searchPattern() const;
|
||||
Status status() const;
|
||||
int visibleResultsCount() const;
|
||||
LineEdit *lineEditSearchResultsFilter() const;
|
||||
|
||||
void assignSearchHandler(SearchHandler *searchHandler);
|
||||
void cancelSearch();
|
||||
|
||||
signals:
|
||||
|
@ -125,8 +127,6 @@ private:
|
|||
void copyTorrentNames() const;
|
||||
void copyField(int column) const;
|
||||
|
||||
static QString statusText(Status st);
|
||||
|
||||
Ui::SearchJobWidget *m_ui = nullptr;
|
||||
SearchHandler *m_searchHandler = nullptr;
|
||||
QStandardItemModel *m_searchListModel = nullptr;
|
||||
|
|
|
@ -162,15 +162,13 @@ bool SearchWidget::eventFilter(QObject *object, QEvent *event)
|
|||
closeTab(tabIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mouseEvent->button() == Qt::RightButton)
|
||||
{
|
||||
QMenu *menu = new QMenu(this);
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
menu->addAction(tr("Close tab"), this, [this, tabIndex]() { closeTab(tabIndex); });
|
||||
menu->addAction(tr("Close all tabs"), this, &SearchWidget::closeAllTabs);
|
||||
menu->popup(QCursor::pos());
|
||||
showTabMenu(tabIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -184,7 +182,8 @@ void SearchWidget::fillCatCombobox()
|
|||
|
||||
using QStrPair = std::pair<QString, QString>;
|
||||
QList<QStrPair> tmpList;
|
||||
for (const QString &cat : asConst(SearchPluginManager::instance()->getPluginCategories(selectedPlugin())))
|
||||
const auto selectedPlugin = m_ui->selectPlugin->itemData(m_ui->selectPlugin->currentIndex()).toString();
|
||||
for (const QString &cat : asConst(SearchPluginManager::instance()->getPluginCategories(selectedPlugin)))
|
||||
tmpList << std::make_pair(SearchPluginManager::categoryFullName(cat), cat);
|
||||
std::sort(tmpList.begin(), tmpList.end(), [](const QStrPair &l, const QStrPair &r) { return (QString::localeAwareCompare(l.first, r.first) < 0); });
|
||||
|
||||
|
@ -223,9 +222,17 @@ QString SearchWidget::selectedCategory() const
|
|||
return m_ui->comboCategory->itemData(m_ui->comboCategory->currentIndex()).toString();
|
||||
}
|
||||
|
||||
QString SearchWidget::selectedPlugin() const
|
||||
QStringList SearchWidget::selectedPlugins() const
|
||||
{
|
||||
return m_ui->selectPlugin->itemData(m_ui->selectPlugin->currentIndex()).toString();
|
||||
const auto itemText = m_ui->selectPlugin->itemData(m_ui->selectPlugin->currentIndex()).toString();
|
||||
|
||||
if (itemText == u"all")
|
||||
return SearchPluginManager::instance()->allPlugins();
|
||||
|
||||
if ((itemText == u"enabled") || (itemText == u"multi"))
|
||||
return SearchPluginManager::instance()->enabledPlugins();
|
||||
|
||||
return {itemText};
|
||||
}
|
||||
|
||||
void SearchWidget::selectActivePage()
|
||||
|
@ -265,7 +272,8 @@ void SearchWidget::tabChanged(const int index)
|
|||
|
||||
void SearchWidget::selectMultipleBox([[maybe_unused]] const int index)
|
||||
{
|
||||
if (selectedPlugin() == u"multi")
|
||||
const auto itemText = m_ui->selectPlugin->itemData(m_ui->selectPlugin->currentIndex()).toString();
|
||||
if (itemText == u"multi")
|
||||
on_pluginsButton_clicked();
|
||||
}
|
||||
|
||||
|
@ -283,6 +291,24 @@ void SearchWidget::toggleFocusBetweenLineEdits()
|
|||
}
|
||||
}
|
||||
|
||||
void SearchWidget::showTabMenu(const int index)
|
||||
{
|
||||
QMenu *menu = new QMenu(this);
|
||||
|
||||
if (auto *searchJobWidget = static_cast<SearchJobWidget *>(m_ui->tabWidget->widget(index));
|
||||
searchJobWidget->status() != SearchJobWidget::Status::Ongoing)
|
||||
{
|
||||
menu->addAction(tr("Refresh"), this, [this, searchJobWidget] { refreshTab(searchJobWidget); });
|
||||
menu->addSeparator();
|
||||
}
|
||||
|
||||
menu->addAction(tr("Close tab"), this, [this, index] { closeTab(index); });
|
||||
menu->addAction(tr("Close all tabs"), this, &SearchWidget::closeAllTabs);
|
||||
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
menu->popup(QCursor::pos());
|
||||
}
|
||||
|
||||
void SearchWidget::on_pluginsButton_clicked()
|
||||
{
|
||||
auto *dlg = new PluginSelectDialog(SearchPluginManager::instance(), this);
|
||||
|
@ -305,12 +331,6 @@ void SearchWidget::giveFocusToSearchInput()
|
|||
// Function called when we click on search button
|
||||
void SearchWidget::on_searchButton_clicked()
|
||||
{
|
||||
if (!Utils::ForeignApps::pythonInfo().isValid())
|
||||
{
|
||||
app()->desktopIntegration()->showNotification(tr("Search Engine"), tr("Please install Python to use the Search Engine."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_activeSearchTab)
|
||||
{
|
||||
m_activeSearchTab->cancelSearch();
|
||||
|
@ -331,20 +351,16 @@ void SearchWidget::on_searchButton_clicked()
|
|||
return;
|
||||
}
|
||||
|
||||
const QString plugin = selectedPlugin();
|
||||
|
||||
QStringList plugins;
|
||||
if (plugin == u"all")
|
||||
plugins = SearchPluginManager::instance()->allPlugins();
|
||||
else if ((plugin == u"enabled") || (plugin == u"multi"))
|
||||
plugins = SearchPluginManager::instance()->enabledPlugins();
|
||||
else
|
||||
plugins << plugin;
|
||||
if (!Utils::ForeignApps::pythonInfo().isValid())
|
||||
{
|
||||
app()->desktopIntegration()->showNotification(tr("Search Engine"), tr("Please install Python to use the Search Engine."));
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug("Search with category: %s", qUtf8Printable(selectedCategory()));
|
||||
|
||||
// Launch search
|
||||
auto *searchHandler = SearchPluginManager::instance()->startSearch(pattern, selectedCategory(), plugins);
|
||||
auto *searchHandler = SearchPluginManager::instance()->startSearch(pattern, selectedCategory(), selectedPlugins());
|
||||
|
||||
// Tab Addition
|
||||
auto *newTab = new SearchJobWidget(searchHandler, app(), this);
|
||||
|
@ -366,12 +382,10 @@ void SearchWidget::tabStatusChanged(QWidget *tab)
|
|||
const int tabIndex = m_ui->tabWidget->indexOf(tab);
|
||||
m_ui->tabWidget->setTabToolTip(tabIndex, tab->statusTip());
|
||||
m_ui->tabWidget->setTabIcon(tabIndex, UIThemeManager::instance()->getIcon(
|
||||
statusIconName(static_cast<SearchJobWidget *>(tab)->status())));
|
||||
statusIconName(static_cast<SearchJobWidget *>(tab)->status())));
|
||||
|
||||
if ((tab == m_activeSearchTab) && (m_activeSearchTab->status() != SearchJobWidget::Status::Ongoing))
|
||||
{
|
||||
Q_ASSERT(m_activeSearchTab->status() != SearchJobWidget::Status::Ongoing);
|
||||
|
||||
emit activeSearchFinished(m_activeSearchTab->status() == SearchJobWidget::Status::Error);
|
||||
|
||||
m_activeSearchTab = nullptr;
|
||||
|
@ -393,3 +407,20 @@ void SearchWidget::closeAllTabs()
|
|||
for (int i = (m_ui->tabWidget->count() - 1); i >= 0; --i)
|
||||
closeTab(i);
|
||||
}
|
||||
|
||||
void SearchWidget::refreshTab(SearchJobWidget *searchJobWidget)
|
||||
{
|
||||
if (!Utils::ForeignApps::pythonInfo().isValid())
|
||||
{
|
||||
app()->desktopIntegration()->showNotification(tr("Search Engine"), tr("Please install Python to use the Search Engine."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Re-launch search
|
||||
auto *searchHandler = SearchPluginManager::instance()->startSearch(searchJobWidget->searchPattern(), selectedCategory(), selectedPlugins());
|
||||
searchJobWidget->assignSearchHandler(searchHandler);
|
||||
if (!m_isNewQueryString)
|
||||
m_ui->searchButton->setText(tr("Stop"));
|
||||
m_activeSearchTab = searchJobWidget;
|
||||
tabStatusChanged(searchJobWidget);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015-2024 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2015-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2020, Will Da Silva <will@willdasilva.xyz>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
|
@ -66,9 +66,13 @@ private slots:
|
|||
private:
|
||||
bool eventFilter(QObject *object, QEvent *event) override;
|
||||
void tabChanged(int index);
|
||||
void tabStatusChanged(QWidget *tab);
|
||||
|
||||
void closeTab(int index);
|
||||
void closeAllTabs();
|
||||
void tabStatusChanged(QWidget *tab);
|
||||
void refreshTab(SearchJobWidget *searchJobWidget);
|
||||
void showTabMenu(int index);
|
||||
|
||||
void selectMultipleBox(int index);
|
||||
void toggleFocusBetweenLineEdits();
|
||||
|
||||
|
@ -78,7 +82,7 @@ private:
|
|||
void searchTextEdited(const QString &);
|
||||
|
||||
QString selectedCategory() const;
|
||||
QString selectedPlugin() const;
|
||||
QStringList selectedPlugins() const;
|
||||
|
||||
Ui::SearchWidget *m_ui = nullptr;
|
||||
QPointer<SearchJobWidget> m_currentSearchTab; // Selected tab
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue