effecttreemodel.cpp 10.4 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
{
25
26
27
28
29
}

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

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

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

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

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

84
        if (type == AssetListType::AssetType::Custom || type == AssetListType::AssetType::CustomAudio) {
85
            targetCategory = self->m_customCategory;
86
87
88
        }

        // we create the data list corresponding to this profile
89
        bool isFav = KdenliveSettings::favorite_effects().contains(effect.first);
90
        bool isPreferred = EffectsRepository::get()->isPreferred(effect.first);
91
92
93
94
95
96
97
98
        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};
        }
99
100
101
        if (KdenliveSettings::favorite_effects().contains(effect.first) && effectCategory.contains(favCategory)) {
            targetCategory = effectCategory[favCategory];
        }
102
103
        targetCategory->appendChild(data);
    }
104
    return self;
105
}
106

107
108
109
110
111
void EffectTreeModel::reloadEffectFromIndex(const QModelIndex &index)
{
    if (!index.isValid()) {
        return;
    }
112
    std::shared_ptr<TreeItem> item = getItemById(int(index.internalId()));
113
114
115
116
    const QString path = EffectsRepository::get()->getCustomPath(item->dataColumn(idCol).toString());
    reloadEffect(path);
}

117
118
void EffectTreeModel::reloadEffect(const QString &path)
{
119
    QPair<QString, QString> asset = EffectsRepository::get()->reloadCustom(path);
120
121
122
    if (asset.first.isEmpty() || m_customCategory == nullptr) {
        return;
    }
123
124
125
126
127
128
129
130
    // 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);
        if (item->dataColumn(idCol).toString() == asset.first) {
            m_customCategory->removeChild(item);
            break;
        }
    }
131
    bool isFav = KdenliveSettings::favorite_effects().contains(asset.first);
132
133
    QString effectName = EffectsRepository::get()->getName(asset.first);
    QList<QVariant> data {effectName, asset.first, QVariant::fromValue(AssetListType::AssetType::Custom), isFav};
134
    m_customCategory->appendChild(data);
135
136
137
138
139
140
141
}

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

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) {
            QMenu *catMenu = new QMenu(item->dataColumn(nameCol).toString(), effectsMenu);
            effectsMenu->addMenu(catMenu);
            for (int j = 0; j < item->childCount(); j++) {
                std::shared_ptr<TreeItem> child = item->child(j);
157
                QAction *a = new QAction(i18n(child->dataColumn(nameCol).toString().toUtf8().data()), catMenu);
158
159
160
161
162
163
164
165
                const QString id = child->dataColumn(idCol).toString();
                a->setData(id);
                catMenu->addAction(a);
                effectActions->addAction("transition_" + id, a);
            }
        }
    }
}
166
167
168
169
170
171

void EffectTreeModel::setFavorite(const QModelIndex &index, bool favorite, bool isEffect)
{
    if (!index.isValid()) {
        return;
    }
172
    std::shared_ptr<TreeItem> item = getItemById(int(index.internalId()));
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
    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: "<<id;
        return;
    }
    QStringList favs = KdenliveSettings::favorite_effects();
    if (!favorite) {
        favs.removeAll(id);
    } else {
        favs << id;
    }
    KdenliveSettings::setFavorite_effects(favs);
}
190

Vivek Yadav's avatar
Vivek Yadav committed
191
192
193
void EffectTreeModel::editCustomAsset(const QString newName,const QString newDescription, const QModelIndex &index)
{

194
    std::shared_ptr<TreeItem> item = getItemById(int(index.internalId()));
Vivek Yadav's avatar
Vivek Yadav committed
195
196
197
198
199
200
201
202
203
204
205
206
207
    QString currentName = item->dataColumn(AssetTreeModel::nameCol).toString();

    QDomDocument doc;

    QDomElement effect = EffectsRepository::get()->getXml(currentName);
    QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/effects/"));
    QString oldpath =  dir.absoluteFilePath(currentName + QStringLiteral(".xml"));

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



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

Vivek Yadav's avatar
Vivek Yadav committed
220
    if(!newName.trimmed().isEmpty() && newName != currentName)
Vivek Yadav's avatar
Vivek Yadav committed
221
222
223
224
225
226
227
228
    {
        QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/effects/"));
        if (!dir.exists()) {
            dir.mkpath(QStringLiteral("."));
        }

        if (dir.exists(newName + QStringLiteral(".xml"))){
            QMessageBox message;
229
            message.critical(nullptr, i18n("Error"), i18n("Effect name %1 already exists.\n Try another name?", newName));
Vivek Yadav's avatar
Vivek Yadav committed
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
            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
246
            QTextStream out(&file);
247
            out.setCodec("UTF-8");
Vivek Yadav's avatar
Vivek Yadav committed
248
249
250
            out << doc.toString();
        }
        file.close();
Vivek Yadav's avatar
Vivek Yadav committed
251

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

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

}