Implement gateway for adding new torrents

PR #19355.
This commit is contained in:
Vladimir Golovnev
2023-08-14 18:17:56 +03:00
committed by GitHub
parent e4313d6651
commit dcf3e97291
42 changed files with 933 additions and 639 deletions

View File

@@ -54,6 +54,7 @@ add_library(qbt_gui STATIC
flowlayout.h
fspathedit.h
fspathedit_p.h
guiaddtorrentmanager.h
guiapplicationcomponent.h
hidabletabwidget.h
interfaces/iguiapplication.h
@@ -148,7 +149,7 @@ add_library(qbt_gui STATIC
flowlayout.cpp
fspathedit.cpp
fspathedit_p.cpp
guiapplicationcomponent.cpp
guiaddtorrentmanager.cpp
hidabletabwidget.cpp
ipsubnetwhitelistoptionsdialog.cpp
lineedit.cpp

View File

@@ -37,6 +37,7 @@
#include <QDir>
#include <QFileDialog>
#include <QMenu>
#include <QMessageBox>
#include <QPushButton>
#include <QShortcut>
#include <QString>
@@ -50,7 +51,6 @@
#include "base/bittorrent/torrentcontenthandler.h"
#include "base/bittorrent/torrentcontentlayout.h"
#include "base/global.h"
#include "base/net/downloadmanager.h"
#include "base/preferences.h"
#include "base/settingsstorage.h"
#include "base/torrentfileguard.h"
@@ -58,19 +58,16 @@
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "lineedit.h"
#include "raisedmessagebox.h"
#include "torrenttagsdialog.h"
#include "ui_addnewtorrentdialog.h"
#include "uithememanager.h"
#include "ui_addnewtorrentdialog.h"
namespace
{
#define SETTINGS_KEY(name) u"AddNewTorrentDialog/" name
const QString KEY_ENABLED = SETTINGS_KEY(u"Enabled"_s);
const QString KEY_TOPLEVEL = SETTINGS_KEY(u"TopLevel"_s);
const QString KEY_SAVEPATHHISTORY = SETTINGS_KEY(u"SavePathHistory"_s);
const QString KEY_DOWNLOADPATHHISTORY = SETTINGS_KEY(u"DownloadPathHistory"_s);
const QString KEY_SAVEPATHHISTORYLENGTH = SETTINGS_KEY(u"SavePathHistoryLength"_s);
// just a shortcut
inline SettingsStorage *settings()
@@ -267,19 +264,18 @@ private:
BitTorrent::TorrentContentLayout m_currentContentLayout;
};
const int AddNewTorrentDialog::minPathHistoryLength;
const int AddNewTorrentDialog::maxPathHistoryLength;
AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inParams, QWidget *parent)
AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::TorrentDescriptor &torrentDescr
, const BitTorrent::AddTorrentParams &inParams, QWidget *parent)
: QDialog(parent)
, m_ui(new Ui::AddNewTorrentDialog)
, m_filterLine(new LineEdit(this))
, m_torrentParams(inParams)
, m_storeDialogSize(SETTINGS_KEY(u"DialogSize"_s))
, m_storeDefaultCategory(SETTINGS_KEY(u"DefaultCategory"_s))
, m_storeRememberLastSavePath(SETTINGS_KEY(u"RememberLastSavePath"_s))
, m_storeTreeHeaderState(u"GUI/Qt6/" SETTINGS_KEY(u"TreeHeaderState"_s))
, m_storeSplitterState(u"GUI/Qt6/" SETTINGS_KEY(u"SplitterState"_s))
, m_ui {new Ui::AddNewTorrentDialog}
, m_torrentDescr {torrentDescr}
, m_torrentParams {inParams}
, m_filterLine {new LineEdit(this)}
, m_storeDialogSize {SETTINGS_KEY(u"DialogSize"_s)}
, m_storeDefaultCategory {SETTINGS_KEY(u"DefaultCategory"_s)}
, m_storeRememberLastSavePath {SETTINGS_KEY(u"RememberLastSavePath"_s)}
, m_storeTreeHeaderState {u"GUI/Qt6/" SETTINGS_KEY(u"TreeHeaderState"_s)}
, m_storeSplitterState {u"GUI/Qt6/" SETTINGS_KEY(u"SplitterState"_s)}
{
// TODO: set dialog file properties using m_torrentParams.filePriorities
m_ui->setupUi(this);
@@ -384,8 +380,6 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inP
loadState();
connect(m_ui->doNotDeleteTorrentCheckBox, &QCheckBox::clicked, this, &AddNewTorrentDialog::doNotDeleteTorrentClicked);
connect(m_ui->buttonSelectAll, &QPushButton::clicked, m_ui->contentTreeView, &TorrentContentWidget::checkAll);
connect(m_ui->buttonSelectNone, &QPushButton::clicked, m_ui->contentTreeView, &TorrentContentWidget::checkNone);
@@ -408,6 +402,33 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inP
m_ui->savePath->setFocus();
else
m_ui->categoryComboBox->setFocus();
connect(Preferences::instance(), &Preferences::changed, []
{
const int length = Preferences::instance()->addNewTorrentDialogSavePathHistoryLength();
settings()->storeValue(KEY_SAVEPATHHISTORY
, QStringList(settings()->loadValue<QStringList>(KEY_SAVEPATHHISTORY).mid(0, length)));
});
const BitTorrent::InfoHash infoHash = m_torrentDescr.infoHash();
m_ui->labelInfohash1Data->setText(infoHash.v1().isValid() ? infoHash.v1().toString() : tr("N/A"));
m_ui->labelInfohash2Data->setText(infoHash.v2().isValid() ? infoHash.v2().toString() : tr("N/A"));
if (hasMetadata())
{
setupTreeview();
}
else
{
// Set dialog title
const QString torrentName = m_torrentDescr.name();
setWindowTitle(torrentName.isEmpty() ? tr("Magnet link") : torrentName);
updateDiskSpaceLabel();
setMetadataProgressIndicator(true, tr("Retrieving metadata..."));
}
TMMChanged(m_ui->comboTTM->currentIndex());
}
AddNewTorrentDialog::~AddNewTorrentDialog()
@@ -416,43 +437,19 @@ AddNewTorrentDialog::~AddNewTorrentDialog()
delete m_ui;
}
bool AddNewTorrentDialog::isEnabled()
BitTorrent::TorrentDescriptor AddNewTorrentDialog::torrentDescriptor() const
{
return settings()->loadValue(KEY_ENABLED, true);
return m_torrentDescr;
}
void AddNewTorrentDialog::setEnabled(const bool value)
BitTorrent::AddTorrentParams AddNewTorrentDialog::addTorrentParams() const
{
settings()->storeValue(KEY_ENABLED, value);
return m_torrentParams;
}
bool AddNewTorrentDialog::isTopLevel()
bool AddNewTorrentDialog::isDoNotDeleteTorrentChecked() const
{
return settings()->loadValue(KEY_TOPLEVEL, true);
}
void AddNewTorrentDialog::setTopLevel(const bool value)
{
settings()->storeValue(KEY_TOPLEVEL, value);
}
int AddNewTorrentDialog::savePathHistoryLength()
{
const int defaultHistoryLength = 8;
const int value = settings()->loadValue(KEY_SAVEPATHHISTORYLENGTH, defaultHistoryLength);
return std::clamp(value, minPathHistoryLength, maxPathHistoryLength);
}
void AddNewTorrentDialog::setSavePathHistoryLength(const int value)
{
const int clampedValue = qBound(minPathHistoryLength, value, maxPathHistoryLength);
const int oldValue = savePathHistoryLength();
if (clampedValue == oldValue)
return;
settings()->storeValue(KEY_SAVEPATHHISTORYLENGTH, clampedValue);
settings()->storeValue(KEY_SAVEPATHHISTORY
, QStringList(settings()->loadValue<QStringList>(KEY_SAVEPATHHISTORY).mid(0, clampedValue)));
return m_ui->doNotDeleteTorrentCheckBox->isChecked();
}
void AddNewTorrentDialog::loadState()
@@ -471,140 +468,10 @@ void AddNewTorrentDialog::saveState()
m_storeTreeHeaderState = m_ui->contentTreeView->header()->saveState();
}
void AddNewTorrentDialog::show(const QString &source, const BitTorrent::AddTorrentParams &inParams, QWidget *parent)
{
auto *dlg = new AddNewTorrentDialog(inParams, parent);
dlg->setAttribute(Qt::WA_DeleteOnClose);
if (Net::DownloadManager::hasSupportedScheme(source))
{
const auto *pref = Preferences::instance();
// Launch downloader
Net::DownloadManager::instance()->download(
Net::DownloadRequest(source).limit(pref->getTorrentFileSizeLimit())
, pref->useProxyForGeneralPurposes()
, dlg, &AddNewTorrentDialog::handleDownloadFinished);
return;
}
if (dlg->loadTorrent(source))
dlg->QDialog::show();
else
delete dlg;
}
void AddNewTorrentDialog::show(const QString &source, QWidget *parent)
{
show(source, BitTorrent::AddTorrentParams(), parent);
}
bool AddNewTorrentDialog::loadTorrent(const QString &source)
{
if (const auto parseResult = BitTorrent::TorrentDescriptor::parse(source))
{
m_torrentDescr = parseResult.value();
return loadTorrentImpl();
}
else if (source.startsWith(u"magnet:", Qt::CaseInsensitive))
{
RaisedMessageBox::critical(this, tr("Invalid torrent")
, tr("Failed to load the torrent: %1.\nError: %2").arg(source, parseResult.error()));
return false;
}
const Path decodedPath {source.startsWith(u"file://", Qt::CaseInsensitive)
? QUrl::fromEncoded(source.toLocal8Bit()).toLocalFile() : source};
if (const auto loadResult = BitTorrent::TorrentDescriptor::loadFromFile(decodedPath))
{
m_torrentDescr = loadResult.value();
m_torrentGuard = std::make_unique<TorrentFileGuard>(decodedPath);
return loadTorrentImpl();
}
else
{
RaisedMessageBox::critical(this, tr("Invalid torrent")
, tr("Failed to load the torrent: %1.\nError: %2", "Don't remove the '\n' characters. They insert a newline.")
.arg(decodedPath.toString(), loadResult.error()));
return false;
}
}
bool AddNewTorrentDialog::loadTorrentImpl()
{
const BitTorrent::InfoHash infoHash = m_torrentDescr.infoHash();
// Prevent showing the dialog if download is already present
const auto *btSession = BitTorrent::Session::instance();
if (btSession->isKnownTorrent(infoHash))
{
if (BitTorrent::Torrent *torrent = btSession->findTorrent(infoHash))
{
if (hasMetadata())
{
// Trying to set metadata to existing torrent in case if it has none
torrent->setMetadata(*m_torrentDescr.info());
}
if (torrent->isPrivate() || (hasMetadata() && m_torrentDescr.info()->isPrivate()))
{
RaisedMessageBox::warning(this, tr("Torrent is already present"), tr("Torrent '%1' is already in the transfer list. Trackers cannot be merged because it is a private torrent.").arg(torrent->name()), QMessageBox::Ok);
}
else
{
bool mergeTrackers = btSession->isMergeTrackersEnabled();
if (Preferences::instance()->confirmMergeTrackers())
{
const QMessageBox::StandardButton btn = RaisedMessageBox::question(this, tr("Torrent is already present")
, tr("Torrent '%1' is already in the transfer list. Do you want to merge trackers from new source?").arg(torrent->name())
, (QMessageBox::Yes | QMessageBox::No), QMessageBox::Yes);
mergeTrackers = (btn == QMessageBox::Yes);
}
if (mergeTrackers)
{
torrent->addTrackers(m_torrentDescr.trackers());
torrent->addUrlSeeds(m_torrentDescr.urlSeeds());
}
}
}
else
{
RaisedMessageBox::information(this, tr("Torrent is already present"), tr("Torrent is already queued for processing."), QMessageBox::Ok);
}
return false;
}
m_ui->labelInfohash1Data->setText(infoHash.v1().isValid() ? infoHash.v1().toString() : tr("N/A"));
m_ui->labelInfohash2Data->setText(infoHash.v2().isValid() ? infoHash.v2().toString() : tr("N/A"));
if (hasMetadata())
{
setupTreeview();
}
else
{
connect(BitTorrent::Session::instance(), &BitTorrent::Session::metadataDownloaded, this, &AddNewTorrentDialog::updateMetadata);
// Set dialog title
const QString torrentName = m_torrentDescr.name();
setWindowTitle(torrentName.isEmpty() ? tr("Magnet link") : torrentName);
updateDiskSpaceLabel();
BitTorrent::Session::instance()->downloadMetadata(m_torrentDescr);
setMetadataProgressIndicator(true, tr("Retrieving metadata..."));
}
TMMChanged(m_ui->comboTTM->currentIndex());
return true;
}
void AddNewTorrentDialog::showEvent(QShowEvent *event)
{
QDialog::showEvent(event);
if (!isTopLevel())
if (!Preferences::instance()->isAddNewTorrentDialogTopLevel())
return;
activateWindow();
@@ -818,16 +685,17 @@ void AddNewTorrentDialog::accept()
m_torrentParams.useAutoTMM = useAutoTMM;
if (!useAutoTMM)
{
const int savePathHistoryLength = Preferences::instance()->addNewTorrentDialogSavePathHistoryLength();
const Path savePath = m_ui->savePath->selectedPath();
m_torrentParams.savePath = savePath;
updatePathHistory(KEY_SAVEPATHHISTORY, savePath, savePathHistoryLength());
updatePathHistory(KEY_SAVEPATHHISTORY, savePath, savePathHistoryLength);
m_torrentParams.useDownloadPath = m_ui->groupBoxDownloadPath->isChecked();
if (m_torrentParams.useDownloadPath)
{
const Path downloadPath = m_ui->downloadPath->selectedPath();
m_torrentParams.downloadPath = downloadPath;
updatePathHistory(KEY_DOWNLOADPATHHISTORY, downloadPath, savePathHistoryLength());
updatePathHistory(KEY_DOWNLOADPATHHISTORY, downloadPath, savePathHistoryLength);
}
else
{
@@ -843,11 +711,6 @@ void AddNewTorrentDialog::accept()
setEnabled(!m_ui->checkBoxNeverShow->isChecked());
// Add torrent
BitTorrent::Session::instance()->addTorrent(m_torrentDescr, m_torrentParams);
if (m_torrentGuard)
m_torrentGuard->markAsAddedToSession();
QDialog::accept();
}
@@ -865,13 +728,13 @@ void AddNewTorrentDialog::reject()
void AddNewTorrentDialog::updateMetadata(const BitTorrent::TorrentInfo &metadata)
{
Q_ASSERT(metadata.isValid());
if (!metadata.matchesInfoHash(m_torrentDescr.infoHash()))
if (!metadata.isValid()) [[unlikely]]
return;
disconnect(BitTorrent::Session::instance(), &BitTorrent::Session::metadataDownloaded, this, &AddNewTorrentDialog::updateMetadata);
Q_ASSERT(metadata.matchesInfoHash(m_torrentDescr.infoHash()));
if (!metadata.matchesInfoHash(m_torrentDescr.infoHash())) [[unlikely]]
return;
// Good to go
m_torrentDescr.setTorrentInfo(metadata);
setMetadataProgressIndicator(true, tr("Parsing metadata..."));
@@ -941,50 +804,6 @@ void AddNewTorrentDialog::setupTreeview()
updateDiskSpaceLabel();
}
void AddNewTorrentDialog::handleDownloadFinished(const Net::DownloadResult &downloadResult)
{
switch (downloadResult.status)
{
case Net::DownloadStatus::Success:
if (const auto loadResult = BitTorrent::TorrentDescriptor::load(downloadResult.data))
{
m_torrentDescr = loadResult.value();
}
else
{
RaisedMessageBox::critical(this, tr("Invalid torrent")
, tr("Failed to load from URL: %1.\nError: %2").arg(downloadResult.url, loadResult.error()));
deleteLater();
return;
}
break;
case Net::DownloadStatus::RedirectedToMagnet:
if (const auto parseResult = BitTorrent::TorrentDescriptor::parse(downloadResult.magnetURI))
{
m_torrentDescr = parseResult.value();
}
else
{
RaisedMessageBox::critical(this, tr("Invalid torrent")
, tr("Failed to load torrent. The request was redirected to invalid Magnet URI.\nError: %1")
.arg(parseResult.error()));
deleteLater();
return;
}
break;
default:
RaisedMessageBox::critical(this, tr("Download Error")
, tr("Cannot download '%1': %2").arg(downloadResult.url, downloadResult.errorString));
deleteLater();
return;
}
if (loadTorrentImpl())
open();
else
deleteLater();
}
void AddNewTorrentDialog::TMMChanged(int index)
{
if (index != 1)
@@ -1014,9 +833,3 @@ void AddNewTorrentDialog::TMMChanged(int index)
updateDiskSpaceLabel();
}
void AddNewTorrentDialog::doNotDeleteTorrentClicked(bool checked)
{
if (m_torrentGuard)
m_torrentGuard->setAutoRemove(!checked);
}

View File

@@ -29,8 +29,6 @@
#pragma once
#include <memory>
#include <QDialog>
#include "base/bittorrent/addtorrentparams.h"
@@ -38,23 +36,12 @@
#include "base/path.h"
#include "base/settingvalue.h"
namespace BitTorrent
{
class InfoHash;
}
namespace Net
{
struct DownloadResult;
}
namespace Ui
{
class AddNewTorrentDialog;
}
class LineEdit;
class TorrentFileGuard;
class AddNewTorrentDialog final : public QDialog
{
@@ -62,32 +49,24 @@ class AddNewTorrentDialog final : public QDialog
Q_DISABLE_COPY_MOVE(AddNewTorrentDialog)
public:
static const int minPathHistoryLength = 0;
static const int maxPathHistoryLength = 99;
explicit AddNewTorrentDialog(const BitTorrent::TorrentDescriptor &torrentDescr
, const BitTorrent::AddTorrentParams &inParams, QWidget *parent);
~AddNewTorrentDialog() override;
static bool isEnabled();
static void setEnabled(bool value);
static bool isTopLevel();
static void setTopLevel(bool value);
static int savePathHistoryLength();
static void setSavePathHistoryLength(int value);
BitTorrent::TorrentDescriptor torrentDescriptor() const;
BitTorrent::AddTorrentParams addTorrentParams() const;
bool isDoNotDeleteTorrentChecked() const;
static void show(const QString &source, const BitTorrent::AddTorrentParams &inParams, QWidget *parent);
static void show(const QString &source, QWidget *parent);
void updateMetadata(const BitTorrent::TorrentInfo &metadata);
private slots:
void updateDiskSpaceLabel();
void onSavePathChanged(const Path &newPath);
void onDownloadPathChanged(const Path &newPath);
void onUseDownloadPathChanged(bool checked);
void updateMetadata(const BitTorrent::TorrentInfo &metadata);
void handleDownloadFinished(const Net::DownloadResult &downloadResult);
void TMMChanged(int index);
void categoryChanged(int index);
void contentLayoutChanged();
void doNotDeleteTorrentClicked(bool checked);
void accept() override;
void reject() override;
@@ -95,10 +74,6 @@ private slots:
private:
class TorrentContentAdaptor;
explicit AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inParams, QWidget *parent);
bool loadTorrent(const QString &source);
bool loadTorrentImpl();
void populateSavePaths();
void loadState();
void saveState();
@@ -112,12 +87,11 @@ private:
Ui::AddNewTorrentDialog *m_ui = nullptr;
TorrentContentAdaptor *m_contentAdaptor = nullptr;
BitTorrent::TorrentDescriptor m_torrentDescr;
BitTorrent::AddTorrentParams m_torrentParams;
int m_savePathIndex = -1;
int m_downloadPathIndex = -1;
bool m_useDownloadPath = false;
LineEdit *m_filterLine = nullptr;
std::unique_ptr<TorrentFileGuard> m_torrentGuard;
BitTorrent::AddTorrentParams m_torrentParams;
SettingValue<QSize> m_storeDialogSize;
SettingValue<QString> m_storeDefaultCategory;

View File

@@ -168,8 +168,7 @@ namespace
}
AdvancedSettings::AdvancedSettings(IGUIApplication *app, QWidget *parent)
: QTableWidget(parent)
, GUIApplicationComponent(app)
: GUIApplicationComponent(app, parent)
{
// column
setColumnCount(COL_COUNT);
@@ -306,7 +305,7 @@ void AdvancedSettings::saveAdvancedSettings() const
session->setReannounceWhenAddressChangedEnabled(m_checkBoxReannounceWhenAddressChanged.isChecked());
// Misc GUI properties
app()->mainWindow()->setDownloadTrackerFavicon(m_checkBoxTrackerFavicon.isChecked());
AddNewTorrentDialog::setSavePathHistoryLength(m_spinBoxSavePathHistoryLength.value());
pref->setAddNewTorrentDialogSavePathHistoryLength(m_spinBoxSavePathHistoryLength.value());
pref->setSpeedWidgetEnabled(m_checkBoxSpeedWidgetEnabled.isChecked());
#ifndef Q_OS_MACOS
pref->setIconsInMenusEnabled(m_checkBoxIconsInMenusEnabled.isChecked());
@@ -786,8 +785,8 @@ void AdvancedSettings::loadAdvancedSettings()
m_checkBoxTrackerFavicon.setChecked(app()->mainWindow()->isDownloadTrackerFavicon());
addRow(DOWNLOAD_TRACKER_FAVICON, tr("Download tracker's favicon"), &m_checkBoxTrackerFavicon);
// Save path history length
m_spinBoxSavePathHistoryLength.setRange(AddNewTorrentDialog::minPathHistoryLength, AddNewTorrentDialog::maxPathHistoryLength);
m_spinBoxSavePathHistoryLength.setValue(AddNewTorrentDialog::savePathHistoryLength());
m_spinBoxSavePathHistoryLength.setRange(0, 99);
m_spinBoxSavePathHistoryLength.setValue(pref->addNewTorrentDialogSavePathHistoryLength());
addRow(SAVE_PATH_HISTORY_LENGTH, tr("Save path history length"), &m_spinBoxSavePathHistoryLength);
// Enable speed graphs
m_checkBoxSpeedWidgetEnabled.setChecked(pref->isSpeedWidgetEnabled());

View File

@@ -38,7 +38,7 @@
#include "guiapplicationcomponent.h"
class AdvancedSettings final : public QTableWidget, public GUIApplicationComponent
class AdvancedSettings final : public GUIApplicationComponent<QTableWidget>
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(AdvancedSettings)

View File

@@ -0,0 +1,200 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015-2023 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* 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 "guiaddtorrentmanager.h"
#include "base/bittorrent/session.h"
#include "base/bittorrent/torrentdescriptor.h"
#include "base/logger.h"
#include "base/net/downloadmanager.h"
#include "base/preferences.h"
#include "base/torrentfileguard.h"
#include "addnewtorrentdialog.h"
#include "interfaces/iguiapplication.h"
#include "mainwindow.h"
#include "raisedmessagebox.h"
GUIAddTorrentManager::GUIAddTorrentManager(IGUIApplication *app, BitTorrent::Session *session, QObject *parent)
: GUIApplicationComponent(app, session, parent)
{
connect(btSession(), &BitTorrent::Session::metadataDownloaded, this, &GUIAddTorrentManager::onMetadataDownloaded);
}
bool GUIAddTorrentManager::addTorrent(const QString &source, const BitTorrent::AddTorrentParams &params, const AddTorrentOption option)
{
// `source`: .torrent file path, magnet URI or URL
if (source.isEmpty())
return false;
const auto *pref = Preferences::instance();
if ((option == AddTorrentOption::SkipDialog)
|| ((option == AddTorrentOption::Default) && !pref->isAddNewTorrentDialogEnabled()))
{
return AddTorrentManager::addTorrent(source, params);
}
if (Net::DownloadManager::hasSupportedScheme(source))
{
LogMsg(tr("Downloading torrent... Source: \"%1\"").arg(source));
// Launch downloader
Net::DownloadManager::instance()->download(Net::DownloadRequest(source).limit(pref->getTorrentFileSizeLimit())
, pref->useProxyForGeneralPurposes(), this, &GUIAddTorrentManager::onDownloadFinished);
m_downloadedTorrents[source] = params;
return true;
}
if (const auto parseResult = BitTorrent::TorrentDescriptor::parse(source))
{
return processTorrent(source, parseResult.value(), params);
}
else if (source.startsWith(u"magnet:", Qt::CaseInsensitive))
{
handleAddTorrentFailed(source, parseResult.error());
return false;
}
const Path decodedPath {source.startsWith(u"file://", Qt::CaseInsensitive)
? QUrl::fromEncoded(source.toLocal8Bit()).toLocalFile() : source};
auto torrentFileGuard = std::make_shared<TorrentFileGuard>(decodedPath);
if (const auto loadResult = BitTorrent::TorrentDescriptor::loadFromFile(decodedPath))
{
const BitTorrent::TorrentDescriptor &torrentDescriptor = loadResult.value();
const bool isProcessing = processTorrent(source, torrentDescriptor, params);
if (isProcessing)
setTorrentFileGuard(source, torrentFileGuard);
return isProcessing;
}
else
{
handleAddTorrentFailed(decodedPath.toString(), loadResult.error());
return false;
}
return false;
}
void GUIAddTorrentManager::onDownloadFinished(const Net::DownloadResult &result)
{
const QString &source = result.url;
const BitTorrent::AddTorrentParams addTorrentParams = m_downloadedTorrents.take(source);
switch (result.status)
{
case Net::DownloadStatus::Success:
if (const auto loadResult = BitTorrent::TorrentDescriptor::load(result.data))
processTorrent(source, loadResult.value(), addTorrentParams);
else
handleAddTorrentFailed(source, loadResult.error());
break;
case Net::DownloadStatus::RedirectedToMagnet:
if (const auto parseResult = BitTorrent::TorrentDescriptor::parse(result.magnetURI))
processTorrent(source, parseResult.value(), addTorrentParams);
else
handleAddTorrentFailed(source, parseResult.error());
break;
default:
handleAddTorrentFailed(source, result.errorString);
}
}
void GUIAddTorrentManager::onMetadataDownloaded(const BitTorrent::TorrentInfo &metadata)
{
Q_ASSERT(metadata.isValid());
if (!metadata.isValid()) [[unlikely]]
return;
for (const auto &[infoHash, dialog] : m_dialogs.asKeyValueRange())
{
if (metadata.matchesInfoHash(infoHash))
dialog->updateMetadata(metadata);
}
}
bool GUIAddTorrentManager::processTorrent(const QString &source, const BitTorrent::TorrentDescriptor &torrentDescr, const BitTorrent::AddTorrentParams &params)
{
const bool hasMetadata = torrentDescr.info().has_value();
const BitTorrent::InfoHash infoHash = torrentDescr.infoHash();
// Prevent showing the dialog if download is already present
if (BitTorrent::Torrent *torrent = btSession()->findTorrent(infoHash))
{
if (hasMetadata)
{
// Trying to set metadata to existing torrent in case if it has none
torrent->setMetadata(*torrentDescr.info());
}
if (torrent->isPrivate() || (hasMetadata && torrentDescr.info()->isPrivate()))
{
handleDuplicateTorrent(source, torrent, tr("Trackers cannot be merged because it is a private torrent"));
}
else
{
bool mergeTrackers = btSession()->isMergeTrackersEnabled();
if (Preferences::instance()->confirmMergeTrackers())
{
const QMessageBox::StandardButton btn = RaisedMessageBox::question(app()->mainWindow(), tr("Torrent is already present")
, tr("Torrent '%1' is already in the transfer list. Do you want to merge trackers from new source?").arg(torrent->name())
, (QMessageBox::Yes | QMessageBox::No), QMessageBox::Yes);
mergeTrackers = (btn == QMessageBox::Yes);
}
if (mergeTrackers)
{
torrent->addTrackers(torrentDescr.trackers());
torrent->addUrlSeeds(torrentDescr.urlSeeds());
}
}
return false;
}
if (!hasMetadata)
btSession()->downloadMetadata(torrentDescr);
auto *dlg = new AddNewTorrentDialog(torrentDescr, params, app()->mainWindow());
dlg->setAttribute(Qt::WA_DeleteOnClose);
m_dialogs[infoHash] = dlg;
connect(dlg, &QDialog::finished, this, [this, source, infoHash, dlg](int result)
{
if (dlg->isDoNotDeleteTorrentChecked())
releaseTorrentFileGuard(source);
if (result == QDialog::Accepted)
addTorrentToSession(source, dlg->torrentDescriptor(), dlg->addTorrentParams());
m_dialogs.remove(infoHash);
});
dlg->show();
return true;
}

View File

@@ -1,6 +1,7 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2015-2023 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -26,14 +27,48 @@
* exception statement from your version.
*/
#pragma once
#include "base/addtorrentmanager.h"
#include "base/bittorrent/infohash.h"
#include "guiapplicationcomponent.h"
GUIApplicationComponent::GUIApplicationComponent(IGUIApplication *app)
: ApplicationComponent(app)
#include <QHash>
namespace BitTorrent
{
class TorrentDescriptor;
}
IGUIApplication *GUIApplicationComponent::app() const
namespace Net
{
return static_cast<IGUIApplication *>(ApplicationComponent::app());
struct DownloadResult;
}
class AddNewTorrentDialog;
enum class AddTorrentOption
{
Default,
ShowDialog,
SkipDialog,
};
class GUIAddTorrentManager : public GUIApplicationComponent<AddTorrentManager>
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(GUIAddTorrentManager)
public:
GUIAddTorrentManager(IGUIApplication *app, BitTorrent::Session *session, QObject *parent = nullptr);
bool addTorrent(const QString &source, const BitTorrent::AddTorrentParams &params = {}, AddTorrentOption option = AddTorrentOption::Default);
private:
void onDownloadFinished(const Net::DownloadResult &result);
void onMetadataDownloaded(const BitTorrent::TorrentInfo &metadata);
bool processTorrent(const QString &source, const BitTorrent::TorrentDescriptor &torrentDescr, const BitTorrent::AddTorrentParams &params);
QHash<QString, BitTorrent::AddTorrentParams> m_downloadedTorrents;
QHash<BitTorrent::InfoHash, AddNewTorrentDialog *> m_dialogs;
};

View File

@@ -29,14 +29,38 @@
#pragma once
#include "base/applicationcomponent.h"
#include "interfaces/iguiapplication.h"
class GUIApplicationComponent : public ApplicationComponent
class IGUIApplication;
template <typename Base>
class GUIApplicationComponent : public Base, public ApplicationComponentBase
{
Q_DISABLE_COPY_MOVE(GUIApplicationComponent)
public:
explicit GUIApplicationComponent(IGUIApplication *app);
template <typename... Args>
explicit GUIApplicationComponent(IGUIApplication *app, Args&&... args)
: Base(std::forward<Args>(args)...)
, ApplicationComponentBase(reinterpret_cast<IApplication *>(app))
{
}
IGUIApplication *app() const override;
IGUIApplication *app() const
{
return reinterpret_cast<IGUIApplication *>(ApplicationComponentBase::app());
}
};
template <IsApplicationComponent Base>
class GUIApplicationComponent<Base> : public Base
{
public:
template <typename... Args>
explicit GUIApplicationComponent(IGUIApplication *app, Args&&... args)
: Base(reinterpret_cast<IApplication *>(app), std::forward<Args>(args)...)
{
}
IGUIApplication *app() const
{
return reinterpret_cast<IGUIApplication *>(ApplicationComponentBase::app());
}
};

View File

@@ -31,6 +31,7 @@
#pragma once
#include "base/interfaces/iapplication.h"
#include "gui/guiaddtorrentmanager.h"
#include "gui/windowstate.h"
class DesktopIntegration;
@@ -41,6 +42,8 @@ class IGUIApplication : public IApplication
public:
~IGUIApplication() override = default;
GUIAddTorrentManager *addTorrentManager() const override = 0;
virtual DesktopIntegration *desktopIntegration() = 0;
virtual MainWindow *mainWindow() = 0;

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2022-2023 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
@@ -75,13 +75,13 @@
#include "base/utils/password.h"
#include "base/version.h"
#include "aboutdialog.h"
#include "addnewtorrentdialog.h"
#include "autoexpandabledialog.h"
#include "cookiesdialog.h"
#include "desktopintegration.h"
#include "downloadfromurldialog.h"
#include "executionlogwidget.h"
#include "hidabletabwidget.h"
#include "interfaces/iguiapplication.h"
#include "lineedit.h"
#include "optionsdialog.h"
#include "powermanagement/powermanagement.h"
@@ -184,7 +184,6 @@ MainWindow::MainWindow(IGUIApplication *app, WindowState initialState)
updateAltSpeedsBtn(BitTorrent::Session::instance()->isAltGlobalSpeedLimitEnabled());
connect(BitTorrent::Session::instance(), &BitTorrent::Session::speedLimitModeChanged, this, &MainWindow::updateAltSpeedsBtn);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::recursiveTorrentDownloadPossible, this, &MainWindow::askRecursiveTorrentDownloadConfirmation);
qDebug("create tabWidget");
m_tabs = new HidableTabWidget(this);
@@ -673,7 +672,7 @@ void MainWindow::displayRSSTab(bool enable)
// RSS tab
if (!m_rssWidget)
{
m_rssWidget = new RSSWidget(m_tabs);
m_rssWidget = new RSSWidget(app(), m_tabs);
connect(m_rssWidget.data(), &RSSWidget::unreadCountUpdated, this, &MainWindow::handleRSSUnreadCountUpdated);
#ifdef Q_OS_MACOS
m_tabs->addTab(m_rssWidget, tr("RSS (%1)").arg(RSS::Session::instance()->rootFolder()->unreadCount()));
@@ -919,37 +918,6 @@ void MainWindow::displayExecutionLogTab()
// End of keyboard shortcuts slots
void MainWindow::askRecursiveTorrentDownloadConfirmation(const BitTorrent::Torrent *torrent)
{
if (!Preferences::instance()->isRecursiveDownloadEnabled())
return;
const auto torrentID = torrent->id();
QMessageBox *confirmBox = new QMessageBox(QMessageBox::Question, tr("Recursive download confirmation")
, tr("The torrent '%1' contains .torrent files, do you want to proceed with their downloads?").arg(torrent->name())
, (QMessageBox::Yes | QMessageBox::No | QMessageBox::NoToAll), this);
confirmBox->setAttribute(Qt::WA_DeleteOnClose);
const QAbstractButton *yesButton = confirmBox->button(QMessageBox::Yes);
QAbstractButton *neverButton = confirmBox->button(QMessageBox::NoToAll);
neverButton->setText(tr("Never"));
connect(confirmBox, &QMessageBox::buttonClicked, this
, [torrentID, yesButton, neverButton](const QAbstractButton *button)
{
if (button == yesButton)
{
BitTorrent::Session::instance()->recursiveTorrentDownload(torrentID);
}
else if (button == neverButton)
{
Preferences::instance()->setRecursiveDownloadEnabled(false);
}
});
confirmBox->open();
}
void MainWindow::on_actionSetGlobalSpeedLimits_triggered()
{
auto *dialog = new SpeedLimitDialog {this};
@@ -1124,7 +1092,6 @@ void MainWindow::keyPressEvent(QKeyEvent *event)
if (mimeData->hasText())
{
const bool useTorrentAdditionDialog = AddNewTorrentDialog::isEnabled();
const QStringList lines = mimeData->text().split(u'\n', Qt::SkipEmptyParts);
for (QString line : lines)
@@ -1134,10 +1101,7 @@ void MainWindow::keyPressEvent(QKeyEvent *event)
if (!isTorrentLink(line))
continue;
if (useTorrentAdditionDialog)
AddNewTorrentDialog::show(line, this);
else
BitTorrent::Session::instance()->addTorrent(line);
app()->addTorrentManager()->addTorrent(line);
}
return;
@@ -1320,14 +1284,8 @@ void MainWindow::dropEvent(QDropEvent *event)
}
// Download torrents
const bool useTorrentAdditionDialog = AddNewTorrentDialog::isEnabled();
for (const QString &file : asConst(torrentFiles))
{
if (useTorrentAdditionDialog)
AddNewTorrentDialog::show(file, this);
else
BitTorrent::Session::instance()->addTorrent(file);
}
app()->addTorrentManager()->addTorrent(file);
if (!torrentFiles.isEmpty()) return;
// Create torrent
@@ -1364,22 +1322,14 @@ void MainWindow::on_actionOpen_triggered()
Preferences *const pref = Preferences::instance();
// Open File Open Dialog
// Note: it is possible to select more than one file
const QStringList pathsList =
QFileDialog::getOpenFileNames(this, tr("Open Torrent Files"), pref->getMainLastDir().data(),
tr("Torrent Files") + u" (*" + TORRENT_FILE_EXTENSION + u')');
const QStringList pathsList = QFileDialog::getOpenFileNames(this, tr("Open Torrent Files")
, pref->getMainLastDir().data(), tr("Torrent Files") + u" (*" + TORRENT_FILE_EXTENSION + u')');
if (pathsList.isEmpty())
return;
const bool useTorrentAdditionDialog = AddNewTorrentDialog::isEnabled();
for (const QString &file : pathsList)
{
if (useTorrentAdditionDialog)
AddNewTorrentDialog::show(file, this);
else
BitTorrent::Session::instance()->addTorrent(file);
}
app()->addTorrentManager()->addTorrent(file);
// Save last dir to remember it
const Path topDir {pathsList.at(0)};
@@ -1576,14 +1526,8 @@ void MainWindow::reloadTorrentStats(const QVector<BitTorrent::Torrent *> &torren
void MainWindow::downloadFromURLList(const QStringList &urlList)
{
const bool useTorrentAdditionDialog = AddNewTorrentDialog::isEnabled();
for (const QString &url : urlList)
{
if (useTorrentAdditionDialog)
AddNewTorrentDialog::show(url, this);
else
BitTorrent::Session::instance()->addTorrent(url);
}
app()->addTorrentManager()->addTorrent(url);
}
/*****************************************************

View File

@@ -71,7 +71,7 @@ namespace Ui
class MainWindow;
}
class MainWindow final : public QMainWindow, public GUIApplicationComponent
class MainWindow final : public GUIApplicationComponent<QMainWindow>
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(MainWindow)
@@ -122,7 +122,6 @@ private slots:
void reloadSessionStats();
void reloadTorrentStats(const QVector<BitTorrent::Torrent *> &torrents);
void loadPreferences();
void askRecursiveTorrentDownloadConfirmation(const BitTorrent::Torrent *torrent);
void optionsSaved();
void toggleAlternativeSpeeds();

View File

@@ -110,8 +110,7 @@ namespace
// Constructor
OptionsDialog::OptionsDialog(IGUIApplication *app, QWidget *parent)
: QDialog(parent)
, GUIApplicationComponent(app)
: GUIApplicationComponent(app, parent)
, m_ui {new Ui::OptionsDialog}
, m_storeDialogSize {SETTINGS_KEY(u"Size"_s)}
, m_storeHSplitterSize {SETTINGS_KEY(u"HorizontalSplitterSizes"_s)}
@@ -484,8 +483,8 @@ void OptionsDialog::loadDownloadsTabOptions()
const auto *pref = Preferences::instance();
const auto *session = BitTorrent::Session::instance();
m_ui->checkAdditionDialog->setChecked(AddNewTorrentDialog::isEnabled());
m_ui->checkAdditionDialogFront->setChecked(AddNewTorrentDialog::isTopLevel());
m_ui->checkAdditionDialog->setChecked(pref->isAddNewTorrentDialogEnabled());
m_ui->checkAdditionDialogFront->setChecked(pref->isAddNewTorrentDialogTopLevel());
m_ui->contentLayoutComboBox->setCurrentIndex(static_cast<int>(session->torrentContentLayout()));
m_ui->checkAddToQueueTop->setChecked(session->isAddTorrentToQueueTop());
@@ -686,8 +685,8 @@ void OptionsDialog::saveDownloadsTabOptions() const
auto *pref = Preferences::instance();
auto *session = BitTorrent::Session::instance();
AddNewTorrentDialog::setEnabled(useAdditionDialog());
AddNewTorrentDialog::setTopLevel(m_ui->checkAdditionDialogFront->isChecked());
pref->setAddNewTorrentDialogEnabled(useAdditionDialog());
pref->setAddNewTorrentDialogTopLevel(m_ui->checkAdditionDialogFront->isChecked());
session->setTorrentContentLayout(static_cast<BitTorrent::TorrentContentLayout>(m_ui->contentLayoutComboBox->currentIndex()));

View File

@@ -58,7 +58,7 @@ namespace Ui
class OptionsDialog;
}
class OptionsDialog final : public QDialog, public GUIApplicationComponent
class OptionsDialog final : public GUIApplicationComponent<QDialog>
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(OptionsDialog)

View File

@@ -39,7 +39,6 @@
#include <QShortcut>
#include <QString>
#include "base/bittorrent/session.h"
#include "base/global.h"
#include "base/net/downloadmanager.h"
#include "base/preferences.h"
@@ -47,17 +46,17 @@
#include "base/rss/rss_feed.h"
#include "base/rss/rss_folder.h"
#include "base/rss/rss_session.h"
#include "gui/addnewtorrentdialog.h"
#include "gui/autoexpandabledialog.h"
#include "gui/interfaces/iguiapplication.h"
#include "gui/uithememanager.h"
#include "articlelistwidget.h"
#include "automatedrssdownloader.h"
#include "feedlistwidget.h"
#include "ui_rsswidget.h"
RSSWidget::RSSWidget(QWidget *parent)
: QWidget(parent)
, m_ui(new Ui::RSSWidget)
RSSWidget::RSSWidget(IGUIApplication *app, QWidget *parent)
: GUIApplicationComponent(app, parent)
, m_ui {new Ui::RSSWidget}
{
m_ui->setupUi(this);
@@ -370,13 +369,7 @@ void RSSWidget::downloadSelectedTorrents()
// Mark as read
article->markAsRead();
if (!article->torrentUrl().isEmpty())
{
if (AddNewTorrentDialog::isEnabled())
AddNewTorrentDialog::show(article->torrentUrl(), window());
else
BitTorrent::Session::instance()->addTorrent(article->torrentUrl());
}
app()->addTorrentManager()->addTorrent(article->torrentUrl());
}
}

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2017 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2017-2023 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
* Copyright (C) 2006 Arnaud Demaiziere <arnaud@qbittorrent.org>
*
@@ -32,6 +32,8 @@
#include <QWidget>
#include "gui/guiapplicationcomponent.h"
class QListWidgetItem;
class QTreeWidgetItem;
@@ -43,14 +45,14 @@ namespace Ui
class RSSWidget;
}
class RSSWidget : public QWidget
class RSSWidget final : public GUIApplicationComponent<QWidget>
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(RSSWidget)
public:
RSSWidget(QWidget *parent);
~RSSWidget();
explicit RSSWidget(IGUIApplication *app, QWidget *parent = nullptr);
~RSSWidget() override;
public slots:
void deleteSelectedItems();

View File

@@ -39,24 +39,22 @@
#include <QStandardItemModel>
#include <QUrl>
#include "base/bittorrent/session.h"
#include "base/preferences.h"
#include "base/search/searchdownloadhandler.h"
#include "base/search/searchhandler.h"
#include "base/search/searchpluginmanager.h"
#include "base/utils/misc.h"
#include "gui/addnewtorrentdialog.h"
#include "gui/interfaces/iguiapplication.h"
#include "gui/lineedit.h"
#include "gui/uithememanager.h"
#include "gui/utils.h"
#include "searchsortmodel.h"
#include "ui_searchjobwidget.h"
SearchJobWidget::SearchJobWidget(SearchHandler *searchHandler, QWidget *parent)
: QWidget(parent)
, m_ui(new Ui::SearchJobWidget)
, m_searchHandler(searchHandler)
, m_nameFilteringMode(u"Search/FilteringMode"_s)
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);
@@ -289,12 +287,7 @@ void SearchJobWidget::downloadTorrent(const QModelIndex &rowIndex, const AddTorr
void SearchJobWidget::addTorrentToSession(const QString &source, const AddTorrentOption option)
{
if (source.isEmpty()) return;
if ((option == AddTorrentOption::ShowDialog) || ((option == AddTorrentOption::Default) && AddNewTorrentDialog::isEnabled()))
AddNewTorrentDialog::show(source, this);
else
BitTorrent::Session::instance()->addTorrent(source);
app()->addTorrentManager()->addTorrent(source, {}, option);
}
void SearchJobWidget::updateResultsCount()

View File

@@ -32,6 +32,8 @@
#include <QWidget>
#include "base/settingvalue.h"
#include "gui/guiaddtorrentmanager.h"
#include "gui/guiapplicationcomponent.h"
#define ENGINE_URL_COLUMN 4
#define URL_COLUMN 5
@@ -52,7 +54,7 @@ namespace Ui
class SearchJobWidget;
}
class SearchJobWidget final : public QWidget
class SearchJobWidget final : public GUIApplicationComponent<QWidget>
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(SearchJobWidget)
@@ -74,7 +76,7 @@ public:
NoResults
};
explicit SearchJobWidget(SearchHandler *searchHandler, QWidget *parent = nullptr);
SearchJobWidget(SearchHandler *searchHandler, IGUIApplication *app, QWidget *parent = nullptr);
~SearchJobWidget() override;
Status status() const;
@@ -94,13 +96,6 @@ private slots:
void displayColumnHeaderMenu();
private:
enum class AddTorrentOption
{
Default,
ShowDialog,
SkipDialog,
};
void loadSettings();
void saveSettings() const;
void updateFilter();

View File

@@ -53,6 +53,7 @@
#include "base/search/searchpluginmanager.h"
#include "base/utils/foreignapps.h"
#include "gui/desktopintegration.h"
#include "gui/interfaces/iguiapplication.h"
#include "gui/mainwindow.h"
#include "gui/uithememanager.h"
#include "pluginselectdialog.h"
@@ -84,8 +85,7 @@ namespace
}
SearchWidget::SearchWidget(IGUIApplication *app, MainWindow *mainWindow)
: QWidget(mainWindow)
, GUIApplicationComponent(app)
: GUIApplicationComponent(app, mainWindow)
, m_ui {new Ui::SearchWidget()}
, m_mainWindow {mainWindow}
{
@@ -347,7 +347,7 @@ void SearchWidget::on_searchButton_clicked()
auto *searchHandler = SearchPluginManager::instance()->startSearch(pattern, selectedCategory(), plugins);
// Tab Addition
auto *newTab = new SearchJobWidget(searchHandler, this);
auto *newTab = new SearchJobWidget(searchHandler, app(), this);
m_allTabs.append(newTab);
QString tabName = pattern;

View File

@@ -48,7 +48,7 @@ namespace Ui
class SearchWidget;
}
class SearchWidget : public QWidget, public GUIApplicationComponent
class SearchWidget : public GUIApplicationComponent<QWidget>
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(SearchWidget)