]> git.sesse.net Git - kdenlive/blob - src/dvdwizard.cpp
Merge branch 'master' into feature/pkey
[kdenlive] / src / dvdwizard.cpp
1 /***************************************************************************
2  *   Copyright (C) 2007 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
18  ***************************************************************************/
19
20
21 #include "dvdwizard.h"
22 #include "kdenlivesettings.h"
23 #include "profilesdialog.h"
24 #include "timecode.h"
25 #include "monitormanager.h"
26
27 #include <KStandardDirs>
28 #include <KLocale>
29 #include <KFileDialog>
30 #include <kmimetype.h>
31 #include <KIO/NetAccess>
32 #include <KMessageBox>
33
34 #include <QFile>
35 #include <QApplication>
36 #include <QTimer>
37 #include <QDomDocument>
38 #include <QMenu>
39 #include <QGridLayout>
40
41
42 DvdWizard::DvdWizard(const QString &url, const QString &profile, QWidget *parent) :
43         QWizard(parent),
44         m_dvdauthor(NULL),
45         m_mkiso(NULL),
46         m_burnMenu(new QMenu(this))
47 {
48     setWindowTitle(i18n("DVD Wizard"));
49     //setPixmap(QWizard::WatermarkPixmap, QPixmap(KStandardDirs::locate("appdata", "banner.png")));
50     m_pageVob = new DvdWizardVob(profile, this);
51     m_pageVob->setTitle(i18n("Select Files For Your DVD"));
52     addPage(m_pageVob);
53     if (!url.isEmpty()) m_pageVob->setUrl(url);
54
55
56     m_pageChapters = new DvdWizardChapters(m_pageVob->isPal(), this);
57     m_pageChapters->setTitle(i18n("DVD Chapters"));
58     addPage(m_pageChapters);
59
60
61
62     m_pageMenu = new DvdWizardMenu(profile, this);
63     m_pageMenu->setTitle(i18n("Create DVD Menu"));
64     addPage(m_pageMenu);
65
66     QWizardPage *page4 = new QWizardPage;
67     page4->setTitle(i18n("Creating DVD Image"));
68     m_status.setupUi(page4);
69     m_status.error_box->setHidden(true);
70     m_status.error_box->setTabBarHidden(true);
71     m_status.tmp_folder->setUrl(KUrl(KdenliveSettings::currenttmpfolder()));
72     m_status.tmp_folder->setMode(KFile::Directory | KFile::ExistingOnly);
73     m_status.iso_image->setUrl(KUrl(QDir::homePath() + "/untitled.iso"));
74     m_status.iso_image->setFilter("*.iso");
75     m_status.iso_image->setMode(KFile::File);
76     m_status.iso_image->fileDialog()->setOperationMode(KFileDialog::Saving);
77
78 #if KDE_IS_VERSION(4,7,0)
79     m_isoMessage = new KMessageWidget;
80     QGridLayout *s =  static_cast <QGridLayout*> (page4->layout());
81     s->addWidget(m_isoMessage, 5, 0, 1, -1);
82     m_isoMessage->hide();
83 #endif
84
85     addPage(page4);
86
87     connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(slotPageChanged(int)));
88     connect(m_status.button_start, SIGNAL(clicked()), this, SLOT(slotGenerate()));
89     connect(m_status.button_abort, SIGNAL(clicked()), this, SLOT(slotAbort()));
90     connect(m_status.button_preview, SIGNAL(clicked()), this, SLOT(slotPreview()));
91
92     QString programName("k3b");
93     QString exec = KStandardDirs::findExe(programName);
94     if (!exec.isEmpty()) {
95         //Add K3b action
96         QAction *k3b = m_burnMenu->addAction(KIcon(programName), i18n("Burn with %1", programName), this, SLOT(slotBurn()));
97         k3b->setData(exec);
98     }
99     programName = "brasero";
100     exec = KStandardDirs::findExe(programName);
101     if (!exec.isEmpty()) {
102         //Add Brasero action
103         QAction *brasero = m_burnMenu->addAction(KIcon(programName), i18n("Burn with %1", programName), this, SLOT(slotBurn()));
104         brasero->setData(exec);
105     }
106     if (m_burnMenu->isEmpty()) m_burnMenu->addAction(i18n("No burning program found (K3b, Brasero)"));
107     m_status.button_burn->setMenu(m_burnMenu);
108     m_status.button_burn->setIcon(KIcon("tools-media-optical-burn"));
109     m_status.button_burn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
110     m_status.button_preview->setIcon(KIcon("media-playback-start"));
111
112     setButtonText(QWizard::CustomButton1, i18n("Load"));
113     setButtonText(QWizard::CustomButton2, i18n("Save"));
114     button(QWizard::CustomButton1)->setIcon(KIcon("document-open"));
115     button(QWizard::CustomButton2)->setIcon(KIcon("document-save"));
116     connect(button(QWizard::CustomButton1), SIGNAL(clicked()), this, SLOT(slotLoad()));
117     connect(button(QWizard::CustomButton2), SIGNAL(clicked()), this, SLOT(slotSave()));
118     setOption(QWizard::HaveCustomButton1, true);
119     setOption(QWizard::HaveCustomButton2, true);
120     QList<QWizard::WizardButton> layout;
121     layout << QWizard::CustomButton1 << QWizard::CustomButton2 << QWizard::Stretch << QWizard::BackButton << QWizard::NextButton << QWizard::CancelButton << QWizard::FinishButton;
122     setButtonLayout(layout);
123 }
124
125 DvdWizard::~DvdWizard()
126 {
127     m_authorFile.remove();
128     m_menuFile.remove();
129     m_menuVobFile.remove();
130     blockSignals(true);
131     delete m_burnMenu;
132     if (m_dvdauthor) {
133         m_dvdauthor->blockSignals(true);
134         m_dvdauthor->close();
135         delete m_dvdauthor;
136     }
137     if (m_mkiso) {
138         m_mkiso->blockSignals(true);
139         m_mkiso->close();
140         delete m_mkiso;
141     }
142 }
143
144
145 void DvdWizard::slotPageChanged(int page)
146 {
147     //kDebug() << "// PAGE CHGD: " << page << ", ID: " << visitedPages();
148     if (page == 0) {
149         // Update chapters that were modified in page 1
150         m_pageChapters->stopMonitor();
151         m_pageVob->updateChapters(m_pageChapters->chaptersData());
152     } else if (page == 1) {
153         m_pageChapters->setVobFiles(m_pageVob->isPal(), m_pageVob->isWide(), m_pageVob->selectedUrls(), m_pageVob->durations(), m_pageVob->chapters());
154     } else if (page == 2) {
155         m_pageChapters->stopMonitor();
156         m_pageVob->updateChapters(m_pageChapters->chaptersData());
157         m_pageMenu->setTargets(m_pageChapters->selectedTitles(), m_pageChapters->selectedTargets());
158         m_pageMenu->changeProfile(m_pageVob->isPal());
159     }
160 }
161
162
163
164 void DvdWizard::generateDvd()
165 {
166 #if KDE_IS_VERSION(4,7,0)
167     m_isoMessage->animatedHide();
168 #endif
169     m_status.error_box->setHidden(true);
170     m_status.error_box->setCurrentIndex(0);
171     m_status.error_box->setTabBarHidden(true);
172     m_status.menu_file->clear();
173     m_status.dvd_file->clear();
174     KTemporaryFile temp1;
175     temp1.setSuffix(".png");
176     //temp1.setAutoRemove(false);
177     temp1.open();
178
179     KTemporaryFile temp2;
180     temp2.setSuffix(".png");
181     //temp2.setAutoRemove(false);
182     temp2.open();
183
184     KTemporaryFile temp3;
185     temp3.setSuffix(".png");
186     //temp3.setAutoRemove(false);
187     temp3.open();
188
189     KTemporaryFile temp4;
190     temp4.setSuffix(".png");
191     //temp4.setAutoRemove(false);
192     temp4.open();
193
194     KTemporaryFile temp5;
195     temp5.setSuffix(".vob");
196     //temp5.setAutoRemove(false);
197     temp5.open();
198
199     KTemporaryFile temp6;
200     temp6.setSuffix(".vob");
201     //temp6.setAutoRemove(false);
202     temp6.open();
203
204     m_menuFile.close();
205     m_menuFile.setSuffix(".xml");
206     m_menuFile.setAutoRemove(false);
207     m_menuFile.open();
208
209     m_menuVobFile.close();
210     m_menuVobFile.setSuffix(".mpg");
211     m_menuVobFile.setAutoRemove(false);
212     m_menuVobFile.open();
213
214     m_authorFile.close();
215     m_authorFile.setSuffix(".xml");
216     m_authorFile.setAutoRemove(false);
217     m_authorFile.open();
218
219     QListWidgetItem *images =  m_status.job_progress->item(0);
220     m_status.job_progress->setCurrentRow(0);
221     images->setIcon(KIcon("system-run"));
222     qApp->processEvents();
223     QMap <QString, QRect> buttons = m_pageMenu->buttonsInfo();
224     QStringList buttonsTarget;
225     m_status.error_log->clear();
226     // initialize html content
227     m_status.error_log->setText("<html></html>");
228
229     if (m_pageMenu->createMenu()) {
230         m_pageMenu->createButtonImages(temp1.fileName(), temp2.fileName(), temp3.fileName());
231         m_pageMenu->createBackgroundImage(temp1.fileName(), temp4.fileName());
232
233
234         images->setIcon(KIcon("dialog-ok"));
235
236         kDebug() << "/// STARTING MLT VOB CREATION";
237         if (!m_pageMenu->menuMovie()) {
238             // create menu vob file
239             QListWidgetItem *vobitem =  m_status.job_progress->item(1);
240             m_status.job_progress->setCurrentRow(1);
241             vobitem->setIcon(KIcon("system-run"));
242             qApp->processEvents();
243
244             QStringList args;
245             args.append("-profile");
246             if (m_pageMenu->isPalMenu()) args.append("dv_pal");
247             else args.append("dv_ntsc");
248             args.append(temp4.fileName());
249             args.append("in=0");
250             args.append("out=100");
251             args << "-consumer" << "avformat:" + temp5.fileName()<<"properties=DVD";
252             QProcess renderbg;
253             renderbg.start(KdenliveSettings::rendererpath(), args);
254             if (renderbg.waitForFinished()) {
255                 if (renderbg.exitStatus() == QProcess::CrashExit) {
256                     kDebug() << "/// RENDERING MENU vob crashed";
257                     errorMessage(i18n("Rendering menu crashed"));
258                     QByteArray result = renderbg.readAllStandardError();
259                     vobitem->setIcon(KIcon("dialog-close"));
260                     m_status.error_log->append(result);
261                     m_status.error_box->setHidden(false);
262                     m_status.button_start->setEnabled(true);
263                     m_status.button_abort->setEnabled(false);
264                     return;
265                 }
266             } else {
267                 kDebug() << "/// RENDERING MENU vob timed out";
268                 errorMessage(i18n("Rendering job timed out"));
269                 vobitem->setIcon(KIcon("dialog-close"));
270                 m_status.error_log->append("<a name=\"result\" /><br /><strong>" + i18n("Rendering job timed out"));
271                 m_status.error_log->scrollToAnchor("result");
272                 m_status.error_box->setHidden(false);
273                 m_status.button_start->setEnabled(true);
274                 m_status.button_abort->setEnabled(false);
275                 return;
276             }
277             vobitem->setIcon(KIcon("dialog-ok"));
278         } else {
279             // Movie as menu background, do the compositing
280             QListWidgetItem *vobitem =  m_status.job_progress->item(1);
281             m_status.job_progress->setCurrentRow(1);
282             vobitem->setIcon(KIcon("system-run"));
283             qApp->processEvents();
284
285             QString std;
286             if (m_pageMenu->isPalMenu()) std = "dv_pal";
287             else std = "dv_ntsc";
288             int menuLength = m_pageMenu->menuMovieLength();
289             if (menuLength == -1) {
290                 // menu movie is invalid
291                 errorMessage(i18n("Menu movie is invalid"));
292                 m_status.button_start->setEnabled(true);
293                 m_status.button_abort->setEnabled(false);
294                 return;
295             }
296
297             QStringList args;
298             args.append("-profile");
299             args.append(std);
300             args.append(m_pageMenu->menuMoviePath());
301             args << "-track" << temp4.fileName();
302             args << "out=" + QString::number(menuLength);
303             args << "-transition" << "composite" << "always_active=1";
304             args << "-consumer" << "avformat:" + temp6.fileName()<<"properties=DVD";
305             QProcess renderbg;
306             renderbg.start(KdenliveSettings::rendererpath(), args);
307             if (renderbg.waitForFinished()) {
308                 if (renderbg.exitStatus() == QProcess::CrashExit) {
309                     kDebug() << "/// RENDERING MENU vob crashed";
310                     errorMessage(i18n("Rendering menu crashed"));
311                     QByteArray result = renderbg.readAllStandardError();
312                     vobitem->setIcon(KIcon("dialog-close"));
313                     m_status.error_log->append(result);
314                     m_status.error_box->setHidden(false);
315                     m_status.button_start->setEnabled(true);
316                     m_status.button_abort->setEnabled(false);
317                     return;
318                 }
319             } else {
320                 kDebug() << "/// RENDERING MENU vob timed out";
321                 errorMessage(i18n("Rendering job timed out"));
322                 vobitem->setIcon(KIcon("dialog-close"));
323                 m_status.error_log->append("<a name=\"result\" /><br /><strong>" + i18n("Rendering job timed out"));
324                 m_status.error_log->scrollToAnchor("result");
325                 m_status.error_box->setHidden(false);
326                 m_status.button_start->setEnabled(true);
327                 m_status.button_abort->setEnabled(false);
328                 return;
329             }
330             vobitem->setIcon(KIcon("dialog-ok"));
331         }
332         kDebug() << "/// STARTING SPUMUX";
333
334         // create xml spumux file
335         QListWidgetItem *spuitem =  m_status.job_progress->item(2);
336         m_status.job_progress->setCurrentRow(2);
337         spuitem->setIcon(KIcon("system-run"));
338         qApp->processEvents();
339         QDomDocument doc;
340         QDomElement sub = doc.createElement("subpictures");
341         doc.appendChild(sub);
342         QDomElement stream = doc.createElement("stream");
343         sub.appendChild(stream);
344         QDomElement spu = doc.createElement("spu");
345         stream.appendChild(spu);
346         spu.setAttribute("force", "yes");
347         spu.setAttribute("start", "00:00:00.00");
348         //spu.setAttribute("image", temp1.fileName());
349         spu.setAttribute("select", temp2.fileName());
350         spu.setAttribute("highlight", temp3.fileName());
351         /*spu.setAttribute("autoorder", "rows");*/
352
353         int max = buttons.count() - 1;
354         int i = 0;
355         QMapIterator<QString, QRect> it(buttons);
356         while (it.hasNext()) {
357             it.next();
358             QDomElement but = doc.createElement("button");
359             but.setAttribute("name", 'b' + QString::number(i));
360             if (i < max) but.setAttribute("down", 'b' + QString::number(i + 1));
361             else but.setAttribute("down", "b0");
362             if (i > 0) but.setAttribute("up", 'b' + QString::number(i - 1));
363             else but.setAttribute("up", 'b' + QString::number(max));
364             QRect r = it.value();
365             //int target = it.key();
366             // TODO: solve play all button
367             //if (target == 0) target = 1;
368
369             // We need to make sure that the y coordinate is a multiple of 2, otherwise button may not be displayed
370             buttonsTarget.append(it.key());
371             int y0 = r.y() - 2;
372             if (y0 % 2 == 1) y0++;
373             int y1 = r.bottom() + 2;
374             if (y1 % 2 == 1) y1++;
375             but.setAttribute("x0", QString::number(r.x()));
376             but.setAttribute("y0", QString::number(y0));
377             but.setAttribute("x1", QString::number(r.right()));
378             but.setAttribute("y1", QString::number(y1));
379             spu.appendChild(but);
380             i++;
381         }
382
383         QFile data(m_menuFile.fileName());
384         if (data.open(QFile::WriteOnly)) {
385             data.write(doc.toString().toUtf8());
386         }
387         data.close();
388
389         kDebug() << " SPUMUX DATA: " << doc.toString();
390
391         QStringList args;
392         args.append(m_menuFile.fileName());
393         kDebug() << "SPM ARGS: " << args << temp5.fileName() << m_menuVobFile.fileName();
394
395         QProcess spumux;
396
397 #if QT_VERSION >= 0x040600
398         QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
399         env.insert("VIDEO_FORMAT", m_pageVob->isPal() ? "PAL" : "NTSC");
400         spumux.setProcessEnvironment(env);
401 #else
402         QStringList env = QProcess::systemEnvironment();
403         env << QString("VIDEO_FORMAT=") + QString(m_pageVob->isPal() ? "PAL" : "NTSC");
404         spumux.setEnvironment(env);
405 #endif
406     
407         if (m_pageMenu->menuMovie()) spumux.setStandardInputFile(temp6.fileName());
408         else spumux.setStandardInputFile(temp5.fileName());
409         spumux.setStandardOutputFile(m_menuVobFile.fileName());
410         spumux.start("spumux", args);
411         if (spumux.waitForFinished()) {
412             m_status.error_log->append(spumux.readAllStandardError());
413             if (spumux.exitStatus() == QProcess::CrashExit) {
414                 //TODO: inform user via messagewidget after string freeze
415                 QByteArray result = spumux.readAllStandardError();
416                 spuitem->setIcon(KIcon("dialog-close"));
417                 m_status.error_log->append(result);
418                 m_status.error_box->setHidden(false);
419                 m_status.error_box->setTabBarHidden(false);
420                 m_status.menu_file->setPlainText(m_menuFile.readAll());
421                 m_status.dvd_file->setPlainText(m_authorFile.readAll());
422                 m_status.button_start->setEnabled(true);
423                 kDebug() << "/// RENDERING SPUMUX MENU crashed";
424                 return;
425             }
426         } else {
427             kDebug() << "/// RENDERING SPUMUX MENU timed out";
428             errorMessage(i18n("Rendering job timed out"));
429             spuitem->setIcon(KIcon("dialog-close"));
430             m_status.error_log->append("<a name=\"result\" /><br /><strong>" + i18n("Menu job timed out"));
431             m_status.error_log->scrollToAnchor("result");
432             m_status.error_box->setHidden(false);
433             m_status.error_box->setTabBarHidden(false);
434             m_status.menu_file->setPlainText(m_menuFile.readAll());
435             m_status.dvd_file->setPlainText(m_authorFile.readAll());
436             m_status.button_start->setEnabled(true);
437             return;
438         }
439
440         spuitem->setIcon(KIcon("dialog-ok"));
441         kDebug() << "/// DONE: " << m_menuVobFile.fileName();
442     }
443
444     // create dvdauthor xml
445     QListWidgetItem *authitem =  m_status.job_progress->item(3);
446     m_status.job_progress->setCurrentRow(3);
447     authitem->setIcon(KIcon("system-run"));
448     qApp->processEvents();
449     KIO::NetAccess::mkdir(KUrl(m_status.tmp_folder->url().path(KUrl::AddTrailingSlash) + "DVD"), this);
450
451     QDomDocument dvddoc;
452     QDomElement auth = dvddoc.createElement("dvdauthor");
453     auth.setAttribute("dest", m_status.tmp_folder->url().path(KUrl::AddTrailingSlash) + "DVD");
454     dvddoc.appendChild(auth);
455     QDomElement vmgm = dvddoc.createElement("vmgm");
456     auth.appendChild(vmgm);
457
458     if (m_pageMenu->createMenu() && !m_pageVob->introMovie().isEmpty()) {
459         // intro movie
460         QDomElement menus = dvddoc.createElement("menus");
461         vmgm.appendChild(menus);
462         QDomElement pgc = dvddoc.createElement("pgc");
463         pgc.setAttribute("entry", "title");
464         menus.appendChild(pgc);
465         QDomElement menuvob = dvddoc.createElement("vob");
466         menuvob.setAttribute("file", m_pageVob->introMovie());
467         pgc.appendChild(menuvob);
468         QDomElement post = dvddoc.createElement("post");
469         QDomText call = dvddoc.createTextNode("jump titleset 1 menu;");
470         post.appendChild(call);
471         pgc.appendChild(post);
472     }
473     QDomElement titleset = dvddoc.createElement("titleset");
474     auth.appendChild(titleset);
475
476     if (m_pageMenu->createMenu()) {
477
478         // DVD main menu
479         QDomElement menus = dvddoc.createElement("menus");
480         titleset.appendChild(menus);
481         QDomElement pgc = dvddoc.createElement("pgc");
482         pgc.setAttribute("entry", "root");
483         menus.appendChild(pgc);
484         QDomElement pre = dvddoc.createElement("pre");
485         pgc.appendChild(pre);
486         QDomText nametext = dvddoc.createTextNode("{g1 = 0;}");
487         pre.appendChild(nametext);
488         QDomElement menuvob = dvddoc.createElement("vob");
489         menuvob.setAttribute("file", m_menuVobFile.fileName());
490         pgc.appendChild(menuvob);
491         for (int i = 0; i < buttons.count(); i++) {
492             QDomElement button = dvddoc.createElement("button");
493             button.setAttribute("name", 'b' + QString::number(i));
494             nametext = dvddoc.createTextNode('{' + buttonsTarget.at(i) + ";}");
495             button.appendChild(nametext);
496             pgc.appendChild(button);
497         }
498
499         if (m_pageMenu->loopMovie()) {
500             QDomElement menuloop = dvddoc.createElement("post");
501             nametext = dvddoc.createTextNode("jump titleset 1 menu;");
502             menuloop.appendChild(nametext);
503             pgc.appendChild(menuloop);
504         } else menuvob.setAttribute("pause", "inf");
505
506     }
507
508     QDomElement titles = dvddoc.createElement("titles");
509     titleset.appendChild(titles);
510     QDomElement video = dvddoc.createElement("video");
511     titles.appendChild(video);
512     if (m_pageVob->isPal()) video.setAttribute("format", "pal");
513     else video.setAttribute("format", "ntsc");
514
515     if (m_pageVob->isWide()) video.setAttribute("aspect", "16:9");
516     else video.setAttribute("aspect", "4:3");
517
518     QStringList voburls = m_pageVob->selectedUrls();
519
520     QDomElement pgc2;
521
522
523     for (int i = 0; i < voburls.count(); i++) {
524         if (!voburls.at(i).isEmpty()) {
525             // Add vob entry
526             pgc2 = dvddoc.createElement("pgc");
527             pgc2.setAttribute("pause", 0);
528             titles.appendChild(pgc2);
529             QDomElement vob = dvddoc.createElement("vob");
530             vob.setAttribute("file", voburls.at(i));
531             // Add chapters
532             QStringList chaptersList = m_pageChapters->chapters(i);
533             if (!chaptersList.isEmpty()) vob.setAttribute("chapters", chaptersList.join(","));
534
535             pgc2.appendChild(vob);
536             if (m_pageMenu->createMenu()) {
537                 QDomElement post = dvddoc.createElement("post");
538                 QDomText call;
539                 if (i == voburls.count() - 1) call = dvddoc.createTextNode("{g1 = 0; call menu;}");
540                 else {
541                     call = dvddoc.createTextNode("{if ( g1 eq 999 ) { call menu; } jump title " + QString::number(i + 2).rightJustified(2, '0') + ";}");
542                 }
543                 post.appendChild(call);
544                 pgc2.appendChild(post);
545             }
546         }
547     }
548
549
550     QFile data2(m_authorFile.fileName());
551     if (data2.open(QFile::WriteOnly)) {
552         data2.write(dvddoc.toString().toUtf8());
553     }
554     data2.close();
555     /*kDebug() << "------------------";
556     kDebug() << dvddoc.toString();
557     kDebug() << "------------------";*/
558
559     QStringList args;
560     args << "-x" << m_authorFile.fileName();
561     kDebug() << "// DVDAUTH ARGS: " << args;
562     if (m_dvdauthor) {
563         m_dvdauthor->blockSignals(true);
564         m_dvdauthor->close();
565         delete m_dvdauthor;
566         m_dvdauthor = NULL;
567     }
568     m_creationLog.clear();
569     m_dvdauthor = new QProcess(this);
570     // Set VIDEO_FORMAT variable (required by dvdauthor 0.7)
571 #if QT_VERSION >= 0x040600
572     QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
573     env.insert("VIDEO_FORMAT", m_pageVob->isPal() ? "PAL" : "NTSC"); 
574     m_dvdauthor->setProcessEnvironment(env);
575 #else
576     QStringList env = QProcess::systemEnvironment();
577     env << QString("VIDEO_FORMAT=") + QString(m_pageVob->isPal() ? "PAL" : "NTSC");
578     m_dvdauthor->setEnvironment(env);
579 #endif
580     connect(m_dvdauthor, SIGNAL(finished(int , QProcess::ExitStatus)), this, SLOT(slotRenderFinished(int, QProcess::ExitStatus)));
581     connect(m_dvdauthor, SIGNAL(readyReadStandardOutput()), this, SLOT(slotShowRenderInfo()));
582     m_dvdauthor->setProcessChannelMode(QProcess::MergedChannels);
583     m_dvdauthor->start("dvdauthor", args);
584     m_status.button_abort->setEnabled(true);
585     button(QWizard::FinishButton)->setEnabled(false);
586 }
587
588 void DvdWizard::slotShowRenderInfo()
589 {
590     QString log = QString(m_dvdauthor->readAll());
591     m_status.error_log->append(log);
592     m_status.error_box->setHidden(false);
593 }
594
595 void DvdWizard::errorMessage(const QString &text) {
596 #if KDE_IS_VERSION(4,7,0)
597     m_isoMessage->setText(text);
598     m_isoMessage->setMessageType(KMessageWidget::Error);
599     m_isoMessage->animatedShow();
600 #endif
601 }
602
603 void DvdWizard::infoMessage(const QString &text) {
604 #if KDE_IS_VERSION(4,7,0)
605     m_isoMessage->setText(text);
606     m_isoMessage->setMessageType(KMessageWidget::Positive);
607     m_isoMessage->animatedShow();
608 #endif
609 }
610
611 void DvdWizard::slotRenderFinished(int exitCode, QProcess::ExitStatus status)
612 {
613     QListWidgetItem *authitem =  m_status.job_progress->item(3);
614     if (status == QProcess::CrashExit || exitCode != 0) {
615         errorMessage(i18n("DVDAuthor process crashed"));
616         QString result(m_dvdauthor->readAllStandardError());
617         result.append("<a name=\"result\" /><br /><strong>");
618         result.append(i18n("DVDAuthor process crashed.</strong><br />"));
619         m_status.error_log->append(result);
620         m_status.error_log->scrollToAnchor("result");
621         m_status.error_box->setHidden(false);
622         m_status.error_box->setTabBarHidden(false);
623         m_status.menu_file->setPlainText(m_menuFile.readAll());
624         m_status.dvd_file->setPlainText(m_authorFile.readAll());
625         kDebug() << "DVDAuthor process crashed";
626         authitem->setIcon(KIcon("dialog-close"));
627         m_dvdauthor->close();
628         delete m_dvdauthor;
629         m_dvdauthor = NULL;
630         m_status.button_start->setEnabled(true);
631         m_status.button_abort->setEnabled(false);
632         cleanup();
633         button(QWizard::FinishButton)->setEnabled(true);
634         return;
635     }
636     m_creationLog.append(m_dvdauthor->readAllStandardError());
637     m_dvdauthor->close();
638     delete m_dvdauthor;
639     m_dvdauthor = NULL;
640
641     // Check if DVD structure has the necessary infos
642     if (!QFile::exists(m_status.tmp_folder->url().path() + "/DVD/VIDEO_TS/VIDEO_TS.IFO")) {
643         errorMessage(i18n("DVD structure broken"));
644         m_status.error_log->append(m_creationLog + "<a name=\"result\" /><br /><strong>" + i18n("DVD structure broken"));
645         m_status.error_log->scrollToAnchor("result");
646         m_status.error_box->setHidden(false);
647         m_status.error_box->setTabBarHidden(false);
648         m_status.menu_file->setPlainText(m_menuFile.readAll());
649         m_status.dvd_file->setPlainText(m_authorFile.readAll());
650         kDebug() << "DVDAuthor process crashed";
651         authitem->setIcon(KIcon("dialog-close"));
652         m_status.button_start->setEnabled(true);
653         m_status.button_abort->setEnabled(false);
654         cleanup();
655         button(QWizard::FinishButton)->setEnabled(true);
656         return;
657     }
658     authitem->setIcon(KIcon("dialog-ok"));
659     qApp->processEvents();
660     QStringList args;
661     args << "-dvd-video" << "-v" << "-o" << m_status.iso_image->url().path() << m_status.tmp_folder->url().path(KUrl::AddTrailingSlash) + "DVD";
662
663     if (m_mkiso) {
664         m_mkiso->blockSignals(true);
665         m_mkiso->close();
666         delete m_mkiso;
667         m_mkiso = NULL;
668     }
669     m_mkiso = new QProcess(this);
670     connect(m_mkiso, SIGNAL(finished(int , QProcess::ExitStatus)), this, SLOT(slotIsoFinished(int, QProcess::ExitStatus)));
671     connect(m_mkiso, SIGNAL(readyReadStandardOutput()), this, SLOT(slotShowIsoInfo()));
672     m_mkiso->setProcessChannelMode(QProcess::MergedChannels);
673     QListWidgetItem *isoitem =  m_status.job_progress->item(4);
674     m_status.job_progress->setCurrentRow(4);
675     isoitem->setIcon(KIcon("system-run"));
676     if (!KStandardDirs::findExe("genisoimage").isEmpty()) m_mkiso->start("genisoimage", args);
677     else m_mkiso->start("mkisofs", args);
678
679 }
680
681 void DvdWizard::slotShowIsoInfo()
682 {
683     QString log = QString(m_mkiso->readAll());
684     m_status.error_log->append(log);
685     m_status.error_box->setHidden(false);
686 }
687
688 void DvdWizard::slotIsoFinished(int exitCode, QProcess::ExitStatus status)
689 {
690     button(QWizard::FinishButton)->setEnabled(true);
691     QListWidgetItem *isoitem =  m_status.job_progress->item(4);
692     if (status == QProcess::CrashExit || exitCode != 0) {
693         errorMessage(i18n("ISO creation process crashed."));
694         QString result(m_mkiso->readAllStandardError());
695         result.append("<a name=\"result\" /><br /><strong>");
696         result.append(i18n("ISO creation process crashed."));
697         m_status.error_log->append(result);
698         m_status.error_log->scrollToAnchor("result");
699         m_status.error_box->setHidden(false);
700         m_status.error_box->setTabBarHidden(false);
701         m_status.menu_file->setPlainText(m_menuFile.readAll());
702         m_status.dvd_file->setPlainText(m_authorFile.readAll());
703         m_mkiso->close();
704         delete m_mkiso;
705         m_mkiso = NULL;
706         cleanup();
707         kDebug() << "Iso process crashed";
708         isoitem->setIcon(KIcon("dialog-close"));
709         m_status.button_start->setEnabled(true);
710         m_status.button_abort->setEnabled(false);
711         return;
712     }
713
714     m_creationLog.append(m_mkiso->readAllStandardError());
715     delete m_mkiso;
716     m_mkiso = NULL;
717     m_status.button_start->setEnabled(true);
718     m_status.button_abort->setEnabled(false);
719
720     // Check if DVD iso is ok
721     QFile iso(m_status.iso_image->url().path());
722     if (!iso.exists() || iso.size() == 0) {
723         if (iso.exists()) {
724             KIO::NetAccess::del(m_status.iso_image->url(), this);
725         }
726         errorMessage(i18n("DVD ISO is broken"));
727         m_status.error_log->append(m_creationLog + "<br /><a name=\"result\" /><strong>" + i18n("DVD ISO is broken") + "</strong>");
728         m_status.error_log->scrollToAnchor("result");
729         m_status.error_box->setHidden(false);
730         m_status.error_box->setTabBarHidden(false);
731         m_status.menu_file->setPlainText(m_menuFile.readAll());
732         m_status.dvd_file->setPlainText(m_authorFile.readAll());
733         isoitem->setIcon(KIcon("dialog-close"));
734         cleanup();
735         return;
736     }
737
738     isoitem->setIcon(KIcon("dialog-ok"));
739     kDebug() << "ISO IMAGE " << m_status.iso_image->url().path() << " Successfully created";
740     cleanup();
741     kDebug() << m_creationLog;
742     infoMessage(i18n("DVD ISO image %1 successfully created.", m_status.iso_image->url().path()));
743
744     m_status.error_log->append("<a name=\"result\" /><strong>" + i18n("DVD ISO image %1 successfully created.", m_status.iso_image->url().path()) + "</strong>");
745     m_status.error_log->scrollToAnchor("result");
746     m_status.button_preview->setEnabled(true);
747     m_status.button_burn->setEnabled(true);
748     m_status.error_box->setHidden(false);
749     //KMessageBox::information(this, i18n("DVD ISO image %1 successfully created.", m_status.iso_image->url().path()));
750
751 }
752
753
754 void DvdWizard::cleanup()
755 {
756     KIO::NetAccess::del(KUrl(m_status.tmp_folder->url().path(KUrl::AddTrailingSlash) + "DVD"), this);
757 }
758
759
760 void DvdWizard::slotPreview()
761 {
762     QStringList programNames;
763     programNames << "xine" << "vlc";
764     QString exec;
765     foreach(const QString &prog, programNames) {
766         exec = KStandardDirs::findExe(prog);
767         if (!exec.isEmpty()) {
768             break;
769         }
770     }
771     if (exec.isEmpty()) {
772         KMessageBox::sorry(this, i18n("Previewing requires one of these applications (%1)", programNames.join(",")));
773     }
774     else QProcess::startDetached(exec, QStringList() << "dvd://" + m_status.iso_image->url().path());
775 }
776
777 void DvdWizard::slotBurn()
778 {
779     QAction *action = qobject_cast<QAction *>(sender());
780     QString exec = action->data().toString();
781     QStringList args;
782     if (exec.endsWith("k3b")) args << "--image" << m_status.iso_image->url().path();
783     else args << "--image=" + m_status.iso_image->url().path();
784     QProcess::startDetached(exec, args);
785 }
786
787
788 void DvdWizard::slotGenerate()
789 {
790     // clear job icons
791     if ((m_dvdauthor && m_dvdauthor->state() != QProcess::NotRunning) || (m_mkiso && m_mkiso->state() != QProcess::NotRunning)) return;
792     for (int i = 0; i < m_status.job_progress->count(); i++)
793         m_status.job_progress->item(i)->setIcon(KIcon());
794     QString warnMessage;
795     if (KIO::NetAccess::exists(KUrl(m_status.tmp_folder->url().path(KUrl::AddTrailingSlash) + "DVD"), KIO::NetAccess::SourceSide, this))
796         warnMessage.append(i18n("Folder %1 already exists. Overwrite?\n", m_status.tmp_folder->url().path(KUrl::AddTrailingSlash) + "DVD"));
797     if (KIO::NetAccess::exists(KUrl(m_status.iso_image->url().path()), KIO::NetAccess::SourceSide, this))
798         warnMessage.append(i18n("Image file %1 already exists. Overwrite?", m_status.iso_image->url().path()));
799
800     if (warnMessage.isEmpty() || KMessageBox::questionYesNo(this, warnMessage) == KMessageBox::Yes) {
801         KIO::NetAccess::del(KUrl(m_status.tmp_folder->url().path(KUrl::AddTrailingSlash) + "DVD"), this);
802         QTimer::singleShot(300, this, SLOT(generateDvd()));
803         m_status.button_preview->setEnabled(false);
804         m_status.button_burn->setEnabled(false);
805         m_status.job_progress->setEnabled(true);
806         m_status.button_start->setEnabled(false);
807     }
808 }
809
810 void DvdWizard::slotAbort()
811 {
812     // clear job icons
813     if (m_dvdauthor && m_dvdauthor->state() != QProcess::NotRunning) m_dvdauthor->terminate();
814     else if (m_mkiso && m_mkiso->state() != QProcess::NotRunning) m_mkiso->terminate();
815 }
816
817 void DvdWizard::slotSave()
818 {
819     KUrl url = KFileDialog::getSaveUrl(KUrl("kfiledialog:///projectfolder"), "*.kdvd", this, i18n("Save DVD Project"));
820     if (url.isEmpty()) return;
821
822     if (currentId() == 0) m_pageChapters->setVobFiles(m_pageVob->isPal(), m_pageVob->isWide(), m_pageVob->selectedUrls(), m_pageVob->durations(), m_pageVob->chapters());
823
824     QDomDocument doc;
825     QDomElement dvdproject = doc.createElement("dvdproject");
826     QString profile;
827     if (m_pageVob->isPal()) profile = "dv_pal";
828     else profile = "dv_ntsc";
829     if (m_pageVob->isWide()) profile.append("_wide");
830     dvdproject.setAttribute("profile", profile);
831
832     dvdproject.setAttribute("tmp_folder", m_status.tmp_folder->url().path());
833     dvdproject.setAttribute("iso_image", m_status.iso_image->url().path());
834
835     dvdproject.setAttribute("intro_movie", m_pageVob->introMovie());
836
837     doc.appendChild(dvdproject);
838     QDomElement menu = m_pageMenu->toXml();
839     if (!menu.isNull()) dvdproject.appendChild(doc.importNode(menu, true));
840     QDomElement chaps = m_pageChapters->toXml();
841     if (!chaps.isNull()) dvdproject.appendChild(doc.importNode(chaps, true));
842
843     QFile file(url.path());
844     if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
845         kWarning() << "//////  ERROR writing to file: " << url.path();
846         KMessageBox::error(this, i18n("Cannot write to file %1", url.path()));
847         return;
848     }
849
850     file.write(doc.toString().toUtf8());
851     if (file.error() != QFile::NoError) {
852         KMessageBox::error(this, i18n("Cannot write to file %1", url.path()));
853     }
854     file.close();
855 }
856
857
858 void DvdWizard::slotLoad()
859 {
860     KUrl url = KFileDialog::getOpenUrl(KUrl("kfiledialog:///projectfolder"), "*.kdvd");
861     if (url.isEmpty()) return;
862     QDomDocument doc;
863     QFile file(url.path());
864     doc.setContent(&file, false);
865     file.close();
866     QDomElement dvdproject = doc.documentElement();
867     if (dvdproject.tagName() != "dvdproject") {
868         KMessageBox::error(this, i18n("File %1 is not a Kdenlive project file.", url.path()));
869         return;
870     }
871
872     QString profile = dvdproject.attribute("profile");
873     m_pageVob->setProfile(profile);
874
875     m_status.tmp_folder->setUrl(KUrl(dvdproject.attribute("tmp_folder")));
876     m_status.iso_image->setUrl(KUrl(dvdproject.attribute("iso_image")));
877     m_pageVob->setIntroMovie(dvdproject.attribute("intro_movie"));
878
879     QDomNodeList vobs = doc.elementsByTagName("vob");
880     m_pageVob->clear();
881     for (int i = 0; i < vobs.count(); i++) {
882         QDomElement e = vobs.at(i).toElement();
883         m_pageVob->slotAddVobFile(KUrl(e.attribute("file")), e.attribute("chapters"));
884     }
885     m_pageMenu->loadXml(dvdproject.firstChildElement("menu"));
886 }