From d4d1b7d6ce505a6bd9940fa4a47d19c635bb6557 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 4 Jul 2019 12:49:31 -0500 Subject: [PATCH] fixed dragging in tree view --- app/CMakeLists.txt | 1 + app/project/projectviewmodel.cpp | 103 ++++++++++++++++++++++++------- app/project/projectviewmodel.h | 2 + app/undo/CMakeLists.txt | 22 +++++++ app/undo/undostack.cpp | 3 + app/undo/undostack.h | 10 +++ 6 files changed, 119 insertions(+), 22 deletions(-) create mode 100644 app/undo/CMakeLists.txt create mode 100644 app/undo/undostack.cpp create mode 100644 app/undo/undostack.h diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 4329f7b8d..fefc4a8d0 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -32,6 +32,7 @@ add_subdirectory(tool) add_subdirectory(ui) add_subdirectory(widget) add_subdirectory(window) +add_subdirectory(undo) set(OLIVE_TARGET "olive-editor") if(APPLE) diff --git a/app/project/projectviewmodel.cpp b/app/project/projectviewmodel.cpp index a526fd89c..15d19795c 100644 --- a/app/project/projectviewmodel.cpp +++ b/app/project/projectviewmodel.cpp @@ -67,32 +67,21 @@ QModelIndex ProjectViewModel::parent(const QModelIndex &child) const Item* item = static_cast(child.internalPointer()); // Get Item's parent object - Item* parent = item->parent(); + Item* par = item->parent(); // If the parent is the root, return an empty index - if (parent == project_->root()) { + if (par == project_->root()) { return QModelIndex(); } // Otherwise return a true index to its parent - - // Find parent's index within its own parent - // (TODO: this model should handle sorting, which means it'll have to "know" the indices) - int child_index = -1; - - Item* double_parent = parent->parent(); - for (int i=0;ichild_count();i++) { - if (double_parent->child(i) == parent) { - child_index = i; - break; - } - } + int parent_index = indexOfChild(par); // Make sure the index is valid (there's no reason it shouldn't be) - Q_ASSERT(child_index > -1); + Q_ASSERT(parent_index > -1); // Return an index to the parent - return createIndex(child_index, 0, parent); + return createIndex(parent_index, 0, par); } int ProjectViewModel::rowCount(const QModelIndex &parent) const @@ -209,26 +198,38 @@ QStringList ProjectViewModel::mimeTypes() const QMimeData *ProjectViewModel::mimeData(const QModelIndexList &indexes) const { + // Encode mime data for the rows/items that were dragged QMimeData* data = new QMimeData(); + // Use QDataStream to stream the item data into a byte array QByteArray encoded_data; QDataStream stream(&encoded_data, QIODevice::WriteOnly); + // The indexes list includes indexes for each column which we don't use. To make sure each row only gets sent *once*, + // we keep a list of dragged items + QVector dragged_items; + foreach (QModelIndex index, indexes) { if (index.isValid()) { - stream << reinterpret_cast(index.internalPointer()); + // Check if we've dragged this item before + if (!dragged_items.contains(index.internalPointer())) { + // If not, add it to the stream (and also keep track of it in the vector)s + stream << index.row() << reinterpret_cast(index.internalPointer()); + dragged_items.append(index.internalPointer()); + } } } + // Set byte array as the mime data and return the mime data data->setData("application/x-oliveprojectitemdata", encoded_data); return data; } -bool ProjectViewModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) +bool ProjectViewModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &drop) { // Default recommended checks from https://doc.qt.io/qt-5/model-view-programming.html#using-drag-and-drop-with-item-views - if (!canDropMimeData(data, action, row, column, parent)) { + if (!canDropMimeData(data, action, row, column, drop)) { return false; } @@ -243,18 +244,58 @@ bool ProjectViewModel::dropMimeData(const QMimeData *data, Qt::DropAction action // Data is drag/drop data from this model QByteArray model_data = data->data("application/x-oliveprojectitemdata"); - // Use stream to deserialize the data + // Use QDataStream to deserialize the data QDataStream stream(&model_data, QIODevice::ReadOnly); + // Get the Item object that the items were dropped on + Item* drop_location; + + // If the index is valid, move them to the containing object + if (drop.isValid()) { + drop_location = static_cast(drop.internalPointer()); + + // If this is not a folder, we cannot drop these items here + if (drop_location->type() != Item::kFolder) { + return false; + } + } else { + // If the index isn't valid, the items are moving to the root + drop_location = project_->root(); + } + // Variables to deserialize into quintptr item_ptr; + int r; // Loop through all data while (!stream.atEnd()) { - stream >> item_ptr; + stream >> r >> item_ptr; + // Get the Item pointer from the mime data Item* item = reinterpret_cast(item_ptr); - qDebug() << "Dragged" << item->name(); + + // Get the Item's parent + Item* parent_item = item->parent(); + + // If the Drop Item is the Item or its parent already, this is a no-op + if (item != drop_location && parent_item != drop_location) { + + // Get an index to the parent + QModelIndex parent_index; + + // If the parent item is not the root, we'll need to actually create an index + if (parent_item != project()->root()) { + parent_index = createIndex(indexOfChild(parent_item), 0, parent_item); + } + + // Signal to all views that we're about to move an object + beginMoveRows(parent_index, r, r, drop, drop_location->child_count()); + + // Move the object + item->set_parent(drop_location); + + endMoveRows(); + } } return true; @@ -262,3 +303,21 @@ bool ProjectViewModel::dropMimeData(const QMimeData *data, Qt::DropAction action return false; } + +int ProjectViewModel::indexOfChild(Item *item) const +{ + // Find parent's index within its own parent + // (TODO: this model should handle sorting, which means it'll have to "know" the indices) + + Item* parent = item->parent(); + + if (parent != nullptr) { + for (int i=0;ichild_count();i++) { + if (parent->child(i) == item) { + return i; + } + } + } + + return -1; +} diff --git a/app/project/projectviewmodel.h b/app/project/projectviewmodel.h index 29fb66b7d..a565af06f 100644 --- a/app/project/projectviewmodel.h +++ b/app/project/projectviewmodel.h @@ -89,6 +89,8 @@ public: virtual QMimeData * mimeData(const QModelIndexList &indexes) const override; virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; private: + int indexOfChild(Item* item) const; + Project* project_; QVector columns_; diff --git a/app/undo/CMakeLists.txt b/app/undo/CMakeLists.txt new file mode 100644 index 000000000..82052b803 --- /dev/null +++ b/app/undo/CMakeLists.txt @@ -0,0 +1,22 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2019 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + undo/undostack.h + undo/undostack.cpp + PARENT_SCOPE +) diff --git a/app/undo/undostack.cpp b/app/undo/undostack.cpp new file mode 100644 index 000000000..74fea6a08 --- /dev/null +++ b/app/undo/undostack.cpp @@ -0,0 +1,3 @@ +#include "undostack.h" + +QUndoStack olive::undo_stack; diff --git a/app/undo/undostack.h b/app/undo/undostack.h new file mode 100644 index 000000000..cb525d370 --- /dev/null +++ b/app/undo/undostack.h @@ -0,0 +1,10 @@ +#ifndef UNDOSTACK_H +#define UNDOSTACK_H + +#include + +namespace olive { +extern QUndoStack undo_stack; +} + +#endif // UNDOSTACK_H