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;