1 /* This file is part of the KDE project
2  * Copyright ( C ) 2007 Thorsten Zachmann <zachmann@kde.org>
3  * Copyright ( C ) 2010 Benjamin Port <port.benjamin@gmail.com>
4  * Copyright ( C ) 2012 Paul Mendez <paulestebanms@gmail.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (  at your option ) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 #include "KPrShapeAnimations.h"
23 
24 //Qt Headers
25 #include <QList>
26 #include <QSet>
27 #include <QPainter>
28 #include <QPainterPath>
29 
30 //Stage Headers
31 #include "KPrDocument.h"
32 #include "animations/KPrAnimationSubStep.h"
33 #include "animations/KPrAnimateMotion.h"
34 #include "commands/KPrAnimationRemoveCommand.h"
35 #include "commands/KPrReorderAnimationCommand.h"
36 #include <commands/KPrEditAnimationTimeLineCommand.h>
37 #include <commands/KPrAnimationEditNodeTypeCommand.h>
38 #include <commands/KPrReplaceAnimationCommand.h>
39 #include <commands/KPrAnimationCreateCommand.h>
40 #include "StageDebug.h"
41 
42 //Calligra Headers
43 #include <KoShape.h>
44 #include <KoShapePainter.h>
45 #include <KoShapeContainer.h>
46 #include <KoPathShape.h>
47 #include <KoIcon.h>
48 
49 //KF5 Headers
50 #include <kiconloader.h>
51 #include <klocalizedstring.h>
52 
53 const int COLUMN_COUNT = 10;
54 const int INVALID = -1;
55 
KPrShapeAnimations(KPrDocument * document,QObject * parent)56 KPrShapeAnimations::KPrShapeAnimations(KPrDocument *document, QObject *parent)
57     :QAbstractTableModel(parent)
58     , m_currentEditedAnimation(0)
59     , m_firstEdition(true)
60     , m_oldBegin(INVALID)
61     , m_oldDuration(INVALID)
62     , m_document(document)
63 {
64 }
65 
~KPrShapeAnimations()66 KPrShapeAnimations::~KPrShapeAnimations()
67 {
68 }
69 
flags(const QModelIndex & index) const70 Qt::ItemFlags KPrShapeAnimations::flags(const QModelIndex &index) const
71 {
72     Qt::ItemFlags theFlags = QAbstractTableModel::flags(index);
73     if (index.isValid()) {
74         theFlags |= Qt::ItemIsSelectable|Qt::ItemIsEnabled;
75         //if (index.column() == Name)
76             //theFlags |= Qt::ItemIsEditable;//|
77                         //Qt::ItemIsDragEnabled|Qt::ItemIsDropEnabled;
78     }
79     return theFlags;
80 }
81 
data(const QModelIndex & index,int role) const82 QVariant KPrShapeAnimations::data(const QModelIndex &index, int role) const
83 {
84     if (!index.isValid() || index.column() < 0 ||
85             index.column() >= COLUMN_COUNT || index.row() < 0
86             || index.row() >= rowCount(QModelIndex())) {
87         return QVariant();
88     }
89 
90     // Read Data
91     KPrShapeAnimation::NodeType nodeType;
92     int currentGroup = -1;
93     KPrShapeAnimation *thisAnimation = animationByRow(index.row(), &currentGroup, &nodeType);
94     if (!thisAnimation) {
95         return QVariant();
96     }
97 
98     if (role == Qt::DisplayRole || role == Qt::EditRole) {
99         switch (index.column()) {
100             case Group: return currentGroup;
101             case StepCount:
102                 if (nodeType == KPrShapeAnimation::OnClick) {
103                     return currentGroup;
104                 }
105                 else {
106                     return QVariant();
107                 }
108             case TriggerEvent: return QVariant();
109             case Name: return getAnimationName(thisAnimation);
110             case ShapeThumbnail: return QVariant();
111             case AnimationIcon: return QVariant();
112             case StartTime: return thisAnimation->timeRange().first;
113             case Duration: return thisAnimation->globalDuration();
114             case AnimationClass: return thisAnimation->presetClass();
115             case NodeType: return nodeType;
116             default: Q_ASSERT(false);
117         }
118     }
119     if (role == Qt::TextAlignmentRole) {
120         if (index.column() == Name) {
121             return static_cast<int>(Qt::AlignLeft|Qt::AlignVCenter);
122         }
123         return static_cast<int>(Qt::AlignCenter);
124     }
125     if (role == Qt::DecorationRole) {
126         switch (index.column()) {
127             case Group: return QVariant();
128             case StepCount: return QVariant();
129             case TriggerEvent:
130                 if (nodeType == KPrShapeAnimation::OnClick)
131                     return koIcon("onclick");
132                 if (nodeType == KPrShapeAnimation::AfterPrevious)
133                     return koIcon("after_previous");
134                 if (nodeType == KPrShapeAnimation::WithPrevious)
135                     return koIcon("with_previous");
136                 return QVariant();
137             case Name: return QVariant();
138             case ShapeThumbnail: return getAnimationShapeThumbnail(thisAnimation);
139             case AnimationIcon:  return getAnimationIcon(thisAnimation);
140             case StartTime: return QVariant();
141             case Duration: return QVariant();
142             case AnimationClass: return QVariant();
143             case NodeType: return QVariant();
144             default: Q_ASSERT(false);
145         }
146     }
147     if (role == Qt::SizeHintRole) {
148         switch (index.column()) {
149             case Group:
150             case StepCount: return QVariant();
151             case TriggerEvent: return QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall);
152             case Name: return QVariant();
153             case ShapeThumbnail: return QSize(KIconLoader::SizeMedium, KIconLoader::SizeMedium);
154             case AnimationIcon:
155             case StartTime:
156             case Duration:
157             case AnimationClass: return QVariant();
158             case NodeType: return QVariant();
159             default: Q_ASSERT(false);
160         }
161     }
162     if (role == Qt::ToolTipRole) {
163             switch (index.column()) {
164             case Group:
165             case StepCount: return QVariant();
166             case TriggerEvent:/// emitted if an item time range has changed (return the index of the item changed)
167                 if (nodeType == KPrShapeAnimation::OnClick)
168                     return i18n("start on mouse click");
169                 if (nodeType == KPrShapeAnimation::AfterPrevious)
170                     return i18n("start after previous animation");
171                 if (nodeType == KPrShapeAnimation::WithPrevious)
172                     return i18n("start with previous animation");
173                 return QVariant();
174             case Name: return QVariant();
175             case ShapeThumbnail: return thisAnimation->shape()->name();
176             case AnimationIcon: return getAnimationName(thisAnimation);
177             case StartTime: {
178                 const float startDelay = thisAnimation->timeRange().first / 1000.0;
179                 const float duration = thisAnimation->globalDuration() / 1000.0;
180                 return i18n("Start after %1 seconds. Duration of %2 seconds.", startDelay, duration);
181             }
182             case Duration: return QVariant();
183             case AnimationClass: return thisAnimation->presetClassText();
184             case NodeType: return QVariant();
185             default: Q_ASSERT(false);
186             }
187         }
188     return QVariant();
189 }
190 
headerData(int section,Qt::Orientation orientation,int role) const191 QVariant KPrShapeAnimations::headerData(int section, Qt::Orientation orientation, int role) const
192 {
193     if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
194         if (section == Name) {
195             return i18n("Animation");
196         }
197         else if (section == TriggerEvent) {
198             return QString();
199         }
200         else if (section == ShapeThumbnail) {
201             return i18n("Shape");
202         }
203     }
204     return QVariant();
205 }
206 
dump() const207 void KPrShapeAnimations::dump() const
208 {
209     debugStageAnimation << "Share animations:";
210     foreach (KPrAnimationStep *step, m_shapeAnimations) {
211         debugStageAnimation << "  Step:";
212         for (int i=0; i < step->animationCount(); i++) {
213             QAbstractAnimation *animation = step->animationAt(i);
214             if (KPrAnimationSubStep *a = dynamic_cast<KPrAnimationSubStep*>(animation)) {
215                 debugStageAnimation << "    Substep" << a;
216                 for (int sub=0; sub < a->animationCount(); ++sub) {
217                     QAbstractAnimation *baseAnim = a->animationAt(sub);
218                     KPrShapeAnimation *anim = dynamic_cast<KPrShapeAnimation *>(baseAnim);
219                     if (anim) {
220                         debugStageAnimation << "      Animation" << anim << getAnimationName(anim);
221                     } else {
222                         debugStageAnimation << "      NOT a KPrShapeAnimation!" << anim;
223                     }
224                 }
225             } else {
226                 debugStageAnimation << "    NOT a KPrAnimationSubStep!" << animation;
227             }
228         }
229     }
230 }
231 
rowCount(const QModelIndex & parent) const232 int KPrShapeAnimations::rowCount(const QModelIndex &parent) const
233 {
234     if (parent.isValid()) {
235         return 0;
236     }
237     int rowCount = 0;
238     foreach (KPrAnimationStep *step, m_shapeAnimations) {
239         for (int i=0; i < step->animationCount(); i++) {
240             QAbstractAnimation *animation = step->animationAt(i);
241             if (KPrAnimationSubStep *a = dynamic_cast<KPrAnimationSubStep*>(animation)) {
242                 rowCount = rowCount + a->animationCount();
243             }
244         }
245     }
246     return rowCount;
247 
248 }
249 
columnCount(const QModelIndex & parent) const250 int KPrShapeAnimations::columnCount(const QModelIndex &parent) const
251 {
252     return parent.isValid() ? 0 : COLUMN_COUNT;
253 }
254 
setData(const QModelIndex & index,const QVariant & value,int role)255 bool KPrShapeAnimations::setData(const QModelIndex &index, const QVariant &value, int role)
256 {
257     if (!index.isValid() || index.column() < 0 ||
258             (index.column() > columnCount(QModelIndex()))) {
259         return false;
260     }
261     // Read Data
262     KPrShapeAnimation *thisAnimation = animationByRow(index.row());
263     if (!thisAnimation) {
264         return false;
265     }
266     if (role == Qt::EditRole) {
267         switch (index.column()) {
268             case Group:
269             case StepCount:
270             case TriggerEvent:
271             case Name:
272             case ShapeThumbnail:
273                 return false;
274             case AnimationIcon:
275                 return false;
276             case StartTime:
277                 setTimeRangeIncrementalChange(thisAnimation, value.toInt(), thisAnimation->globalDuration(), BeginTime);
278                 emit dataChanged(index, index);
279                 return true;
280             case Duration:
281                 setTimeRangeIncrementalChange(thisAnimation, thisAnimation->timeRange().first, value.toInt(), DurationTime);
282                 emit dataChanged(index, index);
283                 return true;
284             case AnimationClass:
285                 return false;
286             default:
287                 return false;
288 
289         }
290     }
291     return false;
292 }
293 
init(const QList<KPrAnimationStep * > & animations)294 void KPrShapeAnimations::init(const QList<KPrAnimationStep *> &animations)
295 {
296     m_shapeAnimations = animations;
297 }
298 
add(KPrShapeAnimation * animation)299 void KPrShapeAnimations::add(KPrShapeAnimation *animation)
300 {
301     // TODO: what is the purpose of this empty KPrAnimationStep?
302     if (m_shapeAnimations.isEmpty()) {
303         m_shapeAnimations.append(new KPrAnimationStep());
304     }
305     if (!animation->step()) {
306         KPrAnimationStep *newStep = new KPrAnimationStep();
307         animation->setStep(newStep);
308     }
309     if (!animation->subStep()) {
310         KPrAnimationSubStep *newSubStep = new KPrAnimationSubStep();
311         animation->setSubStep(newSubStep);
312     }
313     if (!m_shapeAnimations.contains(animation->step())) {
314         if ((animation->stepIndex() >= 0) && (animation->stepIndex() <= m_shapeAnimations.count())) {
315             m_shapeAnimations.insert(animation->stepIndex(), animation->step());
316         }
317         else {
318             m_shapeAnimations.append(animation->step());
319         }
320     }
321     if (!(animation->step()->indexOfAnimation(animation->subStep()) >= 0)) {
322         if ((animation->subStepIndex() >= 0) &&
323                 (animation->subStepIndex() <= animation->step()->animationCount())) {
324             animation->step()->insertAnimation(animation->subStepIndex(), animation->subStep());
325         }
326         else {
327             animation->step()->addAnimation(animation->subStep());
328         }
329     }
330 
331     if ((animation->animIndex() >= 0) &&
332             (animation->animIndex() <= animation->subStep()->animationCount())) {
333         animation->subStep()->insertAnimation(animation->animIndex(), animation);
334     }
335     else {
336         animation->subStep()->addAnimation(animation);
337     }
338 
339     //updateModel
340     QModelIndex index = indexByAnimation(animation);
341     beginInsertRows(QModelIndex(), index.row(), index.row());
342     endInsertRows();
343     return;
344 }
345 
remove(KPrShapeAnimation * animation)346 void KPrShapeAnimations::remove(KPrShapeAnimation *animation)
347 {
348     //updateModel
349     QModelIndex index = indexByAnimation(animation);
350     beginRemoveRows(QModelIndex(), index.row(), index.row());
351 
352     KPrAnimationStep *step = animation->step();
353     KPrAnimationSubStep *subStep = animation->subStep();
354     if (subStep->animationCount() <= 1) {
355         animation->setSubStepIndex(step->indexOfAnimation(subStep));
356         step->removeAnimation(subStep);
357         if (step->animationCount() <= 0) {
358             animation->setStepIndex(m_shapeAnimations.indexOf(step));
359             m_shapeAnimations.removeAll(step);
360         }
361     }
362     animation->setAnimIndex(subStep->indexOfAnimation(animation));
363     subStep->removeAnimation(animation);
364     endRemoveRows();
365 }
366 
insertStep(const int i,KPrAnimationStep * step)367 void KPrShapeAnimations::insertStep(const int i, KPrAnimationStep *step)
368 {
369     if (step) {
370         m_shapeAnimations.insert(i, step);
371     }
372 }
373 
removeStep(KPrAnimationStep * step)374 void KPrShapeAnimations::removeStep(KPrAnimationStep *step)
375 {
376     if (step) {
377         m_shapeAnimations.removeAll(step);
378     }
379 }
380 
swapSteps(int i,int j)381 void KPrShapeAnimations::swapSteps(int i, int j)
382 {
383     m_shapeAnimations.swap(i, j);
384     emit dataChanged(this->index(i,0), this->index(i, COLUMN_COUNT));
385     emit dataChanged(this->index(j,0), this->index(j, COLUMN_COUNT));
386 }
387 
swapAnimations(KPrShapeAnimation * oldAnimation,KPrShapeAnimation * newAnimation)388 void KPrShapeAnimations::swapAnimations(KPrShapeAnimation *oldAnimation, KPrShapeAnimation *newAnimation)
389 {
390     KPrAnimationStep *oldStep = oldAnimation->step();
391     KPrAnimationSubStep *oldSubStep = oldAnimation->subStep();
392     KPrAnimationSubStep *newSubStep = newAnimation->subStep();
393     int oldIndex = oldSubStep->indexOfAnimation(oldAnimation);
394     int newIndex = newSubStep->indexOfAnimation(newAnimation);
395     if (oldSubStep != newSubStep) {
396         oldSubStep->removeAnimation(oldAnimation);
397         newSubStep->removeAnimation(newAnimation);
398         oldSubStep->insertAnimation(oldIndex, newAnimation);
399         newSubStep->insertAnimation(newIndex, oldAnimation);
400     }
401     else {
402         if (oldIndex < newIndex) {
403             oldSubStep->removeAnimation(newAnimation);
404             oldSubStep->insertAnimation(oldIndex, newAnimation);
405         }
406         else {
407             oldSubStep->removeAnimation(oldAnimation);
408             oldSubStep->insertAnimation(newIndex, oldAnimation);
409         }
410     }
411 
412     oldAnimation->setStep(newAnimation->step());
413     oldAnimation->setSubStep(newSubStep);
414     newAnimation->setStep(oldStep);
415     newAnimation->setSubStep(oldSubStep);
416     QModelIndex indexOld = indexByAnimation(oldAnimation);
417     QModelIndex indexNew = indexByAnimation(newAnimation);
418     emit dataChanged(this->index(indexOld.row(), 0), this->index(indexOld.row(), COLUMN_COUNT));
419     emit dataChanged(this->index(indexNew.row(), 0), this->index(indexNew.row(), COLUMN_COUNT));
420 }
421 
replaceAnimation(KPrShapeAnimation * oldAnimation,KPrShapeAnimation * newAnimation)422 void KPrShapeAnimations::replaceAnimation(KPrShapeAnimation *oldAnimation, KPrShapeAnimation *newAnimation)
423 {
424     KPrAnimationSubStep *subStep = oldAnimation->subStep();
425     int currentAnimationIndex = subStep->indexOfAnimation(oldAnimation);
426     newAnimation->setStep(oldAnimation->step());
427     newAnimation->setSubStep(oldAnimation->subStep());
428     newAnimation->setTextBlockUserData(oldAnimation->textBlockUserData());
429     subStep->insertAnimation(currentAnimationIndex, newAnimation);
430     subStep->removeAnimation(oldAnimation);
431     QModelIndex indexModified = indexByAnimation(newAnimation);
432     emit dataChanged(this->index(indexModified.row(), 0), this->index(indexModified.row(), COLUMN_COUNT));
433 }
434 
steps() const435 QList<KPrAnimationStep *> KPrShapeAnimations::steps() const
436 {
437     return m_shapeAnimations;
438 }
439 
endTimeLineEdition()440 void KPrShapeAnimations::endTimeLineEdition()
441 {
442     if (!m_firstEdition && m_currentEditedAnimation && (m_oldBegin != INVALID) && (m_oldDuration != INVALID)) {
443         int begin = m_currentEditedAnimation->timeRange().first;
444         int duration = m_currentEditedAnimation->globalDuration();
445         if ((begin != m_oldBegin) || (duration != m_oldDuration)) {
446             m_currentEditedAnimation->setBeginTime(m_oldBegin);
447             m_currentEditedAnimation->setGlobalDuration(m_oldDuration);
448             setTimeRange(m_currentEditedAnimation, begin, duration);
449             emit timeScaleModified();
450         }
451         m_oldBegin = INVALID;
452         m_oldDuration = INVALID;
453     }
454     m_firstEdition = true;
455     m_currentEditedAnimation = 0;
456 }
457 
setTimeRange(KPrShapeAnimation * item,const int begin,const int duration)458 void KPrShapeAnimations::setTimeRange(KPrShapeAnimation *item, const int begin, const int duration)
459 {
460     if (item && m_document) {
461         KPrEditAnimationTimeLineCommand *command = new KPrEditAnimationTimeLineCommand(item,
462                                                                                      begin, duration);
463         m_document->addCommand(command);
464         connect(item, SIGNAL(timeChanged(int,int)), this, SLOT(notifyAnimationEdited()));
465     }
466 }
467 
animationEnd(const QModelIndex & index) const468 int KPrShapeAnimations::animationEnd(const QModelIndex &index) const
469 {
470     if (index.isValid()) {
471         KPrShapeAnimation *previousAnimation = animationByRow(index.row());
472         KPrShapeAnimation::NodeType previousNodeType =
473                 static_cast<KPrShapeAnimation::NodeType>(data(this->index(index.row(),
474                                                                            KPrShapeAnimations::NodeType)).toInt());
475         if (previousNodeType == KPrShapeAnimation::OnClick) {
476             return previousAnimation->timeRange().second;
477         }
478         if (previousNodeType == KPrShapeAnimation::WithPrevious) {
479             return previousAnimation->timeRange().second +
480                     animationStart(this->index(index.row() - 1, index.column(), QModelIndex()));
481         }
482         else if (previousNodeType == KPrShapeAnimation::AfterPrevious) {
483             return previousAnimation->timeRange().second +
484                     animationEnd(this->index(index.row() - 1, index.column(), QModelIndex()));
485         }
486     }
487     return 0;
488 }
489 
animationStart(const QModelIndex & index) const490 int KPrShapeAnimations::animationStart(const QModelIndex &index) const
491 {
492     if (index.isValid()) {
493         KPrShapeAnimation *previousAnimation = animationByRow(index.row());
494         KPrShapeAnimation::NodeType previousNodeType =
495                 static_cast<KPrShapeAnimation::NodeType>(data(this->index(index.row(),
496                                                                            KPrShapeAnimations::NodeType)).toInt());
497         if (previousNodeType == KPrShapeAnimation::OnClick) {
498             return previousAnimation->timeRange().first;
499         }
500         if (previousNodeType == KPrShapeAnimation::WithPrevious) {
501             return animationStart(this->index(index.row() - 1, index.column(), QModelIndex()));
502         }
503         else if (previousNodeType == KPrShapeAnimation::AfterPrevious) {
504             return animationEnd(this->index(index.row() - 1, index.column(), QModelIndex()));
505         }
506     }
507     return 0;
508 }
509 
replaceAnimation(const QModelIndex & index,KPrShapeAnimation * newAnimation)510 QModelIndex KPrShapeAnimations::replaceAnimation(const QModelIndex &index, KPrShapeAnimation *newAnimation)
511 {
512     if (!index.isValid() || !m_document) {
513         return QModelIndex();
514     }
515     KPrShapeAnimation *oldAnimation = animationByRow(index.row());
516     Q_ASSERT(oldAnimation);
517     KPrReplaceAnimationCommand *cmd = new KPrReplaceAnimationCommand(m_document, oldAnimation, newAnimation);
518     m_document->addCommand(cmd);
519     return index;
520 }
521 
setTriggerEvent(const QModelIndex & index,const KPrShapeAnimation::NodeType type)522 bool KPrShapeAnimations::setTriggerEvent(const QModelIndex &index, const KPrShapeAnimation::NodeType type)
523 {
524     KPrShapeAnimation *animation = animationByRow(index.row());
525     if (animation) {
526         KPrShapeAnimation::NodeType currentType =
527                 static_cast<KPrShapeAnimation::NodeType>(data(this->index(index.row(),
528                                                                            KPrShapeAnimations::NodeType)).toInt());
529         if (currentType == KPrShapeAnimation::AfterPrevious) {
530             if (type == KPrShapeAnimation::WithPrevious) {
531                 Q_ASSERT(index.row() > 0);
532             }
533         }
534         else if (currentType == KPrShapeAnimation::OnClick) {
535              if (index.row() < 1) {
536                  // Resync trigger event edit widget
537                  emit layoutChanged();
538                  return false;
539              }
540         }
541         if (type != currentType) {
542             return createTriggerEventEditCmd(animation, currentType, type);
543         }
544     }
545     return false;
546 }
547 
setNodeType(KPrShapeAnimation * animation,const KPrShapeAnimation::NodeType type)548 bool KPrShapeAnimations::setNodeType(KPrShapeAnimation *animation, const KPrShapeAnimation::NodeType type)
549 {
550     resyncStepsWithAnimations();
551     if (animation) {
552         QModelIndex index = indexByAnimation(animation);
553         if (!index.isValid()) {
554             return false;
555         }
556         QList<KPrShapeAnimation *> movedChildren = QList<KPrShapeAnimation *>();
557         QList<KPrAnimationSubStep *>movedSubSteps = QList<KPrAnimationSubStep *>();
558         KPrAnimationSubStep *newSubStep = 0;
559         KPrAnimationStep *newStep = 0;
560         KPrShapeAnimation::NodeType currentType =
561                 static_cast<KPrShapeAnimation::NodeType>(data(this->index(index.row(),
562                                                                            KPrShapeAnimations::NodeType)).toInt());
563         if (currentType == KPrShapeAnimation::AfterPrevious) {
564             // After Previous to With Previous
565             if (type == KPrShapeAnimation::WithPrevious) {
566                 //use previous animation to reparent current animation
567                 Q_ASSERT(index.row() > 0);
568                 KPrShapeAnimation *previousAnimation = animationByRow(index.row() - 1);
569                 newSubStep = previousAnimation->subStep();
570                 movedChildren = getWithPreviousSiblings(animation);
571             }
572 
573             // After Previous to On Click
574             else if (type == KPrShapeAnimation::OnClick) {
575                  // Get index of current substep
576                  int currentSubStepIndex = animation->step()->indexOfAnimation(animation->subStep());
577                  int subStepCount = animation->step()->animationCount();
578 
579                  //Create new step to reparent current item and all following items.
580                  newStep = new KPrAnimationStep();
581 
582                  // Add step after original one
583                  int currentStepIndex = m_shapeAnimations.indexOf(animation->step());
584                  insertStep(currentStepIndex + 1, newStep);
585 
586                  //reparent children
587                  if (currentSubStepIndex < subStepCount - 1) {
588                      movedSubSteps = getSubSteps(currentSubStepIndex + 1, subStepCount, animation->step());
589                  }
590             }
591             else {
592                 return false;
593             }
594         }
595         else if (currentType == KPrShapeAnimation::WithPrevious) {
596            // With Previous to After Previous
597            if (type == KPrShapeAnimation::AfterPrevious) {
598                // Get index of current substep
599                int currentSubStepIndex = animation->step()->indexOfAnimation(animation->subStep());
600                //Create new substep to reparent current item and all following items.
601                newSubStep = new KPrAnimationSubStep();
602 
603                // Add substep after original one
604                animation->step()->insertAnimation(currentSubStepIndex + 1, newSubStep);
605 
606                //reparent children
607                movedChildren = getWithPreviousSiblings(animation);
608            }
609            // With Previous to On Click
610            else if (type == KPrShapeAnimation::OnClick) {
611                 // Get index of current substep
612                 int currentSubStepIndex = animation->step()->indexOfAnimation(animation->subStep());
613                 int subStepCount = animation->step()->animationCount();
614 
615                 //Create new step to reparent current item and all following items.
616                 newStep = new KPrAnimationStep();
617 
618                 //Create new substep to reparent current item and all following items.
619                 newSubStep = new KPrAnimationSubStep();
620 
621                 // Add step after original one
622                 //insert new Step
623                 int currentStepIndex = m_shapeAnimations.indexOf(animation->step());
624                 insertStep(currentStepIndex + 1, newStep);
625 
626                 //reparent children
627                 if (currentSubStepIndex < subStepCount - 1) {
628                     movedSubSteps = getSubSteps(currentSubStepIndex + 1, subStepCount, animation->step());
629                 }
630                 movedChildren = getWithPreviousSiblings(animation);
631            }
632            else {
633                return false;
634            }
635         }
636         else if (currentType == KPrShapeAnimation::OnClick) {
637              if (index.row() < 1) {
638                  // Resync trigger event edit widget
639                  emit layoutChanged();
640                  return false;
641              }
642             // On click to With Previous
643             if (type == KPrShapeAnimation::WithPrevious) {
644                 // Get previous animation
645                 KPrShapeAnimation *previousAnimation = animationByRow(index.row() - 1);
646                 newStep = previousAnimation->step();
647                 newSubStep = previousAnimation->subStep();
648 
649                 movedChildren = getWithPreviousSiblings(animation);
650 
651                 int subStepCount = animation->step()->animationCount();
652                 int currentSubStepIndex = animation->step()->indexOfAnimation(animation->subStep());
653                 if (subStepCount > 1) {
654                     movedSubSteps = getSubSteps(currentSubStepIndex + 1, subStepCount, animation->step());
655                 }
656             }
657 
658             // On click to After Previous
659             else if (type == KPrShapeAnimation::AfterPrevious) {
660                  // Get previous animation
661                  KPrShapeAnimation *previousAnimation = animationByRow(index.row() - 1);
662                  newStep = previousAnimation->step();
663                  int subStepCount = animation->step()->animationCount();
664                  if (subStepCount > 1) {
665                      movedSubSteps = getSubSteps(1, subStepCount, animation->step());
666                  }
667             }
668             else {
669                 return false;
670             }
671         }
672         else {
673             return false;
674         }
675         KPrAnimationSubStep *oldSubStep = animation->subStep();
676         KPrAnimationStep *oldStep = animation->step();
677 
678         // if new subStep reparent main item and children
679         if (newSubStep) {
680             if (oldSubStep->indexOfAnimation(animation) >= 0) {
681                 newSubStep->addAnimation(oldSubStep->takeAnimation(oldSubStep->indexOfAnimation(animation)));
682             }
683             if (!movedChildren.isEmpty()) {
684                 foreach(KPrShapeAnimation *anim, movedChildren) {
685                     if ((oldSubStep->indexOfAnimation(anim) >= 0) && (oldSubStep->indexOfAnimation(anim) <
686                                                                       oldSubStep->animationCount())) {
687                         newSubStep->addAnimation(oldSubStep->takeAnimation(oldSubStep->indexOfAnimation(anim)));
688                     }
689                 }
690             }
691         }
692         // If newStep reparent subSteps and children
693         if (newStep) {
694             if (!newSubStep) {
695                 newSubStep = oldSubStep;
696             }
697             if (movedSubSteps.isEmpty()) {
698                 movedSubSteps.append(newSubStep);
699             }
700             else {
701                 movedSubSteps.insert(0, newSubStep);
702             }
703             foreach(KPrAnimationSubStep *subStep, movedSubSteps) {
704                 newStep->addAnimation(subStep);
705             }
706         }
707         // If old substep or step is empty remove from list;
708         if (oldSubStep->children().isEmpty()) {
709             oldSubStep->setParent(0);
710         }
711         if (oldStep->children().isEmpty()) {
712             removeStep(oldStep);
713         }
714 
715         if ((currentType == KPrShapeAnimation::OnClick) || (type == KPrShapeAnimation::OnClick)) {
716             notifyOnClickEventChanged();
717         }
718         notifyAnimationChanged(animation);
719         resyncStepsWithAnimations();
720         return true;
721     }
722     return false;
723 }
724 
recalculateStart(const QModelIndex & mIndex)725 void KPrShapeAnimations::recalculateStart(const QModelIndex &mIndex)
726 {
727     if (!mIndex.isValid() || mIndex.row() < 1) {
728         return;
729     }
730     KPrShapeAnimation *animation = animationByRow(mIndex.row());
731 
732     KPrShapeAnimation::NodeType type =
733             static_cast<KPrShapeAnimation::NodeType>(data(this->index(mIndex.row(),
734                                                                        KPrShapeAnimations::NodeType)).toInt());
735     if (type == KPrShapeAnimation::AfterPrevious) {
736         setTimeRange(animation, animationEnd(mIndex), animation->globalDuration());
737         setTriggerEvent(mIndex, KPrShapeAnimation::WithPrevious);
738     }
739     else if (type == KPrShapeAnimation::WithPrevious) {
740         recalculateStart(index(mIndex.row() - 1, 0));
741     }
742 }
743 
moveUp(const QModelIndex & index)744 QModelIndex KPrShapeAnimations::moveUp(const QModelIndex &index)
745 {
746     if (!index.isValid() || index.row() < 1) {
747         return QModelIndex();
748     }
749     return moveAnimation(index.row(), index.row() - 1);
750 }
751 
moveDown(const QModelIndex & index)752 QModelIndex KPrShapeAnimations::moveDown(const QModelIndex &index)
753 {
754     if (!index.isValid() || (index.row() >= (rowCount() - 1))) {
755         return QModelIndex();
756     }
757 
758     return moveAnimation(index.row(), index.row() + 1);
759 }
760 
moveAnimation(int oldRow,int newRow)761 QModelIndex KPrShapeAnimations::moveAnimation(int oldRow, int newRow)
762 {
763     Q_ASSERT(0 <= oldRow && oldRow < rowCount() &&
764              0 <= newRow && newRow < rowCount());
765     QModelIndex newIndex;
766     // swap items
767     KPrShapeAnimation *animationOld = animationByRow(oldRow);
768     KPrShapeAnimation *animationNew = animationByRow(newRow);
769     Q_ASSERT(animationOld);
770     Q_ASSERT(animationNew);
771     if (m_document) {
772         newIndex = index(newRow, 0);
773         KPrReorderAnimationCommand *cmd = new KPrReorderAnimationCommand(this, animationOld, animationNew);
774         m_document->addCommand(cmd);
775     }
776     return newIndex;
777 }
778 
removeAnimationByIndex(const QModelIndex & index)779 QModelIndex KPrShapeAnimations::removeAnimationByIndex(const QModelIndex &index)
780 {
781     if (!index.isValid()) {
782         return index;
783     }
784     KPrShapeAnimation *animation = animationByRow(index.row());
785     Q_ASSERT(animation);
786 
787     if (animation) {
788         Q_ASSERT(m_document);
789         KPrAnimationRemoveCommand *command = new KPrAnimationRemoveCommand(m_document, animation);
790         m_document->addCommand(command);
791     }
792     return QModelIndex();
793 }
794 
shapeByIndex(const QModelIndex & index) const795 KoShape *KPrShapeAnimations::shapeByIndex(const QModelIndex &index) const
796 {
797     if (index.isValid()) {
798         KPrShapeAnimation *animation = animationByRow(index.row());
799         if (animation) {
800             return animation->shape();
801         }
802     }
803     return 0;
804 }
805 
indexByShape(KoShape * shape) const806 QModelIndex KPrShapeAnimations::indexByShape(KoShape *shape) const
807 {
808     int rowCount = 0;
809     foreach (KPrAnimationStep *step, m_shapeAnimations) {
810         for (int i=0; i < step->animationCount(); i++) {
811             QAbstractAnimation *animation = step->animationAt(i);
812             if (KPrAnimationSubStep *a = dynamic_cast<KPrAnimationSubStep*>(animation)) {
813                 for (int j=0; j < a->animationCount(); j++) {
814                     QAbstractAnimation *shapeAnimation = a->animationAt(j);
815                     if (KPrShapeAnimation *b = dynamic_cast<KPrShapeAnimation*>(shapeAnimation)) {
816                         if ((b->presetClass() != KPrShapeAnimation::None) && (b->shape())) {
817                             if (b->shape() == shape)
818                                 return this->index(rowCount, 0);
819                         }
820                         rowCount++;
821                     }
822                 }
823             }
824         }
825     }
826     return QModelIndex();
827 }
828 
setBeginTime(const QModelIndex & index,const int begin)829 void KPrShapeAnimations::setBeginTime(const QModelIndex &index, const int begin)
830 {
831     if (!index.isValid()) {
832         return;
833     }
834     KPrShapeAnimation *item = animationByRow(index.row());
835     if (item) {
836         setTimeRange(item, begin, item->globalDuration());
837         emit dataChanged(index, index);
838     }
839 
840 }
841 
setDuration(const QModelIndex & index,const int duration)842 void KPrShapeAnimations::setDuration(const QModelIndex &index, const int duration)
843 {
844     if (!index.isValid()) {
845         return;
846     }
847     KPrShapeAnimation *item = animationByRow(index.row());
848     if (item) {
849         setTimeRange(item, item->timeRange().first, duration);
850         emit dataChanged(index, index);
851     }
852 }
853 
notifyAnimationEdited()854 void KPrShapeAnimations::notifyAnimationEdited()
855 {
856     if (KPrShapeAnimation *animation = qobject_cast<KPrShapeAnimation*>(sender())) {
857         QModelIndex index = indexByAnimation(animation);
858         if (index.isValid()) {
859             emit dataChanged(index, index);
860         }
861     }
862 }
863 
notifyAnimationChanged(KPrShapeAnimation * animation)864 void KPrShapeAnimations::notifyAnimationChanged(KPrShapeAnimation *animation)
865 {
866     QModelIndex index = indexByAnimation(animation);
867     if (index.isValid()) {
868         emit dataChanged(this->index(index.row(), 0), this->index(index.row(), COLUMN_COUNT));
869     }
870 }
871 
notifyOnClickEventChanged()872 void KPrShapeAnimations::notifyOnClickEventChanged()
873 {
874     emit onClickEventChanged();
875 }
876 
animationByRow(int row,int * pGroup,KPrShapeAnimation::NodeType * pNodeType) const877 KPrShapeAnimation *KPrShapeAnimations::animationByRow(int row, int *pGroup, KPrShapeAnimation::NodeType *pNodeType) const
878 {
879     int rowCount = 0;
880     int groupCount = 0;
881     KPrShapeAnimation::NodeType currentNodeType = KPrShapeAnimation::OnClick;
882     foreach (KPrAnimationStep *step, m_shapeAnimations) {
883         int stepChild = -1;
884         if (step->animationCount() > 0) {
885             currentNodeType = KPrShapeAnimation::OnClick;
886             ++groupCount;
887         }
888         for (int i=0; i < step->animationCount(); i++) {
889             QAbstractAnimation *animation = step->animationAt(i);
890             if (KPrAnimationSubStep *a = dynamic_cast<KPrAnimationSubStep*>(animation)) {
891                 int subStepChild = -1;
892                 if (stepChild != -1) {
893                     currentNodeType = KPrShapeAnimation::AfterPrevious;
894                 }
895                 if (rowCount + a->animationCount() < row) {
896                     rowCount = rowCount + a->animationCount();
897                     stepChild = stepChild + a->animationCount();
898                     subStepChild = subStepChild + a->animationCount();
899                     continue;
900                 }
901                 for (int j=0; j < a->animationCount(); j++) {
902                     QAbstractAnimation *shapeAnimation = a->animationAt(j);
903                     if (KPrShapeAnimation *b = dynamic_cast<KPrShapeAnimation*>(shapeAnimation)) {
904                         stepChild++;
905                         subStepChild++;
906                         if (subStepChild > 0) {
907                             currentNodeType = KPrShapeAnimation::WithPrevious;
908                         }
909                         if (rowCount == row) {
910                             if (pGroup) {
911                                 *pGroup = groupCount;
912                             }
913                             if (pNodeType) {
914                                 *pNodeType = currentNodeType;
915                             }
916                             return b;
917                         }
918                         rowCount++;
919                     }
920                 }
921             }
922         }
923     }
924     return 0;
925 }
926 
insertNewAnimation(KPrShapeAnimation * newAnimation,const QModelIndex & previousAnimation)927 void KPrShapeAnimations::insertNewAnimation(KPrShapeAnimation *newAnimation, const QModelIndex &previousAnimation)
928 {
929     Q_ASSERT(newAnimation);
930     // Create new Parent step and substep
931     KPrAnimationStep *newStep = new KPrAnimationStep();
932     KPrAnimationSubStep *newSubStep = new KPrAnimationSubStep();
933     int stepIndex = -1;
934     // insert step and substep
935     if (previousAnimation.isValid()) {
936         KPrShapeAnimation *previous = animationByRow(previousAnimation.row());
937         stepIndex = m_shapeAnimations.indexOf(previous->step()) + 1;
938     }
939     else if (m_shapeAnimations.count() < 1) {
940         stepIndex = -1;
941     }
942     else {
943         stepIndex = m_shapeAnimations.count();
944     }
945 
946     // Setup new Animation
947     newAnimation->setStepIndex(stepIndex);
948     newAnimation->setStep(newStep);
949     newAnimation->setSubStep(newSubStep);
950     newStep->addAnimation(newSubStep);
951     Q_ASSERT(m_document);
952     KPrAnimationCreateCommand *command = new KPrAnimationCreateCommand(m_document, newAnimation);
953     m_document->addCommand(command);
954 }
955 
getAnimationName(KPrShapeAnimation * animation,bool omitSubType) const956 QString KPrShapeAnimations::getAnimationName(KPrShapeAnimation *animation, bool omitSubType) const
957 {
958     if (animation) {
959         QStringList descriptionList = animation->id().split(QLatin1Char('-'));
960         if (descriptionList.count() > 2) {
961             descriptionList.removeFirst();
962             descriptionList.removeFirst();
963         }
964         if (!omitSubType && (!animation->presetSubType().isEmpty())) {
965             descriptionList.append(animation->presetSubType());
966         }
967         return descriptionList.join(QChar::fromLatin1(' '));
968     }
969     return QString();
970 }
971 
getAnimationShapeThumbnail(KPrShapeAnimation * animation) const972 QPixmap KPrShapeAnimations::getAnimationShapeThumbnail(KPrShapeAnimation *animation) const
973 {
974     if (animation) {
975         //TODO: Draw image file to load when shape thumbnail can't be created
976        QPixmap thumbnail = koIcon("calligrastage").pixmap(KIconLoader::SizeMedium, KIconLoader::SizeMedium);
977 
978         if (
979             thumbnail.convertFromImage(createThumbnail(animation->shape(),
980                                                        QSize(KIconLoader::SizeMedium, KIconLoader::SizeMedium)))
981         ) {
982             thumbnail.scaled(QSize(KIconLoader::SizeMedium, KIconLoader::SizeMedium), Qt::KeepAspectRatio);
983         }
984         return thumbnail;
985     }
986     return QPixmap();
987 }
988 
getAnimationIcon(KPrShapeAnimation * animation) const989 QPixmap KPrShapeAnimations::getAnimationIcon(KPrShapeAnimation *animation) const
990 {
991     if (!animation) {
992         return QPixmap();
993     }
994     QString name = getAnimationName(animation, true);
995     // Return Path Motion Animation icon
996     if (animation->presetClass() == KPrShapeAnimation::MotionPath) {
997         QPainterPath m_path;
998         for (int i = 0; i < animation->animationCount(); i++) {
999             if (KPrAnimateMotion *motion = dynamic_cast<KPrAnimateMotion *>(animation->animationAt(i))) {
1000                 m_path = motion->pathOutline();
1001                 break;
1002             }
1003         }
1004         if (!m_path.isEmpty()) {
1005             const int margin = 8;
1006             const int width = 4;
1007             QImage thumb(QSize(KIconLoader::SizeHuge, KIconLoader::SizeHuge), QImage::Format_RGB32);
1008             // fill backgroung
1009             thumb.fill(QColor(Qt::white).rgb());
1010             QRect imageRect = thumb.rect();
1011             // adjust to left space for margins
1012             imageRect.adjust(margin, margin, -margin, -margin);
1013             //Center path
1014             m_path.translate(-m_path.boundingRect().x() + margin, -m_path.boundingRect().y() + margin);
1015             QTransform transform;
1016             transform.scale(thumb.width() / (m_path.boundingRect().width() + 2 * margin),
1017                             thumb.height() / (m_path.boundingRect().height() + 2 * margin));
1018             m_path = m_path * transform;
1019             QPainter painter(&thumb);
1020             painter.setRenderHints(QPainter::Antialiasing);
1021             painter.setPen(QPen(QColor(0, 100, 224), width, Qt::SolidLine,
1022                                 Qt::FlatCap, Qt::MiterJoin));
1023             painter.drawPath(m_path);
1024             QPixmap iconPixmap;
1025             if (iconPixmap.convertFromImage(thumb)) {
1026                 return iconPixmap;
1027             }
1028         }
1029     }
1030     // Return animation icon
1031     else if (!name.isEmpty()) {
1032         name = name.append("_animation");
1033         name.replace(QLatin1Char(' '), QLatin1Char('_'));
1034         QString path = KIconLoader::global()->iconPath(name, KIconLoader::Toolbar, true);
1035         if (!path.isNull()) {
1036             return QIcon::fromTheme(name).pixmap(KIconLoader::SizeHuge, KIconLoader::SizeHuge);
1037         }
1038     }
1039     return koIcon("unrecognized_animation").pixmap(KIconLoader::SizeMedium, KIconLoader::SizeMedium);
1040 }
1041 
createThumbnail(KoShape * shape,const QSize & thumbSize) const1042 QImage KPrShapeAnimations::createThumbnail(KoShape *shape, const QSize &thumbSize) const
1043 {
1044     KoShapePainter painter;
1045     QList<KoShape*> shapes;
1046     shapes.append(shape);
1047     KoShapeContainer * container = dynamic_cast<KoShapeContainer*>(shape);
1048     if (container) {
1049         shapes.append(container->shapes());
1050     }
1051 
1052     painter.setShapes(shapes);
1053 
1054     QImage thumb(thumbSize, QImage::Format_RGB32);
1055     // draw the background of the thumbnail
1056     thumb.fill(QColor(Qt::white).rgb());
1057 
1058     QRect imageRect = thumb.rect();
1059     // use 2 pixel border around the content
1060     imageRect.adjust(2, 2, -2, -2);
1061 
1062     QPainter p(&thumb);
1063     painter.paint(p, imageRect, painter.contentRect());
1064 
1065     return thumb;
1066 }
1067 
setTimeRangeIncrementalChange(KPrShapeAnimation * item,const int begin,const int duration,TimeUpdated updatedTimes)1068 void KPrShapeAnimations::setTimeRangeIncrementalChange(KPrShapeAnimation *item, const int begin, const int duration, TimeUpdated updatedTimes)
1069 {
1070     if (m_firstEdition) {
1071         m_oldBegin = item->timeRange().first;
1072         m_oldDuration = item->timeRange().second;
1073         m_currentEditedAnimation = item;
1074         m_firstEdition = false;
1075     }
1076     if (item == m_currentEditedAnimation) {
1077         if ((updatedTimes == BothTimes) || (updatedTimes == BeginTime)) {
1078             item->setBeginTime(begin);
1079         }
1080         if ((updatedTimes == BothTimes) || (updatedTimes == DurationTime)) {
1081             item->setGlobalDuration(duration);
1082         }
1083     }
1084     else {
1085         endTimeLineEdition();
1086     }
1087 }
1088 
indexByAnimation(KPrShapeAnimation * animation) const1089 QModelIndex KPrShapeAnimations::indexByAnimation(KPrShapeAnimation *animation) const
1090 {
1091     int rowCount = 0;
1092     foreach (KPrAnimationStep *step, m_shapeAnimations) {
1093         for (int i=0; i < step->animationCount(); i++) {
1094             QAbstractAnimation *subStep = step->animationAt(i);
1095             if (KPrAnimationSubStep *a = dynamic_cast<KPrAnimationSubStep*>(subStep)) {
1096                 for (int j=0; j < a->animationCount(); j++) {
1097                     QAbstractAnimation *shapeAnimation = a->animationAt(j);
1098                     if (KPrShapeAnimation *b = dynamic_cast<KPrShapeAnimation*>(shapeAnimation)) {
1099                         if ((b->presetClass() != KPrShapeAnimation::None) && (b->shape())) {
1100                             if (b == animation) {
1101                                 return this->index(rowCount, 0, QModelIndex());
1102                             }
1103                             rowCount++;
1104                         }
1105                     }
1106                 }
1107             }
1108         }
1109     }
1110     return QModelIndex();
1111 }
1112 
resyncStepsWithAnimations()1113 void KPrShapeAnimations::resyncStepsWithAnimations()
1114 {
1115     int row = -1;
1116     foreach (KPrAnimationStep *step, m_shapeAnimations) {
1117         row++;
1118         for (int i=0; i < step->animationCount(); i++) {
1119             QAbstractAnimation *subStep = step->animationAt(i);
1120             if (KPrAnimationSubStep *a = dynamic_cast<KPrAnimationSubStep*>(subStep)) {
1121                 for (int j=0; j < a->animationCount(); j++) {
1122                     QAbstractAnimation *shapeAnimation = a->animationAt(j);
1123                     if (KPrShapeAnimation *b = dynamic_cast<KPrShapeAnimation*>(shapeAnimation)) {
1124                         if ((b->presetClass() != KPrShapeAnimation::None) && (b->shape())) {
1125                             b->setStep(step);
1126                             b->setSubStep(a);
1127                         }
1128                     }
1129                 }
1130             }
1131         }
1132     }
1133 }
1134 
triggerEventByIndex(const QModelIndex & index)1135 KPrShapeAnimation::NodeType KPrShapeAnimations::triggerEventByIndex(const QModelIndex &index)
1136 {
1137     Q_ASSERT(index.isValid());
1138     KPrShapeAnimation::NodeType nodeType = KPrShapeAnimation::OnClick;
1139     animationByRow(index.row(), 0, &nodeType);
1140     return nodeType;
1141 }
1142 
getWithPreviousSiblings(KPrShapeAnimation * animation) const1143 QList<KPrShapeAnimation *> KPrShapeAnimations::getWithPreviousSiblings(KPrShapeAnimation *animation) const
1144 {
1145     bool startAdding = false;
1146     QList<KPrShapeAnimation *> siblings = QList<KPrShapeAnimation *>();
1147 
1148     if (KPrAnimationSubStep *a = animation->subStep()) {
1149         for (int j=0; j < a->animationCount(); j++) {
1150             QAbstractAnimation *shapeAnimation = a->animationAt(j);
1151             if (KPrShapeAnimation *b = dynamic_cast<KPrShapeAnimation*>(shapeAnimation)) {
1152                 if ((b->presetClass() != KPrShapeAnimation::None) && (b->shape())) {
1153                     if (startAdding) {
1154                         siblings.append(b);
1155                     }
1156                     if (b == animation) {
1157                         startAdding = true;
1158                     }
1159                 }
1160             }
1161         }
1162     }
1163     return siblings;
1164 }
1165 
getSubSteps(int start,int end,KPrAnimationStep * step) const1166 QList<KPrAnimationSubStep *> KPrShapeAnimations::getSubSteps(int start, int end, KPrAnimationStep *step) const
1167 {
1168     QList<KPrAnimationSubStep *>movedSubSteps = QList<KPrAnimationSubStep *>();
1169     for (int i = start; i < end; i++) {
1170         if (KPrAnimationSubStep *substep = dynamic_cast<KPrAnimationSubStep *>(step->animationAt(i))) {
1171            movedSubSteps.append(substep);
1172         }
1173     }
1174     return movedSubSteps;
1175 }
1176 
createTriggerEventEditCmd(KPrShapeAnimation * animation,KPrShapeAnimation::NodeType oldType,KPrShapeAnimation::NodeType newType)1177 bool KPrShapeAnimations::createTriggerEventEditCmd(KPrShapeAnimation *animation, KPrShapeAnimation::NodeType oldType, KPrShapeAnimation::NodeType newType)
1178 {
1179     KPrAnimationEditNodeTypeCommand *command =new KPrAnimationEditNodeTypeCommand(animation, oldType, newType, this);
1180     if (m_document) {
1181         m_document->addCommand(command);
1182         emit timeScaleModified();
1183         return true;
1184     }
1185     return false;
1186 }
1187