Commit eb957889 authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle
Browse files

Partial fix for pasting to a document with a different fps (effect keyframes...

Partial fix for pasting to a document with a different fps (effect keyframes remain broken), display a warning.
Related to #1500
parent 64362c30
Pipeline #237098 passed with stage
in 10 minutes and 15 seconds
......@@ -842,7 +842,7 @@ bool GroupsModel::fromJson(const QString &data)
return ok;
}
void GroupsModel::adjustOffset(QJsonArray &updatedNodes, const QJsonObject &childObject, int offset, const QMap<int, int> &trackMap)
void GroupsModel::adjustOffset(QJsonArray &updatedNodes, const QJsonObject &childObject, int offset, const QMap<int, int> &trackMap, double ratio)
{
auto value = childObject.value(QLatin1String("children"));
auto children = value.toArray();
......@@ -856,7 +856,7 @@ void GroupsModel::adjustOffset(QJsonArray &updatedNodes, const QJsonObject &chil
if (auto ptr = m_parent.lock()) {
QString cur_data = child.value(QLatin1String("data")).toString();
int trackId = cur_data.section(":", 0, 0).toInt();
int pos = cur_data.section(":", 1, 1).toInt();
int pos = cur_data.section(":", 1, 1).toInt() * ratio;
int trackPos = trackId == -2 ? -2 : ptr->getTrackPosition(trackMap.value(trackId));
pos += offset;
child.insert(QLatin1String("data"), QJsonValue(QString("%1:%2").arg(trackPos).arg(pos)));
......@@ -873,7 +873,7 @@ void GroupsModel::adjustOffset(QJsonArray &updatedNodes, const QJsonObject &chil
}
}
bool GroupsModel::fromJsonWithOffset(const QString &data, const QMap<int, int> &trackMap, int offset, Fun &undo, Fun &redo)
bool GroupsModel::fromJsonWithOffset(const QString &data, const QMap<int, int> &trackMap, int offset, double ratio, Fun &undo, Fun &redo)
{
Fun local_undo = []() { return true; };
Fun local_redo = []() { return true; };
......@@ -900,7 +900,7 @@ bool GroupsModel::fromJsonWithOffset(const QString &data, const QMap<int, int> &
continue;
}
// Adjust offset
adjustOffset(updatedNodes, obj, offset, trackMap);
adjustOffset(updatedNodes, obj, offset, trackMap, ratio);
QJsonObject currentGroup;
currentGroup.insert(QLatin1String("children"), QJsonValue(updatedNodes));
currentGroup.insert(QLatin1String("type"), QJsonValue(groupTypeToStr(type)));
......
......@@ -144,7 +144,7 @@ public:
const QString toJson() const;
const QString toJson(const std::unordered_set<int> &roots) const;
bool fromJson(const QString &data);
bool fromJsonWithOffset(const QString &data, const QMap<int, int> &trackMap, int offset, Fun &undo, Fun &redo);
bool fromJsonWithOffset(const QString &data, const QMap<int, int> &trackMap, int offset, double ratio, Fun &undo, Fun &redo);
/** @brief if the clip belongs to a AVSplit group, then return the id of the other corresponding clip. Otherwise, returns -1 */
int getSplitPartner(int id) const;
......@@ -205,7 +205,14 @@ protected:
*/
void setType(int gid, GroupType type);
void adjustOffset(QJsonArray &updatedNodes, const QJsonObject &childObject, int offset, const QMap<int, int> &trackMap);
/** @brief Adjust json group data according to offset
@param updatedNodes The resulting nodes
@param childObject The source data
@param offset The position frame offset
@param trackMap The map from source to destination tracks
@param ratio A ratio to apply to all positions (used in case of fps conversion)
*/
void adjustOffset(QJsonArray &updatedNodes, const QJsonObject &childObject, int offset, const QMap<int, int> &trackMap, double ratio = 1.);
private:
std::weak_ptr<TimelineItemModel> m_parent;
......
......@@ -24,6 +24,7 @@ SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "trackmodel.hpp"
#include "transitions/transitionsrepository.hpp"
#include <KMessageBox>
#include <QApplication>
#include <QDebug>
#include <QInputDialog>
......@@ -1523,6 +1524,7 @@ QString TimelineFunctions::copyClips(const std::shared_ptr<TimelineItemModel> &t
QDomDocument copiedItems;
int offset = -1;
QDomElement container = copiedItems.createElement(QStringLiteral("kdenlive-scene"));
container.setAttribute(QStringLiteral("fps"), QString::number(pCore->getCurrentFps()));
copiedItems.appendChild(container);
QStringList binIds;
for (int id : allIds) {
......@@ -1853,6 +1855,20 @@ bool TimelineFunctions::pasteClips(const std::shared_ptr<TimelineItemModel> &tim
if (!docId.isEmpty() && docId != pCore->currentDoc()->getDocumentProperty(QStringLiteral("documentid"))) {
// paste from another document, import bin clips
// Check if the fps matches
QString currentFps = QString::number(pCore->getCurrentFps());
QString sourceFps = copiedItems.documentElement().attribute(QStringLiteral("fps"));
if (currentFps != sourceFps && !sourceFps.isEmpty()) {
if (KMessageBox::questionYesNo(
pCore->window(),
i18n("The source project has a different framerate (%1fps) than your current project.<br/>Clips or keyframes might be messed up.",
sourceFps),
i18n("Pasting Warning"), KGuiItem(i18n("Paste")), KGuiItem(i18n("Cancel"))) != KMessageBox::Yes) {
semaphore.release(1);
return false;
}
copiedItems.documentElement().setAttribute(QStringLiteral("fps-ratio"), pCore->getCurrentFps() / sourceFps.toDouble());
}
QString folderId = pCore->projectItemModel()->getFolderIdByName(i18n("Pasted clips"));
if (folderId.isEmpty()) {
// Folder does not exist
......@@ -1914,6 +1930,12 @@ bool TimelineFunctions::pasteTimelineClips(const std::shared_ptr<TimelineItemMod
int offset = copiedItems.documentElement().attribute(QStringLiteral("offset")).toInt();
bool res = true;
std::unordered_map<int, int> correspondingIds;
double ratio = 1.0;
if (copiedItems.documentElement().hasAttribute(QStringLiteral("fps-ratio"))) {
ratio = copiedItems.documentElement().attribute(QStringLiteral("fps-ratio")).toDouble();
offset *= ratio;
}
QDomElement documentMixes = copiedItems.createElement(QStringLiteral("mixes"));
for (int i = 0; i < clips.count(); i++) {
QDomElement prod = clips.at(i).toElement();
......@@ -1937,7 +1959,13 @@ bool TimelineFunctions::pasteTimelineClips(const std::shared_ptr<TimelineItemMod
semaphore.release(1);
return false;
}
int pos = prod.attribute(QStringLiteral("position")).toInt() - offset;
int pos = prod.attribute(QStringLiteral("position")).toInt();
if (ratio != 1.0) {
in = in * ratio;
out = out * ratio;
pos = pos * ratio;
}
pos -= offset;
double speed = prod.attribute(QStringLiteral("speed")).toDouble();
bool warp_pitch = false;
if (!qFuzzyCompare(speed, 1.)) {
......@@ -2025,9 +2053,9 @@ bool TimelineFunctions::pasteTimelineClips(const std::shared_ptr<TimelineItemMod
MixInfo mixData;
mixData.firstClipId = correspondingIds[originalFirstClipId];
mixData.secondClipId = correspondingIds[originalSecondClipId];
mixData.firstClipInOut.second = mix.attribute(QLatin1String("mixEnd")).toInt();
mixData.secondClipInOut.first = mix.attribute(QLatin1String("mixStart")).toInt();
mixData.mixOffset = mix.attribute(QLatin1String("mixOffset")).toInt();
mixData.firstClipInOut.second = mix.attribute(QLatin1String("mixEnd")).toInt() * ratio;
mixData.secondClipInOut.first = mix.attribute(QLatin1String("mixStart")).toInt() * ratio;
mixData.mixOffset = mix.attribute(QLatin1String("mixOffset")).toInt() * ratio;
timeline->getTrackById_const(mix.attribute(QLatin1String("tid")).toInt())->createMix(mixData, mixParams, true);
}
}
......@@ -2036,8 +2064,8 @@ bool TimelineFunctions::pasteTimelineClips(const std::shared_ptr<TimelineItemMod
for (int i = 0; res && i < compositions.count(); i++) {
QDomElement prod = compositions.at(i).toElement();
QString originalId = prod.attribute(QStringLiteral("composition"));
int in = prod.attribute(QStringLiteral("in")).toInt();
int out = prod.attribute(QStringLiteral("out")).toInt();
int in = prod.attribute(QStringLiteral("in")).toInt() * ratio;
int out = prod.attribute(QStringLiteral("out")).toInt() * ratio;
int curTrackId = tracksMap.value(prod.attribute(QStringLiteral("track")).toInt());
int aTrackId = prod.attribute(QStringLiteral("a_track")).toInt();
if (tracksMap.contains(aTrackId)) {
......@@ -2045,7 +2073,7 @@ bool TimelineFunctions::pasteTimelineClips(const std::shared_ptr<TimelineItemMod
} else {
aTrackId = 0;
}
int pos = prod.attribute(QStringLiteral("position")).toInt() - offset;
int pos = prod.attribute(QStringLiteral("position")).toInt() * ratio - offset;
int newId;
auto transProps = std::make_unique<Mlt::Properties>();
QDomNodeList props = prod.elementsByTagName(QStringLiteral("property"));
......@@ -2061,8 +2089,8 @@ bool TimelineFunctions::pasteTimelineClips(const std::shared_ptr<TimelineItemMod
auto subModel = pCore->getSubtitleModel(true);
for (int i = 0; res && i < subtitles.count(); i++) {
QDomElement prod = subtitles.at(i).toElement();
int in = prod.attribute(QStringLiteral("in")).toInt() - offset;
int out = prod.attribute(QStringLiteral("out")).toInt() - offset;
int in = prod.attribute(QStringLiteral("in")).toInt() * ratio - offset;
int out = prod.attribute(QStringLiteral("out")).toInt() * ratio - offset;
QString text = prod.attribute(QStringLiteral("text"));
res = res && subModel->addSubtitle(GenTime(position + in, pCore->getCurrentFps()), GenTime(position + out, pCore->getCurrentFps()), text,
timeline_undo, timeline_redo);
......@@ -2077,7 +2105,7 @@ bool TimelineFunctions::pasteTimelineClips(const std::shared_ptr<TimelineItemMod
// Rebuild groups
const QString groupsData = copiedItems.documentElement().firstChildElement(QStringLiteral("groups")).text();
if (!groupsData.isEmpty()) {
timeline->m_groups->fromJsonWithOffset(groupsData, tracksMap, position - offset, timeline_undo, timeline_redo);
timeline->m_groups->fromJsonWithOffset(groupsData, tracksMap, position - offset, ratio, timeline_undo, timeline_redo);
}
// Ensure to clear selection in undo/redo too.
Fun unselect = [timeline]() {
......
Supports Markdown
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