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