Allow drop action only on transfer list

Now drop action is only allowed on transfer list, previously it was on main window.
Having drop action on the whole main window is not preferred because it could allow drop action
on other unrelated widgets, such as execution log or RSS widget which is unexpected behavior.

PR #21332.
This commit is contained in:
Chocobo1 2024-09-30 16:59:57 +08:00 committed by GitHub
commit 10eb921d70
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 104 additions and 91 deletions

View file

@ -47,6 +47,7 @@
#include <QStringView>
#include <QSysInfo>
#include "base/net/downloadmanager.h"
#include "base/path.h"
#include "base/unicodestrings.h"
#include "base/utils/string.h"
@ -212,6 +213,14 @@ bool Utils::Misc::isPreviewable(const Path &filePath)
return multimediaExtensions.contains(filePath.extension().toUpper());
}
bool Utils::Misc::isTorrentLink(const QString &str)
{
return str.startsWith(u"magnet:", Qt::CaseInsensitive)
|| str.endsWith(TORRENT_FILE_EXTENSION, Qt::CaseInsensitive)
|| (!str.startsWith(u"file:", Qt::CaseInsensitive)
&& Net::DownloadManager::hasSupportedScheme(str));
}
QString Utils::Misc::userFriendlyDuration(const qlonglong seconds, const qlonglong maxCap, const TimeResolution resolution)
{
if (seconds < 0)

View file

@ -78,6 +78,7 @@ namespace Utils::Misc
qint64 sizeInBytes(qreal size, SizeUnit unit);
bool isPreviewable(const Path &filePath);
bool isTorrentLink(const QString &str);
// Take a number of seconds and return a user-friendly
// time duration like "1d 2h 10m".

View file

@ -43,7 +43,6 @@
#include <QDesktopServices>
#include <QFileDialog>
#include <QFileSystemWatcher>
#include <QKeyEvent>
#include <QLabel>
#include <QMenu>
#include <QMessageBox>
@ -113,14 +112,6 @@ namespace
#define EXECUTIONLOG_SETTINGS_KEY(name) (SETTINGS_KEY(u"Log/"_s) name)
const std::chrono::seconds PREVENT_SUSPEND_INTERVAL {60};
bool isTorrentLink(const QString &str)
{
return str.startsWith(u"magnet:", Qt::CaseInsensitive)
|| str.endsWith(TORRENT_FILE_EXTENSION, Qt::CaseInsensitive)
|| (!str.startsWith(u"file:", Qt::CaseInsensitive)
&& Net::DownloadManager::hasSupportedScheme(str));
}
}
MainWindow::MainWindow(IGUIApplication *app, const WindowState initialState, const QString &titleSuffix)
@ -241,7 +232,7 @@ MainWindow::MainWindow(IGUIApplication *app, const WindowState initialState, con
m_ui->toolBar->insertWidget(m_columnFilterAction, spacer);
// Transfer List tab
m_transferListWidget = new TransferListWidget(hSplitter, this);
m_transferListWidget = new TransferListWidget(app, this);
m_propertiesWidget = new PropertiesWidget(hSplitter);
connect(m_transferListWidget, &TransferListWidget::currentTorrentChanged, m_propertiesWidget, &PropertiesWidget::loadTorrentInfos);
hSplitter->addWidget(m_transferListWidget);
@ -347,8 +338,6 @@ MainWindow::MainWindow(IGUIApplication *app, const WindowState initialState, con
connect(BitTorrent::Session::instance(), &BitTorrent::Session::statsUpdated, this, &MainWindow::loadSessionStats);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentsUpdated, this, &MainWindow::reloadTorrentStats);
// Accept drag 'n drops
setAcceptDrops(true);
createKeyboardShortcuts();
#ifdef Q_OS_MACOS
@ -1124,16 +1113,13 @@ void MainWindow::keyPressEvent(QKeyEvent *event)
if (event->matches(QKeySequence::Paste))
{
const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData();
if (mimeData->hasText())
{
const QStringList lines = mimeData->text().split(u'\n', Qt::SkipEmptyParts);
for (QString line : lines)
{
line = line.trimmed();
if (!isTorrentLink(line))
if (!Utils::Misc::isTorrentLink(line))
continue;
app()->addTorrentManager()->addTorrent(line);
@ -1284,66 +1270,6 @@ bool MainWindow::event(QEvent *e)
return QMainWindow::event(e);
}
// action executed when a file is dropped
void MainWindow::dropEvent(QDropEvent *event)
{
event->acceptProposedAction();
// remove scheme
QStringList files;
if (event->mimeData()->hasUrls())
{
for (const QUrl &url : asConst(event->mimeData()->urls()))
{
if (url.isEmpty())
continue;
files << ((url.scheme().compare(u"file", Qt::CaseInsensitive) == 0)
? url.toLocalFile()
: url.toString());
}
}
else
{
files = event->mimeData()->text().split(u'\n');
}
// differentiate ".torrent" files/links & magnet links from others
QStringList torrentFiles, otherFiles;
for (const QString &file : asConst(files))
{
if (isTorrentLink(file))
torrentFiles << file;
else
otherFiles << file;
}
// Download torrents
for (const QString &file : asConst(torrentFiles))
app()->addTorrentManager()->addTorrent(file);
if (!torrentFiles.isEmpty()) return;
// Create torrent
for (const QString &file : asConst(otherFiles))
{
createTorrentTriggered(Path(file));
// currently only handle the first entry
// this is a stub that can be expanded later to create many torrents at once
break;
}
}
// Decode if we accept drag 'n drop or not
void MainWindow::dragEnterEvent(QDragEnterEvent *event)
{
for (const QString &mime : asConst(event->mimeData()->formats()))
qDebug("mimeData: %s", mime.toLocal8Bit().data());
if (event->mimeData()->hasFormat(u"text/plain"_s) || event->mimeData()->hasFormat(u"text/uri-list"_s))
event->acceptProposedAction();
}
// Display a dialog to allow user to add
// torrents to download list
void MainWindow::on_actionOpen_triggered()

View file

@ -191,8 +191,6 @@ private:
void installPython();
#endif
void dropEvent(QDropEvent *event) override;
void dragEnterEvent(QDragEnterEvent *event) override;
void closeEvent(QCloseEvent *) override;
void showEvent(QShowEvent *) override;
void keyPressEvent(QKeyEvent *event) override;

View file

@ -38,6 +38,7 @@
#include <QList>
#include <QMenu>
#include <QMessageBox>
#include <QMimeData>
#include <QRegularExpression>
#include <QSet>
#include <QShortcut>
@ -57,11 +58,13 @@
#include "base/utils/string.h"
#include "autoexpandabledialog.h"
#include "deletionconfirmationdialog.h"
#include "interfaces/iguiapplication.h"
#include "mainwindow.h"
#include "optionsdialog.h"
#include "previewselectdialog.h"
#include "speedlimitdialog.h"
#include "torrentcategorydialog.h"
#include "torrentcreatordialog.h"
#include "torrentoptionsdialog.h"
#include "trackerentriesdialog.h"
#include "transferlistdelegate.h"
@ -123,11 +126,10 @@ namespace
}
}
TransferListWidget::TransferListWidget(QWidget *parent, MainWindow *mainWindow)
: QTreeView {parent}
TransferListWidget::TransferListWidget(IGUIApplication *app, QWidget *parent)
: GUIApplicationComponent(app, parent)
, m_listModel {new TransferListModel {this}}
, m_sortFilterModel {new TransferListSortModel {this}}
, m_mainWindow {mainWindow}
{
// Load settings
const bool columnLoaded = loadSettings();
@ -151,7 +153,9 @@ TransferListWidget::TransferListWidget(QWidget *parent, MainWindow *mainWindow)
setSelectionMode(QAbstractItemView::ExtendedSelection);
setItemsExpandable(false);
setAutoScroll(true);
setDragDropMode(QAbstractItemView::DragOnly);
setAcceptDrops(true);
setDragDropMode(QAbstractItemView::DropOnly);
setDropIndicatorShown(true);
#if defined(Q_OS_MACOS)
setAttribute(Qt::WA_MacShowFocusRect, false);
#endif
@ -431,7 +435,7 @@ void TransferListWidget::permDeleteSelectedTorrents()
void TransferListWidget::deleteSelectedTorrents(const bool deleteLocalFiles)
{
if (m_mainWindow->currentTabWidget() != this) return;
if (app()->mainWindow()->currentTabWidget() != this) return;
const QList<BitTorrent::Torrent *> torrents = getSelectedTorrents();
if (torrents.empty()) return;
@ -480,26 +484,26 @@ void TransferListWidget::deleteVisibleTorrents()
void TransferListWidget::increaseQueuePosSelectedTorrents()
{
qDebug() << Q_FUNC_INFO;
if (m_mainWindow->currentTabWidget() == this)
if (app()->mainWindow()->currentTabWidget() == this)
BitTorrent::Session::instance()->increaseTorrentsQueuePos(extractIDs(getSelectedTorrents()));
}
void TransferListWidget::decreaseQueuePosSelectedTorrents()
{
qDebug() << Q_FUNC_INFO;
if (m_mainWindow->currentTabWidget() == this)
if (app()->mainWindow()->currentTabWidget() == this)
BitTorrent::Session::instance()->decreaseTorrentsQueuePos(extractIDs(getSelectedTorrents()));
}
void TransferListWidget::topQueuePosSelectedTorrents()
{
if (m_mainWindow->currentTabWidget() == this)
if (app()->mainWindow()->currentTabWidget() == this)
BitTorrent::Session::instance()->topTorrentsQueuePos(extractIDs(getSelectedTorrents()));
}
void TransferListWidget::bottomQueuePosSelectedTorrents()
{
if (m_mainWindow->currentTabWidget() == this)
if (app()->mainWindow()->currentTabWidget() == this)
BitTorrent::Session::instance()->bottomTorrentsQueuePos(extractIDs(getSelectedTorrents()));
}
@ -1354,6 +1358,79 @@ bool TransferListWidget::loadSettings()
return header()->restoreState(Preferences::instance()->getTransHeaderState());
}
void TransferListWidget::dragEnterEvent(QDragEnterEvent *event)
{
if (const QMimeData *data = event->mimeData(); data->hasText() || data->hasUrls())
{
event->setDropAction(Qt::CopyAction);
event->accept();
}
}
void TransferListWidget::dragMoveEvent(QDragMoveEvent *event)
{
event->acceptProposedAction(); // required, otherwise we won't get `dropEvent`
}
void TransferListWidget::dropEvent(QDropEvent *event)
{
event->acceptProposedAction();
// remove scheme
QStringList files;
if (const QMimeData *data = event->mimeData(); data->hasUrls())
{
const QList<QUrl> urls = data->urls();
files.reserve(urls.size());
for (const QUrl &url : urls)
{
if (url.isEmpty())
continue;
files.append(url.isLocalFile()
? url.toLocalFile()
: url.toString());
}
}
else
{
files = data->text().split(u'\n', Qt::SkipEmptyParts);
}
// differentiate ".torrent" files/links & magnet links from others
QStringList torrentFiles, otherFiles;
torrentFiles.reserve(files.size());
otherFiles.reserve(files.size());
for (const QString &file : asConst(files))
{
if (Utils::Misc::isTorrentLink(file))
torrentFiles << file;
else
otherFiles << file;
}
// Download torrents
if (!torrentFiles.isEmpty())
{
for (const QString &file : asConst(torrentFiles))
app()->addTorrentManager()->addTorrent(file);
return;
}
// Create torrent
for (const QString &file : asConst(otherFiles))
{
auto torrentCreator = new TorrentCreatorDialog(this, Path(file));
torrentCreator->setAttribute(Qt::WA_DeleteOnClose);
torrentCreator->show();
// currently only handle the first entry
// this is a stub that can be expanded later to create many torrents at once
break;
}
}
void TransferListWidget::wheelEvent(QWheelEvent *event)
{
if (event->modifiers() & Qt::ShiftModifier)

View file

@ -35,9 +35,9 @@
#include <QTreeView>
#include "base/bittorrent/infohash.h"
#include "guiapplicationcomponent.h"
#include "transferlistmodel.h"
class MainWindow;
class Path;
class TransferListSortModel;
@ -52,13 +52,13 @@ enum class CopyInfohashPolicy
Version2
};
class TransferListWidget final : public QTreeView
class TransferListWidget final : public GUIApplicationComponent<QTreeView>
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(TransferListWidget)
public:
TransferListWidget(QWidget *parent, MainWindow *mainWindow);
TransferListWidget(IGUIApplication *app, QWidget *parent);
~TransferListWidget() override;
TransferListModel *getSourceModel() const;
@ -119,6 +119,9 @@ private slots:
void saveSettings();
private:
void dragEnterEvent(QDragEnterEvent *event) override;
void dragMoveEvent(QDragMoveEvent *event) override;
void dropEvent(QDropEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
QModelIndex mapToSource(const QModelIndex &index) const;
QModelIndexList mapToSource(const QModelIndexList &indexes) const;
@ -136,5 +139,4 @@ private:
TransferListModel *m_listModel = nullptr;
TransferListSortModel *m_sortFilterModel = nullptr;
MainWindow *m_mainWindow = nullptr;
};