File indexing completed on 2024-05-05 04:52:53

0001 /*
0002     SPDX-FileCopyrightText: 2016 Jean-Baptiste Mardelle <jb@kdenlive.org>
0003 
0004 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include "generators.h"
0008 #include "assets/abstractassetsrepository.hpp"
0009 #include "doc/kthumb.h"
0010 #include "effects/effectsrepository.hpp"
0011 #include "kdenlivesettings.h"
0012 #include "widgets/timecodedisplay.h"
0013 
0014 #include <QDialogButtonBox>
0015 #include <QDir>
0016 #include <QDomDocument>
0017 #include <QFileDialog>
0018 #include <QLabel>
0019 #include <QStandardPaths>
0020 #include <QVBoxLayout>
0021 
0022 #include "core.h"
0023 #include "klocalizedstring.h"
0024 #include "profiles/profilemodel.hpp"
0025 #include <KMessageBox>
0026 #include <KRecentDirs>
0027 #include <memory>
0028 #include <mlt++/MltConsumer.h>
0029 #include <mlt++/MltProducer.h>
0030 #include <mlt++/MltProfile.h>
0031 #include <mlt++/MltTractor.h>
0032 
0033 Generators::Generators(const QString &path, QWidget *parent)
0034     : QDialog(parent)
0035     , m_producer(nullptr)
0036     , m_timePos(nullptr)
0037     , m_container(nullptr)
0038     , m_preview(nullptr)
0039 {
0040     QDomDocument doc;
0041     if (!Xml::docContentFromFile(doc, path, false)) {
0042         return;
0043     }
0044 
0045     QDomElement base = doc.documentElement();
0046     if (base.tagName() == QLatin1String("generator")) {
0047         QString generatorTag = base.attribute(QStringLiteral("tag"));
0048         setWindowTitle(i18n(base.firstChildElement(QStringLiteral("name")).text().toUtf8().data()));
0049         auto *lay = new QVBoxLayout(this);
0050         m_preview = new QLabel;
0051         m_preview->setMinimumSize(1, 1);
0052         lay->addWidget(m_preview);
0053         m_producer = new Mlt::Producer(pCore->getProjectProfile(), generatorTag.toUtf8().constData());
0054         m_pixmap = QPixmap::fromImage(KThumb::getFrame(m_producer, 0, pCore->getCurrentProfile()->width(), pCore->getCurrentProfile()->height()));
0055         m_preview->setPixmap(m_pixmap.scaledToWidth(m_preview->width()));
0056         auto *hlay = new QHBoxLayout;
0057         hlay->addWidget(new QLabel(i18n("Duration:")));
0058         m_timePos = new TimecodeDisplay(this);
0059         if (base.hasAttribute(QStringLiteral("updateonduration"))) {
0060             connect(m_timePos, &TimecodeDisplay::timeCodeEditingFinished, this, &Generators::updateDuration);
0061         }
0062         hlay->addWidget(m_timePos);
0063         lay->addLayout(hlay);
0064         QWidget *frameWidget = new QWidget;
0065         lay->addWidget(frameWidget);
0066 
0067         m_view = new AssetParameterView(frameWidget);
0068         lay->addWidget(m_view);
0069         QString tag = base.attribute(QStringLiteral("tag"), QString());
0070 
0071         auto prop = std::make_unique<Mlt::Properties>(m_producer->get_properties());
0072         m_assetModel.reset(new AssetParameterModel(std::move(prop), base, tag, ObjectId())); // NOLINT
0073         m_view->setModel(m_assetModel, QSize(1920, 1080), false);
0074         connect(m_assetModel.get(), &AssetParameterModel::modelChanged, this, [this]() { updateProducer(); });
0075 
0076         lay->addStretch(10);
0077         QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
0078         connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
0079         connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
0080         lay->addWidget(buttonBox);
0081         m_timePos->setValue(KdenliveSettings::color_duration());
0082     }
0083 }
0084 
0085 void Generators::updateProducer()
0086 {
0087     int w = m_pixmap.width();
0088     int h = m_pixmap.height();
0089     m_pixmap = QPixmap::fromImage(KThumb::getFrame(m_producer, 0, w, h));
0090     m_preview->setPixmap(m_pixmap.scaledToWidth(m_preview->width()));
0091 }
0092 
0093 void Generators::resizeEvent(QResizeEvent *event)
0094 {
0095     QDialog::resizeEvent(event);
0096     m_preview->setPixmap(m_pixmap.scaledToWidth(m_preview->width()));
0097 }
0098 
0099 Generators::~Generators()
0100 {
0101     delete m_timePos;
0102 }
0103 
0104 // static
0105 void Generators::getGenerators(const QStringList &producers, QMenu *menu)
0106 {
0107     const QStringList generatorFolders =
0108         QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("generators"), QStandardPaths::LocateDirectory);
0109     const QStringList filters = QStringList() << QStringLiteral("*.xml");
0110     QStringList parsedGenerators;
0111     for (const QString &folder : generatorFolders) {
0112         QDir directory(folder);
0113         const QStringList filesnames = directory.entryList(filters, QDir::Files);
0114         for (const QString &fname : filesnames) {
0115             if (parsedGenerators.contains(fname)) {
0116                 continue;
0117             }
0118             parsedGenerators << fname;
0119             QPair<QString, QString> result = parseGenerator(directory.absoluteFilePath(fname), producers);
0120             if (!result.first.isEmpty()) {
0121                 QAction *action = menu->addAction(i18n(result.first.toUtf8().data()));
0122                 action->setData(result.second);
0123             }
0124         }
0125     }
0126 }
0127 
0128 // static
0129 QPair<QString, QString> Generators::parseGenerator(const QString &path, const QStringList &producers)
0130 {
0131     QPair<QString, QString> result;
0132     QDomDocument doc;
0133     if (!Xml::docContentFromFile(doc, path, false)) {
0134         return result;
0135     }
0136 
0137     QDomElement base = doc.documentElement();
0138     if (base.tagName() == QLatin1String("generator")) {
0139         QString generatorTag = base.attribute(QStringLiteral("tag"));
0140         if (producers.contains(generatorTag)) {
0141             result.first = base.firstChildElement(QStringLiteral("name")).text();
0142             result.second = path;
0143         }
0144     }
0145     return result;
0146 }
0147 
0148 void Generators::updateDuration(int duration)
0149 {
0150     m_producer->set("length", duration);
0151     m_producer->set_in_and_out(0, duration - 1);
0152     updateProducer();
0153 }
0154 
0155 QUrl Generators::getSavedClip(QString clipFolder)
0156 {
0157     if (clipFolder.isEmpty()) {
0158         clipFolder = KRecentDirs::dir(QStringLiteral(":KdenliveClipFolder"));
0159     }
0160     if (clipFolder.isEmpty()) {
0161         clipFolder = QDir::homePath();
0162     }
0163     QFileDialog fd(this);
0164     fd.setDirectory(clipFolder);
0165     fd.setNameFilter(i18n("MLT Playlist (*.mlt)"));
0166     fd.setAcceptMode(QFileDialog::AcceptSave);
0167     fd.setFileMode(QFileDialog::AnyFile);
0168     fd.setDefaultSuffix(QStringLiteral("mlt"));
0169     if (fd.exec() != QDialog::Accepted || fd.selectedUrls().isEmpty()) {
0170         return QUrl();
0171     }
0172     QUrl url = fd.selectedUrls().constFirst();
0173 
0174     if (url.isValid()) {
0175         Mlt::Tractor trac(pCore->getProjectProfile());
0176         m_producer->set("length", m_timePos->getValue());
0177         m_producer->set_in_and_out(0, m_timePos->getValue() - 1);
0178         trac.set_track(*m_producer, 0);
0179         Mlt::Consumer c(pCore->getProjectProfile(), "xml", url.toLocalFile().toUtf8().constData());
0180         c.connect(trac);
0181         c.run();
0182         return url;
0183     }
0184     return QUrl();
0185 }