Browse Source

FileList: support drag&drop to copy/move files

Le Tan 7 years ago
parent
commit
ba75c94f40
8 changed files with 192 additions and 13 deletions
  1. 4 2
      src/src.pro
  2. 1 0
      src/vconstants.h
  3. 69 0
      src/vdirectorytree.cpp
  4. 13 0
      src/vdirectorytree.h
  5. 24 3
      src/vfilelist.cpp
  6. 9 8
      src/vfilelist.h
  7. 44 0
      src/vfilelistwidget.cpp
  8. 28 0
      src/vfilelistwidget.h

+ 4 - 2
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 \

+ 1 - 0
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 };

+ 69 - 0
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<QString> 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);
+}

+ 13 - 0
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.

+ 24 - 3
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<QListWidgetItem *> &p_items) {
+                return getMimeData(p_format, p_items);
+            });
 
     QVBoxLayout *mainLayout = new QVBoxLayout;
     mainLayout->addLayout(titleLayout);
@@ -1502,3 +1504,22 @@ void VFileList::selectFiles(const QVector<VNoteFile *> &p_files)
         }
     }
 }
+
+QByteArray VFileList::getMimeData(const QString &p_format,
+                                  const QList<QListWidgetItem *> &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);
+}

+ 9 - 8
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<QString> &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<QString> &p_files,
-                    bool p_isCut);
-
     inline QPointer<VNoteFile> getVFile(QListWidgetItem *p_item) const;
 
     // Fill the info of @p_item according to @p_file.
@@ -190,9 +189,11 @@ private:
 
     void selectFiles(const QVector<VNoteFile *> &p_files);
 
+    QByteArray getMimeData(const QString &p_format, const QList<QListWidgetItem *> &p_items) const;
+
     VEditArea *editArea;
 
-    VListWidget *fileList;
+    VFileListWidget *fileList;
 
     QLabel *m_numLabel;
 

+ 44 - 0
src/vfilelistwidget.cpp

@@ -0,0 +1,44 @@
+#include "vfilelistwidget.h"
+
+#include <QDebug>
+#include <QJsonObject>
+#include <QJsonDocument>
+#include <QJsonArray>
+#include <QMimeData>
+
+#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<QListWidgetItem *> 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;
+}

+ 28 - 0
src/vfilelistwidget.h

@@ -0,0 +1,28 @@
+#ifndef VFILELISTWIDGET_H
+#define VFILELISTWIDGET_H
+
+#include "vlistwidget.h"
+
+#include <functional>
+#include <QList>
+
+typedef std::function<QByteArray(const QString &, const QList<QListWidgetItem *> &)> 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<QListWidgetItem *> p_items) const Q_DECL_OVERRIDE;
+
+private:
+    MimeDataGetterFunc m_mimeDataGetter;
+};
+
+#endif // VFILELISTWIDGET_H