diff --git a/Messages.sh b/Messages.sh index 2372d6c56..8ef2ec0c4 100755 --- a/Messages.sh +++ b/Messages.sh @@ -1,9 +1,9 @@ #!/bin/sh kdenlive_subdirs="plugins renderer data src src/ui" -$EXTRACTRC --tag=name --tag=description --tag=label --tag=comment --tag=paramlistdisplay data/effects/*.xml data/kdenliveeffectscategory.rc >> rc.cpp +$EXTRACTRC --tag=name --tag=description --tag=label --tag=comment --tag=paramlistdisplay data/effects/*.xml data/generators/*.xml data/kdenliveeffectscategory.rc >> rc.cpp $EXTRACTRC `find $kdenlive_subdirs -name \*.rc -a ! -name encodingprofiles.rc -a ! -name camcorderfilters.rc -o -name \*.ui` >> rc.cpp $XGETTEXT `find $kdenlive_subdirs -name \*.cpp -o -name \*.h -o -name \*.qml` *.cpp -o $podir/kdenlive.pot rm -f rc.cpp diff --git a/src/assets/model/assetparametermodel.cpp b/src/assets/model/assetparametermodel.cpp index bf738bb3a..4226dbce0 100644 --- a/src/assets/model/assetparametermodel.cpp +++ b/src/assets/model/assetparametermodel.cpp @@ -1,861 +1,861 @@ /*************************************************************************** * 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 "assetparametermodel.hpp" #include "assets/keyframes/model/keyframemodellist.hpp" #include "core.h" #include "kdenlivesettings.h" #include "klocalizedstring.h" #include "profiles/profilemodel.hpp" #include #include #include #include #include AssetParameterModel::AssetParameterModel(std::unique_ptr asset, const QDomElement &assetXml, const QString &assetId, ObjectId ownerId, QObject *parent) : QAbstractListModel(parent) , monitorId(ownerId.first == ObjectType::BinClip ? Kdenlive::ClipMonitor : Kdenlive::ProjectMonitor) , m_assetId(assetId) , m_ownerId(ownerId) , m_asset(std::move(asset)) , m_keyframes(nullptr) { Q_ASSERT(m_asset->is_valid()); QDomNodeList nodeList = assetXml.elementsByTagName(QStringLiteral("parameter")); m_hideKeyframesByDefault = assetXml.hasAttribute(QStringLiteral("hideKeyframes")); m_isAudio = assetXml.attribute(QStringLiteral("type")) == QLatin1String("audio"); bool needsLocaleConversion = false; QChar separator, oldSeparator; // Check locale, default effects xml has no LC_NUMERIC defined and always uses the C locale QLocale locale; if (assetXml.hasAttribute(QStringLiteral("LC_NUMERIC"))) { QLocale effectLocale = QLocale(assetXml.attribute(QStringLiteral("LC_NUMERIC"))); if (QLocale::c().decimalPoint() != effectLocale.decimalPoint()) { needsLocaleConversion = true; separator = QLocale::c().decimalPoint(); oldSeparator = effectLocale.decimalPoint(); } } qDebug() << "XML parsing of " << assetId << ". found : " << nodeList.count(); for (int i = 0; i < nodeList.count(); ++i) { QDomElement currentParameter = nodeList.item(i).toElement(); // Convert parameters if we need to if (needsLocaleConversion) { QDomNamedNodeMap attrs = currentParameter.attributes(); for (int k = 0; k < attrs.count(); ++k) { QString nodeName = attrs.item(k).nodeName(); if (nodeName != QLatin1String("type") && nodeName != QLatin1String("name")) { QString val = attrs.item(k).nodeValue(); if (val.contains(oldSeparator)) { QString newVal = val.replace(oldSeparator, separator); attrs.item(k).setNodeValue(newVal); } } } } // Parse the basic attributes of the parameter QString name = currentParameter.attribute(QStringLiteral("name")); QString type = currentParameter.attribute(QStringLiteral("type")); QString value = currentParameter.attribute(QStringLiteral("value")); ParamRow currentRow; currentRow.type = paramTypeFromStr(type); currentRow.xml = currentParameter; if (value.isEmpty()) { QVariant defaultValue = parseAttribute(m_ownerId, QStringLiteral("default"), currentParameter); value = defaultValue.type() == QVariant::Double ? locale.toString(defaultValue.toDouble()) : defaultValue.toString(); } bool isFixed = (type == QLatin1String("fixed")); if (isFixed) { m_fixedParams[name] = value; } else if (currentRow.type == ParamType::Position) { int val = value.toInt(); if (val < 0) { int in = pCore->getItemIn(m_ownerId); int out = in + pCore->getItemDuration(m_ownerId) - 1; val += out; value = QString::number(val); } } else if (currentRow.type == ParamType::KeyframeParam || currentRow.type == ParamType::AnimatedRect) { if (!value.contains(QLatin1Char('='))) { value.prepend(QStringLiteral("%1=").arg(pCore->getItemIn(m_ownerId))); } } if (!name.isEmpty()) { internalSetParameter(name, value); // Keep track of param order m_paramOrder.push_back(name); } if (isFixed) { // fixed parameters are not displayed so we don't store them. continue; } currentRow.value = value; - QString title = currentParameter.firstChildElement(QStringLiteral("name")).text(); + QString title = i18n(currentParameter.firstChildElement(QStringLiteral("name")).text().toUtf8().data()); currentRow.name = title.isEmpty() ? name : title; m_params[name] = currentRow; m_rows.push_back(name); } if (m_assetId.startsWith(QStringLiteral("sox_"))) { // Sox effects need to have a special "Effect" value set QStringList effectParam = {m_assetId.section(QLatin1Char('_'), 1)}; for (const QString &pName : m_paramOrder) { effectParam << m_asset->get(pName.toUtf8().constData()); } m_asset->set("effect", effectParam.join(QLatin1Char(' ')).toUtf8().constData()); } qDebug() << "END parsing of " << assetId << ". Number of found parameters" << m_rows.size(); emit modelChanged(); } void AssetParameterModel::prepareKeyframes() { if (m_keyframes) return; int ix = 0; for (const auto &name : m_rows) { if (m_params.at(name).type == ParamType::KeyframeParam || m_params.at(name).type == ParamType::AnimatedRect || m_params.at(name).type == ParamType::Roto_spline) { addKeyframeParam(index(ix, 0)); } ix++; } if (m_keyframes) { // Make sure we have keyframes at same position for all parameters m_keyframes->checkConsistency(); } } QStringList AssetParameterModel::getKeyframableParameters() const { QStringList paramNames; int ix = 0; for (const auto &name : m_rows) { if (m_params.at(name).type == ParamType::KeyframeParam || m_params.at(name).type == ParamType::AnimatedRect) { //addKeyframeParam(index(ix, 0)); paramNames << name; } ix++; } return paramNames; } void AssetParameterModel::setParameter(const QString &name, int value, bool update) { Q_ASSERT(m_asset->is_valid()); m_asset->set(name.toLatin1().constData(), value); if (m_fixedParams.count(name) == 0) { m_params[name].value = value; } else { m_fixedParams[name] = value; } if (m_assetId.startsWith(QStringLiteral("sox_"))) { // Warning, SOX effect, need unplug/replug qDebug() << "// Warning, SOX effect, need unplug/replug"; QStringList effectParam = {m_assetId.section(QLatin1Char('_'), 1)}; for (const QString &pName : m_paramOrder) { effectParam << m_asset->get(pName.toUtf8().constData()); } m_asset->set("effect", effectParam.join(QLatin1Char(' ')).toUtf8().constData()); emit replugEffect(shared_from_this()); } else if (m_assetId == QLatin1String("autotrack_rectangle") || m_assetId.startsWith(QStringLiteral("ladspa"))) { // these effects don't understand param change and need to be rebuild emit replugEffect(shared_from_this()); } if (update) { emit modelChanged(); emit dataChanged(index(0, 0), index(m_rows.count() - 1, 0), {}); // Update fades in timeline pCore->updateItemModel(m_ownerId, m_assetId); if (!m_isAudio) { // Trigger monitor refresh pCore->refreshProjectItem(m_ownerId); // Invalidate timeline preview pCore->invalidateItem(m_ownerId); } } } void AssetParameterModel::internalSetParameter(const QString &name, const QString ¶mValue, const QModelIndex ¶mIndex) { Q_ASSERT(m_asset->is_valid()); QLocale locale; locale.setNumberOptions(QLocale::OmitGroupSeparator); // TODO: this does not really belong here, but I don't see another way to do it so that undo works if (data(paramIndex, AssetParameterModel::TypeRole).value() == ParamType::Curve) { QStringList vals = paramValue.split(QLatin1Char(';'), QString::SkipEmptyParts); int points = vals.size(); m_asset->set("3", points / 10.); // for the curve, inpoints are numbered: 6, 8, 10, 12, 14 // outpoints, 7, 9, 11, 13,15 so we need to deduce these enums for (int i = 0; i < points; i++) { const QString &pointVal = vals.at(i); int idx = 2 * i + 6; m_asset->set(QString::number(idx).toLatin1().constData(), pointVal.section(QLatin1Char('/'), 0, 0).toDouble()); idx++; m_asset->set(QString::number(idx).toLatin1().constData(), pointVal.section(QLatin1Char('/'), 1, 1).toDouble()); } } bool conversionSuccess; double doubleValue = locale.toDouble(paramValue, &conversionSuccess); if (conversionSuccess) { m_asset->set(name.toLatin1().constData(), doubleValue); if (m_fixedParams.count(name) == 0) { m_params[name].value = doubleValue; } else { m_fixedParams[name] = doubleValue; } } else { m_asset->set(name.toLatin1().constData(), paramValue.toUtf8().constData()); qDebug() << " = = SET EFFECT PARAM: " << name << " = " << paramValue; if (m_fixedParams.count(name) == 0) { m_params[name].value = paramValue; if (m_keyframes) { KeyframeModel *km = m_keyframes->getKeyModel(paramIndex); if (km) { km->refresh(); } //m_keyframes->refresh(); } } else { m_fixedParams[name] = paramValue; } } } void AssetParameterModel::setParameter(const QString &name, const QString ¶mValue, bool update, const QModelIndex ¶mIndex) { //qDebug() << "// PROCESSING PARAM CHANGE: " << name << ", UPDATE: " << update << ", VAL: " << paramValue; internalSetParameter(name, paramValue, paramIndex); bool updateChildRequired = true; if (m_assetId.startsWith(QStringLiteral("sox_"))) { // Warning, SOX effect, need unplug/replug QStringList effectParam = {m_assetId.section(QLatin1Char('_'), 1)}; for (const QString &pName : m_paramOrder) { effectParam << m_asset->get(pName.toUtf8().constData()); } m_asset->set("effect", effectParam.join(QLatin1Char(' ')).toUtf8().constData()); emit replugEffect(shared_from_this()); updateChildRequired = false; } else if (m_assetId == QLatin1String("autotrack_rectangle") || m_assetId.startsWith(QStringLiteral("ladspa"))) { // these effects don't understand param change and need to be rebuild emit replugEffect(shared_from_this()); updateChildRequired = false; } else if (update) { qDebug() << "// SENDING DATA CHANGE...."; if (paramIndex.isValid()) { emit dataChanged(paramIndex, paramIndex); } else { QModelIndex ix = index(m_rows.indexOf(name), 0); emit dataChanged(ix, ix); } emit modelChanged(); } if (updateChildRequired) { emit updateChildren(name); } // Update timeline view if necessary if (m_ownerId.first == ObjectType::NoItem) { // Used for generator clips if (!update) emit modelChanged(); } else { // Update fades in timeline pCore->updateItemModel(m_ownerId, m_assetId); if (!m_isAudio) { // Trigger monitor refresh pCore->refreshProjectItem(m_ownerId); // Invalidate timeline preview pCore->invalidateItem(m_ownerId); } } } AssetParameterModel::~AssetParameterModel() = default; QVariant AssetParameterModel::data(const QModelIndex &index, int role) const { if (index.row() < 0 || index.row() >= m_rows.size() || !index.isValid()) { return QVariant(); } QString paramName = m_rows[index.row()]; Q_ASSERT(m_params.count(paramName) > 0); const QDomElement &element = m_params.at(paramName).xml; switch (role) { case Qt::DisplayRole: case Qt::EditRole: return m_params.at(paramName).name; case NameRole: return paramName; case TypeRole: return QVariant::fromValue(m_params.at(paramName).type); case CommentRole: { QDomElement commentElem = element.firstChildElement(QStringLiteral("comment")); QString comment; if (!commentElem.isNull()) { comment = i18n(commentElem.text().toUtf8().data()); } return comment; } case InRole: return m_asset->get_int("in"); case OutRole: return m_asset->get_int("out"); case ParentInRole: return pCore->getItemIn(m_ownerId); case ParentDurationRole: return pCore->getItemDuration(m_ownerId); case ParentPositionRole: return pCore->getItemPosition(m_ownerId); case HideKeyframesFirstRole: return m_hideKeyframesByDefault; case MinRole: return parseAttribute(m_ownerId, QStringLiteral("min"), element); case MaxRole: return parseAttribute(m_ownerId, QStringLiteral("max"), element); case FactorRole: return parseAttribute(m_ownerId, QStringLiteral("factor"), element, 1); case ScaleRole: return parseAttribute(m_ownerId, QStringLiteral("scale"), element, 0); case DecimalsRole: return parseAttribute(m_ownerId, QStringLiteral("decimals"), element); case DefaultRole: return parseAttribute(m_ownerId, QStringLiteral("default"), element); case FilterRole: return parseAttribute(m_ownerId, QStringLiteral("filter"), element); case FilterParamsRole: return parseAttribute(m_ownerId, QStringLiteral("filterparams"), element); case FilterJobParamsRole: return parseSubAttributes(QStringLiteral("jobparam"), element); case AlternateNameRole: { QDomNode child = element.firstChildElement(QStringLiteral("name")); if (child.toElement().hasAttribute(QStringLiteral("conditional"))) { return child.toElement().attribute(QStringLiteral("conditional")); } return m_params.at(paramName).name; } case SuffixRole: return element.attribute(QStringLiteral("suffix")); case OpacityRole: return element.attribute(QStringLiteral("opacity")) != QLatin1String("false"); case RelativePosRole: return element.attribute(QStringLiteral("relative")) == QLatin1String("true"); case ShowInTimelineRole: return !element.hasAttribute(QStringLiteral("notintimeline")); case AlphaRole: return element.attribute(QStringLiteral("alpha")) == QLatin1String("1"); case ValueRole: { QString value(m_asset->get(paramName.toUtf8().constData())); return value.isEmpty() ? (element.attribute(QStringLiteral("value")).isNull() ? parseAttribute(m_ownerId, QStringLiteral("default"), element) : element.attribute(QStringLiteral("value"))) : value; } case ListValuesRole: return element.attribute(QStringLiteral("paramlist")).split(QLatin1Char(';')); case ListNamesRole: { QDomElement namesElem = element.firstChildElement(QStringLiteral("paramlistdisplay")); return i18n(namesElem.text().toUtf8().data()).split(QLatin1Char(',')); } case List1Role: return parseAttribute(m_ownerId, QStringLiteral("list1"), element); case List2Role: return parseAttribute(m_ownerId, QStringLiteral("list2"), element); case Enum1Role: return m_asset->get_double("1"); case Enum2Role: return m_asset->get_double("2"); case Enum3Role: return m_asset->get_double("3"); case Enum4Role: return m_asset->get_double("4"); case Enum5Role: return m_asset->get_double("5"); case Enum6Role: return m_asset->get_double("6"); case Enum7Role: return m_asset->get_double("7"); case Enum8Role: return m_asset->get_double("8"); case Enum9Role: return m_asset->get_double("9"); case Enum10Role: return m_asset->get_double("10"); case Enum11Role: return m_asset->get_double("11"); case Enum12Role: return m_asset->get_double("12"); case Enum13Role: return m_asset->get_double("13"); case Enum14Role: return m_asset->get_double("14"); case Enum15Role: return m_asset->get_double("15"); } return QVariant(); } int AssetParameterModel::rowCount(const QModelIndex &parent) const { qDebug() << "===================================================== Requested rowCount" << parent << m_rows.size(); if (parent.isValid()) return 0; return m_rows.size(); } // static ParamType AssetParameterModel::paramTypeFromStr(const QString &type) { if (type == QLatin1String("double") || type == QLatin1String("float") || type == QLatin1String("constant")) { return ParamType::Double; } if (type == QLatin1String("list")) { return ParamType::List; } if (type == QLatin1String("bool")) { return ParamType::Bool; } if (type == QLatin1String("switch")) { return ParamType::Switch; } else if (type == QLatin1String("simplekeyframe")) { return ParamType::KeyframeParam; } else if (type == QLatin1String("animatedrect")) { return ParamType::AnimatedRect; } else if (type == QLatin1String("geometry")) { return ParamType::Geometry; } else if (type == QLatin1String("addedgeometry")) { return ParamType::Addedgeometry; } else if (type == QLatin1String("keyframe") || type == QLatin1String("animated")) { return ParamType::KeyframeParam; } else if (type == QLatin1String("color")) { return ParamType::Color; } else if (type == QLatin1String("colorwheel")) { return ParamType::ColorWheel; } else if (type == QLatin1String("position")) { return ParamType::Position; } else if (type == QLatin1String("curve")) { return ParamType::Curve; } else if (type == QLatin1String("bezier_spline")) { return ParamType::Bezier_spline; } else if (type == QLatin1String("roto-spline")) { return ParamType::Roto_spline; } else if (type == QLatin1String("wipe")) { return ParamType::Wipe; } else if (type == QLatin1String("url")) { return ParamType::Url; } else if (type == QLatin1String("keywords")) { return ParamType::Keywords; } else if (type == QLatin1String("fontfamily")) { return ParamType::Fontfamily; } else if (type == QLatin1String("filterjob")) { return ParamType::Filterjob; } else if (type == QLatin1String("readonly")) { return ParamType::Readonly; } else if (type == QLatin1String("hidden")) { return ParamType::Hidden; } qDebug() << "WARNING: Unknown type :" << type; return ParamType::Double; } // static QString AssetParameterModel::getDefaultKeyframes(int start, const QString &defaultValue, bool linearOnly) { QString keyframes = QString::number(start); if (linearOnly) { keyframes.append(QLatin1Char('=')); } else { switch (KdenliveSettings::defaultkeyframeinterp()) { case mlt_keyframe_discrete: keyframes.append(QStringLiteral("|=")); break; case mlt_keyframe_smooth: keyframes.append(QStringLiteral("~=")); break; default: keyframes.append(QLatin1Char('=')); break; } } keyframes.append(defaultValue); return keyframes; } // static QVariant AssetParameterModel::parseAttribute(const ObjectId &owner, const QString &attribute, const QDomElement &element, QVariant defaultValue) { if (!element.hasAttribute(attribute) && !defaultValue.isNull()) { return defaultValue; } ParamType type = paramTypeFromStr(element.attribute(QStringLiteral("type"))); QString content = element.attribute(attribute); if (content.contains(QLatin1Char('%'))) { std::unique_ptr &profile = pCore->getCurrentProfile(); int width = profile->width(); int height = profile->height(); int in = pCore->getItemIn(owner); int out = in + pCore->getItemDuration(owner); // replace symbols in the double parameter content.replace(QLatin1String("%maxWidth"), QString::number(width)) .replace(QLatin1String("%maxHeight"), QString::number(height)) .replace(QLatin1String("%width"), QString::number(width)) .replace(QLatin1String("%height"), QString::number(height)) .replace(QLatin1String("%out"), QString::number(out)); if (type == ParamType::Double || type == ParamType::Hidden) { // Use a Mlt::Properties to parse mathematical operators Mlt::Properties p; p.set("eval", content.prepend(QLatin1Char('@')).toLatin1().constData()); return p.get_double("eval"); } } else if (type == ParamType::Double || type == ParamType::Hidden) { if (attribute == QLatin1String("default")) { return content.toDouble(); } QLocale locale; locale.setNumberOptions(QLocale::OmitGroupSeparator); return locale.toDouble(content); } if (attribute == QLatin1String("default")) { if (type == ParamType::RestrictedAnim) { content = getDefaultKeyframes(0, content, true); } else if (type == ParamType::KeyframeParam) { return content.toDouble(); } else if (type == ParamType::List) { bool ok; double res = content.toDouble(&ok); if (ok) { return res; } return defaultValue.isNull() ? content : defaultValue; } else if (type == ParamType::Bezier_spline) { QLocale locale; if (locale.decimalPoint() != QLocale::c().decimalPoint()) { return content.replace(QLocale::c().decimalPoint(), locale.decimalPoint()); } } } return content; } QVariant AssetParameterModel::parseSubAttributes(const QString &attribute, const QDomElement &element) const { QDomNodeList nodeList = element.elementsByTagName(attribute); if (nodeList.isEmpty()) { return QVariant(); } QVariantList jobDataList; for (int i = 0; i < nodeList.count(); ++i) { QDomElement currentParameter = nodeList.item(i).toElement(); QStringList jobData {currentParameter.attribute(QStringLiteral("name")), currentParameter.text()}; jobDataList << jobData; } return jobDataList; } QString AssetParameterModel::getAssetId() const { return m_assetId; } QVector> AssetParameterModel::getAllParameters() const { QVector> res; res.reserve((int)m_fixedParams.size() + (int)m_params.size()); for (const auto &fixed : m_fixedParams) { res.push_back(QPair(fixed.first, fixed.second)); } for (const auto ¶m : m_params) { res.push_back(QPair(param.first, param.second.value)); } return res; } QJsonDocument AssetParameterModel::toJson(bool includeFixed) const { QJsonArray list; QLocale locale; if (includeFixed) { for (const auto &fixed : m_fixedParams) { QJsonObject currentParam; QModelIndex ix = index(m_rows.indexOf(fixed.first), 0); currentParam.insert(QLatin1String("name"), QJsonValue(fixed.first)); currentParam.insert(QLatin1String("value"), fixed.second.toString()); int type = data(ix, AssetParameterModel::TypeRole).toInt(); double min = data(ix, AssetParameterModel::MinRole).toDouble(); double max = data(ix, AssetParameterModel::MaxRole).toDouble(); double factor = data(ix, AssetParameterModel::FactorRole).toDouble(); int in = data(ix, AssetParameterModel::ParentInRole).toInt(); int out = in + data(ix, AssetParameterModel::ParentDurationRole).toInt(); if (factor > 0) { min /= factor; max /= factor; } currentParam.insert(QLatin1String("type"), QJsonValue(type)); currentParam.insert(QLatin1String("min"), QJsonValue(min)); currentParam.insert(QLatin1String("max"), QJsonValue(max)); currentParam.insert(QLatin1String("in"), QJsonValue(in)); currentParam.insert(QLatin1String("out"), QJsonValue(out)); list.push_back(currentParam); } } for (const auto ¶m : m_params) { if (!includeFixed && param.second.type != ParamType::KeyframeParam && param.second.type != ParamType::AnimatedRect) { continue; } QJsonObject currentParam; QModelIndex ix = index(m_rows.indexOf(param.first), 0); currentParam.insert(QLatin1String("name"), QJsonValue(param.first)); currentParam.insert(QLatin1String("value"), QJsonValue(param.second.value.toString())); int type = data(ix, AssetParameterModel::TypeRole).toInt(); double min = data(ix, AssetParameterModel::MinRole).toDouble(); double max = data(ix, AssetParameterModel::MaxRole).toDouble(); double factor = data(ix, AssetParameterModel::FactorRole).toDouble(); int in = data(ix, AssetParameterModel::ParentInRole).toInt(); int out = in + data(ix, AssetParameterModel::ParentDurationRole).toInt(); if (factor > 0) { min /= factor; max /= factor; } currentParam.insert(QLatin1String("type"), QJsonValue(type)); currentParam.insert(QLatin1String("min"), QJsonValue(min)); currentParam.insert(QLatin1String("max"), QJsonValue(max)); currentParam.insert(QLatin1String("in"), QJsonValue(in)); currentParam.insert(QLatin1String("out"), QJsonValue(out)); list.push_back(currentParam); } return QJsonDocument(list); } void AssetParameterModel::deletePreset(const QString &presetFile, const QString &presetName) { QJsonObject object; QJsonArray array; QFile loadFile(presetFile); if (loadFile.exists()) { if (loadFile.open(QIODevice::ReadOnly)) { QByteArray saveData = loadFile.readAll(); QJsonDocument loadDoc(QJsonDocument::fromJson(saveData)); if (loadDoc.isArray()) { qDebug() << " * * ** JSON IS AN ARRAY, DELETING: " << presetName; array = loadDoc.array(); QList toDelete; for (int i = 0; i < array.size(); i++) { QJsonValue val = array.at(i); if (val.isObject() && val.toObject().keys().contains(presetName)) { toDelete << i; } } for (int i : toDelete) { array.removeAt(i); } } else if (loadDoc.isObject()) { QJsonObject obj = loadDoc.object(); qDebug() << " * * ** JSON IS AN OBJECT, DELETING: " << presetName; if (obj.keys().contains(presetName)) { obj.remove(presetName); } else { qDebug() << " * * ** JSON DOES NOT CONTAIN: " << obj.keys(); } array.append(obj); } loadFile.close(); } else if (!loadFile.open(QIODevice::ReadWrite)) { // TODO: error message } } if (!loadFile.open(QIODevice::WriteOnly)) { // TODO: error message } loadFile.write(QJsonDocument(array).toJson()); } void AssetParameterModel::savePreset(const QString &presetFile, const QString &presetName) { QJsonObject object; QJsonArray array; QJsonDocument doc = toJson(); QFile loadFile(presetFile); if (loadFile.exists()) { if (loadFile.open(QIODevice::ReadOnly)) { QByteArray saveData = loadFile.readAll(); QJsonDocument loadDoc(QJsonDocument::fromJson(saveData)); if (loadDoc.isArray()) { array = loadDoc.array(); QList toDelete; for (int i = 0; i < array.size(); i++) { QJsonValue val = array.at(i); if (val.isObject() && val.toObject().keys().contains(presetName)) { toDelete << i; } } for (int i : toDelete) { array.removeAt(i); } } else if (loadDoc.isObject()) { QJsonObject obj = loadDoc.object(); if (obj.keys().contains(presetName)) { obj.remove(presetName); } array.append(obj); } loadFile.close(); } else if (!loadFile.open(QIODevice::ReadWrite)) { // TODO: error message } } if (!loadFile.open(QIODevice::WriteOnly)) { // TODO: error message } object[presetName] = doc.array(); array.append(object); loadFile.write(QJsonDocument(array).toJson()); } const QStringList AssetParameterModel::getPresetList(const QString &presetFile) const { QFile loadFile(presetFile); if (loadFile.exists() && loadFile.open(QIODevice::ReadOnly)) { QByteArray saveData = loadFile.readAll(); QJsonDocument loadDoc(QJsonDocument::fromJson(saveData)); if (loadDoc.isObject()) { qDebug() << "// PRESET LIST IS AN OBJECT!!!"; return loadDoc.object().keys(); } else if (loadDoc.isArray()) { qDebug() << "// PRESET LIST IS AN ARRAY!!!"; QStringList result; QJsonArray array = loadDoc.array(); for (auto &&i : array) { QJsonValue val = i; if (val.isObject()) { result << val.toObject().keys(); } } return result; } } return QStringList(); } const QVector> AssetParameterModel::loadPreset(const QString &presetFile, const QString &presetName) { QFile loadFile(presetFile); QVector> params; if (loadFile.exists() && loadFile.open(QIODevice::ReadOnly)) { QByteArray saveData = loadFile.readAll(); QJsonDocument loadDoc(QJsonDocument::fromJson(saveData)); if (loadDoc.isObject() && loadDoc.object().contains(presetName)) { qDebug() << "..........\n..........\nLOADING OBJECT JSON"; QJsonValue val = loadDoc.object().value(presetName); if (val.isObject()) { QVariantMap map = val.toObject().toVariantMap(); QMap::const_iterator i = map.constBegin(); while (i != map.constEnd()) { params.append({i.key(), i.value()}); ++i; } } } else if (loadDoc.isArray()) { QJsonArray array = loadDoc.array(); for (auto &&i : array) { QJsonValue val = i; if (val.isObject() && val.toObject().contains(presetName)) { QJsonValue preset = val.toObject().value(presetName); if (preset.isArray()) { QJsonArray paramArray = preset.toArray(); for (auto &&j : paramArray) { QJsonValue v1 = j; if (v1.isObject()) { QJsonObject ob = v1.toObject(); params.append({ob.value("name").toString(), ob.value("value").toVariant()}); } } } qDebug() << "// LOADED PRESET: " << presetName << "\n" << params; break; } } } } return params; } void AssetParameterModel::setParameters(const QVector> ¶ms) { QLocale locale; for (const auto ¶m : params) { if (param.second.type() == QVariant::Double) { setParameter(param.first, locale.toString(param.second.toDouble()), false); } else { setParameter(param.first, param.second.toString(), false); } } if (m_keyframes) { m_keyframes->refresh(); } emit dataChanged(index(0), index(m_rows.count()), {}); } ObjectId AssetParameterModel::getOwnerId() const { return m_ownerId; } void AssetParameterModel::addKeyframeParam(const QModelIndex &index) { if (m_keyframes) { m_keyframes->addParameter(index); } else { m_keyframes.reset(new KeyframeModelList(shared_from_this(), index, pCore->undoStack())); } } std::shared_ptr AssetParameterModel::getKeyframeModel() { return m_keyframes; } void AssetParameterModel::resetAsset(std::unique_ptr asset) { m_asset = std::move(asset); } bool AssetParameterModel::hasMoreThanOneKeyframe() const { if (m_keyframes) { return (!m_keyframes->isEmpty() && !m_keyframes->singleKeyframe()); } return false; } int AssetParameterModel::time_to_frames(const QString &time) { return m_asset->time_to_frames(time.toUtf8().constData()); } void AssetParameterModel::passProperties(Mlt::Properties &target) { target.set("_profile", pCore->getCurrentProfile()->get_profile(), 0); target.set_lcnumeric(m_asset->get_lcnumeric()); } diff --git a/src/bin/generators/generators.cpp b/src/bin/generators/generators.cpp index c6c1b634a..329cdd70f 100644 --- a/src/bin/generators/generators.cpp +++ b/src/bin/generators/generators.cpp @@ -1,202 +1,202 @@ /*************************************************************************** * Copyright (C) 2016 by Jean-Baptiste Mardelle (jb@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) any later version. * * * * 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, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #include "generators.h" #include "assets/abstractassetsrepository.hpp" #include "doc/kthumb.h" #include "effects/effectsrepository.hpp" #include "kdenlivesettings.h" #include "monitor/monitor.h" #include #include #include #include #include #include #include #include "core.h" #include "klocalizedstring.h" #include "kxmlgui_version.h" #include "profiles/profilemodel.hpp" #include #include #include #include #include #include #include Generators::Generators(Monitor *monitor, const QString &path, QWidget *parent) : QDialog(parent) , m_producer(nullptr) , m_timePos(nullptr) , m_container(nullptr) , m_preview(nullptr) { QFile file(path); QDomDocument doc; doc.setContent(&file, false); file.close(); QDomElement base = doc.documentElement(); if (base.tagName() == QLatin1String("generator")) { QString generatorTag = base.attribute(QStringLiteral("tag")); - setWindowTitle(base.firstChildElement(QStringLiteral("name")).text()); + setWindowTitle(i18n(base.firstChildElement(QStringLiteral("name")).text().toUtf8().data())); auto *lay = new QVBoxLayout(this); m_preview = new QLabel; m_preview->setMinimumSize(1, 1); lay->addWidget(m_preview); m_producer = new Mlt::Producer(pCore->getCurrentProfile()->profile(), generatorTag.toUtf8().constData()); m_pixmap = QPixmap::fromImage(KThumb::getFrame(m_producer, 0, pCore->getCurrentProfile()->width(), pCore->getCurrentProfile()->height())); m_preview->setPixmap(m_pixmap.scaledToWidth(m_preview->width())); auto *hlay = new QHBoxLayout; hlay->addWidget(new QLabel(i18n("Duration"))); m_timePos = new TimecodeDisplay(monitor->timecode(), this); if (base.hasAttribute(QStringLiteral("updateonduration"))) { connect(m_timePos, &TimecodeDisplay::timeCodeEditingFinished, this, &Generators::updateDuration); } hlay->addWidget(m_timePos); lay->addLayout(hlay); QWidget *frameWidget = new QWidget; lay->addWidget(frameWidget); m_view = new AssetParameterView(frameWidget); lay->addWidget(m_view); QString tag = base.attribute(QStringLiteral("tag"), QString()); QString id = base.hasAttribute(QStringLiteral("id")) ? base.attribute(QStringLiteral("id")) : tag; auto prop = std::make_unique(m_producer->get_properties()); m_assetModel.reset(new AssetParameterModel(std::move(prop), base, tag, {ObjectType::NoItem, -1})); // NOLINT m_view->setModel(m_assetModel, QSize(1920, 1080), false); connect(m_assetModel.get(), &AssetParameterModel::modelChanged, [this]() { updateProducer(); }); lay->addStretch(10); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); lay->addWidget(buttonBox); m_timePos->setValue(KdenliveSettings::color_duration()); } } void Generators::updateProducer() { int w = m_pixmap.width(); int h = m_pixmap.height(); m_pixmap = QPixmap::fromImage(KThumb::getFrame(m_producer, 0, w, h)); m_preview->setPixmap(m_pixmap.scaledToWidth(m_preview->width())); } void Generators::resizeEvent(QResizeEvent *event) { QDialog::resizeEvent(event); m_preview->setPixmap(m_pixmap.scaledToWidth(m_preview->width())); } Generators::~Generators() { delete m_timePos; } // static void Generators::getGenerators(const QStringList &producers, QMenu *menu) { const QStringList generatorFolders = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("generators"), QStandardPaths::LocateDirectory); const QStringList filters = QStringList() << QStringLiteral("*.xml"); for (const QString &folder : generatorFolders) { QDir directory(folder); const QStringList filesnames = directory.entryList(filters, QDir::Files); for (const QString &fname : filesnames) { QPair result = parseGenerator(directory.absoluteFilePath(fname), producers); if (!result.first.isEmpty()) { - QAction *action = menu->addAction(result.first); + QAction *action = menu->addAction(i18n(result.first.toUtf8().data())); action->setData(result.second); } } } } // static QPair Generators::parseGenerator(const QString &path, const QStringList &producers) { QPair result; QDomDocument doc; QFile file(path); doc.setContent(&file, false); file.close(); QDomElement base = doc.documentElement(); if (base.tagName() == QLatin1String("generator")) { QString generatorTag = base.attribute(QStringLiteral("tag")); if (producers.contains(generatorTag)) { result.first = base.firstChildElement(QStringLiteral("name")).text(); result.second = path; } } return result; } void Generators::updateDuration(int duration) { m_producer->set("length", duration); m_producer->set_in_and_out(0, duration - 1); updateProducer(); } QUrl Generators::getSavedClip(QString clipFolder) { if (clipFolder.isEmpty()) { clipFolder = KRecentDirs::dir(QStringLiteral(":KdenliveClipFolder")); } if (clipFolder.isEmpty()) { clipFolder = QDir::homePath(); } QFileDialog fd(this); fd.setDirectory(clipFolder); fd.setNameFilter(i18n("MLT playlist (*.mlt)")); fd.setAcceptMode(QFileDialog::AcceptSave); fd.setFileMode(QFileDialog::AnyFile); fd.setDefaultSuffix(QStringLiteral("mlt")); if (fd.exec() != QDialog::Accepted || fd.selectedUrls().isEmpty()) { return QUrl(); } QUrl url = fd.selectedUrls().constFirst(); if (url.isValid()) { #if KXMLGUI_VERSION_MINOR < 23 && KXMLGUI_VERSION_MAJOR == 5 // Since Plasma 5.7 (release at same time as KF 5.23, // the file dialog manages the overwrite check if (QFile::exists(url.toLocalFile())) { if (KMessageBox::warningYesNo(this, i18n("Output file already exists. Do you want to overwrite it?")) != KMessageBox::Yes) { return getSavedClip(url.toLocalFile()); } } #endif Mlt::Tractor trac(*m_producer->profile()); m_producer->set("length", m_timePos->getValue()); m_producer->set_in_and_out(0, m_timePos->getValue() - 1); trac.set_track(*m_producer, 0); Mlt::Consumer c(*m_producer->profile(), "xml", url.toLocalFile().toUtf8().constData()); c.connect(trac); c.run(); return url; } return QUrl(); }