X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fkdenlivedoc.cpp;h=611d5a36346197744c10581d7d37942839de8534;hb=bddf7dbd05d7a0c980934baa9caa71b991b92b2a;hp=025d028a7e557098f359278ee9c942078d60300b;hpb=92e7620dd463e4188da378cee497d2135987f242;p=kdenlive diff --git a/src/kdenlivedoc.cpp b/src/kdenlivedoc.cpp index 025d028a..611d5a36 100644 --- a/src/kdenlivedoc.cpp +++ b/src/kdenlivedoc.cpp @@ -28,17 +28,23 @@ #include "mainwindow.h" #include "documentchecker.h" #include "documentvalidator.h" -#include "kdenlive-config.h" +#include "config-kdenlive.h" #include "initeffects.h" #include #include #include +#include #include #include #include #include +#include #include +#include +#include +#include +#include #include #include @@ -47,9 +53,12 @@ #include -const double DOCUMENTVERSION = 0.85; +#include "locale.h" + + +const double DOCUMENTVERSION = 0.88; -KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup *undoGroup, QString profileName, const QPoint tracks, Render *render, KTextEdit *notes, MainWindow *parent) : +KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup *undoGroup, QString profileName, QMap properties, QMap metadata, const QPoint &tracks, Render *render, KTextEdit *notes, bool *openBackup, MainWindow *parent, KProgressDialog *progressDialog) : QObject(parent), m_autosave(NULL), m_url(url), @@ -59,40 +68,104 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup m_modified(false), m_projectFolder(projectFolder) { + // init m_profile struct + m_profile.frame_rate_num = 0; + m_profile.frame_rate_den = 0; + m_profile.width = 0; + m_profile.height = 0; + m_profile.progressive = 0; + m_profile.sample_aspect_num = 0; + m_profile.sample_aspect_den = 0; + m_profile.display_aspect_num = 0; + m_profile.display_aspect_den = 0; + m_profile.colorspace = 0; + m_clipManager = new ClipManager(this); m_autoSaveTimer = new QTimer(this); m_autoSaveTimer->setSingleShot(true); + connect(m_clipManager, SIGNAL(displayMessage(QString, int)), parent, SLOT(slotGotProgressInfo(QString,int))); bool success = false; // init default document properties - m_documentProperties["zoom"] = "7"; - m_documentProperties["verticalzoom"] = "1"; - m_documentProperties["zonein"] = "0"; + m_documentProperties["zoom"] = '7'; + m_documentProperties["verticalzoom"] = '1'; + m_documentProperties["zonein"] = '0'; m_documentProperties["zoneout"] = "100"; + m_documentProperties["enableproxy"] = QString::number((int) KdenliveSettings::enableproxy()); + m_documentProperties["proxyparams"] = KdenliveSettings::proxyparams(); + m_documentProperties["proxyextension"] = KdenliveSettings::proxyextension(); + m_documentProperties["generateproxy"] = QString::number((int) KdenliveSettings::generateproxy()); + m_documentProperties["proxyminsize"] = QString::number(KdenliveSettings::proxyminsize()); + m_documentProperties["generateimageproxy"] = QString::number((int) KdenliveSettings::generateimageproxy()); + m_documentProperties["proxyimageminsize"] = QString::number(KdenliveSettings::proxyimageminsize()); +#if QT_VERSION >= 0x040700 + m_documentProperties["documentid"] = QString::number(QDateTime::currentMSecsSinceEpoch()); +#else + QDateTime date = QDateTime::currentDateTime(); + m_documentProperties["documentid"] = QString::number(date.toTime_t()); +#endif + + // Load properties + QMapIterator i(properties); + while (i.hasNext()) { + i.next(); + m_documentProperties[i.key()] = i.value(); + } + + // Load metadata + QMapIterator j(metadata); + while (j.hasNext()) { + j.next(); + m_documentMetadata[j.key()] = j.value(); + } + + if (QLocale().decimalPoint() != QLocale::system().decimalPoint()) { + setlocale(LC_NUMERIC, ""); + QLocale systemLocale = QLocale::system(); + systemLocale.setNumberOptions(QLocale::OmitGroupSeparator); + QLocale::setDefault(systemLocale); + // locale conversion might need to be redone + initEffects::parseEffectFiles(setlocale(LC_NUMERIC, NULL)); + } + *openBackup = false; + if (!url.isEmpty()) { QString tmpFile; success = KIO::NetAccess::download(url.path(), tmpFile, parent); - if (!success) // The file cannot be opened - KMessageBox::error(parent, KIO::NetAccess::lastErrorString()); + if (!success) { + // The file cannot be opened + if (KMessageBox::warningContinueCancel(parent, i18n("Cannot open the project file, error is:\n%1\nDo you want to open a backup file?", KIO::NetAccess::lastErrorString()), i18n("Error opening file"), KGuiItem(i18n("Open Backup"))) == KMessageBox::Continue) { + *openBackup = true; + } + //KMessageBox::error(parent, KIO::NetAccess::lastErrorString()); + } else { QFile file(tmpFile); QString errorMsg; - QDomImplementation impl; - impl.setInvalidDataPolicy(QDomImplementation::DropInvalidChars); + QDomImplementation::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); + if (!success) { + // It is corrupted + if (KMessageBox::warningContinueCancel(parent, i18n("Cannot open the project file, error is:\n%1\nDo you want to open a backup file?", errorMsg), i18n("Error opening file"), KGuiItem(i18n("Open Backup"))) == KMessageBox::Continue) { + *openBackup = true; + } + //KMessageBox::error(parent, errorMsg); + } else { parent->slotGotProgressInfo(i18n("Validating"), 0); - DocumentValidator validator(m_document); + qApp->processEvents(); + DocumentValidator validator(m_document, url); 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); + if (KMessageBox::warningContinueCancel(parent, i18n("File %1 is not a valid project file.\nDo you want to open a backup file?", m_url.path()), i18n("Error opening file"), KGuiItem(i18n("Open Backup"))) == KMessageBox::Continue) { + *openBackup = true; + } } else { /* * Validate the file against the current version (upgrade @@ -102,17 +175,23 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup success = validator.validate(DOCUMENTVERSION); if (success) { // Let the validator handle error messages parent->slotGotProgressInfo(i18n("Check missing clips"), 0); + qApp->processEvents(); QDomNodeList infoproducers = m_document.elementsByTagName("kdenlive_producer"); success = checkDocumentClips(infoproducers); if (success) { + if (m_document.documentElement().attribute("modified") == "1") setModified(true); parent->slotGotProgressInfo(i18n("Loading"), 0); QDomElement mlt = m_document.firstChildElement("mlt"); QDomElement infoXml = mlt.firstChildElement("kdenlivedoc"); + // Set profile, fps, etc for the document + setProfilePath(infoXml.attribute("profile")); + // Check embedded effects QDomElement customeffects = infoXml.firstChildElement("customeffects"); if (!customeffects.isNull() && customeffects.hasChildNodes()) { parent->slotGotProgressInfo(i18n("Importing project effects"), 0); + qApp->processEvents(); if (saveCustomEffects(customeffects.childNodes())) parent->slotReloadEffects(); } @@ -138,22 +217,35 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup projectTrack.isBlind = e.attribute("blind").toInt(); projectTrack.isLocked = e.attribute("locked").toInt(); projectTrack.trackName = e.attribute("trackname"); + projectTrack.effectsList = EffectsList(true); m_tracksList.append(projectTrack); } } mlt.removeChild(tracksinfo); } - - QDomNodeList folders = m_document.elementsByTagName("folder"); + QStringList expandedFolders; + QDomNodeList folders = m_document.elementsByTagName("folder"); for (int i = 0; i < folders.count(); i++) { e = folders.item(i).cloneNode().toElement(); + if (e.hasAttribute("opened")) expandedFolders.append(e.attribute("id")); m_clipManager->addFolder(e.attribute("id"), e.attribute("name")); } + m_documentProperties["expandedfolders"] = expandedFolders.join(";"); const int infomax = infoproducers.count(); QDomNodeList producers = m_document.elementsByTagName("producer"); const int max = producers.count(); + if (!progressDialog) { + progressDialog = new KProgressDialog(parent, i18n("Loading project"), i18n("Adding clips")); + progressDialog->setAllowCancel(false); + } else { + progressDialog->setLabelText(i18n("Adding clips")); + } + progressDialog->progressBar()->setMaximum(infomax); + progressDialog->show(); + qApp->processEvents(); + for (int i = 0; i < infomax; i++) { e = infoproducers.item(i).cloneNode().toElement(); QString prodId = e.attribute("id"); @@ -162,13 +254,14 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup // Get MLT's original producer properties QDomElement orig; for (int j = 0; j < max; j++) { - QDomElement o = producers.item(j).cloneNode().toElement(); - QString origId = o.attribute("id").section('_', 0, 0); + QDomNode o = producers.item(j); + QString origId = o.attributes().namedItem("id").nodeValue().section('_', 0, 0); if (origId == prodId) { - orig = o; + orig = o.cloneNode().toElement(); break; } } + if (!addClipInfo(e, orig, prodId)) { // The user manually aborted the loading. success = false; @@ -178,6 +271,8 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup break; } } + if (i % 10 == 0) + progressDialog->progressBar()->setValue(i); } if (success) { @@ -187,8 +282,12 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup int maxchild = markerslist.count(); for (int k = 0; k < maxchild; k++) { e = markerslist.at(k).toElement(); - if (e.tagName() == "marker") - m_clipManager->getClipById(e.attribute("id"))->addSnapMarker(GenTime(e.attribute("time").toDouble()), e.attribute("comment")); + if (e.tagName() == "marker") { + CommentedTime marker(GenTime(e.attribute("time").toDouble()), e.attribute("comment"), e.attribute("type").toInt()); + DocClipBase *baseClip = m_clipManager->getClipById(e.attribute("id")); + if (baseClip) baseClip->addSnapMarker(marker); + else kDebug()<< " / / Warning, missing clip: "<< e.attribute("id"); + } } infoXml.removeChild(markers); } @@ -198,8 +297,12 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup QDomNamedNodeMap props = docproperties.attributes(); for (int i = 0; i < props.count(); i++) m_documentProperties.insert(props.item(i).nodeName(), props.item(i).nodeValue()); - setProfilePath(infoXml.attribute("profile")); - setModified(validator.isModified()); + docproperties = infoXml.firstChildElement("documentmetadata"); + props = docproperties.attributes(); + for (int i = 0; i < props.count(); i++) + m_documentMetadata.insert(props.item(i).nodeName(), props.item(i).nodeValue()); + + if (validator.isModified()) setModified(true); kDebug() << "Reading file: " << url.path() << ", found clips: " << producers.count(); } } @@ -208,17 +311,14 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup } } } - + // Something went wrong, or a new file was requested: create a new project if (!success) { - m_url = KUrl(); + m_url.clear(); setProfilePath(profileName); m_document = createEmptyDocument(tracks.x(), tracks.y()); } - // Set the video profile (empty == default) - 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())); @@ -240,10 +340,13 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup // Make sure that the necessary folders exist KStandardDirs::makeDir(m_projectFolder.path(KUrl::AddTrailingSlash) + "titles/"); KStandardDirs::makeDir(m_projectFolder.path(KUrl::AddTrailingSlash) + "thumbs/"); - KStandardDirs::makeDir(m_projectFolder.path(KUrl::AddTrailingSlash) + "ladspa/"); + KStandardDirs::makeDir(m_projectFolder.path(KUrl::AddTrailingSlash) + "proxy/"); + + updateProjectFolderPlacesEntry(); //kDebug() << "// SETTING SCENE LIST:\n\n" << m_document.toString(); connect(m_autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave())); + connect(m_render, SIGNAL(addClip(const KUrl &, stringMap)), this, SLOT(slotAddClipFile(const KUrl &, stringMap))); } KdenliveDoc::~KdenliveDoc() @@ -262,7 +365,7 @@ KdenliveDoc::~KdenliveDoc() int KdenliveDoc::setSceneList() { - m_render->resetProfile(KdenliveSettings::current_profile()); + m_render->resetProfile(KdenliveSettings::current_profile(), true); if (m_render->setSceneList(m_document.toString(), m_documentProperties.value("position").toInt()) == -1) { // INVALID MLT Consumer, something is wrong return -1; @@ -287,6 +390,8 @@ QDomDocument KdenliveDoc::createEmptyDocument(int videotracks, int audiotracks) audioTrack.isBlind = true; audioTrack.isLocked = false; audioTrack.trackName = QString("Audio ") + QString::number(audiotracks - i); + audioTrack.duration = 0; + audioTrack.effectsList = EffectsList(true); m_tracksList.append(audioTrack); } @@ -297,6 +402,8 @@ QDomDocument KdenliveDoc::createEmptyDocument(int videotracks, int audiotracks) videoTrack.isBlind = false; videoTrack.isLocked = false; videoTrack.trackName = QString("Video ") + QString::number(videotracks - i); + videoTrack.duration = 0; + videoTrack.effectsList = EffectsList(true); m_tracksList.append(videoTrack); } return createEmptyDocument(m_tracksList); @@ -307,9 +414,9 @@ QDomDocument KdenliveDoc::createEmptyDocument(QList tracks) // Creating new document QDomDocument doc; QDomElement mlt = doc.createElement("mlt"); + mlt.setAttribute("LC_NUMERIC", ""); doc.appendChild(mlt); - - + // 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. @@ -326,7 +433,7 @@ QDomDocument KdenliveDoc::createEmptyDocument(QList tracks) property = doc.createElement("property"); property.setAttribute("name", "aspect_ratio"); - value = doc.createTextNode(QString::number(0.0)); + value = doc.createTextNode(QString::number(0)); property.appendChild(value); blk.appendChild(property); @@ -466,15 +573,7 @@ void KdenliveDoc::slotAutoSave() kDebug() << "ERROR; CANNOT CREATE AUTOSAVE FILE"; } kDebug() << "// AUTOSAVE FILE: " << m_autosave->fileName(); - QString doc; - if (KdenliveSettings::dropbframes()) { - KdenliveSettings::setDropbframes(false); - m_clipManager->updatePreviewSettings(); - doc = m_render->sceneList(); - KdenliveSettings::setDropbframes(true); - m_clipManager->updatePreviewSettings(); - } else doc = m_render->sceneList(); - saveSceneList(m_autosave->fileName(), doc); + saveSceneList(m_autosave->fileName(), m_render->sceneList(), QStringList(), true); } } @@ -500,15 +599,26 @@ QPoint KdenliveDoc::zone() const return QPoint(m_documentProperties.value("zonein").toInt(), m_documentProperties.value("zoneout").toInt()); } -bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene) +QDomDocument KdenliveDoc::xmlSceneList(const QString &scene, const QStringList &expandedFolders) { QDomDocument sceneList; sceneList.setContent(scene, true); 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; + //scenelist is corrupted + return sceneList; + } + + // Set playlist audio volume to 100% + QDomElement tractor = mlt.firstChildElement("tractor"); + if (!tractor.isNull()) { + QDomNodeList props = tractor.elementsByTagName("property"); + for (int i = 0; i < props.count(); i++) { + if (props.at(i).toElement().attribute("name") == "meta.volume") { + props.at(i).firstChild().setNodeValue("1"); + break; + } + } } QDomElement addedXml = sceneList.createElement("kdenlivedoc"); @@ -552,9 +662,17 @@ bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene) } docproperties.setAttribute("position", m_render->seekPosition().frames(m_fps)); addedXml.appendChild(docproperties); + + QDomElement docmetadata = sceneList.createElement("documentmetadata"); + QMapIterator j(m_documentMetadata); + while (j.hasNext()) { + j.next(); + docmetadata.setAttribute(j.key(), j.value()); + } + addedXml.appendChild(docmetadata); QDomElement docnotes = sceneList.createElement("documentnotes"); - QDomText value = sceneList.createTextNode(m_notesWidget->toPlainText()); + QDomText value = sceneList.createTextNode(m_notesWidget->toHtml()); docnotes.appendChild(value); addedXml.appendChild(docnotes); @@ -594,6 +712,7 @@ bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene) QDomElement folder = sceneList.createElement("folder"); folder.setAttribute("id", f.key()); folder.setAttribute("name", f.value()); + if (expandedFolders.contains(f.key())) folder.setAttribute("opened", "1"); addedXml.appendChild(folder); } @@ -601,7 +720,7 @@ bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene) QDomElement e; QList list = m_clipManager->documentClipList(); for (int i = 0; i < list.count(); i++) { - e = list.at(i)->toXML(); + e = list.at(i)->toXML(true); e.setTagName("kdenlive_producer"); addedXml.appendChild(sceneList.importNode(e, true)); QList < CommentedTime > marks = list.at(i)->commentedSnapMarkers(); @@ -610,6 +729,7 @@ bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene) marker.setAttribute("time", marks.at(j).time().ms() / 1000); marker.setAttribute("comment", marks.at(j).comment()); marker.setAttribute("id", e.attribute("id")); + marker.setAttribute("type", marks.at(j).markerType()); markers.appendChild(marker); } } @@ -622,8 +742,22 @@ bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene) addedXml.appendChild(sceneList.importNode(m_clipManager->groupsXml(), true)); //wes.appendChild(doc.importNode(kdenliveData, true)); + return sceneList; +} +bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene, const QStringList &expandedFolders, bool autosave) +{ + QDomDocument sceneList = xmlSceneList(scene, expandedFolders); + if (sceneList.isNull()) { + //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; + } + + // Backup current version + if (!autosave) backupLastSavedVersion(path); QFile file(path); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { kWarning() << "////// ERROR writing to file: " << path; KMessageBox::error(kapp->activeWindow(), i18n("Cannot write to file %1", path)); @@ -637,6 +771,18 @@ bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene) return false; } file.close(); + if (!autosave) { + cleanupBackupFiles(); + QFileInfo info(file); + QString fileName = KUrl(path).fileName().section('.', 0, -2); + fileName.append('-' + m_documentProperties.value("documentid")); + fileName.append(info.lastModified().toString("-yyyy-MM-dd-hh-mm")); + fileName.append(".kdenlive.png"); + KUrl backupFile = m_projectFolder; + backupFile.addPath(".backup/"); + backupFile.addPath(fileName); + emit saveTimelinePreview(backupFile.path()); + } return true; } @@ -658,14 +804,16 @@ void KdenliveDoc::setProjectFolder(KUrl url) KStandardDirs::makeDir(url.path()); 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); + 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?", m_projectFolder.path(), url.path())) == KMessageBox::Yes) moveProjectData(url); m_projectFolder = url; + + updateProjectFolderPlacesEntry(); } void KdenliveDoc::moveProjectData(KUrl url) { QList list = m_clipManager->documentClipList(); - //TODO: Also move ladspa effects files + KUrl::List cacheUrls; for (int i = 0; i < list.count(); i++) { DocClipBase *clip = list.at(i); if (clip->clipType() == TEXT) { @@ -679,16 +827,17 @@ void KdenliveDoc::moveProjectData(KUrl url) 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(KUrl::AddTrailingSlash) + "thumbs/" + hash + ".png"); - KIO::Job *job = KIO::copy(oldVideoThumbUrl, newUrl); - KIO::NetAccess::synchronousRun(job, 0); + cacheUrls << oldVideoThumbUrl; } if (KIO::NetAccess::exists(oldAudioThumbUrl, KIO::NetAccess::SourceSide, 0)) { - 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(); + cacheUrls << oldAudioThumbUrl; } } + if (!cacheUrls.isEmpty()) { + KIO::Job *job = KIO::copy(cacheUrls, KUrl(url.path(KUrl::AddTrailingSlash) + "thumbs/")); + job->ui()->setWindow(kapp->activeWindow()); + KIO::NetAccess::synchronousRun(job, 0); + } } const QString &KdenliveDoc::profilePath() const @@ -706,7 +855,7 @@ 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; + double current_fps = m_fps; if (m_profile.path.isEmpty()) { // Profile not found, use embedded profile QDomElement profileInfo = m_document.elementsByTagName("profileinfo").at(0).toElement(); @@ -758,6 +907,7 @@ bool KdenliveDoc::setProfilePath(QString path) m_height = m_profile.height; kDebug() << "Kdenlive document, init timecode from path: " << path << ", " << m_fps; m_timecode.setFormat(m_fps); + KdenliveSettings::setCurrent_profile(m_profile.path); return (current_fps != m_fps); } @@ -790,20 +940,10 @@ void KdenliveDoc::setRenderer(Render *render) { emit progressInfo(QString(), -1); }*/ -void KdenliveDoc::checkProjectClips() +void KdenliveDoc::checkProjectClips(bool displayRatioChanged, bool fpsChanged) { if (m_render == NULL) return; - m_clipManager->resetProducersList(m_render->producersList()); -} - -void KdenliveDoc::updatePreviewSettings() -{ - m_clipManager->updatePreviewSettings(); - m_render->updatePreviewSettings(); - QList prods = m_render->producersList(); - m_clipManager->resetProducersList(m_render->producersList()); - qDeleteAll(prods); - prods.clear(); + m_clipManager->resetProducersList(m_render->producersList(), displayRatioChanged, fpsChanged); } Render *KdenliveDoc::renderer() @@ -811,7 +951,7 @@ Render *KdenliveDoc::renderer() return m_render; } -void KdenliveDoc::updateClip(const QString id) +void KdenliveDoc::updateClip(const QString &id) { emit updateClipDisplay(id); } @@ -921,8 +1061,10 @@ bool KdenliveDoc::addClip(QDomElement elem, QString clipId, bool createClipItem) extension = KUrl(path).fileName(); path = KUrl(path).directory(); } - - if (path.isEmpty() == false && QFile::exists(path) == false && elem.attribute("type").toInt() != TEXT && !elem.hasAttribute("placeholder")) { + if (elem.hasAttribute("_missingsource")) { + // Clip has proxy but missing original source + } + else if (path.isEmpty() == false && QFile::exists(path) == false && elem.attribute("type").toInt() != TEXT && !elem.hasAttribute("placeholder")) { kDebug() << "// FOUND MISSING CLIP: " << path << ", TYPE: " << elem.attribute("type").toInt(); const QString size = elem.attribute("file_size"); const QString hash = elem.attribute("file_hash"); @@ -977,7 +1119,6 @@ bool KdenliveDoc::addClip(QDomElement elem, QString clipId, bool createClipItem) if (createClipItem) { emit addProjectClip(clip); - //qApp->processEvents(); } return true; @@ -1081,17 +1222,17 @@ void KdenliveDoc::deleteClip(const QString &clipId) emit signalDeleteProjectClip(clipId); } -void KdenliveDoc::slotAddClipList(const KUrl::List urls, const QString group, const QString &groupId) +void KdenliveDoc::slotAddClipList(const KUrl::List urls, stringMap data) { - m_clipManager->slotAddClipList(urls, group, groupId); + m_clipManager->slotAddClipList(urls, data); //emit selectLastAddedClip(QString::number(m_clipManager->lastClipId())); setModified(true); } -void KdenliveDoc::slotAddClipFile(const KUrl url, const QString group, const QString &groupId) +void KdenliveDoc::slotAddClipFile(const KUrl &url, stringMap data) { - m_clipManager->slotAddClipFile(url, group, groupId); + m_clipManager->slotAddClipFile(url, data); emit selectLastAddedClip(QString::number(m_clipManager->lastClipId())); setModified(true); } @@ -1120,15 +1261,9 @@ void KdenliveDoc::slotCreateColorClip(const QString &name, const QString &color, 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) +void KdenliveDoc::slotCreateSlideshowClipFile(QMap properties, QString group, const QString &groupId) { - m_clipManager->slotAddSlideshowClipFile(name, path, count, duration, loop, - crop, fade, luma_duration, - luma_file, softness, - animation, group, groupId); + m_clipManager->slotAddSlideshowClipFile(properties, group, groupId); setModified(true); emit selectLastAddedClip(QString::number(m_clipManager->lastClipId())); } @@ -1137,7 +1272,7 @@ void KdenliveDoc::slotCreateTextClip(QString group, const QString &groupId, cons { QString titlesFolder = projectFolder().path(KUrl::AddTrailingSlash) + "titles/"; KStandardDirs::makeDir(titlesFolder); - TitleWidget *dia_ui = new TitleWidget(templatePath, m_timecode, titlesFolder, m_render, kapp->activeWindow()); + QPointer dia_ui = new TitleWidget(templatePath, m_timecode, titlesFolder, m_render, kapp->activeWindow()); if (dia_ui->exec() == QDialog::Accepted) { m_clipManager->slotAddTextClipFile(i18n("Title clip"), dia_ui->outPoint(), dia_ui->xml().toString(), group, groupId); setModified(true); @@ -1150,7 +1285,7 @@ void KdenliveDoc::slotCreateTextTemplateClip(QString group, const QString &group { QString titlesFolder = projectFolder().path(KUrl::AddTrailingSlash) + "titles/"; if (path.isEmpty()) { - path = KFileDialog::getOpenUrl(KUrl(titlesFolder), "*.kdenlivetitle", kapp->activeWindow(), i18n("Enter Template Path")); + path = KFileDialog::getOpenUrl(KUrl(titlesFolder), "application/x-kdenlivetitle", kapp->activeWindow(), i18n("Enter Template Path")); } if (path.isEmpty()) return; @@ -1211,6 +1346,16 @@ void KdenliveDoc::switchTrackVideo(int ix, bool hide) m_tracksList[ix].isBlind = hide; // !m_tracksList.at(ix).isBlind; } +int KdenliveDoc::trackDuration(int ix) +{ + return m_tracksList.at(ix).duration; +} + +void KdenliveDoc::setTrackDuration(int ix, int duration) +{ + m_tracksList[ix].duration = duration; +} + void KdenliveDoc::insertTrack(int ix, TrackInfo type) { if (ix == -1) m_tracksList << type; @@ -1255,26 +1400,15 @@ QPoint KdenliveDoc::getTracksCount() const return QPoint(video, audio); } -void KdenliveDoc::cachePixmap(const QString &fileId, const QPixmap &pix) const +void KdenliveDoc::cacheImage(const QString &fileId, const QImage &img) const { - 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(KUrl::AddTrailingSlash) + "ladspa/" + counter + ".ladspa")) { - ct++; - counter = QString::number(ct).rightJustified(5, '0', false); - } - return m_projectFolder.path(KUrl::AddTrailingSlash) + "ladspa/" + counter + ".ladspa"; + img.save(m_projectFolder.path(KUrl::AddTrailingSlash) + "thumbs/" + fileId + ".png"); } bool KdenliveDoc::checkDocumentClips(QDomNodeList infoproducers) { DocumentChecker d(infoproducers, m_document); - return (d.hasMissingClips() == false); + return (d.hasErrorInClips() == false); /* int clipType; QDomElement e; @@ -1343,7 +1477,7 @@ void KdenliveDoc::addTrackEffect(int ix, QDomElement effect) QDomElement e = params.item(i).toElement(); // Check if this effect has a variable parameter - if (e.attribute("default").startsWith('%')) { + if (e.attribute("default").contains('%')) { double evaluatedValue = ProfilesDialog::getStringEval(m_profile, e.attribute("default")); e.setAttribute("default", evaluatedValue); if (e.hasAttribute("value") && e.attribute("value").startsWith('%')) { @@ -1360,6 +1494,12 @@ void KdenliveDoc::addTrackEffect(int ix, QDomElement effect) //break; } } + + if (effect.attribute("id") == "crop") { + // default use_profile to 1 for clips with proxies to avoid problems when rendering + if (e.attribute("name") == "use_profile" && getDocumentProperty("enableproxy") == "1") + e.setAttribute("value", "1"); + } } m_tracksList[ix].effectsList.append(effect); @@ -1371,15 +1511,13 @@ void KdenliveDoc::removeTrackEffect(int ix, QDomElement effect) kWarning() << "Remove Track effect outisde of range"; return; } - QString index; - QString toRemove = effect.attribute("kdenlive_ix"); + int index; + int toRemove = effect.attribute("kdenlive_ix").toInt(); for (int i = 0; i < m_tracksList.at(ix).effectsList.count(); ++i) { - index = m_tracksList.at(ix).effectsList.at(i).attribute("kdenlive_ix"); + index = m_tracksList.at(ix).effectsList.at(i).attribute("kdenlive_ix").toInt(); 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); + m_tracksList[ix].effectsList.removeAt(toRemove); + break; } } } @@ -1390,12 +1528,28 @@ void KdenliveDoc::setTrackEffect(int trackIndex, int effectIndex, QDomElement ef kWarning() << "Set Track effect outisde of range"; return; } - if (effectIndex < 0 || effectIndex > (m_tracksList.at(trackIndex).effectsList.count() - 1) || effect.isNull()) { + if (effectIndex <= 0 || effectIndex > (m_tracksList.at(trackIndex).effectsList.count()) || effect.isNull()) { kDebug() << "Invalid effect index: " << effectIndex; return; } - effect.setAttribute("kdenlive_ix", effectIndex + 1); - m_tracksList[trackIndex].effectsList.replace(effectIndex, effect); + m_tracksList[trackIndex].effectsList.removeAt(effect.attribute("kdenlive_ix").toInt()); + effect.setAttribute("kdenlive_ix", effectIndex); + m_tracksList[trackIndex].effectsList.insert(effect); + //m_tracksList[trackIndex].effectsList.updateEffect(effect); +} + +void KdenliveDoc::enableTrackEffects(int trackIndex, QList effectIndexes, bool disable) +{ + if (trackIndex < 0 || trackIndex >= m_tracksList.count()) { + kWarning() << "Set Track effect outisde of range"; + return; + } + EffectsList list = m_tracksList.at(trackIndex).effectsList; + QDomElement effect; + for (int i = 0; i < effectIndexes.count(); i++) { + effect = list.itemFromIndex(effectIndexes.at(i)); + if (!effect.isNull()) effect.setAttribute("disable", (int) disable); + } } const EffectsList KdenliveDoc::getTrackEffects(int ix) @@ -1414,8 +1568,18 @@ QDomElement KdenliveDoc::getTrackEffect(int trackIndex, int effectIndex) const 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(); + if (effectIndex > list.count() || effectIndex < 1 || list.itemFromIndex(effectIndex).isNull()) return QDomElement(); + return list.itemFromIndex(effectIndex).cloneNode().toElement(); +} + +int KdenliveDoc::hasTrackEffect(int trackIndex, const QString &tag, const QString &id) const +{ + if (trackIndex < 0 || trackIndex >= m_tracksList.count()) { + kWarning() << "Get Track effect outisde of range"; + return -1; + } + EffectsList list = m_tracksList.at(trackIndex).effectsList; + return list.hasEffect(tag, id); } bool KdenliveDoc::saveCustomEffects(QDomNodeList customeffects) @@ -1449,5 +1613,211 @@ bool KdenliveDoc::saveCustomEffects(QDomNodeList customeffects) return (!importedEffects.isEmpty()); } +void KdenliveDoc::updateProjectFolderPlacesEntry() +{ + /* + * For similar and more code have a look at kfileplacesmodel.cpp and the included files: + * http://websvn.kde.org/trunk/KDE/kdelibs/kfile/kfileplacesmodel.cpp?view=markup + */ + + const QString file = KStandardDirs::locateLocal("data", "kfileplaces/bookmarks.xml"); + KBookmarkManager *bookmarkManager = KBookmarkManager::managerForFile(file, "kfilePlaces"); + if (!bookmarkManager) return; + KBookmarkGroup root = bookmarkManager->root(); + + KBookmark bookmark = root.first(); + + QString kdenliveName = KGlobal::mainComponent().componentName(); + KUrl documentLocation = m_projectFolder; + + bool exists = false; + + while (!bookmark.isNull()) { + // UDI not empty indicates a device + QString udi = bookmark.metaDataItem("UDI"); + QString appName = bookmark.metaDataItem("OnlyInApp"); + + if (udi.isEmpty() && appName == kdenliveName && bookmark.text() == i18n("Project Folder")) { + if (bookmark.url() != documentLocation) { + bookmark.setUrl(documentLocation); + bookmarkManager->emitChanged(root); + } + exists = true; + break; + } + + bookmark = root.next(bookmark); + } + + // if entry does not exist yet (was not found), well, create it then + if (!exists) { + bookmark = root.addBookmark(i18n("Project Folder"), documentLocation, "folder-favorites"); + // Make this user selectable ? + bookmark.setMetaDataItem("OnlyInApp", kdenliveName); + bookmarkManager->emitChanged(root); + } +} + +QStringList KdenliveDoc::getExpandedFolders() +{ + QStringList result = m_documentProperties.value("expandedfolders").split(';'); + // this property is only needed once when opening project, so clear it now + m_documentProperties.remove("expandedfolders"); + return result; +} + +// static +double KdenliveDoc::getDisplayRatio(const QString &path) +{ + QFile file(path); + QDomDocument doc; + if (!file.open(QIODevice::ReadOnly)) { + kWarning() << "ERROR, CANNOT READ: " << path; + return 0; + } + if (!doc.setContent(&file)) { + kWarning() << "ERROR, CANNOT READ: " << path; + file.close(); + return 0; + } + file.close(); + QDomNodeList list = doc.elementsByTagName("profile"); + if (list.isEmpty()) return 0; + QDomElement profile = list.at(0).toElement(); + double den = profile.attribute("display_aspect_den").toDouble(); + if (den > 0) return profile.attribute("display_aspect_num").toDouble() / den; + return 0; +} + +void KdenliveDoc::backupLastSavedVersion(const QString &path) +{ + // Ensure backup folder exists + if (path.isEmpty()) return; + QFile file(path); + KUrl backupFile = m_projectFolder; + backupFile.addPath(".backup/"); + KIO::NetAccess::mkdir(backupFile, kapp->activeWindow()); + QString fileName = KUrl(path).fileName().section('.', 0, -2); + QFileInfo info(file); + fileName.append('-' + m_documentProperties.value("documentid")); + fileName.append(info.lastModified().toString("-yyyy-MM-dd-hh-mm")); + fileName.append(".kdenlive"); + backupFile.addPath(fileName); + + if (file.exists()) { + // delete previous backup if it was done less than 60 seconds ago + QFile::remove(backupFile.path()); + if (!QFile::copy(path, backupFile.path())) { + KMessageBox::information(kapp->activeWindow(), i18n("Cannot create backup copy:\n%1", backupFile.path())); + } + } +} + +void KdenliveDoc::cleanupBackupFiles() +{ + KUrl backupFile = m_projectFolder; + backupFile.addPath(".backup/"); + QDir dir(backupFile.path()); + QString projectFile = url().fileName().section('.', 0, -2); + projectFile.append('-' + m_documentProperties.value("documentid")); + projectFile.append("-??"); + projectFile.append("??"); + projectFile.append("-??"); + projectFile.append("-??"); + projectFile.append("-??"); + projectFile.append("-??.kdenlive"); + + QStringList filter; + backupFile.addPath(projectFile); + filter << projectFile; + dir.setNameFilters(filter); + QFileInfoList resultList = dir.entryInfoList(QDir::Files, QDir::Time); + + QDateTime d = QDateTime::currentDateTime(); + QStringList hourList; + QStringList dayList; + QStringList weekList; + QStringList oldList; + for (int i = 0; i < resultList.count(); i++) { + if (d.secsTo(resultList.at(i).lastModified()) < 3600) { + // files created in the last hour + hourList.append(resultList.at(i).absoluteFilePath()); + } + else if (d.secsTo(resultList.at(i).lastModified()) < 43200) { + // files created in the day + dayList.append(resultList.at(i).absoluteFilePath()); + } + else if (d.daysTo(resultList.at(i).lastModified()) < 8) { + // files created in the week + weekList.append(resultList.at(i).absoluteFilePath()); + } + else { + // older files + oldList.append(resultList.at(i).absoluteFilePath()); + } + } + if (hourList.count() > 20) { + int step = hourList.count() / 10; + for (int i = 0; i < hourList.count(); i += step) { + kDebug()<<"REMOVE AT: "< 20) { + int step = dayList.count() / 10; + for (int i = 0; i < dayList.count(); i += step) { + dayList.removeAt(i); + i--; + } + } else dayList.clear(); + if (weekList.count() > 20) { + int step = weekList.count() / 10; + for (int i = 0; i < weekList.count(); i += step) { + weekList.removeAt(i); + i--; + } + } else weekList.clear(); + if (oldList.count() > 20) { + int step = oldList.count() / 10; + for (int i = 0; i < oldList.count(); i += step) { + oldList.removeAt(i); + i--; + } + } else oldList.clear(); + + QString f; + while (hourList.count() > 0) { + f = hourList.takeFirst(); + QFile::remove(f); + QFile::remove(f + ".png"); + } + while (dayList.count() > 0) { + f = dayList.takeFirst(); + QFile::remove(f); + QFile::remove(f + ".png"); + } + while (weekList.count() > 0) { + f = weekList.takeFirst(); + QFile::remove(f); + QFile::remove(f + ".png"); + } + while (oldList.count() > 0) { + f = oldList.takeFirst(); + QFile::remove(f); + QFile::remove(f + ".png"); + } +} + +const QMap KdenliveDoc::metadata() const +{ + return m_documentMetadata; +} + +void KdenliveDoc::setMetadata(const QMap meta) +{ + m_documentMetadata = meta; +} + #include "kdenlivedoc.moc"