From 10eb921d7087bf392ed17a1ec83176d51565bff8 Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Mon, 30 Sep 2024 16:59:57 +0800 Subject: [PATCH] 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. --- src/base/utils/misc.cpp | 9 ++++ src/base/utils/misc.h | 1 + src/gui/mainwindow.cpp | 78 +--------------------------- src/gui/mainwindow.h | 2 - src/gui/transferlistwidget.cpp | 95 ++++++++++++++++++++++++++++++---- src/gui/transferlistwidget.h | 10 ++-- 6 files changed, 104 insertions(+), 91 deletions(-) diff --git a/src/base/utils/misc.cpp b/src/base/utils/misc.cpp index 8d6e87765..27d305452 100644 --- a/src/base/utils/misc.cpp +++ b/src/base/utils/misc.cpp @@ -47,6 +47,7 @@ #include #include +#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) diff --git a/src/base/utils/misc.h b/src/base/utils/misc.h index d477c3d58..7a1f7b6a9 100644 --- a/src/base/utils/misc.h +++ b/src/base/utils/misc.h @@ -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". diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index b80d0cc70..9960ef4d6 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -43,7 +43,6 @@ #include #include #include -#include #include #include #include @@ -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() diff --git a/src/gui/mainwindow.h b/src/gui/mainwindow.h index c4df799cc..68b7f349a 100644 --- a/src/gui/mainwindow.h +++ b/src/gui/mainwindow.h @@ -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; diff --git a/src/gui/transferlistwidget.cpp b/src/gui/transferlistwidget.cpp index e765b9e38..9435b05be 100644 --- a/src/gui/transferlistwidget.cpp +++ b/src/gui/transferlistwidget.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -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 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 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) diff --git a/src/gui/transferlistwidget.h b/src/gui/transferlistwidget.h index 1caf965fb..c5b24ba24 100644 --- a/src/gui/transferlistwidget.h +++ b/src/gui/transferlistwidget.h @@ -35,9 +35,9 @@ #include #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 { 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; };