|
|
|
|
@ -24,6 +24,8 @@
|
|
|
|
|
#include <QPointer> |
|
|
|
|
#include <QRunnable> |
|
|
|
|
#include <QLocale> |
|
|
|
|
#include <QDropEvent> |
|
|
|
|
#include <QMimeData> |
|
|
|
|
#include <limits> |
|
|
|
|
#include <cstring> |
|
|
|
|
extern "C" { |
|
|
|
|
@ -75,6 +77,13 @@ MemberFileBrowser::MemberFileBrowser(DbManager* db, QWidget* parent) : QDialog(p
|
|
|
|
|
m_uploadStatus = new QLabel(this); m_uploadStatus->setObjectName("memberFilesUploadStatus"); |
|
|
|
|
m_uploadStatus->setWordWrap(true); layout->addWidget(m_uploadStatus); |
|
|
|
|
m_progress = new QProgressBar(this); m_progress->setRange(0, 1000); m_progress->hide(); layout->addWidget(m_progress); |
|
|
|
|
auto dropTargets = findChildren<QWidget*>(); dropTargets.append(this); |
|
|
|
|
for (auto* target : dropTargets) { target->setAcceptDrops(true); target->installEventFilter(this); } |
|
|
|
|
m_dropHint = new QLabel(m_entries->viewport()); m_dropHint->setObjectName("memberFilesDropHint"); |
|
|
|
|
m_dropHint->setAttribute(Qt::WA_TransparentForMouseEvents); m_dropHint->setAlignment(Qt::AlignCenter); |
|
|
|
|
m_dropHint->setStyleSheet("QLabel { border: 2px dashed palette(highlight); background: palette(window); color: palette(text); }"); |
|
|
|
|
m_dropHint->setText(text("Copy to my files")); m_dropHint->hide(); |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "file browser: drop targets installed count=%d", int(dropTargets.size())); |
|
|
|
|
connect(reload, &QPushButton::clicked, this, [this]() { m_staleRetries = 0; refresh(); }); |
|
|
|
|
connect(up, &QPushButton::clicked, this, [this]() { |
|
|
|
|
if (m_path.isEmpty()) { m_owners->setCurrentIndex(0); return; } |
|
|
|
|
@ -113,8 +122,45 @@ MemberFileBrowser::~MemberFileBrowser() {
|
|
|
|
|
m_uploadPool.waitForDone(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool MemberFileBrowser::eventFilter(QObject* target, QEvent* event) { |
|
|
|
|
if (event->type() == QEvent::DragLeave) { m_dropHint->hide(); return true; } |
|
|
|
|
if (event->type() != QEvent::DragEnter && event->type() != QEvent::DragMove && event->type() != QEvent::Drop) |
|
|
|
|
return QDialog::eventFilter(target, event); |
|
|
|
|
auto* drop = static_cast<QDropEvent*>(event); |
|
|
|
|
QString reason; |
|
|
|
|
if (m_dropId || m_uploadBatchId || m_transferId) reason = text("Finish or cancel the current transfer first"); |
|
|
|
|
else if (!m_group || !server() || !m_db || !owner() || owner() != m_db->myNodeId() || !gui_bridge_is_uasync_ready()) |
|
|
|
|
reason = text("Upload unavailable"); |
|
|
|
|
else if (!drop->mimeData()->hasUrls() || drop->mimeData()->urls().isEmpty() || !(drop->possibleActions() & Qt::CopyAction)) |
|
|
|
|
reason = text("Drop local files to copy"); |
|
|
|
|
QStringList paths; |
|
|
|
|
if (reason.isEmpty()) for (const auto& url : drop->mimeData()->urls()) { |
|
|
|
|
if (!url.isLocalFile() || url.toLocalFile().isEmpty()) { reason = text("Only local files can be uploaded"); break; } |
|
|
|
|
paths.append(url.toLocalFile()); |
|
|
|
|
} |
|
|
|
|
if (!reason.isEmpty()) { |
|
|
|
|
if (event->type() != QEvent::DragMove) |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "file browser: drop rejected group=%llu owner=%llu path=%s reason=%s", |
|
|
|
|
(unsigned long long)m_group, (unsigned long long)owner(), qPrintable(m_path), qPrintable(reason)); |
|
|
|
|
m_status->setText(reason); m_dropHint->hide(); drop->ignore(); return true; |
|
|
|
|
} |
|
|
|
|
drop->setDropAction(Qt::CopyAction); drop->accept(); |
|
|
|
|
if (event->type() != QEvent::Drop) { |
|
|
|
|
m_dropHint->setGeometry(m_entries->viewport()->rect()); m_dropHint->show(); m_dropHint->raise(); return true; |
|
|
|
|
} |
|
|
|
|
m_dropHint->hide(); m_dropId = requestId(); updateActions(); |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "file browser: drop accepted group=%llu server=%llu owner=%llu path=%s files=%d", |
|
|
|
|
(unsigned long long)m_group, (unsigned long long)server(), (unsigned long long)owner(), qPrintable(m_path), int(paths.size())); |
|
|
|
|
quint64 id = m_dropId; |
|
|
|
|
QTimer::singleShot(0, this, [this, id, paths] { |
|
|
|
|
if (m_dropId != id) return; |
|
|
|
|
m_dropId = 0; updateActions(); beginUploads(paths, false); |
|
|
|
|
}); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void MemberFileBrowser::setChannel(const QString& channel, quint64 selectedOwner) { |
|
|
|
|
if (m_uploadBatchId || m_transferId) { |
|
|
|
|
if (m_dropId || m_uploadBatchId || m_transferId) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "file browser: context change blocked during transfer channel=%s", qPrintable(channel)); |
|
|
|
|
m_status->setText(text("Finish or cancel the current transfer first")); return; |
|
|
|
|
} |
|
|
|
|
@ -157,7 +203,7 @@ void MemberFileBrowser::updateActions() {
|
|
|
|
|
auto* item = m_entries->currentItem(); bool own = owner() && owner() == m_db->myNodeId(); |
|
|
|
|
bool available = server() && gui_bridge_is_uasync_ready(); |
|
|
|
|
bool file = item && item->data(0, Qt::UserRole + 1).toInt() == MF_FILE; |
|
|
|
|
bool busy = m_uploadBatchId || m_transferId; |
|
|
|
|
bool busy = m_dropId || m_uploadBatchId || m_transferId; |
|
|
|
|
m_servers->setEnabled(!busy); m_owners->setEnabled(!busy); m_navigation->setEnabled(!busy); m_entries->setEnabled(!busy); |
|
|
|
|
m_upload->setEnabled(available && own && !busy); m_mkdir->setEnabled(available && own && !busy); |
|
|
|
|
m_rename->setEnabled(available && own && item && !busy); m_remove->setEnabled(available && own && item && !busy); |
|
|
|
|
@ -368,7 +414,7 @@ void MemberFileBrowser::upload() {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void MemberFileBrowser::uploadOwnFiles(const QString& channel, const QStringList& paths) { |
|
|
|
|
if (m_uploadBatchId || m_transferId) { |
|
|
|
|
if (m_dropId || m_uploadBatchId || m_transferId) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "file browser: drop rejected while busy files=%d", int(paths.size())); |
|
|
|
|
QMessageBox::warning(this, text("Group files"), text("Finish or cancel the current transfer first")); return; |
|
|
|
|
} |
|
|
|
|
@ -383,10 +429,10 @@ void MemberFileBrowser::uploadOwnFiles(const QString& channel, const QStringList
|
|
|
|
|
// Сервер выбирается до первого запроса; batch ID защищает вложенные циклы диалогов от отмены/закрытия окна.
|
|
|
|
|
void MemberFileBrowser::beginUploads(const QStringList& paths, bool chooseServer) { |
|
|
|
|
if (paths.isEmpty()) return; |
|
|
|
|
if (m_uploadBatchId || m_transferId || !server() || !owner() || owner() != m_db->myNodeId() || !gui_bridge_is_uasync_ready()) { |
|
|
|
|
if (m_dropId || m_uploadBatchId || m_transferId || !server() || !owner() || owner() != m_db->myNodeId() || !gui_bridge_is_uasync_ready()) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "file browser: upload unavailable group=%llu server=%llu owner=%llu busy=%d ready=%d", |
|
|
|
|
(unsigned long long)m_group, (unsigned long long)server(), (unsigned long long)owner(), |
|
|
|
|
bool(m_uploadBatchId || m_transferId), gui_bridge_is_uasync_ready()); |
|
|
|
|
bool(m_dropId || m_uploadBatchId || m_transferId), gui_bridge_is_uasync_ready()); |
|
|
|
|
QMessageBox::warning(this, text("Group files"), !server() ? text("No media server in this group") : text("Upload unavailable")); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
@ -533,6 +579,7 @@ void MemberFileBrowser::mutate(uint8_t command) {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void MemberFileBrowser::cancel() { |
|
|
|
|
if (m_dropId) { m_dropId = 0; m_uploadStatus->setText(text("Upload queue cancelled")); updateActions(); } |
|
|
|
|
if (m_uploadBatchId) finishUploads(text("Upload queue cancelled")); |
|
|
|
|
cancelRequest(m_transferId); |
|
|
|
|
} |
|
|
|
|
|