From 40a2b375a7c1085a6f791e05cdd81350916a2a64 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Mardelle Date: Tue, 12 May 2009 23:19:47 +0000 Subject: [PATCH] *Render dialog can now create xml files for dvd chapters based on guides (usable with Kdenlive dvd wizard) *Dvd wizard now supports chapters (through ".dvdchapter" files created by render dialog) and several buttons in menu, needs more testing... svn path=/trunk/kdenlive/; revision=3382 --- src/customtrackview.cpp | 8 ++--- src/dvdwizard.cpp | 35 ++++++++++++++------ src/dvdwizardmenu.cpp | 25 +++++++-------- src/dvdwizardmenu.h | 15 +++++---- src/dvdwizardvob.cpp | 71 +++++++++++++++++++++++++++++++++++++++-- src/dvdwizardvob.h | 3 ++ src/mainwindow.cpp | 56 +++++++++++++++++++++++++++++--- src/renderwidget.cpp | 46 ++++++++++++++++---------- src/timecode.cpp | 21 +++++++++++- src/timecode.h | 1 + 10 files changed, 224 insertions(+), 57 deletions(-) diff --git a/src/customtrackview.cpp b/src/customtrackview.cpp index 42d0c3dc..c42f5866 100644 --- a/src/customtrackview.cpp +++ b/src/customtrackview.cpp @@ -677,11 +677,11 @@ void CustomTrackView::mousePressEvent(QMouseEvent * event) } } // keep this to support multiple guides context menu in the future (?) - /*if (guidesCollisionList.at(0)->type() != GUIDEITEM) - guidesCollisionList.removeAt(0); + /*if (guidesCollisionList.at(0)->type() != GUIDEITEM) + guidesCollisionList.removeAt(0); } if (!guidesCollisionList.isEmpty()) - m_dragGuide = static_cast (guidesCollisionList.at(0));*/ + m_dragGuide = static_cast (guidesCollisionList.at(0));*/ } m_operationMode = NONE; @@ -3509,7 +3509,7 @@ int CustomTrackView::hasGuide(int pos, int offset) { for (int i = 0; i < m_guides.count(); i++) { int guidePos = m_guides.at(i)->position().frames(m_document->fps()); - if (qAbs(guidePos - pos) < offset) return guidePos; + if (qAbs(guidePos - pos) <= offset) return guidePos; else if (guidePos > pos) return -1; } return -1; diff --git a/src/dvdwizard.cpp b/src/dvdwizard.cpp index 1daf23d0..4f871145 100644 --- a/src/dvdwizard.cpp +++ b/src/dvdwizard.cpp @@ -111,11 +111,11 @@ DvdWizard::~DvdWizard() void DvdWizard::slotPageChanged(int page) { - kDebug() << "// PAGE CHGD: " << page; + kDebug() << "// PAGE CHGD: " << page; if (page == 1) { - m_pageMenu->setTargets(m_pageVob->selectedUrls()); + m_pageMenu->setTargets(m_pageVob->selectedTitles(), m_pageVob->selectedTargets()); } else if (page == 2) { - m_pageMenu->buttonsInfo(); + //m_pageMenu->buttonsInfo(); } else if (page == 3) { // clear job icons for (int i = 0; i < m_status.job_progress->count(); i++) @@ -182,7 +182,7 @@ void DvdWizard::generateDvd() QListWidgetItem *images = m_status.job_progress->item(0); images->setIcon(KIcon("system-run")); qApp->processEvents(); - QMap buttons = m_pageMenu->buttonsInfo(); + QMap buttons = m_pageMenu->buttonsInfo(); QStringList buttonsTarget; if (m_pageMenu->createMenu()) { @@ -258,7 +258,7 @@ void DvdWizard::generateDvd() int max = buttons.count() - 1; int i = 0; - QMapIterator it(buttons); + QMapIterator it(buttons); while (it.hasNext()) { it.next(); QDomElement but = doc.createElement("button"); @@ -268,10 +268,10 @@ void DvdWizard::generateDvd() if (i > 0) but.setAttribute("up", 'b' + QString::number(i - 1)); else but.setAttribute("up", 'b' + QString::number(max)); QRect r = it.value(); - int target = it.key(); + //int target = it.key(); // TODO: solve play all button - if (target == 0) target = 1; - buttonsTarget.append(QString::number(target)); + //if (target == 0) target = 1; + buttonsTarget.append(it.key()); but.setAttribute("x0", QString::number(r.x())); but.setAttribute("y0", QString::number(r.y())); but.setAttribute("x1", QString::number(r.right())); @@ -363,7 +363,7 @@ void DvdWizard::generateDvd() for (int i = 0; i < buttons.count(); i++) { QDomElement button = dvddoc.createElement("button"); button.setAttribute("name", 'b' + QString::number(i)); - QDomText nametext = dvddoc.createTextNode("jump title " + buttonsTarget.at(i) + ';'); + QDomText nametext = dvddoc.createTextNode("jump " + buttonsTarget.at(i) + ';'); button.appendChild(nametext); pgc.appendChild(button); } @@ -393,6 +393,23 @@ void DvdWizard::generateDvd() // Add vob entry QDomElement vob = dvddoc.createElement("vob"); vob.setAttribute("file", voburls.at(i)); + if (m_pageVob->useChapters()) { + // Add chapters + if (QFile::exists(voburls.at(i) + ".dvdchapter")) { + QFile file(voburls.at(i) + ".dvdchapter"); + if (file.open(QIODevice::ReadOnly)) { + QDomDocument doc; + doc.setContent(&file); + file.close(); + QDomNodeList chapters = doc.elementsByTagName("chapter"); + QStringList chaptersList; + for (int j = 0; j < chapters.count(); j++) { + chaptersList.append(chapters.at(j).toElement().attribute("time")); + } + if (!chaptersList.isEmpty()) vob.setAttribute("chapters", chaptersList.join(",")); + } + } + } pgc2.appendChild(vob); } } diff --git a/src/dvdwizardmenu.cpp b/src/dvdwizardmenu.cpp index fefa8e80..68a69a73 100644 --- a/src/dvdwizardmenu.cpp +++ b/src/dvdwizardmenu.cpp @@ -37,8 +37,8 @@ DvdWizardMenu::DvdWizardMenu(const QString &profile, QWidget *parent) : m_view.delete_button->setIcon(KIcon("trash-empty")); // TODO: re-enable add button options - m_view.add_button->setVisible(false); - m_view.delete_button->setVisible(false); + /*m_view.add_button->setVisible(false); + m_view.delete_button->setVisible(false);*/ m_view.menu_profile->addItems(QStringList() << i18n("PAL") << i18n("NTSC")); @@ -167,7 +167,7 @@ void DvdWizardMenu::setButtonTarget(int ix) for (int i = 0; i < list.count(); i++) { if (list.at(i)->type() == QGraphicsItem::UserType + 1) { DvdButton *button = static_cast < DvdButton* >(list.at(i)); - button->setTarget(ix); + button->setTarget(ix, m_view.target_list->itemData(ix).toString()); break; } } @@ -263,16 +263,13 @@ void DvdWizardMenu::updatePreview() m_safeRect->setRect(safeW, safeH, m_width - 2 * safeW, m_height - 2 * safeH); } -void DvdWizardMenu::setTargets(QStringList /*list*/) +void DvdWizardMenu::setTargets(QStringList list, QStringList targetlist) { - m_targets = QStringList() << i18n("Play All"); - // TODO: re-enable different targets - /* - if (list.count() > 1) { - m_targets += list; + m_view.target_list->clear(); + m_view.target_list->addItem(i18n("Play All"), "title 1"); + for (int i = 0; i < list.count(); i++) { + m_view.target_list->addItem(list.at(i), targetlist.at(i)); } - m_view.target_list->clear();*/ - m_view.target_list->addItems(m_targets); } void DvdWizardMenu::checkBackgroundType(int ix) @@ -450,9 +447,9 @@ QString DvdWizardMenu::menuMoviePath() const } -QMap DvdWizardMenu::buttonsInfo() +QMap DvdWizardMenu::buttonsInfo() { - QMap info; + QMap info; QList list = m_scene->items(); for (int i = 0; i < list.count(); i++) { if (list.at(i)->type() == QGraphicsItem::UserType + 1) { @@ -461,7 +458,7 @@ QMap DvdWizardMenu::buttonsInfo() // Make sure y1 is not odd (requested by spumux) if (r.height() % 2 == 1) r.setHeight(r.height() + 1); if (r.y() % 2 == 1) r.setY(r.y() - 1); - info.insertMulti(button->target(), r); + info.insertMulti(button->command(), r); } } return info; diff --git a/src/dvdwizardmenu.h b/src/dvdwizardmenu.h index 01a0c73b..ef37af24 100644 --- a/src/dvdwizardmenu.h +++ b/src/dvdwizardmenu.h @@ -56,14 +56,18 @@ class DvdButton : public QGraphicsTextItem { public: - DvdButton(const QString & text): QGraphicsTextItem(text), m_target(0) {} + DvdButton(const QString & text): QGraphicsTextItem(text), m_target(0), m_command(QString("title 1")) {} enum { Type = UserType + 1 }; - void setTarget(int t) { + void setTarget(int t, QString c) { m_target = t; + m_command = c; } int target() const { return m_target; } + QString command() const { + return m_command; + } int type() const { // Enable the use of qgraphicsitem_cast with this item. return Type; @@ -71,7 +75,7 @@ public: private: int m_target; - + QString m_command; protected: @@ -112,8 +116,8 @@ public: bool createMenu() const; void createBackgroundImage(const QString &img1); void createButtonImages(const QString &img1, const QString &img2, const QString &img3); - void setTargets(QStringList list); - QMap buttonsInfo(); + void setTargets(QStringList list, QStringList targetlist); + QMap buttonsInfo(); bool menuMovie() const; QString menuMoviePath() const; bool isPalMenu() const; @@ -127,7 +131,6 @@ private: QGraphicsRectItem *m_safeRect; int m_width; int m_height; - QStringList m_targets; private slots: void buildButton(); diff --git a/src/dvdwizardvob.cpp b/src/dvdwizardvob.cpp index a9dbfc67..c49d116e 100644 --- a/src/dvdwizardvob.cpp +++ b/src/dvdwizardvob.cpp @@ -25,6 +25,7 @@ #include #include +#include DvdWizardVob::DvdWizardVob(QWidget *parent) : QWizardPage(parent) @@ -69,6 +70,11 @@ bool DvdWizardVob::isComplete() const return false; } +bool DvdWizardVob::useChapters() const +{ + return m_view.use_chapters->isChecked(); +} + void DvdWizardVob::setUrl(const QString &url) { m_view.vob_1->setPath(url); @@ -77,15 +83,76 @@ void DvdWizardVob::setUrl(const QString &url) QStringList DvdWizardVob::selectedUrls() const { QStringList result; + QString path; + QList allUrls = m_view.vob_list->findChildren(); + for (int i = 0; i < allUrls.count(); i++) { + path = allUrls.at(i)->url().path(); + if (!path.isEmpty()) { + result.append(path); + } + } + return result; +} + +QStringList DvdWizardVob::selectedTitles() const +{ + QStringList result; + QString path; QList allUrls = m_view.vob_list->findChildren(); for (int i = 0; i < allUrls.count(); i++) { - if (!allUrls.at(i)->url().path().isEmpty()) { - result.append(allUrls.at(i)->url().path()); + path = allUrls.at(i)->url().path(); + if (!path.isEmpty()) { + result.append(path); + if (useChapters() && QFile::exists(path + ".dvdchapter")) { + // insert chapters as targets + QFile file(path + ".dvdchapter"); + if (file.open(QIODevice::ReadOnly)) { + QDomDocument doc; + doc.setContent(&file); + file.close(); + QDomNodeList chapters = doc.elementsByTagName("chapter"); + QStringList chaptersList; + for (int j = 0; j < chapters.count(); j++) { + result.append(QString::number(j) + " - " + chapters.at(j).toElement().attribute("title") + " " + chapters.at(j).toElement().attribute("time")); + } + } + + } + } + } + return result; +} + +QStringList DvdWizardVob::selectedTargets() const +{ + QStringList result; + QString path; + QList allUrls = m_view.vob_list->findChildren(); + for (int i = 0; i < allUrls.count(); i++) { + path = allUrls.at(i)->url().path(); + if (!path.isEmpty()) { + result.append("title " + QString::number(i)); + if (useChapters() && QFile::exists(path + ".dvdchapter")) { + // insert chapters as targets + QFile file(path + ".dvdchapter"); + if (file.open(QIODevice::ReadOnly)) { + QDomDocument doc; + doc.setContent(&file); + file.close(); + QDomNodeList chapters = doc.elementsByTagName("chapter"); + QStringList chaptersList; + for (int j = 0; j < chapters.count(); j++) { + result.append("title " + QString::number(i + 1) + " chapter " + QString::number(j + 1)); + } + } + + } } } return result; } + QString DvdWizardVob::introMovie() const { if (!m_view.use_intro->isChecked()) return QString(); diff --git a/src/dvdwizardvob.h b/src/dvdwizardvob.h index 5544133c..0ce4a297 100644 --- a/src/dvdwizardvob.h +++ b/src/dvdwizardvob.h @@ -40,8 +40,11 @@ public: virtual ~DvdWizardVob(); virtual bool isComplete() const; QStringList selectedUrls() const; + QStringList selectedTitles() const; + QStringList selectedTargets() const; void setUrl(const QString &url); QString introMovie() const; + bool useChapters() const; private: Ui::DvdWizardVob_UI m_view; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 8161941e..7750bc57 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1567,6 +1567,7 @@ void MainWindow::slotDoRender(const QStringList args, const QStringList overlay_ double guideEnd = args.at(6).toDouble(); bool resizeProfile = args.at(7).toInt(); QString scriptExport = args.at(8); + bool createChapterFile = args.at(9).toInt(); if (dest.isEmpty()) return; int in = 0; @@ -1576,6 +1577,7 @@ void MainWindow::slotDoRender(const QStringList args, const QStringList overlay_ in = m_activeTimeline->inPoint(); out = m_activeTimeline->outPoint(); } + KTemporaryFile temp; temp.setAutoRemove(false); temp.setSuffix(".westley"); @@ -1643,11 +1645,11 @@ void MainWindow::slotDoRender(const QStringList args, const QStringList overlay_ return; } - QTextStream out(&file); - out << "#! /bin/sh" << "\n" << "\n"; - out << "SOURCE=" << "\"" + scriptExport + ".westley\"" << "\n"; - out << "TARGET=" << "\"" + dest + "\"" << "\n"; - out << renderer << " " << args.join(" ") << "\n" << "\n"; + QTextStream outStream(&file); + outStream << "#! /bin/sh" << "\n" << "\n"; + outStream << "SOURCE=" << "\"" + scriptExport + ".westley\"" << "\n"; + outStream << "TARGET=" << "\"" + dest + "\"" << "\n"; + outStream << renderer << " " << args.join(" ") << "\n" << "\n"; if (file.error() != QFile::NoError) { KMessageBox::error(this, i18n("Cannot write to file %1", scriptExport)); file.close(); @@ -1656,6 +1658,50 @@ void MainWindow::slotDoRender(const QStringList args, const QStringList overlay_ file.close(); QFile::setPermissions(scriptExport, file.permissions() | QFile::ExeUser); } + + if (createChapterFile) { + QDomDocument doc; + QDomElement chapters = doc.createElement("chapters"); + doc.appendChild(chapters); + + QDomElement guidesxml = m_activeDocument->guidesXml(); + if (!zoneOnly) out = (int) GenTime(m_activeDocument->projectDuration()).frames(m_activeDocument->fps()); + + QDomNodeList nodes = guidesxml.elementsByTagName("guide"); + for (int i = 0; i < nodes.count(); i++) { + QDomElement e = nodes.item(i).toElement(); + if (!e.isNull()) { + QString comment = e.attribute("comment"); + int time = (int) GenTime(e.attribute("time").toDouble()).frames(m_activeDocument->fps()); + if (time >= in && time < out) { + if (zoneOnly) time = time - in; + QDomElement chapter = doc.createElement("chapter"); + chapters.appendChild(chapter); + chapter.setAttribute("title", comment); + chapter.setAttribute("time", Timecode::getStringTimecode(time, m_activeDocument->fps())); + } + } + } + if (chapters.childNodes().count() > 0) { + if (m_activeTimeline->projectView()->hasGuide(out, 0) == -1) { + // Always insert a guide in pos 0 + QDomElement chapter = doc.createElement("chapter"); + chapters.insertBefore(chapter, QDomNode()); + chapter.setAttribute("title", i18n("Start")); + chapter.setAttribute("time", "0"); + } + // save chapters file + QFile file(dest + ".dvdchapter"); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + kWarning() << "////// ERROR writing DVD CHAPTER file: " << dest + ".dvdchapter"; + } else { + file.write(doc.toString().toUtf8()); + if (file.error() != QFile::NoError) + kWarning() << "////// ERROR writing DVD CHAPTER file: " << dest + ".dvdchapter"; + file.close(); + } + } + } } } diff --git a/src/renderwidget.cpp b/src/renderwidget.cpp index fe582db8..d31b8195 100644 --- a/src/renderwidget.cpp +++ b/src/renderwidget.cpp @@ -141,6 +141,7 @@ RenderWidget::RenderWidget(const QString &projectfolder, QWidget * parent) : m_view.rescale_size->setEnabled(false); m_view.guides_box->setVisible(false); m_view.open_dvd->setVisible(false); + m_view.create_chapter->setVisible(false); m_view.open_browser->setVisible(false); m_view.error_box->setVisible(false); @@ -671,26 +672,34 @@ void RenderWidget::slotExport(bool scriptExport) renderParameters << QString::number(m_view.render_zone->isChecked()) << QString::number(m_view.play_after->isChecked()); renderParameters << QString::number(startPos) << QString::number(endPos) << QString::number(resizeProfile); renderParameters << scriptName; - renderItem->setData(1, Qt::UserRole + 3, renderParameters); // Set rendering type QString group = m_view.size_list->currentItem()->data(MetaGroupRole).toString(); - if (group == "dvd" && m_view.open_dvd->isChecked()) { - renderItem->setData(0, Qt::UserRole, group); - if (renderArgs.contains("profile=")) { - // rendering profile contains an MLT profile, so pass it to the running jog item, useful for dvd - QString prof = renderArgs.section("profile=", 1, 1); - prof = prof.section(' ', 0, 0); - kDebug() << "// render profile: " << prof; - renderItem->setData(0, Qt::UserRole + 1, prof); + if (group == "dvd") { + renderParameters << QString::number(m_view.create_chapter->isChecked()); + if (m_view.open_dvd->isChecked()) { + renderItem->setData(0, Qt::UserRole, group); + if (renderArgs.contains("profile=")) { + // rendering profile contains an MLT profile, so pass it to the running jog item, useful for dvd + QString prof = renderArgs.section("profile=", 1, 1); + prof = prof.section(' ', 0, 0); + kDebug() << "// render profile: " << prof; + renderItem->setData(0, Qt::UserRole + 1, prof); + } + } + } else { + renderParameters << QString::number(false); + if (group == "websites" && m_view.open_browser->isChecked()) { + renderItem->setData(0, Qt::UserRole, group); + // pass the url + QString url = m_view.size_list->currentItem()->data(ExtraRole).toString(); + renderItem->setData(0, Qt::UserRole + 1, url); } - } else if (group == "websites" && m_view.open_browser->isChecked()) { - renderItem->setData(0, Qt::UserRole, group); - // pass the url - QString url = m_view.size_list->currentItem()->data(ExtraRole).toString(); - renderItem->setData(0, Qt::UserRole + 1, url); } + + renderItem->setData(1, Qt::UserRole + 3, renderParameters); + checkRenderStatus(); if (scriptName.isEmpty()) m_view.tabWidget->setCurrentIndex(1); else { @@ -736,8 +745,13 @@ void RenderWidget::refreshView() destination = m_view.destination_list->itemData(m_view.destination_list->currentIndex()).toString(); - if (destination == "dvd") m_view.open_dvd->setVisible(true); - else m_view.open_dvd->setVisible(false); + if (destination == "dvd") { + m_view.open_dvd->setVisible(true); + m_view.create_chapter->setVisible(true); + } else { + m_view.open_dvd->setVisible(false); + m_view.create_chapter->setVisible(false); + } if (destination == "websites") m_view.open_browser->setVisible(true); else m_view.open_browser->setVisible(false); if (!destination.isEmpty() && QString("dvd websites audioonly").contains(destination)) diff --git a/src/timecode.cpp b/src/timecode.cpp index 40e7b198..cc429252 100644 --- a/src/timecode.cpp +++ b/src/timecode.cpp @@ -96,6 +96,26 @@ QString Timecode::getTimecodeFromFrames(int frames) return getTimecodeHH_MM_SS_FF(frames); } + +//static +QString Timecode::getStringTimecode(int frames, const double &fps) +{ + // Returns the timecode in an hh:mm:ss format + int seconds = frames / (int) floor(fps + 0.5); + int minutes = seconds / 60; + seconds = seconds % 60; + int hours = minutes / 60; + minutes = minutes % 60; + QString text; + text.append(QString::number(hours).rightJustified(2, '0', false)); + text.append(':'); + text.append(QString::number(minutes).rightJustified(2, '0', false)); + text.append(':'); + text.append(QString::number(seconds).rightJustified(2, '0', false)); + return text; +} + + //static QString Timecode::getEasyTimecode(const GenTime & time, const double &fps) { @@ -161,7 +181,6 @@ QString Timecode::getTimecodeHH_MM_SS_FF(int frames) const minutes = minutes % 60; QString text; - text.append(QString::number(hours).rightJustified(2, '0', false)); text.append(':'); text.append(QString::number(minutes).rightJustified(2, '0', false)); diff --git a/src/timecode.h b/src/timecode.h index 00eae56a..15c8167d 100644 --- a/src/timecode.h +++ b/src/timecode.h @@ -51,6 +51,7 @@ public: QString getTimecode(const GenTime & time, double fps) const; int getFrameCount(const QString duration, double fps) const; static QString getEasyTimecode(const GenTime & time, const double &fps); + static QString getStringTimecode(int frames, const double &fps); QString getTimecodeFromFrames(int frames); int fps(); -- 2.39.5