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(), ¤tGroup, &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