effecttreemodel.cpp 10.7 KB
Newer Older
1
/*
2
    SPDX-FileCopyrightText: 2017 Nicolas Carion
Camille Moulin's avatar
Camille Moulin committed
3
    SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
4
*/
5
6
7

#include "effecttreemodel.hpp"
#include "abstractmodel/treeitem.hpp"
Nicolas Carion's avatar
Nicolas Carion committed
8
#include "effects/effectsrepository.hpp"
9
#include "kdenlivesettings.h"
10
11
12
#include <KLocalizedString>
#include <QDomDocument>
#include <QFile>
Nicolas Carion's avatar
Nicolas Carion committed
13
14
#include <array>
#include <vector>
15

16
#include <KActionCategory>
17
#include <QDebug>
18
#include <QMenu>
Vivek Yadav's avatar
Vivek Yadav committed
19
#include <QMessageBox>
20

21
EffectTreeModel::EffectTreeModel(QObject *parent)
22
    : AssetTreeModel(parent)
23
    , m_customCategory(nullptr)
24
    , m_templateCategory(nullptr)
25
{
26
27
28
29
30
}

std::shared_ptr<EffectTreeModel> EffectTreeModel::construct(const QString &categoryFile, QObject *parent)
{
    std::shared_ptr<EffectTreeModel> self(new EffectTreeModel(parent));
31
    QList<QVariant> rootData {"Name", "ID", "Type", "isFav"};
32
    self->rootItem = TreeItem::construct(rootData, self, true);
33

34
    QHash<QString, std::shared_ptr<TreeItem>> effectCategory; // category in which each effect should land.
35

36
37
    std::shared_ptr<TreeItem> miscCategory = nullptr;
    std::shared_ptr<TreeItem> audioCategory = nullptr;
Nicolas Carion's avatar
Nicolas Carion committed
38
    // We parse category file
39
40
41
42
43
44
    if (!categoryFile.isEmpty()) {
        QDomDocument doc;
        QFile file(categoryFile);
        doc.setContent(&file, false);
        file.close();
        QDomNodeList groups = doc.documentElement().elementsByTagName(QStringLiteral("group"));
45
        auto groupLegacy = self->rootItem->appendChild(QList<QVariant>{i18n("Legacy"), QStringLiteral("root")});
46
47
48

        for (int i = 0; i < groups.count(); i++) {
            QString groupName = i18n(groups.at(i).firstChild().firstChild().nodeValue().toUtf8().constData());
49
50
51
            if (!KdenliveSettings::gpu_accel() && groupName == i18n("GPU effects")) {
                continue;
            }
Laurent Montel's avatar
Laurent Montel committed
52
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
53
            QStringList list = groups.at(i).toElement().attribute(QStringLiteral("list")).split(QLatin1Char(','), QString::SkipEmptyParts);
Laurent Montel's avatar
Laurent Montel committed
54
55
56
#else
            QStringList list = groups.at(i).toElement().attribute(QStringLiteral("list")).split(QLatin1Char(','), Qt::SkipEmptyParts);
#endif
57
            auto groupItem = self->rootItem->appendChild(QList<QVariant>{groupName, QStringLiteral("root")});
Vincent Pinon's avatar
Vincent Pinon committed
58
            for (const QString &effect : qAsConst(list)) {
59
                effectCategory[effect] = groupItem;
60
61
            }
        }
62
63
64
        // We also create "Misc", "Audio" and "Custom" categories
        miscCategory = self->rootItem->appendChild(QList<QVariant>{i18n("Misc"), QStringLiteral("root")});
        audioCategory = self->rootItem->appendChild(QList<QVariant>{i18n("Audio"), QStringLiteral("root")});
65
        self->m_customCategory = self->rootItem->appendChild(QList<QVariant>{i18n("Custom"), QStringLiteral("root")});
66
        self->m_templateCategory = self->rootItem->appendChild(QList<QVariant>{i18n("Templates"), QStringLiteral("root")});
67
68
69
70
    } else {
        // Flat view
        miscCategory = self->rootItem;
        audioCategory = self->rootItem;
71
        self->m_customCategory = self->rootItem;
72
        self->m_templateCategory = self->rootItem;
73
74
    }

Nicolas Carion's avatar
Nicolas Carion committed
75
    // We parse effects
76
    auto allEffects = EffectsRepository::get()->getNames();
77
    QString favCategory = QStringLiteral("kdenlive:favorites");
Vincent Pinon's avatar
Vincent Pinon committed
78
    for (const auto &effect : qAsConst(allEffects)) {
79
        auto targetCategory = miscCategory;
80
        AssetListType::AssetType type = EffectsRepository::get()->getType(effect.first);
81
82
        if (effectCategory.contains(effect.first)) {
            targetCategory = effectCategory[effect.first];
83
        } else if (type == AssetListType::AssetType::Audio) {
84
            targetCategory = audioCategory;
85
86
        }

87
        if (type == AssetListType::AssetType::Custom || type == AssetListType::AssetType::CustomAudio) {
88
            targetCategory = self->m_customCategory;
89
        }
90
91
92
        if (type == AssetListType::AssetType::Template || type == AssetListType::AssetType::TemplateAudio) {
            targetCategory = self->m_templateCategory;
        }
93
94

        // we create the data list corresponding to this profile
95
        bool isFav = KdenliveSettings::favorite_effects().contains(effect.first);
96
        bool isPreferred = EffectsRepository::get()->isPreferred(effect.first);
97
98
99
100
101
102
103
104
        QList<QVariant> data;
        if (targetCategory->dataColumn(0).toString() == i18n("Deprecated")) {
            QString updatedName = effect.second + i18n(" - deprecated");
            data = {updatedName, effect.first, QVariant::fromValue(type), isFav, targetCategory->row(), isPreferred};
        } else {
            //qDebug() << effect.second << effect.first << "in " << targetCategory->dataColumn(0).toString();
            data = {effect.second, effect.first, QVariant::fromValue(type), isFav, targetCategory->row(), isPreferred};
        }
105
106
107
        if (KdenliveSettings::favorite_effects().contains(effect.first) && effectCategory.contains(favCategory)) {
            targetCategory = effectCategory[favCategory];
        }
108
109
        targetCategory->appendChild(data);
    }
110
    return self;
111
}
112

113
114
115
116
117
void EffectTreeModel::reloadEffectFromIndex(const QModelIndex &index)
{
    if (!index.isValid()) {
        return;
    }
118
    std::shared_ptr<TreeItem> item = getItemById(int(index.internalId()));
119
    const QString path = EffectsRepository::get()->getCustomPath(item->dataColumn(IdCol).toString());
120
121
122
    reloadEffect(path);
}

123
124
void EffectTreeModel::reloadEffect(const QString &path)
{
125
    QPair<QString, QString> asset = EffectsRepository::get()->reloadCustom(path);
126
127
128
    if (asset.first.isEmpty() || m_customCategory == nullptr) {
        return;
    }
129
130
131
    // Check if item already existed, and remove
    for (int i = 0; i < m_customCategory->childCount(); i++) {
        std::shared_ptr<TreeItem> item = m_customCategory->child(i);
132
        if (item->dataColumn(IdCol).toString() == asset.first) {
133
134
135
136
            m_customCategory->removeChild(item);
            break;
        }
    }
137
    bool isFav = KdenliveSettings::favorite_effects().contains(asset.first);
138
139
    QString effectName = EffectsRepository::get()->getName(asset.first);
    QList<QVariant> data {effectName, asset.first, QVariant::fromValue(AssetListType::AssetType::Custom), isFav};
140
    m_customCategory->appendChild(data);
141
142
143
144
145
146
147
}

void EffectTreeModel::deleteEffect(const QModelIndex &index)
{
    if (!index.isValid()) {
        return;
    }
148
    std::shared_ptr<TreeItem> item = getItemById(int(index.internalId()));
149
    const QString id = item->dataColumn(IdCol).toString();
150
151
    m_customCategory->removeChild(item);
    EffectsRepository::get()->deleteEffect(id);
152
}
153
154
155
156
157
158

void EffectTreeModel::reloadAssetMenu(QMenu *effectsMenu, KActionCategory *effectActions)
{
    for (int i = 0; i < rowCount(); i++) {
        std::shared_ptr<TreeItem> item = rootItem->child(i);
        if (item->childCount() > 0) {
159
            QMenu *catMenu = new QMenu(item->dataColumn(AssetTreeModel::NameCol).toString(), effectsMenu);
160
161
162
            effectsMenu->addMenu(catMenu);
            for (int j = 0; j < item->childCount(); j++) {
                std::shared_ptr<TreeItem> child = item->child(j);
163
164
                QAction *a = new QAction(i18n(child->dataColumn(AssetTreeModel::NameCol).toString().toUtf8().data()), catMenu);
                const QString id = child->dataColumn(AssetTreeModel::IdCol).toString();
165
166
167
168
169
170
171
                a->setData(id);
                catMenu->addAction(a);
                effectActions->addAction("transition_" + id, a);
            }
        }
    }
}
172
173
174
175
176
177

void EffectTreeModel::setFavorite(const QModelIndex &index, bool favorite, bool isEffect)
{
    if (!index.isValid()) {
        return;
    }
178
    std::shared_ptr<TreeItem> item = getItemById(int(index.internalId()));
179
180
181
    if (isEffect && item->depth() == 1) {
        return;
    }
182
183
    item->setData(AssetTreeModel::FavCol, favorite);
    auto id = item->dataColumn(AssetTreeModel::IdCol).toString();
184
185
186
187
188
189
190
191
192
193
194
195
    if (!EffectsRepository::get()->exists(id)) {
        qDebug()<<"Trying to reparent unknown asset: "<<id;
        return;
    }
    QStringList favs = KdenliveSettings::favorite_effects();
    if (!favorite) {
        favs.removeAll(id);
    } else {
        favs << id;
    }
    KdenliveSettings::setFavorite_effects(favs);
}
196

197
void EffectTreeModel::editCustomAsset(const QString &newName,const QString &newDescription, const QModelIndex &index)
Vivek Yadav's avatar
Vivek Yadav committed
198
199
{

200
    std::shared_ptr<TreeItem> item = getItemById(int(index.internalId()));
201
    QString currentName = item->dataColumn(AssetTreeModel::NameCol).toString();
Vivek Yadav's avatar
Vivek Yadav committed
202
203
204
205
206

    QDomDocument doc;

    QDomElement effect = EffectsRepository::get()->getXml(currentName);
    QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/effects/"));
Julius Künzel's avatar
Julius Künzel committed
207
    QString oldpath = dir.absoluteFilePath(currentName + QStringLiteral(".xml"));
Vivek Yadav's avatar
Vivek Yadav committed
208
209
210
211

    doc.appendChild(doc.importNode(effect, true));

    if(!newDescription.trimmed().isEmpty()){
Vivek Yadav's avatar
Vivek Yadav committed
212
213
214
215
216
        QDomElement root = doc.documentElement();
        QDomElement nodelist = root.firstChildElement("description");
        QDomElement newNodeTag = doc.createElement(QString("description"));
        QDomText text = doc.createTextNode(newDescription);
        newNodeTag.appendChild(text);
217
218
219
220
221
        if (!nodelist.isNull()) {
            root.replaceChild(newNodeTag, nodelist);
        } else {
            root.appendChild(newNodeTag);
        }
Vivek Yadav's avatar
Vivek Yadav committed
222
    }
Vivek Yadav's avatar
Vivek Yadav committed
223

Vivek Yadav's avatar
Vivek Yadav committed
224
    if(!newName.trimmed().isEmpty() && newName != currentName)
Vivek Yadav's avatar
Vivek Yadav committed
225
226
227
228
229
230
231
    {
        if (!dir.exists()) {
            dir.mkpath(QStringLiteral("."));
        }

        if (dir.exists(newName + QStringLiteral(".xml"))){
            QMessageBox message;
232
            message.critical(nullptr, i18n("Error"), i18n("Effect name %1 already exists.\n Try another name?", newName));
Vivek Yadav's avatar
Vivek Yadav committed
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
            message.setFixedSize(400, 200);
            return;
        }
        QFile file(dir.absoluteFilePath(newName + QStringLiteral(".xml")));

        QDomElement root = doc.documentElement();
        QDomElement nodelist = root.firstChildElement("name");
        QDomElement newNodeTag = doc.createElement(QString("name"));
        QDomText text = doc.createTextNode(newName);
        newNodeTag.appendChild(text);
        root.replaceChild(newNodeTag, nodelist);

        QDomElement e = doc.documentElement();
        e.setAttribute("id", newName);

        if (file.open(QFile::WriteOnly | QFile::Truncate)) {
Vivek Yadav's avatar
Vivek Yadav committed
249
            QTextStream out(&file);
250
            out.setCodec("UTF-8");
Vivek Yadav's avatar
Vivek Yadav committed
251
252
253
            out << doc.toString();
        }
        file.close();
Vivek Yadav's avatar
Vivek Yadav committed
254

Vivek Yadav's avatar
Vivek Yadav committed
255
256
        deleteEffect(index);
        reloadEffect(dir.absoluteFilePath(newName + QStringLiteral(".xml")));
Vivek Yadav's avatar
Vivek Yadav committed
257
258
259
260
261
262

    }
    else
    {
        QFile file(dir.absoluteFilePath(currentName + QStringLiteral(".xml")));
        if (file.open(QFile::WriteOnly | QFile::Truncate)) {
Vivek Yadav's avatar
Vivek Yadav committed
263
            QTextStream out(&file);
264
            out.setCodec("UTF-8");
Vivek Yadav's avatar
Vivek Yadav committed
265
266
267
268
            out << doc.toString();
        }
        file.close();
        reloadEffect(oldpath);
Vivek Yadav's avatar
Vivek Yadav committed
269
270
271
    }

}