diff --git a/src/effects/effectstack/model/effectstackmodel.cpp b/src/effects/effectstack/model/effectstackmodel.cpp
index d03067f07..2f1504911 100644
--- a/src/effects/effectstack/model/effectstackmodel.cpp
+++ b/src/effects/effectstack/model/effectstackmodel.cpp
@@ -1,716 +1,727 @@
/***************************************************************************
* Copyright (C) 2017 by Nicolas Carion *
* This file is part of Kdenlive. See www.kdenlive.org. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) version 3 or any later version accepted by the *
* membership of KDE e.V. (or its successor approved by the membership *
* of KDE e.V.), which shall act as a proxy defined in Section 14 of *
* version 3 of the license. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
***************************************************************************/
#include "effectstackmodel.hpp"
#include "assets/keyframes/model/keyframemodellist.hpp"
#include "core.h"
#include "doc/docundostack.hpp"
#include "effectgroupmodel.hpp"
#include "effectitemmodel.hpp"
#include "effects/effectsrepository.hpp"
#include "macros.hpp"
#include
#include
#include
EffectStackModel::EffectStackModel(std::weak_ptr service, ObjectId ownerId, std::weak_ptr undo_stack)
: AbstractTreeModel()
, m_effectStackEnabled(true)
, m_ownerId(ownerId)
, m_undoStack(undo_stack)
, m_loadingExisting(false)
{
m_services.emplace_back(std::move(service));
}
std::shared_ptr EffectStackModel::construct(std::weak_ptr service, ObjectId ownerId, std::weak_ptr undo_stack)
{
std::shared_ptr self(new EffectStackModel(std::move(service), ownerId, undo_stack));
self->rootItem = EffectGroupModel::construct(QStringLiteral("root"), self, true);
return self;
}
void EffectStackModel::resetService(std::weak_ptr service)
{
m_services.clear();
m_services.emplace_back(std::move(service));
// replant all effects in new service
for (int i = 0; i < rootItem->childCount(); ++i) {
for (const auto &s : m_services) {
std::static_pointer_cast(rootItem->child(i))->plant(s);
}
}
}
void EffectStackModel::addService(std::weak_ptr service)
{
m_services.emplace_back(std::move(service));
for (int i = 0; i < rootItem->childCount(); ++i) {
std::static_pointer_cast(rootItem->child(i))->plant(m_services.back());
}
}
void EffectStackModel::removeService(std::shared_ptr service)
{
std::vector to_delete;
for (int i = int(m_services.size()) - 1; i >= 0; --i) {
if (service.get() == m_services[uint(i)].lock().get()) {
to_delete.push_back(i);
}
}
for (int i : to_delete) {
m_services.erase(m_services.begin() + i);
}
}
void EffectStackModel::removeEffect(std::shared_ptr effect)
{
Q_ASSERT(m_allItems.count(effect->getId()) > 0);
int parentId = -1;
if (auto ptr = effect->parentItem().lock()) parentId = ptr->getId();
int current = 0;
bool currentChanged = false;
for (const auto &service : m_services) {
if (auto srv = service.lock()) {
current = srv->get_int("kdenlive:activeeffect");
if (current >= rootItem->childCount() - 1) {
currentChanged = true;
srv->set("kdenlive:activeeffect", --current);
}
}
}
int currentRow = effect->row();
Fun undo = addItem_lambda(effect, parentId);
if (currentRow != rowCount() - 1) {
Fun move = moveItem_lambda(effect->getId(), currentRow, true);
PUSH_LAMBDA(move, undo);
}
Fun redo = removeItem_lambda(effect->getId());
bool res = redo();
if (res) {
int inFades = int(fadeIns.size());
int outFades = int(fadeOuts.size());
fadeIns.erase(effect->getId());
fadeOuts.erase(effect->getId());
inFades = int(fadeIns.size()) - inFades;
outFades = int(fadeOuts.size()) - outFades;
QString effectName = EffectsRepository::get()->getName(effect->getAssetId());
Fun update = [this, current, currentChanged, inFades, outFades]() {
// Required to build the effect view
if (currentChanged) {
if (current < 0 || rowCount() == 0) {
// Stack is now empty
emit dataChanged(QModelIndex(), QModelIndex(), QVector());
} else {
std::shared_ptr effectItem = std::static_pointer_cast(rootItem->child(current));
QModelIndex ix = getIndexFromItem(effectItem);
emit dataChanged(ix, ix, QVector());
}
}
// TODO: only update if effect is fade or keyframe
if (inFades < 0) {
pCore->updateItemModel(m_ownerId, QStringLiteral("fadein"));
} else if (outFades < 0) {
pCore->updateItemModel(m_ownerId, QStringLiteral("fadeout"));
}
pCore->updateItemKeyframes(m_ownerId);
return true;
};
update();
PUSH_LAMBDA(update, redo);
PUSH_LAMBDA(update, undo);
PUSH_UNDO(undo, redo, i18n("Delete effect %1", effectName));
}
}
void EffectStackModel::copyEffect(std::shared_ptr sourceItem, bool logUndo)
{
if (sourceItem->childCount() > 0) {
// TODO: group
return;
}
std::shared_ptr sourceEffect = std::static_pointer_cast(sourceItem);
const QString effectId = sourceEffect->getAssetId();
auto effect = EffectItemModel::construct(effectId, shared_from_this());
effect->setParameters(sourceEffect->getAllParameters());
effect->filter().set("in", sourceEffect->filter().get_int("in"));
effect->filter().set("out", sourceEffect->filter().get_int("out"));
Fun undo = removeItem_lambda(effect->getId());
// TODO the parent should probably not always be the root
Fun redo = addItem_lambda(effect, rootItem->getId());
connect(effect.get(), &AssetParameterModel::modelChanged, this, &EffectStackModel::modelChanged);
connect(effect.get(), &AssetParameterModel::replugEffect, this, &EffectStackModel::replugEffect, Qt::DirectConnection);
if (effectId == QLatin1String("fadein") || effectId == QLatin1String("fade_from_black")) {
fadeIns.insert(effect->getId());
} else if (effectId == QLatin1String("fadeout") || effectId == QLatin1String("fade_to_black")) {
fadeOuts.insert(effect->getId());
}
bool res = redo();
if (res && logUndo) {
QString effectName = EffectsRepository::get()->getName(effectId);
PUSH_UNDO(undo, redo, i18n("copy effect %1", effectName));
}
}
void EffectStackModel::appendEffect(const QString &effectId, bool makeCurrent)
{
auto effect = EffectItemModel::construct(effectId, shared_from_this());
Fun undo = removeItem_lambda(effect->getId());
// TODO the parent should probably not always be the root
Fun redo = addItem_lambda(effect, rootItem->getId());
connect(effect.get(), &AssetParameterModel::modelChanged, this, &EffectStackModel::modelChanged);
connect(effect.get(), &AssetParameterModel::replugEffect, this, &EffectStackModel::replugEffect, Qt::DirectConnection);
int currentActive = getActiveEffect();
if (makeCurrent) {
for (const auto &service : m_services) {
auto srvPtr = service.lock();
if (srvPtr) {
srvPtr->set("kdenlive:activeeffect", rowCount());
}
}
}
bool res = redo();
if (res) {
int inFades = 0;
int outFades = 0;
if (effectId == QLatin1String("fadein") || effectId == QLatin1String("fade_from_black")) {
fadeIns.insert(effect->getId());
inFades++;
} else if (effectId == QLatin1String("fadeout") || effectId == QLatin1String("fade_to_black")) {
fadeOuts.insert(effect->getId());
outFades++;
}
QString effectName = EffectsRepository::get()->getName(effectId);
Fun update = [this, inFades, outFades]() {
// TODO: only update if effect is fade or keyframe
if (inFades > 0) {
pCore->updateItemModel(m_ownerId, QStringLiteral("fadein"));
} else if (outFades > 0) {
pCore->updateItemModel(m_ownerId, QStringLiteral("fadeout"));
}
pCore->updateItemKeyframes(m_ownerId);
emit dataChanged(QModelIndex(), QModelIndex(), QVector());
return true;
};
update();
PUSH_LAMBDA(update, redo);
PUSH_LAMBDA(update, undo);
PUSH_UNDO(undo, redo, i18n("Add effect %1", effectName));
} else if (makeCurrent) {
for (const auto &service : m_services) {
auto srvPtr = service.lock();
if (srvPtr) {
srvPtr->set("kdenlive:activeeffect", currentActive);
}
}
}
}
bool EffectStackModel::adjustStackLength(bool adjustFromEnd, int oldIn, int oldDuration, int newIn, int duration, Fun &undo, Fun &redo, bool logUndo)
{
const int fadeInDuration = getFadePosition(true);
const int fadeOutDuration = getFadePosition(false);
- QList indexes;
int out = newIn + duration;
- for (int i = 0; i < rootItem->childCount(); ++i) {
- if (fadeInDuration > 0 && fadeIns.count(std::static_pointer_cast(rootItem->child(i))->getId()) > 0) {
- std::shared_ptr effect = std::static_pointer_cast(rootItem->child(i));
+ for (const auto &leaf : rootItem->getLeaves()) {
+ std::shared_ptr item = std::static_pointer_cast(leaf);
+ if (item->effectItemType() == EffectItemType::Group) {
+ // probably an empty group, ignore
+ continue;
+ }
+ std::shared_ptr effect = std::static_pointer_cast(leaf);
+ if (fadeInDuration > 0 && fadeIns.count(leaf->getId()) > 0) {
int oldEffectIn = qMax(0, effect->filter().get_in());
int oldEffectOut = effect->filter().get_out();
qDebug() << "--previous effect: " << oldEffectIn << "-" << oldEffectOut;
int effectDuration = qMin(effect->filter().get_length() - 1, duration);
- indexes << getIndexFromItem(effect);
if (!adjustFromEnd && (oldIn != newIn || duration != oldDuration)) {
// Clip start was resized, adjust effect in / out
Fun operation = [this, effect, newIn, effectDuration, logUndo]() {
effect->setParameter(QStringLiteral("in"), newIn, false);
effect->setParameter(QStringLiteral("out"), newIn + effectDuration, logUndo);
qDebug() << "--new effect: " << newIn << "-" << newIn + effectDuration;
return true;
};
- operation();
+ bool res = operation();
+ if (!res) {
+ return false;
+ }
Fun reverse = [this, effect, oldEffectIn, oldEffectOut, logUndo]() {
effect->setParameter(QStringLiteral("in"), oldEffectIn, false);
effect->setParameter(QStringLiteral("out"), oldEffectOut, logUndo);
return true;
};
PUSH_LAMBDA(operation, redo);
PUSH_LAMBDA(reverse, undo);
} else if (effectDuration < oldEffectOut - oldEffectIn || (logUndo && effect->filter().get_int("_refout") > 0)) {
// Clip length changed, shorter than effect length so resize
int referenceEffectOut = effect->filter().get_int("_refout");
if (referenceEffectOut <= 0) {
referenceEffectOut = oldEffectOut;
effect->filter().set("_refout", referenceEffectOut);
}
Fun operation = [this, effect, oldEffectIn, effectDuration, logUndo]() {
effect->setParameter(QStringLiteral("out"), oldEffectIn + effectDuration, logUndo);
return true;
};
- if (operation() && logUndo) {
+ bool res = operation();
+ if (!res) {
+ return false;
+ }
+ if (logUndo) {
Fun reverse = [this, effect, referenceEffectOut]() {
effect->setParameter(QStringLiteral("out"), referenceEffectOut, true);
effect->filter().set("_refout", (char *)nullptr);
return true;
};
PUSH_LAMBDA(operation, redo);
PUSH_LAMBDA(reverse, undo);
}
}
- } else if (fadeOutDuration > 0 && fadeOuts.count(std::static_pointer_cast(rootItem->child(i))->getId()) > 0) {
- std::shared_ptr effect = std::static_pointer_cast(rootItem->child(i));
+ } else if (fadeOutDuration > 0 && fadeOuts.count(leaf->getId()) > 0) {
int effectDuration = qMin(fadeOutDuration, duration);
int newFadeIn = out - effectDuration;
int oldFadeIn = effect->filter().get_int("in");
int oldOut = effect->filter().get_int("out");
int referenceEffectIn = effect->filter().get_int("_refin");
if (referenceEffectIn <= 0) {
referenceEffectIn = oldFadeIn;
effect->filter().set("_refin", referenceEffectIn);
}
Fun operation = [this, effect, newFadeIn, out, logUndo]() {
effect->setParameter(QStringLiteral("in"), newFadeIn, false);
effect->setParameter(QStringLiteral("out"), out, logUndo);
return true;
};
- if (operation() && logUndo) {
+ bool res = operation();
+ if (!res) {
+ return false;
+ }
+ if (logUndo) {
Fun reverse = [this, effect, referenceEffectIn, oldOut]() {
effect->setParameter(QStringLiteral("in"), referenceEffectIn, false);
effect->setParameter(QStringLiteral("out"), oldOut, true);
effect->filter().set("_refin", (char *)nullptr);
return true;
};
PUSH_LAMBDA(operation, redo);
PUSH_LAMBDA(reverse, undo);
}
- indexes << getIndexFromItem(effect);
} else {
// Not a fade effect, check for keyframes
- std::shared_ptr effect = std::static_pointer_cast(rootItem->child(i));
std::shared_ptr keyframes = effect->getKeyframeModel();
if (keyframes != nullptr) {
// Effect has keyframes, update these
keyframes->resizeKeyframes(oldIn, oldIn+oldDuration - 1, newIn, out - 1, undo, redo);
QModelIndex index = getIndexFromItem(effect);
Fun refresh = [this, effect, index]() {
effect->dataChanged(index, index, QVector());
return true;
};
refresh();
PUSH_LAMBDA(refresh, redo);
PUSH_LAMBDA(refresh, undo);
}
}
}
return true;
}
bool EffectStackModel::adjustFadeLength(int duration, bool fromStart, bool audioFade, bool videoFade)
{
if (fromStart) {
// Fade in
if (fadeIns.empty()) {
if (audioFade) {
appendEffect(QStringLiteral("fadein"));
}
if (videoFade) {
appendEffect(QStringLiteral("fade_from_black"));
}
}
QList indexes;
auto ptr = m_services.front().lock();
int in = 0;
if (ptr) {
in = ptr->get_int("in");
}
qDebug() << "//// SETTING CLIP FADIN: " << duration;
for (int i = 0; i < rootItem->childCount(); ++i) {
if (fadeIns.count(std::static_pointer_cast(rootItem->child(i))->getId()) > 0) {
std::shared_ptr effect = std::static_pointer_cast(rootItem->child(i));
effect->filter().set("in", in);
duration = qMin(pCore->getItemDuration(m_ownerId), duration);
effect->filter().set("out", in + duration);
indexes << getIndexFromItem(effect);
}
}
if (!indexes.isEmpty()) {
emit dataChanged(indexes.first(), indexes.last(), QVector());
pCore->updateItemModel(m_ownerId, QStringLiteral("fadein"));
}
} else {
// Fade out
if (fadeOuts.empty()) {
if (audioFade) {
appendEffect(QStringLiteral("fadeout"));
}
if (videoFade) {
appendEffect(QStringLiteral("fade_to_black"));
}
}
int in = 0;
auto ptr = m_services.front().lock();
if (ptr) {
in = ptr->get_int("in");
}
int out = in + pCore->getItemDuration(m_ownerId);
QList indexes;
for (int i = 0; i < rootItem->childCount(); ++i) {
if (fadeOuts.count(std::static_pointer_cast(rootItem->child(i))->getId()) > 0) {
std::shared_ptr effect = std::static_pointer_cast(rootItem->child(i));
effect->filter().set("out", out);
duration = qMin(pCore->getItemDuration(m_ownerId), duration);
effect->filter().set("in", out - duration);
indexes << getIndexFromItem(effect);
}
}
if (!indexes.isEmpty()) {
qDebug() << "// UPDATING DATA INDEXES 2!!!";
emit dataChanged(indexes.first(), indexes.last(), QVector());
pCore->updateItemModel(m_ownerId, QStringLiteral("fadeout"));
}
}
return true;
}
int EffectStackModel::getFadePosition(bool fromStart)
{
if (fromStart) {
if (fadeIns.empty()) {
return 0;
}
for (int i = 0; i < rootItem->childCount(); ++i) {
if (*(fadeIns.begin()) == std::static_pointer_cast(rootItem->child(i))->getId()) {
std::shared_ptr effect = std::static_pointer_cast(rootItem->child(i));
return effect->filter().get_length();
}
}
} else {
if (fadeOuts.empty()) {
return 0;
}
for (int i = 0; i < rootItem->childCount(); ++i) {
if (*(fadeOuts.begin()) == std::static_pointer_cast(rootItem->child(i))->getId()) {
std::shared_ptr effect = std::static_pointer_cast(rootItem->child(i));
return effect->filter().get_length();
}
}
}
return 0;
}
bool EffectStackModel::removeFade(bool fromStart)
{
std::vector toRemove;
for (int i = 0; i < rootItem->childCount(); ++i) {
if ((fromStart && fadeIns.count(std::static_pointer_cast(rootItem->child(i))->getId()) > 0) ||
(!fromStart && fadeOuts.count(std::static_pointer_cast(rootItem->child(i))->getId()) > 0)) {
toRemove.push_back(i);
}
}
for (int i : toRemove) {
std::shared_ptr effect = std::static_pointer_cast(rootItem->child(i));
removeEffect(effect);
}
return true;
}
void EffectStackModel::moveEffect(int destRow, std::shared_ptr item)
{
Q_ASSERT(m_allItems.count(item->getId()) > 0);
int oldRow = item->row();
Fun undo = moveItem_lambda(item->getId(), oldRow);
Fun redo = moveItem_lambda(item->getId(), destRow);
bool res = redo();
if (res) {
Fun update = [this]() {
this->dataChanged(QModelIndex(), QModelIndex(), {});
return true;
};
update();
UPDATE_UNDO_REDO(update, update, undo, redo);
auto effectId = std::static_pointer_cast(item)->getAssetId();
QString effectName = EffectsRepository::get()->getName(effectId);
PUSH_UNDO(undo, redo, i18n("Move effect %1", effectName));
}
}
void EffectStackModel::registerItem(const std::shared_ptr &item)
{
qDebug() << "$$$$$$$$$$$$$$$$$$$$$ Planting effect";
QModelIndex ix;
if (!item->isRoot()) {
auto effectItem = std::static_pointer_cast(item);
if (!m_loadingExisting) {
qDebug() << "$$$$$$$$$$$$$$$$$$$$$ Planting effect in " << m_services.size();
for (const auto &service : m_services) {
qDebug() << "$$$$$$$$$$$$$$$$$$$$$ Planting effect in " << (void *)service.lock().get();
effectItem->plant(service);
}
}
effectItem->setEffectStackEnabled(m_effectStackEnabled);
ix = getIndexFromItem(effectItem);
if (!effectItem->isAudio() && !m_loadingExisting) {
pCore->refreshProjectItem(m_ownerId);
pCore->invalidateItem(m_ownerId);
}
}
AbstractTreeModel::registerItem(item);
if (ix.isValid()) {
// Required to build the effect view
emit dataChanged(ix, ix, QVector());
}
}
void EffectStackModel::deregisterItem(int id, TreeItem *item)
{
if (!item->isRoot()) {
auto effectItem = static_cast(item);
for (const auto &service : m_services) {
effectItem->unplant(service);
}
if (!effectItem->isAudio()) {
pCore->refreshProjectItem(m_ownerId);
pCore->invalidateItem(m_ownerId);
}
}
AbstractTreeModel::deregisterItem(id, item);
}
void EffectStackModel::setEffectStackEnabled(bool enabled)
{
m_effectStackEnabled = enabled;
// Recursively updates children states
for (int i = 0; i < rootItem->childCount(); ++i) {
std::static_pointer_cast(rootItem->child(i))->setEffectStackEnabled(enabled);
}
}
std::shared_ptr EffectStackModel::getEffectStackRow(int row, std::shared_ptr parentItem)
{
return std::static_pointer_cast(parentItem ? rootItem->child(row) : rootItem->child(row));
}
void EffectStackModel::importEffects(std::shared_ptr sourceStack)
{
// TODO: manage fades, keyframes if clips don't have same size / in point
for (int i = 0; i < sourceStack->rowCount(); i++) {
auto item = sourceStack->getEffectStackRow(i);
copyEffect(item, false);
}
modelChanged();
}
void EffectStackModel::importEffects(std::weak_ptr service, bool alreadyExist)
{
m_loadingExisting = alreadyExist;
if (auto ptr = service.lock()) {
for (int i = 0; i < ptr->filter_count(); i++) {
if (ptr->filter(i)->get("kdenlive_id") == nullptr) {
// don't consider internal MLT stuff
continue;
}
QString effectId = ptr->filter(i)->get("kdenlive_id");
auto effect = EffectItemModel::construct(ptr->filter(i), shared_from_this());
copyEffect(effect, false);
}
}
m_loadingExisting = false;
modelChanged();
}
void EffectStackModel::setActiveEffect(int ix)
{
for (const auto &service : m_services) {
auto ptr = service.lock();
if (ptr) {
ptr->set("kdenlive:activeeffect", ix);
}
}
pCore->updateItemKeyframes(m_ownerId);
}
int EffectStackModel::getActiveEffect() const
{
auto ptr = m_services.front().lock();
if (ptr) {
return ptr->get_int("kdenlive:activeeffect");
}
return 0;
}
void EffectStackModel::slotCreateGroup(std::shared_ptr childEffect)
{
auto groupItem = EffectGroupModel::construct(QStringLiteral("group"), shared_from_this());
rootItem->appendChild(groupItem);
groupItem->appendChild(childEffect);
}
ObjectId EffectStackModel::getOwnerId() const
{
return m_ownerId;
}
bool EffectStackModel::checkConsistency()
{
if (!AbstractTreeModel::checkConsistency()) {
return false;
}
std::vector> allFilters;
// We do a DFS on the tree to retrieve all the filters
std::stack> stck;
stck.push(std::static_pointer_cast(rootItem));
while (!stck.empty()) {
auto current = stck.top();
stck.pop();
if (current->effectItemType() == EffectItemType::Effect) {
if (current->childCount() > 0) {
qDebug() << "ERROR: Found an effect with children";
return false;
}
allFilters.push_back(std::static_pointer_cast(current));
continue;
}
for (int i = current->childCount() - 1; i >= 0; --i) {
stck.push(std::static_pointer_cast(current->child(i)));
}
}
for (const auto &service : m_services) {
auto ptr = service.lock();
if (!ptr) {
qDebug() << "ERROR: unavailable service";
return false;
}
if (ptr->filter_count() != (int)allFilters.size()) {
qDebug() << "ERROR: Wrong filter count";
return false;
}
for (uint i = 0; i < allFilters.size(); ++i) {
auto mltFilter = ptr->filter((int)i)->get_filter();
auto currentFilter = allFilters[i]->filter().get_filter();
if (mltFilter != currentFilter) {
qDebug() << "ERROR: filter " << i << "differ";
return false;
}
}
}
return true;
}
void EffectStackModel::adjust(const QString &effectId, const QString &effectName, double value)
{
for (int i = 0; i < rootItem->childCount(); ++i) {
std::shared_ptr sourceEffect = std::static_pointer_cast(rootItem->child(i));
if (effectId == sourceEffect->getAssetId()) {
sourceEffect->setParameter(effectName, QString::number(value));
return;
}
}
}
bool EffectStackModel::hasFilter(const QString &effectId)
{
for (int i = 0; i < rootItem->childCount(); ++i) {
std::shared_ptr sourceEffect = std::static_pointer_cast(rootItem->child(i));
if (effectId == sourceEffect->getAssetId()) {
return true;
}
}
return false;
}
double EffectStackModel::getFilter(const QString &effectId, const QString ¶mName)
{
for (int i = 0; i < rootItem->childCount(); ++i) {
std::shared_ptr sourceEffect = std::static_pointer_cast(rootItem->child(i));
if (effectId == sourceEffect->getAssetId()) {
return sourceEffect->filter().get_double(paramName.toUtf8().constData());
}
}
return 0.0;
}
KeyframeModel *EffectStackModel::getEffectKeyframeModel()
{
if (rootItem->childCount() == 0) return nullptr;
int ix = 0;
auto ptr = m_services.front().lock();
if (ptr) {
ix = ptr->get_int("kdenlive:activeeffect");
}
if (ix < 0) {
return nullptr;
}
std::shared_ptr sourceEffect = std::static_pointer_cast(rootItem->child(ix));
std::shared_ptr listModel = sourceEffect->getKeyframeModel();
if (listModel) {
return listModel->getKeyModel();
}
return nullptr;
}
void EffectStackModel::replugEffect(std::shared_ptr asset)
{
auto effectItem = std::static_pointer_cast(asset);
int oldRow = effectItem->row();
int count = rowCount();
for (int ix = oldRow; ix < count; ix++) {
auto item = std::static_pointer_cast(rootItem->child(ix));
for (const auto &service : m_services) {
item->unplant(service);
}
}
Mlt::Properties *effect = EffectsRepository::get()->getEffect(effectItem->getAssetId());
effect->inherit(effectItem->filter());
effectItem->resetAsset(effect);
for (int ix = oldRow; ix < count; ix++) {
auto item = std::static_pointer_cast(rootItem->child(ix));
for (const auto &service : m_services) {
item->plant(service);
}
}
}
void EffectStackModel::cleanFadeEffects(bool outEffects, Fun &undo, Fun &redo)
{
const auto &toDelete = outEffects ? fadeOuts : fadeIns;
for (int id : toDelete) {
auto effect = std::static_pointer_cast(getItemById(id));
Fun operation = removeItem_lambda(id);
if (operation()) {
Fun reverse = addItem_lambda(effect, rootItem->getId());
UPDATE_UNDO_REDO(operation, reverse, undo, redo);
}
}
if (!toDelete.empty()) {
Fun update = [this]() {
// TODO: only update if effect is fade or keyframe
pCore->updateItemKeyframes(m_ownerId);
return true;
};
update();
PUSH_LAMBDA(update, redo);
}
}