1 /*
2  *  Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 #include "timeline_frames_model.h"
20 #include <QFont>
21 #include <QSize>
22 #include <QColor>
23 #include <QMimeData>
24 #include <QPointer>
25 #include <KoResourceModel.h>
26 
27 #include "kis_layer.h"
28 #include "kis_config.h"
29 
30 #include "kis_global.h"
31 #include "kis_debug.h"
32 #include "kis_image.h"
33 #include "kis_image_animation_interface.h"
34 #include "kis_undo_adapter.h"
35 #include "kis_node_dummies_graph.h"
36 #include "kis_dummies_facade_base.h"
37 #include "KisNodeDisplayModeAdapter.h"
38 #include "kis_signal_compressor.h"
39 #include "kis_signal_compressor_with_param.h"
40 #include "kis_keyframe_channel.h"
41 #include "kundo2command.h"
42 #include "kis_post_execution_undo_adapter.h"
43 #include <commands/kis_node_property_list_command.h>
44 #include <commands_new/kis_switch_current_time_command.h>
45 
46 #include "kis_animation_utils.h"
47 #include "timeline_color_scheme.h"
48 #include "kis_node_model.h"
49 #include "kis_projection_leaf.h"
50 #include "kis_time_range.h"
51 
52 #include "kis_node_view_color_scheme.h"
53 #include "krita_utils.h"
54 #include "KisPart.h"
55 #include <QApplication>
56 #include "KisDocument.h"
57 #include "KisViewManager.h"
58 #include "kis_processing_applicator.h"
59 #include <KisImageBarrierLockerWithFeedback.h>
60 #include "kis_node_uuid_info.h"
61 
62 
63 struct TimelineFramesModel::Private
64 {
PrivateTimelineFramesModel::Private65     Private()
66         : activeLayerIndex(0),
67           dummiesFacade(0),
68           needFinishInsertRows(false),
69           needFinishRemoveRows(false),
70           updateTimer(200, KisSignalCompressor::FIRST_INACTIVE),
71           parentOfRemovedNode(0)
72     {}
73 
74     int activeLayerIndex;
75 
76     QPointer<KisDummiesFacadeBase> dummiesFacade;
77     KisImageWSP image;
78     bool needFinishInsertRows;
79     bool needFinishRemoveRows;
80 
81     QList<KisNodeDummy*> updateQueue;
82     KisSignalCompressor updateTimer;
83 
84     KisNodeDummy* parentOfRemovedNode;
85     QScopedPointer<TimelineNodeListKeeper> converter;
86 
87     QScopedPointer<NodeManipulationInterface> nodeInterface;
88 
89     QPersistentModelIndex lastClickedIndex;
90 
layerNameTimelineFramesModel::Private91     QVariant layerName(int row) const {
92         KisNodeDummy *dummy = converter->dummyFromRow(row);
93         if (!dummy) return QVariant();
94         return dummy->node()->name();
95     }
96 
layerEditableTimelineFramesModel::Private97     bool layerEditable(int row) const {
98         KisNodeDummy *dummy = converter->dummyFromRow(row);
99         if (!dummy) return true;
100         return dummy->node()->visible() && !dummy->node()->userLocked();
101     }
102 
frameExistsTimelineFramesModel::Private103     bool frameExists(int row, int column) const {
104         KisNodeDummy *dummy = converter->dummyFromRow(row);
105         if (!dummy) return false;
106 
107         KisKeyframeChannel *primaryChannel = dummy->node()->getKeyframeChannel(KisKeyframeChannel::Content.id());
108         return (primaryChannel && primaryChannel->keyframeAt(column));
109     }
110 
frameHasContentTimelineFramesModel::Private111     bool frameHasContent(int row, int column) {
112 
113         KisNodeDummy *dummy = converter->dummyFromRow(row);
114 
115         KisKeyframeChannel *primaryChannel = dummy->node()->getKeyframeChannel(KisKeyframeChannel::Content.id());
116         if (!primaryChannel) return false;
117 
118         // first check if we are a key frame
119         KisKeyframeSP frame = primaryChannel->activeKeyframeAt(column);
120         if (!frame) return false;
121 
122         return frame->hasContent();
123     }
124 
specialKeyframeExistsTimelineFramesModel::Private125     bool specialKeyframeExists(int row, int column) {
126         KisNodeDummy *dummy = converter->dummyFromRow(row);
127         if (!dummy) return false;
128         Q_FOREACH(KisKeyframeChannel *channel, dummy->node()->keyframeChannels()) {
129             if (channel->id() != KisKeyframeChannel::Content.id() && channel->keyframeAt(column)) {
130                 return true;
131             }
132         }
133         return false;
134     }
135 
frameColorLabelTimelineFramesModel::Private136     int frameColorLabel(int row, int column) {
137         KisNodeDummy *dummy = converter->dummyFromRow(row);
138         if (!dummy) return -1;
139 
140         KisKeyframeChannel *primaryChannel = dummy->node()->getKeyframeChannel(KisKeyframeChannel::Content.id());
141         if (!primaryChannel) return -1;
142 
143         KisKeyframeSP frame = primaryChannel->activeKeyframeAt(column);
144         if (!frame) return -1;
145 
146         return frame->colorLabel();
147     }
148 
setFrameColorLabelTimelineFramesModel::Private149     void setFrameColorLabel(int row, int column, int color) {
150         KisNodeDummy *dummy = converter->dummyFromRow(row);
151         if (!dummy) return;
152 
153         KisKeyframeChannel *primaryChannel = dummy->node()->getKeyframeChannel(KisKeyframeChannel::Content.id());
154         if (!primaryChannel) return;
155 
156         KisKeyframeSP frame = primaryChannel->keyframeAt(column);
157         if (!frame) return;
158 
159         frame->setColorLabel(color);
160     }
161 
layerColorLabelTimelineFramesModel::Private162     int layerColorLabel(int row) const {
163         KisNodeDummy *dummy = converter->dummyFromRow(row);
164         if (!dummy) return -1;
165         return dummy->node()->colorLabelIndex();
166     }
167 
layerPropertiesTimelineFramesModel::Private168     QVariant layerProperties(int row) const {
169         KisNodeDummy *dummy = converter->dummyFromRow(row);
170         if (!dummy) return QVariant();
171 
172         PropertyList props = dummy->node()->sectionModelProperties();
173         return QVariant::fromValue(props);
174     }
175 
setLayerPropertiesTimelineFramesModel::Private176     bool setLayerProperties(int row, PropertyList props) {
177         KisNodeDummy *dummy = converter->dummyFromRow(row);
178         if (!dummy) return false;
179 
180         nodeInterface->setNodeProperties(dummy->node(), image, props);
181         return true;
182     }
183 
addKeyframeTimelineFramesModel::Private184     bool addKeyframe(int row, int column, bool copy) {
185         KisNodeDummy *dummy = converter->dummyFromRow(row);
186         if (!dummy) return false;
187 
188         KisNodeSP node = dummy->node();
189         if (!KisAnimationUtils::supportsContentFrames(node)) return false;
190 
191         KisAnimationUtils::createKeyframeLazy(image, node, KisKeyframeChannel::Content.id(), column, copy);
192         return true;
193     }
194 
addNewLayerTimelineFramesModel::Private195     bool addNewLayer(int row) {
196         Q_UNUSED(row);
197 
198         if (nodeInterface) {
199             KisLayerSP layer = nodeInterface->addPaintLayer();
200             layer->setUseInTimeline(true);
201         }
202 
203         return true;
204     }
205 
removeLayerTimelineFramesModel::Private206     bool removeLayer(int row) {
207         KisNodeDummy *dummy = converter->dummyFromRow(row);
208         if (!dummy) return false;
209 
210         if (nodeInterface) {
211             nodeInterface->removeNode(dummy->node());
212         }
213 
214         return true;
215     }
216 };
217 
TimelineFramesModel(QObject * parent)218 TimelineFramesModel::TimelineFramesModel(QObject *parent)
219     : ModelWithExternalNotifications(parent),
220       m_d(new Private)
221 {
222     connect(&m_d->updateTimer, SIGNAL(timeout()), SLOT(processUpdateQueue()));
223 }
224 
~TimelineFramesModel()225 TimelineFramesModel::~TimelineFramesModel()
226 {
227 }
228 
hasConnectionToCanvas() const229 bool TimelineFramesModel::hasConnectionToCanvas() const
230 {
231     return m_d->dummiesFacade;
232 }
233 
setNodeManipulationInterface(NodeManipulationInterface * iface)234 void TimelineFramesModel::setNodeManipulationInterface(NodeManipulationInterface *iface)
235 {
236     m_d->nodeInterface.reset(iface);
237 }
238 
nodeAt(QModelIndex index) const239 KisNodeSP TimelineFramesModel::nodeAt(QModelIndex index) const
240 {
241     /**
242      * The dummy might not exist because the user could (quickly) change
243      * active layer and the list of the nodes in m_d->converter will change.
244      */
245     KisNodeDummy *dummy = m_d->converter->dummyFromRow(index.row());
246     return dummy ? dummy->node() : 0;
247 }
248 
channelsAt(QModelIndex index) const249 QMap<QString, KisKeyframeChannel*> TimelineFramesModel::channelsAt(QModelIndex index) const
250 {
251     KisNodeDummy *srcDummy = m_d->converter->dummyFromRow(index.row());
252     return srcDummy->node()->keyframeChannels();
253 }
254 
setDummiesFacade(KisDummiesFacadeBase * dummiesFacade,KisImageSP image,KisNodeDisplayModeAdapter * displayModeAdapter)255 void TimelineFramesModel::setDummiesFacade(KisDummiesFacadeBase *dummiesFacade,
256                                            KisImageSP image,
257                                            KisNodeDisplayModeAdapter *displayModeAdapter)
258 {
259     KisDummiesFacadeBase *oldDummiesFacade = m_d->dummiesFacade;
260 
261     if (m_d->dummiesFacade && m_d->image) {
262         m_d->image->animationInterface()->disconnect(this);
263         m_d->image->disconnect(this);
264         m_d->dummiesFacade->disconnect(this);
265     }
266 
267     m_d->image = image;
268     KisTimeBasedItemModel::setImage(image);
269 
270     m_d->dummiesFacade = dummiesFacade;
271     m_d->converter.reset();
272 
273     if (m_d->dummiesFacade) {
274         m_d->converter.reset(new TimelineNodeListKeeper(this, m_d->dummiesFacade, displayModeAdapter));
275         connect(m_d->dummiesFacade, SIGNAL(sigDummyChanged(KisNodeDummy*)),
276                 SLOT(slotDummyChanged(KisNodeDummy*)));
277         connect(m_d->image->animationInterface(),
278                 SIGNAL(sigFullClipRangeChanged()), SIGNAL(sigInfiniteTimelineUpdateNeeded()));
279         connect(m_d->image->animationInterface(),
280                 SIGNAL(sigAudioChannelChanged()), SIGNAL(sigAudioChannelChanged()));
281         connect(m_d->image->animationInterface(),
282                 SIGNAL(sigAudioVolumeChanged()), SIGNAL(sigAudioChannelChanged()));
283         connect(m_d->image, SIGNAL(sigImageModified()), SLOT(slotImageContentChanged()));
284     }
285 
286     if (m_d->dummiesFacade != oldDummiesFacade) {
287         beginResetModel();
288         endResetModel();
289     }
290 
291     if (m_d->dummiesFacade) {
292         emit sigInfiniteTimelineUpdateNeeded();
293         emit sigAudioChannelChanged();
294         slotCurrentTimeChanged(m_d->image->animationInterface()->currentUITime());
295     }
296 }
297 
slotDummyChanged(KisNodeDummy * dummy)298 void TimelineFramesModel::slotDummyChanged(KisNodeDummy *dummy)
299 {
300     if (!m_d->updateQueue.contains(dummy)) {
301         m_d->updateQueue.append(dummy);
302     }
303     m_d->updateTimer.start();
304 }
305 
slotImageContentChanged()306 void TimelineFramesModel::slotImageContentChanged()
307 {
308     if (m_d->activeLayerIndex < 0) return;
309 
310     KisNodeDummy *dummy = m_d->converter->dummyFromRow(m_d->activeLayerIndex);
311     if (!dummy) return;
312 
313     slotDummyChanged(dummy);
314 }
315 
processUpdateQueue()316 void TimelineFramesModel::processUpdateQueue()
317 {
318     if (!m_d->converter) return;
319 
320     Q_FOREACH (KisNodeDummy *dummy, m_d->updateQueue) {
321         int row = m_d->converter->rowForDummy(dummy);
322 
323         if (row >= 0) {
324             emit headerDataChanged (Qt::Vertical, row, row);
325             emit dataChanged(this->index(row, 0), this->index(row, columnCount() - 1));
326         }
327     }
328     m_d->updateQueue.clear();
329 }
330 
slotCurrentNodeChanged(KisNodeSP node)331 void TimelineFramesModel::slotCurrentNodeChanged(KisNodeSP node)
332 {
333     if (!node) {
334         m_d->activeLayerIndex = -1;
335         return;
336     }
337 
338     KisNodeDummy *dummy = m_d->dummiesFacade->dummyForNode(node);
339     if (!dummy) {
340         // It's perfectly normal that dummyForNode returns 0; that happens
341         // when views get activated while Krita is closing down.
342         return;
343     }
344 
345     m_d->converter->updateActiveDummy(dummy);
346 
347     const int row = m_d->converter->rowForDummy(dummy);
348     if (row < 0) {
349         qWarning() << "WARNING: TimelineFramesModel::slotCurrentNodeChanged: node not found!";
350     }
351 
352     if (row >= 0 && m_d->activeLayerIndex != row) {
353         setData(index(row, 0), true, ActiveLayerRole);
354     }
355 }
356 
rowCount(const QModelIndex & parent) const357 int TimelineFramesModel::rowCount(const QModelIndex &parent) const
358 {
359     Q_UNUSED(parent);
360     if(!m_d->dummiesFacade) return 0;
361 
362     return m_d->converter->rowCount();
363 }
364 
data(const QModelIndex & index,int role) const365 QVariant TimelineFramesModel::data(const QModelIndex &index, int role) const
366 {
367     if(!m_d->dummiesFacade) return QVariant();
368 
369     switch (role) {
370     case ActiveLayerRole: {
371         return index.row() == m_d->activeLayerIndex;
372     }
373     case FrameEditableRole: {
374         return m_d->layerEditable(index.row());
375     }
376     case FrameHasContent: {
377         return m_d->frameHasContent(index.row(), index.column());
378     }
379     case FrameExistsRole: {
380         return m_d->frameExists(index.row(), index.column());
381     }
382     case SpecialKeyframeExists: {
383         return m_d->specialKeyframeExists(index.row(), index.column());
384     }
385     case FrameColorLabelIndexRole: {
386         int label = m_d->frameColorLabel(index.row(), index.column());
387         return label > 0 ? label : QVariant();
388     }
389     case Qt::DisplayRole: {
390         return m_d->layerName(index.row());
391     }
392     case Qt::TextAlignmentRole: {
393         return QVariant(Qt::AlignHCenter | Qt::AlignVCenter);
394     }
395     case KoResourceModel::LargeThumbnailRole: {
396         KisNodeDummy *dummy = m_d->converter->dummyFromRow(index.row());
397         if (!dummy) {
398             return  QVariant();
399         }
400         const int maxSize = 200;
401 
402         QImage image(dummy->node()->createThumbnailForFrame(maxSize, maxSize, index.column(), Qt::KeepAspectRatio));
403         return image;
404     }
405     }
406 
407     return ModelWithExternalNotifications::data(index, role);
408 }
409 
setData(const QModelIndex & index,const QVariant & value,int role)410 bool TimelineFramesModel::setData(const QModelIndex &index, const QVariant &value, int role)
411 {
412     if (!index.isValid() || !m_d->dummiesFacade) return false;
413 
414     switch (role) {
415     case ActiveLayerRole: {
416         if (value.toBool() &&
417             index.row() != m_d->activeLayerIndex) {
418 
419             int prevLayer = m_d->activeLayerIndex;
420             m_d->activeLayerIndex = index.row();
421 
422             emit dataChanged(this->index(prevLayer, 0), this->index(prevLayer, columnCount() - 1));
423             emit dataChanged(this->index(m_d->activeLayerIndex, 0), this->index(m_d->activeLayerIndex, columnCount() - 1));
424 
425             emit headerDataChanged(Qt::Vertical, prevLayer, prevLayer);
426             emit headerDataChanged(Qt::Vertical, m_d->activeLayerIndex, m_d->activeLayerIndex);
427 
428             KisNodeDummy *dummy = m_d->converter->dummyFromRow(m_d->activeLayerIndex);
429             KIS_ASSERT_RECOVER(dummy) { return true; }
430 
431             emit requestCurrentNodeChanged(dummy->node());
432             emit sigEnsureRowVisible(m_d->activeLayerIndex);
433         }
434         break;
435     }
436     case FrameColorLabelIndexRole: {
437         m_d->setFrameColorLabel(index.row(), index.column(), value.toInt());
438     }
439         break;
440     }
441 
442     return ModelWithExternalNotifications::setData(index, value, role);
443 }
444 
headerData(int section,Qt::Orientation orientation,int role) const445 QVariant TimelineFramesModel::headerData(int section, Qt::Orientation orientation, int role) const
446 {
447     if(!m_d->dummiesFacade) return QVariant();
448 
449     if (orientation == Qt::Vertical) {
450         switch (role) {
451         case ActiveLayerRole:
452             return section == m_d->activeLayerIndex;
453         case Qt::DisplayRole: {
454             QVariant value = headerData(section, orientation, Qt::ToolTipRole);
455             if (!value.isValid()) return value;
456 
457             QString name = value.toString();
458             const int maxNameSize = 13;
459 
460             if (name.size() > maxNameSize) {
461                 name = QString("%1...").arg(name.left(maxNameSize));
462             }
463 
464             return name;
465         }
466         case Qt::TextColorRole: {
467             // WARNING: this role doesn't work for header views! Use
468             //          bold font to show isolated mode instead!
469             return QVariant();
470         }
471         case Qt::FontRole: {
472             KisNodeDummy *dummy = m_d->converter->dummyFromRow(section);
473             if (!dummy) return QVariant();
474             KisNodeSP node = dummy->node();
475 
476             QFont baseFont;
477             if (node->projectionLeaf()->isDroppedNode()) {
478                 baseFont.setStrikeOut(true);
479             } else if (m_d->image && m_d->image->isolatedModeRoot() &&
480                        KisNodeModel::belongsToIsolatedGroup(m_d->image, node, m_d->dummiesFacade)) {
481                 baseFont.setBold(true);
482             }
483             return baseFont;
484         }
485         case Qt::ToolTipRole: {
486             return m_d->layerName(section);
487         }
488         case TimelinePropertiesRole: {
489             return QVariant::fromValue(m_d->layerProperties(section));
490         }
491         case OtherLayersRole: {
492             TimelineNodeListKeeper::OtherLayersList list =
493                 m_d->converter->otherLayersList();
494 
495             return QVariant::fromValue(list);
496         }
497         case LayerUsedInTimelineRole: {
498             KisNodeDummy *dummy = m_d->converter->dummyFromRow(section);
499             if (!dummy) return QVariant();
500             return dummy->node()->useInTimeline();
501         }
502         case Qt::BackgroundRole: {
503             int label = m_d->layerColorLabel(section);
504             if (label > 0) {
505                 KisNodeViewColorScheme scm;
506                 QColor color = scm.colorLabel(label);
507                 QPalette pal = qApp->palette();
508                 color = KritaUtils::blendColors(color, pal.color(QPalette::Button), 0.3);
509                 return QBrush(color);
510             } else {
511                 return QVariant();
512             }
513         }
514         }
515     }
516 
517     return ModelWithExternalNotifications::headerData(section, orientation, role);
518 }
519 
setHeaderData(int section,Qt::Orientation orientation,const QVariant & value,int role)520 bool TimelineFramesModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role)
521 {
522     if (!m_d->dummiesFacade) return false;
523 
524     if (orientation == Qt::Vertical) {
525         switch (role) {
526         case ActiveLayerRole: {
527             setData(index(section, 0), value, role);
528             break;
529         }
530         case TimelinePropertiesRole: {
531             TimelineFramesModel::PropertyList props = value.value<TimelineFramesModel::PropertyList>();
532 
533             int result = m_d->setLayerProperties(section, props);
534             emit headerDataChanged (Qt::Vertical, section, section);
535             return result;
536         }
537         case LayerUsedInTimelineRole: {
538             KisNodeDummy *dummy = m_d->converter->dummyFromRow(section);
539             if (!dummy) return false;
540             dummy->node()->setUseInTimeline(value.toBool());
541             return true;
542         }
543         }
544     }
545 
546     return ModelWithExternalNotifications::setHeaderData(section, orientation, value, role);
547 }
548 
supportedDragActions() const549 Qt::DropActions TimelineFramesModel::supportedDragActions() const
550 {
551     return Qt::MoveAction | Qt::CopyAction;
552 }
553 
supportedDropActions() const554 Qt::DropActions TimelineFramesModel::supportedDropActions() const
555 {
556     return Qt::MoveAction | Qt::CopyAction;
557 }
558 
mimeTypes() const559 QStringList TimelineFramesModel::mimeTypes() const
560 {
561     QStringList types;
562     types << QLatin1String("application/x-krita-frame");
563     return types;
564 }
565 
setLastClickedIndex(const QModelIndex & index)566 void TimelineFramesModel::setLastClickedIndex(const QModelIndex &index)
567 {
568     m_d->lastClickedIndex = index;
569 }
570 
mimeData(const QModelIndexList & indexes) const571 QMimeData* TimelineFramesModel::mimeData(const QModelIndexList &indexes) const
572 {
573     return mimeDataExtended(indexes, m_d->lastClickedIndex, UndefinedPolicy);
574 }
575 
mimeDataExtended(const QModelIndexList & indexes,const QModelIndex & baseIndex,TimelineFramesModel::MimeCopyPolicy copyPolicy) const576 QMimeData *TimelineFramesModel::mimeDataExtended(const QModelIndexList &indexes,
577                                                  const QModelIndex &baseIndex,
578                                                  TimelineFramesModel::MimeCopyPolicy copyPolicy) const
579 {
580     QMimeData *data = new QMimeData();
581 
582     QByteArray encoded;
583     QDataStream stream(&encoded, QIODevice::WriteOnly);
584 
585     const int baseRow = baseIndex.row();
586     const int baseColumn = baseIndex.column();
587 
588     const QByteArray uuidDataRoot = m_d->image->root()->uuid().toRfc4122();
589     stream << int(uuidDataRoot.size());
590     stream.writeRawData(uuidDataRoot.data(), uuidDataRoot.size());
591 
592     stream << indexes.size();
593     stream << baseRow << baseColumn;
594 
595     Q_FOREACH (const QModelIndex &index, indexes) {
596         KisNodeSP node = nodeAt(index);
597         KIS_SAFE_ASSERT_RECOVER(node) { continue; }
598 
599         stream << index.row() - baseRow << index.column() - baseColumn;
600 
601         const QByteArray uuidData = node->uuid().toRfc4122();
602         stream << int(uuidData.size());
603         stream.writeRawData(uuidData.data(), uuidData.size());
604     }
605 
606     stream << int(copyPolicy);
607     data->setData("application/x-krita-frame", encoded);
608 
609     return data;
610 }
611 
decodeBaseIndex(QByteArray * encoded,int * row,int * col)612 inline void decodeBaseIndex(QByteArray *encoded, int *row, int *col)
613 {
614     int size_UNUSED = 0;
615 
616     QDataStream stream(encoded, QIODevice::ReadOnly);
617     stream >> size_UNUSED >> *row >> *col;
618 }
619 
canDropFrameData(const QMimeData *,const QModelIndex & index)620 bool TimelineFramesModel::canDropFrameData(const QMimeData */*data*/, const QModelIndex &index)
621 {
622     if (!index.isValid()) return false;
623 
624     if ( !m_d->layerEditable(index.row()) ) return false;
625 
626     /**
627      * Now we support D&D around any layer, so just return 'true' all
628      * the time.
629      */
630     return true;
631 }
632 
dropMimeData(const QMimeData * data,Qt::DropAction action,int row,int column,const QModelIndex & parent)633 bool TimelineFramesModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
634 {
635     Q_UNUSED(row);
636     Q_UNUSED(column);
637 
638     return dropMimeDataExtended(data, action, parent);
639 }
640 
dropMimeDataExtended(const QMimeData * data,Qt::DropAction action,const QModelIndex & parent,bool * dataMoved)641 bool TimelineFramesModel::dropMimeDataExtended(const QMimeData *data, Qt::DropAction action, const QModelIndex &parent, bool *dataMoved)
642 {
643     bool result = false;
644 
645     if ((action != Qt::MoveAction && action != Qt::CopyAction) ||
646         !parent.isValid()) return result;
647 
648     QByteArray encoded = data->data("application/x-krita-frame");
649     QDataStream stream(&encoded, QIODevice::ReadOnly);
650 
651     int uuidLenRoot = 0;
652     stream >> uuidLenRoot;
653     QByteArray uuidDataRoot(uuidLenRoot, '\0');
654     stream.readRawData(uuidDataRoot.data(), uuidLenRoot);
655     QUuid nodeUuidRoot = QUuid::fromRfc4122(uuidDataRoot);
656 
657     KisPart *partInstance = KisPart::instance();
658     QList<QPointer<KisDocument>> documents = partInstance->documents();
659 
660     KisImageSP srcImage = 0;
661     Q_FOREACH(KisDocument *doc, documents) {
662         KisImageSP tmpSrcImage = doc->image();
663         if (tmpSrcImage->root()->uuid() == nodeUuidRoot) {
664             srcImage = tmpSrcImage;
665             break;
666         }
667     }
668 
669     if (!srcImage) {
670         KisPart *kisPartInstance = KisPart::instance();
671         kisPartInstance->currentMainwindow()->viewManager()->showFloatingMessage(
672                     i18n("Dropped frames are not available in this Krita instance")
673                     , QIcon());
674         return false;
675     }
676 
677     int size, baseRow, baseColumn;
678     stream >> size >> baseRow >> baseColumn;
679 
680     const QPoint offset(parent.column() - baseColumn, parent.row() - baseRow);
681 
682     KisAnimationUtils::FrameMovePairList frameMoves;
683 
684     for (int i = 0; i < size; i++) {
685         int relRow, relColumn;
686         stream >> relRow >> relColumn;
687 
688         const int srcRow = baseRow + relRow;
689         const int srcColumn = baseColumn + relColumn;
690 
691         int uuidLen = 0;
692         stream >> uuidLen;
693         QByteArray uuidData(uuidLen, '\0');
694         stream.readRawData(uuidData.data(), uuidLen);
695         QUuid nodeUuid = QUuid::fromRfc4122(uuidData);
696 
697         KisNodeSP srcNode;
698 
699         if (!nodeUuid.isNull()) {
700             KisNodeUuidInfo nodeInfo(nodeUuid);
701             srcNode = nodeInfo.findNode(srcImage->root());
702         } else {
703             QModelIndex index = this->index(srcRow, srcColumn);
704             srcNode = nodeAt(index);
705         }
706 
707         KIS_SAFE_ASSERT_RECOVER(srcNode) { continue; }
708 
709         const QModelIndex dstRowIndex = this->index(srcRow + offset.y(), 0);
710         if (!dstRowIndex.isValid()) continue;
711 
712         KisNodeSP dstNode = nodeAt(dstRowIndex);
713         KIS_SAFE_ASSERT_RECOVER(dstNode) { continue; }
714 
715         Q_FOREACH (KisKeyframeChannel *channel, srcNode->keyframeChannels().values()) {
716             KisAnimationUtils::FrameItem srcItem(srcNode, channel->id(), srcColumn);
717             KisAnimationUtils::FrameItem dstItem(dstNode, channel->id(), srcColumn + offset.x());
718             frameMoves << std::make_pair(srcItem, dstItem);
719         }
720     }
721 
722     MimeCopyPolicy copyPolicy = UndefinedPolicy;
723 
724     if (!stream.atEnd()) {
725         int value = 0;
726         stream >> value;
727         copyPolicy = MimeCopyPolicy(value);
728     }
729 
730     const bool copyFrames =
731         copyPolicy == UndefinedPolicy ?
732         action == Qt::CopyAction :
733         copyPolicy == CopyFramesPolicy;
734 
735     if (dataMoved) {
736         *dataMoved = !copyFrames;
737     }
738 
739     KUndo2Command *cmd = 0;
740 
741     if (!frameMoves.isEmpty()) {
742         KisImageBarrierLockerWithFeedback locker(m_d->image);
743         cmd = KisAnimationUtils::createMoveKeyframesCommand(frameMoves, copyFrames, false, 0);
744     }
745 
746     if (cmd) {
747         KisProcessingApplicator::runSingleCommandStroke(m_d->image, cmd,
748                                                         KisStrokeJobData::BARRIER,
749                                                         KisStrokeJobData::EXCLUSIVE);
750     }
751 
752     return cmd;
753 }
754 
flags(const QModelIndex & index) const755 Qt::ItemFlags TimelineFramesModel::flags(const QModelIndex &index) const
756 {
757     Qt::ItemFlags flags = ModelWithExternalNotifications::flags(index);
758     if (!index.isValid()) return flags;
759 
760     if (m_d->frameExists(index.row(), index.column()) || m_d->specialKeyframeExists(index.row(), index.column())) {
761         if (data(index, FrameEditableRole).toBool()) {
762             flags |= Qt::ItemIsDragEnabled;
763         }
764     }
765 
766     /**
767      * Basically we should forbid overrides only if we D&D a single frame
768      * and allow it when we D&D multiple frames. But we cannot distinguish
769      * it here... So allow all the time.
770      */
771     flags |= Qt::ItemIsDropEnabled;
772 
773     return flags;
774 }
775 
insertRows(int row,int count,const QModelIndex & parent)776 bool TimelineFramesModel::insertRows(int row, int count, const QModelIndex &parent)
777 {
778     Q_UNUSED(parent);
779 
780     KIS_ASSERT_RECOVER(count == 1) { return false; }
781 
782     if (row < 0 || row > rowCount()) return false;
783 
784     bool result = m_d->addNewLayer(row);
785     return result;
786 }
787 
removeRows(int row,int count,const QModelIndex & parent)788 bool TimelineFramesModel::removeRows(int row, int count, const QModelIndex &parent)
789 {
790     Q_UNUSED(parent);
791     KIS_ASSERT_RECOVER(count == 1) { return false; }
792 
793     if (row < 0 || row >= rowCount()) return false;
794 
795     bool result = m_d->removeLayer(row);
796     return result;
797 }
798 
insertOtherLayer(int index,int dstRow)799 bool TimelineFramesModel::insertOtherLayer(int index, int dstRow)
800 {
801     Q_UNUSED(dstRow);
802 
803     TimelineNodeListKeeper::OtherLayersList list =
804         m_d->converter->otherLayersList();
805 
806     if (index < 0 || index >= list.size()) return false;
807 
808     list[index].dummy->node()->setUseInTimeline(true);
809     dstRow = m_d->converter->rowForDummy(list[index].dummy);
810     setData(this->index(dstRow, 0), true, ActiveLayerRole);
811 
812     return true;
813 }
814 
activeLayerRow() const815 int TimelineFramesModel::activeLayerRow() const
816 {
817     return m_d->activeLayerIndex;
818 }
819 
createFrame(const QModelIndex & dstIndex)820 bool TimelineFramesModel::createFrame(const QModelIndex &dstIndex)
821 {
822     if (!dstIndex.isValid()) return false;
823 
824     return m_d->addKeyframe(dstIndex.row(), dstIndex.column(), false);
825 }
826 
copyFrame(const QModelIndex & dstIndex)827 bool TimelineFramesModel::copyFrame(const QModelIndex &dstIndex)
828 {
829     if (!dstIndex.isValid()) return false;
830 
831     return m_d->addKeyframe(dstIndex.row(), dstIndex.column(), true);
832 }
833 
insertFrames(int dstColumn,const QList<int> & dstRows,int count,int timing)834 bool TimelineFramesModel::insertFrames(int dstColumn, const QList<int> &dstRows, int count, int timing)
835 {
836     if (dstRows.isEmpty() || count <= 0) return true;
837     timing = qMax(timing, 1);
838 
839     KUndo2Command *parentCommand = new KUndo2Command(kundo2_i18np("Insert frame", "Insert %1 frames", count));
840 
841     {
842         KisImageBarrierLockerWithFeedback locker(m_d->image);
843 
844         QModelIndexList indexes;
845 
846         Q_FOREACH (int row, dstRows) {
847             for (int column = dstColumn; column < columnCount(); column++) {
848                 indexes << index(row, column);
849             }
850         }
851 
852         setLastVisibleFrame(columnCount() + (count * timing) - 1);
853 
854         createOffsetFramesCommand(indexes, QPoint((count * timing), 0), false, false, parentCommand);
855 
856         Q_FOREACH (int row, dstRows) {
857             KisNodeDummy *dummy = m_d->converter->dummyFromRow(row);
858             if (!dummy) continue;
859 
860             KisNodeSP node = dummy->node();
861             if (!KisAnimationUtils::supportsContentFrames(node)) continue;
862 
863             for (int column = dstColumn; column < dstColumn + (count * timing); column += timing) {
864                 KisAnimationUtils::createKeyframeCommand(m_d->image, node, KisKeyframeChannel::Content.id(), column, false, parentCommand);
865             }
866         }
867 
868         const int oldTime = m_d->image->animationInterface()->currentUITime();
869         const int newTime = dstColumn > oldTime ? dstColumn : dstColumn + (count * timing) - 1;
870 
871         new KisSwitchCurrentTimeCommand(m_d->image->animationInterface(),
872                                         oldTime,
873                                         newTime, parentCommand);
874     }
875 
876     KisProcessingApplicator::runSingleCommandStroke(m_d->image, parentCommand,
877                                                     KisStrokeJobData::BARRIER,
878                                                     KisStrokeJobData::EXCLUSIVE);
879 
880     return true;
881 }
882 
insertHoldFrames(const QModelIndexList & selectedIndexes,int count)883 bool TimelineFramesModel::insertHoldFrames(const QModelIndexList &selectedIndexes, int count)
884 {
885     if (selectedIndexes.isEmpty() || count == 0) return true;
886 
887     QScopedPointer<KUndo2Command> parentCommand(new KUndo2Command(kundo2_i18np("Insert frame", "Insert %1 frames", count)));
888 
889     {
890         KisImageBarrierLockerWithFeedback locker(m_d->image);
891 
892         QSet<KisKeyframeSP> uniqueKeyframesInSelection;
893 
894         int minSelectedTime = std::numeric_limits<int>::max();
895 
896         Q_FOREACH (const QModelIndex &index, selectedIndexes) {
897             KisNodeSP node = nodeAt(index);
898             KIS_SAFE_ASSERT_RECOVER(node) { continue; }
899 
900             KisKeyframeChannel *channel = node->getKeyframeChannel(KisKeyframeChannel::Content.id());
901             if (!channel) continue;
902 
903             minSelectedTime = qMin(minSelectedTime, index.column());
904             KisKeyframeSP keyFrame = channel->activeKeyframeAt(index.column());
905 
906             if (keyFrame) {
907                 uniqueKeyframesInSelection.insert(keyFrame);
908             }
909         }
910 
911         QList<KisKeyframeSP> keyframesToMove;
912 
913         for (auto it = uniqueKeyframesInSelection.begin(); it != uniqueKeyframesInSelection.end(); ++it) {
914             KisKeyframeSP keyframe = *it;
915 
916             KisKeyframeChannel *channel = keyframe->channel();
917             KisKeyframeSP nextKeyframe = channel->nextKeyframe(keyframe);
918 
919             if (nextKeyframe) {
920                 keyframesToMove << nextKeyframe;
921             }
922         }
923 
924         std::sort(keyframesToMove.begin(), keyframesToMove.end(),
925             [] (KisKeyframeSP lhs, KisKeyframeSP rhs) {
926                 return lhs->time() > rhs->time();
927             });
928 
929         if (keyframesToMove.isEmpty()) return true;
930 
931         const int maxColumn = columnCount();
932 
933         if (count > 0) {
934             setLastVisibleFrame(columnCount() + count);
935         }
936 
937         Q_FOREACH (KisKeyframeSP keyframe, keyframesToMove) {
938             int plannedFrameMove = count;
939 
940             if (count < 0) {
941                 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(keyframe->time() > 0, false);
942 
943                 KisKeyframeSP prevFrame = keyframe->channel()->previousKeyframe(keyframe);
944                 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(prevFrame, false);
945 
946                 plannedFrameMove = qMax(count, prevFrame->time() - keyframe->time() + 1);
947 
948                 minSelectedTime = qMin(minSelectedTime, prevFrame->time());
949             }
950 
951             KisNodeDummy *dummy = m_d->dummiesFacade->dummyForNode(keyframe->channel()->node());
952             KIS_SAFE_ASSERT_RECOVER(dummy) { continue; }
953 
954             const int row = m_d->converter->rowForDummy(dummy);
955             KIS_SAFE_ASSERT_RECOVER(row >= 0) { continue; }
956 
957             QModelIndexList indexes;
958             for (int column = keyframe->time(); column < maxColumn; column++) {
959                 indexes << index(row, column);
960             }
961 
962             createOffsetFramesCommand(indexes,
963                                       QPoint(plannedFrameMove, 0),
964                                       false, true, parentCommand.data());
965         }
966 
967         const int oldTime = m_d->image->animationInterface()->currentUITime();
968         const int newTime = minSelectedTime;
969 
970         new KisSwitchCurrentTimeCommand(m_d->image->animationInterface(),
971                                         oldTime,
972                                         newTime, parentCommand.data());
973     }
974 
975 
976     KisProcessingApplicator::runSingleCommandStroke(m_d->image, parentCommand.take(),
977                                                     KisStrokeJobData::BARRIER,
978                                                     KisStrokeJobData::EXCLUSIVE);
979     return true;
980 }
981 
audioChannelFileName() const982 QString TimelineFramesModel::audioChannelFileName() const
983 {
984     return m_d->image ? m_d->image->animationInterface()->audioChannelFileName() : QString();
985 }
986 
setAudioChannelFileName(const QString & fileName)987 void TimelineFramesModel::setAudioChannelFileName(const QString &fileName)
988 {
989     KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->image);
990     m_d->image->animationInterface()->setAudioChannelFileName(fileName);
991 }
992 
isAudioMuted() const993 bool TimelineFramesModel::isAudioMuted() const
994 {
995     return m_d->image ? m_d->image->animationInterface()->isAudioMuted() : false;
996 }
997 
setAudioMuted(bool value)998 void TimelineFramesModel::setAudioMuted(bool value)
999 {
1000     KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->image);
1001     m_d->image->animationInterface()->setAudioMuted(value);
1002 }
1003 
audioVolume() const1004 qreal TimelineFramesModel::audioVolume() const
1005 {
1006     return m_d->image ? m_d->image->animationInterface()->audioVolume() : 0.5;
1007 }
1008 
setAudioVolume(qreal value)1009 void TimelineFramesModel::setAudioVolume(qreal value)
1010 {
1011     KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->image);
1012     m_d->image->animationInterface()->setAudioVolume(value);
1013 }
1014 
setFullClipRangeStart(int column)1015 void TimelineFramesModel::setFullClipRangeStart(int column)
1016 {
1017     m_d->image->animationInterface()->setFullClipRangeStartTime(column);
1018 }
1019 
setFullClipRangeEnd(int column)1020 void TimelineFramesModel::setFullClipRangeEnd(int column)
1021 {
1022     m_d->image->animationInterface()->setFullClipRangeEndTime(column);
1023 }
1024