Skip to content

Commit

Permalink
Allow to refresh existing search
Browse files Browse the repository at this point in the history
PR #22122.
Closes #17184.
  • Loading branch information
glassez authored Jan 8, 2025
1 parent 4fc36b9 commit f9f4b60
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 66 deletions.
2 changes: 1 addition & 1 deletion src/base/search/searchpluginmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
89 changes: 58 additions & 31 deletions src/gui/search/searchjobwidget.cpp
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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..."));
Expand Down Expand Up @@ -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);
}

Expand All @@ -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);
Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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());
Expand Down
6 changes: 3 additions & 3 deletions src/gui/search/searchjobwidget.h
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -79,10 +79,12 @@ class SearchJobWidget final : public GUIApplicationComponent<QWidget>
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:
Expand Down Expand Up @@ -125,8 +127,6 @@ private slots:
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;
Expand Down
87 changes: 59 additions & 28 deletions src/gui/search/searchwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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); });

Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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();
}

Expand All @@ -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);
Expand All @@ -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();
Expand All @@ -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);
Expand All @@ -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;
Expand All @@ -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);
}
10 changes: 7 additions & 3 deletions src/gui/search/searchwidget.h
Original file line number Diff line number Diff line change
@@ -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>
*
Expand Down Expand Up @@ -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();

Expand All @@ -78,7 +82,7 @@ private slots:
void searchTextEdited(const QString &);

QString selectedCategory() const;
QString selectedPlugin() const;
QStringList selectedPlugins() const;

Ui::SearchWidget *m_ui = nullptr;
QPointer<SearchJobWidget> m_currentSearchTab; // Selected tab
Expand Down

0 comments on commit f9f4b60

Please sign in to comment.