[krita] /: Implement copy-paste of the animation frames

Dmitry Kazakov null at kde.org
Fri Apr 13 18:07:43 UTC 2018


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 at 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 @@
       <statusTip></statusTip>
     </Action>
 
+    <Action name="copy_frames_to_clipboard">
+      <icon></icon>
+      <text>Copy to Clipboard</text>
+      <whatsThis></whatsThis>
+      <toolTip>Copy frames to clipboard</toolTip>
+      <iconText></iconText>
+      <activationFlags>100000</activationFlags>
+      <activationConditions>0</activationConditions>
+      <shortcut></shortcut>
+      <isCheckable>false</isCheckable>
+      <statusTip></statusTip>
+    </Action>
+
+    <Action name="cut_frames_to_clipboard">
+      <icon></icon>
+      <text>Cut to Clipboard</text>
+      <whatsThis></whatsThis>
+      <toolTip>Cut frames to clipboard</toolTip>
+      <iconText></iconText>
+      <activationFlags>100000</activationFlags>
+      <activationConditions>0</activationConditions>
+      <shortcut></shortcut>
+      <isCheckable>false</isCheckable>
+      <statusTip></statusTip>
+    </Action>
+
+    <Action name="paste_frames_from_clipboard">
+      <icon></icon>
+      <text>Paste from Clipboard</text>
+      <whatsThis></whatsThis>
+      <toolTip>Paste frames from clipboard</toolTip>
+      <iconText></iconText>
+      <activationFlags>100000</activationFlags>
+      <activationConditions>0</activationConditions>
+      <shortcut></shortcut>
+      <isCheckable>false</isCheckable>
+      <statusTip></statusTip>
+    </Action>
+
+    <Action name="copy_columns_to_clipboard">
+      <icon></icon>
+      <text>Copy Columns to Clipboard</text>
+      <whatsThis></whatsThis>
+      <toolTip>Copy columns to clipboard</toolTip>
+      <iconText></iconText>
+      <activationFlags>100000</activationFlags>
+      <activationConditions>0</activationConditions>
+      <shortcut></shortcut>
+      <isCheckable>false</isCheckable>
+      <statusTip></statusTip>
+    </Action>
+
+    <Action name="cut_columns_to_clipboard">
+      <icon></icon>
+      <text>Cut Columns to Clipboard</text>
+      <whatsThis></whatsThis>
+      <toolTip>Cut columns to clipboard</toolTip>
+      <iconText></iconText>
+      <activationFlags>100000</activationFlags>
+      <activationConditions>0</activationConditions>
+      <shortcut></shortcut>
+      <isCheckable>false</isCheckable>
+      <statusTip></statusTip>
+    </Action>
+
+    <Action name="paste_columns_from_clipboard">
+      <icon></icon>
+      <text>Paste Columns from Clipboard</text>
+      <whatsThis></whatsThis>
+      <toolTip>Paste columns from clipboard</toolTip>
+      <iconText></iconText>
+      <activationFlags>100000</activationFlags>
+      <activationConditions>0</activationConditions>
+      <shortcut></shortcut>
+      <isCheckable>false</isCheckable>
+      <statusTip></statusTip>
+    </Action>
+
   </Actions>
 
 
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 QModelIndex &index)
 }
 
 QMimeData* TimelineFramesModel::mimeData(const QModelIndexList &indexes) const
+{
+    return mimeDataExtended(indexes, m_d->lastClickedIndex, UndefinedPolicy);
+}
+
+QMimeData *TimelineFramesModel::mimeDataExtended(const QModelIndexList &indexes,
+                                                 const QModelIndex &baseIndex,
+                                                 TimelineFramesModel::MimeCopyPolicy copyPolicy) const
 {
     QMimeData *data = new QMimeData();
 
     QByteArray encoded;
     QDataStream stream(&encoded, QIODevice::WriteOnly);
 
-    const int baseRow = m_d->lastClickedIndex.row();
-    const int baseColumn = m_d->lastClickedIndex.column();
+    const int baseRow = baseIndex.row();
+    const int baseColumn = baseIndex.column();
 
     stream << indexes.size();
     stream << baseRow << baseColumn;
@@ -553,6 +560,7 @@ QMimeData* TimelineFramesModel::mimeData(const QModelIndexList &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 QMimeData *data, Qt::DropAction act
     Q_UNUSED(row);
     Q_UNUSED(column);
 
-    bool result = false;
+    return dropMimeDataExtended(data, action, parent);
+}
 
-    if ((action != Qt::MoveAction &&
-         action != Qt::CopyAction) || !parent.isValid()) return result;
+bool TimelineFramesModel::dropMimeDataExtended(const QMimeData *data, Qt::DropAction action, const QModelIndex &parent, bool *dataMoved)
+{
+    bool result = false;
 
-    const bool copyFrames = action == Qt::CopyAction;
+    if ((action != Qt::MoveAction && action != Qt::CopyAction) ||
+        !parent.isValid()) return result;
 
     QByteArray encoded = 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 = UndefinedPolicy;
+
+    if (!stream.atEnd()) {
+        int value = 0;
+        stream >> value;
+        copyPolicy = MimeCopyPolicy(value);
+    }
+
+    const bool copyFrames =
+        copyPolicy == UndefinedPolicy ?
+        action == Qt::CopyAction :
+        copyPolicy == CopyFramesPolicy;
+
+    if (dataMoved) {
+        *dataMoved = !copyFrames;
+    }
+
     const QPoint offset(parent.column() - baseColumn, parent.row() - baseRow);
 
     return offsetFrames(srcIndexes, offset, copyFrames);
diff --git a/plugins/dockers/animation/timeline_frames_model.h b/plugins/dockers/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 TimelineNodeListKeeper::ModelWithExternalNotifications
 {
     Q_OBJECT
+
+public:
+    enum MimeCopyPolicy {
+        UndefinedPolicy = 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 QModelIndex &baseIndex, MimeCopyPolicy copyPolicy) const;
     bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) override;
+
+    bool dropMimeDataExtended(const QMimeData *data, Qt::DropAction action, const QModelIndex &parent, bool *dataMoved = 0);
+
     Qt::ItemFlags flags(const QModelIndex &index) const override;
 
     bool insertRows(int row, int count, const QModelIndex &parent) override;
diff --git a/plugins/dockers/animation/timeline_frames_view.cpp b/plugins/dockers/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 <QWidgetAction>
 #include <QInputDialog>
 
+#include <QClipboard>
+#include <QMimeData>
+
 #include "config-qtmultimedia.h"
 
 typedef QPair<QRect, QModelIndex> QItemViewPaintPair;
@@ -172,6 +175,10 @@ TimelineFramesView::TimelineFramesView(QWidget *parent)
 
     connect(m_d->horizontalRuler, SIGNAL(sigMirrorColumns()), SLOT(slotMirrorColumns()));
 
+    connect(m_d->horizontalRuler, SIGNAL(sigCopyColumns()), SLOT(slotCopyColumns()));
+    connect(m_d->horizontalRuler, SIGNAL(sigCutColumns()), SLOT(slotCutColumns()));
+    connect(m_d->horizontalRuler, SIGNAL(sigPasteColumns()), SLOT(slotPasteColumns()));
+
     m_d->layersHeader = new TimelineLayersHeader(this);
 
     m_d->layersHeader->setSectionResizeMode(QHeaderView::Fixed);
@@ -336,6 +343,16 @@ void TimelineFramesView::setActionManager( KisActionManager * actionManager)
 
         action = m_d->actionMan->createAction("mirror_frames");
         connect(action, SIGNAL(triggered()), SLOT(slotMirrorFrames()));
+
+        action = m_d->actionMan->createAction("copy_frames_to_clipboard");
+        connect(action, SIGNAL(triggered()), SLOT(slotCopyFrames()));
+
+        action = m_d->actionMan->createAction("cut_frames_to_clipboard");
+        connect(action, SIGNAL(triggered()), SLOT(slotCutFrames()));
+
+        action = m_d->actionMan->createAction("paste_frames_from_clipboard");
+        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 forceEntireColumn) const
+QModelIndexList TimelineFramesView::calculateSelectionSpan(bool forceEntireColumn, bool editableOnly) const
 {
     QModelIndexList indexes;
 
@@ -1349,7 +1378,8 @@ QModelIndexList TimelineFramesView::calculateSelectionSpan(bool forceEntireColum
 
         rows.clear();
         for (int i = 0; i < m_d->model->rowCount(); i++) {
-            if (!m_d->model->data(m_d->model->index(i, minColumn), TimelineFramesModel::FrameEditableRole).toBool()) continue;
+            if (editableOnly &&
+                !m_d->model->data(m_d->model->index(i, minColumn), TimelineFramesModel::FrameEditableRole).toBool()) continue;
 
             for (int column = minColumn; column <= maxColumn; column++) {
                 indexes << m_d->model->index(i, column);
@@ -1357,7 +1387,7 @@ QModelIndexList TimelineFramesView::calculateSelectionSpan(bool forceEntireColum
         }
     } else {
         Q_FOREACH (const QModelIndex &index, selectionModel()->selectedIndexes()) {
-            if (m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) {
+            if (!editableOnly || m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) {
                 indexes << index;
             }
         }
@@ -1509,6 +1539,78 @@ void TimelineFramesView::slotMirrorColumns()
     slotMirrorFrames(true);
 }
 
+void TimelineFramesView::cutCopyImpl(bool forceEntireColumn, bool copy)
+{
+    const QModelIndexList indexes = calculateSelectionSpan(forceEntireColumn, !copy);
+    if (indexes.isEmpty()) return;
+
+    int minColumn = std::numeric_limits<int>::max();
+    int minRow = std::numeric_limits<int>::max();
+
+    Q_FOREACH (const QModelIndex &index, indexes) {
+        minRow = qMin(minRow, index.row());
+        minColumn = qMin(minColumn, index.column());
+    }
+
+    const QModelIndex baseIndex = m_d->model->index(minRow, minColumn);
+
+    QMimeData *data = m_d->model->mimeDataExtended(indexes,
+                                                   baseIndex,
+                                                   copy ?
+                                                       TimelineFramesModel::CopyFramesPolicy :
+                                                       TimelineFramesModel::MoveFramesPolicy);
+    if (data) {
+        QClipboard *cb = 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 =
+        !forceEntireColumn ? this->currentIndex() : m_d->model->index(0, this->currentIndex().column());
+
+    if (!currentIndex.isValid()) return;
+
+    QClipboard *cb = QApplication::clipboard();
+    const QMimeData *data = cb->mimeData();
+
+    if (data && data->hasFormat("application/x-krita-frame")) {
+
+        bool dataMoved = false;
+        bool result = m_d->model->dropMimeDataExtended(data, Qt::MoveAction, 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 =  KSharedConfig::openConfig()->group("FrameActionsDefaultValues");
diff --git a/plugins/dockers/animation/timeline_frames_view.h b/plugins/dockers/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 = false);
     void slotMirrorColumns();
 
+
+    void slotCopyFrames(bool forceEntireColumn = false);
+    void slotCutFrames(bool forceEntireColumn = false);
+    void slotPasteFrames(bool forceEntireColumn = 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<int> rows, bool forceEntireColumn);
 
-    QModelIndexList calculateSelectionSpan(bool forceEntireColumn) const;
+    QModelIndexList calculateSelectionSpan(bool forceEntireColumn, bool editableOnly = 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( KisActionManager * actionManager)
 
         action = actionManager->createAction("mirror_columns");
         connect(action, SIGNAL(triggered()), SIGNAL(sigMirrorColumns()));
+
+        action = actionManager->createAction("copy_columns_to_clipboard");
+        connect(action, SIGNAL(triggered()), SIGNAL(sigCopyColumns()));
+
+        action = actionManager->createAction("cut_columns_to_clipboard");
+        connect(action, SIGNAL(triggered()), SIGNAL(sigCutColumns()));
+
+        action = actionManager->createAction("paste_columns_from_clipboard");
+        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/dockers/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<Private> m_d;



More information about the kimageshop mailing list