diff --git a/plugins/dockers/storyboarddocker/commentModel.cpp b/plugins/dockers/storyboarddocker/commentModel.cpp index 362b163338..801f70ffb9 100644 --- a/plugins/dockers/storyboarddocker/commentModel.cpp +++ b/plugins/dockers/storyboarddocker/commentModel.cpp @@ -1,124 +1,194 @@ /* * Copyright (c) 2020 Saurabh Kumar * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "commentModel.h" #include +#include #include CommentModel::CommentModel(QObject *parent) : QAbstractListModel(parent) { //initialize variables } int CommentModel::rowCount(const QModelIndex &parent) const { return m_commentList.count(); } QVariant CommentModel::data(const QModelIndex &index, int role) const { if(!index.isValid()) return QVariant(); if(index.row() >= m_commentList.size()) return QVariant(); if (role == Qt::DisplayRole || role == Qt::EditRole) { return m_commentList.at(index.row()).name; } if (role == Qt::DecorationRole) { if (m_commentList.at(index.row()).visibility){ return KisIconUtils::loadIcon("visible"); } else { return KisIconUtils::loadIcon("novisible"); } } return QVariant(); } bool CommentModel::setData(const QModelIndex & index, const QVariant & value, int role) { if (index.isValid() && (role == Qt::EditRole || role == Qt::DisplayRole)) { m_commentList[index.row()].name = value.toString(); emit dataChanged(index, index); return true; } if (index.isValid() && role == Qt::DecorationRole){ m_commentList[index.row()].visibility = !m_commentList[index.row()].visibility; emit dataChanged(index, index); return true; } return false; } Qt::ItemFlags CommentModel::flags(const QModelIndex & index) const { if(!index.isValid()) return Qt::ItemIsDropEnabled; return Qt::ItemIsDragEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled ; } bool CommentModel::insertRows(int position, int rows, const QModelIndex &parent) { beginInsertRows(QModelIndex(), position, position+rows-1); for (int row = 0; row < rows; ++row) { Comment newcomment; //maybe set a default name like comment 1 newcomment.name = ""; newcomment.visibility = true; if (position < 0 && position>=m_commentList.size()) return false; m_commentList.insert(position, newcomment); } endInsertRows(); return true; } bool CommentModel::removeRows(int position, int rows, const QModelIndex &parent) { beginRemoveRows(QModelIndex(), position, position+rows-1); for (int row = 0; row < rows; ++row) { if (position < 0 || position >= m_commentList.size()) return false; m_commentList.removeAt(position); } - endRemoveRows(); return true; } +bool CommentModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, + const QModelIndex &destinationParent, int destinationChild) +{ + if (destinationChild == sourceRow || destinationChild == sourceRow + 1){ + return false; + } + if (destinationChild > sourceRow + count - 1){ + //we adjust for the upward shift, see qt doc for why this is needed + beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild + count - 1); + destinationChild = destinationChild - count; + } + else { + beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild); + } + for (int row = 0; row < count; row++){ + if (sourceRow < 0 || sourceRow >= m_commentList.size()) return false; + if (destinationChild + row < 0 || destinationChild + row >= m_commentList.size()) return false; + m_commentList.move(sourceRow, destinationChild + row); + } + endMoveRows(); + return true; +} + +QMimeData *CommentModel::mimeData(const QModelIndexList &indexes) const +{ + QMimeData *mimeData = new QMimeData(); + QByteArray encodeData; + + QDataStream stream(&encodeData, QIODevice::WriteOnly); + + //take the row number of the index where drag started + foreach (QModelIndex index, indexes){ + if (index.isValid()) { + int row = index.row(); + stream << row; + } + } + + mimeData->setData("application/x-qabstractitemmodeldatalist", encodeData); //default mimetype + return mimeData; +} + +bool CommentModel::dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent) +{ + if (action == Qt::IgnoreAction) + return false; + + if (action == Qt::MoveAction && data->hasFormat("application/x-qabstractitemmodeldatalist")){ + QByteArray bytes = data->data("application/x-qabstractitemmodeldatalist"); + QDataStream stream(&bytes, QIODevice::ReadOnly); + + if (parent.isValid()){ + return false; + } + int sourceRow; + QModelIndexList moveRowIndexes; + while (!stream.atEnd()) { + stream >> sourceRow; + QModelIndex index = createIndex(sourceRow, 0); + moveRowIndexes.append(index); + } + moveRows(QModelIndex(), moveRowIndexes.at(0).row(), moveRowIndexes.count(), parent, row); + + //returning true deletes the source row + return false; + } + return false; +} + Qt::DropActions CommentModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } Qt::DropActions CommentModel::supportedDragActions() const { return Qt::CopyAction | Qt::MoveAction; } diff --git a/plugins/dockers/storyboarddocker/commentModel.h b/plugins/dockers/storyboarddocker/commentModel.h index 45603448f3..41ba1b578b 100644 --- a/plugins/dockers/storyboarddocker/commentModel.h +++ b/plugins/dockers/storyboarddocker/commentModel.h @@ -1,59 +1,63 @@ /* * Copyright (c) 2020 Saurabh Kumar * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef COMMENT_MODEL #define COMMENT_MODEL #include class StoryboardModel; struct Comment { QString name; bool visibility; }; /* This model manages the comment data of StoryboardModel */ class CommentModel : public QAbstractListModel { Q_OBJECT public: CommentModel(QObject *parent = 0); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()); bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()); + bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, + const QModelIndex &destinationParent, int destinationChild); + QMimeData *mimeData(const QModelIndexList &indexes) const; + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); Qt::DropActions supportedDropActions() const override; Qt::DropActions supportedDragActions() const override; private: QVector m_commentList; friend class StoryboardModel; }; #endif \ No newline at end of file diff --git a/plugins/dockers/storyboarddocker/storyboardItem.cpp b/plugins/dockers/storyboarddocker/storyboardItem.cpp index 5284d9435f..e0b3b0fd11 100644 --- a/plugins/dockers/storyboarddocker/storyboardItem.cpp +++ b/plugins/dockers/storyboarddocker/storyboardItem.cpp @@ -1,62 +1,67 @@ /* * Copyright (c) 2020 Saurabh Kumar * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "storyboardItem.h" //create 3 rows by default StoryboardItem::StoryboardItem() { insertChild(childCount(), 1); insertChild(childCount(), QString("scene 1")); insertChild(childCount(), 100); //duration-seconds insertChild(childCount(), 23); //duration-frames } StoryboardItem::~StoryboardItem() { qDeleteAll(m_childData); } void StoryboardItem::appendChild(QVariant data) { StoryboardChild* child = new StoryboardChild(data, this); m_childData.append(child); } void StoryboardItem::insertChild(int row, QVariant data) { StoryboardChild* child = new StoryboardChild(data, this); m_childData.insert(row, child); } void StoryboardItem::removeChild(int row) { m_childData.removeAt(row); } +void StoryboardItem::moveChild(int from, int to) +{ + m_childData.move(from, to); +} + int StoryboardItem::childCount() const { return m_childData.count(); } StoryboardChild* StoryboardItem::child(int row) { if (row < 0 || row >= m_childData.size()) return nullptr; return m_childData.at(row); } diff --git a/plugins/dockers/storyboarddocker/storyboardItem.h b/plugins/dockers/storyboarddocker/storyboardItem.h index 55dc0978c0..91260e5d4a 100644 --- a/plugins/dockers/storyboarddocker/storyboardItem.h +++ b/plugins/dockers/storyboarddocker/storyboardItem.h @@ -1,71 +1,72 @@ /* * Copyright (c) 2020 Saurabh Kumar * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef STORYBOARD_ITEM #define STORYBOARD_ITEM #include #include //each storyboardItem contains pointer to child data class StoryboardItem; class StoryboardChild { public: StoryboardChild(QVariant data, StoryboardItem *parent) : m_data(data) , m_parentItem(parent) {} StoryboardItem *parent(){ return m_parentItem;} QVariant data(){ return m_data;} //returns the row number of this child relative to its parent /*int row() const{ if (m_parentItem) return m_parentItem->m_childData.indexOf(const_cast(this)); return 0; } */ void setData(QVariant value){ m_data = value; } private: QVariant m_data; StoryboardItem *m_parentItem; }; class StoryboardItem { public: //see later if the constructor needs data explicit StoryboardItem(/*const QVector &data*/); ~StoryboardItem(); void appendChild(QVariant data); void insertChild(int row, QVariant data); void removeChild(int row); + void moveChild(int from, int to); int childCount() const; StoryboardChild *child(int row); private: QVector m_childData; }; #endif diff --git a/plugins/dockers/storyboarddocker/storyboardModel.cpp b/plugins/dockers/storyboarddocker/storyboardModel.cpp index 173ba0106e..7fd6b57310 100644 --- a/plugins/dockers/storyboarddocker/storyboardModel.cpp +++ b/plugins/dockers/storyboarddocker/storyboardModel.cpp @@ -1,277 +1,317 @@ /* * Copyright (c) 2020 Saurabh Kumar * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "storyboardModel.h" #include #include StoryboardModel::StoryboardModel(QObject *parent) : QAbstractItemModel(parent) , m_commentCount(0) {} QModelIndex StoryboardModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) return QModelIndex(); if (row < 0 || row >= rowCount()) return QModelIndex(); if (column !=0) return QModelIndex(); //1st level node has invalid parent if (!parent.isValid()){ return createIndex(row, column, m_items.at(row)); } else if (!parent.parent().isValid()){ StoryboardItem *parentItem = static_cast(parent.internalPointer()); StoryboardChild *childItem = parentItem->child(row); if (childItem) return createIndex(row, column, childItem); } return QModelIndex(); } QModelIndex StoryboardModel::parent(const QModelIndex &index) const { if (!index.isValid()) return QModelIndex(); { //no parent for 1st level node StoryboardItem *childItem = static_cast(index.internalPointer()); if (m_items.contains(childItem)){ return QModelIndex(); } } //return parent only for 2nd level nodes StoryboardChild *childItem = static_cast(index.internalPointer()); StoryboardItem *parentItem = childItem->parent(); int indexOfParent = m_items.indexOf(const_cast(parentItem)); return createIndex(indexOfParent, 0, parentItem); } int StoryboardModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) return m_items.count(); else if (!parent.parent().isValid()){ StoryboardItem *parentItem = static_cast(parent.internalPointer()); return parentItem->childCount(); } return 0; //2nd level nodes have no child } int StoryboardModel::columnCount(const QModelIndex &parent) const { if (!parent.isValid()){ return 1; } //1st level nodes have 1 column if (!parent.parent().isValid()){ return 1; } //end level nodes have no child return 0; } QVariant StoryboardModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (index.row() >= m_items.size()) return QVariant(); if(role == Qt::SizeHintRole){ return QSize(200,200); } //return data only for the storyboardChild i.e. 2nd level nodes if (!index.parent().isValid()){ return QVariant(); } if (role == Qt::DisplayRole || role == Qt::EditRole) { StoryboardChild *child = static_cast(index.internalPointer()); return child->data(); } return QVariant(); } bool StoryboardModel::setData(const QModelIndex & index, const QVariant & value, int role) { if (index.isValid() && (role == Qt::EditRole || role == Qt::DisplayRole)) { if (!index.parent().isValid()) return false; StoryboardChild *child = static_cast(index.internalPointer()); if (child){ int fps = 24; if(index.row() == 3 && value.toInt() >= fps){ //TODO : set fps QModelIndex secondIndex = index.siblingAtRow(2); setData(secondIndex, secondIndex.data().toInt() + value.toInt() / fps, role); child->setData(value.toInt() % fps); } else { child->setData(value); } emit dataChanged(index, index); return true; } } return false; } Qt::ItemFlags StoryboardModel::flags(const QModelIndex & index) const { if(!index.isValid()) return Qt::ItemIsDropEnabled; //1st level nodes if (!index.parent().isValid()) return Qt::ItemIsDragEnabled | Qt::ItemIsSelectable | Qt::ItemIsEnabled; //2nd level nodes return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren; } bool StoryboardModel::insertRows(int position, int rows, const QModelIndex &parent) { if (!parent.isValid()){ beginInsertRows(QModelIndex(), position, position+rows-1); for (int row = 0; row < rows; ++row) { StoryboardItem *newItem = new StoryboardItem(); m_items.insert(position, newItem); } endInsertRows(); return true; } //insert 2nd level nodes if (!parent.parent().isValid()){ StoryboardItem *item = static_cast(parent.internalPointer()); beginInsertRows(QModelIndex(), position, position+rows-1); for (int row = 0; row < rows; ++row) { item->insertChild(position, QVariant()); } endInsertRows(); return true; } //we can't insert to 2nd level nodes as they are leaf nodes return false; } bool StoryboardModel::removeRows(int position, int rows, const QModelIndex &parent) { //remove 1st level nodes if (!parent.isValid()){ beginRemoveRows(QModelIndex(), position, position+rows-1); for (int row = 0; row < rows; ++row) { delete m_items.at(row); m_items.removeAt(position); } endRemoveRows(); return true; } //remove 2nd level nodes if (!parent.parent().isValid()){ StoryboardItem *item = static_cast(parent.internalPointer()); if (m_items.contains(item)){ beginRemoveRows(QModelIndex(), position, position+rows-1); for (int row = 0; row < rows; ++row) { item->removeChild(position); } endRemoveRows(); return true; } } //2nd level node has no child return false; } -Qt::DropActions StoryboardModel::supportedDropActions() const +bool StoryboardModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, + const QModelIndex &destinationParent, int destinationChild) { + if (sourceParent != destinationParent){ + return false; + } + if (destinationChild == sourceRow || destinationChild == sourceRow + 1){ + return false; + } + if (destinationChild > sourceRow + count - 1){ + //we adjust for the upward shift, see qt doc for why this is needed + beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild + count - 1); + destinationChild = destinationChild - count; + } + else { + beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild); + } + + //only implementing for moves within the 1st level nodes for comment nodes + if (sourceParent == destinationParent && !sourceParent.parent().isValid()){ + const QModelIndex parent = sourceParent; + for (int row = 0; row < count; row++){ + if (sourceRow < 4 || sourceRow >= rowCount(parent)) return false; + if (destinationChild + row < 4 || destinationChild + row >= rowCount(parent)) return false; + + StoryboardItem *item = static_cast(parent.internalPointer()); + item->moveChild(sourceRow, destinationChild + row); + } + endMoveRows(); + return true; + } + else { + return false; + } +} +Qt::DropActions StoryboardModel::supportedDropActions() const +{ return Qt::CopyAction | Qt::MoveAction; } Qt::DropActions StoryboardModel::supportedDragActions() const { return Qt::CopyAction | Qt::MoveAction; } int StoryboardModel::commentCount() const { return m_commentList.count(); } void StoryboardModel::setCommentModel(CommentModel *commentModel) { m_commentModel = commentModel; connect(m_commentModel, SIGNAL(dataChanged(const QModelIndex ,const QModelIndex)), this, SLOT(slotCommentDataChanged())); connect(m_commentModel, SIGNAL(rowsRemoved(const QModelIndex ,int, int)), this, SLOT(slotCommentRowRemoved(const QModelIndex ,int, int))); connect(m_commentModel, SIGNAL(rowsInserted(const QModelIndex, int, int)), this, SLOT(slotCommentRowInserted(const QModelIndex, int, int))); - //TODO: handle move events + connect(m_commentModel, SIGNAL(rowsMoved(const QModelIndex, int, int, const QModelIndex, int)), + this, SLOT(slotCommentRowMoved(const QModelIndex, int, int, const QModelIndex, int))); } Comment StoryboardModel::getComment(int row) const { return m_commentList.at(row); } void StoryboardModel::slotCommentDataChanged() { m_commentList = m_commentModel->m_commentList; emit(dataChanged(QModelIndex(), QModelIndex())); } void StoryboardModel::slotCommentRowInserted(const QModelIndex parent, int first, int last) { int numItems = rowCount(); for(int row = 0; row < numItems; row++){ QModelIndex parentIndex = index(row, 0); insertRows(4 + first, last - first + 1, parentIndex); //four indices are already there } slotCommentDataChanged(); } void StoryboardModel::slotCommentRowRemoved(const QModelIndex parent, int first, int last) { int numItems = rowCount(); for(int row = 0; row < numItems; row++){ QModelIndex parentIndex = index(row, 0); removeRows(4 + first, last - first + 1, parentIndex); } slotCommentDataChanged(); } -/* -void StoryboardModel::slotCommentRowsMoved() +void StoryboardModel::slotCommentRowMoved(const QModelIndex &sourceParent, int start, int end, + const QModelIndex &destinationParent, int destinationRow) { - qDebug()<<"comment row moved"; -} -*/ + int numItems = rowCount(); + for(int row = 0; row < numItems; row++){ + QModelIndex parentIndex = index(row, 0); + moveRows(parentIndex, start + 4, end - start + 1, parentIndex, destinationRow + 4); + } + slotCommentDataChanged(); +} \ No newline at end of file diff --git a/plugins/dockers/storyboarddocker/storyboardModel.h b/plugins/dockers/storyboarddocker/storyboardModel.h index cf19df1cce..d934ae9380 100644 --- a/plugins/dockers/storyboarddocker/storyboardModel.h +++ b/plugins/dockers/storyboarddocker/storyboardModel.h @@ -1,76 +1,80 @@ /* * Copyright (c) 2020 Saurabh Kumar * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef STORYBOARD_MODEL #define STORYBOARD_MODEL #include #include #include "storyboardItem.h" #include "commentModel.h" /* The main storyboard model. */ class StoryboardModel : public QAbstractItemModel { Q_OBJECT public: //if we don't need this constructor change it StoryboardModel(QObject *parent = nullptr); QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; //QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; //for removing and inserting rows bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()); bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()); + bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, + const QModelIndex &destinationParent, int destinationChild); //for drag and drop Qt::DropActions supportedDropActions() const override; Qt::DropActions supportedDragActions() const override; //this function accesses the value from the comment model int commentCount() const; void setCommentModel(CommentModel *commentModel); Comment getComment(int row) const; private Q_SLOTS: void slotCommentDataChanged(); void slotCommentRowInserted(const QModelIndex, int, int); void slotCommentRowRemoved(const QModelIndex, int, int); + void slotCommentRowMoved(const QModelIndex &sourceParent, int sourceRow, int count, + const QModelIndex &destinationParent, int destinationChild); private: QVector m_items; int m_commentCount = 0; QVector m_commentList; CommentModel *m_commentModel; }; #endif \ No newline at end of file