46#include <QApplication>
85 if (!dummy)
return QVariant();
92 if (!dummy)
return true;
104 if (!dummy)
return false;
108 return (primaryChannel && primaryChannel->
keyframeAt(column));
114 if (!dummy)
return false;
117 if (!primaryChannel)
return false;
121 if (!frame)
return false;
123 return frame->hasContent();
128 if (!dummy)
return false;
140 if (!dummy)
return -1;
143 if (!primaryChannel)
return -1;
146 if (!frame)
return -1;
148 return frame->colorLabel();
156 if (!primaryChannel)
return;
161 frame->setColorLabel(color);
166 if (!dummy)
return -1;
172 if (!dummy)
return QVariant();
175 return QVariant::fromValue(props);
180 if (!dummy)
return false;
188 if (!dummy)
return false;
210 if (!dummy)
return false;
221 : ModelWithExternalNotifications(parent),
233 return m_d->dummiesFacade;
238 m_d->nodeInterface.reset(iface);
248 return dummy ? dummy->
node() :
nullptr;
279 if (
m_d->dummiesFacade &&
m_d->image) {
282 m_d->dummiesFacade->disconnect(
this);
288 m_d->dummiesFacade = dummiesFacade;
289 m_d->converter.reset();
291 if (
m_d->dummiesFacade) {
299 if (
m_d->dummiesFacade != oldDummiesFacade) {
304 if (
m_d->dummiesFacade) {
312 if (!
m_d->updateQueue.contains(dummy)) {
313 m_d->updateQueue.append(dummy);
315 m_d->updateTimer.start();
320 if (
m_d->activeLayerIndex < 0)
return;
330 if (!
m_d->converter)
return;
333 int row =
m_d->converter->rowForDummy(dummy);
336 Q_EMIT headerDataChanged (Qt::Vertical, row, row);
337 Q_EMIT dataChanged(this->index(row, 0), this->index(row,
columnCount() - 1));
340 m_d->updateQueue.clear();
346 m_d->activeLayerIndex = -1;
357 int lastActiveLayerIndex =
m_d->activeLayerIndex;
359 m_d->converter->updateActiveDummy(dummy);
361 const int row =
m_d->converter->rowForDummy(dummy);
363 qWarning() <<
"WARNING: TimelineFramesModel::slotCurrentNodeChanged: node not found!";
366 if (row >= 0 &&
m_d->activeLayerIndex != row) {
368 }
else if (row >= 0 ){
374 if (prevActiveWasPinned) {
375 lastActiveLayerIndex += 1;
388 if(!
m_d->dummiesFacade)
return 0;
390 return m_d->converter->rowCount();
395 if(!
m_d->dummiesFacade)
return QVariant();
399 return index.row() ==
m_d->activeLayerIndex;
402 return m_d->layerEditable(index.row());
405 return m_d->frameHasContent(index.row(), index.column());
408 return m_d->frameExists(index.row(), index.column());
411 return m_d->specialKeyframeExists(index.row(), index.column());
414 int label =
m_d->frameColorLabel(index.row(), index.column());
415 return label > 0 ? label : QVariant();
417 case Qt::DisplayRole: {
418 return m_d->layerName(index.row());
420 case Qt::TextAlignmentRole: {
421 return QVariant(Qt::AlignHCenter | Qt::AlignVCenter);
428 const int maxSize = 200;
435 return ModelWithExternalNotifications::data(index, role);
440 if (!index.isValid() || !
m_d->dummiesFacade)
return false;
444 if (
value.toBool() &&
445 index.row() !=
m_d->activeLayerIndex) {
446 int prevLayer =
m_d->activeLayerIndex;
447 m_d->activeLayerIndex = index.row();
449 Q_EMIT dataChanged(this->index(prevLayer, 0), this->index(prevLayer,
columnCount() - 1));
450 Q_EMIT dataChanged(this->index(
m_d->activeLayerIndex, 0), this->index(
m_d->activeLayerIndex,
columnCount() - 1));
452 Q_EMIT headerDataChanged(Qt::Vertical, prevLayer, prevLayer);
453 Q_EMIT headerDataChanged(Qt::Vertical,
m_d->activeLayerIndex,
m_d->activeLayerIndex);
464 m_d->setFrameColorLabel(index.row(), index.column(),
value.toInt());
469 return ModelWithExternalNotifications::setData(index,
value, role);
474 if(!
m_d->dummiesFacade)
return QVariant();
476 if (orientation == Qt::Vertical) {
479 return section ==
m_d->activeLayerIndex;
480 case Qt::DisplayRole: {
484 QString name =
value.toString();
485 const int maxNameSize = 13;
487 if (name.size() > maxNameSize) {
488 name = QString(
"%1...").arg(name.left(maxNameSize));
493 case Qt::ForegroundRole: {
500 if (!dummy)
return QVariant();
505 baseFont.setStrikeOut(
true);
506 }
else if (
m_d->image &&
m_d->image->isolationRootNode() &&
508 baseFont.setBold(
true);
512 case Qt::ToolTipRole: {
513 return m_d->layerName(section);
516 return QVariant::fromValue(
m_d->layerProperties(section));
520 m_d->converter->otherLayersList();
522 return QVariant::fromValue(list);
526 if (!dummy)
return QVariant();
529 case Qt::BackgroundRole: {
530 int label =
m_d->layerColorLabel(section);
534 QPalette pal = qApp->palette();
536 return QBrush(color);
544 return ModelWithExternalNotifications::headerData(section, orientation, role);
549 if (!
m_d->dummiesFacade)
return false;
551 if (orientation == Qt::Vertical) {
560 int result =
m_d->setLayerProperties(section, props);
561 Q_EMIT headerDataChanged (Qt::Vertical, section, section);
566 if (!dummy)
return false;
573 return ModelWithExternalNotifications::setHeaderData(section, orientation,
value, role);
578 return Qt::MoveAction | Qt::CopyAction | Qt::LinkAction;
583 return Qt::MoveAction | Qt::CopyAction | Qt::LinkAction;
589 types << QLatin1String(
"application/x-krita-frame");
595 m_d->lastClickedIndex = index;
604 const QModelIndex &baseIndex,
607 QMimeData *
data =
new QMimeData();
610 QDataStream stream(&encoded, QIODevice::WriteOnly);
612 const int baseRow = baseIndex.row();
613 const int baseColumn = baseIndex.column();
615 const QByteArray uuidDataRoot =
m_d->image->root()->uuid().toRfc4122();
616 stream << int(uuidDataRoot.size());
617 stream.writeRawData(uuidDataRoot.data(), uuidDataRoot.size());
619 stream << indexes.size();
620 stream << baseRow << baseColumn;
622 Q_FOREACH (
const QModelIndex &index, indexes) {
626 stream << index.row() - baseRow << index.column() - baseColumn;
628 const QByteArray uuidData = node->
uuid().toRfc4122();
629 stream << int(uuidData.size());
630 stream.writeRawData(uuidData.data(), uuidData.size());
633 stream << int(copyPolicy);
634 data->setData(
"application/x-krita-frame", encoded);
643 QDataStream stream(encoded, QIODevice::ReadOnly);
644 stream >> size_UNUSED >> *row >> *col;
649 if (!index.isValid())
return false;
651 if ( !
m_d->layerEditable(index.row()) )
return false;
672 if ((action != Qt::MoveAction && action != Qt::CopyAction && action != Qt::LinkAction) ||
673 !parent.isValid())
return result;
675 QByteArray encoded =
data->data(
"application/x-krita-frame");
676 QDataStream stream(&encoded, QIODevice::ReadOnly);
679 stream >> uuidLenRoot;
680 QByteArray uuidDataRoot(uuidLenRoot,
'\0');
681 stream.readRawData(uuidDataRoot.data(), uuidLenRoot);
682 QUuid nodeUuidRoot = QUuid::fromRfc4122(uuidDataRoot);
690 if (tmpSrcImage->
root()->
uuid() == nodeUuidRoot) {
691 srcImage = tmpSrcImage;
699 i18n(
"Dropped frames are not available in this Krita instance")
704 int size, baseRow, baseColumn;
705 stream >> size >> baseRow >> baseColumn;
707 const QPoint offset(parent.column() - baseColumn, parent.row() - baseRow);
709 int necessaryOffset = 0;
711 for (
int i = 0; i < size; i++) {
712 int relRow, relColumn;
713 stream >> relRow >> relColumn;
715 const int srcRow = baseRow + relRow;
716 const int srcColumn = baseColumn + relColumn;
720 QByteArray uuidData(uuidLen,
'\0');
721 stream.readRawData(uuidData.data(), uuidLen);
722 QUuid nodeUuid = QUuid::fromRfc4122(uuidData);
726 if (!nodeUuid.isNull()) {
730 QModelIndex index = this->index(srcRow, srcColumn);
736 const QModelIndex dstRowIndex = this->index(srcRow + offset.y(), 0);
737 if (!dstRowIndex.isValid())
continue;
746 if ((srcColumn + offset.x()) * -1 > necessaryOffset ) {
747 necessaryOffset = (srcColumn + offset.x()) * -1;
750 frameMoves << std::make_pair(srcItem, dstItem);
756 if (!stream.atEnd()) {
763 action == Qt::CopyAction :
766 const bool cloneFrames = action == Qt::LinkAction || copyPolicy ==
CloneFramesPolicy;
769 *dataMoved = !copyFrames;
774 if (!frameMoves.isEmpty()) {
778 if (necessaryOffset > 0) {
779 for (
int i = 0; i < frameMoves.count(); i++){
780 frameMoves[i].second.time += necessaryOffset;
784 KisImageBarrierLock lock(
m_d->image);
804 Qt::ItemFlags
flags = ModelWithExternalNotifications::flags(index);
805 if (!index.isValid())
return flags;
807 if (
m_d->frameExists(index.row(), index.column()) ||
m_d->specialKeyframeExists(index.row(), index.column())) {
809 flags |= Qt::ItemIsDragEnabled;
818 flags |= Qt::ItemIsDropEnabled;
829 if (row < 0 || row >
rowCount())
return false;
831 bool result =
m_d->addNewLayer(row);
840 if (row < 0 || row >=
rowCount())
return false;
842 bool result =
m_d->removeLayer(row);
851 m_d->converter->otherLayersList();
853 if (index < 0 || index >= list.size())
return false;
855 list[index].dummy->node()->setPinnedToTimeline(
true);
856 dstRow =
m_d->converter->rowForDummy(list[index].dummy);
864 return m_d->activeLayerIndex;
871 Q_FOREACH(
const QModelIndex &index, dstIndex){
872 if (!index.isValid())
continue;
873 selectedCells.append(QPair<int,int>(index.row(), index.column()));
876 if (selectedCells.size() == 0) {
882 Q_FOREACH (
auto &cell, selectedCells) {
884 if (!dummy)
continue;
901 if (!dstIndex.isValid())
return false;
903 return m_d->addKeyframe(dstIndex.row(), dstIndex.column(),
true);
910 Q_FOREACH (
const QModelIndex &index, indices) {
911 const int time = index.column();
913 if (!channel)
continue;
922 if (dstRows.isEmpty() || count <= 0)
return true;
923 timing = qMax(timing, 1);
928 KisImageBarrierLock locker(
m_d->image);
932 Q_FOREACH (
int row, dstRows) {
933 for (
int column = dstColumn; column <
columnCount(); column++) {
934 indexes << index(row, column);
942 Q_FOREACH (
int row, dstRows) {
944 if (!dummy)
continue;
949 for (
int column = dstColumn; column < dstColumn + (count * timing); column += timing) {
954 const int oldTime =
m_d->image->animationInterface()->currentUITime();
955 const int newTime = dstColumn > oldTime ? dstColumn : dstColumn + (count * timing) - 1;
959 newTime, parentCommand);
971 if (selectedIndexes.isEmpty() || insertCount == 0)
return true;
973 QScopedPointer<KUndo2Command> parentCommand(
new KUndo2Command(
kundo2_i18np(
"Insert frame",
"Insert %1 frames", insertCount)));
976 KisImageBarrierLock locker(
m_d->image);
979 QSet<TimelineSelectionEntry> uniqueKeyframesInSelection;
980 int earliestAffectedTime = std::numeric_limits<int>::max();
982 Q_FOREACH (
const QModelIndex &index, selectedIndexes) {
987 if (!channel)
continue;
989 earliestAffectedTime = qMin(earliestAffectedTime, index.column());
1002 for (
auto it = uniqueKeyframesInSelection.begin(); it != uniqueKeyframesInSelection.end(); ++it) {
1014 std::sort(keyframesToMove.begin(), keyframesToMove.end(),
1016 return lhs.time > rhs.time;
1019 if (keyframesToMove.isEmpty())
return true;
1021 const int oldTime =
m_d->image->animationInterface()->currentUITime();
1026 QModelIndex activeIndex = index(
m_d->activeLayerIndex, oldTime);
1031 if (activeChannel) {
1036 if (insertCount > 0) {
1041 int plannedFrameMove = insertCount;
1043 if (insertCount < 0) {
1051 plannedFrameMove = qMax(insertCount, prevKeyframeTime - entry.
time + 1);
1053 earliestAffectedTime = qMin(earliestAffectedTime, prevKeyframeTime);
1059 const int row =
m_d->converter->rowForDummy(dummy);
1063 for (
int column = entry.
time; column < maxColumn; column++) {
1064 indices << index(row, column);
1068 QPoint(plannedFrameMove, 0),
1071 parentCommand.data());
1074 if (originalActiveKeyframe) {
1079 originalActiveKeyframe,
1080 parentCommand.data());
1094 if (files.count() > 0) {
1095 return files.first().baseName();
1106 if (fileName.exists()) {
1141 m_d->image->animationInterface()->setDocumentRangeStartFrame(column);
1146 m_d->image->animationInterface()->setDocumentRangeEndFrame(column);
1156 if (!
m_d->image)
return;
1158 m_d->image->animationInterface()->setActiveLayerSelectedTimes(times);
float value(const T *src, size_t ch)
void decodeBaseIndex(QByteArray *encoded, int *row, int *col)
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
@ LargeThumbnail
A larger thumbnail for displaying in a tooltip. 200x200 or so.
Qt::DropActions supportedDragActions() const override
qreal audioVolume() const
void requestCurrentNodeChanged(KisNodeSP node)
QMap< QString, KisKeyframeChannel * > channelsAt(QModelIndex index) const override
void setActiveLayerSelectedTimes(const QSet< int > ×)
void setAudioVolume(qreal value)
bool hasConnectionToCanvas() const
@ FrameColorLabelIndexRole
KisNodeSP nodeAt(QModelIndex index) const override
void setDocumentClipRangeEnd(int column)
bool canDropFrameData(const QMimeData *data, const QModelIndex &index)
void setDummiesFacade(KisDummiesFacadeBase *dummiesFacade, KisImageSP image, KisNodeDisplayModeAdapter *displayModeAdapter)
void sigInfiniteTimelineUpdateNeeded()
void setAudioChannelFileName(const QFileInfo &fileName)
void slotImageContentChanged()
void processUpdateQueue()
void setDocumentClipRangeStart(int column)
bool insertRows(int row, int count, const QModelIndex &parent) override
int rowCount(const QModelIndex &parent=QModelIndex()) const override
void requestTransferSelectionBetweenRows(int rowFrom, int rowTo)
~KisAnimTimelineFramesModel() override
bool isAudioMuted() const
bool copyFrame(const QModelIndex &dstIndex)
QVariant data(const QModelIndex &index, int role) const override
bool createFrame(const QModelIndexList &dstIndex)
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
bool insertFrames(int dstColumn, const QList< int > &dstRows, int count, int timing=1)
int activeLayerRow() const
const QScopedPointer< Private > m_d
void slotCurrentNodeChanged(KisNodeSP node)
bool insertOtherLayer(int index, int dstRow)
bool setData(const QModelIndex &index, const QVariant &value, int role) override
QMimeData * mimeData(const QModelIndexList &indexes) const override
bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role) override
void slotDummyChanged(KisNodeDummy *dummy)
QStringList mimeTypes() const override
void sigEnsureRowVisible(int row)
void setLastClickedIndex(const QModelIndex &index)
QMimeData * mimeDataExtended(const QModelIndexList &indexes, const QModelIndex &baseIndex, MimeCopyPolicy copyPolicy) const
void setAudioMuted(bool value)
Qt::DropActions supportedDropActions() const override
void setNodeManipulationInterface(NodeManipulationInterface *iface)
KisKeyframeChannel * channelByID(QModelIndex index, const QString &id) const override
bool removeRows(int row, int count, const QModelIndex &parent) override
void makeClonesUnique(const QModelIndexList &indices)
KisAnimTimelineFramesModel(QObject *parent)
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 insertHoldFrames(const QModelIndexList &selectedIndexes, int insertCount)
QString audioChannelFileName() const
void setAudioTracks(QVector< QFileInfo > f)
void setAudioVolume(qreal level)
QVector< QFileInfo > getAudioTracks() const
KisImageWSP image() const
KisImageWSP image() const
bool isIsolatingLayer() const
KisImageAnimationInterface * animationInterface() const
KisKeyframeChannel stores and manages KisKeyframes. Maps units of time to virtual keyframe values....
int previousKeyframeTime(const int time) const
KisKeyframeSP keyframeAt(int time) const
Get a keyframe at specified time. Used primarily when the value of a given keyframe is needed.
KisKeyframeSP activeKeyframeAt(int time) const
int activeKeyframeTime(int time) const
Get the time of the active keyframe. Useful for snapping any time to that of the most recent keyframe...
int nextKeyframeTime(const int time) const
KisViewManager * viewManager
static bool belongsToIsolatedGroup(KisImageSP image, KisNodeSP node, KisDummiesFacadeBase *dummiesFacade)
KisNodeSP findNode(KisNodeSP rootNode)
QColor colorFromLabelIndex(int index) const
QList< QPointer< KisDocument > > documents
static KisPart * instance()
QScopedPointer< KisPlaybackEngine > playbackEngine
KisMainWindow * currentMainwindow() const
static void runSingleCommandStroke(KisImageSP image, KUndo2Command *cmd, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
runSingleCommandStroke creates a stroke and runs cmd in it. The text() field of cmd is used as a titl...
The KisRasterKeyframeChannel is a concrete KisKeyframeChannel subclass that stores and manages KisRas...
The KisRasterKeyframe class is a concrete subclass of KisKeyframe that wraps a physical raster image ...
KUndo2Command * createOffsetFramesCommand(QModelIndexList srcIndexes, const QPoint &offset, bool copyFrames, bool moveEmptyFrames, KUndo2Command *parentCommand=0)
void setImage(KisImageWSP image)
KisDocument * document() const
void setLastVisibleFrame(int time)
int columnCount(const QModelIndex &parent=QModelIndex()) const override
void slotCurrentTimeChanged(int time)
KisImageWSP image() const
static KisTimeSpan infinite(int start)
void showFloatingMessage(const QString &message, const QIcon &icon, int timeout=4500, KisFloatingMessage::Priority priority=KisFloatingMessage::Medium, int alignment=Qt::AlignCenter|Qt::TextWordWrap)
shows a floating message in the top right corner of the canvas
#define KIS_ASSERT_RECOVER(cond)
#define KIS_SAFE_ASSERT_RECOVER(cond)
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
KUndo2MagicString kundo2_i18np(const char *sing, const char *plur, const A1 &a1)
void makeClonesUnique(KisImageSP image, const FrameItemList &frames)
void createKeyframeLazy(KisImageSP image, KisNodeSP node, const QString &channelId, int time, bool copy)
KUndo2Command * createKeyframeCommand(KisImageSP image, KisNodeSP node, const QString &channelId, int time, bool copy, KUndo2Command *parentCommand)
bool supportsContentFrames(KisNodeSP node)
KUndo2Command * createMoveKeyframesCommand(const FrameItemList &srcFrames, const FrameItemList &dstFrames, bool copy, bool moveEmpty, KUndo2Command *parentCommand)
KUndo2Command * createCloneKeyframesCommand(const FrameMovePairList &srcDstPairs, KUndo2Command *parentCommand)
QColor blendColors(const QColor &c1, const QColor &c2, qreal r1)
int frameColorLabel(int row, int column)
bool needFinishInsertRows
bool layerEditable(int row) const
KisNodeDummy * parentOfRemovedNode
QPersistentModelIndex lastClickedIndex
bool addNewLayer(int row)
QVariant layerProperties(int row) const
QScopedPointer< NodeManipulationInterface > nodeInterface
bool addKeyframe(int row, int column, bool copy)
KisSignalCompressor updateTimer
QList< KisNodeDummy * > updateQueue
QVariant layerName(int row) const
QPointer< KisDummiesFacadeBase > dummiesFacade
bool needFinishRemoveRows
bool frameExists(int row, int column) const
bool removeLayer(int row)
QScopedPointer< TimelineNodeListKeeper > converter
bool specialKeyframeExists(int row, int column)
bool setLayerProperties(int row, PropertyList props)
int layerColorLabel(int row) const
void setFrameColorLabel(int row, int column, int color)
bool frameHasContent(int row, int column) const
void setPinnedToTimeline(bool pinned)
QMap< QString, KisKeyframeChannel * > keyframeChannels
bool isIsolatedRoot() const
bool isPinnedToTimeline() const
KisKeyframeChannel * getKeyframeChannel(const QString &id, bool create)
int colorLabelIndex() const
virtual PropertyList sectionModelProperties() const
virtual bool visible(bool recursive=false) const
virtual QImage createThumbnailForFrame(qint32 w, qint32 h, int time, Qt::AspectRatioMode aspectRatioMode=Qt::IgnoreAspectRatio)
KisProjectionLeafSP projectionLeaf
KisRasterKeyframeChannel * channel