Commit 870a07b8 authored by Nicolas Carion's avatar Nicolas Carion

Starting to convert EffectList to a QAbstractItemModel

parent 8aa278e1
......@@ -92,6 +92,7 @@ install(FILES kdenlivesettings.kcfg DESTINATION ${KCFG_INSTALL_DIR})
kconfig_add_kcfg_files(kdenlive_SRCS kdenlivesettings.kcfgc)
add_subdirectory(abstractmodel)
add_subdirectory(effects)
add_subdirectory(doc)
add_subdirectory(project)
add_subdirectory(timeline)
......
set(kdenlive_SRCS
${kdenlive_SRCS}
effects/effectsrepository.cpp
effects/effectlist/view/effectlistwidget.cpp
effects/effectlist/view/qmltypes/effecticonprovider.cpp
effects/effectlist/model/effecttreemodel.cpp
effects/effectlist/model/effectfilter.cpp
PARENT_SCOPE)
/***************************************************************************
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include "effectfilter.hpp"
#include "effecttreemodel.hpp"
#include "abstractmodel/treeitem.hpp"
EffectFilter::EffectFilter(QObject *parent)
: QSortFilterProxyModel(parent)
{
m_name_enabled = m_type_enabled = false;
}
void EffectFilter::setFilterName(bool enabled, const QString& pattern)
{
m_name_enabled = enabled;
m_name_value = pattern;
invalidateFilter();
}
bool EffectFilter::filterName(TreeItem* item) const
{
if (!m_name_enabled) {
return true;
}
QString itemText = item->data(EffectTreeModel::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);
}
void EffectFilter::setFilterType(bool enabled, EffectType type)
{
m_type_enabled = enabled;
m_type_value = type;
invalidateFilter();
}
bool EffectFilter::filterType(TreeItem* item) const
{
if (!m_type_enabled) {
return true;
}
EffectType itemType = item->data(EffectTreeModel::typeCol).value<EffectType>();
return itemType == m_type_value;
}
bool EffectFilter::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
if (sourceParent == QModelIndex()) {
//In that case, we have a category. We hide it if it does not have children.
QModelIndex category = sourceModel()->index(sourceRow, 0, sourceParent);
bool accepted = false;
for (int i = 0; i < sourceModel()->rowCount(category) && !accepted; ++i) {
accepted = filterAcceptsRow(i, category);
}
return accepted;
}
QModelIndex row = sourceModel()->index(sourceRow, 0, sourceParent);
TreeItem *item = static_cast<TreeItem*>(row.internalPointer());
return filterName(item) && filterType(item);
}
bool EffectFilter::isVisible(const QModelIndex &sourceIndex)
{
auto parent = sourceModel()->parent(sourceIndex);
return filterAcceptsRow(sourceIndex.row(), parent);
}
/***************************************************************************
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
#ifndef EFFECTFILTER_H
#define EFFECTFILTER_H
#include <QSortFilterProxyModel>
#include <memory>
#include "effects/effectsrepository.hpp"
class TreeItem;
/* @brief This class is used as a proxy model to filter the effect tree based on given criterion (name, type)
*/
class EffectFilter : public QSortFilterProxyModel
{
Q_OBJECT
public:
EffectFilter(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 Manage the type filter
@param enabled whether to enable this filter
@param type Effect type to display
*/
void setFilterType(bool enabled, EffectType type);
/** @brief Returns true if the ModelIndex in the source model is visible after filtering
*/
bool isVisible(const QModelIndex &sourceIndex);
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
private:
bool filterName(TreeItem* item) const;
bool filterType(TreeItem* item) const;
bool m_name_enabled;
QString m_name_value;
bool m_type_enabled;
EffectType m_type_value;
};
#endif
/***************************************************************************
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include "effecttreemodel.hpp"
#include "effects/effectsrepository.hpp"
#include "abstractmodel/treeitem.hpp"
#include <vector>
#include <QVector>
#include <array>
#include <KLocalizedString>
#include <QDomDocument>
#include <QFile>
#include <QDebug>
int EffectTreeModel::nameCol = 0;
int EffectTreeModel::idCol = 1;
int EffectTreeModel::typeCol = 2;
int EffectTreeModel::favCol = 3;
EffectTreeModel::EffectTreeModel(const QString &categoryFile, QObject *parent)
: AbstractTreeModel(parent)
{
QList<QVariant> rootData;
rootData << "Name" << "ID" << "Type" << "isFav";
rootItem = new TreeItem(rootData);
QHash<QString, TreeItem*> effectCategory; //category in which each effect should land.
//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"));
for (int i = 0; i < groups.count(); i++) {
QString groupName = i18n(groups.at(i).firstChild().firstChild().nodeValue().toUtf8().constData());
QStringList list = groups.at(i).toElement().attribute(QStringLiteral("list")).split(QLatin1Char(','), QString::SkipEmptyParts);
TreeItem *groupItem = rootItem->appendChild(QList<QVariant>{groupName});
for (const QString& effect : list){
effectCategory[effect] = groupItem;
}
}
}
//We also create "Misc" and "Custom" categories
TreeItem *miscCategory = rootItem->appendChild(QList<QVariant>{i18n("Misc")});
TreeItem *customCategory = rootItem->appendChild(QList<QVariant>{i18n("Custom")});
//We parse effects
auto allEffects = EffectsRepository::get()->getEffectsNames();
for (const auto& effect : allEffects) {
TreeItem *targetCategory = miscCategory;
EffectType type = EffectsRepository::get()->getEffectType(effect.first);
if (effectCategory.contains(effect.first)) {
targetCategory = effectCategory[effect.first];
}
if (type == EffectType::Custom) {
targetCategory = customCategory;
}
// we create the data list corresponding to this profile
QList<QVariant> data;
bool isFav = EffectsRepository::get()->isFavorite(effect.first);
qDebug()<<effect.second << effect.first << "in "<<targetCategory->data(0).toString();
data << effect.second << effect.first << QVariant::fromValue(type) << isFav;
targetCategory->appendChild(data);
}
}
QHash<int, QByteArray> EffectTreeModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[IdRole] = "id";
roles[NameRole] = "name";
return roles;
}
QString EffectTreeModel::getName(const QModelIndex& index) const
{
if (!index.isValid()) {
return QString();
}
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
if (item->depth() == 1) {
return item->data(0).toString();
} else {
return item->data(EffectTreeModel::nameCol).toString();
}
}
QVariant EffectTreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
if(role == IdRole) {
if (item->depth() == 1) {
return "root";
} else {
return item->data(EffectTreeModel::idCol);
}
}
if (role != NameRole) {
return QVariant();
}
return item->data(index.column());
}
/***************************************************************************
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
#ifndef EFFECTTREEMODEL_H
#define EFFECTTREEMODEL_H
#include "abstractmodel/abstracttreemodel.hpp"
#include <QHash>
#include <QIcon>
/* @brief This class represents an effect hierarchy to be displayed as a tree
*/
class TreeItem;
class EffectTreeModel : public AbstractTreeModel
{
Q_OBJECT
public:
explicit EffectTreeModel(const QString &categoryFile, QObject *parent = 0);
enum {
IdRole = Qt::UserRole + 1,
NameRole
};
//Helper function to retrieve name
QString getName(const QModelIndex& index) const;
QHash<int, QByteArray> roleNames() const override;
QVariant data(const QModelIndex &index, int role) const override;
// for convenience, we store the column of each data field
static int nameCol, idCol, favCol, typeCol;
protected:
};
#endif
/***************************************************************************
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include "effectlistwidget.hpp"
#include "../model/effecttreemodel.hpp"
#include "../model/effectfilter.hpp"
#include "effects/effectsrepository.hpp"
#include <QStandardPaths>
#include <QQmlContext>
EffectListWidget::EffectListWidget(QWidget *parent)
: QQuickWidget(parent)
{
QString effectCategory = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("kdenliveeffectscategory.rc"));
m_model.reset(new EffectTreeModel(effectCategory, this));
m_proxyModel = new EffectFilter(this);
m_proxyModel->setSourceModel(m_model.get());
setResizeMode(QQuickWidget::SizeRootObjectToView);
rootContext()->setContextProperty("effectlist", this);
rootContext()->setContextProperty("effectListModel", m_proxyModel);
m_effectIconProvider.reset(new EffectIconProvider);
engine()->addImageProvider(QStringLiteral("effecticon"), m_effectIconProvider.get());
setSource(QUrl(QStringLiteral("qrc:/qml/effectList.qml")));
setFocusPolicy(Qt::StrongFocus);
}
QString EffectListWidget::getName(const QModelIndex& index) const
{
return m_model->getName(m_proxyModel->mapToSource(index));
}
/***************************************************************************
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
#ifndef EFFECTLISTWIDGET_H
#define EFFECTLISTWIDGET_H
#include <QQuickWidget>
#include <memory>
#include "qmltypes/effecticonprovider.hpp"
/* @brief This class is a widget that display the list of available effects
*/
class EffectFilter;
class EffectTreeModel;
class EffectListWidget : public QQuickWidget
{
Q_OBJECT
public:
EffectListWidget(QWidget *parent = Q_NULLPTR);
Q_INVOKABLE QString getName(const QModelIndex& index) const;
private:
std::unique_ptr<EffectTreeModel> m_model;
EffectFilter *m_proxyModel;
std::unique_ptr<EffectIconProvider> m_effectIconProvider;
signals:
};
#endif
/***************************************************************************
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
import QtQuick 2.4
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Window 2.2
Rectangle {
id: listRoot
TreeView {
anchors.fill: parent
alternatingRowColors: false
headerVisible: false
itemDelegate: Row {
Image{
visible: styleData.value != "root"
source: 'image://effecticon/' + styleData.value
}
Text{
text: effectlist.getName(styleData.index)
}
}
TableViewColumn { role: "id"; title: "Name"; width: 200 }
model: effectListModel
}
}
/***************************************************************************
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include "effecticonprovider.hpp"
#include "effects/effectsrepository.hpp"
#include <QDebug>
#include <QIcon>
#include <QPainter>
#include <QFont>
EffectIconProvider::EffectIconProvider()
: QQuickImageProvider(QQmlImageProviderBase::Image, QQmlImageProviderBase::ForceAsynchronousImageLoading)
{
}
QImage EffectIconProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
QImage result;
// id is [hash]/mlt_service/resource#frameNumber
if (EffectsRepository::get()->hasEffect(id)) {
QString name = EffectsRepository::get()->getEffectName(id);
result = makeIcon(id, name, requestedSize);
if (size) {
*size = result.size();
}
} else {
qDebug() << "Effect not found "<<id;
}
return result;
}
QImage EffectIconProvider::makeIcon(const QString &effectId, const QString &effectName, const QSize& size)
{
qDebug() <<"Painting"<<effectName<<"size"<<size;
QPixmap pix(30,30);
if (effectName.isEmpty()) {
pix.fill(Qt::red);
return pix.toImage();
}
QFont ft = QFont();
ft.setBold(true);
uint hex = qHash(effectName);
QString t = "#" + QString::number(hex, 16).toUpper().left(6);
QColor col(t);
bool isAudio = EffectsRepository::get()->getEffectType(effectId) == EffectType::Audio;
qDebug() <<"is audio"<<isAudio;
if (isAudio) {
pix.fill(Qt::transparent);
} else {
pix.fill(col);
}
QPainter p(&pix);
if (isAudio) {
p.setPen(Qt::NoPen);
p.setBrush(col);
p.drawEllipse(pix.rect());
p.setPen(QPen());
}
p.setFont(ft);
p.drawText(pix.rect(), Qt::AlignCenter, effectName.at(0));
p.end();
return pix.toImage();
}
/***************************************************************************
* 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 *