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);
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();
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 << int(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);
857 m_d->converter->otherLayersList();
859 if (index < 0 || index >= list.size())
return false;
861 list[index].dummy->node()->setPinnedToTimeline(
true);
862 dstRow =
m_d->converter->rowForDummy(list[index].dummy);
870 return m_d->activeLayerIndex;
877 Q_FOREACH(
const QModelIndex &index, dstIndex){
878 if (!index.isValid())
continue;
879 selectedCells.append(QPair<int,int>(index.row(), index.column()));
882 if (selectedCells.size() == 0) {
888 Q_FOREACH (
auto &cell, selectedCells) {
890 if (!dummy)
continue;
907 if (!dstIndex.isValid())
return false;
909 return m_d->addKeyframe(dstIndex.row(), dstIndex.column(),
true);
916 Q_FOREACH (
const QModelIndex &index, indices) {
917 const int time = index.column();
919 if (!channel)
continue;
928 if (dstRows.isEmpty() || count <= 0)
return true;
929 timing = qMax(timing, 1);
934 KisImageBarrierLock locker(
m_d->image);
938 Q_FOREACH (
int row, dstRows) {
939 for (
int column = dstColumn; column <
columnCount(); column++) {
940 indexes << index(row, column);
948 Q_FOREACH (
int row, dstRows) {
950 if (!dummy)
continue;
955 for (
int column = dstColumn; column < dstColumn + (count * timing); column += timing) {
960 const int oldTime =
m_d->image->animationInterface()->currentUITime();
961 const int newTime = dstColumn > oldTime ? dstColumn : dstColumn + (count * timing) - 1;
965 newTime, parentCommand);
977 if (selectedIndexes.isEmpty() || insertCount == 0)
return true;
979 QScopedPointer<KUndo2Command> parentCommand(
new KUndo2Command(
kundo2_i18np(
"Insert frame",
"Insert %1 frames", insertCount)));
982 KisImageBarrierLock locker(
m_d->image);
985 QSet<TimelineSelectionEntry> uniqueKeyframesInSelection;
986 int earliestAffectedTime = std::numeric_limits<int>::max();
988 Q_FOREACH (
const QModelIndex &index, selectedIndexes) {
993 if (!channel)
continue;
995 earliestAffectedTime = qMin(earliestAffectedTime, index.column());
1008 for (
auto it = uniqueKeyframesInSelection.begin(); it != uniqueKeyframesInSelection.end(); ++it) {
1020 std::sort(keyframesToMove.begin(), keyframesToMove.end(),
1022 return lhs.time > rhs.time;
1025 if (keyframesToMove.isEmpty())
return true;
1027 const int oldTime =
m_d->image->animationInterface()->currentUITime();
1032 QModelIndex activeIndex = index(
m_d->activeLayerIndex, oldTime);
1037 if (activeChannel) {
1042 if (insertCount > 0) {
1047 int plannedFrameMove = insertCount;
1049 if (insertCount < 0) {
1057 plannedFrameMove = qMax(insertCount, prevKeyframeTime - entry.
time + 1);
1059 earliestAffectedTime = qMin(earliestAffectedTime, prevKeyframeTime);
1065 const int row =
m_d->converter->rowForDummy(dummy);
1069 for (
int column = entry.
time; column < maxColumn; column++) {
1070 indices << index(row, column);
1074 QPoint(plannedFrameMove, 0),
1077 parentCommand.data());
1080 if (originalActiveKeyframe) {
1085 originalActiveKeyframe,
1086 parentCommand.data());
1100 if (files.count() > 0) {
1101 return files.first().baseName();
1112 if (fileName.exists()) {
1147 m_d->image->animationInterface()->setDocumentRangeStartFrame(column);
1152 m_d->image->animationInterface()->setDocumentRangeEndFrame(column);
1162 if (!
m_d->image)
return;
1164 m_d->image->animationInterface()->setActiveLayerSelectedTimes(times);
1171 if (row >= 0 && row < rows && cols > 0) {
1172 Q_EMIT dataChanged(index(row, 0), index(row, cols - 1));
float value(const T *src, size_t ch)
void decodeBaseIndex(QByteArray *encoded, int *row, int *col)
@ 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 requestNodeChange(const QModelIndex &nodeIndex)
void slotImageContentChanged()
void processUpdateQueue()
void setDocumentClipRangeStart(int column)
bool insertRows(int row, int count, const QModelIndex &parent) override
void emitRowDataChanged(int row)
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
QImage createPreferredThumbnailForFrame(qint32 w, qint32 h, int time, Qt::AspectRatioMode aspectRatioMode=Qt::IgnoreAspectRatio)
KisProjectionLeafSP projectionLeaf
KisRasterKeyframeChannel * channel