X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fkdenlivedoc.cpp;h=7ea9272a11a01febe267adc656dd95fa6675577a;hb=e3b4bf11679a3a195be8896f465b1616581f1fb3;hp=d746f87bedbacc20724210eb131d6b635b34f093;hpb=99aa6b9d23d0a3b603b71d1e2feba0041ea3e3f5;p=kdenlive diff --git a/src/kdenlivedoc.cpp b/src/kdenlivedoc.cpp index d746f87b..7ea9272a 100644 --- a/src/kdenlivedoc.cpp +++ b/src/kdenlivedoc.cpp @@ -41,31 +41,34 @@ #include #include +#include +#include #include -const double DOCUMENTVERSION = 0.83; +const double DOCUMENTVERSION = 0.85; -KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup *undoGroup, const QString &profileName, const QPoint tracks, Render *render, MainWindow *parent) : +KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup *undoGroup, QString profileName, const QPoint tracks, Render *render, MainWindow *parent) : QObject(parent), m_autosave(NULL), m_url(url), - m_zoom(7), - m_startPos(0), m_render(render), m_commandStack(new QUndoStack(undoGroup)), m_modified(false), m_projectFolder(projectFolder), - m_documentLoadingStep(0.0), - m_documentLoadingProgress(0), - m_abortLoading(false), - m_zoneStart(0), - m_zoneEnd(100) + m_abortLoading(false) { m_clipManager = new ClipManager(this); m_autoSaveTimer = new QTimer(this); m_autoSaveTimer->setSingleShot(true); bool success = false; + + // init default document properties + m_documentProperties["zoom"] = "7"; + m_documentProperties["verticalzoom"] = "1"; + m_documentProperties["zonein"] = "0"; + m_documentProperties["zoneout"] = "100"; + if (!url.isEmpty()) { QString tmpFile; success = KIO::NetAccess::download(url.path(), tmpFile, parent); @@ -74,17 +77,22 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup else { QFile file(tmpFile); QString errorMsg; + QDomImplementation impl; + impl.setInvalidDataPolicy(QDomImplementation::DropInvalidChars); success = m_document.setContent(&file, false, &errorMsg); file.close(); KIO::NetAccess::removeTempFile(tmpFile); + if (!success) // It is corrupted KMessageBox::error(parent, errorMsg); else { + parent->slotGotProgressInfo(i18n("Validating"), 0); DocumentValidator validator(m_document); success = validator.isProject(); - if (!success) // It is not a project file - parent->slotGotProgressInfo(i18n("File %1 is not a Kdenlive project file.", m_url.path()), 100); - else { + if (!success) { + // It is not a project file + parent->slotGotProgressInfo(i18n("File %1 is not a Kdenlive project file", m_url.path()), 0); + } else { /* * Validate the file against the current version (upgrade * and recover it if needed). It is NOT a passive operation @@ -92,34 +100,33 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup // TODO: backup the document or alert the user? success = validator.validate(DOCUMENTVERSION); if (success) { // Let the validator handle error messages - setModified(validator.isModified()); - QDomNode infoXmlNode = m_document.elementsByTagName("kdenlivedoc").at(0); - QDomElement infoXml = infoXmlNode.toElement(); - QDomNode mlt = m_document.elementsByTagName("mlt").at(0); - - QString profilePath = infoXml.attribute("profile"); - m_projectFolder = infoXml.attribute("projectfolder"); - - m_startPos = infoXml.attribute("position").toInt(); - m_zoom = infoXml.attribute("zoom", "7").toInt(); - m_zoneStart = infoXml.attribute("zonein", "0").toInt(); - m_zoneEnd = infoXml.attribute("zoneout", "100").toInt(); - + parent->slotGotProgressInfo(i18n("Loading"), 0); + QDomElement mlt = m_document.firstChildElement("mlt"); + QDomElement infoXml = mlt.firstChildElement("kdenlivedoc"); + + profileName = infoXml.attribute("profile"); + m_projectFolder = KUrl(infoXml.attribute("projectfolder")); + QDomElement docproperties = infoXml.firstChildElement("documentproperties"); + QDomNamedNodeMap props = docproperties.attributes(); + for (int i = 0; i < props.count(); i++) { + m_documentProperties.insert(props.item(i).nodeName(), props.item(i).nodeValue()); + } // Build tracks QDomElement e; - QDomNode tracksinfo = m_document.elementsByTagName("tracksinfo").at(0); - TrackInfo projectTrack; + QDomElement tracksinfo = infoXml.firstChildElement("tracksinfo"); if (!tracksinfo.isNull()) { QDomNodeList trackslist = tracksinfo.childNodes(); int maxchild = trackslist.count(); for (int k = 0; k < maxchild; k++) { e = trackslist.at(k).toElement(); if (e.tagName() == "trackinfo") { + TrackInfo projectTrack; if (e.attribute("type") == "audio") projectTrack.type = AUDIOTRACK; else projectTrack.type = VIDEOTRACK; projectTrack.isMute = e.attribute("mute").toInt(); projectTrack.isBlind = e.attribute("blind").toInt(); projectTrack.isLocked = e.attribute("locked").toInt(); + projectTrack.trackName = e.attribute("trackname"); m_tracksList.append(projectTrack); } } @@ -127,29 +134,19 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup } QDomNodeList producers = m_document.elementsByTagName("producer"); QDomNodeList infoproducers = m_document.elementsByTagName("kdenlive_producer"); + parent->slotGotProgressInfo(i18n("Check missing clips"), 0); if (checkDocumentClips(infoproducers) == false) m_abortLoading = true; const int max = producers.count(); const int infomax = infoproducers.count(); - QDomNodeList folders = m_document.elementsByTagName("folder"); + QDomNodeList folders = m_document.elementsByTagName("folder"); for (int i = 0; i < folders.count(); i++) { e = folders.item(i).cloneNode().toElement(); m_clipManager->addFolder(e.attribute("id"), e.attribute("name")); } - if (max > 0) { - m_documentLoadingStep = 100.0 / (max + infomax + m_document.elementsByTagName("entry").count()); - parent->slotGotProgressInfo(i18n("Loading project clips"), (int) m_documentLoadingProgress); - } - - for (int i = 0; i < infomax && !m_abortLoading; i++) { e = infoproducers.item(i).cloneNode().toElement(); - if (m_documentLoadingStep > 0) { - m_documentLoadingProgress += m_documentLoadingStep; - parent->slotGotProgressInfo(QString(), (int) m_documentLoadingProgress); - //qApp->processEvents(); - } QString prodId = e.attribute("id"); if (!e.isNull() && prodId != "black" && !prodId.startsWith("slowmotion") && !m_abortLoading) { e.setTagName("producer"); @@ -164,13 +161,12 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup } } addClipInfo(e, orig, prodId); - kDebug() << "// KDENLIVE PRODUCER: " << prodId; } } if (m_abortLoading) { //parent->slotGotProgressInfo(i18n("File %1 is not a Kdenlive project file."), 100); emit resetProjectList(); - m_startPos = 0; + m_documentProperties.remove("position"); m_url = KUrl(); m_tracksList.clear(); kWarning() << "Aborted loading of: " << url.path(); @@ -178,7 +174,7 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup setProfilePath(KdenliveSettings::default_profile()); m_clipManager->clear(); } else { - QDomNode markers = m_document.elementsByTagName("markers").at(0); + QDomElement markers = infoXml.firstChildElement("markers"); if (!markers.isNull()) { QDomNodeList markerslist = markers.childNodes(); int maxchild = markerslist.count(); @@ -188,9 +184,10 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup m_clipManager->getClipById(e.attribute("id"))->addSnapMarker(GenTime(e.attribute("time").toDouble()), e.attribute("comment")); } } - mlt.removeChild(markers); + infoXml.removeChild(markers); } - m_document.removeChild(infoXmlNode); + setProfilePath(profileName); + setModified(validator.isModified()); kDebug() << "Reading file: " << url.path() << ", found clips: " << producers.count(); } } @@ -201,12 +198,25 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup // Something went wrong, or a new file was requested: create a new project if (!success) { + setProfilePath(profileName); m_url = KUrl(); m_document = createEmptyDocument(tracks.x(), tracks.y()); } // Set the video profile (empty == default) - setProfilePath(profileName); + KdenliveSettings::setCurrent_profile(profilePath()); + + // Ask to create the project directory if it does not exist + if (!QFile::exists(m_projectFolder.path())) { + int create = KMessageBox::questionYesNo(parent, i18n("Project directory %1 does not exist. Create it?", m_projectFolder.path())); + if (create == KMessageBox::Yes) { + QDir projectDir(m_projectFolder.path()); + bool ok = projectDir.mkpath(m_projectFolder.path()); + if (!ok) { + KMessageBox::sorry(parent, i18n("The directory %1, could not be created.\nPlease make sure you have the required permissions.", m_projectFolder.path())); + } + } + } // Make sure the project folder is usable if (m_projectFolder.isEmpty() || !KIO::NetAccess::exists(m_projectFolder.path(), KIO::NetAccess::DestinationSide, parent)) { @@ -215,13 +225,9 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup } // Make sure that the necessary folders exist - KStandardDirs::makeDir(m_projectFolder.path() + "/titles/"); - KStandardDirs::makeDir(m_projectFolder.path() + "/thumbs/"); - KStandardDirs::makeDir(m_projectFolder.path() + "/ladspa/"); - - kDebug() << "Kdenlive document, init timecode: " << m_fps; - if (m_fps == 30000.0 / 1001.0) m_timecode.setFormat(30, true); - else m_timecode.setFormat((int) m_fps); + KStandardDirs::makeDir(m_projectFolder.path(KUrl::AddTrailingSlash) + "titles/"); + KStandardDirs::makeDir(m_projectFolder.path(KUrl::AddTrailingSlash) + "thumbs/"); + KStandardDirs::makeDir(m_projectFolder.path(KUrl::AddTrailingSlash) + "ladspa/"); //kDebug() << "// SETTING SCENE LIST:\n\n" << m_document.toString(); connect(m_autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave())); @@ -229,24 +235,61 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup KdenliveDoc::~KdenliveDoc() { + m_autoSaveTimer->stop(); delete m_commandStack; + kDebug() << "// DEL CLP MAN"; delete m_clipManager; + kDebug() << "// DEL CLP MAN done"; delete m_autoSaveTimer; if (m_autosave) { - m_autosave->remove(); + if (!m_autosave->fileName().isEmpty()) m_autosave->remove(); delete m_autosave; } } -void KdenliveDoc::setSceneList() +int KdenliveDoc::setSceneList() { - m_render->setSceneList(m_document.toString(), m_startPos); + m_render->resetProfile(KdenliveSettings::current_profile()); + if (m_render->setSceneList(m_document.toString(), m_documentProperties.value("position").toInt()) == -1) { + // INVALID MLT Consumer, something is wrong + return -1; + } + m_documentProperties.remove("position"); // m_document xml is now useless, clear it m_document.clear(); - checkProjectClips(); + return 0; } -QDomDocument KdenliveDoc::createEmptyDocument(const int videotracks, const int audiotracks) +QDomDocument KdenliveDoc::createEmptyDocument(int videotracks, int audiotracks) +{ + m_tracksList.clear(); + + // Tracks are added «backwards», so we need to reverse the track numbering + // mbt 331: http://www.kdenlive.org/mantis/view.php?id=331 + // Better default names for tracks: Audio 1 etc. instead of blank numbers + for (int i = 0; i < audiotracks; i++) { + TrackInfo audioTrack; + audioTrack.type = AUDIOTRACK; + audioTrack.isMute = false; + audioTrack.isBlind = true; + audioTrack.isLocked = false; + audioTrack.trackName = QString("Audio ") + QString::number(audiotracks - i); + m_tracksList.append(audioTrack); + + } + for (int i = 0; i < videotracks; i++) { + TrackInfo videoTrack; + videoTrack.type = VIDEOTRACK; + videoTrack.isMute = false; + videoTrack.isBlind = false; + videoTrack.isLocked = false; + videoTrack.trackName = QString("Video ") + QString::number(videotracks - i); + m_tracksList.append(videoTrack); + } + return createEmptyDocument(m_tracksList); +} + +QDomDocument KdenliveDoc::createEmptyDocument(QList tracks) { // Creating new document QDomDocument doc; @@ -254,17 +297,52 @@ QDomDocument KdenliveDoc::createEmptyDocument(const int videotracks, const int a doc.appendChild(mlt); - TrackInfo videoTrack; - videoTrack.type = VIDEOTRACK; - videoTrack.isMute = false; - videoTrack.isBlind = false; - videoTrack.isLocked = false; + // Create black producer + // For some unknown reason, we have to build the black producer here and not in renderer.cpp, otherwise + // the composite transitions with the black track are corrupted. + QDomElement blk = doc.createElement("producer"); + blk.setAttribute("in", 0); + blk.setAttribute("out", 500); + blk.setAttribute("id", "black"); + + QDomElement property = doc.createElement("property"); + property.setAttribute("name", "mlt_type"); + QDomText value = doc.createTextNode("producer"); + property.appendChild(value); + blk.appendChild(property); + + property = doc.createElement("property"); + property.setAttribute("name", "aspect_ratio"); + value = doc.createTextNode(QString::number(0.0)); + property.appendChild(value); + blk.appendChild(property); + + property = doc.createElement("property"); + property.setAttribute("name", "length"); + value = doc.createTextNode(QString::number(15000)); + property.appendChild(value); + blk.appendChild(property); + + property = doc.createElement("property"); + property.setAttribute("name", "eof"); + value = doc.createTextNode("pause"); + property.appendChild(value); + blk.appendChild(property); + + property = doc.createElement("property"); + property.setAttribute("name", "resource"); + value = doc.createTextNode("black"); + property.appendChild(value); + blk.appendChild(property); + + property = doc.createElement("property"); + property.setAttribute("name", "mlt_service"); + value = doc.createTextNode("colour"); + property.appendChild(value); + blk.appendChild(property); + + mlt.appendChild(blk); - TrackInfo audioTrack; - audioTrack.type = AUDIOTRACK; - audioTrack.isMute = false; - audioTrack.isBlind = true; - audioTrack.isLocked = false; QDomElement tractor = doc.createElement("tractor"); tractor.setAttribute("id", "maintractor"); @@ -273,9 +351,14 @@ QDomDocument KdenliveDoc::createEmptyDocument(const int videotracks, const int a playlist.setAttribute("id", "black_track"); mlt.appendChild(playlist); + QDomElement blank0 = doc.createElement("entry"); + blank0.setAttribute("in", "0"); + blank0.setAttribute("out", "1"); + blank0.setAttribute("producer", "black"); + playlist.appendChild(blank0); // create playlists - int total = audiotracks + videotracks + 1; + int total = tracks.count() + 1; for (int i = 1; i < total; i++) { QDomElement playlist = doc.createElement("playlist"); @@ -287,21 +370,17 @@ QDomDocument KdenliveDoc::createEmptyDocument(const int videotracks, const int a track0.setAttribute("producer", "black_track"); tractor.appendChild(track0); - // create audio tracks - for (int i = 1; i < audiotracks + 1; i++) { - QDomElement track = doc.createElement("track"); - track.setAttribute("producer", "playlist" + QString::number(i)); - track.setAttribute("hide", "video"); - tractor.appendChild(track); - m_tracksList.append(audioTrack); - } - - // create video tracks - for (int i = audiotracks + 1; i < total; i++) { + // create audio and video tracks + for (int i = 1; i < total; i++) { QDomElement track = doc.createElement("track"); track.setAttribute("producer", "playlist" + QString::number(i)); + if (tracks.at(i - 1).type == AUDIOTRACK) { + track.setAttribute("hide", "video"); + } else if (tracks.at(i - 1).isBlind) + track.setAttribute("hide", "video"); + if (tracks.at(i - 1).isMute) + track.setAttribute("hide", "audio"); tractor.appendChild(track); - m_tracksList.append(videoTrack); } for (int i = 2; i < total ; i++) { @@ -386,32 +465,38 @@ void KdenliveDoc::slotAutoSave() } } -void KdenliveDoc::setZoom(int factor) +void KdenliveDoc::setZoom(int horizontal, int vertical) { - m_zoom = factor; + m_documentProperties["zoom"] = QString::number(horizontal); + m_documentProperties["verticalzoom"] = QString::number(vertical); } -int KdenliveDoc::zoom() const +QPoint KdenliveDoc::zoom() const { - return m_zoom; + return QPoint(m_documentProperties.value("zoom").toInt(), m_documentProperties.value("verticalzoom").toInt()); } void KdenliveDoc::setZone(int start, int end) { - m_zoneStart = start; - m_zoneEnd = end; + m_documentProperties["zonein"] = QString::number(start); + m_documentProperties["zoneout"] = QString::number(end); } QPoint KdenliveDoc::zone() const { - return QPoint(m_zoneStart, m_zoneEnd); + return QPoint(m_documentProperties.value("zonein").toInt(), m_documentProperties.value("zoneout").toInt()); } bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene) { QDomDocument sceneList; sceneList.setContent(scene, true); - QDomNode mlt = sceneList.elementsByTagName("mlt").at(0); + QDomElement mlt = sceneList.firstChildElement("mlt"); + if (mlt.isNull() || !mlt.hasChildNodes()) { + //Make sure we don't save if scenelist is corrupted + KMessageBox::error(kapp->activeWindow(), i18n("Cannot write to file %1, scene list is corrupted.", path)); + return false; + } QDomElement addedXml = sceneList.createElement("kdenlivedoc"); mlt.appendChild(addedXml); @@ -419,20 +504,40 @@ bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene) addedXml.setAttribute("version", DOCUMENTVERSION); addedXml.setAttribute("kdenliveversion", VERSION); addedXml.setAttribute("profile", profilePath()); - addedXml.setAttribute("position", m_render->seekPosition().frames(m_fps)); - addedXml.setAttribute("zonein", m_zoneStart); - addedXml.setAttribute("zoneout", m_zoneEnd); addedXml.setAttribute("projectfolder", m_projectFolder.path()); - addedXml.setAttribute("zoom", m_zoom); + + QDomElement docproperties = sceneList.createElement("documentproperties"); + QMapIterator i(m_documentProperties); + while (i.hasNext()) { + i.next(); + docproperties.setAttribute(i.key(), i.value()); + } + docproperties.setAttribute("position", m_render->seekPosition().frames(m_fps)); + addedXml.appendChild(docproperties); + + // Add profile info + QDomElement profileinfo = sceneList.createElement("profileinfo"); + profileinfo.setAttribute("description", m_profile.description); + profileinfo.setAttribute("frame_rate_num", m_profile.frame_rate_num); + profileinfo.setAttribute("frame_rate_den", m_profile.frame_rate_den); + profileinfo.setAttribute("width", m_profile.width); + profileinfo.setAttribute("height", m_profile.height); + profileinfo.setAttribute("progressive", m_profile.progressive); + profileinfo.setAttribute("sample_aspect_num", m_profile.sample_aspect_num); + profileinfo.setAttribute("sample_aspect_den", m_profile.sample_aspect_den); + profileinfo.setAttribute("display_aspect_num", m_profile.display_aspect_num); + profileinfo.setAttribute("display_aspect_den", m_profile.display_aspect_den); + addedXml.appendChild(profileinfo); // tracks info QDomElement tracksinfo = sceneList.createElement("tracksinfo"); - foreach(const TrackInfo &info, m_tracksList) { + foreach(const TrackInfo & info, m_tracksList) { QDomElement trackinfo = sceneList.createElement("trackinfo"); if (info.type == AUDIOTRACK) trackinfo.setAttribute("type", "audio"); trackinfo.setAttribute("mute", info.isMute); trackinfo.setAttribute("blind", info.isBlind); trackinfo.setAttribute("locked", info.isLocked); + trackinfo.setAttribute("trackname", info.trackName); tracksinfo.appendChild(trackinfo); } addedXml.appendChild(tracksinfo); @@ -508,8 +613,8 @@ void KdenliveDoc::setProjectFolder(KUrl url) if (url == m_projectFolder) return; setModified(true); KStandardDirs::makeDir(url.path()); - KStandardDirs::makeDir(url.path() + "/titles/"); - KStandardDirs::makeDir(url.path() + "/thumbs/"); + KStandardDirs::makeDir(url.path(KUrl::AddTrailingSlash) + "titles/"); + KStandardDirs::makeDir(url.path(KUrl::AddTrailingSlash) + "thumbs/"); if (KMessageBox::questionYesNo(kapp->activeWindow(), i18n("You have changed the project folder. Do you want to copy the cached data from %1 to the new folder %2?").arg(m_projectFolder.path(), url.path())) == KMessageBox::Yes) moveProjectData(url); m_projectFolder = url; } @@ -523,20 +628,20 @@ void KdenliveDoc::moveProjectData(KUrl url) if (clip->clipType() == TEXT) { // the image for title clip must be moved KUrl oldUrl = clip->fileURL(); - KUrl newUrl = KUrl(url.path() + "/titles/" + oldUrl.fileName()); + KUrl newUrl = KUrl(url.path(KUrl::AddTrailingSlash) + "titles/" + oldUrl.fileName()); KIO::Job *job = KIO::copy(oldUrl, newUrl); if (KIO::NetAccess::synchronousRun(job, 0)) clip->setProperty("resource", newUrl.path()); } QString hash = clip->getClipHash(); - KUrl oldVideoThumbUrl = KUrl(m_projectFolder.path() + "/thumbs/" + hash + ".png"); - KUrl oldAudioThumbUrl = KUrl(m_projectFolder.path() + "/thumbs/" + hash + ".thumb"); + KUrl oldVideoThumbUrl = KUrl(m_projectFolder.path(KUrl::AddTrailingSlash) + "thumbs/" + hash + ".png"); + KUrl oldAudioThumbUrl = KUrl(m_projectFolder.path(KUrl::AddTrailingSlash) + "thumbs/" + hash + ".thumb"); if (KIO::NetAccess::exists(oldVideoThumbUrl, KIO::NetAccess::SourceSide, 0)) { - KUrl newUrl = KUrl(url.path() + "/thumbs/" + hash + ".png"); + KUrl newUrl = KUrl(url.path(KUrl::AddTrailingSlash) + "thumbs/" + hash + ".png"); KIO::Job *job = KIO::copy(oldVideoThumbUrl, newUrl); KIO::NetAccess::synchronousRun(job, 0); } if (KIO::NetAccess::exists(oldAudioThumbUrl, KIO::NetAccess::SourceSide, 0)) { - KUrl newUrl = KUrl(url.path() + "/thumbs/" + hash + ".thumb"); + KUrl newUrl = KUrl(url.path(KUrl::AddTrailingSlash) + "thumbs/" + hash + ".thumb"); KIO::Job *job = KIO::copy(oldAudioThumbUrl, newUrl); if (KIO::NetAccess::synchronousRun(job, 0)) clip->refreshThumbUrl(); } @@ -553,19 +658,64 @@ MltVideoProfile KdenliveDoc::mltProfile() const return m_profile; } -void KdenliveDoc::setProfilePath(QString path) +bool KdenliveDoc::setProfilePath(QString path) { if (path.isEmpty()) path = KdenliveSettings::default_profile(); if (path.isEmpty()) path = "dv_pal"; m_profile = ProfilesDialog::getVideoProfile(path); + bool current_fps = m_fps; + if (m_profile.path.isEmpty()) { + // Profile not found, use embedded profile + QDomElement profileInfo = m_document.elementsByTagName("profileinfo").at(0).toElement(); + if (profileInfo.isNull()) { + KMessageBox::information(kapp->activeWindow(), i18n("Project profile was not found, using default profile."), i18n("Missing Profile")); + m_profile = ProfilesDialog::getVideoProfile(KdenliveSettings::default_profile()); + } else { + m_profile.description = profileInfo.attribute("description"); + m_profile.frame_rate_num = profileInfo.attribute("frame_rate_num").toInt(); + m_profile.frame_rate_den = profileInfo.attribute("frame_rate_den").toInt(); + m_profile.width = profileInfo.attribute("width").toInt(); + m_profile.height = profileInfo.attribute("height").toInt(); + m_profile.progressive = profileInfo.attribute("progressive").toInt(); + m_profile.sample_aspect_num = profileInfo.attribute("sample_aspect_num").toInt(); + m_profile.sample_aspect_den = profileInfo.attribute("sample_aspect_den").toInt(); + m_profile.display_aspect_num = profileInfo.attribute("display_aspect_num").toInt(); + m_profile.display_aspect_den = profileInfo.attribute("display_aspect_den").toInt(); + QString existing = ProfilesDialog::existingProfile(m_profile); + if (!existing.isEmpty()) { + m_profile = ProfilesDialog::getVideoProfile(existing); + KMessageBox::information(kapp->activeWindow(), i18n("Project profile not found, replacing with existing one: %1", m_profile.description), i18n("Missing Profile")); + } else { + QString newDesc = m_profile.description; + bool ok = true; + while (ok && (newDesc.isEmpty() || ProfilesDialog::existingProfileDescription(newDesc))) { + newDesc = QInputDialog::getText(kapp->activeWindow(), i18n("Existing Profile"), i18n("Your project uses an unknown profile.\nIt uses an existing profile name: %1.\nPlease choose a new name to save it", newDesc), QLineEdit::Normal, newDesc, &ok); + } + if (ok == false) { + // User canceled, use default profile + m_profile = ProfilesDialog::getVideoProfile(KdenliveSettings::default_profile()); + } else { + if (newDesc != m_profile.description) { + // Profile description existed, was replaced by new one + m_profile.description = newDesc; + } else { + KMessageBox::information(kapp->activeWindow(), i18n("Project profile was not found, it will be added to your system now."), i18n("Missing Profile")); + } + ProfilesDialog::saveProfile(m_profile); + } + } + setModified(true); + } + } + KdenliveSettings::setProject_display_ratio((double) m_profile.display_aspect_num / m_profile.display_aspect_den); m_fps = (double) m_profile.frame_rate_num / m_profile.frame_rate_den; KdenliveSettings::setProject_fps(m_fps); m_width = m_profile.width; m_height = m_profile.height; kDebug() << "Kdenlive document, init timecode from path: " << path << ", " << m_fps; - if (m_fps == 30000.0 / 1001.0) m_timecode.setFormat(30, true); - else m_timecode.setFormat((int) m_fps); + m_timecode.setFormat(m_fps); + return (current_fps != m_fps); } double KdenliveDoc::dar() @@ -578,12 +728,6 @@ void KdenliveDoc::setThumbsProgress(const QString &message, int progress) emit progressInfo(message, progress); } -void KdenliveDoc::loadingProgressed() -{ - m_documentLoadingProgress += m_documentLoadingStep; - emit progressInfo(QString(), (int) m_documentLoadingProgress); -} - QUndoStack *KdenliveDoc::commandStack() { return m_commandStack; @@ -605,56 +749,18 @@ void KdenliveDoc::setRenderer(Render *render) { void KdenliveDoc::checkProjectClips() { - kDebug() << "+++++++++++++ + + + + CHK PCLIPS"; if (m_render == NULL) return; m_clipManager->resetProducersList(m_render->producersList()); - return; - - // Useless now... - QList prods = m_render->producersList(); - QString id ; - QString prodId ; - QString prodTrack ; - for (int i = 0; i < prods.count(); i++) { - id = prods.at(i)->get("id"); - prodId = id.section('_', 0, 0); - prodTrack = id.section('_', 1, 1); - DocClipBase *clip = m_clipManager->getClipById(prodId); - if (clip) clip->setProducer(prods.at(i)); - if (clip && clip->clipType() == TEXT && !QFile::exists(clip->fileURL().path())) { - // regenerate text clip image if required - //kDebug() << "// TITLE: " << clip->getProperty("titlename") << " Preview file: " << clip->getProperty("resource") << " DOES NOT EXIST"; - QString titlename = clip->getProperty("name"); - QString titleresource; - if (titlename.isEmpty()) { - QStringList titleInfo = TitleWidget::getFreeTitleInfo(projectFolder()); - titlename = titleInfo.at(0); - titleresource = titleInfo.at(1); - clip->setProperty("name", titlename); - kDebug() << "// New title set to: " << titlename; - } else { - titleresource = TitleWidget::getFreeTitleInfo(projectFolder()).at(1); - //titleresource = TitleWidget::getTitleResourceFromName(projectFolder(), titlename); - } - TitleWidget *dia_ui = new TitleWidget(KUrl(), KUrl(titleresource).directory(), m_render, kapp->activeWindow()); - QDomDocument doc; - doc.setContent(clip->getProperty("xmldata")); - dia_ui->setXml(doc); - QImage pix = dia_ui->renderedPixmap(); - pix.save(titleresource); - clip->setProperty("resource", titleresource); - delete dia_ui; - clip->producer()->set("force_reload", 1); - } - } } void KdenliveDoc::updatePreviewSettings() { m_clipManager->updatePreviewSettings(); m_render->updatePreviewSettings(); + QList prods = m_render->producersList(); m_clipManager->resetProducersList(m_render->producersList()); - + qDeleteAll(prods); + prods.clear(); } Render *KdenliveDoc::renderer() @@ -662,14 +768,14 @@ Render *KdenliveDoc::renderer() return m_render; } -void KdenliveDoc::updateClip(const QString &id) +void KdenliveDoc::updateClip(const QString id) { emit updateClipDisplay(id); } int KdenliveDoc::getFramePos(QString duration) { - return m_timecode.getFrameCount(duration, m_fps); + return m_timecode.getFrameCount(duration); } QString KdenliveDoc::producerName(const QString &id) @@ -771,29 +877,6 @@ void KdenliveDoc::addClip(QDomElement elem, QString clipId, bool createClipItem) if (elem.attribute("type").toInt() == SLIDESHOW) { extension = KUrl(path).fileName(); path = KUrl(path).directory(); - } else if (elem.attribute("type").toInt() == TEXT && QFile::exists(path) == false) { - kDebug() << "// TITLE: " << elem.attribute("name") << " Preview file: " << elem.attribute("resource") << " DOES NOT EXIST"; - QString titlename = elem.attribute("name"); - QString titleresource; - if (titlename.isEmpty()) { - QStringList titleInfo = TitleWidget::getFreeTitleInfo(projectFolder()); - titlename = titleInfo.at(0); - titleresource = titleInfo.at(1); - elem.setAttribute("name", titlename); - kDebug() << "// New title set to: " << titlename; - } else { - titleresource = TitleWidget::getFreeTitleInfo(projectFolder()).at(1); - //titleresource = TitleWidget::getTitleResourceFromName(projectFolder(), titlename); - } - TitleWidget *dia_ui = new TitleWidget(KUrl(), KUrl(titleresource).directory(), m_render, kapp->activeWindow()); - QDomDocument doc; - doc.setContent(elem.attribute("xmldata")); - dia_ui->setXml(doc); - QImage pix = dia_ui->renderedPixmap(); - pix.save(titleresource); - elem.setAttribute("resource", titleresource); - setNewClipResource(clipId, titleresource); - delete dia_ui; } if (path.isEmpty() == false && QFile::exists(path) == false && elem.attribute("type").toInt() != TEXT && !elem.hasAttribute("placeholder")) { @@ -804,10 +887,10 @@ void KdenliveDoc::addClip(QDomElement elem, QString clipId, bool createClipItem) int action = KMessageBox::No; if (!size.isEmpty() && !hash.isEmpty()) { if (!m_searchFolder.isEmpty()) newpath = searchFileRecursively(m_searchFolder, size, hash); - else action = (KMessageBox::ButtonCode) KMessageBox::questionYesNoCancel(kapp->activeWindow(), i18n("Clip %1
is invalid, what do you want to do?", path), i18n("File not found"), KGuiItem(i18n("Search automatically")), KGuiItem(i18n("Keep as placeholder"))); + else action = (KMessageBox::ButtonCode) KMessageBox::questionYesNoCancel(kapp->activeWindow(), i18n("Clip %1
is invalid, what do you want to do?", path), i18n("File not found"), KGuiItem(i18n("Search automatically")), KGuiItem(i18n("Keep as placeholder"))); } else { if (elem.attribute("type").toInt() == SLIDESHOW) { - int res = KMessageBox::questionYesNoCancel(kapp->activeWindow(), i18n("Clip %1
is invalid or missing, what do you want to do?", path), i18n("File not found"), KGuiItem(i18n("Search manually")), KGuiItem(i18n("Keep as placeholder"))); + int res = KMessageBox::questionYesNoCancel(kapp->activeWindow(), i18n("Clip %1
is invalid or missing, what do you want to do?", path), i18n("File not found"), KGuiItem(i18n("Search manually")), KGuiItem(i18n("Keep as placeholder"))); if (res == KMessageBox::Yes) newpath = KFileDialog::getExistingDirectory(KUrl("kfiledialog:///clipfolder"), kapp->activeWindow(), i18n("Looking for %1", path)); else { @@ -815,7 +898,7 @@ void KdenliveDoc::addClip(QDomElement elem, QString clipId, bool createClipItem) action = res; } } else { - int res = KMessageBox::questionYesNoCancel(kapp->activeWindow(), i18n("Clip %1
is invalid or missing, what do you want to do?", path), i18n("File not found"), KGuiItem(i18n("Search manually")), KGuiItem(i18n("Keep as placeholder"))); + int res = KMessageBox::questionYesNoCancel(kapp->activeWindow(), i18n("Clip %1
is invalid or missing, what do you want to do?", path), i18n("File not found"), KGuiItem(i18n("Search manually")), KGuiItem(i18n("Keep as placeholder"))); if (res == KMessageBox::Yes) newpath = KFileDialog::getOpenFileName(KUrl("kfiledialog:///clipfolder"), QString(), kapp->activeWindow(), i18n("Looking for %1", path)); else { @@ -850,12 +933,10 @@ void KdenliveDoc::addClip(QDomElement elem, QString clipId, bool createClipItem) if (createClipItem) { emit addProjectClip(clip); - qApp->processEvents(); - m_render->getFileProperties(clip->toXML(), clip->getId()); + //qApp->processEvents(); } } - void KdenliveDoc::setNewClipResource(const QString &id, const QString &path) { QDomNodeList prods = m_document.elementsByTagName("producer"); @@ -890,7 +971,7 @@ QString KdenliveDoc::searchFileRecursively(const QDir &dir, const QString &match * 1 MB = 1 second per 450 files (or faster) * 10 MB = 9 seconds per 450 files (or faster) */ - if (file.size() > 1000000*2) { + if (file.size() > 1000000 * 2) { fileData = file.read(1000000); if (file.seek(file.size() - 1000000)) fileData.append(file.readAll()); @@ -948,32 +1029,22 @@ void KdenliveDoc::addClipInfo(QDomElement elem, QDomElement orig, QString clipId } } -void KdenliveDoc::deleteProjectClip(QList ids) -{ - for (int i = 0; i < ids.size(); ++i) { - emit deleteTimelineClip(ids.at(i)); - m_clipManager->slotDeleteClip(ids.at(i)); - } - setModified(true); -} void KdenliveDoc::deleteClip(const QString &clipId) { emit signalDeleteProjectClip(clipId); - m_clipManager->deleteClip(clipId); } void KdenliveDoc::slotAddClipList(const KUrl::List urls, const QString group, const QString &groupId) { m_clipManager->slotAddClipList(urls, group, groupId); - emit selectLastAddedClip(QString::number(m_clipManager->lastClipId())); + //emit selectLastAddedClip(QString::number(m_clipManager->lastClipId())); setModified(true); } void KdenliveDoc::slotAddClipFile(const KUrl url, const QString group, const QString &groupId) { - //kDebug() << "///////// DOCUM, ADD CLP: " << url; m_clipManager->slotAddClipFile(url, group, groupId); emit selectLastAddedClip(QString::number(m_clipManager->lastClipId())); setModified(true); @@ -989,22 +1060,61 @@ DocClipBase *KdenliveDoc::getBaseClip(const QString &clipId) return m_clipManager->getClipById(clipId); } -void KdenliveDoc::slotCreateTextClip(QString /*group*/, const QString &/*groupId*/) +void KdenliveDoc::slotCreateXmlClip(const QString &name, const QDomElement xml, QString group, const QString &groupId) { - QString titlesFolder = projectFolder().path() + "/titles/"; + m_clipManager->slotAddXmlClipFile(name, xml, group, groupId); + setModified(true); + emit selectLastAddedClip(QString::number(m_clipManager->lastClipId())); +} + +void KdenliveDoc::slotCreateColorClip(const QString &name, const QString &color, const QString &duration, QString group, const QString &groupId) +{ + m_clipManager->slotAddColorClipFile(name, color, duration, group, groupId); + setModified(true); + emit selectLastAddedClip(QString::number(m_clipManager->lastClipId())); +} + +void KdenliveDoc::slotCreateSlideshowClipFile(const QString name, const QString path, int count, const QString duration, + const bool loop, const bool crop, const bool fade, + const QString &luma_duration, const QString &luma_file, const int softness, + const QString &animation, QString group, const QString &groupId) +{ + m_clipManager->slotAddSlideshowClipFile(name, path, count, duration, loop, + crop, fade, luma_duration, + luma_file, softness, + animation, group, groupId); + setModified(true); + emit selectLastAddedClip(QString::number(m_clipManager->lastClipId())); +} + +void KdenliveDoc::slotCreateTextClip(QString group, const QString &groupId, const QString &templatePath) +{ + QString titlesFolder = projectFolder().path(KUrl::AddTrailingSlash) + "titles/"; KStandardDirs::makeDir(titlesFolder); - TitleWidget *dia_ui = new TitleWidget(KUrl(), titlesFolder, m_render, kapp->activeWindow()); + TitleWidget *dia_ui = new TitleWidget(templatePath, m_timecode, titlesFolder, m_render, kapp->activeWindow()); if (dia_ui->exec() == QDialog::Accepted) { - QStringList titleInfo = TitleWidget::getFreeTitleInfo(projectFolder()); - QImage pix = dia_ui->renderedPixmap(); - pix.save(titleInfo.at(1)); - //dia_ui->saveTitle(path + ".kdenlivetitle"); - m_clipManager->slotAddTextClipFile(titleInfo.at(0), titleInfo.at(1), dia_ui->xml().toString(), QString(), QString()); + m_clipManager->slotAddTextClipFile(i18n("Title clip"), dia_ui->outPoint(), dia_ui->xml().toString(), group, groupId); setModified(true); + emit selectLastAddedClip(QString::number(m_clipManager->lastClipId())); } delete dia_ui; } +void KdenliveDoc::slotCreateTextTemplateClip(QString group, const QString &groupId, KUrl path) +{ + QString titlesFolder = projectFolder().path(KUrl::AddTrailingSlash) + "titles/"; + if (path.isEmpty()) { + path = KFileDialog::getOpenUrl(KUrl(titlesFolder), "*.kdenlivetitle", kapp->activeWindow(), i18n("Enter Template Path")); + } + + if (path.isEmpty()) return; + + //TODO: rewrite with new title system (just set resource) + m_clipManager->slotAddTextTemplateClip(i18n("Template title clip"), path, group, groupId); + setModified(true); + emit selectLastAddedClip(QString::number(m_clipManager->lastClipId())); +} + int KdenliveDoc::tracksCount() const { return m_tracksList.count(); @@ -1012,26 +1122,46 @@ int KdenliveDoc::tracksCount() const TrackInfo KdenliveDoc::trackInfoAt(int ix) const { + if (ix < 0 || ix >= m_tracksList.count()) { + kWarning() << "Track INFO outisde of range"; + return TrackInfo(); + } return m_tracksList.at(ix); } void KdenliveDoc::switchTrackAudio(int ix, bool hide) { + if (ix < 0 || ix >= m_tracksList.count()) { + kWarning() << "SWITCH Track outisde of range"; + return; + } m_tracksList[ix].isMute = hide; // !m_tracksList.at(ix).isMute; } void KdenliveDoc::switchTrackLock(int ix, bool lock) { + if (ix < 0 || ix >= m_tracksList.count()) { + kWarning() << "Track Lock outisde of range"; + return; + } m_tracksList[ix].isLocked = lock; } bool KdenliveDoc::isTrackLocked(int ix) const { + if (ix < 0 || ix >= m_tracksList.count()) { + kWarning() << "Track Lock outisde of range"; + return true; + } return m_tracksList.at(ix).isLocked; } void KdenliveDoc::switchTrackVideo(int ix, bool hide) { + if (ix < 0 || ix >= m_tracksList.count()) { + kWarning() << "SWITCH Track outisde of range"; + return; + } m_tracksList[ix].isBlind = hide; // !m_tracksList.at(ix).isBlind; } @@ -1043,15 +1173,24 @@ void KdenliveDoc::insertTrack(int ix, TrackInfo type) void KdenliveDoc::deleteTrack(int ix) { + if (ix < 0 || ix >= m_tracksList.count()) { + kWarning() << "Delete Track outisde of range"; + return; + } m_tracksList.removeAt(ix); } void KdenliveDoc::setTrackType(int ix, TrackInfo type) { + if (ix < 0 || ix >= m_tracksList.count()) { + kWarning() << "SET Track Type outisde of range"; + return; + } m_tracksList[ix].type = type.type; m_tracksList[ix].isMute = type.isMute; m_tracksList[ix].isBlind = type.isBlind; m_tracksList[ix].isLocked = type.isLocked; + m_tracksList[ix].trackName = type.trackName; } const QList KdenliveDoc::tracksList() const @@ -1063,7 +1202,7 @@ QPoint KdenliveDoc::getTracksCount() const { int audio = 0; int video = 0; - foreach(const TrackInfo &info, m_tracksList) { + foreach(const TrackInfo & info, m_tracksList) { if (info.type == VIDEOTRACK) video++; else audio++; } @@ -1072,44 +1211,166 @@ QPoint KdenliveDoc::getTracksCount() const void KdenliveDoc::cachePixmap(const QString &fileId, const QPixmap &pix) const { - pix.save(m_projectFolder.path() + "/thumbs/" + fileId + ".png"); + pix.save(m_projectFolder.path(KUrl::AddTrailingSlash) + "thumbs/" + fileId + ".png"); } QString KdenliveDoc::getLadspaFile() const { int ct = 0; QString counter = QString::number(ct).rightJustified(5, '0', false); - while (QFile::exists(m_projectFolder.path() + "/ladspa/" + counter + ".ladspa")) { + while (QFile::exists(m_projectFolder.path(KUrl::AddTrailingSlash) + "ladspa/" + counter + ".ladspa")) { ct++; counter = QString::number(ct).rightJustified(5, '0', false); } - return m_projectFolder.path() + "/ladspa/" + counter + ".ladspa"; + return m_projectFolder.path(KUrl::AddTrailingSlash) + "ladspa/" + counter + ".ladspa"; } bool KdenliveDoc::checkDocumentClips(QDomNodeList infoproducers) { - int clipType; - QDomElement e; - QString id; - QString resource; - QList missingClips; - for (int i = 0; i < infoproducers.count(); i++) { - e = infoproducers.item(i).toElement(); - clipType = e.attribute("type").toInt(); - if (clipType == TEXT || clipType == COLOR) continue; - id = e.attribute("id"); - resource = e.attribute("resource"); - if (clipType == SLIDESHOW) resource = KUrl(resource).directory(); - if (!KIO::NetAccess::exists(KUrl(resource), KIO::NetAccess::SourceSide, 0)) { - // Missing clip found - missingClips.append(e); + DocumentChecker d(infoproducers, m_document); + return (d.hasMissingClips() == false); + + /* int clipType; + QDomElement e; + QString id; + QString resource; + QList missingClips; + for (int i = 0; i < infoproducers.count(); i++) { + e = infoproducers.item(i).toElement(); + clipType = e.attribute("type").toInt(); + if (clipType == COLOR) continue; + if (clipType == TEXT) { + //TODO: Check is clip template is missing (xmltemplate) or hash changed + continue; + } + id = e.attribute("id"); + resource = e.attribute("resource"); + if (clipType == SLIDESHOW) resource = KUrl(resource).directory(); + if (!KIO::NetAccess::exists(KUrl(resource), KIO::NetAccess::SourceSide, 0)) { + // Missing clip found + missingClips.append(e); + } else { + // Check if the clip has changed + if (clipType != SLIDESHOW && e.hasAttribute("file_hash")) { + if (e.attribute("file_hash") != DocClipBase::getHash(e.attribute("resource"))) + e.removeAttribute("file_hash"); + } + } + } + if (missingClips.isEmpty()) return true; + DocumentChecker d(missingClips, m_document); + return (d.exec() == QDialog::Accepted);*/ +} + +void KdenliveDoc::setDocumentProperty(const QString &name, const QString &value) +{ + m_documentProperties[name] = value; +} + +const QString KdenliveDoc::getDocumentProperty(const QString &name) const +{ + return m_documentProperties.value(name); +} + +QMap KdenliveDoc::getRenderProperties() const +{ + QMap renderProperties; + QMapIterator i(m_documentProperties); + while (i.hasNext()) { + i.next(); + if (i.key().startsWith("render")) renderProperties.insert(i.key(), i.value()); + } + return renderProperties; +} + +void KdenliveDoc::addTrackEffect(int ix, QDomElement effect) +{ + if (ix < 0 || ix >= m_tracksList.count()) { + kWarning() << "Add Track effect outisde of range"; + return; + } + effect.setAttribute("kdenlive_ix", m_tracksList.at(ix).effectsList.count() + 1); + + // Init parameter value & keyframes if required + QDomNodeList params = effect.elementsByTagName("parameter"); + for (int i = 0; i < params.count(); i++) { + QDomElement e = params.item(i).toElement(); + + // Check if this effect has a variable parameter + if (e.attribute("default").startsWith('%')) { + double evaluatedValue = ProfilesDialog::getStringEval(m_profile, e.attribute("default")); + e.setAttribute("default", evaluatedValue); + if (e.hasAttribute("value") && e.attribute("value").startsWith('%')) { + e.setAttribute("value", evaluatedValue); + } + } + + if (!e.isNull() && (e.attribute("type") == "keyframe" || e.attribute("type") == "simplekeyframe")) { + QString def = e.attribute("default"); + // Effect has a keyframe type parameter, we need to set the values + if (e.attribute("keyframes").isEmpty()) { + e.setAttribute("keyframes", "0:" + def + ';'); + kDebug() << "///// EFFECT KEYFRAMES INITED: " << e.attribute("keyframes"); + //break; + } + } + } + + m_tracksList[ix].effectsList.append(effect); +} + +void KdenliveDoc::removeTrackEffect(int ix, QDomElement effect) +{ + if (ix < 0 || ix >= m_tracksList.count()) { + kWarning() << "Remove Track effect outisde of range"; + return; + } + QString index; + QString toRemove = effect.attribute("kdenlive_ix"); + for (int i = 0; i < m_tracksList.at(ix).effectsList.count(); ++i) { + index = m_tracksList.at(ix).effectsList.at(i).attribute("kdenlive_ix"); + if (toRemove == index) { + m_tracksList[ix].effectsList.removeAt(i); + i--; + } else if (index.toInt() > toRemove.toInt()) { + m_tracksList[ix].effectsList.item(i).setAttribute("kdenlive_ix", index.toInt() - 1); } } - if (missingClips.isEmpty()) return true; - DocumentChecker d(missingClips, m_document); - return (d.exec() == QDialog::Accepted); } +void KdenliveDoc::setTrackEffect(int trackIndex, int effectIndex, QDomElement effect) +{ + if (trackIndex < 0 || trackIndex >= m_tracksList.count()) { + kWarning() << "Set Track effect outisde of range"; + return; + } + if (effectIndex < 0 || effectIndex > (m_tracksList.at(trackIndex).effectsList.count() - 1) || effect.isNull()) { + kDebug() << "Invalid effect index: " << effectIndex; + return; + } + effect.setAttribute("kdenlive_ix", effectIndex + 1); + m_tracksList[trackIndex].effectsList.replace(effectIndex, effect); +} + +const EffectsList KdenliveDoc::getTrackEffects(int ix) +{ + if (ix < 0 || ix >= m_tracksList.count()) { + kWarning() << "Get Track effects outisde of range"; + return EffectsList(); + } + return m_tracksList.at(ix).effectsList; +} + +QDomElement KdenliveDoc::getTrackEffect(int trackIndex, int effectIndex) const +{ + if (trackIndex < 0 || trackIndex >= m_tracksList.count()) { + kWarning() << "Get Track effect outisde of range"; + return QDomElement(); + } + EffectsList list = m_tracksList.at(trackIndex).effectsList; + if (effectIndex > list.count() - 1 || effectIndex < 0 || list.at(effectIndex).isNull()) return QDomElement(); + return list.at(effectIndex).cloneNode().toElement(); +} #include "kdenlivedoc.moc"