diff --git a/src/assets/abstractassetsrepository.hpp b/src/assets/abstractassetsrepository.hpp
index afbc60dd0..fdc704eb7 100644
--- a/src/assets/abstractassetsrepository.hpp
+++ b/src/assets/abstractassetsrepository.hpp
@@ -1,121 +1,116 @@
/***************************************************************************
* 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 . *
***************************************************************************/
#ifndef ASSETSREPOSITORY_H
#define ASSETSREPOSITORY_H
#include "definitions.h"
#include
#include
#include
#include
#include
#include
/** @brief This class is the base class for assets (transitions or effets) repositories
*/
template class AbstractAssetsRepository
{
public:
AbstractAssetsRepository();
virtual ~AbstractAssetsRepository() = default;
/* @brief Returns true if a given asset exists
*/
bool exists(const QString &assetId) const;
/* @brief Returns a vector of pair (asset id, asset name)
*/
QVector> getNames() const;
/* @brief Return type of asset */
AssetType getType(const QString &assetId) const;
/* @brief Return name of asset */
Q_INVOKABLE QString getName(const QString &assetId) const;
/* @brief Return description of asset */
QString getDescription(const QString &assetId) const;
- /* @brief Set an asset as favorite (or not)*/
- virtual void setFavorite(const QString &assetId, bool favorite) = 0;
-
/* @brief Returns a DomElement representing the asset's properties */
QDomElement getXml(const QString &assetId) const;
protected:
struct Info
{
QString id; // identifier of the asset
QString mltId; //"tag" of the asset, that is the name of the mlt service
QString name, description, author, version_str;
int version{};
QDomElement xml;
AssetType type;
};
// Reads the blacklist file and populate appropriate structure
void parseBlackList(const QString &path);
void init();
virtual Mlt::Properties *retrieveListFromMlt() const = 0;
- virtual void parseFavorites() = 0;
/* @brief Parse some info from a mlt structure
@param res Datastructure to fill
@return true on success
*/
bool parseInfoFromMlt(const QString &assetId, Info &res);
/* @brief Returns the metadata associated with the given asset*/
virtual Mlt::Properties *getMetadata(const QString &assetId) = 0;
/* @brief Parse one asset from its XML content
@param res data structure to fill
@return true of success
*/
bool parseInfoFromXml(const QDomElement ¤tAsset, Info &res) const;
/* @brief Figure what is the type of the asset based on its metadata and store it in res*/
virtual void parseType(QScopedPointer &metadata, Info &res) = 0;
/* @brief Retrieves additional info about asset from a custom XML file
The resulting assets are stored in customAssets
*/
virtual void parseCustomAssetFile(const QString &file_name, std::unordered_map &customAssets) const = 0;
/* @brief Returns the path to custom XML description of the assets*/
virtual QStringList assetDirs() const = 0;
/* @brief Returns the path to the assets' blacklist*/
virtual QString assetBlackListPath() const = 0;
std::unordered_map m_assets;
QSet m_blacklist;
- QSet m_favorites;
};
#include "abstractassetsrepository.ipp"
#endif
diff --git a/src/assets/abstractassetsrepository.ipp b/src/assets/abstractassetsrepository.ipp
index 9ecb6bf78..b10ec84ab 100644
--- a/src/assets/abstractassetsrepository.ipp
+++ b/src/assets/abstractassetsrepository.ipp
@@ -1,317 +1,316 @@
/***************************************************************************
* 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 "xml/xml.hpp"
#include
#include
#include
#include
#include
#include
#ifdef Q_OS_MAC
#include
#endif
template AbstractAssetsRepository::AbstractAssetsRepository() = default;
template void AbstractAssetsRepository::init()
{
// Warning: Mlt::Factory::init() resets the locale to the default system value, make sure we keep correct locale
#ifndef Q_OS_MAC
setlocale(LC_NUMERIC, nullptr);
#else
setlocale(LC_NUMERIC_MASK, nullptr);
#endif
// Parse effects blacklist
parseBlackList(assetBlackListPath());
- parseFavorites();
// Retrieve the list of MLT's available assets.
QScopedPointer assets(retrieveListFromMlt());
int max = assets->count();
QString sox = QStringLiteral("sox.");
for (int i = 0; i < max; ++i) {
Info info;
QString name = assets->get_name(i);
info.id = name;
if (name.startsWith(sox)) {
// sox effects are not usage directly (parameters not available)
continue;
}
// qDebug() << "trying to parse " < customAssets;
for (const auto &dir : asset_dirs) {
QDir current_dir(dir);
QStringList filter;
filter << QStringLiteral("*.xml");
QStringList fileList = current_dir.entryList(filter, QDir::Files);
for (const auto &file : fileList) {
QString path = current_dir.absoluteFilePath(file);
parseCustomAssetFile(path, customAssets);
}
}
// We add the custom assets
for (const auto &custom : customAssets) {
// Custom assets should override default ones
m_assets[custom.first] = custom.second;
/*if (m_assets.count(custom.second.mltId) > 0) {
m_assets.erase(custom.second.mltId);
}
if (m_assets.count(custom.first) == 0) {
m_assets[custom.first] = custom.second;
} else {
qDebug() << "Error: conflicting asset name " << custom.first;
}*/
}
}
template void AbstractAssetsRepository::parseBlackList(const QString &path)
{
QFile blacklist_file(path);
if (blacklist_file.open(QIODevice::ReadOnly)) {
QTextStream stream(&blacklist_file);
QString line;
while (stream.readLineInto(&line)) {
line = line.simplified();
if (!line.isEmpty() && !line.startsWith('#')) {
m_blacklist.insert(line);
}
}
blacklist_file.close();
}
}
template bool AbstractAssetsRepository::parseInfoFromMlt(const QString &assetId, Info &res)
{
QScopedPointer metadata(getMetadata(assetId));
if (metadata && metadata->is_valid()) {
if (metadata->get("title") && metadata->get("identifier") && strlen(metadata->get("title")) > 0) {
QString id = metadata->get("identifier");
res.name = metadata->get("title");
res.name[0] = res.name[0].toUpper();
res.description = metadata->get("description");
res.description.append(QString(" (%1)").arg(id));
res.author = metadata->get("creator");
res.version_str = metadata->get("version");
res.version = ceil(100 * metadata->get_double("version"));
res.id = res.mltId = assetId;
parseType(metadata, res);
// Create params
QDomDocument doc;
QDomElement eff = doc.createElement(QStringLiteral("effect"));
eff.setAttribute(QStringLiteral("tag"), id);
eff.setAttribute(QStringLiteral("id"), id);
////qCDebug(KDENLIVE_LOG)<<"Effect: "<get_data("parameters"));
for (int j = 0; param_props.is_valid() && j < param_props.count(); ++j) {
QDomElement params = doc.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") && (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 = doc.createElement(QStringLiteral("name"));
pname.appendChild(doc.createTextNode(paramName));
params.appendChild(pname);
}
if (paramdesc.get("description")) {
QDomElement comment = doc.createElement(QStringLiteral("comment"));
comment.appendChild(doc.createTextNode(paramdesc.get("description")));
params.appendChild(comment);
}
eff.appendChild(params);
}
doc.appendChild(eff);
res.xml = eff;
return true;
}
}
return false;
}
template bool AbstractAssetsRepository::exists(const QString &assetId) const
{
return m_assets.count(assetId) > 0;
}
template QVector> AbstractAssetsRepository::getNames() const
{
QVector> res;
res.reserve((int)m_assets.size());
for (const auto &asset : m_assets) {
res.push_back({asset.first, asset.second.name});
}
std::sort(res.begin(), res.end(), [](const QPair &a, const QPair &b) { return a.second < b.second; });
return res;
}
template AssetType AbstractAssetsRepository::getType(const QString &assetId) const
{
Q_ASSERT(m_assets.count(assetId) > 0);
return m_assets.at(assetId).type;
}
template QString AbstractAssetsRepository::getName(const QString &assetId) const
{
Q_ASSERT(m_assets.count(assetId) > 0);
return m_assets.at(assetId).name;
}
template QString AbstractAssetsRepository::getDescription(const QString &assetId) const
{
Q_ASSERT(m_assets.count(assetId) > 0);
return m_assets.at(assetId).description;
}
template bool AbstractAssetsRepository::parseInfoFromXml(const QDomElement ¤tAsset, Info &res) const
{
QString tag = currentAsset.attribute(QStringLiteral("tag"), QString());
QString id = currentAsset.attribute(QStringLiteral("id"), QString());
if (id.isEmpty()) {
id = tag;
}
if (!exists(tag)) {
qDebug() << "++++++ Unknown asset : " << tag;
return false;
}
// Check if there is a maximal version set
if (currentAsset.hasAttribute(QStringLiteral("version"))) {
// a specific version of the filter is required
if (m_assets.at(tag).version < (int)(100 * currentAsset.attribute(QStringLiteral("version")).toDouble())) {
return false;
}
}
res = m_assets.at(tag);
res.id = id;
res.mltId = tag;
// Update description if the xml provide one
QString description = Xml::getSubTagContent(currentAsset, QStringLiteral("description"));
if (!description.isEmpty()) {
res.description = description;
res.description.append(QString(" (%1)").arg(tag));
}
// Update name if the xml provide one
QString name = Xml::getSubTagContent(currentAsset, QStringLiteral("name"));
if (!name.isEmpty()) {
res.name = name;
}
return true;
}
template QDomElement AbstractAssetsRepository::getXml(const QString &assetId) const
{
if (m_assets.count(assetId) == 0) {
qDebug() << "Error : Requesting info on unknown transition " << assetId;
return QDomElement();
}
return m_assets.at(assetId).xml.cloneNode().toElement();
}
diff --git a/src/assets/assetlist/model/assetfilter.cpp b/src/assets/assetlist/model/assetfilter.cpp
index 52bb300a7..2c0abcdfa 100644
--- a/src/assets/assetlist/model/assetfilter.cpp
+++ b/src/assets/assetlist/model/assetfilter.cpp
@@ -1,176 +1,188 @@
/***************************************************************************
* 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 "assetfilter.hpp"
#include "abstractmodel/abstracttreemodel.hpp"
#include "abstractmodel/treeitem.hpp"
#include "assettreemodel.hpp"
#include
+#include
AssetFilter::AssetFilter(QObject *parent)
: QSortFilterProxyModel(parent)
{
setFilterRole(Qt::DisplayRole);
setSortRole(Qt::DisplayRole);
setDynamicSortFilter(false);
}
void AssetFilter::setFilterName(bool enabled, const QString &pattern)
{
m_name_enabled = enabled;
m_name_value = pattern;
invalidateFilter();
if (rowCount() > 1) {
sort(0);
}
}
+bool AssetFilter::lessThan(const QModelIndex &left,
+ const QModelIndex &right) const
+{
+ QString leftData = sourceModel()->data(left).toString();
+ QString rightData = sourceModel()->data(right).toString();
+ if (rightData == i18n("Favorites")) {
+ return false;
+ }
+ return QString::localeAwareCompare(leftData, rightData) < 0;
+}
+
bool AssetFilter::filterName(const std::shared_ptr &item) const
{
if (!m_name_enabled) {
return true;
}
QString itemText = item->dataColumn(AssetTreeModel::nameCol).toString();
itemText = itemText.normalized(QString::NormalizationForm_D).remove(QRegExp(QStringLiteral("[^a-zA-Z0-9\\s]")));
QString patt = m_name_value.normalized(QString::NormalizationForm_D).remove(QRegExp(QStringLiteral("[^a-zA-Z0-9\\s]")));
return itemText.contains(patt, Qt::CaseInsensitive);
}
bool AssetFilter::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
QModelIndex row = sourceModel()->index(sourceRow, 0, sourceParent);
auto *model = static_cast(sourceModel());
std::shared_ptr item = model->getItemById((int)row.internalId());
if (item->dataColumn(AssetTreeModel::idCol) == QStringLiteral("root")) {
// In that case, we have a category. We hide it if it does not have children.
QModelIndex category = sourceModel()->index(sourceRow, 0, sourceParent);
if (!category.isValid()) {
return false;
}
bool accepted = false;
for (int i = 0; i < sourceModel()->rowCount(category) && !accepted; ++i) {
accepted = filterAcceptsRow(i, category);
}
return accepted;
}
return applyAll(item);
}
bool AssetFilter::isVisible(const QModelIndex &sourceIndex)
{
auto parent = sourceModel()->parent(sourceIndex);
return filterAcceptsRow(sourceIndex.row(), parent);
}
bool AssetFilter::applyAll(std::shared_ptr item) const
{
return filterName(item);
}
QModelIndex AssetFilter::getNextChild(const QModelIndex ¤t)
{
QModelIndex nextItem = current.sibling(current.row() + 1, current.column());
if (!nextItem.isValid()) {
QModelIndex folder = index(current.parent().row() + 1, 0, QModelIndex());
if (!folder.isValid()) {
return current;
}
while (folder.isValid() && rowCount(folder) == 0) {
folder = folder.sibling(folder.row() + 1, folder.column());
}
if (folder.isValid() && rowCount(folder) > 0) {
return index(0, current.column(), folder);
}
nextItem = current;
}
return nextItem;
}
QModelIndex AssetFilter::getPreviousChild(const QModelIndex ¤t)
{
QModelIndex nextItem = current.sibling(current.row() - 1, current.column());
if (!nextItem.isValid()) {
QModelIndex folder = index(current.parent().row() - 1, 0, QModelIndex());
if (!folder.isValid()) {
return current;
}
while (folder.isValid() && rowCount(folder) == 0) {
folder = folder.sibling(folder.row() - 1, folder.column());
}
if (folder.isValid() && rowCount(folder) > 0) {
return index(rowCount(folder) - 1, current.column(), folder);
}
nextItem = current;
}
return nextItem;
}
QModelIndex AssetFilter::firstVisibleItem(const QModelIndex ¤t)
{
if (current.isValid() && isVisible(mapToSource(current))) {
return current;
}
QModelIndex folder = index(0, 0, QModelIndex());
if (!folder.isValid()) {
return current;
}
while (folder.isValid() && rowCount(folder) == 0) {
folder = index(folder.row() + 1, 0, QModelIndex());
}
if (rowCount(folder) > 0) {
return index(0, 0, folder);
}
return current;
}
QModelIndex AssetFilter::getCategory(int catRow)
{
QModelIndex cat = index(catRow, 0, QModelIndex());
return cat;
}
QVariantList AssetFilter::getCategories()
{
QVariantList list;
for (int i = 0; i < sourceModel()->rowCount(); i++) {
QModelIndex cat = getCategory(i);
if (cat.isValid()) {
list << cat;
}
}
return list;
}
QModelIndex AssetFilter::getModelIndex(QModelIndex current)
{
QModelIndex sourceIndex = mapToSource(current);
return sourceIndex; // this returns an integer
}
QModelIndex AssetFilter::getProxyIndex(QModelIndex current)
{
QModelIndex sourceIndex = mapFromSource(current);
return sourceIndex; // this returns an integer
}
diff --git a/src/assets/assetlist/model/assetfilter.hpp b/src/assets/assetlist/model/assetfilter.hpp
index 9c556a202..22aa93351 100644
--- a/src/assets/assetlist/model/assetfilter.hpp
+++ b/src/assets/assetlist/model/assetfilter.hpp
@@ -1,70 +1,70 @@
/***************************************************************************
* 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 . *
***************************************************************************/
#ifndef ASSETFILTER_H
#define ASSETFILTER_H
#include
#include
/* @brief This class is used as a proxy model to filter an asset list based on given criterion (name, ...)
*/
class TreeItem;
class AssetFilter : public QSortFilterProxyModel
{
Q_OBJECT
public:
AssetFilter(QObject *parent = nullptr);
/* @brief Manage the name filter
@param enabled whether to enable this filter
@param pattern to match against effects' names
*/
void setFilterName(bool enabled, const QString &pattern);
/** @brief Returns true if the ModelIndex in the source model is visible after filtering
*/
bool isVisible(const QModelIndex &sourceIndex);
/** @brief If we are in favorite view, invalidate filter to refresh. Call this after a favorite has changed
*/
virtual void reloadFilterOnFavorite() = 0;
QVariantList getCategories();
Q_INVOKABLE QModelIndex getNextChild(const QModelIndex ¤t);
Q_INVOKABLE QModelIndex getPreviousChild(const QModelIndex ¤t);
Q_INVOKABLE QModelIndex firstVisibleItem(const QModelIndex ¤t);
Q_INVOKABLE QModelIndex getCategory(int catRow);
Q_INVOKABLE QModelIndex getModelIndex(QModelIndex current);
Q_INVOKABLE QModelIndex getProxyIndex(QModelIndex current);
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
-
+ bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
bool filterName(const std::shared_ptr &item) const;
/* @brief Apply all filter and returns true if the object should be kept after filtering */
virtual bool applyAll(std::shared_ptr item) const;
bool m_name_enabled{false};
QString m_name_value;
};
#endif
diff --git a/src/assets/assetlist/model/assettreemodel.cpp b/src/assets/assetlist/model/assettreemodel.cpp
index e4b12b1ca..36c57ef0b 100644
--- a/src/assets/assetlist/model/assettreemodel.cpp
+++ b/src/assets/assetlist/model/assettreemodel.cpp
@@ -1,128 +1,107 @@
/***************************************************************************
* 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 "assettreemodel.hpp"
#include "abstractmodel/treeitem.hpp"
#include "effects/effectsrepository.hpp"
#include "transitions/transitionsrepository.hpp"
int AssetTreeModel::nameCol = 0;
int AssetTreeModel::idCol = 1;
int AssetTreeModel::typeCol = 2;
int AssetTreeModel::favCol = 3;
AssetTreeModel::AssetTreeModel(QObject *parent)
: AbstractTreeModel(parent)
{
}
QHash AssetTreeModel::roleNames() const
{
QHash roles;
roles[IdRole] = "identifier";
roles[NameRole] = "name";
roles[FavoriteRole] = "favorite";
return roles;
}
QString AssetTreeModel::getName(const QModelIndex &index) const
{
if (!index.isValid()) {
return QString();
}
std::shared_ptr item = getItemById((int)index.internalId());
if (item->depth() == 1) {
return item->dataColumn(0).toString();
}
return item->dataColumn(AssetTreeModel::nameCol).toString();
}
bool AssetTreeModel::isFavorite(const QModelIndex &index) const
{
if (!index.isValid()) {
return false;
}
std::shared_ptr item = getItemById((int)index.internalId());
if (item->depth() == 1) {
return false;
}
return item->dataColumn(AssetTreeModel::favCol).toBool();
}
-void AssetTreeModel::setFavorite(const QModelIndex &index, bool favorite, bool isEffect)
-{
- if (!index.isValid()) {
- return;
- }
- std::shared_ptr item = getItemById((int)index.internalId());
- if (isEffect && item->depth() == 1) {
- return;
- }
- item->setData(AssetTreeModel::favCol, favorite);
- auto id = item->dataColumn(AssetTreeModel::idCol).toString();
- if (isEffect) {
- if (EffectsRepository::get()->exists(id)) {
- EffectsRepository::get()->setFavorite(id, favorite);
- }
- } else {
- if (TransitionsRepository::get()->exists(id)) {
- TransitionsRepository::get()->setFavorite(id, favorite);
- }
- }
-}
-
QString AssetTreeModel::getDescription(const QModelIndex &index) const
{
if (!index.isValid()) {
return QString();
}
std::shared_ptr item = getItemById((int)index.internalId());
if (item->depth() == 1) {
return QString();
}
auto id = item->dataColumn(AssetTreeModel::idCol).toString();
if (EffectsRepository::get()->exists(id)) {
return EffectsRepository::get()->getDescription(id);
}
if (TransitionsRepository::get()->exists(id)) {
return TransitionsRepository::get()->getDescription(id);
}
return QString();
}
QVariant AssetTreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
std::shared_ptr item = getItemById((int)index.internalId());
switch (role) {
case IdRole:
return item->dataColumn(AssetTreeModel::idCol);
case FavoriteRole:
return item->dataColumn(AssetTreeModel::favCol);
case NameRole:
+ case Qt::DisplayRole:
return item->dataColumn(index.column());
default:
return QVariant();
}
}
diff --git a/src/assets/assetlist/model/assettreemodel.hpp b/src/assets/assetlist/model/assettreemodel.hpp
index 42a4137c5..455d888d6 100644
--- a/src/assets/assetlist/model/assettreemodel.hpp
+++ b/src/assets/assetlist/model/assettreemodel.hpp
@@ -1,58 +1,58 @@
/***************************************************************************
* 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 . *
***************************************************************************/
#ifndef ASSETTREEMODEL_H
#define ASSETTREEMODEL_H
#include "abstractmodel/abstracttreemodel.hpp"
/* @brief This class represents an effect hierarchy to be displayed as a tree
*/
class TreeItem;
class QMenu;
class KActionCategory;
class AssetTreeModel : public AbstractTreeModel
{
public:
explicit AssetTreeModel(QObject *parent = nullptr);
enum { IdRole = Qt::UserRole + 1, NameRole, FavoriteRole };
// Helper function to retrieve name
QString getName(const QModelIndex &index) const;
// Helper function to retrieve description
QString getDescription(const QModelIndex &index) const;
// Helper function to retrieve if an effect is categorized as favorite
bool isFavorite(const QModelIndex &index) const;
- void setFavorite(const QModelIndex &index, bool favorite, bool isEffect);
QHash roleNames() const override;
QVariant data(const QModelIndex &index, int role) const override;
virtual void reloadAssetMenu(QMenu *effectsMenu, KActionCategory *effectActions) = 0;
+ virtual void setFavorite(const QModelIndex &index, bool favorite, bool isEffect) = 0;
// for convenience, we store the column of each data field
static int nameCol, idCol, favCol, typeCol;
protected:
};
#endif
diff --git a/src/assets/assetlist/view/assetlistwidget.cpp b/src/assets/assetlist/view/assetlistwidget.cpp
index 918bacc90..4d6769d8d 100644
--- a/src/assets/assetlist/view/assetlistwidget.cpp
+++ b/src/assets/assetlist/view/assetlistwidget.cpp
@@ -1,109 +1,110 @@
/***************************************************************************
* 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 "assetlistwidget.hpp"
#include "assets/assetlist/model/assetfilter.hpp"
#include "assets/assetlist/model/assettreemodel.hpp"
#include "assets/assetlist/view/qmltypes/asseticonprovider.hpp"
#include
#include
#include
#include
#include
AssetListWidget::AssetListWidget(QWidget *parent)
: QQuickWidget(parent)
{
KDeclarative::KDeclarative kdeclarative;
kdeclarative.setDeclarativeEngine(engine());
#if KDECLARATIVE_VERSION >= QT_VERSION_CHECK(5, 45, 0)
kdeclarative.setupEngine(engine());
kdeclarative.setupContext();
#else
kdeclarative.setupBindings();
#endif
}
AssetListWidget::~AssetListWidget()
{
// clear source
setSource(QUrl());
}
void AssetListWidget::setup()
{
setResizeMode(QQuickWidget::SizeRootObjectToView);
engine()->addImageProvider(QStringLiteral("asseticon"), m_assetIconProvider);
setSource(QUrl(QStringLiteral("qrc:/qml/assetList.qml")));
setFocusPolicy(Qt::StrongFocus);
}
void AssetListWidget::reset()
{
setSource(QUrl(QStringLiteral("qrc:/qml/assetList.qml")));
}
QString AssetListWidget::getName(const QModelIndex &index) const
{
return m_model->getName(m_proxyModel->mapToSource(index));
}
bool AssetListWidget::isFavorite(const QModelIndex &index) const
{
return m_model->isFavorite(m_proxyModel->mapToSource(index));
}
void AssetListWidget::setFavorite(const QModelIndex &index, bool favorite, bool isEffect)
{
m_model->setFavorite(m_proxyModel->mapToSource(index), favorite, isEffect);
+ m_proxyModel->sort(0);
}
QString AssetListWidget::getDescription(const QModelIndex &index) const
{
return m_model->getDescription(m_proxyModel->mapToSource(index));
}
void AssetListWidget::setFilterName(const QString &pattern)
{
m_proxyModel->setFilterName(!pattern.isEmpty(), pattern);
if (!pattern.isEmpty()) {
QVariantList mapped = m_proxyModel->getCategories();
QMetaObject::invokeMethod(rootObject(), "expandNodes", Qt::DirectConnection, Q_ARG(QVariant, mapped));
}
}
QVariantMap AssetListWidget::getMimeData(const QString &assetId) const
{
QVariantMap mimeData;
mimeData.insert(getMimeType(assetId), assetId);
return mimeData;
}
void AssetListWidget::activate(const QModelIndex &ix)
{
if (!ix.isValid()) {
return;
}
const QString assetId = m_model->data(m_proxyModel->mapToSource(ix), AssetTreeModel::IdRole).toString();
emit activateAsset(getMimeData(assetId));
}
diff --git a/src/effects/effectlist/model/effecttreemodel.cpp b/src/effects/effectlist/model/effecttreemodel.cpp
index 96ea0e187..5cd0de709 100644
--- a/src/effects/effectlist/model/effecttreemodel.cpp
+++ b/src/effects/effectlist/model/effecttreemodel.cpp
@@ -1,151 +1,181 @@
/***************************************************************************
* 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 "effecttreemodel.hpp"
#include "abstractmodel/treeitem.hpp"
#include "effects/effectsrepository.hpp"
#include "kdenlivesettings.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
EffectTreeModel::EffectTreeModel(QObject *parent)
: AssetTreeModel(parent)
, m_customCategory(nullptr)
{
}
std::shared_ptr EffectTreeModel::construct(const QString &categoryFile, QObject *parent)
{
std::shared_ptr self(new EffectTreeModel(parent));
- QList rootData;
- rootData << "Name"
- << "ID"
- << "Type"
- << "isFav";
+ QList rootData {"Name", "ID", "Type", "isFav"};
self->rootItem = TreeItem::construct(rootData, self, true);
QHash> effectCategory; // category in which each effect should land.
std::shared_ptr miscCategory = nullptr;
std::shared_ptr audioCategory = nullptr;
// We parse category file
if (!categoryFile.isEmpty()) {
QDomDocument doc;
QFile file(categoryFile);
doc.setContent(&file, false);
file.close();
QDomNodeList groups = doc.documentElement().elementsByTagName(QStringLiteral("group"));
// Create favorite group
- auto groupFav = self->rootItem->appendChild(QList{i18n("Favorites"), QStringLiteral("root")});
+ auto groupFav = self->rootItem->appendChild(QList{i18n("Favorites"), QStringLiteral("root"), -1});
+
+ effectCategory[QStringLiteral("kdenlive:favorites")] = groupFav;
auto groupLegacy = self->rootItem->appendChild(QList{i18n("Legacy"), QStringLiteral("root")});
for (int i = 0; i < groups.count(); i++) {
QString groupName = i18n(groups.at(i).firstChild().firstChild().nodeValue().toUtf8().constData());
if (!KdenliveSettings::gpu_accel() && groupName == i18n("GPU effects")) {
continue;
}
QStringList list = groups.at(i).toElement().attribute(QStringLiteral("list")).split(QLatin1Char(','), QString::SkipEmptyParts);
-
auto groupItem = self->rootItem->appendChild(QList{groupName, QStringLiteral("root")});
for (const QString &effect : list) {
- if (KdenliveSettings::favorite_effects().contains(effect)) {
- effectCategory[effect] = groupFav;
- } else {
- effectCategory[effect] = groupItem;
- }
+ effectCategory[effect] = groupItem;
}
}
// We also create "Misc", "Audio" and "Custom" categories
miscCategory = self->rootItem->appendChild(QList{i18n("Misc"), QStringLiteral("root")});
audioCategory = self->rootItem->appendChild(QList{i18n("Audio"), QStringLiteral("root")});
self->m_customCategory = self->rootItem->appendChild(QList{i18n("Custom"), QStringLiteral("root")});
} else {
// Flat view
miscCategory = self->rootItem;
audioCategory = self->rootItem;
self->m_customCategory = self->rootItem;
}
// We parse effects
auto allEffects = EffectsRepository::get()->getNames();
+ QString favCategory = QStringLiteral("kdenlive:favorites");
for (const auto &effect : allEffects) {
if (!KdenliveSettings::gpu_accel() && effect.first.contains(QLatin1String("movit."))) {
continue;
}
auto targetCategory = miscCategory;
EffectType type = EffectsRepository::get()->getType(effect.first);
if (effectCategory.contains(effect.first)) {
targetCategory = effectCategory[effect.first];
} else if (type == EffectType::Audio) {
targetCategory = audioCategory;
}
if (type == EffectType::Custom) {
targetCategory = self->m_customCategory;
}
// we create the data list corresponding to this profile
bool isFav = KdenliveSettings::favorite_effects().contains(effect.first);
//qDebug() << effect.second << effect.first << "in " << targetCategory->dataColumn(0).toString();
- QList data {effect.second, effect.first, QVariant::fromValue(type), isFav};
+ QList data {effect.second, effect.first, QVariant::fromValue(type), isFav, targetCategory->row()};
+ if (KdenliveSettings::favorite_effects().contains(effect.first) && effectCategory.contains(favCategory)) {
+ targetCategory = effectCategory[favCategory];
+ }
targetCategory->appendChild(data);
}
return self;
}
void EffectTreeModel::reloadEffect(const QString &path)
{
QPair asset = EffectsRepository::get()->reloadCustom(path);
if (asset.first.isEmpty() || m_customCategory == nullptr) {
return;
}
bool isFav = KdenliveSettings::favorite_effects().contains(asset.first);
QList data {asset.second, asset.first, QVariant::fromValue(EffectType::Custom), isFav};
m_customCategory->appendChild(data);
}
void EffectTreeModel::reloadAssetMenu(QMenu *effectsMenu, KActionCategory *effectActions)
{
for (int i = 0; i < rowCount(); i++) {
std::shared_ptr item = rootItem->child(i);
if (item->childCount() > 0) {
QMenu *catMenu = new QMenu(item->dataColumn(nameCol).toString(), effectsMenu);
effectsMenu->addMenu(catMenu);
for (int j = 0; j < item->childCount(); j++) {
std::shared_ptr child = item->child(j);
QAction *a = new QAction(child->dataColumn(nameCol).toString(), catMenu);
const QString id = child->dataColumn(idCol).toString();
a->setData(id);
catMenu->addAction(a);
effectActions->addAction("transition_" + id, a);
}
}
}
}
+
+void EffectTreeModel::setFavorite(const QModelIndex &index, bool favorite, bool isEffect)
+{
+ if (!index.isValid()) {
+ return;
+ }
+ std::shared_ptr item = getItemById((int)index.internalId());
+ if (isEffect && item->depth() == 1) {
+ return;
+ }
+ item->setData(AssetTreeModel::favCol, favorite);
+ auto id = item->dataColumn(AssetTreeModel::idCol).toString();
+ if (!EffectsRepository::get()->exists(id)) {
+ qDebug()<<"Trying to reparent unknown asset: "<childCount();
+ QStringList favs = KdenliveSettings::favorite_effects();
+ if (!favorite) {
+ int ix = item->dataColumn(4).toInt();
+ item->changeParent(rootItem->child(ix));
+ favs.removeAll(id);
+ } else {
+ for (int i = 0; i < max; i++) {
+ if (rootItem->child(i)->dataColumn(2).toInt() == -1) {
+ item->changeParent(rootItem->child(i));
+ break;
+ }
+ }
+ favs << id;
+ }
+ KdenliveSettings::setFavorite_effects(favs);
+}
diff --git a/src/effects/effectlist/model/effecttreemodel.hpp b/src/effects/effectlist/model/effecttreemodel.hpp
index 347f4bdfe..b46a8233f 100644
--- a/src/effects/effectlist/model/effecttreemodel.hpp
+++ b/src/effects/effectlist/model/effecttreemodel.hpp
@@ -1,47 +1,47 @@
/***************************************************************************
* 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 . *
***************************************************************************/
#ifndef EFFECTTREEMODEL_H
#define EFFECTTREEMODEL_H
#include "abstractmodel/abstracttreemodel.hpp"
#include "assets/assetlist/model/assettreemodel.hpp"
/* @brief This class represents an effect hierarchy to be displayed as a tree
*/
class TreeItem;
class EffectTreeModel : public AssetTreeModel
{
protected:
explicit EffectTreeModel(QObject *parent = nullptr);
public:
static std::shared_ptr construct(const QString &categoryFile, QObject *parent);
void reloadEffect(const QString &path);
void reloadAssetMenu(QMenu *effectsMenu, KActionCategory *effectActions) override;
-
+ void setFavorite(const QModelIndex &index, bool favorite, bool isEffect) override;
protected:
std::shared_ptr m_customCategory;
};
#endif
diff --git a/src/effects/effectsrepository.cpp b/src/effects/effectsrepository.cpp
index b5cbb63b4..d58bab35c 100644
--- a/src/effects/effectsrepository.cpp
+++ b/src/effects/effectsrepository.cpp
@@ -1,212 +1,196 @@
/***************************************************************************
* 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 "kdenlivesettings.h"
#include "profiles/profilemodel.hpp"
#include "xml/xml.hpp"
#include
#include
#include
#include
#include
#include
std::unique_ptr EffectsRepository::instance;
std::once_flag EffectsRepository::m_onceFlag;
EffectsRepository::EffectsRepository()
: AbstractAssetsRepository()
{
init();
// Check that our favorite effects are valid
QStringList invalidEffect;
for (const QString &effect : KdenliveSettings::favorite_effects()) {
if (!exists(effect)) {
invalidEffect << effect;
}
}
if (!invalidEffect.isEmpty()) {
pCore->displayMessage(i18n("Some of your favorite effects are invalid and were removed: %1", invalidEffect.join(QLatin1Char(','))), ErrorMessage);
QStringList newFavorites = KdenliveSettings::favorite_effects();
for (const QString &effect : invalidEffect) {
newFavorites.removeAll(effect);
}
KdenliveSettings::setFavorite_effects(newFavorites);
}
}
Mlt::Properties *EffectsRepository::retrieveListFromMlt() const
{
return pCore->getMltRepository()->filters();
}
-void EffectsRepository::parseFavorites()
-{
- m_favorites = KdenliveSettings::favorite_effects().toSet();
-}
-
-void EffectsRepository::setFavorite(const QString &id, bool favorite)
-{
- Q_ASSERT(exists(id));
- if (favorite) {
- m_favorites << id;
- } else {
- m_favorites.remove(id);
- }
- KdenliveSettings::setFavorite_effects(QStringList::fromSet(m_favorites));
-}
-
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. Duplicate found in"
<< file_name;
}
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 if (type == QLatin1String("hidden")) {
result.type = EffectType::Hidden;
} 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 QStringLiteral(":data/blacklisted_effects.txt");
}
std::unique_ptr 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
auto filter = std::make_unique(pCore->getCurrentProfile()->profile(), service_name.toLatin1().constData(), nullptr);
return filter;
}
bool EffectsRepository::hasInternalEffect(const QString &effectId) const
{
// Retrieve the list of MLT's available assets.
QScopedPointer assets(retrieveListFromMlt());
int max = assets->count();
for (int i = 0; i < max; ++i) {
if (assets->get_name(i) == effectId) {
return true;
}
}
return false;
}
QPair EffectsRepository::reloadCustom(const QString &path)
{
std::unordered_map customAssets;
parseCustomAssetFile(path, customAssets);
QPair result;
// TODO: handle files with several effects
for (const auto &custom : customAssets) {
// Custom assets should override default ones
m_assets[custom.first] = custom.second;
result.first = custom.first;
result.second = custom.second.mltId;
}
return result;
}
diff --git a/src/effects/effectsrepository.hpp b/src/effects/effectsrepository.hpp
index 954d770a9..df7767f10 100644
--- a/src/effects/effectsrepository.hpp
+++ b/src/effects/effectsrepository.hpp
@@ -1,84 +1,80 @@
/***************************************************************************
* 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 . *
***************************************************************************/
#ifndef EFFECTSREPOSITORY_H
#define EFFECTSREPOSITORY_H
#include "assets/abstractassetsrepository.hpp"
#include "definitions.h"
#include
#include
#include
#include
#include
/** @brief This class stores all the effects that can be added by the user.
* You can query any effect based on its name.
* Note that this class is a Singleton
*/
enum class EffectType { Video, Audio, Custom, Favorites, Hidden };
Q_DECLARE_METATYPE(EffectType)
class EffectsRepository : public AbstractAssetsRepository
{
public:
// Returns the instance of the Singleton
static std::unique_ptr &get();
/* @brief returns a fresh instance of the given effect */
std::unique_ptr getEffect(const QString &effectId) const;
/* @brief returns true if an effect exists in MLT (bypasses the blacklist/metadata parsing) */
bool hasInternalEffect(const QString &effectId) const;
- void setFavorite(const QString &id, bool favorite) override;
QPair reloadCustom(const QString &path);
protected:
// Constructor is protected because class is a Singleton
EffectsRepository();
/* Retrieves the list of all available effects from Mlt*/
Mlt::Properties *retrieveListFromMlt() const override;
- /* Retrieves the list of favorite effects */
- void parseFavorites() override;
-
/* @brief Retrieves additional info about effects from a custom XML file
The resulting assets are stored in customAssets
*/
void parseCustomAssetFile(const QString &file_name, std::unordered_map &customAssets) const override;
/* @brief Returns the path to the effects' blacklist*/
QString assetBlackListPath() const override;
QStringList assetDirs() const override;
void parseType(QScopedPointer &metadata, Info &res) override;
/* @brief Returns the metadata associated with the given asset*/
Mlt::Properties *getMetadata(const QString &assetId) override;
static std::unique_ptr instance;
static std::once_flag m_onceFlag; // flag to create the repository only once;
};
#endif
diff --git a/src/transitions/transitionlist/model/transitiontreemodel.cpp b/src/transitions/transitionlist/model/transitiontreemodel.cpp
index 00d7a5480..8a4293b5d 100644
--- a/src/transitions/transitionlist/model/transitiontreemodel.cpp
+++ b/src/transitions/transitionlist/model/transitiontreemodel.cpp
@@ -1,96 +1,115 @@
/***************************************************************************
* 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 "transitiontreemodel.hpp"
#include "abstractmodel/treeitem.hpp"
#include "kdenlivesettings.h"
#include "transitions/transitionsrepository.hpp"
#include
#include
#include
#include
TransitionTreeModel::TransitionTreeModel(QObject *parent)
: AssetTreeModel(parent)
{
}
std::shared_ptr TransitionTreeModel::construct(bool flat, QObject *parent)
{
std::shared_ptr self(new TransitionTreeModel(parent));
- QList rootData;
- rootData << "Name"
- << "ID"
- << "Type"
- << "isFav";
+ QList rootData {"Name", "ID", "Type", "isFav"};
self->rootItem = TreeItem::construct(rootData, self, true);
// We create categories, if requested
std::shared_ptr compoCategory, transCategory;
if (!flat) {
compoCategory = self->rootItem->appendChild(QList{i18n("Compositions"), QStringLiteral("root")});
transCategory = self->rootItem->appendChild(QList{i18n("Transitions"), QStringLiteral("root")});
}
// We parse transitions
auto allTransitions = TransitionsRepository::get()->getNames();
for (const auto &transition : allTransitions) {
if (!KdenliveSettings::gpu_accel() && transition.first.contains(QLatin1String("movit."))) {
// Hide GPU compositions when movit disabled
continue;
}
std::shared_ptr targetCategory = compoCategory;
TransitionType type = TransitionsRepository::get()->getType(transition.first);
if (type == TransitionType::AudioTransition || type == TransitionType::VideoTransition) {
targetCategory = transCategory;
}
if (flat) {
targetCategory = self->rootItem;
}
// we create the data list corresponding to this transition
bool isFav = KdenliveSettings::favorite_transitions().contains(transition.first);
//qDebug() << transition.second << transition.first << "in " << targetCategory->dataColumn(0).toString();
QList data {transition.second, transition.first, QVariant::fromValue(type), isFav};
targetCategory->appendChild(data);
}
return self;
}
void TransitionTreeModel::reloadAssetMenu(QMenu *effectsMenu, KActionCategory *effectActions)
{
for (int i = 0; i < rowCount(); i++) {
std::shared_ptr item = rootItem->child(i);
if (item->childCount() > 0) {
QMenu *catMenu = new QMenu(item->dataColumn(nameCol).toString(), effectsMenu);
effectsMenu->addMenu(catMenu);
for (int j = 0; j < item->childCount(); j++) {
std::shared_ptr child = item->child(j);
QAction *a = new QAction(child->dataColumn(nameCol).toString(), catMenu);
const QString id = child->dataColumn(idCol).toString();
a->setData(id);
catMenu->addAction(a);
effectActions->addAction("transition_" + id, a);
}
}
}
}
+
+void TransitionTreeModel::setFavorite(const QModelIndex &index, bool favorite, bool isEffect)
+{
+ if (!index.isValid()) {
+ return;
+ }
+ std::shared_ptr item = getItemById((int)index.internalId());
+ if (isEffect && item->depth() == 1) {
+ return;
+ }
+ item->setData(AssetTreeModel::favCol, favorite);
+ auto id = item->dataColumn(AssetTreeModel::idCol).toString();
+ QStringList favs = KdenliveSettings::favorite_effects();
+ if (favorite) {
+ favs << id;
+ } else {
+ favs.removeAll(id);
+ }
+ KdenliveSettings::setFavorite_effects(favs);
+ /*if (TransitionsRepository::get()->exists(id)) {
+ TransitionsRepository::get()->setFavorite(id, favorite);
+ }*/
+}
diff --git a/src/transitions/transitionlist/model/transitiontreemodel.hpp b/src/transitions/transitionlist/model/transitiontreemodel.hpp
index 082706e7a..ad150f12f 100644
--- a/src/transitions/transitionlist/model/transitiontreemodel.hpp
+++ b/src/transitions/transitionlist/model/transitiontreemodel.hpp
@@ -1,45 +1,46 @@
/***************************************************************************
* 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 . *
***************************************************************************/
#ifndef TRANSITIONTREEMODEL_H
#define TRANSITIONTREEMODEL_H
#include "abstractmodel/abstracttreemodel.hpp"
#include "assets/assetlist/model/assettreemodel.hpp"
/* @brief This class represents a transition hierarchy to be displayed as a tree
*/
class TreeItem;
class TransitionTreeModel : public AssetTreeModel
{
protected:
explicit TransitionTreeModel(QObject *parent);
public:
// if flat = true, then the categories are not created
static std::shared_ptr construct(bool flat = false, QObject *parent = nullptr);
void reloadAssetMenu(QMenu *effectsMenu, KActionCategory *effectActions) override;
+ void setFavorite(const QModelIndex &index, bool favorite, bool isEffect) override;
protected:
};
#endif
diff --git a/src/transitions/transitionsrepository.cpp b/src/transitions/transitionsrepository.cpp
index 05623ad3d..27906e560 100644
--- a/src/transitions/transitionsrepository.cpp
+++ b/src/transitions/transitionsrepository.cpp
@@ -1,197 +1,181 @@
/***************************************************************************
* 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();
QStringList invalidTransition;
for (const QString &effect : KdenliveSettings::favorite_transitions()) {
if (!exists(effect)) {
invalidTransition << effect;
}
}
if (!invalidTransition.isEmpty()) {
pCore->displayMessage(i18n("Some of your favorite compositions are invalid and were removed: %1", invalidTransition.join(QLatin1Char(','))),
ErrorMessage);
QStringList newFavorites = KdenliveSettings::favorite_transitions();
for (const QString &effect : invalidTransition) {
newFavorites.removeAll(effect);
}
KdenliveSettings::setFavorite_transitions(newFavorites);
}
}
Mlt::Properties *TransitionsRepository::retrieveListFromMlt() const
{
return pCore->getMltRepository()->transitions();
}
-void TransitionsRepository::parseFavorites()
-{
- m_favorites = KdenliveSettings::favorite_transitions().toSet();
-}
-
-void TransitionsRepository::setFavorite(const QString &id, bool favorite)
-{
- Q_ASSERT(exists(id));
- if (favorite) {
- m_favorites << id;
- } else {
- m_favorites.remove(id);
- }
- KdenliveSettings::setFavorite_transitions(QStringList::fromSet(m_favorites));
-}
-
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();
QString type = result.xml.attribute(QStringLiteral("type"), QString());
if (type == QLatin1String("hidden")) {
result.type = TransitionType::Hidden;
}
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()
{
// Disabled until same track transitions is implemented
return {/*QStringLiteral("composite"), QStringLiteral("dissolve")*/};
}
QString TransitionsRepository::assetBlackListPath() const
{
return QStringLiteral(":data/blacklisted_transitions.txt");
}
std::unique_ptr 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
auto transition = std::make_unique(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/transitions/transitionsrepository.hpp b/src/transitions/transitionsrepository.hpp
index 384a2a760..27bc7c36d 100644
--- a/src/transitions/transitionsrepository.hpp
+++ b/src/transitions/transitionsrepository.hpp
@@ -1,90 +1,86 @@
/***************************************************************************
* 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 . *
***************************************************************************/
#ifndef TRANSITIONSREPOSITORY_H
#define TRANSITIONSREPOSITORY_H
#include "assets/abstractassetsrepository.hpp"
#include "assets/model/assetparametermodel.hpp"
#include "definitions.h"
#include
#include
/** @brief This class stores all the transitions that can be added by the user.
* You can query any transitions based on its name.
* Note that this class is a Singleton
*/
enum class TransitionType { AudioComposition, VideoComposition, AudioTransition, VideoTransition, Favorites, Hidden };
Q_DECLARE_METATYPE(TransitionType)
class TransitionsRepository : public AbstractAssetsRepository
{
public:
// Returns the instance of the Singleton
static std::unique_ptr &get();
/* @brief Creates and return an instance of a transition given its id.
*/
std::unique_ptr getTransition(const QString &transitionId) const;
/* @brief returns true if the transition corresponding to @transitionId is a composition*/
bool isComposition(const QString &transitionId) const;
/* @brief Returns the id of the transition to be used for compositing */
const QString getCompositingTransition();
- void setFavorite(const QString &id, bool favorite) override;
protected:
// Constructor is protected because class is a Singleton
TransitionsRepository();
/* Retrieves the list of all available effects from Mlt*/
Mlt::Properties *retrieveListFromMlt() const override;
- /* Retrieves the list of favorite effects */
- void parseFavorites() override;
-
/* @brief Retrieves additional info about effects from a custom XML file
The resulting assets are stored in customAssets
*/
void parseCustomAssetFile(const QString &file_name, std::unordered_map &customAssets) const override;
/* @brief Returns the paths where the custom transitions' descriptions are stored */
QStringList assetDirs() const override;
/* @brief Returns the path to the transitions' blacklist*/
QString assetBlackListPath() const override;
void parseType(QScopedPointer &metadata, Info &res) override;
/* @brief Returns the metadata associated with the given asset*/
Mlt::Properties *getMetadata(const QString &assetId) override;
/* @brief Returns all transitions that can be represented as Single Track Transitions*/
static QSet getSingleTrackTransitions();
static std::unique_ptr instance;
static std::once_flag m_onceFlag; // flag to create the repository only once;
};
#endif