Fix subclip cannot be renamed. Store them in json and bump document version

Fixes #140
parent c603cadc
Pipeline #4602 passed with stage
in 15 minutes and 31 seconds
......@@ -2396,8 +2396,7 @@ void Bin::renameSubClip(const QString &id, const QString &newName, const QString
return;
}
sub->setName(newName);
clip->setProducerProperty("kdenlive:clipzone." + oldName, QString());
clip->setProducerProperty("kdenlive:clipzone." + newName, QString::number(in) + QLatin1Char(';') + QString::number(out));
clip->updateZones();
emit itemUpdated(sub);
}
......
......@@ -111,7 +111,7 @@ std::shared_ptr<ProjectClip> ProjectClip::construct(const QString &id, const QIc
{
std::shared_ptr<ProjectClip> self(new ProjectClip(id, thumb, model, producer));
baseFinishConstruct(self);
QMetaObject::invokeMethod(model.get(), "loadSubClips", Qt::QueuedConnection, Q_ARG(const QString&, id), Q_ARG(const stringMap&, self->getPropertiesFromPrefix(QStringLiteral("kdenlive:clipzone."))));
QMetaObject::invokeMethod(model.get(), "loadSubClips", Qt::QueuedConnection, Q_ARG(const QString&, id), Q_ARG(const QString&, self->getProducerProperty(QStringLiteral("kdenlive:clipzones"))));
return self;
}
......@@ -1368,3 +1368,26 @@ void ProjectClip::updateTimelineClips(const QVector<int> &roles)
}
}
}
void ProjectClip::updateZones()
{
int zonesCount = childCount();
if (zonesCount == 0) {
resetProducerProperty(QStringLiteral("kdenlive:clipzones"));
return;
}
QJsonArray list;
for (int i = 0; i < zonesCount; ++i) {
std::shared_ptr<AbstractProjectItem> clip = std::static_pointer_cast<AbstractProjectItem>(child(i));
if (clip) {
QJsonObject currentZone;
currentZone.insert(QLatin1String("name"), QJsonValue(clip->name()));
QPoint zone = clip->zone();
currentZone.insert(QLatin1String("in"), QJsonValue(zone.x()));
currentZone.insert(QLatin1String("out"), QJsonValue(zone.y()));
list.push_back(currentZone);
}
}
QJsonDocument json(list);
setProducerProperty(QStringLiteral("kdenlive:clipzones"), QString(json.toJson()));
}
......@@ -217,6 +217,9 @@ public:
static std::shared_ptr<Mlt::Producer> cloneProducer(const std::shared_ptr<Mlt::Producer> &producer);
std::shared_ptr<Mlt::Producer> softClone(const char *list);
void updateTimelineClips(const QVector<int> &roles);
/** @brief Saves the subclips data as json
*/
void updateZones();
protected:
friend class ClipModel;
......
......@@ -458,7 +458,7 @@ std::shared_ptr<ProjectFolder> ProjectItemModel::getRootFolder() const
return std::static_pointer_cast<ProjectFolder>(rootItem);
}
void ProjectItemModel::loadSubClips(const QString &id, const stringMap &clipData)
void ProjectItemModel::loadSubClips(const QString &id, const QString &clipData)
{
QWriteLocker locker(&m_lock);
Fun undo = []() { return true; };
......@@ -466,7 +466,7 @@ void ProjectItemModel::loadSubClips(const QString &id, const stringMap &clipData
loadSubClips(id, clipData, undo, redo);
}
void ProjectItemModel::loadSubClips(const QString &id, const stringMap &dataMap, Fun &undo, Fun &redo)
void ProjectItemModel::loadSubClips(const QString &id, const QString &dataMap, Fun &undo, Fun &redo)
{
QWriteLocker locker(&m_lock);
std::shared_ptr<ProjectClip> clip = getClipByBinID(id);
......@@ -474,23 +474,35 @@ void ProjectItemModel::loadSubClips(const QString &id, const stringMap &dataMap,
qDebug()<<" = = = = = CLIP NOT LOADED";
return;
}
QMapIterator<QString, QString> i(dataMap);
QList<int> missingThumbs;
auto json = QJsonDocument::fromJson(dataMap.toUtf8());
if (!json.isArray()) {
qDebug() << "Error loading zones : Json file should be an array";
return;
}
int maxFrame = clip->duration().frames(pCore->getCurrentFps()) - 1;
while (i.hasNext()) {
i.next();
if (!i.value().contains(QLatin1Char(';'))) {
// Problem, the zone has no in/out points
auto list = json.array();
for (const auto &entry : list) {
if (!entry.isObject()) {
qDebug() << "Warning : Skipping invalid marker data";
continue;
}
auto entryObj = entry.toObject();
if (!entryObj.contains(QLatin1String("name"))) {
qDebug() << "Warning : Skipping invalid zone(does not contain name)";
continue;
}
int in = entryObj[QLatin1String("in")].toInt();
int out = entryObj[QLatin1String("out")].toInt();
QString name = entryObj[QLatin1String("name")].toString(i18n("Zone"));
if (in >= out) {
qDebug() << "Warning : Invalid zone: "<<name<<", "<<in<<"-"<<out;
continue;
}
int in = i.value().section(QLatin1Char(';'), 0, 0).toInt();
int out = i.value().section(QLatin1Char(';'), 1, 1).toInt();
if (maxFrame > 0) {
out = qMin(out, maxFrame);
}
QString subId;
requestAddBinSubClip(subId, in, out, i.key(), id, undo, redo);
requestAddBinSubClip(subId, in, out, name, id, undo, redo);
}
}
......@@ -506,13 +518,31 @@ bool ProjectItemModel::requestBinClipDeletion(const std::shared_ptr<AbstractProj
Q_ASSERT(clip);
if (!clip) return false;
int parentId = -1;
if (auto ptr = clip->parent()) parentId = ptr->getId();
QString binId;
if (auto ptr = clip->parent()) {
parentId = ptr->getId();
binId = ptr->clipId();
}
bool isSubClip = clip->itemType() == AbstractProjectItem::SubClipItem;
clip->selfSoftDelete(undo, redo);
int id = clip->getId();
Fun operation = removeItem_lambda(id);
Fun reverse = addItem_lambda(clip, parentId);
bool res = operation();
if (res) {
if (isSubClip) {
Fun update_doc = [this, binId]() {
std::shared_ptr<AbstractProjectItem> parentItem = getItemByBinId(binId);
if (parentItem && parentItem->itemType() == AbstractProjectItem::ClipItem) {
auto clipItem = std::static_pointer_cast<ProjectClip>(parentItem);
clipItem->updateZones();
}
return true;
};
update_doc();
PUSH_LAMBDA(update_doc, operation);
PUSH_LAMBDA(update_doc, reverse);
}
UPDATE_UNDO_REDO(operation, reverse, undo, redo);
}
return res;
......@@ -689,6 +719,17 @@ bool ProjectItemModel::requestAddBinSubClip(QString &id, int in, int out, const
Fun redo = []() { return true; };
bool res = requestAddBinSubClip(id, in, out, zoneName, parentId, undo, redo);
if (res) {
Fun update_doc = [this, parentId]() {
std::shared_ptr<AbstractProjectItem> parentItem = getItemByBinId(parentId);
if (parentItem && parentItem->itemType() == AbstractProjectItem::ClipItem) {
auto clipItem = std::static_pointer_cast<ProjectClip>(parentItem);
clipItem->updateZones();
}
return true;
};
update_doc();
PUSH_LAMBDA(update_doc, undo);
PUSH_LAMBDA(update_doc, redo);
pCore->pushUndo(undo, redo, i18n("Add a sub clip"));
}
return res;
......
......@@ -99,7 +99,7 @@ public:
/** @brief Convenience method to access root folder */
std::shared_ptr<ProjectFolder> getRootFolder() const;
void loadSubClips(const QString &id, const stringMap &dataMap, Fun &undo, Fun &redo);
void loadSubClips(const QString &id, const QString &dataMap, Fun &undo, Fun &redo);
/* @brief Convenience method to retrieve a pointer to an element given its index */
std::shared_ptr<AbstractProjectItem> getBinItemByIndex(const QModelIndex &index) const;
......@@ -231,7 +231,7 @@ public slots:
/** @brief Create the subclips defined in the parent clip.
@param id is the id of the parent clip
@param data is a definition of the subclips (keys are subclips' names, value are "in:out")*/
void loadSubClips(const QString &id, const stringMap &clipData);
void loadSubClips(const QString &id, const QString &clipData);
private:
/** @brief Return reference to column specific data */
......
......@@ -22,6 +22,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "projectsubclip.h"
#include "projectclip.h"
#include "projectitemmodel.h"
#include "core.h"
#include "doc/kdenlivedoc.h"
#include "doc/docundostack.hpp"
#include "bincommands.h"
#include <KLocalizedString>
#include <QDomElement>
......@@ -47,7 +51,6 @@ ProjectSubClip::ProjectSubClip(const QString &id, const std::shared_ptr<ProjectC
}
m_clipStatus = StatusReady;
// Save subclip in MLT
parent->setProducerProperty("kdenlive:clipzone." + m_name, QString::number(in) + QLatin1Char(';') + QString::number(out));
connect(parent.get(), &ProjectClip::thumbReady, this, &ProjectSubClip::gotThumb);
}
......@@ -73,13 +76,6 @@ void ProjectSubClip::gotThumb(int pos, const QImage &img)
}
}
void ProjectSubClip::discard()
{
if (m_masterClip) {
m_masterClip->resetProducerProperty("kdenlive:clipzone." + m_name);
}
}
QString ProjectSubClip::getToolTip() const
{
return QStringLiteral("test");
......@@ -155,7 +151,8 @@ bool ProjectSubClip::rename(const QString &name, int column)
return false;
}
// Rename folder
// if (auto ptr = m_model.lock()) std::static_pointer_cast<ProjectItemModel>(ptr)->bin()->renameSubClipCommand(m_binId, name, m_name, m_in, m_out);
auto *command = new RenameBinSubClipCommand(pCore->bin(), m_masterClip->clipId(), name, m_name, m_inPoint, m_out);
pCore->currentDoc()->commandStack()->push(command);
return true;
}
......
......@@ -74,8 +74,6 @@ public:
void setThumbnail(const QImage &);
QPixmap thumbnail(int width, int height);
/** @brief Remove reference to this subclip in the master clip, to be done before a subclip is deleted. */
void discard();
QPoint zone() const override;
QString getToolTip() const override;
bool rename(const QString &name, int column) override;
......
......@@ -1939,7 +1939,33 @@ bool DocumentValidator::upgrade(double version, const double currentVersion)
}
Xml::setXmlProperty(mainplaylist.toElement(), QStringLiteral("kdenlive:docproperties.groups"), groupsData);
}
if (version < 0.99) {
// rename main bin playlist, create extra tracks for old type AV clips, port groups to JSon
QDomNodeList masterProducers = m_doc.elementsByTagName(QStringLiteral("producer"));
for (int i = 0; i < masterProducers.count(); i++) {
QMap<QString, QString> map = Xml::getXmlPropertyByWildcard(masterProducers.at(i).toElement(), QLatin1String("kdenlive:clipzone."));
if (map.isEmpty()) {
continue;
}
QJsonArray list;
QMapIterator<QString, QString> j(map);
while (j.hasNext()) {
j.next();
Xml::removeXmlProperty(masterProducers.at(i).toElement(), j.key());
QJsonObject currentZone;
currentZone.insert(QLatin1String("name"), QJsonValue(j.key().section(QLatin1Char('.'),1)));
if (!j.value().contains(QLatin1Char(';'))) {
// invalid zone
continue;
}
currentZone.insert(QLatin1String("in"), QJsonValue(j.value().section(QLatin1Char(';'), 0, 0).toInt()));
currentZone.insert(QLatin1String("out"), QJsonValue(j.value().section(QLatin1Char(';'), 1, 1).toInt()));
list.push_back(currentZone);
}
QJsonDocument json(list);
Xml::setXmlProperty(masterProducers.at(i).toElement(), QStringLiteral("kdenlive:clipzones"), QString(json.toJson()));
}
}
m_modified = true;
return true;
}
......
......@@ -69,7 +69,7 @@
#include <xlocale.h>
#endif
const double DOCUMENTVERSION = 0.98;
const double DOCUMENTVERSION = 0.99;
KdenliveDoc::KdenliveDoc(const QUrl &url, QString projectFolder, QUndoGroup *undoGroup, const QString &profileName, const QMap<QString, QString> &properties,
const QMap<QString, QString> &metadata, const QPoint &tracks, bool *openBackup, MainWindow *parent)
......@@ -833,11 +833,6 @@ QString KdenliveDoc::searchFileRecursively(const QDir &dir, const QString &match
return foundFileName;
}
// TODO refac : delete
std::shared_ptr<ProjectClip> KdenliveDoc::getBinClip(const QString &clipId)
{
return pCore->bin()->getBinClip(clipId);
}
QStringList KdenliveDoc::getBinFolderClipIds(const QString &folderId) const
{
......
......@@ -74,8 +74,6 @@ public:
std::shared_ptr<DocUndoStack> commandStack();
int getFramePos(const QString &duration);
/** @brief Get a bin's clip from its id. */
std::shared_ptr<ProjectClip> getBinClip(const QString &clipId);
/** @brief Get a list of all clip ids that are inside a folder. */
QStringList getBinFolderClipIds(const QString &folderId) const;
......
......@@ -152,17 +152,23 @@ bool SceneSplitJob::commitResult(Fun &undo, Fun &redo)
int ix = 1;
int lastCut = 0;
QMap<QString, QString> zoneData;
QJsonArray list;
QJsonDocument json(list);
for (const QString &marker : markerData) {
int pos = marker.section(QLatin1Char('='), 0, 0).toInt();
if (pos <= lastCut + 1 || pos - lastCut < m_minInterval) {
continue;
}
zoneData.insert(i18n("Scene %1", ix), QString("%1;%2").arg(lastCut).arg(pos - 1));
QJsonObject currentZone;
currentZone.insert(QLatin1String("name"), QJsonValue(i18n("Scene %1", ix)));
currentZone.insert(QLatin1String("in"), QJsonValue(lastCut));
currentZone.insert(QLatin1String("out"), QJsonValue(pos - 1));
list.push_back(currentZone);
lastCut = pos;
ix++;
}
if (!zoneData.isEmpty()) {
pCore->projectItemModel()->loadSubClips(m_clipId, zoneData, undo, redo);
if (!json.isEmpty()) {
pCore->projectItemModel()->loadSubClips(m_clipId, QString(json.toJson()), undo, redo);
}
}
qDebug() << "RESULT of the SCENESPLIT filter:" << result;
......
......@@ -146,6 +146,19 @@ bool Xml::hasXmlProperty(QDomElement element, const QString &propertyName)
return false;
}
QMap<QString, QString> Xml::getXmlPropertyByWildcard(QDomElement element, const QString &propertyName)
{
QMap <QString, QString> props;
QDomNodeList params = element.elementsByTagName(QStringLiteral("property"));
for (int i = 0; i < params.count(); ++i) {
QDomElement e = params.item(i).toElement();
if (e.attribute(QStringLiteral("name")).startsWith(propertyName)) {
props.insert(e.attribute(QStringLiteral("name")), e.text());
}
}
return props;
}
void Xml::removeXmlProperty(QDomElement effect, const QString &name)
{
QDomNodeList params = effect.elementsByTagName(QStringLiteral("property"));
......
......@@ -82,6 +82,8 @@ void removeMetaProperties(QDomElement producer);
void renameXmlProperty(const QDomElement &effect, const QString &oldName, const QString &newName);
QMap<QString, QString> getXmlPropertyByWildcard(QDomElement element, const QString &propertyName);
} // namespace Xml
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment