diff --git a/src/src.pro b/src/src.pro index bdffbe10..3c9c5084 100644 --- a/src/src.pro +++ b/src/src.pro @@ -148,7 +148,8 @@ SOURCES += main.cpp\ peghighlighterresult.cpp \ vtexteditcompleter.cpp \ utils/vkeyboardlayoutmanager.cpp \ - dialog/vkeyboardlayoutmappingdialog.cpp + dialog/vkeyboardlayoutmappingdialog.cpp \ + vfilelistwidget.cpp HEADERS += vmainwindow.h \ vdirectorytree.h \ @@ -289,7 +290,8 @@ HEADERS += vmainwindow.h \ vtexteditcompleter.h \ vtextdocumentlayoutdata.h \ utils/vkeyboardlayoutmanager.h \ - dialog/vkeyboardlayoutmappingdialog.h + dialog/vkeyboardlayoutmappingdialog.h \ + vfilelistwidget.h RESOURCES += \ vnote.qrc \ diff --git a/src/vconstants.h b/src/vconstants.h index 6fba82e4..bc02c183 100644 --- a/src/vconstants.h +++ b/src/vconstants.h @@ -25,6 +25,7 @@ namespace ClipboardConfig static const QString c_isCut = "is_cut"; static const QString c_files = "files"; static const QString c_dirs = "dirs"; + static const QString c_format = "text/json"; } enum class OpenFileMode {Read = 0, Edit, Invalid }; diff --git a/src/vdirectorytree.cpp b/src/vdirectorytree.cpp index 2a246325..3ad3b6f7 100644 --- a/src/vdirectorytree.cpp +++ b/src/vdirectorytree.cpp @@ -30,6 +30,9 @@ VDirectoryTree::VDirectoryTree(QWidget *parent) setContextMenuPolicy(Qt::CustomContextMenu); setFitContent(true); + viewport()->setAcceptDrops(true); + setDropIndicatorShown(true); + initShortcuts(); connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)), @@ -1229,3 +1232,69 @@ void VDirectoryTree::pinDirectoryToHistory() g_mainWin->getHistoryList()->pinFolder(getVDirectory(curItem)->fetchPath()); g_mainWin->showStatusMessage(tr("1 folder pinned to History")); } + +Qt::DropActions VDirectoryTree::supportedDropActions() const +{ + return Qt::CopyAction | Qt::MoveAction; +} + +bool VDirectoryTree::dropMimeData(QTreeWidgetItem *p_parent, + int p_index, + const QMimeData *p_data, + Qt::DropAction p_action) +{ + Q_UNUSED(p_index); + + if (p_data->hasFormat(ClipboardConfig::c_format)) { + VDirectory *dir = getVDirectory(p_parent); + if (!dir) { + return false; + } + + QByteArray ba = p_data->data(ClipboardConfig::c_format); + QJsonObject obj = QJsonDocument::fromJson(ba).object(); + if (obj.isEmpty()) { + return false; + } + + if (obj[ClipboardConfig::c_type] != (int)ClipboardOpType::CopyFile) { + return false; + } + + QJsonArray files = obj[ClipboardConfig::c_files].toArray(); + if (files.isEmpty()) { + return false; + } + + QVector filesToPaste; + for (auto const & file : files) { + filesToPaste.append(file.toString()); + } + + qDebug() << "paste files from dropped mime data" << dir->getName() << filesToPaste; + g_mainWin->getFileList()->pasteFiles(dir, filesToPaste, p_action == Qt::MoveAction); + return true; + } + + return false; +} + +void VDirectoryTree::dropEvent(QDropEvent *p_event) +{ + // Distinguish copy and cut. + int modifiers = p_event->keyboardModifiers(); + if (modifiers & Qt::ControlModifier) { + p_event->setDropAction(Qt::CopyAction); + } else { + // Cut. + // Will pass to dropMimeData(). + p_event->setDropAction(Qt::MoveAction); + } + + QTreeWidget::dropEvent(p_event); +} + +QStringList VDirectoryTree::mimeTypes() const +{ + return QStringList(ClipboardConfig::c_format); +} diff --git a/src/vdirectorytree.h b/src/vdirectorytree.h index 355c35cc..d7c93888 100644 --- a/src/vdirectorytree.h +++ b/src/vdirectorytree.h @@ -101,6 +101,19 @@ protected: void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE; + QStringList mimeTypes() const Q_DECL_OVERRIDE; + + Qt::DropActions supportedDropActions() const Q_DECL_OVERRIDE; + + // Will be called inside dropEvent(). + bool dropMimeData(QTreeWidgetItem *p_parent, + int p_index, + const QMimeData *p_data, + Qt::DropAction p_action) Q_DECL_OVERRIDE; + + // Drop the data. + void dropEvent(QDropEvent *p_event) Q_DECL_OVERRIDE; + private: // Build the subtree of @p_parent recursively to the depth @p_depth. // @p_depth: negative - infinite levels. diff --git a/src/vfilelist.cpp b/src/vfilelist.cpp index 85248a75..7a3c4982 100644 --- a/src/vfilelist.cpp +++ b/src/vfilelist.cpp @@ -83,12 +83,14 @@ void VFileList::setupUI() titleLayout->setContentsMargins(0, 0, 0, 0); - fileList = new VListWidget(this); + fileList = new VFileListWidget(this); fileList->setContextMenuPolicy(Qt::CustomContextMenu); - fileList->setSelectionMode(QAbstractItemView::ExtendedSelection); fileList->setObjectName("FileList"); fileList->setAttribute(Qt::WA_MacShowFocusRect, false); - fileList->setMouseTracking(true); + fileList->setMimeDataGetter([this](const QString &p_format, + const QList &p_items) { + return getMimeData(p_format, p_items); + }); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addLayout(titleLayout); @@ -1502,3 +1504,22 @@ void VFileList::selectFiles(const QVector &p_files) } } } + +QByteArray VFileList::getMimeData(const QString &p_format, + const QList &p_items) const +{ + Q_UNUSED(p_format); + Q_ASSERT(p_format ==ClipboardConfig::c_format); + + QJsonArray files; + for (int i = 0; i < p_items.size(); ++i) { + VNoteFile *file = getVFile(p_items[i]); + files.append(file->fetchPath()); + } + + QJsonObject obj; + obj[ClipboardConfig::c_type] = (int)ClipboardOpType::CopyFile; + obj[ClipboardConfig::c_files] = files; + + return QJsonDocument(obj).toJson(QJsonDocument::Compact); +} diff --git a/src/vfilelist.h b/src/vfilelist.h index 2a90821f..488b83e9 100644 --- a/src/vfilelist.h +++ b/src/vfilelist.h @@ -13,10 +13,9 @@ #include "vdirectory.h" #include "vnotefile.h" #include "vnavigationmode.h" -#include "vlistwidget.h" +#include "vfilelistwidget.h" class VNote; -class QListWidget; class QPushButton; class VEditArea; class QFocusEvent; @@ -50,6 +49,11 @@ public: QWidget *getContentWidget() const; + // Paste files given path by @p_files to destination directory @p_destDir. + void pasteFiles(VDirectory *p_destDir, + const QVector &p_files, + bool p_isCut); + // Implementations for VNavigationMode. void showNavigation() Q_DECL_OVERRIDE; bool handleKeyNavigation(int p_key, bool &p_succeed) Q_DECL_OVERRIDE; @@ -155,11 +159,6 @@ private: // Return the corresponding QListWidgetItem of @p_file. QListWidgetItem *findItem(const VNoteFile *p_file); - // Paste files given path by @p_files to destination directory @p_destDir. - void pasteFiles(VDirectory *p_destDir, - const QVector &p_files, - bool p_isCut); - inline QPointer getVFile(QListWidgetItem *p_item) const; // Fill the info of @p_item according to @p_file. @@ -190,9 +189,11 @@ private: void selectFiles(const QVector &p_files); + QByteArray getMimeData(const QString &p_format, const QList &p_items) const; + VEditArea *editArea; - VListWidget *fileList; + VFileListWidget *fileList; QLabel *m_numLabel; diff --git a/src/vfilelistwidget.cpp b/src/vfilelistwidget.cpp new file mode 100644 index 00000000..61a8dcba --- /dev/null +++ b/src/vfilelistwidget.cpp @@ -0,0 +1,44 @@ +#include "vfilelistwidget.h" + +#include +#include +#include +#include +#include + +#include "vconstants.h" + +VFileListWidget::VFileListWidget(QWidget *p_parent) + : VListWidget(p_parent) +{ + setSelectionMode(QAbstractItemView::ExtendedSelection); + setDragEnabled(true); + setMouseTracking(true); +} + +QStringList VFileListWidget::mimeTypes() const +{ + return QStringList(ClipboardConfig::c_format); +} + +QMimeData *VFileListWidget::mimeData(const QList p_items) const +{ + const QString format(ClipboardConfig::c_format); + QStringList types = mimeTypes(); + if (!types.contains(format) || p_items.isEmpty()) { + return NULL; + } + + if (m_mimeDataGetter) { + QMimeData *data = new QMimeData(); + data->setData(format, m_mimeDataGetter(format, p_items)); + return data; + } + + return NULL; +} + +void VFileListWidget::setMimeDataGetter(const MimeDataGetterFunc &p_getter) +{ + m_mimeDataGetter = p_getter; +} diff --git a/src/vfilelistwidget.h b/src/vfilelistwidget.h new file mode 100644 index 00000000..8d2b7109 --- /dev/null +++ b/src/vfilelistwidget.h @@ -0,0 +1,28 @@ +#ifndef VFILELISTWIDGET_H +#define VFILELISTWIDGET_H + +#include "vlistwidget.h" + +#include +#include + +typedef std::function &)> MimeDataGetterFunc; + +class VFileListWidget : public VListWidget +{ + Q_OBJECT +public: + explicit VFileListWidget(QWidget *p_parent = nullptr); + + void setMimeDataGetter(const MimeDataGetterFunc &p_getter); + +protected: + QStringList mimeTypes() const Q_DECL_OVERRIDE; + + QMimeData *mimeData(const QList p_items) const Q_DECL_OVERRIDE; + +private: + MimeDataGetterFunc m_mimeDataGetter; +}; + +#endif // VFILELISTWIDGET_H