File indexing completed on 2024-05-19 04:54:23

0001 /*
0002     SPDX-FileCopyrightText: 2017 Nicolas Carion
0003     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 */
0005 
0006 #include "effectitemmodel.hpp"
0007 
0008 #include "core.h"
0009 #include "effects/effectsrepository.hpp"
0010 #include "effectstackmodel.hpp"
0011 #include <utility>
0012 
0013 EffectItemModel::EffectItemModel(const QList<QVariant> &effectData, std::unique_ptr<Mlt::Properties> effect, const QDomElement &xml, const QString &effectId,
0014                                  const std::shared_ptr<AbstractTreeModel> &stack, bool isEnabled, QString originalDecimalPoint)
0015     : AbstractEffectItem(EffectItemType::Effect, effectData, stack, false, isEnabled)
0016     , AssetParameterModel(std::move(effect), xml, effectId, std::static_pointer_cast<EffectStackModel>(stack)->getOwnerId(), originalDecimalPoint)
0017     , m_childId(0)
0018 {
0019     connect(this, &AssetParameterModel::updateChildren, [&](const QStringList &names) {
0020         if (m_childEffects.size() == 0) {
0021             return;
0022         }
0023         // qDebug() << "* * *SETTING EFFECT PARAM: " << name << " = " << m_asset->get(name.toUtf8().constData());
0024         QMapIterator<int, std::shared_ptr<EffectItemModel>> i(m_childEffects);
0025         while (i.hasNext()) {
0026             i.next();
0027             for (const QString &name : names) {
0028                 i.value()->filter().set(name.toUtf8().constData(), m_asset->get(name.toUtf8().constData()));
0029             }
0030         }
0031     });
0032 }
0033 
0034 // static
0035 std::shared_ptr<EffectItemModel> EffectItemModel::construct(const QString &effectId, std::shared_ptr<AbstractTreeModel> stack, bool effectEnabled)
0036 {
0037     Q_ASSERT(EffectsRepository::get()->exists(effectId));
0038     QDomElement xml = EffectsRepository::get()->getXml(effectId);
0039 
0040     std::unique_ptr<Mlt::Properties> effect = EffectsRepository::get()->getEffect(effectId);
0041     effect->set("kdenlive_id", effectId.toUtf8().constData());
0042 
0043     QList<QVariant> data;
0044     data << EffectsRepository::get()->getName(effectId) << effectId;
0045 
0046     std::shared_ptr<EffectItemModel> self(new EffectItemModel(data, std::move(effect), xml, effectId, stack, effectEnabled));
0047 
0048     baseFinishConstruct(self);
0049     return self;
0050 }
0051 
0052 std::shared_ptr<EffectItemModel> EffectItemModel::construct(std::unique_ptr<Mlt::Properties> effect, std::shared_ptr<AbstractTreeModel> stack,
0053                                                             const QString &originalDecimalPoint)
0054 {
0055     QString effectId = effect->get("kdenlive_id");
0056     if (effectId.isEmpty()) {
0057         effectId = effect->get("mlt_service");
0058     }
0059     Q_ASSERT(EffectsRepository::get()->exists(effectId));
0060 
0061     // Get the effect XML and add parameter values from the project file
0062     QDomElement xml = EffectsRepository::get()->getXml(effectId);
0063     QDomNodeList params = xml.elementsByTagName(QStringLiteral("parameter"));
0064     for (int i = 0; i < params.count(); ++i) {
0065         QDomElement currentParameter = params.item(i).toElement();
0066         QString paramName = currentParameter.attribute(QStringLiteral("name"));
0067         QString paramType = currentParameter.attribute(QStringLiteral("type"));
0068         if (paramType == QLatin1String("multiswitch")) {
0069             // multiswitch params have a composited param name, skip
0070             QStringList names = paramName.split(QLatin1Char('\n'));
0071             QStringList paramValues;
0072             for (const QString &n : qAsConst(names)) {
0073                 paramValues << effect->get(n.toUtf8().constData());
0074             }
0075             currentParameter.setAttribute(QStringLiteral("value"), paramValues.join(QLatin1Char('\n')));
0076             continue;
0077         }
0078         QString paramValue = effect->get(paramName.toUtf8().constData());
0079         qDebug() << effectId << ": Setting parameter " << paramName << " to " << paramValue;
0080         currentParameter.setAttribute(QStringLiteral("value"), paramValue);
0081     }
0082 
0083     QList<QVariant> data;
0084     data << EffectsRepository::get()->getName(effectId) << effectId;
0085 
0086     bool disable = effect->get_int("disable") == 0;
0087     std::shared_ptr<EffectItemModel> self(new EffectItemModel(data, std::move(effect), xml, effectId, stack, disable, originalDecimalPoint));
0088     baseFinishConstruct(self);
0089     return self;
0090 }
0091 
0092 void EffectItemModel::plant(const std::weak_ptr<Mlt::Service> &service)
0093 {
0094     if (auto ptr = service.lock()) {
0095         int ret = ptr->attach(filter());
0096         Q_ASSERT(ret == 0);
0097     } else {
0098         qDebug() << "Error : Cannot plant effect because parent service is not available anymore";
0099         Q_ASSERT(false);
0100     }
0101 }
0102 
0103 void EffectItemModel::loadClone(const std::weak_ptr<Mlt::Service> &service)
0104 {
0105     if (auto ptr = service.lock()) {
0106         std::shared_ptr<EffectItemModel> effect = nullptr;
0107         for (int i = 0; i < ptr->filter_count(); i++) {
0108             std::unique_ptr<Mlt::Filter> filt(ptr->filter(i));
0109             QString effName = filt->get("kdenlive_id");
0110             if (effName == m_assetId && filt->get_int("_kdenlive_processed") == 0) {
0111                 if (auto ptr2 = m_model.lock()) {
0112                     effect = EffectItemModel::construct(std::move(filt), ptr2, QString());
0113                     int childId = ptr->get_int("_childid");
0114                     if (childId == 0) {
0115                         childId = m_childId++;
0116                         ptr->set("_childid", childId);
0117                     }
0118                     m_childEffects.insert(childId, effect);
0119                 }
0120                 break;
0121             }
0122             filt->set("_kdenlive_processed", 1);
0123         }
0124         return;
0125     }
0126     qDebug() << "Error : Cannot plant effect because parent service is not available anymore";
0127     Q_ASSERT(false);
0128 }
0129 
0130 void EffectItemModel::plantClone(const std::weak_ptr<Mlt::Service> &service)
0131 {
0132     if (auto ptr = service.lock()) {
0133         const QString effectId = getAssetId();
0134         std::shared_ptr<EffectItemModel> effect = nullptr;
0135         if (auto ptr2 = m_model.lock()) {
0136             effect = EffectItemModel::construct(effectId, ptr2);
0137             effect->setParameters(getAllParameters(), false);
0138             int childId = ptr->get_int("_childid");
0139             if (childId == 0) {
0140                 childId = m_childId++;
0141                 ptr->set("_childid", childId);
0142             }
0143             m_childEffects.insert(childId, effect);
0144             int ret = ptr->attach(effect->filter());
0145             Q_ASSERT(ret == 0);
0146             return;
0147         }
0148     }
0149     qDebug() << "Error : Cannot plant effect because parent service is not available anymore";
0150     Q_ASSERT(false);
0151 }
0152 
0153 void EffectItemModel::unplant(const std::weak_ptr<Mlt::Service> &service)
0154 {
0155     if (auto ptr = service.lock()) {
0156         int ret = ptr->detach(filter());
0157         Q_ASSERT(ret == 0);
0158     } else {
0159         qDebug() << "Error : Cannot plant effect because parent service is not available anymore";
0160         Q_ASSERT(false);
0161     }
0162 }
0163 
0164 void EffectItemModel::unplantClone(const std::weak_ptr<Mlt::Service> &service)
0165 {
0166     if (m_childEffects.size() == 0) {
0167         return;
0168     }
0169     if (auto ptr = service.lock()) {
0170         int ret = ptr->detach(filter());
0171         Q_ASSERT(ret == 0);
0172         int childId = ptr->get_int("_childid");
0173         auto effect = m_childEffects.take(childId);
0174         if (effect && effect->isValid()) {
0175             ptr->detach(effect->filter());
0176             effect.reset();
0177         }
0178     } else {
0179         qDebug() << "Error : Cannot plant effect because parent service is not available anymore";
0180         Q_ASSERT(false);
0181     }
0182 }
0183 
0184 Mlt::Filter &EffectItemModel::filter() const
0185 {
0186     return *static_cast<Mlt::Filter *>(m_asset.get());
0187 }
0188 
0189 bool EffectItemModel::isValid() const
0190 {
0191     return m_asset && m_asset->is_valid();
0192 }
0193 
0194 void EffectItemModel::updateEnable(bool updateTimeline)
0195 {
0196     filter().set("disable", isEnabled() ? 0 : 1);
0197     if (updateTimeline) {
0198         pCore->refreshProjectItem(m_ownerId);
0199         pCore->invalidateItem(m_ownerId);
0200     }
0201     const QModelIndex start = AssetParameterModel::index(0, 0);
0202     const QModelIndex end = AssetParameterModel::index(rowCount() - 1, 0);
0203     Q_EMIT dataChanged(start, end, QVector<int>());
0204     Q_EMIT enabledChange(!isEnabled());
0205     // Update timeline child producers
0206     Q_EMIT AssetParameterModel::updateChildren({QStringLiteral("disable")});
0207 }
0208 
0209 void EffectItemModel::setCollapsed(bool collapsed)
0210 {
0211     filter().set("kdenlive:collapsed", collapsed ? 1 : 0);
0212 }
0213 
0214 bool EffectItemModel::isCollapsed() const
0215 {
0216     return filter().get_int("kdenlive:collapsed") == 1;
0217 }
0218 
0219 void EffectItemModel::setKeyframesHidden(bool hidden)
0220 {
0221     Fun undo = [this, hidden]() {
0222         filter().set("kdenlive:kfrhidden", hidden ? 0 : 1);
0223         Q_EMIT hideKeyframesChange(hidden ? false : true);
0224         return true;
0225     };
0226     Fun redo = [this, hidden]() {
0227         filter().set("kdenlive:kfrhidden", hidden ? 1 : 0);
0228         Q_EMIT hideKeyframesChange(hidden ? true : false);
0229         return true;
0230     };
0231     redo();
0232     pCore->pushUndo(undo, redo, hidden ? i18n("Hide keyframes") : i18n("Show keyframes"));
0233 }
0234 
0235 bool EffectItemModel::isKeyframesHidden() const
0236 {
0237     return filter().get_int("kdenlive:kfrhidden") == 1;
0238 }
0239 
0240 bool EffectItemModel::keyframesHiddenUnset() const
0241 {
0242     return filter().property_exists("kdenlive:kfrhidden") == false;
0243 }
0244 
0245 bool EffectItemModel::hasForcedInOut() const
0246 {
0247     return filter().get_int("kdenlive:force_in_out") == 1;
0248 }
0249 
0250 bool EffectItemModel::isAudio() const
0251 {
0252     return EffectsRepository::get()->isAudioEffect(m_assetId);
0253 }
0254 
0255 bool EffectItemModel::isUnique() const
0256 {
0257     return EffectsRepository::get()->isUnique(m_assetId);
0258 }
0259 
0260 QPair<int, int> EffectItemModel::getInOut() const
0261 {
0262     return {m_asset->get_int("in"), m_asset->get_int("out")};
0263 }
0264 
0265 void EffectItemModel::setInOut(const QString &effectName, QPair<int, int> bounds, bool enabled, bool withUndo)
0266 {
0267     QPair<int, int> currentInOut = {m_asset->get_int("in"), m_asset->get_int("out")};
0268     int currentState = m_asset->get_int("kdenlive:force_in_out");
0269     Fun undo = [this, currentState, currentInOut]() {
0270         m_asset->set("kdenlive:force_in_out", currentState);
0271         m_asset->set("in", currentInOut.first);
0272         m_asset->set("out", currentInOut.second);
0273         Q_EMIT AssetParameterModel::updateChildren({QStringLiteral("in"), QStringLiteral("out")});
0274         if (!isAudio()) {
0275             pCore->refreshProjectItem(m_ownerId);
0276             pCore->invalidateItem(m_ownerId);
0277         }
0278         Q_EMIT showEffectZone(m_ownerId, currentInOut, currentState == 1);
0279         return true;
0280     };
0281     Fun redo = [this, enabled, bounds]() {
0282         m_asset->set("kdenlive:force_in_out", enabled ? 1 : 0);
0283         m_asset->set("in", bounds.first);
0284         m_asset->set("out", bounds.second);
0285         Q_EMIT AssetParameterModel::updateChildren({QStringLiteral("in"), QStringLiteral("out")});
0286         if (!isAudio()) {
0287             pCore->refreshProjectItem(m_ownerId);
0288             pCore->invalidateItem(m_ownerId);
0289         }
0290         Q_EMIT showEffectZone(m_ownerId, bounds, enabled);
0291         return true;
0292     };
0293     std::shared_ptr<KeyframeModelList> keyframes = getKeyframeModel();
0294     if (keyframes != nullptr) {
0295         // Effect has keyframes, update these
0296         const QModelIndex start = AssetParameterModel::index(0, 0);
0297         const QModelIndex end = AssetParameterModel::index(rowCount() - 1, 0);
0298         Fun refresh = [this, start, end]() {
0299             Q_EMIT dataChanged(start, end, QVector<int>());
0300             return true;
0301         };
0302         refresh();
0303         PUSH_LAMBDA(refresh, redo);
0304         PUSH_LAMBDA(refresh, undo);
0305     }
0306     redo();
0307     if (withUndo) {
0308         pCore->pushUndo(undo, redo, i18n("Update zone for %1", effectName));
0309     }
0310 }