File indexing completed on 2024-05-19 16:09:36
0001 /* This file is part of the KDE project 0002 * Copyright ( C ) 2007 Thorsten Zachmann <zachmann@kde.org> 0003 * Copyright ( C ) 2010 Benjamin Port <port.benjamin@gmail.com> 0004 * Copyright ( C ) 2012 Paul Mendez <paulestebanms@gmail.com> 0005 * 0006 * This library is free software; you can redistribute it and/or 0007 * modify it under the terms of the GNU Library General Public 0008 * License as published by the Free Software Foundation; either 0009 * version 2 of the License, or ( at your option ) any later version. 0010 * 0011 * This library is distributed in the hope that it will be useful, 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 * Library General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU Library General Public License 0017 * along with this library; see the file COPYING.LIB. If not, write to 0018 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0019 * Boston, MA 02110-1301, USA. 0020 */ 0021 0022 #include "KPrShapeAnimations.h" 0023 0024 //Qt Headers 0025 #include <QList> 0026 #include <QSet> 0027 #include <QPainter> 0028 #include <QPainterPath> 0029 0030 //Stage Headers 0031 #include "KPrDocument.h" 0032 #include "animations/KPrAnimationSubStep.h" 0033 #include "animations/KPrAnimateMotion.h" 0034 #include "commands/KPrAnimationRemoveCommand.h" 0035 #include "commands/KPrReorderAnimationCommand.h" 0036 #include <commands/KPrEditAnimationTimeLineCommand.h> 0037 #include <commands/KPrAnimationEditNodeTypeCommand.h> 0038 #include <commands/KPrReplaceAnimationCommand.h> 0039 #include <commands/KPrAnimationCreateCommand.h> 0040 #include "StageDebug.h" 0041 0042 //Calligra Headers 0043 #include <KoShape.h> 0044 #include <KoShapePainter.h> 0045 #include <KoShapeContainer.h> 0046 #include <KoPathShape.h> 0047 #include <KoIcon.h> 0048 0049 //KF5 Headers 0050 #include <kiconloader.h> 0051 #include <klocalizedstring.h> 0052 0053 const int COLUMN_COUNT = 10; 0054 const int INVALID = -1; 0055 0056 KPrShapeAnimations::KPrShapeAnimations(KPrDocument *document, QObject *parent) 0057 :QAbstractTableModel(parent) 0058 , m_currentEditedAnimation(0) 0059 , m_firstEdition(true) 0060 , m_oldBegin(INVALID) 0061 , m_oldDuration(INVALID) 0062 , m_document(document) 0063 { 0064 } 0065 0066 KPrShapeAnimations::~KPrShapeAnimations() 0067 { 0068 } 0069 0070 Qt::ItemFlags KPrShapeAnimations::flags(const QModelIndex &index) const 0071 { 0072 Qt::ItemFlags theFlags = QAbstractTableModel::flags(index); 0073 if (index.isValid()) { 0074 theFlags |= Qt::ItemIsSelectable|Qt::ItemIsEnabled; 0075 //if (index.column() == Name) 0076 //theFlags |= Qt::ItemIsEditable;//| 0077 //Qt::ItemIsDragEnabled|Qt::ItemIsDropEnabled; 0078 } 0079 return theFlags; 0080 } 0081 0082 QVariant KPrShapeAnimations::data(const QModelIndex &index, int role) const 0083 { 0084 if (!index.isValid() || index.column() < 0 || 0085 index.column() >= COLUMN_COUNT || index.row() < 0 0086 || index.row() >= rowCount(QModelIndex())) { 0087 return QVariant(); 0088 } 0089 0090 // Read Data 0091 KPrShapeAnimation::NodeType nodeType; 0092 int currentGroup = -1; 0093 KPrShapeAnimation *thisAnimation = animationByRow(index.row(), ¤tGroup, &nodeType); 0094 if (!thisAnimation) { 0095 return QVariant(); 0096 } 0097 0098 if (role == Qt::DisplayRole || role == Qt::EditRole) { 0099 switch (index.column()) { 0100 case Group: return currentGroup; 0101 case StepCount: 0102 if (nodeType == KPrShapeAnimation::OnClick) { 0103 return currentGroup; 0104 } 0105 else { 0106 return QVariant(); 0107 } 0108 case TriggerEvent: return QVariant(); 0109 case Name: return getAnimationName(thisAnimation); 0110 case ShapeThumbnail: return QVariant(); 0111 case AnimationIcon: return QVariant(); 0112 case StartTime: return thisAnimation->timeRange().first; 0113 case Duration: return thisAnimation->globalDuration(); 0114 case AnimationClass: return thisAnimation->presetClass(); 0115 case NodeType: return nodeType; 0116 default: Q_ASSERT(false); 0117 } 0118 } 0119 if (role == Qt::TextAlignmentRole) { 0120 if (index.column() == Name) { 0121 return static_cast<int>(Qt::AlignLeft|Qt::AlignVCenter); 0122 } 0123 return static_cast<int>(Qt::AlignCenter); 0124 } 0125 if (role == Qt::DecorationRole) { 0126 switch (index.column()) { 0127 case Group: return QVariant(); 0128 case StepCount: return QVariant(); 0129 case TriggerEvent: 0130 if (nodeType == KPrShapeAnimation::OnClick) 0131 return koIcon("onclick"); 0132 if (nodeType == KPrShapeAnimation::AfterPrevious) 0133 return koIcon("after_previous"); 0134 if (nodeType == KPrShapeAnimation::WithPrevious) 0135 return koIcon("with_previous"); 0136 return QVariant(); 0137 case Name: return QVariant(); 0138 case ShapeThumbnail: return getAnimationShapeThumbnail(thisAnimation); 0139 case AnimationIcon: return getAnimationIcon(thisAnimation); 0140 case StartTime: return QVariant(); 0141 case Duration: return QVariant(); 0142 case AnimationClass: return QVariant(); 0143 case NodeType: return QVariant(); 0144 default: Q_ASSERT(false); 0145 } 0146 } 0147 if (role == Qt::SizeHintRole) { 0148 switch (index.column()) { 0149 case Group: 0150 case StepCount: return QVariant(); 0151 case TriggerEvent: return QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall); 0152 case Name: return QVariant(); 0153 case ShapeThumbnail: return QSize(KIconLoader::SizeMedium, KIconLoader::SizeMedium); 0154 case AnimationIcon: 0155 case StartTime: 0156 case Duration: 0157 case AnimationClass: return QVariant(); 0158 case NodeType: return QVariant(); 0159 default: Q_ASSERT(false); 0160 } 0161 } 0162 if (role == Qt::ToolTipRole) { 0163 switch (index.column()) { 0164 case Group: 0165 case StepCount: return QVariant(); 0166 case TriggerEvent:/// emitted if an item time range has changed (return the index of the item changed) 0167 if (nodeType == KPrShapeAnimation::OnClick) 0168 return i18n("start on mouse click"); 0169 if (nodeType == KPrShapeAnimation::AfterPrevious) 0170 return i18n("start after previous animation"); 0171 if (nodeType == KPrShapeAnimation::WithPrevious) 0172 return i18n("start with previous animation"); 0173 return QVariant(); 0174 case Name: return QVariant(); 0175 case ShapeThumbnail: return thisAnimation->shape()->name(); 0176 case AnimationIcon: return getAnimationName(thisAnimation); 0177 case StartTime: { 0178 const float startDelay = thisAnimation->timeRange().first / 1000.0; 0179 const float duration = thisAnimation->globalDuration() / 1000.0; 0180 return i18n("Start after %1 seconds. Duration of %2 seconds.", startDelay, duration); 0181 } 0182 case Duration: return QVariant(); 0183 case AnimationClass: return thisAnimation->presetClassText(); 0184 case NodeType: return QVariant(); 0185 default: Q_ASSERT(false); 0186 } 0187 } 0188 return QVariant(); 0189 } 0190 0191 QVariant KPrShapeAnimations::headerData(int section, Qt::Orientation orientation, int role) const 0192 { 0193 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { 0194 if (section == Name) { 0195 return i18n("Animation"); 0196 } 0197 else if (section == TriggerEvent) { 0198 return QString(); 0199 } 0200 else if (section == ShapeThumbnail) { 0201 return i18n("Shape"); 0202 } 0203 } 0204 return QVariant(); 0205 } 0206 0207 void KPrShapeAnimations::dump() const 0208 { 0209 debugStageAnimation << "Share animations:"; 0210 foreach (KPrAnimationStep *step, m_shapeAnimations) { 0211 debugStageAnimation << " Step:"; 0212 for (int i=0; i < step->animationCount(); i++) { 0213 QAbstractAnimation *animation = step->animationAt(i); 0214 if (KPrAnimationSubStep *a = dynamic_cast<KPrAnimationSubStep*>(animation)) { 0215 debugStageAnimation << " Substep" << a; 0216 for (int sub=0; sub < a->animationCount(); ++sub) { 0217 QAbstractAnimation *baseAnim = a->animationAt(sub); 0218 KPrShapeAnimation *anim = dynamic_cast<KPrShapeAnimation *>(baseAnim); 0219 if (anim) { 0220 debugStageAnimation << " Animation" << anim << getAnimationName(anim); 0221 } else { 0222 debugStageAnimation << " NOT a KPrShapeAnimation!" << anim; 0223 } 0224 } 0225 } else { 0226 debugStageAnimation << " NOT a KPrAnimationSubStep!" << animation; 0227 } 0228 } 0229 } 0230 } 0231 0232 int KPrShapeAnimations::rowCount(const QModelIndex &parent) const 0233 { 0234 if (parent.isValid()) { 0235 return 0; 0236 } 0237 int rowCount = 0; 0238 foreach (KPrAnimationStep *step, m_shapeAnimations) { 0239 for (int i=0; i < step->animationCount(); i++) { 0240 QAbstractAnimation *animation = step->animationAt(i); 0241 if (KPrAnimationSubStep *a = dynamic_cast<KPrAnimationSubStep*>(animation)) { 0242 rowCount = rowCount + a->animationCount(); 0243 } 0244 } 0245 } 0246 return rowCount; 0247 0248 } 0249 0250 int KPrShapeAnimations::columnCount(const QModelIndex &parent) const 0251 { 0252 return parent.isValid() ? 0 : COLUMN_COUNT; 0253 } 0254 0255 bool KPrShapeAnimations::setData(const QModelIndex &index, const QVariant &value, int role) 0256 { 0257 if (!index.isValid() || index.column() < 0 || 0258 (index.column() > columnCount(QModelIndex()))) { 0259 return false; 0260 } 0261 // Read Data 0262 KPrShapeAnimation *thisAnimation = animationByRow(index.row()); 0263 if (!thisAnimation) { 0264 return false; 0265 } 0266 if (role == Qt::EditRole) { 0267 switch (index.column()) { 0268 case Group: 0269 case StepCount: 0270 case TriggerEvent: 0271 case Name: 0272 case ShapeThumbnail: 0273 return false; 0274 case AnimationIcon: 0275 return false; 0276 case StartTime: 0277 setTimeRangeIncrementalChange(thisAnimation, value.toInt(), thisAnimation->globalDuration(), BeginTime); 0278 emit dataChanged(index, index); 0279 return true; 0280 case Duration: 0281 setTimeRangeIncrementalChange(thisAnimation, thisAnimation->timeRange().first, value.toInt(), DurationTime); 0282 emit dataChanged(index, index); 0283 return true; 0284 case AnimationClass: 0285 return false; 0286 default: 0287 return false; 0288 0289 } 0290 } 0291 return false; 0292 } 0293 0294 void KPrShapeAnimations::init(const QList<KPrAnimationStep *> &animations) 0295 { 0296 m_shapeAnimations = animations; 0297 } 0298 0299 void KPrShapeAnimations::add(KPrShapeAnimation *animation) 0300 { 0301 // TODO: what is the purpose of this empty KPrAnimationStep? 0302 if (m_shapeAnimations.isEmpty()) { 0303 m_shapeAnimations.append(new KPrAnimationStep()); 0304 } 0305 if (!animation->step()) { 0306 KPrAnimationStep *newStep = new KPrAnimationStep(); 0307 animation->setStep(newStep); 0308 } 0309 if (!animation->subStep()) { 0310 KPrAnimationSubStep *newSubStep = new KPrAnimationSubStep(); 0311 animation->setSubStep(newSubStep); 0312 } 0313 if (!m_shapeAnimations.contains(animation->step())) { 0314 if ((animation->stepIndex() >= 0) && (animation->stepIndex() <= m_shapeAnimations.count())) { 0315 m_shapeAnimations.insert(animation->stepIndex(), animation->step()); 0316 } 0317 else { 0318 m_shapeAnimations.append(animation->step()); 0319 } 0320 } 0321 if (!(animation->step()->indexOfAnimation(animation->subStep()) >= 0)) { 0322 if ((animation->subStepIndex() >= 0) && 0323 (animation->subStepIndex() <= animation->step()->animationCount())) { 0324 animation->step()->insertAnimation(animation->subStepIndex(), animation->subStep()); 0325 } 0326 else { 0327 animation->step()->addAnimation(animation->subStep()); 0328 } 0329 } 0330 0331 if ((animation->animIndex() >= 0) && 0332 (animation->animIndex() <= animation->subStep()->animationCount())) { 0333 animation->subStep()->insertAnimation(animation->animIndex(), animation); 0334 } 0335 else { 0336 animation->subStep()->addAnimation(animation); 0337 } 0338 0339 //updateModel 0340 QModelIndex index = indexByAnimation(animation); 0341 beginInsertRows(QModelIndex(), index.row(), index.row()); 0342 endInsertRows(); 0343 return; 0344 } 0345 0346 void KPrShapeAnimations::remove(KPrShapeAnimation *animation) 0347 { 0348 //updateModel 0349 QModelIndex index = indexByAnimation(animation); 0350 beginRemoveRows(QModelIndex(), index.row(), index.row()); 0351 0352 KPrAnimationStep *step = animation->step(); 0353 KPrAnimationSubStep *subStep = animation->subStep(); 0354 if (subStep->animationCount() <= 1) { 0355 animation->setSubStepIndex(step->indexOfAnimation(subStep)); 0356 step->removeAnimation(subStep); 0357 if (step->animationCount() <= 0) { 0358 animation->setStepIndex(m_shapeAnimations.indexOf(step)); 0359 m_shapeAnimations.removeAll(step); 0360 } 0361 } 0362 animation->setAnimIndex(subStep->indexOfAnimation(animation)); 0363 subStep->removeAnimation(animation); 0364 endRemoveRows(); 0365 } 0366 0367 void KPrShapeAnimations::insertStep(const int i, KPrAnimationStep *step) 0368 { 0369 if (step) { 0370 m_shapeAnimations.insert(i, step); 0371 } 0372 } 0373 0374 void KPrShapeAnimations::removeStep(KPrAnimationStep *step) 0375 { 0376 if (step) { 0377 m_shapeAnimations.removeAll(step); 0378 } 0379 } 0380 0381 void KPrShapeAnimations::swapSteps(int i, int j) 0382 { 0383 m_shapeAnimations.swap(i, j); 0384 emit dataChanged(this->index(i,0), this->index(i, COLUMN_COUNT)); 0385 emit dataChanged(this->index(j,0), this->index(j, COLUMN_COUNT)); 0386 } 0387 0388 void KPrShapeAnimations::swapAnimations(KPrShapeAnimation *oldAnimation, KPrShapeAnimation *newAnimation) 0389 { 0390 KPrAnimationStep *oldStep = oldAnimation->step(); 0391 KPrAnimationSubStep *oldSubStep = oldAnimation->subStep(); 0392 KPrAnimationSubStep *newSubStep = newAnimation->subStep(); 0393 int oldIndex = oldSubStep->indexOfAnimation(oldAnimation); 0394 int newIndex = newSubStep->indexOfAnimation(newAnimation); 0395 if (oldSubStep != newSubStep) { 0396 oldSubStep->removeAnimation(oldAnimation); 0397 newSubStep->removeAnimation(newAnimation); 0398 oldSubStep->insertAnimation(oldIndex, newAnimation); 0399 newSubStep->insertAnimation(newIndex, oldAnimation); 0400 } 0401 else { 0402 if (oldIndex < newIndex) { 0403 oldSubStep->removeAnimation(newAnimation); 0404 oldSubStep->insertAnimation(oldIndex, newAnimation); 0405 } 0406 else { 0407 oldSubStep->removeAnimation(oldAnimation); 0408 oldSubStep->insertAnimation(newIndex, oldAnimation); 0409 } 0410 } 0411 0412 oldAnimation->setStep(newAnimation->step()); 0413 oldAnimation->setSubStep(newSubStep); 0414 newAnimation->setStep(oldStep); 0415 newAnimation->setSubStep(oldSubStep); 0416 QModelIndex indexOld = indexByAnimation(oldAnimation); 0417 QModelIndex indexNew = indexByAnimation(newAnimation); 0418 emit dataChanged(this->index(indexOld.row(), 0), this->index(indexOld.row(), COLUMN_COUNT)); 0419 emit dataChanged(this->index(indexNew.row(), 0), this->index(indexNew.row(), COLUMN_COUNT)); 0420 } 0421 0422 void KPrShapeAnimations::replaceAnimation(KPrShapeAnimation *oldAnimation, KPrShapeAnimation *newAnimation) 0423 { 0424 KPrAnimationSubStep *subStep = oldAnimation->subStep(); 0425 int currentAnimationIndex = subStep->indexOfAnimation(oldAnimation); 0426 newAnimation->setStep(oldAnimation->step()); 0427 newAnimation->setSubStep(oldAnimation->subStep()); 0428 newAnimation->setTextBlockUserData(oldAnimation->textBlockUserData()); 0429 subStep->insertAnimation(currentAnimationIndex, newAnimation); 0430 subStep->removeAnimation(oldAnimation); 0431 QModelIndex indexModified = indexByAnimation(newAnimation); 0432 emit dataChanged(this->index(indexModified.row(), 0), this->index(indexModified.row(), COLUMN_COUNT)); 0433 } 0434 0435 QList<KPrAnimationStep *> KPrShapeAnimations::steps() const 0436 { 0437 return m_shapeAnimations; 0438 } 0439 0440 void KPrShapeAnimations::endTimeLineEdition() 0441 { 0442 if (!m_firstEdition && m_currentEditedAnimation && (m_oldBegin != INVALID) && (m_oldDuration != INVALID)) { 0443 int begin = m_currentEditedAnimation->timeRange().first; 0444 int duration = m_currentEditedAnimation->globalDuration(); 0445 if ((begin != m_oldBegin) || (duration != m_oldDuration)) { 0446 m_currentEditedAnimation->setBeginTime(m_oldBegin); 0447 m_currentEditedAnimation->setGlobalDuration(m_oldDuration); 0448 setTimeRange(m_currentEditedAnimation, begin, duration); 0449 emit timeScaleModified(); 0450 } 0451 m_oldBegin = INVALID; 0452 m_oldDuration = INVALID; 0453 } 0454 m_firstEdition = true; 0455 m_currentEditedAnimation = 0; 0456 } 0457 0458 void KPrShapeAnimations::setTimeRange(KPrShapeAnimation *item, const int begin, const int duration) 0459 { 0460 if (item && m_document) { 0461 KPrEditAnimationTimeLineCommand *command = new KPrEditAnimationTimeLineCommand(item, 0462 begin, duration); 0463 m_document->addCommand(command); 0464 connect(item, SIGNAL(timeChanged(int,int)), this, SLOT(notifyAnimationEdited())); 0465 } 0466 } 0467 0468 int KPrShapeAnimations::animationEnd(const QModelIndex &index) const 0469 { 0470 if (index.isValid()) { 0471 KPrShapeAnimation *previousAnimation = animationByRow(index.row()); 0472 KPrShapeAnimation::NodeType previousNodeType = 0473 static_cast<KPrShapeAnimation::NodeType>(data(this->index(index.row(), 0474 KPrShapeAnimations::NodeType)).toInt()); 0475 if (previousNodeType == KPrShapeAnimation::OnClick) { 0476 return previousAnimation->timeRange().second; 0477 } 0478 if (previousNodeType == KPrShapeAnimation::WithPrevious) { 0479 return previousAnimation->timeRange().second + 0480 animationStart(this->index(index.row() - 1, index.column(), QModelIndex())); 0481 } 0482 else if (previousNodeType == KPrShapeAnimation::AfterPrevious) { 0483 return previousAnimation->timeRange().second + 0484 animationEnd(this->index(index.row() - 1, index.column(), QModelIndex())); 0485 } 0486 } 0487 return 0; 0488 } 0489 0490 int KPrShapeAnimations::animationStart(const QModelIndex &index) const 0491 { 0492 if (index.isValid()) { 0493 KPrShapeAnimation *previousAnimation = animationByRow(index.row()); 0494 KPrShapeAnimation::NodeType previousNodeType = 0495 static_cast<KPrShapeAnimation::NodeType>(data(this->index(index.row(), 0496 KPrShapeAnimations::NodeType)).toInt()); 0497 if (previousNodeType == KPrShapeAnimation::OnClick) { 0498 return previousAnimation->timeRange().first; 0499 } 0500 if (previousNodeType == KPrShapeAnimation::WithPrevious) { 0501 return animationStart(this->index(index.row() - 1, index.column(), QModelIndex())); 0502 } 0503 else if (previousNodeType == KPrShapeAnimation::AfterPrevious) { 0504 return animationEnd(this->index(index.row() - 1, index.column(), QModelIndex())); 0505 } 0506 } 0507 return 0; 0508 } 0509 0510 QModelIndex KPrShapeAnimations::replaceAnimation(const QModelIndex &index, KPrShapeAnimation *newAnimation) 0511 { 0512 if (!index.isValid() || !m_document) { 0513 return QModelIndex(); 0514 } 0515 KPrShapeAnimation *oldAnimation = animationByRow(index.row()); 0516 Q_ASSERT(oldAnimation); 0517 KPrReplaceAnimationCommand *cmd = new KPrReplaceAnimationCommand(m_document, oldAnimation, newAnimation); 0518 m_document->addCommand(cmd); 0519 return index; 0520 } 0521 0522 bool KPrShapeAnimations::setTriggerEvent(const QModelIndex &index, const KPrShapeAnimation::NodeType type) 0523 { 0524 KPrShapeAnimation *animation = animationByRow(index.row()); 0525 if (animation) { 0526 KPrShapeAnimation::NodeType currentType = 0527 static_cast<KPrShapeAnimation::NodeType>(data(this->index(index.row(), 0528 KPrShapeAnimations::NodeType)).toInt()); 0529 if (currentType == KPrShapeAnimation::AfterPrevious) { 0530 if (type == KPrShapeAnimation::WithPrevious) { 0531 Q_ASSERT(index.row() > 0); 0532 } 0533 } 0534 else if (currentType == KPrShapeAnimation::OnClick) { 0535 if (index.row() < 1) { 0536 // Resync trigger event edit widget 0537 emit layoutChanged(); 0538 return false; 0539 } 0540 } 0541 if (type != currentType) { 0542 return createTriggerEventEditCmd(animation, currentType, type); 0543 } 0544 } 0545 return false; 0546 } 0547 0548 bool KPrShapeAnimations::setNodeType(KPrShapeAnimation *animation, const KPrShapeAnimation::NodeType type) 0549 { 0550 resyncStepsWithAnimations(); 0551 if (animation) { 0552 QModelIndex index = indexByAnimation(animation); 0553 if (!index.isValid()) { 0554 return false; 0555 } 0556 QList<KPrShapeAnimation *> movedChildren = QList<KPrShapeAnimation *>(); 0557 QList<KPrAnimationSubStep *>movedSubSteps = QList<KPrAnimationSubStep *>(); 0558 KPrAnimationSubStep *newSubStep = 0; 0559 KPrAnimationStep *newStep = 0; 0560 KPrShapeAnimation::NodeType currentType = 0561 static_cast<KPrShapeAnimation::NodeType>(data(this->index(index.row(), 0562 KPrShapeAnimations::NodeType)).toInt()); 0563 if (currentType == KPrShapeAnimation::AfterPrevious) { 0564 // After Previous to With Previous 0565 if (type == KPrShapeAnimation::WithPrevious) { 0566 //use previous animation to reparent current animation 0567 Q_ASSERT(index.row() > 0); 0568 KPrShapeAnimation *previousAnimation = animationByRow(index.row() - 1); 0569 newSubStep = previousAnimation->subStep(); 0570 movedChildren = getWithPreviousSiblings(animation); 0571 } 0572 0573 // After Previous to On Click 0574 else if (type == KPrShapeAnimation::OnClick) { 0575 // Get index of current substep 0576 int currentSubStepIndex = animation->step()->indexOfAnimation(animation->subStep()); 0577 int subStepCount = animation->step()->animationCount(); 0578 0579 //Create new step to reparent current item and all following items. 0580 newStep = new KPrAnimationStep(); 0581 0582 // Add step after original one 0583 int currentStepIndex = m_shapeAnimations.indexOf(animation->step()); 0584 insertStep(currentStepIndex + 1, newStep); 0585 0586 //reparent children 0587 if (currentSubStepIndex < subStepCount - 1) { 0588 movedSubSteps = getSubSteps(currentSubStepIndex + 1, subStepCount, animation->step()); 0589 } 0590 } 0591 else { 0592 return false; 0593 } 0594 } 0595 else if (currentType == KPrShapeAnimation::WithPrevious) { 0596 // With Previous to After Previous 0597 if (type == KPrShapeAnimation::AfterPrevious) { 0598 // Get index of current substep 0599 int currentSubStepIndex = animation->step()->indexOfAnimation(animation->subStep()); 0600 //Create new substep to reparent current item and all following items. 0601 newSubStep = new KPrAnimationSubStep(); 0602 0603 // Add substep after original one 0604 animation->step()->insertAnimation(currentSubStepIndex + 1, newSubStep); 0605 0606 //reparent children 0607 movedChildren = getWithPreviousSiblings(animation); 0608 } 0609 // With Previous to On Click 0610 else if (type == KPrShapeAnimation::OnClick) { 0611 // Get index of current substep 0612 int currentSubStepIndex = animation->step()->indexOfAnimation(animation->subStep()); 0613 int subStepCount = animation->step()->animationCount(); 0614 0615 //Create new step to reparent current item and all following items. 0616 newStep = new KPrAnimationStep(); 0617 0618 //Create new substep to reparent current item and all following items. 0619 newSubStep = new KPrAnimationSubStep(); 0620 0621 // Add step after original one 0622 //insert new Step 0623 int currentStepIndex = m_shapeAnimations.indexOf(animation->step()); 0624 insertStep(currentStepIndex + 1, newStep); 0625 0626 //reparent children 0627 if (currentSubStepIndex < subStepCount - 1) { 0628 movedSubSteps = getSubSteps(currentSubStepIndex + 1, subStepCount, animation->step()); 0629 } 0630 movedChildren = getWithPreviousSiblings(animation); 0631 } 0632 else { 0633 return false; 0634 } 0635 } 0636 else if (currentType == KPrShapeAnimation::OnClick) { 0637 if (index.row() < 1) { 0638 // Resync trigger event edit widget 0639 emit layoutChanged(); 0640 return false; 0641 } 0642 // On click to With Previous 0643 if (type == KPrShapeAnimation::WithPrevious) { 0644 // Get previous animation 0645 KPrShapeAnimation *previousAnimation = animationByRow(index.row() - 1); 0646 newStep = previousAnimation->step(); 0647 newSubStep = previousAnimation->subStep(); 0648 0649 movedChildren = getWithPreviousSiblings(animation); 0650 0651 int subStepCount = animation->step()->animationCount(); 0652 int currentSubStepIndex = animation->step()->indexOfAnimation(animation->subStep()); 0653 if (subStepCount > 1) { 0654 movedSubSteps = getSubSteps(currentSubStepIndex + 1, subStepCount, animation->step()); 0655 } 0656 } 0657 0658 // On click to After Previous 0659 else if (type == KPrShapeAnimation::AfterPrevious) { 0660 // Get previous animation 0661 KPrShapeAnimation *previousAnimation = animationByRow(index.row() - 1); 0662 newStep = previousAnimation->step(); 0663 int subStepCount = animation->step()->animationCount(); 0664 if (subStepCount > 1) { 0665 movedSubSteps = getSubSteps(1, subStepCount, animation->step()); 0666 } 0667 } 0668 else { 0669 return false; 0670 } 0671 } 0672 else { 0673 return false; 0674 } 0675 KPrAnimationSubStep *oldSubStep = animation->subStep(); 0676 KPrAnimationStep *oldStep = animation->step(); 0677 0678 // if new subStep reparent main item and children 0679 if (newSubStep) { 0680 if (oldSubStep->indexOfAnimation(animation) >= 0) { 0681 newSubStep->addAnimation(oldSubStep->takeAnimation(oldSubStep->indexOfAnimation(animation))); 0682 } 0683 if (!movedChildren.isEmpty()) { 0684 foreach(KPrShapeAnimation *anim, movedChildren) { 0685 if ((oldSubStep->indexOfAnimation(anim) >= 0) && (oldSubStep->indexOfAnimation(anim) < 0686 oldSubStep->animationCount())) { 0687 newSubStep->addAnimation(oldSubStep->takeAnimation(oldSubStep->indexOfAnimation(anim))); 0688 } 0689 } 0690 } 0691 } 0692 // If newStep reparent subSteps and children 0693 if (newStep) { 0694 if (!newSubStep) { 0695 newSubStep = oldSubStep; 0696 } 0697 if (movedSubSteps.isEmpty()) { 0698 movedSubSteps.append(newSubStep); 0699 } 0700 else { 0701 movedSubSteps.insert(0, newSubStep); 0702 } 0703 foreach(KPrAnimationSubStep *subStep, movedSubSteps) { 0704 newStep->addAnimation(subStep); 0705 } 0706 } 0707 // If old substep or step is empty remove from list; 0708 if (oldSubStep->children().isEmpty()) { 0709 oldSubStep->setParent(0); 0710 } 0711 if (oldStep->children().isEmpty()) { 0712 removeStep(oldStep); 0713 } 0714 0715 if ((currentType == KPrShapeAnimation::OnClick) || (type == KPrShapeAnimation::OnClick)) { 0716 notifyOnClickEventChanged(); 0717 } 0718 notifyAnimationChanged(animation); 0719 resyncStepsWithAnimations(); 0720 return true; 0721 } 0722 return false; 0723 } 0724 0725 void KPrShapeAnimations::recalculateStart(const QModelIndex &mIndex) 0726 { 0727 if (!mIndex.isValid() || mIndex.row() < 1) { 0728 return; 0729 } 0730 KPrShapeAnimation *animation = animationByRow(mIndex.row()); 0731 0732 KPrShapeAnimation::NodeType type = 0733 static_cast<KPrShapeAnimation::NodeType>(data(this->index(mIndex.row(), 0734 KPrShapeAnimations::NodeType)).toInt()); 0735 if (type == KPrShapeAnimation::AfterPrevious) { 0736 setTimeRange(animation, animationEnd(mIndex), animation->globalDuration()); 0737 setTriggerEvent(mIndex, KPrShapeAnimation::WithPrevious); 0738 } 0739 else if (type == KPrShapeAnimation::WithPrevious) { 0740 recalculateStart(index(mIndex.row() - 1, 0)); 0741 } 0742 } 0743 0744 QModelIndex KPrShapeAnimations::moveUp(const QModelIndex &index) 0745 { 0746 if (!index.isValid() || index.row() < 1) { 0747 return QModelIndex(); 0748 } 0749 return moveAnimation(index.row(), index.row() - 1); 0750 } 0751 0752 QModelIndex KPrShapeAnimations::moveDown(const QModelIndex &index) 0753 { 0754 if (!index.isValid() || (index.row() >= (rowCount() - 1))) { 0755 return QModelIndex(); 0756 } 0757 0758 return moveAnimation(index.row(), index.row() + 1); 0759 } 0760 0761 QModelIndex KPrShapeAnimations::moveAnimation(int oldRow, int newRow) 0762 { 0763 Q_ASSERT(0 <= oldRow && oldRow < rowCount() && 0764 0 <= newRow && newRow < rowCount()); 0765 QModelIndex newIndex; 0766 // swap items 0767 KPrShapeAnimation *animationOld = animationByRow(oldRow); 0768 KPrShapeAnimation *animationNew = animationByRow(newRow); 0769 Q_ASSERT(animationOld); 0770 Q_ASSERT(animationNew); 0771 if (m_document) { 0772 newIndex = index(newRow, 0); 0773 KPrReorderAnimationCommand *cmd = new KPrReorderAnimationCommand(this, animationOld, animationNew); 0774 m_document->addCommand(cmd); 0775 } 0776 return newIndex; 0777 } 0778 0779 QModelIndex KPrShapeAnimations::removeAnimationByIndex(const QModelIndex &index) 0780 { 0781 if (!index.isValid()) { 0782 return index; 0783 } 0784 KPrShapeAnimation *animation = animationByRow(index.row()); 0785 Q_ASSERT(animation); 0786 0787 if (animation) { 0788 Q_ASSERT(m_document); 0789 KPrAnimationRemoveCommand *command = new KPrAnimationRemoveCommand(m_document, animation); 0790 m_document->addCommand(command); 0791 } 0792 return QModelIndex(); 0793 } 0794 0795 KoShape *KPrShapeAnimations::shapeByIndex(const QModelIndex &index) const 0796 { 0797 if (index.isValid()) { 0798 KPrShapeAnimation *animation = animationByRow(index.row()); 0799 if (animation) { 0800 return animation->shape(); 0801 } 0802 } 0803 return 0; 0804 } 0805 0806 QModelIndex KPrShapeAnimations::indexByShape(KoShape *shape) const 0807 { 0808 int rowCount = 0; 0809 foreach (KPrAnimationStep *step, m_shapeAnimations) { 0810 for (int i=0; i < step->animationCount(); i++) { 0811 QAbstractAnimation *animation = step->animationAt(i); 0812 if (KPrAnimationSubStep *a = dynamic_cast<KPrAnimationSubStep*>(animation)) { 0813 for (int j=0; j < a->animationCount(); j++) { 0814 QAbstractAnimation *shapeAnimation = a->animationAt(j); 0815 if (KPrShapeAnimation *b = dynamic_cast<KPrShapeAnimation*>(shapeAnimation)) { 0816 if ((b->presetClass() != KPrShapeAnimation::None) && (b->shape())) { 0817 if (b->shape() == shape) 0818 return this->index(rowCount, 0); 0819 } 0820 rowCount++; 0821 } 0822 } 0823 } 0824 } 0825 } 0826 return QModelIndex(); 0827 } 0828 0829 void KPrShapeAnimations::setBeginTime(const QModelIndex &index, const int begin) 0830 { 0831 if (!index.isValid()) { 0832 return; 0833 } 0834 KPrShapeAnimation *item = animationByRow(index.row()); 0835 if (item) { 0836 setTimeRange(item, begin, item->globalDuration()); 0837 emit dataChanged(index, index); 0838 } 0839 0840 } 0841 0842 void KPrShapeAnimations::setDuration(const QModelIndex &index, const int duration) 0843 { 0844 if (!index.isValid()) { 0845 return; 0846 } 0847 KPrShapeAnimation *item = animationByRow(index.row()); 0848 if (item) { 0849 setTimeRange(item, item->timeRange().first, duration); 0850 emit dataChanged(index, index); 0851 } 0852 } 0853 0854 void KPrShapeAnimations::notifyAnimationEdited() 0855 { 0856 if (KPrShapeAnimation *animation = qobject_cast<KPrShapeAnimation*>(sender())) { 0857 QModelIndex index = indexByAnimation(animation); 0858 if (index.isValid()) { 0859 emit dataChanged(index, index); 0860 } 0861 } 0862 } 0863 0864 void KPrShapeAnimations::notifyAnimationChanged(KPrShapeAnimation *animation) 0865 { 0866 QModelIndex index = indexByAnimation(animation); 0867 if (index.isValid()) { 0868 emit dataChanged(this->index(index.row(), 0), this->index(index.row(), COLUMN_COUNT)); 0869 } 0870 } 0871 0872 void KPrShapeAnimations::notifyOnClickEventChanged() 0873 { 0874 emit onClickEventChanged(); 0875 } 0876 0877 KPrShapeAnimation *KPrShapeAnimations::animationByRow(int row, int *pGroup, KPrShapeAnimation::NodeType *pNodeType) const 0878 { 0879 int rowCount = 0; 0880 int groupCount = 0; 0881 KPrShapeAnimation::NodeType currentNodeType = KPrShapeAnimation::OnClick; 0882 foreach (KPrAnimationStep *step, m_shapeAnimations) { 0883 int stepChild = -1; 0884 if (step->animationCount() > 0) { 0885 currentNodeType = KPrShapeAnimation::OnClick; 0886 ++groupCount; 0887 } 0888 for (int i=0; i < step->animationCount(); i++) { 0889 QAbstractAnimation *animation = step->animationAt(i); 0890 if (KPrAnimationSubStep *a = dynamic_cast<KPrAnimationSubStep*>(animation)) { 0891 int subStepChild = -1; 0892 if (stepChild != -1) { 0893 currentNodeType = KPrShapeAnimation::AfterPrevious; 0894 } 0895 if (rowCount + a->animationCount() < row) { 0896 rowCount = rowCount + a->animationCount(); 0897 stepChild = stepChild + a->animationCount(); 0898 subStepChild = subStepChild + a->animationCount(); 0899 continue; 0900 } 0901 for (int j=0; j < a->animationCount(); j++) { 0902 QAbstractAnimation *shapeAnimation = a->animationAt(j); 0903 if (KPrShapeAnimation *b = dynamic_cast<KPrShapeAnimation*>(shapeAnimation)) { 0904 stepChild++; 0905 subStepChild++; 0906 if (subStepChild > 0) { 0907 currentNodeType = KPrShapeAnimation::WithPrevious; 0908 } 0909 if (rowCount == row) { 0910 if (pGroup) { 0911 *pGroup = groupCount; 0912 } 0913 if (pNodeType) { 0914 *pNodeType = currentNodeType; 0915 } 0916 return b; 0917 } 0918 rowCount++; 0919 } 0920 } 0921 } 0922 } 0923 } 0924 return 0; 0925 } 0926 0927 void KPrShapeAnimations::insertNewAnimation(KPrShapeAnimation *newAnimation, const QModelIndex &previousAnimation) 0928 { 0929 Q_ASSERT(newAnimation); 0930 // Create new Parent step and substep 0931 KPrAnimationStep *newStep = new KPrAnimationStep(); 0932 KPrAnimationSubStep *newSubStep = new KPrAnimationSubStep(); 0933 int stepIndex = -1; 0934 // insert step and substep 0935 if (previousAnimation.isValid()) { 0936 KPrShapeAnimation *previous = animationByRow(previousAnimation.row()); 0937 stepIndex = m_shapeAnimations.indexOf(previous->step()) + 1; 0938 } 0939 else if (m_shapeAnimations.count() < 1) { 0940 stepIndex = -1; 0941 } 0942 else { 0943 stepIndex = m_shapeAnimations.count(); 0944 } 0945 0946 // Setup new Animation 0947 newAnimation->setStepIndex(stepIndex); 0948 newAnimation->setStep(newStep); 0949 newAnimation->setSubStep(newSubStep); 0950 newStep->addAnimation(newSubStep); 0951 Q_ASSERT(m_document); 0952 KPrAnimationCreateCommand *command = new KPrAnimationCreateCommand(m_document, newAnimation); 0953 m_document->addCommand(command); 0954 } 0955 0956 QString KPrShapeAnimations::getAnimationName(KPrShapeAnimation *animation, bool omitSubType) const 0957 { 0958 if (animation) { 0959 QStringList descriptionList = animation->id().split(QLatin1Char('-')); 0960 if (descriptionList.count() > 2) { 0961 descriptionList.removeFirst(); 0962 descriptionList.removeFirst(); 0963 } 0964 if (!omitSubType && (!animation->presetSubType().isEmpty())) { 0965 descriptionList.append(animation->presetSubType()); 0966 } 0967 return descriptionList.join(QChar::fromLatin1(' ')); 0968 } 0969 return QString(); 0970 } 0971 0972 QPixmap KPrShapeAnimations::getAnimationShapeThumbnail(KPrShapeAnimation *animation) const 0973 { 0974 if (animation) { 0975 //TODO: Draw image file to load when shape thumbnail can't be created 0976 QPixmap thumbnail = koIcon("calligrastage").pixmap(KIconLoader::SizeMedium, KIconLoader::SizeMedium); 0977 0978 if ( 0979 thumbnail.convertFromImage(createThumbnail(animation->shape(), 0980 QSize(KIconLoader::SizeMedium, KIconLoader::SizeMedium))) 0981 ) { 0982 thumbnail.scaled(QSize(KIconLoader::SizeMedium, KIconLoader::SizeMedium), Qt::KeepAspectRatio); 0983 } 0984 return thumbnail; 0985 } 0986 return QPixmap(); 0987 } 0988 0989 QPixmap KPrShapeAnimations::getAnimationIcon(KPrShapeAnimation *animation) const 0990 { 0991 if (!animation) { 0992 return QPixmap(); 0993 } 0994 QString name = getAnimationName(animation, true); 0995 // Return Path Motion Animation icon 0996 if (animation->presetClass() == KPrShapeAnimation::MotionPath) { 0997 QPainterPath m_path; 0998 for (int i = 0; i < animation->animationCount(); i++) { 0999 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 1042 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 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 1089 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 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 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 1143 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 1166 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 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 }