[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-kimageshop
Subject:    [krita] /: Implement copy-paste of the animation frames
From:       Dmitry Kazakov <null () kde ! org>
Date:       2018-04-13 18:07:43
Message-ID: E1f736t-0008NL-Av () code ! kde ! org
[Download RAW message or body]

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 @@
       <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;


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic