Commit 7030f6a9 authored by Emmet O'Neill's avatar Emmet O'Neill

Animation Timeline Refactor + Bugfix

+ Refactor: Cleans up code concerning Krita's Animation Timeline Docker
in order to minimize code duplication, generalize functions, and simplify some aspects of the interface for developers and users alike.

+ Bugfix: Fixes a regression caused by 7c639729 which caused "insert
frames left/right" and "insert columns left/right" to behave incorrectly
when multiple frame slots were selected.

~ Reviewed and corrected by Dmitry. Programmed with Eoin. Thanks as
always!
parent 96bf129c
......@@ -2141,24 +2141,11 @@
<statusTip></statusTip>
</Action>
<Action name="insert_keyframes_right">
<icon></icon>
<text>Insert Keyframe Right</text>
<whatsThis></whatsThis>
<toolTip>Insert keyframes to the right of selection moving the tail of animation to the right</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="insert_keyframes_left">
<Action name="insert_keyframe_left">
<icon></icon>
<text>Insert Keyframe Left</text>
<whatsThis></whatsThis>
<toolTip>Insert keyframes to the left of selection moving the tail of animation to the right</toolTip>
<toolTip>Insert keyframes to the left of selection, moving the tail of animation to the right.</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
......@@ -2167,11 +2154,11 @@
<statusTip></statusTip>
</Action>
<Action name="insert_n_keyframes_right">
<Action name="insert_keyframe_right">
<icon></icon>
<text>Insert N Keyframes Right</text>
<text>Insert Keyframe Right</text>
<whatsThis></whatsThis>
<toolTip>Insert several keyframes to the right of selection moving the tail of animation to the right</toolTip>
<toolTip>Insert keyframes to the right of selection, moving the tail of animation to the right.</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
......@@ -2180,11 +2167,11 @@
<statusTip></statusTip>
</Action>
<Action name="insert_n_keyframes_left">
<Action name="insert_multiple_keyframes">
<icon></icon>
<text>Insert N Keyframes Left</text>
<text>Insert Multiple Keyframes</text>
<whatsThis></whatsThis>
<toolTip>Insert several keyframes to the left of selection moving the tail of animation to the right</toolTip>
<toolTip>Insert several keyframes based on user parameters.</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
......@@ -2219,24 +2206,11 @@
<statusTip></statusTip>
</Action>
<Action name="insert_columns_right">
<icon></icon>
<text>Insert Column Right</text>
<whatsThis></whatsThis>
<toolTip>Insert column to the right of selection moving the tail of animation to the right</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="insert_columns_left">
<Action name="insert_column_left">
<icon></icon>
<text>Insert Column Left</text>
<whatsThis></whatsThis>
<toolTip>Insert column to the left of selection moving the tail of animation to the right</toolTip>
<toolTip>Insert column to the left of selection, moving the tail of animation to the right</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
......@@ -2245,11 +2219,11 @@
<statusTip></statusTip>
</Action>
<Action name="insert_n_columns_right">
<Action name="insert_column_right">
<icon></icon>
<text>Insert N Columns Right</text>
<text>Insert Column Right</text>
<whatsThis></whatsThis>
<toolTip>Insert several columns to the right of selection moving the tail of animation to the right</toolTip>
<toolTip>Insert column to the right of selection, moving the tail of animation to the right</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
......@@ -2258,11 +2232,11 @@
<statusTip></statusTip>
</Action>
<Action name="insert_n_columns_left">
<Action name="insert_multiple_columns">
<icon></icon>
<text>Insert N Columns Left</text>
<text>Insert Multiple Columns</text>
<whatsThis></whatsThis>
<toolTip>Insert several columns to the left of selection moving the tail of animation to the right</toolTip>
<toolTip>Insert several columns based on user parameters.</toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
......@@ -2310,9 +2284,9 @@
<statusTip></statusTip>
</Action>
<Action name="insert_n_hold_frames">
<Action name="insert_multiple_hold_frames">
<icon></icon>
<text>Insert N Hold Frames</text>
<text>Insert Multiple Hold Frames</text>
<whatsThis></whatsThis>
<toolTip>Insert N hold frames after every keyframe</toolTip>
<iconText></iconText>
......@@ -2336,9 +2310,9 @@
<statusTip></statusTip>
</Action>
<Action name="remove_n_hold_frames">
<Action name="remove_multiple_hold_frames">
<icon></icon>
<text>Remove N Hold Frames</text>
<text>Remove Multiple Hold Frames</text>
<whatsThis></whatsThis>
<toolTip>Remove N hold frames after every keyframe</toolTip>
<iconText></iconText>
......@@ -2362,9 +2336,9 @@
<statusTip></statusTip>
</Action>
<Action name="insert_n_hold_columns">
<Action name="insert_multiple_hold_columns">
<icon></icon>
<text>Insert N Hold Columns</text>
<text>Insert Multiple Hold Columns</text>
<whatsThis></whatsThis>
<toolTip>Insert N hold columns into the frame at the current position</toolTip>
<iconText></iconText>
......@@ -2388,9 +2362,9 @@
<statusTip></statusTip>
</Action>
<Action name="remove_n_hold_columns">
<Action name="remove_multiple_hold_columns">
<icon></icon>
<text>Remove N Hold Columns</text>
<text>Remove Multiple Hold Columns</text>
<whatsThis></whatsThis>
<toolTip>Remove N hold columns from the frame at the current position</toolTip>
<iconText></iconText>
......
......@@ -304,7 +304,7 @@ KUndo2Command* KisTimeBasedItemModel::createOffsetFramesCommand(QModelIndexList
}
Q_FOREACH(KisKeyframeChannel *channel, channelsAt(srcIndex)) {
if (moveEmptyFrames || channel->keyframeAt(srcIndex.column())) {
if (moveEmptyFrames || channel->keyframeAt(srcIndex.column())) {
srcFrameItems << KisAnimationUtils::FrameItem(srcNode, channel->id(), srcIndex.column());
dstFrameItems << KisAnimationUtils::FrameItem(dstNode, channel->id(), dstIndex.column());
}
......@@ -350,8 +350,8 @@ bool KisTimeBasedItemModel::removeFramesAndOffset(QModelIndexList indexes)
new KisSwitchCurrentTimeCommand(m_d->image->animationInterface(),
oldTime,
newTime, parentCommand);
newTime,
parentCommand);
}
KisProcessingApplicator::runSingleCommandStroke(m_d->image, parentCommand, KisStrokeJobData::BARRIER);
......
......@@ -19,6 +19,7 @@
#ifndef __TIMELINE_FRAMES_MODEL_H
#define __TIMELINE_FRAMES_MODEL_H
#include <QScopedPointer>
#include <QIcon>
......@@ -86,9 +87,9 @@ public:
Qt::DropActions supportedDragActions() const override;
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;
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);
......
......@@ -19,6 +19,7 @@
#ifndef __TIMELINE_FRAMES_VIEW_H
#define __TIMELINE_FRAMES_VIEW_H
#include <QScopedPointer>
#include <QTableView>
#include "kis_action_manager.h"
......@@ -27,9 +28,20 @@
class KisAction;
class TimelineWidget;
enum TimelineDirection : short
{
LEFT = -1,
BEFORE = -1,
RIGHT = 1,
AFTER = 1
};
class KRITAANIMATIONDOCKER_EXPORT TimelineFramesView : public QTableView
{
Q_OBJECT
public:
TimelineFramesView(QWidget *parent);
~TimelineFramesView() override;
......@@ -40,7 +52,7 @@ public:
void setShowInTimeline(KisAction *action);
void setActionManager( KisActionManager * actionManager);
void setActionManager(KisActionManager *actionManager);
public Q_SLOTS:
void slotSelectionChanged();
......@@ -61,51 +73,49 @@ private Q_SLOTS:
void slotRemoveLayer();
void slotLayerContextMenuRequested(const QPoint &globalPos);
// New, Copy, Insert and Remove Frames
void slotNewFrame();
void slotCopyFrame();
void slotInsertKeyframesLeft(int count = 1, int timing = 1, bool forceEntireColumn = false);
void slotInsertKeyframesRight(int count = 1, int timing = 1, bool forceEntireColumn = false);
// New, Insert and Remove Frames
void slotAddBlankFrame();
void slotAddDuplicateFrame();
void slotInsertKeyframesLeftCustom();
void slotInsertKeyframesRightCustom();
void slotInsertKeyframeLeft() {insertKeyframes(-1, 1, TimelineDirection::LEFT, false);}
void slotInsertKeyframeRight() {insertKeyframes(-1, 1, TimelineDirection::RIGHT, false);}
void slotRemoveFrame(bool forceEntireColumn = false, bool needsOffset = false);
void slotRemoveFramesAndShift(bool forceEntireColumn = false);
void slotInsertKeyframeColumnLeft() {insertKeyframes(-1, 1, TimelineDirection::LEFT, true);}
void slotInsertKeyframeColumnRight() {insertKeyframes(-1, 1, TimelineDirection::RIGHT, true);}
void slotInsertColumnsLeft(int count = 1, int timing = 1);
void slotInsertColumnsLeftCustom();
void slotInsertMultipleKeyframes() {insertMultipleKeyframes(false);}
void slotInsertMultipleKeyframeColumns() {insertMultipleKeyframes(true);}
void slotInsertColumnsRight(int count = 1, int timing = 1);
void slotInsertColumnsRightCustom();
void slotRemoveSelectedFrames(bool entireColumn = false, bool needsOffset = false);
void slotRemoveSelectedFramesAndShift() {slotRemoveSelectedFrames(false, true);}
void slotRemoveColumns();
void slotRemoveColumnsAndShift();
void slotRemoveSelectedColumns() {slotRemoveSelectedFrames(true);}
void slotRemoveSelectedColumnsAndShift() {slotRemoveSelectedFrames(true, true);}
void slotInsertHoldFrames(int count = 1, bool forceEntireColumn = false);
void slotRemoveHoldFrames(int count = 1, bool forceEntireColumn = false);
void slotInsertHoldFrame() {insertOrRemoveHoldFrames(1);}
void slotRemoveHoldFrame() {insertOrRemoveHoldFrames(-1);}
void slotInsertHoldFramesCustom();
void slotRemoveHoldFramesCustom();
void slotInsertHoldFrameColumn() {insertOrRemoveHoldFrames(1,true);}
void slotRemoveHoldFrameColumn() {insertOrRemoveHoldFrames(-1,true);}
void slotInsertHoldColumns(int count = 1);
void slotRemoveHoldColumns(int count = 1);
void slotInsertMultipleHoldFrames() {insertOrRemoveMultipleHoldFrames(true);}
void slotRemoveMultipleHoldFrames() {insertOrRemoveMultipleHoldFrames(false);}
void slotInsertHoldColumnsCustom();
void slotRemoveHoldColumnsCustom();
void slotInsertMultipleHoldFrameColumns() {insertOrRemoveMultipleHoldFrames(true, true);}
void slotRemoveMultipleHoldFrameColumns() {insertOrRemoveMultipleHoldFrames(false, true);}
void slotMirrorFrames(bool forceEntireColumn = false);
void slotMirrorColumns();
void slotMirrorFrames(bool entireColumn = false);
void slotMirrorColumns() {slotMirrorFrames(true);}
// Copy-paste
void slotCopyFrames(bool forceEntireColumn = false);
void slotCutFrames(bool forceEntireColumn = false);
void slotPasteFrames(bool forceEntireColumn = false);
void slotCopyFrames() {cutCopyImpl(false, true);}
void slotCutFrames() {cutCopyImpl(false, false);}
void slotCopyColumns() {cutCopyImpl(true, true);}
void slotCutColumns() {cutCopyImpl(true, false);}
void slotCopyColumns();
void slotCutColumns();
void slotPasteColumns();
void slotPasteFrames(bool entireColumn = false);
void slotPasteColumns() {slotPasteFrames(true);}
void slotReselectCurrentIndex();
......@@ -128,14 +138,28 @@ private Q_SLOTS:
private:
void setFramesPerSecond(int fps);
void calculateSelectionMetrics(int &minColumn, int &maxColumn, QSet<int> &rows) const;
void insertFramesImpl(int insertAtColumn, int count, int timing, QSet<int> rows, bool forceEntireColumn);
/* Insert new keyframes/columns.
*
* count - Number of frames to add. If <0, use number of currently SELECTED frames.
* timing - Animation timing of frames to be added (on 1s, 2s, 3s, etc.)
* direction - Insert frames before (left) or after (right) selection scrubber.
* entireColumn - Create frames on all layers (rows) instead of just the active layer?
*/
void insertKeyframes(int count = 1, int timing = 1,
TimelineDirection direction = TimelineDirection::LEFT, bool entireColumn = false);
void insertMultipleKeyframes(bool entireColumn = false);
void insertOrRemoveHoldFrames(int count, bool entireColumn = false);
void insertOrRemoveMultipleHoldFrames(bool insertion, bool entireColumn = false);
void cutCopyImpl(bool entireColumn, bool copy);
void createFrameEditingMenuActions(QMenu *menu, bool addFrameCreationActions);
QModelIndexList calculateSelectionSpan(bool forceEntireColumn, bool editableOnly = true) const;
void cutCopyImpl(bool forceEntireColumn, bool copy);
QModelIndexList calculateSelectionSpan(bool entireColumn, bool editableOnly = true) const;
int defaultNumberOfFramesToAdd() const;
void setDefaultNumberOfFramesToAdd(int value) const;
......@@ -163,7 +187,7 @@ protected:
void mouseMoveEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void wheelEvent(QWheelEvent *e) override;
void rowsInserted(const QModelIndex& parent, int start, int end) override;
void rowsInserted(const QModelIndex &parent, int start, int end) override;
bool viewportEvent(QEvent *event) override;
private:
......
/*
* Copyright (c) 2018 Emmet O'Neill <emmetoneill.pdx@gmail.com>
* Copyright (c) 2018 Eoin O'Neill <eoinoneill1991@gmail.com>
*
* 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 "timeline_insert_keyframe_dialog.h"
#include "timeline_frames_view.h"
#include <QLabel>
#include <QGroupBox>
#include <QSpinBox>
#include <QRadioButton>
#include <QDialogButtonBox>
#include <QVBoxLayout>
#include <QFormLayout>
......@@ -14,31 +36,58 @@ TimelineInsertKeyframeDialog::TimelineInsertKeyframeDialog(QWidget *parent) :
setModal(true);
setLayout(new QVBoxLayout(this));
frameCountSpinbox.setMinimum(1);
frameCountSpinbox.setValue(1);
{ // Count and Spacing Forms.
QWidget *forms = new QWidget(this);
layout()->addWidget(forms);
frameCountSpinbox.setMinimum(1);
frameCountSpinbox.setValue(1);
frameTimingSpinbox.setMinimum(1);
frameTimingSpinbox.setValue(1);
QFormLayout *LO = new QFormLayout(this);
forms->setLayout(LO);
LO->addRow(QString(i18nc("@label:spinbox", "Number of frames:")), &frameCountSpinbox);
LO->addRow(QString(i18nc("@label:spinbox", "Frame timing:")), &frameTimingSpinbox);
}
{ // Side Buttons.
QGroupBox *sideRadioButtons = new QGroupBox(i18nc("@label:group","Side:"), this);
layout()->addWidget(sideRadioButtons);
frameTimingSpinbox.setMinimum(1);
frameTimingSpinbox.setValue(1);
leftBefore = new QRadioButton(i18nc("@label:radio", "Left / Before"), sideRadioButtons);
rightAfter = new QRadioButton(i18nc("@label:radio", "Right / After"), sideRadioButtons);
leftBefore->setChecked(true);
QWidget *formsWidget = new QWidget();
QFormLayout *formLayout = new QFormLayout();
formsWidget->setLayout( formLayout );
layout()->addWidget(formsWidget);
QVBoxLayout *LO = new QVBoxLayout(this);
sideRadioButtons->setLayout(LO);
QDialogButtonBox *buttonbox = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
layout()->addWidget(buttonbox);
LO->addWidget(leftBefore);
LO->addWidget(rightAfter);
}
formLayout->addRow(QString(i18nc("@label:spinbox", "Number of frames:")), &frameCountSpinbox);
formLayout->addRow(QString(i18nc("@label:spinbox", "Frame timing:")), &frameTimingSpinbox);
{ // Cancel / OK Buttons.
QDialogButtonBox *buttonbox = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
layout()->addWidget(buttonbox);
connect(buttonbox, SIGNAL(accepted()), this, SLOT(accept()));
connect(buttonbox, SIGNAL(rejected()), this, SLOT(reject()));
connect(buttonbox, SIGNAL(accepted()), this, SLOT(accept()));
connect(buttonbox, SIGNAL(rejected()), this, SLOT(reject()));
}
}
bool TimelineInsertKeyframeDialog::promptUserSettings(int &out_count, int &out_timing){
if(exec() == QDialog::Accepted){
bool TimelineInsertKeyframeDialog::promptUserSettings(int &out_count, int &out_timing, TimelineDirection &out_direction)
{
if (exec() == QDialog::Accepted) {
out_count = frameCountSpinbox.value();
out_timing = frameTimingSpinbox.value();
out_direction = TimelineDirection::LEFT; // Default
if (rightAfter && rightAfter->isChecked()) {
out_direction = TimelineDirection::RIGHT;
}
return true;
}
return false;
......
/*
* Copyright (c) 2018 Emmet O'Neill <emmetoneill.pdx@gmail.com>
* Copyright (c) 2018 Eoin O'Neill <eoinoneill1991@gmail.com>
*
* 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 __TIMELINE_INSERT_KEYFRAME_DIALOG_H
#define __TIMELINE_INSERT_KEYFRAME_DIALOG_H
#include "kritaanimationdocker_export.h"
#include <QDialog>
#include <QSpinBox>
#include <QRadioButton>
enum TimelineDirection : short;
class KRITAANIMATIONDOCKER_EXPORT TimelineInsertKeyframeDialog : QDialog {
Q_OBJECT
......@@ -11,10 +33,13 @@ private:
QSpinBox frameCountSpinbox;
QSpinBox frameTimingSpinbox;
QRadioButton *leftBefore;
QRadioButton *rightAfter;
public:
TimelineInsertKeyframeDialog(QWidget *parent = 0);
bool promptUserSettings(int &count, int &timing);
bool promptUserSettings(int &count, int &timing, TimelineDirection &out_direction);
};
#endif // __TIMELINE_INSERT_KEYFRAME_DIALOG_H
......@@ -55,10 +55,6 @@ TimelineRulerHeader::TimelineRulerHeader(QWidget *parent)
{
setSectionResizeMode(QHeaderView::Fixed);
setDefaultSectionSize(18);
}
TimelineRulerHeader::~TimelineRulerHeader()
......@@ -66,24 +62,21 @@ TimelineRulerHeader::~TimelineRulerHeader()
}
void TimelineRulerHeader::setActionManager( KisActionManager * actionManager)
void TimelineRulerHeader::setActionManager(KisActionManager *actionManager)
{
m_d->actionMan = actionManager;
if (actionManager) {
KisAction *action;
action = actionManager->createAction("insert_columns_right");
connect(action, SIGNAL(triggered()), SIGNAL(sigInsertColumnsRight()));
action = actionManager->createAction("insert_n_columns_right");
connect(action, SIGNAL(triggered()), SIGNAL(sigInsertColumnsRightCustom()));
action = actionManager->createAction("insert_column_left");
connect(action, SIGNAL(triggered()), SIGNAL(sigInsertColumnLeft()));
action = actionManager->createAction("insert_columns_left");
connect(action, SIGNAL(triggered()), SIGNAL(sigInsertColumnsLeft()));
action = actionManager->createAction("insert_column_right");
connect(action, SIGNAL(triggered()), SIGNAL(sigInsertColumnRight()));
action = actionManager->createAction("insert_n_columns_left");
connect(action, SIGNAL(triggered()), SIGNAL(sigInsertColumnsLeftCustom()));
action = actionManager->createAction("insert_multiple_columns");
connect(action, SIGNAL(triggered()), SIGNAL(sigInsertMultipleColumns()));
action = actionManager->createAction("remove_columns_and_pull");
connect(action, SIGNAL(triggered()), SIGNAL(sigRemoveColumnsAndShift()));
......@@ -94,13 +87,13 @@ void TimelineRulerHeader::setActionManager( KisActionManager * actionManager)
action = actionManager->createAction("insert_hold_column");
connect(action, SIGNAL(triggered()), SIGNAL(sigInsertHoldColumns()));
action = actionManager->createAction("insert_n_hold_columns");
action = actionManager->createAction("insert_multiple_hold_columns");
connect(action, SIGNAL(triggered()), SIGNAL(sigInsertHoldColumnsCustom()));
action = actionManager->createAction("remove_hold_column");
connect(action, SIGNAL(triggered()), SIGNAL(sigRemoveHoldColumns()));
action = actionManager->createAction("remove_n_hold_columns");
action = actionManager->createAction("remove_multiple_hold_columns");
connect(action, SIGNAL(triggered()), SIGNAL(sigRemoveHoldColumnsCustom()));
action = actionManager->createAction("mirror_columns");
......@@ -440,27 +433,31 @@ void TimelineRulerHeader::mousePressEvent(QMouseEvent *e)
KisActionManager::safePopulateMenu(&menu, "cut_columns_to_clipboard", m_d->actionMan);
KisActionManager::safePopulateMenu(&menu, "copy_columns_to_clipboard", m_d->actionMan);
KisActionManager::safePopulateMenu(&menu, "paste_columns_from_clipboard", m_d->actionMan);
menu.addSeparator();
QMenu *frames = menu.addMenu(i18nc("@item:inmenu", "Keyframe Columns"));
KisActionManager::safePopulateMenu(frames, "insert_columns_right", m_d->actionMan);
KisActionManager::safePopulateMenu(frames, "insert_columns_left", m_d->actionMan);
menu.addSeparator();
KisActionManager::safePopulateMenu(frames, "insert_n_columns_right", m_d->actionMan);
KisActionManager::safePopulateMenu(frames, "insert_n_columns_left", m_d->actionMan);
QMenu *hold = menu.addMenu(i18nc("@item:inmenu", "Hold Frame Columns"));
KisActionManager::safePopulateMenu(hold, "insert_hold_column", m_d->actionMan);
KisActionManager::safePopulateMenu(hold, "remove_hold_column", m_d->actionMan);
menu.addSeparator();
KisActionManager::safePopulateMenu(hold, "insert_n_hold_columns", m_d->actionMan);
KisActionManager::safePopulateMenu(hold, "remove_n_hold_columns", m_d->actionMan);
{ //Frame Columns Submenu
QMenu *frames = menu.addMenu(i18nc("@item:inmenu", "Keyframe Columns"));
KisActionManager::safePopulateMenu(frames, "insert_column_left", m_d->actionMan);
KisActionManager::safePopulateMenu(frames, "insert_column_right", m_d->actionMan);
frames->addSeparator();
KisActionManager::safePopulateMenu(frames, "insert_multiple_columns", m_d->actionMan);