diff --git a/src/assets/view/widgets/boolparamwidget.cpp b/src/assets/view/widgets/boolparamwidget.cpp index 408b89026..f5d59791e 100644 --- a/src/assets/view/widgets/boolparamwidget.cpp +++ b/src/assets/view/widgets/boolparamwidget.cpp @@ -1,68 +1,67 @@ /*************************************************************************** * Copyright (C) 2016 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 "boolparamwidget.hpp" #include "assets/model/assetparametermodel.hpp" BoolParamWidget::BoolParamWidget(std::shared_ptr model, QModelIndex index, QWidget *parent) : AbstractParamWidget(std::move(model), index, parent) { setupUi(this); // setup the comment QString name = m_model->data(m_index, AssetParameterModel::NameRole).toString(); - bool checked = m_model->data(m_index, AssetParameterModel::ValueRole).toInt(); QString comment = m_model->data(m_index, AssetParameterModel::CommentRole).toString(); setToolTip(comment); m_labelComment->setText(comment); m_widgetComment->setHidden(true); // setup the name m_labelName->setText(m_model->data(m_index, Qt::DisplayRole).toString()); // set check state - m_checkBox->setChecked(checked); + slotRefresh(); // emit the signal of the base class when appropriate connect(this->m_checkBox, &QCheckBox::stateChanged, [this](int) { emit valueChanged(m_index, QString::number(m_checkBox->isChecked()), true); }); } void BoolParamWidget::slotShowComment(bool show) { if (!m_labelComment->text().isEmpty()) { m_widgetComment->setVisible(show); } } void BoolParamWidget::slotRefresh() { bool checked = m_model->data(m_index, AssetParameterModel::ValueRole).toInt(); m_checkBox->setChecked(checked); } bool BoolParamWidget::getValue() { return m_checkBox->isChecked(); } void BoolParamWidget::slotSetRange(QPair) { } diff --git a/src/assets/view/widgets/positioneditwidget.cpp b/src/assets/view/widgets/positioneditwidget.cpp index 989b94020..ee227c92d 100644 --- a/src/assets/view/widgets/positioneditwidget.cpp +++ b/src/assets/view/widgets/positioneditwidget.cpp @@ -1,127 +1,122 @@ /*************************************************************************** positionedit.cpp - description ------------------- begin : 03 Aug 2008 copyright : (C) 2008 by Marco Gittler email : g.marco@freenet.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "positioneditwidget.hpp" #include "kdenlivesettings.h" #include "timecodedisplay.h" #include "core.h" #include "monitor/monitormanager.h" #include "assets/model/assetparametermodel.hpp" #include #include #include PositionEditWidget::PositionEditWidget(std::shared_ptr model, QModelIndex index, QWidget *parent) : AbstractParamWidget(std::move(model), index, parent) { auto *layout = new QHBoxLayout(this); QString name = m_model->data(m_index, Qt::DisplayRole).toString(); QString comment = m_model->data(m_index, AssetParameterModel::CommentRole).toString(); //TODO: take absolute from effect data m_absolute = false; - int value = m_model->data(m_index, AssetParameterModel::ValueRole).toInt(); - int min = m_model->data(m_index, AssetParameterModel::ParentInRole).toInt(); - int max = min + m_model->data(m_index, AssetParameterModel::ParentDurationRole).toInt(); QLabel *label = new QLabel(name, this); m_slider = new QSlider(Qt::Horizontal, this); m_slider->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred)); m_display = new TimecodeDisplay(pCore->monitorManager()->timecode(), this); m_display->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred)); - slotSetRange(QPair(min, max)); layout->addWidget(label); layout->addWidget(m_slider); layout->addWidget(m_display); + slotRefresh(); - m_slider->setValue(value); - m_display->setValue(value); connect(m_slider, &QAbstractSlider::valueChanged, m_display, static_cast(&TimecodeDisplay::setValue)); connect(m_slider, &QAbstractSlider::valueChanged, this, &PositionEditWidget::valueChanged); // emit the signal of the base class when appropriate connect(this->m_slider, &QAbstractSlider::valueChanged, [this](int val) { emit AbstractParamWidget::valueChanged(m_index, QString::number(val), true); }); setToolTip(comment); } PositionEditWidget::~PositionEditWidget() { } void PositionEditWidget::updateTimecodeFormat() { m_display->slotUpdateTimeCodeFormat(); } int PositionEditWidget::getPosition() const { return m_slider->value(); } void PositionEditWidget::setPosition(int pos) { m_slider->setValue(pos); } void PositionEditWidget::slotUpdatePosition() { m_slider->blockSignals(true); m_slider->setValue(m_display->getValue()); m_slider->blockSignals(false); emit valueChanged(); } void PositionEditWidget::setAbsolute(bool absolute) { m_absolute = absolute; } void PositionEditWidget::slotSetRange(QPair range) { if (m_absolute) { m_slider->setRange(range.first, range.second); m_display->setRange(range.first, range.second); } else { m_slider->setRange(0, range.second - range.first); m_display->setRange(0, range.second - range.first); } } void PositionEditWidget::slotRefresh() { int min = m_model->data(m_index, AssetParameterModel::ParentInRole).toInt(); int max = min + m_model->data(m_index, AssetParameterModel::ParentDurationRole).toInt(); int val = m_model->data(m_index, AssetParameterModel::ValueRole).toInt(); m_slider->blockSignals(true); slotSetRange(QPair(min, max)); m_slider->setValue(val); m_display->setValue(val); m_slider->blockSignals(false); } bool PositionEditWidget::isValid() const { return m_slider->minimum() != m_slider->maximum(); } void PositionEditWidget::slotShowComment(bool show) { Q_UNUSED(show); } diff --git a/src/effects/effectsrepository.cpp b/src/effects/effectsrepository.cpp index 170541a1d..e4cffe597 100644 --- a/src/effects/effectsrepository.cpp +++ b/src/effects/effectsrepository.cpp @@ -1,151 +1,151 @@ /*************************************************************************** * 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 "effectsrepository.hpp" #include "core.h" #include "xml/xml.hpp" #include #include #include #include #include "profiles/profilemodel.hpp" #include std::unique_ptr EffectsRepository::instance; std::once_flag EffectsRepository::m_onceFlag; EffectsRepository::EffectsRepository() : AbstractAssetsRepository() { init(); } Mlt::Properties *EffectsRepository::retrieveListFromMlt() { return pCore->getMltRepository()->filters(); } Mlt::Properties *EffectsRepository::getMetadata(const QString &effectId) { return pCore->getMltRepository()->metadata(filter_type, effectId.toLatin1().data()); } void EffectsRepository::parseCustomAssetFile(const QString &file_name, std::unordered_map &customAssets) const { QFile file(file_name); QDomDocument doc; doc.setContent(&file, false); file.close(); QDomElement base = doc.documentElement(); if (base.tagName() == QLatin1String("effectgroup")) { // in that case we have a custom effect Info info; info.xml = base; info.type = EffectType::Custom; QString tag = base.attribute(QStringLiteral("tag"), QString()); QString id = base.hasAttribute(QStringLiteral("id")) ? base.attribute(QStringLiteral("id")) : tag; QString name = base.attribute(QStringLiteral("name"), QString()); info.name = name; info.id = id; info.mltId = tag; if (customAssets.count(id) > 0) { qDebug() << "Error: conflicting effect name" << id; } else { customAssets[id] = info; } return; } QDomNodeList effects = doc.elementsByTagName(QStringLiteral("effect")); int nbr_effect = effects.count(); if (nbr_effect == 0) { qDebug() << "+++++++++++++\nEffect broken: " << file_name << "\n+++++++++++"; return; } for (int i = 0; i < nbr_effect; ++i) { QDomNode currentNode = effects.item(i); if (currentNode.isNull()) { continue; } QDomElement currentEffect = currentNode.toElement(); Info result; bool ok = parseInfoFromXml(currentEffect, result); if (!ok) { continue; } if (customAssets.count(result.id) > 0) { qDebug() << "Warning: duplicate custom definition of effect" << result.id << "found. Only last one will be considered"; } result.xml = currentEffect; // Parse type information. QString type = currentEffect.attribute(QStringLiteral("type"), QString()); if (type == QLatin1String("audio")) { result.type = EffectType::Audio; } else if (type == QLatin1String("custom")) { result.type = EffectType::Custom; } else { result.type = EffectType::Video; } customAssets[result.id] = result; } } std::unique_ptr &EffectsRepository::get() { std::call_once(m_onceFlag, [] { instance.reset(new EffectsRepository()); }); return instance; } QStringList EffectsRepository::assetDirs() const { return QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("effects"), QStandardPaths::LocateDirectory); } void EffectsRepository::parseType(QScopedPointer &metadata, Info &res) { res.type = EffectType::Video; Mlt::Properties tags((mlt_properties)metadata->get_data("tags")); if (QString(tags.get(0)) == QLatin1String("Audio")) { res.type = EffectType::Audio; } } QString EffectsRepository::assetBlackListPath() const { - return QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("blacklisted_effects.txt")); + return QStringLiteral(":data/blacklisted_effects.txt"); } Mlt::Filter *EffectsRepository::getEffect(const QString &effectId) const { Q_ASSERT(exists(effectId)); QString service_name = m_assets.at(effectId).mltId; // We create the Mlt element from its name Mlt::Filter *filter = new Mlt::Filter(pCore->getCurrentProfile()->profile(), service_name.toLatin1().constData(), nullptr); return filter; } diff --git a/src/effectslist/initeffects.cpp b/src/effectslist/initeffects.cpp index 15ee33448..665e81b97 100644 --- a/src/effectslist/initeffects.cpp +++ b/src/effectslist/initeffects.cpp @@ -1,1059 +1,1059 @@ /*************************************************************************** initeffects.cpp - description ------------------- begin : Jul 2006 copyright : (C) 2006 by Jean-Baptiste Mardelle email : jb@ader.ch copyright : (C) 2008 Marco Gittler email : g.marco@freenet.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "initeffects.h" #include "effectslist.h" #include "kdenlivesettings.h" #include "mainwindow.h" #include "kdenlive_debug.h" #include #include #include #include #include #ifdef Q_OS_MAC #include #endif // static void initEffects::refreshLumas() { // Check for Kdenlive installed luma files, add empty string at start for no luma QStringList imagefiles; QStringList fileFilters; MainWindow::m_lumaFiles.clear(); fileFilters << QStringLiteral("*.png") << QStringLiteral("*.pgm"); QStringList customLumas = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("lumas"), QStandardPaths::LocateDirectory); customLumas.append(QString(mlt_environment("MLT_DATA")) + QStringLiteral("/lumas")); for (const QString &folder : customLumas) { QDir topDir(folder); QStringList folders = topDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot); for (const QString &f : folders) { QDir dir(topDir.absoluteFilePath(f)); QStringList filesnames = dir.entryList(fileFilters, QDir::Files); if (MainWindow::m_lumaFiles.contains(f)) { imagefiles = MainWindow::m_lumaFiles.value(f); } for (const QString &fname : filesnames) { imagefiles.append(dir.absoluteFilePath(fname)); } MainWindow::m_lumaFiles.insert(f, imagefiles); } } /* QStringList imagenamelist = QStringList() << i18n("None"); QStringList imagefiles = QStringList() << QString(); QStringList filters; filters << QStringLiteral("*.pgm") << QStringLiteral("*.png"); QStringList customLumas = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("lumas"), QStandardPaths::LocateDirectory); for (const QString & folder : customLumas) { QDir directory(folder); QStringList filesnames = directory.entryList(filters, QDir::Files); for (const QString & fname : filesnames) { imagenamelist.append(fname); imagefiles.append(directory.absoluteFilePath(fname)); } } // Check for MLT lumas QUrl folder(QString(mlt_environment("MLT_DATA")) + QDir::separator() + "lumas" + QDir::separator() + QString(mlt_environment("MLT_NORMALISATION"))); QDir lumafolder(folder.path()); QStringList filesnames = lumafolder.entryList(filters, QDir::Files); for (const QString & fname : filesnames) { imagenamelist.append(fname); imagefiles.append(lumafolder.absoluteFilePath(fname)); } //TODO adapt to Wipe transition QDomElement lumaTransition = MainWindow::transitions.getEffectByTag(QStringLiteral("luma"), QStringLiteral("luma")); QDomNodeList params = lumaTransition.elementsByTagName(QStringLiteral("parameter")); for (int i = 0; i < params.count(); ++i) { QDomElement e = params.item(i).toElement(); if (e.attribute(QStringLiteral("tag")) == QLatin1String("resource")) { e.setAttribute(QStringLiteral("paramlistdisplay"), imagenamelist.join(QLatin1Char(','))); e.setAttribute(QStringLiteral("paramlist"), imagefiles.join(QLatin1Char(';'))); break; } } QDomElement compositeTransition = MainWindow::transitions.getEffectByTag(QStringLiteral("composite"), QStringLiteral("composite")); params = compositeTransition.elementsByTagName(QStringLiteral("parameter")); for (int i = 0; i < params.count(); ++i) { QDomElement e = params.item(i).toElement(); if (e.attribute(QStringLiteral("tag")) == QLatin1String("luma")) { e.setAttribute(QStringLiteral("paramlistdisplay"), imagenamelist.join(QLatin1Char(','))); e.setAttribute(QStringLiteral("paramlist"), imagefiles.join(QLatin1Char(';'))); break; } }*/ } // static QDomDocument initEffects::getUsedCustomEffects(const QMap &effectids) { QMapIterator i(effectids); QDomDocument doc; QDomElement list = doc.createElement(QStringLiteral("customeffects")); doc.appendChild(list); while (i.hasNext()) { i.next(); int ix = MainWindow::customEffects.hasEffect(i.value(), i.key()); if (ix > -1) { QDomElement e = MainWindow::customEffects.at(ix); list.appendChild(doc.importNode(e, true)); } } return doc; } // static bool initEffects::parseEffectFiles(std::unique_ptr &repository, const QString &locale) { bool movit = false; QStringList::Iterator more; QStringList::Iterator it; QStringList fileList; QString itemName; if (!repository) { // qCDebug(KDENLIVE_LOG) << "Repository didn't finish initialisation" ; return movit; } // Warning: Mlt::Factory::init() resets the locale to the default system value, make sure we keep correct locale if (!locale.isEmpty()) { #ifndef Q_OS_MAC setlocale(LC_NUMERIC, locale.toUtf8().constData()); #else setlocale(LC_NUMERIC_MASK, locale.toUtf8().constData()); #endif } // Retrieve the list of MLT's available effects. Mlt::Properties *filters = repository->filters(); QStringList filtersList; int max = filters->count(); filtersList.reserve(max); for (int i = 0; i < max; ++i) { filtersList << filters->get_name(i); } delete filters; // Retrieve the list of available producers. Mlt::Properties *producers = repository->producers(); QStringList producersList; max = producers->count(); producersList.reserve(max); for (int i = 0; i < max; ++i) { producersList << producers->get_name(i); } KdenliveSettings::setProducerslist(producersList); delete producers; if (filtersList.contains(QStringLiteral("glsl.manager"))) { Mlt::Properties *consumers = repository->consumers(); QStringList consumersList; max = consumers->count(); consumersList.reserve(max); for (int i = 0; i < max; ++i) { consumersList << consumers->get_name(i); } delete consumers; movit = true; } else { KdenliveSettings::setGpu_accel(false); } // Retrieve the list of available transitions. Mlt::Properties *transitions = repository->transitions(); QStringList transitionsItemList; max = transitions->count(); for (int i = 0; i < max; ++i) { // qCDebug(KDENLIVE_LOG)<<"TRANSITION "<get_name(i); transitionsItemList << transitions->get_name(i); } delete transitions; // Create structure holding all transitions descriptions so that if an XML file has no description, we take it from MLT QMap transDescriptions; for (const QString &transname : transitionsItemList) { QDomDocument doc = createDescriptionFromMlt(repository, QStringLiteral("transitions"), transname); if (!doc.isNull()) { if (doc.elementsByTagName(QStringLiteral("description")).count() > 0) { QString desc = doc.documentElement().firstChildElement(QStringLiteral("description")).text(); if (!desc.isEmpty()) { transDescriptions.insert(transname, desc); } } } } transitionsItemList.sort(); // Get list of installed luma files refreshLumas(); // Parse xml transition files QStringList direc = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("transitions"), QStandardPaths::LocateDirectory); // Iterate through effects directories to parse all XML files. for (more = direc.begin(); more != direc.end(); ++more) { QDir directory(*more); QStringList filter; filter << QStringLiteral("*.xml"); fileList = directory.entryList(filter, QDir::Files); for (it = fileList.begin(); it != fileList.end(); ++it) { itemName = directory.absoluteFilePath(*it); parseTransitionFile(&MainWindow::transitions, itemName, repository, transitionsItemList, transDescriptions); } } // Remove blacklisted transitions from the list. - QFile file(QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("blacklisted_transitions.txt"))); + QFile file(QStringLiteral(":data/blacklisted_transitions.txt")); if (file.open(QIODevice::ReadOnly)) { QTextStream in(&file); while (!in.atEnd()) { QString black = in.readLine().simplified(); if (!black.isEmpty() && !black.startsWith('#') && transitionsItemList.contains(black)) { transitionsItemList.removeAll(black); } } file.close(); } // Fill transitions list. fillTransitionsList(repository, &MainWindow::transitions, transitionsItemList); // Remove blacklisted effects from the filters list. QStringList mltFiltersList = filtersList; QStringList mltBlackList; - QFile file2(QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("blacklisted_effects.txt"))); + QFile file2(QStringLiteral(":data/blacklisted_effects.txt")); if (file2.open(QIODevice::ReadOnly)) { QTextStream in(&file2); while (!in.atEnd()) { QString black = in.readLine().simplified(); if (!black.isEmpty() && !black.startsWith('#') && mltFiltersList.contains(black)) { mltFiltersList.removeAll(black); mltBlackList << black; } } file2.close(); } /* * Cleanup the global lists. We use QMap because of its automatic sorting * (by key) and key uniqueness (using insert() instead of insertMulti()). * This introduces some more cycles (while removing them from other parts of * the code and centralising them), but due to the way this methods, QMap * and EffectsList are implemented, there's no easy way to make it * differently without reinplementing something (which should really be * done). */ QDomElement effectInfo; QMap effectsMap; QMap videoEffectsMap; QMap audioEffectsMap; // Create transitions max = MainWindow::transitions.count(); for (int i = 0; i < max; ++i) { effectInfo = MainWindow::transitions.at(i); effectsMap.insert(effectInfo.firstChildElement(QStringLiteral("name")).text().toLower().toUtf8().data(), effectInfo); } MainWindow::transitions.clearList(); for (const QDomElement &effect : effectsMap) { MainWindow::transitions.append(effect); } effectsMap.clear(); // Create structure holding all effects descriptions so that if an XML effect has no description, we take it from MLT QMap effectDescriptions; for (const QString &filtername : mltBlackList) { QDomDocument doc = createDescriptionFromMlt(repository, QStringLiteral("filters"), filtername); if (!doc.isNull()) { if (doc.elementsByTagName(QStringLiteral("description")).count() > 0) { QString desc = doc.documentElement().firstChildElement(QStringLiteral("description")).text(); // WARNING: TEMPORARY FIX for unusable MLT SOX parameters description if (desc.startsWith(QLatin1String("Process audio using a SoX"))) { // Remove MLT's SOX generated effects since the parameters properties are unusable for us continue; } if (!desc.isEmpty()) { effectDescriptions.insert(filtername, desc); } } } } // Create effects from MLT for (const QString &filtername : mltFiltersList) { QDomDocument doc = createDescriptionFromMlt(repository, QStringLiteral("filters"), filtername); // WARNING: TEMPORARY FIX for empty MLT effects descriptions - disable effects without parameters - jbm 09-06-2011 if (!doc.isNull() && doc.elementsByTagName(QStringLiteral("parameter")).count() > 0) { if (doc.documentElement().attribute(QStringLiteral("type")) == QLatin1String("audio")) { if (doc.elementsByTagName(QStringLiteral("description")).count() > 0) { QString desc = doc.documentElement().firstChildElement(QStringLiteral("description")).text(); // WARNING: TEMPORARY FIX for unusable MLT SOX parameters description if (desc.startsWith(QLatin1String("Process audio using a SoX"))) { // Remove MLT's SOX generated effects since the parameters properties are unusable for us } else { audioEffectsMap.insert(doc.documentElement().firstChildElement(QStringLiteral("name")).text().toLower().toUtf8().data(), doc.documentElement()); } } } else { videoEffectsMap.insert(doc.documentElement().firstChildElement(QStringLiteral("name")).text().toLower().toUtf8().data(), doc.documentElement()); } } } // Set the directories to look into for effects. direc = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("effects"), QStandardPaths::LocateDirectory); // Iterate through effects directories to parse all XML files. for (more = direc.begin(); more != direc.end(); ++more) { QDir directory(*more); QStringList filter; filter << QStringLiteral("*.xml"); fileList = directory.entryList(filter, QDir::Files); for (it = fileList.begin(); it != fileList.end(); ++it) { itemName = directory.absoluteFilePath(*it); parseEffectFile(&MainWindow::customEffects, &MainWindow::audioEffects, &MainWindow::videoEffects, itemName, filtersList, producersList, repository, effectDescriptions); } } // Create custom effects max = MainWindow::customEffects.count(); for (int i = 0; i < max; ++i) { effectInfo = MainWindow::customEffects.at(i); if (effectInfo.tagName() == QLatin1String("effectgroup")) { effectsMap.insert(effectInfo.attribute(QStringLiteral("name")).toUtf8().data(), effectInfo); } else { effectsMap.insert(effectInfo.firstChildElement(QStringLiteral("name")).text().toUtf8().data(), effectInfo); } } MainWindow::customEffects.clearList(); for (const QDomElement &effect : effectsMap) { MainWindow::customEffects.append(effect); } effectsMap.clear(); // Create audio effects max = MainWindow::audioEffects.count(); for (int i = 0; i < max; ++i) { effectInfo = MainWindow::audioEffects.at(i); audioEffectsMap.insert(effectInfo.firstChildElement(QStringLiteral("name")).text().toLower().toUtf8().data(), effectInfo); } MainWindow::audioEffects.clearList(); for (const QDomElement &effect : audioEffectsMap) { MainWindow::audioEffects.append(effect); } // Create video effects max = MainWindow::videoEffects.count(); for (int i = 0; i < max; ++i) { effectInfo = MainWindow::videoEffects.at(i); videoEffectsMap.insert(effectInfo.firstChildElement(QStringLiteral("name")).text().toLower().toUtf8().data(), effectInfo); } MainWindow::videoEffects.clearList(); for (const QDomElement &effect : videoEffectsMap) { MainWindow::videoEffects.append(effect); } return movit; } // static void initEffects::parseCustomEffectsFile() { MainWindow::customEffects.clearList(); /* * Why a QMap? See parseEffectFiles(). It's probably useless here, but we * cannot be sure about it. */ QMap effectsMap; QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/effects"); QDir directory = QDir(path); QStringList filter; filter << QStringLiteral("*.xml"); const QStringList fileList = directory.entryList(filter, QDir::Files); /* * We need to declare these variables outside the foreach, or the QMap will * refer to non existing variables (QMap::insert() takes references as * parameters). */ QDomDocument doc; QDomNodeList effects; QDomElement e; int unknownGroupCount = 0; for (const QString &filename : fileList) { QString itemName = directory.absoluteFilePath(filename); QFile file(itemName); doc.setContent(&file, false); file.close(); QDomElement base = doc.documentElement(); if (base.tagName() == QLatin1String("effectgroup")) { QString groupName = base.attribute(QStringLiteral("name")); if (groupName.isEmpty()) { groupName = i18n("Group %1", unknownGroupCount); base.setAttribute(QStringLiteral("name"), groupName); unknownGroupCount++; } effectsMap.insert(groupName.toLower().toUtf8().data(), base); } else if (base.tagName() == QLatin1String("effect")) { effectsMap.insert(base.firstChildElement(QStringLiteral("name")).text().toLower().toUtf8().data(), base); } else { qCDebug(KDENLIVE_LOG) << "Unsupported effect file: " << itemName; } } for (const QDomElement &effect : effectsMap) { MainWindow::customEffects.append(effect); } } // static void initEffects::parseEffectFile(EffectsList *customEffectList, EffectsList *audioEffectList, EffectsList *videoEffectList, const QString &name, const QStringList &filtersList, const QStringList &producersList, std::unique_ptr &repository, const QMap &effectDescriptions) { QDomDocument doc; QFile file(name); doc.setContent(&file, false); file.close(); QDomElement documentElement; QDomNodeList effects; QDomElement base = doc.documentElement(); QStringList addedTags; effects = doc.elementsByTagName(QStringLiteral("effect")); int i = effects.count(); if (i == 0) { qCDebug(KDENLIVE_LOG) << "+++++++++++++\nEffect broken: " << name << "\n+++++++++++"; return; } bool needsLocaleConversion = false; i--; for (; i >= 0; i--) { QLocale locale; QDomNode n = effects.item(i); if (n.isNull()) { continue; } documentElement = n.toElement(); QString tag = documentElement.attribute(QStringLiteral("tag"), QString()); QString id = documentElement.hasAttribute(QStringLiteral("id")) ? documentElement.attribute(QStringLiteral("id")) : tag; if (addedTags.contains(id)) { // We already processed a version of that filter continue; } // If XML has no description, take it fom MLT's descriptions if (effectDescriptions.contains(tag)) { QDomNodeList desc = documentElement.elementsByTagName(QStringLiteral("description")); if (desc.isEmpty()) { QDomElement d = documentElement.ownerDocument().createElement(QStringLiteral("description")); QDomText value = documentElement.ownerDocument().createTextNode(effectDescriptions.value(tag)); d.appendChild(value); documentElement.appendChild(d); } } if (documentElement.hasAttribute(QStringLiteral("LC_NUMERIC"))) { // set a locale for that file locale = QLocale(documentElement.attribute(QStringLiteral("LC_NUMERIC"))); if (locale.decimalPoint() != QLocale().decimalPoint()) { needsLocaleConversion = true; } } locale.setNumberOptions(QLocale::OmitGroupSeparator); if (needsLocaleConversion) { // we need to convert all numbers to the system's locale (for example 0.5 -> 0,5) QChar separator = QLocale().decimalPoint(); QChar oldSeparator = locale.decimalPoint(); QDomNodeList params = documentElement.elementsByTagName(QStringLiteral("parameter")); for (int j = 0; j < params.count(); ++j) { QDomNamedNodeMap attrs = params.at(j).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); } } } } } double version = -1; Mlt::Properties *metadata = repository->metadata(filter_type, tag.toUtf8().data()); if ((metadata != nullptr) && metadata->is_valid()) { version = metadata->get_double("version"); } delete metadata; if (documentElement.hasAttribute(QStringLiteral("version"))) { // a specific version of the filter is required if (locale.toDouble(documentElement.attribute(QStringLiteral("version"))) > version) { continue; } } if (version > -1) { // Add version info to XML QDomNode versionNode = doc.createElement(QStringLiteral("version")); versionNode.appendChild(doc.createTextNode(QLocale().toString(version))); documentElement.appendChild(versionNode); } // Parse effect information. if (base.tagName() != QLatin1String("effectgroup") && (filtersList.contains(tag) || producersList.contains(tag))) { QString type = documentElement.attribute(QStringLiteral("type"), QString()); if (type == QLatin1String("audio")) { audioEffectList->append(documentElement); } else if (type == QLatin1String("custom")) { customEffectList->append(documentElement); } else { videoEffectList->append(documentElement); } addedTags << id; } } if (base.tagName() == QLatin1String("effectgroup")) { QString type = base.attribute(QStringLiteral("type"), QString()); if (type == QLatin1String("audio")) { audioEffectList->append(base); } else if (type == QLatin1String("custom")) { customEffectList->append(base); } else { videoEffectList->append(base); } } } QDomDocument initEffects::createDescriptionFromMlt(std::unique_ptr &repository, const QString & /*type*/, const QString &filtername) { QDomDocument ret; Mlt::Properties *metadata = repository->metadata(filter_type, filtername.toLatin1().data()); ////qCDebug(KDENLIVE_LOG) << filtername; if ((metadata != nullptr) && metadata->is_valid()) { if ((metadata->get("title") != nullptr) && (metadata->get("identifier") != nullptr) && strlen(metadata->get("title")) > 0) { QDomElement eff = ret.createElement(QStringLiteral("effect")); QString id = metadata->get("identifier"); eff.setAttribute(QStringLiteral("tag"), id); eff.setAttribute(QStringLiteral("id"), id); ////qCDebug(KDENLIVE_LOG)<<"Effect: "<get("title"); name_str[0] = name_str[0].toUpper(); name.appendChild(ret.createTextNode(name_str)); QDomElement desc = ret.createElement(QStringLiteral("description")); desc.appendChild(ret.createTextNode(metadata->get("description"))); QDomElement author = ret.createElement(QStringLiteral("author")); author.appendChild(ret.createTextNode(metadata->get("creator"))); QDomElement version = ret.createElement(QStringLiteral("version")); version.appendChild(ret.createTextNode(metadata->get("version"))); eff.appendChild(name); eff.appendChild(author); eff.appendChild(desc); eff.appendChild(version); Mlt::Properties tags((mlt_properties)metadata->get_data("tags")); if (QString(tags.get(0)) == QLatin1String("Audio")) { eff.setAttribute(QStringLiteral("type"), QStringLiteral("audio")); } /*for (int i = 0; i < tags.count(); ++i) //qCDebug(KDENLIVE_LOG)<get_data("parameters")); for (int j = 0; param_props.is_valid() && j < param_props.count(); ++j) { QDomElement params = ret.createElement(QStringLiteral("parameter")); Mlt::Properties paramdesc((mlt_properties)param_props.get_data(param_props.get_name(j))); params.setAttribute(QStringLiteral("name"), paramdesc.get("identifier")); if (params.attribute(QStringLiteral("name")) == QLatin1String("argument")) { // This parameter has to be given as attribute when using command line, do not show it in Kdenlive continue; } if ((paramdesc.get("readonly") != nullptr) && (strcmp(paramdesc.get("readonly"), "yes") == 0)) { // Do not expose readonly parameters continue; } if (paramdesc.get("maximum")) { params.setAttribute(QStringLiteral("max"), paramdesc.get("maximum")); } if (paramdesc.get("minimum")) { params.setAttribute(QStringLiteral("min"), paramdesc.get("minimum")); } QString paramType = paramdesc.get("type"); if (paramType == QLatin1String("integer")) { if (params.attribute(QStringLiteral("min")) == QLatin1String("0") && params.attribute(QStringLiteral("max")) == QLatin1String("1")) { params.setAttribute(QStringLiteral("type"), QStringLiteral("bool")); } else { params.setAttribute(QStringLiteral("type"), QStringLiteral("constant")); } } else if (paramType == QLatin1String("float")) { params.setAttribute(QStringLiteral("type"), QStringLiteral("constant")); // param type is float, set default decimals to 3 params.setAttribute(QStringLiteral("decimals"), QStringLiteral("3")); } else if (paramType == QLatin1String("boolean")) { params.setAttribute(QStringLiteral("type"), QStringLiteral("bool")); } else if (paramType == QLatin1String("geometry")) { params.setAttribute(QStringLiteral("type"), QStringLiteral("geometry")); } else if (paramType == QLatin1String("string")) { // string parameter are not really supported, so if we have a default value, enforce it params.setAttribute(QStringLiteral("type"), QStringLiteral("fixed")); if (paramdesc.get("default")) { QString stringDefault = paramdesc.get("default"); stringDefault.remove(QLatin1Char('\'')); params.setAttribute(QStringLiteral("value"), stringDefault); } else { // String parameter without default, skip it completely continue; } } else { params.setAttribute(QStringLiteral("type"), paramType); if (!QString(paramdesc.get("format")).isEmpty()) { params.setAttribute(QStringLiteral("format"), paramdesc.get("format")); } } if (!params.hasAttribute(QStringLiteral("value"))) { if (paramdesc.get("default")) { params.setAttribute(QStringLiteral("default"), paramdesc.get("default")); } if (paramdesc.get("value")) { params.setAttribute(QStringLiteral("value"), paramdesc.get("value")); } else { params.setAttribute(QStringLiteral("value"), paramdesc.get("default")); } } QString paramName = paramdesc.get("title"); if (!paramName.isEmpty()) { QDomElement pname = ret.createElement(QStringLiteral("name")); pname.appendChild(ret.createTextNode(paramName)); params.appendChild(pname); } if (paramdesc.get("description")) { QDomElement comment = ret.createElement(QStringLiteral("comment")); comment.appendChild(ret.createTextNode(paramdesc.get("description"))); params.appendChild(comment); } eff.appendChild(params); } ret.appendChild(eff); } } delete metadata; metadata = nullptr; /*QString outstr; QTextStream str(&outstr); ret.save(str, 2); //qCDebug(KDENLIVE_LOG) << outstr;*/ return ret; } void initEffects::fillTransitionsList(std::unique_ptr &repository, EffectsList *transitions, QStringList names) { // Remove transitions that are not implemented. int pos = names.indexOf(QStringLiteral("mix")); if (pos != -1) { names.takeAt(pos); } // Remove unused luma transition, replaced with a custom composite Wipe transition pos = names.indexOf(QStringLiteral("luma")); if (pos != -1) { names.takeAt(pos); } // WARNING: this is a hack to get around temporary invalid metadata in MLT, 2nd of june 2011 JBM QStringList customTransitions; customTransitions << QStringLiteral("composite") << QStringLiteral("affine") << QStringLiteral("mix") << QStringLiteral("region"); for (const QString &name : names) { QDomDocument ret; QDomElement ktrans = ret.createElement(QStringLiteral("transition")); ret.appendChild(ktrans); ktrans.setAttribute(QStringLiteral("tag"), name); QDomElement tname = ret.createElement(QStringLiteral("name")); QDomElement desc = ret.createElement(QStringLiteral("description")); ktrans.appendChild(tname); ktrans.appendChild(desc); Mlt::Properties *metadata = nullptr; if (!customTransitions.contains(name)) { metadata = repository->metadata(transition_type, name.toUtf8().data()); } if ((metadata != nullptr) && metadata->is_valid()) { // If possible, set name and description. // qCDebug(KDENLIVE_LOG)<<" / / FOUND TRANS: "<get("title"); if ((metadata->get("title") != nullptr) && (metadata->get("identifier") != nullptr)) { tname.appendChild(ret.createTextNode(metadata->get("title"))); } desc.appendChild(ret.createTextNode(metadata->get("description"))); Mlt::Properties param_props((mlt_properties)metadata->get_data("parameters")); for (int i = 0; param_props.is_valid() && i < param_props.count(); ++i) { QDomElement params = ret.createElement(QStringLiteral("parameter")); Mlt::Properties paramdesc((mlt_properties)param_props.get_data(param_props.get_name(i))); params.setAttribute(QStringLiteral("name"), paramdesc.get("identifier")); if (paramdesc.get("maximum")) { params.setAttribute(QStringLiteral("max"), paramdesc.get_double("maximum")); } if (paramdesc.get("minimum")) { params.setAttribute(QStringLiteral("min"), paramdesc.get_double("minimum")); } if (paramdesc.get("default")) { params.setAttribute(QStringLiteral("default"), paramdesc.get("default")); } if (QString(paramdesc.get("type")) == QLatin1String("integer")) { if (params.attribute(QStringLiteral("min")) == QLatin1String("0") && params.attribute(QStringLiteral("max")) == QLatin1String("1")) { params.setAttribute(QStringLiteral("type"), QStringLiteral("bool")); } else { params.setAttribute(QStringLiteral("type"), QStringLiteral("constant")); params.setAttribute(QStringLiteral("factor"), QStringLiteral("100")); } } else if (QString(paramdesc.get("type")) == QLatin1String("float")) { params.setAttribute(QStringLiteral("type"), QStringLiteral("double")); if (paramdesc.get_int("maximum") == 1) { params.setAttribute(QStringLiteral("factor"), 100); params.setAttribute(QStringLiteral("max"), 100); params.setAttribute(QStringLiteral("default"), paramdesc.get_double("default") * 100); } } else if (QString(paramdesc.get("type")) == QLatin1String("boolean")) { params.setAttribute(QStringLiteral("type"), QStringLiteral("bool")); } if (!QString(paramdesc.get("format")).isEmpty()) { params.setAttribute(QStringLiteral("type"), QStringLiteral("complex")); params.setAttribute(QStringLiteral("format"), paramdesc.get("format")); } if (paramdesc.get("value")) { params.setAttribute(QStringLiteral("value"), paramdesc.get("value")); } else { params.setAttribute(QStringLiteral("value"), params.attribute(QStringLiteral("default"))); } QDomElement pname = ret.createElement(QStringLiteral("name")); pname.appendChild(ret.createTextNode(paramdesc.get("title"))); params.appendChild(pname); ktrans.appendChild(params); } } else { /* * Check for Kdenlive installed luma files, add empty string at * start for no luma file. */ // Implement default transitions. // TODO: create xml files for transitions in data/transitions instead of hardcoding here QList paramList; if (name == QLatin1String("composite")) { ktrans.setAttribute(QStringLiteral("id"), name); tname.appendChild(ret.createTextNode(i18n("Composite"))); desc.appendChild(ret.createTextNode(i18n("A key-framable alpha-channel compositor for two frames."))); paramList.append(quickParameterFill(ret, i18n("Geometry"), QStringLiteral("geometry"), QStringLiteral("geometry"), QStringLiteral("0%/0%:100%x100%:100"), QStringLiteral("-500;-500;-500;-500;0"), QStringLiteral("500;500;500;500;100"))); paramList.append(quickParameterFill(ret, i18n("Alpha Channel Operation"), QStringLiteral("operator"), QStringLiteral("list"), QStringLiteral("over"), QString(), QString(), QStringLiteral("over,and,or,xor"), i18n("Over,And,Or,Xor"))); paramList.append(quickParameterFill(ret, i18n("Align"), QStringLiteral("aligned"), QStringLiteral("bool"), QStringLiteral("1"), QStringLiteral("0"), QStringLiteral("1"))); paramList.append(quickParameterFill(ret, i18n("Align"), QStringLiteral("valign"), QStringLiteral("fixed"), QStringLiteral("middle"), QStringLiteral("middle"), QStringLiteral("middle"))); paramList.append(quickParameterFill(ret, i18n("Align"), QStringLiteral("halign"), QStringLiteral("fixed"), QStringLiteral("centre"), QStringLiteral("centre"), QStringLiteral("centre"))); paramList.append(quickParameterFill(ret, i18n("Fill"), QStringLiteral("fill"), QStringLiteral("bool"), QStringLiteral("1"), QStringLiteral("0"), QStringLiteral("1"))); paramList.append(quickParameterFill(ret, i18n("Distort"), QStringLiteral("distort"), QStringLiteral("bool"), QStringLiteral("0"), QStringLiteral("0"), QStringLiteral("1"))); paramList.append(quickParameterFill(ret, i18n("Wipe Method"), QStringLiteral("luma"), QStringLiteral("list"), QString(), QString(), QString(), QStringLiteral("%lumaPaths"), QString())); paramList.append(quickParameterFill(ret, i18n("Wipe Softness"), QStringLiteral("softness"), QStringLiteral("double"), QStringLiteral("0"), QStringLiteral("0"), QStringLiteral("100"), QString(), QString(), QStringLiteral("100"))); paramList.append(quickParameterFill(ret, i18n("Wipe Invert"), QStringLiteral("luma_invert"), QStringLiteral("bool"), QStringLiteral("0"), QStringLiteral("0"), QStringLiteral("1"))); paramList.append(quickParameterFill(ret, i18n("Force Progressive Rendering"), QStringLiteral("progressive"), QStringLiteral("bool"), QStringLiteral("1"), QStringLiteral("0"), QStringLiteral("1"))); paramList.append(quickParameterFill(ret, i18n("Force Deinterlace Overlay"), QStringLiteral("deinterlace"), QStringLiteral("bool"), QStringLiteral("0"), QStringLiteral("0"), QStringLiteral("1"))); } else if (name == QLatin1String("affine")) { tname.appendChild(ret.createTextNode(i18n("Affine"))); ret.documentElement().setAttribute(QStringLiteral("showrotation"), QStringLiteral("1")); /*paramList.append(quickParameterFill(ret, i18n("Rotate Y"), "rotate_y", "double", "0", "0", "360")); paramList.append(quickParameterFill(ret, i18n("Rotate X"), "rotate_x", "double", "0", "0", "360")); paramList.append(quickParameterFill(ret, i18n("Rotate Z"), "rotate_z", "double", "0", "0", "360")); paramList.append(quickParameterFill(ret, i18n("Fix Rotate Y"), "fix_rotate_y", "double", "0", "0", "360")); paramList.append(quickParameterFill(ret, i18n("Fix Rotate X"), "fix_rotate_x", "double", "0", "0", "360")); paramList.append(quickParameterFill(ret, i18n("Fix Rotate Z"), "fix_rotate_z", "double", "0", "0", "360")); paramList.append(quickParameterFill(ret, i18n("Shear Y"), "shear_y", "double", "0", "0", "360")); paramList.append(quickParameterFill(ret, i18n("Shear X"), "shear_x", "double", "0", "0", "360")); paramList.append(quickParameterFill(ret, i18n("Shear Z"), "shear_z", "double", "0", "0", "360"));*/ /*paramList.append(quickParameterFill(ret, i18n("Fix Shear Y"), "fix_shear_y", "double", "0", "0", "360")); paramList.append(quickParameterFill(ret, i18n("Fix Shear X"), "fix_shear_x", "double", "0", "0", "360")); paramList.append(quickParameterFill(ret, i18n("Fix Shear Z"), "fix_shear_z", "double", "0", "0", "360"));*/ paramList.append(quickParameterFill(ret, QStringLiteral("keyed"), QStringLiteral("keyed"), QStringLiteral("fixed"), QStringLiteral("1"), QStringLiteral("1"), QStringLiteral("1"))); paramList.append(quickParameterFill(ret, i18n("Geometry"), QStringLiteral("geometry"), QStringLiteral("geometry"), QStringLiteral("0/0:100%x100%:100%"), QStringLiteral("0/0:100%x100%:100%"), QStringLiteral("0/0:100%x100%:100%"), QString(), QString(), QString(), QStringLiteral("true"))); paramList.append(quickParameterFill(ret, i18n("Distort"), QStringLiteral("distort"), QStringLiteral("bool"), QStringLiteral("0"), QStringLiteral("0"), QStringLiteral("1"))); paramList.append(quickParameterFill(ret, i18n("Rotate X"), QStringLiteral("rotate_x"), QStringLiteral("addedgeometry"), QStringLiteral("0"), QStringLiteral("-1800"), QStringLiteral("1800"), QString(), QString(), QStringLiteral("10"))); paramList.append(quickParameterFill(ret, i18n("Rotate Y"), QStringLiteral("rotate_y"), QStringLiteral("addedgeometry"), QStringLiteral("0"), QStringLiteral("-1800"), QStringLiteral("1800"), QString(), QString(), QStringLiteral("10"))); paramList.append(quickParameterFill(ret, i18n("Rotate Z"), QStringLiteral("rotate_z"), QStringLiteral("addedgeometry"), QStringLiteral("0"), QStringLiteral("-1800"), QStringLiteral("1800"), QString(), QString(), QStringLiteral("10"))); /*paramList.append(quickParameterFill(ret, i18n("Rotate Y"), "rotate_y", "simplekeyframe", "0", "-1800", "1800", QString(), QString(), "10")); paramList.append(quickParameterFill(ret, i18n("Rotate Z"), "rotate_z", "simplekeyframe", "0", "-1800", "1800", QString(), QString(), "10"));*/ paramList.append(quickParameterFill(ret, i18n("Fix Shear Y"), QStringLiteral("shear_y"), QStringLiteral("double"), QStringLiteral("0"), QStringLiteral("0"), QStringLiteral("360"))); paramList.append(quickParameterFill(ret, i18n("Fix Shear X"), QStringLiteral("shear_x"), QStringLiteral("double"), QStringLiteral("0"), QStringLiteral("0"), QStringLiteral("360"))); paramList.append(quickParameterFill(ret, i18n("Fix Shear Z"), QStringLiteral("shear_z"), QStringLiteral("double"), QStringLiteral("0"), QStringLiteral("0"), QStringLiteral("360"))); } else if (name == QLatin1String("mix")) { tname.appendChild(ret.createTextNode(i18n("Mix"))); } else if (name == QLatin1String("region")) { ktrans.setAttribute(QStringLiteral("id"), name); tname.appendChild(ret.createTextNode(i18n("Region"))); desc.appendChild(ret.createTextNode(i18n("Use alpha channel of another clip to create a transition."))); paramList.append(quickParameterFill(ret, i18n("Transparency clip"), QStringLiteral("resource"), QStringLiteral("url"), QString(), QString(), QString(), QString(), QString(), QString())); paramList.append(quickParameterFill(ret, i18n("Geometry"), QStringLiteral("composite.geometry"), QStringLiteral("geometry"), QStringLiteral("0%/0%:100%x100%:100"), QStringLiteral("-500;-500;-500;-500;0"), QStringLiteral("500;500;500;500;100"))); paramList.append(quickParameterFill(ret, i18n("Alpha Channel Operation"), QStringLiteral("composite.operator"), QStringLiteral("list"), QStringLiteral("over"), QString(), QString(), QStringLiteral("over,and,or,xor"), i18n("Over,And,Or,Xor"))); paramList.append(quickParameterFill(ret, i18n("Align"), QStringLiteral("composite.aligned"), QStringLiteral("bool"), QStringLiteral("1"), QStringLiteral("0"), QStringLiteral("1"))); paramList.append(quickParameterFill(ret, i18n("Fill"), QStringLiteral("composite.fill"), QStringLiteral("bool"), QStringLiteral("1"), QStringLiteral("0"), QStringLiteral("1"))); paramList.append(quickParameterFill(ret, i18n("Distort"), QStringLiteral("composite.distort"), QStringLiteral("bool"), QStringLiteral("0"), QStringLiteral("0"), QStringLiteral("1"))); paramList.append(quickParameterFill(ret, i18n("Wipe File"), QStringLiteral("composite.luma"), QStringLiteral("list"), QString(), QString(), QString(), QStringLiteral("%lumaPaths"), QString())); paramList.append(quickParameterFill(ret, i18n("Wipe Softness"), QStringLiteral("composite.softness"), QStringLiteral("double"), QStringLiteral("0"), QStringLiteral("0"), QStringLiteral("100"), QString(), QString(), QStringLiteral("100"))); paramList.append(quickParameterFill(ret, i18n("Wipe Invert"), QStringLiteral("composite.luma_invert"), QStringLiteral("bool"), QStringLiteral("0"), QStringLiteral("0"), QStringLiteral("1"))); paramList.append(quickParameterFill(ret, i18n("Force Progressive Rendering"), QStringLiteral("composite.progressive"), QStringLiteral("bool"), QStringLiteral("1"), QStringLiteral("0"), QStringLiteral("1"))); paramList.append(quickParameterFill(ret, i18n("Force Deinterlace Overlay"), QStringLiteral("composite.deinterlace"), QStringLiteral("bool"), QStringLiteral("0"), QStringLiteral("0"), QStringLiteral("1"))); } for (const QDomElement &e : paramList) { ktrans.appendChild(e); } } delete metadata; metadata = nullptr; // Add the transition to the global list. ////qCDebug(KDENLIVE_LOG) << ret.toString(); transitions->append(ret.documentElement()); } // Add some virtual transitions. QString slidetrans = QStringLiteral("") + i18n("Slide") + QStringLiteral("") + i18n("Slide image from one side to another.") + QStringLiteral("") + i18n("Direction") + QStringLiteral(" ") + i18n("Align") + QStringLiteral("") + i18n("Force Progressive Rendering") + QStringLiteral("") + i18n("Force Deinterlace Overlay") + QStringLiteral("") + i18nc("@property: means that the image is inverted", "Invert") + QStringLiteral(""); QDomDocument ret; ret.setContent(slidetrans); transitions->append(ret.documentElement()); QString dissolve = QStringLiteral("") + i18n("Dissolve") + QStringLiteral("") + i18n("Fade out one video while fading in the other video.") + QStringLiteral("") + i18n("Reverse") + QStringLiteral(""); ret.setContent(dissolve); transitions->append(ret.documentElement()); /*QString alphatrans = "" + i18n("Alpha Transparency") + QStringLiteral("") + i18n("Make alpha channel transparent.") + "" + i18n("Direction") + "" + i18n("Rescale") + "" + i18n("Align") + ""; ret.setContent(alphatrans); transitions->append(ret.documentElement());*/ } QDomElement initEffects::quickParameterFill(QDomDocument &doc, const QString &name, const QString &tag, const QString &type, const QString &def, const QString &min, const QString &max, const QString &list, const QString &listdisplaynames, const QString &factor, const QString &opacity) { QDomElement parameter = doc.createElement(QStringLiteral("parameter")); parameter.setAttribute(QStringLiteral("tag"), tag); parameter.setAttribute(QStringLiteral("default"), def); parameter.setAttribute(QStringLiteral("value"), def); parameter.setAttribute(QStringLiteral("type"), type); parameter.setAttribute(QStringLiteral("name"), tag); parameter.setAttribute(QStringLiteral("max"), max); parameter.setAttribute(QStringLiteral("min"), min); if (!list.isEmpty()) { parameter.setAttribute(QStringLiteral("paramlist"), list); } if (!listdisplaynames.isEmpty()) { parameter.setAttribute(QStringLiteral("paramlistdisplay"), listdisplaynames); } if (!factor.isEmpty()) { parameter.setAttribute(QStringLiteral("factor"), factor); } if (!opacity.isEmpty()) { parameter.setAttribute(QStringLiteral("opacity"), opacity); } QDomElement pname = doc.createElement(QStringLiteral("name")); pname.appendChild(doc.createTextNode(name)); parameter.appendChild(pname); return parameter; } // static void initEffects::parseTransitionFile(EffectsList *transitionList, const QString &name, std::unique_ptr &repository, const QStringList &installedTransitions, const QMap &effectDescriptions) { QFile file(name); if (!file.open(QIODevice::ReadOnly)) { return; } QDomDocument doc; QTextStream out(&file); QString fileContent = out.readAll(); file.close(); doc.setContent(fileContent, false); QDomElement documentElement; QDomNodeList effects; QDomElement base = doc.documentElement(); QStringList addedTags; effects = doc.elementsByTagName(QStringLiteral("transition")); int i = effects.count(); if (i == 0) { qCDebug(KDENLIVE_LOG) << "+++++++++++++Transition broken: " << name << "\n+++++++++++"; return; } bool needsLocaleConversion = false; i--; for (; i >= 0; i--) { QLocale locale; QDomNode n = effects.item(i); if (n.isNull()) { continue; } documentElement = n.toElement(); QString tag = documentElement.attribute(QStringLiteral("tag")); if (!installedTransitions.contains(tag)) { // This transition is not available return; } QString id = documentElement.attribute(QStringLiteral("id")); if (addedTags.contains(id)) { // We already processed a version of that filter continue; } // If XML has no description, take it fom MLT's descriptions if (effectDescriptions.contains(id)) { QDomNodeList desc = documentElement.elementsByTagName(QStringLiteral("description")); if (desc.isEmpty()) { QDomElement d = documentElement.ownerDocument().createElement(QStringLiteral("description")); QDomText value = documentElement.ownerDocument().createTextNode(effectDescriptions.value(id)); d.appendChild(value); documentElement.appendChild(d); } } if (documentElement.hasAttribute(QStringLiteral("LC_NUMERIC"))) { // set a locale for that file locale = QLocale(documentElement.attribute(QStringLiteral("LC_NUMERIC"))); if (locale.decimalPoint() != QLocale().decimalPoint()) { needsLocaleConversion = true; } } locale.setNumberOptions(QLocale::OmitGroupSeparator); if (needsLocaleConversion) { // we need to convert all numbers to the system's locale (for example 0.5 -> 0,5) QChar separator = QLocale().decimalPoint(); QChar oldSeparator = locale.decimalPoint(); QDomNodeList params = documentElement.elementsByTagName(QStringLiteral("parameter")); for (int j = 0; j < params.count(); ++j) { QDomNamedNodeMap attrs = params.at(j).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); } } } } } double version = -1; Mlt::Properties *metadata = repository->metadata(transition_type, id.toUtf8().data()); if ((metadata != nullptr) && metadata->is_valid()) { version = metadata->get_double("version"); } delete metadata; if (documentElement.hasAttribute(QStringLiteral("version"))) { // a specific version of the filter is required if (locale.toDouble(documentElement.attribute(QStringLiteral("version"))) > version) { continue; } } if (version > -1) { // Add version info to XML QDomNode versionNode = doc.createElement(QStringLiteral("version")); versionNode.appendChild(doc.createTextNode(QLocale().toString(version))); documentElement.appendChild(versionNode); } addedTags << id; } transitionList->append(base); } diff --git a/src/transitions/transitionsrepository.cpp b/src/transitions/transitionsrepository.cpp index 1325bc20e..2203a7a89 100644 --- a/src/transitions/transitionsrepository.cpp +++ b/src/transitions/transitionsrepository.cpp @@ -1,161 +1,161 @@ /*************************************************************************** * 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 "transitionsrepository.hpp" #include "core.h" #include "kdenlivesettings.h" #include "xml/xml.hpp" #include #include #include #include #include "profiles/profilemodel.hpp" #include std::unique_ptr TransitionsRepository::instance; std::once_flag TransitionsRepository::m_onceFlag; TransitionsRepository::TransitionsRepository() : AbstractAssetsRepository() { init(); } Mlt::Properties *TransitionsRepository::retrieveListFromMlt() { return pCore->getMltRepository()->transitions(); } Mlt::Properties *TransitionsRepository::getMetadata(const QString &assetId) { return pCore->getMltRepository()->metadata(transition_type, assetId.toLatin1().data()); } void TransitionsRepository::parseCustomAssetFile(const QString &file_name, std::unordered_map &customAssets) const { QFile file(file_name); QDomDocument doc; doc.setContent(&file, false); file.close(); QDomElement base = doc.documentElement(); QDomNodeList transitions = doc.elementsByTagName(QStringLiteral("transition")); int nbr_transition = transitions.count(); if (nbr_transition == 0) { qDebug() << "+++++++++++++\n Transition broken: " << file_name << "\n+++++++++++"; return; } for (int i = 0; i < nbr_transition; ++i) { QDomNode currentNode = transitions.item(i); if (currentNode.isNull()) { continue; } Info result; bool ok = parseInfoFromXml(currentNode.toElement(), result); if (!ok) { continue; } if (customAssets.count(result.id) > 0) { qDebug() << "Warning: duplicate custom definition of transition" << result.id << "found. Only last one will be considered"; } result.xml = currentNode.toElement(); customAssets[result.id] = result; } } std::unique_ptr &TransitionsRepository::get() { std::call_once(m_onceFlag, [] { instance.reset(new TransitionsRepository()); }); return instance; } QStringList TransitionsRepository::assetDirs() const { return QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("transitions"), QStandardPaths::LocateDirectory); } void TransitionsRepository::parseType(QScopedPointer &metadata, Info &res) { Mlt::Properties tags((mlt_properties)metadata->get_data("tags")); bool audio = QString(tags.get(0)) == QLatin1String("Audio"); if (getSingleTrackTransitions().contains(res.id)) { if (audio) { res.type = TransitionType::AudioTransition; } else { res.type = TransitionType::VideoTransition; } } else { if (audio) { res.type = TransitionType::AudioComposition; } else { res.type = TransitionType::VideoComposition; } } } QSet TransitionsRepository::getSingleTrackTransitions() { return {QStringLiteral("composite"), QStringLiteral("dissolve")}; } QString TransitionsRepository::assetBlackListPath() const { - return QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("blacklisted_transitions.txt")); + return QStringLiteral(":data/blacklisted_transitions.txt"); } Mlt::Transition *TransitionsRepository::getTransition(const QString &transitionId) const { Q_ASSERT(exists(transitionId)); QString service_name = m_assets.at(transitionId).mltId; // We create the Mlt element from its name Mlt::Transition *transition = new Mlt::Transition(pCore->getCurrentProfile()->profile(), service_name.toLatin1().constData(), nullptr); transition->set("kdenlive_id", transitionId.toUtf8().constData()); return transition; } bool TransitionsRepository::isComposition(const QString &transitionId) const { auto type = getType(transitionId); return type == TransitionType::AudioComposition || type == TransitionType::VideoComposition; } const QString TransitionsRepository::getCompositingTransition() { if (KdenliveSettings::gpu_accel()) { return QStringLiteral("movit.overlay"); } if (exists(QStringLiteral("qtblend"))) { return QStringLiteral("qtblend"); } if (exists(QStringLiteral("frei0r.cairoblend"))) { return QStringLiteral("frei0r.cairoblend"); } if (exists(QStringLiteral("composite"))) { return QStringLiteral("composite"); } qDebug() << "Warning: no compositing found"; return QString(); } diff --git a/src/uiresources.qrc b/src/uiresources.qrc index 8d4d15549..757b7f704 100644 --- a/src/uiresources.qrc +++ b/src/uiresources.qrc @@ -1,36 +1,40 @@ kdenliveui.rc monitor/view/kdenlivemonitor.qml monitor/view/kdenliveclipmonitor.qml monitor/view/kdenlivemonitoreffectscene.qml monitor/view/kdenlivemonitorcornerscene.qml monitor/view/kdenlivemonitorrotoscene.qml monitor/view/kdenlivemonitorsplit.qml monitor/view/kdenlivemonitorripple.qml monitor/view/SceneToolBar.qml monitor/view/EffectToolBar.qml monitor/view/MonitorRuler.qml timeline2/view/qml/timeline.qml timeline2/view/qml/TrackHead.qml timeline2/view/qml/Track.qml timeline2/view/qml/Ruler.qml timeline2/view/qml/Clip.qml timeline2/view/qml/KeyframeView.qml timeline2/view/qml/Composition.qml timeline2/view/qml/ClipMenu.qml timeline2/view/qml/CompositionMenu.qml timeline2/view/qml/PulsingAnimation.qml timeline2/view/qml/CornerSelectionShadow.qml timeline2/view/qml/Timeline.js assets/assetlist/view/qml/assetList.qml assets/view/qml/AssetView.qml transitions/view/qml/transitionView.qml timeline2/view/qml/AssetMenu.qml effects/effectstack/view/qml/BuiltStack.qml effects/effectstack/view/qml/EffectSlider.qml effects/effectstack/view/qml/LiftGammaGain.qml + + ../data/blacklisted_effects.txt + ../data/blacklisted_transitions.txt +