X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fkdenlivedoc.cpp;h=e190123f84171a467c3b7041c07893cd4f875182;hb=672e4b17670b666350d820ce6fb30e40a862cbc3;hp=d4eaa6da10b21259885cc3948c26a552e28dbb89;hpb=e71084bf388a41bc9494da9cd6f2a4d7af487962;p=kdenlive diff --git a/src/kdenlivedoc.cpp b/src/kdenlivedoc.cpp index d4eaa6da..e190123f 100644 --- a/src/kdenlivedoc.cpp +++ b/src/kdenlivedoc.cpp @@ -52,9 +52,9 @@ #include -const double DOCUMENTVERSION = 0.85; +const double DOCUMENTVERSION = 0.87; -KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup *undoGroup, QString profileName, QMap properties, const QPoint tracks, Render *render, KTextEdit *notes, MainWindow *parent, KProgressDialog *progressDialog) : +KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup *undoGroup, QString profileName, QMap properties, const QPoint tracks, Render *render, KTextEdit *notes, bool *openBackup, MainWindow *parent, KProgressDialog *progressDialog) : QObject(parent), m_autosave(NULL), m_url(url), @@ -81,19 +81,32 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup 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(); } + + *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; @@ -103,8 +116,13 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup 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); qApp->processEvents(); @@ -113,6 +131,9 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup 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 @@ -131,6 +152,9 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup 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()) { @@ -166,12 +190,14 @@ KdenliveDoc::KdenliveDoc(const KUrl &url, const KUrl &projectFolder, QUndoGroup } mlt.removeChild(tracksinfo); } - + 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"); @@ -234,7 +260,7 @@ 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")); + if (validator.isModified()) setModified(true); kDebug() << "Reading file: " << url.path() << ", found clips: " << producers.count(); } @@ -244,17 +270,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())); @@ -301,7 +324,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; @@ -349,8 +372,7 @@ QDomDocument KdenliveDoc::createEmptyDocument(QList tracks) QDomDocument doc; QDomElement mlt = doc.createElement("mlt"); 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. @@ -367,7 +389,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); @@ -507,7 +529,7 @@ void KdenliveDoc::slotAutoSave() kDebug() << "ERROR; CANNOT CREATE AUTOSAVE FILE"; } kDebug() << "// AUTOSAVE FILE: " << m_autosave->fileName(); - saveSceneList(m_autosave->fileName(), m_render->sceneList()); + saveSceneList(m_autosave->fileName(), m_render->sceneList(), QStringList(), true); } } @@ -533,15 +555,14 @@ 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; } QDomElement addedXml = sceneList.createElement("kdenlivedoc"); @@ -587,7 +608,7 @@ bool KdenliveDoc::saveSceneList(const QString &path, const QString &scene) addedXml.appendChild(docproperties); 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); @@ -627,6 +648,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); } @@ -655,8 +677,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)); @@ -670,6 +706,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; } @@ -793,6 +841,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); } @@ -825,10 +874,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()); + m_clipManager->resetProducersList(m_render->producersList(), displayRatioChanged, fpsChanged); } Render *KdenliveDoc::renderer() @@ -912,6 +961,7 @@ void KdenliveDoc::setUrl(KUrl url) void KdenliveDoc::setModified(bool mod) { + if (isReadOnly()) return; if (!m_url.isEmpty() && mod && KdenliveSettings::crashrecovery()) { m_autoSaveTimer->start(3000); } @@ -1308,7 +1358,7 @@ QString KdenliveDoc::getLadspaFile() const bool KdenliveDoc::checkDocumentClips(QDomNodeList infoproducers) { DocumentChecker d(infoproducers, m_document); - return (d.hasMissingClips() == false); + return (d.hasErrorInClips() == false); /* int clipType; QDomElement e; @@ -1377,7 +1427,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('%')) { @@ -1394,6 +1444,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); @@ -1526,5 +1582,161 @@ void KdenliveDoc::updateProjectFolderPlacesEntry() } } +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())); + } + } +} + +bool KdenliveDoc::isReadOnly() const +{ + return m_documentProperties.contains("readonly"); +} + +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"); + } +} + #include "kdenlivedoc.moc"