|
|
|
|
@ -33,6 +33,7 @@
|
|
|
|
|
#include <QDir> |
|
|
|
|
#include <QTemporaryDir> |
|
|
|
|
#include <QStandardPaths> |
|
|
|
|
#include <QEventLoop> |
|
|
|
|
#include <functional> |
|
|
|
|
#include <limits> |
|
|
|
|
#include <cstring> |
|
|
|
|
@ -53,6 +54,26 @@ public:
|
|
|
|
|
protected: |
|
|
|
|
void startDrag(Qt::DropActions) override { exportRequested(); } |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// Системный менеджер может запросить URI после drop. До завершения скачивания путь не выдаётся.
|
|
|
|
|
class DeferredFileMimeData : public QMimeData { |
|
|
|
|
public: |
|
|
|
|
std::function<QString()> localFile; |
|
|
|
|
QStringList formats() const override { return {QStringLiteral("text/uri-list")}; } |
|
|
|
|
bool hasFormat(const QString& format) const override { return format == QStringLiteral("text/uri-list"); } |
|
|
|
|
protected: |
|
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) |
|
|
|
|
QVariant retrieveData(const QString& format, QMetaType type) const override { |
|
|
|
|
#else |
|
|
|
|
QVariant retrieveData(const QString& format, QVariant::Type type) const override { |
|
|
|
|
#endif |
|
|
|
|
if (!hasFormat(format)) return {}; |
|
|
|
|
QString local = localFile(); |
|
|
|
|
if (local.isEmpty()) return {}; |
|
|
|
|
const_cast<DeferredFileMimeData*>(this)->setUrls({QUrl::fromLocalFile(local)}); |
|
|
|
|
return QMimeData::retrieveData(format, type); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
static QString quotaSize(quint64 bytes) { |
|
|
|
|
return bytes <= (quint64)std::numeric_limits<qint64>::max() ? QLocale().formattedDataSize((qint64)bytes, 2) : |
|
|
|
|
text("%1 bytes").arg(QLocale().toString(bytes)); |
|
|
|
|
@ -585,9 +606,33 @@ void MemberFileBrowser::exportFile(bool clipboard) {
|
|
|
|
|
if (local.isEmpty() || !startTransfer(remote, local, false, version)) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "file browser: export could not start remote=%s local=%s", qPrintable(remote), qPrintable(local)); |
|
|
|
|
m_exportId = 0; updateActions(); m_status->setText(text("Cannot prepare a local copy")); |
|
|
|
|
emit exportFinished(); |
|
|
|
|
} |
|
|
|
|
}, Qt::QueuedConnection); |
|
|
|
|
})); |
|
|
|
|
if (!clipboard) { |
|
|
|
|
QPointer<MemberFileBrowser> guard(this); |
|
|
|
|
auto* mime = new DeferredFileMimeData; |
|
|
|
|
mime->localFile = [guard, id, key] { |
|
|
|
|
if (!guard) return QString(); |
|
|
|
|
if (guard->m_exportId == id) { |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "file browser: drag waiting for local copy id=%llu", (unsigned long long)id); |
|
|
|
|
QEventLoop wait; |
|
|
|
|
QObject::connect(guard, &MemberFileBrowser::exportFinished, &wait, &QEventLoop::quit); |
|
|
|
|
QObject::connect(guard, &QObject::destroyed, &wait, &QEventLoop::quit); |
|
|
|
|
wait.exec(); |
|
|
|
|
} |
|
|
|
|
QString local = guard ? guard->m_exportCache.value(key) : QString(); |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "file browser: drag data ready id=%llu local=%s", (unsigned long long)id, qPrintable(local)); |
|
|
|
|
return local; |
|
|
|
|
}; |
|
|
|
|
auto* drag = new QDrag(m_entries); drag->setParent(QCoreApplication::instance()); drag->setMimeData(mime); |
|
|
|
|
// MIME живёт до возврата из native drag, даже если окно файлов закрыли во вложенном цикле событий.
|
|
|
|
|
connect(this, &QObject::destroyed, drag, [] { QDrag::cancel(); }); |
|
|
|
|
Qt::DropAction action = drag->exec(Qt::CopyAction, Qt::CopyAction); |
|
|
|
|
if (guard && m_exportId == id) cancel(); |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "file browser: native drag finished action=%d", int(action)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool MemberFileBrowser::startTransfer(const QString& remote, const QString& local, bool uploading, const QByteArray& version) { |
|
|
|
|
@ -623,8 +668,9 @@ void MemberFileBrowser::onTransfer(const uint8_t* data, int length) {
|
|
|
|
|
m_exportCache.insert(m_exportKey, m_local); |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "file browser: local copy ready path=%s", qPrintable(m_local)); |
|
|
|
|
if (m_exportClipboard) publishExport(m_local, true); |
|
|
|
|
else m_status->setText(text("Local copy ready. Drag the file again, or use Copy file.")); |
|
|
|
|
else m_status->setText(text("Local copy ready")); |
|
|
|
|
} |
|
|
|
|
emit exportFinished(); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
if (m_uploadBatchId) { |
|
|
|
|
@ -675,6 +721,7 @@ void MemberFileBrowser::cancel() {
|
|
|
|
|
if (m_exportId) { |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "file browser: export cancelled id=%llu", (unsigned long long)m_exportId); |
|
|
|
|
m_exportId = 0; m_status->setText(text("Copy cancelled")); updateActions(); |
|
|
|
|
emit exportFinished(); |
|
|
|
|
} |
|
|
|
|
if (m_dropId) { m_dropId = 0; m_uploadStatus->setText(text("Upload queue cancelled")); updateActions(); } |
|
|
|
|
if (m_uploadBatchId) finishUploads(text("Upload queue cancelled")); |
|
|
|
|
|