Git commit 0808a2108026edf667d6ab916426b6918275fea6 by Dmitry Kazakov. Committed on 13/04/2018 at 18:07. Pushed by dkazakov into branch 'master'. Implement copy-paste of the animation frames Now one can copy-paste frames and columns on the timeline using a special context menu. CC:kimageshop@kde.org M +78 -0 krita/krita.action M +34 -7 plugins/dockers/animation/timeline_frames_model.cpp M +12 -0 plugins/dockers/animation/timeline_frames_model.h M +105 -3 plugins/dockers/animation/timeline_frames_view.cpp M +11 -1 plugins/dockers/animation/timeline_frames_view.h M +13 -0 plugins/dockers/animation/timeline_ruler_header.cpp M +4 -0 plugins/dockers/animation/timeline_ruler_header.h https://commits.kde.org/krita/0808a2108026edf667d6ab916426b6918275fea6 diff --git a/krita/krita.action b/krita/krita.action index 3ede68c367a..55a465f0580 100644 --- a/krita/krita.action +++ b/krita/krita.action @@ -2426,6 +2426,84 @@ = + + + Copy to Clipboard + + Copy frames to clipboard + + 100000 + 0 + + false + + + + + + Cut to Clipboard + + Cut frames to clipboard + + 100000 + 0 + + false + + + + + + Paste from Clipboard + + Paste frames from clipboard + + 100000 + 0 + + false + + + + + + Copy Columns to Clipboard + + Copy columns to clipboard + + 100000 + 0 + + false + + + + + + Cut Columns to Clipboard + + Cut columns to clipboard + + 100000 + 0 + + false + + + + + + Paste Columns from Clipboard + + Paste columns from clipboard + + 100000 + 0 + + false + + + = = diff --git a/plugins/dockers/animation/timeline_frames_model.cpp b/plugins/= dockers/animation/timeline_frames_model.cpp index f98e1473393..8bfa6cd8693 100644 --- a/plugins/dockers/animation/timeline_frames_model.cpp +++ b/plugins/dockers/animation/timeline_frames_model.cpp @@ -537,14 +537,21 @@ void TimelineFramesModel::setLastClickedIndex(const Q= ModelIndex &index) } = QMimeData* TimelineFramesModel::mimeData(const QModelIndexList &indexes) c= onst +{ + return mimeDataExtended(indexes, m_d->lastClickedIndex, UndefinedPolic= y); +} + +QMimeData *TimelineFramesModel::mimeDataExtended(const QModelIndexList &in= dexes, + const QModelIndex &baseIn= dex, + TimelineFramesModel::Mime= CopyPolicy copyPolicy) const { QMimeData *data =3D new QMimeData(); = QByteArray encoded; QDataStream stream(&encoded, QIODevice::WriteOnly); = - const int baseRow =3D m_d->lastClickedIndex.row(); - const int baseColumn =3D m_d->lastClickedIndex.column(); + const int baseRow =3D baseIndex.row(); + const int baseColumn =3D baseIndex.column(); = stream << indexes.size(); stream << baseRow << baseColumn; @@ -553,6 +560,7 @@ QMimeData* TimelineFramesModel::mimeData(const QModelIn= dexList &indexes) const stream << index.row() - baseRow << index.column() - baseColumn; } = + stream << int(copyPolicy); data->setData("application/x-krita-frame", encoded); = return data; @@ -582,17 +590,19 @@ bool TimelineFramesModel::dropMimeData(const QMimeDat= a *data, Qt::DropAction act Q_UNUSED(row); Q_UNUSED(column); = - bool result =3D false; + return dropMimeDataExtended(data, action, parent); +} = - if ((action !=3D Qt::MoveAction && - action !=3D Qt::CopyAction) || !parent.isValid()) return result; +bool TimelineFramesModel::dropMimeDataExtended(const QMimeData *data, Qt::= DropAction action, const QModelIndex &parent, bool *dataMoved) +{ + bool result =3D false; = - const bool copyFrames =3D action =3D=3D Qt::CopyAction; + if ((action !=3D Qt::MoveAction && action !=3D Qt::CopyAction) || + !parent.isValid()) return result; = QByteArray encoded =3D data->data("application/x-krita-frame"); QDataStream stream(&encoded, QIODevice::ReadOnly); = - int size, baseRow, baseColumn; stream >> size >> baseRow >> baseColumn; = @@ -608,6 +618,23 @@ bool TimelineFramesModel::dropMimeData(const QMimeData= *data, Qt::DropAction act srcIndexes << index(srcRow, srcColumn); } = + MimeCopyPolicy copyPolicy =3D UndefinedPolicy; + + if (!stream.atEnd()) { + int value =3D 0; + stream >> value; + copyPolicy =3D MimeCopyPolicy(value); + } + + const bool copyFrames =3D + copyPolicy =3D=3D UndefinedPolicy ? + action =3D=3D Qt::CopyAction : + copyPolicy =3D=3D CopyFramesPolicy; + + if (dataMoved) { + *dataMoved =3D !copyFrames; + } + const QPoint offset(parent.column() - baseColumn, parent.row() - baseR= ow); = return offsetFrames(srcIndexes, offset, copyFrames); diff --git a/plugins/dockers/animation/timeline_frames_model.h b/plugins/do= ckers/animation/timeline_frames_model.h index 0f053c29587..7c93cd05dca 100644 --- a/plugins/dockers/animation/timeline_frames_model.h +++ b/plugins/dockers/animation/timeline_frames_model.h @@ -36,6 +36,14 @@ class KisAnimationPlayer; class KRITAANIMATIONDOCKER_EXPORT TimelineFramesModel : public TimelineNod= eListKeeper::ModelWithExternalNotifications { Q_OBJECT + +public: + enum MimeCopyPolicy { + UndefinedPolicy =3D 0, + MoveFramesPolicy, + CopyFramesPolicy + }; + public: TimelineFramesModel(QObject *parent); ~TimelineFramesModel() override; @@ -76,7 +84,11 @@ public: Qt::DropActions supportedDropActions() const override; QStringList mimeTypes() const override; QMimeData * mimeData(const QModelIndexList &indexes) const override; + QMimeData * mimeDataExtended(const QModelIndexList &indexes, const QMo= delIndex &baseIndex, MimeCopyPolicy copyPolicy) const; bool dropMimeData(const QMimeData * data, Qt::DropAction action, int r= ow, int column, const QModelIndex & parent) override; + + bool dropMimeDataExtended(const QMimeData *data, Qt::DropAction action= , const QModelIndex &parent, bool *dataMoved =3D 0); + Qt::ItemFlags flags(const QModelIndex &index) const override; = bool insertRows(int row, int count, const QModelIndex &parent) overrid= e; diff --git a/plugins/dockers/animation/timeline_frames_view.cpp b/plugins/d= ockers/animation/timeline_frames_view.cpp index 0f3448d57a8..6c5f2b3c788 100644 --- a/plugins/dockers/animation/timeline_frames_view.cpp +++ b/plugins/dockers/animation/timeline_frames_view.cpp @@ -65,6 +65,9 @@ #include #include = +#include +#include + #include "config-qtmultimedia.h" = typedef QPair QItemViewPaintPair; @@ -172,6 +175,10 @@ TimelineFramesView::TimelineFramesView(QWidget *parent) = connect(m_d->horizontalRuler, SIGNAL(sigMirrorColumns()), SLOT(slotMir= rorColumns())); = + connect(m_d->horizontalRuler, SIGNAL(sigCopyColumns()), SLOT(slotCopyC= olumns())); + connect(m_d->horizontalRuler, SIGNAL(sigCutColumns()), SLOT(slotCutCol= umns())); + connect(m_d->horizontalRuler, SIGNAL(sigPasteColumns()), SLOT(slotPast= eColumns())); + m_d->layersHeader =3D new TimelineLayersHeader(this); = m_d->layersHeader->setSectionResizeMode(QHeaderView::Fixed); @@ -336,6 +343,16 @@ void TimelineFramesView::setActionManager( KisActionMa= nager * actionManager) = action =3D m_d->actionMan->createAction("mirror_frames"); connect(action, SIGNAL(triggered()), SLOT(slotMirrorFrames())); + + action =3D m_d->actionMan->createAction("copy_frames_to_clipboard"= ); + connect(action, SIGNAL(triggered()), SLOT(slotCopyFrames())); + + action =3D m_d->actionMan->createAction("cut_frames_to_clipboard"); + connect(action, SIGNAL(triggered()), SLOT(slotCutFrames())); + + action =3D m_d->actionMan->createAction("paste_frames_from_clipboa= rd"); + connect(action, SIGNAL(triggered()), SLOT(slotPasteFrames())); + } } = @@ -952,6 +969,10 @@ void TimelineFramesView::mousePressEvent(QMouseEvent *= event) } = QMenu menu; + addActionToMenu(&menu, "cut_frames_to_clipboard"); + addActionToMenu(&menu, "copy_frames_to_clipboard"); + addActionToMenu(&menu, "paste_frames_from_clipboard"); + menu.addSeparator(); addActionToMenu(&menu, "insert_keyframes_right"); addActionToMenu(&menu, "insert_keyframes_left"); menu.addSeparator(); @@ -979,6 +1000,10 @@ void TimelineFramesView::mousePressEvent(QMouseEvent = *event) } = QMenu menu; + addActionToMenu(&menu, "cut_frames_to_clipboard"); + addActionToMenu(&menu, "copy_frames_to_clipboard"); + addActionToMenu(&menu, "paste_frames_from_clipboard"); + menu.addSeparator(); addActionToMenu(&menu, "add_blank_frame"); addActionToMenu(&menu, "add_duplicate_frame"); menu.addSeparator(); @@ -1023,6 +1048,10 @@ void TimelineFramesView::mousePressEvent(QMouseEvent= *event) } = QMenu menu; + addActionToMenu(&menu, "cut_frames_to_clipboard"); + addActionToMenu(&menu, "copy_frames_to_clipboard"); + addActionToMenu(&menu, "paste_frames_from_clipboard"); + menu.addSeparator(); addActionToMenu(&menu, "insert_keyframes_right"); addActionToMenu(&menu, "insert_keyframes_left"); menu.addSeparator(); @@ -1336,7 +1365,7 @@ void TimelineFramesView::slotInsertColumnsRightCustom= () } } = -QModelIndexList TimelineFramesView::calculateSelectionSpan(bool forceEntir= eColumn) const +QModelIndexList TimelineFramesView::calculateSelectionSpan(bool forceEntir= eColumn, bool editableOnly) const { QModelIndexList indexes; = @@ -1349,7 +1378,8 @@ QModelIndexList TimelineFramesView::calculateSelectio= nSpan(bool forceEntireColum = rows.clear(); for (int i =3D 0; i < m_d->model->rowCount(); i++) { - if (!m_d->model->data(m_d->model->index(i, minColumn), Timelin= eFramesModel::FrameEditableRole).toBool()) continue; + if (editableOnly && + !m_d->model->data(m_d->model->index(i, minColumn), Timelin= eFramesModel::FrameEditableRole).toBool()) continue; = for (int column =3D minColumn; column <=3D maxColumn; column++= ) { indexes << m_d->model->index(i, column); @@ -1357,7 +1387,7 @@ QModelIndexList TimelineFramesView::calculateSelectio= nSpan(bool forceEntireColum } } else { Q_FOREACH (const QModelIndex &index, selectionModel()->selectedInd= exes()) { - if (m_d->model->data(index, TimelineFramesModel::FrameEditable= Role).toBool()) { + if (!editableOnly || m_d->model->data(index, TimelineFramesMod= el::FrameEditableRole).toBool()) { indexes << index; } } @@ -1509,6 +1539,78 @@ void TimelineFramesView::slotMirrorColumns() slotMirrorFrames(true); } = +void TimelineFramesView::cutCopyImpl(bool forceEntireColumn, bool copy) +{ + const QModelIndexList indexes =3D calculateSelectionSpan(forceEntireCo= lumn, !copy); + if (indexes.isEmpty()) return; + + int minColumn =3D std::numeric_limits::max(); + int minRow =3D std::numeric_limits::max(); + + Q_FOREACH (const QModelIndex &index, indexes) { + minRow =3D qMin(minRow, index.row()); + minColumn =3D qMin(minColumn, index.column()); + } + + const QModelIndex baseIndex =3D m_d->model->index(minRow, minColumn); + + QMimeData *data =3D m_d->model->mimeDataExtended(indexes, + baseIndex, + copy ? + TimelineFramesModel= ::CopyFramesPolicy : + TimelineFramesModel= ::MoveFramesPolicy); + if (data) { + QClipboard *cb =3D QApplication::clipboard(); + cb->setMimeData(data); + } +} + +void TimelineFramesView::slotCopyFrames(bool forceEntireColumn) +{ + cutCopyImpl(forceEntireColumn, true); +} + +void TimelineFramesView::slotCutFrames(bool forceEntireColumn) +{ + cutCopyImpl(forceEntireColumn, false); +} + +void TimelineFramesView::slotPasteFrames(bool forceEntireColumn) +{ + const QModelIndex currentIndex =3D + !forceEntireColumn ? this->currentIndex() : m_d->model->index(0, t= his->currentIndex().column()); + + if (!currentIndex.isValid()) return; + + QClipboard *cb =3D QApplication::clipboard(); + const QMimeData *data =3D cb->mimeData(); + + if (data && data->hasFormat("application/x-krita-frame")) { + + bool dataMoved =3D false; + bool result =3D m_d->model->dropMimeDataExtended(data, Qt::MoveAct= ion, currentIndex, &dataMoved); + + if (result && dataMoved) { + cb->clear(); + } + } +} + +void TimelineFramesView::slotCutColumns() +{ + slotCutFrames(true); +} + +void TimelineFramesView::slotPasteColumns() +{ + slotPasteFrames(true); +} + +void TimelineFramesView::slotCopyColumns() +{ + slotCopyFrames(true); +} + int TimelineFramesView::defaultNumberOfFramesToAdd() const { KConfigGroup cfg =3D KSharedConfig::openConfig()->group("FrameActions= DefaultValues"); diff --git a/plugins/dockers/animation/timeline_frames_view.h b/plugins/doc= kers/animation/timeline_frames_view.h index 919de0c8424..f1b1bbfa719 100644 --- a/plugins/dockers/animation/timeline_frames_view.h +++ b/plugins/dockers/animation/timeline_frames_view.h @@ -95,6 +95,15 @@ private Q_SLOTS: void slotMirrorFrames(bool forceEntireColumn =3D false); void slotMirrorColumns(); = + + void slotCopyFrames(bool forceEntireColumn =3D false); + void slotCutFrames(bool forceEntireColumn =3D false); + void slotPasteFrames(bool forceEntireColumn =3D false); + + void slotCopyColumns(); + void slotCutColumns(); + void slotPasteColumns(); + void slotReselectCurrentIndex(); = void slotUpdateInfiniteFramesCount(); @@ -123,7 +132,8 @@ private: KisAction* addActionToMenu(QMenu *menu, const QString &actionId); void insertFramesImpl(int insertAtColumn, int count, QSet rows, b= ool forceEntireColumn); = - QModelIndexList calculateSelectionSpan(bool forceEntireColumn) const; + QModelIndexList calculateSelectionSpan(bool forceEntireColumn, bool ed= itableOnly =3D true) const; + void cutCopyImpl(bool forceEntireColumn, bool copy); = int defaultNumberOfFramesToAdd() const; void setDefaultNumberOfFramesToAdd(int value) const; diff --git a/plugins/dockers/animation/timeline_ruler_header.cpp b/plugins/= dockers/animation/timeline_ruler_header.cpp index 6ad35f957ef..4fd60fe4bd3 100644 --- a/plugins/dockers/animation/timeline_ruler_header.cpp +++ b/plugins/dockers/animation/timeline_ruler_header.cpp @@ -105,6 +105,15 @@ void TimelineRulerHeader::setActionManager( KisActionM= anager * actionManager) = action =3D actionManager->createAction("mirror_columns"); connect(action, SIGNAL(triggered()), SIGNAL(sigMirrorColumns())); + + action =3D actionManager->createAction("copy_columns_to_clipboard"= ); + connect(action, SIGNAL(triggered()), SIGNAL(sigCopyColumns())); + + action =3D actionManager->createAction("cut_columns_to_clipboard"); + connect(action, SIGNAL(triggered()), SIGNAL(sigCutColumns())); + + action =3D actionManager->createAction("paste_columns_from_clipboa= rd"); + connect(action, SIGNAL(triggered()), SIGNAL(sigPasteColumns())); } } = @@ -441,6 +450,10 @@ void TimelineRulerHeader::mousePressEvent(QMouseEvent = *e) } = QMenu menu; + addActionToMenu(&menu, "cut_columns_to_clipboard"); + addActionToMenu(&menu, "copy_columns_to_clipboard"); + addActionToMenu(&menu, "paste_columns_from_clipboard"); + menu.addSeparator(); addActionToMenu(&menu, "insert_columns_right"); addActionToMenu(&menu, "insert_columns_left"); menu.addSeparator(); diff --git a/plugins/dockers/animation/timeline_ruler_header.h b/plugins/do= ckers/animation/timeline_ruler_header.h index 09dd082dd7f..3dec7b98e4f 100644 --- a/plugins/dockers/animation/timeline_ruler_header.h +++ b/plugins/dockers/animation/timeline_ruler_header.h @@ -76,6 +76,10 @@ Q_SIGNALS: = void sigMirrorColumns(); = + void sigCutColumns(); + void sigCopyColumns(); + void sigPasteColumns(); + private: struct Private; const QScopedPointer m_d;