summaryrefslogtreecommitdiffstats
path: root/libk3b/projects
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-02-03 02:15:56 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-02-03 02:15:56 +0000
commit50b48aec6ddd451a6d1709c0942477b503457663 (patch)
treea9ece53ec06fd0a2819de7a2a6de997193566626 /libk3b/projects
downloadk3b-50b48aec6ddd451a6d1709c0942477b503457663.tar.gz
k3b-50b48aec6ddd451a6d1709c0942477b503457663.zip
Added abandoned KDE3 version of K3B
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/k3b@1084400 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'libk3b/projects')
-rw-r--r--libk3b/projects/Makefile.am32
-rw-r--r--libk3b/projects/audiocd/Makefile.am35
-rw-r--r--libk3b/projects/audiocd/k3baudiocdtrackdrag.cpp109
-rw-r--r--libk3b/projects/audiocd/k3baudiocdtrackdrag.h50
-rw-r--r--libk3b/projects/audiocd/k3baudiocdtracksource.cpp262
-rw-r--r--libk3b/projects/audiocd/k3baudiocdtracksource.h99
-rw-r--r--libk3b/projects/audiocd/k3baudiodatasource.cpp210
-rw-r--r--libk3b/projects/audiocd/k3baudiodatasource.h168
-rw-r--r--libk3b/projects/audiocd/k3baudiodatasourceiterator.cpp71
-rw-r--r--libk3b/projects/audiocd/k3baudiodatasourceiterator.h61
-rw-r--r--libk3b/projects/audiocd/k3baudiodoc.cpp1127
-rw-r--r--libk3b/projects/audiocd/k3baudiodoc.h263
-rw-r--r--libk3b/projects/audiocd/k3baudiofile.cpp112
-rw-r--r--libk3b/projects/audiocd/k3baudiofile.h85
-rw-r--r--libk3b/projects/audiocd/k3baudioimager.cpp203
-rw-r--r--libk3b/projects/audiocd/k3baudioimager.h59
-rw-r--r--libk3b/projects/audiocd/k3baudiojob.cpp864
-rw-r--r--libk3b/projects/audiocd/k3baudiojob.h107
-rw-r--r--libk3b/projects/audiocd/k3baudiojobtempdata.cpp132
-rw-r--r--libk3b/projects/audiocd/k3baudiojobtempdata.h64
-rw-r--r--libk3b/projects/audiocd/k3baudiomaxspeedjob.cpp224
-rw-r--r--libk3b/projects/audiocd/k3baudiomaxspeedjob.h43
-rw-r--r--libk3b/projects/audiocd/k3baudionormalizejob.cpp205
-rw-r--r--libk3b/projects/audiocd/k3baudionormalizejob.h63
-rw-r--r--libk3b/projects/audiocd/k3baudiotrack.cpp628
-rw-r--r--libk3b/projects/audiocd/k3baudiotrack.h214
-rw-r--r--libk3b/projects/audiocd/k3baudiozerodata.cpp115
-rw-r--r--libk3b/projects/audiocd/k3baudiozerodata.h55
-rw-r--r--libk3b/projects/datacd/Makefile.am35
-rw-r--r--libk3b/projects/datacd/k3bbootitem.cpp58
-rw-r--r--libk3b/projects/datacd/k3bbootitem.h66
-rw-r--r--libk3b/projects/datacd/k3bdatadoc.cpp1376
-rw-r--r--libk3b/projects/datacd/k3bdatadoc.h297
-rw-r--r--libk3b/projects/datacd/k3bdataitem.cpp264
-rw-r--r--libk3b/projects/datacd/k3bdataitem.h225
-rw-r--r--libk3b/projects/datacd/k3bdatajob.cpp972
-rw-r--r--libk3b/projects/datacd/k3bdatajob.h111
-rw-r--r--libk3b/projects/datacd/k3bdatapreparationjob.cpp283
-rw-r--r--libk3b/projects/datacd/k3bdatapreparationjob.h51
-rw-r--r--libk3b/projects/datacd/k3bdiritem.cpp406
-rw-r--r--libk3b/projects/datacd/k3bdiritem.h155
-rw-r--r--libk3b/projects/datacd/k3bfilecompilationsizehandler.cpp228
-rw-r--r--libk3b/projects/datacd/k3bfilecompilationsizehandler.h73
-rw-r--r--libk3b/projects/datacd/k3bfileitem.cpp300
-rw-r--r--libk3b/projects/datacd/k3bfileitem.h124
-rw-r--r--libk3b/projects/datacd/k3bisoimager.cpp1187
-rw-r--r--libk3b/projects/datacd/k3bisoimager.h188
-rw-r--r--libk3b/projects/datacd/k3bisooptions.cpp216
-rw-r--r--libk3b/projects/datacd/k3bisooptions.h183
-rw-r--r--libk3b/projects/datacd/k3bmkisofshandler.cpp150
-rw-r--r--libk3b/projects/datacd/k3bmkisofshandler.h74
-rw-r--r--libk3b/projects/datacd/k3bmsinfofetcher.cpp243
-rw-r--r--libk3b/projects/datacd/k3bmsinfofetcher.h64
-rw-r--r--libk3b/projects/datacd/k3bsessionimportitem.cpp59
-rw-r--r--libk3b/projects/datacd/k3bsessionimportitem.h63
-rw-r--r--libk3b/projects/datacd/k3bspecialdataitem.h76
-rw-r--r--libk3b/projects/datadvd/Makefile.am21
-rw-r--r--libk3b/projects/datadvd/k3bdvdbooktypejob.cpp350
-rw-r--r--libk3b/projects/datadvd/k3bdvdbooktypejob.h99
-rw-r--r--libk3b/projects/datadvd/k3bdvddoc.cpp39
-rw-r--r--libk3b/projects/datadvd/k3bdvddoc.h37
-rw-r--r--libk3b/projects/datadvd/k3bdvdjob.cpp344
-rw-r--r--libk3b/projects/datadvd/k3bdvdjob.h57
-rw-r--r--libk3b/projects/datadvd/k3bdvdview.cpp48
-rw-r--r--libk3b/projects/datadvd/k3bdvdview.h40
-rw-r--r--libk3b/projects/k3babstractwriter.cpp96
-rw-r--r--libk3b/projects/k3babstractwriter.h92
-rw-r--r--libk3b/projects/k3bcdrdaowriter.cpp1101
-rw-r--r--libk3b/projects/k3bcdrdaowriter.h157
-rw-r--r--libk3b/projects/k3bcdrecordwriter.cpp810
-rw-r--r--libk3b/projects/k3bcdrecordwriter.h123
-rw-r--r--libk3b/projects/k3bcuefileparser.cpp461
-rw-r--r--libk3b/projects/k3bcuefileparser.h57
-rw-r--r--libk3b/projects/k3bdoc.cpp221
-rw-r--r--libk3b/projects/k3bdoc.h229
-rw-r--r--libk3b/projects/k3bdvdrecordwriter.cpp119
-rw-r--r--libk3b/projects/k3bdvdrecordwriter.h40
-rw-r--r--libk3b/projects/k3bgrowisofshandler.cpp318
-rw-r--r--libk3b/projects/k3bgrowisofshandler.h87
-rw-r--r--libk3b/projects/k3bgrowisofswriter.cpp630
-rw-r--r--libk3b/projects/k3bgrowisofswriter.h106
-rw-r--r--libk3b/projects/k3bimagefilereader.cpp88
-rw-r--r--libk3b/projects/k3bimagefilereader.h55
-rw-r--r--libk3b/projects/k3binffilewriter.cpp186
-rw-r--r--libk3b/projects/k3binffilewriter.h119
-rw-r--r--libk3b/projects/k3bpipebuffer.cpp281
-rw-r--r--libk3b/projects/k3bpipebuffer.h59
-rw-r--r--libk3b/projects/k3btocfilewriter.cpp356
-rw-r--r--libk3b/projects/k3btocfilewriter.h62
-rw-r--r--libk3b/projects/mixedcd/Makefile.am23
-rw-r--r--libk3b/projects/mixedcd/k3bmixeddoc.cpp249
-rw-r--r--libk3b/projects/mixedcd/k3bmixeddoc.h95
-rw-r--r--libk3b/projects/mixedcd/k3bmixedjob.cpp1339
-rw-r--r--libk3b/projects/mixedcd/k3bmixedjob.h144
-rw-r--r--libk3b/projects/movixcd/Makefile.am23
-rw-r--r--libk3b/projects/movixcd/k3bmovixdoc.cpp445
-rw-r--r--libk3b/projects/movixcd/k3bmovixdoc.h125
-rw-r--r--libk3b/projects/movixcd/k3bmovixdocpreparer.cpp490
-rw-r--r--libk3b/projects/movixcd/k3bmovixdocpreparer.h67
-rw-r--r--libk3b/projects/movixcd/k3bmovixfileitem.cpp68
-rw-r--r--libk3b/projects/movixcd/k3bmovixfileitem.h52
-rw-r--r--libk3b/projects/movixcd/k3bmovixjob.cpp132
-rw-r--r--libk3b/projects/movixcd/k3bmovixjob.h60
-rw-r--r--libk3b/projects/movixcd/k3bmovixprogram.cpp339
-rw-r--r--libk3b/projects/movixcd/k3bmovixprogram.h103
-rw-r--r--libk3b/projects/movixdvd/Makefile.am21
-rw-r--r--libk3b/projects/movixdvd/k3bmovixdvddoc.cpp36
-rw-r--r--libk3b/projects/movixdvd/k3bmovixdvddoc.h40
-rw-r--r--libk3b/projects/movixdvd/k3bmovixdvdjob.cpp131
-rw-r--r--libk3b/projects/movixdvd/k3bmovixdvdjob.h60
-rw-r--r--libk3b/projects/videocd/Makefile.am20
-rw-r--r--libk3b/projects/videocd/cdi/Makefile.am5
-rw-r--r--libk3b/projects/videocd/cdi/cdi_imag.rtfbin0 -> 1314877 bytes
-rw-r--r--libk3b/projects/videocd/cdi/cdi_text.fntbin0 -> 13616 bytes
-rw-r--r--libk3b/projects/videocd/cdi/cdi_vcd.appbin0 -> 102400 bytes
-rw-r--r--libk3b/projects/videocd/cdi/cdi_vcd.cfg12
-rw-r--r--libk3b/projects/videocd/cdi/icdia.htm12
-rw-r--r--libk3b/projects/videocd/cdi/vcd_on_cdi_41.pdfbin0 -> 126675 bytes
-rw-r--r--libk3b/projects/videocd/extra/Makefile.am5
-rw-r--r--libk3b/projects/videocd/extra/k3bphotosvcd.mpgbin0 -> 824970 bytes
-rw-r--r--libk3b/projects/videocd/extra/k3bphotovcd.mpgbin0 -> 1731138 bytes
-rw-r--r--libk3b/projects/videocd/k3bvcddoc.cpp894
-rw-r--r--libk3b/projects/videocd/k3bvcddoc.h192
-rw-r--r--libk3b/projects/videocd/k3bvcdjob.cpp567
-rw-r--r--libk3b/projects/videocd/k3bvcdjob.h115
-rw-r--r--libk3b/projects/videocd/k3bvcdoptions.cpp146
-rw-r--r--libk3b/projects/videocd/k3bvcdoptions.h377
-rw-r--r--libk3b/projects/videocd/k3bvcdtrack.cpp456
-rw-r--r--libk3b/projects/videocd/k3bvcdtrack.h198
-rw-r--r--libk3b/projects/videocd/k3bvcdxmlview.cpp440
-rw-r--r--libk3b/projects/videocd/k3bvcdxmlview.h59
-rw-r--r--libk3b/projects/videocd/mpeginfo/Makefile.am5
-rw-r--r--libk3b/projects/videocd/mpeginfo/k3bmpeginfo.cpp844
-rw-r--r--libk3b/projects/videocd/mpeginfo/k3bmpeginfo.h178
-rw-r--r--libk3b/projects/videodvd/Makefile.am20
-rw-r--r--libk3b/projects/videodvd/k3bvideodvddoc.cpp71
-rw-r--r--libk3b/projects/videodvd/k3bvideodvddoc.h46
-rw-r--r--libk3b/projects/videodvd/k3bvideodvdimager.cpp221
-rw-r--r--libk3b/projects/videodvd/k3bvideodvdimager.h61
-rw-r--r--libk3b/projects/videodvd/k3bvideodvdjob.cpp101
-rw-r--r--libk3b/projects/videodvd/k3bvideodvdjob.h46
141 files changed, 29208 insertions, 0 deletions
diff --git a/libk3b/projects/Makefile.am b/libk3b/projects/Makefile.am
new file mode 100644
index 0000000..1a07372
--- /dev/null
+++ b/libk3b/projects/Makefile.am
@@ -0,0 +1,32 @@
+AM_CPPFLAGS= -I$(srcdir)/../../src -I$(srcdir)/../core -I$(srcdir)/../plugin -I$(srcdir)/../../libk3bdevice -I$(srcdir)/../tools -I$(srcdir)/datacd -I$(srcdir)/audiocd -I$(srcdir)/mixedcd -I$(srcdir)/movixcd -I$(srcdir)/movixdvd -I$(srcdir)/datadvd -I$(srcdir)/videocd -I$(srcdir)/videodvd $(all_includes)
+
+noinst_LTLIBRARIES = libk3bproject.la
+
+libk3bproject_la_LIBADD = ./datacd/libdata.la ./datadvd/libdvd.la ./mixedcd/libmixed.la ./videocd/libvcd.la ./videodvd/libvideodvd.la ./movixcd/libmovix.la ./movixdvd/libmovixdvd.la ./audiocd/libaudio.la
+
+libk3bproject_la_LDFLAGS = $(all_libraries)
+
+libk3bproject_la_SOURCES = k3babstractwriter.cpp \
+ k3bgrowisofswriter.cpp \
+ k3bgrowisofshandler.cpp \
+ k3bdoc.cpp \
+ k3bcdrdaowriter.cpp \
+ k3bcdrecordwriter.cpp \
+ k3binffilewriter.cpp \
+ k3btocfilewriter.cpp \
+ k3bimagefilereader.cpp \
+ k3bcuefileparser.cpp \
+ k3bpipebuffer.cpp
+
+include_HEADERS = k3bdoc.h \
+ k3bgrowisofswriter.h \
+ k3bcdrdaowriter.h \
+ k3bcdrecordwriter.h \
+ k3binffilewriter.h \
+ k3btocfilewriter.h \
+ k3bcuefileparser.h \
+ k3bimagefilereader.h
+
+SUBDIRS = datacd audiocd mixedcd movixcd videocd datadvd movixdvd videodvd
+
+METASOURCES = AUTO
diff --git a/libk3b/projects/audiocd/Makefile.am b/libk3b/projects/audiocd/Makefile.am
new file mode 100644
index 0000000..60942cc
--- /dev/null
+++ b/libk3b/projects/audiocd/Makefile.am
@@ -0,0 +1,35 @@
+AM_CPPFLAGS= -I$(srcdir)/../../../src \
+ -I$(srcdir)/.. \
+ -I$(srcdir)/../../core \
+ -I$(srcdir)/../../plugin \
+ -I$(srcdir)/../../cddb \
+ -I$(srcdir)/../../../libk3bdevice \
+ -I$(srcdir)/../../tools $(all_includes)
+
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libaudio.la
+
+libaudio_la_SOURCES = k3baudiojob.cpp \
+ k3baudiotrack.cpp \
+ k3baudiodoc.cpp \
+ k3baudiofile.cpp \
+ k3baudiozerodata.cpp \
+ k3baudiodatasource.cpp \
+ k3baudionormalizejob.cpp \
+ k3baudiojobtempdata.cpp \
+ k3baudioimager.cpp \
+ k3baudiomaxspeedjob.cpp \
+ k3baudiocdtracksource.cpp \
+ k3baudiocdtrackdrag.cpp \
+ k3baudiodatasourceiterator.cpp
+
+include_HEADERS = k3baudiodoc.h \
+ k3baudiojob.h \
+ k3baudiocdtrackdrag.h \
+ k3baudiotrack.h \
+ k3baudiodatasource.h \
+ k3baudiofile.h \
+ k3baudiozerodata.h \
+ k3baudiocdtracksource.h \
+ k3baudiodatasourceiterator.h
diff --git a/libk3b/projects/audiocd/k3baudiocdtrackdrag.cpp b/libk3b/projects/audiocd/k3baudiocdtrackdrag.cpp
new file mode 100644
index 0000000..8429f25
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiocdtrackdrag.cpp
@@ -0,0 +1,109 @@
+/*
+ *
+ * $Id: k3baudiocdtrackdrag.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2005 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3baudiocdtrackdrag.h"
+
+#include <k3bdevice.h>
+#include <k3bdevicemanager.h>
+#include <k3btoc.h>
+#include <k3btrack.h>
+#include <k3bcore.h>
+
+#include <qdatastream.h>
+#include <qcstring.h>
+
+
+// FIXME: multiple tracks
+K3bAudioCdTrackDrag::K3bAudioCdTrackDrag( const K3bDevice::Toc& toc, const QValueList<int>& cdTrackNumbers,
+ const K3bCddbResultEntry& cddb,
+ K3bDevice::Device* lastDev, QWidget* dragSource, const char* name )
+ : QStoredDrag( "k3b/audio_track_drag", dragSource, name ),
+ m_toc(toc),
+ m_cdTrackNumbers(cdTrackNumbers),
+ m_cddb(cddb),
+ m_device(lastDev)
+{
+ QByteArray data;
+ QDataStream s( data, IO_WriteOnly );
+ s << (unsigned int)toc.count();
+ for( K3bDevice::Toc::const_iterator it = toc.begin(); it != toc.end(); ++it ) {
+ const K3bDevice::Track& track = *it;
+ s << track.firstSector().lba() << track.lastSector().lba();
+ }
+ QTextStream t( s.device() );
+ t << cddb.cdArtist << endl
+ << cddb.cdTitle << endl;
+ for( unsigned int i = 0; i < toc.count(); ++i ) {
+ t << cddb.artists[i] << endl
+ << cddb.titles[i] << endl;
+ }
+
+ s << (unsigned int)cdTrackNumbers.count();
+
+ for( QValueList<int>::const_iterator it = cdTrackNumbers.begin();
+ it != cdTrackNumbers.end(); ++it )
+ s << *it;
+
+ if( lastDev )
+ t << lastDev->blockDeviceName() << endl;
+ else
+ t << endl;
+
+ // TODO: the rest
+ setEncodedData( data );
+}
+
+
+bool K3bAudioCdTrackDrag::decode( const QMimeSource* e,
+ K3bDevice::Toc& toc, QValueList<int>& trackNumbers,
+ K3bCddbResultEntry& cddb, K3bDevice::Device** dev )
+{
+ QByteArray data = e->encodedData( "k3b/audio_track_drag" );
+
+ QDataStream s( data, IO_ReadOnly );
+
+ unsigned int trackCnt;
+ s >> trackCnt;
+ for( unsigned int i = 0; i < trackCnt; ++i ) {
+ int fs, ls;
+ s >> fs;
+ s >> ls;
+ toc.append( K3bDevice::Track( fs, ls, K3bDevice::Track::AUDIO ) );
+ }
+
+ QTextStream t( s.device() );
+ cddb.artists.clear();
+ cddb.titles.clear();
+ cddb.cdArtist = t.readLine();
+ cddb.cdTitle = t.readLine();
+ for( unsigned int i = 0; i < trackCnt; ++i ) {
+ cddb.artists.append( t.readLine() );
+ cddb.titles.append( t.readLine() );
+ }
+
+ s >> trackCnt;
+ trackNumbers.clear();
+ for( unsigned int i = 0; i < trackCnt; ++i ) {
+ int trackNumber = 0;
+ s >> trackNumber;
+ trackNumbers.append( trackNumber );
+ }
+
+ QString devName = t.readLine();
+ if( dev && !devName.isEmpty() )
+ *dev = k3bcore->deviceManager()->findDevice( devName );
+
+ return true;
+}
diff --git a/libk3b/projects/audiocd/k3baudiocdtrackdrag.h b/libk3b/projects/audiocd/k3baudiocdtrackdrag.h
new file mode 100644
index 0000000..3148466
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiocdtrackdrag.h
@@ -0,0 +1,50 @@
+/*
+ *
+ * $Id: k3baudiocdtrackdrag.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2005 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_AUDIO_CDTRACK_DRAG_H_
+#define _K3B_AUDIO_CDTRACK_DRAG_H_
+
+#include <qdragobject.h>
+#include <qcstring.h>
+#include <qvaluelist.h>
+
+#include <k3btoc.h>
+#include <k3bcddbresult.h>
+#include <k3bdevice.h>
+#include "k3b_export.h"
+
+class LIBK3B_EXPORT K3bAudioCdTrackDrag : public QStoredDrag
+{
+ public:
+ K3bAudioCdTrackDrag( const K3bDevice::Toc& toc, const QValueList<int>& cdTrackNumbers, const K3bCddbResultEntry& cddb,
+ K3bDevice::Device* lastDev = 0, QWidget* dragSource = 0, const char* name = 0 );
+
+ const K3bDevice::Toc& toc() const { return m_toc; }
+ const QValueList<int>& cdTrackNumbers() const { return m_cdTrackNumbers; }
+ const K3bCddbResultEntry& cddbEntry() const { return m_cddb; }
+
+ bool provides( const char* mimetype ) const { return !qstrcmp( mimetype, "k3b/audio_track_drag" ); }
+
+ static bool canDecode( const QMimeSource* s ) { return s->provides( "k3b/audio_track_drag" ); }
+ static bool decode( const QMimeSource* s, K3bDevice::Toc&, QValueList<int>& trackNumbers, K3bCddbResultEntry&, K3bDevice::Device** dev = 0 );
+
+ private:
+ K3bDevice::Toc m_toc;
+ QValueList<int> m_cdTrackNumbers;
+ K3bCddbResultEntry m_cddb;
+ K3bDevice::Device* m_device;
+};
+
+#endif
diff --git a/libk3b/projects/audiocd/k3baudiocdtracksource.cpp b/libk3b/projects/audiocd/k3baudiocdtracksource.cpp
new file mode 100644
index 0000000..b61e865
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiocdtracksource.cpp
@@ -0,0 +1,262 @@
+/*
+ *
+ * $Id: k3baudiocdtracksource.cpp 676194 2007-06-16 08:59:19Z trueg $
+ * Copyright (C) 2005 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3baudiocdtracksource.h"
+#include "k3baudiotrack.h"
+#include "k3baudiodoc.h"
+
+#include <k3bthreadwidget.h>
+#include <k3btoc.h>
+#include <k3bdevice.h>
+#include <k3bdevicemanager.h>
+#include <k3bcdparanoialib.h>
+#include <k3bdeviceselectiondialog.h>
+#include <k3bcore.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+
+
+K3bAudioCdTrackSource::K3bAudioCdTrackSource( const K3bDevice::Toc& toc, int cdTrackNumber,
+ const K3bCddbResultEntry& cddb, K3bDevice::Device* dev )
+ : K3bAudioDataSource(),
+ m_discId( toc.discId() ),
+ m_length( toc[cdTrackNumber-1].length() ),
+ m_toc( toc ),
+ m_cdTrackNumber( cdTrackNumber ),
+ m_cddbEntry( cddb ),
+ m_lastUsedDevice( dev ),
+ m_cdParanoiaLib( 0 ),
+ m_initialized( false )
+{
+}
+
+
+K3bAudioCdTrackSource::K3bAudioCdTrackSource( unsigned int discid, const K3b::Msf& length, int cdTrackNumber,
+ const QString& artist, const QString& title,
+ const QString& cdArtist, const QString& cdTitle )
+ : K3bAudioDataSource(),
+ m_discId( discid ),
+ m_length( length ),
+ m_cdTrackNumber( cdTrackNumber ),
+ m_lastUsedDevice( 0 ),
+ m_cdParanoiaLib( 0 ),
+ m_initialized( false )
+{
+ for( int i = 1; i < cdTrackNumber; ++i ) {
+ m_cddbEntry.titles.append( QString::null );
+ m_cddbEntry.artists.append( QString::null );
+ }
+ m_cddbEntry.titles.append( title );
+ m_cddbEntry.artists.append( artist );
+ m_cddbEntry.cdTitle = cdTitle;
+ m_cddbEntry.cdArtist = cdArtist;
+}
+
+
+K3bAudioCdTrackSource::K3bAudioCdTrackSource( const K3bAudioCdTrackSource& source )
+ : K3bAudioDataSource( source ),
+ m_discId( source.m_discId ),
+ m_toc( source.m_toc ),
+ m_cdTrackNumber( source.m_cdTrackNumber ),
+ m_cddbEntry( source.m_cddbEntry ),
+ m_lastUsedDevice( source.m_lastUsedDevice ),
+ m_cdParanoiaLib( 0 ),
+ m_initialized( false )
+{
+}
+
+
+K3bAudioCdTrackSource::~K3bAudioCdTrackSource()
+{
+ closeParanoia();
+ delete m_cdParanoiaLib;
+}
+
+
+bool K3bAudioCdTrackSource::initParanoia()
+{
+ if( !m_initialized ) {
+ if( !m_cdParanoiaLib )
+ m_cdParanoiaLib = K3bCdparanoiaLib::create();
+
+ if( m_cdParanoiaLib ) {
+ m_lastUsedDevice = searchForAudioCD();
+
+ // ask here for the cd since searchForAudioCD() may also be called from outside
+ if( !m_lastUsedDevice ) {
+ // could not find the CD, so ask for it
+ QString s = i18n("Please insert Audio CD %1%2")
+ .arg(m_discId, 0, 16)
+ .arg(m_cddbEntry.cdTitle.isEmpty() || m_cddbEntry.cdArtist.isEmpty()
+ ? QString::null
+ : " (" + m_cddbEntry.cdArtist + " - " + m_cddbEntry.cdTitle + ")");
+
+ while( K3bDevice::Device* dev = K3bThreadWidget::selectDevice( track()->doc()->view(), s ) ) {
+ if( searchForAudioCD( dev ) ) {
+ m_lastUsedDevice = dev;
+ break;
+ }
+ }
+ }
+
+ // user canceled
+ if( !m_lastUsedDevice )
+ return false;
+
+ k3bcore->blockDevice( m_lastUsedDevice );
+
+ if( m_toc.isEmpty() )
+ m_toc = m_lastUsedDevice->readToc();
+
+ if( !m_cdParanoiaLib->initParanoia( m_lastUsedDevice, m_toc ) ) {
+ k3bcore->unblockDevice( m_lastUsedDevice );
+ return false;
+ }
+
+ if( doc() ) {
+ m_cdParanoiaLib->setParanoiaMode( doc()->audioRippingParanoiaMode() );
+ m_cdParanoiaLib->setNeverSkip( !doc()->audioRippingIgnoreReadErrors() );
+ m_cdParanoiaLib->setMaxRetries( doc()->audioRippingRetries() );
+ }
+
+ m_cdParanoiaLib->initReading( m_toc[m_cdTrackNumber-1].firstSector().lba() + startOffset().lba() + m_position.lba(),
+ m_toc[m_cdTrackNumber-1].firstSector().lba() + lastSector().lba() );
+
+ // we only block during the initialization because we cannot determine the end of the reading process :(
+ k3bcore->unblockDevice( m_lastUsedDevice );
+
+ m_initialized = true;
+ kdDebug() << "(K3bAudioCdTrackSource) initialized." << endl;
+ }
+ }
+
+ return m_initialized;
+}
+
+
+void K3bAudioCdTrackSource::closeParanoia()
+{
+ if( m_cdParanoiaLib && m_initialized ) {
+ m_cdParanoiaLib->close();
+ }
+ m_initialized = false;
+}
+
+
+K3bDevice::Device* K3bAudioCdTrackSource::searchForAudioCD() const
+{
+ kdDebug() << "(K3bAudioCdTrackSource::searchForAudioCD()" << endl;
+ // first try the saved device
+ if( m_lastUsedDevice && searchForAudioCD( m_lastUsedDevice ) )
+ return m_lastUsedDevice;
+
+ const QPtrList<K3bDevice::Device>& devices = k3bcore->deviceManager()->readingDevices();
+ for( QPtrListIterator<K3bDevice::Device> it(devices); *it; ++it ) {
+ if( searchForAudioCD( *it ) ) {
+ return *it;
+ }
+ }
+
+ kdDebug() << "(K3bAudioCdTrackSource::searchForAudioCD) failed." << endl;
+
+ return 0;
+}
+
+
+bool K3bAudioCdTrackSource::searchForAudioCD( K3bDevice::Device* dev ) const
+{
+ kdDebug() << "(K3bAudioCdTrackSource::searchForAudioCD(" << dev->description() << ")" << endl;
+ K3bDevice::Toc toc = dev->readToc();
+ return ( toc.discId() == m_discId );
+}
+
+
+void K3bAudioCdTrackSource::setDevice( K3bDevice::Device* dev )
+{
+ if( dev && dev != m_lastUsedDevice ) {
+ m_lastUsedDevice = dev;
+ if( m_initialized ) {
+ }
+ }
+}
+
+
+K3b::Msf K3bAudioCdTrackSource::originalLength() const
+{
+ return m_length;
+}
+
+
+bool K3bAudioCdTrackSource::seek( const K3b::Msf& msf )
+{
+ // HACK: to reinitialize every time we restart the decoding
+ if( msf == 0 && m_cdParanoiaLib )
+ closeParanoia();
+
+ m_position = msf;
+
+ if( m_cdParanoiaLib )
+ m_cdParanoiaLib->initReading( m_toc[m_cdTrackNumber-1].firstSector().lba() + startOffset().lba() + m_position.lba(),
+ m_toc[m_cdTrackNumber-1].firstSector().lba() + lastSector().lba() );
+
+ return true;
+}
+
+
+int K3bAudioCdTrackSource::read( char* data, unsigned int )
+{
+ if( initParanoia() ) {
+ int status = 0;
+ char* buf = m_cdParanoiaLib->read( &status, 0, false /* big endian */ );
+ if( status == K3bCdparanoiaLib::S_OK ) {
+ if( buf == 0 ) {
+ // done
+ closeParanoia();
+ return 0;
+ }
+ else {
+ ++m_position;
+ ::memcpy( data, buf, CD_FRAMESIZE_RAW );
+ return CD_FRAMESIZE_RAW;
+ }
+ }
+ else {
+ // in case the reading fails we go back to "not initialized"
+ closeParanoia();
+ return -1;
+ }
+ }
+ else
+ return -1;
+}
+
+
+QString K3bAudioCdTrackSource::type() const
+{
+ return i18n("CD Track");
+}
+
+
+QString K3bAudioCdTrackSource::sourceComment() const
+{
+ return i18n("Track %1 from Audio CD %2").arg(m_cdTrackNumber).arg(m_discId,0,16);
+}
+
+
+K3bAudioDataSource* K3bAudioCdTrackSource::copy() const
+{
+ return new K3bAudioCdTrackSource( *this );
+}
diff --git a/libk3b/projects/audiocd/k3baudiocdtracksource.h b/libk3b/projects/audiocd/k3baudiocdtracksource.h
new file mode 100644
index 0000000..6eaaa5b
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiocdtracksource.h
@@ -0,0 +1,99 @@
+/*
+ *
+ * $Id: k3baudiocdtracksource.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2005 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_AUDIO_CD_TRACK_SOURCE_H_
+#define _K3B_AUDIO_CD_TRACK_SOURCE_H_
+
+#include "k3baudiodatasource.h"
+
+#include <k3btoc.h>
+#include <k3bcddbresult.h>
+
+#include "k3b_export.h"
+
+
+namespace K3bDevice {
+ class Device;
+}
+class K3bCdparanoiaLib;
+
+
+/**
+ * Audio data source which reads it's data directly from an audio CD.
+ *
+ * Be aware that since GUI elements are not allowed in sources (other thread)
+ * the source relies on the audio CD being inserted before any read operations.
+ * It will search all available devices for the CD starting with the last used drive.
+ */
+class LIBK3B_EXPORT K3bAudioCdTrackSource : public K3bAudioDataSource
+{
+ public:
+ /**
+ * Default constructor to create a new source.
+ */
+ K3bAudioCdTrackSource( const K3bDevice::Toc& toc, int cdTrackNumber, const K3bCddbResultEntry& cddb,
+ K3bDevice::Device* dev = 0 );
+
+ /**
+ * Constructor to create sources when loading from a project file without toc information
+ */
+ K3bAudioCdTrackSource( unsigned int discid, const K3b::Msf& length, int cdTrackNumber,
+ const QString& artist, const QString& title,
+ const QString& cdartist, const QString& cdtitle );
+ K3bAudioCdTrackSource( const K3bAudioCdTrackSource& );
+ ~K3bAudioCdTrackSource();
+
+ unsigned int discId() const { return m_discId; }
+ int cdTrackNumber() const { return m_cdTrackNumber; }
+ const K3bCddbResultEntry& metaInfo() const { return m_cddbEntry; }
+
+ K3b::Msf originalLength() const;
+ bool seek( const K3b::Msf& );
+ int read( char* data, unsigned int max );
+ QString type() const;
+ QString sourceComment() const;
+ K3bAudioDataSource* copy() const;
+
+ /**
+ * Searches for the corresponding Audio CD and returns the device in which it has
+ * been found or 0 if it could not be found.
+ */
+ K3bDevice::Device* searchForAudioCD() const;
+
+ /**
+ * Set the device the source should start to look for the CD.
+ */
+ void setDevice( K3bDevice::Device* dev );
+
+ private:
+ bool initParanoia();
+ void closeParanoia();
+ bool searchForAudioCD( K3bDevice::Device* ) const;
+
+ unsigned int m_discId;
+ K3b::Msf m_length;
+ K3bDevice::Toc m_toc;
+ int m_cdTrackNumber;
+ K3bCddbResultEntry m_cddbEntry;
+
+ // ripping
+ // we only save the device we last saw the CD in
+ K3bDevice::Device* m_lastUsedDevice;
+ K3bCdparanoiaLib* m_cdParanoiaLib;
+ K3b::Msf m_position;
+ bool m_initialized;
+};
+
+#endif
diff --git a/libk3b/projects/audiocd/k3baudiodatasource.cpp b/libk3b/projects/audiocd/k3baudiodatasource.cpp
new file mode 100644
index 0000000..0f705f4
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiodatasource.cpp
@@ -0,0 +1,210 @@
+/*
+ *
+ * $Id: k3baudiodatasource.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2004 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3baudiodatasource.h"
+#include "k3baudiotrack.h"
+#include "k3baudiodoc.h"
+
+
+K3bAudioDataSource::K3bAudioDataSource()
+ : m_track(0),
+ m_prev(0),
+ m_next(0)
+{
+}
+
+
+K3bAudioDataSource::K3bAudioDataSource( const K3bAudioDataSource& source )
+ : m_track( 0 ),
+ m_prev( 0 ),
+ m_next( 0 ),
+ m_startOffset( source.m_startOffset ),
+ m_endOffset( source.m_endOffset )
+{
+}
+
+
+K3bAudioDataSource::~K3bAudioDataSource()
+{
+ take();
+}
+
+
+K3bAudioDoc* K3bAudioDataSource::doc() const
+{
+ if( m_track )
+ return m_track->doc();
+ else
+ return 0;
+}
+
+
+K3bAudioDataSource* K3bAudioDataSource::take()
+{
+ // if we do not have a track we are not in any list
+ if( m_track ) {
+ if( !m_prev )
+ m_track->setFirstSource( m_next );
+
+ if( m_prev )
+ m_prev->m_next = m_next;
+ if( m_next )
+ m_next->m_prev = m_prev;
+
+ m_prev = m_next = 0;
+
+ emitChange();
+ m_track = 0;
+ }
+
+ return this;
+}
+
+
+void K3bAudioDataSource::moveAfter( K3bAudioDataSource* source )
+{
+ // cannot create a list outside a track!
+ if( !source->track() )
+ return;
+
+ if( source == this )
+ return;
+
+ // remove this from the list
+ take();
+
+ K3bAudioDataSource* oldNext = source->m_next;
+
+ // set track as prev
+ source->m_next = this;
+ m_prev = source;
+
+ // set oldNext as next
+ if( oldNext )
+ oldNext->m_prev = this;
+ m_next = oldNext;
+
+ m_track = source->track();
+ emitChange();
+}
+
+
+void K3bAudioDataSource::moveAhead( K3bAudioDataSource* source )
+{
+ // cannot create a list outside a track!
+ if( !source->track() )
+ return;
+
+ if( source == this )
+ return;
+
+ // remove this from the list
+ take();
+
+ K3bAudioDataSource* oldPrev = source->m_prev;
+
+ // set track as next
+ m_next = source;
+ source->m_prev = this;
+
+ // set oldPrev as prev
+ m_prev = oldPrev;
+ if( oldPrev )
+ oldPrev->m_next = this;
+
+ m_track = source->track();
+
+ if( !m_prev )
+ m_track->setFirstSource( this );
+
+ emitChange();
+}
+
+
+void K3bAudioDataSource::emitChange()
+{
+ if( m_track )
+ m_track->sourceChanged( this );
+}
+
+
+K3bAudioDataSource* K3bAudioDataSource::split( const K3b::Msf& pos )
+{
+ if( pos < length() ) {
+ K3bAudioDataSource* s = copy();
+ s->setStartOffset( startOffset() + pos );
+ s->setEndOffset( endOffset() );
+ setEndOffset( startOffset() + pos );
+ s->moveAfter( this );
+ emitChange();
+ return s;
+ }
+ else
+ return 0;
+}
+
+
+K3b::Msf K3bAudioDataSource::lastSector() const
+{
+ if( endOffset() > 0 )
+ return endOffset()-1;
+ else
+ return originalLength()-1;
+}
+
+
+K3b::Msf K3bAudioDataSource::length() const
+{
+ if( originalLength() == 0 )
+ return 0;
+ else if( lastSector() < m_startOffset )
+ return 1;
+ else
+ return lastSector() - m_startOffset + 1;
+}
+
+
+void K3bAudioDataSource::setStartOffset( const K3b::Msf& msf )
+{
+ m_startOffset = msf;
+ fixupOffsets();
+ emitChange();
+}
+
+
+void K3bAudioDataSource::setEndOffset( const K3b::Msf& msf )
+{
+ m_endOffset = msf;
+ fixupOffsets();
+ emitChange();
+}
+
+
+void K3bAudioDataSource::fixupOffsets()
+{
+ // no length available yet
+ if( originalLength() == 0 )
+ return;
+
+ if( startOffset() >= originalLength() ) {
+ setStartOffset( 0 );
+ }
+ if( endOffset() > originalLength() ) {
+ setEndOffset( 0 ); // whole source
+ }
+ if( endOffset() > 0 && endOffset() <= startOffset() ) {
+ setEndOffset( startOffset() );
+ }
+}
diff --git a/libk3b/projects/audiocd/k3baudiodatasource.h b/libk3b/projects/audiocd/k3baudiodatasource.h
new file mode 100644
index 0000000..d12fd10
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiodatasource.h
@@ -0,0 +1,168 @@
+/*
+ *
+ * $Id: k3baudiodatasource.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2004 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_AUDIO_DATA_SOURCE_H_
+#define _K3B_AUDIO_DATA_SOURCE_H_
+
+#include <k3bmsf.h>
+#include "k3b_export.h"
+class K3bAudioTrack;
+class K3bAudioDoc;
+
+
+/**
+ * An AudioDataSource has an original length which represents the maximum amount of audio
+ * sectors this source can provide (in special cases this is not true, see K3bAudioZeroData).
+ *
+ * It is possible to just use a portion of that data by changing the startOffset and endOffset.
+ * This will change the actual length of the data provided by this source through the read method.
+ *
+ * Sources are part of a list which can be traversed via the prev() and next() methods. This list
+ * is part of a K3bAudioTrack which in turn is part of a list which is owned by a K3bAudioDoc.
+ *
+ * The list may be modified with the take(), moveAfter(), and moveAhead() methods. The source takes
+ * care of fixing the list and notifying the track about the change (It is also possible to move sources
+ * from one track to the other).
+ *
+ * When a source is deleted it automatically removes itself from it's list.
+ */
+class LIBK3B_EXPORT K3bAudioDataSource
+{
+ friend class K3bAudioTrack;
+
+ public:
+ K3bAudioDataSource();
+
+ /**
+ * Create en identical copy except that the copy will not be in any list.
+ */
+ K3bAudioDataSource( const K3bAudioDataSource& );
+ virtual ~K3bAudioDataSource();
+
+ /**
+ * The original length of the source is the maximum data which is available
+ * when startOffset is 0 this is the max for endOffset
+ *
+ * Be aware that this may change (see K3bAudioZeroData)
+ */
+ virtual K3b::Msf originalLength() const = 0;
+
+ /**
+ * The default implementation returns the originalLength modified by startOffset and endOffset
+ */
+ virtual K3b::Msf length() const;
+
+ /**
+ * @return The raw size in pcm samples (16bit, 44800 kHz, stereo)
+ */
+ KIO::filesize_t size() const { return length().audioBytes(); }
+
+ virtual bool seek( const K3b::Msf& ) = 0;
+
+ /**
+ * Read data from the source.
+ */
+ virtual int read( char* data, unsigned int max ) = 0;
+
+ /**
+ * Type of the data in readable form.
+ */
+ virtual QString type() const = 0;
+
+ /**
+ * The source in readable form (this is the filename for files)
+ */
+ virtual QString sourceComment() const = 0;
+
+ /**
+ * Used in case an error occurred. For now this is used if the
+ * decoder was not able to decode an audiofile
+ */
+ virtual bool isValid() const { return true; }
+
+ /**
+ * The doc the source is currently a part of or null.
+ */
+ K3bAudioDoc* doc() const;
+ K3bAudioTrack* track() const { return m_track; }
+
+ K3bAudioDataSource* prev() const { return m_prev; }
+ K3bAudioDataSource* next() const { return m_next; }
+
+ K3bAudioDataSource* take();
+
+ void moveAfter( K3bAudioDataSource* track );
+ void moveAhead( K3bAudioDataSource* track );
+
+ /**
+ * Set the start offset from the beginning of the source's originalLength.
+ */
+ virtual void setStartOffset( const K3b::Msf& );
+
+ /**
+ * Set the end offset from the beginning of the file. The endOffset sector
+ * is not included in the data.
+ * The maximum value is originalLength() which means to use all data.
+ * 0 means the same as originalLength().
+ * This has to be bigger than the start offset.
+ */
+ virtual void setEndOffset( const K3b::Msf& );
+
+ virtual const K3b::Msf& startOffset() const { return m_startOffset; }
+
+ /**
+ * The end offset. It is the first sector not included in the data.
+ * If 0 the last sector is determined by the originalLength
+ */
+ virtual const K3b::Msf& endOffset() const { return m_endOffset; }
+
+ /**
+ * Get the last used sector in the source.
+ * The default implementation uses originalLength() and endOffset()
+ */
+ virtual K3b::Msf lastSector() const;
+
+ /**
+ * Create a copy of this source which is not part of a list
+ */
+ virtual K3bAudioDataSource* copy() const = 0;
+
+ /**
+ * Split the source at position pos and return the splitted source
+ * on success.
+ * The new source will be moved after this source.
+ *
+ * The default implementation uses copy() to create a new source instance
+ */
+ virtual K3bAudioDataSource* split( const K3b::Msf& pos );
+
+ protected:
+ /**
+ * Informs the parent track about changes.
+ */
+ void emitChange();
+
+ private:
+ void fixupOffsets();
+
+ K3bAudioTrack* m_track;
+ K3bAudioDataSource* m_prev;
+ K3bAudioDataSource* m_next;
+
+ K3b::Msf m_startOffset;
+ K3b::Msf m_endOffset;
+};
+
+#endif
diff --git a/libk3b/projects/audiocd/k3baudiodatasourceiterator.cpp b/libk3b/projects/audiocd/k3baudiodatasourceiterator.cpp
new file mode 100644
index 0000000..81e0a59
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiodatasourceiterator.cpp
@@ -0,0 +1,71 @@
+/*
+ *
+ * $Id: sourceheader,v 1.3 2005/01/19 13:03:46 trueg Exp $
+ * Copyright (C) 2005 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3baudiodatasourceiterator.h"
+#include "k3baudiodoc.h"
+#include "k3baudiotrack.h"
+#include "k3baudiodatasource.h"
+
+
+K3bAudioDataSourceIterator::K3bAudioDataSourceIterator( K3bAudioDoc* doc )
+ : m_doc( doc )
+{
+ first();
+}
+
+
+K3bAudioDataSource* K3bAudioDataSourceIterator::current() const
+{
+ return m_currentSource;
+}
+
+
+K3bAudioDataSource* K3bAudioDataSourceIterator::next()
+{
+ m_currentSource = m_currentSource->next();
+ if( !m_currentSource ) {
+ m_currentTrack = m_currentTrack->next();
+ if( m_currentTrack )
+ m_currentSource = m_currentTrack->firstSource();
+ }
+
+ return m_currentSource;
+}
+
+
+bool K3bAudioDataSourceIterator::hasNext() const
+{
+ if( !m_currentSource )
+ return false;
+ if( m_currentSource->next() )
+ return true;
+ if( m_currentTrack->next() )
+ return true;
+
+ return false;
+}
+
+
+K3bAudioDataSource* K3bAudioDataSourceIterator::first()
+{
+ m_currentTrack = m_doc->firstTrack();
+
+ if( m_currentTrack )
+ m_currentSource = m_currentTrack->firstSource();
+ else
+ m_currentSource = 0;
+
+ return m_currentSource;
+}
diff --git a/libk3b/projects/audiocd/k3baudiodatasourceiterator.h b/libk3b/projects/audiocd/k3baudiodatasourceiterator.h
new file mode 100644
index 0000000..7a0ce59
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiodatasourceiterator.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * $Id: sourceheader,v 1.3 2005/01/19 13:03:46 trueg Exp $
+ * Copyright (C) 2005 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_AUDIO_DATA_SOURCE_ITERATOR_H_
+#define _K3B_AUDIO_DATA_SOURCE_ITERATOR_H_
+#include "k3b_export.h"
+class K3bAudioDataSource;
+class K3bAudioTrack;
+class K3bAudioDoc;
+
+
+/**
+ * This Iterator iterates over the sources in an audio project
+ *
+ * Be aware that this iterator does not properly update when the doc
+ * changes. A manual update can be issued with first(). This is becasue
+ * an update would either involve slots (this being a QObject) which is
+ * too much overhead or the AudioDoc would need to have knowledge of all
+ * the iterators which is also overhead that would be overkill.
+ */
+class LIBK3B_EXPORT K3bAudioDataSourceIterator
+{
+ public:
+ /**
+ * This will place the iterator on the first source just like first() does.
+ */
+ explicit K3bAudioDataSourceIterator( K3bAudioDoc* );
+
+ K3bAudioDataSource* current() const;
+
+ bool hasNext() const;
+
+ /**
+ * \return the next source or 0 if at end.
+ */
+ K3bAudioDataSource* next();
+
+ /**
+ * Reset the iterator
+ */
+ K3bAudioDataSource* first();
+
+ private:
+ K3bAudioDoc* m_doc;
+ K3bAudioTrack* m_currentTrack;
+ K3bAudioDataSource* m_currentSource;
+};
+
+#endif
diff --git a/libk3b/projects/audiocd/k3baudiodoc.cpp b/libk3b/projects/audiocd/k3baudiodoc.cpp
new file mode 100644
index 0000000..e7661ba
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiodoc.cpp
@@ -0,0 +1,1127 @@
+/*
+ *
+ * $Id: k3baudiodoc.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include <k3bglobals.h>
+#include "k3baudiodoc.h"
+#include "k3baudiotrack.h"
+#include "k3baudiojob.h"
+#include "k3baudiofile.h"
+#include "k3baudiozerodata.h"
+#include "k3baudiocdtracksource.h"
+
+#include <k3bcuefileparser.h>
+#include <k3bcdtextvalidator.h>
+#include <k3bcore.h>
+#include <k3baudiodecoder.h>
+
+
+// QT-includes
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qdatastream.h>
+#include <qdir.h>
+#include <qdom.h>
+#include <qdatetime.h>
+#include <qtextstream.h>
+#include <qsemaphore.h>
+
+// KDE-includes
+#include <kprocess.h>
+#include <kurl.h>
+#include <kapplication.h>
+#include <kmessagebox.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kio/global.h>
+#include <kdebug.h>
+
+#include <iostream>
+
+
+class K3bAudioDoc::Private
+{
+public:
+ Private() {
+ cdTextValidator = new K3bCdTextValidator();
+ }
+
+ ~Private() {
+ delete cdTextValidator;
+ }
+
+ K3bCdTextValidator* cdTextValidator;
+};
+
+
+K3bAudioDoc::K3bAudioDoc( QObject* parent )
+ : K3bDoc( parent ),
+ m_firstTrack(0),
+ m_lastTrack(0)
+{
+ d = new Private;
+ m_docType = AUDIO;
+}
+
+K3bAudioDoc::~K3bAudioDoc()
+{
+ // delete all tracks
+ int i = 1;
+ int cnt = numOfTracks();
+ while( m_firstTrack ) {
+ kdDebug() << "(K3bAudioDoc::~K3bAudioDoc) deleting track " << i << " of " << cnt << endl;
+ delete m_firstTrack->take();
+ kdDebug() << "(K3bAudioDoc::~K3bAudioDoc) deleted." << endl;
+ ++i;
+ }
+
+ delete d;
+}
+
+bool K3bAudioDoc::newDocument()
+{
+ // delete all tracks
+ while( m_firstTrack )
+ delete m_firstTrack->take();
+
+ m_normalize = false;
+ m_hideFirstTrack = false;
+ m_cdText = false;
+ m_cdTextData.clear();
+ m_audioRippingParanoiaMode = 0;
+ m_audioRippingRetries = 5;
+ m_audioRippingIgnoreReadErrors = true;
+
+ return K3bDoc::newDocument();
+}
+
+
+QString K3bAudioDoc::name() const
+{
+ if( !m_cdTextData.title().isEmpty() )
+ return m_cdTextData.title();
+ else
+ return K3bDoc::name();
+}
+
+
+K3bAudioTrack* K3bAudioDoc::firstTrack() const
+{
+ return m_firstTrack;
+}
+
+
+K3bAudioTrack* K3bAudioDoc::lastTrack() const
+{
+ return m_lastTrack;
+}
+
+
+// this one is called by K3bAudioTrack to update the list
+void K3bAudioDoc::setFirstTrack( K3bAudioTrack* track )
+{
+ m_firstTrack = track;
+}
+
+// this one is called by K3bAudioTrack to update the list
+void K3bAudioDoc::setLastTrack( K3bAudioTrack* track )
+{
+ m_lastTrack = track;
+}
+
+
+KIO::filesize_t K3bAudioDoc::size() const
+{
+ // This is not really correct but what the user expects ;)
+ return length().mode1Bytes();
+}
+
+
+K3b::Msf K3bAudioDoc::length() const
+{
+ K3b::Msf length = 0;
+ K3bAudioTrack* track = m_firstTrack;
+ while( track ) {
+ length += track->length();
+ track = track->next();
+ }
+
+ return length;
+}
+
+
+void K3bAudioDoc::addUrls( const KURL::List& urls )
+{
+ // make sure we add them at the end even if urls are in the queue
+ addTracks( urls, 99 );
+}
+
+
+void K3bAudioDoc::addTracks( const KURL::List& urls, uint position )
+{
+ KURL::List allUrls = extractUrlList( K3b::convertToLocalUrls(urls) );
+ KURL::List::iterator end( allUrls.end());
+ for( KURL::List::iterator it = allUrls.begin(); it != end; it++, position++ ) {
+ KURL& url = *it;
+ if( url.path().right(3).lower() == "cue" ) {
+ // try adding a cue file
+ if( K3bAudioTrack* newAfter = importCueFile( url.path(), getTrack(position) ) ) {
+ position = newAfter->trackNumber();
+ continue;
+ }
+ }
+
+ if( K3bAudioTrack* track = createTrack( url ) ) {
+ addTrack( track, position );
+
+ K3bAudioDecoder* dec = static_cast<K3bAudioFile*>( track->firstSource() )->decoder();
+ track->setTitle( dec->metaInfo( K3bAudioDecoder::META_TITLE ) );
+ track->setArtist( dec->metaInfo( K3bAudioDecoder::META_ARTIST ) );
+ track->setSongwriter( dec->metaInfo( K3bAudioDecoder::META_SONGWRITER ) );
+ track->setComposer( dec->metaInfo( K3bAudioDecoder::META_COMPOSER ) );
+ track->setCdTextMessage( dec->metaInfo( K3bAudioDecoder::META_COMMENT ) );
+ }
+ }
+
+ emit changed();
+
+ informAboutNotFoundFiles();
+}
+
+
+KURL::List K3bAudioDoc::extractUrlList( const KURL::List& urls )
+{
+ KURL::List allUrls = urls;
+ KURL::List urlsFromPlaylist;
+ KURL::List::iterator it = allUrls.begin();
+ while( it != allUrls.end() ) {
+
+ const KURL& url = *it;
+ QFileInfo fi( url.path() );
+
+ if( !url.isLocalFile() ) {
+ kdDebug() << url.path() << " no local file" << endl;
+ it = allUrls.remove( it );
+ m_notFoundFiles.append( url );
+ }
+ else if( !fi.exists() ) {
+ it = allUrls.remove( it );
+ kdDebug() << url.path() << " not found" << endl;
+ m_notFoundFiles.append( url );
+ }
+ else if( fi.isDir() ) {
+ it = allUrls.remove( it );
+ // add all files in the dir
+ QDir dir(fi.filePath());
+ QStringList entries = dir.entryList( QDir::Files );
+ KURL::List::iterator oldIt = it;
+ // add all files into the list after the current item
+ for( QStringList::iterator dirIt = entries.begin();
+ dirIt != entries.end(); ++dirIt )
+ it = allUrls.insert( oldIt, KURL::fromPathOrURL( dir.absPath() + "/" + *dirIt ) );
+ }
+ else if( readPlaylistFile( url, urlsFromPlaylist ) ) {
+ it = allUrls.remove( it );
+ KURL::List::iterator oldIt = it;
+ // add all files into the list after the current item
+ for( KURL::List::iterator dirIt = urlsFromPlaylist.begin();
+ dirIt != urlsFromPlaylist.end(); ++dirIt )
+ it = allUrls.insert( oldIt, *dirIt );
+ }
+ else
+ ++it;
+ }
+
+ return allUrls;
+}
+
+
+bool K3bAudioDoc::readPlaylistFile( const KURL& url, KURL::List& playlist )
+{
+ // check if the file is a m3u playlist
+ // and if so add all listed files
+
+ QFile f( url.path() );
+ if( !f.open( IO_ReadOnly ) )
+ return false;
+
+ QTextStream t( &f );
+ char buf[7];
+ t.readRawBytes( buf, 7 );
+ if( QString::fromLatin1( buf, 7 ) != "#EXTM3U" )
+ return false;
+
+ // skip the first line
+ t.readLine();
+
+ // read the file
+ while( !t.atEnd() ) {
+ QString line = t.readLine();
+ if( line[0] != '#' ) {
+ KURL mp3url;
+ // relative paths
+ if( line[0] != '/' )
+ mp3url.setPath( url.directory(false) + line );
+ else
+ mp3url.setPath( line );
+
+ playlist.append( mp3url );
+ }
+ }
+
+ return true;
+}
+
+
+void K3bAudioDoc::addSources( K3bAudioTrack* parent,
+ const KURL::List& urls,
+ K3bAudioDataSource* sourceAfter )
+{
+ kdDebug() << "(K3bAudioDoc::addSources( " << parent << ", "
+ << urls.first().path() << ", "
+ << sourceAfter << " )" << endl;
+ KURL::List allUrls = extractUrlList( urls );
+ KURL::List::const_iterator end(allUrls.end());
+ for( KURL::List::const_iterator it = allUrls.begin(); it != end; ++it ) {
+ if( K3bAudioFile* file = createAudioFile( *it ) ) {
+ if( sourceAfter )
+ file->moveAfter( sourceAfter );
+ else
+ file->moveAhead( parent->firstSource() );
+ sourceAfter = file;
+ }
+ }
+
+ informAboutNotFoundFiles();
+ kdDebug() << "(K3bAudioDoc::addSources) finished." << endl;
+}
+
+
+K3bAudioTrack* K3bAudioDoc::importCueFile( const QString& cuefile, K3bAudioTrack* after, K3bAudioDecoder* decoder )
+{
+ if( !after )
+ after = m_lastTrack;
+
+ kdDebug() << "(K3bAudioDoc::importCueFile( " << cuefile << ", " << after << ")" << endl;
+ K3bCueFileParser parser( cuefile );
+ if( parser.isValid() && parser.toc().contentType() == K3bDevice::AUDIO ) {
+
+ kdDebug() << "(K3bAudioDoc::importCueFile) parsed with image: " << parser.imageFilename() << endl;
+
+ // global cd-text
+ if( !parser.cdText().title().isEmpty() )
+ setTitle( parser.cdText().title() );
+ if( !parser.cdText().performer().isEmpty() )
+ setPerformer( parser.cdText().performer() );
+
+ bool reused = true;
+ if( !decoder )
+ decoder = getDecoderForUrl( KURL::fromPathOrURL(parser.imageFilename()), &reused );
+
+ if( decoder ) {
+ if( !reused )
+ decoder->analyseFile();
+
+ K3bAudioFile* newFile = 0;
+ unsigned int i = 0;
+ for( K3bDevice::Toc::const_iterator it = parser.toc().begin();
+ it != parser.toc().end(); ++it ) {
+ const K3bDevice::Track& track = *it;
+
+ newFile = new K3bAudioFile( decoder, this );
+ newFile->setStartOffset( track.firstSector() );
+ newFile->setEndOffset( track.lastSector()+1 );
+
+ K3bAudioTrack* newTrack = new K3bAudioTrack( this );
+ newTrack->addSource( newFile );
+ newTrack->moveAfter( after );
+
+ // we do not know the length of the source yet so we have to force the index value
+ if( track.index0() > 0 )
+ newTrack->m_index0Offset = track.length() - track.index0();
+ else
+ newTrack->m_index0Offset = 0;
+
+ // cd-text
+ newTrack->setTitle( parser.cdText()[i].title() );
+ newTrack->setPerformer( parser.cdText()[i].performer() );
+
+ // add the next track after this one
+ after = newTrack;
+ ++i;
+ }
+
+ // let the last source use the data up to the end of the file
+ if( newFile )
+ newFile->setEndOffset(0);
+
+ return after;
+ }
+ }
+ return 0;
+}
+
+
+K3bAudioDecoder* K3bAudioDoc::getDecoderForUrl( const KURL& url, bool* reused )
+{
+ K3bAudioDecoder* decoder = 0;
+
+ // check if we already have a proper decoder
+ if( m_decoderPresenceMap.contains( url.path() ) ) {
+ decoder = m_decoderPresenceMap[url.path()];
+ *reused = true;
+ }
+ else if( (decoder = K3bAudioDecoderFactory::createDecoder( url )) ) {
+ kdDebug() << "(K3bAudioDoc) using " << decoder->className()
+ << " for decoding of " << url.path() << endl;
+
+ decoder->setFilename( url.path() );
+ *reused = false;
+ }
+
+ return decoder;
+}
+
+
+K3bAudioFile* K3bAudioDoc::createAudioFile( const KURL& url )
+{
+ if( !QFile::exists( url.path() ) ) {
+ m_notFoundFiles.append( url.path() );
+ kdDebug() << "(K3bAudioDoc) could not find file " << url.path() << endl;
+ return 0;
+ }
+
+ bool reused;
+ K3bAudioDecoder* decoder = getDecoderForUrl( url, &reused );
+ if( decoder ) {
+ if( !reused )
+ decoder->analyseFile();
+ return new K3bAudioFile( decoder, this );
+ }
+ else {
+ m_unknownFileFormatFiles.append( url.path() );
+ kdDebug() << "(K3bAudioDoc) unknown file type in file " << url.path() << endl;
+ return 0;
+ }
+}
+
+
+K3bAudioTrack* K3bAudioDoc::createTrack( const KURL& url )
+{
+ kdDebug() << "(K3bAudioDoc::createTrack( " << url.path() << " )" << endl;
+ if( K3bAudioFile* file = createAudioFile( url ) ) {
+ K3bAudioTrack* newTrack = new K3bAudioTrack( this );
+ newTrack->setFirstSource( file );
+ return newTrack;
+ }
+ else
+ return 0;
+}
+
+
+void K3bAudioDoc::addTrack( const KURL& url, uint position )
+{
+ addTracks( KURL::List(url), position );
+}
+
+
+
+K3bAudioTrack* K3bAudioDoc::getTrack( unsigned int trackNum )
+{
+ K3bAudioTrack* track = m_firstTrack;
+ unsigned int i = 1;
+ while( track ) {
+ if( i == trackNum )
+ return track;
+ track = track->next();
+ ++i;
+ }
+
+ return 0;
+}
+
+
+void K3bAudioDoc::addTrack( K3bAudioTrack* track, uint position )
+{
+ kdDebug() << "(K3bAudioDoc::addTrack( " << track << ", " << position << " )" << endl;
+ track->m_parent = this;
+ if( !m_firstTrack )
+ m_firstTrack = m_lastTrack = track;
+ else if( position == 0 )
+ track->moveAhead( m_firstTrack );
+ else {
+ K3bAudioTrack* after = getTrack( position );
+ if( after )
+ track->moveAfter( after );
+ else
+ track->moveAfter( m_lastTrack ); // just to be sure it's anywhere...
+ }
+
+ emit changed();
+}
+
+
+void K3bAudioDoc::removeTrack( K3bAudioTrack* track )
+{
+ delete track;
+}
+
+
+void K3bAudioDoc::moveTrack( K3bAudioTrack* track, K3bAudioTrack* after )
+{
+ track->moveAfter( after );
+}
+
+
+QString K3bAudioDoc::typeString() const
+{
+ return "audio";
+}
+
+
+bool K3bAudioDoc::loadDocumentData( QDomElement* root )
+{
+ newDocument();
+
+ // we will parse the dom-tree and create a K3bAudioTrack for all entries immediately
+ // this should not take long and so not block the gui
+
+ QDomNodeList nodes = root->childNodes();
+
+ for( uint i = 0; i < nodes.count(); i++ ) {
+
+ QDomElement e = nodes.item(i).toElement();
+
+ if( e.isNull() )
+ return false;
+
+ if( e.nodeName() == "general" ) {
+ if( !readGeneralDocumentData( e ) )
+ return false;
+ }
+
+ else if( e.nodeName() == "normalize" )
+ setNormalize( e.text() == "yes" );
+
+ else if( e.nodeName() == "hide_first_track" )
+ setHideFirstTrack( e.text() == "yes" );
+
+ else if( e.nodeName() == "audio_ripping" ) {
+ QDomNodeList ripNodes = e.childNodes();
+ for( uint j = 0; j < ripNodes.length(); j++ ) {
+ if( ripNodes.item(j).nodeName() == "paranoia_mode" )
+ setAudioRippingParanoiaMode( ripNodes.item(j).toElement().text().toInt() );
+ else if( ripNodes.item(j).nodeName() == "read_retries" )
+ setAudioRippingRetries( ripNodes.item(j).toElement().text().toInt() );
+ else if( ripNodes.item(j).nodeName() == "ignore_read_errors" )
+ setAudioRippingIgnoreReadErrors( ripNodes.item(j).toElement().text() == "yes" );
+ }
+ }
+
+ // parse cd-text
+ else if( e.nodeName() == "cd-text" ) {
+ if( !e.hasAttribute( "activated" ) )
+ return false;
+
+ writeCdText( e.attributeNode( "activated" ).value() == "yes" );
+
+ QDomNodeList cdTextNodes = e.childNodes();
+ for( uint j = 0; j < cdTextNodes.length(); j++ ) {
+ if( cdTextNodes.item(j).nodeName() == "title" )
+ setTitle( cdTextNodes.item(j).toElement().text() );
+
+ else if( cdTextNodes.item(j).nodeName() == "artist" )
+ setArtist( cdTextNodes.item(j).toElement().text() );
+
+ else if( cdTextNodes.item(j).nodeName() == "arranger" )
+ setArranger( cdTextNodes.item(j).toElement().text() );
+
+ else if( cdTextNodes.item(j).nodeName() == "songwriter" )
+ setSongwriter( cdTextNodes.item(j).toElement().text() );
+
+ else if( cdTextNodes.item(j).nodeName() == "composer" )
+ setComposer( cdTextNodes.item(j).toElement().text() );
+
+ else if( cdTextNodes.item(j).nodeName() == "disc_id" )
+ setDisc_id( cdTextNodes.item(j).toElement().text() );
+
+ else if( cdTextNodes.item(j).nodeName() == "upc_ean" )
+ setUpc_ean( cdTextNodes.item(j).toElement().text() );
+
+ else if( cdTextNodes.item(j).nodeName() == "message" )
+ setCdTextMessage( cdTextNodes.item(j).toElement().text() );
+ }
+ }
+
+ else if( e.nodeName() == "contents" ) {
+
+ QDomNodeList contentNodes = e.childNodes();
+
+ for( uint j = 0; j< contentNodes.length(); j++ ) {
+
+ QDomElement trackElem = contentNodes.item(j).toElement();
+
+ // first of all we need a track
+ K3bAudioTrack* track = new K3bAudioTrack();
+
+
+ // backwards compatibility
+ // -----------------------------------------------------------------------------------------------------
+ QDomAttr oldUrlAttr = trackElem.attributeNode( "url" );
+ if( !oldUrlAttr.isNull() ) {
+ if( K3bAudioFile* file =
+ createAudioFile( KURL::fromPathOrURL( oldUrlAttr.value() ) ) ) {
+ track->addSource( file );
+ }
+ }
+ // -----------------------------------------------------------------------------------------------------
+
+
+ QDomNodeList trackNodes = trackElem.childNodes();
+ for( uint trackJ = 0; trackJ < trackNodes.length(); trackJ++ ) {
+
+ if( trackNodes.item(trackJ).nodeName() == "sources" ) {
+ QDomNodeList sourcesNodes = trackNodes.item(trackJ).childNodes();
+ for( unsigned int sourcesIndex = 0; sourcesIndex < sourcesNodes.length(); sourcesIndex++ ) {
+ QDomElement sourceElem = sourcesNodes.item(sourcesIndex).toElement();
+ if( sourceElem.nodeName() == "file" ) {
+ if( K3bAudioFile* file =
+ createAudioFile( KURL::fromPathOrURL( sourceElem.attributeNode( "url" ).value() ) ) ) {
+ file->setStartOffset( K3b::Msf::fromString( sourceElem.attributeNode( "start_offset" ).value() ) );
+ file->setEndOffset( K3b::Msf::fromString( sourceElem.attributeNode( "end_offset" ).value() ) );
+ track->addSource( file );
+ }
+ }
+ else if( sourceElem.nodeName() == "silence" ) {
+ K3bAudioZeroData* zero = new K3bAudioZeroData();
+ zero->setLength( K3b::Msf::fromString( sourceElem.attributeNode( "length" ).value() ) );
+ track->addSource( zero );
+ }
+ else if( sourceElem.nodeName() == "cdtrack" ) {
+ K3b::Msf length = K3b::Msf::fromString( sourceElem.attributeNode( "length" ).value() );
+ int titlenum = 0;
+ unsigned int discid = 0;
+ QString title, artist, cdTitle, cdArtist;
+
+ QDomNodeList cdTrackSourceNodes = sourceElem.childNodes();
+ for( unsigned int cdTrackSourceIndex = 0; cdTrackSourceIndex < cdTrackSourceNodes.length(); ++cdTrackSourceIndex ) {
+ QDomElement cdTrackSourceItemElem = cdTrackSourceNodes.item(cdTrackSourceIndex).toElement();
+ if( cdTrackSourceItemElem.nodeName() == "title_number" )
+ titlenum = cdTrackSourceItemElem.text().toInt();
+ else if( cdTrackSourceItemElem.nodeName() == "disc_id" )
+ discid = cdTrackSourceItemElem.text().toUInt( 0, 16 );
+ else if( cdTrackSourceItemElem.nodeName() == "title" )
+ title = cdTrackSourceItemElem.text().toInt();
+ else if( cdTrackSourceItemElem.nodeName() == "artist" )
+ artist = cdTrackSourceItemElem.text().toInt();
+ else if( cdTrackSourceItemElem.nodeName() == "cdtitle" )
+ cdTitle = cdTrackSourceItemElem.text().toInt();
+ else if( cdTrackSourceItemElem.nodeName() == "cdartist" )
+ cdArtist = cdTrackSourceItemElem.text().toInt();
+ }
+
+ if( discid != 0 && titlenum > 0 ) {
+ K3bAudioCdTrackSource* cdtrack = new K3bAudioCdTrackSource( discid, length, titlenum,
+ artist, title,
+ cdArtist, cdTitle );
+ cdtrack->setStartOffset( K3b::Msf::fromString( sourceElem.attributeNode( "start_offset" ).value() ) );
+ cdtrack->setEndOffset( K3b::Msf::fromString( sourceElem.attributeNode( "end_offset" ).value() ) );
+ track->addSource( cdtrack );
+ }
+ else {
+ kdDebug() << "(K3bAudioDoc) invalid cdtrack source." << endl;
+ return false;
+ }
+ }
+ else {
+ kdDebug() << "(K3bAudioDoc) unknown source type: " << sourceElem.nodeName() << endl;
+ return false;
+ }
+ }
+ }
+
+ // load cd-text
+ else if( trackNodes.item(trackJ).nodeName() == "cd-text" ) {
+ QDomNodeList cdTextNodes = trackNodes.item(trackJ).childNodes();
+ for( uint trackCdTextJ = 0; trackCdTextJ < cdTextNodes.length(); trackCdTextJ++ ) {
+ if( cdTextNodes.item(trackCdTextJ).nodeName() == "title" )
+ track->setTitle( cdTextNodes.item(trackCdTextJ).toElement().text() );
+
+ else if( cdTextNodes.item(trackCdTextJ).nodeName() == "artist" )
+ track->setArtist( cdTextNodes.item(trackCdTextJ).toElement().text() );
+
+ else if( cdTextNodes.item(trackCdTextJ).nodeName() == "arranger" )
+ track->setArranger( cdTextNodes.item(trackCdTextJ).toElement().text() );
+
+ else if( cdTextNodes.item(trackCdTextJ).nodeName() == "songwriter" )
+ track->setSongwriter( cdTextNodes.item(trackCdTextJ).toElement().text() );
+
+ else if( cdTextNodes.item(trackCdTextJ).nodeName() == "composer" )
+ track->setComposer( cdTextNodes.item(trackCdTextJ).toElement().text() );
+
+ else if( cdTextNodes.item(trackCdTextJ).nodeName() == "isrc" )
+ track->setIsrc( cdTextNodes.item(trackCdTextJ).toElement().text() );
+
+ else if( cdTextNodes.item(trackCdTextJ).nodeName() == "message" )
+ track->setCdTextMessage( cdTextNodes.item(trackCdTextJ).toElement().text() );
+ }
+ }
+
+ else if( trackNodes.item(trackJ).nodeName() == "index0" )
+ track->setIndex0( K3b::Msf::fromString( trackNodes.item(trackJ).toElement().text() ) );
+
+ // TODO: load other indices
+
+ // load options
+ else if( trackNodes.item(trackJ).nodeName() == "copy_protection" )
+ track->setCopyProtection( trackNodes.item(trackJ).toElement().text() == "yes" );
+
+ else if( trackNodes.item(trackJ).nodeName() == "pre_emphasis" )
+ track->setPreEmp( trackNodes.item(trackJ).toElement().text() == "yes" );
+ }
+
+ // add the track
+ if( track->numberSources() > 0 )
+ addTrack( track, 99 ); // append to the end // TODO improve
+ else {
+ kdDebug() << "(K3bAudioDoc) no sources. deleting track " << track << endl;
+ delete track;
+ }
+ }
+ }
+ }
+
+ informAboutNotFoundFiles();
+
+ setModified(false);
+
+ return true;
+}
+
+bool K3bAudioDoc::saveDocumentData( QDomElement* docElem )
+{
+ QDomDocument doc = docElem->ownerDocument();
+ saveGeneralDocumentData( docElem );
+
+ // add normalize
+ QDomElement normalizeElem = doc.createElement( "normalize" );
+ normalizeElem.appendChild( doc.createTextNode( normalize() ? "yes" : "no" ) );
+ docElem->appendChild( normalizeElem );
+
+ // add hide track
+ QDomElement hideFirstTrackElem = doc.createElement( "hide_first_track" );
+ hideFirstTrackElem.appendChild( doc.createTextNode( hideFirstTrack() ? "yes" : "no" ) );
+ docElem->appendChild( hideFirstTrackElem );
+
+ // save the audio cd ripping settings
+ // paranoia mode, read retries, and ignore read errors
+ // ------------------------------------------------------------
+ QDomElement ripMain = doc.createElement( "audio_ripping" );
+ docElem->appendChild( ripMain );
+
+ QDomElement ripElem = doc.createElement( "paranoia_mode" );
+ ripElem.appendChild( doc.createTextNode( QString::number( audioRippingParanoiaMode() ) ) );
+ ripMain.appendChild( ripElem );
+
+ ripElem = doc.createElement( "read_retries" );
+ ripElem.appendChild( doc.createTextNode( QString::number( audioRippingRetries() ) ) );
+ ripMain.appendChild( ripElem );
+
+ ripElem = doc.createElement( "ignore_read_errors" );
+ ripElem.appendChild( doc.createTextNode( audioRippingIgnoreReadErrors() ? "yes" : "no" ) );
+ ripMain.appendChild( ripElem );
+ // ------------------------------------------------------------
+
+ // save disc cd-text
+ // -------------------------------------------------------------
+ QDomElement cdTextMain = doc.createElement( "cd-text" );
+ cdTextMain.setAttribute( "activated", cdText() ? "yes" : "no" );
+ QDomElement cdTextElem = doc.createElement( "title" );
+ cdTextElem.appendChild( doc.createTextNode( (title())) );
+ cdTextMain.appendChild( cdTextElem );
+
+ cdTextElem = doc.createElement( "artist" );
+ cdTextElem.appendChild( doc.createTextNode( (artist())) );
+ cdTextMain.appendChild( cdTextElem );
+
+ cdTextElem = doc.createElement( "arranger" );
+ cdTextElem.appendChild( doc.createTextNode( (arranger())) );
+ cdTextMain.appendChild( cdTextElem );
+
+ cdTextElem = doc.createElement( "songwriter" );
+ cdTextElem.appendChild( doc.createTextNode( (songwriter())) );
+ cdTextMain.appendChild( cdTextElem );
+
+ cdTextElem = doc.createElement( "composer" );
+ cdTextElem.appendChild( doc.createTextNode( composer()) );
+ cdTextMain.appendChild( cdTextElem );
+
+ cdTextElem = doc.createElement( "disc_id" );
+ cdTextElem.appendChild( doc.createTextNode( (disc_id())) );
+ cdTextMain.appendChild( cdTextElem );
+
+ cdTextElem = doc.createElement( "upc_ean" );
+ cdTextElem.appendChild( doc.createTextNode( (upc_ean())) );
+ cdTextMain.appendChild( cdTextElem );
+
+ cdTextElem = doc.createElement( "message" );
+ cdTextElem.appendChild( doc.createTextNode( (cdTextMessage())) );
+ cdTextMain.appendChild( cdTextElem );
+
+ docElem->appendChild( cdTextMain );
+ // -------------------------------------------------------------
+
+ // save the tracks
+ // -------------------------------------------------------------
+ QDomElement contentsElem = doc.createElement( "contents" );
+
+ for( K3bAudioTrack* track = firstTrack(); track != 0; track = track->next() ) {
+
+ QDomElement trackElem = doc.createElement( "track" );
+
+ // add sources
+ QDomElement sourcesParent = doc.createElement( "sources" );
+
+ for( K3bAudioDataSource* source = track->firstSource(); source; source = source->next() ) {
+ // TODO: save a source element with a type attribute and start- and endoffset
+ // then distict between the different source types.
+ if( K3bAudioFile* file = dynamic_cast<K3bAudioFile*>(source) ) {
+ QDomElement sourceElem = doc.createElement( "file" );
+ sourceElem.setAttribute( "url", file->filename() );
+ sourceElem.setAttribute( "start_offset", file->startOffset().toString() );
+ sourceElem.setAttribute( "end_offset", file->endOffset().toString() );
+ sourcesParent.appendChild( sourceElem );
+ }
+ else if( K3bAudioZeroData* zero = dynamic_cast<K3bAudioZeroData*>(source) ) {
+ QDomElement sourceElem = doc.createElement( "silence" );
+ sourceElem.setAttribute( "length", zero->length().toString() );
+ sourcesParent.appendChild( sourceElem );
+ }
+ else if( K3bAudioCdTrackSource* cdTrack = dynamic_cast<K3bAudioCdTrackSource*>(source) ) {
+ QDomElement sourceElem = doc.createElement( "cdtrack" );
+ sourceElem.setAttribute( "length", cdTrack->originalLength().toString() );
+ sourceElem.setAttribute( "start_offset", cdTrack->startOffset().toString() );
+ sourceElem.setAttribute( "end_offset", cdTrack->endOffset().toString() );
+
+ QDomElement subElem = doc.createElement( "title_number" );
+ subElem.appendChild( doc.createTextNode( QString::number(cdTrack->cdTrackNumber()) ) );
+ sourceElem.appendChild( subElem );
+
+ subElem = doc.createElement( "disc_id" );
+ subElem.appendChild( doc.createTextNode( QString::number(cdTrack->discId(), 16) ) );
+ sourceElem.appendChild( subElem );
+
+ subElem = doc.createElement( "title" );
+ subElem.appendChild( doc.createTextNode( cdTrack->metaInfo().titles[cdTrack->cdTrackNumber()-1] ) );
+ sourceElem.appendChild( subElem );
+
+ subElem = doc.createElement( "artist" );
+ subElem.appendChild( doc.createTextNode( cdTrack->metaInfo().artists[cdTrack->cdTrackNumber()-1] ) );
+ sourceElem.appendChild( subElem );
+
+ subElem = doc.createElement( "cdtitle" );
+ subElem.appendChild( doc.createTextNode( cdTrack->metaInfo().cdTitle ) );
+ sourceElem.appendChild( subElem );
+
+ subElem = doc.createElement( "cdartist" );
+ subElem.appendChild( doc.createTextNode( cdTrack->metaInfo().cdArtist ) );
+ sourceElem.appendChild( subElem );
+
+ sourcesParent.appendChild( sourceElem );
+ }
+ else {
+ kdError() << "(K3bAudioDoc) saving sources other than file or zero not supported yet." << endl;
+ return false;
+ }
+ }
+ trackElem.appendChild( sourcesParent );
+
+ // index 0
+ QDomElement index0Elem = doc.createElement( "index0" );
+ index0Elem.appendChild( doc.createTextNode( track->index0().toString() ) );
+ trackElem.appendChild( index0Elem );
+
+ // TODO: other indices
+
+ // add cd-text
+ cdTextMain = doc.createElement( "cd-text" );
+ cdTextElem = doc.createElement( "title" );
+ cdTextElem.appendChild( doc.createTextNode( (track->title())) );
+ cdTextMain.appendChild( cdTextElem );
+
+ cdTextElem = doc.createElement( "artist" );
+ cdTextElem.appendChild( doc.createTextNode( (track->artist())) );
+ cdTextMain.appendChild( cdTextElem );
+
+ cdTextElem = doc.createElement( "arranger" );
+ cdTextElem.appendChild( doc.createTextNode( (track->arranger()) ) );
+ cdTextMain.appendChild( cdTextElem );
+
+ cdTextElem = doc.createElement( "songwriter" );
+ cdTextElem.appendChild( doc.createTextNode( (track->songwriter()) ) );
+ cdTextMain.appendChild( cdTextElem );
+
+ cdTextElem = doc.createElement( "composer" );
+ cdTextElem.appendChild( doc.createTextNode( (track->composer()) ) );
+ cdTextMain.appendChild( cdTextElem );
+
+ cdTextElem = doc.createElement( "isrc" );
+ cdTextElem.appendChild( doc.createTextNode( ( track->isrc()) ) );
+ cdTextMain.appendChild( cdTextElem );
+
+ cdTextElem = doc.createElement( "message" );
+ cdTextElem.appendChild( doc.createTextNode( (track->cdTextMessage()) ) );
+ cdTextMain.appendChild( cdTextElem );
+
+ trackElem.appendChild( cdTextMain );
+
+ // add copy protection
+ QDomElement copyElem = doc.createElement( "copy_protection" );
+ copyElem.appendChild( doc.createTextNode( track->copyProtection() ? "yes" : "no" ) );
+ trackElem.appendChild( copyElem );
+
+ // add pre emphasis
+ copyElem = doc.createElement( "pre_emphasis" );
+ copyElem.appendChild( doc.createTextNode( track->preEmp() ? "yes" : "no" ) );
+ trackElem.appendChild( copyElem );
+
+ contentsElem.appendChild( trackElem );
+ }
+ // -------------------------------------------------------------
+
+ docElem->appendChild( contentsElem );
+
+ return true;
+}
+
+
+int K3bAudioDoc::numOfTracks() const
+{
+ return ( m_lastTrack ? m_lastTrack->trackNumber() : 0 );
+}
+
+
+K3bBurnJob* K3bAudioDoc::newBurnJob( K3bJobHandler* hdl, QObject* parent )
+{
+ return new K3bAudioJob( this, hdl, parent );
+}
+
+
+void K3bAudioDoc::informAboutNotFoundFiles()
+{
+ if( !m_notFoundFiles.isEmpty() ) {
+ QStringList l;
+ for( KURL::List::const_iterator it = m_notFoundFiles.begin();
+ it != m_notFoundFiles.end(); ++it )
+ l.append( (*it).path() );
+ KMessageBox::informationList( qApp->activeWindow(),
+ i18n("Could not find the following files:"),
+ l,
+ i18n("Not Found") );
+
+ m_notFoundFiles.clear();
+ }
+ if( !m_unknownFileFormatFiles.isEmpty() ) {
+ QStringList l;
+ for( KURL::List::const_iterator it = m_unknownFileFormatFiles.begin();
+ it != m_unknownFileFormatFiles.end(); ++it )
+ l.append( (*it).path() );
+ KMessageBox::informationList( qApp->activeWindow(),
+ i18n("<p>Unable to handle the following files due to an unsupported format:"
+ "<p>You may manually convert these audio files to wave using another "
+ "application supporting the audio format and then add the wave files "
+ "to the K3b project."),
+ l,
+ i18n("Unsupported Format") );
+
+ m_unknownFileFormatFiles.clear();
+ }
+}
+
+
+
+void K3bAudioDoc::removeCorruptTracks()
+{
+// K3bAudioTrack* track = m_tracks->first();
+// while( track ) {
+// if( track->status() != 0 ) {
+// removeTrack(track);
+// track = m_tracks->current();
+// }
+// else
+// track = m_tracks->next();
+// }
+}
+
+
+void K3bAudioDoc::slotTrackChanged( K3bAudioTrack* track )
+{
+ kdDebug() << "(K3bAudioDoc::slotTrackChanged " << track << endl;
+ setModified( true );
+ // if the track is empty now we simply delete it
+ if( track->firstSource() ) {
+ emit trackChanged(track);
+ emit changed();
+ }
+ else {
+ kdDebug() << "(K3bAudioDoc::slotTrackChanged) track " << track << " empty. Deleting." << endl;
+ delete track; // this will emit the proper signal
+ }
+ kdDebug() << "(K3bAudioDoc::slotTrackChanged done" << track << endl;
+}
+
+
+void K3bAudioDoc::slotTrackRemoved( K3bAudioTrack* track )
+{
+ setModified( true );
+ emit trackRemoved(track);
+ emit changed();
+}
+
+
+void K3bAudioDoc::increaseDecoderUsage( K3bAudioDecoder* decoder )
+{
+ kdDebug() << "(K3bAudioDoc::increaseDecoderUsage)" << endl;
+ if( !m_decoderUsageCounterMap.contains( decoder ) ) {
+ m_decoderUsageCounterMap[decoder] = 1;
+ m_decoderPresenceMap[decoder->filename()] = decoder;
+ }
+ else
+ m_decoderUsageCounterMap[decoder]++;
+ kdDebug() << "(K3bAudioDoc::increaseDecoderUsage) finished" << endl;
+}
+
+
+void K3bAudioDoc::decreaseDecoderUsage( K3bAudioDecoder* decoder )
+{
+ m_decoderUsageCounterMap[decoder]--;
+ if( m_decoderUsageCounterMap[decoder] <= 0 ) {
+ m_decoderUsageCounterMap.erase(decoder);
+ m_decoderPresenceMap.erase(decoder->filename());
+ delete decoder;
+ }
+}
+
+
+K3bDevice::CdText K3bAudioDoc::cdTextData() const
+{
+ K3bDevice::CdText text( m_cdTextData );
+ text.reserve( numOfTracks() );
+ K3bAudioTrack* track = firstTrack();
+ while( track ) {
+ text.append( track->cdText() );
+
+ track = track->next();
+ }
+ return text;
+}
+
+
+K3bDevice::Toc K3bAudioDoc::toToc() const
+{
+ K3bDevice::Toc toc;
+
+ // FIXME: add MCN
+
+ K3bAudioTrack* track = firstTrack();
+ K3b::Msf pos = 0;
+ while( track ) {
+ toc.append( track->toCdTrack() );
+ track = track->next();
+ }
+
+ return toc;
+}
+
+
+void K3bAudioDoc::setTitle( const QString& v )
+{
+ m_cdTextData.setTitle( v );
+ emit changed();
+}
+
+
+void K3bAudioDoc::setArtist( const QString& v )
+{
+ setPerformer( v );
+}
+
+
+void K3bAudioDoc::setPerformer( const QString& v )
+{
+ QString s( v );
+ d->cdTextValidator->fixup( s );
+ m_cdTextData.setPerformer( s );
+ emit changed();
+}
+
+
+void K3bAudioDoc::setDisc_id( const QString& v )
+{
+ QString s( v );
+ d->cdTextValidator->fixup( s );
+ m_cdTextData.setDiscId( s );
+ emit changed();
+}
+
+
+void K3bAudioDoc::setArranger( const QString& v )
+{
+ QString s( v );
+ d->cdTextValidator->fixup( s );
+ m_cdTextData.setArranger( s );
+ emit changed();
+}
+
+
+void K3bAudioDoc::setSongwriter( const QString& v )
+{
+ QString s( v );
+ d->cdTextValidator->fixup( s );
+ m_cdTextData.setSongwriter( s );
+ emit changed();
+}
+
+
+void K3bAudioDoc::setComposer( const QString& v )
+{
+ QString s( v );
+ d->cdTextValidator->fixup( s );
+ m_cdTextData.setComposer( s );
+ emit changed();
+}
+
+
+void K3bAudioDoc::setUpc_ean( const QString& v )
+{
+ QString s( v );
+ d->cdTextValidator->fixup( s );
+ m_cdTextData.setUpcEan( s );
+ emit changed();
+}
+
+
+void K3bAudioDoc::setCdTextMessage( const QString& v )
+{
+ QString s( v );
+ d->cdTextValidator->fixup( s );
+ m_cdTextData.setMessage( s );
+ emit changed();
+}
+
+#include "k3baudiodoc.moc"
diff --git a/libk3b/projects/audiocd/k3baudiodoc.h b/libk3b/projects/audiocd/k3baudiodoc.h
new file mode 100644
index 0000000..87a512e
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiodoc.h
@@ -0,0 +1,263 @@
+/*
+ *
+ * $Id: k3baudiodoc.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BAUDIODOC_H
+#define K3BAUDIODOC_H
+
+#include <k3bdoc.h>
+
+#include <k3bcdtext.h>
+#include <k3btoc.h>
+
+#include <qptrlist.h>
+#include <qfile.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qdatetime.h>
+#include <qtextstream.h>
+#include "k3b_export.h"
+#include <kurl.h>
+
+class K3bApp;
+class K3bAudioTrack;
+class QWidget;
+class QTimer;
+class QDomDocument;
+class QDomElement;
+class K3bThreadJob;
+class KConfig;
+class K3bAudioDataSource;
+class K3bAudioDecoder;
+class K3bAudioFile;
+
+/**Document class for an audio project.
+ *@author Sebastian Trueg
+ */
+
+class LIBK3B_EXPORT K3bAudioDoc : public K3bDoc
+{
+ Q_OBJECT
+
+ friend class K3bMixedDoc;
+ friend class K3bAudioTrack;
+ friend class K3bAudioFile;
+
+ public:
+ K3bAudioDoc( QObject* );
+ ~K3bAudioDoc();
+
+ QString name() const;
+
+ bool newDocument();
+
+ bool hideFirstTrack() const { return m_hideFirstTrack; }
+ int numOfTracks() const;
+
+ bool normalize() const { return m_normalize; }
+
+ K3bAudioTrack* firstTrack() const;
+ K3bAudioTrack* lastTrack() const;
+
+ /**
+ * Slow.
+ * \return the K3bAudioTrack with track number trackNum starting at 1 or 0 if trackNum > numOfTracks()
+ */
+ K3bAudioTrack* getTrack( unsigned int trackNum );
+
+ /**
+ * Creates a new audiofile inside this doc which has no track yet.
+ */
+ K3bAudioFile* createAudioFile( const KURL& url );
+
+ /** get the current size of the project */
+ KIO::filesize_t size() const;
+ K3b::Msf length() const;
+
+ // CD-Text
+ bool cdText() const { return m_cdText; }
+ const QString& title() const { return m_cdTextData.title(); }
+ const QString& artist() const { return m_cdTextData.performer(); }
+ const QString& disc_id() const { return m_cdTextData.discId(); }
+ const QString& arranger() const { return m_cdTextData.arranger(); }
+ const QString& songwriter() const { return m_cdTextData.songwriter(); }
+ const QString& composer() const { return m_cdTextData.composer(); }
+ const QString& upc_ean() const { return m_cdTextData.upcEan(); }
+ const QString& cdTextMessage() const { return m_cdTextData.message(); }
+
+ /**
+ * Create complete CD-Text including the tracks' data.
+ */
+ K3bDevice::CdText cdTextData() const;
+
+ int audioRippingParanoiaMode() const { return m_audioRippingParanoiaMode; }
+ int audioRippingRetries() const { return m_audioRippingRetries; }
+ bool audioRippingIgnoreReadErrors() const { return m_audioRippingIgnoreReadErrors; }
+
+ /**
+ * Represent the structure of the doc as CD Table of Contents.
+ */
+ K3bDevice::Toc toToc() const;
+
+ K3bBurnJob* newBurnJob( K3bJobHandler*, QObject* parent = 0 );
+
+ /**
+ * Shows dialogs.
+ */
+ void informAboutNotFoundFiles();
+
+ /**
+ * returns the new after track, ie. the the last added track or null if
+ * the import failed.
+ *
+ * This is a blocking method.
+ *
+ * \param cuefile The Cuefile to be imported
+ * \param after The track after which the new tracks should be inserted
+ * \param decoder The decoder to be used for the new tracks. If 0 a new one will be created.
+ *
+ * BE AWARE THAT THE DECODER HAS TO FIT THE AUDIO FILE IN THE CUE.
+ */
+ K3bAudioTrack* importCueFile( const QString& cuefile, K3bAudioTrack* after, K3bAudioDecoder* decoder = 0 );
+
+ /**
+ * Create a decoder for a specific url. If another AudioFileSource with this
+ * url is already part of this project the associated decoder is returned.
+ *
+ * In the first case the decoder will not be initialized yet (K3bAudioDecoder::analyseFile
+ * is not called yet).
+ *
+ * \param url The url for which a decoder is requested.
+ * \param reused If not null this variable is set to true if the decoder is already in
+ * use and K3bAudioDecoder::analyseFile() does not have to be called anymore.
+ */
+ K3bAudioDecoder* getDecoderForUrl( const KURL& url, bool* reused = 0 );
+
+ static bool readPlaylistFile( const KURL& url, KURL::List& playlist );
+
+ public slots:
+ void addUrls( const KURL::List& );
+ void addTrack( const KURL&, uint );
+ void addTracks( const KURL::List&, uint );
+ /**
+ * Adds a track without any testing
+ *
+ * Slow because it uses getTrack.
+ */
+ void addTrack( K3bAudioTrack* track, uint position = 0 );
+
+ void addSources( K3bAudioTrack* parent, const KURL::List& urls, K3bAudioDataSource* sourceAfter = 0 );
+
+ void removeTrack( K3bAudioTrack* );
+ void moveTrack( K3bAudioTrack* track, K3bAudioTrack* after );
+
+ void setHideFirstTrack( bool b ) { m_hideFirstTrack = b; }
+ void setNormalize( bool b ) { m_normalize = b; }
+
+ // CD-Text
+ void writeCdText( bool b ) { m_cdText = b; }
+ void setTitle( const QString& v );
+ void setArtist( const QString& v );
+ void setPerformer( const QString& v );
+ void setDisc_id( const QString& v );
+ void setArranger( const QString& v );
+ void setSongwriter( const QString& v );
+ void setComposer( const QString& v );
+ void setUpc_ean( const QString& v );
+ void setCdTextMessage( const QString& v );
+
+ // Audio-CD Ripping
+ void setAudioRippingParanoiaMode( int i ) { m_audioRippingParanoiaMode = i; }
+ void setAudioRippingRetries( int r ) { m_audioRippingRetries = r; }
+ void setAudioRippingIgnoreReadErrors( bool b ) { m_audioRippingIgnoreReadErrors = b; }
+
+ void removeCorruptTracks();
+
+ private slots:
+ void slotTrackChanged( K3bAudioTrack* );
+ void slotTrackRemoved( K3bAudioTrack* );
+
+ signals:
+ void trackChanged( K3bAudioTrack* );
+ void trackRemoved( K3bAudioTrack* );
+
+ protected:
+ /** reimplemented from K3bDoc */
+ bool loadDocumentData( QDomElement* );
+ /** reimplemented from K3bDoc */
+ bool saveDocumentData( QDomElement* );
+
+ QString typeString() const;
+
+ private:
+ // the stuff for adding files
+ // ---------------------------------------------------------
+ K3bAudioTrack* createTrack( const KURL& url );
+
+ /**
+ * Handle directories and M3u files
+ */
+ KURL::List extractUrlList( const KURL::List& urls );
+ // ---------------------------------------------------------
+
+ /**
+ * Used by K3bAudioTrack to update the track list
+ */
+ void setFirstTrack( K3bAudioTrack* track );
+ /**
+ * Used by K3bAudioTrack to update the track list
+ */
+ void setLastTrack( K3bAudioTrack* track );
+
+ /**
+ * Used by K3bAudioFile to tell the doc that it does not need the decoder anymore.
+ */
+ void decreaseDecoderUsage( K3bAudioDecoder* );
+ void increaseDecoderUsage( K3bAudioDecoder* );
+
+ K3bAudioTrack* m_firstTrack;
+ K3bAudioTrack* m_lastTrack;
+
+ bool m_hideFirstTrack;
+ bool m_normalize;
+
+ KURL::List m_notFoundFiles;
+ KURL::List m_unknownFileFormatFiles;
+
+ // CD-Text
+ // --------------------------------------------------
+ K3bDevice::CdText m_cdTextData;
+ bool m_cdText;
+ // --------------------------------------------------
+
+ // Audio ripping
+ int m_audioRippingParanoiaMode;
+ int m_audioRippingRetries;
+ bool m_audioRippingIgnoreReadErrors;
+
+ //
+ // decoder housekeeping
+ // --------------------------------------------------
+ // used to check if we may delete a decoder
+ QMap<K3bAudioDecoder*, int> m_decoderUsageCounterMap;
+ // used to check if we already have a decoder for a specific file
+ QMap<QString, K3bAudioDecoder*> m_decoderPresenceMap;
+
+ class Private;
+ Private* d;
+};
+
+
+#endif
diff --git a/libk3b/projects/audiocd/k3baudiofile.cpp b/libk3b/projects/audiocd/k3baudiofile.cpp
new file mode 100644
index 0000000..2011e73
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiofile.cpp
@@ -0,0 +1,112 @@
+/*
+ *
+ * $Id: k3baudiofile.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2004 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3baudiofile.h"
+#include "k3baudiodoc.h"
+#include "k3baudiotrack.h"
+
+#include <k3baudiodecoder.h>
+
+
+K3bAudioFile::K3bAudioFile( K3bAudioDecoder* dec, K3bAudioDoc* doc )
+ : K3bAudioDataSource(),
+ m_doc(doc),
+ m_decoder(dec),
+ m_decodedData(0)
+{
+ // FIXME: somehow make it possible to switch docs
+ doc->increaseDecoderUsage( m_decoder );
+}
+
+
+K3bAudioFile::K3bAudioFile( const K3bAudioFile& file )
+ : K3bAudioDataSource( file ),
+ m_doc( file.m_doc ),
+ m_decoder( file.m_decoder ),
+ m_decodedData(0)
+{
+ m_doc->increaseDecoderUsage( m_decoder );
+}
+
+
+K3bAudioFile::~K3bAudioFile()
+{
+ m_doc->decreaseDecoderUsage( m_decoder );
+}
+
+
+QString K3bAudioFile::type() const
+{
+ return m_decoder->fileType();
+}
+
+
+QString K3bAudioFile::sourceComment() const
+{
+ return m_decoder->filename().section( "/", -1 );
+}
+
+
+const QString& K3bAudioFile::filename() const
+{
+ return m_decoder->filename();
+}
+
+
+bool K3bAudioFile::isValid() const
+{
+ return m_decoder->isValid();
+}
+
+
+K3b::Msf K3bAudioFile::originalLength() const
+{
+ return m_decoder->length();
+}
+
+
+bool K3bAudioFile::seek( const K3b::Msf& msf )
+{
+ // this is valid once the decoder has been initialized.
+ if( startOffset() + msf <= lastSector() &&
+ m_decoder->seek( startOffset() + msf ) ) {
+ m_decodedData = msf.audioBytes();
+ return true;
+ }
+ else
+ return false;
+}
+
+
+int K3bAudioFile::read( char* data, unsigned int max )
+{
+ // here we can trust on the decoder to always provide enough data
+ // see if we decode too much
+ if( max + m_decodedData > length().audioBytes() )
+ max = length().audioBytes() - m_decodedData;
+
+ int read = m_decoder->decode( data, max );
+
+ if( read > 0 )
+ m_decodedData += read;
+
+ return read;
+}
+
+
+K3bAudioDataSource* K3bAudioFile::copy() const
+{
+ return new K3bAudioFile( *this );
+}
diff --git a/libk3b/projects/audiocd/k3baudiofile.h b/libk3b/projects/audiocd/k3baudiofile.h
new file mode 100644
index 0000000..83f75eb
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiofile.h
@@ -0,0 +1,85 @@
+/*
+ *
+ * $Id: k3baudiofile.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2004 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_AUDIO_FILE_H_
+#define _K3B_AUDIO_FILE_H_
+
+#include "k3baudiodatasource.h"
+
+#include <k3bmsf.h>
+#include <kurl.h>
+#include "k3b_export.h"
+
+class K3bAudioDecoder;
+class K3bAudioTrack;
+
+
+/**
+ * The K3bAudioFile is the most important audio data source. It gets its data
+ * from an audio file and uses a K3bAudioDecoder to decode this data.
+ *
+ * Be aware that it is currently not possible to change the doc of an AudioFile.
+ * The reason for this is the decoder sharing which is in place to allow gapless
+ * splitting of audio files into several tracks.
+ *
+ * \see K3bAudioDoc::createDecoderForUrl
+ */
+class LIBK3B_EXPORT K3bAudioFile : public K3bAudioDataSource
+{
+ public:
+ /**
+ * The AudioFile registers itself with the doc. This is part of the
+ * decoder handling facility in K3bAudioDoc which reuses the same decoder
+ * for sources with the same url.
+ *
+ * Use K3bAudioDoc::getDecoderForUrl to create a decoder.
+ */
+ K3bAudioFile( K3bAudioDecoder*, K3bAudioDoc* );
+ K3bAudioFile( const K3bAudioFile& );
+
+ /**
+ * The AudioFile deregisters itself from the doc. If it was the last file
+ * to use the decoder the doc will take care of deleting it.
+ */
+ ~K3bAudioFile();
+
+ const QString& filename() const;
+
+ /**
+ * The complete length of the file used by this source.
+ */
+ K3b::Msf originalLength() const;
+
+ QString type() const;
+ QString sourceComment() const;
+
+ bool isValid() const;
+
+ K3bAudioDecoder* decoder() const { return m_decoder; }
+
+ bool seek( const K3b::Msf& );
+
+ int read( char* data, unsigned int max );
+
+ K3bAudioDataSource* copy() const;
+
+ private:
+ K3bAudioDoc* m_doc;
+ K3bAudioDecoder* m_decoder;
+
+ unsigned long long m_decodedData;
+};
+
+#endif
diff --git a/libk3b/projects/audiocd/k3baudioimager.cpp b/libk3b/projects/audiocd/k3baudioimager.cpp
new file mode 100644
index 0000000..b8a7a11
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudioimager.cpp
@@ -0,0 +1,203 @@
+/*
+ *
+ * $Id: k3baudioimager.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2004 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3baudioimager.h"
+#include "k3baudiodoc.h"
+#include "k3baudiotrack.h"
+#include "k3baudiodatasource.h"
+
+#include <k3bthread.h>
+#include <k3bwavefilewriter.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <qfile.h>
+
+#include <unistd.h>
+
+
+class K3bAudioImager::WorkThread : public K3bThread
+{
+public:
+ WorkThread( K3bAudioDoc* doc );
+
+ void run();
+
+ void cancel();
+
+ bool m_canceled;
+ int m_fd;
+ QStringList m_imageNames;
+ K3bAudioImager::ErrorType lastError;
+
+private:
+ K3bAudioDoc* m_doc;
+};
+
+
+K3bAudioImager::WorkThread::WorkThread( K3bAudioDoc* doc )
+ : K3bThread(),
+ m_canceled(false),
+ m_fd(-1),
+ m_doc(doc)
+{
+}
+
+
+void K3bAudioImager::WorkThread::run()
+{
+ m_canceled = false;
+
+ emitStarted();
+
+ lastError = K3bAudioImager::ERROR_UNKNOWN;
+
+ //
+ //
+ //
+ QStringList::iterator imageFileIt = m_imageNames.begin();
+ K3bWaveFileWriter waveFileWriter;
+
+ K3bAudioTrack* track = m_doc->firstTrack();
+ int trackNumber = 1;
+ unsigned long long totalSize = m_doc->length().audioBytes();
+ unsigned long long totalRead = 0;
+ char buffer[2352 * 10];
+
+ while( track ) {
+
+ emitNextTrack( trackNumber, m_doc->numOfTracks() );
+
+ //
+ // Seek to the beginning of the track
+ //
+ if( !track->seek(0) ) {
+ emitInfoMessage( i18n("Unable to seek in track %1.").arg(trackNumber), K3bJob::ERROR );
+ emitFinished(false);
+ return;
+ }
+
+ //
+ // Initialize the reading
+ //
+ int read = 0;
+ unsigned long long trackRead = 0;
+
+ //
+ // Create the image file
+ //
+ if( m_fd == -1 ) {
+ if( !waveFileWriter.open( *imageFileIt ) ) {
+ emitInfoMessage( i18n("Could not open %1 for writing").arg(*imageFileIt), K3bJob::ERROR );
+ emitFinished(false);
+ return;
+ }
+ }
+
+ //
+ // Read data from the track
+ //
+ while( (read = track->read( buffer, sizeof(buffer) )) > 0 ) {
+ if( m_fd == -1 ) {
+ waveFileWriter.write( buffer, read, K3bWaveFileWriter::BigEndian );
+ }
+ else {
+ if( ::write( m_fd, reinterpret_cast<void*>(buffer), read ) != read ) {
+ kdDebug() << "(K3bAudioImager::WorkThread) writing to fd " << m_fd << " failed." << endl;
+ lastError = K3bAudioImager::ERROR_FD_WRITE;
+ emitFinished(false);
+ return;
+ }
+ }
+
+ if( m_canceled ) {
+ emitCanceled();
+ emitFinished(false);
+ return;
+ }
+
+ //
+ // Emit progress
+ //
+ totalRead += read;
+ trackRead += read;
+
+ emitSubPercent( 100*trackRead/track->length().audioBytes() );
+ emitPercent( 100*totalRead/totalSize );
+ emitProcessedSubSize( trackRead/1024/1024, track->length().audioBytes()/1024/1024 );
+ emitProcessedSize( totalRead/1024/1024, totalSize/1024/1024 );
+ }
+
+ if( read < 0 ) {
+ emitInfoMessage( i18n("Error while decoding track %1.").arg(trackNumber), K3bJob::ERROR );
+ kdDebug() << "(K3bAudioImager::WorkThread) read error on track " << trackNumber
+ << " at pos " << K3b::Msf(trackRead/2352) << endl;
+ lastError = K3bAudioImager::ERROR_DECODING_TRACK;
+ emitFinished(false);
+ return;
+ }
+
+ track = track->next();
+ trackNumber++;
+ imageFileIt++;
+ }
+
+ emitFinished(true);
+}
+
+
+void K3bAudioImager::WorkThread::cancel()
+{
+ m_canceled = true;
+}
+
+
+
+
+K3bAudioImager::K3bAudioImager( K3bAudioDoc* doc, K3bJobHandler* jh, QObject* parent, const char* name )
+ : K3bThreadJob( jh, parent, name ),
+ m_doc(doc)
+{
+ m_thread = new WorkThread(doc);
+ setThread( m_thread );
+}
+
+
+K3bAudioImager::~K3bAudioImager()
+{
+ delete m_thread;
+}
+
+
+void K3bAudioImager::writeToFd( int fd )
+{
+ m_thread->m_fd = fd;
+}
+
+
+void K3bAudioImager::setImageFilenames( const QStringList& p )
+{
+ m_thread->m_imageNames = p;
+ m_thread->m_fd = -1;
+}
+
+
+K3bAudioImager::ErrorType K3bAudioImager::lastErrorType() const
+{
+ return m_thread->lastError;
+}
+
+#include "k3baudioimager.moc"
diff --git a/libk3b/projects/audiocd/k3baudioimager.h b/libk3b/projects/audiocd/k3baudioimager.h
new file mode 100644
index 0000000..df4ae7a
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudioimager.h
@@ -0,0 +1,59 @@
+/*
+ *
+ * $Id: k3baudioimager.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2004 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_AUDIO_IMAGER_H_
+#define _K3B_AUDIO_IMAGER_H_
+
+#include <k3bthreadjob.h>
+
+class K3bAudioDoc;
+
+class K3bAudioImager : public K3bThreadJob
+{
+ Q_OBJECT
+
+ public:
+ K3bAudioImager( K3bAudioDoc*, K3bJobHandler*, QObject* parent = 0, const char* name = 0 );
+ ~K3bAudioImager();
+
+ /**
+ * the data gets written directly into fd instead of the imagefile.
+ * Be aware that this only makes sense before starting the job.
+ * To disable just set fd to -1
+ */
+ void writeToFd( int fd );
+
+ /**
+ * Image path. Should be an empty directory or a non-existing
+ * directory in which case it will be created.
+ */
+ void setImageFilenames( const QStringList& p );
+
+ enum ErrorType {
+ ERROR_FD_WRITE,
+ ERROR_DECODING_TRACK,
+ ERROR_UNKNOWN
+ };
+
+ ErrorType lastErrorType() const;
+
+ private:
+ K3bAudioDoc* m_doc;
+
+ class WorkThread;
+ WorkThread* m_thread;
+};
+
+#endif
diff --git a/libk3b/projects/audiocd/k3baudiojob.cpp b/libk3b/projects/audiocd/k3baudiojob.cpp
new file mode 100644
index 0000000..c2e62c2
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiojob.cpp
@@ -0,0 +1,864 @@
+/*
+ *
+ * $Id: k3baudiojob.cpp 690212 2007-07-20 11:02:13Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3baudiojob.h"
+
+#include "k3baudioimager.h"
+#include <k3baudiodoc.h>
+#include "k3baudiotrack.h"
+#include "k3baudiodatasource.h"
+#include "k3baudionormalizejob.h"
+#include "k3baudiojobtempdata.h"
+#include "k3baudiomaxspeedjob.h"
+#include "k3baudiocdtracksource.h"
+#include "k3baudiofile.h"
+#include <k3bdevicemanager.h>
+#include <k3bdevicehandler.h>
+#include <k3bdevice.h>
+#include <k3bcdtext.h>
+#include <k3bmsf.h>
+#include <k3bglobals.h>
+#include <k3bexternalbinmanager.h>
+#include <k3bcore.h>
+#include <k3bcdrecordwriter.h>
+#include <k3bcdrdaowriter.h>
+#include <k3btocfilewriter.h>
+#include <k3binffilewriter.h>
+
+#include <qfile.h>
+#include <qvaluevector.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <ktempfile.h>
+#include <kstringhandler.h>
+
+
+
+static QString createNonExistingFilesString( const QValueList<K3bAudioFile*>& items, unsigned int max )
+{
+ QString s;
+ unsigned int cnt = 0;
+ for( QValueList<K3bAudioFile*>::const_iterator it = items.begin();
+ it != items.end(); ++it ) {
+
+ s += KStringHandler::csqueeze( (*it)->filename(), 60 );
+
+ ++cnt;
+ if( cnt >= max || it == items.end() )
+ break;
+
+ s += "<br>";
+ }
+
+ if( items.count() > max )
+ s += "...";
+
+ return s;
+}
+
+
+
+class K3bAudioJob::Private
+{
+ public:
+ Private()
+ : copies(1),
+ copiesDone(0) {
+ }
+
+ int copies;
+ int copiesDone;
+ int usedSpeed;
+
+ bool useCdText;
+ bool maxSpeed;
+
+ bool zeroPregap;
+ bool less4Sec;
+};
+
+
+K3bAudioJob::K3bAudioJob( K3bAudioDoc* doc, K3bJobHandler* hdl, QObject* parent )
+ : K3bBurnJob( hdl, parent ),
+ m_doc( doc ),
+ m_normalizeJob(0),
+ m_maxSpeedJob(0)
+{
+ d = new Private;
+
+ m_audioImager = new K3bAudioImager( m_doc, this, this );
+ connect( m_audioImager, SIGNAL(infoMessage(const QString&, int)),
+ this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( m_audioImager, SIGNAL(percent(int)),
+ this, SLOT(slotAudioDecoderPercent(int)) );
+ connect( m_audioImager, SIGNAL(subPercent(int)),
+ this, SLOT(slotAudioDecoderSubPercent(int)) );
+ connect( m_audioImager, SIGNAL(finished(bool)),
+ this, SLOT(slotAudioDecoderFinished(bool)) );
+ connect( m_audioImager, SIGNAL(nextTrack(int, int)),
+ this, SLOT(slotAudioDecoderNextTrack(int, int)) );
+
+ m_writer = 0;
+ m_tempData = new K3bAudioJobTempData( m_doc, this );
+}
+
+
+K3bAudioJob::~K3bAudioJob()
+{
+ delete d;
+}
+
+
+K3bDevice::Device* K3bAudioJob::writer() const
+{
+ if( m_doc->onlyCreateImages() )
+ return 0; // no writer needed -> no blocking on K3bBurnJob
+ else
+ return m_doc->burner();
+}
+
+
+K3bDoc* K3bAudioJob::doc() const
+{
+ return m_doc;
+}
+
+
+void K3bAudioJob::start()
+{
+ jobStarted();
+
+ m_written = true;
+ m_canceled = false;
+ m_errorOccuredAndAlreadyReported = false;
+ d->copies = m_doc->copies();
+ d->copiesDone = 0;
+ d->useCdText = m_doc->cdText();
+ d->usedSpeed = m_doc->speed();
+ d->maxSpeed = false;
+
+ if( m_doc->dummy() )
+ d->copies = 1;
+
+ emit newTask( i18n("Preparing data") );
+
+ //
+ // Check if all files exist
+ //
+ QValueList<K3bAudioFile*> nonExistingFiles;
+ K3bAudioTrack* track = m_doc->firstTrack();
+ while( track ) {
+ K3bAudioDataSource* source = track->firstSource();
+ while( source ) {
+ if( K3bAudioFile* file = dynamic_cast<K3bAudioFile*>( source ) ) {
+ if( !QFile::exists( file->filename() ) )
+ nonExistingFiles.append( file );
+ }
+ source = source->next();
+ }
+ track = track->next();
+ }
+ if( !nonExistingFiles.isEmpty() ) {
+ if( questionYesNo( "<p>" + i18n("The following files could not be found. Do you want to remove them from the "
+ "project and continue without adding them to the image?") +
+ "<p>" + createNonExistingFilesString( nonExistingFiles, 10 ),
+ i18n("Warning"),
+ i18n("Remove missing files and continue"),
+ i18n("Cancel and go back") ) ) {
+ for( QValueList<K3bAudioFile*>::const_iterator it = nonExistingFiles.begin();
+ it != nonExistingFiles.end(); ++it ) {
+ delete *it;
+ }
+ }
+ else {
+ m_canceled = true;
+ emit canceled();
+ jobFinished(false);
+ return;
+ }
+ }
+
+ //
+ // Make sure the project is not empty
+ //
+ if( m_doc->numOfTracks() == 0 ) {
+ emit infoMessage( i18n("Please add files to your project first."), ERROR );
+ jobFinished(false);
+ return;
+ }
+
+ if( m_doc->onTheFly() && !checkAudioSources() ) {
+ emit infoMessage( i18n("Unable to write on-the-fly with these audio sources."), WARNING );
+ m_doc->setOnTheFly(false);
+ }
+
+
+ // we don't need this when only creating image and it is possible
+ // that the burn device is null
+ if( !m_doc->onlyCreateImages() ) {
+
+ //
+ // there are a lot of writers out there which produce coasters
+ // in dao mode if the CD contains pregaps of length 0 (or maybe already != 2 secs?)
+ //
+ // Also most writers do not accept cuesheets with tracks smaller than 4 seconds (a violation
+ // of the red book standard) in DAO mode.
+ //
+ d->zeroPregap = false;
+ d->less4Sec = false;
+ track = m_doc->firstTrack();
+ while( track ) {
+ if( track->postGap() == 0 && track->next() != 0 ) // the last track's postgap is always 0
+ d->zeroPregap = true;
+
+ if( track->length() < K3b::Msf( 0, 4, 0 ) )
+ d->less4Sec = true;
+
+ track = track->next();
+ }
+
+ // determine writing mode
+ if( m_doc->writingMode() == K3b::WRITING_MODE_AUTO ) {
+ //
+ // DAO is always the first choice
+ // RAW second and TAO last
+ // there are none-DAO writers that are supported by cdrdao
+ //
+ // older cdrecord versions do not support the -shorttrack option in RAW writing mode
+ //
+ if( !writer()->dao() && writingApp() == K3b::CDRECORD ) {
+ if(!writer()->supportsRawWriting() &&
+ ( !d->less4Sec || k3bcore->externalBinManager()->binObject("cdrecord")->hasFeature( "short-track-raw" ) ) )
+ m_usedWritingMode = K3b::RAW;
+ else
+ m_usedWritingMode = K3b::TAO;
+ }
+ else {
+ if( (d->zeroPregap||d->less4Sec) && writer()->supportsRawWriting() ) {
+ m_usedWritingMode = K3b::RAW;
+ if( d->less4Sec )
+ emit infoMessage( i18n("Tracklengths below 4 seconds violate the Red Book standard."), WARNING );
+ }
+ else
+ m_usedWritingMode = K3b::DAO;
+ }
+ }
+ else
+ m_usedWritingMode = m_doc->writingMode();
+
+ bool cdrecordOnTheFly = false;
+ bool cdrecordCdText = false;
+ if( k3bcore->externalBinManager()->binObject("cdrecord") ) {
+ cdrecordOnTheFly = k3bcore->externalBinManager()->binObject("cdrecord")->hasFeature( "audio-stdin" );
+ cdrecordCdText = k3bcore->externalBinManager()->binObject("cdrecord")->hasFeature( "cdtext" );
+ }
+
+ // determine writing app
+ if( writingApp() == K3b::DEFAULT ) {
+ if( m_usedWritingMode == K3b::DAO ) {
+ // there are none-DAO writers that are supported by cdrdao
+ if( !writer()->dao() ||
+ ( !cdrecordOnTheFly && m_doc->onTheFly() ) ||
+ ( d->useCdText && !cdrecordCdText ) ||
+ m_doc->hideFirstTrack() )
+ m_usedWritingApp = K3b::CDRDAO;
+ else
+ m_usedWritingApp = K3b::CDRECORD;
+ }
+ else
+ m_usedWritingApp = K3b::CDRECORD;
+ }
+ else
+ m_usedWritingApp = writingApp();
+
+ // on-the-fly writing with cdrecord >= 2.01a13
+ if( m_usedWritingApp == K3b::CDRECORD &&
+ m_doc->onTheFly() &&
+ !cdrecordOnTheFly ) {
+ emit infoMessage( i18n("On-the-fly writing with cdrecord < 2.01a13 not supported."), ERROR );
+ m_doc->setOnTheFly(false);
+ }
+
+ if( m_usedWritingApp == K3b::CDRECORD &&
+ d->useCdText ) {
+ if( !cdrecordCdText ) {
+ emit infoMessage( i18n("Cdrecord %1 does not support CD-Text writing.")
+ .arg(k3bcore->externalBinManager()->binObject("cdrecord")->version), ERROR );
+ d->useCdText = false;
+ }
+ else if( m_usedWritingMode == K3b::TAO ) {
+ emit infoMessage( i18n("It is not possible to write CD-Text in TAO mode."), WARNING );
+ d->useCdText = false;
+ }
+ }
+ }
+
+
+ if( !m_doc->onlyCreateImages() && m_doc->onTheFly() ) {
+ if( m_doc->speed() == 0 ) {
+ // try to determine the max possible speed
+ emit newSubTask( i18n("Determining maximum writing speed") );
+ if( !m_maxSpeedJob ) {
+ m_maxSpeedJob = new K3bAudioMaxSpeedJob( m_doc, this, this );
+ connect( m_maxSpeedJob, SIGNAL(percent(int)),
+ this, SIGNAL(subPercent(int)) );
+ connect( m_maxSpeedJob, SIGNAL(finished(bool)),
+ this, SLOT(slotMaxSpeedJobFinished(bool)) );
+ }
+ m_maxSpeedJob->start();
+ return;
+ }
+ else {
+ if( !prepareWriter() ) {
+ cleanupAfterError();
+ jobFinished(false);
+ return;
+ }
+
+ if( startWriting() ) {
+
+ // now the writer is running and we can get it's stdin
+ // we only use this method when writing on-the-fly since
+ // we cannot easily change the audioDecode fd while it's working
+ // which we would need to do since we write into several
+ // image files.
+ m_audioImager->writeToFd( m_writer->fd() );
+ }
+ else {
+ // startWriting() already did the cleanup
+ return;
+ }
+ }
+ }
+ else {
+ emit burning(false);
+ emit infoMessage( i18n("Creating image files in %1").arg(m_doc->tempDir()), INFO );
+ emit newTask( i18n("Creating image files") );
+ m_tempData->prepareTempFileNames( doc()->tempDir() );
+ QStringList filenames;
+ for( int i = 1; i <= m_doc->numOfTracks(); ++i )
+ filenames += m_tempData->bufferFileName( i );
+ m_audioImager->setImageFilenames( filenames );
+ }
+
+ m_audioImager->start();
+}
+
+
+void K3bAudioJob::slotMaxSpeedJobFinished( bool success )
+{
+ d->maxSpeed = success;
+ if( !success )
+ emit infoMessage( i18n("Unable to determine maximum speed for some reason. Ignoring."), WARNING );
+
+ // now start the writing
+ // same code as above. See the commecnts there
+ if( !prepareWriter() ) {
+ cleanupAfterError();
+ jobFinished(false);
+ return;
+ }
+
+ if( startWriting() )
+ m_audioImager->writeToFd( m_writer->fd() );
+
+ m_audioImager->start();
+}
+
+
+void K3bAudioJob::cancel()
+{
+ m_canceled = true;
+
+ if( m_maxSpeedJob )
+ m_maxSpeedJob->cancel();
+
+ if( m_writer )
+ m_writer->cancel();
+
+ m_audioImager->cancel();
+ emit infoMessage( i18n("Writing canceled."), K3bJob::ERROR );
+ removeBufferFiles();
+ emit canceled();
+ jobFinished(false);
+}
+
+
+void K3bAudioJob::slotWriterFinished( bool success )
+{
+ if( m_canceled || m_errorOccuredAndAlreadyReported )
+ return;
+
+ if( !success ) {
+ cleanupAfterError();
+ jobFinished(false);
+ return;
+ }
+ else {
+ d->copiesDone++;
+
+ if( d->copiesDone == d->copies ) {
+ if( m_doc->onTheFly() || m_doc->removeImages() )
+ removeBufferFiles();
+
+ jobFinished(true);
+ }
+ else {
+ K3bDevice::eject( m_doc->burner() );
+
+ if( startWriting() ) {
+ if( m_doc->onTheFly() ) {
+ // now the writer is running and we can get it's stdin
+ // we only use this method when writing on-the-fly since
+ // we cannot easily change the audioDecode fd while it's working
+ // which we would need to do since we write into several
+ // image files.
+ m_audioImager->writeToFd( m_writer->fd() );
+ m_audioImager->start();
+ }
+ }
+ }
+ }
+}
+
+
+void K3bAudioJob::slotAudioDecoderFinished( bool success )
+{
+ if( m_canceled || m_errorOccuredAndAlreadyReported )
+ return;
+
+ if( !success ) {
+ if( m_audioImager->lastErrorType() == K3bAudioImager::ERROR_FD_WRITE ) {
+ // this means that the writer job failed so let's use the error handling there.
+ return;
+ }
+
+ emit infoMessage( i18n("Error while decoding audio tracks."), ERROR );
+ cleanupAfterError();
+ jobFinished(false);
+ return;
+ }
+
+ if( m_doc->onlyCreateImages() || !m_doc->onTheFly() ) {
+
+ emit infoMessage( i18n("Successfully decoded all tracks."), SUCCESS );
+
+ if( m_doc->normalize() ) {
+ normalizeFiles();
+ }
+ else if( !m_doc->onlyCreateImages() ) {
+ if( !prepareWriter() ) {
+ cleanupAfterError();
+ jobFinished(false);
+ }
+ else
+ startWriting();
+ }
+ else {
+ jobFinished(true);
+ }
+ }
+}
+
+
+void K3bAudioJob::slotAudioDecoderNextTrack( int t, int tt )
+{
+ if( m_doc->onlyCreateImages() || !m_doc->onTheFly() ) {
+ K3bAudioTrack* track = m_doc->getTrack(t);
+ emit newSubTask( i18n("Decoding audio track %1 of %2%3")
+ .arg(t)
+ .arg(tt)
+ .arg( track->title().isEmpty() || track->artist().isEmpty()
+ ? QString::null
+ : " (" + track->artist() + " - " + track->title() + ")" ) );
+ }
+}
+
+
+bool K3bAudioJob::prepareWriter()
+{
+ delete m_writer;
+
+ if( m_usedWritingApp == K3b::CDRECORD ) {
+
+ if( !writeInfFiles() ) {
+ kdDebug() << "(K3bAudioJob) could not write inf-files." << endl;
+ emit infoMessage( i18n("IO Error. Most likely no space left on harddisk."), ERROR );
+
+ return false;
+ }
+
+ K3bCdrecordWriter* writer = new K3bCdrecordWriter( m_doc->burner(), this, this );
+
+ writer->setWritingMode( m_usedWritingMode );
+ writer->setSimulate( m_doc->dummy() );
+ writer->setBurnSpeed( d->usedSpeed );
+
+ writer->addArgument( "-useinfo" );
+
+ if( d->useCdText ) {
+ writer->setRawCdText( m_doc->cdTextData().rawPackData() );
+ }
+
+ // add all the audio tracks
+ writer->addArgument( "-audio" );
+
+ // we only need to pad in one case. cdrecord < 2.01.01a03 cannot handle shorttrack + raw
+ if( d->less4Sec ) {
+ if( m_usedWritingMode == K3b::RAW &&
+ !k3bcore->externalBinManager()->binObject( "cdrecord" )->hasFeature( "short-track-raw" ) ) {
+ writer->addArgument( "-pad" );
+ }
+ else {
+ // Allow tracks shorter than 4 seconds
+ writer->addArgument( "-shorttrack" );
+ }
+ }
+
+ K3bAudioTrack* track = m_doc->firstTrack();
+ while( track ) {
+ if( m_doc->onTheFly() ) {
+ // this is only supported by cdrecord versions >= 2.01a13
+ writer->addArgument( QFile::encodeName( m_tempData->infFileName( track ) ) );
+ }
+ else {
+ writer->addArgument( QFile::encodeName( m_tempData->bufferFileName( track ) ) );
+ }
+ track = track->next();
+ }
+
+ m_writer = writer;
+ }
+ else {
+ if( !writeTocFile() ) {
+ kdDebug() << "(K3bDataJob) could not write tocfile." << endl;
+ emit infoMessage( i18n("IO Error"), ERROR );
+
+ return false;
+ }
+
+ // create the writer
+ // create cdrdao job
+ K3bCdrdaoWriter* writer = new K3bCdrdaoWriter( m_doc->burner(), this, this );
+ writer->setCommand( K3bCdrdaoWriter::WRITE );
+ writer->setSimulate( m_doc->dummy() );
+ writer->setBurnSpeed( d->usedSpeed );
+ writer->setTocFile( m_tempData->tocFileName() );
+
+ m_writer = writer;
+ }
+
+ connect( m_writer, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( m_writer, SIGNAL(percent(int)), this, SLOT(slotWriterJobPercent(int)) );
+ connect( m_writer, SIGNAL(processedSize(int, int)), this, SIGNAL(processedSize(int, int)) );
+ connect( m_writer, SIGNAL(subPercent(int)), this, SIGNAL(subPercent(int)) );
+ connect( m_writer, SIGNAL(processedSubSize(int, int)), this, SIGNAL(processedSubSize(int, int)) );
+ connect( m_writer, SIGNAL(nextTrack(int, int)), this, SLOT(slotWriterNextTrack(int, int)) );
+ connect( m_writer, SIGNAL(buffer(int)), this, SIGNAL(bufferStatus(int)) );
+ connect( m_writer, SIGNAL(deviceBuffer(int)), this, SIGNAL(deviceBuffer(int)) );
+ connect( m_writer, SIGNAL(writeSpeed(int, int)), this, SIGNAL(writeSpeed(int, int)) );
+ connect( m_writer, SIGNAL(finished(bool)), this, SLOT(slotWriterFinished(bool)) );
+ // connect( m_writer, SIGNAL(newTask(const QString&)), this, SIGNAL(newTask(const QString&)) );
+ connect( m_writer, SIGNAL(newSubTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) );
+ connect( m_writer, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+
+ return true;
+}
+
+
+void K3bAudioJob::slotWriterNextTrack( int t, int tt )
+{
+ K3bAudioTrack* track = m_doc->getTrack(t);
+ // t is in range 1..tt
+ if( m_doc->hideFirstTrack() )
+ track = m_doc->getTrack(t+1);
+ emit newSubTask( i18n("Writing track %1 of %2%3")
+ .arg(t)
+ .arg(tt)
+ .arg( track->title().isEmpty() || track->artist().isEmpty()
+ ? QString::null
+ : " (" + track->artist() + " - " + track->title() + ")" ) );
+}
+
+
+void K3bAudioJob::slotWriterJobPercent( int p )
+{
+ double totalTasks = d->copies;
+ double tasksDone = d->copiesDone;
+ if( m_doc->normalize() ) {
+ totalTasks+=1.0;
+ tasksDone+=1.0;
+ }
+ if( !m_doc->onTheFly() ) {
+ totalTasks+=1.0;
+ tasksDone+=1.0;
+ }
+
+ emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
+}
+
+
+void K3bAudioJob::slotAudioDecoderPercent( int p )
+{
+ if( m_doc->onlyCreateImages() ) {
+ if( m_doc->normalize() )
+ emit percent( p/2 );
+ else
+ emit percent( p );
+ }
+ else if( !m_doc->onTheFly() ) {
+ double totalTasks = d->copies;
+ double tasksDone = d->copiesDone; // =0 when creating an image
+ if( m_doc->normalize() ) {
+ totalTasks+=1.0;
+ }
+ if( !m_doc->onTheFly() ) {
+ totalTasks+=1.0;
+ }
+
+ emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
+ }
+}
+
+
+void K3bAudioJob::slotAudioDecoderSubPercent( int p )
+{
+ // when writing on the fly the writer produces the subPercent
+ if( m_doc->onlyCreateImages() || !m_doc->onTheFly() ) {
+ emit subPercent( p );
+ }
+}
+
+
+bool K3bAudioJob::startWriting()
+{
+ if( m_doc->dummy() )
+ emit newTask( i18n("Simulating") );
+ else if( d->copies > 1 )
+ emit newTask( i18n("Writing Copy %1").arg(d->copiesDone+1) );
+ else
+ emit newTask( i18n("Writing") );
+
+
+ emit newSubTask( i18n("Waiting for media") );
+ if( waitForMedia( m_doc->burner() ) < 0 ) {
+ cancel();
+ return false;
+ }
+
+ // just to be sure we did not get canceled during the async discWaiting
+ if( m_canceled )
+ return false;
+
+ // in case we determined the max possible writing speed we have to reset the speed on the writer job
+ // here since an inserted media is necessary
+ // the Max speed job will compare the max speed value with the supported values of the writer
+ if( d->maxSpeed )
+ m_writer->setBurnSpeed( m_maxSpeedJob->maxSpeed() );
+
+ emit burning(true);
+ m_writer->start();
+ return true;
+}
+
+
+void K3bAudioJob::cleanupAfterError()
+{
+ m_errorOccuredAndAlreadyReported = true;
+ m_audioImager->cancel();
+
+ if( m_writer )
+ m_writer->cancel();
+
+ // remove the temp files
+ removeBufferFiles();
+}
+
+
+void K3bAudioJob::removeBufferFiles()
+{
+ if ( !m_doc->onTheFly() ) {
+ emit infoMessage( i18n("Removing temporary files."), INFO );
+ }
+
+ // removes buffer images and temp toc or inf files
+ m_tempData->cleanup();
+}
+
+
+void K3bAudioJob::normalizeFiles()
+{
+ if( !m_normalizeJob ) {
+ m_normalizeJob = new K3bAudioNormalizeJob( this, this );
+
+ connect( m_normalizeJob, SIGNAL(infoMessage(const QString&, int)),
+ this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( m_normalizeJob, SIGNAL(percent(int)), this, SLOT(slotNormalizeProgress(int)) );
+ connect( m_normalizeJob, SIGNAL(subPercent(int)), this, SLOT(slotNormalizeSubProgress(int)) );
+ connect( m_normalizeJob, SIGNAL(finished(bool)), this, SLOT(slotNormalizeJobFinished(bool)) );
+ connect( m_normalizeJob, SIGNAL(newTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) );
+ connect( m_normalizeJob, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+ }
+
+ // add all the files
+ // TODO: we may need to split the wave files and put them back together!
+ QValueVector<QString> files;
+ K3bAudioTrack* track = m_doc->firstTrack();
+ while( track ) {
+ files.append( m_tempData->bufferFileName(track) );
+ track = track->next();
+ }
+
+ m_normalizeJob->setFilesToNormalize( files );
+
+ emit newTask( i18n("Normalizing volume levels") );
+ m_normalizeJob->start();
+}
+
+void K3bAudioJob::slotNormalizeJobFinished( bool success )
+{
+ if( m_canceled || m_errorOccuredAndAlreadyReported )
+ return;
+
+ if( success ) {
+ if( m_doc->onlyCreateImages() ) {
+ jobFinished(true);
+ }
+ else {
+ // start the writing
+ if( !prepareWriter() ) {
+ cleanupAfterError();
+ jobFinished(false);
+ }
+ else
+ startWriting();
+ }
+ }
+ else {
+ cleanupAfterError();
+ jobFinished(false);
+ }
+}
+
+void K3bAudioJob::slotNormalizeProgress( int p )
+{
+ double totalTasks = d->copies+2.0;
+ double tasksDone = 1; // the decoding has been finished
+
+ emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
+}
+
+
+void K3bAudioJob::slotNormalizeSubProgress( int p )
+{
+ emit subPercent( p );
+}
+
+
+bool K3bAudioJob::writeTocFile()
+{
+ K3bTocFileWriter tocWriter;
+ tocWriter.setData( m_doc->toToc() );
+ tocWriter.setHideFirstTrack( m_doc->hideFirstTrack() );
+ if( d->useCdText )
+ tocWriter.setCdText( m_doc->cdTextData() );
+ if( !m_doc->onTheFly() ) {
+ QStringList filenames;
+ for( int i = 1; i <= m_doc->numOfTracks(); ++i )
+ filenames += m_tempData->bufferFileName( i );
+ tocWriter.setFilenames( filenames );
+ }
+ return tocWriter.save( m_tempData->tocFileName() );
+}
+
+
+bool K3bAudioJob::writeInfFiles()
+{
+ K3bInfFileWriter infFileWriter;
+ K3bAudioTrack* track = m_doc->firstTrack();
+ while( track ) {
+
+ infFileWriter.setTrack( track->toCdTrack() );
+ infFileWriter.setTrackNumber( track->trackNumber() );
+ if( !m_doc->onTheFly() )
+ infFileWriter.setBigEndian( false );
+
+ if( !infFileWriter.save( m_tempData->infFileName(track) ) )
+ return false;
+
+ track = track->next();
+ }
+ return true;
+}
+
+
+// checks if the doc contains sources from an audio cd which cannot be read on-the-fly
+bool K3bAudioJob::checkAudioSources()
+{
+ K3bAudioTrack* track = m_doc->firstTrack();
+ K3bAudioDataSource* source = track->firstSource();
+
+ while( source ) {
+
+ if( K3bAudioCdTrackSource* cdSource = dynamic_cast<K3bAudioCdTrackSource*>(source) ) {
+ //
+ // If which cases we cannot wite on-the-fly:
+ // 1. the writing device contains one of the audio cds
+ // 2. Well, one of the cds is missing
+ //
+ K3bDevice::Device* dev = cdSource->searchForAudioCD();
+ if( !dev || dev == writer() )
+ return false;
+ else
+ cdSource->setDevice( dev );
+ }
+
+ // next source
+ source = source->next();
+ if( !source ) {
+ track = track->next();
+ if( track )
+ source = track->firstSource();
+ }
+ }
+
+ return true;
+}
+
+
+QString K3bAudioJob::jobDescription() const
+{
+ return i18n("Writing Audio CD")
+ + ( m_doc->title().isEmpty()
+ ? QString::null
+ : QString( " (%1)" ).arg(m_doc->title()) );
+}
+
+
+QString K3bAudioJob::jobDetails() const
+{
+ return ( i18n( "1 track (%1 minutes)",
+ "%n tracks (%1 minutes)",
+ m_doc->numOfTracks() ).arg(m_doc->length().toString())
+ + ( m_doc->copies() > 1 && !m_doc->dummy()
+ ? i18n(" - %n copy", " - %n copies", m_doc->copies())
+ : QString::null ) );
+}
+
+#include "k3baudiojob.moc"
diff --git a/libk3b/projects/audiocd/k3baudiojob.h b/libk3b/projects/audiocd/k3baudiojob.h
new file mode 100644
index 0000000..af37639
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiojob.h
@@ -0,0 +1,107 @@
+/*
+ *
+ * $Id: k3baudiojob.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BAUDIOJOB_H
+#define K3BAUDIOJOB_H
+
+#include <k3bjob.h>
+
+
+class K3bAudioDoc;
+class K3bAudioImager;
+class QFile;
+class QDataStream;
+class K3bAbstractWriter;
+class KTempFile;
+class K3bCdrecordWriter;
+class K3bAudioNormalizeJob;
+class K3bAudioJobTempData;
+class K3bDevice::Device;
+class K3bAudioMaxSpeedJob;
+
+/**
+ *@author Sebastian Trueg
+ */
+class K3bAudioJob : public K3bBurnJob
+{
+ Q_OBJECT
+
+ public:
+ K3bAudioJob( K3bAudioDoc*, K3bJobHandler*, QObject* parent = 0 );
+ ~K3bAudioJob();
+
+ K3bDoc* doc() const;
+ K3bDevice::Device* writer() const;
+
+ QString jobDescription() const;
+ QString jobDetails() const;
+
+ public slots:
+ void cancel();
+ void start();
+
+ protected slots:
+ // writer slots
+ void slotWriterFinished( bool success );
+ void slotWriterNextTrack(int, int);
+ void slotWriterJobPercent(int);
+
+ // audiodecoder slots
+ void slotAudioDecoderFinished( bool );
+ void slotAudioDecoderNextTrack( int, int );
+ void slotAudioDecoderPercent(int);
+ void slotAudioDecoderSubPercent( int );
+
+ // normalizing slots
+ void slotNormalizeJobFinished( bool );
+ void slotNormalizeProgress( int );
+ void slotNormalizeSubProgress( int );
+
+ // max speed
+ void slotMaxSpeedJobFinished( bool );
+
+ private:
+ bool prepareWriter();
+ bool startWriting();
+ void cleanupAfterError();
+ void removeBufferFiles();
+ void normalizeFiles();
+ bool writeTocFile();
+ bool writeInfFiles();
+ bool checkAudioSources();
+
+ K3bAudioDoc* m_doc;
+ K3bAudioImager* m_audioImager;
+ K3bAbstractWriter* m_writer;
+ K3bAudioNormalizeJob* m_normalizeJob;
+ K3bAudioJobTempData* m_tempData;
+ K3bAudioMaxSpeedJob* m_maxSpeedJob;
+
+ KTempFile* m_tocFile;
+
+ bool m_canceled;
+ bool m_errorOccuredAndAlreadyReported;
+
+ bool m_written;
+
+ int m_usedWritingApp;
+ int m_usedWritingMode;
+
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/projects/audiocd/k3baudiojobtempdata.cpp b/libk3b/projects/audiocd/k3baudiojobtempdata.cpp
new file mode 100644
index 0000000..af98c2e
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiojobtempdata.cpp
@@ -0,0 +1,132 @@
+/*
+ *
+ * $Id: k3baudiojobtempdata.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3baudiojobtempdata.h"
+#include "k3baudiodoc.h"
+#include "k3baudiotrack.h"
+#include <k3bglobals.h>
+#include <k3bversion.h>
+#include <k3bmsf.h>
+#include <k3bcore.h>
+
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qvaluevector.h>
+
+#include <kdebug.h>
+
+
+class K3bAudioJobTempData::Private
+{
+public:
+ Private( K3bAudioDoc* _doc )
+ : doc(_doc) {
+ }
+
+ QValueVector<QString> bufferFiles;
+ QValueVector<QString> infFiles;
+ QString tocFile;
+
+ K3bAudioDoc* doc;
+};
+
+
+K3bAudioJobTempData::K3bAudioJobTempData( K3bAudioDoc* doc, QObject* parent, const char* name )
+ : QObject( parent, name )
+{
+ d = new Private( doc );
+}
+
+
+K3bAudioJobTempData::~K3bAudioJobTempData()
+{
+ delete d;
+}
+
+
+const QString& K3bAudioJobTempData::bufferFileName( int track )
+{
+ if( (int)d->bufferFiles.count() < track )
+ prepareTempFileNames();
+ return d->bufferFiles.at(track-1);
+}
+
+const QString& K3bAudioJobTempData::bufferFileName( K3bAudioTrack* track )
+{
+ return bufferFileName( track->trackNumber() );
+}
+
+
+const QString& K3bAudioJobTempData::tocFileName()
+{
+ if( d->tocFile.isEmpty() )
+ prepareTempFileNames();
+ return d->tocFile;
+}
+
+
+const QString& K3bAudioJobTempData::infFileName( int track )
+{
+ if( (int)d->infFiles.count() < track )
+ prepareTempFileNames();
+ return d->infFiles.at( track - 1 );
+}
+
+const QString& K3bAudioJobTempData::infFileName( K3bAudioTrack* track )
+{
+ return infFileName( track->trackNumber() );
+}
+
+
+K3bAudioDoc* K3bAudioJobTempData::doc() const
+{
+ return d->doc;
+}
+
+
+void K3bAudioJobTempData::prepareTempFileNames( const QString& path )
+{
+ d->bufferFiles.clear();
+ d->infFiles.clear();
+
+ QString prefix = K3b::findUniqueFilePrefix( "k3b_audio_", path ) + "_";
+
+ for( int i = 0; i < d->doc->numOfTracks(); i++ ) {
+ d->bufferFiles.append( prefix + QString::number( i+1 ).rightJustify( 2, '0' ) + ".wav" );
+ d->infFiles.append( prefix + QString::number( i+1 ).rightJustify( 2, '0' ) + ".inf" );
+ }
+
+ d->tocFile = prefix + ".toc";
+}
+
+
+void K3bAudioJobTempData::cleanup()
+{
+ for( uint i = 0; i < d->infFiles.count(); ++i ) {
+ if( QFile::exists( d->infFiles[i] ) )
+ QFile::remove( d->infFiles[i] );
+ }
+
+ for( uint i = 0; i < d->bufferFiles.count(); ++i ) {
+ if( QFile::exists( d->bufferFiles[i] ) )
+ QFile::remove( d->bufferFiles[i] );
+ }
+
+ if( QFile::exists( d->tocFile ) )
+ QFile::remove( d->tocFile );
+}
+
+
+#include "k3baudiojobtempdata.moc"
diff --git a/libk3b/projects/audiocd/k3baudiojobtempdata.h b/libk3b/projects/audiocd/k3baudiojobtempdata.h
new file mode 100644
index 0000000..72b753f
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiojobtempdata.h
@@ -0,0 +1,64 @@
+/*
+ *
+ * $Id: k3baudiojobtempdata.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_AUDIO_JOB_TEMPDATA_H_
+#define _K3B_AUDIO_JOB_TEMPDATA_H_
+
+#include <qobject.h>
+#include <k3bmsf.h>
+
+class K3bAudioTrack;
+class K3bAudioDoc;
+class QTextStream;
+
+
+class K3bAudioJobTempData : public QObject
+{
+ Q_OBJECT
+
+ public:
+ K3bAudioJobTempData( K3bAudioDoc* doc, QObject* parent = 0, const char* name = 0 );
+ ~K3bAudioJobTempData();
+
+ const QString& bufferFileName( int track );
+ const QString& bufferFileName( K3bAudioTrack* track );
+
+ const QString& infFileName( int track );
+ const QString& infFileName( K3bAudioTrack* track );
+
+ const QString& tocFileName();
+
+ K3bAudioDoc* doc() const;
+
+ /**
+ * use this if you want
+ * a specific directory
+ * it defaults to the default K3b temp dir
+ */
+ void prepareTempFileNames( const QString& path = QString::null );
+
+ /**
+ * remove all temp files (this does not include the audio buffer files
+ * since these are not created and thus not handled by the K3bAudioJobTempData)
+ */
+ void cleanup();
+
+ private:
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/projects/audiocd/k3baudiomaxspeedjob.cpp b/libk3b/projects/audiocd/k3baudiomaxspeedjob.cpp
new file mode 100644
index 0000000..975ab89
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiomaxspeedjob.cpp
@@ -0,0 +1,224 @@
+/*
+ *
+ * $Id: k3baudiomaxspeedjob.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2005 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3baudiomaxspeedjob.h"
+#include "k3baudiotrack.h"
+#include "k3baudiodatasource.h"
+#include "k3baudiodoc.h"
+#include "k3baudiocdtracksource.h"
+#include "k3baudiodatasourceiterator.h"
+
+#include <k3bdevice.h>
+
+#include <k3bthread.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <qdatetime.h>
+
+
+class K3bAudioMaxSpeedJob::WorkThread : public K3bThread
+{
+public:
+ WorkThread( K3bAudioDoc* doc );
+ ~WorkThread();
+
+ void run();
+
+ int speedTest( K3bAudioDataSource* source );
+ void cancel();
+ int maxSpeedByMedia() const;
+
+ int maxSpeed;
+
+private:
+ K3bAudioDoc* m_doc;
+ bool m_canceled;
+ char* m_buffer;
+};
+
+
+K3bAudioMaxSpeedJob::WorkThread::WorkThread( K3bAudioDoc* doc )
+ : K3bThread(),
+ m_doc(doc),
+ m_canceled(false)
+{
+ m_buffer = new char[2352*10];
+}
+
+
+K3bAudioMaxSpeedJob::WorkThread::~WorkThread()
+{
+ delete [] m_buffer;
+}
+
+
+void K3bAudioMaxSpeedJob::WorkThread::run()
+{
+ kdDebug() << k_funcinfo << endl;
+ m_canceled = false;
+
+ emitStarted();
+
+ K3bAudioDataSourceIterator it( m_doc );
+
+ // count sources for minimal progress info
+ int numSources = 0;
+ int sourcesDone = 0;
+ while( it.current() ) {
+ ++numSources;
+ it.next();
+ }
+
+ bool success = true;
+ maxSpeed = 175*1000;
+ it.first();
+
+ while( it.current() && !m_canceled ) {
+ if( !it.current()->seek(0) ) {
+ kdDebug() << "(K3bAudioMaxSpeedJob) seek failed." << endl;
+ success = false;
+ break;
+ }
+
+ // read some data
+ int speed = speedTest( it.current() );
+
+ ++sourcesDone;
+ emitPercent( 100*numSources/sourcesDone );
+
+ if( speed < 0 ) {
+ success = false;
+ break;
+ }
+ else if( speed > 0 ) {
+ // update the max speed
+ maxSpeed = QMIN( maxSpeed, speed );
+ }
+
+ it.next();
+ }
+
+ if( m_canceled ) {
+ emitCanceled();
+ success = false;
+ }
+
+ if( success )
+ kdDebug() << "(K3bAudioMaxSpeedJob) max speed: " << maxSpeed << endl;
+
+ emitFinished( success );
+}
+
+// returns the amount of data read from this source
+int K3bAudioMaxSpeedJob::WorkThread::speedTest( K3bAudioDataSource* source )
+{
+ //
+ // in case of an audio track source we only test when the cd is inserted since asking the user would
+ // confuse him a lot.
+ //
+ // FIXME: there is still the problem of the spin up time.
+ //
+ if( K3bAudioCdTrackSource* cdts = dynamic_cast<K3bAudioCdTrackSource*>( source ) ) {
+ if( K3bDevice::Device* dev = cdts->searchForAudioCD() ) {
+ cdts->setDevice( dev );
+ }
+ else {
+ kdDebug() << "(K3bAudioMaxSpeedJob) ignoring audio cd track source." << endl;
+ return 0;
+ }
+ }
+
+ QTime t;
+ int dataRead = 0;
+ int r = 0;
+
+ // start the timer
+ t.start();
+
+ // read ten seconds of audio data. This is some value which seemed about right. :)
+ while( dataRead < 2352*75*10 && (r = source->read( m_buffer, 2352*10 )) > 0 ) {
+ dataRead += r;
+ }
+
+ // elapsed millisec
+ int usedT = t.elapsed();
+
+ if( r < 0 ) {
+ kdDebug() << "(K3bAudioMaxSpeedJob) read failure." << endl;
+ return -1;
+ }
+
+ // KB/sec (add 1 millisecond to avoid division by 0)
+ int throughput = (dataRead*1000+usedT)/(usedT+1)/1024;
+ kdDebug() << "(K3bAudioMaxSpeedJob) throughput: " << throughput
+ << " (" << dataRead << "/" << usedT << ")" << endl;
+
+
+ return throughput;
+}
+
+
+void K3bAudioMaxSpeedJob::WorkThread::cancel()
+{
+ kdDebug() << k_funcinfo << endl;
+ m_canceled = true;
+}
+
+
+int K3bAudioMaxSpeedJob::WorkThread::maxSpeedByMedia() const
+{
+ int s = 0;
+
+ QValueList<int> speeds = m_doc->burner()->determineSupportedWriteSpeeds();
+ // simply use what we have and let the writer decide if the speeds are empty
+ if( !speeds.isEmpty() ) {
+ // start with the highest speed and go down the list until we are below our max
+ QValueListIterator<int> it = speeds.end();
+ --it;
+ while( *it > maxSpeed && it != speeds.begin() )
+ --it;
+
+ // this is the first valid speed or the lowest supported one
+ s = *it;
+ kdDebug() << "(K3bAudioMaxSpeedJob) using speed factor: " << (s/175) << endl;
+ }
+
+ return s;
+}
+
+
+
+
+K3bAudioMaxSpeedJob::K3bAudioMaxSpeedJob( K3bAudioDoc* doc, K3bJobHandler* jh, QObject* parent, const char* name )
+ : K3bThreadJob( jh, parent, name )
+{
+ m_thread = new WorkThread( doc );
+ setThread( m_thread );
+}
+
+
+K3bAudioMaxSpeedJob::~K3bAudioMaxSpeedJob()
+{
+ delete m_thread;
+}
+
+
+int K3bAudioMaxSpeedJob::maxSpeed() const
+{
+ return m_thread->maxSpeedByMedia();
+}
+#include "k3baudiomaxspeedjob.moc"
diff --git a/libk3b/projects/audiocd/k3baudiomaxspeedjob.h b/libk3b/projects/audiocd/k3baudiomaxspeedjob.h
new file mode 100644
index 0000000..876bc7f
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiomaxspeedjob.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * $Id: k3baudiomaxspeedjob.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2005 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_AUDIO_MAX_SPEED_JOB_H_
+#define _K3B_AUDIO_MAX_SPEED_JOB_H_
+
+#include <k3bthreadjob.h>
+
+class K3bAudioDoc;
+
+
+class K3bAudioMaxSpeedJob : public K3bThreadJob
+{
+ Q_OBJECT
+
+ public:
+ K3bAudioMaxSpeedJob( K3bAudioDoc* doc, K3bJobHandler*, QObject* parent = 0, const char* name = 0 );
+ ~K3bAudioMaxSpeedJob();
+
+ /**
+ * KB/sec
+ * Only valid if the job finished successfully.
+ */
+ int maxSpeed() const;
+
+ private:
+ class WorkThread;
+ WorkThread* m_thread;
+};
+
+#endif
diff --git a/libk3b/projects/audiocd/k3baudionormalizejob.cpp b/libk3b/projects/audiocd/k3baudionormalizejob.cpp
new file mode 100644
index 0000000..782712b
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudionormalizejob.cpp
@@ -0,0 +1,205 @@
+/*
+ *
+ * $Id: k3baudionormalizejob.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3baudionormalizejob.h"
+#include <k3bexternalbinmanager.h>
+#include <k3bprocess.h>
+#include <k3bcore.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+
+K3bAudioNormalizeJob::K3bAudioNormalizeJob( K3bJobHandler* hdl, QObject* parent, const char* name )
+ : K3bJob( hdl, parent, name ),
+ m_process(0)
+{
+}
+
+
+K3bAudioNormalizeJob::~K3bAudioNormalizeJob()
+{
+ if( m_process )
+ delete m_process;
+}
+
+
+void K3bAudioNormalizeJob::start()
+{
+ m_canceled = false;
+ m_currentAction = COMPUTING_LEVELS;
+ m_currentTrack = 1;
+
+ jobStarted();
+
+ if( m_process )
+ delete m_process;
+
+ m_process = new K3bProcess();
+ connect( m_process, SIGNAL(stderrLine(const QString&)), this, SLOT(slotStdLine(const QString&)) );
+ connect( m_process, SIGNAL(processExited(KProcess*)), this, SLOT(slotProcessExited(KProcess*)) );
+
+ const K3bExternalBin* bin = k3bcore->externalBinManager()->binObject( "normalize-audio" );
+
+ if( !bin ) {
+ emit infoMessage( i18n("Could not find normalize-audio executable."), ERROR );
+ jobFinished(false);
+ return;
+ }
+
+ if( !bin->copyright.isEmpty() )
+ emit infoMessage( i18n("Using %1 %2 - Copyright (C) %3").arg(bin->name()).arg(bin->version).arg(bin->copyright), INFO );
+
+ // create the commandline
+ *m_process << bin;
+
+ // additional user parameters from config
+ const QStringList& params = bin->userParameters();
+ for( QStringList::const_iterator it = params.begin(); it != params.end(); ++it )
+ *m_process << *it;
+
+ // end the options
+ *m_process << "--";
+
+ // add the files
+ for( uint i = 0; i < m_files.count(); ++i )
+ *m_process << m_files[i];
+
+ // now start the process
+ if( !m_process->start( KProcess::NotifyOnExit, KProcess::AllOutput ) ) {
+ // something went wrong when starting the program
+ // it "should" be the executable
+ kdDebug() << "(K3bAudioNormalizeJob) could not start normalize-audio" << endl;
+ emit infoMessage( i18n("Could not start normalize-audio."), K3bJob::ERROR );
+ jobFinished(false);
+ }
+}
+
+
+void K3bAudioNormalizeJob::cancel()
+{
+ m_canceled = true;
+
+ if( m_process )
+ if( m_process->isRunning() ) {
+ m_process->kill();
+ }
+}
+
+
+void K3bAudioNormalizeJob::slotStdLine( const QString& line )
+{
+ // percent, subPercent, newTask (compute level and adjust)
+
+ // emit newSubTask( i18n("Normalizing track %1 of %2 (%3)").arg(t).arg(tt).arg(m_files.at(t-1)) );
+
+ emit debuggingOutput( "normalize-audio", line );
+
+ // wenn "% done" drin:
+ // wenn ein --% drin ist, so beginnt ein neuer track
+ // sonst prozent parsen "batch xxx" ist der fortschritt der action
+ // also ev. den batch fortschritt * 1/2
+
+ if( line.startsWith( "Applying adjustment" ) ) {
+ if( m_currentAction == COMPUTING_LEVELS ) {
+ // starting the adjustment with track 1
+ m_currentTrack = 1;
+ m_currentAction = ADJUSTING_LEVELS;
+ }
+ }
+
+ else if( line.contains( "already normalized" ) ) {
+ // no normalization necessary for the current track
+ emit infoMessage( i18n("Track %1 is already normalized.").arg(m_currentTrack), INFO );
+ m_currentTrack++;
+ }
+
+ else if( line.contains( "--% done") ) {
+ if( m_currentAction == ADJUSTING_LEVELS ) {
+ emit newTask( i18n("Adjusting volume level for track %1 of %2").arg(m_currentTrack).arg(m_files.count()) );
+ kdDebug() << "(K3bAudioNormalizeJob) adjusting level for track "
+ << m_currentTrack
+ << " "
+ << m_files.at(m_currentTrack-1)
+ << endl;
+ }
+ else {
+ emit newTask( i18n("Computing level for track %1 of %2").arg(m_currentTrack).arg(m_files.count()) );
+ kdDebug() << "(K3bAudioNormalizeJob) computing level for track "
+ << m_currentTrack
+ << " "
+ << m_files.at(m_currentTrack-1)
+ << endl;
+ }
+
+ m_currentTrack++;
+ }
+
+ else if( int pos = line.find( "% done" ) > 0 ) {
+ // parse progress: "XXX% done" and "batch XXX% done"
+ pos -= 3;
+ bool ok;
+ // TODO: do not use fixed values
+ // track progress starts at position 19 in version 0.7.6
+ int p = line.mid( 19, 3 ).toInt(&ok);
+ if( ok )
+ emit subPercent( p );
+ else
+ kdDebug() << "(K3bAudioNormalizeJob) subPercent parsing error at pos "
+ << 19 << " in line '" << line.mid( 19, 3 ) << "'" << endl;
+
+ // batch progress starts at position 50 in version 0.7.6
+ p = line.mid( 50, 3 ).toInt(&ok);
+ if( ok && m_currentAction == COMPUTING_LEVELS )
+ emit percent( (int)((double)p/2.0) );
+ else if( ok && m_currentAction == ADJUSTING_LEVELS )
+ emit percent( 50 + (int)((double)p/2.0) );
+ else
+ kdDebug() << "(K3bAudioNormalizeJob) percent parsing error at pos "
+ << 50 << " in line '" << line.mid( 50, 3 ) << "'" << endl;
+
+ }
+}
+
+
+void K3bAudioNormalizeJob::slotProcessExited( KProcess* p )
+{
+ if( p->normalExit() ) {
+ switch( p->exitStatus() ) {
+ case 0:
+ emit infoMessage( i18n("Successfully normalized all tracks."), SUCCESS );
+ jobFinished(true);
+ break;
+ default:
+ if( !m_canceled ) {
+ emit infoMessage( i18n("%1 returned an unknown error (code %2).").arg("normalize-audio").arg(p->exitStatus()),
+ K3bJob::ERROR );
+ emit infoMessage( i18n("Please send me an email with the last output."), K3bJob::ERROR );
+ emit infoMessage( i18n("Error while normalizing tracks."), ERROR );
+ }
+ else
+ emit canceled();
+ jobFinished(false);
+ break;
+ }
+ }
+ else {
+ emit infoMessage( i18n("%1 did not exit cleanly.").arg("Normalize"), K3bJob::ERROR );
+ jobFinished( false );
+ }
+}
+
+#include "k3baudionormalizejob.moc"
diff --git a/libk3b/projects/audiocd/k3baudionormalizejob.h b/libk3b/projects/audiocd/k3baudionormalizejob.h
new file mode 100644
index 0000000..e56086b
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudionormalizejob.h
@@ -0,0 +1,63 @@
+/*
+ *
+ * $Id: k3baudionormalizejob.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_AUDIO_NORMALIZE_JOB_H_
+#define _K3B_AUDIO_NORMALIZE_JOB_H_
+
+
+#include <k3bjob.h>
+
+#include <qvaluevector.h>
+
+class K3bProcess;
+class KProcess;
+
+
+class K3bAudioNormalizeJob : public K3bJob
+{
+ Q_OBJECT
+
+ public:
+ K3bAudioNormalizeJob( K3bJobHandler*, QObject* parent = 0, const char* name = 0 );
+ ~K3bAudioNormalizeJob();
+
+ public slots:
+ void start();
+ void cancel();
+
+ void setFilesToNormalize( const QValueVector<QString>& files ) { m_files = files; }
+
+ private slots:
+ void slotStdLine( const QString& line );
+ void slotProcessExited( KProcess* p );
+
+ private:
+ K3bProcess* m_process;
+
+ QValueVector<QString> m_files;
+ bool m_canceled;
+
+ enum Action {
+ COMPUTING_LEVELS,
+ ADJUSTING_LEVELS
+ };
+
+ int m_currentAction;
+ int m_currentTrack;
+};
+
+
+#endif
diff --git a/libk3b/projects/audiocd/k3baudiotrack.cpp b/libk3b/projects/audiocd/k3baudiotrack.cpp
new file mode 100644
index 0000000..a1d12e4
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiotrack.cpp
@@ -0,0 +1,628 @@
+/*
+ *
+ * $Id: k3baudiotrack.cpp 620139 2007-01-05 11:59:05Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3baudiotrack.h"
+#include "k3baudiodoc.h"
+#include "k3baudiodatasource.h"
+
+#include <k3baudiodecoder.h>
+#include <k3bcore.h>
+#include <k3bcdtextvalidator.h>
+
+#include <qstring.h>
+
+#include <kdebug.h>
+
+
+
+class K3bAudioTrack::Private
+{
+public:
+ Private() {
+ cdTextValidator = new K3bCdTextValidator();
+ }
+
+ ~Private() {
+ delete cdTextValidator;
+ }
+
+ K3bCdTextValidator* cdTextValidator;
+};
+
+
+K3bAudioTrack::K3bAudioTrack()
+ : m_parent(0),
+ m_copy(false),
+ m_preEmp(false),
+ m_index0Offset(150),
+ m_prev(0),
+ m_next(0),
+ m_firstSource(0),
+ m_currentSource(0),
+ m_alreadyReadBytes(0),
+ m_currentlyDeleting(false)
+{
+ d = new Private;
+}
+
+
+K3bAudioTrack::K3bAudioTrack( K3bAudioDoc* parent )
+ : m_parent(parent),
+ m_copy(false),
+ m_preEmp(false),
+ m_index0Offset(150),
+ m_prev(0),
+ m_next(0),
+ m_firstSource(0),
+ m_currentSource(0),
+ m_alreadyReadBytes(0),
+ m_currentlyDeleting(false)
+{
+ d = new Private;
+}
+
+
+K3bAudioTrack::~K3bAudioTrack()
+{
+ kdDebug() << "(K3bAudioTrack::~K3bAudioTrack) " << this << endl;
+ //
+ // It is crucial that we do not emit the changed signal here because otherwise
+ // the doc will delete us again once we are empty!
+ //
+ m_currentlyDeleting = true;
+
+ // fix the list
+ take();
+
+ kdDebug() << "(K3bAudioTrack::~K3bAudioTrack) deleting sources." << endl;
+
+ // delete all sources
+ while( m_firstSource )
+ delete m_firstSource->take();
+
+ kdDebug() << "(K3bAudioTrack::~K3bAudioTrack) finished" << endl;
+
+ delete d;
+}
+
+
+void K3bAudioTrack::emitChanged()
+{
+ if( m_parent )
+ m_parent->slotTrackChanged( this );
+}
+
+
+void K3bAudioTrack::setArtist( const QString& a )
+{
+ setPerformer( a );
+}
+
+
+void K3bAudioTrack::setPerformer( const QString& a )
+{
+ QString s( a );
+ d->cdTextValidator->fixup( s );
+ m_cdText.setPerformer(s);
+ emitChanged();
+}
+
+
+void K3bAudioTrack::setTitle( const QString& t )
+{
+ QString s( t );
+ d->cdTextValidator->fixup( s );
+ m_cdText.setTitle(s);
+ emitChanged();
+}
+
+
+void K3bAudioTrack::setArranger( const QString& t )
+{
+ QString s( t );
+ d->cdTextValidator->fixup( s );
+ m_cdText.setArranger(s);
+ emitChanged();
+}
+
+
+void K3bAudioTrack::setSongwriter( const QString& t )
+{
+ QString s( t );
+ d->cdTextValidator->fixup( s );
+ m_cdText.setSongwriter(s);
+ emitChanged();
+}
+
+
+void K3bAudioTrack::setComposer( const QString& t )
+{
+ QString s( t );
+ d->cdTextValidator->fixup( s );
+ m_cdText.setComposer(s);
+ emitChanged();
+}
+
+
+void K3bAudioTrack::setIsrc( const QString& t )
+{
+ m_cdText.setIsrc(t);
+ emitChanged();
+}
+
+
+void K3bAudioTrack::setCdTextMessage( const QString& t )
+{
+ QString s( t );
+ d->cdTextValidator->fixup( s );
+ m_cdText.setMessage(s);
+ emitChanged();
+}
+
+
+void K3bAudioTrack::setCdText( const K3bDevice::TrackCdText& cdtext )
+{
+ m_cdText = cdtext;
+ emitChanged();
+}
+
+
+K3bAudioDataSource* K3bAudioTrack::lastSource() const
+{
+ K3bAudioDataSource* s = m_firstSource;
+ while( s && s->next() )
+ s = s->next();
+ return s;
+}
+
+
+bool K3bAudioTrack::inList() const
+{
+ if( doc() )
+ return ( doc()->firstTrack() == this || m_prev != 0 );
+ else
+ return false;
+}
+
+
+K3b::Msf K3bAudioTrack::length() const
+{
+ K3b::Msf length;
+ K3bAudioDataSource* source = m_firstSource;
+ while( source ) {
+ length += source->length();
+ source = source->next();
+ }
+ return length;
+}
+
+
+KIO::filesize_t K3bAudioTrack::size() const
+{
+ return length().audioBytes();
+}
+
+
+unsigned int K3bAudioTrack::trackNumber() const
+{
+ if( m_prev )
+ return m_prev->trackNumber() + 1;
+ else
+ return 1;
+}
+
+
+K3b::Msf K3bAudioTrack::index0() const
+{
+ // we save the index0Offset as length of the resulting pregap
+ // this way the length of the track does not need to be ready
+ // when creating the track.
+ return length() - m_index0Offset;
+}
+
+
+K3b::Msf K3bAudioTrack::postGap() const
+{
+ if( next() )
+ return m_index0Offset;
+ else
+ return 0;
+}
+
+
+void K3bAudioTrack::setIndex0( const K3b::Msf& msf )
+{
+ if( msf == 0 )
+ m_index0Offset = 0;
+ else
+ m_index0Offset = length() - msf;
+}
+
+
+K3bAudioTrack* K3bAudioTrack::take()
+{
+ if( inList() ) {
+ if( !m_prev )
+ doc()->setFirstTrack( m_next );
+ if( !m_next )
+ doc()->setLastTrack( m_prev );
+
+ if( m_prev )
+ m_prev->m_next = m_next;
+ if( m_next )
+ m_next->m_prev = m_prev;
+
+ m_prev = m_next = 0;
+
+ // remove from doc
+ if( m_parent )
+ m_parent->slotTrackRemoved(this);
+ m_parent = 0;
+ }
+
+ return this;
+}
+
+
+void K3bAudioTrack::moveAfter( K3bAudioTrack* track )
+{
+ kdDebug() << "(K3bAudioTrack::moveAfter( " << track << " )" << endl;
+ if( !track ) {
+ if( !doc() ) {
+ kdDebug() << "(K3bAudioTrack::moveAfter) no parent set" << endl;
+ return;
+ }
+
+ // make sure we do not mess up the list
+ if( doc()->lastTrack() )
+ moveAfter( doc()->lastTrack() );
+ else {
+ doc()->setFirstTrack( take() );
+ doc()->setLastTrack( this );
+ }
+ }
+ else if( track == this ) {
+ kdDebug() << "(K3bAudioTrack::moveAfter) trying to move this after this." << endl;
+ return;
+ }
+ else {
+ // remove this from the list
+ take();
+
+ // set the new parent doc
+ m_parent = track->doc();
+
+ K3bAudioTrack* oldNext = track->m_next;
+
+ // set track as prev
+ track->m_next = this;
+ m_prev = track;
+
+ // set oldNext as next
+ if( oldNext )
+ oldNext->m_prev = this;
+ m_next = oldNext;
+
+ if( !m_prev )
+ doc()->setFirstTrack( this );
+ if( !m_next )
+ doc()->setLastTrack( this );
+ }
+
+ emitChanged();
+}
+
+
+void K3bAudioTrack::moveAhead( K3bAudioTrack* track )
+{
+ if( !track ) {
+ if( !doc() ) {
+ kdDebug() << "(K3bAudioTrack::moveAfter) no parent set" << endl;
+ return;
+ }
+
+ // make sure we do not mess up the list
+ if( doc()->firstTrack() )
+ moveAhead( doc()->firstTrack() );
+ else {
+ doc()->setFirstTrack( take() );
+ doc()->setLastTrack( this );
+ }
+ }
+ else if( track == this ) {
+ kdDebug() << "(K3bAudioTrack::moveAhead) trying to move this ahead of this." << endl;
+ return;
+ }
+ else {
+ // remove this from the list
+ take();
+
+ // set the new parent doc
+ m_parent = track->doc();
+
+ K3bAudioTrack* oldPrev = track->m_prev;
+
+ // set track as next
+ m_next = track;
+ track->m_prev = this;
+
+ // set oldPrev as prev
+ m_prev = oldPrev;
+ if( oldPrev )
+ oldPrev->m_next = this;
+
+ if( !m_prev )
+ doc()->setFirstTrack( this );
+ if( !m_next )
+ doc()->setLastTrack( this );
+ }
+
+ emitChanged();
+}
+
+
+void K3bAudioTrack::merge( K3bAudioTrack* trackToMerge, K3bAudioDataSource* sourceAfter )
+{
+ kdDebug() << "(K3bAudioTrack::merge) " << trackToMerge << " into " << this << endl;
+ if( this == trackToMerge ) {
+ kdDebug() << "(K3bAudioTrack::merge) trying to merge this with this." << endl;
+ return;
+ }
+
+ // remove the track to merge to make sure it does not get deleted by the doc too early
+ trackToMerge->take();
+
+ // in case we prepend all of trackToMerge's sources
+ if( !sourceAfter ) {
+ kdDebug() << "(K3bAudioTrack::merge) merging " << trackToMerge->firstSource() << endl;
+ if( m_firstSource ) {
+ trackToMerge->firstSource()->moveAhead( m_firstSource );
+ }
+ else {
+ addSource( trackToMerge->firstSource()->take() );
+ }
+ sourceAfter = m_firstSource;
+ }
+
+ kdDebug() << "(K3bAudioTrack::merge) now merge the other sources." << endl;
+ // now merge all sources into this track
+ while( trackToMerge->firstSource() ) {
+ K3bAudioDataSource* s = trackToMerge->firstSource();
+ kdDebug() << "(K3bAudioTrack::merge) merging source " << s << " from track " << s->track() << " into track "
+ << this << " after source " << sourceAfter << endl;
+ s->moveAfter( sourceAfter );
+ sourceAfter = s;
+ }
+
+ // TODO: should we also merge the indices?
+
+ // now we can safely delete the track we merged
+ delete trackToMerge;
+
+ kdDebug() << "(K3bAudioTrack::merge) finished" << endl;
+
+ emitChanged();
+}
+
+
+void K3bAudioTrack::setFirstSource( K3bAudioDataSource* source )
+{
+ // reset the reading stuff since this might be a completely new source list
+ m_currentSource = 0;
+ m_alreadyReadBytes = 0;
+
+ m_firstSource = source;
+ while( source ) {
+ source->m_track = this;
+ source = source->next();
+ }
+
+ emitChanged();
+}
+
+
+void K3bAudioTrack::addSource( K3bAudioDataSource* source )
+{
+ if( !source )
+ return;
+
+ K3bAudioDataSource* s = m_firstSource;
+ while( s && s->next() )
+ s = s->next();
+ if( s )
+ source->moveAfter( s );
+ else
+ setFirstSource( source->take() );
+}
+
+
+void K3bAudioTrack::sourceChanged( K3bAudioDataSource* )
+{
+ if( m_currentlyDeleting )
+ return;
+
+ // TODO: update indices
+
+ if( m_index0Offset > length() )
+ m_index0Offset = length()-1;
+
+ emitChanged();
+}
+
+
+int K3bAudioTrack::numberSources() const
+{
+ K3bAudioDataSource* source = m_firstSource;
+ int i = 0;
+ while( source ) {
+ source = source->next();
+ ++i;
+ }
+ return i;
+}
+
+
+bool K3bAudioTrack::seek( const K3b::Msf& msf )
+{
+ K3bAudioDataSource* source = m_firstSource;
+
+ K3b::Msf pos;
+ while( source && pos + source->length() < msf ) {
+ pos += source->length();
+ source = source->next();
+ }
+
+ if( source ) {
+ m_currentSource = source;
+ m_alreadyReadBytes = msf.audioBytes();
+ return source->seek( msf - pos );
+ }
+ else
+ return false;
+}
+
+
+int K3bAudioTrack::read( char* data, unsigned int max )
+{
+ if( !m_currentSource ) {
+ m_currentSource = m_firstSource;
+ if( m_currentSource )
+ m_currentSource->seek(0);
+ m_alreadyReadBytes = 0;
+ }
+
+ int readData = m_currentSource->read( data, max );
+ if( readData == 0 ) {
+ m_currentSource = m_currentSource->next();
+ if( m_currentSource ) {
+ m_currentSource->seek(0);
+ return read( data, max ); // read from next source
+ }
+ }
+
+ m_alreadyReadBytes += readData;
+
+ return readData;
+}
+
+
+K3bAudioTrack* K3bAudioTrack::copy() const
+{
+ K3bAudioTrack* track = new K3bAudioTrack();
+
+ track->m_copy = m_copy;
+ track->m_preEmp = m_preEmp;
+ track->m_index0Offset = m_index0Offset;
+ track->m_cdText = m_cdText;
+ K3bAudioDataSource* source = m_firstSource;
+ while( source ) {
+ track->addSource( source->copy() );
+ source = source->next();
+ }
+
+ return track;
+}
+
+
+K3bAudioTrack* K3bAudioTrack::split( const K3b::Msf& pos )
+{
+ if( pos < length() ) {
+ // search the source
+ // pos will be the first sector of the new track
+ K3b::Msf currentPos;
+ K3bAudioDataSource* source = firstSource();
+ while( source && currentPos + source->length() <= pos ) {
+ currentPos += source->length();
+ source = source->next();
+ }
+
+ K3bAudioDataSource* splitSource = 0;
+ if( currentPos > 0 && currentPos == pos ) {
+ // no need to split a source
+ splitSource = source;
+ }
+ else {
+ splitSource = source->split( pos - currentPos );
+ }
+
+ // the new track should include all sources from splitSource and below
+ K3bAudioTrack* splitTrack = new K3bAudioTrack();
+ splitTrack->m_cdText = m_cdText;
+ source = splitSource;
+ while( source ) {
+ K3bAudioDataSource* addSource = source;
+ source = source->next();
+ splitTrack->addSource( addSource );
+ }
+
+ kdDebug() << "(K3bAudioTrack) moving track " << splitTrack << " after this (" << this << ") with parent " << doc() << endl;
+ splitTrack->moveAfter( this );
+
+ return splitTrack;
+ }
+ else
+ return 0;
+}
+
+
+K3bDevice::Track K3bAudioTrack::toCdTrack() const
+{
+ if( !inList() )
+ return K3bDevice::Track();
+
+ K3b::Msf firstSector;
+ K3bAudioTrack* track = doc()->firstTrack();
+ while( track != this ) {
+ firstSector += track->length();
+ track = track->next();
+ }
+
+ K3bDevice::Track cdTrack( firstSector,
+ firstSector + length() - 1,
+ K3bDevice::Track::AUDIO );
+
+ // FIXME: auch im audiotrack copy permitted
+ cdTrack.setCopyPermitted( !copyProtection() );
+ cdTrack.setPreEmphasis( preEmp() );
+
+ // FIXME: add indices != 0
+
+ // no index 0 for the last track. Or should we allow this???
+ if( doc()->lastTrack() != this )
+ cdTrack.setIndex0( index0() );
+
+ // FIXME: convert to QCString
+ // cdTrack.setIsrc( isrc() );
+
+ return cdTrack;
+}
+
+
+void K3bAudioTrack::debug()
+{
+ kdDebug() << "Track " << this << endl
+ << " Prev: " << m_prev << endl
+ << " Next: " << m_next << endl
+ << " Sources:" << endl;
+ K3bAudioDataSource* s = m_firstSource;
+ while( s ) {
+ kdDebug() << " " << s << " - Prev: " << s->prev() << " Next: " << s->next() << endl;
+ s = s->next();
+ }
+}
+
+
+
diff --git a/libk3b/projects/audiocd/k3baudiotrack.h b/libk3b/projects/audiocd/k3baudiotrack.h
new file mode 100644
index 0000000..ab0ee1b
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiotrack.h
@@ -0,0 +1,214 @@
+/*
+ *
+ * $Id: k3baudiotrack.h 620139 2007-01-05 11:59:05Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BAUDIOTRACK_H
+#define K3BAUDIOTRACK_H
+
+#include <qstring.h>
+#include <qfileinfo.h>
+#include <qfile.h>
+#include <qptrlist.h>
+
+#include <kio/global.h>
+
+#include <k3bmsf.h>
+
+#include <k3bcdtext.h>
+#include <k3btrack.h>
+#include "k3b_export.h"
+
+class K3bAudioDecoder;
+class K3bAudioDataSource;
+class K3bAudioDoc;
+
+
+/**
+ * @author Sebastian Trueg
+ */
+class LIBK3B_EXPORT K3bAudioTrack
+{
+ friend class K3bAudioDataSource;
+ friend class K3bAudioDoc;
+
+ public:
+ K3bAudioTrack();
+ K3bAudioTrack( K3bAudioDoc* parent );
+ ~K3bAudioTrack();
+
+ K3bAudioDoc* doc() const { return m_parent; }
+
+ K3bDevice::Track toCdTrack() const;
+
+ /**
+ * @return length of track in frames
+ */
+ K3b::Msf length() const;
+ KIO::filesize_t size() const;
+
+ const QString& artist() const { return m_cdText.performer(); }
+ const QString& performer() const { return m_cdText.performer(); }
+ const QString& title() const { return m_cdText.title(); }
+ const QString& arranger() const { return m_cdText.arranger(); }
+ const QString& songwriter() const { return m_cdText.songwriter(); }
+ const QString& composer() const { return m_cdText.composer(); }
+ const QString& isrc() const { return m_cdText.isrc(); }
+ const QString& cdTextMessage() const { return m_cdText.message(); }
+ const K3bDevice::TrackCdText& cdText() const { return m_cdText; }
+
+ bool copyProtection() const { return m_copy; }
+ bool preEmp() const { return m_preEmp; }
+
+ /**
+ * @obsolete use setPerformer
+ **/
+ void setArtist( const QString& a );
+ void setPerformer( const QString& a );
+ void setTitle( const QString& t );
+ void setArranger( const QString& t );
+ void setSongwriter( const QString& t );
+ void setComposer( const QString& t );
+ void setIsrc( const QString& t );
+ void setCdTextMessage( const QString& t );
+
+ void setCdText( const K3bDevice::TrackCdText& cdtext );
+
+ void setPreEmp( bool b ) { m_preEmp = b; emitChanged(); }
+ void setCopyProtection( bool b ) { m_copy = b; emitChanged(); }
+
+ K3b::Msf index0() const;
+ /**
+ * The length of the postgap, ie. the number of blocks with index0.
+ * This is always 0 for the last track.
+ */
+ K3b::Msf postGap() const;
+ void setIndex0( const K3b::Msf& );
+
+ /**
+ * \return The track number starting at 1.
+ */
+ unsigned int trackNumber() const;
+
+ /**
+ * Remove this track from the list and return it.
+ */
+ K3bAudioTrack* take();
+
+ /**
+ * Move this track after @p track.
+ * If @p track is null this track will be merged into the beginning
+ * of the docs list.
+ */
+ void moveAfter( K3bAudioTrack* track );
+
+ /**
+ * Move this track ahead of @p track.
+ * If @p track is null this track will be appended to the end
+ * of the docs list.
+ */
+ void moveAhead( K3bAudioTrack* track );
+
+ /**
+ * Merge @p trackToMerge into this one.
+ */
+ void merge( K3bAudioTrack* trackToMerge, K3bAudioDataSource* sourceAfter = 0 );
+
+ K3bAudioTrack* prev() const { return m_prev; }
+ K3bAudioTrack* next() const { return m_next; }
+
+ /**
+ * Use with care.
+ */
+ void setFirstSource( K3bAudioDataSource* source );
+ K3bAudioDataSource* firstSource() const { return m_firstSource; }
+ K3bAudioDataSource* lastSource() const;
+ int numberSources() const;
+
+ /**
+ * Append source to the end of the sources list.
+ */
+ void addSource( K3bAudioDataSource* source );
+
+ bool seek( const K3b::Msf& );
+
+ /**
+ * Read data from the track.
+ *
+ * @return number of read bytes
+ */
+ int read( char* data, unsigned int max );
+
+ /**
+ * called by K3bAudioDataSource because of the lack of signals
+ */
+ void sourceChanged( K3bAudioDataSource* );
+
+ /**
+ * Create a copy of this track containing copies of all the sources
+ * but not being part of some list.
+ */
+ K3bAudioTrack* copy() const;
+
+ /**
+ * Split the track at position pos and return the splitted track
+ * on success.
+ * The new track will be moved after this track.
+ *
+ * \param pos The position at which to split. \a pos will be the
+ * first frame in the new track.
+ */
+ K3bAudioTrack* split( const K3b::Msf& pos );
+
+ /**
+ * Is this track in a list
+ */
+ bool inList() const;
+
+ private:
+ /**
+ * Tells the doc that the track has changed
+ */
+ void emitChanged();
+
+ void debug();
+
+ K3bAudioDoc* m_parent;
+
+ /** copy protection */
+ bool m_copy;
+ bool m_preEmp;
+
+ K3b::Msf m_index0Offset;
+
+ K3bDevice::TrackCdText m_cdText;
+
+ // list
+ K3bAudioTrack* m_prev;
+ K3bAudioTrack* m_next;
+
+ K3bAudioDataSource* m_firstSource;
+
+
+ K3bAudioDataSource* m_currentSource;
+ long long m_alreadyReadBytes;
+
+ bool m_currentlyDeleting;
+
+ class Private;
+ Private* d;
+};
+
+
+#endif
diff --git a/libk3b/projects/audiocd/k3baudiozerodata.cpp b/libk3b/projects/audiocd/k3baudiozerodata.cpp
new file mode 100644
index 0000000..f5c985d
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiozerodata.cpp
@@ -0,0 +1,115 @@
+/*
+ *
+ * $Id: k3baudiozerodata.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2004 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3baudiozerodata.h"
+#include "k3baudiotrack.h"
+
+#include <klocale.h>
+
+#include <string.h>
+
+
+K3bAudioZeroData::K3bAudioZeroData( const K3b::Msf& len )
+ : K3bAudioDataSource(),
+ m_length(len),
+ m_writtenData(0)
+{
+}
+
+
+K3bAudioZeroData::K3bAudioZeroData( const K3bAudioZeroData& zero )
+ : K3bAudioDataSource( zero ),
+ m_length( zero.m_length ),
+ m_writtenData( 0 )
+{
+}
+
+
+K3bAudioZeroData::~K3bAudioZeroData()
+{
+}
+
+
+void K3bAudioZeroData::setLength( const K3b::Msf& msf )
+{
+ if( msf > 0 )
+ m_length = msf;
+ else
+ m_length = 1; // 1 frame
+
+ m_writtenData = 0;
+
+ emitChange();
+}
+
+
+QString K3bAudioZeroData::type() const
+{
+ return i18n("Silence");
+}
+
+
+QString K3bAudioZeroData::sourceComment() const
+{
+ return QString::null;
+}
+
+
+bool K3bAudioZeroData::seek( const K3b::Msf& msf )
+{
+ if( msf < length() ) {
+ m_writtenData = msf.audioBytes();
+ return true;
+ }
+ else
+ return false;
+}
+
+
+int K3bAudioZeroData::read( char* data, unsigned int max )
+{
+ if( m_writtenData + max > length().audioBytes() )
+ max = length().audioBytes() - m_writtenData;
+
+ m_writtenData += max;
+
+ ::memset( data, 0, max );
+
+ return max;
+}
+
+
+K3bAudioDataSource* K3bAudioZeroData::copy() const
+{
+ return new K3bAudioZeroData( *this );
+}
+
+
+void K3bAudioZeroData::setStartOffset( const K3b::Msf& pos )
+{
+ if( pos >= length() )
+ setLength( 1 );
+ else
+ setLength( length() - pos );
+}
+
+
+void K3bAudioZeroData::setEndOffset( const K3b::Msf& pos )
+{
+ if( pos < 1 )
+ setLength( 1 );
+ else
+ setLength( pos );
+}
diff --git a/libk3b/projects/audiocd/k3baudiozerodata.h b/libk3b/projects/audiocd/k3baudiozerodata.h
new file mode 100644
index 0000000..8cb2911
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiozerodata.h
@@ -0,0 +1,55 @@
+/*
+ *
+ * $Id: k3baudiozerodata.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2004 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_AUDIO_ZERO_DATA_H_
+#define _K3B_AUDIO_ZERO_DATA_H_
+
+#include "k3baudiodatasource.h"
+#include "k3b_export.h"
+
+class LIBK3B_EXPORT K3bAudioZeroData : public K3bAudioDataSource
+{
+ public:
+ K3bAudioZeroData( const K3b::Msf& msf = 150 );
+ K3bAudioZeroData( const K3bAudioZeroData& );
+ ~K3bAudioZeroData();
+
+ K3b::Msf originalLength() const { return m_length; }
+ void setLength( const K3b::Msf& msf );
+
+ QString type() const;
+ QString sourceComment() const;
+
+ bool seek( const K3b::Msf& );
+ int read( char* data, unsigned int max );
+
+ K3bAudioDataSource* copy() const;
+
+ /**
+ * Only changes the length
+ */
+ void setStartOffset( const K3b::Msf& );
+
+ /**
+ * Only changes the length
+ */
+ void setEndOffset( const K3b::Msf& );
+
+ private:
+ K3b::Msf m_length;
+ unsigned long long m_writtenData;
+};
+
+#endif
diff --git a/libk3b/projects/datacd/Makefile.am b/libk3b/projects/datacd/Makefile.am
new file mode 100644
index 0000000..dea5cb2
--- /dev/null
+++ b/libk3b/projects/datacd/Makefile.am
@@ -0,0 +1,35 @@
+AM_CPPFLAGS= -I$(srcdir)/.. \
+ -I$(srcdir)/../../core \
+ -I$(srcdir)/../../plugin \
+ -I$(srcdir)/../../../libk3bdevice \
+ -I$(srcdir)/../../../src \
+ -I$(srcdir)/../../tools \
+ -I$(srcdir)/../../jobs \
+ -I$(srcdir)/../.. \
+ $(all_includes)
+
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libdata.la
+
+libdata_la_SOURCES = k3bdatajob.cpp \
+ k3bdatadoc.cpp \
+ k3bdataitem.cpp \
+ k3bdiritem.cpp \
+ k3bfileitem.cpp \
+ k3bisoimager.cpp \
+ k3bmsinfofetcher.cpp \
+ k3bbootitem.cpp \
+ k3bisooptions.cpp \
+ k3bfilecompilationsizehandler.cpp \
+ k3bsessionimportitem.cpp \
+ k3bmkisofshandler.cpp \
+ k3bdatapreparationjob.cpp
+
+include_HEADERS = k3bdatadoc.h \
+ k3bdatajob.h \
+ k3bdataitem.h \
+ k3bdiritem.h \
+ k3bfileitem.h \
+ k3bbootitem.h \
+ k3bisooptions.h
diff --git a/libk3b/projects/datacd/k3bbootitem.cpp b/libk3b/projects/datacd/k3bbootitem.cpp
new file mode 100644
index 0000000..e94830e
--- /dev/null
+++ b/libk3b/projects/datacd/k3bbootitem.cpp
@@ -0,0 +1,58 @@
+/*
+ *
+ * $Id: k3bbootitem.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bbootitem.h"
+#include "k3bdatadoc.h"
+#include "k3bdiritem.h"
+
+#include <klocale.h>
+
+#include <qptrlist.h>
+
+
+K3bBootItem::K3bBootItem( const QString& fileName, K3bDataDoc* doc, K3bDirItem* dir, const QString& k3bName )
+ : K3bFileItem( fileName, doc, dir, k3bName, FILE|BOOT_IMAGE ),
+ m_noBoot(false),
+ m_bootInfoTable(false),
+ m_loadSegment(0),
+ m_loadSize(0),
+ m_imageType(FLOPPY)
+{
+ setExtraInfo( i18n("El Torito Boot image") );
+}
+
+
+K3bBootItem::K3bBootItem( const K3bBootItem& item )
+ : K3bFileItem( item ),
+ m_noBoot( item.m_noBoot ),
+ m_bootInfoTable( item.m_bootInfoTable ),
+ m_loadSegment( item.m_loadSegment ),
+ m_loadSize( item.m_loadSize ),
+ m_imageType( item.m_imageType ),
+ m_tempPath( item.m_tempPath )
+{
+}
+
+
+K3bBootItem::~K3bBootItem()
+{
+ take();
+}
+
+
+K3bDataItem* K3bBootItem::copy() const
+{
+ return new K3bBootItem( *this );
+}
diff --git a/libk3b/projects/datacd/k3bbootitem.h b/libk3b/projects/datacd/k3bbootitem.h
new file mode 100644
index 0000000..9dd8704
--- /dev/null
+++ b/libk3b/projects/datacd/k3bbootitem.h
@@ -0,0 +1,66 @@
+/*
+ *
+ * $Id: k3bbootitem.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_BOOT_ITEM_H_
+#define _K3B_BOOT_ITEM_H_
+
+#include "k3bfileitem.h"
+
+class K3bBootItem : public K3bFileItem
+{
+ public:
+ K3bBootItem( const QString& fileName, K3bDataDoc* doc, K3bDirItem* dir, const QString& k3bName = 0 );
+ K3bBootItem( const K3bBootItem& );
+ ~K3bBootItem();
+
+ K3bDataItem* copy() const;
+
+ bool isHideable() const { return false; }
+
+ bool isBootItem() const { return true; }
+
+ enum imageType { FLOPPY, HARDDISK, NONE };
+
+ void setNoBoot( bool b ) { m_noBoot = b; }
+ void setBootInfoTable( bool b ) { m_bootInfoTable = b; }
+ void setLoadSegment( int s ) { m_loadSegment = s; }
+ void setLoadSize( int s ) { m_loadSize = s; }
+ void setImageType( int t ) { m_imageType = t; }
+
+ void setTempPath( const QString& p ) { m_tempPath = p; }
+
+ bool noBoot() const { return m_noBoot; }
+ bool bootInfoTable() const { return m_bootInfoTable; }
+ int loadSegment() const { return m_loadSegment; }
+ int loadSize() const { return m_loadSize; }
+ int imageType() const { return m_imageType; }
+
+ /**
+ * mkisofs changes boot images on disk. That is why the iso imager
+ * buffers them and saves the path to the buffered copy here.
+ */
+ const QString& tempPath() const { return m_tempPath; }
+
+ private:
+ bool m_noBoot;
+ bool m_bootInfoTable;
+ int m_loadSegment;
+ int m_loadSize;
+ int m_imageType;
+
+ QString m_tempPath;
+};
+
+#endif
diff --git a/libk3b/projects/datacd/k3bdatadoc.cpp b/libk3b/projects/datacd/k3bdatadoc.cpp
new file mode 100644
index 0000000..d12c8d2
--- /dev/null
+++ b/libk3b/projects/datacd/k3bdatadoc.cpp
@@ -0,0 +1,1376 @@
+/*
+ *
+ * $Id: k3bdatadoc.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bdatadoc.h"
+#include "k3bfileitem.h"
+#include "k3bdiritem.h"
+#include "k3bsessionimportitem.h"
+#include "k3bdatajob.h"
+#include "k3bbootitem.h"
+#include "k3bspecialdataitem.h"
+#include "k3bfilecompilationsizehandler.h"
+#include "k3bmkisofshandler.h"
+#include <k3bcore.h>
+#include <k3bglobals.h>
+#include <k3bmsf.h>
+#include <k3biso9660.h>
+#include <k3bdevicehandler.h>
+#include <k3bdevice.h>
+#include <k3btoc.h>
+#include <k3btrack.h>
+#include <k3bmultichoicedialog.h>
+#include <k3bvalidators.h>
+
+#include <qdir.h>
+#include <qstring.h>
+#include <qfileinfo.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qtimer.h>
+#include <qdom.h>
+#include <qptrlist.h>
+
+#include <kstandarddirs.h>
+#include <kurl.h>
+#include <kstatusbar.h>
+#include <klocale.h>
+#include <kinputdialog.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kprogress.h>
+#include <kconfig.h>
+#include <kapplication.h>
+
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+
+/**
+ * There are two ways to fill a data project with files and folders:
+ * \li Use the addUrl and addUrls methods
+ * \li or create your own K3bDirItems and K3bFileItems. The doc will be properly updated
+ * by the constructors of the items.
+ */
+K3bDataDoc::K3bDataDoc( QObject* parent )
+ : K3bDoc( parent )
+{
+ m_root = 0;
+
+ m_sizeHandler = new K3bFileCompilationSizeHandler();
+}
+
+K3bDataDoc::~K3bDataDoc()
+{
+ delete m_root;
+ delete m_sizeHandler;
+ // delete m_oldSessionSizeHandler;
+}
+
+
+bool K3bDataDoc::newDocument()
+{
+ clearImportedSession();
+
+ m_bootCataloge = 0;
+ m_oldSessionSize = 0;
+ m_bExistingItemsReplaceAll = m_bExistingItemsIgnoreAll = false;
+
+ if( m_root ) {
+ while( m_root->children().getFirst() )
+ removeItem( m_root->children().getFirst() );
+ }
+ else
+ m_root = new K3bRootItem( this );
+
+ m_sizeHandler->clear();
+
+ m_multisessionMode = AUTO;
+ m_dataMode = K3b::DATA_MODE_AUTO;
+
+ m_isoOptions = K3bIsoOptions();
+
+ return K3bDoc::newDocument();
+}
+
+
+QString K3bDataDoc::name() const
+{
+ return m_isoOptions.volumeID();
+}
+
+
+void K3bDataDoc::setIsoOptions( const K3bIsoOptions& o )
+{
+ m_isoOptions = o;
+ emit changed();
+}
+
+
+void K3bDataDoc::setVolumeID( const QString& v )
+{
+ m_isoOptions.setVolumeID( v );
+ emit changed();
+}
+
+
+void K3bDataDoc::addUrls( const KURL::List& urls )
+{
+ addUrls( urls, root() );
+}
+
+
+void K3bDataDoc::addUrls( const KURL::List& l, K3bDirItem* dir )
+{
+ if( !dir )
+ dir = root();
+
+ KURL::List urls = K3b::convertToLocalUrls(l);
+
+ for( KURL::List::ConstIterator it = urls.begin(); it != urls.end(); ++it ) {
+ const KURL& url = *it;
+ QFileInfo f( url.path() );
+ QString k3bname = f.absFilePath().section( "/", -1 );
+
+ // filenames cannot end in backslashes (mkisofs problem. See comments in k3bisoimager.cpp (escapeGraftPoint()))
+ while( k3bname[k3bname.length()-1] == '\\' )
+ k3bname.truncate( k3bname.length()-1 );
+
+ // backup dummy name
+ if( k3bname.isEmpty() )
+ k3bname = "1";
+
+ K3bDirItem* newDirItem = 0;
+
+ // rename the new item if an item with that name already exists
+ int cnt = 0;
+ bool ok = false;
+ while( !ok ) {
+ ok = true;
+ QString name( k3bname );
+ if( cnt > 0 )
+ name += QString("_%1").arg(cnt);
+ if( K3bDataItem* oldItem = dir->find( name ) ) {
+ if( f.isDir() && oldItem->isDir() ) {
+ // ok, just reuse the dir
+ newDirItem = static_cast<K3bDirItem*>(oldItem);
+ }
+ // directories cannot replace files in an old session (I think)
+ // and also directories can for sure never be replaced (only be reused as above)
+ // so we always rename if the old item is a dir.
+ else if( !oldItem->isFromOldSession() ||
+ f.isDir() ||
+ oldItem->isDir() ) {
+ ++cnt;
+ ok = false;
+ }
+ }
+ }
+ if( cnt > 0 )
+ k3bname += QString("_%1").arg(cnt);
+
+ // QFileInfo::exists and QFileInfo::isReadable return false for broken symlinks :(
+ if( f.isDir() && !f.isSymLink() ) {
+ if( !newDirItem ) {
+ newDirItem = new K3bDirItem( k3bname, this, dir );
+ newDirItem->setLocalPath( url.path() ); // HACK: see k3bdiritem.h
+ }
+
+ // recursively add all the files in the directory
+ QStringList dlist = QDir( f.absFilePath() ).entryList( QDir::All|QDir::System|QDir::Hidden );
+ dlist.remove(".");
+ dlist.remove("..");
+ KURL::List newUrls;
+ for( QStringList::Iterator it = dlist.begin(); it != dlist.end(); ++it )
+ newUrls.append( KURL::fromPathOrURL( f.absFilePath() + "/" + *it ) );
+ addUrls( newUrls, newDirItem );
+ }
+ else if( f.isSymLink() || f.isFile() )
+ (void)new K3bFileItem( url.path(), this, dir, k3bname );
+ }
+
+ emit changed();
+
+ setModified( true );
+}
+
+
+bool K3bDataDoc::nameAlreadyInDir( const QString& name, K3bDirItem* dir )
+{
+ if( !dir )
+ return false;
+ else
+ return ( dir->find( name ) != 0 );
+}
+
+
+K3bDirItem* K3bDataDoc::addEmptyDir( const QString& name, K3bDirItem* parent )
+{
+ K3bDirItem* item = new K3bDirItem( name, this, parent );
+
+ setModified( true );
+
+ return item;
+}
+
+
+KIO::filesize_t K3bDataDoc::size() const
+{
+ if( m_isoOptions.doNotCacheInodes() )
+ return root()->blocks().mode1Bytes() + m_oldSessionSize;
+ else
+ return m_sizeHandler->blocks( m_isoOptions.followSymbolicLinks() ||
+ !m_isoOptions.createRockRidge() ).mode1Bytes() + m_oldSessionSize;
+}
+
+
+KIO::filesize_t K3bDataDoc::burningSize() const
+{
+ return size() - m_oldSessionSize; //m_oldSessionSizeHandler->size();
+}
+
+
+K3b::Msf K3bDataDoc::length() const
+{
+ // 1 block consists of 2048 bytes real data
+ // and 1 block equals to 1 audio frame
+ // so this is the way to calculate:
+
+ return K3b::Msf( size() / 2048 );
+}
+
+
+K3b::Msf K3bDataDoc::burningLength() const
+{
+ return K3b::Msf( burningSize() / 2048 );
+}
+
+
+QString K3bDataDoc::typeString() const
+{
+ return QString::fromLatin1("data");
+}
+
+
+bool K3bDataDoc::loadDocumentData( QDomElement* rootElem )
+{
+ if( !root() )
+ newDocument();
+
+ QDomNodeList nodes = rootElem->childNodes();
+
+ if( nodes.item(0).nodeName() != "general" ) {
+ kdDebug() << "(K3bDataDoc) could not find 'general' section." << endl;
+ return false;
+ }
+ if( !readGeneralDocumentData( nodes.item(0).toElement() ) )
+ return false;
+
+
+ // parse options
+ // -----------------------------------------------------------------
+ if( nodes.item(1).nodeName() != "options" ) {
+ kdDebug() << "(K3bDataDoc) could not find 'options' section." << endl;
+ return false;
+ }
+ if( !loadDocumentDataOptions( nodes.item(1).toElement() ) )
+ return false;
+ // -----------------------------------------------------------------
+
+
+
+ // parse header
+ // -----------------------------------------------------------------
+ if( nodes.item(2).nodeName() != "header" ) {
+ kdDebug() << "(K3bDataDoc) could not find 'header' section." << endl;
+ return false;
+ }
+ if( !loadDocumentDataHeader( nodes.item(2).toElement() ) )
+ return false;
+ // -----------------------------------------------------------------
+
+
+
+ // parse files
+ // -----------------------------------------------------------------
+ if( nodes.item(3).nodeName() != "files" ) {
+ kdDebug() << "(K3bDataDoc) could not find 'files' section." << endl;
+ return false;
+ }
+
+ if( m_root == 0 )
+ m_root = new K3bRootItem( this );
+
+ QDomNodeList filesList = nodes.item(3).childNodes();
+ for( uint i = 0; i < filesList.count(); i++ ) {
+
+ QDomElement e = filesList.item(i).toElement();
+ if( !loadDataItem( e, root() ) )
+ return false;
+ }
+
+ // -----------------------------------------------------------------
+
+ //
+ // Old versions of K3b do not properly save the boot catalog location
+ // and name. So to ensure we have one around even if loading an old project
+ // file we create a default one here.
+ //
+ if( !m_bootImages.isEmpty() && !m_bootCataloge )
+ createBootCatalogeItem( m_bootImages.first()->parent() );
+
+
+ informAboutNotFoundFiles();
+
+ return true;
+}
+
+
+bool K3bDataDoc::loadDocumentDataOptions( QDomElement elem )
+{
+ QDomNodeList headerList = elem.childNodes();
+ for( uint i = 0; i < headerList.count(); i++ ) {
+
+ QDomElement e = headerList.item(i).toElement();
+ if( e.isNull() )
+ return false;
+
+ if( e.nodeName() == "rock_ridge")
+ m_isoOptions.setCreateRockRidge( e.attributeNode( "activated" ).value() == "yes" );
+
+ else if( e.nodeName() == "joliet")
+ m_isoOptions.setCreateJoliet( e.attributeNode( "activated" ).value() == "yes" );
+
+ else if( e.nodeName() == "udf")
+ m_isoOptions.setCreateUdf( e.attributeNode( "activated" ).value() == "yes" );
+
+ else if( e.nodeName() == "joliet_allow_103_characters")
+ m_isoOptions.setJolietLong( e.attributeNode( "activated" ).value() == "yes" );
+
+ else if( e.nodeName() == "iso_allow_lowercase")
+ m_isoOptions.setISOallowLowercase( e.attributeNode( "activated" ).value() == "yes" );
+
+ else if( e.nodeName() == "iso_allow_period_at_begin")
+ m_isoOptions.setISOallowPeriodAtBegin( e.attributeNode( "activated" ).value() == "yes" );
+
+ else if( e.nodeName() == "iso_allow_31_char")
+ m_isoOptions.setISOallow31charFilenames( e.attributeNode( "activated" ).value() == "yes" );
+
+ else if( e.nodeName() == "iso_omit_version_numbers")
+ m_isoOptions.setISOomitVersionNumbers( e.attributeNode( "activated" ).value() == "yes" );
+
+ else if( e.nodeName() == "iso_omit_trailing_period")
+ m_isoOptions.setISOomitTrailingPeriod( e.attributeNode( "activated" ).value() == "yes" );
+
+ else if( e.nodeName() == "iso_max_filename_length")
+ m_isoOptions.setISOmaxFilenameLength( e.attributeNode( "activated" ).value() == "yes" );
+
+ else if( e.nodeName() == "iso_relaxed_filenames")
+ m_isoOptions.setISOrelaxedFilenames( e.attributeNode( "activated" ).value() == "yes" );
+
+ else if( e.nodeName() == "iso_no_iso_translate")
+ m_isoOptions.setISOnoIsoTranslate( e.attributeNode( "activated" ).value() == "yes" );
+
+ else if( e.nodeName() == "iso_allow_multidot")
+ m_isoOptions.setISOallowMultiDot( e.attributeNode( "activated" ).value() == "yes" );
+
+ else if( e.nodeName() == "iso_untranslated_filenames")
+ m_isoOptions.setISOuntranslatedFilenames( e.attributeNode( "activated" ).value() == "yes" );
+
+ else if( e.nodeName() == "follow_symbolic_links")
+ m_isoOptions.setFollowSymbolicLinks( e.attributeNode( "activated" ).value() == "yes" );
+
+ else if( e.nodeName() == "create_trans_tbl")
+ m_isoOptions.setCreateTRANS_TBL( e.attributeNode( "activated" ).value() == "yes" );
+
+ else if( e.nodeName() == "hide_trans_tbl")
+ m_isoOptions.setHideTRANS_TBL( e.attributeNode( "activated" ).value() == "yes" );
+
+ else if( e.nodeName() == "iso_level")
+ m_isoOptions.setISOLevel( e.text().toInt() );
+
+ else if( e.nodeName() == "discard_symlinks")
+ m_isoOptions.setDiscardSymlinks( e.attributeNode( "activated" ).value() == "yes" );
+
+ else if( e.nodeName() == "discard_broken_symlinks")
+ m_isoOptions.setDiscardBrokenSymlinks( e.attributeNode( "activated" ).value() == "yes" );
+
+ else if( e.nodeName() == "preserve_file_permissions")
+ m_isoOptions.setPreserveFilePermissions( e.attributeNode( "activated" ).value() == "yes" );
+
+ else if( e.nodeName() == "force_input_charset")
+ m_isoOptions.setForceInputCharset( e.attributeNode( "activated" ).value() == "yes" );
+
+ else if( e.nodeName() == "input_charset")
+ m_isoOptions.setInputCharset( e.text() );
+
+ else if( e.nodeName() == "do_not_cache_inodes" )
+ m_isoOptions.setDoNotCacheInodes( e.attributeNode( "activated" ).value() == "yes" );
+
+ else if( e.nodeName() == "whitespace_treatment" ) {
+ if( e.text() == "strip" )
+ m_isoOptions.setWhiteSpaceTreatment( K3bIsoOptions::strip );
+ else if( e.text() == "extended" )
+ m_isoOptions.setWhiteSpaceTreatment( K3bIsoOptions::extended );
+ else if( e.text() == "extended" )
+ m_isoOptions.setWhiteSpaceTreatment( K3bIsoOptions::replace );
+ else
+ m_isoOptions.setWhiteSpaceTreatment( K3bIsoOptions::noChange );
+ }
+
+ else if( e.nodeName() == "whitespace_replace_string")
+ m_isoOptions.setWhiteSpaceTreatmentReplaceString( e.text() );
+
+ else if( e.nodeName() == "data_track_mode" ) {
+ if( e.text() == "mode1" )
+ m_dataMode = K3b::MODE1;
+ else if( e.text() == "mode2" )
+ m_dataMode = K3b::MODE2;
+ else
+ m_dataMode = K3b::DATA_MODE_AUTO;
+ }
+
+ else if( e.nodeName() == "multisession" ) {
+ QString mode = e.text();
+ if( mode == "start" )
+ setMultiSessionMode( START );
+ else if( mode == "continue" )
+ setMultiSessionMode( CONTINUE );
+ else if( mode == "finish" )
+ setMultiSessionMode( FINISH );
+ else if( mode == "none" )
+ setMultiSessionMode( NONE );
+ else
+ setMultiSessionMode( AUTO );
+ }
+
+ else if( e.nodeName() == "verify_data" )
+ setVerifyData( e.attributeNode( "activated" ).value() == "yes" );
+
+ else
+ kdDebug() << "(K3bDataDoc) unknown option entry: " << e.nodeName() << endl;
+ }
+
+ return true;
+}
+
+
+bool K3bDataDoc::loadDocumentDataHeader( QDomElement headerElem )
+{
+ QDomNodeList headerList = headerElem.childNodes();
+ for( uint i = 0; i < headerList.count(); i++ ) {
+
+ QDomElement e = headerList.item(i).toElement();
+ if( e.isNull() )
+ return false;
+
+ if( e.nodeName() == "volume_id" )
+ m_isoOptions.setVolumeID( e.text() );
+
+ else if( e.nodeName() == "application_id" )
+ m_isoOptions.setApplicationID( e.text() );
+
+ else if( e.nodeName() == "publisher" )
+ m_isoOptions.setPublisher( e.text() );
+
+ else if( e.nodeName() == "preparer" )
+ m_isoOptions.setPreparer( e.text() );
+
+ else if( e.nodeName() == "volume_set_id" )
+ m_isoOptions.setVolumeSetId( e.text() );
+
+ else if( e.nodeName() == "volume_set_size" )
+ m_isoOptions.setVolumeSetSize( e.text().toInt() );
+
+ else if( e.nodeName() == "volume_set_number" )
+ m_isoOptions.setVolumeSetNumber( e.text().toInt() );
+
+ else if( e.nodeName() == "system_id" )
+ m_isoOptions.setSystemId( e.text() );
+
+ else
+ kdDebug() << "(K3bDataDoc) unknown header entry: " << e.nodeName() << endl;
+ }
+
+ return true;
+}
+
+
+bool K3bDataDoc::loadDataItem( QDomElement& elem, K3bDirItem* parent )
+{
+ K3bDataItem* newItem = 0;
+
+ if( elem.nodeName() == "file" ) {
+ QDomElement urlElem = elem.firstChild().toElement();
+ if( urlElem.isNull() ) {
+ kdDebug() << "(K3bDataDoc) file-element without url!" << endl;
+ return false;
+ }
+
+ QFileInfo f( urlElem.text() );
+
+ // We canot use exists() here since this always disqualifies broken symlinks
+ if( !f.isFile() && !f.isSymLink() )
+ m_notFoundFiles.append( urlElem.text() );
+
+ // broken symlinks are not readable according to QFileInfo which is wrong in our case
+ else if( f.isFile() && !f.isReadable() )
+ m_noPermissionFiles.append( urlElem.text() );
+
+ else if( !elem.attribute( "bootimage" ).isEmpty() ) {
+ K3bBootItem* bootItem = new K3bBootItem( urlElem.text(),
+ this,
+ parent,
+ elem.attributeNode( "name" ).value() );
+ if( elem.attribute( "bootimage" ) == "floppy" )
+ bootItem->setImageType( K3bBootItem::FLOPPY );
+ else if( elem.attribute( "bootimage" ) == "harddisk" )
+ bootItem->setImageType( K3bBootItem::HARDDISK );
+ else
+ bootItem->setImageType( K3bBootItem::NONE );
+ bootItem->setNoBoot( elem.attribute( "no_boot" ) == "yes" );
+ bootItem->setBootInfoTable( elem.attribute( "boot_info_table" ) == "yes" );
+ bootItem->setLoadSegment( elem.attribute( "load_segment" ).toInt() );
+ bootItem->setLoadSize( elem.attribute( "load_size" ).toInt() );
+
+ newItem = bootItem;
+ }
+
+ else {
+ newItem = new K3bFileItem( urlElem.text(),
+ this,
+ parent,
+ elem.attributeNode( "name" ).value() );
+ }
+ }
+ else if( elem.nodeName() == "special" ) {
+ if( elem.attributeNode( "type" ).value() == "boot cataloge" )
+ createBootCatalogeItem( parent )->setK3bName( elem.attributeNode( "name" ).value() );
+ }
+ else if( elem.nodeName() == "directory" ) {
+ // This is for the VideoDVD project which already contains the *_TS folders
+ K3bDirItem* newDirItem = 0;
+ if( K3bDataItem* item = parent->find( elem.attributeNode( "name" ).value() ) ) {
+ if( item->isDir() ) {
+ newDirItem = static_cast<K3bDirItem*>(item);
+ }
+ else {
+ kdError() << "(K3bDataDoc) INVALID DOCUMENT: item " << item->k3bPath() << " saved twice" << endl;
+ return false;
+ }
+ }
+
+ if( !newDirItem )
+ newDirItem = new K3bDirItem( elem.attributeNode( "name" ).value(), this, parent );
+ QDomNodeList childNodes = elem.childNodes();
+ for( uint i = 0; i < childNodes.count(); i++ ) {
+
+ QDomElement e = childNodes.item(i).toElement();
+ if( !loadDataItem( e, newDirItem ) )
+ return false;
+ }
+
+ newItem = newDirItem;
+ }
+ else {
+ kdDebug() << "(K3bDataDoc) wrong tag in files-section: " << elem.nodeName() << endl;
+ return false;
+ }
+
+ // load the sort weight
+ if( newItem )
+ newItem->setSortWeight( elem.attribute( "sort_weight", "0" ).toInt() );
+
+ return true;
+}
+
+
+bool K3bDataDoc::saveDocumentData( QDomElement* docElem )
+{
+ QDomDocument doc = docElem->ownerDocument();
+
+ saveGeneralDocumentData( docElem );
+
+ // all options
+ // ----------------------------------------------------------------------
+ QDomElement optionsElem = doc.createElement( "options" );
+ saveDocumentDataOptions( optionsElem );
+ docElem->appendChild( optionsElem );
+ // ----------------------------------------------------------------------
+
+ // the header stuff
+ // ----------------------------------------------------------------------
+ QDomElement headerElem = doc.createElement( "header" );
+ saveDocumentDataHeader( headerElem );
+ docElem->appendChild( headerElem );
+
+
+ // now do the "real" work: save the entries
+ // ----------------------------------------------------------------------
+ QDomElement topElem = doc.createElement( "files" );
+
+ QPtrListIterator<K3bDataItem> it( root()->children() );
+ for( ; it.current(); ++it ) {
+ saveDataItem( it.current(), &doc, &topElem );
+ }
+
+ docElem->appendChild( topElem );
+ // ----------------------------------------------------------------------
+
+ return true;
+}
+
+
+void K3bDataDoc::saveDocumentDataOptions( QDomElement& optionsElem )
+{
+ QDomDocument doc = optionsElem.ownerDocument();
+
+ QDomElement topElem = doc.createElement( "rock_ridge" );
+ topElem.setAttribute( "activated", isoOptions().createRockRidge() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "joliet" );
+ topElem.setAttribute( "activated", isoOptions().createJoliet() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "udf" );
+ topElem.setAttribute( "activated", isoOptions().createUdf() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "joliet_allow_103_characters" );
+ topElem.setAttribute( "activated", isoOptions().jolietLong() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "iso_allow_lowercase" );
+ topElem.setAttribute( "activated", isoOptions().ISOallowLowercase() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "iso_allow_period_at_begin" );
+ topElem.setAttribute( "activated", isoOptions().ISOallowPeriodAtBegin() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "iso_allow_31_char" );
+ topElem.setAttribute( "activated", isoOptions().ISOallow31charFilenames() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "iso_omit_version_numbers" );
+ topElem.setAttribute( "activated", isoOptions().ISOomitVersionNumbers() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "iso_omit_trailing_period" );
+ topElem.setAttribute( "activated", isoOptions().ISOomitTrailingPeriod() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "iso_max_filename_length" );
+ topElem.setAttribute( "activated", isoOptions().ISOmaxFilenameLength() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "iso_relaxed_filenames" );
+ topElem.setAttribute( "activated", isoOptions().ISOrelaxedFilenames() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "iso_no_iso_translate" );
+ topElem.setAttribute( "activated", isoOptions().ISOnoIsoTranslate() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "iso_allow_multidot" );
+ topElem.setAttribute( "activated", isoOptions().ISOallowMultiDot() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "iso_untranslated_filenames" );
+ topElem.setAttribute( "activated", isoOptions().ISOuntranslatedFilenames() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "follow_symbolic_links" );
+ topElem.setAttribute( "activated", isoOptions().followSymbolicLinks() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "create_trans_tbl" );
+ topElem.setAttribute( "activated", isoOptions().createTRANS_TBL() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "hide_trans_tbl" );
+ topElem.setAttribute( "activated", isoOptions().hideTRANS_TBL() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "iso_level" );
+ topElem.appendChild( doc.createTextNode( QString::number(isoOptions().ISOLevel()) ) );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "discard_symlinks" );
+ topElem.setAttribute( "activated", isoOptions().discardSymlinks() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "discard_broken_symlinks" );
+ topElem.setAttribute( "activated", isoOptions().discardBrokenSymlinks() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "preserve_file_permissions" );
+ topElem.setAttribute( "activated", isoOptions().preserveFilePermissions() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "force_input_charset" );
+ topElem.setAttribute( "activated", isoOptions().forceInputCharset() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "do_not_cache_inodes" );
+ topElem.setAttribute( "activated", isoOptions().doNotCacheInodes() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "input_charset" );
+ topElem.appendChild( doc.createTextNode( isoOptions().inputCharset() ) );
+ optionsElem.appendChild( topElem );
+
+
+ topElem = doc.createElement( "whitespace_treatment" );
+ switch( isoOptions().whiteSpaceTreatment() ) {
+ case K3bIsoOptions::strip:
+ topElem.appendChild( doc.createTextNode( "strip" ) );
+ break;
+ case K3bIsoOptions::extended:
+ topElem.appendChild( doc.createTextNode( "extended" ) );
+ break;
+ case K3bIsoOptions::replace:
+ topElem.appendChild( doc.createTextNode( "replace" ) );
+ break;
+ default:
+ topElem.appendChild( doc.createTextNode( "noChange" ) );
+ break;
+ }
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "whitespace_replace_string" );
+ topElem.appendChild( doc.createTextNode( isoOptions().whiteSpaceTreatmentReplaceString() ) );
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "data_track_mode" );
+ if( m_dataMode == K3b::MODE1 )
+ topElem.appendChild( doc.createTextNode( "mode1" ) );
+ else if( m_dataMode == K3b::MODE2 )
+ topElem.appendChild( doc.createTextNode( "mode2" ) );
+ else
+ topElem.appendChild( doc.createTextNode( "auto" ) );
+ optionsElem.appendChild( topElem );
+
+
+ // save multisession
+ topElem = doc.createElement( "multisession" );
+ switch( m_multisessionMode ) {
+ case START:
+ topElem.appendChild( doc.createTextNode( "start" ) );
+ break;
+ case CONTINUE:
+ topElem.appendChild( doc.createTextNode( "continue" ) );
+ break;
+ case FINISH:
+ topElem.appendChild( doc.createTextNode( "finish" ) );
+ break;
+ case NONE:
+ topElem.appendChild( doc.createTextNode( "none" ) );
+ break;
+ default:
+ topElem.appendChild( doc.createTextNode( "auto" ) );
+ break;
+ }
+ optionsElem.appendChild( topElem );
+
+ topElem = doc.createElement( "verify_data" );
+ topElem.setAttribute( "activated", verifyData() ? "yes" : "no" );
+ optionsElem.appendChild( topElem );
+ // ----------------------------------------------------------------------
+}
+
+
+void K3bDataDoc::saveDocumentDataHeader( QDomElement& headerElem )
+{
+ QDomDocument doc = headerElem.ownerDocument();
+
+ QDomElement topElem = doc.createElement( "volume_id" );
+ topElem.appendChild( doc.createTextNode( isoOptions().volumeID() ) );
+ headerElem.appendChild( topElem );
+
+ topElem = doc.createElement( "volume_set_id" );
+ topElem.appendChild( doc.createTextNode( isoOptions().volumeSetId() ) );
+ headerElem.appendChild( topElem );
+
+ topElem = doc.createElement( "volume_set_size" );
+ topElem.appendChild( doc.createTextNode( QString::number(isoOptions().volumeSetSize()) ) );
+ headerElem.appendChild( topElem );
+
+ topElem = doc.createElement( "volume_set_number" );
+ topElem.appendChild( doc.createTextNode( QString::number(isoOptions().volumeSetNumber()) ) );
+ headerElem.appendChild( topElem );
+
+ topElem = doc.createElement( "system_id" );
+ topElem.appendChild( doc.createTextNode( isoOptions().systemId() ) );
+ headerElem.appendChild( topElem );
+
+ topElem = doc.createElement( "application_id" );
+ topElem.appendChild( doc.createTextNode( isoOptions().applicationID() ) );
+ headerElem.appendChild( topElem );
+
+ topElem = doc.createElement( "publisher" );
+ topElem.appendChild( doc.createTextNode( isoOptions().publisher() ) );
+ headerElem.appendChild( topElem );
+
+ topElem = doc.createElement( "preparer" );
+ topElem.appendChild( doc.createTextNode( isoOptions().preparer() ) );
+ headerElem.appendChild( topElem );
+ // ----------------------------------------------------------------------
+}
+
+
+void K3bDataDoc::saveDataItem( K3bDataItem* item, QDomDocument* doc, QDomElement* parent )
+{
+ if( K3bFileItem* fileItem = dynamic_cast<K3bFileItem*>( item ) ) {
+ if( m_oldSession.contains( fileItem ) ) {
+ kdDebug() << "(K3bDataDoc) ignoring fileitem " << fileItem->k3bName() << " from old session while saving..." << endl;
+ }
+ else {
+ QDomElement topElem = doc->createElement( "file" );
+ topElem.setAttribute( "name", fileItem->k3bName() );
+ QDomElement subElem = doc->createElement( "url" );
+ subElem.appendChild( doc->createTextNode( fileItem->localPath() ) );
+ topElem.appendChild( subElem );
+
+ if( item->sortWeight() != 0 )
+ topElem.setAttribute( "sort_weight", QString::number(item->sortWeight()) );
+
+ parent->appendChild( topElem );
+
+ // add boot options as attributes to preserve compatibility to older K3b versions
+ if( K3bBootItem* bootItem = dynamic_cast<K3bBootItem*>( fileItem ) ) {
+ if( bootItem->imageType() == K3bBootItem::FLOPPY )
+ topElem.setAttribute( "bootimage", "floppy" );
+ else if( bootItem->imageType() == K3bBootItem::HARDDISK )
+ topElem.setAttribute( "bootimage", "harddisk" );
+ else
+ topElem.setAttribute( "bootimage", "none" );
+
+ topElem.setAttribute( "no_boot", bootItem->noBoot() ? "yes" : "no" );
+ topElem.setAttribute( "boot_info_table", bootItem->bootInfoTable() ? "yes" : "no" );
+ topElem.setAttribute( "load_segment", QString::number( bootItem->loadSegment() ) );
+ topElem.setAttribute( "load_size", QString::number( bootItem->loadSize() ) );
+ }
+ }
+ }
+ else if( item == m_bootCataloge ) {
+ QDomElement topElem = doc->createElement( "special" );
+ topElem.setAttribute( "name", m_bootCataloge->k3bName() );
+ topElem.setAttribute( "type", "boot cataloge" );
+
+ parent->appendChild( topElem );
+ }
+ else if( K3bDirItem* dirItem = dynamic_cast<K3bDirItem*>( item ) ) {
+ QDomElement topElem = doc->createElement( "directory" );
+ topElem.setAttribute( "name", dirItem->k3bName() );
+
+ if( item->sortWeight() != 0 )
+ topElem.setAttribute( "sort_weight", QString::number(item->sortWeight()) );
+
+ QPtrListIterator<K3bDataItem> it( dirItem->children() );
+ for( ; it.current(); ++it ) {
+ saveDataItem( it.current(), doc, &topElem );
+ }
+
+ parent->appendChild( topElem );
+ }
+}
+
+
+void K3bDataDoc::removeItem( K3bDataItem* item )
+{
+ if( !item )
+ return;
+
+ if( item->isRemoveable() ) {
+ delete item;
+ }
+ else
+ kdDebug() << "(K3bDataDoc) tried to remove non-removable entry!" << endl;
+}
+
+
+void K3bDataDoc::itemRemovedFromDir( K3bDirItem*, K3bDataItem* removedItem )
+{
+ // update the project size
+ if( !removedItem->isFromOldSession() )
+ m_sizeHandler->removeFile( removedItem );
+
+ // update the boot item list
+ if( removedItem->isBootItem() ) {
+ m_bootImages.removeRef( static_cast<K3bBootItem*>( removedItem ) );
+ if( m_bootImages.isEmpty() ) {
+ delete m_bootCataloge;
+ m_bootCataloge = 0;
+ }
+ }
+
+ emit itemRemoved( removedItem );
+ emit changed();
+}
+
+
+void K3bDataDoc::itemAddedToDir( K3bDirItem*, K3bDataItem* item )
+{
+ // update the project size
+ if( !item->isFromOldSession() )
+ m_sizeHandler->addFile( item );
+
+ // update the boot item list
+ if( item->isBootItem() )
+ m_bootImages.append( static_cast<K3bBootItem*>( item ) );
+
+ emit itemAdded( item );
+ emit changed();
+}
+
+
+void K3bDataDoc::moveItem( K3bDataItem* item, K3bDirItem* newParent )
+{
+ if( !item || !newParent ) {
+ kdDebug() << "(K3bDataDoc) item or parentitem was NULL while moving." << endl;
+ return;
+ }
+
+ if( !item->isMoveable() ) {
+ kdDebug() << "(K3bDataDoc) item is not movable! " << endl;
+ return;
+ }
+
+ item->reparent( newParent );
+}
+
+
+void K3bDataDoc::moveItems( QPtrList<K3bDataItem> itemList, K3bDirItem* newParent )
+{
+ if( !newParent ) {
+ kdDebug() << "(K3bDataDoc) tried to move items to nowhere...!" << endl;
+ return;
+ }
+
+ QPtrListIterator<K3bDataItem> it( itemList );
+ for( ; it.current(); ++it ) {
+ // check if newParent is subdir of item
+ if( K3bDirItem* dirItem = dynamic_cast<K3bDirItem*>( it.current() ) ) {
+ if( dirItem->isSubItem( newParent ) ) {
+ continue;
+ }
+ }
+
+ if( it.current()->isMoveable() )
+ it.current()->reparent( newParent );
+ }
+}
+
+
+K3bBurnJob* K3bDataDoc::newBurnJob( K3bJobHandler* hdl, QObject* parent )
+{
+ return new K3bDataJob( this, hdl, parent );
+}
+
+
+QString K3bDataDoc::treatWhitespace( const QString& path )
+{
+
+ // TODO:
+ // It could happen that two files with different names
+ // will have the same name after the treatment
+ // Perhaps we should add a number at the end or something
+ // similar (s.a.)
+
+
+ if( isoOptions().whiteSpaceTreatment() != K3bIsoOptions::noChange ) {
+ QString result = path;
+
+ if( isoOptions().whiteSpaceTreatment() == K3bIsoOptions::replace ) {
+ result.replace( ' ', isoOptions().whiteSpaceTreatmentReplaceString() );
+ }
+ else if( isoOptions().whiteSpaceTreatment() == K3bIsoOptions::strip ) {
+ result.remove( ' ' );
+ }
+ else if( isoOptions().whiteSpaceTreatment() == K3bIsoOptions::extended ) {
+ result.truncate(0);
+ for( uint i = 0; i < path.length(); i++ ) {
+ if( path[i] == ' ' ) {
+ if( path[i+1] != ' ' )
+ result.append( path[++i].upper() );
+ }
+ else
+ result.append( path[i] );
+ }
+ }
+
+ kdDebug() << "(K3bDataDoc) converted " << path << " to " << result << endl;
+ return result;
+ }
+ else
+ return path;
+}
+
+
+void K3bDataDoc::prepareFilenames()
+{
+ m_needToCutFilenames = false;
+ m_needToCutFilenameItems.clear();
+
+ //
+ // if joliet is used cut the names and rename if necessary
+ // 64 characters for standard joliet and 103 characters for long joliet names
+ //
+ // Rockridge supports the full 255 UNIX chars and in case Rockridge is disabled we leave
+ // it to mkisofs for now since handling all the options to alter the ISO9660 standard it just
+ // too much.
+ //
+
+ K3bDataItem* item = root();
+ unsigned int maxlen = ( isoOptions().jolietLong() ? 103 : 64 );
+ while( (item = item->nextSibling()) ) {
+ item->setWrittenName( treatWhitespace( item->k3bName() ) );
+
+ if( isoOptions().createJoliet() && item->writtenName().length() > maxlen ) {
+ m_needToCutFilenames = true;
+ item->setWrittenName( K3b::cutFilename( item->writtenName(), maxlen ) );
+ m_needToCutFilenameItems.append( item );
+ }
+
+ // TODO: check the Joliet charset
+ }
+
+ //
+ // 3. check if a directory contains items with the same name
+ //
+ prepareFilenamesInDir( root() );
+}
+
+
+void K3bDataDoc::prepareFilenamesInDir( K3bDirItem* dir )
+{
+ if( !dir )
+ return;
+
+ QPtrList<K3bDataItem> sortedChildren;
+ QPtrListIterator<K3bDataItem> it( dir->children() );
+
+ for( it.toLast(); it.current(); --it ) {
+ K3bDataItem* item = it.current();
+
+ if( item->isDir() )
+ prepareFilenamesInDir( dynamic_cast<K3bDirItem*>( item ) );
+
+ // insertion sort
+ unsigned int i = 0;
+ while( i < sortedChildren.count() && item->writtenName() > sortedChildren.at(i)->writtenName() )
+ ++i;
+
+ sortedChildren.insert( i, item );
+ }
+
+
+ if( isoOptions().createJoliet() || isoOptions().createRockRidge() ) {
+ QPtrList<K3bDataItem> sameNameList;
+ while( !sortedChildren.isEmpty() ) {
+
+ sameNameList.clear();
+
+ do {
+ sameNameList.append( sortedChildren.first() );
+ sortedChildren.removeFirst();
+ } while( !sortedChildren.isEmpty() &&
+ sortedChildren.first()->writtenName() == sameNameList.first()->writtenName() );
+
+ if( sameNameList.count() > 1 ) {
+ // now we need to rename the items
+ unsigned int maxlen = 255;
+ if( isoOptions().createJoliet() ) {
+ if( isoOptions().jolietLong() )
+ maxlen = 103;
+ else
+ maxlen = 64;
+ }
+
+ int cnt = 1;
+ for( QPtrListIterator<K3bDataItem> it( sameNameList );
+ it.current(); ++it ) {
+ it.current()->setWrittenName( K3b::appendNumberToFilename( it.current()->writtenName(), cnt++, maxlen ) );
+ }
+ }
+ }
+ }
+}
+
+
+void K3bDataDoc::informAboutNotFoundFiles()
+{
+ if( !m_notFoundFiles.isEmpty() ) {
+ KMessageBox::informationList( qApp->activeWindow(), i18n("Could not find the following files:"),
+ m_notFoundFiles, i18n("Not Found") );
+ m_notFoundFiles.clear();
+ }
+
+ if( !m_noPermissionFiles.isEmpty() ) {
+ KMessageBox::informationList( qApp->activeWindow(), i18n("No permission to read the following files:"),
+ m_noPermissionFiles, i18n("No Read Permission") );
+
+ m_noPermissionFiles.clear();
+ }
+}
+
+
+void K3bDataDoc::setMultiSessionMode( K3bDataDoc::MultiSessionMode mode )
+{
+ if( m_multisessionMode == NONE || m_multisessionMode == START )
+ clearImportedSession();
+
+ m_multisessionMode = mode;
+}
+
+
+bool K3bDataDoc::importSession( K3bDevice::Device* device )
+{
+ K3bDevice::DiskInfo diskInfo = device->diskInfo();
+ // DVD+RW media is reported as non-appendable
+ if( !diskInfo.appendable() &&
+ !(diskInfo.mediaType() & (K3bDevice::MEDIA_DVD_PLUS_RW|K3bDevice::MEDIA_DVD_RW_OVWR)) )
+ return false;
+
+ K3bDevice::Toc toc = device->readToc();
+ if( toc.isEmpty() ||
+ toc.last().type() != K3bDevice::Track::DATA )
+ return false;
+
+ long startSec = toc.last().firstSector().lba();
+ K3bIso9660 iso( device, startSec );
+
+ if( iso.open() ) {
+ // remove previously imported sessions
+ clearImportedSession();
+
+ // set multisession option
+ if( m_multisessionMode != FINISH && m_multisessionMode != AUTO )
+ m_multisessionMode = CONTINUE;
+
+ // since in iso9660 it is possible that two files share it's data
+ // simply summing the file sizes could result in wrong values
+ // that's why we use the size from the toc. This is more accurate
+ // anyway since there might be files overwritten or removed
+ m_oldSessionSize = toc.last().lastSector().mode1Bytes();
+
+ kdDebug() << "(K3bDataDoc) imported session size: " << KIO::convertSize(m_oldSessionSize) << endl;
+
+ // the track size for DVD+RW media and DVD-RW Overwrite media has nothing to do with the filesystem
+ // size. in that case we need to use the filesystem's size (which is ok since it's one track anyway,
+ // no real multisession)
+ if( diskInfo.mediaType() & (K3bDevice::MEDIA_DVD_PLUS_RW|K3bDevice::MEDIA_DVD_RW_OVWR) ) {
+ m_oldSessionSize = iso.primaryDescriptor().volumeSpaceSize
+ * iso.primaryDescriptor().logicalBlockSize;
+ }
+
+ // import some former settings
+ m_isoOptions.setCreateRockRidge( iso.firstRRDirEntry() != 0 );
+ m_isoOptions.setCreateJoliet( iso.firstJolietDirEntry() != 0 );
+ m_isoOptions.setVolumeID( iso.primaryDescriptor().volumeId );
+ // TODO: also import some other pd fields
+
+ const K3bIso9660Directory* rootDir = iso.firstRRDirEntry();
+ // Jörg Schilling says that it is impossible to import the joliet tree for multisession
+// if( !rootDir )
+// rootDir = iso.firstJolietDirEntry();
+ if( !rootDir )
+ rootDir = iso.firstIsoDirEntry();
+
+ if( rootDir ) {
+ createSessionImportItems( rootDir, root() );
+ emit changed();
+ return true;
+ }
+ else {
+ kdDebug() << "(K3bDataDoc::importSession) Could not find primary volume desc." << endl;
+ return false;
+ }
+ }
+ else {
+ kdDebug() << "(K3bDataDoc) unable to read toc." << endl;
+ return false;
+ }
+}
+
+
+void K3bDataDoc::createSessionImportItems( const K3bIso9660Directory* importDir, K3bDirItem* parent )
+{
+ Q_ASSERT(importDir);
+ QStringList entries = importDir->entries();
+ entries.remove( "." );
+ entries.remove( ".." );
+ for( QStringList::const_iterator it = entries.begin();
+ it != entries.end(); ++it ) {
+ const K3bIso9660Entry* entry = importDir->entry( *it );
+ K3bDataItem* oldItem = parent->find( entry->name() );
+ if( entry->isDirectory() ) {
+ K3bDirItem* dir = 0;
+ if( oldItem && oldItem->isDir() ) {
+ dir = (K3bDirItem*)oldItem;
+ }
+ else {
+ // we overwrite without warning!
+ if( oldItem )
+ removeItem( oldItem );
+ dir = new K3bDirItem( entry->name(), this, parent );
+ }
+
+ dir->setRemoveable(false);
+ dir->setRenameable(false);
+ dir->setMoveable(false);
+ dir->setHideable(false);
+ dir->setWriteToCd(false);
+ dir->setExtraInfo( i18n("From previous session") );
+ m_oldSession.append( dir );
+
+ createSessionImportItems( static_cast<const K3bIso9660Directory*>(entry), dir );
+ }
+ else {
+ const K3bIso9660File* file = static_cast<const K3bIso9660File*>(entry);
+
+ // we overwrite without warning!
+ if( oldItem )
+ removeItem( oldItem );
+
+ K3bSessionImportItem* item = new K3bSessionImportItem( file, this, parent );
+ item->setExtraInfo( i18n("From previous session") );
+ m_oldSession.append( item );
+ }
+ }
+}
+
+
+void K3bDataDoc::clearImportedSession()
+{
+ // m_oldSessionSizeHandler->clear();
+ m_oldSessionSize = 0;
+ m_oldSession.setAutoDelete(false);
+ K3bDataItem* item = m_oldSession.first();
+ while( !m_oldSession.isEmpty() ) {
+ if( item == 0 )
+ item = m_oldSession.first();
+
+ if( item->isDir() ) {
+ K3bDirItem* dir = (K3bDirItem*)item;
+ if( dir->numDirs() + dir->numFiles() == 0 ) {
+ // this imported dir is not needed anymore
+ // since it is empty
+ m_oldSession.remove();
+ delete dir;
+ }
+ else {
+ for( QPtrListIterator<K3bDataItem> it( dir->children() ); it.current(); ++it ) {
+ if( !m_oldSession.contains(it.current()) ) {
+ m_oldSession.remove();
+ // now the dir becomes a totally normal dir
+ dir->setRemoveable(true);
+ dir->setRenameable(true);
+ dir->setMoveable(true);
+ dir->setHideable(true);
+ dir->setWriteToCd(true);
+ dir->setExtraInfo( "" );
+ break;
+ }
+ }
+ }
+ }
+ else {
+ m_oldSession.remove();
+ delete item;
+ }
+
+ item = m_oldSession.next();
+ }
+
+ m_multisessionMode = AUTO;
+
+ emit changed();
+}
+
+
+K3bDirItem* K3bDataDoc::bootImageDir()
+{
+ K3bDataItem* b = m_root->find( "boot" );
+ if( !b ) {
+ b = new K3bDirItem( "boot", this, m_root );
+ setModified( true );
+ }
+
+ // if we cannot create the dir because there is a file named boot just use the root dir
+ if( !b->isDir() )
+ return m_root;
+ else
+ return static_cast<K3bDirItem*>(b);
+}
+
+
+K3bBootItem* K3bDataDoc::createBootItem( const QString& filename, K3bDirItem* dir )
+{
+ if( !dir )
+ dir = bootImageDir();
+
+ K3bBootItem* boot = new K3bBootItem( filename, this, dir );
+
+ if( !m_bootCataloge )
+ createBootCatalogeItem(dir);
+
+ return boot;
+}
+
+
+K3bDataItem* K3bDataDoc::createBootCatalogeItem( K3bDirItem* dir )
+{
+ if( !m_bootCataloge ) {
+ QString newName = "boot.catalog";
+ int i = 0;
+ while( dir->alreadyInDirectory( "boot.catalog" ) ) {
+ ++i;
+ newName = QString( "boot%1.catalog" ).arg(i);
+ }
+
+ K3bSpecialDataItem* b = new K3bSpecialDataItem( this, 0, dir, newName );
+ m_bootCataloge = b;
+ m_bootCataloge->setRemoveable(false);
+ m_bootCataloge->setHideable(false);
+ m_bootCataloge->setWriteToCd(false);
+ m_bootCataloge->setExtraInfo( i18n("El Torito boot catalog file") );
+ b->setMimeType( i18n("Boot catalog") );
+ }
+ else
+ m_bootCataloge->reparent( dir );
+
+ return m_bootCataloge;
+}
+
+
+QValueList<K3bDataItem*> K3bDataDoc::findItemByLocalPath( const QString& path ) const
+{
+ Q_UNUSED( path );
+ return QValueList<K3bDataItem*>();
+}
+
+
+bool K3bDataDoc::sessionImported() const
+{
+ return !m_oldSession.isEmpty();
+}
+
+#include "k3bdatadoc.moc"
diff --git a/libk3b/projects/datacd/k3bdatadoc.h b/libk3b/projects/datacd/k3bdatadoc.h
new file mode 100644
index 0000000..e09177a
--- /dev/null
+++ b/libk3b/projects/datacd/k3bdatadoc.h
@@ -0,0 +1,297 @@
+/*
+ *
+ * $Id: k3bdatadoc.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BDATADOC_H
+#define K3BDATADOC_H
+
+#include <k3bdoc.h>
+#include <k3bdataitem.h>
+
+#include "k3bisooptions.h"
+
+#include <qptrlist.h>
+#include <qfileinfo.h>
+#include <qstringlist.h>
+
+#include <kurl.h>
+#include <kio/global.h>
+#include "k3b_export.h"
+
+class K3bDataItem;
+class K3bRootItem;
+class K3bDirItem;
+class K3bFileItem;
+class K3bJob;
+class K3bBootItem;
+class K3bFileCompilationSizeHandler;
+
+class KProgressDialog;
+//class K3bView;
+class KConfig;
+class QString;
+class QStringList;
+class QWidget;
+class QDomDocument;
+class QDomElement;
+class K3bIso9660Directory;
+
+namespace K3bDevice {
+ class Device;
+ class DeviceHandler;
+}
+
+
+/**
+ *@author Sebastian Trueg
+ */
+
+class LIBK3B_EXPORT K3bDataDoc : public K3bDoc
+{
+ Q_OBJECT
+
+ public:
+ K3bDataDoc( QObject* parent = 0 );
+ virtual ~K3bDataDoc();
+
+ virtual int type() const { return DATA; }
+ virtual QString typeString() const;
+
+ virtual QString name() const;
+
+ enum MultiSessionMode {
+ /**
+ * Let the K3bDataJob decide if to close the CD or not.
+ * The decision is based on the state of the inserted media
+ * (appendable/closed), the size of the project (will it fill
+ * up the CD?), and the free space on the inserted media.
+ */
+ AUTO,
+ NONE,
+ START,
+ CONTINUE,
+ FINISH
+ };
+
+ K3bRootItem* root() const { return m_root; }
+
+ virtual bool newDocument();
+ virtual KIO::filesize_t size() const;
+
+ /**
+ * This is used for multisession where size() also returnes the imported session's size
+ */
+ virtual KIO::filesize_t burningSize() const;
+ virtual K3b::Msf length() const;
+ virtual K3b::Msf burningLength() const;
+
+ /**
+ * Simply deletes the item if it is removable (meaning isRemovable() returns true.
+ * Be aware that you can remove items simply by deleting them even if isRemovable()
+ * returns false.
+ */
+ void removeItem( K3bDataItem* item );
+
+ /**
+ * Simply calls reparent.
+ */
+ void moveItem( K3bDataItem* item, K3bDirItem* newParent );
+ void moveItems( QPtrList<K3bDataItem> itemList, K3bDirItem* newParent );
+
+ K3bDirItem* addEmptyDir( const QString& name, K3bDirItem* parent );
+
+ QString treatWhitespace( const QString& );
+
+ virtual K3bBurnJob* newBurnJob( K3bJobHandler* hdl, QObject* parent = 0 );
+
+ MultiSessionMode multiSessionMode() const { return m_multisessionMode; }
+ void setMultiSessionMode( MultiSessionMode mode );
+
+ int dataMode() const { return m_dataMode; }
+ void setDataMode( int m ) { m_dataMode = m; }
+
+ void setVerifyData( bool b ) { m_verifyData = b; }
+ bool verifyData() const { return m_verifyData; }
+
+ static bool nameAlreadyInDir( const QString&, K3bDirItem* );
+
+ /**
+ * Most of the options that map to the mkisofs parameters are grouped
+ * together in the K3bIsoOptions class to allow easy saving to and loading
+ * from a KConfig object.
+ */
+ const K3bIsoOptions& isoOptions() const { return m_isoOptions; }
+ void setIsoOptions( const K3bIsoOptions& );
+
+ const QPtrList<K3bBootItem>& bootImages() { return m_bootImages; }
+ K3bDataItem* bootCataloge() { return m_bootCataloge; }
+
+ K3bDirItem* bootImageDir();
+
+ /**
+ * Create a boot item and also create a boot cataloge file in case none
+ * exists in the project.
+ *
+ * Calling this method has the same effect like creating a new K3bBootItem
+ * instance manually and then calling createBootCatalogeItem.
+ *
+ * \return The new boot item on success or 0 in case a file with the same
+ * name already exists.
+ */
+ K3bBootItem* createBootItem( const QString& filename, K3bDirItem* bootDir = 0 );
+
+ /**
+ * Create a new boot catalog item.
+ * For now this is not called automatically for internal reasons.
+ *
+ * Call this if you create boot items manually instead of using createBootItem.
+ *
+ * The boot catalog is automatically deleted once the last boot item is removed
+ * from the doc.
+ *
+ * \return The new boot catalog item or the old one if it already exists.
+ */
+ K3bDataItem* createBootCatalogeItem( K3bDirItem* bootDir );
+
+ /**
+ * This will prepare the filenames as written to the image.
+ * These filenames are saved in K3bDataItem::writtenName
+ */
+ void prepareFilenames();
+
+ /**
+ * Returns true if filenames need to be cut due to the limitations of Joliet.
+ *
+ * This is only valid after a call to @p prepareFilenames()
+ */
+ bool needToCutFilenames() const { return m_needToCutFilenames; }
+
+ const QValueList<K3bDataItem*>& needToCutFilenameItems() const { return m_needToCutFilenameItems; }
+
+ /**
+ * Imports a session into the project. This will create K3bSessionImportItems
+ * and properly set the imported session size.
+ * Some settings will be adjusted to the imported session (joliet, rr).
+ *
+ * Be aware that this method is blocking.
+ *
+ * \return true if the old session was successfully imported, false if no
+ * session could be found.
+ *
+ * \see clearImportedSession()
+ */
+ bool importSession( K3bDevice::Device* );
+
+ bool sessionImported() const;
+
+ /**
+ * Searches for an item by it's local path.
+ *
+ * NOT IMPLEMENTED YET!
+ *
+ * \return The items that correspond to the specified local path.
+ */
+ QValueList<K3bDataItem*> findItemByLocalPath( const QString& path ) const;
+
+ public slots:
+ virtual void addUrls( const KURL::List& urls );
+
+ /**
+ * Add urls syncroneously
+ * This method adds files recursively including symlinks, hidden, and system files.
+ * If a file already exists the new file's name will be appended a number.
+ */
+ virtual void addUrls( const KURL::List& urls, K3bDirItem* dir );
+
+ void clearImportedSession();
+
+ /**
+ * Just a convience method to prevent using setIsoOptions for this
+ * often used value.
+ */
+ void setVolumeID( const QString& );
+
+ signals:
+ void itemRemoved( K3bDataItem* );
+ void itemAdded( K3bDataItem* );
+
+ protected:
+ /** reimplemented from K3bDoc */
+ virtual bool loadDocumentData( QDomElement* root );
+ /** reimplemented from K3bDoc */
+ virtual bool saveDocumentData( QDomElement* );
+
+ void saveDocumentDataOptions( QDomElement& optionsElem );
+ void saveDocumentDataHeader( QDomElement& headerElem );
+ bool loadDocumentDataOptions( QDomElement optionsElem );
+ bool loadDocumentDataHeader( QDomElement optionsElem );
+
+ K3bFileCompilationSizeHandler* m_sizeHandler;
+
+ // K3bFileCompilationSizeHandler* m_oldSessionSizeHandler;
+ KIO::filesize_t m_oldSessionSize;
+
+ private:
+ void prepareFilenamesInDir( K3bDirItem* dir );
+ void createSessionImportItems( const K3bIso9660Directory*, K3bDirItem* parent );
+
+ /**
+ * used by K3bDirItem to inform about removed items.
+ */
+ void itemRemovedFromDir( K3bDirItem* parent, K3bDataItem* removedItem );
+ void itemAddedToDir( K3bDirItem* parent, K3bDataItem* addedItem );
+
+ /**
+ * load recursivly
+ */
+ bool loadDataItem( QDomElement& e, K3bDirItem* parent );
+ /**
+ * save recursivly
+ */
+ void saveDataItem( K3bDataItem* item, QDomDocument* doc, QDomElement* parent );
+
+ void informAboutNotFoundFiles();
+
+ QStringList m_notFoundFiles;
+ QStringList m_noPermissionFiles;
+
+ K3bRootItem* m_root;
+
+ int m_dataMode;
+
+ bool m_verifyData;
+
+ KIO::filesize_t m_size;
+
+ K3bIsoOptions m_isoOptions;
+
+ MultiSessionMode m_multisessionMode;
+ QPtrList<K3bDataItem> m_oldSession;
+
+ // boot cd stuff
+ K3bDataItem* m_bootCataloge;
+ QPtrList<K3bBootItem> m_bootImages;
+
+ bool m_bExistingItemsReplaceAll;
+ bool m_bExistingItemsIgnoreAll;
+
+ bool m_needToCutFilenames;
+ QValueList<K3bDataItem*> m_needToCutFilenameItems;
+
+ friend class K3bMixedDoc;
+ friend class K3bDirItem;
+};
+
+#endif
diff --git a/libk3b/projects/datacd/k3bdataitem.cpp b/libk3b/projects/datacd/k3bdataitem.cpp
new file mode 100644
index 0000000..6f2a861
--- /dev/null
+++ b/libk3b/projects/datacd/k3bdataitem.cpp
@@ -0,0 +1,264 @@
+/*
+ *
+ * $Id: k3bdataitem.cpp 659634 2007-04-30 14:51:32Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bdataitem.h"
+#include "k3bdiritem.h"
+#include "k3bdatadoc.h"
+#include <kdebug.h>
+
+#include <math.h>
+
+
+class K3bDataItem::Private
+{
+public:
+ int flags;
+};
+
+
+K3bDataItem::K3bDataItem( K3bDataDoc* doc, K3bDataItem* parent, int flags )
+ : m_bHideOnRockRidge(false),
+ m_bHideOnJoliet(false),
+ m_bRemoveable(true),
+ m_bRenameable(true),
+ m_bMovable(true),
+ m_bHideable(true),
+ m_bWriteToCd(true),
+ m_sortWeight(0)
+{
+ d = new Private;
+ d->flags = flags;
+
+ m_doc = doc;
+ m_bHideOnRockRidge = m_bHideOnJoliet = false;
+
+ if( parent )
+ m_parentDir = parent->getDirItem();
+ else
+ m_parentDir = 0;
+}
+
+
+K3bDataItem::K3bDataItem( const K3bDataItem& item )
+ : m_k3bName( item.m_k3bName ),
+ m_doc( 0 ),
+ m_parentDir( 0 ),
+ m_bHideOnRockRidge( item.m_bHideOnRockRidge ),
+ m_bHideOnJoliet( item.m_bHideOnJoliet ),
+ m_bRemoveable( item.m_bRemoveable ),
+ m_bRenameable( item.m_bRenameable ),
+ m_bMovable( item.m_bMovable ),
+ m_bHideable( item.m_bHideable ),
+ m_bWriteToCd( item.m_bWriteToCd ),
+ m_extraInfo( item.m_extraInfo ),
+ m_sortWeight( item.m_sortWeight )
+{
+ d = new Private;
+ d->flags = item.d->flags;
+}
+
+
+K3bDataItem::~K3bDataItem()
+{
+ delete d;
+}
+
+
+void K3bDataItem::setFlags( int flags )
+{
+ d->flags = flags;
+}
+
+
+bool K3bDataItem::isBootItem() const
+{
+ return d->flags & BOOT_IMAGE;
+}
+
+
+KIO::filesize_t K3bDataItem::size() const
+{
+ return itemSize( m_doc
+ ? m_doc->isoOptions().followSymbolicLinks() ||
+ !m_doc->isoOptions().createRockRidge()
+ : false );
+}
+
+
+K3b::Msf K3bDataItem::blocks() const
+{
+ return itemBlocks( m_doc
+ ? m_doc->isoOptions().followSymbolicLinks() ||
+ !m_doc->isoOptions().createRockRidge()
+ : false );
+}
+
+
+K3b::Msf K3bDataItem::itemBlocks( bool followSymbolicLinks ) const
+{
+ return (long)::ceil( (double)itemSize( followSymbolicLinks ) / 2048.0 );
+}
+
+
+void K3bDataItem::setK3bName( const QString& name ) {
+ if ( name != m_k3bName ) {
+ // test for not-allowed characters
+ if( name.contains('/') ) {
+ kdDebug() << "(K3bDataItem) name contained invalid characters!" << endl;
+ return;
+ }
+
+ if( parent() ) {
+ K3bDataItem* item = parent()->find( name );
+ if( item && item != this ) {
+ kdDebug() << "(K3bDataItem) item with that name already exists." << endl;
+ return;
+ }
+ }
+
+ m_k3bName = name;
+ m_doc->setModified();
+}
+}
+
+
+const QString& K3bDataItem::k3bName() const
+{
+ return m_k3bName;
+}
+
+
+K3bDataItem* K3bDataItem::take()
+{
+ if( parent() )
+ parent()->takeDataItem( this );
+
+ return this;
+}
+
+
+QString K3bDataItem::k3bPath() const
+{
+ if( !getParent() )
+ return QString::null; // the root item is the only one not having a parent
+ else if( isDir() )
+ return getParent()->k3bPath() + k3bName() + "/";
+ else
+ return getParent()->k3bPath() + k3bName();
+}
+
+
+QString K3bDataItem::writtenPath() const
+{
+ if( !getParent() )
+ return QString::null; // the root item is the only one not having a parent
+ else if( isDir() )
+ return getParent()->writtenPath() + writtenName() + "/";
+ else
+ return getParent()->writtenPath() + writtenName();
+}
+
+
+QString K3bDataItem::iso9660Path() const
+{
+ if( !getParent() )
+ return QString::null; // the root item is the only one not having a parent
+ else if( isDir() )
+ return getParent()->iso9660Path() + iso9660Name() + "/";
+ else
+ return getParent()->iso9660Path() + iso9660Name();
+}
+
+
+K3bDataItem* K3bDataItem::nextSibling() const
+{
+ K3bDataItem* item = const_cast<K3bDataItem*>(this); // urg, but we know that we don't mess with it, so...
+ K3bDirItem* parentItem = getParent();
+
+ while( parentItem ) {
+ if( K3bDataItem* i = parentItem->nextChild( item ) )
+ return i;
+
+ item = parentItem;
+ parentItem = item->getParent();
+ }
+
+ return 0;
+}
+
+
+void K3bDataItem::reparent( K3bDirItem* newParent )
+{
+ // addDataItem will do all the stuff including taking this
+ newParent->addDataItem( this );
+}
+
+
+bool K3bDataItem::hideOnRockRidge() const
+{
+ if( !isHideable() )
+ return false;
+ if( getParent() )
+ return m_bHideOnRockRidge || getParent()->hideOnRockRidge();
+ else
+ return m_bHideOnRockRidge;
+}
+
+
+bool K3bDataItem::hideOnJoliet() const
+{
+ if( !isHideable() )
+ return false;
+ if( getParent() )
+ return m_bHideOnJoliet || getParent()->hideOnJoliet();
+ else
+ return m_bHideOnJoliet;
+}
+
+
+void K3bDataItem::setHideOnRockRidge( bool b )
+{
+ // there is no use in changing the value if
+ // it is already set by the parent
+ if( ( !getParent() || !getParent()->hideOnRockRidge() ) &&
+ b != m_bHideOnRockRidge ) {
+ m_bHideOnRockRidge = b;
+ if ( m_doc )
+ m_doc->setModified();
+}
+}
+
+
+void K3bDataItem::setHideOnJoliet( bool b )
+{
+ // there is no use in changing the value if
+ // it is already set by the parent
+ if( ( !getParent() || !getParent()->hideOnJoliet() ) &&
+ b != m_bHideOnJoliet ) {
+ m_bHideOnJoliet = b;
+ if ( m_doc )
+ m_doc->setModified();
+}
+}
+
+
+int K3bDataItem::depth() const
+{
+ if( getParent() )
+ return getParent()->depth() + 1;
+ else
+ return 0;
+}
diff --git a/libk3b/projects/datacd/k3bdataitem.h b/libk3b/projects/datacd/k3bdataitem.h
new file mode 100644
index 0000000..36cdf05
--- /dev/null
+++ b/libk3b/projects/datacd/k3bdataitem.h
@@ -0,0 +1,225 @@
+/*
+ *
+ * $Id: k3bdataitem.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BDATAITEM_H
+#define K3BDATAITEM_H
+
+
+class K3bDirItem;
+class K3bDataDoc;
+
+#include <qstring.h>
+
+#include <kio/global.h>
+
+#include <k3bmsf.h>
+#include "k3b_export.h"
+
+
+/**
+ *@author Sebastian Trueg
+ */
+class LIBK3B_EXPORT K3bDataItem
+{
+ public:
+ K3bDataItem( K3bDataDoc* doc, K3bDataItem* parent = 0, int flags = 0 );
+
+ /**
+ * Default copy constructor.
+ *
+ * The result is an exact copy except that no parent dir it set and, thus, also no doc.
+ */
+ K3bDataItem( const K3bDataItem& );
+
+ virtual ~K3bDataItem();
+
+ /**
+ * Return an exact copy of this data item.
+ *
+ * The result is an exact copy except that no parent dir it set and, thus, also no doc.
+ *
+ * Implementations should use the default constructor.
+ */
+ virtual K3bDataItem* copy() const = 0;
+
+ K3bDirItem* parent() { return m_parentDir; }
+ K3bDirItem* getParent() const { return m_parentDir; }
+
+ /**
+ * Remove this item from it's parent and return a pointer to it.
+ */
+ K3bDataItem* take();
+
+ K3bDataDoc* doc() const { return m_doc; }
+ virtual const QString& k3bName() const;
+ virtual void setK3bName( const QString& );
+
+ /**
+ * returns the path as defined by the k3b-hierachy, NOT starting with a slash
+ * (since this is used for graft-points!)
+ * directories have a trailing "/"
+ */
+ virtual QString k3bPath() const;
+
+ /**
+ * Returns the name of the item as used on the CD or DVD image.
+ *
+ * This is only valid after a call to @p K3bDataDoc::prepareFilenames()
+ */
+ const QString& writtenName() const { return m_writtenName; }
+
+ /**
+ * \return the pure name used in the Iso9660 tree.
+ *
+ * This is only valid after a call to @p K3bDataDoc::prepareFilenames()
+ */
+ const QString& iso9660Name() const { return m_rawIsoName; }
+
+ /**
+ * Returns the path of the item as written to the CD or DVD image.
+ *
+ * This is suited to be used for mkisofs graftpoints.
+ *
+ * This is only valid after a call to @p K3bDataDoc::prepareFilenames()
+ */
+ virtual QString writtenPath() const;
+
+ virtual QString iso9660Path() const;
+
+ /**
+ * Used to set the written name by @p K3bDataDoc::prepareFilenames()
+ */
+ void setWrittenName( const QString& s ) { m_writtenName = s; }
+
+ /**
+ * Used to set the pure Iso9660 name by @p K3bDataDoc::prepareFilenames()
+ */
+ void setIso9660Name( const QString& s ) { m_rawIsoName = s; }
+
+ virtual K3bDataItem* nextSibling() const;
+
+ /** returns the path to the file on the local filesystem */
+ virtual QString localPath() const { return QString::null; }
+
+ /**
+ * The size of the item
+ */
+ KIO::filesize_t size() const;
+
+ /**
+ * \return The number of blocks (2048 bytes) occupied by this item.
+ * This value equals to ceil(size()/2048)
+ */
+ K3b::Msf blocks() const;
+
+ /**
+ * \returne the dir of the item (or the item itself if it is a dir)
+ */
+ virtual K3bDirItem* getDirItem() const { return getParent(); }
+
+ virtual void reparent( K3bDirItem* );
+
+ // FIXME: use all these flags and make the isXXX methods
+ // non-virtual. Then move the parent()->addDataItem call
+ // to the K3bDataItem constructor
+ enum ItemFlags {
+ DIR = 0x1,
+ FILE = 0x2,
+ SPECIALFILE = 0x4,
+ SYMLINK = 0x8,
+ OLD_SESSION = 0x10,
+ BOOT_IMAGE = 0x11
+ };
+
+ int flags() const;
+
+ virtual bool isDir() const { return false; }
+ virtual bool isFile() const { return false; }
+ virtual bool isSpecialFile() const { return false; }
+ virtual bool isSymLink() const { return false; }
+ virtual bool isFromOldSession() const { return false; }
+ bool isBootItem() const;
+
+ bool hideOnRockRidge() const;
+ bool hideOnJoliet() const;
+
+ virtual void setHideOnRockRidge( bool b );
+ virtual void setHideOnJoliet( bool b );
+
+ virtual long sortWeight() const { return m_sortWeight; }
+ virtual void setSortWeight( long w ) { m_sortWeight = w; }
+
+ virtual int depth() const;
+
+ virtual bool isValid() const { return true; }
+
+ // these are all needed for special fileitems like
+ // imported sessions or the movix filesystem
+ virtual bool isRemoveable() const { return m_bRemoveable; }
+ virtual bool isMoveable() const { return m_bMovable; }
+ virtual bool isRenameable() const { return m_bRenameable; }
+ virtual bool isHideable() const { return m_bHideable; }
+ virtual bool writeToCd() const { return m_bWriteToCd; }
+ virtual const QString& extraInfo() const { return m_extraInfo; }
+
+ void setRenameable( bool b ) { m_bRenameable = b; }
+ void setMoveable( bool b ) { m_bMovable = b; }
+ void setRemoveable( bool b ) { m_bRemoveable = b; }
+ void setHideable( bool b ) { m_bHideable = b; }
+ void setWriteToCd( bool b ) { m_bWriteToCd = b; }
+ void setExtraInfo( const QString& i ) { m_extraInfo = i; }
+
+ protected:
+ virtual KIO::filesize_t itemSize( bool followSymlinks ) const = 0;
+
+ /**
+ * \param followSymlinks If true symlinks will be followed and their
+ * size equals the size of the file they are
+ * pointing to.
+ *
+ * \return The number of blocks (2048 bytes) occupied by this item.
+ */
+ virtual K3b::Msf itemBlocks( bool followSymlinks ) const;
+
+ QString m_k3bName;
+
+ void setFlags( int flags );
+
+ private:
+ class Private;
+ Private* d;
+
+ QString m_writtenName;
+ QString m_rawIsoName;
+
+ K3bDataDoc* m_doc;
+ K3bDirItem* m_parentDir;
+
+ bool m_bHideOnRockRidge;
+ bool m_bHideOnJoliet;
+ bool m_bRemoveable;
+ bool m_bRenameable;
+ bool m_bMovable;
+ bool m_bHideable;
+ bool m_bWriteToCd;
+ QString m_extraInfo;
+
+ long m_sortWeight;
+
+ friend class K3bDirItem;
+};
+
+#endif
diff --git a/libk3b/projects/datacd/k3bdatajob.cpp b/libk3b/projects/datacd/k3bdatajob.cpp
new file mode 100644
index 0000000..7009a43
--- /dev/null
+++ b/libk3b/projects/datacd/k3bdatajob.cpp
@@ -0,0 +1,972 @@
+/*
+ *
+ * $Id: k3bdatajob.cpp 690187 2007-07-20 09:18:03Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bdatajob.h"
+#include "k3bdatadoc.h"
+#include "k3bisoimager.h"
+#include "k3bmsinfofetcher.h"
+
+#include <k3bcore.h>
+#include <k3bglobals.h>
+#include <k3bversion.h>
+#include <k3bdevice.h>
+#include <k3bdevicehandler.h>
+#include <k3btoc.h>
+#include <k3btrack.h>
+#include <k3bdevicehandler.h>
+#include <k3bexternalbinmanager.h>
+#include <k3bcdrecordwriter.h>
+#include <k3bcdrdaowriter.h>
+#include <k3bglobalsettings.h>
+#include <k3bactivepipe.h>
+#include <k3bfilesplitter.h>
+#include <k3bverificationjob.h>
+
+#include <kprocess.h>
+#include <kapplication.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <ktempfile.h>
+#include <kio/global.h>
+#include <kio/job.h>
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qdatetime.h>
+#include <qfile.h>
+#include <qdatastream.h>
+#include <kdebug.h>
+
+
+
+class K3bDataJob::Private
+{
+public:
+ Private()
+ : usedWritingApp(K3b::CDRECORD),
+ verificationJob(0) {
+ }
+
+ K3bDataDoc* doc;
+
+ bool initializingImager;
+ bool imageFinished;
+ bool canceled;
+
+ KTempFile* tocFile;
+
+ int usedDataMode;
+ int usedWritingApp;
+ int usedWritingMode;
+ K3bDataDoc::MultiSessionMode usedMultiSessionMode;
+
+ int copies;
+ int copiesDone;
+
+ K3bVerificationJob* verificationJob;
+
+ K3bFileSplitter imageFile;
+ K3bActivePipe pipe;
+};
+
+
+K3bDataJob::K3bDataJob( K3bDataDoc* doc, K3bJobHandler* hdl, QObject* parent )
+ : K3bBurnJob( hdl, parent )
+{
+ d = new Private;
+
+ d->doc = doc;
+ m_writerJob = 0;
+ d->tocFile = 0;
+
+ m_isoImager = 0;
+
+ m_msInfoFetcher = new K3bMsInfoFetcher( this, this );
+ connect( m_msInfoFetcher, SIGNAL(finished(bool)), this, SLOT(slotMsInfoFetched(bool)) );
+ connect( m_msInfoFetcher, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( m_msInfoFetcher, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+
+ d->imageFinished = true;
+}
+
+K3bDataJob::~K3bDataJob()
+{
+ delete d->tocFile;
+ delete d;
+}
+
+
+K3bDoc* K3bDataJob::doc() const
+{
+ return d->doc;
+}
+
+
+K3bDevice::Device* K3bDataJob::writer() const
+{
+ if( doc()->onlyCreateImages() )
+ return 0; // no writer needed -> no blocking on K3bBurnJob
+ else
+ return doc()->burner();
+}
+
+
+void K3bDataJob::start()
+{
+ jobStarted();
+
+ d->canceled = false;
+ d->imageFinished = false;
+ d->copies = d->doc->copies();
+ d->copiesDone = 0;
+ d->usedMultiSessionMode = d->doc->multiSessionMode();
+
+ prepareImager();
+
+ if( d->doc->dummy() ) {
+ d->doc->setVerifyData( false );
+ d->copies = 1;
+ }
+
+ emit newTask( i18n("Preparing data") );
+
+ // there is no harm in setting these even if we write on-the-fly
+ d->imageFile.setName( d->doc->tempDir() );
+ d->pipe.readFromIODevice( &d->imageFile );
+
+ if( d->usedMultiSessionMode == K3bDataDoc::AUTO && !d->doc->onlyCreateImages() )
+ determineMultiSessionMode();
+ else
+ prepareWriting();
+}
+
+
+void K3bDataJob::prepareWriting()
+{
+ if( !d->doc->onlyCreateImages() &&
+ ( d->usedMultiSessionMode == K3bDataDoc::CONTINUE ||
+ d->usedMultiSessionMode == K3bDataDoc::FINISH ) ) {
+ // no sense continuing the same session twice
+ // FIXME: why not?
+ d->copies = 1;
+
+ m_msInfoFetcher->setDevice( d->doc->burner() );
+
+ if( !waitForMedium() ) {
+ cancel();
+ return;
+ }
+
+ if( K3b::isMounted( d->doc->burner() ) ) {
+ emit infoMessage( i18n("Unmounting disk"), INFO );
+ K3b::unmount( d->doc->burner() );
+ }
+
+ m_msInfoFetcher->start();
+ }
+ else {
+ m_isoImager->setMultiSessionInfo( QString::null );
+ prepareData();
+
+ d->initializingImager = true;
+ m_isoImager->init();
+ }
+}
+
+
+void K3bDataJob::slotMsInfoFetched(bool success)
+{
+ if( success ) {
+ // we call this here since in ms mode we might want to check
+ // the last track's datamode
+ prepareData();
+
+ if( d->usedWritingApp == K3b::CDRDAO ) // cdrdao seems to write a 150 blocks pregap that is not used by cdrecord
+ m_isoImager->setMultiSessionInfo( QString("%1,%2").arg(m_msInfoFetcher->lastSessionStart()).arg(m_msInfoFetcher->nextSessionStart()+150), d->doc->burner() );
+ else
+ m_isoImager->setMultiSessionInfo( m_msInfoFetcher->msInfo(), d->doc->burner() );
+
+ d->initializingImager = true;
+ m_isoImager->init();
+ }
+ else {
+ // the MsInfoFetcher already emitted failure info
+ cancelAll();
+ jobFinished( false );
+ }
+}
+
+
+void K3bDataJob::writeImage()
+{
+ d->initializingImager = false;
+
+ emit burning(false);
+
+ // get image file path
+ if( d->doc->tempDir().isEmpty() )
+ d->doc->setTempDir( K3b::findUniqueFilePrefix( d->doc->isoOptions().volumeID() ) + ".iso" );
+
+ // TODO: check if the image file is part of the project and if so warn the user
+ // and append some number to make the path unique.
+
+ emit newTask( i18n("Creating image file") );
+ emit newSubTask( i18n("Track 1 of 1") );
+ emit infoMessage( i18n("Creating image file in %1").arg(d->doc->tempDir()), INFO );
+
+ m_isoImager->writeToImageFile( d->doc->tempDir() );
+ m_isoImager->start();
+}
+
+
+bool K3bDataJob::startOnTheFlyWriting()
+{
+ if( prepareWriterJob() ) {
+ if( startWriterJob() ) {
+ // try a direct connection between the processes
+ if( m_writerJob->fd() != -1 )
+ m_isoImager->writeToFd( m_writerJob->fd() );
+ d->initializingImager = false;
+ m_isoImager->start();
+ return true;
+ }
+ }
+ return false;
+}
+
+
+void K3bDataJob::cancel()
+{
+ emit infoMessage( i18n("Writing canceled."), K3bJob::ERROR );
+ emit canceled();
+
+ if( m_writerJob && m_writerJob->active() ) {
+ //
+ // lets wait for the writer job to finish
+ // and let it finish the job for good.
+ //
+ cancelAll();
+ }
+ else {
+ //
+ // Just cancel all and return
+ // This is bad design as we should wait for all subjobs to finish
+ //
+ cancelAll();
+ jobFinished( false );
+ }
+}
+
+
+void K3bDataJob::slotIsoImagerPercent( int p )
+{
+ if( d->doc->onlyCreateImages() ) {
+ emit subPercent( p );
+ emit percent( p );
+ }
+ else if( !d->doc->onTheFly() ) {
+ double totalTasks = d->copies;
+ double tasksDone = d->copiesDone; // =0 when creating an image
+ if( d->doc->verifyData() ) {
+ totalTasks*=2;
+ tasksDone*=2;
+ }
+ if( !d->doc->onTheFly() ) {
+ totalTasks+=1.0;
+ }
+
+ emit subPercent( p );
+ emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
+ }
+}
+
+
+void K3bDataJob::slotIsoImagerFinished( bool success )
+{
+ if( d->initializingImager ) {
+ if( success ) {
+ if( d->doc->onTheFly() && !d->doc->onlyCreateImages() ) {
+ if( !startOnTheFlyWriting() ) {
+ cancelAll();
+ jobFinished( false );
+ }
+ }
+ else {
+ writeImage();
+ }
+ }
+ else {
+ if( m_isoImager->hasBeenCanceled() )
+ emit canceled();
+ jobFinished( false );
+ }
+ }
+ else {
+ // tell the writer that there won't be more data
+ if( d->doc->onTheFly() && m_writerJob )
+ m_writerJob->closeFd();
+
+ if( !d->doc->onTheFly() ||
+ d->doc->onlyCreateImages() ) {
+
+ if( success ) {
+ emit infoMessage( i18n("Image successfully created in %1").arg(d->doc->tempDir()), K3bJob::SUCCESS );
+ d->imageFinished = true;
+
+ if( d->doc->onlyCreateImages() ) {
+ jobFinished( true );
+ }
+ else {
+ if( prepareWriterJob() ) {
+ startWriterJob();
+ d->pipe.writeToFd( m_writerJob->fd(), true );
+ d->pipe.open(true);
+ }
+ }
+ }
+ else {
+ if( m_isoImager->hasBeenCanceled() )
+ emit canceled();
+ else
+ emit infoMessage( i18n("Error while creating ISO image"), ERROR );
+
+ cancelAll();
+ jobFinished( false );
+ }
+ }
+ else if( !success ) { // on-the-fly
+ //
+ // In case the imager failed let's make sure the writer does not emit an unusable
+ // error message.
+ //
+ if( m_writerJob && m_writerJob->active() )
+ m_writerJob->setSourceUnreadable( true );
+
+ // there is one special case which we need to handle here: the iso imager might be canceled
+ // FIXME: the iso imager should not be able to cancel itself
+ if( m_isoImager->hasBeenCanceled() && !this->hasBeenCanceled() )
+ cancel();
+ }
+ }
+}
+
+
+bool K3bDataJob::startWriterJob()
+{
+ if( d->doc->dummy() )
+ emit newTask( i18n("Simulating") );
+ else if( d->copies > 1 )
+ emit newTask( i18n("Writing Copy %1").arg(d->copiesDone+1) );
+ else
+ emit newTask( i18n("Writing") );
+
+ // if we append a new session we asked for an appendable cd already
+ if( d->usedMultiSessionMode == K3bDataDoc::NONE ||
+ d->usedMultiSessionMode == K3bDataDoc::START ) {
+
+ if( !waitForMedium() ) {
+ return false;
+ }
+ }
+
+ emit burning(true);
+ m_writerJob->start();
+ return true;
+}
+
+
+void K3bDataJob::slotWriterJobPercent( int p )
+{
+ double totalTasks = d->copies;
+ double tasksDone = d->copiesDone;
+ if( d->doc->verifyData() ) {
+ totalTasks*=2;
+ tasksDone*=2;
+ }
+ if( !d->doc->onTheFly() ) {
+ totalTasks+=1.0;
+ tasksDone+=1.0;
+ }
+
+ emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
+}
+
+
+void K3bDataJob::slotWriterNextTrack( int t, int tt )
+{
+ emit newSubTask( i18n("Writing Track %1 of %2").arg(t).arg(tt) );
+}
+
+
+void K3bDataJob::slotWriterJobFinished( bool success )
+{
+ d->pipe.close();
+
+ //
+ // This is a little workaround for the bad cancellation handling in this job
+ // see cancel()
+ //
+ if( d->canceled ) {
+ if( active() )
+ jobFinished( false );
+ }
+
+ if( success ) {
+ // allright
+ // the writerJob should have emited the "simulation/writing successful" signal
+
+ if( d->doc->verifyData() ) {
+ if( !d->verificationJob ) {
+ d->verificationJob = new K3bVerificationJob( this, this );
+ connect( d->verificationJob, SIGNAL(infoMessage(const QString&, int)),
+ this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( d->verificationJob, SIGNAL(newTask(const QString&)),
+ this, SIGNAL(newSubTask(const QString&)) );
+ connect( d->verificationJob, SIGNAL(newSubTask(const QString&)),
+ this, SIGNAL(newSubTask(const QString&)) );
+ connect( d->verificationJob, SIGNAL(percent(int)),
+ this, SLOT(slotVerificationProgress(int)) );
+ connect( d->verificationJob, SIGNAL(percent(int)),
+ this, SIGNAL(subPercent(int)) );
+ connect( d->verificationJob, SIGNAL(finished(bool)),
+ this, SLOT(slotVerificationFinished(bool)) );
+ connect( d->verificationJob, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+
+ }
+ d->verificationJob->clear();
+ d->verificationJob->setDevice( d->doc->burner() );
+ d->verificationJob->setGrownSessionSize( m_isoImager->size() );
+ d->verificationJob->addTrack( 0, m_isoImager->checksum(), m_isoImager->size() );
+
+ emit burning(false);
+
+ emit newTask( i18n("Verifying written data") );
+
+ d->verificationJob->start();
+ }
+ else {
+ d->copiesDone++;
+
+ if( d->copiesDone < d->copies ) {
+ K3bDevice::eject( d->doc->burner() );
+
+ bool failed = false;
+ if( d->doc->onTheFly() )
+ failed = !startOnTheFlyWriting();
+ else
+ failed = !startWriterJob();
+
+ if( failed ) {
+ cancel();
+ }
+ else if( !d->doc->onTheFly() ) {
+ d->pipe.writeToFd( m_writerJob->fd(), true );
+ d->pipe.open(true);
+ }
+ }
+ else {
+ cleanup();
+ jobFinished(true);
+ }
+ }
+ }
+ else {
+ cancelAll();
+ jobFinished( false );
+ }
+}
+
+
+void K3bDataJob::slotVerificationProgress( int p )
+{
+ double totalTasks = d->copies*2;
+ double tasksDone = d->copiesDone*2 + 1; // the writing of the current copy has already been finished
+
+ if( !d->doc->onTheFly() ) {
+ totalTasks+=1.0;
+ tasksDone+=1.0;
+ }
+
+ emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
+}
+
+
+void K3bDataJob::slotVerificationFinished( bool success )
+{
+ d->copiesDone++;
+
+ // reconnect our imager which we deconnected for the verification
+ connectImager();
+
+ if( k3bcore->globalSettings()->ejectMedia() || d->copiesDone < d->copies )
+ K3bDevice::eject( d->doc->burner() );
+
+ if( !d->canceled && d->copiesDone < d->copies ) {
+ bool failed = false;
+ if( d->doc->onTheFly() )
+ failed = !startOnTheFlyWriting();
+ else
+ failed = !startWriterJob();
+
+ if( failed )
+ cancel();
+ else if( !d->doc->onTheFly() ) {
+ d->pipe.writeToFd( m_writerJob->fd(), true );
+ d->pipe.open(true);
+ }
+ }
+ else {
+ cleanup();
+ jobFinished( success );
+ }
+}
+
+
+void K3bDataJob::setWriterJob( K3bAbstractWriter* writer )
+{
+ // FIXME: progressedsize for multiple copies
+ m_writerJob = writer;
+ connect( m_writerJob, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( m_writerJob, SIGNAL(percent(int)), this, SLOT(slotWriterJobPercent(int)) );
+ connect( m_writerJob, SIGNAL(processedSize(int, int)), this, SIGNAL(processedSize(int, int)) );
+ connect( m_writerJob, SIGNAL(subPercent(int)), this, SIGNAL(subPercent(int)) );
+ connect( m_writerJob, SIGNAL(processedSubSize(int, int)), this, SIGNAL(processedSubSize(int, int)) );
+ connect( m_writerJob, SIGNAL(nextTrack(int, int)), this, SLOT(slotWriterNextTrack(int, int)) );
+ connect( m_writerJob, SIGNAL(buffer(int)), this, SIGNAL(bufferStatus(int)) );
+ connect( m_writerJob, SIGNAL(deviceBuffer(int)), this, SIGNAL(deviceBuffer(int)) );
+ connect( m_writerJob, SIGNAL(writeSpeed(int, int)), this, SIGNAL(writeSpeed(int, int)) );
+ connect( m_writerJob, SIGNAL(finished(bool)), this, SLOT(slotWriterJobFinished(bool)) );
+ connect( m_writerJob, SIGNAL(newSubTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) );
+ connect( m_writerJob, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+}
+
+
+void K3bDataJob::setImager( K3bIsoImager* imager )
+{
+ if( m_isoImager != imager ) {
+ delete m_isoImager;
+
+ m_isoImager = imager;
+
+ connectImager();
+ }
+}
+
+
+void K3bDataJob::connectImager()
+{
+ m_isoImager->disconnect( this );
+ connect( m_isoImager, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( m_isoImager, SIGNAL(percent(int)), this, SLOT(slotIsoImagerPercent(int)) );
+ connect( m_isoImager, SIGNAL(finished(bool)), this, SLOT(slotIsoImagerFinished(bool)) );
+ connect( m_isoImager, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+}
+
+
+void K3bDataJob::prepareImager()
+{
+ if( !m_isoImager )
+ setImager( new K3bIsoImager( d->doc, this, this ) );
+}
+
+
+bool K3bDataJob::prepareWriterJob()
+{
+ if( m_writerJob )
+ return true;
+
+ // It seems as if cdrecord is not able to append sessions in dao mode whereas cdrdao is
+ if( d->usedWritingApp == K3b::CDRECORD ) {
+ K3bCdrecordWriter* writer = new K3bCdrecordWriter( d->doc->burner(), this, this );
+
+ // cdrecord manpage says that "not all" writers are able to write
+ // multisession disks in dao mode. That means there are writers that can.
+
+ // Does it really make sence to write DAta ms cds in DAO mode since writing the
+ // first session of a cd-extra in DAO mode is no problem with my writer while
+ // writing the second data session is only possible in TAO mode.
+ if( d->usedWritingMode == K3b::DAO &&
+ d->usedMultiSessionMode != K3bDataDoc::NONE )
+ emit infoMessage( i18n("Most writers do not support writing "
+ "multisession CDs in DAO mode."), INFO );
+
+ writer->setWritingMode( d->usedWritingMode );
+ writer->setSimulate( d->doc->dummy() );
+ writer->setBurnSpeed( d->doc->speed() );
+
+ // multisession
+ if( d->usedMultiSessionMode == K3bDataDoc::START ||
+ d->usedMultiSessionMode == K3bDataDoc::CONTINUE ) {
+ writer->addArgument("-multi");
+ }
+
+ if( d->doc->onTheFly() &&
+ ( d->usedMultiSessionMode == K3bDataDoc::CONTINUE ||
+ d->usedMultiSessionMode == K3bDataDoc::FINISH ) )
+ writer->addArgument("-waiti");
+
+ if( d->usedDataMode == K3b::MODE1 )
+ writer->addArgument( "-data" );
+ else {
+ if( k3bcore->externalBinManager()->binObject("cdrecord") &&
+ k3bcore->externalBinManager()->binObject("cdrecord")->hasFeature( "xamix" ) )
+ writer->addArgument( "-xa" );
+ else
+ writer->addArgument( "-xa1" );
+ }
+
+ writer->addArgument( QString("-tsize=%1s").arg(m_isoImager->size()) )->addArgument("-");
+
+ setWriterJob( writer );
+ }
+ else {
+ // create cdrdao job
+ K3bCdrdaoWriter* writer = new K3bCdrdaoWriter( d->doc->burner(), this, this );
+ writer->setCommand( K3bCdrdaoWriter::WRITE );
+ writer->setSimulate( d->doc->dummy() );
+ writer->setBurnSpeed( d->doc->speed() );
+ // multisession
+ writer->setMulti( d->usedMultiSessionMode == K3bDataDoc::START ||
+ d->usedMultiSessionMode == K3bDataDoc::CONTINUE );
+
+ // now write the tocfile
+ if( d->tocFile ) delete d->tocFile;
+ d->tocFile = new KTempFile( QString::null, "toc" );
+ d->tocFile->setAutoDelete(true);
+
+ if( QTextStream* s = d->tocFile->textStream() ) {
+ if( d->usedDataMode == K3b::MODE1 ) {
+ *s << "CD_ROM" << "\n";
+ *s << "\n";
+ *s << "TRACK MODE1" << "\n";
+ }
+ else {
+ *s << "CD_ROM_XA" << "\n";
+ *s << "\n";
+ *s << "TRACK MODE2_FORM1" << "\n";
+ }
+
+ *s << "DATAFILE \"-\" " << m_isoImager->size()*2048 << "\n";
+
+ d->tocFile->close();
+ }
+ else {
+ kdDebug() << "(K3bDataJob) could not write tocfile." << endl;
+ emit infoMessage( i18n("IO Error"), ERROR );
+ cancelAll();
+ return false;
+ }
+
+ writer->setTocFile( d->tocFile->name() );
+
+ setWriterJob( writer );
+ }
+
+ return true;
+}
+
+
+void K3bDataJob::prepareData()
+{
+ // we don't need this when only creating image and it is possible
+ // that the burn device is null
+ if( d->doc->onlyCreateImages() )
+ return;
+
+ // first of all we determine the data mode
+ if( d->doc->dataMode() == K3b::DATA_MODE_AUTO ) {
+ if( !d->doc->onlyCreateImages() &&
+ ( d->usedMultiSessionMode == K3bDataDoc::CONTINUE ||
+ d->usedMultiSessionMode == K3bDataDoc::FINISH ) ) {
+
+ // try to get the last track's datamode
+ // we already asked for an appendable cdr when fetching
+ // the ms info
+ kdDebug() << "(K3bDataJob) determining last track's datamode..." << endl;
+
+ // FIXME: use a devicethread
+ K3bDevice::Toc toc = d->doc->burner()->readToc();
+ if( toc.isEmpty() ) {
+ kdDebug() << "(K3bDataJob) could not retrieve toc." << endl;
+ emit infoMessage( i18n("Unable to determine the last track's datamode. Using default."), ERROR );
+ d->usedDataMode = K3b::MODE2;
+ }
+ else {
+ if( toc[toc.count()-1].mode() == K3bDevice::Track::MODE1 )
+ d->usedDataMode = K3b::MODE1;
+ else
+ d->usedDataMode = K3b::MODE2;
+
+ kdDebug() << "(K3bDataJob) using datamode: "
+ << (d->usedDataMode == K3b::MODE1 ? "mode1" : "mode2")
+ << endl;
+ }
+ }
+ else if( d->usedMultiSessionMode == K3bDataDoc::NONE )
+ d->usedDataMode = K3b::MODE1;
+ else
+ d->usedDataMode = K3b::MODE2;
+ }
+ else
+ d->usedDataMode = d->doc->dataMode();
+
+
+ // determine the writing mode
+ if( d->doc->writingMode() == K3b::WRITING_MODE_AUTO ) {
+ // TODO: put this into the cdreocrdwriter and decide based on the size of the
+ // track
+ if( writer()->dao() && d->usedDataMode == K3b::MODE1 &&
+ d->usedMultiSessionMode == K3bDataDoc::NONE )
+ d->usedWritingMode = K3b::DAO;
+ else
+ d->usedWritingMode = K3b::TAO;
+ }
+ else
+ d->usedWritingMode = d->doc->writingMode();
+
+
+ // cdrecord seems to have problems writing xa 1 disks in dao mode? At least on my system!
+ if( writingApp() == K3b::DEFAULT ) {
+ if( d->usedWritingMode == K3b::DAO ) {
+ if( d->usedMultiSessionMode != K3bDataDoc::NONE )
+ d->usedWritingApp = K3b::CDRDAO;
+ else if( d->usedDataMode == K3b::MODE2 )
+ d->usedWritingApp = K3b::CDRDAO;
+ else
+ d->usedWritingApp = K3b::CDRECORD;
+ }
+ else
+ d->usedWritingApp = K3b::CDRECORD;
+ }
+ else
+ d->usedWritingApp = writingApp();
+}
+
+
+void K3bDataJob::determineMultiSessionMode()
+{
+ //
+ // THIS IS ONLY CALLED IF d->doc->multiSessionMode() == K3bDataDoc::AUTO!
+ //
+
+ if( d->doc->writingMode() == K3b::WRITING_MODE_AUTO ||
+ d->doc->writingMode() == K3b::TAO ) {
+ emit newSubTask( i18n("Searching for old session") );
+
+ //
+ // Wait for the medium.
+ // In case an old session was imported we always want to continue or finish a multisession CD/DVD.
+ // Otherwise we wait for everything we could handle and decide what to do in
+ // determineMultiSessionMode( K3bDevice::DeviceHandler* ) below.
+ //
+
+ int wantedMediaState = K3bDevice::STATE_INCOMPLETE|K3bDevice::STATE_EMPTY;
+ if( d->doc->sessionImported() )
+ wantedMediaState = K3bDevice::STATE_INCOMPLETE;
+
+ int m = waitForMedia( d->doc->burner(),
+ wantedMediaState,
+ K3bDevice::MEDIA_WRITABLE_CD );
+
+ if( m < 0 )
+ cancel();
+ else {
+ // now we need to determine the media's size
+ connect( K3bDevice::sendCommand( K3bDevice::DeviceHandler::NG_DISKINFO, d->doc->burner() ),
+ SIGNAL(finished(K3bDevice::DeviceHandler*)),
+ this,
+ SLOT(slotDetermineMultiSessionMode(K3bDevice::DeviceHandler*)) );
+ }
+ }
+ else {
+ // we need TAO for multisession
+ d->usedMultiSessionMode = K3bDataDoc::NONE;
+
+ // carry on with the writing
+ prepareWriting();
+ }
+}
+
+
+void K3bDataJob::slotDetermineMultiSessionMode( K3bDevice::DeviceHandler* dh )
+{
+ //
+ // This is a little workaround for the bad cancellation handling in this job
+ // see cancel()
+ //
+ if( d->canceled ) {
+ if( active() ) {
+ cleanup();
+ jobFinished( false );
+ }
+ }
+ else {
+ d->usedMultiSessionMode = getMultiSessionMode( dh->diskInfo() );
+
+ // carry on with the writing
+ prepareWriting();
+ }
+}
+
+
+K3bDataDoc::MultiSessionMode K3bDataJob::getMultiSessionMode( const K3bDevice::DiskInfo& info )
+{
+ if( info.appendable() ) {
+ //
+ // 3 cases:
+ // 1. the project does not fit -> no multisession (resulting in asking for another media above)
+ // 2. the project does fit and fills up the CD -> finish multisession
+ // 3. the project does fit and does not fill up the CD -> continue multisession
+ //
+ // In case a session has been imported we do not consider NONE at all.
+ //
+ if( d->doc->size() > info.remainingSize().mode1Bytes() && !d->doc->sessionImported() )
+ d->usedMultiSessionMode = K3bDataDoc::NONE;
+ else if( d->doc->size() >= info.remainingSize().mode1Bytes()*9/10 )
+ d->usedMultiSessionMode = K3bDataDoc::FINISH;
+ else
+ d->usedMultiSessionMode = K3bDataDoc::CONTINUE;
+ }
+
+ else if( info.empty() ) {
+ //
+ // We only close the CD if the project fills up the CD almost completely (90%)
+ //
+ if( d->doc->size() >= info.capacity().mode1Bytes()*9/10 ||
+ d->doc->writingMode() == K3b::DAO )
+ d->usedMultiSessionMode = K3bDataDoc::NONE;
+ else
+ d->usedMultiSessionMode = K3bDataDoc::START;
+ }
+
+ else { // complete (WE SHOULD ACTUALLY NEVER GET HERE SINCE WE WAIT FOR AN EMPTY/APPENDABLE CD ABOVE!)
+ //
+ // Now we decide only based on the project size.
+ // let's just use a 680 MB CD as our reference
+ //
+ if( d->doc->size()/1024/1024 >= 680*9/10 ||
+ d->doc->writingMode() == K3b::DAO )
+ d->usedMultiSessionMode = K3bDataDoc::NONE;
+ else
+ d->usedMultiSessionMode = K3bDataDoc::START;
+ }
+
+ return d->usedMultiSessionMode;
+}
+
+
+void K3bDataJob::cancelAll()
+{
+ d->canceled = true;
+
+ m_isoImager->cancel();
+ m_msInfoFetcher->cancel();
+ if( m_writerJob )
+ m_writerJob->cancel();
+ if( d->verificationJob )
+ d->verificationJob->cancel();
+
+ d->pipe.close();
+
+ cleanup();
+}
+
+
+bool K3bDataJob::waitForMedium()
+{
+ emit newSubTask( i18n("Waiting for a medium") );
+ if( waitForMedia( d->doc->burner(),
+ d->usedMultiSessionMode == K3bDataDoc::CONTINUE ||
+ d->usedMultiSessionMode == K3bDataDoc::FINISH ?
+ K3bDevice::STATE_INCOMPLETE :
+ K3bDevice::STATE_EMPTY,
+ K3bDevice::MEDIA_WRITABLE_CD ) < 0 ) {
+ return false;
+ }
+ else
+ return !d->canceled;
+}
+
+
+QString K3bDataJob::jobDescription() const
+{
+ if( d->doc->onlyCreateImages() ) {
+ return i18n("Creating Data Image File");
+ }
+ else if( d->doc->multiSessionMode() == K3bDataDoc::NONE ||
+ d->doc->multiSessionMode() == K3bDataDoc::AUTO ) {
+ return i18n("Writing Data CD")
+ + ( d->doc->isoOptions().volumeID().isEmpty()
+ ? QString::null
+ : QString( " (%1)" ).arg(d->doc->isoOptions().volumeID()) );
+ }
+ else {
+ return i18n("Writing Multisession CD")
+ + ( d->doc->isoOptions().volumeID().isEmpty()
+ ? QString::null
+ : QString( " (%1)" ).arg(d->doc->isoOptions().volumeID()) );
+ }
+}
+
+
+QString K3bDataJob::jobDetails() const
+{
+ if( d->doc->copies() > 1 &&
+ !d->doc->dummy() &&
+ !(d->doc->multiSessionMode() == K3bDataDoc::CONTINUE ||
+ d->doc->multiSessionMode() == K3bDataDoc::FINISH) )
+ return i18n("ISO9660 Filesystem (Size: %1) - %n copy",
+ "ISO9660 Filesystem (Size: %1) - %n copies",
+ d->doc->copies() )
+ .arg(KIO::convertSize( d->doc->size() ));
+ else
+ return i18n("ISO9660 Filesystem (Size: %1)")
+ .arg(KIO::convertSize( d->doc->size() ));
+}
+
+
+K3bDataDoc::MultiSessionMode K3bDataJob::usedMultiSessionMode() const
+{
+ return d->usedMultiSessionMode;
+}
+
+
+void K3bDataJob::cleanup()
+{
+ if( !d->doc->onTheFly() && d->doc->removeImages() ) {
+ if( QFile::exists( d->doc->tempDir() ) ) {
+ d->imageFile.remove();
+ emit infoMessage( i18n("Removed image file %1").arg(d->doc->tempDir()), K3bJob::SUCCESS );
+ }
+ }
+
+ if( d->tocFile ) {
+ delete d->tocFile;
+ d->tocFile = 0;
+ }
+}
+
+
+bool K3bDataJob::hasBeenCanceled() const
+{
+ return d->canceled;
+}
+
+#include "k3bdatajob.moc"
diff --git a/libk3b/projects/datacd/k3bdatajob.h b/libk3b/projects/datacd/k3bdatajob.h
new file mode 100644
index 0000000..58de969
--- /dev/null
+++ b/libk3b/projects/datacd/k3bdatajob.h
@@ -0,0 +1,111 @@
+/*
+ *
+ * $Id: k3bdatajob.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BDATAJOB_H
+#define K3BDATAJOB_H
+
+#include <k3bjob.h>
+#include <k3bdatadoc.h>
+
+#include <qfile.h>
+
+class QString;
+class QDataStream;
+class K3bAbstractWriter;
+class K3bIsoImager;
+class KTempFile;
+class K3bMsInfoFetcher;
+
+namespace K3bDevice {
+ class DeviceHandler;
+ class DiskInfo;
+}
+
+/**
+ *@author Sebastian Trueg
+ */
+class K3bDataJob : public K3bBurnJob
+{
+ Q_OBJECT
+
+ public:
+ K3bDataJob( K3bDataDoc*, K3bJobHandler*, QObject* parent = 0 );
+ virtual ~K3bDataJob();
+
+ K3bDoc* doc() const;
+ K3bDevice::Device* writer() const;
+
+ virtual bool hasBeenCanceled() const;
+
+ virtual QString jobDescription() const;
+ virtual QString jobDetails() const;
+
+ public slots:
+ void cancel();
+ void start();
+
+ /**
+ * Used to specify a non-default writer.
+ * If this does notget called K3bDataJob determines
+ * the writer itself.
+ */
+ void setWriterJob( K3bAbstractWriter* );
+ void setImager( K3bIsoImager* );
+
+ protected slots:
+ void slotIsoImagerFinished( bool success );
+ void slotIsoImagerPercent(int);
+ void slotWriterJobPercent( int p );
+ void slotWriterNextTrack( int t, int tt );
+ void slotWriterJobFinished( bool success );
+ void slotVerificationProgress( int );
+ void slotVerificationFinished( bool );
+ void slotMsInfoFetched(bool);
+ void slotDetermineMultiSessionMode( K3bDevice::DeviceHandler* dh );
+ void writeImage();
+ void cancelAll();
+
+ /**
+ * Just a little helper method that makes subclassing easier.
+ * Basically used for DVD writing.
+ */
+ virtual bool waitForMedium();
+
+ protected:
+ virtual void prepareData();
+ virtual bool prepareWriterJob();
+ virtual void prepareImager();
+ virtual void determineMultiSessionMode();
+ virtual K3bDataDoc::MultiSessionMode getMultiSessionMode( const K3bDevice::DiskInfo& );
+ virtual void cleanup();
+
+ K3bDataDoc::MultiSessionMode usedMultiSessionMode() const;
+
+ K3bAbstractWriter* m_writerJob;
+ K3bIsoImager* m_isoImager;
+ K3bMsInfoFetcher* m_msInfoFetcher;
+
+ private:
+ bool startWriterJob();
+ bool startOnTheFlyWriting();
+ void prepareWriting();
+ void connectImager();
+
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/projects/datacd/k3bdatapreparationjob.cpp b/libk3b/projects/datacd/k3bdatapreparationjob.cpp
new file mode 100644
index 0000000..dd29d6d
--- /dev/null
+++ b/libk3b/projects/datacd/k3bdatapreparationjob.cpp
@@ -0,0 +1,283 @@
+/*
+ *
+ * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $
+ * Copyright (C) 2006 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bdatapreparationjob.h"
+#include "k3bdatadoc.h"
+#include "k3bisooptions.h"
+
+#include <k3bthreadjob.h>
+#include <k3bthread.h>
+#include <k3bdiritem.h>
+#include <k3bfileitem.h>
+#include <k3bglobals.h>
+
+#include <klocale.h>
+#include <kstringhandler.h>
+
+#include <qfile.h>
+#include <qvaluelist.h>
+
+
+class K3bDataPreparationJob::Private : public K3bThread
+{
+public:
+ Private( K3bDataDoc* doc );
+
+ void run();
+ void cancel();
+
+ K3bDataDoc* doc;
+
+ QValueList<K3bDataItem*> nonExistingItems;
+ QString listOfRenamedItems;
+ QValueList<K3bDataItem*> folderSymLinkItems;
+
+ K3bThreadJob* threadJob;
+
+ bool canceled;
+};
+
+
+K3bDataPreparationJob::Private::Private( K3bDataDoc* _doc )
+ : doc(_doc)
+{
+}
+
+
+void K3bDataPreparationJob::Private::run()
+{
+ emitStarted();
+
+ // clean up
+ nonExistingItems.clear();
+ listOfRenamedItems.truncate(0);
+ folderSymLinkItems.clear();
+
+ // initialize filenames in the project
+ doc->prepareFilenames();
+
+ // create the message string for the renamed files
+ if( doc->needToCutFilenames() ) {
+ int maxlines = 10;
+ QValueList<K3bDataItem*>::const_iterator it;
+ for( it = doc->needToCutFilenameItems().begin();
+ maxlines > 0 && it != doc->needToCutFilenameItems().end();
+ ++it, --maxlines ) {
+ K3bDataItem* item = *it;
+ listOfRenamedItems += i18n("<em>%1</em> renamed to <em>%2</em>")
+ .arg( KStringHandler::csqueeze( item->k3bName(), 30 ) )
+ .arg( KStringHandler::csqueeze( item->writtenName(), 30 ) );
+ listOfRenamedItems += "<br>";
+ }
+ if( it != doc->needToCutFilenameItems().end() )
+ listOfRenamedItems += "...";
+ }
+
+ //
+ // Check for missing files and folder symlinks
+ //
+ K3bDataItem* item = doc->root();
+ while( (item = item->nextSibling()) ) {
+
+ if( item->isSymLink() ) {
+ if( doc->isoOptions().followSymbolicLinks() ) {
+ QFileInfo f( K3b::resolveLink( item->localPath() ) );
+ if( !f.exists() ) {
+ nonExistingItems.append( item );
+ }
+ else if( f.isDir() ) {
+ folderSymLinkItems.append( item );
+ }
+ }
+ }
+ else if( item->isFile() && !QFile::exists( item->localPath() ) ) {
+ nonExistingItems.append( item );
+ }
+
+ if( canceled ) {
+ emitCanceled();
+ emitFinished(false);
+ return;
+ }
+ }
+
+
+ emitFinished( true );
+}
+
+
+void K3bDataPreparationJob::Private::cancel()
+{
+ canceled = true;
+}
+
+
+
+
+static QString createItemsString( const QValueList<K3bDataItem*>& items, unsigned int max )
+{
+ QString s;
+ unsigned int cnt = 0;
+ for( QValueList<K3bDataItem*>::const_iterator it = items.begin();
+ it != items.end(); ++it ) {
+
+ s += KStringHandler::csqueeze( (*it)->localPath(), 60 );
+
+ ++cnt;
+ if( cnt >= max || it == items.end() )
+ break;
+
+ s += "<br>";
+ }
+
+ if( items.count() > max )
+ s += "...";
+
+ return s;
+}
+
+
+K3bDataPreparationJob::K3bDataPreparationJob( K3bDataDoc* doc, K3bJobHandler* hdl, QObject* parent )
+ : K3bJob( hdl, parent )
+{
+ d = new Private( doc );
+ d->threadJob = new K3bThreadJob( d, this, this );
+ connectSubJob( d->threadJob, SLOT(slotWorkDone(bool)), K3bJob::DEFAULT_SIGNAL_CONNECTION );
+}
+
+
+K3bDataPreparationJob::~K3bDataPreparationJob()
+{
+ delete d;
+}
+
+
+void K3bDataPreparationJob::start()
+{
+ if( !active() ) {
+ d->canceled = false;
+ jobStarted();
+ d->threadJob->start();
+ }
+}
+
+
+void K3bDataPreparationJob::slotWorkDone( bool success )
+{
+ if( success ) {
+ if( !d->listOfRenamedItems.isEmpty() ) {
+ if( !questionYesNo( "<p>" + i18n("Some filenames need to be shortened due to the %1 char restriction "
+ "of the Joliet extensions. If the Joliet extensions are disabled filenames "
+ "do not have to be shortened but long filenames will not be available on "
+ "Windows systems.")
+ .arg( d->doc->isoOptions().jolietLong() ? 103 : 64 )
+ + "<p>" + d->listOfRenamedItems,
+ i18n("Warning"),
+ i18n("Shorten Filenames"),
+ i18n("Disable Joliet extensions") ) ) {
+ // No -> disable joliet
+ // for now we enable RockRidge to be sure we did not lie above (keep long filenames)
+ K3bIsoOptions op = d->doc->isoOptions();
+ op.setCreateJoliet( false );
+ op.setCreateRockRidge( true );
+ d->doc->setIsoOptions( op );
+ d->doc->prepareFilenames();
+ }
+ }
+
+ //
+ // The joliet extension encodes the volume desc in UCS-2, i.e. uses 16 bit for each char.
+ // Thus, the max length here is 16.
+ //
+ if( d->doc->isoOptions().createJoliet() &&
+ d->doc->isoOptions().volumeID().length() > 16 ) {
+ if( !questionYesNo( "<p>" + i18n("The Joliet extensions (which are needed for long filenames on Windows systems) "
+ "restrict the length of the volume descriptior (the name of the filesystem) "
+ "to %1 characters. The selected descriptor '%2' is longer than that. Do you "
+ "want it to be cut or do you want to go back and change it manually?")
+ .arg( 16 ).arg( d->doc->isoOptions().volumeID() ),
+ i18n("Warning"),
+ i18n("Cut volume descriptor in the Joliet tree"),
+ i18n("Cancel and go back") ) ) {
+ d->canceled = true;
+ emit canceled();
+ jobFinished( false );
+ return;
+ }
+ }
+
+ //
+ // Check for missing files
+ //
+ if( !d->nonExistingItems.isEmpty() ) {
+ if( questionYesNo( "<p>" + i18n("The following files could not be found. Do you want to remove them from the "
+ "project and continue without adding them to the image?") +
+ "<p>" + createItemsString( d->nonExistingItems, 10 ),
+ i18n("Warning"),
+ i18n("Remove missing files and continue"),
+ i18n("Cancel and go back") ) ) {
+ for( QValueList<K3bDataItem*>::const_iterator it = d->nonExistingItems.begin();
+ it != d->nonExistingItems.end(); ++it ) {
+ delete *it;
+ }
+ }
+ else {
+ d->canceled = true;
+ emit canceled();
+ jobFinished(false);
+ return;
+ }
+ }
+
+ //
+ // Warn about symlinks to folders
+ //
+ if( d->doc->isoOptions().followSymbolicLinks() && !d->folderSymLinkItems.isEmpty() ) {
+ if( !questionYesNo( "<p>" + i18n("K3b is not able to follow symbolic links to folders after they have been added "
+ "to the project. Do you want to continue "
+ "without writing the symbolic links to the image?") +
+ "<p>" + createItemsString( d->folderSymLinkItems, 10 ),
+ i18n("Warning"),
+ i18n("Discard symbolic links to folders"),
+ i18n("Cancel and go back") ) ) {
+ d->canceled = true;
+ emit canceled();
+ jobFinished(false);
+ return;
+ }
+ }
+
+ jobFinished( true );
+ }
+ else {
+ if( d->canceled )
+ emit canceled();
+ jobFinished(false);
+ }
+}
+
+
+void K3bDataPreparationJob::cancel()
+{
+ d->cancel();
+}
+
+
+bool K3bDataPreparationJob::hasBeenCanceled() const
+{
+ return d->canceled;
+}
+
+#include "k3bdatapreparationjob.moc"
diff --git a/libk3b/projects/datacd/k3bdatapreparationjob.h b/libk3b/projects/datacd/k3bdatapreparationjob.h
new file mode 100644
index 0000000..1c83a42
--- /dev/null
+++ b/libk3b/projects/datacd/k3bdatapreparationjob.h
@@ -0,0 +1,51 @@
+/*
+ *
+ * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $
+ * Copyright (C) 2006 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_DATA_PREPARATION_JOB_H_
+#define _K3B_DATA_PREPARATION_JOB_H_
+
+#include <k3bjob.h>
+
+
+class K3bDataDoc;
+class K3bJobHandler;
+
+/**
+ * The K3bDataPreparationJob performs some checks on the data in a data project
+ * It is used by th K3bIsoImager.
+ */
+class K3bDataPreparationJob : public K3bJob
+{
+ Q_OBJECT
+
+ public:
+ K3bDataPreparationJob( K3bDataDoc* doc, K3bJobHandler* hdl, QObject* parent );
+ ~K3bDataPreparationJob();
+
+ bool hasBeenCanceled() const;
+
+ public slots:
+ void start();
+ void cancel();
+
+ private slots:
+ void slotWorkDone( bool success );
+
+ private:
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/projects/datacd/k3bdiritem.cpp b/libk3b/projects/datacd/k3bdiritem.cpp
new file mode 100644
index 0000000..3ea3236
--- /dev/null
+++ b/libk3b/projects/datacd/k3bdiritem.cpp
@@ -0,0 +1,406 @@
+/*
+ *
+ * $Id: k3bdiritem.cpp 652578 2007-04-11 14:21:04Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bdiritem.h"
+#include "k3bdatadoc.h"
+#include "k3bsessionimportitem.h"
+#include "k3bfileitem.h"
+
+#include <qstring.h>
+#include <qptrlist.h>
+
+#include <kdebug.h>
+
+
+K3bDirItem::K3bDirItem(const QString& name, K3bDataDoc* doc, K3bDirItem* parentDir)
+ : K3bDataItem( doc, parentDir ),
+ m_size(0),
+ m_followSymlinksSize(0),
+ m_blocks(0),
+ m_followSymlinksBlocks(0),
+ m_files(0),
+ m_dirs(0)
+{
+ m_k3bName = name;
+
+ // add automagically like a qlistviewitem
+ if( parent() )
+ parent()->addDataItem( this );
+}
+
+
+K3bDirItem::K3bDirItem( const K3bDirItem& item )
+ : K3bDataItem( item ),
+ m_size(0),
+ m_followSymlinksSize(0),
+ m_blocks(0),
+ m_followSymlinksBlocks(0),
+ m_files(0),
+ m_dirs(0),
+ m_localPath( item.m_localPath )
+{
+ for( QPtrListIterator<K3bDataItem> it( item.children() ); *it; ++it )
+ addDataItem( (*it)->copy() );
+}
+
+K3bDirItem::~K3bDirItem()
+{
+ // delete all children
+ // doing this by hand is much saver than using the
+ // auto-delete feature since some of the items' destructors
+ // may change the list
+ K3bDataItem* i = m_children.first();
+ while( i ) {
+ // it is important to use takeDataItem here to be sure
+ // the size gets updated properly
+ takeDataItem(i);
+ delete i;
+ i = m_children.first();
+ }
+
+ // this has to be done after deleting the children
+ // because the directory itself has a size of 0 in K3b
+ // and all it's files' sizes have already been substracted
+ take();
+}
+
+
+K3bDataItem* K3bDirItem::copy() const
+{
+ return new K3bDirItem( *this );
+}
+
+
+K3bDirItem* K3bDirItem::getDirItem() const
+{
+ return const_cast<K3bDirItem*>(this);
+}
+
+K3bDirItem* K3bDirItem::addDataItem( K3bDataItem* item )
+{
+ // check if we are a subdir of item
+ if( K3bDirItem* dirItem = dynamic_cast<K3bDirItem*>(item) ) {
+ if( dirItem->isSubItem( this ) ) {
+ kdDebug() << "(K3bDirItem) trying to move a dir item down in it's own tree." << endl;
+ return this;
+ }
+ }
+
+ if( m_children.findRef( item ) == -1 ) {
+ if( item->isFile() ) {
+ // do we replace an old item?
+ QString name = item->k3bName();
+ int cnt = 1;
+ while( K3bDataItem* oldItem = find( name ) ) {
+ if( !oldItem->isDir() && oldItem->isFromOldSession() ) {
+ // in this case we remove this item from it's parent and save it in the new one
+ // to be able to recover it
+ oldItem->take();
+ static_cast<K3bSessionImportItem*>(oldItem)->setReplaceItem( static_cast<K3bFileItem*>(item) );
+ static_cast<K3bFileItem*>(item)->setReplacedItemFromOldSession( oldItem );
+ break;
+ }
+ else {
+ //
+ // add a counter to the filename
+ //
+ if( item->k3bName()[item->k3bName().length()-4] == '.' )
+ name = item->k3bName().left( item->k3bName().length()-4 ) + QString::number(cnt++) + item->k3bName().right(4);
+ else
+ name = item->k3bName() + QString::number(cnt++);
+ }
+ }
+ item->setK3bName( name );
+ }
+
+ m_children.append( item->take() );
+ updateSize( item, false );
+ if( item->isDir() )
+ updateFiles( ((K3bDirItem*)item)->numFiles(), ((K3bDirItem*)item)->numDirs()+1 );
+ else
+ updateFiles( 1, 0 );
+
+ item->m_parentDir = this;
+
+ // inform the doc
+ if( doc() )
+ doc()->itemAddedToDir( this, item );
+ }
+
+ return this;
+}
+
+
+K3bDataItem* K3bDirItem::takeDataItem( K3bDataItem* item )
+{
+ int x = m_children.findRef( item );
+ if( x > -1 ) {
+ K3bDataItem* item = m_children.take();
+ updateSize( item, true );
+ if( item->isDir() )
+ updateFiles( -1*((K3bDirItem*)item)->numFiles(), -1*((K3bDirItem*)item)->numDirs()-1 );
+ else
+ updateFiles( -1, 0 );
+
+ item->m_parentDir = 0;
+
+ // inform the doc
+ if( doc() )
+ doc()->itemRemovedFromDir( this, item );
+
+ if( item->isFile() ) {
+ // restore the item imported from an old session
+ if( static_cast<K3bFileItem*>(item)->replaceItemFromOldSession() )
+ addDataItem( static_cast<K3bFileItem*>(item)->replaceItemFromOldSession() );
+ }
+
+ return item;
+ }
+ else
+ return 0;
+}
+
+
+K3bDataItem* K3bDirItem::nextSibling() const
+{
+ if( !m_children.isEmpty() )
+ return m_children.getFirst();
+ else
+ return K3bDataItem::nextSibling();
+}
+
+
+K3bDataItem* K3bDirItem::nextChild( K3bDataItem* prev ) const
+{
+ // search for prev in children
+ if( m_children.findRef( prev ) < 0 ) {
+ return 0;
+ }
+ else
+ return m_children.next();
+}
+
+
+bool K3bDirItem::alreadyInDirectory( const QString& filename ) const
+{
+ return (find( filename ) != 0);
+}
+
+
+K3bDataItem* K3bDirItem::find( const QString& filename ) const
+{
+ for( QPtrListIterator<K3bDataItem> it( m_children ); it.current(); ++it ) {
+ if( it.current()->k3bName() == filename )
+ return it.current();
+ }
+ return 0;
+}
+
+
+K3bDataItem* K3bDirItem::findByPath( const QString& p )
+{
+ if( p.isEmpty() || p == "/" )
+ return this;
+
+ QString path = p;
+ if( path.startsWith("/") )
+ path = path.mid(1);
+ int pos = path.find( "/" );
+ if( pos < 0 )
+ return find( path );
+ else {
+ // do it recursivly
+ K3bDataItem* item = find( path.left(pos) );
+ if( item && item->isDir() )
+ return ((K3bDirItem*)item)->findByPath( path.mid( pos+1 ) );
+ else
+ return 0;
+ }
+}
+
+
+bool K3bDirItem::mkdir( const QString& dirPath )
+{
+ //
+ // An absolut path always starts at the root item
+ //
+ if( dirPath[0] == '/' ) {
+ if( parent() )
+ return parent()->mkdir( dirPath );
+ else
+ return mkdir( dirPath.mid( 1 ) );
+ }
+
+ if( findByPath( dirPath ) )
+ return false;
+
+ QString restPath;
+ QString dirName;
+ int pos = dirPath.find( '/' );
+ if( pos == -1 ) {
+ dirName = dirPath;
+ }
+ else {
+ dirName = dirPath.left( pos );
+ restPath = dirPath.mid( pos+1 );
+ }
+
+ K3bDataItem* dir = find( dirName );
+ if( !dir )
+ dir = new K3bDirItem( dirName, doc(), this );
+ else if( !dir->isDir() )
+ return false;
+
+ if( !restPath.isEmpty() )
+ return static_cast<K3bDirItem*>(dir)->mkdir( restPath );
+
+ return true;
+}
+
+
+KIO::filesize_t K3bDirItem::itemSize( bool followsylinks ) const
+{
+ if( followsylinks )
+ return m_followSymlinksSize;
+ else
+ return m_size;
+}
+
+
+K3b::Msf K3bDirItem::itemBlocks( bool followSymlinks ) const
+{
+ if( followSymlinks )
+ return m_followSymlinksBlocks;
+ else
+ return m_blocks;
+}
+
+
+bool K3bDirItem::isSubItem( K3bDataItem* item ) const
+{
+ if( dynamic_cast<K3bDirItem*>(item) == this )
+ return true;
+
+ K3bDirItem* d = item->parent();
+ while( d ) {
+ if( d == this ) {
+ return true;
+ }
+ d = d->parent();
+ }
+
+ return false;
+}
+
+
+long K3bDirItem::numFiles() const
+{
+ return m_files;
+}
+
+
+long K3bDirItem::numDirs() const
+{
+ return m_dirs;
+}
+
+
+bool K3bDirItem::isRemoveable() const
+{
+ if( !K3bDataItem::isRemoveable() )
+ return false;
+
+ for( QPtrListIterator<K3bDataItem> it( m_children ); it.current(); ++it ) {
+ if( !it.current()->isRemoveable() )
+ return false;
+ }
+
+ return true;
+}
+
+
+void K3bDirItem::updateSize( K3bDataItem* item, bool removed )
+{
+ if ( !item->isFromOldSession() ) {
+ if( removed ) {
+ m_followSymlinksSize -= item->itemSize( true );
+ m_size -= item->itemSize( false );
+ m_followSymlinksBlocks -= item->itemBlocks( true ).lba();
+ m_blocks -= item->itemBlocks( false ).lba();
+ }
+ else {
+ m_followSymlinksSize += item->itemSize( true );
+ m_size += item->itemSize( false );
+ m_followSymlinksBlocks += item->itemBlocks( true ).lba();
+ m_blocks += item->itemBlocks( false ).lba();
+ }
+ }
+
+ if( parent() )
+ parent()->updateSize( item, removed );
+}
+
+void K3bDirItem::updateFiles( long files, long dirs )
+{
+ m_files += files;
+ m_dirs += dirs;
+ if( parent() )
+ parent()->updateFiles( files, dirs );
+}
+
+
+bool K3bDirItem::isFromOldSession() const
+{
+ for( QPtrListIterator<K3bDataItem> it( m_children ); it.current(); ++it ) {
+ if( (*it)->isFromOldSession() )
+ return true;
+ }
+ return false;
+}
+
+
+bool K3bDirItem::writeToCd() const
+{
+ // check if this dir contains items to write
+ for( QPtrListIterator<K3bDataItem> it( m_children ); it.current(); ++it ) {
+ if( (*it)->writeToCd() )
+ return true;
+ }
+ return K3bDataItem::writeToCd();
+}
+
+
+K3bRootItem::K3bRootItem( K3bDataDoc* doc )
+ : K3bDirItem( "root", doc, 0 )
+{
+}
+
+
+K3bRootItem::~K3bRootItem()
+{
+}
+
+
+const QString& K3bRootItem::k3bName() const
+{
+ return doc()->isoOptions().volumeID();
+}
+
+
+void K3bRootItem::setK3bName( const QString& text )
+{
+ doc()->setVolumeID( text );
+}
diff --git a/libk3b/projects/datacd/k3bdiritem.h b/libk3b/projects/datacd/k3bdiritem.h
new file mode 100644
index 0000000..a64b4fd
--- /dev/null
+++ b/libk3b/projects/datacd/k3bdiritem.h
@@ -0,0 +1,155 @@
+/*
+ *
+ * $Id: k3bdiritem.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BDIRITEM_H
+#define K3BDIRITEM_H
+
+
+#include <qstring.h>
+#include <qptrlist.h>
+
+#include <kio/global.h>
+
+#include "k3bdataitem.h"
+#include "k3b_export.h"
+class K3bDataDoc;
+
+/**
+ *@author Sebastian Trueg
+ */
+
+class LIBK3B_EXPORT K3bDirItem : public K3bDataItem
+{
+ public:
+ K3bDirItem( const QString& name, K3bDataDoc*, K3bDirItem* parentDir = 0 );
+
+ /**
+ * Default copy constructor. Copies the dir including all children. However, none of the
+ * children will have set a doc and the copy dir will not have set a parent dir.
+ */
+ K3bDirItem( const K3bDirItem& );
+
+ virtual ~K3bDirItem();
+
+ K3bDataItem* copy() const;
+
+ K3bDirItem* getDirItem() const;
+
+ const QPtrList<K3bDataItem>& children() const { return m_children; }
+ K3bDirItem* addDataItem( K3bDataItem* item );
+ K3bDataItem* takeDataItem( K3bDataItem* item );
+
+ K3bDataItem* nextSibling() const;
+ K3bDataItem* nextChild( K3bDataItem* ) const;
+
+ bool alreadyInDirectory( const QString& fileName ) const;
+ K3bDataItem* find( const QString& filename ) const;
+ K3bDataItem* findByPath( const QString& );
+
+ long numFiles() const;
+ long numDirs() const;
+
+ bool isEmpty() const { return ( numDirs() + numFiles() == 0 ); }
+
+ /**
+ * returns true if item is a subItem of
+ * this dir item
+ * (returns also true if item == this
+ */
+ bool isSubItem( K3bDataItem* item ) const;
+
+ bool isDir() const { return true; }
+
+ virtual bool isRemoveable() const;
+
+ /**
+ * \return true if some child is from an old session.
+ */
+ virtual bool isFromOldSession() const;
+
+ /**
+ * Recursively creates a directory.
+ */
+ bool mkdir( const QString& dir );
+
+ void setLocalPath( const QString& p ) { m_localPath = p; }
+ QString localPath() const { return m_localPath; }
+
+ /**
+ * \reimplemented
+ */
+ bool writeToCd() const;
+
+ protected:
+ /**
+ * Normally one does not use this method but K3bDataItem::size()
+ *
+ * This method does not take into account the possibility to share the data
+ * between files with the same inode in an iso9660 filesystem.
+ * For that one has to use K3bFileCompilationSizeHandler.
+ */
+ KIO::filesize_t itemSize( bool followSymlinks ) const;
+
+ /*
+ * Normally one does not use this method but K3bDataItem::blocks()
+ */
+ K3b::Msf itemBlocks( bool followSymlinks ) const;
+
+ private:
+ /**
+ * this recursivly updates the size of the directories.
+ * The size of this dir and the parent dir is updated.
+ * These values are just used for user information.
+ */
+ void updateSize( K3bDataItem*, bool removed = false );
+ /**
+ * Updates the number of files and directories. These values are
+ * just used for user information.
+ */
+ void updateFiles( long files, long dirs );
+
+ mutable QPtrList<K3bDataItem> m_children;
+
+ // size of the items simply added
+ KIO::filesize_t m_size;
+ KIO::filesize_t m_followSymlinksSize;
+
+ // number of blocks (2048 bytes) used by all the items
+ long m_blocks;
+ long m_followSymlinksBlocks;
+
+ long m_files;
+ long m_dirs;
+
+ // HACK: store the original path to be able to use it's permissions
+ // ´remove this once we have a backup project
+ QString m_localPath;
+};
+
+
+class K3bRootItem : public K3bDirItem
+{
+ public:
+ K3bRootItem( K3bDataDoc* );
+ ~K3bRootItem();
+
+ const QString& k3bName() const;
+ void setK3bName( const QString& );
+
+ bool isMoveable() const { return false; }
+ bool isRemoveable() const { return false; }
+};
+#endif
diff --git a/libk3b/projects/datacd/k3bfilecompilationsizehandler.cpp b/libk3b/projects/datacd/k3bfilecompilationsizehandler.cpp
new file mode 100644
index 0000000..0ddab76
--- /dev/null
+++ b/libk3b/projects/datacd/k3bfilecompilationsizehandler.cpp
@@ -0,0 +1,228 @@
+/*
+ *
+ * $Id: k3bfilecompilationsizehandler.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bfilecompilationsizehandler.h"
+#include "k3bfileitem.h"
+
+#include <kdebug.h>
+
+#include <qfile.h>
+#include <qmap.h>
+#include <qptrlist.h>
+
+
+// TODO: remove the items from the project if the savedSize differs
+// with some info-widget: "Files xxx have changed on disk. Removing them from the project."
+// or we just update the sizes!
+
+
+static long usedBlocks( const KIO::filesize_t& bytes )
+{
+ if( bytes % 2048 )
+ return bytes/2048 + 1;
+ else
+ return bytes/2048;
+}
+
+
+class InodeInfo
+{
+public:
+ InodeInfo() {
+ number = 0;
+ savedSize = 0;
+ }
+
+ /**
+ * How often has the file with
+ * the corresponding inode been added
+ */
+ int number;
+
+ /**
+ * The size of the first added file. This has to be saved
+ * to check further addings and to avoid the following situation:
+ * A file with inode 1 is added, then deleted. Another file is created
+ * at inode 1 and added to the project. Now the first file gets
+ * removed and then the second. If we had not saved the size we would
+ * have added the size of the first and removed the size of the second
+ * file resulting in a corrupted project size.
+ * This way we always use the size of the first added file and may
+ * warn the user if sizes differ.
+ */
+ KIO::filesize_t savedSize;
+
+ KIO::filesize_t completeSize() const { return savedSize*number; }
+
+ /**
+ * In an iso9660 filesystem a file occupies complete blocks of 2048 bytes.
+ */
+ K3b::Msf blocks() const { return K3b::Msf( usedBlocks(savedSize) ); }
+
+ QPtrList<K3bDataItem> items;
+};
+
+
+class K3bFileCompilationSizeHandler::Private
+{
+public:
+ Private()
+ : size(0) {
+ }
+
+ void clear() {
+ inodeMap.clear();
+ size = 0;
+ blocks = 0;
+ }
+
+ void addFile( K3bFileItem* item, bool followSymlinks ) {
+ InodeInfo& inodeInfo = inodeMap[item->localId(followSymlinks)];
+
+ inodeInfo.items.append( item );
+
+ if( inodeInfo.number == 0 ) {
+ inodeInfo.savedSize = item->itemSize( followSymlinks );
+
+ size += inodeInfo.savedSize;
+ blocks += inodeInfo.blocks();
+ }
+
+ inodeInfo.number++;
+ }
+
+ void addSpecialItem( K3bDataItem* item ) {
+ // special files do not have a corresponding local file
+ // so we just add their k3bSize
+ size += item->size();
+ blocks += usedBlocks(item->size());
+ specialItems.append( item );
+ }
+
+ void removeFile( K3bFileItem* item, bool followSymlinks ) {
+ InodeInfo& inodeInfo = inodeMap[item->localId(followSymlinks)];
+
+ if( inodeInfo.items.findRef( item ) == -1 ) {
+ kdError() << "(K3bFileCompilationSizeHandler) "
+ << item->localPath()
+ << " has been removed without being added!" << endl;
+ }
+ else {
+ if( item->itemSize(followSymlinks) != inodeInfo.savedSize ) {
+ kdError() << "(K3bFileCompilationSizeHandler) savedSize differs!" << endl;
+ }
+
+ inodeInfo.items.removeRef( item );
+ inodeInfo.number--;
+ if( inodeInfo.number == 0 ) {
+ size -= inodeInfo.savedSize;
+ blocks -= inodeInfo.blocks();
+ }
+ }
+ }
+
+ void removeSpecialItem( K3bDataItem* item ) {
+ // special files do not have a corresponding local file
+ // so we just substract their k3bSize
+ if( specialItems.findRef( item ) == -1 ) {
+ kdError() << "(K3bFileCompilationSizeHandler) Special item "
+ << item->k3bName()
+ << " has been removed without being added!" << endl;
+ }
+ else {
+ specialItems.removeRef( item );
+ size -= item->size();
+ blocks -= usedBlocks(item->size());
+ }
+ }
+
+
+ /**
+ * This maps from inodes to the number of occurrences of the inode.
+ */
+ QMap<K3bFileItem::Id, InodeInfo> inodeMap;
+
+ KIO::filesize_t size;
+ K3b::Msf blocks;
+
+ QPtrList<K3bDataItem> specialItems;
+};
+
+
+
+K3bFileCompilationSizeHandler::K3bFileCompilationSizeHandler()
+{
+ d_symlinks = new Private;
+ d_noSymlinks = new Private;
+}
+
+K3bFileCompilationSizeHandler::~K3bFileCompilationSizeHandler()
+{
+ delete d_symlinks;
+ delete d_noSymlinks;
+}
+
+
+const KIO::filesize_t& K3bFileCompilationSizeHandler::size( bool followSymlinks ) const
+{
+ if( followSymlinks )
+ return d_noSymlinks->size;
+ else
+ return d_symlinks->size;
+}
+
+
+const K3b::Msf& K3bFileCompilationSizeHandler::blocks( bool followSymlinks ) const
+{
+ if( followSymlinks )
+ return d_noSymlinks->blocks;
+ else
+ return d_symlinks->blocks;
+}
+
+
+void K3bFileCompilationSizeHandler::addFile( K3bDataItem* item )
+{
+ if( item->isSpecialFile() ) {
+ d_symlinks->addSpecialItem( item );
+ d_noSymlinks->addSpecialItem( item );
+ }
+ else if( item->isFile() ) {
+ K3bFileItem* fileItem = static_cast<K3bFileItem*>( item );
+ d_symlinks->addFile( fileItem, false );
+ d_noSymlinks->addFile( fileItem, true );
+ }
+}
+
+
+void K3bFileCompilationSizeHandler::removeFile( K3bDataItem* item )
+{
+ if( item->isSpecialFile() ) {
+ d_symlinks->removeSpecialItem( item );
+ d_noSymlinks->removeSpecialItem( item );
+ }
+ else if( item->isFile() ) {
+ K3bFileItem* fileItem = static_cast<K3bFileItem*>( item );
+ d_symlinks->removeFile( fileItem, false );
+ d_noSymlinks->removeFile( fileItem, true );
+ }
+}
+
+
+void K3bFileCompilationSizeHandler::clear()
+{
+ d_symlinks->clear();
+ d_noSymlinks->clear();
+}
diff --git a/libk3b/projects/datacd/k3bfilecompilationsizehandler.h b/libk3b/projects/datacd/k3bfilecompilationsizehandler.h
new file mode 100644
index 0000000..c996657
--- /dev/null
+++ b/libk3b/projects/datacd/k3bfilecompilationsizehandler.h
@@ -0,0 +1,73 @@
+/*
+ *
+ * $Id: k3bfilecompilationsizehandler.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_FILECOMPILATION_SIZE_HANDLER_H_
+#define _K3B_FILECOMPILATION_SIZE_HANDLER_H_
+
+
+#include <kio/global.h>
+#include <k3bmsf.h>
+
+class K3bDataItem;
+
+
+/**
+ * This class maintains a map of indoes and the number
+ * of files in the doc that belong to that inode.
+ * This way a more accurate size calculation is possible
+ *
+ * It has to be noted that the sizes of the directories
+ * are only locally true. That means that in some cases
+ * the root directory of the project may show a much
+ * higher size than calculated by this class.
+ */
+class K3bFileCompilationSizeHandler
+{
+ public:
+ K3bFileCompilationSizeHandler();
+ ~K3bFileCompilationSizeHandler();
+
+ /**
+ * This does NOT equal blocks() * 2048.
+ * This is the sum of the actual file sizes.
+ */
+ const KIO::filesize_t& size( bool followSymlinks = false ) const;
+
+ /**
+ * Number of blocks the files will occupy.
+ */
+ const K3b::Msf& blocks( bool followSymlinks = false ) const;
+
+ /**
+ * This will increase the counter for the inode of
+ * the file in url and update the totel size.
+ */
+ void addFile( K3bDataItem* );
+
+ /**
+ * This will decrease the counter for the inode of
+ * the file in url and update the totel size.
+ */
+ void removeFile( K3bDataItem* );
+
+ void clear();
+
+ private:
+ class Private;
+ Private* d_symlinks;
+ Private* d_noSymlinks;
+};
+
+#endif
diff --git a/libk3b/projects/datacd/k3bfileitem.cpp b/libk3b/projects/datacd/k3bfileitem.cpp
new file mode 100644
index 0000000..d9e288f
--- /dev/null
+++ b/libk3b/projects/datacd/k3bfileitem.cpp
@@ -0,0 +1,300 @@
+/*
+ *
+ * $Id: k3bfileitem.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include <config.h>
+#include <k3bglobals.h>
+
+#include "k3bfileitem.h"
+#include "k3bdatadoc.h"
+#include "k3bdiritem.h"
+#include "k3bisooptions.h"
+#include <k3bglobals.h>
+
+#include <qfileinfo.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qregexp.h>
+#include <qfile.h>
+
+#include <kurl.h>
+#include <kdebug.h>
+
+#include <errno.h>
+#include <string.h>
+
+
+bool operator==( const K3bFileItem::Id& id1, const K3bFileItem::Id& id2 )
+{
+ return ( id1.device == id2.device && id1.inode == id2.inode );
+}
+
+
+bool operator<( const K3bFileItem::Id& id1, const K3bFileItem::Id& id2 )
+{
+ if( id1.device == id2.device )
+ return ( id1.inode < id2.inode );
+ else
+ return ( id1.device < id2.device );
+}
+
+
+bool operator>( const K3bFileItem::Id& id1, const K3bFileItem::Id& id2 )
+{
+ return !( id2 < id1 || id1 == id2 );
+}
+
+
+
+K3bFileItem::K3bFileItem( const QString& filePath, K3bDataDoc* doc, K3bDirItem* dir, const QString& k3bName, int flags )
+ : K3bDataItem( doc, dir, flags ),
+ m_replacedItemFromOldSession(0),
+ m_localPath(filePath)
+{
+ if( k3bName.isEmpty() )
+ m_k3bName = filePath.section( '/', -1 );
+ else
+ m_k3bName = k3bName;
+
+ // we determine the size here to avoid problems with removed or renamed files
+ // we need to use lstat here since for symlinks both KDE and QT return the size of the file pointed to
+ // instead the size of the link.
+ k3b_struct_stat statBuf;
+ if( k3b_lstat( QFile::encodeName(filePath), &statBuf ) ) {
+ m_size = K3b::filesize( filePath );
+ m_id.inode = 0;
+ m_id.device = 0;
+ m_bSymLink = false;
+
+ kdError() << "(KFileItem) lstat failed: " << strerror(errno) << endl;
+
+ // since we have no proper inode info, disable the inode caching in the doc
+ if( doc ) {
+ K3bIsoOptions o( doc->isoOptions() );
+ o.setDoNotCacheInodes( true );
+ doc->setIsoOptions( o );
+ }
+ }
+ else {
+ m_size = (KIO::filesize_t)statBuf.st_size;
+
+ m_bSymLink = S_ISLNK(statBuf.st_mode);
+
+ //
+ // integrate the device number into the inode since files on different
+ // devices may have the same inode number!
+ //
+ m_id.inode = statBuf.st_ino;
+ m_id.device = statBuf.st_dev;
+ }
+
+ m_idFollowed = m_id;
+ m_sizeFollowed = m_size;
+
+ if( isSymLink() ) {
+ k3b_struct_stat statBuf;
+ if( k3b_stat( QFile::encodeName(filePath), &statBuf ) == 0 ) {
+ m_idFollowed.inode = statBuf.st_ino;
+ m_idFollowed.device = statBuf.st_dev;
+
+ m_sizeFollowed = (KIO::filesize_t)statBuf.st_size;
+ }
+ }
+
+ // add automagically like a qlistviewitem
+ if( parent() )
+ parent()->addDataItem( this );
+}
+
+
+K3bFileItem::K3bFileItem( const k3b_struct_stat* stat,
+ const k3b_struct_stat* followedStat,
+ const QString& filePath, K3bDataDoc* doc, K3bDirItem* dir, const QString& k3bName )
+ : K3bDataItem( doc, dir ),
+ m_replacedItemFromOldSession(0),
+ m_localPath(filePath)
+{
+ if( k3bName.isEmpty() )
+ m_k3bName = filePath.section( '/', -1 );
+ else
+ m_k3bName = k3bName;
+
+ m_size = (KIO::filesize_t)stat->st_size;
+ m_bSymLink = S_ISLNK(stat->st_mode);
+
+ //
+ // integrate the device number into the inode since files on different
+ // devices may have the same inode number!
+ //
+ m_id.inode = stat->st_ino;
+ m_id.device = stat->st_dev;
+
+ if( isSymLink() ) {
+ m_idFollowed.inode = followedStat->st_ino;
+ m_idFollowed.device = followedStat->st_dev;
+
+ m_sizeFollowed = (KIO::filesize_t)followedStat->st_size;
+ }
+ else {
+ m_idFollowed = m_id;
+ m_sizeFollowed = m_size;
+ }
+
+ if( parent() )
+ parent()->addDataItem( this );
+}
+
+
+K3bFileItem::K3bFileItem( const K3bFileItem& item )
+ : K3bDataItem( item ),
+ m_replacedItemFromOldSession(0),
+ m_size( item.m_size ),
+ m_sizeFollowed( item.m_sizeFollowed ),
+ m_id( item.m_id ),
+ m_idFollowed( item.m_idFollowed ),
+ m_localPath( item.m_localPath ),
+ m_bSymLink( item.m_bSymLink )
+{
+}
+
+
+K3bFileItem::~K3bFileItem()
+{
+ // remove this from parentdir
+ take();
+}
+
+
+K3bDataItem* K3bFileItem::copy() const
+{
+ return new K3bFileItem( *this );
+}
+
+
+KIO::filesize_t K3bFileItem::itemSize( bool followSymlinks ) const
+{
+ if( followSymlinks )
+ return m_sizeFollowed;
+ else
+ return m_size;
+}
+
+
+K3bFileItem::Id K3bFileItem::localId() const
+{
+ return localId( doc() ? doc()->isoOptions().followSymbolicLinks() || !doc()->isoOptions().createRockRidge() : false );
+}
+
+
+K3bFileItem::Id K3bFileItem::localId( bool followSymlinks ) const
+{
+ if( followSymlinks )
+ return m_idFollowed;
+ else
+ return m_id;
+}
+
+
+bool K3bFileItem::exists() const
+{
+ return true;
+}
+
+QString K3bFileItem::absIsoPath()
+{
+ // return m_dir->absIsoPath() + m_isoName;
+ return QString::null;
+}
+
+
+QString K3bFileItem::localPath() const
+{
+ return m_localPath;
+}
+
+K3bDirItem* K3bFileItem::getDirItem() const
+{
+ return getParent();
+}
+
+
+bool K3bFileItem::isSymLink() const
+{
+ return m_bSymLink;
+}
+
+
+QString K3bFileItem::linkDest() const
+{
+ return QFileInfo( localPath() ).readLink();
+}
+
+
+bool K3bFileItem::isValid() const
+{
+ if( isSymLink() ) {
+
+ // this link is not valid if we cannot follow it if we want to
+ if( doc()->isoOptions().followSymbolicLinks() ) {
+ return QFile::exists( K3b::resolveLink( localPath() ) );
+ }
+
+ QString dest = linkDest();
+
+ if( dest[0] == '/' )
+ return false; // absolut links can never be part of the compilation!
+
+ // parse the link
+ K3bDirItem* dir = getParent();
+
+ QStringList tokens = QStringList::split( QRegExp("/+"), dest ); // two slashes or more do the same as one does!
+
+ unsigned int i = 0;
+ while( i < tokens.size() ) {
+ if( tokens[i] == "." ) {
+ // ignore it
+ }
+ else if( tokens[i] == ".." ) {
+ // change the directory
+ dir = dir->parent();
+ if( dir == 0 )
+ return false;
+ }
+ else {
+ // search for the item in dir
+ K3bDataItem* d = dir->find( tokens[i] );
+ if( d == 0 )
+ return false;
+
+ if( d->isDir() ) {
+ // change directory
+ dir = (K3bDirItem*)d;
+ }
+ else {
+ if( i+1 != tokens.size() )
+ return false; // if di is a file we need to be at the last token
+ else
+ return (dest[dest.length()-1] != '/'); // if the link destination ends with a slash
+ // it can only point to a directory!
+ }
+ }
+
+ i++;
+ }
+
+ return true;
+ }
+ else
+ return true;
+}
diff --git a/libk3b/projects/datacd/k3bfileitem.h b/libk3b/projects/datacd/k3bfileitem.h
new file mode 100644
index 0000000..f23644f
--- /dev/null
+++ b/libk3b/projects/datacd/k3bfileitem.h
@@ -0,0 +1,124 @@
+/*
+ *
+ * $Id: k3bfileitem.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BFILEITEM_H
+#define K3BFILEITEM_H
+
+
+#include "k3bdataitem.h"
+#include <k3bglobals.h>
+
+#include <kio/global.h>
+#include <qstring.h>
+
+#include "k3b_export.h"
+
+class K3bDataDoc;
+class K3bDirItem;
+
+
+/**
+ *@author Sebastian Trueg
+ */
+class LIBK3B_EXPORT K3bFileItem : public K3bDataItem
+{
+public:
+ /**
+ * Creates a new K3bFileItem
+ */
+ K3bFileItem( const QString& fileName, K3bDataDoc* doc, K3bDirItem* dir, const QString& k3bName = 0, int flags = 0 );
+
+ /**
+ * Constructor for optimized file item creation which does no additional stat.
+ *
+ * Used by K3b to speedup file item creation.
+ */
+ K3bFileItem( const k3b_struct_stat* stat,
+ const k3b_struct_stat* followedStat,
+ const QString& fileName, K3bDataDoc* doc, K3bDirItem* dir, const QString& k3bName = 0 );
+
+ /**
+ * Default copy constructor
+ * Creates a copy of the fileitem. The copy, however, is not an exact duplicate of this item.
+ * The copy does not have a parent dir set and any old session items are set to 0.
+ */
+ K3bFileItem( const K3bFileItem& );
+
+ virtual ~K3bFileItem();
+
+ virtual K3bDataItem* copy() const;
+
+ bool exists() const;
+
+ QString absIsoPath();
+
+ /** reimplemented from K3bDataItem */
+ QString localPath() const;
+
+ /**
+ * Identification of the files on the local device.
+ */
+ struct Id {
+ dev_t device;
+ ino_t inode;
+ };
+
+ /**
+ * This is not the normal inode number but it also contains
+ * the device number.
+ */
+ Id localId() const;
+
+ /**
+ * The id of the file the symlink is pointing to
+ */
+ Id localId( bool followSymlinks ) const;
+
+ K3bDirItem* getDirItem() const;
+
+ bool isSymLink() const;
+ QString linkDest() const;
+ bool isFile() const { return true; }
+
+ /** returns true if the item is not a link or
+ * if the link's destination is part of the compilation */
+ bool isValid() const;
+
+ K3bDataItem* replaceItemFromOldSession() const { return m_replacedItemFromOldSession; }
+ void setReplacedItemFromOldSession( K3bDataItem* item ) { m_replacedItemFromOldSession = item; }
+
+ /**
+ * Normally one does not use this method but K3bDataItem::size()
+ */
+ KIO::filesize_t itemSize( bool followSymlinks ) const;
+
+ private:
+ K3bDataItem* m_replacedItemFromOldSession;
+
+ KIO::filesize_t m_size;
+ KIO::filesize_t m_sizeFollowed;
+ Id m_id;
+ Id m_idFollowed;
+
+ QString m_localPath;
+ bool m_bSymLink;
+};
+
+bool operator==( const K3bFileItem::Id&, const K3bFileItem::Id& );
+bool operator<( const K3bFileItem::Id&, const K3bFileItem::Id& );
+bool operator>( const K3bFileItem::Id&, const K3bFileItem::Id& );
+
+#endif
diff --git a/libk3b/projects/datacd/k3bisoimager.cpp b/libk3b/projects/datacd/k3bisoimager.cpp
new file mode 100644
index 0000000..f44d3ab
--- /dev/null
+++ b/libk3b/projects/datacd/k3bisoimager.cpp
@@ -0,0 +1,1187 @@
+/*
+ *
+ * $Id: k3bisoimager.cpp 655085 2007-04-17 17:48:36Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include <config.h>
+#include <k3bglobals.h>
+
+#include "k3bisoimager.h"
+#include "k3bdiritem.h"
+#include "k3bbootitem.h"
+#include "k3bdatadoc.h"
+#include "k3bdatapreparationjob.h"
+#include <k3bexternalbinmanager.h>
+#include <k3bdevice.h>
+#include <k3bprocess.h>
+#include <k3bcore.h>
+#include <k3bversion.h>
+#include <k3bglobals.h>
+#include <k3bchecksumpipe.h>
+#include <k3bfilesplitter.h>
+
+#include <kdebug.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+#include <ktempfile.h>
+#include <kio/netaccess.h>
+#include <kio/global.h>
+#include <kio/job.h>
+#include <kstringhandler.h>
+
+#include <qfile.h>
+#include <qregexp.h>
+#include <qdir.h>
+#include <qapplication.h>
+#include <qvaluestack.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <utime.h>
+
+
+int K3bIsoImager::s_imagerSessionCounter = 0;
+
+
+class K3bIsoImager::Private
+{
+public:
+ Private()
+ : pipe(0) {
+ }
+
+ ~Private() {
+ delete pipe;
+ }
+
+ QString imagePath;
+ K3bFileSplitter imageFile;
+ const K3bExternalBin* mkisofsBin;
+
+ enum LinkHandling {
+ KEEP_ALL,
+ FOLLOW,
+ DISCARD_ALL,
+ DISCARD_BROKEN
+ };
+
+ int usedLinkHandling;
+
+ bool knownError;
+
+ K3bActivePipe* pipe;
+ K3bDataPreparationJob* dataPreparationJob;
+};
+
+
+K3bIsoImager::K3bIsoImager( K3bDataDoc* doc, K3bJobHandler* hdl, QObject* parent, const char* name )
+ : K3bJob( hdl, parent, name ),
+ m_pathSpecFile(0),
+ m_rrHideFile(0),
+ m_jolietHideFile(0),
+ m_sortWeightFile(0),
+ m_process( 0 ),
+ m_processExited(false),
+ m_doc( doc ),
+ m_noDeepDirectoryRelocation( false ),
+ m_importSession( false ),
+ m_device(0),
+ m_mkisofsPrintSizeResult( 0 ),
+ m_fdToWriteTo(-1)
+{
+ d = new Private();
+ d->dataPreparationJob = new K3bDataPreparationJob( doc, this, this );
+ connectSubJob( d->dataPreparationJob,
+ SLOT(slotDataPreparationDone(bool)),
+ DEFAULT_SIGNAL_CONNECTION );
+}
+
+
+K3bIsoImager::~K3bIsoImager()
+{
+ cleanup();
+ delete d;
+}
+
+
+bool K3bIsoImager::active() const
+{
+ return K3bJob::active();
+}
+
+
+void K3bIsoImager::writeToFd( int fd )
+{
+ m_fdToWriteTo = fd;
+}
+
+
+void K3bIsoImager::writeToImageFile( const QString& path )
+{
+ d->imagePath = path;
+ m_fdToWriteTo = -1;
+}
+
+
+void K3bIsoImager::slotReceivedStderr( const QString& line )
+{
+ parseMkisofsOutput( line );
+ emit debuggingOutput( "mkisofs", line );
+}
+
+
+void K3bIsoImager::handleMkisofsProgress( int p )
+{
+ emit percent( p );
+}
+
+
+void K3bIsoImager::handleMkisofsInfoMessage( const QString& line, int type )
+{
+ emit infoMessage( line, type );
+ if( type == ERROR )
+ d->knownError = true;
+}
+
+
+void K3bIsoImager::slotProcessExited( KProcess* p )
+{
+ kdDebug() << k_funcinfo << endl;
+
+ m_processExited = true;
+
+ d->pipe->close();
+
+ emit debuggingOutput( "K3bIsoImager",
+ QString("Pipe throughput: %1 bytes read, %2 bytes written.")
+ .arg(d->pipe->bytesRead()).arg(d->pipe->bytesWritten()) );
+
+ if( d->imageFile.isOpen() ) {
+ d->imageFile.close();
+
+ if( m_canceled || p->exitStatus() != 0 ) {
+ d->imageFile.remove();
+ emit infoMessage( i18n("Removed incomplete image file %1.").arg(d->imageFile.name()), WARNING );
+ }
+ }
+
+ if( m_canceled ) {
+ emit canceled();
+ jobFinished(false);
+ }
+ else {
+ if( p->normalExit() ) {
+ if( p->exitStatus() == 0 ) {
+ jobFinished( !mkisofsReadError() );
+ }
+ else {
+ switch( p->exitStatus() ) {
+ case 104:
+ // connection reset by peer
+ // This only happens if cdrecord does not finish successfully
+ // so we may leave the error handling to it meaning we handle this
+ // as a known error
+ break;
+
+ case 2:
+ // mkisofs seems to have a bug that prevents to use filenames
+ // that contain one or more backslashes
+ // mkisofs 1.14 has the bug, 1.15a40 not
+ // TODO: find out the version that fixed the bug
+ if( m_containsFilesWithMultibleBackslashes &&
+ !k3bcore->externalBinManager()->binObject( "mkisofs" )->hasFeature( "backslashed_filenames" ) ) {
+ emit infoMessage( i18n("Due to a bug in mkisofs <= 1.15a40, K3b is unable to handle "
+ "filenames that contain more than one backslash:"), ERROR );
+
+ break;
+ }
+ // otherwise just fall through
+
+ default:
+ if( !d->knownError && !mkisofsReadError() ) {
+ emit infoMessage( i18n("%1 returned an unknown error (code %2).").arg("mkisofs").arg(p->exitStatus()),
+ K3bJob::ERROR );
+ emit infoMessage( i18n("Please send me an email with the last output."), K3bJob::ERROR );
+ }
+ }
+
+ jobFinished( false );
+ }
+ }
+ else {
+ emit infoMessage( i18n("%1 did not exit cleanly.").arg("mkisofs"), ERROR );
+ jobFinished( false );
+ }
+ }
+
+ cleanup();
+}
+
+
+void K3bIsoImager::cleanup()
+{
+ // remove all temp files
+ delete m_pathSpecFile;
+ delete m_rrHideFile;
+ delete m_jolietHideFile;
+ delete m_sortWeightFile;
+
+ // remove boot-images-temp files
+ for( QStringList::iterator it = m_tempFiles.begin();
+ it != m_tempFiles.end(); ++it )
+ QFile::remove( *it );
+ m_tempFiles.clear();
+
+ m_pathSpecFile = m_jolietHideFile = m_rrHideFile = m_sortWeightFile = 0;
+
+ delete m_process;
+ m_process = 0;
+
+ clearDummyDirs();
+}
+
+
+void K3bIsoImager::init()
+{
+ jobStarted();
+
+ cleanup();
+
+ d->dataPreparationJob->start();
+}
+
+
+void K3bIsoImager::slotDataPreparationDone( bool success )
+{
+ if( success ) {
+ //
+ // We always calculate the image size. It does not take long and at least the mixed job needs it
+ // anyway
+ //
+ startSizeCalculation();
+ }
+ else {
+ if( d->dataPreparationJob->hasBeenCanceled() ) {
+ m_canceled = true;
+ emit canceled();
+ }
+ jobFinished( false );
+ }
+}
+
+
+void K3bIsoImager::calculateSize()
+{
+ jobStarted();
+ startSizeCalculation();
+}
+
+
+void K3bIsoImager::startSizeCalculation()
+{
+ d->mkisofsBin = initMkisofs();
+ if( !d->mkisofsBin ) {
+ jobFinished( false );
+ return;
+ }
+
+ initVariables();
+
+ delete m_process;
+ m_process = new K3bProcess();
+ m_process->setRunPrivileged(true);
+ m_process->setSplitStdout(true);
+
+ emit debuggingOutput( "Used versions", "mkisofs: " + d->mkisofsBin->version );
+
+ *m_process << d->mkisofsBin;
+
+ if( !prepareMkisofsFiles() ||
+ !addMkisofsParameters(true) ) {
+ cleanup();
+ jobFinished( false );
+ return;
+ }
+
+ // add empty dummy dir since one path-spec is needed
+ // ??? Seems it is not needed after all. At least mkisofs 1.14 and above don't need it. ???
+ // *m_process << dummyDir();
+
+ kdDebug() << "***** mkisofs calculate size parameters:\n";
+ const QValueList<QCString>& args = m_process->args();
+ QString s;
+ for( QValueList<QCString>::const_iterator it = args.begin(); it != args.end(); ++it ) {
+ s += *it + " ";
+ }
+ kdDebug() << s << endl << flush;
+ emit debuggingOutput("mkisofs calculate size command:", s);
+
+ // since output changed during mkisofs version changes we grab both
+ // stdout and stderr
+
+ // mkisofs version >= 1.15 (don't know about 1.14!)
+ // the extends on stdout (as lonely number)
+ // and error and warning messages on stderr
+
+ // mkisofs >= 1.13
+ // everything is written to stderr
+ // last line is: "Total extents scheduled to be written = XXXXX"
+
+ // TODO: use K3bProcess::OutputCollector instead iof our own two slots.
+
+ connect( m_process, SIGNAL(receivedStderr(KProcess*, char*, int)),
+ this, SLOT(slotCollectMkisofsPrintSizeStderr(KProcess*, char*, int)) );
+ connect( m_process, SIGNAL(stdoutLine(const QString&)),
+ this, SLOT(slotCollectMkisofsPrintSizeStdout(const QString&)) );
+ connect( m_process, SIGNAL(processExited(KProcess*)),
+ this, SLOT(slotMkisofsPrintSizeFinished()) );
+
+ // we also want error messages
+ connect( m_process, SIGNAL(stderrLine( const QString& )),
+ this, SLOT(slotReceivedStderr( const QString& )) );
+
+ m_collectedMkisofsPrintSizeStdout = QString::null;
+ m_collectedMkisofsPrintSizeStderr = QString::null;
+ m_mkisofsPrintSizeResult = 0;
+
+ if( !m_process->start( KProcess::NotifyOnExit, KProcess::AllOutput ) ) {
+ emit infoMessage( i18n("Could not start %1.").arg("mkisofs"), K3bJob::ERROR );
+ cleanup();
+
+ jobFinished( false );
+ return;
+ }
+}
+
+
+void K3bIsoImager::slotCollectMkisofsPrintSizeStderr(KProcess*, char* data , int len)
+{
+ emit debuggingOutput( "mkisofs", QString::fromLocal8Bit( data, len ) );
+ m_collectedMkisofsPrintSizeStderr.append( QString::fromLocal8Bit( data, len ) );
+}
+
+
+void K3bIsoImager::slotCollectMkisofsPrintSizeStdout( const QString& line )
+{
+ // newer versions of mkisofs outut additional lines of junk before the size :(
+ // so we only use the last line
+ emit debuggingOutput( "mkisofs", line );
+ m_collectedMkisofsPrintSizeStdout = line;
+}
+
+
+void K3bIsoImager::slotMkisofsPrintSizeFinished()
+{
+ if( m_canceled ) {
+ emit canceled();
+ jobFinished( false );
+ return;
+ }
+
+ bool success = true;
+
+ // if m_collectedMkisofsPrintSizeStdout is not empty we have a recent version of
+ // mkisofs and parsing is very easy (s.o.)
+ if( !m_collectedMkisofsPrintSizeStdout.isEmpty() ) {
+ kdDebug() << "(K3bIsoImager) iso size: " << m_collectedMkisofsPrintSizeStdout << endl;
+ m_mkisofsPrintSizeResult = m_collectedMkisofsPrintSizeStdout.toInt( &success );
+ }
+ else {
+ // parse the stderr output
+ // I hope parsing the last line is enough!
+ int pos = m_collectedMkisofsPrintSizeStderr.findRev( "extents scheduled to be written" );
+
+ if( pos == -1 )
+ success = false;
+ else
+ m_mkisofsPrintSizeResult = m_collectedMkisofsPrintSizeStderr.mid( pos+33 ).toInt( &success );
+ }
+
+ emit debuggingOutput( "K3bIsoImager",
+ QString("mkisofs print size result: %1 (%2 bytes)")
+ .arg(m_mkisofsPrintSizeResult)
+ .arg(Q_UINT64(m_mkisofsPrintSizeResult)*2048ULL) );
+
+ cleanup();
+
+
+ if( success ) {
+ jobFinished( true );
+ }
+ else {
+ m_mkisofsPrintSizeResult = 0;
+ kdDebug() << "(K3bIsoImager) Parsing mkisofs -print-size failed: " << m_collectedMkisofsPrintSizeStdout << endl;
+ emit infoMessage( i18n("Could not determine size of resulting image file."), ERROR );
+ jobFinished( false );
+ }
+}
+
+
+void K3bIsoImager::initVariables()
+{
+ m_containsFilesWithMultibleBackslashes = false;
+ m_processExited = false;
+ m_canceled = false;
+ d->knownError = false;
+
+ // determine symlink handling
+ // follow links superseeds discard all links which superseeds discard broken links
+ // without rockridge we follow the links or discard all
+ if( m_doc->isoOptions().followSymbolicLinks() )
+ d->usedLinkHandling = Private::FOLLOW;
+ else if( m_doc->isoOptions().discardSymlinks() )
+ d->usedLinkHandling = Private::DISCARD_ALL;
+ else if( m_doc->isoOptions().createRockRidge() ) {
+ if( m_doc->isoOptions().discardBrokenSymlinks() )
+ d->usedLinkHandling = Private::DISCARD_BROKEN;
+ else
+ d->usedLinkHandling = Private::KEEP_ALL;
+ }
+ else {
+ d->usedLinkHandling = Private::FOLLOW;
+ }
+
+ m_sessionNumber = s_imagerSessionCounter++;
+}
+
+
+void K3bIsoImager::start()
+{
+ jobStarted();
+
+ cleanup();
+
+ d->mkisofsBin = initMkisofs();
+ if( !d->mkisofsBin ) {
+ jobFinished( false );
+ return;
+ }
+
+ initVariables();
+
+ m_process = new K3bProcess();
+ m_process->setRunPrivileged(true);
+
+ *m_process << d->mkisofsBin;
+
+ // prepare the filenames as written to the image
+ m_doc->prepareFilenames();
+
+ if( !prepareMkisofsFiles() ||
+ !addMkisofsParameters() ) {
+ cleanup();
+ jobFinished( false );
+ return;
+ }
+
+ connect( m_process, SIGNAL(processExited(KProcess*)),
+ this, SLOT(slotProcessExited(KProcess*)) );
+
+ connect( m_process, SIGNAL(stderrLine( const QString& )),
+ this, SLOT(slotReceivedStderr( const QString& )) );
+
+ //
+ // Check the image file
+ if( m_fdToWriteTo == -1 ) {
+ d->imageFile.setName( d->imagePath );
+ if( !d->imageFile.open( IO_WriteOnly ) ) {
+ emit infoMessage( i18n("Could not open %1 for writing").arg(d->imagePath), ERROR );
+ cleanup();
+ jobFinished(false);
+ return;
+ }
+ }
+
+ //
+ // Open the active pipe which does the streaming
+ delete d->pipe;
+ if( m_doc->verifyData() )
+ d->pipe = new K3bChecksumPipe();
+ else
+ d->pipe = new K3bActivePipe();
+
+ if( m_fdToWriteTo == -1 )
+ d->pipe->writeToIODevice( &d->imageFile );
+ else
+ d->pipe->writeToFd( m_fdToWriteTo );
+ d->pipe->open();
+ m_process->writeToFd( d->pipe->in() );
+
+
+ kdDebug() << "***** mkisofs parameters:\n";
+ const QValueList<QCString>& args = m_process->args();
+ QString s;
+ for( QValueList<QCString>::const_iterator it = args.begin(); it != args.end(); ++it ) {
+ s += *it + " ";
+ }
+ kdDebug() << s << endl << flush;
+ emit debuggingOutput("mkisofs command:", s);
+
+ if( !m_process->start( KProcess::NotifyOnExit, KProcess::AllOutput) ) {
+ // something went wrong when starting the program
+ // it "should" be the executable
+ kdDebug() << "(K3bIsoImager) could not start mkisofs" << endl;
+ emit infoMessage( i18n("Could not start %1.").arg("mkisofs"), K3bJob::ERROR );
+ jobFinished( false );
+ cleanup();
+ }
+}
+
+
+void K3bIsoImager::cancel()
+{
+ m_canceled = true;
+
+ if( m_process && !m_processExited ) {
+ m_process->kill();
+ }
+ else if( active() ) {
+ emit canceled();
+ jobFinished(false);
+ }
+}
+
+
+void K3bIsoImager::setMultiSessionInfo( const QString& info, K3bDevice::Device* dev )
+{
+ m_multiSessionInfo = info;
+ m_device = dev;
+}
+
+
+// iso9660 + RR use some latin1 variant. So we need to cut the desc fields
+// counting 8bit chars. The GUI should take care of restricting the length
+// and the charset
+static void truncateTheHardWay( QString& s, int max )
+{
+ QCString cs = s.utf8();
+ cs.truncate(max);
+ s = QString::fromUtf8( cs );
+}
+
+
+bool K3bIsoImager::addMkisofsParameters( bool printSize )
+{
+ // add multisession info
+ if( !m_multiSessionInfo.isEmpty() ) {
+ *m_process << "-cdrecord-params" << m_multiSessionInfo;
+ if( m_device )
+ *m_process << "-prev-session" << m_device->blockDeviceName();
+ }
+
+ // add the arguments
+ *m_process << "-gui";
+ *m_process << "-graft-points";
+
+ if( printSize )
+ *m_process << "-print-size" << "-quiet";
+
+ if( !m_doc->isoOptions().volumeID().isEmpty() ) {
+ QString s = m_doc->isoOptions().volumeID();
+ truncateTheHardWay(s, 32); // ensure max length
+ *m_process << "-volid" << s;
+ }
+ else {
+ emit infoMessage( i18n("No volume id specified. Using default."), WARNING );
+ *m_process << "-volid" << "CDROM";
+ }
+
+ QString s = m_doc->isoOptions().volumeSetId();
+ truncateTheHardWay(s, 128); // ensure max length
+ *m_process << "-volset" << s;
+
+ s = m_doc->isoOptions().applicationID();
+ truncateTheHardWay(s, 128); // ensure max length
+ *m_process << "-appid" << s;
+
+ s = m_doc->isoOptions().publisher();
+ truncateTheHardWay(s, 128); // ensure max length
+ *m_process << "-publisher" << s;
+
+ s = m_doc->isoOptions().preparer();
+ truncateTheHardWay(s, 128); // ensure max length
+ *m_process << "-preparer" << s;
+
+ s = m_doc->isoOptions().systemId();
+ truncateTheHardWay(s, 32); // ensure max length
+ *m_process << "-sysid" << s;
+
+ s = m_doc->isoOptions().abstractFile();
+ truncateTheHardWay(s, 37); // ensure max length
+ if ( !s.isEmpty() )
+ *m_process << "-abstract" << s;
+
+ s = m_doc->isoOptions().copyrightFile();
+ truncateTheHardWay(s, 37); // ensure max length
+ if ( !s.isEmpty() )
+ *m_process << "-copyright" << s;
+
+ s = m_doc->isoOptions().bibliographFile();
+ truncateTheHardWay(s, 37); // ensure max length
+ if ( !s.isEmpty() )
+ *m_process << "-biblio" << s;
+
+ int volsetSize = m_doc->isoOptions().volumeSetSize();
+ int volsetSeqNo = m_doc->isoOptions().volumeSetNumber();
+ if( volsetSeqNo > volsetSize ) {
+ kdDebug() << "(K3bIsoImager) invalid volume set sequence number: " << volsetSeqNo
+ << " with volume set size: " << volsetSize << endl;
+ volsetSeqNo = volsetSize;
+ }
+ *m_process << "-volset-size" << QString::number(volsetSize);
+ *m_process << "-volset-seqno" << QString::number(volsetSeqNo);
+
+ if( m_sortWeightFile ) {
+ *m_process << "-sort" << m_sortWeightFile->name();
+ }
+
+ if( m_doc->isoOptions().createRockRidge() ) {
+ if( m_doc->isoOptions().preserveFilePermissions() )
+ *m_process << "-rock";
+ else
+ *m_process << "-rational-rock";
+ if( m_rrHideFile )
+ *m_process << "-hide-list" << m_rrHideFile->name();
+ }
+
+ if( m_doc->isoOptions().createJoliet() ) {
+ *m_process << "-joliet";
+ if( m_doc->isoOptions().jolietLong() )
+ *m_process << "-joliet-long";
+ if( m_jolietHideFile )
+ *m_process << "-hide-joliet-list" << m_jolietHideFile->name();
+ }
+
+ if( m_doc->isoOptions().doNotCacheInodes() )
+ *m_process << "-no-cache-inodes";
+
+ //
+ // Check if we have files > 2 GB and enable udf in that case.
+ //
+ bool filesGreaterThan2Gb = false;
+ K3bDataItem* item = m_doc->root();
+ while( (item = item->nextSibling()) ) {
+ if( item->isFile() && item->size() > 2LL*1024LL*1024LL*1024LL ) {
+ filesGreaterThan2Gb = true;
+ break;
+ }
+ }
+
+ if( filesGreaterThan2Gb ) {
+ emit infoMessage( i18n("Found files bigger than 2 GB. These files will only be fully accessible if mounted with UDF."),
+ WARNING );
+
+ // in genisoimage 1.1.3 "they" silently introduced this aweful parameter
+ if ( d->mkisofsBin->hasFeature( "genisoimage" ) && d->mkisofsBin->version >= K3bVersion( 1, 1, 3 ) ) {
+ *m_process << "-allow-limited-size";
+ }
+ }
+
+ bool udf = m_doc->isoOptions().createUdf();
+ if( !udf && filesGreaterThan2Gb ) {
+ emit infoMessage( i18n("Enabling UDF extension."), INFO );
+ udf = true;
+ }
+ if( udf )
+ *m_process << "-udf";
+
+ if( m_doc->isoOptions().ISOuntranslatedFilenames() ) {
+ *m_process << "-untranslated-filenames";
+ }
+ else {
+ if( m_doc->isoOptions().ISOallowPeriodAtBegin() )
+ *m_process << "-allow-leading-dots";
+ if( m_doc->isoOptions().ISOallow31charFilenames() )
+ *m_process << "-full-iso9660-filenames";
+ if( m_doc->isoOptions().ISOomitVersionNumbers() && !m_doc->isoOptions().ISOmaxFilenameLength() )
+ *m_process << "-omit-version-number";
+ if( m_doc->isoOptions().ISOrelaxedFilenames() )
+ *m_process << "-relaxed-filenames";
+ if( m_doc->isoOptions().ISOallowLowercase() )
+ *m_process << "-allow-lowercase";
+ if( m_doc->isoOptions().ISOnoIsoTranslate() )
+ *m_process << "-no-iso-translate";
+ if( m_doc->isoOptions().ISOallowMultiDot() )
+ *m_process << "-allow-multidot";
+ if( m_doc->isoOptions().ISOomitTrailingPeriod() )
+ *m_process << "-omit-period";
+ }
+
+ if( m_doc->isoOptions().ISOmaxFilenameLength() )
+ *m_process << "-max-iso9660-filenames";
+
+ if( m_noDeepDirectoryRelocation )
+ *m_process << "-disable-deep-relocation";
+
+ // We do our own following
+// if( m_doc->isoOptions().followSymbolicLinks() || !m_doc->isoOptions().createRockRidge() )
+// *m_process << "-follow-links";
+
+ if( m_doc->isoOptions().createTRANS_TBL() )
+ *m_process << "-translation-table";
+ if( m_doc->isoOptions().hideTRANS_TBL() )
+ *m_process << "-hide-joliet-trans-tbl";
+
+ *m_process << "-iso-level" << QString::number(m_doc->isoOptions().ISOLevel());
+
+ if( m_doc->isoOptions().forceInputCharset() )
+ *m_process << "-input-charset" << m_doc->isoOptions().inputCharset();
+
+ *m_process << "-path-list" << QFile::encodeName(m_pathSpecFile->name());
+
+
+ // boot stuff
+ if( !m_doc->bootImages().isEmpty() ) {
+ bool first = true;
+ for( QPtrListIterator<K3bBootItem> it( m_doc->bootImages() );
+ *it; ++it ) {
+ if( !first )
+ *m_process << "-eltorito-alt-boot";
+
+ K3bBootItem* bootItem = *it;
+
+ *m_process << "-eltorito-boot";
+ *m_process << bootItem->writtenPath();
+
+ if( bootItem->imageType() == K3bBootItem::HARDDISK ) {
+ *m_process << "-hard-disk-boot";
+ }
+ else if( bootItem->imageType() == K3bBootItem::NONE ) {
+ *m_process << "-no-emul-boot";
+ if( bootItem->loadSegment() > 0 )
+ *m_process << "-boot-load-seg" << QString::number(bootItem->loadSegment());
+ if( bootItem->loadSize() > 0 )
+ *m_process << "-boot-load-size" << QString::number(bootItem->loadSize());
+ }
+
+ if( bootItem->imageType() != K3bBootItem::NONE && bootItem->noBoot() )
+ *m_process << "-no-boot";
+ if( bootItem->bootInfoTable() )
+ *m_process << "-boot-info-table";
+
+ first = false;
+ }
+
+ *m_process << "-eltorito-catalog" << m_doc->bootCataloge()->writtenPath();
+ }
+
+
+ // additional parameters from config
+ const QStringList& params = k3bcore->externalBinManager()->binObject( "mkisofs" )->userParameters();
+ for( QStringList::const_iterator it = params.begin(); it != params.end(); ++it )
+ *m_process << *it;
+
+ return true;
+}
+
+
+int K3bIsoImager::writePathSpec()
+{
+ delete m_pathSpecFile;
+ m_pathSpecFile = new KTempFile();
+ m_pathSpecFile->setAutoDelete(true);
+
+ if( QTextStream* t = m_pathSpecFile->textStream() ) {
+ // recursive path spec writing
+ int num = writePathSpecForDir( m_doc->root(), *t );
+
+ m_pathSpecFile->close();
+
+ return num;
+ }
+ else
+ return -1;
+}
+
+
+int K3bIsoImager::writePathSpecForDir( K3bDirItem* dirItem, QTextStream& stream )
+{
+ if( !m_noDeepDirectoryRelocation && dirItem->depth() > 7 ) {
+ kdDebug() << "(K3bIsoImager) found directory depth > 7. Enabling no deep directory relocation." << endl;
+ m_noDeepDirectoryRelocation = true;
+ }
+
+ // now create the graft points
+ int num = 0;
+ for( QPtrListIterator<K3bDataItem> it( dirItem->children() ); it.current(); ++it ) {
+ K3bDataItem* item = it.current();
+ bool writeItem = item->writeToCd();
+
+ if( item->isSymLink() ) {
+ if( d->usedLinkHandling == Private::DISCARD_ALL ||
+ ( d->usedLinkHandling == Private::DISCARD_BROKEN &&
+ !item->isValid() ) )
+ writeItem = false;
+
+ else if( d->usedLinkHandling == Private::FOLLOW ) {
+ QFileInfo f( K3b::resolveLink( item->localPath() ) );
+ if( !f.exists() ) {
+ emit infoMessage( i18n("Could not follow link %1 to non-existing file %2. Skipping...")
+ .arg(item->k3bName())
+ .arg(f.filePath()), WARNING );
+ writeItem = false;
+ }
+ else if( f.isDir() ) {
+ emit infoMessage( i18n("Ignoring link %1 to folder %2. K3b is unable to follow links to folders.")
+ .arg(item->k3bName())
+ .arg(f.filePath()), WARNING );
+ writeItem = false;
+ }
+ }
+ }
+ else if( item->isFile() ) {
+ QFileInfo f( item->localPath() );
+ if( !f.exists() ) {
+ emit infoMessage( i18n("Could not find file %1. Skipping...").arg(item->localPath()), WARNING );
+ writeItem = false;
+ }
+ else if( !f.isReadable() ) {
+ emit infoMessage( i18n("Could not read file %1. Skipping...").arg(item->localPath()), WARNING );
+ writeItem = false;
+ }
+ }
+
+ if( writeItem ) {
+ num++;
+
+ // some versions of mkisofs seem to have a bug that prevents to use filenames
+ // that contain one or more backslashes
+ if( item->writtenPath().contains("\\") )
+ m_containsFilesWithMultibleBackslashes = true;
+
+
+ if( item->isDir() ) {
+ stream << escapeGraftPoint( item->writtenPath() )
+ << "="
+ << escapeGraftPoint( dummyDir( static_cast<K3bDirItem*>(item) ) ) << "\n";
+
+ int x = writePathSpecForDir( dynamic_cast<K3bDirItem*>(item), stream );
+ if( x >= 0 )
+ num += x;
+ else
+ return -1;
+ }
+ else {
+ writePathSpecForFile( static_cast<K3bFileItem*>(item), stream );
+ }
+ }
+ }
+
+ return num;
+}
+
+
+void K3bIsoImager::writePathSpecForFile( K3bFileItem* item, QTextStream& stream )
+{
+ stream << escapeGraftPoint( item->writtenPath() )
+ << "=";
+
+ if( m_doc->bootImages().containsRef( dynamic_cast<K3bBootItem*>(item) ) ) { // boot-image-backup-hack
+
+ // create temp file
+ KTempFile temp;
+ QString tempPath = temp.name();
+ temp.unlink();
+
+ if( !KIO::NetAccess::copy( KURL(item->localPath()), KURL::fromPathOrURL(tempPath) ) ) {
+ emit infoMessage( i18n("Failed to backup boot image file %1").arg(item->localPath()), ERROR );
+ return;
+ }
+
+ static_cast<K3bBootItem*>(item)->setTempPath( tempPath );
+
+ m_tempFiles.append(tempPath);
+ stream << escapeGraftPoint( tempPath ) << "\n";
+ }
+ else if( item->isSymLink() && d->usedLinkHandling == Private::FOLLOW )
+ stream << escapeGraftPoint( K3b::resolveLink( item->localPath() ) ) << "\n";
+ else
+ stream << escapeGraftPoint( item->localPath() ) << "\n";
+}
+
+
+bool K3bIsoImager::writeRRHideFile()
+{
+ delete m_rrHideFile;
+ m_rrHideFile = new KTempFile();
+ m_rrHideFile->setAutoDelete(true);
+
+ if( QTextStream* t = m_rrHideFile->textStream() ) {
+
+ K3bDataItem* item = m_doc->root();
+ while( item ) {
+ if( item->hideOnRockRidge() ) {
+ if( !item->isDir() ) // hiding directories does not work (all dirs point to the dummy-dir)
+ *t << escapeGraftPoint( item->localPath() ) << endl;
+ }
+ item = item->nextSibling();
+ }
+
+ m_rrHideFile->close();
+ return true;
+ }
+ else
+ return false;
+}
+
+
+bool K3bIsoImager::writeJolietHideFile()
+{
+ delete m_jolietHideFile;
+ m_jolietHideFile = new KTempFile();
+ m_jolietHideFile->setAutoDelete(true);
+
+ if( QTextStream* t = m_jolietHideFile->textStream() ) {
+
+ K3bDataItem* item = m_doc->root();
+ while( item ) {
+ if( item->hideOnRockRidge() ) {
+ if( !item->isDir() ) // hiding directories does not work (all dirs point to the dummy-dir but we could introduce a second hidden dummy dir)
+ *t << escapeGraftPoint( item->localPath() ) << endl;
+ }
+ item = item->nextSibling();
+ }
+
+ m_jolietHideFile->close();
+ return true;
+ }
+ else
+ return false;
+}
+
+
+bool K3bIsoImager::writeSortWeightFile()
+{
+ delete m_sortWeightFile;
+ m_sortWeightFile = new KTempFile();
+ m_sortWeightFile->setAutoDelete(true);
+
+ if( QTextStream* t = m_sortWeightFile->textStream() ) {
+ //
+ // We need to write the local path in combination with the sort weight
+ // mkisofs will take care of multiple entries for one local file and always
+ // use the highest weight
+ //
+ K3bDataItem* item = m_doc->root();
+ while( (item = item->nextSibling()) ) { // we skip the root here
+ if( item->sortWeight() != 0 ) {
+ if( m_doc->bootImages().containsRef( dynamic_cast<K3bBootItem*>(item) ) ) { // boot-image-backup-hack
+ *t << escapeGraftPoint( static_cast<K3bBootItem*>(item)->tempPath() ) << " " << item->sortWeight() << endl;
+ }
+ else if( item->isDir() ) {
+ //
+ // Since we use dummy dirs for all directories in the filesystem and mkisofs uses the local path
+ // for sorting we need to create a different dummy dir for every sort weight value.
+ //
+ *t << escapeGraftPoint( dummyDir( static_cast<K3bDirItem*>(item) ) ) << " " << item->sortWeight() << endl;
+ }
+ else
+ *t << escapeGraftPoint( item->localPath() ) << " " << item->sortWeight() << endl;
+ }
+ }
+
+ m_sortWeightFile->close();
+ return true;
+ }
+ else
+ return false;
+}
+
+
+QString K3bIsoImager::escapeGraftPoint( const QString& str )
+{
+ QString enc = str;
+
+ //
+ // mkisofs manpage (-graft-points) is incorrect (as of mkisofs 2.01.01)
+ //
+ // Actually an equal sign needs to be escaped with one backslash only
+ // Single backslashes inside a filename can be used without change
+ // while single backslashes at the end of a filename need to be escaped
+ // with two backslashes.
+ //
+ // There is one more problem though: the name in the iso tree can never
+ // in any number of backslashes. mkisofs simply cannot handle it. So we
+ // need to remove these slashes somewhere or ignore those files (we do
+ // that in K3bDataDoc::addUrls)
+ //
+
+ //
+ // we do not use QString::replace to have full control
+ // this might be slow since QString::insert is slow but we don't care
+ // since this is only called to prepare the iso creation which is not
+ // time critical. :)
+ //
+
+ unsigned int pos = 0;
+ while( pos < enc.length() ) {
+ // escape every equal sign with one backslash
+ if( enc[pos] == '=' ) {
+ enc.insert( pos, "\\" );
+ pos += 2;
+ }
+ else if( enc[pos] == '\\' ) {
+ // escape every occurrence of two backslashes with two backslashes
+ if( pos+1 < enc.length() && enc[pos+1] == '\\' ) {
+ enc.insert( pos, "\\\\" );
+ pos += 4;
+ }
+ // escape the last single backslash in the filename (see above)
+ else if( pos == enc.length()-1 ) {
+ enc.insert( pos, "\\" );
+ pos += 2;
+ }
+ else
+ ++pos;
+ }
+ else
+ ++pos;
+ }
+
+// enc.replace( "\\\\", "\\\\\\\\" );
+// enc.replace( "=", "\\=" );
+
+ return enc;
+}
+
+
+bool K3bIsoImager::prepareMkisofsFiles()
+{
+ // write path spec file
+ // ----------------------------------------------------
+ int num = writePathSpec();
+ if( num < 0 ) {
+ emit infoMessage( i18n("Could not write temporary file"), K3bJob::ERROR );
+ return false;
+ }
+ else if( num == 0 ) {
+ emit infoMessage( i18n("No files to be written."), K3bJob::ERROR );
+ return false;
+ }
+
+ if( m_doc->isoOptions().createRockRidge() ) {
+ if( !writeRRHideFile() ) {
+ emit infoMessage( i18n("Could not write temporary file"), K3bJob::ERROR );
+ return false;
+ }
+ }
+
+ if( m_doc->isoOptions().createJoliet() ) {
+ if( !writeJolietHideFile() ) {
+ emit infoMessage( i18n("Could not write temporary file"), K3bJob::ERROR );
+ return false ;
+ }
+ }
+
+ if( !writeSortWeightFile() ) {
+ emit infoMessage( i18n("Could not write temporary file"), K3bJob::ERROR );
+ return false;
+ }
+
+ return true;
+}
+
+
+QString K3bIsoImager::dummyDir( K3bDirItem* dir )
+{
+ //
+ // since we use virtual folders in order to have folders with different weight factors and different
+ // permissions we create different dummy dirs to be passed to mkisofs
+ //
+
+ QDir _appDir( locateLocal( "appdata", "temp/" ) );
+
+ //
+ // create a unique isoimager session id
+ // This might become important in case we will allow multiple instances of the isoimager
+ // to run at the same time.
+ //
+ QString jobId = qApp->sessionId() + "_" + QString::number( m_sessionNumber );
+
+ if( !_appDir.cd( jobId ) ) {
+ _appDir.mkdir( jobId );
+ _appDir.cd( jobId );
+ }
+
+ QString name( "dummydir_" );
+ name += QString::number( dir->sortWeight() );
+
+ bool perm = false;
+ k3b_struct_stat statBuf;
+ if( !dir->localPath().isEmpty() ) {
+ // permissions
+ if( k3b_stat( QFile::encodeName(dir->localPath()), &statBuf ) == 0 ) {
+ name += "_";
+ name += QString::number( statBuf.st_uid );
+ name += "_";
+ name += QString::number( statBuf.st_gid );
+ name += "_";
+ name += QString::number( statBuf.st_mode );
+ name += "_";
+ name += QString::number( statBuf.st_mtime );
+
+ perm = true;
+ }
+ }
+
+
+ if( !_appDir.cd( name ) ) {
+
+ kdDebug() << "(K3bIsoImager) creating dummy dir: " << _appDir.absPath() << "/" << name << endl;
+
+ _appDir.mkdir( name );
+ _appDir.cd( name );
+
+ if( perm ) {
+ ::chmod( QFile::encodeName( _appDir.absPath() ), statBuf.st_mode );
+ ::chown( QFile::encodeName( _appDir.absPath() ), statBuf.st_uid, statBuf.st_gid );
+ struct utimbuf tb;
+ tb.actime = tb.modtime = statBuf.st_mtime;
+ ::utime( QFile::encodeName( _appDir.absPath() ), &tb );
+ }
+ }
+
+ return _appDir.absPath() + "/";
+}
+
+
+void K3bIsoImager::clearDummyDirs()
+{
+ QString jobId = qApp->sessionId() + "_" + QString::number( m_sessionNumber );
+ QDir appDir( locateLocal( "appdata", "temp/" ) );
+ if( appDir.cd( jobId ) ) {
+ QStringList dummyDirEntries = appDir.entryList( "dummydir*", QDir::Dirs );
+ for( QStringList::iterator it = dummyDirEntries.begin(); it != dummyDirEntries.end(); ++it )
+ appDir.rmdir( *it );
+ appDir.cdUp();
+ appDir.rmdir( jobId );
+ }
+}
+
+
+QCString K3bIsoImager::checksum() const
+{
+ if( K3bChecksumPipe* p = dynamic_cast<K3bChecksumPipe*>( d->pipe ) )
+ return p->checksum();
+ else
+ return QCString();
+}
+
+
+bool K3bIsoImager::hasBeenCanceled() const
+{
+ return m_canceled;
+}
+
+#include "k3bisoimager.moc"
diff --git a/libk3b/projects/datacd/k3bisoimager.h b/libk3b/projects/datacd/k3bisoimager.h
new file mode 100644
index 0000000..82501ba
--- /dev/null
+++ b/libk3b/projects/datacd/k3bisoimager.h
@@ -0,0 +1,188 @@
+/*
+ *
+ * $Id: k3bisoimager.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef K3B_ISO_IMAGER_H
+#define K3B_ISO_IMAGER_H
+
+#include <k3bjob.h>
+#include "k3bmkisofshandler.h"
+
+#include <qptrqueue.h>
+#include <qstringlist.h>
+
+class K3bDataDoc;
+class K3bDirItem;
+class K3bDataItem;
+class K3bFileItem;
+class QTextStream;
+class K3bProcess;
+class KProcess;
+class K3bDevice::Device;
+class KTempFile;
+
+
+class K3bIsoImager : public K3bJob, public K3bMkisofsHandler
+{
+ Q_OBJECT
+
+ public:
+ K3bIsoImager( K3bDataDoc*, K3bJobHandler*, QObject* parent = 0, const char* name = 0 );
+ virtual ~K3bIsoImager();
+
+ virtual bool active() const;
+
+ int size() const { return m_mkisofsPrintSizeResult; }
+
+ virtual bool hasBeenCanceled() const;
+
+ /**
+ * Get the checksum calculated during the creation of the image.
+ */
+ QCString checksum() const;
+
+ public slots:
+ /**
+ * Starts the actual image creation. Always run init()
+ * before starting the image creation
+ */
+ virtual void start();
+ virtual void cancel();
+
+ /**
+ * Initialize the image creator. This calculates the image size and performs
+ * some checks on the project.
+ *
+ * The initialization process also finishes with the finished() signal just
+ * like a normal job operation. Get the calculated image size via size()
+ */
+ virtual void init();
+
+ /**
+ * Only calculates the size of the image without the additional checks in
+ * init()
+ *
+ * Use this if you need to recalculate the image size for example if the
+ * multisession info changed.
+ */
+ virtual void calculateSize();
+
+ /**
+ * lets the isoimager write directly into fd instead of writing
+ * to an image file.
+ * Be aware that this only makes sense before starting the job.
+ * To disable just set @p fd to -1
+ */
+ void writeToFd( int fd );
+
+ void writeToImageFile( const QString& path );
+
+ /**
+ * If dev == 0 K3bIsoImager will ignore the data in the previous session.
+ * This is usable for CD-Extra.
+ */
+ void setMultiSessionInfo( const QString&, K3bDevice::Device* dev = 0 );
+
+ K3bDevice::Device* device() const { return m_device; }
+ K3bDataDoc* doc() const { return m_doc; }
+
+ protected:
+ virtual void handleMkisofsProgress( int );
+ virtual void handleMkisofsInfoMessage( const QString&, int );
+
+ virtual bool addMkisofsParameters( bool printSize = false );
+
+ /**
+ * calls writePathSpec, writeRRHideFile, and writeJolietHideFile
+ */
+ bool prepareMkisofsFiles();
+
+ /**
+ * The dummy dir is used to create dirs on the iso-filesystem.
+ *
+ * @return an empty dummy dir for use with K3bDirItems.
+ */
+ QString dummyDir( K3bDirItem* );
+
+ void outputData();
+ void initVariables();
+ virtual void cleanup();
+ void clearDummyDirs();
+
+ /**
+ * @returns The number of entries written or -1 on error
+ */
+ virtual int writePathSpec();
+ bool writeRRHideFile();
+ bool writeJolietHideFile();
+ bool writeSortWeightFile();
+
+ // used by writePathSpec
+ virtual int writePathSpecForDir( K3bDirItem* dirItem, QTextStream& stream );
+ virtual void writePathSpecForFile( K3bFileItem*, QTextStream& stream );
+ QString escapeGraftPoint( const QString& str );
+
+ KTempFile* m_pathSpecFile;
+ KTempFile* m_rrHideFile;
+ KTempFile* m_jolietHideFile;
+ KTempFile* m_sortWeightFile;
+
+ K3bProcess* m_process;
+
+ bool m_processExited;
+ bool m_canceled;
+
+ protected slots:
+ virtual void slotReceivedStderr( const QString& );
+ virtual void slotProcessExited( KProcess* );
+
+ private slots:
+ void slotCollectMkisofsPrintSizeStderr(KProcess*, char*, int);
+ void slotCollectMkisofsPrintSizeStdout( const QString& );
+ void slotMkisofsPrintSizeFinished();
+ void slotDataPreparationDone( bool success );
+
+ private:
+ void startSizeCalculation();
+
+ class Private;
+ Private* d;
+
+ K3bDataDoc* m_doc;
+
+ bool m_noDeepDirectoryRelocation;
+
+ bool m_importSession;
+ QString m_multiSessionInfo;
+ K3bDevice::Device* m_device;
+
+ // used for mkisofs -print-size parsing
+ QString m_collectedMkisofsPrintSizeStdout;
+ QString m_collectedMkisofsPrintSizeStderr;
+ int m_mkisofsPrintSizeResult;
+
+ QStringList m_tempFiles;
+
+ int m_fdToWriteTo;
+
+ bool m_containsFilesWithMultibleBackslashes;
+
+ // used to create a unique session id
+ static int s_imagerSessionCounter;
+
+ int m_sessionNumber;
+};
+
+
+#endif
diff --git a/libk3b/projects/datacd/k3bisooptions.cpp b/libk3b/projects/datacd/k3bisooptions.cpp
new file mode 100644
index 0000000..bd7314d
--- /dev/null
+++ b/libk3b/projects/datacd/k3bisooptions.cpp
@@ -0,0 +1,216 @@
+/*
+ *
+ * $Id: k3bisooptions.cpp 639665 2007-03-05 16:29:52Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bisooptions.h"
+#include <k3bcore.h>
+#include <k3bversion.h>
+#include <k3bglobals.h>
+
+#include <kconfig.h>
+#include <klocale.h>
+#include <qstring.h>
+
+
+K3bIsoOptions::K3bIsoOptions()
+ : m_volumeID( "K3b data project" ),
+ m_applicationID( QString("K3B THE CD KREATOR (C) 1998-2006 SEBASTIAN TRUEG AND THE K3B TEAM") ),
+ m_systemId( K3b::systemName().upper() ),
+ m_inputCharset( "iso8859-1" ),
+ m_whiteSpaceTreatmentReplaceString( "_" )
+{
+ m_bForceInputCharset = false;
+
+ m_createRockRidge = true;
+ m_createJoliet = true;
+ m_createUdf = false;
+ m_ISOallowLowercase = false;
+ m_ISOallowPeriodAtBegin = false;
+ m_ISOallow31charFilenames = true;
+ m_ISOomitVersionNumbers = false;
+ m_ISOomitTrailingPeriod = false;
+ m_ISOmaxFilenameLength = false;
+ m_ISOrelaxedFilenames = false;
+ m_ISOnoIsoTranslate = false;
+ m_ISOallowMultiDot = false;
+ m_ISOuntranslatedFilenames = false;
+ m_followSymbolicLinks = false;
+ m_createTRANS_TBL = false;
+ m_hideTRANS_TBL = false;
+ m_jolietLong = true;
+
+ m_doNotCacheInodes = true;
+
+ m_isoLevel = 2;
+
+ m_discardSymlinks = false;
+ m_discardBrokenSymlinks = false;
+
+ m_preserveFilePermissions = false;
+
+ m_whiteSpaceTreatment = noChange;
+
+ m_volumeSetSize = 1;
+ m_volumeSetNumber = 1;
+}
+
+
+void K3bIsoOptions::save( KConfigBase* c, bool saveVolumeDesc )
+{
+ if( saveVolumeDesc ) {
+ c->writeEntry( "volume id", m_volumeID );
+ c->writeEntry( "application id", m_applicationID );
+ c->writeEntry( "preparer", m_preparer );
+ c->writeEntry( "publisher", m_publisher );
+ c->writeEntry( "system id", m_systemId );
+ c->writeEntry( "volume set id", m_volumeSetId );
+ c->writeEntry( "volume set size", m_volumeSetSize );
+ c->writeEntry( "volume set number", m_volumeSetNumber );
+ c->writeEntry( "abstract file", m_abstractFile );
+ c->writeEntry( "copyright file", m_copyrightFile );
+ c->writeEntry( "bibliograph file", m_bibliographFile );
+ }
+
+ c->writeEntry( "rock_ridge", m_createRockRidge );
+ c->writeEntry( "joliet", m_createJoliet );
+ c->writeEntry( "udf", m_createUdf );
+
+ // save iso-level
+ c->writeEntry( "iso_level", m_isoLevel );
+
+ c->writeEntry( "create TRANS_TBL", m_createTRANS_TBL );
+ c->writeEntry( "hide TRANS_TBL", m_hideTRANS_TBL );
+ c->writeEntry( "untranslated filenames", m_ISOuntranslatedFilenames );
+ c->writeEntry( "allow 31 character filenames", m_ISOallow31charFilenames );
+ c->writeEntry( "max ISO filenames", m_ISOmaxFilenameLength );
+ c->writeEntry( "allow beginning period", m_ISOallowPeriodAtBegin );
+ c->writeEntry( "relaxed filenames", m_ISOrelaxedFilenames );
+ c->writeEntry( "omit version numbers", m_ISOomitVersionNumbers );
+ c->writeEntry( "omit trailing period", m_ISOomitTrailingPeriod );
+ c->writeEntry( "no iSO translation", m_ISOnoIsoTranslate );
+ c->writeEntry( "allow multiple dots", m_ISOallowMultiDot );
+ c->writeEntry( "allow lowercase filenames", m_ISOallowLowercase );
+ // c->writeEntry( "follow symbolic links", m_followSymbolicLinks );
+
+ c->writeEntry( "joliet long", m_jolietLong );
+
+ c->writeEntry( "force input charset", m_bForceInputCharset );
+ c->writeEntry( "input charset", m_inputCharset );
+
+ c->writeEntry( "do not cache inodes", m_doNotCacheInodes );
+
+ // save whitespace-treatment
+ switch( m_whiteSpaceTreatment ) {
+ case strip:
+ c->writeEntry( "white_space_treatment", "strip" );
+ break;
+ case extended:
+ c->writeEntry( "white_space_treatment", "extended" );
+ break;
+ case replace:
+ c->writeEntry( "white_space_treatment", "replace" );
+ break;
+ default:
+ c->writeEntry( "white_space_treatment", "noChange" );
+ }
+
+ c->writeEntry( "whitespace replace string", m_whiteSpaceTreatmentReplaceString );
+
+ c->writeEntry( "discard symlinks", discardSymlinks() );
+ c->writeEntry( "discard broken symlinks", discardBrokenSymlinks() );
+
+ c->writeEntry( "preserve file permissions", m_preserveFilePermissions );
+}
+
+
+K3bIsoOptions K3bIsoOptions::load( KConfigBase* c, bool loadVolumeDesc )
+{
+ K3bIsoOptions options;
+
+ if( loadVolumeDesc ) {
+ options.setVolumeID( c->readEntry( "volume id", options.volumeID() ) );
+ options.setApplicationID( c->readEntry( "application id", options.applicationID() ) );
+ options.setPreparer( c->readEntry( "preparer", options.preparer() ) );
+ options.setPublisher( c->readEntry( "publisher", options.publisher() ) );
+ options.setSystemId( c->readEntry( "system id", options.systemId() ) );
+ options.setVolumeSetId( c->readEntry( "volume set id", options.volumeSetId() ) );
+ options.setVolumeSetSize( c->readNumEntry( "volume set size", options.volumeSetSize() ) );
+ options.setVolumeSetNumber( c->readNumEntry( "volume set number", options.volumeSetNumber() ) );
+ options.setAbstractFile( c->readEntry( "abstract file", options.abstractFile() ) );
+ options.setCoprightFile( c->readEntry( "copyright file", options.copyrightFile() ) );
+ options.setBibliographFile( c->readEntry( "bibliograph file", options.bibliographFile() ) );
+ }
+
+ options.setForceInputCharset( c->readBoolEntry( "force input charset", options.forceInputCharset() ) );
+ if( options.forceInputCharset() )
+ options.setInputCharset( c->readEntry( "input charset", options.inputCharset() ) );
+
+ options.setCreateRockRidge( c->readBoolEntry( "rock_ridge", options.createRockRidge() ) );
+ options.setCreateJoliet( c->readBoolEntry( "joliet", options.createJoliet() ) );
+ options.setCreateUdf( c->readBoolEntry( "udf", options.createUdf() ) );
+
+ options.setISOLevel( c->readNumEntry( "iso_level", options.ISOLevel() ) );
+
+ options.setCreateTRANS_TBL( c->readBoolEntry( "create TRANS_TBL", options.createTRANS_TBL() ) );
+ options.setHideTRANS_TBL( c->readBoolEntry( "hide TRANS_TBL", options.hideTRANS_TBL() ) );
+
+ //
+ // We need to use the memeber variables here instead of the access methods
+ // which do not return the actual value of the member variables but the value
+ // representing the use in mkisofs (i.e. ISOomitVersionNumbers is also enabled
+ // if ISOmaxFilenameLength is enabled.
+ //
+ options.setISOuntranslatedFilenames( c->readBoolEntry( "untranslated filenames", options.m_ISOuntranslatedFilenames ) );
+ options.setISOallow31charFilenames( c->readBoolEntry( "allow 31 character filenames", options.m_ISOallow31charFilenames ) );
+ options.setISOmaxFilenameLength( c->readBoolEntry( "max ISO filenames", options.m_ISOmaxFilenameLength ) );
+ options.setISOallowPeriodAtBegin( c->readBoolEntry( "allow beginning period", options.m_ISOallowPeriodAtBegin ) );
+ options.setISOrelaxedFilenames( c->readBoolEntry( "relaxed filenames", options.m_ISOrelaxedFilenames ) );
+ options.setISOomitVersionNumbers( c->readBoolEntry( "omit version numbers", options.m_ISOomitVersionNumbers ) );
+ options.setISOnoIsoTranslate( c->readBoolEntry( "no iSO translation", options.m_ISOnoIsoTranslate ) );
+ options.setISOallowMultiDot( c->readBoolEntry( "allow multiple dots", options.m_ISOallowMultiDot ) );
+ options.setISOallowLowercase( c->readBoolEntry( "allow lowercase filenames", options.m_ISOallowLowercase ) );
+ options.setISOomitTrailingPeriod( c->readBoolEntry( "omit trailing period", options.m_ISOomitTrailingPeriod ) );
+
+ // options.setFollowSymbolicLinks( c->readBoolEntry( "follow symbolic links", options.m_followSymbolicLinks ) );
+
+ options.setJolietLong( c->readBoolEntry( "joliet long", options.jolietLong() ) );
+
+ options.setDoNotCacheInodes( c->readBoolEntry( "do not cache inodes", options.doNotCacheInodes() ) );
+
+ QString w = c->readEntry( "white_space_treatment", "noChange" );
+ if( w == "replace" )
+ options.setWhiteSpaceTreatment( replace );
+ else if( w == "strip" )
+ options.setWhiteSpaceTreatment( strip );
+ else if( w == "extended" )
+ options.setWhiteSpaceTreatment( extended );
+ else
+ options.setWhiteSpaceTreatment( noChange );
+
+ options.setWhiteSpaceTreatmentReplaceString( c->readEntry( "whitespace replace string", options.whiteSpaceTreatmentReplaceString() ) );
+
+ options.setDiscardSymlinks( c->readBoolEntry("discard symlinks", options.discardSymlinks() ) );
+ options.setDiscardBrokenSymlinks( c->readBoolEntry("discard broken symlinks", options.discardBrokenSymlinks() ) );
+
+ options.setPreserveFilePermissions( c->readBoolEntry( "preserve file permissions", options.preserveFilePermissions() ) );
+
+ return options;
+}
+
+
+K3bIsoOptions K3bIsoOptions::defaults()
+{
+ // let the constructor create defaults
+ return K3bIsoOptions();
+}
diff --git a/libk3b/projects/datacd/k3bisooptions.h b/libk3b/projects/datacd/k3bisooptions.h
new file mode 100644
index 0000000..254c998
--- /dev/null
+++ b/libk3b/projects/datacd/k3bisooptions.h
@@ -0,0 +1,183 @@
+/*
+ *
+ * $Id: k3bisooptions.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef K3B_ISO_OPTIONS_H
+#define K3B_ISO_OPTIONS_H
+
+#include <qstring.h>
+#include "k3b_export.h"
+
+class KConfigBase;
+
+
+class LIBK3B_EXPORT K3bIsoOptions
+{
+ public:
+ K3bIsoOptions();
+
+ bool forceInputCharset() const { return m_bForceInputCharset; }
+ const QString& inputCharset() const { return m_inputCharset; }
+
+ void setForceInputCharset( bool b ) { m_bForceInputCharset = b; }
+ void setInputCharset( const QString& cs ) { m_inputCharset = cs; }
+
+
+ // -- mkisofs-options ----------------------------------------------------------------------
+ bool createRockRidge() const { return m_createRockRidge; }
+ bool createJoliet() const { return m_createJoliet; }
+ bool createUdf() const { return m_createUdf; }
+ bool ISOallowLowercase() const { return m_ISOallowLowercase || ISOuntranslatedFilenames(); }
+ bool ISOallowPeriodAtBegin() const { return m_ISOallowPeriodAtBegin || ISOuntranslatedFilenames(); }
+ bool ISOallow31charFilenames() const { return m_ISOallow31charFilenames || ISOmaxFilenameLength() || ISOuntranslatedFilenames(); }
+ bool ISOomitVersionNumbers() const { return m_ISOomitVersionNumbers || ISOmaxFilenameLength(); }
+ bool ISOomitTrailingPeriod() const { return m_ISOomitTrailingPeriod || ISOuntranslatedFilenames(); }
+ bool ISOmaxFilenameLength() const { return m_ISOmaxFilenameLength || ISOuntranslatedFilenames(); }
+ bool ISOrelaxedFilenames() const { return m_ISOrelaxedFilenames || ISOuntranslatedFilenames(); }
+ bool ISOnoIsoTranslate() const { return m_ISOnoIsoTranslate; }
+ bool ISOallowMultiDot() const { return m_ISOallowMultiDot || ISOuntranslatedFilenames(); }
+ bool ISOuntranslatedFilenames() const { return m_ISOuntranslatedFilenames; }
+ bool followSymbolicLinks() const { return m_followSymbolicLinks; }
+ bool createTRANS_TBL() const { return m_createTRANS_TBL; }
+ bool hideTRANS_TBL() const { return m_hideTRANS_TBL; }
+ bool jolietLong() const { return m_jolietLong; }
+
+ bool preserveFilePermissions() const { return m_preserveFilePermissions; }
+
+ int ISOLevel() const { return m_isoLevel; }
+ const QString& systemId() const { return m_systemId; }
+ const QString& applicationID() const { return m_applicationID; }
+ const QString& volumeID() const { return m_volumeID; }
+ const QString& volumeSetId() const { return m_volumeSetId; }
+ int volumeSetSize() const { return m_volumeSetSize; }
+ int volumeSetNumber() const { return m_volumeSetNumber; }
+ const QString& publisher() const { return m_publisher; }
+ const QString& preparer() const { return m_preparer; }
+ const QString& abstractFile() const { return m_abstractFile; }
+ const QString& copyrightFile() const { return m_copyrightFile; }
+ const QString& bibliographFile() const { return m_bibliographFile; }
+
+ void setCreateRockRidge( bool b ) { m_createRockRidge = b; }
+ void setCreateJoliet( bool b ) { m_createJoliet = b; }
+ void setCreateUdf( bool b ) { m_createUdf = b; }
+ void setISOallowLowercase( bool b ) { m_ISOallowLowercase = b; }
+ void setISOallowPeriodAtBegin( bool b ) { m_ISOallowPeriodAtBegin = b; }
+ void setISOallow31charFilenames( bool b ) { m_ISOallow31charFilenames = b; }
+ void setISOomitVersionNumbers( bool b ) { m_ISOomitVersionNumbers = b; }
+ void setISOomitTrailingPeriod( bool b ) { m_ISOomitTrailingPeriod = b; }
+ void setISOmaxFilenameLength( bool b ) { m_ISOmaxFilenameLength = b; }
+ void setISOrelaxedFilenames( bool b ) { m_ISOrelaxedFilenames = b; }
+ void setISOnoIsoTranslate( bool b ) { m_ISOnoIsoTranslate = b; }
+ void setISOallowMultiDot( bool b ) { m_ISOallowMultiDot = b; }
+ void setISOuntranslatedFilenames( bool b ) { m_ISOuntranslatedFilenames = b; }
+ void setFollowSymbolicLinks( bool b ) { m_followSymbolicLinks = b; }
+ void setCreateTRANS_TBL( bool b ) { m_createTRANS_TBL = b; }
+ void setHideTRANS_TBL( bool b ) { m_hideTRANS_TBL = b; }
+ void setJolietLong( bool b ) { m_jolietLong = b; }
+
+ void setISOLevel( int i ) { m_isoLevel = i; }
+ void setSystemId( const QString& s ) { m_systemId = s; }
+ void setApplicationID( const QString& s ) { m_applicationID = s; }
+
+ /**
+ * Set the filesystems volume id.
+ *
+ * max length for this field is 32 chars.
+ */
+ void setVolumeID( const QString& s ) { m_volumeID = s; }
+ void setVolumeSetId( const QString& s ) { m_volumeSetId = s; }
+ void setVolumeSetSize( int size ) { m_volumeSetSize = size; }
+ void setVolumeSetNumber( int n ) { m_volumeSetNumber = n; }
+ void setPublisher( const QString& s ) { m_publisher = s; }
+ void setPreparer( const QString& s ) { m_preparer = s; }
+ void setAbstractFile( const QString& s ) { m_abstractFile = s; }
+ void setCoprightFile( const QString& s ) { m_copyrightFile = s; }
+ void setBibliographFile( const QString& s ) { m_bibliographFile = s; }
+
+ void setPreserveFilePermissions( bool b ) { m_preserveFilePermissions = b; }
+ // ----------------------------------------------------------------- mkisofs-options -----------
+
+ enum whiteSpaceTreatments { noChange = 0, replace = 1, strip = 2, extended = 3 };
+
+ void setWhiteSpaceTreatment( int i ) { m_whiteSpaceTreatment = i; }
+ int whiteSpaceTreatment() const { return m_whiteSpaceTreatment; }
+ const QString& whiteSpaceTreatmentReplaceString() const { return m_whiteSpaceTreatmentReplaceString; }
+ void setWhiteSpaceTreatmentReplaceString( const QString& s ) { m_whiteSpaceTreatmentReplaceString = s; }
+
+ bool discardSymlinks() const { return m_discardSymlinks; }
+ void setDiscardSymlinks( bool b ) { m_discardSymlinks = b; }
+
+ bool discardBrokenSymlinks() const { return m_discardBrokenSymlinks; }
+ void setDiscardBrokenSymlinks( bool b ) { m_discardBrokenSymlinks = b; }
+
+ bool doNotCacheInodes() const { return m_doNotCacheInodes; }
+ void setDoNotCacheInodes( bool b ) { m_doNotCacheInodes = b; }
+
+ void save( KConfigBase* c, bool saveVolumeDesc = true );
+
+ static K3bIsoOptions load( KConfigBase* c, bool loadVolumeDesc = true );
+ static K3bIsoOptions defaults();
+
+ private:
+ // volume descriptor
+ QString m_volumeID;
+ QString m_applicationID;
+ QString m_preparer;
+ QString m_publisher;
+ QString m_systemId;
+ QString m_volumeSetId;
+ QString m_abstractFile;
+ QString m_copyrightFile;
+ QString m_bibliographFile;
+
+ int m_volumeSetSize;
+ int m_volumeSetNumber;
+
+ bool m_bForceInputCharset;
+ QString m_inputCharset;
+
+ // mkisofs options -------------------------------------
+ bool m_createRockRidge; // -r or -R
+ bool m_createJoliet; // -J
+ bool m_createUdf; // -udf
+ bool m_ISOallowLowercase; // -allow-lowercase
+ bool m_ISOallowPeriodAtBegin; // -L
+ bool m_ISOallow31charFilenames; // -I
+ bool m_ISOomitVersionNumbers; // -N
+ bool m_ISOomitTrailingPeriod; // -d
+ bool m_ISOmaxFilenameLength; // -max-iso9660-filenames (forces -N)
+ bool m_ISOrelaxedFilenames; // -relaxed-filenames
+ bool m_ISOnoIsoTranslate; // -no-iso-translate
+ bool m_ISOallowMultiDot; // -allow-multidot
+ bool m_ISOuntranslatedFilenames; // -U (forces -d, -I, -L, -N, -relaxed-filenames, -allow-lowercase, -allow-multidot, -no-iso-translate)
+ bool m_followSymbolicLinks; // -f
+ bool m_createTRANS_TBL; // -T
+ bool m_hideTRANS_TBL; // -hide-joliet-trans-tbl
+
+ bool m_preserveFilePermissions; // if true -R instead of -r is used
+ bool m_jolietLong;
+
+ bool m_doNotCacheInodes;
+
+ int m_isoLevel;
+
+
+ int m_whiteSpaceTreatment;
+ QString m_whiteSpaceTreatmentReplaceString;
+
+ bool m_discardSymlinks;
+ bool m_discardBrokenSymlinks;
+};
+
+#endif
diff --git a/libk3b/projects/datacd/k3bmkisofshandler.cpp b/libk3b/projects/datacd/k3bmkisofshandler.cpp
new file mode 100644
index 0000000..a3579ec
--- /dev/null
+++ b/libk3b/projects/datacd/k3bmkisofshandler.cpp
@@ -0,0 +1,150 @@
+/*
+ *
+ * $Id: k3bmkisofshandler.cpp 802340 2008-04-29 07:43:07Z trueg $
+ * Copyright (C) 2005 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bmkisofshandler.h"
+
+#include <k3bexternalbinmanager.h>
+#include <k3bcore.h>
+#include <k3bjob.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <cmath>
+
+
+
+class K3bMkisofsHandler::Private
+{
+public:
+ const K3bExternalBin* mkisofsBin;
+ double firstProgressValue;
+ bool readError;
+};
+
+
+K3bMkisofsHandler::K3bMkisofsHandler()
+{
+ d = new Private;
+ d->mkisofsBin = 0;
+}
+
+
+K3bMkisofsHandler::~K3bMkisofsHandler()
+{
+ delete d;
+}
+
+
+bool K3bMkisofsHandler::mkisofsReadError() const
+{
+ return d->readError;
+}
+
+
+const K3bExternalBin* K3bMkisofsHandler::initMkisofs()
+{
+ d->mkisofsBin = k3bcore->externalBinManager()->binObject( "mkisofs" );
+
+ if( d->mkisofsBin ) {
+ if( !d->mkisofsBin->copyright.isEmpty() )
+ handleMkisofsInfoMessage( i18n("Using %1 %2 - Copyright (C) %3")
+ .arg("mkisofs").arg(d->mkisofsBin->version).arg(d->mkisofsBin->copyright),
+ K3bJob::INFO );
+
+ d->firstProgressValue = -1;
+ d->readError = false;
+ }
+ else {
+ kdDebug() << "(K3bMkisofsHandler) could not find mkisofs executable" << endl;
+ handleMkisofsInfoMessage( i18n("Mkisofs executable not found."), K3bJob::ERROR );
+ }
+
+ return d->mkisofsBin;
+}
+
+
+void K3bMkisofsHandler::parseMkisofsOutput( const QString& line )
+{
+ if( !line.isEmpty() ) {
+ if( line.startsWith( d->mkisofsBin->path ) ) {
+ // error or warning
+ QString errorLine = line.mid( d->mkisofsBin->path.length() + 2 );
+ if( errorLine.startsWith( "Input/output error. Cannot read from" ) ) {
+ handleMkisofsInfoMessage( i18n("Read error from file '%1'").arg( errorLine.mid( 38, errorLine.length()-40 ) ),
+ K3bJob::ERROR );
+ d->readError = true;
+ }
+ else if( errorLine.startsWith( "Value too large for defined data type" ) ) {
+ handleMkisofsInfoMessage( i18n("Used version of mkisofs does not have large file support."), K3bJob::ERROR );
+ handleMkisofsInfoMessage( i18n("Files bigger than 2 GB cannot be handled."), K3bJob::ERROR );
+ d->readError = true;
+ }
+ }
+ else if( line.contains( "done, estimate" ) ) {
+ int p = parseMkisofsProgress( line );
+ if( p != -1 )
+ handleMkisofsProgress( p );
+ }
+ else if( line.contains( "extents written" ) ) {
+ handleMkisofsProgress( 100 );
+ }
+ else if( line.startsWith( "Incorrectly encoded string" ) ) {
+ handleMkisofsInfoMessage( i18n("Encountered an incorrectly encoded filename '%1'")
+ .arg(line.section( QRegExp("[\\(\\)]"), 1, 1 )), K3bJob::ERROR );
+ handleMkisofsInfoMessage( i18n("This may be caused by a system update which changed the local character set."), K3bJob::ERROR );
+ handleMkisofsInfoMessage( i18n("You may use convmv (http://j3e.de/linux/convmv/) to fix the filename encoding."), K3bJob::ERROR );
+ d->readError = true;
+ }
+ else if( line.endsWith( "has not an allowable size." ) ) {
+ handleMkisofsInfoMessage( i18n("The boot image has an invalid size."), K3bJob::ERROR );
+ d->readError = true;
+ }
+ else if( line.endsWith( "has multiple partitions." ) ) {
+ handleMkisofsInfoMessage( i18n("The boot image contains multiple partitions.."), K3bJob::ERROR );
+ handleMkisofsInfoMessage( i18n("A hard-disk boot image has to contain a single partition."), K3bJob::ERROR );
+ d->readError = true;
+ }
+ else {
+ kdDebug() << "(mkisofs) " << line << endl;
+ }
+ }
+}
+
+
+int K3bMkisofsHandler::parseMkisofsProgress( const QString& line )
+{
+ //
+ // in multisession mode mkisofs' progress does not start at 0 but at (X+Y)/X
+ // where X is the data already on the cd and Y the data to create
+ // This is not very dramatic but kind or ugly.
+ // We just save the first emitted progress value and to some math ;)
+ //
+
+ QString perStr = line;
+ perStr.truncate( perStr.find('%') );
+ bool ok;
+ double p = perStr.toDouble( &ok );
+ if( !ok ) {
+ kdDebug() << "(K3bMkisofsHandler) Parsing did not work for " << perStr << endl;
+ return -1;
+ }
+ else {
+ if( d->firstProgressValue < 0 )
+ d->firstProgressValue = p;
+
+ return( (int)::ceil( (p - d->firstProgressValue)*100.0/(100.0 - d->firstProgressValue) ) );
+ }
+}
diff --git a/libk3b/projects/datacd/k3bmkisofshandler.h b/libk3b/projects/datacd/k3bmkisofshandler.h
new file mode 100644
index 0000000..32576bc
--- /dev/null
+++ b/libk3b/projects/datacd/k3bmkisofshandler.h
@@ -0,0 +1,74 @@
+/*
+ *
+ * $Id: k3bmkisofshandler.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2005 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_MKISOfS_HANDLER_H_
+#define _K3B_MKISOfS_HANDLER_H_
+
+#include <qstring.h>
+
+class K3bExternalBin;
+
+
+/**
+ * Derive from this to handle mkisofs.
+ */
+class K3bMkisofsHandler
+{
+ public:
+ K3bMkisofsHandler();
+ virtual ~K3bMkisofsHandler();
+
+ /**
+ * \return true if there was a read error.
+ */
+ bool mkisofsReadError() const;
+
+ protected:
+ /**
+ * Initialize the MkisofsHandler.
+ * This method emits copyright information and an error message in case mkisofs is not installed
+ * through handleMkisofsInfoMessage.
+ *
+ * \return A mkisofs bin object to be used or 0 if mkisofs is not installed.
+ */
+ const K3bExternalBin* initMkisofs();
+
+ void parseMkisofsOutput( const QString& line );
+
+ /**
+ * Used internally by handleMkisofsOutput.
+ * May be used in case handleMkisofsOutput is not sufficient.
+ */
+ int parseMkisofsProgress( const QString& line );
+
+ /**
+ * Called by handleMkisofsOutput
+ */
+ virtual void handleMkisofsProgress( int ) = 0;
+
+ /**
+ * Called by handleMkisofsOutput
+ *
+ * Uses K3bJob::MessageType
+ */
+ virtual void handleMkisofsInfoMessage( const QString&, int ) = 0;
+
+ private:
+ class Private;
+ Private* d;
+};
+
+
+#endif
diff --git a/libk3b/projects/datacd/k3bmsinfofetcher.cpp b/libk3b/projects/datacd/k3bmsinfofetcher.cpp
new file mode 100644
index 0000000..c30d0ff
--- /dev/null
+++ b/libk3b/projects/datacd/k3bmsinfofetcher.cpp
@@ -0,0 +1,243 @@
+/*
+ *
+ * $Id: k3bmsinfofetcher.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bmsinfofetcher.h"
+
+#include <k3bexternalbinmanager.h>
+#include <k3bdevicemanager.h>
+#include <k3bdevicehandler.h>
+#include <k3bdevice.h>
+#include <k3bcore.h>
+#include <k3bglobals.h>
+#include <k3biso9660.h>
+
+#include <klocale.h>
+#include <kprocess.h>
+#include <kdebug.h>
+
+#include <qstringlist.h>
+
+
+K3bMsInfoFetcher::K3bMsInfoFetcher( K3bJobHandler* jh, QObject* parent, const char* name )
+ : K3bJob( jh, parent, name ),
+ m_process(0),
+ m_device(0),
+ m_dvd(false)
+{
+}
+
+
+K3bMsInfoFetcher::~K3bMsInfoFetcher()
+{
+ delete m_process;
+}
+
+
+void K3bMsInfoFetcher::start()
+{
+ jobStarted();
+
+ emit infoMessage( i18n("Searching previous session"), K3bJob::INFO );
+
+ if( !k3bcore->externalBinManager()->foundBin( "cdrecord" ) ) {
+ kdDebug() << "(K3bMsInfoFetcher) could not find cdrecord executable" << endl;
+ emit infoMessage( i18n("Could not find %1 executable.").arg("cdrecord"), K3bJob::ERROR );
+ jobFinished(false);
+ return;
+ }
+
+ if( m_device == 0 ) {
+ kdDebug() << "(K3bMsInfoFetcher) internal error: No device set!" << endl;
+ jobFinished(false);
+ return;
+ }
+
+ //
+ // first we try to determine if it is a dvd. If so we need to
+ // read the info on our own
+ //
+
+ connect( K3bDevice::sendCommand( K3bDevice::DeviceHandler::NG_DISKINFO, m_device ),
+ SIGNAL(finished(K3bDevice::DeviceHandler*)),
+ this,
+ SLOT(slotMediaDetectionFinished(K3bDevice::DeviceHandler*)) );
+}
+
+
+void K3bMsInfoFetcher::getMsInfo()
+{
+ delete m_process;
+ m_process = new KProcess();
+
+ const K3bExternalBin* bin = 0;
+ if( m_dvd ) {
+ // already handled
+ }
+ else {
+ bin = k3bcore->externalBinManager()->binObject( "cdrecord" );
+
+ if( !bin ) {
+ emit infoMessage( i18n("Could not find %1 executable.").arg( m_dvd ? "dvdrecord" : "cdrecord" ), ERROR );
+ jobFinished(false);
+ return;
+ }
+
+ *m_process << bin->path;
+
+ // add the device (e.g. /dev/sg1)
+ *m_process << QString("dev=%1").arg( K3b::externalBinDeviceParameter(m_device, bin) );
+
+ *m_process << "-msinfo";
+
+ // additional user parameters from config
+ const QStringList& params = bin->userParameters();
+ for( QStringList::const_iterator it = params.begin(); it != params.end(); ++it )
+ *m_process << *it;
+
+ kdDebug() << "***** " << bin->name() << " parameters:\n";
+ const QValueList<QCString>& args = m_process->args();
+ QString s;
+ for( QValueList<QCString>::const_iterator it = args.begin(); it != args.end(); ++it ) {
+ s += *it + " ";
+ }
+ kdDebug() << s << flush << endl;
+ emit debuggingOutput( "msinfo command:", s );
+
+
+ // connect( m_process, SIGNAL(receivedStderr(KProcess*, char*, int)),
+ // this, SLOT(slotCollectOutput(KProcess*, char*, int)) );
+ connect( m_process, SIGNAL(receivedStdout(KProcess*, char*, int)),
+ this, SLOT(slotCollectOutput(KProcess*, char*, int)) );
+ connect( m_process, SIGNAL(processExited(KProcess*)),
+ this, SLOT(slotProcessExited()) );
+
+ m_msInfo = QString::null;
+ m_collectedOutput = QString::null;
+ m_canceled = false;
+
+ if( !m_process->start( KProcess::NotifyOnExit, KProcess::AllOutput ) ) {
+ emit infoMessage( i18n("Could not start %1.").arg(bin->name()), K3bJob::ERROR );
+ jobFinished(false);
+ }
+ }
+}
+
+
+void K3bMsInfoFetcher::slotMediaDetectionFinished( K3bDevice::DeviceHandler* h )
+{
+ if( h->success() ) {
+ m_dvd = h->diskInfo().isDvdMedia();
+ }
+ else {
+ // for now we just default to cd and go on with the detecting
+ m_dvd = false;
+ }
+
+ if( m_dvd ) {
+ if( h->diskInfo().mediaType() & (K3bDevice::MEDIA_DVD_PLUS_RW|K3bDevice::MEDIA_DVD_RW_OVWR) ) {
+ // get info from iso filesystem
+ K3bIso9660 iso( m_device, h->toc().last().firstSector().lba() );
+ if( iso.open() ) {
+ unsigned long long nextSession = iso.primaryDescriptor().volumeSpaceSize;
+ // pad to closest 32K boundary
+ nextSession += 15;
+ nextSession /= 16;
+ nextSession *= 16;
+ m_msInfo.sprintf( "16,%llu", nextSession );
+
+ jobFinished( true );
+ }
+ else {
+ emit infoMessage( i18n("Could not open Iso9660 filesystem in %1.")
+ .arg( m_device->vendor() + " " + m_device->description() ), ERROR );
+ jobFinished( false );
+ }
+ }
+ else {
+ unsigned int lastSessionStart, nextWritableAdress;
+ if( m_device->getNextWritableAdress( lastSessionStart, nextWritableAdress ) ) {
+ m_msInfo.sprintf( "%u,%u", lastSessionStart+16, nextWritableAdress );
+ jobFinished( true );
+ }
+ else {
+ emit infoMessage( i18n("Could not determine next writable address."), ERROR );
+ jobFinished( false );
+ }
+ }
+ }
+ else // call cdrecord
+ getMsInfo();
+}
+
+
+void K3bMsInfoFetcher::slotProcessExited()
+{
+ if( m_canceled )
+ return;
+
+ kdDebug() << "(K3bMsInfoFetcher) msinfo fetched" << endl;
+
+ // now parse the output
+ QString firstLine = m_collectedOutput.left( m_collectedOutput.find("\n") );
+ QStringList list = QStringList::split( ",", firstLine );
+ if( list.count() == 2 ) {
+ bool ok1, ok2;
+ m_lastSessionStart = list.first().toInt( &ok1 );
+ m_nextSessionStart = list[1].toInt( &ok2 );
+ if( ok1 && ok2 )
+ m_msInfo = firstLine.stripWhiteSpace();
+ else
+ m_msInfo = QString::null;
+ }
+ else {
+ m_msInfo = QString::null;
+ }
+
+ kdDebug() << "(K3bMsInfoFetcher) msinfo parsed: " << m_msInfo << endl;
+
+ if( m_msInfo.isEmpty() ) {
+ emit infoMessage( i18n("Could not retrieve multisession information from disk."), K3bJob::ERROR );
+ emit infoMessage( i18n("The disk is either empty or not appendable."), K3bJob::ERROR );
+ jobFinished(false);
+ }
+ else {
+ jobFinished(true);
+ }
+}
+
+
+void K3bMsInfoFetcher::slotCollectOutput( KProcess*, char* output, int len )
+{
+ emit debuggingOutput( "msinfo", QString::fromLocal8Bit( output, len ) );
+
+ m_collectedOutput += QString::fromLocal8Bit( output, len );
+}
+
+
+void K3bMsInfoFetcher::cancel()
+{
+ // FIXME: this does not work if the devicehandler is running
+
+ if( m_process )
+ if( m_process->isRunning() ) {
+ m_canceled = true;
+ m_process->kill();
+ emit canceled();
+ jobFinished(false);
+ }
+}
+
+
+#include "k3bmsinfofetcher.moc"
diff --git a/libk3b/projects/datacd/k3bmsinfofetcher.h b/libk3b/projects/datacd/k3bmsinfofetcher.h
new file mode 100644
index 0000000..593664f
--- /dev/null
+++ b/libk3b/projects/datacd/k3bmsinfofetcher.h
@@ -0,0 +1,64 @@
+/*
+ *
+ * $Id: k3bmsinfofetcher.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef K3B_MSINFO_FETCHER_H
+#define K3B_MSINFO_FETCHER_H
+
+#include <k3bjob.h>
+
+namespace K3bDevice {
+ class Device;
+ class DeviceHandler;
+}
+class KProcess;
+
+class K3bMsInfoFetcher : public K3bJob
+{
+ Q_OBJECT
+
+ public:
+ K3bMsInfoFetcher( K3bJobHandler*, QObject* parent = 0, const char* name = 0 );
+ ~K3bMsInfoFetcher();
+
+ const QString& msInfo() const { return m_msInfo; }
+ int lastSessionStart() const { return m_lastSessionStart; }
+ int nextSessionStart() const { return m_nextSessionStart; }
+
+ public slots:
+ void start();
+ void cancel();
+
+ void setDevice( K3bDevice::Device* dev ) { m_device = dev; }
+
+ private slots:
+ void slotProcessExited();
+ void slotCollectOutput( KProcess*, char* output, int len );
+ void slotMediaDetectionFinished( K3bDevice::DeviceHandler* );
+ void getMsInfo();
+
+ private:
+ QString m_msInfo;
+ int m_lastSessionStart;
+ int m_nextSessionStart;
+ QString m_collectedOutput;
+
+ KProcess* m_process;
+ K3bDevice::Device* m_device;
+
+ bool m_canceled;
+ bool m_dvd;
+};
+
+#endif
diff --git a/libk3b/projects/datacd/k3bsessionimportitem.cpp b/libk3b/projects/datacd/k3bsessionimportitem.cpp
new file mode 100644
index 0000000..35f7936
--- /dev/null
+++ b/libk3b/projects/datacd/k3bsessionimportitem.cpp
@@ -0,0 +1,59 @@
+/*
+ *
+ * $Id: k3bsessionimportitem.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bsessionimportitem.h"
+#include "k3bfileitem.h"
+#include "k3bdiritem.h"
+
+#include <k3biso9660.h>
+
+
+K3bSessionImportItem::K3bSessionImportItem( const K3bIso9660File* isoF, K3bDataDoc* doc, K3bDirItem* dir )
+ : K3bDataItem( doc, dir ),
+ m_replaceItem(0),
+ m_size( isoF->size() )
+
+{
+ setK3bName( isoF->name() );
+
+ // add automagically like a qlistviewitem
+ if( parent() )
+ parent()->addDataItem( this );
+}
+
+
+K3bSessionImportItem::K3bSessionImportItem( const K3bSessionImportItem& item )
+ : K3bDataItem( item ),
+ m_replaceItem( item.m_replaceItem ),
+ m_size( item.m_size )
+{
+}
+
+
+K3bSessionImportItem::~K3bSessionImportItem()
+{
+ if( m_replaceItem )
+ m_replaceItem->setReplacedItemFromOldSession(0);
+
+ // remove this from parentdir
+ if( parent() )
+ parent()->takeDataItem( this );
+}
+
+
+K3bDataItem* K3bSessionImportItem::copy() const
+{
+ return new K3bSessionImportItem( *this );
+}
diff --git a/libk3b/projects/datacd/k3bsessionimportitem.h b/libk3b/projects/datacd/k3bsessionimportitem.h
new file mode 100644
index 0000000..33f8124
--- /dev/null
+++ b/libk3b/projects/datacd/k3bsessionimportitem.h
@@ -0,0 +1,63 @@
+/*
+ *
+ * $Id: k3bsessionimportitem.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_SESSION_IMPORT_ITEM_H_
+#define _K3B_SESSION_IMPORT_ITEM_H_
+
+
+#include "k3bdataitem.h"
+
+
+class K3bDataDoc;
+class K3bFileItem;
+class K3bDirItem;
+class K3bIso9660File;
+
+
+class K3bSessionImportItem : public K3bDataItem
+{
+ public:
+ K3bSessionImportItem( const K3bIso9660File*, K3bDataDoc* doc, K3bDirItem* );
+ K3bSessionImportItem( const K3bSessionImportItem& );
+ ~K3bSessionImportItem();
+
+ K3bDataItem* copy() const;
+
+ K3bFileItem* replaceItem() const { return m_replaceItem; }
+ void setReplaceItem( K3bFileItem* item ) { m_replaceItem = item; }
+
+ bool isFile() const { return false; }
+ bool isFromOldSession() const { return true; }
+
+ bool isRemoveable() const { return false; }
+ bool isMoveable() const { return false; }
+ bool isRenameable() const { return false; }
+ bool isHideable() const { return false; }
+ bool writeToCd() const { return false; }
+
+ protected:
+ // the size of an item from an imported session does not depend
+ // on the value of followSymlinks
+ /**
+ * Normally one does not use this method but K3bDataItem::size()
+ */
+ KIO::filesize_t itemSize( bool ) const { return m_size; }
+
+ private:
+ K3bFileItem* m_replaceItem;
+ KIO::filesize_t m_size;
+};
+
+#endif
diff --git a/libk3b/projects/datacd/k3bspecialdataitem.h b/libk3b/projects/datacd/k3bspecialdataitem.h
new file mode 100644
index 0000000..05005ed
--- /dev/null
+++ b/libk3b/projects/datacd/k3bspecialdataitem.h
@@ -0,0 +1,76 @@
+/*
+ *
+ * $Id: k3bspecialdataitem.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BSPECIALDATAITEM_H
+#define K3BSPECIALDATAITEM_H
+
+#include "k3bdataitem.h"
+#include "k3bdiritem.h"
+
+#include <kio/global.h>
+
+/**
+ * This can be used to create fake items like the boot catalog
+ * It's mainly a K3bDataItem where everything has to be set manually
+ */
+class K3bSpecialDataItem : public K3bDataItem
+{
+ public:
+ K3bSpecialDataItem( K3bDataDoc* doc, KIO::filesize_t size, K3bDirItem* parent = 0, const QString& k3bName = QString::null )
+ : K3bDataItem( doc, parent ),
+ m_size( size )
+ {
+ setK3bName( k3bName );
+
+ // add automagically like a qlistviewitem
+ if( parent )
+ parent->addDataItem( this );
+ }
+
+ K3bSpecialDataItem( const K3bSpecialDataItem& item )
+ : K3bDataItem( item ),
+ m_mimeType( item.m_mimeType ),
+ m_size( item.m_size ) {
+ }
+
+ ~K3bSpecialDataItem() {
+ // remove this from parentdir
+ if( parent() )
+ parent()->takeDataItem( this );
+ }
+
+ K3bDataItem* copy() const {
+ return new K3bSpecialDataItem( *this );
+ }
+
+ void setMimeType( const QString& s ) { m_mimeType = s; }
+ const QString& mimeType() const { return m_mimeType; }
+
+ bool isSpecialFile() const { return true; }
+
+ protected:
+ /**
+ * Normally one does not use this method but K3bDataItem::size()
+ */
+ KIO::filesize_t itemSize( bool ) const { return m_size; }
+
+ private:
+ QString m_mimeType;
+ KIO::filesize_t m_size;
+};
+
+#endif
+
diff --git a/libk3b/projects/datadvd/Makefile.am b/libk3b/projects/datadvd/Makefile.am
new file mode 100644
index 0000000..99ae10c
--- /dev/null
+++ b/libk3b/projects/datadvd/Makefile.am
@@ -0,0 +1,21 @@
+# we need the ../datacd for the uic generated header files
+AM_CPPFLAGS= -I$(srcdir)/../../core \
+ -I$(srcdir)/../../../libk3bdevice \
+ -I$(srcdir)/../../../src \
+ -I$(srcdir)/../../tools \
+ -I$(srcdir)/../../jobs \
+ -I$(srcdir)/../datacd \
+ -I$(srcdir)/.. \
+ -I../datacd \
+ $(all_includes)
+
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libdvd.la
+
+libdvd_la_SOURCES = k3bdvddoc.cpp \
+ k3bdvdjob.cpp \
+ k3bdvdbooktypejob.cpp
+
+include_HEADERS = k3bdvddoc.h \
+ k3bdvdjob.h
diff --git a/libk3b/projects/datadvd/k3bdvdbooktypejob.cpp b/libk3b/projects/datadvd/k3bdvdbooktypejob.cpp
new file mode 100644
index 0000000..f703452
--- /dev/null
+++ b/libk3b/projects/datadvd/k3bdvdbooktypejob.cpp
@@ -0,0 +1,350 @@
+/*
+ *
+ * $Id: k3bdvdbooktypejob.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bdvdbooktypejob.h"
+
+#include <k3bglobals.h>
+#include <k3bprocess.h>
+#include <k3bdevice.h>
+#include <k3bdeviceglobals.h>
+#include <k3bdevicehandler.h>
+#include <k3bdiskinfo.h>
+#include <k3bexternalbinmanager.h>
+#include <k3bcore.h>
+#include <k3bversion.h>
+#include <k3bglobalsettings.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <qvaluelist.h>
+#include <qregexp.h>
+
+#include <errno.h>
+#include <string.h>
+
+
+class K3bDvdBooktypeJob::Private
+{
+public:
+ Private()
+ : device(0),
+ process(0),
+ dvdBooktypeBin(0),
+ running(false),
+ forceNoEject(false) {
+ }
+
+ K3bDevice::Device* device;
+ K3bProcess* process;
+ const K3bExternalBin* dvdBooktypeBin;
+
+ bool success;
+ bool canceled;
+ bool running;
+
+ bool forceNoEject;
+
+ int foundMediaType;
+};
+
+
+K3bDvdBooktypeJob::K3bDvdBooktypeJob( K3bJobHandler* jh, QObject* parent, const char* name )
+ : K3bJob( jh, parent, name ),
+ m_action(0)
+{
+ d = new Private;
+}
+
+
+K3bDvdBooktypeJob::~K3bDvdBooktypeJob()
+{
+ delete d->process;
+ delete d;
+}
+
+
+void K3bDvdBooktypeJob::setForceNoEject( bool b )
+{
+ d->forceNoEject = b;
+}
+
+
+QString K3bDvdBooktypeJob::jobDescription() const
+{
+ return i18n("Changing DVD Booktype"); // Changing DVD±R(W) Booktype
+}
+
+
+QString K3bDvdBooktypeJob::jobDetails() const
+{
+ return QString::null;
+}
+
+
+void K3bDvdBooktypeJob::start()
+{
+ d->canceled = false;
+ d->running = true;
+
+ jobStarted();
+
+ if( !d->device ) {
+ emit infoMessage( i18n("No device set"), ERROR );
+ jobFinished(false);
+ d->running = false;
+ return;
+ }
+
+ //
+ // In case we want to change the writers default we do not need to wait for a media
+ //
+ if( m_action == SET_MEDIA_DVD_ROM ||
+ m_action == SET_MEDIA_DVD_R_W ) {
+ emit newSubTask( i18n("Waiting for media") );
+ if( waitForMedia( d->device,
+ K3bDevice::STATE_COMPLETE|K3bDevice::STATE_INCOMPLETE|K3bDevice::STATE_EMPTY,
+ K3bDevice::MEDIA_DVD_PLUS_RW|K3bDevice::MEDIA_DVD_PLUS_R,
+ i18n("Please insert an empty DVD+R or a DVD+RW medium into drive<p><b>%1 %2 (%3)</b>.")
+ .arg(d->device->vendor()).arg(d->device->description()).arg(d->device->devicename()) ) == -1 ) {
+ emit canceled();
+ jobFinished(false);
+ d->running = false;
+ return;
+ }
+
+ emit infoMessage( i18n("Checking media..."), INFO );
+ emit newTask( i18n("Checking media") );
+
+ connect( K3bDevice::sendCommand( K3bDevice::DeviceHandler::NG_DISKINFO, d->device ),
+ SIGNAL(finished(K3bDevice::DeviceHandler*)),
+ this,
+ SLOT(slotDeviceHandlerFinished(K3bDevice::DeviceHandler*)) );
+ }
+ else {
+ // change writer defaults
+ startBooktypeChange();
+ }
+}
+
+
+void K3bDvdBooktypeJob::start( K3bDevice::DeviceHandler* dh )
+{
+ d->canceled = false;
+ d->running = true;
+
+ jobStarted();
+
+ slotDeviceHandlerFinished( dh );
+}
+
+
+void K3bDvdBooktypeJob::cancel()
+{
+ if( d->running ) {
+ d->canceled = true;
+ if( d->process )
+ d->process->kill();
+ }
+ else {
+ kdDebug() << "(K3bDvdBooktypeJob) not running." << endl;
+ }
+}
+
+
+void K3bDvdBooktypeJob::setDevice( K3bDevice::Device* dev )
+{
+ d->device = dev;
+}
+
+
+void K3bDvdBooktypeJob::slotStderrLine( const QString& line )
+{
+ emit debuggingOutput( "dvd+rw-booktype", line );
+ // FIXME
+}
+
+
+void K3bDvdBooktypeJob::slotProcessFinished( KProcess* p )
+{
+ if( d->canceled ) {
+ emit canceled();
+ d->success = false;
+ }
+ else if( p->normalExit() ) {
+ if( p->exitStatus() == 0 ) {
+ emit infoMessage( i18n("Booktype successfully changed"), K3bJob::SUCCESS );
+ d->success = true;
+ }
+ else {
+ emit infoMessage( i18n("%1 returned an unknown error (code %2).").arg(d->dvdBooktypeBin->name()).arg(p->exitStatus()),
+ K3bJob::ERROR );
+ emit infoMessage( i18n("Please send me an email with the last output."), K3bJob::ERROR );
+
+ d->success = false;
+ }
+ }
+ else {
+ emit infoMessage( i18n("%1 did not exit cleanly.").arg(d->dvdBooktypeBin->name()),
+ ERROR );
+ d->success = false;
+ }
+
+ //
+ // No need to eject the media if we changed the writer's default
+ //
+ if( m_action == SET_MEDIA_DVD_ROM ||
+ m_action == SET_MEDIA_DVD_R_W ) {
+
+ if( d->forceNoEject ||
+ !k3bcore->globalSettings()->ejectMedia() ) {
+ d->running = false;
+ jobFinished(d->success);
+ }
+ else {
+ emit infoMessage( i18n("Ejecting DVD..."), INFO );
+ connect( K3bDevice::eject( d->device ),
+ SIGNAL(finished(K3bDevice::DeviceHandler*)),
+ this,
+ SLOT(slotEjectingFinished(K3bDevice::DeviceHandler*)) );
+ }
+ }
+ else {
+ d->running = false;
+ jobFinished(d->success);
+ }
+}
+
+
+void K3bDvdBooktypeJob::slotEjectingFinished( K3bDevice::DeviceHandler* dh )
+{
+ if( !dh->success() )
+ emit infoMessage( i18n("Unable to eject media."), ERROR );
+
+ d->running = false;
+ jobFinished(d->success);
+}
+
+
+void K3bDvdBooktypeJob::slotDeviceHandlerFinished( K3bDevice::DeviceHandler* dh )
+{
+ if( d->canceled ) {
+ emit canceled();
+ d->running = false;
+ jobFinished(false);
+ }
+
+ if( dh->success() ) {
+
+ d->foundMediaType = dh->diskInfo().mediaType();
+ if( d->foundMediaType == K3bDevice::MEDIA_DVD_PLUS_R ) {
+ // the media needs to be empty
+ if( dh->diskInfo().empty() )
+ startBooktypeChange();
+ else {
+ emit infoMessage( i18n("Cannot change booktype on non-empty DVD+R media."), ERROR );
+ jobFinished(false);
+ }
+ }
+ else if( d->foundMediaType == K3bDevice::MEDIA_DVD_PLUS_RW ) {
+ startBooktypeChange();
+ }
+ else {
+ emit infoMessage( i18n("No DVD+R(W) media found."), ERROR );
+ jobFinished(false);
+ }
+ }
+ else {
+ emit infoMessage( i18n("Unable to determine media state."), ERROR );
+ d->running = false;
+ jobFinished(false);
+ }
+}
+
+
+void K3bDvdBooktypeJob::startBooktypeChange()
+{
+ delete d->process;
+ d->process = new K3bProcess();
+ d->process->setRunPrivileged(true);
+ d->process->setSuppressEmptyLines(true);
+ connect( d->process, SIGNAL(stderrLine(const QString&)), this, SLOT(slotStderrLine(const QString&)) );
+ connect( d->process, SIGNAL(processExited(KProcess*)), this, SLOT(slotProcessFinished(KProcess*)) );
+
+ d->dvdBooktypeBin = k3bcore->externalBinManager()->binObject( "dvd+rw-booktype" );
+ if( !d->dvdBooktypeBin ) {
+ emit infoMessage( i18n("Could not find %1 executable.").arg("dvd+rw-booktype"), ERROR );
+ d->running = false;
+ jobFinished(false);
+ return;
+ }
+
+ *d->process << d->dvdBooktypeBin;
+
+ switch( m_action ) {
+ case SET_MEDIA_DVD_ROM:
+ *d->process << "-dvd-rom-spec"
+ << "-media";
+ break;
+ case SET_MEDIA_DVD_R_W:
+ if( d->foundMediaType == K3bDevice::MEDIA_DVD_PLUS_RW )
+ *d->process << "-dvd+rw-spec";
+ else
+ *d->process << "-dvd+r-spec";
+ *d->process << "-media";
+ break;
+ case SET_UNIT_DVD_ROM_ON_NEW_DVD_R:
+ *d->process << "-dvd-rom-spec"
+ << "-unit+r";
+ break;
+ case SET_UNIT_DVD_ROM_ON_NEW_DVD_RW:
+ *d->process << "-dvd-rom-spec"
+ << "-unit+rw";
+ break;
+ case SET_UNIT_DVD_R_ON_NEW_DVD_R:
+ *d->process << "-dvd+r-spec"
+ << "-unit+r";
+ break;
+ case SET_UNIT_DVD_RW_ON_NEW_DVD_RW:
+ *d->process << "-dvd+rw-spec"
+ << "-unit+rw";
+ break;
+ }
+
+ *d->process << d->device->blockDeviceName();
+
+ kdDebug() << "***** dvd+rw-booktype parameters:\n";
+ const QValueList<QCString>& args = d->process->args();
+ QString s;
+ for( QValueList<QCString>::const_iterator it = args.begin(); it != args.end(); ++it ) {
+ s += *it + " ";
+ }
+ kdDebug() << s << endl << flush;
+ emit debuggingOutput( "dvd+rw-booktype command:", s );
+
+
+ if( !d->process->start( KProcess::NotifyOnExit, KProcess::All ) ) {
+ // something went wrong when starting the program
+ // it "should" be the executable
+ emit infoMessage( i18n("Could not start %1.").arg(d->dvdBooktypeBin->name()), K3bJob::ERROR );
+ d->running = false;
+ jobFinished(false);
+ }
+ else {
+ emit newTask( i18n("Changing Booktype") );
+ }
+}
+
+#include "k3bdvdbooktypejob.moc"
diff --git a/libk3b/projects/datadvd/k3bdvdbooktypejob.h b/libk3b/projects/datadvd/k3bdvdbooktypejob.h
new file mode 100644
index 0000000..b9e7e4b
--- /dev/null
+++ b/libk3b/projects/datadvd/k3bdvdbooktypejob.h
@@ -0,0 +1,99 @@
+/*
+ *
+ * $Id: k3bdvdbooktypejob.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_DVD_BOOKTYPE_JOB_H_
+#define _K3B_DVD_BOOKTYPE_JOB_H_
+
+
+#include <k3bjob.h>
+
+
+class KProcess;
+namespace K3bDevice {
+ class Device;
+ class DeviceHandler;
+}
+
+
+/**
+ * This job can change the compatibility bit of DVD+R(W) media
+ * with supported dvd writers.
+ */
+class K3bDvdBooktypeJob : public K3bJob
+{
+ Q_OBJECT
+
+ public:
+ K3bDvdBooktypeJob( K3bJobHandler*, QObject* parent = 0, const char* name = 0 );
+ ~K3bDvdBooktypeJob();
+
+ QString jobDescription() const;
+ QString jobDetails() const;
+
+ /**
+ * @list SET_MEDIA_DVD_ROM Change media identification on current media to DVD-ROM.
+ * @list SET_MEDIA_DVD_R_W Change media identification on current media to DVD+R or DVD+RW.
+ * @list SET_UNIT_DVD_ROM_ON_NEW_DVD_R Set the drive to write DVD-ROM specification on future written DVD+R discs.
+ * @list SET_UNIT_DVD_ROM_ON_NEW_DVD_RW Set the drive to write DVD-ROM specification on future written DVD+RW discs.
+ * @list SET_UNIT_DVD_R_ON_NEW_DVD_R Set the drive to write DVD+R specification on future written DVD+R discs.
+ * @list SET_UNIT_DVD_RW_ON_NEW_DVD_RW Set the drive to write DVD+RW specification on future written DVD+RW discs.
+ */
+ enum Action {
+ SET_MEDIA_DVD_ROM,
+ SET_MEDIA_DVD_R_W,
+ SET_UNIT_DVD_ROM_ON_NEW_DVD_R,
+ SET_UNIT_DVD_ROM_ON_NEW_DVD_RW,
+ SET_UNIT_DVD_R_ON_NEW_DVD_R,
+ SET_UNIT_DVD_RW_ON_NEW_DVD_RW
+ };
+
+ public slots:
+ void start();
+
+ /**
+ * The devicehandler needs to have a valid NgDiskInfo
+ * Use this to prevent the job from searching a media.
+ */
+ void start( K3bDevice::DeviceHandler* );
+
+ void cancel();
+
+ void setDevice( K3bDevice::Device* );
+
+ void setAction( int a ) { m_action = a; }
+
+ /**
+ * If set true the job ignores the global K3b setting
+ * and does not eject the CD-RW after finishing
+ */
+ void setForceNoEject( bool );
+
+ private slots:
+ void slotStderrLine( const QString& );
+ void slotProcessFinished( KProcess* );
+ void slotDeviceHandlerFinished( K3bDevice::DeviceHandler* );
+ void slotEjectingFinished( K3bDevice::DeviceHandler* );
+
+ private:
+ void startBooktypeChange();
+
+ int m_action;
+
+ class Private;
+ Private* d;
+};
+
+
+#endif
diff --git a/libk3b/projects/datadvd/k3bdvddoc.cpp b/libk3b/projects/datadvd/k3bdvddoc.cpp
new file mode 100644
index 0000000..4ab8b9f
--- /dev/null
+++ b/libk3b/projects/datadvd/k3bdvddoc.cpp
@@ -0,0 +1,39 @@
+/*
+ *
+ * $Id: k3bdvddoc.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bdvddoc.h"
+#include "k3bdvdjob.h"
+
+#include <k3bisooptions.h>
+
+#include <kconfig.h>
+
+
+K3bDvdDoc::K3bDvdDoc( QObject* parent )
+ : K3bDataDoc( parent )
+{
+}
+
+K3bDvdDoc::~K3bDvdDoc()
+{
+}
+
+K3bBurnJob* K3bDvdDoc::newBurnJob( K3bJobHandler* hdl, QObject* parent )
+{
+ return new K3bDvdJob( this, hdl, parent );
+}
+
+//#include "k3bdvddoc.moc"
diff --git a/libk3b/projects/datadvd/k3bdvddoc.h b/libk3b/projects/datadvd/k3bdvddoc.h
new file mode 100644
index 0000000..03b5c3d
--- /dev/null
+++ b/libk3b/projects/datadvd/k3bdvddoc.h
@@ -0,0 +1,37 @@
+/*
+ *
+ * $Id: k3bdvddoc.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_DVDDOC_H_
+#define _K3B_DVDDOC_H_
+
+#include <k3bdatadoc.h>
+#include "k3b_export.h"
+class KConfig;
+
+class LIBK3B_EXPORT K3bDvdDoc : public K3bDataDoc
+{
+ public:
+ K3bDvdDoc( QObject* parent = 0 );
+ virtual ~K3bDvdDoc();
+
+ virtual int type() const { return DVD; }
+
+ virtual K3bBurnJob* newBurnJob( K3bJobHandler* hdl, QObject* parent = 0 );
+
+ protected:
+ virtual QString typeString() const { return "dvd"; }
+};
+
+#endif
diff --git a/libk3b/projects/datadvd/k3bdvdjob.cpp b/libk3b/projects/datadvd/k3bdvdjob.cpp
new file mode 100644
index 0000000..3cd1521
--- /dev/null
+++ b/libk3b/projects/datadvd/k3bdvdjob.cpp
@@ -0,0 +1,344 @@
+/*
+ *
+ * $Id: k3bdvdjob.cpp 690187 2007-07-20 09:18:03Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bdvdjob.h"
+#include "k3bdvddoc.h"
+
+#include <k3bcore.h>
+#include <k3bisoimager.h>
+#include <k3bgrowisofswriter.h>
+#include <k3bglobals.h>
+#include <k3bdevice.h>
+#include <k3bdevicehandler.h>
+#include <k3bdiskinfo.h>
+#include <k3bdeviceglobals.h>
+#include <k3bglobalsettings.h>
+#include <k3biso9660.h>
+#include <k3bmsinfofetcher.h>
+
+#include <klocale.h>
+#include <kapplication.h>
+
+
+class K3bDvdJob::Private
+{
+public:
+};
+
+
+K3bDvdJob::K3bDvdJob( K3bDataDoc* doc, K3bJobHandler* hdl, QObject* parent )
+ : K3bDataJob( doc, hdl, parent ),
+ m_doc( doc )
+{
+ d = new Private();
+}
+
+
+K3bDvdJob::~K3bDvdJob()
+{
+ delete d;
+}
+
+
+void K3bDvdJob::prepareData()
+{
+}
+
+
+bool K3bDvdJob::prepareWriterJob()
+{
+ K3bGrowisofsWriter* writer = new K3bGrowisofsWriter( m_doc->burner(), this, this );
+
+ // these do only make sense with DVD-R(W)
+ writer->setSimulate( m_doc->dummy() );
+ writer->setBurnSpeed( m_doc->speed() );
+
+ // Andy said incremental sequential is the default mode and it seems uses have more problems with DAO anyway
+ // BUT: I also had a report that incremental sequential produced unreadable media!
+ if( m_doc->writingMode() == K3b::DAO )
+// || ( m_doc->writingMode() == K3b::WRITING_MODE_AUTO &&
+// usedMultiSessionMode() == K3bDataDoc::NONE ) )
+ writer->setWritingMode( K3b::DAO );
+
+ writer->setMultiSession( usedMultiSessionMode() == K3bDataDoc::CONTINUE ||
+ usedMultiSessionMode() == K3bDataDoc::FINISH );
+
+ writer->setCloseDvd( usedMultiSessionMode() == K3bDataDoc::NONE ||
+ usedMultiSessionMode() == K3bDataDoc::FINISH );
+
+ writer->setImageToWrite( QString::null ); // read from stdin
+ writer->setTrackSize( m_isoImager->size() );
+
+ if( usedMultiSessionMode() != K3bDataDoc::NONE ) {
+ //
+ // growisofs wants a valid -C parameter for multisession, so we get it from the
+ // K3bMsInfoFetcher (see K3bDataJob::slotMsInfoFetched)
+ //
+ writer->setMultiSessionInfo( m_msInfoFetcher->msInfo() );
+ }
+
+ setWriterJob( writer );
+
+ return true;
+}
+
+
+void K3bDvdJob::determineMultiSessionMode()
+{
+ int m = requestMedia( K3bDevice::STATE_INCOMPLETE|K3bDevice::STATE_EMPTY );
+
+ if( m < 0 ) {
+ cancel();
+ }
+ else {
+ connect( K3bDevice::sendCommand( K3bDevice::DeviceHandler::NG_DISKINFO, m_doc->burner() ),
+ SIGNAL(finished(K3bDevice::DeviceHandler*)),
+ this,
+ SLOT(slotDetermineMultiSessionMode(K3bDevice::DeviceHandler*)) );
+ }
+}
+
+
+K3bDataDoc::MultiSessionMode K3bDvdJob::getMultiSessionMode( const K3bDevice::DiskInfo& info )
+{
+ K3bDataDoc::MultiSessionMode mode = K3bDataDoc::NONE;
+
+ if( info.mediaType() & (K3bDevice::MEDIA_DVD_PLUS_RW|K3bDevice::MEDIA_DVD_RW_OVWR) ) {
+ //
+ // we need to handle DVD+RW and DVD-RW overwrite media differently since remainingSize() is not valid
+ // in both cases
+ // Since one never closes a DVD+RW we only differ between CONTINUE and START
+ //
+
+ // try to check the filesystem size
+ K3bIso9660 iso( m_doc->burner() );
+ if( iso.open() && info.capacity() - iso.primaryDescriptor().volumeSpaceSize >= m_doc->burningLength() ) {
+ mode = K3bDataDoc::CONTINUE;
+ }
+ else {
+ mode = K3bDataDoc::START;
+ }
+ }
+ else if( info.appendable() ) {
+ //
+ // 3 cases:
+ // 1. the project does not fit -> no multisession (resulting in asking for another media above)
+ // 2. the project does fit and fills up the CD (No new sessions after the 4GB boundary) -> finish multisession
+ // 3. the project does fit and does not fill up the CD -> continue multisession
+ //
+ if( m_doc->size() > info.remainingSize().mode1Bytes() && !m_doc->sessionImported() )
+ mode = K3bDataDoc::NONE;
+ else if( info.size() + m_doc->burningLength() + 11400 /* used size + project size + session gap */ > 2097152 /* 4 GB */ )
+ mode = K3bDataDoc::FINISH;
+ else
+ mode = K3bDataDoc::CONTINUE;
+ }
+ else {
+ //
+ // We only close the DVD if the project fills it beyond the 4GB boundary
+ //
+ if( info.size() + m_doc->burningLength() + 11400 /* used size + project size + session gap */ > 2097152 /* 4 GB */ ||
+ m_doc->writingMode() == K3b::DAO )
+ mode = K3bDataDoc::NONE;
+ else
+ mode = K3bDataDoc::START;
+ }
+
+ return mode;
+}
+
+
+int K3bDvdJob::requestMedia( int state )
+{
+ int mt = 0;
+ if( m_doc->writingMode() == K3b::WRITING_MODE_RES_OVWR ) // we treat DVD+R(W) as restricted overwrite media
+ mt = K3bDevice::MEDIA_DVD_RW_OVWR|K3bDevice::MEDIA_DVD_PLUS_RW|K3bDevice::MEDIA_DVD_PLUS_R;
+ else
+ mt = K3bDevice::MEDIA_WRITABLE_DVD;
+
+ // double layer media
+ // in case overburn is enabled we allow some made up max size
+ // before we force a DL medium
+ if( m_doc->size() > 4700372992LL ) {
+ if( !k3bcore->globalSettings()->overburn() ||
+ m_doc->size() > 4900000000LL ) {
+ mt = K3bDevice::MEDIA_WRITABLE_DVD_DL;
+ }
+ }
+
+ return waitForMedia( m_doc->burner(),
+ state,
+ mt );
+}
+
+
+bool K3bDvdJob::waitForMedium()
+{
+ emit infoMessage( i18n("Waiting for media") + "...", INFO );
+
+ int foundMedium = requestMedia( usedMultiSessionMode() == K3bDataDoc::CONTINUE ||
+ usedMultiSessionMode() == K3bDataDoc::FINISH ?
+ K3bDevice::STATE_INCOMPLETE :
+ K3bDevice::STATE_EMPTY );
+
+ if( foundMedium < 0 || hasBeenCanceled() ) {
+ return false;
+ }
+
+ if( foundMedium == 0 ) {
+ emit infoMessage( i18n("Forced by user. Growisofs will be called without further tests."), INFO );
+ }
+
+ else {
+ // -------------------------------
+ // DVD Plus
+ // -------------------------------
+ if( foundMedium & K3bDevice::MEDIA_DVD_PLUS_ALL ) {
+ if( m_doc->dummy() ) {
+ if( !questionYesNo( i18n("K3b does not support simulation with DVD+R(W) media. "
+ "Do you really want to continue? The media will be written "
+ "for real."),
+ i18n("No Simulation with DVD+R(W)") ) ) {
+ return false;
+ }
+
+ m_doc->setDummy( false );
+ emit newTask( i18n("Writing") );
+ }
+
+ if( m_doc->writingMode() != K3b::WRITING_MODE_AUTO && m_doc->writingMode() != K3b::WRITING_MODE_RES_OVWR )
+ emit infoMessage( i18n("Writing mode ignored when writing DVD+R(W) media."), INFO );
+
+ if( foundMedium & K3bDevice::MEDIA_DVD_PLUS_RW ) {
+ if( usedMultiSessionMode() == K3bDataDoc::NONE ||
+ usedMultiSessionMode() == K3bDataDoc::START )
+ emit infoMessage( i18n("Writing DVD+RW."), INFO );
+ else
+ emit infoMessage( i18n("Growing ISO9660 filesystem on DVD+RW."), INFO );
+ }
+ else if( foundMedium & K3bDevice::MEDIA_DVD_PLUS_R_DL )
+ emit infoMessage( i18n("Writing Double Layer DVD+R."), INFO );
+ else
+ emit infoMessage( i18n("Writing DVD+R."), INFO );
+ }
+
+ // -------------------------------
+ // DVD Minus
+ // -------------------------------
+ else {
+ if( m_doc->dummy() && !m_doc->burner()->dvdMinusTestwrite() ) {
+ if( !questionYesNo( i18n("Your writer (%1 %2) does not support simulation with DVD-R(W) media. "
+ "Do you really want to continue? The media will be written "
+ "for real.")
+ .arg(m_doc->burner()->vendor())
+ .arg(m_doc->burner()->description()),
+ i18n("No Simulation with DVD-R(W)") ) ) {
+ return false;
+ }
+
+ m_doc->setDummy( false );
+ }
+
+ // RESTRICTED OVERWRITE
+ // --------------------
+ if( foundMedium & K3bDevice::MEDIA_DVD_RW_OVWR ) {
+ if( usedMultiSessionMode() == K3bDataDoc::NONE ||
+ usedMultiSessionMode() == K3bDataDoc::START )
+ emit infoMessage( i18n("Writing DVD-RW in restricted overwrite mode."), INFO );
+ else
+ emit infoMessage( i18n("Growing ISO9660 filesystem on DVD-RW in restricted overwrite mode."), INFO );
+ }
+
+ // NORMAL
+ // ------
+ else {
+
+ // FIXME: DVD-R DL jump and stuff
+
+ if( m_doc->writingMode() == K3b::DAO )
+ // || ( m_doc->writingMode() == K3b::WRITING_MODE_AUTO &&
+// usedMultiSessionMode() == K3bDataDoc::NONE ) )
+ emit infoMessage( i18n("Writing %1 in DAO mode.").arg( K3bDevice::mediaTypeString(foundMedium, true) ), INFO );
+
+ else {
+ // check if the writer supports writing sequential and thus multisession (on -1 the burner cannot handle
+ // features and we simply ignore it and hope for the best)
+ if( m_doc->burner()->featureCurrent( K3bDevice::FEATURE_INCREMENTAL_STREAMING_WRITABLE ) == 0 ) {
+ if( !questionYesNo( i18n("Your writer (%1 %2) does not support Incremental Streaming with %3 "
+ "media. Multisession will not be possible. Continue anyway?")
+ .arg(m_doc->burner()->vendor())
+ .arg(m_doc->burner()->description())
+ .arg( K3bDevice::mediaTypeString(foundMedium, true) ),
+ i18n("No Incremental Streaming") ) ) {
+ return false;
+ }
+ else {
+ emit infoMessage( i18n("Writing %1 in DAO mode.").arg( K3bDevice::mediaTypeString(foundMedium, true) ), INFO );
+ }
+ }
+ else {
+ if( !(foundMedium & (K3bDevice::MEDIA_DVD_RW|K3bDevice::MEDIA_DVD_RW_OVWR|K3bDevice::MEDIA_DVD_RW_SEQ)) &&
+ m_doc->writingMode() == K3b::WRITING_MODE_RES_OVWR )
+ emit infoMessage( i18n("Restricted Overwrite is not possible with DVD-R media."), INFO );
+
+ emit infoMessage( i18n("Writing %1 in incremental mode.").arg( K3bDevice::mediaTypeString(foundMedium, true) ), INFO );
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+
+QString K3bDvdJob::jobDescription() const
+{
+ if( m_doc->onlyCreateImages() ) {
+ return i18n("Creating Data Image File");
+ }
+ else if( m_doc->multiSessionMode() == K3bDataDoc::NONE ||
+ m_doc->multiSessionMode() == K3bDataDoc::AUTO ) {
+ return i18n("Writing Data DVD")
+ + ( m_doc->isoOptions().volumeID().isEmpty()
+ ? QString::null
+ : QString( " (%1)" ).arg(m_doc->isoOptions().volumeID()) );
+ }
+ else {
+ return i18n("Writing Multisession DVD")
+ + ( m_doc->isoOptions().volumeID().isEmpty()
+ ? QString::null
+ : QString( " (%1)" ).arg(m_doc->isoOptions().volumeID()) );
+ }
+}
+
+
+QString K3bDvdJob::jobDetails() const
+{
+ if( m_doc->copies() > 1 &&
+ !m_doc->dummy() &&
+ !(m_doc->multiSessionMode() == K3bDataDoc::CONTINUE ||
+ m_doc->multiSessionMode() == K3bDataDoc::FINISH) )
+ return i18n("ISO9660 Filesystem (Size: %1) - %n copy",
+ "ISO9660 Filesystem (Size: %1) - %n copies",
+ m_doc->copies())
+ .arg(KIO::convertSize( m_doc->size() ));
+ else
+ return i18n("ISO9660 Filesystem (Size: %1)")
+ .arg(KIO::convertSize( m_doc->size() ));
+}
+
+#include "k3bdvdjob.moc"
diff --git a/libk3b/projects/datadvd/k3bdvdjob.h b/libk3b/projects/datadvd/k3bdvdjob.h
new file mode 100644
index 0000000..381bc1d
--- /dev/null
+++ b/libk3b/projects/datadvd/k3bdvdjob.h
@@ -0,0 +1,57 @@
+/*
+ *
+ * $Id: k3bdvdjob.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_DVD_JOB_H_
+#define _K3B_DVD_JOB_H_
+
+#include <k3bdatajob.h>
+
+#include <qfile.h>
+
+class K3bDataDoc;
+class K3bGrowisofsWriter;
+
+
+class K3bDvdJob : public K3bDataJob
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * To be more flexible we allow writing of any data doc
+ */
+ K3bDvdJob( K3bDataDoc*, K3bJobHandler*, QObject* parent = 0 );
+ virtual ~K3bDvdJob();
+
+ virtual QString jobDescription() const;
+ virtual QString jobDetails() const;
+
+ protected:
+ void prepareData();
+ virtual bool prepareWriterJob();
+ void determineMultiSessionMode();
+ K3bDataDoc::MultiSessionMode getMultiSessionMode( const K3bDevice::DiskInfo& );
+ bool waitForMedium();
+ int requestMedia( int state );
+
+ private:
+ K3bDataDoc* m_doc;
+
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/projects/datadvd/k3bdvdview.cpp b/libk3b/projects/datadvd/k3bdvdview.cpp
new file mode 100644
index 0000000..512ec4b
--- /dev/null
+++ b/libk3b/projects/datadvd/k3bdvdview.cpp
@@ -0,0 +1,48 @@
+/*
+ *
+ * $Id: k3bdvdview.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bdvdview.h"
+#include "k3bdvddoc.h"
+#include "k3bdvdburndialog.h"
+#include <k3bfillstatusdisplay.h>
+#include <k3bdatafileview.h>
+
+#include <klocale.h>
+
+
+K3bDvdView::K3bDvdView( K3bDvdDoc* doc, QWidget *parent, const char *name )
+ : K3bDataView( doc, parent, name )
+{
+ m_doc = doc;
+
+ fillStatusDisplay()->showDvdSizes(true);
+
+ m_dataFileView->setNoItemText( i18n("Use drag'n'drop to add files and directories to the project.\n"
+ "To remove or rename files use the context menu.\n"
+ "After that press the burn button to write the DVD.") );
+}
+
+
+K3bDvdView::~K3bDvdView()
+{
+}
+
+
+K3bProjectBurnDialog* K3bDvdView::newBurnDialog( QWidget* parent, const char* name )
+{
+ return new K3bDvdBurnDialog( m_doc, parent, name, true );
+}
+
+#include "k3bdvdview.moc"
diff --git a/libk3b/projects/datadvd/k3bdvdview.h b/libk3b/projects/datadvd/k3bdvdview.h
new file mode 100644
index 0000000..d9f30f3
--- /dev/null
+++ b/libk3b/projects/datadvd/k3bdvdview.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * $Id: k3bdvdview.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_DVDVIEW_H_
+#define _K3B_DVDVIEW_H_
+
+#include <k3bdataview.h>
+
+class K3bDvdDoc;
+
+
+class K3bDvdView : public K3bDataView
+{
+ Q_OBJECT
+
+ public:
+ K3bDvdView( K3bDvdDoc* doc, QWidget *parent = 0, const char *name = 0 );
+ ~K3bDvdView();
+
+ protected:
+ virtual K3bProjectBurnDialog* newBurnDialog( QWidget* parent = 0, const char* name = 0 );
+
+ private:
+ K3bDvdDoc* m_doc;
+};
+
+#endif
diff --git a/libk3b/projects/k3babstractwriter.cpp b/libk3b/projects/k3babstractwriter.cpp
new file mode 100644
index 0000000..df22bc3
--- /dev/null
+++ b/libk3b/projects/k3babstractwriter.cpp
@@ -0,0 +1,96 @@
+/*
+ *
+ * $Id: k3babstractwriter.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3babstractwriter.h"
+
+#include <k3bcore.h>
+#include <k3bdevicemanager.h>
+#include <k3bdevicehandler.h>
+#include <k3bglobalsettings.h>
+
+#include <klocale.h>
+#include <kglobal.h>
+
+
+
+K3bAbstractWriter::K3bAbstractWriter( K3bDevice::Device* dev, K3bJobHandler* jh, QObject* parent, const char* name )
+ : K3bJob( jh, parent, name ),
+ m_burnDevice(dev),
+ m_burnSpeed(1),
+ m_simulate(false),
+ m_sourceUnreadable(false)
+{
+}
+
+
+K3bAbstractWriter::~K3bAbstractWriter()
+{
+}
+
+
+K3bDevice::Device* K3bAbstractWriter::burnDevice()
+{
+ if( m_burnDevice )
+ return m_burnDevice;
+ else
+ return k3bcore->deviceManager()->burningDevices().getFirst();
+}
+
+
+void K3bAbstractWriter::cancel()
+{
+ if( burnDevice() ) {
+ // we need to unlock the writer because cdrecord locked it while writing
+ emit infoMessage( i18n("Unlocking drive..."), INFO );
+ connect( K3bDevice::unblock( burnDevice() ), SIGNAL(finished(bool)),
+ this, SLOT(slotUnblockWhileCancellationFinished(bool)) );
+ }
+ else {
+ emit canceled();
+ jobFinished(false);
+ }
+}
+
+
+void K3bAbstractWriter::slotUnblockWhileCancellationFinished( bool success )
+{
+ if( !success )
+ emit infoMessage( i18n("Could not unlock CD drive."), K3bJob::ERROR ); // FIXME: simply "drive", not "CD drive"
+
+ if( k3bcore->globalSettings()->ejectMedia() ) {
+ emit newSubTask( i18n("Ejecting CD") ); // FIXME: "media" instead of "CD"
+ connect( K3bDevice::eject( burnDevice() ), SIGNAL(finished(bool)),
+ this, SLOT(slotEjectWhileCancellationFinished(bool)) );
+ }
+ else {
+ emit canceled();
+ jobFinished( false );
+ }
+}
+
+
+void K3bAbstractWriter::slotEjectWhileCancellationFinished( bool success )
+{
+ if( !success ) {
+ emit infoMessage( i18n("Unable to eject media."), K3bJob::ERROR );
+ }
+
+ emit canceled();
+ jobFinished( false );
+}
+
+
+#include "k3babstractwriter.moc"
diff --git a/libk3b/projects/k3babstractwriter.h b/libk3b/projects/k3babstractwriter.h
new file mode 100644
index 0000000..3f91ee3
--- /dev/null
+++ b/libk3b/projects/k3babstractwriter.h
@@ -0,0 +1,92 @@
+/*
+ *
+ * $Id: k3babstractwriter.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3B_ABSTRACT_WRITER_H
+#define K3B_ABSTRACT_WRITER_H
+
+
+#include "k3bjob.h"
+
+#include <qdatetime.h>
+
+class K3bDevice::Device;
+class K3bJobHandler;
+
+
+class K3bAbstractWriter : public K3bJob
+{
+ Q_OBJECT
+
+ public:
+ virtual ~K3bAbstractWriter();
+
+ K3bDevice::Device* burnDevice();
+ int burnSpeed() const { return m_burnSpeed; }
+ bool simulate() const { return m_simulate; }
+
+ /**
+ * This can be used to setup direct streaming between two processes
+ * for example the cdrecordwriter returnes the stdin fd which can be
+ * connected to the stdout fd of mkisofs in the isoimager
+ */
+ virtual int fd() const { return -1; }
+ virtual bool closeFd() { return false; }
+
+ public slots:
+ /**
+ * If the burnDevice is set this will try to unlock the drive and
+ * eject the disk if K3b is configured to do so.
+ * Will also emit canceled and finished signals.
+ * may be called by subclasses.
+ */
+ void cancel();
+
+ void setBurnDevice( K3bDevice::Device* dev ) { m_burnDevice = dev; }
+ void setBurnSpeed( int s ) { m_burnSpeed = s; }
+ void setSimulate( bool b ) { m_simulate = b; }
+
+ /**
+ * Used to inform the writer that the source (especially useful when reading from
+ * another cd/dvd media) could not be read.
+ *
+ * Basically it should be used to make sure no "write an email" message is thrown.
+ */
+ void setSourceUnreadable( bool b = true ) { m_sourceUnreadable = b; }
+
+ signals:
+ void buffer( int );
+ void deviceBuffer( int );
+ void writeSpeed( int, int );
+
+ protected:
+ K3bAbstractWriter( K3bDevice::Device* dev, K3bJobHandler* hdl,
+ QObject* parent = 0, const char* name = 0 );
+
+ bool wasSourceUnreadable() const { return m_sourceUnreadable; }
+
+ protected slots:
+ void slotUnblockWhileCancellationFinished( bool success );
+ void slotEjectWhileCancellationFinished( bool success );
+
+ private:
+ K3bDevice::Device* m_burnDevice;
+ int m_burnSpeed;
+ bool m_simulate;
+ bool m_sourceUnreadable;
+};
+
+
+#endif
diff --git a/libk3b/projects/k3bcdrdaowriter.cpp b/libk3b/projects/k3bcdrdaowriter.cpp
new file mode 100644
index 0000000..c49cb4b
--- /dev/null
+++ b/libk3b/projects/k3bcdrdaowriter.cpp
@@ -0,0 +1,1101 @@
+/*
+ *
+ * $Id: k3bcdrdaowriter.cpp 654649 2007-04-16 17:55:50Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ * Klaus-Dieter Krannich <kd@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bcdrdaowriter.h"
+
+#include <k3bcore.h>
+#include <k3bexternalbinmanager.h>
+#include <k3bdevicemanager.h>
+#include <k3bprocess.h>
+#include <k3bdevice.h>
+#include <k3bdevicehandler.h>
+#include <k3bthroughputestimator.h>
+#include <k3bglobals.h>
+#include <k3bglobalsettings.h>
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qvaluelist.h>
+#include <qregexp.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qdir.h>
+#include <qurl.h>
+#include <qsocket.h>
+#include <qsocketdevice.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kio/netaccess.h>
+#include <kstandarddirs.h>
+#include <ktempfile.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+
+
+#define PGSMSG_MIN PGSMSG_RCD_ANALYZING
+#define PGSMSG_RCD_ANALYZING 1
+#define PGSMSG_RCD_EXTRACTING 2
+#define PGSMSG_WCD_LEADIN 3
+#define PGSMSG_WCD_DATA 4
+#define PGSMSG_WCD_LEADOUT 5
+#define PGSMSG_BLK 6
+#define PGSMSG_MAX PGSMSG_BLK
+
+struct ProgressMsg {
+ int status; // see PGSMSG_* constants
+ int totalTracks; // total number of tracks
+ int track; // actually written track
+ int trackProgress; // progress for current track 0..1000
+ int totalProgress; // total writing progress 0..1000
+ int bufferFillRate; // buffer fill rate 0..100
+};
+
+#define PSGMSG_MINSIZE 24
+
+struct ProgressMsg2 {
+ int status; // see PGSMSG_* constants
+ int totalTracks; // total number of tracks
+ int track; // actually written track
+ int trackProgress; // progress for current track 0..1000
+ int totalProgress; // total writing progress 0..1000
+ int bufferFillRate; // buffer fill rate 0..100
+ int writerFillRate; // device write buffer fill rate 0..100
+};
+
+
+inline bool operator<( const ProgressMsg2& m1, const ProgressMsg2& m2 )
+{
+ return m1.track < m2.track
+ || ( m1.track == m2.track
+ && m1.trackProgress < m2.trackProgress )
+ || m1.totalProgress < m2.totalProgress;
+}
+
+
+inline bool operator==( const ProgressMsg2& m1, const ProgressMsg2& m2 )
+{
+ return m1.status == m2.status
+ && m1.track == m2.track
+ && m1.totalTracks == m2.totalTracks
+ && m1.trackProgress == m2.trackProgress
+ && m1.totalProgress == m2.totalProgress
+ && m1.bufferFillRate == m2.bufferFillRate;
+}
+
+inline bool operator!=( const ProgressMsg2& m1, const ProgressMsg2& m2 )
+{
+ return !( m1 == m2 );
+}
+
+
+
+class K3bCdrdaoWriter::Private
+{
+public:
+ Private() {
+ }
+
+ K3bThroughputEstimator* speedEst;
+
+ int usedSpeed;
+
+ ProgressMsg2 oldMsg;
+ ProgressMsg2 newMsg;
+
+ unsigned int progressMsgSize;
+};
+
+
+K3bCdrdaoWriter::K3bCdrdaoWriter( K3bDevice::Device* dev, K3bJobHandler* hdl,
+ QObject* parent, const char* name )
+ : K3bAbstractWriter( dev, hdl, parent, name ),
+ m_command(WRITE),
+ m_blankMode(MINIMAL),
+ m_sourceDevice(0),
+ m_readRaw(false),
+ m_multi(false),
+ m_force(false),
+ m_onTheFly(false),
+ m_fastToc(false),
+ m_readSubchan(None),
+ m_taoSource(false),
+ m_taoSourceAdjust(-1),
+ m_paranoiaMode(-1),
+ m_session(-1),
+ m_process(0),
+ m_comSock(0),
+ m_currentTrack(0),
+ m_forceNoEject(false)
+{
+ d = new Private();
+ d->speedEst = new K3bThroughputEstimator( this );
+ connect( d->speedEst, SIGNAL(throughput(int)),
+ this, SLOT(slotThroughput(int)) );
+
+ m_eject = k3bcore->globalSettings()->ejectMedia();
+
+ ::memset( &d->oldMsg, 0, sizeof(ProgressMsg2) );
+ ::memset( &d->newMsg, 0, sizeof(ProgressMsg2) );
+
+ if( socketpair(AF_UNIX,SOCK_STREAM,0,m_cdrdaoComm) )
+ {
+ kdDebug() << "(K3bCdrdaoWriter) could not open socketpair for cdrdao remote messages" << endl;
+ }
+ else
+ {
+ delete m_comSock;
+ m_comSock = new QSocket();
+ m_comSock->setSocket(m_cdrdaoComm[1]);
+ m_comSock->socketDevice()->setReceiveBufferSize(49152);
+ // magic number from Qt documentation
+ m_comSock->socketDevice()->setBlocking(false);
+ connect( m_comSock, SIGNAL(readyRead()),
+ this, SLOT(parseCdrdaoMessage()));
+ }
+}
+
+K3bCdrdaoWriter::~K3bCdrdaoWriter()
+{
+ delete d->speedEst;
+ delete d;
+
+ // close the socket
+ if( m_comSock ) {
+ m_comSock->close();
+ ::close( m_cdrdaoComm[0] );
+ }
+ delete m_process;
+ delete m_comSock;
+}
+
+
+int K3bCdrdaoWriter::fd() const
+{
+ if( m_process )
+ return m_process->stdinFd();
+ else
+ return -1;
+}
+
+
+bool K3bCdrdaoWriter::active() const
+{
+ return (m_process ? m_process->isRunning() : false);
+}
+
+
+void K3bCdrdaoWriter::prepareArgumentList()
+{
+
+ // binary
+ *m_process << m_cdrdaoBinObject;
+
+ // command
+ switch ( m_command )
+ {
+ case COPY:
+ *m_process << "copy";
+ setWriteArguments();
+ setReadArguments();
+ setCopyArguments();
+ break;
+ case WRITE:
+ *m_process << "write";
+ setWriteArguments();
+ break;
+ case READ:
+ *m_process << "read-cd";
+ // source device and source driver
+ if ( m_sourceDevice )
+ *m_process << "--device"
+ << K3b::externalBinDeviceParameter(m_sourceDevice, m_cdrdaoBinObject);
+ if ( m_sourceDevice->cdrdaoDriver() != "auto" )
+ *m_process << "--driver" << m_sourceDevice->cdrdaoDriver();
+ else if( defaultToGenericMMC( m_sourceDevice, false ) ) {
+ kdDebug() << "(K3bCdrdaoWriter) defaulting to generic-mmc driver for " << m_sourceDevice->blockDeviceName() << endl;
+ *m_process << "--driver" << "generic-mmc";
+ }
+ setReadArguments();
+ break;
+ case BLANK:
+ *m_process << "blank";
+ setBlankArguments();
+ break;
+ }
+
+ setCommonArguments();
+}
+
+void K3bCdrdaoWriter::setWriteArguments()
+{
+ // device and driver
+ *m_process << "--device"
+ << K3b::externalBinDeviceParameter(burnDevice(), m_cdrdaoBinObject);
+
+ if( burnDevice()->cdrdaoDriver() != "auto" )
+ {
+ *m_process << "--driver";
+ if( burnDevice()->cdTextCapable() == 1 )
+ *m_process << QString("%1:0x00000010").arg( burnDevice()->cdrdaoDriver() );
+ else
+ *m_process << burnDevice()->cdrdaoDriver();
+ }
+ else if( defaultToGenericMMC( burnDevice(), true ) ) {
+ kdDebug() << "(K3bCdrdaoWriter) defaulting to generic-mmc driver for " << burnDevice()->blockDeviceName() << endl;
+ *m_process << "--driver" << "generic-mmc:0x00000010";
+ }
+
+ // burn speed
+ if( d->usedSpeed != 0 )
+ *m_process << "--speed" << QString("%1").arg(d->usedSpeed);
+
+ //simulate
+ if( simulate() )
+ *m_process << "--simulate";
+
+ // multi
+ if( m_multi )
+ *m_process << "--multi";
+
+ // force
+ if( m_force )
+ *m_process << "--force";
+
+ // burnproof
+ if ( !k3bcore->globalSettings()->burnfree() ) {
+ if( m_cdrdaoBinObject->hasFeature( "disable-burnproof" ) )
+ *m_process << "--buffer-under-run-protection" << "0";
+ else
+ emit infoMessage( i18n("Cdrdao %1 does not support disabling burnfree.").arg(m_cdrdaoBinObject->version), WARNING );
+ }
+
+ if( k3bcore->globalSettings()->force() ) {
+ *m_process << "--force";
+ emit infoMessage( i18n("'Force unsafe operations' enabled."), WARNING );
+ }
+
+ bool manualBufferSize =
+ k3bcore->globalSettings()->useManualBufferSize();
+ if( manualBufferSize ) {
+ //
+ // one buffer in cdrdao holds 1 second of audio data = 75 frames = 75 * 2352 bytes
+ //
+ int bufSizeInMb = k3bcore->globalSettings()->bufferSize();
+ *m_process << "--buffers" << QString::number( bufSizeInMb*1024*1024/(75*2352) );
+ }
+
+ bool overburn =
+ k3bcore->globalSettings()->overburn();
+ if( overburn ) {
+ if( m_cdrdaoBinObject->hasFeature("overburn") )
+ *m_process << "--overburn";
+ else
+ emit infoMessage( i18n("Cdrdao %1 does not support overburning.").arg(m_cdrdaoBinObject->version), WARNING );
+ }
+
+}
+
+void K3bCdrdaoWriter::setReadArguments()
+{
+ // readRaw
+ if ( m_readRaw )
+ *m_process << "--read-raw";
+
+ // subchan
+ if ( m_readSubchan != None )
+ {
+ *m_process << "--read-subchan";
+ switch ( m_readSubchan )
+ {
+ case RW:
+ *m_process << "rw";
+ break;
+ case RW_RAW:
+ *m_process << "rw_raw";
+ break;
+ case None:
+ break;
+ }
+ }
+
+ // TAO Source
+ if ( m_taoSource )
+ *m_process << "--tao-source";
+
+ // TAO Source Adjust
+ if ( m_taoSourceAdjust != -1 )
+ *m_process << "--tao-source-adjust"
+ << QString("%1").arg(m_taoSourceAdjust);
+
+ // paranoia Mode
+ if ( m_paranoiaMode != -1 )
+ *m_process << "--paranoia-mode"
+ << QString("%1").arg(m_paranoiaMode);
+
+ // session
+ if ( m_session != -1 )
+ *m_process << "--session"
+ << QString("%1").arg(m_session);
+
+ // fast TOC
+ if ( m_fastToc )
+ *m_process << "--fast-toc";
+
+}
+
+void K3bCdrdaoWriter::setCopyArguments()
+{
+ // source device and source driver
+ *m_process << "--source-device" << K3b::externalBinDeviceParameter(m_sourceDevice, m_cdrdaoBinObject);
+ if ( m_sourceDevice->cdrdaoDriver() != "auto" )
+ *m_process << "--source-driver" << m_sourceDevice->cdrdaoDriver();
+ else if( defaultToGenericMMC( m_sourceDevice, false ) ) {
+ kdDebug() << "(K3bCdrdaoWriter) defaulting to generic-mmc driver for " << m_sourceDevice->blockDeviceName() << endl;
+ *m_process << "--source-driver" << "generic-mmc";
+ }
+
+ // on-the-fly
+ if ( m_onTheFly )
+ *m_process << "--on-the-fly";
+}
+
+void K3bCdrdaoWriter::setBlankArguments()
+{
+ // device and driver
+ *m_process << "--device"
+ << K3b::externalBinDeviceParameter(burnDevice(), m_cdrdaoBinObject);
+
+ if( burnDevice()->cdrdaoDriver() != "auto" )
+ {
+ *m_process << "--driver";
+ if( burnDevice()->cdTextCapable() == 1 )
+ *m_process << QString("%1:0x00000010").arg( burnDevice()->cdrdaoDriver() );
+ else
+ *m_process << burnDevice()->cdrdaoDriver();
+ }
+ else if( defaultToGenericMMC( burnDevice(), true ) ) {
+ kdDebug() << "(K3bCdrdaoWriter) defaulting to generic-mmc driver for " << burnDevice()->blockDeviceName() << endl;
+ *m_process << "--driver" << "generic-mmc";
+ }
+
+ // burn speed
+ if( d->usedSpeed != 0 )
+ *m_process << "--speed" << QString("%1").arg(d->usedSpeed);
+
+ // blank-mode
+ *m_process << "--blank-mode";
+ switch (m_blankMode)
+ {
+ case FULL:
+ *m_process << "full";
+ break;
+ case MINIMAL:
+ *m_process << "minimal";
+ break;
+ }
+}
+
+void K3bCdrdaoWriter::setCommonArguments()
+{
+
+ // additional user parameters from config
+ const QStringList& params = m_cdrdaoBinObject->userParameters();
+ for( QStringList::const_iterator it = params.begin(); it != params.end(); ++it )
+ *m_process << *it;
+
+
+ // display debug info
+ *m_process << "-n" << "-v" << "2";
+
+ // we have the power to do what ever we want. ;)
+ *m_process << "--force";
+
+ // eject
+ if( m_eject && !m_forceNoEject )
+ *m_process << "--eject";
+
+ // remote
+ *m_process << "--remote" << QString("%1").arg(m_cdrdaoComm[0]);
+
+ // data File
+ if ( ! m_dataFile.isEmpty() )
+ *m_process << "--datafile" << m_dataFile;
+
+ // BIN/CUE
+ if ( ! m_cueFileLnk.isEmpty() )
+ *m_process << m_cueFileLnk;
+ // TOC File
+ else if ( ! m_tocFile.isEmpty() )
+ *m_process << m_tocFile;
+}
+
+K3bCdrdaoWriter* K3bCdrdaoWriter::addArgument( const QString& arg )
+{
+ *m_process << arg;
+ return this;
+}
+
+
+void K3bCdrdaoWriter::start()
+{
+ jobStarted();
+
+ d->speedEst->reset();
+
+ delete m_process; // kdelibs want this!
+ m_process = new K3bProcess();
+ m_process->setRunPrivileged(true);
+ m_process->setSplitStdout(false);
+ m_process->setRawStdin(true);
+ connect( m_process, SIGNAL(stderrLine(const QString&)),
+ this, SLOT(slotStdLine(const QString&)) );
+ connect( m_process, SIGNAL(processExited(KProcess*)),
+ this, SLOT(slotProcessExited(KProcess*)) );
+
+ m_canceled = false;
+ m_knownError = false;
+
+ m_cdrdaoBinObject = k3bcore->externalBinManager()->binObject("cdrdao");
+
+ if( !m_cdrdaoBinObject ) {
+ emit infoMessage( i18n("Could not find %1 executable.").arg("cdrdao"), ERROR );
+ jobFinished(false);
+ return;
+ }
+
+ emit debuggingOutput( "Used versions", "cdrdao: " + m_cdrdaoBinObject->version );
+
+ if( !m_cdrdaoBinObject->copyright.isEmpty() )
+ emit infoMessage( i18n("Using %1 %2 - Copyright (C) %3").arg(m_cdrdaoBinObject->name()).arg(m_cdrdaoBinObject->version).arg(m_cdrdaoBinObject->copyright), INFO );
+
+
+ // the message size changed in cdrdao 1.1.8)
+ if( m_cdrdaoBinObject->version >= K3bVersion( 1, 1, 8 ) )
+ d->progressMsgSize = sizeof(ProgressMsg2);
+ else
+ d->progressMsgSize = sizeof(ProgressMsg);
+
+ // since the --speed parameter is used several times in this code we
+ // determine the speed in auto once at the beginning
+ d->usedSpeed = burnSpeed();
+ if( d->usedSpeed == 0 ) {
+ // try to determine the writeSpeed
+ // if it fails determineMaximalWriteSpeed() will return 0 and
+ // the choice is left to cdrdao
+ d->usedSpeed = burnDevice()->determineMaximalWriteSpeed();
+ }
+ d->usedSpeed /= 175;
+
+ switch ( m_command )
+ {
+ case WRITE:
+ case COPY:
+ if (!m_tocFile.isEmpty())
+ {
+
+ // if tocfile is a cuesheet than create symlinks to *.cue and the binary listed inside the cuesheet.
+ // now works without the .bin extension too.
+ if ( !cueSheet() ) {
+ m_backupTocFile = m_tocFile + ".k3bbak";
+
+ // workaround, cdrdao deletes the tocfile when --remote parameter is set
+ if ( !KIO::NetAccess::copy(KURL(m_tocFile),KURL(m_backupTocFile), (QWidget*) 0) )
+ {
+ kdDebug() << "(K3bCdrdaoWriter) could not backup " << m_tocFile << " to " << m_backupTocFile << endl;
+ emit infoMessage( i18n("Could not backup tocfile."), ERROR );
+ jobFinished(false);
+ return;
+ }
+ }
+ }
+ break;
+ case BLANK:
+ case READ:
+ break;
+ }
+ prepareArgumentList();
+ // set working dir to dir part of toc file (to allow rel names in toc-file)
+ m_process->setWorkingDirectory(QUrl(m_tocFile).dirPath());
+
+ kdDebug() << "***** cdrdao parameters:\n";
+ const QValueList<QCString>& args = m_process->args();
+ QString s;
+ for( QValueList<QCString>::const_iterator it = args.begin(); it != args.end(); ++it )
+ {
+ s += *it + " ";
+ }
+ kdDebug() << s << flush << endl;
+ emit debuggingOutput("cdrdao command:", s);
+
+ m_currentTrack = 0;
+ reinitParser();
+
+ switch ( m_command )
+ {
+ case READ:
+ emit newSubTask( i18n("Preparing read process...") );
+ break;
+ case WRITE:
+ emit newSubTask( i18n("Preparing write process...") );
+ break;
+ case COPY:
+ emit newSubTask( i18n("Preparing copy process...") );
+ break;
+ case BLANK:
+ emit newSubTask( i18n("Preparing blanking process...") );
+ break;
+ }
+
+ // FIXME: check the return value
+ if( K3b::isMounted( burnDevice() ) ) {
+ emit infoMessage( i18n("Unmounting medium"), INFO );
+ K3b::unmount( burnDevice() );
+ }
+
+ // block the device (including certain checks)
+ k3bcore->blockDevice( burnDevice() );
+
+ // lock the device for good in this process since it will
+ // be opened in the growisofs process
+ burnDevice()->close();
+ burnDevice()->usageLock();
+
+ if( !m_process->start( KProcess::NotifyOnExit, KProcess::AllOutput ) )
+ {
+ // something went wrong when starting the program
+ // it "should" be the executable
+ kdDebug() << "(K3bCdrdaoWriter) could not start cdrdao" << endl;
+ emit infoMessage( i18n("Could not start %1.").arg("cdrdao"), K3bJob::ERROR );
+ jobFinished(false);
+ }
+ else
+ {
+ switch ( m_command )
+ {
+ case WRITE:
+ if( simulate() )
+ {
+ emit infoMessage(i18n("Starting DAO simulation at %1x speed...").arg(d->usedSpeed),
+ K3bJob::INFO );
+ emit newTask( i18n("Simulating") );
+ }
+ else
+ {
+ emit infoMessage( i18n("Starting DAO writing at %1x speed...").arg(d->usedSpeed), K3bJob::INFO );
+ emit newTask( i18n("Writing") );
+ }
+ break;
+ case READ:
+ emit infoMessage(i18n("Starting reading..."), K3bJob::INFO );
+ emit newTask( i18n("Reading") );
+ break;
+ case COPY:
+ if( simulate() )
+ {
+ emit infoMessage(i18n("Starting simulation copy at %1x speed...").arg(d->usedSpeed), K3bJob::INFO );
+ emit newTask( i18n("Simulating") );
+ }
+ else
+ {
+ emit infoMessage( i18n("Starting copy at %1x speed...").arg(d->usedSpeed), K3bJob::INFO );
+ emit newTask( i18n("Copying") );
+ }
+ break;
+ case BLANK:
+ emit infoMessage(i18n("Starting blanking..."), K3bJob::INFO );
+ emit newTask( i18n("Blanking") );
+ }
+ }
+}
+
+
+void K3bCdrdaoWriter::cancel()
+{
+ m_canceled = true;
+
+ if( m_process ) {
+ if( m_process->isRunning() ) {
+ m_process->disconnect();
+ m_process->kill();
+
+ // we need to unlock the device because cdrdao locked it while writing
+ //
+ // FIXME: try to determine wheater we are writing or reading and choose
+ // the device to unblock based on that result.
+ //
+ if( m_command == READ ) {
+ // FIXME: this is a hack
+ setBurnDevice( m_sourceDevice );
+ }
+
+ // this will unblock and eject the drive and emit the finished/canceled signals
+ K3bAbstractWriter::cancel();
+ }
+ }
+}
+
+
+bool K3bCdrdaoWriter::cueSheet()
+{
+
+ // TODO: do this in the K3bCueFileParser
+
+ if ( m_tocFile.lower().endsWith( ".cue" ) ) {
+ QFile f( m_tocFile );
+ if ( f.open( IO_ReadOnly ) ) {
+ QTextStream ts( &f );
+ if ( !ts.eof() ) {
+ QString line = ts.readLine();
+ f.close();
+ int pos = line.find( "FILE \"" );
+ if( pos < 0 )
+ return false;
+
+ pos += 6;
+ int endPos = line.find( "\" BINARY", pos+1 );
+ if( endPos < 0 )
+ return false;
+
+ line = line.mid( pos, endPos-pos );
+ QFileInfo fi( QFileInfo( m_tocFile ).dirPath() + "/" + QFileInfo( line ).fileName() );
+ QString binpath = fi.filePath();
+ kdDebug() << QString("K3bCdrdaoWriter::cueSheet() BinFilePath from CueFile: %1").arg( line ) << endl;
+ kdDebug() << QString("K3bCdrdaoWriter::cueSheet() absolute BinFilePath: %1").arg( binpath ) << endl;
+
+ if ( !fi.exists() )
+ return false;
+
+ KTempFile tempF;
+ QString tempFile = tempF.name();
+ tempF.unlink();
+
+ if ( symlink(QFile::encodeName( binpath ), QFile::encodeName( tempFile + ".bin") ) == -1 )
+ return false;
+ if ( symlink(QFile::encodeName( m_tocFile ), QFile::encodeName( tempFile + ".cue") ) == -1 )
+ return false;
+
+ kdDebug() << QString("K3bCdrdaoWriter::cueSheet() symlink BinFileName: %1.bin").arg( tempFile ) << endl;
+ kdDebug() << QString("K3bCdrdaoWriter::cueSheet() symlink CueFileName: %1.cue").arg( tempFile ) << endl;
+ m_binFileLnk = tempFile + ".bin";
+ m_cueFileLnk = tempFile + ".cue";
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void K3bCdrdaoWriter::slotStdLine( const QString& line )
+{
+ parseCdrdaoLine(line);
+}
+
+
+void K3bCdrdaoWriter::slotProcessExited( KProcess* p )
+{
+ // release the device within this process
+ burnDevice()->usageUnlock();
+
+ // unblock the device
+ k3bcore->unblockDevice( burnDevice() );
+
+ switch ( m_command )
+ {
+ case WRITE:
+ case COPY:
+ if ( !m_binFileLnk.isEmpty() ) {
+ KIO::NetAccess::del(KURL::fromPathOrURL(m_cueFileLnk), (QWidget*) 0);
+ KIO::NetAccess::del(KURL::fromPathOrURL(m_binFileLnk), (QWidget*) 0);
+ }
+ else if( (!QFile::exists( m_tocFile ) || K3b::filesize( KURL::fromPathOrURL(m_tocFile) ) == 0 ) && !m_onTheFly )
+ {
+ // cdrdao removed the tocfile :(
+ // we need to recover it
+ if ( !KIO::NetAccess::copy(KURL::fromPathOrURL(m_backupTocFile), KURL::fromPathOrURL(m_tocFile), (QWidget*) 0) )
+ {
+ kdDebug() << "(K3bCdrdaoWriter) restoring tocfile " << m_tocFile << " failed." << endl;
+ emit infoMessage( i18n("Due to a bug in cdrdao the toc/cue file %1 has been deleted. "
+ "K3b was unable to restore it from the backup %2.").arg(m_tocFile).arg(m_backupTocFile), ERROR );
+ }
+ else if ( !KIO::NetAccess::del(KURL::fromPathOrURL(m_backupTocFile), (QWidget*) 0) )
+ {
+ kdDebug() << "(K3bCdrdaoWriter) delete tocfile backkup " << m_backupTocFile << " failed." << endl;
+ }
+ }
+ break;
+ case BLANK:
+ case READ:
+ break;
+ }
+
+ if( m_canceled )
+ return;
+
+ if( p->normalExit() )
+ {
+ switch( p->exitStatus() )
+ {
+ case 0:
+ if( simulate() )
+ emit infoMessage( i18n("Simulation successfully completed"), K3bJob::SUCCESS );
+ else
+ switch ( m_command )
+ {
+ case READ:
+ emit infoMessage( i18n("Reading successfully completed"), K3bJob::SUCCESS );
+ break;
+ case WRITE:
+ emit infoMessage( i18n("Writing successfully completed"), K3bJob::SUCCESS );
+ break;
+ case COPY:
+ emit infoMessage( i18n("Copying successfully completed"), K3bJob::SUCCESS );
+ break;
+ case BLANK:
+ emit infoMessage( i18n("Blanking successfully completed"), K3bJob::SUCCESS );
+ break;
+ }
+
+ if( m_command == WRITE || m_command == COPY ) {
+ int s = d->speedEst->average();
+ emit infoMessage( i18n("Average overall write speed: %1 KB/s (%2x)").arg(s).arg(KGlobal::locale()->formatNumber((double)s/150.0), 2), INFO );
+ }
+
+ jobFinished( true );
+ break;
+
+ default:
+ if( !m_knownError && !wasSourceUnreadable() ) {
+ emit infoMessage( i18n("%1 returned an unknown error (code %2).").arg(m_cdrdaoBinObject->name()).arg(p->exitStatus()),
+ K3bJob::ERROR );
+ emit infoMessage( i18n("Please include the debugging output in your problem report."), K3bJob::ERROR );
+ }
+
+ jobFinished( false );
+ break;
+ }
+ }
+ else
+ {
+ emit infoMessage( i18n("%1 did not exit cleanly.").arg("cdrdao"), K3bJob::ERROR );
+ jobFinished( false );
+ }
+}
+
+
+void K3bCdrdaoWriter::unknownCdrdaoLine( const QString& line )
+{
+ if( line.contains( "at speed" ) )
+ {
+ // parse the speed and inform the user if cdrdao switched it down
+ int pos = line.find( "at speed" );
+ int po2 = line.find( QRegExp("\\D"), pos + 9 );
+ int speed = line.mid( pos+9, po2-pos-9 ).toInt();
+ if( speed < d->usedSpeed )
+ {
+ emit infoMessage( i18n("Medium or burner do not support writing at %1x speed").arg(d->usedSpeed), K3bJob::WARNING );
+ emit infoMessage( i18n("Switching down burn speed to %1x").arg(speed), K3bJob::WARNING );
+ }
+ }
+}
+
+
+void K3bCdrdaoWriter::reinitParser()
+{
+ ::memset( &d->oldMsg, 0, sizeof(ProgressMsg2) );
+ ::memset( &d->newMsg, 0, sizeof(ProgressMsg2) );
+
+ m_currentTrack=0;
+}
+
+void K3bCdrdaoWriter::parseCdrdaoLine( const QString& str )
+{
+ emit debuggingOutput( "cdrdao", str );
+ // kdDebug() << "(cdrdaoparse)" << str << endl;
+ // find some messages from cdrdao
+ // -----------------------------------------------------------------------------------------
+ if( (str).startsWith( "Warning" ) || (str).startsWith( "WARNING" ) || (str).startsWith( "ERROR" ) )
+ {
+ parseCdrdaoError( str );
+ }
+ else if( (str).startsWith( "Wrote" ) && !str.contains("blocks") )
+ {
+ parseCdrdaoWrote( str );
+ }
+ else if( (str).startsWith( "Executing power" ) )
+ {
+ emit newSubTask( i18n("Executing Power calibration") );
+ }
+ else if( (str).startsWith( "Power calibration successful" ) )
+ {
+ emit infoMessage( i18n("Power calibration successful"), K3bJob::INFO );
+ emit newSubTask( i18n("Preparing burn process...") );
+ }
+ else if( (str).startsWith( "Flushing cache" ) )
+ {
+ emit newSubTask( i18n("Flushing cache") );
+ }
+ else if( (str).startsWith( "Writing CD-TEXT lead" ) )
+ {
+ emit newSubTask( i18n("Writing CD-Text lead-in...") );
+ }
+ else if( (str).startsWith( "Turning BURN-Proof on" ) )
+ {
+ emit infoMessage( i18n("Turning BURN-Proof on"), K3bJob::INFO );
+ }
+ else if( str.startsWith( "Copying" ) )
+ {
+ emit infoMessage( str, K3bJob::INFO );
+ }
+ else if( str.startsWith( "Found ISRC" ) )
+ {
+ emit infoMessage( i18n("Found ISRC code"), K3bJob::INFO );
+ }
+ else if( str.startsWith( "Found pre-gap" ) )
+ {
+ emit infoMessage( i18n("Found pregap: %1").arg( str.mid(str.find(":")+1) ), K3bJob::INFO );
+ }
+ else
+ unknownCdrdaoLine(str);
+}
+
+void K3bCdrdaoWriter::parseCdrdaoError( const QString& line )
+{
+ int pos = -1;
+
+ if( line.contains( "No driver found" ) ||
+ line.contains( "use option --driver" ) )
+ {
+ emit infoMessage( i18n("No cdrdao driver found."), K3bJob::ERROR );
+ emit infoMessage( i18n("Please select one manually in the device settings."), K3bJob::ERROR );
+ emit infoMessage( i18n("For most current drives this would be 'generic-mmc'."), K3bJob::ERROR );
+ m_knownError = true;
+ }
+ else if( line.contains( "Cannot setup device" ) )
+ {
+ // no nothing...
+ }
+ else if( line.contains( "not ready") )
+ {
+ emit infoMessage( i18n("Device not ready, waiting."),K3bJob::WARNING );
+ }
+ else if( line.contains("Drive does not accept any cue sheet") )
+ {
+ emit infoMessage( i18n("Cue sheet not accepted."), K3bJob::ERROR );
+ m_knownError = true;
+ }
+ else if( (pos = line.find( "Illegal option" )) > 0 ) {
+ // ERROR: Illegal option: -wurst
+ emit infoMessage( i18n("No valid %1 option: %2").arg(m_cdrdaoBinObject->name()).arg(line.mid(pos+16)),
+ ERROR );
+ m_knownError = true;
+ }
+ else if( line.contains( "exceeds capacity" ) ) {
+ emit infoMessage( i18n("Data does not fit on disk."), ERROR );
+ if( m_cdrdaoBinObject->hasFeature("overburn") )
+ emit infoMessage( i18n("Enable overburning in the advanced K3b settings to burn anyway."), INFO );
+ m_knownError = true;
+ }
+ // else if( !line.contains( "remote progress message" ) )
+// emit infoMessage( line, K3bJob::ERROR );
+}
+
+void K3bCdrdaoWriter::parseCdrdaoWrote( const QString& line )
+{
+ int pos, po2;
+ pos = line.find( "Wrote" );
+ po2 = line.find( " ", pos + 6 );
+ int processed = line.mid( pos+6, po2-pos-6 ).toInt();
+
+ pos = line.find( "of" );
+ po2 = line.find( " ", pos + 3 );
+ m_size = line.mid( pos+3, po2-pos-3 ).toInt();
+
+ d->speedEst->dataWritten( processed*1024 );
+
+ emit processedSize( processed, m_size );
+}
+
+
+void K3bCdrdaoWriter::parseCdrdaoMessage()
+{
+ static const char msgSync[] = { 0xff, 0x00, 0xff, 0x00 };
+ unsigned int avail = m_comSock->bytesAvailable();
+ unsigned int msgs = avail / ( sizeof(msgSync)+d->progressMsgSize );
+ unsigned int count = 0;
+
+ if ( msgs < 1 )
+ return;
+ else if ( msgs > 1) {
+ // move the read-index forward to the beginnig of the most recent message
+ count = ( msgs-1 ) * ( sizeof(msgSync)+d->progressMsgSize );
+ m_comSock->at(count);
+ kdDebug() << "(K3bCdrdaoParser) " << msgs-1 << " message(s) skipped" << endl;
+ }
+
+ while( count < avail ) {
+
+ // search for msg sync
+ int state = 0;
+ char buf;
+ while( state < 4 ) {
+ buf = m_comSock->getch();
+ ++count;
+ if( count == avail ) {
+ // kdDebug() << "(K3bCdrdaoParser) remote message sync not found (" << count << ")" << endl;
+ return;
+ }
+
+ if( buf == msgSync[state] )
+ ++state;
+ else
+ state = 0;
+ }
+
+ if( (avail - count) < d->progressMsgSize ) {
+ kdDebug() << "(K3bCdrdaoParser) could not read complete remote message." << endl;
+ return;
+ }
+
+ // read one message (the message size changed in cdrdao 1.1.8)
+ ::memset( &d->newMsg, 0, d->progressMsgSize );
+ int size = m_comSock->readBlock( (char*)&d->newMsg, d->progressMsgSize);
+ if( size == -1 ) {
+ kdDebug() << "(K3bCdrdaoParser) read error" << endl;
+ return;
+ }
+ count += size;
+
+ // sometimes the progress takes one step back (on my system when using paranoia-level 3)
+ // so we just use messages that are greater than the previous or first messages
+ if( d->oldMsg < d->newMsg
+ || ( d->newMsg.track == 1 &&
+ d->newMsg.trackProgress <= 10 )) {
+
+ if( d->newMsg.track != m_currentTrack ) {
+ switch( d->newMsg.status ) {
+ case PGSMSG_RCD_EXTRACTING:
+ emit nextTrack( d->newMsg.track, d->newMsg.totalTracks );
+ break;
+ case PGSMSG_WCD_LEADIN:
+ emit newSubTask( i18n("Writing leadin ") );
+ break;
+ case PGSMSG_WCD_DATA:
+ emit nextTrack( d->newMsg.track, d->newMsg.totalTracks );
+ break;
+ case PGSMSG_WCD_LEADOUT:
+ emit newSubTask( i18n("Writing leadout ") );
+ break;
+ }
+
+ m_currentTrack = d->newMsg.track;
+ }
+
+ if( d->newMsg.status == PGSMSG_WCD_LEADIN || d->newMsg.status == PGSMSG_WCD_LEADOUT ) {
+ // cdrdao >= 1.1.8 emits progress data when writing the lead-in and lead-out :)
+ emit subPercent( d->newMsg.totalProgress/10 );
+ }
+ else {
+ emit subPercent( d->newMsg.trackProgress/10 );
+ emit percent( d->newMsg.totalProgress/10 );
+ }
+
+ emit buffer(d->newMsg.bufferFillRate);
+
+ if( d->progressMsgSize == (unsigned int)sizeof(ProgressMsg2) )
+ emit deviceBuffer( d->newMsg.writerFillRate );
+
+ ::memcpy( &d->oldMsg, &d->newMsg, d->progressMsgSize );
+ }
+ }
+}
+
+
+void K3bCdrdaoWriter::slotThroughput( int t )
+{
+ // FIXME: determine sector size
+ emit writeSpeed( t, 150 );
+}
+
+
+QString K3bCdrdaoWriter::findDriverFile( const K3bExternalBin* bin )
+{
+ if( !bin )
+ return QString::null;
+
+ // cdrdao normally in (prefix)/bin and driver table in (prefix)/share/cdrdao
+ QString path = bin->path;
+ path.truncate( path.findRev("/") );
+ path.truncate( path.findRev("/") );
+ path += "/share/cdrdao/drivers";
+ if( QFile::exists(path) )
+ return path;
+ else {
+ kdDebug() << "(K3bCdrdaoWriter) could not find cdrdao driver table." << endl;
+ return QString::null;
+ }
+}
+
+
+// returns true if the driver file could be opened and no driver could be found
+// TODO: cache the drivers
+bool K3bCdrdaoWriter::defaultToGenericMMC( K3bDevice::Device* dev, bool writer )
+{
+ QString driverTable = findDriverFile( m_cdrdaoBinObject );
+ if( !driverTable.isEmpty() ) {
+ QFile f( driverTable );
+ if( f.open( IO_ReadOnly ) ) {
+ // read all drivers
+ QStringList drivers;
+ QTextStream fStr( &f );
+ while( !fStr.atEnd() ) {
+ QString line = fStr.readLine();
+ if( line.isEmpty() )
+ continue;
+ if( line[0] == '#' )
+ continue;
+ if( line[0] == 'R' && writer )
+ continue;
+ if( line[0] == 'W' && !writer )
+ continue;
+ drivers.append(line);
+ }
+
+ // search for the driver
+ for( QStringList::const_iterator it = drivers.begin(); it != drivers.end(); ++it ) {
+ if( (*it).section( '|', 1, 1 ) == dev->vendor() &&
+ (*it).section( '|', 2, 2 ) == dev->description() )
+ return false;
+ }
+
+ // no driver found
+ return true;
+ }
+ else {
+ kdDebug() << "(K3bCdrdaoWriter) could not open driver table " << driverTable << endl;
+ return false;
+ }
+ }
+ else
+ return false;
+}
+
+
+#include "k3bcdrdaowriter.moc"
diff --git a/libk3b/projects/k3bcdrdaowriter.h b/libk3b/projects/k3bcdrdaowriter.h
new file mode 100644
index 0000000..94a0c9f
--- /dev/null
+++ b/libk3b/projects/k3bcdrdaowriter.h
@@ -0,0 +1,157 @@
+/*
+ *
+ * $Id: k3bcdrdaowriter.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ * Klaus-Dieter Krannich <kd@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3B_CDRDAO_WRITER_H
+#define K3B_CDRDAO_WRITER_H
+
+
+#include "k3babstractwriter.h"
+
+class K3bExternalBin;
+class K3bProcess;
+class KProcess;
+class K3bDevice::Device;
+class QSocket;
+
+
+
+class K3bCdrdaoWriter : public K3bAbstractWriter
+{
+ Q_OBJECT
+
+ public:
+
+ enum Command { WRITE, COPY, READ, BLANK };
+ enum BlankMode { FULL, MINIMAL };
+ enum SubMode { None, RW, RW_RAW };
+
+ K3bCdrdaoWriter( K3bDevice::Device* dev, K3bJobHandler*,
+ QObject* parent = 0, const char* name = 0 );
+ ~K3bCdrdaoWriter();
+
+ /**
+ * to be used in chain: addArgument(x)->addArgument(y)
+ */
+ K3bCdrdaoWriter* addArgument( const QString& );
+ K3bDevice::Device* sourceDevice() { return m_sourceDevice; };
+
+ int fd() const;
+
+ bool active() const;
+
+ private:
+ void reinitParser();
+ void parseCdrdaoLine( const QString& line );
+ void parseCdrdaoWrote( const QString& line );
+ void parseCdrdaoError( const QString& line );
+
+ public slots:
+ void start();
+ void cancel();
+
+ // options
+ // ---------------------
+ void setCommand( int c ) { m_command = c; }
+ void setBlankMode( int b ) { m_blankMode = b; }
+ void setMulti( bool b ) { m_multi = b; }
+ void setForce( bool b ) { m_force = b; }
+ void setOnTheFly( bool b ) { m_onTheFly = b; }
+ void setDataFile( const QString& s ) { m_dataFile = s; }
+ void setTocFile( const QString& s ) { m_tocFile = s; }
+
+ void setSourceDevice( K3bDevice::Device* dev ) { m_sourceDevice = dev; }
+ void setFastToc( bool b ) { m_fastToc = b; }
+ void setReadRaw( bool b ) { m_readRaw = b; }
+ void setReadSubchan(SubMode m) { m_readSubchan=m; };
+ void setParanoiaMode( int i ) { m_paranoiaMode = i; }
+ void setTaoSource(bool b) { m_taoSource=b; };
+ void setTaoSourceAdjust(int a) { m_taoSourceAdjust=a; };
+ void setSession(int s) { m_session=s; };
+ void setEject(bool e) { m_eject=e; };
+// ---------------------
+
+ /**
+ * If set true the job ignores the global K3b setting
+ * and does not eject the CD-RW after finishing
+ */
+ void setForceNoEject( bool b ) { m_forceNoEject = b; }
+
+ private slots:
+ void slotStdLine( const QString& line );
+ void slotProcessExited(KProcess*);
+ void parseCdrdaoMessage();
+ void slotThroughput( int t );
+
+ private:
+ void unknownCdrdaoLine( const QString& );
+ void prepareArgumentList();
+ void setWriteArguments();
+ void setReadArguments();
+ void setCopyArguments();
+ void setBlankArguments();
+ void setCommonArguments();
+
+ bool cueSheet();
+
+ QString findDriverFile( const K3bExternalBin* bin );
+ bool defaultToGenericMMC( K3bDevice::Device* dev, bool writer );
+
+ // options
+ // ---------------------
+ int m_command;
+ int m_blankMode;
+ K3bDevice::Device* m_sourceDevice;
+ QString m_dataFile;
+ QString m_tocFile;
+ QString m_cueFileLnk;
+ QString m_binFileLnk;
+ QString m_backupTocFile;
+ bool m_readRaw;
+ bool m_multi;
+ bool m_force;
+ bool m_onTheFly;
+ bool m_fastToc;
+ SubMode m_readSubchan;
+ bool m_taoSource;
+ int m_taoSourceAdjust;
+ int m_paranoiaMode;
+ int m_session;
+ bool m_eject;
+ // ---------------------
+
+ const K3bExternalBin* m_cdrdaoBinObject;
+ K3bProcess* m_process;
+
+ int m_cdrdaoComm[2];
+ QSocket *m_comSock;
+
+ bool m_canceled;
+
+ bool m_knownError;
+
+// parser
+
+ int m_size;
+ int m_currentTrack;
+
+ bool m_forceNoEject;
+
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/projects/k3bcdrecordwriter.cpp b/libk3b/projects/k3bcdrecordwriter.cpp
new file mode 100644
index 0000000..e87c767
--- /dev/null
+++ b/libk3b/projects/k3bcdrecordwriter.cpp
@@ -0,0 +1,810 @@
+/*
+ *
+ * $Id: k3bcdrecordwriter.cpp 690529 2007-07-21 10:51:47Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include <config.h>
+
+
+#include "k3bcdrecordwriter.h"
+
+#include <k3bcore.h>
+#include <k3bexternalbinmanager.h>
+#include <k3bprocess.h>
+#include <k3bdevice.h>
+#include <k3bdevicemanager.h>
+#include <k3bdevicehandler.h>
+#include <k3bglobals.h>
+#include <k3bthroughputestimator.h>
+#include <k3bglobalsettings.h>
+#include <k3btempfile.h>
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qurl.h>
+#include <qvaluelist.h>
+#include <qregexp.h>
+#include <qfile.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kglobal.h>
+
+
+
+class K3bCdrecordWriter::Private
+{
+public:
+ Private()
+ : cdTextFile(0) {
+ }
+
+ K3bThroughputEstimator* speedEst;
+ bool canceled;
+ bool usingBurnfree;
+ int usedSpeed;
+
+ struct Track {
+ int size;
+ bool audio;
+ };
+
+ QValueList<Track> tracks;
+
+ KTempFile* cdTextFile;
+};
+
+
+K3bCdrecordWriter::K3bCdrecordWriter( K3bDevice::Device* dev, K3bJobHandler* hdl,
+ QObject* parent, const char* name )
+ : K3bAbstractWriter( dev, hdl, parent, name ),
+ m_clone(false),
+ m_cue(false),
+ m_forceNoEject(false)
+{
+ d = new Private();
+ d->speedEst = new K3bThroughputEstimator( this );
+ connect( d->speedEst, SIGNAL(throughput(int)),
+ this, SLOT(slotThroughput(int)) );
+
+ m_process = 0;
+ m_writingMode = K3b::TAO;
+}
+
+
+K3bCdrecordWriter::~K3bCdrecordWriter()
+{
+ delete d->cdTextFile;
+ delete d;
+ delete m_process;
+}
+
+
+bool K3bCdrecordWriter::active() const
+{
+ return ( m_process && m_process->isRunning() );
+}
+
+
+int K3bCdrecordWriter::fd() const
+{
+ if( m_process )
+ return m_process->stdinFd();
+ else
+ return -1;
+}
+
+
+void K3bCdrecordWriter::setDao( bool b )
+{
+ m_writingMode = ( b ? K3b::DAO : K3b::TAO );
+}
+
+void K3bCdrecordWriter::setCueFile( const QString& s)
+{
+ m_cue = true;
+ m_cueFile = s;
+
+ // cuefile only works in DAO mode
+ setWritingMode( K3b::DAO );
+}
+
+void K3bCdrecordWriter::setClone( bool b )
+{
+ m_clone = b;
+}
+
+
+void K3bCdrecordWriter::setWritingMode( int mode )
+{
+ if( mode == K3b::DAO ||
+ mode == K3b::TAO ||
+ mode == K3b::RAW )
+ m_writingMode = mode;
+ else
+ kdError() << "(K3bCdrecordWriter) wrong writing mode: " << mode << endl;
+}
+
+
+void K3bCdrecordWriter::prepareProcess()
+{
+ if( m_process ) delete m_process; // kdelibs want this!
+ m_process = new K3bProcess();
+ m_process->setRunPrivileged(true);
+ // m_process->setPriority( KProcess::PrioHighest );
+ m_process->setSplitStdout(true);
+ m_process->setSuppressEmptyLines(true);
+ m_process->setRawStdin(true); // we only use stdin when writing on-the-fly
+ connect( m_process, SIGNAL(stdoutLine(const QString&)), this, SLOT(slotStdLine(const QString&)) );
+ connect( m_process, SIGNAL(stderrLine(const QString&)), this, SLOT(slotStdLine(const QString&)) );
+ connect( m_process, SIGNAL(processExited(KProcess*)), this, SLOT(slotProcessExited(KProcess*)) );
+
+ m_cdrecordBinObject = k3bcore->externalBinManager()->binObject("cdrecord");
+
+ if( !m_cdrecordBinObject )
+ return;
+
+ *m_process << m_cdrecordBinObject;
+
+ // display progress
+ *m_process << "-v";
+
+ if( m_cdrecordBinObject->hasFeature( "gracetime") )
+ *m_process << "gracetime=2"; // 2 is the lowest allowed value (Joerg, why do you do this to us?)
+
+ // Again we assume the device to be set!
+ *m_process << QString("dev=%1").arg(K3b::externalBinDeviceParameter(burnDevice(), m_cdrecordBinObject));
+
+ d->usedSpeed = burnSpeed();
+ if( d->usedSpeed == 0 ) {
+ // try to determine the writeSpeed
+ // if it fails determineMaximalWriteSpeed() will return 0 and
+ // the choice is left to cdrecord
+ d->usedSpeed = burnDevice()->determineMaximalWriteSpeed();
+ }
+ d->usedSpeed /= 175;
+ if( d->usedSpeed != 0 )
+ *m_process << QString("speed=%1").arg(d->usedSpeed);
+
+ if( m_writingMode == K3b::DAO || m_cue ) {
+ if( burnDevice()->dao() )
+ *m_process << "-dao";
+ else {
+ if( m_cdrecordBinObject->hasFeature( "tao" ) )
+ *m_process << "-tao";
+ emit infoMessage( i18n("Writer does not support disk at once (DAO) recording"), WARNING );
+ }
+ }
+ else if( m_writingMode == K3b::RAW ) {
+ if( burnDevice()->supportsWritingMode( K3bDevice::RAW_R96R ) )
+ *m_process << "-raw96r";
+ else if( burnDevice()->supportsWritingMode( K3bDevice::RAW_R16 ) )
+ *m_process << "-raw16";
+ else if( burnDevice()->supportsWritingMode( K3bDevice::RAW_R96P ) )
+ *m_process << "-raw96p";
+ else {
+ emit infoMessage( i18n("Writer does not support raw writing."), WARNING );
+ if( m_cdrecordBinObject->hasFeature( "tao" ) )
+ *m_process << "-tao";
+ }
+ }
+ else if( m_cdrecordBinObject->hasFeature( "tao" ) )
+ *m_process << "-tao";
+
+ if( simulate() )
+ *m_process << "-dummy";
+
+ d->usingBurnfree = false;
+ if( k3bcore->globalSettings()->burnfree() ) {
+ if( burnDevice()->burnproof() ) {
+
+ d->usingBurnfree = true;
+
+ // with cdrecord 1.11a02 burnproof was renamed to burnfree
+ if( m_cdrecordBinObject->hasFeature( "burnproof" ) )
+ *m_process << "driveropts=burnproof";
+ else
+ *m_process << "driveropts=burnfree";
+ }
+ else
+ emit infoMessage( i18n("Writer does not support buffer underrun free recording (Burnfree)"), WARNING );
+ }
+
+ if( k3bcore->globalSettings()->force() ) {
+ *m_process << "-force";
+ emit infoMessage( i18n("'Force unsafe operations' enabled."), WARNING );
+ }
+
+ if( m_cue ) {
+ m_process->setWorkingDirectory(QUrl(m_cueFile).dirPath());
+ *m_process << QString("cuefile=%1").arg( m_cueFile );
+ }
+
+ if( m_clone )
+ *m_process << "-clone";
+
+ if( m_rawCdText.size() > 0 ) {
+ delete d->cdTextFile;
+ d->cdTextFile = new K3bTempFile( QString::null, ".dat" );
+ d->cdTextFile->setAutoDelete(true);
+ d->cdTextFile->file()->writeBlock( m_rawCdText );
+ d->cdTextFile->close();
+
+ *m_process << "textfile=" + d->cdTextFile->name();
+ }
+
+ if( k3bcore->globalSettings()->ejectMedia() &&
+ !m_forceNoEject )
+ *m_process << "-eject";
+
+ bool manualBufferSize = k3bcore->globalSettings()->useManualBufferSize();
+ if( manualBufferSize ) {
+ *m_process << QString("fs=%1m").arg( k3bcore->globalSettings()->bufferSize() );
+ }
+
+ bool overburn = k3bcore->globalSettings()->overburn();
+ if( overburn )
+ if( m_cdrecordBinObject->hasFeature("overburn") )
+ *m_process << "-overburn";
+ else
+ emit infoMessage( i18n("Cdrecord %1 does not support overburning.").arg(m_cdrecordBinObject->version), WARNING );
+
+ // additional user parameters from config
+ const QStringList& params = m_cdrecordBinObject->userParameters();
+ for( QStringList::const_iterator it = params.begin(); it != params.end(); ++it )
+ *m_process << *it;
+
+ // add the user parameters
+ for( QStringList::const_iterator it = m_arguments.begin(); it != m_arguments.end(); ++it )
+ *m_process << *it;
+}
+
+
+K3bCdrecordWriter* K3bCdrecordWriter::addArgument( const QString& arg )
+{
+ m_arguments.append( arg );
+ return this;
+}
+
+
+void K3bCdrecordWriter::clearArguments()
+{
+ m_arguments.clear();
+}
+
+
+void K3bCdrecordWriter::start()
+{
+ jobStarted();
+
+ d->canceled = false;
+ d->speedEst->reset();
+
+ prepareProcess();
+
+ if( !m_cdrecordBinObject ) {
+ emit infoMessage( i18n("Could not find %1 executable.").arg("cdrecord"), ERROR );
+ jobFinished(false);
+ return;
+ }
+
+ emit debuggingOutput( "Used versions", "cdrecord: " + m_cdrecordBinObject->version );
+
+ if( !m_cdrecordBinObject->copyright.isEmpty() )
+ emit infoMessage( i18n("Using %1 %2 - Copyright (C) %3")
+ .arg(m_cdrecordBinObject->hasFeature( "wodim" ) ? "Wodim" : "Cdrecord" )
+ .arg(m_cdrecordBinObject->version)
+ .arg(m_cdrecordBinObject->copyright), INFO );
+
+
+ kdDebug() << "***** " << m_cdrecordBinObject->name() << " parameters:\n";
+ const QValueList<QCString>& args = m_process->args();
+ QString s;
+ for( QValueList<QCString>::const_iterator it = args.begin(); it != args.end(); ++it ) {
+ s += *it + " ";
+ }
+ kdDebug() << s << flush << endl;
+ emit debuggingOutput( m_cdrecordBinObject->name() + " command:", s);
+
+ m_currentTrack = 0;
+ m_cdrecordError = UNKNOWN;
+ m_totalTracksParsed = false;
+ m_alreadyWritten = 0;
+ d->tracks.clear();
+ m_totalSize = 0;
+
+ emit newSubTask( i18n("Preparing write process...") );
+
+ // FIXME: check the return value
+ if( K3b::isMounted( burnDevice() ) ) {
+ emit infoMessage( i18n("Unmounting medium"), INFO );
+ K3b::unmount( burnDevice() );
+ }
+
+ // block the device (including certain checks)
+ k3bcore->blockDevice( burnDevice() );
+
+ // lock the device for good in this process since it will
+ // be opened in the growisofs process
+ burnDevice()->close();
+ burnDevice()->usageLock();
+
+ if( !m_process->start( KProcess::NotifyOnExit, KProcess::All ) ) {
+ // something went wrong when starting the program
+ // it "should" be the executable
+ kdDebug() << "(K3bCdrecordWriter) could not start " << m_cdrecordBinObject->name() << endl;
+ emit infoMessage( i18n("Could not start %1.").arg(m_cdrecordBinObject->name()), K3bJob::ERROR );
+ jobFinished(false);
+ }
+ else {
+ if( simulate() ) {
+ emit newTask( i18n("Simulating") );
+ emit infoMessage( i18n("Starting %1 simulation at %2x speed...")
+ .arg(K3b::writingModeString(m_writingMode))
+ .arg(d->usedSpeed),
+ K3bJob::INFO );
+ }
+ else {
+ emit newTask( i18n("Writing") );
+ emit infoMessage( i18n("Starting %1 writing at %2x speed...")
+ .arg(K3b::writingModeString(m_writingMode))
+ .arg(d->usedSpeed),
+ K3bJob::INFO );
+ }
+ }
+}
+
+
+void K3bCdrecordWriter::cancel()
+{
+ if( active() ) {
+ d->canceled = true;
+ if( m_process && m_process->isRunning() )
+ m_process->kill();
+ }
+}
+
+
+void K3bCdrecordWriter::slotStdLine( const QString& line )
+{
+ static QRegExp s_burnfreeCounterRx( "^BURN\\-Free\\swas\\s(\\d+)\\stimes\\sused" );
+ static QRegExp s_burnfreeCounterRxPredict( "^Total\\sof\\s(\\d+)\\s\\spossible\\sbuffer\\sunderruns\\spredicted" );
+
+ // tracknumber: cap(1)
+ // done: cap(2)
+ // complete: cap(3)
+ // fifo: cap(4) (it seems as if some patched cdrecord versions do not emit the fifo info but only the buf... :(
+ // buffer: cap(5)
+ static QRegExp s_progressRx( "Track\\s(\\d\\d)\\:\\s*(\\d*)\\sof\\s*(\\d*)\\sMB\\swritten\\s(?:\\(fifo\\s*(\\d*)\\%\\)\\s*)?(?:\\[buf\\s*(\\d*)\\%\\])?.*" );
+
+ emit debuggingOutput( m_cdrecordBinObject->name(), line );
+
+ //
+ // Progress and toc parsing
+ //
+
+ if( line.startsWith( "Track " ) ) {
+ if( !m_totalTracksParsed ) {
+ // this is not the progress display but the list of tracks that will get written
+ // we always extract the tracknumber to get the highest at last
+ bool ok;
+ int tt = line.mid( 6, 2 ).toInt(&ok);
+
+ if( ok ) {
+ struct Private::Track track;
+ track.audio = ( line.mid( 10, 5 ) == "audio" );
+
+ m_totalTracks = tt;
+
+ int sizeStart = line.find( QRegExp("\\d"), 10 );
+ int sizeEnd = line.find( "MB", sizeStart );
+ track.size = line.mid( sizeStart, sizeEnd-sizeStart ).toInt(&ok);
+
+ if( ok ) {
+ d->tracks.append(track);
+ m_totalSize += track.size;
+ }
+ else
+ kdDebug() << "(K3bCdrecordWriter) track number parse error: "
+ << line.mid( sizeStart, sizeEnd-sizeStart ) << endl;
+ }
+ else
+ kdDebug() << "(K3bCdrecordWriter) track number parse error: "
+ << line.mid( 6, 2 ) << endl;
+ }
+
+ else if( s_progressRx.exactMatch( line ) ) {
+ // int num = s_progressRx.cap(1).toInt();
+ int made = s_progressRx.cap(2).toInt();
+ int size = s_progressRx.cap(3).toInt();
+ int fifo = s_progressRx.cap(4).toInt();
+
+ emit buffer( fifo );
+ m_lastFifoValue = fifo;
+
+ if( s_progressRx.numCaptures() > 4 )
+ emit deviceBuffer( s_progressRx.cap(5).toInt() );
+
+ //
+ // cdrecord's output sucks a bit.
+ // we get track sizes that differ from the sizes in the progress
+ // info since these are dependant on the writing mode.
+ // so we just use the track sizes and do a bit of math...
+ //
+
+ if( d->tracks.count() > m_currentTrack-1 && size > 0 ) {
+ double convV = (double)d->tracks[m_currentTrack-1].size/(double)size;
+ made = (int)((double)made * convV);
+ size = d->tracks[m_currentTrack-1].size;
+ }
+ else {
+ kdError() << "(K3bCdrecordWriter) Did not parse all tracks sizes!" << endl;
+ }
+
+ if( size > 0 ) {
+ emit processedSubSize( made, size );
+ emit subPercent( 100*made/size );
+ }
+
+ if( m_totalSize > 0 ) {
+ emit processedSize( m_alreadyWritten+made, m_totalSize );
+ emit percent( 100*(m_alreadyWritten+made)/m_totalSize );
+ }
+
+ d->speedEst->dataWritten( (m_alreadyWritten+made)*1024 );
+ }
+ }
+
+ //
+ // Cdrecord starts all error and warning messages with it's path
+ // With Debian's script it starts with cdrecord (or /usr/bin/cdrecord or whatever! I hate this script!)
+ //
+
+ else if( line.startsWith( "cdrecord" ) ||
+ line.startsWith( m_cdrecordBinObject->path ) ||
+ line.startsWith( m_cdrecordBinObject->path.left(m_cdrecordBinObject->path.length()-5) ) ) {
+ // get rid of the path and the following colon and space
+ QString errStr = line.mid( line.find(':') + 2 );
+
+ if( errStr.startsWith( "Drive does not support SAO" ) ) {
+ emit infoMessage( i18n("DAO (Disk At Once) recording not supported with this writer"), K3bJob::ERROR );
+ emit infoMessage( i18n("Please choose TAO (Track At Once) and try again"), K3bJob::ERROR );
+ }
+ else if( errStr.startsWith( "Drive does not support RAW" ) ) {
+ emit infoMessage( i18n("RAW recording not supported with this writer"), K3bJob::ERROR );
+ }
+ else if( errStr.startsWith("Input/output error.") ) {
+ emit infoMessage( i18n("Input/output error. Not necessarily serious."), WARNING );
+ }
+ else if( errStr.startsWith("shmget failed") ) {
+ m_cdrecordError = SHMGET_FAILED;
+ }
+ else if( errStr.startsWith("OPC failed") ) {
+ m_cdrecordError = OPC_FAILED;
+ }
+ else if( errStr.startsWith( "Drive needs to reload the media" ) ) {
+ emit infoMessage( i18n("Reloading of medium required"), K3bJob::INFO );
+ }
+ else if( errStr.startsWith( "The current problem looks like a buffer underrun" ) ) {
+ if( m_cdrecordError == UNKNOWN ) // it is almost never a buffer underrun these days.
+ m_cdrecordError = BUFFER_UNDERRUN;
+ }
+ else if( errStr.startsWith("WARNING: Data may not fit") ) {
+ bool overburn = k3bcore->globalSettings()->overburn();
+ if( overburn && m_cdrecordBinObject->hasFeature("overburn") )
+ emit infoMessage( i18n("Trying to write more than the official disk capacity"), K3bJob::WARNING );
+ m_cdrecordError = OVERSIZE;
+ }
+ else if( errStr.startsWith("Bad Option") ) {
+ m_cdrecordError = BAD_OPTION;
+ // parse option
+ int pos = line.find( "Bad Option" ) + 13;
+ int len = line.length() - pos - 1;
+ emit infoMessage( i18n("No valid %1 option: %2").arg(m_cdrecordBinObject->name()).arg(line.mid(pos, len)),
+ ERROR );
+ }
+ else if( errStr.startsWith("Cannot set speed/dummy") ) {
+ m_cdrecordError = CANNOT_SET_SPEED;
+ }
+ else if( errStr.startsWith("Cannot open new session") ) {
+ m_cdrecordError = CANNOT_OPEN_NEW_SESSION;
+ }
+ else if( errStr.startsWith("Cannot send CUE sheet") ) {
+ m_cdrecordError = CANNOT_SEND_CUE_SHEET;
+ }
+ else if( errStr.startsWith( "Trying to use ultra high speed" ) ||
+ errStr.startsWith( "Trying to use high speed" ) ||
+ errStr.startsWith( "Probably trying to use ultra high speed" ) ||
+ errStr.startsWith( "You did use a high speed medium on an improper writer" ) ||
+ errStr.startsWith( "You did use a ultra high speed medium on an improper writer" ) ) {
+ m_cdrecordError = HIGH_SPEED_MEDIUM;
+ }
+ else if( errStr.startsWith( "You may have used an ultra low speed medium" ) ) {
+ m_cdrecordError = LOW_SPEED_MEDIUM;
+ }
+ else if( errStr.startsWith( "Permission denied. Cannot open" ) ||
+ errStr.startsWith( "Operation not permitted." ) ) {
+ m_cdrecordError = PERMISSION_DENIED;
+ }
+ else if( errStr.startsWith( "Can only copy session # 1") ) {
+ emit infoMessage( i18n("Only session 1 will be cloned."), WARNING );
+ }
+ else if( errStr == "Cannot fixate disk." ) {
+ emit infoMessage( i18n("Unable to fixate the disk."), ERROR );
+ if( m_cdrecordError == UNKNOWN )
+ m_cdrecordError = CANNOT_FIXATE_DISK;
+ }
+ else if( errStr == "A write error occurred." ) {
+ m_cdrecordError = WRITE_ERROR;
+ }
+ else if( errStr.startsWith( "Try again with cdrecord blank=all." ) ) {
+ m_cdrecordError = BLANK_FAILED;
+ }
+ }
+
+ //
+ // All other messages
+ //
+
+ else if( line.contains( "at speed" ) ) {
+ // parse the speed and inform the user if cdrdao switched it down
+ int pos = line.find( "at speed" );
+ int pos2 = line.find( "in", pos+9 );
+ int speed = static_cast<int>( line.mid( pos+9, pos2-pos-10 ).toDouble() ); // cdrecord-dvd >= 2.01a25 uses 8.0 and stuff
+ if( speed != d->usedSpeed ) {
+ emit infoMessage( i18n("Medium or burner do not support writing at %1x speed").arg(d->usedSpeed), K3bJob::WARNING );
+ if( speed > d->usedSpeed )
+ emit infoMessage( i18n("Switching burn speed up to %1x").arg(speed), K3bJob::WARNING );
+ else
+ emit infoMessage( i18n("Switching burn speed down to %1x").arg(speed), K3bJob::WARNING );
+ }
+ }
+ else if( line.startsWith( "Starting new" ) ) {
+ m_totalTracksParsed = true;
+ if( m_currentTrack > 0 ) {// nothing has been written at the start of track 1
+ if( d->tracks.count() > m_currentTrack-1 )
+ m_alreadyWritten += d->tracks[m_currentTrack-1].size;
+ else
+ kdError() << "(K3bCdrecordWriter) Did not parse all tracks sizes!" << endl;
+ }
+ else
+ emit infoMessage( i18n("Starting disc write"), INFO );
+
+ m_currentTrack++;
+
+ if( m_currentTrack > d->tracks.count() ) {
+ kdDebug() << "(K3bCdrecordWriter) need to add dummy track struct." << endl;
+ struct Private::Track t;
+ t.size = 1;
+ t.audio = false;
+ d->tracks.append(t);
+ }
+
+ kdDebug() << "(K3bCdrecordWriter) writing track " << m_currentTrack << " of " << m_totalTracks << " tracks." << endl;
+ emit nextTrack( m_currentTrack, m_totalTracks );
+ }
+ else if( line.startsWith( "Fixating" ) ) {
+ emit newSubTask( i18n("Closing Session") );
+ }
+ else if( line.startsWith( "Writing lead-in" ) ) {
+ m_totalTracksParsed = true;
+ emit newSubTask( i18n("Writing Leadin") );
+ }
+ else if( line.startsWith( "Writing Leadout") ) {
+ emit newSubTask( i18n("Writing Leadout") );
+ }
+ else if( line.startsWith( "Writing pregap" ) ) {
+ emit newSubTask( i18n("Writing pregap") );
+ }
+ else if( line.startsWith( "Performing OPC" ) ) {
+ emit infoMessage( i18n("Performing Optimum Power Calibration"), K3bJob::INFO );
+ }
+ else if( line.startsWith( "Sending" ) ) {
+ emit infoMessage( i18n("Sending CUE sheet"), K3bJob::INFO );
+ }
+ else if( line.startsWith( "Turning BURN-Free on" ) || line.startsWith( "BURN-Free is ON") ) {
+ emit infoMessage( i18n("Enabled Burnfree"), K3bJob::INFO );
+ }
+ else if( line.startsWith( "Turning BURN-Free off" ) ) {
+ emit infoMessage( i18n("Disabled Burnfree"), K3bJob::WARNING );
+ }
+ else if( line.startsWith( "Re-load disk and hit" ) ) {
+ // this happens on some notebooks where cdrecord is not able to close the
+ // tray itself, so we need to ask the user to do so
+ blockingInformation( i18n("Please reload the medium and press 'ok'"),
+ i18n("Unable to close the tray") );
+
+ // now send a <CR> to cdrecord
+ // hopefully this will do it since I have no possibility to test it!
+ ::write( fd(), "\n", 1 );
+ }
+ else if( s_burnfreeCounterRx.search( line ) ) {
+ bool ok;
+ int num = s_burnfreeCounterRx.cap(1).toInt(&ok);
+ if( ok )
+ emit infoMessage( i18n("Burnfree was used 1 time.", "Burnfree was used %n times.", num), INFO );
+ }
+ else if( s_burnfreeCounterRxPredict.search( line ) ) {
+ bool ok;
+ int num = s_burnfreeCounterRxPredict.cap(1).toInt(&ok);
+ if( ok )
+ emit infoMessage( i18n("Buffer was low 1 time.", "Buffer was low %n times.", num), INFO );
+ }
+ else if( line.contains("Medium Error") ) {
+ m_cdrecordError = MEDIUM_ERROR;
+ }
+ else if( line.startsWith( "Error trying to open" ) && line.contains( "(Device or resource busy)" ) ) {
+ m_cdrecordError = DEVICE_BUSY;
+ }
+ else {
+ // debugging
+ kdDebug() << "(" << m_cdrecordBinObject->name() << ") " << line << endl;
+ }
+}
+
+
+void K3bCdrecordWriter::slotProcessExited( KProcess* p )
+{
+ // remove temporary cdtext file
+ delete d->cdTextFile;
+ d->cdTextFile = 0;
+
+ // release the device within this process
+ burnDevice()->usageUnlock();
+
+ // unblock the device
+ k3bcore->unblockDevice( burnDevice() );
+
+ if( d->canceled ) {
+ // this will unblock and eject the drive and emit the finished/canceled signals
+ K3bAbstractWriter::cancel();
+ return;
+ }
+
+
+ if( p->normalExit() ) {
+ switch( p->exitStatus() ) {
+ case 0:
+ {
+ if( simulate() )
+ emit infoMessage( i18n("Simulation successfully completed"), K3bJob::SUCCESS );
+ else
+ emit infoMessage( i18n("Writing successfully completed"), K3bJob::SUCCESS );
+
+ int s = d->speedEst->average();
+ emit infoMessage( i18n("Average overall write speed: %1 KB/s (%2x)").arg(s).arg(KGlobal::locale()->formatNumber((double)s/150.0), 2), INFO );
+
+ jobFinished( true );
+ }
+ break;
+
+ default:
+ kdDebug() << "(K3bCdrecordWriter) error: " << p->exitStatus() << endl;
+
+ if( m_cdrecordError == UNKNOWN && m_lastFifoValue <= 3 )
+ m_cdrecordError = BUFFER_UNDERRUN;
+
+ switch( m_cdrecordError ) {
+ case OVERSIZE:
+ if( k3bcore->globalSettings()->overburn() &&
+ m_cdrecordBinObject->hasFeature("overburn") )
+ emit infoMessage( i18n("Data did not fit on disk."), ERROR );
+ else {
+ emit infoMessage( i18n("Data does not fit on disk."), ERROR );
+ if( m_cdrecordBinObject->hasFeature("overburn") )
+ emit infoMessage( i18n("Enable overburning in the advanced K3b settings to burn anyway."), INFO );
+ }
+ break;
+ case BAD_OPTION:
+ // error message has already been emited earlier since we needed the actual line
+ break;
+ case SHMGET_FAILED:
+ emit infoMessage( i18n("%1 could not reserve shared memory segment of requested size.").arg(m_cdrecordBinObject->name()), ERROR );
+ emit infoMessage( i18n("Probably you chose a too large buffer size."), ERROR );
+ break;
+ case OPC_FAILED:
+ emit infoMessage( i18n("OPC failed. Probably the writer does not like the medium."), ERROR );
+ break;
+ case CANNOT_SET_SPEED:
+ emit infoMessage( i18n("Unable to set write speed to %1.").arg(d->usedSpeed), ERROR );
+ emit infoMessage( i18n("Probably this is lower than your writer's lowest writing speed."), ERROR );
+ break;
+ case CANNOT_SEND_CUE_SHEET:
+ emit infoMessage( i18n("Unable to send CUE sheet."), ERROR );
+ if( m_writingMode == K3b::DAO )
+ emit infoMessage( i18n("Sometimes using TAO writing mode solves this issue."), ERROR );
+ break;
+ case CANNOT_OPEN_NEW_SESSION:
+ emit infoMessage( i18n("Unable to open new session."), ERROR );
+ emit infoMessage( i18n("Probably a problem with the medium."), ERROR );
+ break;
+ case CANNOT_FIXATE_DISK:
+ emit infoMessage( i18n("The disk might still be readable."), ERROR );
+ if( m_writingMode == K3b::TAO && burnDevice()->dao() )
+ emit infoMessage( i18n("Try DAO writing mode."), ERROR );
+ break;
+ case PERMISSION_DENIED:
+ emit infoMessage( i18n("%1 has no permission to open the device.").arg("Cdrecord"), ERROR );
+#ifdef HAVE_K3BSETUP
+ emit infoMessage( i18n("You may use K3bsetup2 to solve this problem."), ERROR );
+#endif
+ break;
+ case BUFFER_UNDERRUN:
+ emit infoMessage( i18n("Probably a buffer underrun occurred."), ERROR );
+ if( !d->usingBurnfree && burnDevice()->burnproof() )
+ emit infoMessage( i18n("Please enable Burnfree or choose a lower burning speed."), ERROR );
+ else
+ emit infoMessage( i18n("Please choose a lower burning speed."), ERROR );
+ break;
+ case HIGH_SPEED_MEDIUM:
+ emit infoMessage( i18n("Found a high-speed medium not suitable for the writer being used."), ERROR );
+ emit infoMessage( i18n("Use the 'force unsafe operations' option to ignore this."), ERROR );
+ break;
+ case LOW_SPEED_MEDIUM:
+ emit infoMessage( i18n("Found a low-speed medium not suitable for the writer being used."), ERROR );
+ emit infoMessage( i18n("Use the 'force unsafe operations' option to ignore this."), ERROR );
+ break;
+ case MEDIUM_ERROR:
+ emit infoMessage( i18n("Most likely the burning failed due to low-quality media."), ERROR );
+ break;
+ case DEVICE_BUSY:
+ emit infoMessage( i18n("Another application is blocking the device (most likely automounting)."), ERROR );
+ break;
+ case WRITE_ERROR:
+ emit infoMessage( i18n("A write error occurred."), ERROR );
+ if( m_writingMode == K3b::DAO )
+ emit infoMessage( i18n("Sometimes using TAO writing mode solves this issue."), ERROR );
+ break;
+ case BLANK_FAILED:
+ emit infoMessage( i18n("Some drives do not support all erase types."), ERROR );
+ emit infoMessage( i18n("Try again using 'Complete' erasing."), ERROR );
+ break;
+ case UNKNOWN:
+ if( p->exitStatus() == 12 && K3b::kernelVersion() >= K3bVersion( 2, 6, 8 ) && m_cdrecordBinObject->hasFeature( "suidroot" ) ) {
+ emit infoMessage( i18n("Since kernel version 2.6.8 cdrecord cannot use SCSI transport when running suid root anymore."), ERROR );
+ emit infoMessage( i18n("You may use K3bSetup to solve this problem or remove the suid bit manually."), ERROR );
+ }
+ else if( !wasSourceUnreadable() ) {
+ emit infoMessage( i18n("%1 returned an unknown error (code %2).")
+ .arg(m_cdrecordBinObject->name()).arg(p->exitStatus()),
+ K3bJob::ERROR );
+
+ if( p->exitStatus() >= 254 && m_writingMode == K3b::DAO ) {
+ emit infoMessage( i18n("Sometimes using TAO writing mode solves this issue."), ERROR );
+ }
+ else {
+ emit infoMessage( i18n("If you are running an unpatched cdrecord version..."), ERROR );
+ emit infoMessage( i18n("...and this error also occurs with high quality media..."), ERROR );
+ emit infoMessage( i18n("...and the K3b FAQ does not help you..."), ERROR );
+ emit infoMessage( i18n("...please include the debugging output in your problem report."), ERROR );
+ }
+ }
+ break;
+ }
+ jobFinished( false );
+ }
+ }
+ else {
+ emit infoMessage( i18n("%1 did not exit cleanly.").arg(m_cdrecordBinObject->name()),
+ ERROR );
+ jobFinished( false );
+ }
+}
+
+
+void K3bCdrecordWriter::slotThroughput( int t )
+{
+ emit writeSpeed( t, d->tracks[m_currentTrack-1].audio ? 175 : 150 );
+}
+
+#include "k3bcdrecordwriter.moc"
diff --git a/libk3b/projects/k3bcdrecordwriter.h b/libk3b/projects/k3bcdrecordwriter.h
new file mode 100644
index 0000000..9333588
--- /dev/null
+++ b/libk3b/projects/k3bcdrecordwriter.h
@@ -0,0 +1,123 @@
+/*
+ *
+ * $Id: k3bcdrecordwriter.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3B_CDRECORD_WRITER_H
+#define K3B_CDRECORD_WRITER_H
+
+
+#include "k3babstractwriter.h"
+
+#include <qstringlist.h>
+
+class K3bExternalBin;
+class K3bProcess;
+class KProcess;
+class K3bDevice::Device;
+
+
+class K3bCdrecordWriter : public K3bAbstractWriter
+{
+ Q_OBJECT
+
+ public:
+ K3bCdrecordWriter( K3bDevice::Device*, K3bJobHandler* hdl,
+ QObject* parent = 0, const char* name = 0 );
+ ~K3bCdrecordWriter();
+
+ bool active() const;
+
+ /**
+ * to be used in chain: addArgument(x)->addArgument(y)
+ */
+ K3bCdrecordWriter* addArgument( const QString& );
+ void clearArguments();
+
+ int fd() const;
+
+ public slots:
+ void start();
+ void cancel();
+
+ void setDao( bool b );
+ void setWritingMode( int );
+ void setCueFile( const QString& s);
+ void setClone( bool b );
+
+ void setRawCdText( const QByteArray& a ) { m_rawCdText = a; }
+
+ /**
+ * If set true the job ignores the global K3b setting
+ * and does not eject the CD-RW after finishing
+ */
+ void setForceNoEject( bool b ) { m_forceNoEject = b; }
+
+ protected slots:
+ void slotStdLine( const QString& line );
+ void slotProcessExited(KProcess*);
+ void slotThroughput( int t );
+
+ protected:
+ virtual void prepareProcess();
+
+ const K3bExternalBin* m_cdrecordBinObject;
+ K3bProcess* m_process;
+
+ int m_writingMode;
+ bool m_totalTracksParsed;
+ bool m_clone;
+ bool m_cue;
+
+ QString m_cueFile;
+
+ enum CdrecordError { UNKNOWN,
+ OVERSIZE,
+ BAD_OPTION,
+ SHMGET_FAILED,
+ OPC_FAILED,
+ CANNOT_SET_SPEED,
+ CANNOT_SEND_CUE_SHEET,
+ CANNOT_OPEN_NEW_SESSION,
+ CANNOT_FIXATE_DISK,
+ WRITE_ERROR,
+ PERMISSION_DENIED,
+ BUFFER_UNDERRUN,
+ HIGH_SPEED_MEDIUM,
+ LOW_SPEED_MEDIUM,
+ MEDIUM_ERROR,
+ DEVICE_BUSY,
+ BLANK_FAILED };
+
+ QStringList m_arguments;
+
+ private:
+ unsigned int m_currentTrack;
+ int m_totalTracks;
+ int m_totalSize;
+ int m_alreadyWritten;
+
+ int m_lastFifoValue;
+
+ int m_cdrecordError;
+
+ bool m_forceNoEject;
+
+ QByteArray m_rawCdText;
+
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/projects/k3bcuefileparser.cpp b/libk3b/projects/k3bcuefileparser.cpp
new file mode 100644
index 0000000..49ca4fc
--- /dev/null
+++ b/libk3b/projects/k3bcuefileparser.cpp
@@ -0,0 +1,461 @@
+/*
+ *
+ * $Id: k3bcuefileparser.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bcuefileparser.h"
+
+#include <k3bmsf.h>
+#include <k3bglobals.h>
+#include <k3btrack.h>
+#include <k3bcdtext.h>
+
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qtextstream.h>
+#include <qregexp.h>
+#include <qdir.h>
+
+#include <kdebug.h>
+
+
+// avoid usage of QTextStream since K3b often
+// tries to open big files (iso images) in a
+// cue file parser to test it.
+static QString readLine( QFile* f )
+{
+ QString s;
+ Q_LONG r = f->readLine( s, 1024 );
+ if( r >= 0 ) {
+ // remove the trailing newline
+ return s.stripWhiteSpace();
+ }
+ else {
+ // end of file or error
+ return QString::null;
+ }
+}
+
+
+// TODO: add method: usableByCdrecordDirectly()
+// TODO: add Toc with sector sizes
+
+class K3bCueFileParser::Private
+{
+public:
+ bool inFile;
+ bool inTrack;
+ int trackType;
+ int trackMode;
+ bool rawData;
+ bool haveIndex1;
+ K3b::Msf currentDataPos;
+ K3b::Msf index0;
+
+ K3bDevice::Toc toc;
+ int currentParsedTrack;
+
+ K3bDevice::CdText cdText;
+};
+
+
+
+K3bCueFileParser::K3bCueFileParser( const QString& filename )
+ : K3bImageFileReader()
+{
+ d = new Private;
+ openFile( filename );
+}
+
+
+K3bCueFileParser::~K3bCueFileParser()
+{
+ delete d;
+}
+
+
+void K3bCueFileParser::readFile()
+{
+ setValid(true);
+
+ d->inFile = d->inTrack = d->haveIndex1 = false;
+ d->trackMode = K3bDevice::Track::UNKNOWN;
+ d->toc.clear();
+ d->cdText.clear();
+ d->currentParsedTrack = 0;
+
+ QFile f( filename() );
+ if( f.open( IO_ReadOnly ) ) {
+ QString line = readLine( &f );
+ while( !line.isNull() ) {
+
+ if( !parseLine(line) ) {
+ setValid(false);
+ break;
+ }
+
+ line = readLine( &f );
+ }
+
+ if( isValid() ) {
+ // save last parsed track for which we do not have the proper length :(
+ if( d->currentParsedTrack > 0 ) {
+ d->toc.append( K3bDevice::Track( d->currentDataPos,
+ d->currentDataPos,
+ d->trackType,
+ d->trackMode ) );
+ }
+
+ // debug the toc
+ kdDebug() << "(K3bCueFileParser) successfully parsed cue file." << endl
+ << "------------------------------------------------" << endl;
+ for( unsigned int i = 0; i < d->toc.count(); ++i ) {
+ K3bDevice::Track& track = d->toc[i];
+ kdDebug() << "Track " << (i+1)
+ << " (" << ( track.type() == K3bDevice::Track::AUDIO ? "audio" : "data" ) << ") "
+ << track.firstSector().toString() << " - " << track.lastSector().toString() << endl;
+ }
+
+ kdDebug() << "------------------------------------------------" << endl;
+ }
+ }
+ else {
+ kdDebug() << "(K3bCueFileParser) could not open file " << filename() << endl;
+ setValid(false);
+ }
+}
+
+
+bool K3bCueFileParser::parseLine( QString& line )
+{
+ // use cap(1) for the filename
+ static QRegExp fileRx( "FILE\\s\"?([^\"]*)\"?\\s[^\"\\s]*" );
+
+ // use cap(1) for the flags
+ static QRegExp flagsRx( "FLAGS(\\s(DCP|4CH|PRE|SCMS)){1,4}" );
+
+ // use cap(1) for the tracknumber and cap(2) for the datatype
+ static QRegExp trackRx( "TRACK\\s(\\d{1,2})\\s(AUDIO|CDG|MODE1/2048|MODE1/2352|MODE2/2336|MODE2/2352|CDI/2336|CDI/2352)" );
+
+ // use cap(1) for the index number, cap(3) for the minutes, cap(4) for the seconds, cap(5) for the frames,
+ // and cap(2) for the MSF value string
+ static QRegExp indexRx( "INDEX\\s(\\d{1,2})\\s((\\d+):([0-5]\\d):((?:[0-6]\\d)|(?:7[0-4])))" );
+
+ // use cap(1) for the MCN
+ static QRegExp catalogRx( "CATALOG\\s(\\w{13,13})" );
+
+ // use cap(1) for the ISRC
+ static QRegExp isrcRx( "ISRC\\s(\\w{5,5}\\d{7,7})" );
+
+ static QString cdTextRxStr = "\"?([^\"]{0,80})\"?";
+
+ // use cap(1) for the string
+ static QRegExp titleRx( "TITLE\\s" + cdTextRxStr );
+ static QRegExp performerRx( "PERFORMER\\s" + cdTextRxStr );
+ static QRegExp songwriterRx( "SONGWRITER\\s" + cdTextRxStr );
+
+
+ // simplify all white spaces except those in filenames and CD-TEXT
+ simplifyWhiteSpace( line );
+
+ // skip comments and empty lines
+ if( line.startsWith("REM") || line.startsWith("#") || line.isEmpty() )
+ return true;
+
+
+ //
+ // FILE
+ //
+ if( fileRx.exactMatch( line ) ) {
+
+ setValid( findImageFileName( fileRx.cap(1) ) );
+
+ if( d->inFile ) {
+ kdDebug() << "(K3bCueFileParser) only one FILE statement allowed." << endl;
+ return false;
+ }
+ d->inFile = true;
+ d->inTrack = false;
+ d->haveIndex1 = false;
+ return true;
+ }
+
+
+ //
+ // TRACK
+ //
+ else if( trackRx.exactMatch( line ) ) {
+ if( !d->inFile ) {
+ kdDebug() << "(K3bCueFileParser) TRACK statement before FILE." << endl;
+ return false;
+ }
+
+ // check if we had index1 for the last track
+ if( d->inTrack && !d->haveIndex1 ) {
+ kdDebug() << "(K3bCueFileParser) TRACK without INDEX 1." << endl;
+ return false;
+ }
+
+ // save last track
+ // TODO: use d->rawData in some way
+ if( d->currentParsedTrack > 0 ) {
+ d->toc.append( K3bDevice::Track( d->currentDataPos,
+ d->currentDataPos,
+ d->trackType,
+ d->trackMode ) );
+ }
+
+ d->currentParsedTrack++;
+
+ d->cdText.resize( d->currentParsedTrack );
+
+ // parse the tracktype
+ if( trackRx.cap(2) == "AUDIO" ) {
+ d->trackType = K3bDevice::Track::AUDIO;
+ d->trackMode = K3bDevice::Track::UNKNOWN;
+ }
+ else {
+ d->trackType = K3bDevice::Track::DATA;
+ if( trackRx.cap(2).startsWith("MODE1") ) {
+ d->trackMode = K3bDevice::Track::MODE1;
+ d->rawData = (trackRx.cap(2) == "MODE1/2352");
+ }
+ else if( trackRx.cap(2).startsWith("MODE2") ) {
+ d->trackMode = K3bDevice::Track::MODE2;
+ d->rawData = (trackRx.cap(2) == "MODE2/2352");
+ }
+ else {
+ kdDebug() << "(K3bCueFileParser) unsupported track type: " << trackRx.cap(2) << endl;
+ return false;
+ }
+ }
+
+ d->haveIndex1 = false;
+ d->inTrack = true;
+ d->index0 = 0;
+
+ return true;
+ }
+
+
+ //
+ // FLAGS
+ //
+ else if( flagsRx.exactMatch( line ) ) {
+ if( !d->inTrack ) {
+ kdDebug() << "(K3bCueFileParser) FLAGS statement without TRACK." << endl;
+ return false;
+ }
+
+ // TODO: save the flags
+ return true;
+ }
+
+
+ //
+ // INDEX
+ //
+ else if( indexRx.exactMatch( line ) ) {
+ if( !d->inTrack ) {
+ kdDebug() << "(K3bCueFileParser) INDEX statement without TRACK." << endl;
+ return false;
+ }
+
+ unsigned int indexNumber = indexRx.cap(1).toInt();
+
+ K3b::Msf indexStart = K3b::Msf::fromString( indexRx.cap(2) );
+
+ if( indexNumber == 0 ) {
+ d->index0 = indexStart;
+
+ if( d->currentParsedTrack < 2 && indexStart > 0 ) {
+ kdDebug() << "(K3bCueFileParser) first track is not allowed to have a pregap > 0." << endl;
+ return false;
+ }
+ }
+ else if( indexNumber == 1 ) {
+ d->haveIndex1 = true;
+ d->currentDataPos = indexStart;
+ if( d->currentParsedTrack > 1 ) {
+ d->toc[d->currentParsedTrack-2].setLastSector( indexStart-1 );
+ if( d->index0 > 0 && d->index0 < indexStart ) {
+ d->toc[d->currentParsedTrack-2].setIndex0( d->index0 - d->toc[d->currentParsedTrack-2].firstSector() );
+ }
+ }
+ }
+ else {
+ // TODO: add index > 0
+ }
+
+ return true;
+ }
+
+
+ //
+ // CATALOG
+ //
+ if( catalogRx.exactMatch( line ) ) {
+ // TODO: set the toc's mcn
+ return true;
+ }
+
+
+ //
+ // ISRC
+ //
+ if( isrcRx.exactMatch( line ) ) {
+ if( d->inTrack ) {
+ // TODO: set the track's ISRC
+ return true;
+ }
+ else {
+ kdDebug() << "(K3bCueFileParser) ISRC without TRACK." << endl;
+ return false;
+ }
+ }
+
+
+ //
+ // CD-TEXT
+ // TODO: create K3bDevice::TrackCdText entries
+ //
+ else if( titleRx.exactMatch( line ) ) {
+ if( d->inTrack )
+ d->cdText[d->currentParsedTrack-1].setTitle( titleRx.cap(1) );
+ else
+ d->cdText.setTitle( titleRx.cap(1) );
+ return true;
+ }
+
+ else if( performerRx.exactMatch( line ) ) {
+ if( d->inTrack )
+ d->cdText[d->currentParsedTrack-1].setPerformer( performerRx.cap(1) );
+ else
+ d->cdText.setPerformer( performerRx.cap(1) );
+ return true;
+ }
+
+ else if( songwriterRx.exactMatch( line ) ) {
+ if( d->inTrack )
+ d->cdText[d->currentParsedTrack-1].setSongwriter( songwriterRx.cap(1) );
+ else
+ d->cdText.setSongwriter( songwriterRx.cap(1) );
+ return true;
+ }
+
+ else {
+ kdDebug() << "(K3bCueFileParser) unknown Cue line: '" << line << "'" << endl;
+ return false;
+ }
+}
+
+
+void K3bCueFileParser::simplifyWhiteSpace( QString& s )
+{
+ s = s.stripWhiteSpace();
+
+ unsigned int i = 0;
+ bool insideQuote = false;
+ while( i < s.length() ) {
+ if( !insideQuote ) {
+ if( s[i].isSpace() && s[i+1].isSpace() )
+ s.remove( i, 1 );
+ }
+
+ if( s[i] == '"' )
+ insideQuote = !insideQuote;
+
+ ++i;
+ }
+}
+
+
+const K3bDevice::Toc& K3bCueFileParser::toc() const
+{
+ return d->toc;
+}
+
+
+const K3bDevice::CdText& K3bCueFileParser::cdText() const
+{
+ return d->cdText;
+}
+
+
+bool K3bCueFileParser::findImageFileName( const QString& dataFile )
+{
+ //
+ // CDRDAO does not use this image filename but replaces the extension from the cue file
+ // with "bin" to get the image filename, we should take this into account
+ //
+
+ m_imageFilenameInCue = true;
+
+ // first try filename as a hole (absolut)
+ if( QFile::exists( dataFile ) ) {
+ setImageFilename( QFileInfo(dataFile).absFilePath() );
+ return true;
+ }
+
+ // try the filename in the cue's directory
+ if( QFileInfo( K3b::parentDir(filename()) + dataFile.section( '/', -1 ) ).isFile() ) {
+ setImageFilename( K3b::parentDir(filename()) + dataFile.section( '/', -1 ) );
+ kdDebug() << "(K3bCueFileParser) found image file: " << imageFilename() << endl;
+ return true;
+ }
+
+ // try the filename ignoring case
+ if( QFileInfo( K3b::parentDir(filename()) + dataFile.section( '/', -1 ).lower() ).isFile() ) {
+ setImageFilename( K3b::parentDir(filename()) + dataFile.section( '/', -1 ).lower() );
+ kdDebug() << "(K3bCueFileParser) found image file: " << imageFilename() << endl;
+ return true;
+ }
+
+ m_imageFilenameInCue = false;
+
+ // try removing the ending from the cue file (image.bin.cue and image.bin)
+ if( QFileInfo( filename().left( filename().length()-4 ) ).isFile() ) {
+ setImageFilename( filename().left( filename().length()-4 ) );
+ kdDebug() << "(K3bCueFileParser) found image file: " << imageFilename() << endl;
+ return true;
+ }
+
+ //
+ // we did not find the image specified in the cue.
+ // Search for another one having the same filename as the cue but a different extension
+ //
+
+ QDir parentDir( K3b::parentDir(filename()) );
+ QString filenamePrefix = filename().section( '/', -1 );
+ filenamePrefix.truncate( filenamePrefix.length() - 3 ); // remove cue extension
+ kdDebug() << "(K3bCueFileParser) checking folder " << parentDir.path() << " for files: " << filenamePrefix << "*" << endl;
+
+ //
+ // we cannot use the nameFilter in QDir because of the spaces that may occur in filenames
+ //
+ QStringList possibleImageFiles = parentDir.entryList( QDir::Files );
+ int cnt = 0;
+ for( QStringList::const_iterator it = possibleImageFiles.constBegin(); it != possibleImageFiles.constEnd(); ++it ) {
+ if( (*it).lower() == dataFile.section( '/', -1 ).lower() ||
+ (*it).startsWith( filenamePrefix ) && !(*it).endsWith( "cue" ) ) {
+ ++cnt;
+ setImageFilename( K3b::parentDir(filename()) + *it );
+ }
+ }
+
+ //
+ // we only do this if there is one unique file which fits the requirements.
+ // Otherwise we cannot be certain to have the right file.
+ //
+ return ( cnt == 1 && QFileInfo( imageFilename() ).isFile() );
+}
diff --git a/libk3b/projects/k3bcuefileparser.h b/libk3b/projects/k3bcuefileparser.h
new file mode 100644
index 0000000..41a5ee6
--- /dev/null
+++ b/libk3b/projects/k3bcuefileparser.h
@@ -0,0 +1,57 @@
+/*
+ *
+ * $Id: k3bcuefileparser.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_CUEFILE_PARSER_H_
+#define _K3B_CUEFILE_PARSER_H_
+
+#include "k3bimagefilereader.h"
+
+#include <k3btoc.h>
+#include <k3bcdtext.h>
+#include "k3b_export.h"
+/**
+ * Parses a cue file.
+ * Datatracks have either mode1 or mode2 where the latter contains xa form1/2.
+ * The last track may not have a proper length!
+ */
+class LIBK3B_EXPORT K3bCueFileParser : public K3bImageFileReader
+{
+ public:
+ K3bCueFileParser( const QString& filename = QString::null );
+ ~K3bCueFileParser();
+
+ /**
+ * CDRDAO does not use this image filename but replaces the extension from the cue file
+ * with "bin" to get the image filename.
+ * So in this case cdrecord won't be able to burn the cue file. That is why we need this hack.
+ */
+ bool imageFilenameInCue() const { return m_imageFilenameInCue; }
+
+ const K3bDevice::Toc& toc() const;
+ const K3bDevice::CdText& cdText() const;
+
+ private:
+ void readFile();
+ bool parseLine( QString& line );
+ void simplifyWhiteSpace( QString& s );
+ bool findImageFileName( const QString& fileEntry );
+
+ bool m_imageFilenameInCue;
+
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/projects/k3bdoc.cpp b/libk3b/projects/k3bdoc.cpp
new file mode 100644
index 0000000..ac5346d
--- /dev/null
+++ b/libk3b/projects/k3bdoc.cpp
@@ -0,0 +1,221 @@
+/*
+ *
+ * $Id: k3bdoc.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+// include files for Qt
+#include <qwidget.h>
+#include <qstring.h>
+#include <qdom.h>
+
+// include files for KDE
+#include <klocale.h>
+#include <kdebug.h>
+
+// application specific includes
+#include "k3bdoc.h"
+#include <k3bglobals.h>
+#include <k3bdevice.h>
+#include <k3bmsf.h>
+#include <k3baudiodoc.h>
+#include <k3bdatadoc.h>
+#include <k3bvcddoc.h>
+#include <k3bmixeddoc.h>
+#include <k3bmovixdoc.h>
+#include <k3bmovixdvddoc.h>
+#include <k3bdvddoc.h>
+#include <k3bvideodvddoc.h>
+#include <k3bcore.h>
+#include <k3bdevicemanager.h>
+
+
+K3bDoc::K3bDoc( QObject* parent )
+ : QObject( parent ),
+ m_modified(false),
+ m_view(0)
+{
+ connect( this, SIGNAL(changed()), this, SLOT(slotChanged()) );
+}
+
+
+K3bDoc::~K3bDoc()
+{
+}
+
+
+void K3bDoc::slotChanged()
+{
+ setModified( true );
+ emit changed( this );
+}
+
+
+void K3bDoc::setModified( bool m )
+{
+ if( m != m_modified ) {
+ m_modified = m;
+ if( m )
+ emit changed();
+ }
+}
+
+
+void K3bDoc::setDummy( bool b )
+{
+ m_dummy = b;
+}
+
+void K3bDoc::setSpeed( int speed )
+{
+ m_speed = speed;
+}
+
+void K3bDoc::setBurner( K3bDevice::Device* dev )
+{
+ m_burner = dev;
+}
+
+
+void K3bDoc::addUrl( const KURL& url )
+{
+ KURL::List urls(url);
+ addUrls( urls );
+}
+
+
+void K3bDoc::setURL( const KURL& url )
+{
+ doc_url = url;
+
+ emit changed();
+}
+
+const KURL& K3bDoc::URL() const
+{
+ return doc_url;
+}
+
+
+QString K3bDoc::name() const
+{
+ return URL().path().section( '/', -1 );
+}
+
+
+bool K3bDoc::newDocument()
+{
+ setModified( false );
+
+ m_copies = 1;
+ m_burner = 0;
+ m_onTheFly = true;
+ m_speed = 0; // Auto
+ m_onlyCreateImages = false;
+ m_removeImages = true;
+ m_dummy = false;
+ m_writingApp = K3b::DEFAULT;
+ m_writingMode = K3b::WRITING_MODE_AUTO;
+ m_saved = false;
+
+ return true;
+}
+
+
+bool K3bDoc::saveGeneralDocumentData( QDomElement* part )
+{
+ QDomDocument doc = part->ownerDocument();
+ QDomElement mainElem = doc.createElement( "general" );
+
+ QDomElement propElem = doc.createElement( "writing_mode" );
+ switch( writingMode() ) {
+ case K3b::DAO:
+ propElem.appendChild( doc.createTextNode( "dao" ) );
+ break;
+ case K3b::TAO:
+ propElem.appendChild( doc.createTextNode( "tao" ) );
+ break;
+ case K3b::RAW:
+ propElem.appendChild( doc.createTextNode( "raw" ) );
+ break;
+ default:
+ propElem.appendChild( doc.createTextNode( "auto" ) );
+ break;
+ }
+ mainElem.appendChild( propElem );
+
+ propElem = doc.createElement( "dummy" );
+ propElem.setAttribute( "activated", dummy() ? "yes" : "no" );
+ mainElem.appendChild( propElem );
+
+ propElem = doc.createElement( "on_the_fly" );
+ propElem.setAttribute( "activated", onTheFly() ? "yes" : "no" );
+ mainElem.appendChild( propElem );
+
+ propElem = doc.createElement( "only_create_images" );
+ propElem.setAttribute( "activated", onlyCreateImages() ? "yes" : "no" );
+ mainElem.appendChild( propElem );
+
+ propElem = doc.createElement( "remove_images" );
+ propElem.setAttribute( "activated", removeImages() ? "yes" : "no" );
+ mainElem.appendChild( propElem );
+
+ part->appendChild( mainElem );
+
+ return true;
+}
+
+
+bool K3bDoc::readGeneralDocumentData( const QDomElement& elem )
+{
+ if( elem.nodeName() != "general" )
+ return false;
+
+ QDomNodeList nodes = elem.childNodes();
+ for( uint i = 0; i < nodes.count(); i++ ) {
+
+ QDomElement e = nodes.item(i).toElement();
+ if( e.isNull() )
+ return false;
+
+ if( e.nodeName() == "writing_mode") {
+ QString mode = e.text();
+ if( mode == "dao" )
+ setWritingMode( K3b::DAO );
+ else if( mode == "tao" )
+ setWritingMode( K3b::TAO );
+ else if( mode == "raw" )
+ setWritingMode( K3b::RAW );
+ else
+ setWritingMode( K3b::WRITING_MODE_AUTO );
+ }
+
+ if( e.nodeName() == "dummy")
+ setDummy( e.attributeNode( "activated" ).value() == "yes" );
+
+ if( e.nodeName() == "on_the_fly")
+ setOnTheFly( e.attributeNode( "activated" ).value() == "yes" );
+
+ if( e.nodeName() == "only_create_images")
+ setOnlyCreateImages( e.attributeNode( "activated" ).value() == "yes" );
+
+ if( e.nodeName() == "remove_images")
+ setRemoveImages( e.attributeNode( "activated" ).value() == "yes" );
+ }
+
+
+ return true;
+}
+
+
+#include "k3bdoc.moc"
diff --git a/libk3b/projects/k3bdoc.h b/libk3b/projects/k3bdoc.h
new file mode 100644
index 0000000..f241487
--- /dev/null
+++ b/libk3b/projects/k3bdoc.h
@@ -0,0 +1,229 @@
+/*
+ *
+ * $Id: k3bdoc.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BDOC_H
+#define K3BDOC_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+// include files for QT
+#include <qobject.h>
+#include <qstring.h>
+#include <qptrlist.h>
+
+
+// include files for KDE
+#include <kurl.h>
+#include <kio/global.h>
+#include "k3b_export.h"
+
+// forward declaration of the K3b classes
+class QTimer;
+class KTempFile;
+class K3bBurnJob;
+class QDomDocument;
+class QDomElement;
+class KConfig;
+class KActionCollection;
+class K3bJobHandler;
+
+
+namespace K3bDevice {
+ class Device;
+}
+namespace K3b {
+ class Msf;
+}
+
+/**
+ * K3bDoc is the base document class.
+ * It handles some general settings.
+ */
+class LIBK3B_EXPORT K3bDoc : public QObject
+{
+ Q_OBJECT
+
+ public:
+ K3bDoc( QObject* = 0 );
+ virtual ~K3bDoc();
+
+ enum DocType {
+ AUDIO = 1,
+ DATA,
+ MIXED,
+ VCD,
+ MOVIX,
+ MOVIX_DVD,
+ DVD,
+ VIDEODVD
+ };
+
+ virtual int type() const { return m_docType; }
+
+ /**
+ * \return A name for the project which might for example be used as a suggestion for a file name
+ * when saving. The default implementation extracts a name from the URL.
+ */
+ virtual QString name() const;
+
+ /**
+ * \return A string representation of the document type.
+ */
+ virtual QString typeString() const = 0;
+
+ /**
+ * returns the view widget set with setView() or null if none has been set.
+ */
+ QWidget* view() const { return m_view; }
+
+ /**
+ * Just for convenience to make an easy mapping from doc to GUI possible.
+ */
+ void setView( QWidget* v ) { m_view = v; }
+
+ /**
+ * sets the modified flag for the document after a modifying action on the view connected to the document.
+ */
+ virtual void setModified( bool m = true );
+
+ /**
+ * returns if the document is modified or not. Use this to determine
+ * if your document needs saving by the user on closing.
+ */
+ virtual bool isModified() const { return m_modified; }
+
+ /**
+ * Subclasses should call this when reimplementing.
+ * Sets some defaults.
+ */
+ virtual bool newDocument();
+
+ /**
+ * Load a project from an xml stream.
+ *
+ * This is used to load/save k3b projects.
+ */
+ virtual bool loadDocumentData( QDomElement* root ) = 0;
+
+ /**
+ * Save a project to an xml stream.
+ *
+ * This is used to load/save k3b projects.
+ */
+ virtual bool saveDocumentData( QDomElement* docElem ) = 0;
+
+ /** returns the KURL of the document */
+ const KURL& URL() const;
+ /** sets the URL of the document */
+ virtual void setURL( const KURL& url );
+
+ int writingMode() const { return m_writingMode; }
+ bool dummy() const { return m_dummy; }
+ bool onTheFly() const { return m_onTheFly; }
+ bool removeImages() const { return m_removeImages; }
+ bool onlyCreateImages() const { return m_onlyCreateImages; }
+ int copies() const { return m_copies; }
+ int speed() const { return m_speed; }
+ K3bDevice::Device* burner() const { return m_burner; }
+ virtual KIO::filesize_t size() const = 0;
+ virtual K3b::Msf length() const = 0;
+
+ // FIXME: rename this to something like imagePath
+ const QString& tempDir() const { return m_tempDir; }
+
+ virtual int numOfTracks() const { return 1; }
+
+ /**
+ * Create a new BurnJob to burn this project. It is not mandatory to use this
+ * method. You may also just create the BurnJob you need manually. It is just
+ * easier this way since you don't need to distinguish between the different
+ * project types.
+ */
+ virtual K3bBurnJob* newBurnJob( K3bJobHandler*, QObject* parent = 0 ) = 0;
+
+ int writingApp() const { return m_writingApp; }
+ void setWritingApp( int a ) { m_writingApp = a; }
+
+ /**
+ * @return true if the document has successfully been saved to a file
+ */
+ bool isSaved() const { return m_saved; }
+
+ /**
+ * Used for session management. Use with care.
+ */
+ void setSaved( bool s ) { m_saved = s; }
+
+ signals:
+ void changed();
+ void changed( K3bDoc* );
+
+ public slots:
+ void setDummy( bool d );
+ void setWritingMode( int m ) { m_writingMode = m; }
+ void setOnTheFly( bool b ) { m_onTheFly = b; }
+ void setSpeed( int speed );
+ void setBurner( K3bDevice::Device* dev );
+ void setTempDir( const QString& dir ) { m_tempDir = dir; }
+ void setRemoveImages( bool b ) { m_removeImages = b; }
+ void setOnlyCreateImages( bool b ) { m_onlyCreateImages = b; }
+ void setCopies( int c ) { m_copies = c; }
+
+ /**
+ * the default implementation just calls addUrls with
+ * list containing the url
+ */
+ virtual void addUrl( const KURL& url );
+ virtual void addUrls( const KURL::List& urls ) = 0;
+
+ protected:
+ int m_docType;
+
+ bool saveGeneralDocumentData( QDomElement* );
+
+ bool readGeneralDocumentData( const QDomElement& );
+
+ private slots:
+ void slotChanged();
+
+ private:
+ /** the modified flag of the current document */
+ bool m_modified;
+ KURL doc_url;
+
+ QWidget* m_view;
+
+ QString m_tempDir;
+ K3bDevice::Device* m_burner;
+ bool m_dummy;
+ bool m_onTheFly;
+ bool m_removeImages;
+ bool m_onlyCreateImages;
+ int m_speed;
+
+ /** see k3bglobals.h */
+ int m_writingApp;
+
+ int m_writingMode;
+
+ int m_copies;
+
+ bool m_saved;
+};
+
+#endif // K3BDOC_H
diff --git a/libk3b/projects/k3bdvdrecordwriter.cpp b/libk3b/projects/k3bdvdrecordwriter.cpp
new file mode 100644
index 0000000..0910d4a
--- /dev/null
+++ b/libk3b/projects/k3bdvdrecordwriter.cpp
@@ -0,0 +1,119 @@
+/*
+ *
+ * $Id: k3bdvdrecordwriter.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bdvdrecordwriter.h"
+
+#include <k3bcore.h>
+#include <k3bexternalbinmanager.h>
+#include <k3bprocess.h>
+#include <k3bdevice.h>
+#include <k3bdevicemanager.h>
+#include <k3bglobals.h>
+#include <k3bglobalsettings.h>
+
+#include <klocale.h>
+
+
+K3bDvdrecordWriter::K3bDvdrecordWriter( K3bDevice::Device* dev, QObject* parent, const char* name )
+ : K3bCdrecordWriter( dev, parent, name )
+{
+}
+
+
+K3bDvdrecordWriter::~K3bDvdrecordWriter()
+{
+}
+
+void K3bDvdrecordWriter::prepareProcess()
+{
+ if( m_process ) delete m_process; // kdelibs want this!
+ m_process = new K3bProcess();
+ m_process->setRunPrivileged(true);
+ m_process->setSplitStdout(true);
+ connect( m_process, SIGNAL(stdoutLine(const QString&)), this, SLOT(slotStdLine(const QString&)) );
+ connect( m_process, SIGNAL(stderrLine(const QString&)), this, SLOT(slotStdLine(const QString&)) );
+ connect( m_process, SIGNAL(processExited(KProcess*)), this, SLOT(slotProcessExited(KProcess*)) );
+ connect( m_process, SIGNAL(wroteStdin(KProcess*)), this, SIGNAL(dataWritten()) );
+
+// if( k3bcore->externalBinManager()->binObject("cdrecord")->hasFeature( "dvd-patch" ) )
+// m_cdrecordBinObject = k3bcore->externalBinManager()->binObject("cdrecord");
+// else
+ m_cdrecordBinObject = k3bcore->externalBinManager()->binObject("dvdrecord");
+
+ if( !m_cdrecordBinObject )
+ return;
+
+ *m_process << m_cdrecordBinObject->path;
+
+ // display progress
+ *m_process << "-v";
+
+ if( m_cdrecordBinObject->hasFeature( "delay") )
+ *m_process << "-delay" << "0";
+ else if( m_cdrecordBinObject->hasFeature( "gracetime") )
+ *m_process << "gracetime=2"; // 2 is the lowest allowed value (Joerg, why do you do this to us?)
+
+ // Again we assume the device to be set!
+ *m_process << QString("dev=%1").arg(K3b::externalBinDeviceParameter(burnDevice(), m_cdrecordBinObject));
+ *m_process << QString("speed=%1").arg(burnSpeed());
+
+ // DVDs are only written in DAO mode (and Packet, but we do not support that since it does not
+ // make much sense here)
+ *m_process << "-dao";
+ setWritingMode( K3b::DAO ); // just to make sure the CdrecordWriter emits the correct messages
+
+ if( simulate() )
+ *m_process << "-dummy";
+
+ if( burnproof() ) {
+ if( burnDevice()->burnproof() ) {
+ // with cdrecord 1.11a02 burnproof was renamed to burnfree
+ // what about dvdrecord??
+ if( m_cdrecordBinObject->version < K3bVersion( "1.11a02" ) )
+ *m_process << "driveropts=burnproof";
+ else
+ *m_process << "driveropts=burnfree";
+ }
+ else
+ emit infoMessage( i18n("Writer does not support buffer underrun free recording (BURNPROOF)"), INFO );
+ }
+
+ if( k3bcore->globalSettings()->ejectMedia() )
+ *m_process << "-eject";
+
+ bool manualBufferSize = k3bcore->globalSettings()->manualBufferSize();
+ if( manualBufferSize ) {
+ *m_process << QString("fs=%1m").arg( k3bcore->globalSettings()->writingBuffer() );
+ }
+
+ bool overburn = k3bcore->globalSettings()->overburn();
+ if( overburn )
+ if( m_cdrecordBinObject->hasFeature("overburn") )
+ *m_process << "-overburn";
+ else
+ emit infoMessage( i18n("Cdrecord %1 does not support overburning.").arg(m_cdrecordBinObject->version), INFO );
+
+ // additional user parameters from config
+ const QStringList& params = m_cdrecordBinObject->userParameters();
+ for( QStringList::const_iterator it = params.begin(); it != params.end(); ++it )
+ *m_process << *it;
+
+ // add the user parameters
+ for( QStringList::const_iterator it = m_arguments.begin(); it != m_arguments.end(); ++it )
+ *m_process << *it;
+}
+
+#include "k3bdvdrecordwriter.moc"
+
diff --git a/libk3b/projects/k3bdvdrecordwriter.h b/libk3b/projects/k3bdvdrecordwriter.h
new file mode 100644
index 0000000..f9dcf4a
--- /dev/null
+++ b/libk3b/projects/k3bdvdrecordwriter.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * $Id: k3bdvdrecordwriter.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_DVDRECORD_WRITER_H_
+#define _K3B_DVDRECORD_WRITER_H_
+
+#include "k3bcdrecordwriter.h"
+
+
+class K3bDevice::Device;
+
+/**
+ * Basically this is just a wrapper around K3bCdrecordWriter
+ * which uses another K3bExternalBin and ignores the writingMode setting.
+ */
+class K3bDvdrecordWriter : public K3bCdrecordWriter
+{
+ Q_OBJECT
+
+ public:
+ K3bDvdrecordWriter( K3bDevice::Device*, QObject* parent = 0, const char* name = 0 );
+ ~K3bDvdrecordWriter();
+
+ protected:
+ void prepareProcess();
+};
+
+#endif
diff --git a/libk3b/projects/k3bgrowisofshandler.cpp b/libk3b/projects/k3bgrowisofshandler.cpp
new file mode 100644
index 0000000..0b582ce
--- /dev/null
+++ b/libk3b/projects/k3bgrowisofshandler.cpp
@@ -0,0 +1,318 @@
+/*
+ *
+ * $Id: k3bgrowisofshandler.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bgrowisofshandler.h"
+
+#include <k3bjob.h>
+#include <k3bcore.h>
+#include <k3bglobalsettings.h>
+#include <k3bdevice.h>
+#include <k3bdevicehandler.h>
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <kdebug.h>
+
+#include <qtimer.h>
+
+#include <errno.h>
+#include <string.h>
+
+
+class K3bGrowisofsHandler::Private
+{
+public:
+ int lastBuffer;
+ int lastDeviceBuffer;
+};
+
+
+K3bGrowisofsHandler::K3bGrowisofsHandler( QObject* parent, const char* name )
+ : QObject( parent, name )
+{
+ d = new Private;
+ reset();
+}
+
+
+K3bGrowisofsHandler::~K3bGrowisofsHandler()
+{
+ delete d;
+}
+
+
+void K3bGrowisofsHandler::reset( K3bDevice::Device* dev, bool dao )
+{
+ m_device = dev;
+ m_error = ERROR_UNKNOWN;
+ m_dao = dao;
+ d->lastBuffer = 0;
+ d->lastDeviceBuffer = 0;
+}
+
+
+void K3bGrowisofsHandler::handleStart()
+{
+// QTimer::singleShot( 2000, this, SLOT(slotCheckBufferStatus()) );
+}
+
+
+void K3bGrowisofsHandler::handleLine( const QString& line )
+{
+ int pos = 0;
+
+ if( line.startsWith( ":-[" ) ) {
+ // Error
+
+ if( line.contains( "ASC=30h" ) )
+ m_error = ERROR_MEDIA;
+
+ // :-[ PERFORM OPC failed with SK=3h/ASC=73h/ASCQ=03h
+ else if( line.startsWith( ":-[ PERFORM OPC failed" ) )
+ emit infoMessage( i18n("OPC failed. Please try writing speed 1x."), K3bJob::ERROR );
+
+ // :-[ attempt -blank=full or re-run with -dvd-compat -dvd-compat to engage DAO ]
+ else if( !m_dao &&
+ ( line.contains( "engage DAO" ) || line.contains( "media is not formatted or unsupported" ) ) )
+ emit infoMessage( i18n("Please try again with writing mode DAO."), K3bJob::ERROR );
+
+ else if( line.startsWith( ":-[ Failed to change write speed" ) ) {
+ m_error = ERROR_SPEED_SET_FAILED;
+ }
+ }
+ else if( line.startsWith( ":-(" ) ) {
+ if( line.contains( "No space left on device" ) )
+ m_error = ERROR_OVERSIZE;
+
+ else if( line.contains( "blocks are free" ) && line.contains( "to be written" ) ) {
+ m_error = ERROR_OVERSIZE;
+ if( k3bcore->globalSettings()->overburn() )
+ emit infoMessage( i18n("Trying to write more than the official disk capacity"), K3bJob::WARNING );
+ }
+
+ else if( line.startsWith( ":-( unable to anonymously mmap" ) ) {
+ m_error = ERROR_MEMLOCK;
+ }
+
+ else if( line.startsWith( ":-( write failed" ) ) {
+ m_error = ERROR_WRITE_FAILED;
+ }
+
+ else
+ emit infoMessage( line, K3bJob::ERROR );
+ }
+ else if( line.startsWith( "PERFORM OPC" ) ) {
+ m_error = ERROR_OPC;
+ }
+ else if( line.contains( "flushing cache" ) ) {
+ // here is where we already should stop queriying the buffer fill
+ // since the device is only used there so far...
+ m_device = 0;
+
+ emit flushingCache();
+ emit newSubTask( i18n("Flushing Cache") );
+ emit infoMessage( i18n("Flushing the cache may take some time."), K3bJob::INFO );
+ }
+
+ // FIXME: I think this starts with dev->blockDeviceName() so we could improve parsing with:
+ // if( line.startsWith( dev->blockDeviceName() ) ) {
+ // line = line.mid( dev->blockDeviceName().length() );
+ // if( line.startsWith( "closing.....
+
+ else if( line.contains( "closing track" ) ) {
+ emit newSubTask( i18n("Closing Track") );
+ }
+ else if( line.contains( "closing disc" ) ) {
+ emit newSubTask( i18n("Closing Disk") );
+ }
+ else if( line.contains( "closing session" ) ) {
+ emit newSubTask( i18n("Closing Session") );
+ }
+ else if( line.contains( "updating RMA" ) ) {
+ emit newSubTask( i18n("Updating RMA") );
+ emit infoMessage( i18n("Updating RMA") + "...", K3bJob::INFO );
+ }
+ else if( line.contains( "closing session" ) ) {
+ emit newSubTask( i18n("Closing Session") );
+ emit infoMessage( i18n("Closing Session") + "...", K3bJob::INFO );
+ }
+ else if( line.contains( "writing lead-out" ) ) {
+ emit newSubTask( i18n("Writing Lead-out") );
+ emit infoMessage( i18n("Writing the lead-out may take some time."), K3bJob::INFO );
+ }
+ else if( line.contains( "Quick Grow" ) ) {
+ emit infoMessage( i18n("Removing reference to lead-out."), K3bJob::INFO );
+ }
+ else if( line.contains( "copying volume descriptor" ) ) {
+ emit infoMessage( i18n("Modifying ISO9660 volume descriptor"), K3bJob::INFO );
+ }
+ else if( line.contains( "FEATURE 21h is not on" ) ) {
+ if( !m_dao ) {
+ // FIXME: it's not only the writer. It may be the media: something like does not support it with this media
+ // da war was mit: wenn einmal formattiert, dann geht nur noch dao oder wenn einmal als overwrite
+ // formattiert, dann nur noch dao oder sowas
+ emit infoMessage( i18n("Writing mode Incremental Streaming not available"), K3bJob::WARNING );
+ emit infoMessage( i18n("Engaging DAO"), K3bJob::WARNING );
+ }
+ }
+ else if( ( pos = line.find( "Current Write Speed" ) ) > 0 ) {
+ // parse write speed
+ // /dev/sr0: "Current Write Speed" is 2.4x1385KBps
+
+ pos += 24;
+ int endPos = line.find( 'x', pos+1 );
+ bool ok = true;
+ double speed = line.mid( pos, endPos-pos ).toDouble(&ok);
+ if( ok )
+ emit infoMessage( i18n("Writing speed: %1 KB/s (%2x)")
+ .arg((int)(speed*1385.0))
+ .arg(KGlobal::locale()->formatNumber(speed)), K3bJob::INFO );
+ else
+ kdDebug() << "(K3bGrowisofsHandler) parsing error: '" << line.mid( pos, endPos-pos ) << "'" << endl;
+ }
+ else if( (pos = line.find( "RBU" )) > 0 ) {
+
+ // FIXME: use QRegExp
+
+ // parse ring buffer fill for growisofs >= 6.0
+ pos += 4;
+ int endPos = line.find( '%', pos+1 );
+ bool ok = true;
+ double val = line.mid( pos, endPos-pos ).toDouble( &ok );
+ if( ok ) {
+ int newBuffer = (int)(val+0.5);
+ if( newBuffer != d->lastBuffer ) {
+ d->lastBuffer = newBuffer;
+ emit buffer( newBuffer );
+ }
+
+ // device buffer for growisofs >= 7.0
+ pos = line.find( "UBU", pos );
+ endPos = line.find( '%', pos+5 );
+ if( pos > 0 ) {
+ pos += 4;
+ val = line.mid( pos, endPos-pos ).toDouble( &ok );
+ if( ok ) {
+ int newBuffer = (int)(val+0.5);
+ if( newBuffer != d->lastDeviceBuffer ) {
+ d->lastDeviceBuffer = newBuffer;
+ emit deviceBuffer( newBuffer );
+ }
+ }
+ }
+ }
+ else
+ kdDebug() << "(K3bGrowisofsHandler) failed to parse ring buffer fill from '" << line.mid( pos, endPos-pos ) << "'" << endl;
+ }
+
+ else {
+ kdDebug() << "(growisofs) " << line << endl;
+ }
+}
+
+
+void K3bGrowisofsHandler::handleExit( int exitCode )
+{
+ switch( m_error ) {
+ case ERROR_MEDIA:
+ emit infoMessage( i18n("K3b detected a problem with the media."), K3bJob::ERROR );
+ emit infoMessage( i18n("Please try another media brand, preferably one explicitly recommended by your writer's vendor."), K3bJob::ERROR );
+ emit infoMessage( i18n("Report the problem if it persists anyway."), K3bJob::ERROR );
+ break;
+
+ case ERROR_OVERSIZE:
+ if( k3bcore->globalSettings()->overburn() )
+ emit infoMessage( i18n("Data did not fit on disk."), K3bJob::ERROR );
+ else
+ emit infoMessage( i18n("Data does not fit on disk."), K3bJob::ERROR );
+ break;
+
+ case ERROR_SPEED_SET_FAILED:
+ emit infoMessage( i18n("Unable to set writing speed."), K3bJob::ERROR );
+ emit infoMessage( i18n("Please try again with the 'ignore speed' setting."), K3bJob::ERROR );
+ break;
+
+ case ERROR_OPC:
+ emit infoMessage( i18n("Optimum Power Calibration failed."), K3bJob::ERROR );
+ emit infoMessage( i18n("Try adding '-use-the-force-luke=noopc' to the "
+ "growisofs user parameters in the K3b settings."), K3bJob::ERROR );
+ break;
+
+ case ERROR_MEMLOCK:
+ emit infoMessage( i18n("Unable to allocate software buffer."), K3bJob::ERROR );
+ emit infoMessage( i18n("This error is caused by the low memorylocked resource limit."), K3bJob::ERROR );
+ emit infoMessage( i18n("It can be solved by issuing the command 'ulimit -l unlimited'..."), K3bJob::ERROR );
+ emit infoMessage( i18n("...or by lowering the used software buffer size in the advanced K3b settings."), K3bJob::ERROR );
+ break;
+
+ case ERROR_WRITE_FAILED:
+ emit infoMessage( i18n("Write error"), K3bJob::ERROR );
+ break;
+
+ default:
+
+ //
+ // The growisofs error codes:
+ //
+ // 128 + errno: fatal error upon program startup
+ // errno : fatal error during recording
+ //
+
+ if( exitCode > 128 ) {
+ // for now we just emit a message with the error
+ // in the future when I know more about what kinds of errors may occur
+ // we will enhance this
+ emit infoMessage( i18n("Fatal error at startup: %1").arg(strerror(exitCode-128)),
+ K3bJob::ERROR );
+ }
+ else if( exitCode == 1 ) {
+ // Doku says: warning at exit
+ // Example: mkisofs error
+ // unable to reload
+ // So basically this is just for mkisofs failure since we do not let growisofs reload the media
+ emit infoMessage( i18n("Warning at exit: (1)"), K3bJob::ERROR );
+ emit infoMessage( i18n("Most likely mkisofs failed in some way."), K3bJob::ERROR );
+ }
+ else {
+ emit infoMessage( i18n("Fatal error during recording: %1").arg(strerror(exitCode)),
+ K3bJob::ERROR );
+ }
+ }
+
+ reset();
+}
+
+
+void K3bGrowisofsHandler::slotCheckBufferStatus()
+{
+ connect( K3bDevice::sendCommand( K3bDevice::DeviceHandler::BUFFER_CAPACITY, m_device ),
+ SIGNAL(finished(K3bDevice::DeviceHandler*)),
+ this,
+ SLOT(slotCheckBufferStatusDone(K3bDevice::DeviceHandler*)) );
+}
+
+
+void K3bGrowisofsHandler::slotCheckBufferStatusDone( K3bDevice::DeviceHandler* dh )
+{
+ if( dh->success() && dh->bufferCapacity() > 0 ) {
+ emit deviceBuffer( 100 * (dh->bufferCapacity() - dh->availableBufferCapacity() ) / dh->bufferCapacity() );
+ QTimer::singleShot( 500, this, SLOT(slotCheckBufferStatus()) );
+ }
+ else {
+ kdDebug() << "(K3bGrowisofsHandler) stopping buffer check." << endl;
+ }
+}
+
+#include "k3bgrowisofshandler.moc"
diff --git a/libk3b/projects/k3bgrowisofshandler.h b/libk3b/projects/k3bgrowisofshandler.h
new file mode 100644
index 0000000..42fcd2a
--- /dev/null
+++ b/libk3b/projects/k3bgrowisofshandler.h
@@ -0,0 +1,87 @@
+/*
+ *
+ * $Id: k3bgrowisofshandler.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_GROWISOFS_HANDLER_H_
+#define _K3B_GROWISOFS_HANDLER_H_
+
+#include <qobject.h>
+
+namespace K3bDevice {
+ class Device;
+ class DeviceHandler;
+}
+
+
+/**
+ * This class handles the output parsing for growisofs
+ * We put it in an extra class since we have two classes
+ * using growisofs: the writer and the imager.
+ */
+class K3bGrowisofsHandler : public QObject
+{
+ Q_OBJECT
+
+ public:
+ K3bGrowisofsHandler( QObject* parent = 0, const char* name = 0 );
+ ~K3bGrowisofsHandler();
+
+ enum ErrorType {
+ ERROR_UNKNOWN,
+ ERROR_MEDIA,
+ ERROR_OVERSIZE,
+ ERROR_SPEED_SET_FAILED,
+ ERROR_OPC,
+ ERROR_MEMLOCK,
+ ERROR_WRITE_FAILED
+ };
+
+ int error() const { return m_error; }
+
+ public slots:
+ /**
+ * This will basically reset the error type
+ * @param dao was growisofs called with DAO?
+ */
+ void reset( K3bDevice::Device* = 0, bool dao = false );
+
+ void handleStart();
+ void handleLine( const QString& );
+ void handleExit( int exitCode );
+
+ signals:
+ void infoMessage( const QString&, int );
+ void newSubTask( const QString& );
+ void buffer( int );
+ void deviceBuffer( int );
+
+ /**
+ * We need this to know when the writing finished to update the progress
+ */
+ void flushingCache();
+
+ private slots:
+ void slotCheckBufferStatus();
+ void slotCheckBufferStatusDone( K3bDevice::DeviceHandler* );
+
+ private:
+ class Private;
+ Private* d;
+
+ int m_error;
+ bool m_dao;
+ K3bDevice::Device* m_device;
+};
+
+#endif
diff --git a/libk3b/projects/k3bgrowisofswriter.cpp b/libk3b/projects/k3bgrowisofswriter.cpp
new file mode 100644
index 0000000..3144547
--- /dev/null
+++ b/libk3b/projects/k3bgrowisofswriter.cpp
@@ -0,0 +1,630 @@
+/*
+ *
+ * $Id: k3bgrowisofswriter.cpp 731898 2007-11-02 08:22:18Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bgrowisofswriter.h"
+
+#include <k3bcore.h>
+#include <k3bdevice.h>
+#include <k3bdevicehandler.h>
+#include <k3bprocess.h>
+#include <k3bexternalbinmanager.h>
+#include <k3bversion.h>
+#include <k3bdiskinfo.h>
+#include <k3bglobals.h>
+#include <k3bthroughputestimator.h>
+#include "k3bgrowisofshandler.h"
+#include <k3bpipebuffer.h>
+#include <k3bglobalsettings.h>
+#include <k3bdeviceglobals.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kglobal.h>
+
+#include <qvaluelist.h>
+#include <qfile.h>
+
+#include <unistd.h>
+
+
+class K3bGrowisofsWriter::Private
+{
+public:
+ Private()
+ : writingMode( 0 ),
+ closeDvd(false),
+ multiSession(false),
+ process( 0 ),
+ growisofsBin( 0 ),
+ trackSize(-1),
+ layerBreak(0),
+ usingRingBuffer(false),
+ ringBuffer(0),
+ forceNoEject( false ) {
+ }
+
+ int writingMode;
+ bool closeDvd;
+ bool multiSession;
+ K3bProcess* process;
+ const K3bExternalBin* growisofsBin;
+ QString image;
+
+ bool success;
+ bool canceled;
+ bool finished;
+
+ QTime lastSpeedCalculationTime;
+ int lastSpeedCalculationBytes;
+ int lastProgress;
+ unsigned int lastProgressed;
+ double lastWritingSpeed;
+
+ bool writingStarted;
+
+ K3bThroughputEstimator* speedEst;
+ K3bGrowisofsHandler* gh;
+
+ // used in DAO with growisofs >= 5.15
+ long trackSize;
+
+ long layerBreak;
+
+ unsigned long long overallSizeFromOutput;
+ long long firstSizeFromOutput;
+
+ QFile inputFile;
+
+ bool usingRingBuffer;
+ K3bPipeBuffer* ringBuffer;
+
+ QString multiSessionInfo;
+
+ bool forceNoEject;
+};
+
+
+K3bGrowisofsWriter::K3bGrowisofsWriter( K3bDevice::Device* dev, K3bJobHandler* hdl,
+ QObject* parent, const char* name )
+ : K3bAbstractWriter( dev, hdl, parent, name )
+{
+ d = new Private;
+ d->speedEst = new K3bThroughputEstimator( this );
+ connect( d->speedEst, SIGNAL(throughput(int)),
+ this, SLOT(slotThroughput(int)) );
+
+ d->gh = new K3bGrowisofsHandler( this );
+ connect( d->gh, SIGNAL(infoMessage(const QString&, int)),
+ this,SIGNAL(infoMessage(const QString&, int)) );
+ connect( d->gh, SIGNAL(newSubTask(const QString&)),
+ this, SIGNAL(newSubTask(const QString&)) );
+ connect( d->gh, SIGNAL(buffer(int)),
+ this, SIGNAL(buffer(int)) );
+ connect( d->gh, SIGNAL(deviceBuffer(int)),
+ this, SIGNAL(deviceBuffer(int)) );
+ connect( d->gh, SIGNAL(flushingCache()),
+ this, SLOT(slotFlushingCache()) );
+}
+
+
+K3bGrowisofsWriter::~K3bGrowisofsWriter()
+{
+ delete d->process;
+ delete d;
+}
+
+
+bool K3bGrowisofsWriter::active() const
+{
+ return (d->process ? d->process->isRunning() : false);
+}
+
+
+int K3bGrowisofsWriter::fd() const
+{
+ if( d->process ) {
+ if( d->usingRingBuffer )
+ return d->ringBuffer->inFd();
+ else
+ return d->process->stdinFd();
+ }
+ else
+ return -1;
+}
+
+
+bool K3bGrowisofsWriter::closeFd()
+{
+ return ( !::close( fd() ) );
+}
+
+
+bool K3bGrowisofsWriter::prepareProcess()
+{
+ d->growisofsBin = k3bcore->externalBinManager()->binObject( "growisofs" );
+ if( !d->growisofsBin ) {
+ emit infoMessage( i18n("Could not find %1 executable.").arg("growisofs"), ERROR );
+ return false;
+ }
+
+ if( d->growisofsBin->version < K3bVersion( 5, 10 ) ) {
+ emit infoMessage( i18n("Growisofs version %1 is too old. "
+ "K3b needs at least version 5.10.").arg(d->growisofsBin->version),
+ ERROR );
+ return false;
+ }
+
+ emit debuggingOutput( "Used versions", "growisofs: " + d->growisofsBin->version );
+
+ if( !d->growisofsBin->copyright.isEmpty() )
+ emit infoMessage( i18n("Using %1 %2 - Copyright (C) %3").arg("growisofs")
+ .arg(d->growisofsBin->version).arg(d->growisofsBin->copyright), INFO );
+
+
+ //
+ // The growisofs bin is ready. Now we add the parameters
+ //
+ delete d->process;
+ d->process = new K3bProcess();
+ d->process->setRunPrivileged(true);
+ // d->process->setPriority( KProcess::PrioHighest );
+ d->process->setSplitStdout(true);
+ d->process->setRawStdin(true);
+ connect( d->process, SIGNAL(stderrLine(const QString&)), this, SLOT(slotReceivedStderr(const QString&)) );
+ connect( d->process, SIGNAL(stdoutLine(const QString&)), this, SLOT(slotReceivedStderr(const QString&)) );
+ connect( d->process, SIGNAL(processExited(KProcess*)), this, SLOT(slotProcessExited(KProcess*)) );
+
+
+ //
+ // growisofs < 5.20 wants the tracksize to be a multiple of 16 (1 ECC block: 16*2048 bytes)
+ // we simply pad ourselves.
+ //
+ // But since the writer itself properly pads or writes a longer lead-out we don't really need
+ // to write zeros. We just tell growisofs to reserve a multiple of 16 blocks.
+ // This is only releveant in DAO mode anyway.
+ //
+ // FIXME: seems as we also need this for double layer writing. Better make it the default and
+ // actually write the pad bytes. The only possibility I see right now is to add a padding option
+ // to the pipebuffer.
+ int trackSizePadding = 0;
+ if( d->trackSize > 0 && d->growisofsBin->version < K3bVersion( 5, 20 ) ) {
+ if( d->trackSize % 16 ) {
+ trackSizePadding = (16 - d->trackSize%16);
+ kdDebug() << "(K3bGrowisofsWriter) need to pad " << trackSizePadding << " blocks." << endl;
+ }
+ }
+
+
+ *d->process << d->growisofsBin;
+
+ // set this var to true to enable the ringbuffer
+ d->usingRingBuffer = ( d->growisofsBin->version < K3bVersion( 6, 0 ) );
+
+ QString s = burnDevice()->blockDeviceName() + "=";
+ if( d->usingRingBuffer || d->image.isEmpty() ) {
+ // we always read from stdin since the ringbuffer does the actual reading from the source
+ s += "/dev/fd/0";
+ }
+ else
+ s += d->image;
+
+ if( d->multiSession && !d->multiSessionInfo.isEmpty() )
+ *d->process << "-C" << d->multiSessionInfo;
+
+ if( d->multiSession )
+ *d->process << "-M";
+ else
+ *d->process << "-Z";
+ *d->process << s;
+
+
+ if( !d->image.isEmpty() && d->usingRingBuffer ) {
+ d->inputFile.setName( d->image );
+ d->trackSize = (K3b::filesize( d->image ) + 1024) / 2048;
+ if( !d->inputFile.open( IO_ReadOnly ) ) {
+ emit infoMessage( i18n("Could not open file %1.").arg(d->image), ERROR );
+ return false;
+ }
+ }
+
+ // now we use the force (luke ;) do not reload the dvd, K3b does that.
+ *d->process << "-use-the-force-luke=notray";
+
+ // we check for existing filesystems ourselves, so we always force the overwrite...
+ *d->process << "-use-the-force-luke=tty";
+
+ bool dvdCompat = d->closeDvd;
+
+ // DL writing with forced layer break
+ if( d->layerBreak > 0 ) {
+ *d->process << "-use-the-force-luke=break:" + QString::number(d->layerBreak);
+ dvdCompat = true;
+ }
+
+ // the tracksize parameter takes priority over the dao:tracksize parameter since growisofs 5.18
+ else if( d->growisofsBin->version > K3bVersion( 5, 17 ) && d->trackSize > 0 )
+ *d->process << "-use-the-force-luke=tracksize:" + QString::number(d->trackSize + trackSizePadding);
+
+ if( simulate() )
+ *d->process << "-use-the-force-luke=dummy";
+
+ if( d->writingMode == K3b::DAO ) {
+ dvdCompat = true;
+ if( d->growisofsBin->version >= K3bVersion( 5, 15 ) && d->trackSize > 0 )
+ *d->process << "-use-the-force-luke=dao:" + QString::number(d->trackSize + trackSizePadding);
+ else
+ *d->process << "-use-the-force-luke=dao";
+ d->gh->reset( burnDevice(), true );
+ }
+ else
+ d->gh->reset( burnDevice(), false );
+
+ //
+ // Never use the -dvd-compat parameter with DVD+RW media
+ // because the only thing it does is creating problems.
+ // Normally this should be done in growisofs
+ //
+ int mediaType = burnDevice()->mediaType();
+ if( dvdCompat &&
+ mediaType != K3bDevice::MEDIA_DVD_PLUS_RW &&
+ mediaType != K3bDevice::MEDIA_DVD_RW_OVWR )
+ *d->process << "-dvd-compat";
+
+ //
+ // Some DVD writers do not allow changing the writing speed so we allow
+ // the user to ignore the speed setting
+ //
+ int speed = burnSpeed();
+ if( speed >= 0 ) {
+ if( speed == 0 ) {
+ // try to determine the writeSpeed
+ // if it fails determineOptimalWriteSpeed() will return 0 and
+ // the choice is left to growisofs which means that the choice is
+ // really left to the drive since growisofs does not change the speed
+ // if no option is given
+ speed = burnDevice()->determineMaximalWriteSpeed();
+ }
+
+ // speed may be a float number. example: DVD+R(W): 2.4x
+ if( speed != 0 )
+ *d->process << QString("-speed=%1").arg( speed%1385 > 0
+ ? QString::number( (float)speed/1385.0, 'f', 1 )
+ : QString::number( speed/1385 ) );
+ }
+
+ if( k3bcore->globalSettings()->overburn() )
+ *d->process << "-overburn";
+
+ if( !d->usingRingBuffer && d->growisofsBin->version >= K3bVersion( 6, 0 ) ) {
+ bool manualBufferSize = k3bcore->globalSettings()->useManualBufferSize();
+ int bufSize = ( manualBufferSize ? k3bcore->globalSettings()->bufferSize() : 32 );
+ *d->process << QString("-use-the-force-luke=bufsize:%1m").arg(bufSize);
+ }
+
+ // additional user parameters from config
+ const QStringList& params = d->growisofsBin->userParameters();
+ for( QStringList::const_iterator it = params.begin(); it != params.end(); ++it )
+ *d->process << *it;
+
+ emit debuggingOutput( "Burned media", K3bDevice::mediaTypeString(mediaType) );
+
+ return true;
+}
+
+
+void K3bGrowisofsWriter::start()
+{
+ jobStarted();
+
+ d->lastWritingSpeed = 0;
+ d->lastProgressed = 0;
+ d->lastProgress = 0;
+ d->firstSizeFromOutput = -1;
+ d->lastSpeedCalculationTime = QTime::currentTime();
+ d->lastSpeedCalculationBytes = 0;
+ d->writingStarted = false;
+ d->canceled = false;
+ d->speedEst->reset();
+ d->finished = false;
+
+ if( !prepareProcess() ) {
+ jobFinished( false );
+ }
+ else {
+
+ kdDebug() << "***** " << d->growisofsBin->name() << " parameters:\n";
+ const QValueList<QCString>& args = d->process->args();
+ QString s;
+ for( QValueList<QCString>::const_iterator it = args.begin(); it != args.end(); ++it ) {
+ s += *it + " ";
+ }
+ kdDebug() << s << flush << endl;
+ emit debuggingOutput( d->growisofsBin->name() + " command:", s);
+
+
+ emit newSubTask( i18n("Preparing write process...") );
+
+ // FIXME: check the return value
+ if( K3b::isMounted( burnDevice() ) ) {
+ emit infoMessage( i18n("Unmounting medium"), INFO );
+ K3b::unmount( burnDevice() );
+ }
+
+ // block the device (including certain checks)
+ k3bcore->blockDevice( burnDevice() );
+
+ // lock the device for good in this process since it will
+ // be opened in the growisofs process
+ burnDevice()->close();
+ burnDevice()->usageLock();
+
+ if( !d->process->start( KProcess::NotifyOnExit, KProcess::All ) ) {
+ // something went wrong when starting the program
+ // it "should" be the executable
+ kdDebug() << "(K3bGrowisofsWriter) could not start " << d->growisofsBin->path << endl;
+ emit infoMessage( i18n("Could not start %1.").arg(d->growisofsBin->name()), K3bJob::ERROR );
+ jobFinished(false);
+ }
+ else {
+ if( simulate() ) {
+ emit newTask( i18n("Simulating") );
+ emit infoMessage( i18n("Starting simulation..."),
+ K3bJob::INFO );
+ }
+ else {
+ emit newTask( i18n("Writing") );
+ emit infoMessage( i18n("Starting disc write..."), K3bJob::INFO );
+ }
+
+ d->gh->handleStart();
+
+ // create the ring buffer
+ if( d->usingRingBuffer ) {
+ if( !d->ringBuffer ) {
+ d->ringBuffer = new K3bPipeBuffer( this, this );
+ connect( d->ringBuffer, SIGNAL(percent(int)), this, SIGNAL(buffer(int)) );
+ connect( d->ringBuffer, SIGNAL(finished(bool)), this, SLOT(slotRingBufferFinished(bool)) );
+ }
+
+ d->ringBuffer->writeToFd( d->process->stdinFd() );
+ bool manualBufferSize = k3bcore->globalSettings()->useManualBufferSize();
+ int bufSize = ( manualBufferSize ? k3bcore->globalSettings()->bufferSize() : 20 );
+ d->ringBuffer->setBufferSize( bufSize );
+
+ if( !d->image.isEmpty() )
+ d->ringBuffer->readFromFd( d->inputFile.handle() );
+
+ d->ringBuffer->start();
+ }
+ }
+ }
+}
+
+
+void K3bGrowisofsWriter::cancel()
+{
+ if( active() ) {
+ d->canceled = true;
+ closeFd();
+ if( d->usingRingBuffer && d->ringBuffer )
+ d->ringBuffer->cancel();
+ d->process->kill();
+ }
+}
+
+
+void K3bGrowisofsWriter::setWritingMode( int m )
+{
+ d->writingMode = m;
+}
+
+
+void K3bGrowisofsWriter::setTrackSize( long size )
+{
+ d->trackSize = size;
+}
+
+
+void K3bGrowisofsWriter::setLayerBreak( long lb )
+{
+ d->layerBreak = lb;
+}
+
+
+void K3bGrowisofsWriter::setCloseDvd( bool b )
+{
+ d->closeDvd = b;
+}
+
+
+void K3bGrowisofsWriter::setMultiSession( bool b )
+{
+ d->multiSession = b;
+}
+
+
+void K3bGrowisofsWriter::setImageToWrite( const QString& filename )
+{
+ d->image = filename;
+}
+
+
+void K3bGrowisofsWriter::slotReceivedStderr( const QString& line )
+{
+ emit debuggingOutput( d->growisofsBin->name(), line );
+
+ if( line.contains( "remaining" ) ) {
+
+ if( !d->writingStarted ) {
+ d->writingStarted = true;
+ emit newSubTask( i18n("Writing data") );
+ }
+
+ // parse progress
+ int pos = line.find( "/" );
+ unsigned long long done = line.left( pos ).toULongLong();
+ bool ok = true;
+ d->overallSizeFromOutput = line.mid( pos+1, line.find( "(", pos ) - pos - 1 ).toULongLong( &ok );
+ if( d->firstSizeFromOutput == -1 )
+ d->firstSizeFromOutput = done;
+ done -= d->firstSizeFromOutput;
+ d->overallSizeFromOutput -= d->firstSizeFromOutput;
+ if( ok ) {
+ int p = (int)(100 * done / d->overallSizeFromOutput);
+ if( p > d->lastProgress ) {
+ emit percent( p );
+ emit subPercent( p );
+ d->lastProgress = p;
+ }
+ if( (unsigned int)(done/1024/1024) > d->lastProgressed ) {
+ d->lastProgressed = (unsigned int)(done/1024/1024);
+ emit processedSize( d->lastProgressed, (int)(d->overallSizeFromOutput/1024/1024) );
+ emit processedSubSize( d->lastProgressed, (int)(d->overallSizeFromOutput/1024/1024) );
+ }
+
+ // try parsing write speed (since growisofs 5.11)
+ pos = line.find( '@' );
+ if( pos != -1 ) {
+ pos += 1;
+ double speed = line.mid( pos, line.find( 'x', pos ) - pos ).toDouble(&ok);
+ if( ok ) {
+ if( d->lastWritingSpeed != speed )
+ emit writeSpeed( (int)(speed*1385.0), 1385 );
+ d->lastWritingSpeed = speed;
+ }
+ else
+ kdDebug() << "(K3bGrowisofsWriter) speed parsing failed: '"
+ << line.mid( pos, line.find( 'x', pos ) - pos ) << "'" << endl;
+ }
+ else {
+ d->speedEst->dataWritten( done/1024 );
+ }
+ }
+ else
+ kdDebug() << "(K3bGrowisofsWriter) progress parsing failed: '"
+ << line.mid( pos+1, line.find( "(", pos ) - pos - 1 ).stripWhiteSpace() << "'" << endl;
+ }
+
+ // else
+ // to be able to parse the ring buffer fill in growisofs 6.0 we need to do this all the time
+ // FIXME: get rid of the K3bGrowisofsHandler once it is sure that we do not need the K3bGrowisofsImager anymore
+ d->gh->handleLine( line );
+}
+
+
+void K3bGrowisofsWriter::slotProcessExited( KProcess* p )
+{
+ d->inputFile.close();
+
+ // release the device within this process
+ burnDevice()->usageUnlock();
+
+ // unblock the device
+ k3bcore->unblockDevice( burnDevice() );
+
+ if( d->canceled ) {
+ if( !d->finished ) {
+ d->finished = true;
+ // this will unblock and eject the drive and emit the finished/canceled signals
+ K3bAbstractWriter::cancel();
+ }
+ return;
+ }
+
+ d->finished = true;
+
+ // it seems that growisofs sometimes exits with a valid exit code while a write error occured
+ if( p->exitStatus() == 0 && d->gh->error() != K3bGrowisofsHandler::ERROR_WRITE_FAILED ) {
+
+ int s = d->speedEst->average();
+ if( s > 0 )
+ emit infoMessage( i18n("Average overall write speed: %1 KB/s (%2x)")
+ .arg(s).arg(KGlobal::locale()->formatNumber((double)s/1385.0), 2), INFO );
+
+ if( simulate() )
+ emit infoMessage( i18n("Simulation successfully completed"), K3bJob::SUCCESS );
+ else
+ emit infoMessage( i18n("Writing successfully completed"), K3bJob::SUCCESS );
+
+ d->success = true;
+ }
+ else {
+ if( !wasSourceUnreadable() )
+ d->gh->handleExit( p->exitStatus() );
+ d->success = false;
+ }
+
+ if( !k3bcore->globalSettings()->ejectMedia() || d->forceNoEject )
+ jobFinished(d->success);
+ else {
+ emit newSubTask( i18n("Ejecting DVD") );
+ connect( K3bDevice::eject( burnDevice() ),
+ SIGNAL(finished(K3bDevice::DeviceHandler*)),
+ this,
+ SLOT(slotEjectingFinished(K3bDevice::DeviceHandler*)) );
+ }
+}
+
+
+void K3bGrowisofsWriter::slotRingBufferFinished( bool )
+{
+ if( !d->finished ) {
+ d->finished = true;
+ // this will unblock and eject the drive and emit the finished/canceled signals
+ K3bAbstractWriter::cancel();
+ }
+}
+
+
+void K3bGrowisofsWriter::slotEjectingFinished( K3bDevice::DeviceHandler* dh )
+{
+ if( !dh->success() )
+ emit infoMessage( i18n("Unable to eject media."), ERROR );
+
+ jobFinished(d->success);
+}
+
+
+void K3bGrowisofsWriter::slotThroughput( int t )
+{
+ emit writeSpeed( t, 1385 );
+}
+
+
+void K3bGrowisofsWriter::slotFlushingCache()
+{
+ if( !d->canceled ) {
+ //
+ // growisofs's progress output stops before 100%, so we do it manually
+ //
+ emit percent( 100 );
+ emit processedSize( d->overallSizeFromOutput/1024/1024,
+ d->overallSizeFromOutput/1024/1024 );
+ }
+}
+
+
+void K3bGrowisofsWriter::setMultiSessionInfo( const QString& info )
+{
+ d->multiSessionInfo = info;
+}
+
+
+void K3bGrowisofsWriter::setForceNoEject( bool b )
+{
+ d->forceNoEject = b;
+}
+
+#include "k3bgrowisofswriter.moc"
diff --git a/libk3b/projects/k3bgrowisofswriter.h b/libk3b/projects/k3bgrowisofswriter.h
new file mode 100644
index 0000000..ed69923
--- /dev/null
+++ b/libk3b/projects/k3bgrowisofswriter.h
@@ -0,0 +1,106 @@
+/*
+ *
+ * $Id: k3bgrowisofswriter.h 679276 2007-06-23 13:25:21Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_GROWISOFS_WRITER_H_
+#define _K3B_GROWISOFS_WRITER_H_
+
+#include "k3babstractwriter.h"
+
+
+namespace K3bDevice {
+ class Device;
+ class DeviceHandler;
+}
+class KProcess;
+
+
+
+class K3bGrowisofsWriter : public K3bAbstractWriter
+{
+ Q_OBJECT
+
+ public:
+ K3bGrowisofsWriter( K3bDevice::Device*, K3bJobHandler*,
+ QObject* parent = 0, const char* name = 0 );
+ ~K3bGrowisofsWriter();
+
+ bool active() const;
+
+ int fd() const;
+ bool closeFd();
+
+ public slots:
+ void start();
+ void cancel();
+
+ void setWritingMode( int );
+
+ /**
+ * If true the growisofs parameter -M is used in favor of -Z.
+ */
+ void setMultiSession( bool b );
+
+ /**
+ * Only used in DAO mode and only supported with growisofs >= 5.15
+ * @param size size in blocks
+ */
+ void setTrackSize( long size );
+
+ /**
+ * Use this in combination with setTrackSize when writing double layer media.
+ * @param lb The number of data sectors in the first layer. It needs to be less or equal
+ * to tracksize/2. The writer will pad the second layer with zeros if
+ * break < tracksize/2.
+ * If set to 0 this setting will be ignored.
+ */
+ void setLayerBreak( long lb );
+
+ /**
+ * Close the DVD to enable max DVD compatibility (uses the growisofs --dvd-compat parameter)
+ * This will also be used in DAO mode and when the layerBreak has been set.
+ */
+ void setCloseDvd( bool );
+
+ /**
+ * set this to QString::null or an empty string to let the writer
+ * read it's data from fd()
+ */
+ void setImageToWrite( const QString& );
+
+ /**
+ * While reading the image from stdin growisofs needs
+ * a valid -C parameter for multisession.
+ */
+ void setMultiSessionInfo( const QString& );
+
+ void setForceNoEject( bool );
+
+ protected:
+ bool prepareProcess();
+
+ protected slots:
+ void slotReceivedStderr( const QString& );
+ void slotProcessExited( KProcess* );
+ void slotEjectingFinished( K3bDevice::DeviceHandler* dh );
+ void slotThroughput( int t );
+ void slotFlushingCache();
+ void slotRingBufferFinished( bool );
+
+ private:
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/projects/k3bimagefilereader.cpp b/libk3b/projects/k3bimagefilereader.cpp
new file mode 100644
index 0000000..70ece16
--- /dev/null
+++ b/libk3b/projects/k3bimagefilereader.cpp
@@ -0,0 +1,88 @@
+/*
+ *
+ * $Id: k3bimagefilereader.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bimagefilereader.h"
+
+#include <qfile.h>
+#include <qtextstream.h>
+
+#include <kdebug.h>
+
+
+
+class K3bImageFileReader::Private
+{
+public:
+ Private()
+ : isValid(false) {
+ }
+
+ QString filename;
+ QString imageFilename;
+ bool isValid;
+};
+
+
+K3bImageFileReader::K3bImageFileReader()
+{
+ d = new Private();
+}
+
+
+K3bImageFileReader::~K3bImageFileReader()
+{
+ delete d;
+}
+
+
+void K3bImageFileReader::openFile( const QString& filename )
+{
+ d->filename = filename;
+ d->imageFilename = QString::null;
+ setValid(false);
+
+ if( !filename.isEmpty() )
+ readFile();
+}
+
+
+void K3bImageFileReader::setValid( bool b )
+{
+ d->isValid = b;
+}
+
+
+void K3bImageFileReader::setImageFilename( const QString& filename )
+{
+ d->imageFilename = filename;
+}
+
+
+bool K3bImageFileReader::isValid() const
+{
+ return d->isValid;
+}
+
+
+const QString& K3bImageFileReader::filename() const
+{
+ return d->filename;
+}
+
+
+const QString& K3bImageFileReader::imageFilename() const
+{
+ return d->imageFilename;
+}
diff --git a/libk3b/projects/k3bimagefilereader.h b/libk3b/projects/k3bimagefilereader.h
new file mode 100644
index 0000000..2bf727e
--- /dev/null
+++ b/libk3b/projects/k3bimagefilereader.h
@@ -0,0 +1,55 @@
+/*
+ *
+ * $Id: k3bimagefilereader.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_IMAGE_FILE_READER_H_
+#define _K3B_IMAGE_FILE_READER_H_
+
+#include <qstring.h>
+#include "k3b_export.h"
+
+class LIBK3B_EXPORT K3bImageFileReader
+{
+ public:
+ K3bImageFileReader();
+ virtual ~K3bImageFileReader();
+
+ /**
+ * Open a file. In most cases the TOC file
+ */
+ void openFile( const QString& filename );
+
+ virtual bool isValid() const;
+
+ /**
+ * Return the current set filename;
+ */
+ const QString& filename() const;
+
+ /**
+ * returns the name of the corresponding image file.
+ */
+ virtual const QString& imageFilename() const;
+
+ protected:
+ virtual void readFile() = 0;
+ void setValid( bool );
+ void setImageFilename( const QString& );
+
+ private:
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/projects/k3binffilewriter.cpp b/libk3b/projects/k3binffilewriter.cpp
new file mode 100644
index 0000000..9395b2a
--- /dev/null
+++ b/libk3b/projects/k3binffilewriter.cpp
@@ -0,0 +1,186 @@
+/*
+ *
+ * $Id: k3binffilewriter.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3binffilewriter.h"
+
+#include <k3bcdtext.h>
+#include <k3btrack.h>
+#include <k3bmsf.h>
+#include <k3bcore.h>
+#include <k3bversion.h>
+
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qdatetime.h>
+
+
+
+K3bInfFileWriter::K3bInfFileWriter()
+ : m_index0(-1),
+ m_trackNumber(1),
+ m_trackStart(0),
+ m_trackLength(0),
+ m_preEmphasis(false),
+ m_copyPermitted(true),
+ m_bigEndian(false)
+{
+}
+
+
+bool K3bInfFileWriter::save( const QString& filename )
+{
+ QFile f( filename );
+
+ if( !f.open( IO_WriteOnly ) ) {
+ kdDebug() << "(K3bInfFileWriter) could not open file " << f.name() << endl;
+ return false;
+ }
+
+ QTextStream s( &f );
+
+ return save( s );
+}
+
+
+bool K3bInfFileWriter::save( QTextStream& s )
+{
+ // now write the inf data
+ // ----------------------
+ // header
+ s << "# Cdrecord-Inf-File written by K3b " << k3bcore->version()
+ << ", " << QDateTime::currentDateTime().toString() << endl
+ << "#" << endl;
+
+ s << "ISRC=\t\t" << m_isrc << endl;
+ s << "MCN=\t\t" << m_mcn << endl;
+
+ // CD-Text
+ s << "Albumperformer=\t" << "'" << m_albumPerformer << "'" << endl;
+ s << "Albumtitle=\t" << "'" << m_albumTitle << "'" << endl;
+
+ s << "Performer=\t" << "'" << m_trackPerformer << "'" << endl;
+ s << "Songwriter=\t" << "'" << m_trackSongwriter << "'" << endl;
+ s << "Composer=\t" << "'" << m_trackComposer << "'" << endl;
+ s << "Arranger=\t" << "'" << m_trackArranger << "'" << endl;
+ s << "Message=\t" << "'" << m_trackMessage << "'" << endl;
+
+ s << "Tracktitle=\t" << "'" << m_trackTitle << "'" << endl;
+
+ s << "Tracknumber=\t" << m_trackNumber << endl;
+
+ // track start
+ s << "Trackstart=\t" << m_trackStart.lba() << endl;
+
+ // track length
+ s << "# Tracklength: " << m_trackLength.toString() << endl;
+ s << "Tracklength=\t" << m_trackLength.totalFrames() << ", 0" << endl;
+
+ // pre-emphasis
+ s << "Pre-emphasis=\t";
+ if( m_preEmphasis )
+ s << "yes";
+ else
+ s << "no";
+ s << endl;
+
+ // channels (always 2)
+ s << "Channels=\t2" << endl;
+
+ // copy-permitted
+ // TODO: not sure about this!
+ // there are three options: yes, no, once
+ // but using "once" gives the same result as with cdrdao
+ // and that's important.
+ s << "Copy_permitted=\t";
+ if( m_copyPermitted )
+ s << "yes";
+ else
+ s << "once";
+ s << endl;
+
+ // endianess - wav is little -> onthefly: big, with images: little
+ s << "Endianess=\t";
+ if( m_bigEndian )
+ s << "big";
+ else
+ s << "little";
+ s << endl;
+
+ // write indices
+ // the current tracks' data contains the pregap of the next track
+ // if the pregap has length 0 we need no index 0
+ if( m_indices.isEmpty() )
+ s << "Index=\t\t0" << endl;
+ else {
+ for( unsigned int i = 0; i < m_indices.count(); ++i )
+ s << "Index=\t\t" << m_indices[i] << endl;
+ }
+
+ s << "Index0=\t\t" << m_index0 << endl;
+
+ return ( s.device()->status() == IO_Ok );
+}
+
+
+void K3bInfFileWriter::setTrack( const K3bDevice::Track& track )
+{
+ m_indices.clear();
+
+ // the first index always has to be a zero (cdrecord manpage)
+ m_indices.append( 0 );
+
+ const QValueVector<K3b::Msf>& indexList = track.indices();
+ for( unsigned int i = 0; i < indexList.count(); ++i )
+ m_indices.append( indexList[i].lba() );
+
+ if( track.index0() > 0 )
+ m_index0 = track.index0().lba();
+ else
+ m_index0 = -1;
+
+ setPreEmphasis( track.preEmphasis() );
+ setCopyPermitted( track.copyPermitted() );
+
+ setTrackStart( track.firstSector() );
+ setTrackLength( track.length() );
+
+ setIsrc( track.isrc() );
+
+ setBigEndian( true );
+}
+
+
+void K3bInfFileWriter::addIndex( long i )
+{
+ m_indices.append( i );
+}
+
+
+void K3bInfFileWriter::setTrackCdText( const K3bDevice::TrackCdText& cdtext )
+{
+ setTrackTitle( cdtext.title() );
+ setTrackPerformer( cdtext.performer() );
+ setTrackSongwriter( cdtext.songwriter() );
+ setTrackComposer( cdtext.composer() );
+ setTrackArranger( cdtext.arranger() );
+ setTrackMessage( cdtext.message() );
+}
+
+
+void K3bInfFileWriter::setCdText( const K3bDevice::CdText& cdtext )
+{
+ setAlbumTitle( cdtext.title() );
+ setAlbumPerformer( cdtext.performer() );
+}
diff --git a/libk3b/projects/k3binffilewriter.h b/libk3b/projects/k3binffilewriter.h
new file mode 100644
index 0000000..74e23e4
--- /dev/null
+++ b/libk3b/projects/k3binffilewriter.h
@@ -0,0 +1,119 @@
+/*
+ *
+ * $Id: k3binffilewriter.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_INF_FILE_WRITER_H_
+#define _K3B_INF_FILE_WRITER_H_
+
+#include <qvaluevector.h>
+#include <qtextstream.h>
+
+#include <k3bmsf.h>
+
+
+namespace K3bDevice {
+ class Track;
+ class TrackCdText;
+ class CdText;
+}
+
+
+class K3bInfFileWriter
+{
+ public:
+ K3bInfFileWriter();
+
+ bool save( QTextStream& );
+ bool save( const QString& filename );
+
+ /**
+ * Use this to set:
+ * @li trackStart
+ * @li trackLength
+ * @li index0
+ * @li all indices
+ * @li preemphasis
+ * @li copyPermitted
+ * @li ISRC
+ *
+ * Endianess is set to big.
+ *
+ * Tracknumber needs to be set manually.
+ */
+ void setTrack( const K3bDevice::Track& );
+
+ void clearIndices() { m_indices.clear(); }
+
+ /**
+ * This is relative to the track start
+ */
+ void setIndex0( int i ) { m_index0 = i; }
+ void addIndex( long i );
+
+ void setTrackNumber( int i ) { m_trackNumber = i; }
+
+ void setTrackStart( const K3b::Msf& i ) { m_trackStart = i; }
+ void setTrackLength( const K3b::Msf& i ) { m_trackLength = i; }
+
+ void setPreEmphasis( bool b ) { m_preEmphasis = b; }
+ void setCopyPermitted( bool b ) { m_copyPermitted = b; }
+
+ /**
+ * Cdrecord seems to ignore this anyway and always expect big endian
+ * data on stdin and wavs are little endian anyway.
+ */
+ void setBigEndian( bool b ) { m_bigEndian = b; }
+
+ void setTrackCdText( const K3bDevice::TrackCdText& );
+ void setTrackTitle( const QString& s ) { m_trackTitle = s; }
+ void setTrackPerformer( const QString& s ) { m_trackPerformer = s; }
+ void setTrackSongwriter( const QString& s ) { m_trackSongwriter = s; }
+ void setTrackComposer( const QString& s ) { m_trackComposer = s; }
+ void setTrackArranger( const QString& s ) { m_trackArranger = s; }
+ void setTrackMessage( const QString& s ) { m_trackMessage = s; }
+
+ void setCdText( const K3bDevice::CdText& );
+ void setAlbumTitle( const QString& s ) { m_albumTitle = s; }
+ void setAlbumPerformer( const QString& s ) { m_albumPerformer = s; }
+
+ void setIsrc( const QCString& s ) { m_isrc = s; }
+ void setMcn( const QCString& s ) { m_mcn = s; }
+
+ private:
+ long m_index0;
+
+ QValueVector<long> m_indices;
+
+ int m_trackNumber;
+ K3b::Msf m_trackStart;
+ K3b::Msf m_trackLength;
+ bool m_preEmphasis;
+ bool m_copyPermitted;
+ bool m_bigEndian;
+
+ QString m_trackTitle;
+ QString m_trackPerformer;
+ QString m_trackSongwriter;
+ QString m_trackComposer;
+ QString m_trackArranger;
+ QString m_trackMessage;
+
+ QString m_albumTitle;
+ QString m_albumPerformer;
+
+ QCString m_isrc;
+ QCString m_mcn;
+};
+
+#endif
diff --git a/libk3b/projects/k3bpipebuffer.cpp b/libk3b/projects/k3bpipebuffer.cpp
new file mode 100644
index 0000000..3b61116
--- /dev/null
+++ b/libk3b/projects/k3bpipebuffer.cpp
@@ -0,0 +1,281 @@
+/*
+ *
+ * $Id: k3bpipebuffer.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bpipebuffer.h"
+
+#include <k3bthread.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+
+//
+// This one is based on the little pipebuf2 program by Peter Osterlund <petero2@telia.com>
+//
+
+
+class K3bPipeBuffer::WorkThread : public K3bThread
+{
+public:
+ WorkThread()
+ : K3bThread(),
+ buffer(0),
+ bufSize(4*1024*1024),
+ canceled(false) {
+ outFd = inFd = -1;
+ inFdPair[0] = inFdPair[1] = -1;
+ }
+
+ ~WorkThread() {
+ delete [] buffer;
+ }
+
+ bool initFds() {
+ if( inFd == -1 ) {
+ if( ::socketpair(AF_UNIX, SOCK_STREAM, 0, inFdPair) ) {
+ // if( ::pipe( inFdPair ) ) {
+ kdDebug() << "(K3bPipeBuffer::WorkThread) unable to create socketpair" << endl;
+ inFdPair[0] = inFdPair[1] = -1;
+ return false;
+ }
+ else {
+ ::fcntl(inFdPair[0], F_SETFL, O_NONBLOCK);
+ ::fcntl(outFd, F_SETFL, O_NONBLOCK);
+ }
+ }
+ else {
+ ::fcntl(inFd, F_SETFL, O_NONBLOCK);
+ }
+
+ delete [] buffer;
+ buffer = new char[bufSize];
+
+ return (buffer != 0);
+ }
+
+ void run() {
+ emitStarted();
+
+ int usedInFd = -1;
+ if( inFd > 0 )
+ usedInFd = inFd;
+ else
+ usedInFd = inFdPair[0];
+
+ kdDebug() << "(K3bPipeBuffer::WorkThread) reading from " << usedInFd
+ << " and writing to " << outFd << endl;
+ kdDebug() << "(K3bPipeBuffer::WorkThread) using buffer size of " << bufSize << endl;
+
+ // start the buffering
+ unsigned int bufPos = 0;
+ unsigned int dataLen = 0;
+ bool eof = false;
+ bool error = false;
+ canceled = false;
+ int oldPercent = 0;
+
+ static const unsigned int MAX_BUFFER_READ = 2048*3;
+
+ while( !canceled && !error && (!eof || dataLen > 0) ) {
+ //
+ // create two fd sets
+ //
+ fd_set readFds, writeFds;
+ FD_ZERO(&readFds);
+ FD_ZERO(&writeFds);
+
+ //
+ // fill the fd sets
+ //
+ if( !eof && dataLen < bufSize )
+ FD_SET(usedInFd, &readFds);
+ if( dataLen > 0 )
+ FD_SET(outFd, &writeFds);
+
+ //
+ // wait for data
+ //
+ int ret = select( QMAX(usedInFd, outFd) + 1, &readFds, &writeFds, NULL, NULL);
+
+ //
+ // Do the buffering
+ //
+ if( !canceled && ret > 0 ) {
+
+ int percent = -1;
+
+ //
+ // Read from the buffer and write to the output
+ //
+ if( FD_ISSET(outFd, &writeFds) ) {
+ unsigned int maxLen = QMIN(bufSize - bufPos, dataLen);
+
+ ret = ::write( outFd, &buffer[bufPos], maxLen );
+
+ if( ret < 0 ) {
+ if( (errno != EINTR) && (errno != EAGAIN) ) {
+ kdDebug() << "(K3bPipeBuffer::WorkThread) error while writing to " << outFd << endl;
+ error = true;
+ }
+ }
+ else {
+ //
+ // we always emit before the reading from the buffer since
+ // it makes way more sense to show the buffer before the reading.
+ //
+ percent = (int)((double)dataLen*100.0/(double)bufSize);
+
+ bufPos = (bufPos + ret) % bufSize;
+ dataLen -= ret;
+ }
+ }
+
+ //
+ // Read into the buffer
+ //
+ else if( FD_ISSET(usedInFd, &readFds) ) {
+ unsigned int readPos = (bufPos + dataLen) % bufSize;
+ unsigned int maxLen = QMIN(bufSize - readPos, bufSize - dataLen);
+ //
+ // never read more than xxx bytes
+ // This is some tuning to prevent the reading from blocking the whole thread
+ //
+ if( maxLen > MAX_BUFFER_READ ) // some dummy value below 1 MB
+ maxLen = MAX_BUFFER_READ;
+ ret = ::read( usedInFd, &buffer[readPos], maxLen );
+ if( ret < 0 ) {
+ if( (errno != EINTR) && (errno != EAGAIN) ) {
+ kdDebug() << "(K3bPipeBuffer::WorkThread) error while reading from " << usedInFd << endl;
+ error = true;
+ }
+ }
+ else if( ret == 0 ) {
+ kdDebug() << "(K3bPipeBuffer::WorkThread) end of input." << endl;
+ eof = true;
+ }
+ else {
+ dataLen += ret;
+
+ percent = (int)((double)dataLen*100.0/(double)bufSize);
+ }
+ }
+
+ // A little hack to keep the buffer display from flickering
+ if( percent == 99 )
+ percent = 100;
+
+ if( percent != -1 && percent != oldPercent ) {
+ emitPercent( percent );
+ oldPercent = percent;
+ }
+ }
+ else if( !canceled ) {
+ error = true;
+ kdDebug() << "(K3bPipeBuffer::WorkThread) select: " << ::strerror(errno) << endl;
+ }
+ }
+
+ if( inFd == -1 ) {
+ ::close( inFdPair[0] );
+ ::close( inFdPair[1] );
+ inFdPair[0] = inFdPair[1] = -1;
+ }
+
+ //
+ // close the fd we are writing to (this is need to make growisofs happy
+ // TODO: perhaps make this configurable
+ //
+ ::close( outFd );
+
+ if( canceled )
+ emitCanceled();
+ emitFinished( !error && !canceled );
+ }
+
+ char* buffer;
+ size_t bufSize;
+ int outFd;
+ int inFd;
+ int inFdPair[2];
+ bool canceled;
+};
+
+
+K3bPipeBuffer::K3bPipeBuffer( K3bJobHandler* jh, QObject* parent, const char* name )
+ : K3bThreadJob( jh, parent, name )
+{
+ m_thread = new WorkThread();
+ setThread( m_thread );
+}
+
+
+K3bPipeBuffer::~K3bPipeBuffer()
+{
+ delete m_thread;
+}
+
+
+void K3bPipeBuffer::start()
+{
+ //
+ // Create the socketpair in the gui thread to be sure it's available after
+ // this method returns.
+ //
+ if( !m_thread->initFds() )
+ jobFinished(false);
+ else
+ K3bThreadJob::start();
+}
+
+
+void K3bPipeBuffer::cancel()
+{
+ m_thread->canceled = true;
+}
+
+
+void K3bPipeBuffer::setBufferSize( int mb )
+{
+ m_thread->bufSize = mb * 1024 * 1024;
+}
+
+
+void K3bPipeBuffer::readFromFd( int fd )
+{
+ m_thread->inFd = fd;
+}
+
+
+void K3bPipeBuffer::writeToFd( int fd )
+{
+ m_thread->outFd = fd;
+}
+
+
+int K3bPipeBuffer::inFd() const
+{
+ if( m_thread->inFd == -1 )
+ return m_thread->inFdPair[1];
+ else
+ return m_thread->inFd;
+}
diff --git a/libk3b/projects/k3bpipebuffer.h b/libk3b/projects/k3bpipebuffer.h
new file mode 100644
index 0000000..6aae368
--- /dev/null
+++ b/libk3b/projects/k3bpipebuffer.h
@@ -0,0 +1,59 @@
+/*
+ *
+ * $Id: k3bpipebuffer.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_PIPE_BUFFER_H_
+#define _K3B_PIPE_BUFFER_H_
+
+
+#include <k3bthreadjob.h>
+
+/**
+ * the pipebuffer uses the signal percent to show it's status.
+ */
+class K3bPipeBuffer : public K3bThreadJob
+{
+ public:
+ K3bPipeBuffer( K3bJobHandler*, QObject* parent = 0, const char* name = 0 );
+ ~K3bPipeBuffer();
+
+ /**
+ * Set the buffer size in MB. The default value is 4 MB.
+ */
+ void setBufferSize( int );
+
+ /**
+ * If this is set to -1 (which is the default) the pipebuffer
+ * will create a fd pair which can be obtained by inFd() after
+ * the buffer has been started.
+ */
+ void readFromFd( int fd );
+ void writeToFd( int fd );
+
+ /**
+ * This is only valid after the piepbuffer has been started and no fd
+ * has been set with readFromFd.
+ */
+ int inFd() const;
+
+ public slots:
+ void start();
+ void cancel();
+
+ private:
+ class WorkThread;
+ WorkThread* m_thread;
+};
+
+#endif
diff --git a/libk3b/projects/k3btocfilewriter.cpp b/libk3b/projects/k3btocfilewriter.cpp
new file mode 100644
index 0000000..77662d6
--- /dev/null
+++ b/libk3b/projects/k3btocfilewriter.cpp
@@ -0,0 +1,356 @@
+/*
+ *
+ * $Id: k3btocfilewriter.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2004 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3btocfilewriter.h"
+
+#include <k3btrack.h>
+#include <k3bmsf.h>
+#include <k3bcore.h>
+#include <k3bversion.h>
+
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qdatetime.h>
+
+
+K3bTocFileWriter::K3bTocFileWriter()
+ : m_hideFirstTrack(false),
+ m_sessionToWrite(1)
+{
+}
+
+
+bool K3bTocFileWriter::save( const QString& filename )
+{
+ QFile f( filename );
+
+ if( !f.open( IO_WriteOnly ) ) {
+ kdDebug() << "(K3bCueFileWriter) could not open file " << f.name() << endl;
+ return false;
+ }
+
+ QTextStream s( &f );
+
+ return save( s );
+}
+
+
+bool K3bTocFileWriter::save( QTextStream& t )
+{
+ writeHeader(t);
+
+ if( !m_cdText.isEmpty() )
+ writeGlobalCdText(t);
+
+ //
+ // see if we have multiple sessions
+ //
+ int sessions = 1;
+ for( K3bDevice::Toc::iterator it = m_toc.begin(); it != m_toc.end(); ++it ) {
+ if( (*it).session() > 1 )
+ sessions = (*it).session();
+ }
+
+ if( m_sessionToWrite > sessions )
+ m_sessionToWrite = 1;
+
+ //
+ // We can only hide the first track if both the first and the second track are
+ // audio tracks.
+ // We also can only hide the first track in the first session.
+ //
+ bool hideFirstTrack = m_hideFirstTrack;
+ if( m_toc.count() < 2 ||
+ m_toc[0].type() != K3bDevice::Track::AUDIO ||
+ m_toc[1].type() != K3bDevice::Track::AUDIO ||
+ (sessions > 1 && m_sessionToWrite != 1 ) )
+ hideFirstTrack = false;
+
+
+ // the dataStart will be the offset in case we do not write the first session
+ K3b::Msf dataStart;
+
+ unsigned int trackIndex = 0;
+ if( hideFirstTrack ) {
+ const K3bDevice::Track& hiddenTrack = m_toc[0];
+ const K3bDevice::Track& track = m_toc[1];
+
+ t << "// Track number 1 (hidden) and track number 2 (as track 1)" << endl;
+ t << "TRACK AUDIO" << endl;
+
+ if( track.copyPermitted() )
+ t << "COPY" << endl;
+ else
+ t << "NO COPY" << endl;
+
+ if( track.preEmphasis() )
+ t << "PRE_EMPHASIS" << endl;
+ else
+ t << "NO PRE_EMPHASIS" << endl;
+
+ if( !m_cdText.isEmpty() )
+ writeTrackCdText( m_cdText[0], t );
+
+ // the "hidden" file will be used as pregap for the "first" track
+ t << "AUDIOFILE ";
+ writeDataSource( 0, t );
+ if( readFromStdin() )
+ t << hiddenTrack.firstSector().toString();
+ else
+ t << " 0";
+ t << " " << hiddenTrack.length().toString() << endl;
+ t << "START" << endl; // use the whole hidden file as pregap
+
+ // now comes the "real" first track
+ t << "AUDIOFILE ";
+ writeDataSource( 1, t );
+ if( readFromStdin() )
+ t << track.firstSector().toString() << " ";
+ else
+ t << "0 ";
+ // no index 0 for the last track. Or should we allow this???
+ if( m_toc.count() == 2 )
+ t << track.length().toString();
+ else
+ t << track.realAudioLength().toString();
+ t << endl << endl;
+
+ trackIndex+=2;
+ }
+ else {
+ //
+ // Seek to the first track to write.
+ // In case we hid the first track above it was the first track anyway.
+ //
+ while( m_toc[trackIndex].session() < m_sessionToWrite &&
+ m_toc[trackIndex].session() > 0 )
+ ++trackIndex;
+
+ dataStart = m_toc[trackIndex].firstSector();
+ }
+
+ kdDebug() << "(K3bTocFileWriter) using offset of: " << dataStart.toString() << endl;
+
+ while( trackIndex < m_toc.count() ) {
+ if( m_toc[trackIndex].session() == 0 || m_toc[trackIndex].session() == m_sessionToWrite )
+ writeTrack( trackIndex, dataStart, t );
+ trackIndex++;
+ }
+
+ return ( t.device()->status() == IO_Ok );
+}
+
+
+void K3bTocFileWriter::writeHeader( QTextStream& t )
+{
+ // little comment
+ t << "// TOC-file to use with cdrdao created by K3b " << k3bcore->version()
+ << ", " << QDateTime::currentDateTime().toString() << endl << endl;
+
+ t << "// " << m_toc.count() << " tracks" << endl;
+ if( m_toc.back().session() > 0 ) {
+ t << "// " << m_toc.back().session() << " sessions" << endl
+ << "// this is session number " << m_sessionToWrite << endl;
+ }
+ t << endl;
+
+ // check the cd type
+ if( m_toc.contentType() == K3bDevice::AUDIO ) {
+ t << "CD_DA";
+ }
+ else {
+ bool hasMode2Tracks = false;
+ for( K3bDevice::Toc::iterator it = m_toc.begin(); it != m_toc.end(); ++it ) {
+ const K3bDevice::Track& track = *it;
+ if( track.type() == K3bDevice::Track::DATA &&
+ (track.mode() == K3bDevice::Track::MODE2 ||
+ track.mode() == K3bDevice::Track::XA_FORM1 ||
+ track.mode() == K3bDevice::Track::XA_FORM2 ) ) {
+ hasMode2Tracks = true;
+ break;
+ }
+ }
+
+ if( hasMode2Tracks )
+ t << "CD_ROM_XA";
+ else
+ t << "CD_ROM";
+ }
+
+ t << endl << endl;
+}
+
+
+void K3bTocFileWriter::writeTrack( unsigned int index, const K3b::Msf& offset, QTextStream& t )
+{
+ const K3bDevice::Track& track = m_toc[index];
+
+ t << "// Track number " << (index+1) << endl;
+
+ if( track.type() == K3bDevice::Track::AUDIO ) {
+ t << "TRACK AUDIO" << endl;
+
+ if( track.copyPermitted() )
+ t << "COPY" << endl;
+ else
+ t << "NO COPY" << endl;
+
+ if( track.preEmphasis() )
+ t << "PRE_EMPHASIS" << endl;
+ else
+ t << "NO PRE_EMPHASIS" << endl;
+
+ if( !m_cdText.isEmpty() )
+ writeTrackCdText( m_cdText[index], t );
+
+ //
+ // cdrdao sees the pregap as part of the current track and not as part of
+ // the previous like it really is.
+ //
+
+ if( index == 0 ) {
+ if( (track.firstSector()-offset) > 0 ) {
+ //
+ // the first track is the only track K3b does not generate null-pregap data for
+ // since cdrecord does not allow this. So We just do it here the same way and tell
+ // cdrdao to create the first pregap for us
+ //
+
+ t << "PREGAP "
+ << (track.firstSector()-offset).toString() << endl;
+ }
+ }
+ else {
+ const K3bDevice::Track& lastTrack = m_toc[index-1];
+
+ //
+ // the pregap data
+ //
+ if( lastTrack.index0() > 0 ) {
+ t << "AUDIOFILE ";
+ writeDataSource( index-1, t );
+ if( readFromStdin() )
+ t << (lastTrack.firstSector() + lastTrack.index0() - offset).toString();
+ else
+ t << (lastTrack.index0() - offset).toString();
+ t << " "
+ << (lastTrack.length() - lastTrack.index0()).toString()
+ << endl
+ << "START" << endl;
+ }
+ }
+
+ //
+ // The track data
+ //
+ t << "AUDIOFILE ";
+ writeDataSource( index, t );
+ if( readFromStdin() )
+ t << (track.firstSector() - offset).toString() << " ";
+ else
+ t << "0 ";
+ // no index 0 for the last track. Or should we allow this???
+ if( index == m_toc.count()-1 )
+ t << track.length().toString();
+ else
+ t << track.realAudioLength().toString();
+ t << endl;
+ }
+ else {
+ if( track.mode() == K3bDevice::Track::XA_FORM1 )
+ t << "TRACK MODE2_FORM1" << endl;
+ else if( track.mode() == K3bDevice::Track::XA_FORM2 )
+ t << "TRACK MODE2_FORM2" << endl;
+ else
+ t << "TRACK MODE1" << endl;
+
+ if( !m_cdText.isEmpty() && !m_toc.contentType() != K3bDevice::DATA ) {
+ //
+ // insert fake cdtext
+ // cdrdao does not work without it and it seems not to do any harm.
+ //
+ t << "CD_TEXT {" << endl
+ << " LANGUAGE 0 {" << endl
+ << " TITLE " << "\"\"" << endl
+ << " PERFORMER " << "\"\"" << endl
+ << " ISRC " << "\"\"" << endl
+ << " ARRANGER " << "\"\"" << endl
+ << " SONGWRITER " << "\"\"" << endl
+ << " COMPOSER " << "\"\"" << endl
+ << " MESSAGE " << "\"\"" << endl
+ << " }" << endl
+ << "}" << endl;
+ }
+
+ if( readFromStdin() )
+ t << "DATAFILE \"-\" " << track.length().toString() << endl;
+ else
+ t << "DATAFILE \"" << m_filenames[index] << "\"" << endl;
+ t << endl;
+ }
+
+ t << endl;
+}
+
+
+void K3bTocFileWriter::writeGlobalCdText( QTextStream& t )
+{
+ t << "CD_TEXT {" << endl;
+ t << " LANGUAGE_MAP { 0: EN }" << endl;
+ t << " LANGUAGE 0 {" << endl;
+ t << " TITLE " << "\"" << m_cdText.title() << "\"" << endl;
+ t << " PERFORMER " << "\"" << m_cdText.performer() << "\"" << endl;
+ t << " DISC_ID " << "\"" << m_cdText.discId() << "\"" << endl;
+ t << " UPC_EAN " << "\"" << m_cdText.upcEan() << "\"" << endl;
+ t << endl;
+ t << " ARRANGER " << "\"" << m_cdText.arranger() << "\"" << endl;
+ t << " SONGWRITER " << "\"" << m_cdText.songwriter() << "\"" << endl;
+ t << " COMPOSER " << "\"" << m_cdText.composer() << "\"" << endl;
+ t << " MESSAGE " << "\"" << m_cdText.message() << "\"" << endl;
+ t << " }" << endl;
+ t << "}" << endl;
+ t << endl;
+}
+
+
+void K3bTocFileWriter::writeTrackCdText( const K3bDevice::TrackCdText& track, QTextStream& t )
+{
+ t << "CD_TEXT {" << endl;
+ t << " LANGUAGE 0 {" << endl;
+ t << " TITLE " << "\"" << track.title() << "\"" << endl;
+ t << " PERFORMER " << "\"" << track.performer() << "\"" << endl;
+ t << " ISRC " << "\"" << track.isrc() << "\"" << endl;
+ t << " ARRANGER " << "\"" << track.arranger() << "\"" << endl;
+ t << " SONGWRITER " << "\"" << track.songwriter() << "\"" << endl;
+ t << " COMPOSER " << "\"" << track.composer() << "\"" << endl;
+ t << " MESSAGE " << "\"" << track.message() << "\"" << endl;
+ t << " }" << endl;
+ t << "}" << endl;
+}
+
+
+void K3bTocFileWriter::writeDataSource( unsigned int trackIndex, QTextStream& t )
+{
+ if( readFromStdin() )
+ t << "\"-\" ";
+ else
+ t << "\"" << m_filenames[trackIndex] << "\" ";
+}
+
+
+bool K3bTocFileWriter::readFromStdin() const
+{
+ return ( m_toc.count() > m_filenames.count() );
+}
diff --git a/libk3b/projects/k3btocfilewriter.h b/libk3b/projects/k3btocfilewriter.h
new file mode 100644
index 0000000..1c1da47
--- /dev/null
+++ b/libk3b/projects/k3btocfilewriter.h
@@ -0,0 +1,62 @@
+/*
+ *
+ * $Id: k3btocfilewriter.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2004 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_TOC_FILE_WRITER_H_
+#define _K3B_TOC_FILE_WRITER_H_
+
+#include <qtextstream.h>
+#include <qstringlist.h>
+
+#include <k3btoc.h>
+#include <k3bcdtext.h>
+
+namespace K3bDevice {
+ class TrackCdText;
+}
+
+class K3bTocFileWriter
+{
+ public:
+ K3bTocFileWriter();
+
+ bool save( QTextStream& );
+ bool save( const QString& filename );
+
+ void setData( const K3bDevice::Toc& toc ) { m_toc = toc; }
+ void setCdText( const K3bDevice::CdText& text ) { m_cdText = text; }
+ void setFilenames( const QStringList& names ) { m_filenames = names; }
+ void setHideFirstTrack( bool b ) { m_hideFirstTrack = b; }
+
+ /**
+ * The default is 1.
+ */
+ void setSession( int s ) { m_sessionToWrite = s; }
+
+ private:
+ void writeHeader( QTextStream& t );
+ void writeGlobalCdText( QTextStream& t );
+ void writeTrackCdText( const K3bDevice::TrackCdText& track, QTextStream& t );
+ void writeTrack( unsigned int index, const K3b::Msf& offset, QTextStream& t );
+ void writeDataSource( unsigned int trackNumber, QTextStream& t );
+ bool readFromStdin() const;
+
+ K3bDevice::Toc m_toc;
+ K3bDevice::CdText m_cdText;
+ QStringList m_filenames;
+ bool m_hideFirstTrack;
+ int m_sessionToWrite;
+};
+
+#endif
diff --git a/libk3b/projects/mixedcd/Makefile.am b/libk3b/projects/mixedcd/Makefile.am
new file mode 100644
index 0000000..fb5f44c
--- /dev/null
+++ b/libk3b/projects/mixedcd/Makefile.am
@@ -0,0 +1,23 @@
+# we need the ../datacd and ../audiocd for the uic generated header files
+AM_CPPFLAGS= -I$(srcdir)/../../core \
+ -I$(srcdir)/../../plugin \
+ -I$(srcdir)/../../../libk3bdevice \
+ -I$(srcdir)/../../../src \
+ -I$(srcdir)/../../tools \
+ -I$(srcdir)/.. \
+ -I$(srcdir)/../datacd \
+ -I$(srcdir)/../audiocd \
+ -I$(srcdir)/../../plugin \
+ -I../datacd \
+ -I../audiocd \
+ $(all_includes)
+
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libmixed.la
+
+libmixed_la_SOURCES = k3bmixeddoc.cpp \
+ k3bmixedjob.cpp
+
+include_HEADERS = k3bmixeddoc.h \
+ k3bmixedjob.h
diff --git a/libk3b/projects/mixedcd/k3bmixeddoc.cpp b/libk3b/projects/mixedcd/k3bmixeddoc.cpp
new file mode 100644
index 0000000..a2c76b0
--- /dev/null
+++ b/libk3b/projects/mixedcd/k3bmixeddoc.cpp
@@ -0,0 +1,249 @@
+/*
+ *
+ * $Id: k3bmixeddoc.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bmixeddoc.h"
+#include "k3bmixedjob.h"
+
+#include <k3bdatadoc.h>
+#include <k3baudiodoc.h>
+#include <k3bglobals.h>
+#include <k3bmsf.h>
+
+#include <qfileinfo.h>
+#include <qdom.h>
+
+#include <klocale.h>
+#include <kconfig.h>
+#include <kapplication.h>
+#include <kmessagebox.h>
+
+
+
+K3bMixedDoc::K3bMixedDoc( QObject* parent )
+ : K3bDoc( parent )
+{
+ m_dataDoc = new K3bDataDoc( this );
+ m_audioDoc = new K3bAudioDoc( this );
+
+ connect( m_dataDoc, SIGNAL(changed()),
+ this, SIGNAL(changed()) );
+ connect( m_audioDoc, SIGNAL(changed()),
+ this, SIGNAL(changed()) );
+}
+
+
+K3bMixedDoc::~K3bMixedDoc()
+{
+}
+
+
+bool K3bMixedDoc::newDocument()
+{
+ m_dataDoc->newDocument();
+ m_audioDoc->newDocument();
+
+ return K3bDoc::newDocument();
+}
+
+
+QString K3bMixedDoc::name() const
+{
+ return m_dataDoc->name();
+}
+
+
+void K3bMixedDoc::setURL( const KURL& url )
+{
+ K3bDoc::setURL( url );
+ m_audioDoc->setURL( url );
+ m_dataDoc->setURL( url );
+}
+
+
+void K3bMixedDoc::setModified( bool m )
+{
+ m_audioDoc->setModified( m );
+ m_dataDoc->setModified( m );
+}
+
+
+bool K3bMixedDoc::isModified() const
+{
+ return ( m_audioDoc->isModified() || m_dataDoc->isModified() );
+}
+
+
+KIO::filesize_t K3bMixedDoc::size() const
+{
+ return m_dataDoc->size() + m_audioDoc->size();
+}
+
+K3b::Msf K3bMixedDoc::length() const
+{
+ return m_dataDoc->length() + m_audioDoc->length();
+}
+
+
+int K3bMixedDoc::numOfTracks() const
+{
+ return m_audioDoc->numOfTracks() + 1;
+}
+
+
+K3bBurnJob* K3bMixedDoc::newBurnJob( K3bJobHandler* hdl, QObject* parent )
+{
+ return new K3bMixedJob( this, hdl, parent );
+}
+
+
+void K3bMixedDoc::addUrls( const KURL::List& urls )
+{
+ dataDoc()->addUrls( urls );
+}
+
+
+bool K3bMixedDoc::loadDocumentData( QDomElement* rootElem )
+{
+ QDomNodeList nodes = rootElem->childNodes();
+
+ if( nodes.length() < 4 )
+ return false;
+
+ if( nodes.item(0).nodeName() != "general" )
+ return false;
+ if( !readGeneralDocumentData( nodes.item(0).toElement() ) )
+ return false;
+
+ if( nodes.item(1).nodeName() != "audio" )
+ return false;
+ QDomElement audioElem = nodes.item(1).toElement();
+ if( !m_audioDoc->loadDocumentData( &audioElem ) )
+ return false;
+
+ if( nodes.item(2).nodeName() != "data" )
+ return false;
+ QDomElement dataElem = nodes.item(2).toElement();
+ if( !m_dataDoc->loadDocumentData( &dataElem ) )
+ return false;
+
+ if( nodes.item(3).nodeName() != "mixed" )
+ return false;
+
+ QDomNodeList optionList = nodes.item(3).childNodes();
+ for( uint i = 0; i < optionList.count(); i++ ) {
+
+ QDomElement e = optionList.item(i).toElement();
+ if( e.isNull() )
+ return false;
+
+ if( e.nodeName() == "remove_buffer_files" )
+ setRemoveImages( e.toElement().text() == "yes" );
+ else if( e.nodeName() == "image_path" )
+ setTempDir( e.toElement().text() );
+ else if( e.nodeName() == "mixed_type" ) {
+ QString mt = e.toElement().text();
+ if( mt == "last_track" )
+ setMixedType( DATA_LAST_TRACK );
+ else if( mt == "second_session" )
+ setMixedType( DATA_SECOND_SESSION );
+ else
+ setMixedType( DATA_FIRST_TRACK );
+ }
+ }
+
+ return true;
+}
+
+
+bool K3bMixedDoc::saveDocumentData( QDomElement* docElem )
+{
+ QDomDocument doc = docElem->ownerDocument();
+ saveGeneralDocumentData( docElem );
+
+ QDomElement audioElem = doc.createElement( "audio" );
+ m_audioDoc->saveDocumentData( &audioElem );
+ docElem->appendChild( audioElem );
+
+ QDomElement dataElem = doc.createElement( "data" );
+ m_dataDoc->saveDocumentData( &dataElem );
+ docElem->appendChild( dataElem );
+
+ QDomElement mixedElem = doc.createElement( "mixed" );
+ docElem->appendChild( mixedElem );
+
+ QDomElement bufferFilesElem = doc.createElement( "remove_buffer_files" );
+ bufferFilesElem.appendChild( doc.createTextNode( removeImages() ? "yes" : "no" ) );
+ mixedElem.appendChild( bufferFilesElem );
+
+ QDomElement imagePathElem = doc.createElement( "image_path" );
+ imagePathElem.appendChild( doc.createTextNode( tempDir() ) );
+ mixedElem.appendChild( imagePathElem );
+
+ QDomElement mixedTypeElem = doc.createElement( "mixed_type" );
+ switch( mixedType() ) {
+ case DATA_FIRST_TRACK:
+ mixedTypeElem.appendChild( doc.createTextNode( "first_track" ) );
+ break;
+ case DATA_LAST_TRACK:
+ mixedTypeElem.appendChild( doc.createTextNode( "last_track" ) );
+ break;
+ case DATA_SECOND_SESSION:
+ mixedTypeElem.appendChild( doc.createTextNode( "second_session" ) );
+ break;
+ }
+ mixedElem.appendChild( mixedTypeElem );
+
+ setModified( false );
+
+ return true;
+}
+
+
+K3bDevice::Toc K3bMixedDoc::toToc( int dataMode, const K3b::Msf& dataTrackLength ) const
+{
+ // !inaccurate datatrack size!
+ K3bDevice::Track dataTrack( 0, dataTrackLength > 0 ? dataTrackLength-1 : m_dataDoc->length()-1,
+ K3bDevice::Track::DATA, dataMode );
+ K3bDevice::Toc toc = audioDoc()->toToc();
+ if( mixedType() == DATA_FIRST_TRACK ) {
+ // fix the audio tracks' sectors
+ for( K3bDevice::Toc::iterator it = toc.begin(); it != toc.end(); ++it ) {
+ (*it).setLastSector( (*it).lastSector() + dataTrack.length() );
+ (*it).setFirstSector( (*it).firstSector() + dataTrack.length() );
+ }
+ toc.insert( toc.begin(), dataTrack );
+ }
+ else {
+ // fix the datatrack's sectors
+ dataTrack.setLastSector( dataTrack.lastSector() + toc.back().lastSector()+1 );
+ dataTrack.setFirstSector( toc.back().lastSector()+1 );
+ toc.append( dataTrack );
+
+ if( mixedType() == DATA_SECOND_SESSION ) {
+ // fix the session numbers
+ for( K3bDevice::Toc::iterator it = toc.begin(); it != toc.end(); ++it ) {
+ if( (*it).type() == K3bDevice::Track::DATA )
+ (*it).setSession( 2 );
+ else
+ (*it).setSession( 1 );
+ }
+ }
+ }
+
+ return toc;
+}
+
+#include "k3bmixeddoc.moc"
+
diff --git a/libk3b/projects/mixedcd/k3bmixeddoc.h b/libk3b/projects/mixedcd/k3bmixeddoc.h
new file mode 100644
index 0000000..7d71b39
--- /dev/null
+++ b/libk3b/projects/mixedcd/k3bmixeddoc.h
@@ -0,0 +1,95 @@
+/*
+ *
+ * $Id: k3bmixeddoc.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef K3B_MIXED_DOC_H
+#define K3B_MIXED_DOC_H
+
+#include <k3bdoc.h>
+#include <k3bdatadoc.h>
+#include <k3baudiodoc.h>
+
+#include <k3btoc.h>
+#include "k3b_export.h"
+class QDomDocument;
+class QDomElement;
+class K3bBurnJob;
+//class K3bView;
+class QWidget;
+class KConfig;
+
+
+class LIBK3B_EXPORT K3bMixedDoc : public K3bDoc
+{
+ Q_OBJECT
+
+ public:
+ K3bMixedDoc( QObject* parent = 0 );
+ ~K3bMixedDoc();
+
+ QString name() const;
+
+ bool newDocument();
+
+ void setModified( bool m = true );
+ bool isModified() const;
+
+ KIO::filesize_t size() const;
+ K3b::Msf length() const;
+
+ int numOfTracks() const;
+
+ K3bBurnJob* newBurnJob( K3bJobHandler*, QObject* parent = 0 );
+
+ K3bAudioDoc* audioDoc() const { return m_audioDoc; }
+ K3bDataDoc* dataDoc() const { return m_dataDoc; }
+
+ enum MixedType { DATA_FIRST_TRACK,
+ DATA_LAST_TRACK,
+ DATA_SECOND_SESSION };
+
+ int mixedType() const { return m_mixedType; }
+ int type() const { return MIXED; }
+
+ void setURL( const KURL& url );
+
+ /**
+ * Represent the structure of the doc as CD Table of Contents.
+ * Be aware that the length of the data track is just an estimate
+ * and needs to be corrected if not specified here.
+ *
+ * @param dataMode mode of the data track (MODE1 or XA_FORM1)
+ * @param dataTrackLength exact length of the dataTrack
+ */
+
+ K3bDevice::Toc toToc( int dataMode, const K3b::Msf& dataTrackLength = 0 ) const;
+
+ public slots:
+ void setMixedType( MixedType t ) { m_mixedType = t; }
+ void addUrls( const KURL::List& urls );
+
+ protected:
+ bool loadDocumentData( QDomElement* );
+ bool saveDocumentData( QDomElement* );
+ QString typeString() const { return "mixed"; }
+
+ private:
+ K3bDataDoc* m_dataDoc;
+ K3bAudioDoc* m_audioDoc;
+
+ int m_mixedType;
+};
+
+
+#endif
diff --git a/libk3b/projects/mixedcd/k3bmixedjob.cpp b/libk3b/projects/mixedcd/k3bmixedjob.cpp
new file mode 100644
index 0000000..a4be92c
--- /dev/null
+++ b/libk3b/projects/mixedcd/k3bmixedjob.cpp
@@ -0,0 +1,1339 @@
+/*
+ *
+ * $Id: k3bmixedjob.cpp 690212 2007-07-20 11:02:13Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+
+
+#include "k3bmixedjob.h"
+#include "k3bmixeddoc.h"
+
+#include <k3bdatadoc.h>
+#include <k3bisoimager.h>
+#include <k3bmsinfofetcher.h>
+#include <k3baudioimager.h>
+#include <k3baudiodoc.h>
+#include <k3baudiotrack.h>
+#include <k3baudionormalizejob.h>
+#include <k3baudiojobtempdata.h>
+#include <k3baudiomaxspeedjob.h>
+#include <k3bdevicemanager.h>
+#include <k3bdevice.h>
+#include <k3bdevicehandler.h>
+#include <k3bmsf.h>
+#include <k3bglobals.h>
+#include <k3bexternalbinmanager.h>
+#include <k3bversion.h>
+#include <k3bcore.h>
+#include <k3bcdrecordwriter.h>
+#include <k3bcdrdaowriter.h>
+#include <k3btocfilewriter.h>
+#include <k3binffilewriter.h>
+#include <k3bglobalsettings.h>
+#include <k3baudiofile.h>
+
+#include <qfile.h>
+#include <qdatastream.h>
+#include <qapplication.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <ktempfile.h>
+#include <kio/netaccess.h>
+#include <kio/global.h>
+#include <kstringhandler.h>
+
+
+static QString createNonExistingFilesString( const QValueList<K3bAudioFile*>& items, unsigned int max )
+{
+ QString s;
+ unsigned int cnt = 0;
+ for( QValueList<K3bAudioFile*>::const_iterator it = items.begin();
+ it != items.end(); ++it ) {
+
+ s += KStringHandler::csqueeze( (*it)->filename(), 60 );
+
+ ++cnt;
+ if( cnt >= max || it == items.end() )
+ break;
+
+ s += "<br>";
+ }
+
+ if( items.count() > max )
+ s += "...";
+
+ return s;
+}
+
+
+
+class K3bMixedJob::Private
+{
+public:
+ Private()
+ : maxSpeedJob(0) {
+ }
+
+
+ int copies;
+ int copiesDone;
+
+ K3bAudioMaxSpeedJob* maxSpeedJob;
+ bool maxSpeed;
+};
+
+
+K3bMixedJob::K3bMixedJob( K3bMixedDoc* doc, K3bJobHandler* hdl, QObject* parent )
+ : K3bBurnJob( hdl, parent ),
+ m_doc( doc ),
+ m_normalizeJob(0)
+{
+ d = new Private;
+
+ m_isoImager = new K3bIsoImager( doc->dataDoc(), this, this );
+ connect( m_isoImager, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( m_isoImager, SIGNAL(percent(int)), this, SLOT(slotIsoImagerPercent(int)) );
+ connect( m_isoImager, SIGNAL(finished(bool)), this, SLOT(slotIsoImagerFinished(bool)) );
+ connect( m_isoImager, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+
+ m_audioImager = new K3bAudioImager( doc->audioDoc(), this, this );
+ connect( m_audioImager, SIGNAL(infoMessage(const QString&, int)),
+ this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( m_audioImager, SIGNAL(percent(int)), this, SLOT(slotAudioDecoderPercent(int)) );
+ connect( m_audioImager, SIGNAL(subPercent(int)), this, SLOT(slotAudioDecoderSubPercent(int)) );
+ connect( m_audioImager, SIGNAL(finished(bool)), this, SLOT(slotAudioDecoderFinished(bool)) );
+ connect( m_audioImager, SIGNAL(nextTrack(int, int)), this, SLOT(slotAudioDecoderNextTrack(int, int)) );
+
+ m_msInfoFetcher = new K3bMsInfoFetcher( this, this );
+ connect( m_msInfoFetcher, SIGNAL(finished(bool)), this, SLOT(slotMsInfoFetched(bool)) );
+ connect( m_msInfoFetcher, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) );
+
+ m_writer = 0;
+ m_tocFile = 0;
+ m_tempData = new K3bAudioJobTempData( m_doc->audioDoc(), this );
+}
+
+
+K3bMixedJob::~K3bMixedJob()
+{
+ delete m_tocFile;
+ delete d;
+}
+
+
+K3bDevice::Device* K3bMixedJob::writer() const
+{
+ if( m_doc->onlyCreateImages() )
+ return 0;
+ else
+ return m_doc->burner();
+}
+
+
+K3bDoc* K3bMixedJob::doc() const
+{
+ return m_doc;
+}
+
+
+void K3bMixedJob::start()
+{
+ jobStarted();
+
+ m_canceled = false;
+ m_errorOccuredAndAlreadyReported = false;
+ d->copiesDone = 0;
+ d->copies = m_doc->copies();
+ m_currentAction = PREPARING_DATA;
+ d->maxSpeed = false;
+
+ if( m_doc->dummy() )
+ d->copies = 1;
+
+ prepareProgressInformation();
+
+ //
+ // Check if all files exist
+ //
+ QValueList<K3bAudioFile*> nonExistingFiles;
+ K3bAudioTrack* track = m_doc->audioDoc()->firstTrack();
+ while( track ) {
+ K3bAudioDataSource* source = track->firstSource();
+ while( source ) {
+ if( K3bAudioFile* file = dynamic_cast<K3bAudioFile*>( source ) ) {
+ if( !QFile::exists( file->filename() ) )
+ nonExistingFiles.append( file );
+ }
+ source = source->next();
+ }
+ track = track->next();
+ }
+ if( !nonExistingFiles.isEmpty() ) {
+ if( questionYesNo( "<p>" + i18n("The following files could not be found. Do you want to remove them from the "
+ "project and continue without adding them to the image?") +
+ "<p>" + createNonExistingFilesString( nonExistingFiles, 10 ),
+ i18n("Warning"),
+ i18n("Remove missing files and continue"),
+ i18n("Cancel and go back") ) ) {
+ for( QValueList<K3bAudioFile*>::const_iterator it = nonExistingFiles.begin();
+ it != nonExistingFiles.end(); ++it ) {
+ delete *it;
+ }
+ }
+ else {
+ m_canceled = true;
+ emit canceled();
+ jobFinished(false);
+ return;
+ }
+ }
+
+ //
+ // Make sure the project is not empty
+ //
+ if( m_doc->audioDoc()->numOfTracks() == 0 ) {
+ emit infoMessage( i18n("Please add files to your project first."), ERROR );
+ jobFinished(false);
+ return;
+ }
+
+
+ // set some flags that are needed
+ m_doc->audioDoc()->setOnTheFly( m_doc->onTheFly() ); // for the toc writer
+ m_doc->audioDoc()->setHideFirstTrack( false ); // unsupported
+ m_doc->dataDoc()->setBurner( m_doc->burner() ); // so the isoImager can read ms data
+
+ emit newTask( i18n("Preparing data") );
+
+ determineWritingMode();
+
+ //
+ // First we make sure the data portion is valid
+ //
+
+ // we do not have msinfo yet
+ m_currentAction = INITIALIZING_IMAGER;
+ m_isoImager->setMultiSessionInfo( QString::null );
+ m_isoImager->init();
+}
+
+
+void K3bMixedJob::startFirstCopy()
+{
+ //
+ // if not onthefly create the iso image and then the wavs
+ // and write then
+ // if onthefly calculate the iso size
+ //
+ if( m_doc->onTheFly() ) {
+ if( m_doc->speed() == 0 ) {
+ emit newSubTask( i18n("Determining maximum writing speed") );
+
+ //
+ // try to determine the max possible speed
+ // no need to check the data track's max speed. Most current systems are able
+ // to handle the maxium possible
+ //
+ if( !d->maxSpeedJob ) {
+ // the maxspeed job gets the device from the doc:
+ m_doc->audioDoc()->setBurner( m_doc->burner() );
+ d->maxSpeedJob = new K3bAudioMaxSpeedJob( m_doc->audioDoc(), this, this );
+ connect( d->maxSpeedJob, SIGNAL(percent(int)),
+ this, SIGNAL(subPercent(int)) );
+ connect( d->maxSpeedJob, SIGNAL(finished(bool)),
+ this, SLOT(slotMaxSpeedJobFinished(bool)) );
+ }
+ d->maxSpeedJob->start();
+ }
+ else if( m_doc->mixedType() != K3bMixedDoc::DATA_SECOND_SESSION ) {
+ m_currentAction = PREPARING_DATA;
+ m_isoImager->calculateSize();
+ }
+ else {
+ // we cannot calculate the size since we don't have the msinfo yet
+ // so first write the audio session
+ writeNextCopy();
+ }
+ }
+ else {
+ emit burning(false);
+
+ emit infoMessage( i18n("Creating audio image files in %1").arg(m_doc->tempDir()), INFO );
+
+ m_tempFilePrefix = K3b::findUniqueFilePrefix( ( !m_doc->audioDoc()->title().isEmpty()
+ ? m_doc->audioDoc()->title()
+ : m_doc->dataDoc()->isoOptions().volumeID() ),
+ m_doc->tempDir() );
+
+ m_tempData->prepareTempFileNames( m_doc->tempDir() );
+ QStringList filenames;
+ for( K3bAudioTrack* track = m_doc->audioDoc()->firstTrack(); track; track = track->next() )
+ filenames += m_tempData->bufferFileName( track );
+ m_audioImager->setImageFilenames( filenames );
+
+ if( m_doc->mixedType() != K3bMixedDoc::DATA_SECOND_SESSION ) {
+ createIsoImage();
+ }
+ else {
+ emit newTask( i18n("Creating audio image files") );
+ m_currentAction = CREATING_AUDIO_IMAGE;
+ m_audioImager->start();
+ }
+ }
+}
+
+
+void K3bMixedJob::slotMaxSpeedJobFinished( bool success )
+{
+ d->maxSpeed = success;
+ if( !success )
+ emit infoMessage( i18n("Unable to determine maximum speed for some reason. Ignoring."), WARNING );
+
+ if( m_doc->mixedType() != K3bMixedDoc::DATA_SECOND_SESSION ) {
+ m_currentAction = PREPARING_DATA;
+ m_isoImager->calculateSize();
+ }
+ else {
+ // we cannot calculate the size since we don't have the msinfo yet
+ // so first write the audio session
+ writeNextCopy();
+ }
+}
+
+
+void K3bMixedJob::writeNextCopy()
+{
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) {
+ m_currentAction = WRITING_AUDIO_IMAGE;
+ if( !prepareWriter() || !startWriting() ) {
+ cleanupAfterError();
+ jobFinished(false);
+ }
+ else if( m_doc->onTheFly() )
+ m_audioImager->start();
+ }
+ else {
+ // the prepareWriter method needs the action to be set
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_LAST_TRACK )
+ m_currentAction = WRITING_AUDIO_IMAGE;
+ else
+ m_currentAction = WRITING_ISO_IMAGE;
+
+ if( !prepareWriter() || !startWriting() ) {
+ cleanupAfterError();
+ jobFinished(false);
+ }
+ else if( m_doc->onTheFly() ) {
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_LAST_TRACK )
+ m_audioImager->start();
+ else
+ m_isoImager->start();
+ }
+ }
+}
+
+
+void K3bMixedJob::cancel()
+{
+ m_canceled = true;
+
+ if( d->maxSpeedJob )
+ d->maxSpeedJob->cancel();
+
+ if( m_writer )
+ m_writer->cancel();
+ m_isoImager->cancel();
+ m_audioImager->cancel();
+ m_msInfoFetcher->cancel();
+ emit infoMessage( i18n("Writing canceled."), K3bJob::ERROR );
+ removeBufferFiles();
+ emit canceled();
+ jobFinished(false);
+}
+
+
+void K3bMixedJob::slotMsInfoFetched( bool success )
+{
+ if( m_canceled || m_errorOccuredAndAlreadyReported )
+ return;
+
+ if( success ) {
+ if( m_usedDataWritingApp == K3b::CDRECORD )
+ m_isoImager->setMultiSessionInfo( m_msInfoFetcher->msInfo() );
+ else // cdrdao seems to write a 150 blocks pregap that is not used by cdrecord
+ m_isoImager->setMultiSessionInfo( QString("%1,%2")
+ .arg(m_msInfoFetcher->lastSessionStart())
+ .arg(m_msInfoFetcher->nextSessionStart()+150) );
+
+ if( m_doc->onTheFly() ) {
+ m_currentAction = PREPARING_DATA;
+ m_isoImager->calculateSize();
+ }
+ else {
+ createIsoImage();
+ }
+ }
+ else {
+ // the MsInfoFetcher already emitted failure info
+ cleanupAfterError();
+ jobFinished(false);
+ }
+}
+
+
+void K3bMixedJob::slotIsoImagerFinished( bool success )
+{
+ if( m_canceled || m_errorOccuredAndAlreadyReported )
+ return;
+
+ //
+ // Initializing imager before the first copy
+ //
+ if( m_currentAction == INITIALIZING_IMAGER ) {
+ if( success ) {
+ m_currentAction = PREPARING_DATA;
+
+ // check the size
+ m_projectSize = m_isoImager->size() + m_doc->audioDoc()->length();
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION )
+ m_projectSize += 11400; // the session gap
+
+ startFirstCopy();
+ }
+ else {
+ cleanupAfterError();
+ jobFinished( false );
+ }
+ }
+
+ //
+ // Recalculated iso image size
+ //
+ else if( m_currentAction == PREPARING_DATA ) {
+ if( success ) {
+ // 1. data in first track:
+ // start isoimager and writer
+ // when isoimager finishes start audiodecoder
+
+ // 2. data in last track
+ // start audiodecoder and writer
+ // when audiodecoder finishes start isoimager
+
+ // 3. data in second session
+ // start audiodecoder and writer
+ // start isoimager and writer
+
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) {
+ m_currentAction = WRITING_ISO_IMAGE;
+ if( !prepareWriter() || !startWriting() ) {
+ cleanupAfterError();
+ jobFinished(false);
+ }
+ else
+ m_isoImager->start();
+ }
+ else
+ writeNextCopy();
+ }
+ else {
+ cleanupAfterError();
+ jobFinished( false );
+ }
+ }
+
+ //
+ // Image creation finished
+ //
+ else {
+ if( !success ) {
+ emit infoMessage( i18n("Error while creating ISO image."), ERROR );
+ cleanupAfterError();
+
+ jobFinished( false );
+ return;
+ }
+
+ if( m_doc->onTheFly() ) {
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_FIRST_TRACK ) {
+ m_currentAction = WRITING_AUDIO_IMAGE;
+ m_audioImager->start();
+ }
+ }
+ else {
+ emit infoMessage( i18n("ISO image successfully created."), SUCCESS );
+
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) {
+ m_currentAction = WRITING_ISO_IMAGE;
+
+ if( !prepareWriter() || !startWriting() ) {
+ cleanupAfterError();
+ jobFinished(false);
+ }
+ }
+ else {
+ emit newTask( i18n("Creating audio image files") );
+ m_currentAction = CREATING_AUDIO_IMAGE;
+ m_audioImager->start();
+ }
+ }
+ }
+}
+
+
+void K3bMixedJob::slotWriterFinished( bool success )
+{
+ if( m_canceled || m_errorOccuredAndAlreadyReported )
+ return;
+
+ if( !success ) {
+ cleanupAfterError();
+ jobFinished(false);
+ return;
+ }
+
+ emit burning(false);
+
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION && m_currentAction == WRITING_AUDIO_IMAGE ) {
+ // reload the media (as a subtask so the user does not see the "Flushing cache" or "Fixating" messages while
+ // doing so
+ emit newSubTask( i18n("Reloading the medium") );
+ connect( K3bDevice::reload( m_doc->burner() ), SIGNAL(finished(bool)),
+ this, SLOT(slotMediaReloadedForSecondSession(bool)) );
+ }
+ else {
+ d->copiesDone++;
+ if( d->copiesDone < d->copies ) {
+ K3bDevice::eject( m_doc->burner() );
+ writeNextCopy();
+ }
+ else {
+ if( !m_doc->onTheFly() && m_doc->removeImages() )
+ removeBufferFiles();
+
+ if( k3bcore->globalSettings()->ejectMedia() )
+ K3bDevice::eject( m_doc->burner() );
+
+ jobFinished(true);
+ }
+ }
+}
+
+
+void K3bMixedJob::slotMediaReloadedForSecondSession( bool success )
+{
+ if( !success )
+ blockingInformation( i18n("Please reload the medium and press 'ok'"),
+ i18n("Unable to close the tray") );
+
+ // start the next session
+ m_currentAction = WRITING_ISO_IMAGE;
+ if( d->copiesDone > 0 ) {
+ // we only create the image once. This should not be a problem???
+ if( !prepareWriter() || !startWriting() ) {
+ cleanupAfterError();
+ jobFinished(false);
+ }
+ else if( m_doc->onTheFly() ) {
+ m_isoImager->start();
+ }
+ }
+ else if( m_doc->dummy() ) {
+ // do not try to get ms info in simulation mode since the cd is empty!
+ if( m_doc->onTheFly() ) {
+ m_currentAction = PREPARING_DATA;
+ m_isoImager->calculateSize();
+ }
+ else
+ createIsoImage();
+ }
+ else {
+ m_currentAction = FETCHING_MSINFO;
+ m_msInfoFetcher->setDevice( m_doc->burner() );
+ m_msInfoFetcher->start();
+ }
+}
+
+
+void K3bMixedJob::slotAudioDecoderFinished( bool success )
+{
+ if( m_canceled || m_errorOccuredAndAlreadyReported )
+ return;
+
+ if( !success ) {
+ emit infoMessage( i18n("Error while decoding audio tracks."), ERROR );
+ cleanupAfterError();
+ jobFinished(false);
+ return;
+ }
+
+ if( m_doc->onTheFly() ) {
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_LAST_TRACK ) {
+ m_currentAction = WRITING_ISO_IMAGE;
+ m_isoImager->start();
+ }
+ }
+ else {
+ emit infoMessage( i18n("Audio images successfully created."), SUCCESS );
+
+ if( m_doc->audioDoc()->normalize() ) {
+ normalizeFiles();
+ }
+ else {
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_FIRST_TRACK )
+ m_currentAction = WRITING_ISO_IMAGE;
+ else
+ m_currentAction = WRITING_AUDIO_IMAGE;
+
+ if( !prepareWriter() || !startWriting() ) {
+ cleanupAfterError();
+ jobFinished(false);
+ }
+ }
+ }
+}
+
+
+void K3bMixedJob::slotAudioDecoderNextTrack( int t, int tt )
+{
+ if( m_doc->onlyCreateImages() || !m_doc->onTheFly() ) {
+ K3bAudioTrack* track = m_doc->audioDoc()->getTrack(t);
+ emit newSubTask( i18n("Decoding audio track %1 of %2%3")
+ .arg(t)
+ .arg(tt)
+ .arg( track->title().isEmpty() || track->artist().isEmpty()
+ ? QString::null
+ : " (" + track->artist() + " - " + track->title() + ")" ) );
+ }
+}
+
+
+bool K3bMixedJob::prepareWriter()
+{
+ if( m_writer ) delete m_writer;
+
+ if( ( m_currentAction == WRITING_ISO_IMAGE && m_usedDataWritingApp == K3b::CDRECORD ) ||
+ ( m_currentAction == WRITING_AUDIO_IMAGE && m_usedAudioWritingApp == K3b::CDRECORD ) ) {
+
+ if( !writeInfFiles() ) {
+ kdDebug() << "(K3bMixedJob) could not write inf-files." << endl;
+ emit infoMessage( i18n("IO Error"), ERROR );
+
+ return false;
+ }
+
+ K3bCdrecordWriter* writer = new K3bCdrecordWriter( m_doc->burner(), this, this );
+
+ // only write the audio tracks in DAO mode
+ if( m_currentAction == WRITING_ISO_IMAGE )
+ writer->setWritingMode( m_usedDataWritingMode );
+ else
+ writer->setWritingMode( m_usedAudioWritingMode );
+
+ writer->setSimulate( m_doc->dummy() );
+ writer->setBurnSpeed( m_doc->speed() );
+
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) {
+ if( m_currentAction == WRITING_ISO_IMAGE ) {
+ if( m_doc->onTheFly() )
+ writer->addArgument("-waiti");
+
+ addDataTrack( writer );
+ }
+ else {
+ writer->addArgument("-multi");
+ addAudioTracks( writer );
+ }
+ }
+ else {
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_FIRST_TRACK )
+ addDataTrack( writer );
+ addAudioTracks( writer );
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_LAST_TRACK )
+ addDataTrack( writer );
+ }
+
+ m_writer = writer;
+ }
+ else {
+ if( !writeTocFile() ) {
+ kdDebug() << "(K3bDataJob) could not write tocfile." << endl;
+ emit infoMessage( i18n("IO Error"), ERROR );
+
+ return false;
+ }
+
+ // create the writer
+ // create cdrdao job
+ K3bCdrdaoWriter* writer = new K3bCdrdaoWriter( m_doc->burner(), this, this );
+ writer->setSimulate( m_doc->dummy() );
+ writer->setBurnSpeed( m_doc->speed() );
+
+ // multisession only for the first session
+ writer->setMulti( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION
+ && m_currentAction == WRITING_AUDIO_IMAGE );
+
+ writer->setTocFile( m_tocFile->name() );
+
+ m_writer = writer;
+ }
+
+ connect( m_writer, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( m_writer, SIGNAL(percent(int)), this, SLOT(slotWriterJobPercent(int)) );
+ connect( m_writer, SIGNAL(processedSize(int, int)), this, SIGNAL(processedSize(int, int)) );
+ connect( m_writer, SIGNAL(subPercent(int)), this, SIGNAL(subPercent(int)) );
+ connect( m_writer, SIGNAL(processedSubSize(int, int)), this, SIGNAL(processedSubSize(int, int)) );
+ connect( m_writer, SIGNAL(nextTrack(int, int)), this, SLOT(slotWriterNextTrack(int, int)) );
+ connect( m_writer, SIGNAL(buffer(int)), this, SIGNAL(bufferStatus(int)) );
+ connect( m_writer, SIGNAL(deviceBuffer(int)), this, SIGNAL(deviceBuffer(int)) );
+ connect( m_writer, SIGNAL(writeSpeed(int, int)), this, SIGNAL(writeSpeed(int, int)) );
+ connect( m_writer, SIGNAL(finished(bool)), this, SLOT(slotWriterFinished(bool)) );
+ // connect( m_writer, SIGNAL(newTask(const QString&)), this, SIGNAL(newTask(const QString&)) );
+ connect( m_writer, SIGNAL(newSubTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) );
+ connect( m_writer, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+
+ return true;
+}
+
+
+bool K3bMixedJob::writeInfFiles()
+{
+ K3bInfFileWriter infFileWriter;
+ K3bAudioTrack* track = m_doc->audioDoc()->firstTrack();
+ while( track ) {
+
+ infFileWriter.setTrack( track->toCdTrack() );
+ infFileWriter.setTrackNumber( track->trackNumber() );
+ if( !m_doc->onTheFly() )
+ infFileWriter.setBigEndian( false );
+
+ if( !infFileWriter.save( m_tempData->infFileName(track) ) )
+ return false;
+
+ track = track->next();
+ }
+ return true;
+}
+
+
+bool K3bMixedJob::writeTocFile()
+{
+ // FIXME: create the tocfile in the same directory like all the other files.
+
+ if( m_tocFile ) delete m_tocFile;
+ m_tocFile = new KTempFile( QString::null, "toc" );
+ m_tocFile->setAutoDelete(true);
+
+ // write the toc-file
+ if( QTextStream* s = m_tocFile->textStream() ) {
+
+ K3bTocFileWriter tocFileWriter;
+
+ //
+ // TOC
+ //
+ tocFileWriter.setData( m_doc->toToc( m_usedDataMode == K3b::MODE2
+ ? K3bDevice::Track::XA_FORM1
+ : K3bDevice::Track::MODE1,
+ m_doc->onTheFly()
+ ? m_isoImager->size()
+ : m_doc->dataDoc()->length() ) );
+
+ //
+ // CD-Text
+ //
+ if( m_doc->audioDoc()->cdText() ) {
+ K3bDevice::CdText text = m_doc->audioDoc()->cdTextData();
+ // if data in first track we need to add a dummy cdtext
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_FIRST_TRACK )
+ text.insert( text.begin(), K3bDevice::TrackCdText() );
+
+ tocFileWriter.setCdText( text );
+ }
+
+ //
+ // Session to write
+ //
+ tocFileWriter.setSession( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION &&
+ m_currentAction == WRITING_ISO_IMAGE ? 2 : 1 );
+
+ //
+ // image filenames
+ //
+ if( !m_doc->onTheFly() ) {
+ QStringList files;
+ K3bAudioTrack* track = m_doc->audioDoc()->firstTrack();
+ while( track ) {
+ files += m_tempData->bufferFileName( track );
+ track = track->next();
+ }
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_FIRST_TRACK )
+ files.prepend( m_isoImageFilePath );
+ else
+ files.append( m_isoImageFilePath );
+
+ tocFileWriter.setFilenames( files );
+ }
+
+ bool success = tocFileWriter.save( *s );
+
+ m_tocFile->close();
+
+ // backup for debugging
+// KIO::NetAccess::del("/tmp/trueg/tocfile_debug_backup.toc",0L);
+// KIO::NetAccess::copy( m_tocFile->name(), "/tmp/trueg/tocfile_debug_backup.toc",0L );
+
+ return success;
+ }
+ else
+ return false;
+}
+
+
+void K3bMixedJob::addAudioTracks( K3bCdrecordWriter* writer )
+{
+ writer->addArgument( "-useinfo" );
+
+ // add raw cdtext data
+ if( m_doc->audioDoc()->cdText() ) {
+ writer->setRawCdText( m_doc->audioDoc()->cdTextData().rawPackData() );
+ }
+
+ writer->addArgument( "-audio" );
+
+ // we always pad because although K3b makes sure all tracks' length are multiples of 2352
+ // it seems that normalize sometimes corrupts these lengths
+ // FIXME: see K3bAudioJob for the whole less4secs and zeroPregap handling
+ writer->addArgument( "-pad" );
+
+ // Allow tracks shorter than 4 seconds
+ writer->addArgument( "-shorttrack" );
+
+ // add all the audio tracks
+ K3bAudioTrack* track = m_doc->audioDoc()->firstTrack();
+ while( track ) {
+ if( m_doc->onTheFly() ) {
+ // this is only supported by cdrecord versions >= 2.01a13
+ writer->addArgument( QFile::encodeName( m_tempData->infFileName( track ) ) );
+ }
+ else {
+ writer->addArgument( QFile::encodeName( m_tempData->bufferFileName( track ) ) );
+ }
+ track = track->next();
+ }
+}
+
+void K3bMixedJob::addDataTrack( K3bCdrecordWriter* writer )
+{
+ // add data track
+ if( m_usedDataMode == K3b::MODE2 ) {
+ if( k3bcore->externalBinManager()->binObject("cdrecord") &&
+ k3bcore->externalBinManager()->binObject("cdrecord")->hasFeature( "xamix" ) )
+ writer->addArgument( "-xa" );
+ else
+ writer->addArgument( "-xa1" );
+ }
+ else
+ writer->addArgument( "-data" );
+
+ if( m_doc->onTheFly() )
+ writer->addArgument( QString("-tsize=%1s").arg(m_isoImager->size()) )->addArgument("-");
+ else
+ writer->addArgument( m_isoImageFilePath );
+}
+
+
+void K3bMixedJob::slotWriterNextTrack( int t, int )
+{
+ K3bAudioTrack* track = 0;
+
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_FIRST_TRACK ) {
+ if( t > 1 )
+ track = m_doc->audioDoc()->getTrack(t-1);
+ }
+ else if( m_doc->mixedType() == K3bMixedDoc::DATA_LAST_TRACK ) {
+ if( t < m_doc->audioDoc()->numOfTracks()+1 )
+ track = m_doc->audioDoc()->getTrack(t);
+ }
+ else if( m_currentAction == WRITING_AUDIO_IMAGE )
+ track = m_doc->audioDoc()->getTrack(t);
+ else
+ t = m_doc->numOfTracks();
+
+ if( track )
+ emit newSubTask( i18n("Writing track %1 of %2%3")
+ .arg(t)
+ .arg(m_doc->numOfTracks())
+ .arg( track->title().isEmpty() || track->artist().isEmpty()
+ ? QString::null
+ : " (" + track->artist() + " - " + track->title() + ")" ) );
+ else
+ emit newSubTask( i18n("Writing track %1 of %2 (%3)").arg(t).arg(m_doc->numOfTracks()).arg(i18n("ISO9660 data")) );
+}
+
+
+void K3bMixedJob::slotWriterJobPercent( int p )
+{
+ double totalTasks = d->copies;
+ double tasksDone = d->copiesDone;
+ if( m_doc->audioDoc()->normalize() ) {
+ totalTasks+=1.0;
+ tasksDone+=1.0;
+ }
+ if( !m_doc->onTheFly() ) {
+ totalTasks+=1.0;
+ }
+
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) {
+ if( m_currentAction == WRITING_AUDIO_IMAGE ) {
+ // the audio imager has finished in all cases
+ // the iso imager only if this is not the first copy
+ if( d->copiesDone > 0 )
+ tasksDone += 1.0;
+ else if( !m_doc->onTheFly() )
+ tasksDone += m_audioDocPartOfProcess;
+
+ p = (int)((double)p*m_audioDocPartOfProcess);
+ }
+ else {
+ // all images have been created
+ if( !m_doc->onTheFly() )
+ tasksDone += 1.0;
+
+ p = (int)(100.0*m_audioDocPartOfProcess + (double)p*(1.0-m_audioDocPartOfProcess));
+ }
+ }
+ else if( !m_doc->onTheFly() )
+ tasksDone += 1.0;
+
+ emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
+}
+
+
+void K3bMixedJob::slotAudioDecoderPercent( int p )
+{
+ // the only thing finished here might be the isoimager which is part of this task
+ if( !m_doc->onTheFly() ) {
+ double totalTasks = d->copies+1;
+ if( m_doc->audioDoc()->normalize() )
+ totalTasks+=1.0;
+
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION )
+ p = (int)((double)p*m_audioDocPartOfProcess);
+ else
+ p = (int)(100.0*(1.0-m_audioDocPartOfProcess) + (double)p*m_audioDocPartOfProcess);
+
+ emit percent( (int)((double)p / totalTasks) );
+ }
+}
+
+
+void K3bMixedJob::slotAudioDecoderSubPercent( int p )
+{
+ if( !m_doc->onTheFly() ) {
+ emit subPercent( p );
+ }
+}
+
+
+void K3bMixedJob::slotIsoImagerPercent( int p )
+{
+ if( !m_doc->onTheFly() ) {
+ emit subPercent( p );
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) {
+
+ double totalTasks = d->copies+1.0;
+ double tasksDone = d->copiesDone;
+ if( m_doc->audioDoc()->normalize() ) {
+ totalTasks+=1.0;
+ // the normalizer finished
+ tasksDone+=1.0;
+ }
+
+ // the writing of the audio part finished
+ tasksDone += m_audioDocPartOfProcess;
+
+ // the audio decoder finished (which is part of this task in terms of progress)
+ p = (int)(100.0*m_audioDocPartOfProcess + (double)p*(1.0-m_audioDocPartOfProcess));
+
+ emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
+ }
+ else {
+ double totalTasks = d->copies+1.0;
+ if( m_doc->audioDoc()->normalize() )
+ totalTasks+=1.0;
+
+ emit percent( (int)((double)(p*(1.0-m_audioDocPartOfProcess)) / totalTasks) );
+ }
+ }
+}
+
+
+bool K3bMixedJob::startWriting()
+{
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) {
+ if( m_currentAction == WRITING_ISO_IMAGE) {
+ if( m_doc->dummy() )
+ emit newTask( i18n("Simulating second session") );
+ else if( d->copies > 1 )
+ emit newTask( i18n("Writing second session of copy %1").arg(d->copiesDone+1) );
+ else
+ emit newTask( i18n("Writing second session") );
+ }
+ else {
+ if( m_doc->dummy() )
+ emit newTask( i18n("Simulating first session") );
+ else if( d->copies > 1 )
+ emit newTask( i18n("Writing first session of copy %1").arg(d->copiesDone+1) );
+ else
+ emit newTask( i18n("Writing first session") );
+ }
+ }
+ else if( m_doc->dummy() )
+ emit newTask( i18n("Simulating") );
+ else
+ emit newTask( i18n("Writing Copy %1").arg(d->copiesDone+1) );
+
+
+ // if we append the second session the cd is already in the drive
+ if( !(m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION
+ && m_currentAction == WRITING_ISO_IMAGE) ) {
+
+ emit newSubTask( i18n("Waiting for media") );
+ if( waitForMedia( m_doc->burner() ) < 0 ) {
+ cancel();
+ return false;
+ }
+
+ // just to be sure we did not get canceled during the async discWaiting
+ if( m_canceled )
+ return false;
+
+ // check if the project will fit on the CD
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) {
+ // the media is in and has been checked so this should be fast (hopefully)
+ K3b::Msf mediaSize = m_doc->burner()->diskInfo().capacity();
+ if( mediaSize < m_projectSize ) {
+ if( k3bcore->globalSettings()->overburn() ) {
+ emit infoMessage( i18n("Trying to write more than the official disk capacity"), K3bJob::WARNING );
+ }
+ else {
+ emit infoMessage( i18n("Data does not fit on disk."), ERROR );
+ return false;
+ }
+ }
+ }
+ }
+
+ // in case we determined the max possible writing speed we have to reset the speed on the writer job
+ // here since an inserted media is necessary
+ // the Max speed job will compare the max speed value with the supported values of the writer
+ if( d->maxSpeed )
+ m_writer->setBurnSpeed( d->maxSpeedJob->maxSpeed() );
+
+ emit burning(true);
+ m_writer->start();
+
+ if( m_doc->onTheFly() ) {
+ // now the writer is running and we can get it's stdin
+ // we only use this method when writing on-the-fly since
+ // we cannot easily change the audioDecode fd while it's working
+ // which we would need to do since we write into several
+ // image files.
+ m_audioImager->writeToFd( m_writer->fd() );
+ m_isoImager->writeToFd( m_writer->fd() );
+ }
+
+ return true;
+}
+
+
+void K3bMixedJob::createIsoImage()
+{
+ m_currentAction = CREATING_ISO_IMAGE;
+
+ // prepare iso image file
+ m_isoImageFilePath = m_tempFilePrefix + "_datatrack.iso";
+
+ if( !m_doc->onTheFly() )
+ emit newTask( i18n("Creating ISO image file") );
+ emit newSubTask( i18n("Creating ISO image in %1").arg(m_isoImageFilePath) );
+ emit infoMessage( i18n("Creating ISO image in %1").arg(m_isoImageFilePath), INFO );
+
+ m_isoImager->writeToImageFile( m_isoImageFilePath );
+ m_isoImager->start();
+}
+
+
+void K3bMixedJob::cleanupAfterError()
+{
+ m_errorOccuredAndAlreadyReported = true;
+ // m_audioImager->cancel();
+ m_isoImager->cancel();
+ if( m_writer )
+ m_writer->cancel();
+
+ if( m_tocFile ) delete m_tocFile;
+ m_tocFile = 0;
+
+ // remove the temp files
+ removeBufferFiles();
+}
+
+
+void K3bMixedJob::removeBufferFiles()
+{
+ if ( !m_doc->onTheFly() ) {
+ emit infoMessage( i18n("Removing buffer files."), INFO );
+ }
+
+ if( QFile::exists( m_isoImageFilePath ) )
+ if( !QFile::remove( m_isoImageFilePath ) )
+ emit infoMessage( i18n("Could not delete file %1.").arg(m_isoImageFilePath), ERROR );
+
+ // removes buffer images and temp toc or inf files
+ m_tempData->cleanup();
+}
+
+
+void K3bMixedJob::determineWritingMode()
+{
+ // we don't need this when only creating image and it is possible
+ // that the burn device is null
+ if( m_doc->onlyCreateImages() )
+ return;
+
+ // at first we determine the data mode
+ // --------------------------------------------------------------
+ if( m_doc->dataDoc()->dataMode() == K3b::DATA_MODE_AUTO ) {
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION )
+ m_usedDataMode = K3b::MODE2;
+ else
+ m_usedDataMode = K3b::MODE1;
+ }
+ else
+ m_usedDataMode = m_doc->dataDoc()->dataMode();
+
+
+ // we try to use cdrecord if possible
+ bool cdrecordOnTheFly = false;
+ bool cdrecordCdText = false;
+ bool cdrecordUsable = false;
+
+ if( k3bcore->externalBinManager()->binObject("cdrecord") ) {
+ cdrecordOnTheFly =
+ k3bcore->externalBinManager()->binObject("cdrecord")->hasFeature( "audio-stdin" );
+ cdrecordCdText =
+ k3bcore->externalBinManager()->binObject("cdrecord")->hasFeature( "cdtext" );
+ cdrecordUsable =
+ !( !cdrecordOnTheFly && m_doc->onTheFly() ) &&
+ !( m_doc->audioDoc()->cdText() && !cdrecordCdText );
+ }
+
+ // Writing Application
+ // --------------------------------------------------------------
+ // cdrecord seems to have problems writing xa 1 disks in dao mode? At least on my system!
+ if( writingApp() == K3b::DEFAULT ) {
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) {
+ if( m_doc->writingMode() == K3b::DAO ||
+ ( m_doc->writingMode() == K3b::WRITING_MODE_AUTO && !cdrecordUsable ) ) {
+ m_usedAudioWritingApp = K3b::CDRDAO;
+ m_usedDataWritingApp = K3b::CDRDAO;
+ }
+ else {
+ m_usedAudioWritingApp = K3b::CDRECORD;
+ m_usedDataWritingApp = K3b::CDRECORD;
+ }
+ }
+ else {
+ if( cdrecordUsable ) {
+ m_usedAudioWritingApp = K3b::CDRECORD;
+ m_usedDataWritingApp = K3b::CDRECORD;
+ }
+ else {
+ m_usedAudioWritingApp = K3b::CDRDAO;
+ m_usedDataWritingApp = K3b::CDRDAO;
+ }
+ }
+ }
+ else {
+ m_usedAudioWritingApp = writingApp();
+ m_usedDataWritingApp = writingApp();
+ }
+
+ // TODO: use K3bExceptions::brokenDaoAudio
+
+ // Writing Mode (TAO/DAO/RAW)
+ // --------------------------------------------------------------
+ if( m_doc->writingMode() == K3b::WRITING_MODE_AUTO ) {
+
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) {
+ if( m_usedDataWritingApp == K3b::CDRECORD )
+ m_usedDataWritingMode = K3b::TAO;
+ else
+ m_usedDataWritingMode = K3b::DAO;
+
+ // default to Session at once for the audio part
+ m_usedAudioWritingMode = K3b::DAO;
+ }
+ else if( writer()->dao() ) {
+ m_usedDataWritingMode = K3b::DAO;
+ m_usedAudioWritingMode = K3b::DAO;
+ }
+ else {
+ m_usedDataWritingMode = K3b::TAO;
+ m_usedAudioWritingMode = K3b::TAO;
+ }
+ }
+ else {
+ m_usedAudioWritingMode = m_doc->writingMode();
+ m_usedDataWritingMode = m_doc->writingMode();
+ }
+
+
+ if( m_usedDataWritingApp == K3b::CDRECORD ) {
+ if( !cdrecordOnTheFly && m_doc->onTheFly() ) {
+ m_doc->setOnTheFly( false );
+ emit infoMessage( i18n("On-the-fly writing with cdrecord < 2.01a13 not supported."), ERROR );
+ }
+
+ if( m_doc->audioDoc()->cdText() ) {
+ if( !cdrecordCdText ) {
+ m_doc->audioDoc()->writeCdText( false );
+ emit infoMessage( i18n("Cdrecord %1 does not support CD-Text writing.").arg(k3bcore->externalBinManager()->binObject("cdrecord")->version), ERROR );
+ }
+ else if( m_usedAudioWritingMode == K3b::TAO ) {
+ emit infoMessage( i18n("It is not possible to write CD-Text in TAO mode. Try DAO or RAW."), WARNING );
+ }
+ }
+ }
+}
+
+
+void K3bMixedJob::normalizeFiles()
+{
+ if( !m_normalizeJob ) {
+ m_normalizeJob = new K3bAudioNormalizeJob( this, this );
+
+ connect( m_normalizeJob, SIGNAL(infoMessage(const QString&, int)),
+ this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( m_normalizeJob, SIGNAL(percent(int)), this, SLOT(slotNormalizeProgress(int)) );
+ connect( m_normalizeJob, SIGNAL(subPercent(int)), this, SLOT(slotNormalizeSubProgress(int)) );
+ connect( m_normalizeJob, SIGNAL(finished(bool)), this, SLOT(slotNormalizeJobFinished(bool)) );
+ connect( m_normalizeJob, SIGNAL(newTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) );
+ connect( m_normalizeJob, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+ }
+
+ // add all the files
+ QValueVector<QString> files;
+ K3bAudioTrack* track = m_doc->audioDoc()->firstTrack();
+ while( track ) {
+ files.append( m_tempData->bufferFileName(track) );
+ track = track->next();
+ }
+
+ m_normalizeJob->setFilesToNormalize( files );
+
+ emit newTask( i18n("Normalizing volume levels") );
+ m_normalizeJob->start();
+}
+
+void K3bMixedJob::slotNormalizeJobFinished( bool success )
+{
+ if( m_canceled || m_errorOccuredAndAlreadyReported )
+ return;
+
+ if( success ) {
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_FIRST_TRACK )
+ m_currentAction = WRITING_ISO_IMAGE;
+ else
+ m_currentAction = WRITING_AUDIO_IMAGE;
+
+ if( !prepareWriter() || !startWriting() ) {
+ cleanupAfterError();
+ jobFinished(false);
+ }
+ }
+ else {
+ cleanupAfterError();
+ jobFinished(false);
+ }
+}
+
+void K3bMixedJob::slotNormalizeProgress( int p )
+{
+ double totalTasks = d->copies+2.0;
+ double tasksDone = 0;
+
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) {
+ // the audio imager finished (m_audioDocPartOfProcess*1 task)
+ // plus the normalize progress
+ tasksDone = m_audioDocPartOfProcess;
+ }
+ else {
+ // the iso and audio imagers already finished (one task)
+ // plus the normalize progress
+ tasksDone = 1.0;
+ }
+
+ emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
+}
+
+
+void K3bMixedJob::slotNormalizeSubProgress( int p )
+{
+ emit subPercent( p );
+}
+
+
+void K3bMixedJob::prepareProgressInformation()
+{
+ // calculate percentage of audio and data
+ // this is also used in on-the-fly mode
+ double ds = (double)m_doc->dataDoc()->length().totalFrames();
+ double as = (double)m_doc->audioDoc()->length().totalFrames();
+ m_audioDocPartOfProcess = as/(ds+as);
+}
+
+
+QString K3bMixedJob::jobDescription() const
+{
+ if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION )
+ return i18n("Writing Enhanced Audio CD")
+ + ( m_doc->audioDoc()->title().isEmpty()
+ ? QString::null
+ : QString( " (%1)" ).arg(m_doc->audioDoc()->title()) );
+ else
+ return i18n("Writing Mixed Mode CD")
+ + ( m_doc->audioDoc()->title().isEmpty()
+ ? QString::null
+ : QString( " (%1)" ).arg(m_doc->audioDoc()->title()) );
+}
+
+
+QString K3bMixedJob::jobDetails() const
+{
+ return ( i18n("%1 tracks (%2 minutes audio data, %3 ISO9660 data)")
+ .arg(m_doc->numOfTracks())
+ .arg(m_doc->audioDoc()->length().toString())
+ .arg(KIO::convertSize(m_doc->dataDoc()->size()))
+ + ( m_doc->copies() > 1 && !m_doc->dummy()
+ ? i18n(" - %n copy", " - %n copies", m_doc->copies())
+ : QString::null ) );
+}
+
+#include "k3bmixedjob.moc"
diff --git a/libk3b/projects/mixedcd/k3bmixedjob.h b/libk3b/projects/mixedcd/k3bmixedjob.h
new file mode 100644
index 0000000..50a1dc7
--- /dev/null
+++ b/libk3b/projects/mixedcd/k3bmixedjob.h
@@ -0,0 +1,144 @@
+/*
+ *
+ * $Id: k3bmixedjob.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BMIXEDJOB_H
+#define K3BMIXEDJOB_H
+
+#include <k3bjob.h>
+
+
+class K3bMixedDoc;
+class K3bIsoImager;
+class K3bAudioImager;
+class QFile;
+class QDataStream;
+class K3bAbstractWriter;
+class K3bWaveFileWriter;
+class KTempFile;
+class K3bCdrecordWriter;
+class K3bMsInfoFetcher;
+class K3bAudioNormalizeJob;
+class K3bAudioJobTempData;
+class K3bDevice::Device;
+
+/**
+ *@author Sebastian Trueg
+ */
+class K3bMixedJob : public K3bBurnJob
+{
+ Q_OBJECT
+
+ public:
+ K3bMixedJob( K3bMixedDoc*, K3bJobHandler*, QObject* parent = 0 );
+ ~K3bMixedJob();
+
+ K3bDoc* doc() const;
+ K3bDevice::Device* writer() const;
+
+ QString jobDescription() const;
+ QString jobDetails() const;
+
+ public slots:
+ void cancel();
+ void start();
+
+ protected slots:
+ // iso imager slots
+ void slotIsoImagerFinished( bool success );
+ void slotIsoImagerPercent(int);
+
+ // ms info fetcher slots
+ void slotMsInfoFetched(bool);
+
+ // audio decoder slots
+ void slotAudioDecoderFinished( bool );
+ void slotAudioDecoderNextTrack( int, int );
+ void slotAudioDecoderPercent(int);
+ void slotAudioDecoderSubPercent( int );
+
+ // writer slots
+ void slotWriterFinished( bool success );
+ void slotWriterNextTrack(int, int);
+ void slotWriterJobPercent(int);
+
+ // normalizing slots
+ void slotNormalizeJobFinished( bool );
+ void slotNormalizeProgress( int );
+ void slotNormalizeSubProgress( int );
+
+ // misc slots
+ void slotMediaReloadedForSecondSession( bool );
+ void slotMaxSpeedJobFinished( bool );
+
+ private:
+ bool prepareWriter();
+ bool writeTocFile();
+ bool writeInfFiles();
+ bool startWriting();
+ void startFirstCopy();
+ void addAudioTracks( K3bCdrecordWriter* writer );
+ void addDataTrack( K3bCdrecordWriter* writer );
+ void cleanupAfterError();
+ void removeBufferFiles();
+ void createIsoImage();
+ void determineWritingMode();
+ void normalizeFiles();
+ void prepareProgressInformation();
+ void writeNextCopy();
+ void determinePreliminaryDataImageSize();
+
+ K3bMixedDoc* m_doc;
+ K3bIsoImager* m_isoImager;
+ K3bAudioImager* m_audioImager;
+ K3bAudioJobTempData* m_tempData;
+ K3bWaveFileWriter* m_waveFileWriter;
+ K3bAbstractWriter* m_writer;
+ K3bMsInfoFetcher* m_msInfoFetcher;
+ K3bAudioNormalizeJob* m_normalizeJob;
+
+ QString m_isoImageFilePath;
+
+ KTempFile* m_tocFile;
+
+ enum Action { INITIALIZING_IMAGER,
+ PREPARING_DATA,
+ CREATING_ISO_IMAGE,
+ CREATING_AUDIO_IMAGE,
+ WRITING_ISO_IMAGE,
+ WRITING_AUDIO_IMAGE,
+ FETCHING_MSINFO };
+
+ int m_currentAction;
+ double m_audioDocPartOfProcess;
+
+ bool m_canceled;
+ bool m_errorOccuredAndAlreadyReported;
+
+ int m_usedDataMode;
+ int m_usedDataWritingApp;
+ int m_usedAudioWritingApp;
+ int m_usedDataWritingMode;
+ int m_usedAudioWritingMode;
+
+ QString m_tempFilePrefix;
+
+ K3b::Msf m_projectSize;
+
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/projects/movixcd/Makefile.am b/libk3b/projects/movixcd/Makefile.am
new file mode 100644
index 0000000..d1b4f5d
--- /dev/null
+++ b/libk3b/projects/movixcd/Makefile.am
@@ -0,0 +1,23 @@
+# we need the ../datacd for the uic generated header files
+AM_CPPFLAGS= -I$(srcdir)/../../core \
+ -I$(srcdir)/../../../libk3bdevice \
+ -I$(srcdir)/../../../src \
+ -I$(srcdir)/../../tools \
+ -I$(srcdir)/../datacd \
+ -I$(srcdir)/.. \
+ -I../datacd \
+ $(all_includes)
+
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libmovix.la
+
+libmovix_la_SOURCES = k3bmovixprogram.cpp \
+ k3bmovixdoc.cpp \
+ k3bmovixjob.cpp \
+ k3bmovixfileitem.cpp \
+ k3bmovixdocpreparer.cpp
+
+include_HEADERS = k3bmovixdoc.h \
+ k3bmovixjob.h \
+ k3bmovixfileitem.h
diff --git a/libk3b/projects/movixcd/k3bmovixdoc.cpp b/libk3b/projects/movixcd/k3bmovixdoc.cpp
new file mode 100644
index 0000000..f7b2198
--- /dev/null
+++ b/libk3b/projects/movixcd/k3bmovixdoc.cpp
@@ -0,0 +1,445 @@
+/*
+ *
+ * $Id: k3bmovixdoc.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bmovixdoc.h"
+#include "k3bmovixjob.h"
+#include "k3bmovixfileitem.h"
+
+#include <k3bdiritem.h>
+#include <k3bfileitem.h>
+#include <k3bglobals.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kurl.h>
+#include <kinputdialog.h>
+#include <kmessagebox.h>
+#include <kconfig.h>
+#include <kapplication.h>
+
+#include <qdom.h>
+#include <qfileinfo.h>
+
+
+K3bMovixDoc::K3bMovixDoc( QObject* parent )
+ : K3bDataDoc( parent )
+{
+ connect( this, SIGNAL(itemRemoved(K3bDataItem*)),
+ this, SLOT(slotDataItemRemoved(K3bDataItem*)) );
+}
+
+
+K3bMovixDoc::~K3bMovixDoc()
+{
+}
+
+
+K3bBurnJob* K3bMovixDoc::newBurnJob( K3bJobHandler* hdl, QObject* parent )
+{
+ return new K3bMovixJob( this, hdl, parent );
+}
+
+
+bool K3bMovixDoc::newDocument()
+{
+ m_loopPlaylist = 1;
+ m_ejectDisk = false;
+ m_reboot = false;
+ m_shutdown = false;
+ m_randomPlay = false;
+
+ return K3bDataDoc::newDocument();
+}
+
+
+void K3bMovixDoc::addUrls( const KURL::List& urls )
+{
+ for( KURL::List::ConstIterator it = urls.begin(); it != urls.end(); ++it ) {
+ addMovixFile( *it );
+ }
+
+ emit newMovixFileItems();
+}
+
+
+void K3bMovixDoc::addMovixFile( const KURL& _url, int pos )
+{
+ KURL url = K3b::convertToLocalUrl( _url );
+
+ QFileInfo f( url.path() );
+ if( !f.isFile() || !url.isLocalFile() )
+ return;
+
+ QString newName = f.fileName();
+ if( nameAlreadyInDir( newName, root() ) ) {
+ kapp->config()->setGroup("Data project settings");
+ bool dropDoubles = kapp->config()->readBoolEntry( "Drop doubles", false );
+ if( dropDoubles )
+ return;
+
+ bool ok = true;
+ do {
+ newName = KInputDialog::getText( i18n("Enter New Filename"),
+ i18n("A file with that name already exists. Please enter a new name:"),
+ newName, &ok, 0 );
+ } while( ok && nameAlreadyInDir( newName, root() ) );
+
+ if( !ok )
+ return;
+ }
+
+ K3bMovixFileItem* newK3bItem = new K3bMovixFileItem( f.absFilePath(), this, root(), newName );
+ if( pos < 0 || pos > (int)m_movixFiles.count() )
+ pos = m_movixFiles.count();
+
+ m_movixFiles.insert( pos, newK3bItem );
+
+ emit newMovixFileItems();
+
+ setModified(true);
+}
+
+
+bool K3bMovixDoc::loadDocumentData( QDomElement* rootElem )
+{
+ if( !root() )
+ newDocument();
+
+ QDomNodeList nodes = rootElem->childNodes();
+
+ if( nodes.item(0).nodeName() != "general" ) {
+ kdDebug() << "(K3bMovixDoc) could not find 'general' section." << endl;
+ return false;
+ }
+ if( !readGeneralDocumentData( nodes.item(0).toElement() ) )
+ return false;
+
+
+ // parse options
+ // -----------------------------------------------------------------
+ if( nodes.item(1).nodeName() != "data_options" ) {
+ kdDebug() << "(K3bMovixDoc) could not find 'data_options' section." << endl;
+ return false;
+ }
+ if( !loadDocumentDataOptions( nodes.item(1).toElement() ) )
+ return false;
+ // -----------------------------------------------------------------
+
+
+
+ // parse header
+ // -----------------------------------------------------------------
+ if( nodes.item(2).nodeName() != "data_header" ) {
+ kdDebug() << "(K3bMovixDoc) could not find 'data_header' section." << endl;
+ return false;
+ }
+ if( !loadDocumentDataHeader( nodes.item(2).toElement() ) )
+ return false;
+ // -----------------------------------------------------------------
+
+
+
+ // parse movix options
+ // -----------------------------------------------------------------
+ if( nodes.item(3).nodeName() != "movix_options" ) {
+ kdDebug() << "(K3bMovixDoc) could not find 'movix_options' section." << endl;
+ return false;
+ }
+
+ // load the options
+ QDomNodeList optionList = nodes.item(3).childNodes();
+ for( uint i = 0; i < optionList.count(); i++ ) {
+
+ QDomElement e = optionList.item(i).toElement();
+ if( e.isNull() )
+ return false;
+
+ if( e.nodeName() == "shutdown")
+ setShutdown( e.attributeNode( "activated" ).value() == "yes" );
+ else if( e.nodeName() == "reboot")
+ setReboot( e.attributeNode( "activated" ).value() == "yes" );
+ else if( e.nodeName() == "eject_disk")
+ setEjectDisk( e.attributeNode( "activated" ).value() == "yes" );
+ else if( e.nodeName() == "random_play")
+ setRandomPlay( e.attributeNode( "activated" ).value() == "yes" );
+ else if( e.nodeName() == "no_dma")
+ setNoDma( e.attributeNode( "activated" ).value() == "yes" );
+ else if( e.nodeName() == "subtitle_fontset")
+ setSubtitleFontset( e.text() );
+ else if( e.nodeName() == "boot_message_language")
+ setBootMessageLanguage( e.text() );
+ else if( e.nodeName() == "audio_background")
+ setAudioBackground( e.text() );
+ else if( e.nodeName() == "keyboard_language")
+ setKeyboardLayout( e.text() );
+ else if( e.nodeName() == "codecs")
+ setCodecs( QStringList::split( ',', e.text() ) );
+ else if( e.nodeName() == "default_boot_label")
+ setDefaultBootLabel( e.text() );
+ else if( e.nodeName() == "additional_mplayer_options")
+ setAdditionalMPlayerOptions( e.text() );
+ else if( e.nodeName() == "unwanted_mplayer_options")
+ setUnwantedMPlayerOptions( e.text() );
+ else if( e.nodeName() == "loop_playlist")
+ setLoopPlaylist( e.text().toInt() );
+ else
+ kdDebug() << "(K3bMovixDoc) unknown movix option: " << e.nodeName() << endl;
+ }
+ // -----------------------------------------------------------------
+
+ // parse files
+ // -----------------------------------------------------------------
+ if( nodes.item(4).nodeName() != "movix_files" ) {
+ kdDebug() << "(K3bMovixDoc) could not find 'movix_files' section." << endl;
+ return false;
+ }
+
+ // load file items
+ QDomNodeList fileList = nodes.item(4).childNodes();
+ for( uint i = 0; i < fileList.count(); i++ ) {
+
+ QDomElement e = fileList.item(i).toElement();
+ if( e.isNull() )
+ return false;
+
+ if( e.nodeName() == "file" ) {
+ if( !e.hasAttribute( "name" ) ) {
+ kdDebug() << "(K3bMovixDoc) found file tag without name attribute." << endl;
+ return false;
+ }
+
+ QDomElement urlElem = e.firstChild().toElement();
+ if( urlElem.isNull() ) {
+ kdDebug() << "(K3bMovixDoc) found file tag without url child." << endl;
+ return false;
+ }
+
+ // create the item
+ K3bMovixFileItem* newK3bItem = new K3bMovixFileItem( urlElem.text(),
+ this,
+ root(),
+ e.attributeNode("name").value() );
+ m_movixFiles.append( newK3bItem );
+
+ // subtitle file?
+ QDomElement subTitleElem = e.childNodes().item(1).toElement();
+ if( !subTitleElem.isNull() && subTitleElem.nodeName() == "subtitle_file" ) {
+ urlElem = subTitleElem.firstChild().toElement();
+ if( urlElem.isNull() ) {
+ kdDebug() << "(K3bMovixDoc) found subtitle_file tag without url child." << endl;
+ return false;
+ }
+
+ QString name = K3bMovixFileItem::subTitleFileName( newK3bItem->k3bName() );
+ K3bFileItem* subItem = new K3bFileItem( urlElem.text(), this, root(), name );
+ newK3bItem->setSubTitleItem( subItem );
+ }
+ }
+ else {
+ kdDebug() << "(K3bMovixDoc) found " << e.nodeName() << " node where 'file' was expected." << endl;
+ return false;
+ }
+ }
+ // -----------------------------------------------------------------
+
+
+ emit newMovixFileItems();
+
+ return true;
+}
+
+
+bool K3bMovixDoc::saveDocumentData( QDomElement* docElem )
+{
+ QDomDocument doc = docElem->ownerDocument();
+
+ saveGeneralDocumentData( docElem );
+
+ QDomElement optionsElem = doc.createElement( "data_options" );
+ saveDocumentDataOptions( optionsElem );
+
+ QDomElement headerElem = doc.createElement( "data_header" );
+ saveDocumentDataHeader( headerElem );
+
+ QDomElement movixOptElem = doc.createElement( "movix_options" );
+ QDomElement movixFilesElem = doc.createElement( "movix_files" );
+
+
+ // save the movix options
+ QDomElement propElem = doc.createElement( "shutdown" );
+ propElem.setAttribute( "activated", shutdown() ? "yes" : "no" );
+ movixOptElem.appendChild( propElem );
+
+ propElem = doc.createElement( "reboot" );
+ propElem.setAttribute( "activated", reboot() ? "yes" : "no" );
+ movixOptElem.appendChild( propElem );
+
+ propElem = doc.createElement( "eject_disk" );
+ propElem.setAttribute( "activated", ejectDisk() ? "yes" : "no" );
+ movixOptElem.appendChild( propElem );
+
+ propElem = doc.createElement( "random_play" );
+ propElem.setAttribute( "activated", randomPlay() ? "yes" : "no" );
+ movixOptElem.appendChild( propElem );
+
+ propElem = doc.createElement( "no_dma" );
+ propElem.setAttribute( "activated", noDma() ? "yes" : "no" );
+ movixOptElem.appendChild( propElem );
+
+ propElem = doc.createElement( "subtitle_fontset" );
+ propElem.appendChild( doc.createTextNode( subtitleFontset() ) );
+ movixOptElem.appendChild( propElem );
+
+ propElem = doc.createElement( "boot_message_language" );
+ propElem.appendChild( doc.createTextNode( bootMessageLanguage() ) );
+ movixOptElem.appendChild( propElem );
+
+ propElem = doc.createElement( "audio_background" );
+ propElem.appendChild( doc.createTextNode( audioBackground() ) );
+ movixOptElem.appendChild( propElem );
+
+ propElem = doc.createElement( "keyboard_language" );
+ propElem.appendChild( doc.createTextNode( keyboardLayout() ) );
+ movixOptElem.appendChild( propElem );
+
+ propElem = doc.createElement( "codecs" );
+ propElem.appendChild( doc.createTextNode( codecs().join(",") ) );
+ movixOptElem.appendChild( propElem );
+
+ propElem = doc.createElement( "default_boot_label" );
+ propElem.appendChild( doc.createTextNode( defaultBootLabel() ) );
+ movixOptElem.appendChild( propElem );
+
+ propElem = doc.createElement( "additional_mplayer_options" );
+ propElem.appendChild( doc.createTextNode( additionalMPlayerOptions() ) );
+ movixOptElem.appendChild( propElem );
+
+ propElem = doc.createElement( "unwanted_mplayer_options" );
+ propElem.appendChild( doc.createTextNode( unwantedMPlayerOptions() ) );
+ movixOptElem.appendChild( propElem );
+
+ propElem = doc.createElement( "loop_playlist" );
+ propElem.appendChild( doc.createTextNode( QString::number(loopPlaylist()) ) );
+ movixOptElem.appendChild( propElem );
+
+
+ // save the movix items
+ for( QPtrListIterator<K3bMovixFileItem> it( m_movixFiles );
+ *it; ++it ) {
+ K3bMovixFileItem* item = *it;
+
+ QDomElement topElem = doc.createElement( "file" );
+ topElem.setAttribute( "name", item->k3bName() );
+ QDomElement urlElem = doc.createElement( "url" );
+ urlElem.appendChild( doc.createTextNode( item->localPath() ) );
+ topElem.appendChild( urlElem );
+ if( item->subTitleItem() ) {
+ QDomElement subElem = doc.createElement( "subtitle_file" );
+ urlElem = doc.createElement( "url" );
+ urlElem.appendChild( doc.createTextNode( item->subTitleItem()->localPath() ) );
+ subElem.appendChild( urlElem );
+ topElem.appendChild( subElem );
+ }
+
+ movixFilesElem.appendChild( topElem );
+ }
+
+ docElem->appendChild( optionsElem );
+ docElem->appendChild( headerElem );
+ docElem->appendChild( movixOptElem );
+ docElem->appendChild( movixFilesElem );
+
+ return true;
+}
+
+
+void K3bMovixDoc::slotDataItemRemoved( K3bDataItem* item )
+{
+ // check if it's a movix item
+ if( K3bMovixFileItem* fi = dynamic_cast<K3bMovixFileItem*>(item) )
+ if( m_movixFiles.containsRef( fi ) ) {
+ emit movixItemRemoved( fi );
+ m_movixFiles.removeRef( fi );
+ setModified(true);
+ }
+}
+
+
+int K3bMovixDoc::indexOf( K3bMovixFileItem* item )
+{
+ return m_movixFiles.findRef(item)+1;
+}
+
+
+void K3bMovixDoc::moveMovixItem( K3bMovixFileItem* item, K3bMovixFileItem* itemAfter )
+{
+ if( item == itemAfter )
+ return;
+
+ // set the current item to track
+ m_movixFiles.findRef( item );
+ // take the current item
+ item = m_movixFiles.take();
+
+ // if after == 0 findRef returnes -1
+ int pos = m_movixFiles.findRef( itemAfter );
+ m_movixFiles.insert( pos+1, item );
+
+ emit newMovixFileItems();
+
+ setModified(true);
+}
+
+
+void K3bMovixDoc::addSubTitleItem( K3bMovixFileItem* item, const KURL& url )
+{
+ if( item->subTitleItem() )
+ removeSubTitleItem( item );
+
+ QFileInfo f( url.path() );
+ if( !f.isFile() || !url.isLocalFile() )
+ return;
+
+ // check if there already is a file named like we want to name the subTitle file
+ QString name = K3bMovixFileItem::subTitleFileName( item->k3bName() );
+
+ if( nameAlreadyInDir( name, root() ) ) {
+ KMessageBox::error( 0, i18n("Could not rename subtitle file. File with requested name %1 already exists.").arg(name) );
+ return;
+ }
+
+ K3bFileItem* subItem = new K3bFileItem( f.absFilePath(), this, root(), name );
+ item->setSubTitleItem( subItem );
+
+ emit newMovixFileItems();
+
+ setModified(true);
+}
+
+
+void K3bMovixDoc::removeSubTitleItem( K3bMovixFileItem* item )
+{
+ if( item->subTitleItem() ) {
+ emit subTitleItemRemoved( item );
+
+ delete item->subTitleItem();
+ item->setSubTitleItem(0);
+
+ setModified(true);
+ }
+}
+
+#include "k3bmovixdoc.moc"
diff --git a/libk3b/projects/movixcd/k3bmovixdoc.h b/libk3b/projects/movixcd/k3bmovixdoc.h
new file mode 100644
index 0000000..53debfc
--- /dev/null
+++ b/libk3b/projects/movixcd/k3bmovixdoc.h
@@ -0,0 +1,125 @@
+/*
+ *
+ * $Id: k3bmovixdoc.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_MOVIX_DOC_H_
+#define _K3B_MOVIX_DOC_H_
+
+
+#include <k3bdatadoc.h>
+
+#include <qptrlist.h>
+#include "k3b_export.h"
+//class K3bView;
+class KURL;
+class QDomElement;
+class K3bFileItem;
+class K3bMovixFileItem;
+class K3bDataItem;
+class KConfig;
+
+
+class LIBK3B_EXPORT K3bMovixDoc : public K3bDataDoc
+{
+ Q_OBJECT
+
+ public:
+ K3bMovixDoc( QObject* parent = 0 );
+ virtual ~K3bMovixDoc();
+
+ virtual int type() const { return MOVIX; }
+
+ virtual K3bBurnJob* newBurnJob( K3bJobHandler* hdl, QObject* parent );
+
+ bool newDocument();
+
+ const QPtrList<K3bMovixFileItem>& movixFileItems() const { return m_movixFiles; }
+
+ int indexOf( K3bMovixFileItem* );
+
+
+ bool shutdown() const { return m_shutdown; }
+ bool reboot() const { return m_reboot; }
+ bool ejectDisk() const { return m_ejectDisk; }
+ bool randomPlay() const { return m_randomPlay; }
+ const QString& subtitleFontset() const { return m_subtitleFontset; }
+ const QString& bootMessageLanguage() const { return m_bootMessageLanguage; }
+ const QString& audioBackground() const { return m_audioBackground; }
+ const QString& keyboardLayout() const { return m_keyboardLayout; }
+ const QStringList& codecs() const { return m_codecs; }
+ const QString& defaultBootLabel() const { return m_defaultBootLabel; }
+ const QString& additionalMPlayerOptions() const { return m_additionalMPlayerOptions; }
+ const QString& unwantedMPlayerOptions() const { return m_unwantedMPlayerOptions; }
+ int loopPlaylist() const { return m_loopPlaylist; }
+ bool noDma() const { return m_noDma; }
+
+ void setShutdown( bool v ) { m_shutdown = v; }
+ void setReboot( bool v ) { m_reboot = v; }
+ void setEjectDisk( bool v ) { m_ejectDisk = v; }
+ void setRandomPlay( bool v ) { m_randomPlay = v; }
+ void setSubtitleFontset( const QString& v ) { m_subtitleFontset = v; }
+ void setBootMessageLanguage( const QString& v ) { m_bootMessageLanguage = v; }
+ void setAudioBackground( const QString& b ) { m_audioBackground = b; }
+ void setKeyboardLayout( const QString& l ) { m_keyboardLayout = l; }
+ void setCodecs( const QStringList& c ) { m_codecs = c; }
+ void setDefaultBootLabel( const QString& v ) { m_defaultBootLabel = v; }
+ void setAdditionalMPlayerOptions( const QString& v ) { m_additionalMPlayerOptions = v; }
+ void setUnwantedMPlayerOptions( const QString& v ) { m_unwantedMPlayerOptions = v; }
+ void setLoopPlaylist( int v ) { m_loopPlaylist = v; }
+ void setNoDma( bool b ) { m_noDma = b; }
+
+ signals:
+ void newMovixFileItems();
+ void movixItemRemoved( K3bMovixFileItem* );
+ void subTitleItemRemoved( K3bMovixFileItem* );
+
+ public slots:
+ void addUrls( const KURL::List& urls );
+ void addMovixFile( const KURL& url, int pos = -1 );
+ void moveMovixItem( K3bMovixFileItem* item, K3bMovixFileItem* itemAfter );
+ void addSubTitleItem( K3bMovixFileItem*, const KURL& );
+ void removeSubTitleItem( K3bMovixFileItem* );
+
+ protected:
+ /** reimplemented from K3bDoc */
+ bool loadDocumentData( QDomElement* root );
+ /** reimplemented from K3bDoc */
+ bool saveDocumentData( QDomElement* );
+
+ virtual QString typeString() const { return "movix"; }
+
+ private slots:
+ void slotDataItemRemoved( K3bDataItem* );
+
+ private:
+ QPtrList<K3bMovixFileItem> m_movixFiles;
+
+ bool m_shutdown;
+ bool m_reboot;
+ bool m_ejectDisk;
+ bool m_randomPlay;
+ QString m_subtitleFontset;
+ QString m_bootMessageLanguage;
+ QString m_audioBackground;
+ QString m_keyboardLayout;
+ QStringList m_codecs;
+ QString m_defaultBootLabel;
+ QString m_additionalMPlayerOptions;
+ QString m_unwantedMPlayerOptions;
+ int m_loopPlaylist;
+ bool m_noDma;
+};
+
+#endif
diff --git a/libk3b/projects/movixcd/k3bmovixdocpreparer.cpp b/libk3b/projects/movixcd/k3bmovixdocpreparer.cpp
new file mode 100644
index 0000000..57e18af
--- /dev/null
+++ b/libk3b/projects/movixcd/k3bmovixdocpreparer.cpp
@@ -0,0 +1,490 @@
+/*
+ *
+ * $Id: k3bmovixdocpreparer.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bmovixdocpreparer.h"
+#include "k3bmovixdoc.h"
+#include "k3bmovixprogram.h"
+#include "k3bmovixfileitem.h"
+
+
+#include <k3bcore.h>
+#include <k3bdiritem.h>
+#include <k3bfileitem.h>
+#include <k3bbootitem.h>
+#include <k3bexternalbinmanager.h>
+#include <k3bisoimager.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <ktempfile.h>
+#include <kio/global.h>
+
+#include <qtextstream.h>
+#include <qdir.h>
+
+
+class K3bMovixDocPreparer::Private
+{
+public:
+ Private()
+ : doc(0),
+ playlistFile(0),
+ isolinuxConfigFile(0),
+ movixRcFile(0),
+ isolinuxDir(0),
+ movixDir(0),
+ mplayerDir(0),
+ playlistFileItem(0),
+ structuresCreated(false) {
+ }
+
+ K3bMovixDoc* doc;
+ const K3bMovixBin* eMovixBin;
+
+ KTempFile* playlistFile;
+ KTempFile* isolinuxConfigFile;
+ KTempFile* movixRcFile;
+
+ K3bDirItem* isolinuxDir;
+ K3bDirItem* movixDir;
+ K3bDirItem* mplayerDir;
+ K3bFileItem* playlistFileItem;
+
+ QPtrList<K3bDataItem> newMovixItems;
+
+ bool structuresCreated;
+};
+
+
+K3bMovixDocPreparer::K3bMovixDocPreparer( K3bMovixDoc* doc, K3bJobHandler* jh, QObject* parent, const char* name )
+ : K3bJob( jh, parent, name )
+{
+ d = new Private();
+ d->doc = doc;
+}
+
+
+K3bMovixDocPreparer::~K3bMovixDocPreparer()
+{
+ removeMovixStructures();
+ delete d;
+}
+
+
+K3bMovixDoc* K3bMovixDocPreparer::doc() const
+{
+ return d->doc;
+}
+
+
+void K3bMovixDocPreparer::start()
+{
+ kdDebug() << k_funcinfo << endl;
+ jobStarted();
+
+ bool success = true;
+ if( d->structuresCreated )
+ removeMovixStructures();
+ else
+ success = createMovixStructures();
+
+ jobFinished(success);
+}
+
+
+void K3bMovixDocPreparer::cancel()
+{
+ // do nothing...
+}
+
+
+bool K3bMovixDocPreparer::createMovixStructures()
+{
+ kdDebug() << k_funcinfo << endl;
+ removeMovixStructures();
+
+ if( doc() ) {
+ doc()->setMultiSessionMode( K3bDataDoc::NONE );
+ doc()->prepareFilenames();
+ }
+
+ d->eMovixBin = dynamic_cast<const K3bMovixBin*>( k3bcore->externalBinManager()->binObject("eMovix") );
+ if( d->eMovixBin ) {
+ bool success = false;
+ if( d->eMovixBin->version >= K3bVersion( 0, 9, 0 ) )
+ success = addMovixFilesNew();
+ else
+ success = addMovixFiles();
+
+ d->structuresCreated = success;
+ return success;
+ }
+ else {
+ emit infoMessage( i18n("Could not find a valid eMovix installation."), ERROR );
+ return false;
+ }
+}
+
+
+void K3bMovixDocPreparer::removeMovixStructures()
+{
+ kdDebug() << k_funcinfo << endl;
+ // remove movix files from doc
+ // the dataitems do the cleanup in the doc
+ delete d->movixDir;
+ delete d->isolinuxDir;
+ delete d->mplayerDir;
+ delete d->playlistFileItem;
+
+ d->movixDir = 0;
+ d->isolinuxDir = 0;
+ d->mplayerDir = 0;
+ d->playlistFileItem = 0;
+
+ d->newMovixItems.setAutoDelete( true );
+ d->newMovixItems.clear();
+
+ // remove all the temp files
+ delete d->playlistFile;
+ delete d->isolinuxConfigFile;
+ delete d->movixRcFile;
+
+ d->playlistFile = 0;
+ d->isolinuxConfigFile = 0;
+ d->movixRcFile = 0;
+
+ d->structuresCreated = false;
+}
+
+
+bool K3bMovixDocPreparer::writePlaylistFile()
+{
+ delete d->playlistFile;
+ d->playlistFile = new KTempFile();
+ d->playlistFile->setAutoDelete(true);
+
+ if( QTextStream* s = d->playlistFile->textStream() ) {
+
+ const QPtrList<K3bMovixFileItem>& movixFileItems = d->doc->movixFileItems();
+
+ for( QPtrListIterator<K3bMovixFileItem> it( movixFileItems );
+ *it; ++it ) {
+ *s << "/cdrom/";
+ *s << it.current()->writtenName();
+ *s << endl;
+ }
+
+ d->playlistFile->close();
+ return true;
+ }
+ else {
+ emit infoMessage( i18n("Could not write to temporary file %1").arg(d->playlistFile->name()), ERROR );
+ return false;
+ }
+}
+
+
+bool K3bMovixDocPreparer::writeIsolinuxConfigFile( const QString& originalPath )
+{
+ delete d->isolinuxConfigFile;
+ d->isolinuxConfigFile = new KTempFile();
+ d->isolinuxConfigFile->setAutoDelete(true);
+
+ if( QTextStream* s = d->isolinuxConfigFile->textStream() ) {
+
+ // now open the default isolinux.cfg and copy everything except the first line which contains
+ // the default boot label
+ QFile f( originalPath );
+ if( f.open( IO_ReadOnly ) ) {
+
+ QTextStream isolinuxConfigOrig( &f );
+
+ if( d->doc->defaultBootLabel() != i18n("default") ) {
+ isolinuxConfigOrig.readLine(); // skip first line
+ *s << "default " << d->doc->defaultBootLabel() << endl;
+ }
+
+ QString line = isolinuxConfigOrig.readLine();
+ while( !line.isNull() ) {
+ *s << line << endl;
+ line = isolinuxConfigOrig.readLine();
+ }
+
+ d->isolinuxConfigFile->close();
+ return true;
+ }
+ else
+ return false;
+ }
+ else {
+ emit infoMessage( i18n("Could not write to temporary file %1").arg(d->isolinuxConfigFile->name()), ERROR );
+ return false;
+ }
+}
+
+
+bool K3bMovixDocPreparer::writeMovixRcFile()
+{
+ delete d->movixRcFile;
+ d->movixRcFile = new KTempFile();
+ d->movixRcFile->setAutoDelete(true);
+
+ if( QTextStream* s = d->movixRcFile->textStream() ) {
+
+ if( !d->doc->additionalMPlayerOptions().isEmpty() )
+ *s << "extra-mplayer-options=" << d->doc->additionalMPlayerOptions() << endl;
+ if( !d->doc->unwantedMPlayerOptions().isEmpty() )
+ *s << "unwanted-mplayer-options=" << d->doc->unwantedMPlayerOptions() << endl;
+ *s << "loop=" << d->doc->loopPlaylist() << endl;
+ if( d->doc->shutdown() )
+ *s << "shut=y" << endl;
+ if( d->doc->reboot() )
+ *s << "reboot=y" << endl;
+ if( d->doc->ejectDisk() )
+ *s << "eject=y" << endl;
+ if( d->doc->randomPlay() )
+ *s << "random=y" << endl;
+ if( d->doc->noDma() )
+ *s << "dma=n" << endl;
+
+ d->movixRcFile->close();
+ return true;
+ }
+ else {
+ emit infoMessage( i18n("Could not write to temporary file %1").arg(d->movixRcFile->name()), ERROR );
+ return false;
+ }
+}
+
+
+bool K3bMovixDocPreparer::addMovixFiles()
+{
+ // first of all we create the directories
+ d->isolinuxDir = new K3bDirItem( "isolinux", d->doc, d->doc->root() );
+ d->movixDir = new K3bDirItem( "movix", d->doc, d->doc->root() );
+ K3bDirItem* kernelDir = d->doc->addEmptyDir( "kernel", d->isolinuxDir );
+
+ // add the linux kernel
+ (void)new K3bFileItem( d->eMovixBin->path + "/isolinux/kernel/vmlinuz", d->doc, kernelDir );
+
+ // add the boot image
+ K3bBootItem* bootItem = d->doc->createBootItem( d->eMovixBin->path + "/isolinux/isolinux.bin",
+ d->isolinuxDir );
+ bootItem->setImageType( K3bBootItem::NONE );
+ bootItem->setLoadSize( 4 );
+ bootItem->setBootInfoTable(true);
+
+ // some sort weights as defined in isolinux
+ d->isolinuxDir->setSortWeight( 100 );
+ kernelDir->setSortWeight( 50 );
+ bootItem->setSortWeight( 200 );
+
+ // rename the boot catalog file
+ d->doc->bootCataloge()->setK3bName( "isolinux.boot" );
+
+ // the following sucks! Redesign it!
+
+ // add all the isolinux files
+ QStringList isolinuxFiles = d->eMovixBin->isolinuxFiles();
+ isolinuxFiles.remove( "isolinux.bin" );
+ isolinuxFiles.remove( "isolinux.cfg" );
+ isolinuxFiles.remove( "kernel/vmlinuz" );
+ for( QStringList::const_iterator it = isolinuxFiles.begin();
+ it != isolinuxFiles.end(); ++it ) {
+ QString path = d->eMovixBin->path + "/isolinux/" + *it;
+ (void)new K3bFileItem( path, d->doc, d->isolinuxDir );
+ }
+
+ const QStringList& movixFiles = d->eMovixBin->movixFiles();
+ for( QStringList::const_iterator it = movixFiles.begin();
+ it != movixFiles.end(); ++it ) {
+ QString path = d->eMovixBin->path + "/movix/" + *it;
+ (void)new K3bFileItem( path, d->doc, d->movixDir );
+ }
+
+ // add doku files
+ QString path = d->eMovixBin->languageDir( d->doc->bootMessageLanguage() );
+ QDir dir(path);
+ QStringList helpFiles = dir.entryList(QDir::Files);
+ for( QStringList::const_iterator it = helpFiles.begin();
+ it != helpFiles.end(); ++it ) {
+ // some emovix installations include backup-files, no one's perfect ;)
+ if( !(*it).endsWith( "~" ) )
+ (void)new K3bFileItem( path + "/" + *it, d->doc, d->isolinuxDir );
+ }
+
+
+ // add subtitle font dir
+ if( !d->doc->subtitleFontset().isEmpty() &&
+ d->doc->subtitleFontset() != i18n("none") ) {
+ d->mplayerDir = new K3bDirItem( "mplayer", d->doc, d->doc->root() );
+
+ QString fontPath = d->eMovixBin->subtitleFontDir( d->doc->subtitleFontset() );
+ QFileInfo fontType( fontPath );
+ if( fontType.isDir() ) {
+ K3bDirItem* fontDir = new K3bDirItem( "font", d->doc, d->mplayerDir );
+ QDir dir( fontPath );
+ QStringList fontFiles = dir.entryList( QDir::Files );
+ for( QStringList::const_iterator it = fontFiles.begin();
+ it != fontFiles.end(); ++it ) {
+ (void)new K3bFileItem( fontPath + "/" + *it, d->doc, fontDir );
+ }
+ }
+ else {
+ // just a ttf file
+ // needs to be named: subfont.ttf and needs to be placed in mplayer/
+ // instead of mplayer/font
+ (void)new K3bFileItem( fontPath,
+ d->doc,
+ d->mplayerDir,
+ "subfont.ttf" );
+ }
+ }
+
+
+ // add movix-config-file and boot-config file
+ if( writeMovixRcFile() &&
+ writeIsolinuxConfigFile( d->eMovixBin->path + "/isolinux/isolinux.cfg" ) &&
+ writePlaylistFile() ) {
+
+ (void)new K3bFileItem( d->movixRcFile->name(), d->doc, d->movixDir, "movixrc" );
+ (void)new K3bFileItem( d->isolinuxConfigFile->name(), d->doc, d->isolinuxDir, "isolinux.cfg" );
+ d->playlistFileItem = new K3bFileItem( d->playlistFile->name(), d->doc, d->doc->root(), "movix.list" );
+
+ return true;
+ }
+ else
+ return false;
+}
+
+
+bool K3bMovixDocPreparer::addMovixFilesNew()
+{
+ // 1. get a list of files from the movixbin
+ // 2. create file items (replace isolinux.cfg with the one created above)
+ // 3. add movixrc and movix.list files
+ // 4. set weights for isolinux files
+
+ // FIXME: use the settings from the doc
+ QStringList files = d->eMovixBin->files( d->doc->keyboardLayout(),
+ d->doc->subtitleFontset(),
+ d->doc->audioBackground(),
+ d->doc->bootMessageLanguage(),
+ "all" /*d->doc->codecs()*/ ); // for now we simply don't allow selection
+
+ for( QStringList::iterator it = files.begin(); it != files.end(); ++it ) {
+ QString docPath = (*it).section( ' ', 0, 0 );
+ QString filePath = (*it).section( ' ', 1, 1 );
+ QString fileName = filePath.section( '/', -1 );
+
+ if( fileName == "isolinux.cfg" ) {
+ // replace the local file with our modified one
+ if( writeIsolinuxConfigFile( filePath ) )
+ createItem( d->isolinuxConfigFile->name(), docPath )->setK3bName( "isolinux.cfg" );
+ else
+ return false;
+ }
+ else if( fileName == "isolinux.bin" ) {
+ // create boot item (no need to remember this since it's in a dir which will be removed
+ // anyway)
+ K3bBootItem* bootItem = d->doc->createBootItem( filePath, createDir(docPath) );
+ bootItem->setImageType( K3bBootItem::NONE );
+ bootItem->setLoadSize( 4 );
+ bootItem->setBootInfoTable(true);
+
+ // set the proper sort weight
+ bootItem->setSortWeight( 200 );
+ bootItem->parent()->setSortWeight( 100 );
+ }
+ else if( fileName != "movixrc" ) { // we create our own movixrc
+ K3bFileItem* item = createItem( filePath, docPath );
+
+ // Truetype subtitle fonts needs to be named subfont.ttf
+ if( fileName == d->doc->subtitleFontset() + ".ttf" ) {
+ item->setK3bName( "subfont.ttf" );
+ }
+ else if( fileName == "vmlinuz" )
+ item->setSortWeight( 50 );
+ }
+ }
+
+ // Some distributions (such as Gentoo for example) do use the win32codecs package instead of the
+ // eMovix supplied codecs. These codecs are not picked up by the movix-conf script
+ K3bDirItem* codecDir = dynamic_cast<K3bDirItem*>( d->doc->root()->findByPath( "/eMoviX/codecs" ) );
+ if( !codecDir || codecDir->isEmpty() ) {
+ QDir localCodecDir( d->eMovixBin->movixDataDir() + "/codecs" );
+ if( localCodecDir.exists() ) {
+ QStringList codecFiles = localCodecDir.entryList( QDir::Files );
+ for( QStringList::const_iterator it = codecFiles.begin(); it != codecFiles.end(); ++it )
+ createItem( localCodecDir.path() + '/' + *it, "/eMoviX/codecs" );
+ }
+ }
+
+ if( writePlaylistFile() && writeMovixRcFile() ) {
+ // add the two items that are not listed by the script
+ createItem( d->movixRcFile->name(), "/eMoviX/movix" )->setK3bName( "movixrc" );
+ createItem( d->playlistFile->name(), "/" )->setK3bName( "movix.list" );
+ return true;
+ }
+ else
+ return false;
+}
+
+
+K3bFileItem* K3bMovixDocPreparer::createItem( const QString& localPath, const QString& docPath )
+{
+ // make sure the path in the doc exists
+ K3bDirItem* dir = createDir( docPath );
+
+ // create the file in dir
+ K3bFileItem* item = new K3bFileItem( localPath, d->doc, dir );
+
+ // remember the item to remove it becasue the dir cannot be removed
+ if( dir == d->doc->root() )
+ d->newMovixItems.append( item );
+
+ return item;
+}
+
+
+K3bDirItem* K3bMovixDocPreparer::createDir( const QString& docPath )
+{
+ QStringList docPathSections = QStringList::split( '/', docPath );
+ K3bDirItem* dir = d->doc->root();
+ for( QStringList::iterator it = docPathSections.begin(); it != docPathSections.end(); ++it ) {
+ K3bDataItem* next = dir->find( *it );
+ if( !next )
+ dir = new K3bDirItem( *it, d->doc, dir );
+ else if( next->isDir() )
+ dir = static_cast<K3bDirItem*>( next );
+ else {
+ kdError() << "(K3bMovixDocPreparer) found non-dir item where a dir was needed." << endl;
+ return 0;
+ }
+ }
+
+ // remember the dir to remove it
+ if( dir != d->doc->root() ) {
+ K3bDirItem* delDir = dir;
+ while( delDir->parent() != d->doc->root() )
+ delDir = delDir->parent();
+ if( d->newMovixItems.findRef( delDir ) == -1 )
+ d->newMovixItems.append( delDir );
+ }
+
+ return dir;
+}
+
+#include "k3bmovixdocpreparer.moc"
diff --git a/libk3b/projects/movixcd/k3bmovixdocpreparer.h b/libk3b/projects/movixcd/k3bmovixdocpreparer.h
new file mode 100644
index 0000000..3844eae
--- /dev/null
+++ b/libk3b/projects/movixcd/k3bmovixdocpreparer.h
@@ -0,0 +1,67 @@
+/*
+ *
+ * $Id: k3bmovixdocpreparer.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_MOVIX_DOC_PREPARER_H_
+#define _K3B_MOVIX_DOC_PREPARER_H_
+
+#include <k3bjob.h>
+
+class K3bMovixDoc;
+class K3bFileItem;
+class K3bDirItem;
+
+
+/**
+ * This class creates the needed eMovix structures in an eMovix doc
+ * and removes them after creating the image.
+ */
+class K3bMovixDocPreparer : public K3bJob
+{
+ Q_OBJECT
+
+ public:
+ explicit K3bMovixDocPreparer( K3bMovixDoc* doc, K3bJobHandler*, QObject* parent = 0, const char* name = 0 );
+ ~K3bMovixDocPreparer();
+
+ K3bMovixDoc* doc() const;
+
+ bool createMovixStructures();
+ void removeMovixStructures();
+
+ public slots:
+ /**
+ * use createMovixStructures and removeMovixStructures instead.
+ */
+ void start();
+
+ /**
+ * Useless since this job works syncronously
+ */
+ void cancel();
+
+ private:
+ bool writePlaylistFile();
+ bool writeIsolinuxConfigFile( const QString& );
+ bool writeMovixRcFile();
+ bool addMovixFiles();
+ bool addMovixFilesNew();
+ K3bFileItem* createItem( const QString& localPath, const QString& docPath );
+ K3bDirItem* createDir( const QString& docPath );
+
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/projects/movixcd/k3bmovixfileitem.cpp b/libk3b/projects/movixcd/k3bmovixfileitem.cpp
new file mode 100644
index 0000000..12803a2
--- /dev/null
+++ b/libk3b/projects/movixcd/k3bmovixfileitem.cpp
@@ -0,0 +1,68 @@
+/*
+ *
+ * $Id: k3bmovixfileitem.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bmovixfileitem.h"
+#include "k3bmovixdoc.h"
+
+#include <k3bdiritem.h>
+
+
+K3bMovixFileItem::K3bMovixFileItem( const QString& fileName,
+ K3bMovixDoc* doc,
+ K3bDirItem* dir,
+ const QString& k3bName )
+ : K3bFileItem( fileName, doc, dir, k3bName ),
+ m_doc(doc),
+ m_subTitleItem(0)
+{
+}
+
+
+K3bMovixFileItem::~K3bMovixFileItem()
+{
+ if( m_subTitleItem )
+ m_doc->removeSubTitleItem( this );
+
+ // remove this from parentdir
+ // it is important to do it here and not
+ // rely on the K3bFileItem destructor becasue
+ // otherwise the doc is not informed early enough
+ if( parent() )
+ parent()->takeDataItem( this );
+}
+
+
+void K3bMovixFileItem::setK3bName( const QString& newName )
+{
+ K3bFileItem::setK3bName( newName );
+
+ // take care of the subTitle file
+ if( m_subTitleItem ) {
+ m_subTitleItem->setK3bName( subTitleFileName(k3bName()) );
+ }
+}
+
+
+QString K3bMovixFileItem::subTitleFileName( const QString& name )
+{
+ // remove ending from k3bName
+ QString subName = name;
+ int pos = subName.findRev(".");
+ if( pos > 0 )
+ subName.truncate( pos );
+ subName += ".sub";
+ return subName;
+}
diff --git a/libk3b/projects/movixcd/k3bmovixfileitem.h b/libk3b/projects/movixcd/k3bmovixfileitem.h
new file mode 100644
index 0000000..343f00b
--- /dev/null
+++ b/libk3b/projects/movixcd/k3bmovixfileitem.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * $Id: k3bmovixfileitem.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_MOVIX_FILEITEM_H_
+#define _K3B_MOVIX_FILEITEM_H_
+
+#include <k3bfileitem.h>
+
+class K3bMovixDoc;
+
+
+class K3bMovixFileItem : public K3bFileItem
+{
+ public:
+ K3bMovixFileItem( const QString& fileName, K3bMovixDoc* doc, K3bDirItem* dir, const QString& k3bName = 0 );
+ ~K3bMovixFileItem();
+
+ K3bFileItem* subTitleItem() const { return m_subTitleItem; }
+ void setSubTitleItem( K3bFileItem* i ) { m_subTitleItem = i; }
+
+ /**
+ * reimplemented from K3bDataItem
+ * also renames the subTitleItem
+ */
+ void setK3bName( const QString& );
+
+ /**
+ * returnes the name that the subtitle file must have in
+ * order to work with mplayer
+ */
+ static QString subTitleFileName( const QString& );
+
+ private:
+ K3bMovixDoc* m_doc;
+
+ K3bFileItem* m_subTitleItem;
+};
+
+#endif
diff --git a/libk3b/projects/movixcd/k3bmovixjob.cpp b/libk3b/projects/movixcd/k3bmovixjob.cpp
new file mode 100644
index 0000000..2579453
--- /dev/null
+++ b/libk3b/projects/movixcd/k3bmovixjob.cpp
@@ -0,0 +1,132 @@
+/*
+ *
+ * $Id: k3bmovixjob.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bmovixjob.h"
+#include "k3bmovixdoc.h"
+#include "k3bmovixfileitem.h"
+#include "k3bmovixdocpreparer.h"
+
+#include <k3bcore.h>
+#include <k3bdatajob.h>
+#include <k3bdevice.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+
+
+K3bMovixJob::K3bMovixJob( K3bMovixDoc* doc, K3bJobHandler* jh, QObject* parent )
+ : K3bBurnJob( jh, parent ),
+ m_doc(doc)
+{
+ m_dataJob = new K3bDataJob( doc, this, this );
+ m_movixDocPreparer = new K3bMovixDocPreparer( doc, this, this );
+
+ // pipe signals
+ connect( m_dataJob, SIGNAL(percent(int)), this, SIGNAL(percent(int)) );
+ connect( m_dataJob, SIGNAL(subPercent(int)), this, SIGNAL(subPercent(int)) );
+ connect( m_dataJob, SIGNAL(processedSubSize(int, int)), this, SIGNAL(processedSubSize(int, int)) );
+ connect( m_dataJob, SIGNAL(processedSize(int, int)), this, SIGNAL(processedSize(int, int)) );
+ connect( m_dataJob, SIGNAL(bufferStatus(int)), this, SIGNAL(bufferStatus(int)) );
+ connect( m_dataJob, SIGNAL(deviceBuffer(int)), this, SIGNAL(deviceBuffer(int)) );
+ connect( m_dataJob, SIGNAL(writeSpeed(int, int)), this, SIGNAL(writeSpeed(int, int)) );
+ connect( m_dataJob, SIGNAL(newTask(const QString&)), this, SIGNAL(newTask(const QString&)) );
+ connect( m_dataJob, SIGNAL(newSubTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) );
+ connect( m_dataJob, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+ connect( m_dataJob, SIGNAL(infoMessage(const QString&, int)),
+ this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( m_dataJob, SIGNAL(burning(bool)), this, SIGNAL(burning(bool)) );
+
+ // we need to clean up here
+ connect( m_dataJob, SIGNAL(finished(bool)), this, SLOT(slotDataJobFinished(bool)) );
+
+ connect( m_movixDocPreparer, SIGNAL(infoMessage(const QString&, int)),
+ this, SIGNAL(infoMessage(const QString&, int)) );
+}
+
+
+K3bMovixJob::~K3bMovixJob()
+{
+}
+
+
+K3bDevice::Device* K3bMovixJob::writer() const
+{
+ return m_dataJob->writer();
+}
+
+
+K3bDoc* K3bMovixJob::doc() const
+{
+ return m_doc;
+}
+
+
+void K3bMovixJob::start()
+{
+ jobStarted();
+
+ m_canceled = false;
+ m_dataJob->setWritingApp( writingApp() );
+
+ if( m_movixDocPreparer->createMovixStructures() ) {
+ m_dataJob->start();
+ }
+ else {
+ m_movixDocPreparer->removeMovixStructures();
+ jobFinished(false);
+ }
+}
+
+
+void K3bMovixJob::cancel()
+{
+ m_canceled = true;
+ m_dataJob->cancel();
+}
+
+
+void K3bMovixJob::slotDataJobFinished( bool success )
+{
+ m_movixDocPreparer->removeMovixStructures();
+
+ if( m_canceled || m_dataJob->hasBeenCanceled() )
+ emit canceled();
+
+ jobFinished( success );
+}
+
+
+QString K3bMovixJob::jobDescription() const
+{
+ if( m_doc->isoOptions().volumeID().isEmpty() )
+ return i18n("Writing eMovix CD");
+ else
+ return i18n("Writing eMovix CD (%1)").arg(m_doc->isoOptions().volumeID());
+}
+
+
+QString K3bMovixJob::jobDetails() const
+{
+ return ( i18n("1 file (%1) and about 8 MB eMovix data",
+ "%n files (%1) and about 8 MB eMovix data",
+ m_doc->movixFileItems().count()).arg(KIO::convertSize(m_doc->size()))
+ + ( m_doc->copies() > 1
+ ? i18n(" - %n copy", " - %n copies", m_doc->copies())
+ : QString::null ) );
+}
+
+#include "k3bmovixjob.moc"
diff --git a/libk3b/projects/movixcd/k3bmovixjob.h b/libk3b/projects/movixcd/k3bmovixjob.h
new file mode 100644
index 0000000..81dea8e
--- /dev/null
+++ b/libk3b/projects/movixcd/k3bmovixjob.h
@@ -0,0 +1,60 @@
+/*
+ *
+ * $Id: k3bmovixjob.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_MOVIX_JOB_H_
+#define _K3B_MOVIX_JOB_H_
+
+#include <k3bjob.h>
+
+class K3bMovixDoc;
+class K3bDevice::Device;
+class K3bDataJob;
+class KTempFile;
+class K3bMovixInstallation;
+class K3bMovixDocPreparer;
+class K3bDirItem;
+class K3bFileItem;
+
+class K3bMovixJob : public K3bBurnJob
+{
+ Q_OBJECT
+
+ public:
+ K3bMovixJob( K3bMovixDoc* doc, K3bJobHandler*, QObject* parent = 0 );
+ ~K3bMovixJob();
+
+ K3bDoc* doc() const;
+ K3bDevice::Device* writer() const;
+
+ QString jobDescription() const;
+ QString jobDetails() const;
+
+ public slots:
+ void start();
+ void cancel();
+
+ private slots:
+ void slotDataJobFinished( bool );
+
+ private:
+ K3bMovixDoc* m_doc;
+ K3bDataJob* m_dataJob;
+ K3bMovixDocPreparer* m_movixDocPreparer;
+
+ bool m_canceled;
+};
+
+#endif
diff --git a/libk3b/projects/movixcd/k3bmovixprogram.cpp b/libk3b/projects/movixcd/k3bmovixprogram.cpp
new file mode 100644
index 0000000..8720e8d
--- /dev/null
+++ b/libk3b/projects/movixcd/k3bmovixprogram.cpp
@@ -0,0 +1,339 @@
+/*
+ *
+ * $Id: k3bmovixprogram.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bmovixprogram.h"
+
+#include <k3bprocess.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <qdir.h>
+#include <qfile.h>
+#include <qtextstream.h>
+
+
+K3bMovixProgram::K3bMovixProgram()
+ : K3bExternalProgram( "eMovix" )
+{
+}
+
+bool K3bMovixProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ QString path = p;
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+
+ // first test if we have a version info (eMovix >= 0.8.0pre3)
+ if( !QFile::exists( path + "movix-version" ) )
+ return false;
+
+ K3bMovixBin* bin = 0;
+
+ //
+ // probe version and data dir
+ //
+ KProcess vp, dp;
+ vp << path + "movix-version";
+ dp << path + "movix-conf";
+ K3bProcessOutputCollector vout( &vp ), dout( &dp );
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) && dp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ // movix-version just gives us the version number on stdout
+ if( !vout.output().isEmpty() && !dout.output().isEmpty() ) {
+ bin = new K3bMovixBin( this );
+ bin->version = vout.output().stripWhiteSpace();
+ bin->path = path;
+ bin->m_movixPath = dout.output().stripWhiteSpace();
+ }
+ }
+ else {
+ kdDebug() << "(K3bMovixProgram) could not start " << path << "movix-version" << endl;
+ return false;
+ }
+
+ if( bin->version >= K3bVersion( 0, 9, 0 ) )
+ return scanNewEMovix( bin, path );
+ else
+ return scanOldEMovix( bin, path );
+}
+
+
+bool K3bMovixProgram::scanNewEMovix( K3bMovixBin* bin, const QString& path )
+{
+ QStringList files = bin->files();
+ for( QStringList::iterator it = files.begin();
+ it != files.end(); ++it ) {
+ if( (*it).contains( "isolinux.cfg" ) ) {
+ bin->m_supportedBootLabels = determineSupportedBootLabels( QStringList::split( " ", *it )[1] );
+ break;
+ }
+ }
+
+ // here we simply check for the movix-conf program
+ if( QFile::exists( path + "movix-conf" ) ) {
+ bin->addFeature( "newfiles" );
+ addBin(bin);
+ return true;
+ }
+ else {
+ delete bin;
+ return false;
+ }
+}
+
+
+bool K3bMovixProgram::scanOldEMovix( K3bMovixBin* bin, const QString& path )
+{
+ //
+ // first check if all necessary directories are present
+ //
+ QDir dir( bin->movixDataDir() );
+ QStringList subdirs = dir.entryList( QDir::Dirs );
+ if( !subdirs.contains( "boot-messages" ) ) {
+ kdDebug() << "(K3bMovixProgram) could not find subdir 'boot-messages'" << endl;
+ delete bin;
+ return false;
+ }
+ if( !subdirs.contains( "isolinux" ) ) {
+ kdDebug() << "(K3bMovixProgram) could not find subdir 'isolinux'" << endl;
+ delete bin;
+ return false;
+ }
+ if( !subdirs.contains( "movix" ) ) {
+ kdDebug() << "(K3bMovixProgram) could not find subdir 'movix'" << endl;
+ delete bin;
+ return false;
+ }
+ if( !subdirs.contains( "mplayer-fonts" ) ) {
+ kdDebug() << "(K3bMovixProgram) could not find subdir 'mplayer-fonts'" << endl;
+ delete bin;
+ return false;
+ }
+
+
+ //
+ // check if we have a version of eMovix which contains the movix-files script
+ //
+ if( QFile::exists( path + "movix-files" ) ) {
+ bin->addFeature( "files" );
+
+ KProcess p;
+ K3bProcessOutputCollector out( &p );
+ p << bin->path + "movix-files";
+ if( p.start( KProcess::Block, KProcess::AllOutput ) ) {
+ bin->m_movixFiles = QStringList::split( "\n", out.output() );
+ }
+ }
+
+ //
+ // fallback: to be compatible with 0.8.0rc2 we just add all files in the movix directory
+ //
+ if( bin->m_movixFiles.isEmpty() ) {
+ QDir dir( bin->movixDataDir() + "/movix" );
+ bin->m_movixFiles = dir.entryList(QDir::Files);
+ }
+
+ //
+ // these files are fixed. That should not be a problem
+ // since Isolinux is quite stable as far as I know.
+ //
+ bin->m_isolinuxFiles.append( "initrd.gz" );
+ bin->m_isolinuxFiles.append( "isolinux.bin" );
+ bin->m_isolinuxFiles.append( "isolinux.cfg" );
+ bin->m_isolinuxFiles.append( "kernel/vmlinuz" );
+ bin->m_isolinuxFiles.append( "movix.lss" );
+ bin->m_isolinuxFiles.append( "movix.msg" );
+
+
+ //
+ // check every single necessary file :(
+ //
+ for( QStringList::const_iterator it = bin->m_isolinuxFiles.begin();
+ it != bin->m_isolinuxFiles.end(); ++it ) {
+ if( !QFile::exists( bin->movixDataDir() + "/isolinux/" + *it ) ) {
+ kdDebug() << "(K3bMovixProgram) Could not find file " << *it << endl;
+ delete bin;
+ return false;
+ }
+ }
+
+ //
+ // now check the boot-messages languages
+ //
+ dir.cd( "boot-messages" );
+ bin->m_supportedLanguages = dir.entryList(QDir::Dirs);
+ bin->m_supportedLanguages.remove(".");
+ bin->m_supportedLanguages.remove("..");
+ bin->m_supportedLanguages.remove("CVS"); // the eMovix makefile stuff seems not perfect ;)
+ bin->m_supportedLanguages.prepend( i18n("default") );
+ dir.cdUp();
+
+ //
+ // now check the supported mplayer-fontsets
+ // FIXME: every font dir needs to contain the "font.desc" file!
+ //
+ dir.cd( "mplayer-fonts" );
+ bin->m_supportedSubtitleFonts = dir.entryList( QDir::Dirs );
+ bin->m_supportedSubtitleFonts.remove(".");
+ bin->m_supportedSubtitleFonts.remove("..");
+ bin->m_supportedSubtitleFonts.remove("CVS"); // the eMovix makefile stuff seems not perfect ;)
+ // new ttf fonts in 0.8.0rc2
+ bin->m_supportedSubtitleFonts += dir.entryList( "*.ttf", QDir::Files );
+ bin->m_supportedSubtitleFonts.prepend( i18n("none") );
+ dir.cdUp();
+
+ //
+ // now check the supported boot labels
+ //
+ dir.cd( "isolinux" );
+ bin->m_supportedBootLabels = determineSupportedBootLabels( dir.filePath("isolinux.cfg") );
+
+ //
+ // This seems to be a valid eMovix installation. :)
+ //
+
+ addBin(bin);
+ return true;
+}
+
+
+QStringList K3bMovixProgram::determineSupportedBootLabels( const QString& isoConfigFile ) const
+{
+ QStringList list( i18n("default") );
+
+ QFile f( isoConfigFile );
+ if( !f.open( IO_ReadOnly ) ) {
+ kdDebug() << "(K3bMovixProgram) could not open file '" << f.name() << "'" << endl;
+ }
+ else {
+ QTextStream fs( &f );
+ QString line = fs.readLine();
+ while( !line.isNull() ) {
+ if( line.startsWith( "label" ) )
+ list.append( line.mid( 5 ).stripWhiteSpace() );
+
+ line = fs.readLine();
+ }
+ f.close();
+ }
+
+ return list;
+}
+
+
+QString K3bMovixBin::subtitleFontDir( const QString& font ) const
+{
+ if( font == i18n("none" ) )
+ return "";
+ else if( m_supportedSubtitleFonts.contains( font ) )
+ return path + "/mplayer-fonts/" + font;
+ else
+ return "";
+}
+
+
+QString K3bMovixBin::languageDir( const QString& lang ) const
+{
+ if( lang == i18n("default") )
+ return languageDir( "en" );
+ else if( m_supportedLanguages.contains( lang ) )
+ return path + "/boot-messages/" + lang;
+ else
+ return "";
+}
+
+
+QStringList K3bMovixBin::supportedSubtitleFonts() const
+{
+ if( version >= K3bVersion( 0, 9, 0 ) )
+ return QStringList( i18n("default") ) += supported( "font" );
+ else
+ return m_supportedSubtitleFonts;
+}
+
+
+QStringList K3bMovixBin::supportedLanguages() const
+{
+ if( version >= K3bVersion( 0, 9, 0 ) )
+ return QStringList( i18n("default") ) += supported( "lang" );
+ else
+ return m_supportedLanguages;
+}
+
+
+// only used for eMovix >= 0.9.0
+QStringList K3bMovixBin::supportedKbdLayouts() const
+{
+ return QStringList( i18n("default") ) += supported( "kbd" );
+}
+
+
+// only used for eMovix >= 0.9.0
+QStringList K3bMovixBin::supportedBackgrounds() const
+{
+ return QStringList( i18n("default") ) += supported( "background" );
+}
+
+
+// only used for eMovix >= 0.9.0
+QStringList K3bMovixBin::supportedCodecs() const
+{
+ return supported( "codecs" );
+}
+
+
+QStringList K3bMovixBin::supported( const QString& type ) const
+{
+ KProcess p;
+ K3bProcessOutputCollector out( &p );
+ p << path + "movix-conf" << "--supported=" + type;
+ if( p.start( KProcess::Block, KProcess::AllOutput ) )
+ return QStringList::split( "\n", out.output() );
+ else
+ return QStringList();
+}
+
+
+QStringList K3bMovixBin::files( const QString& kbd,
+ const QString& font,
+ const QString& bg,
+ const QString& lang,
+ const QStringList& codecs ) const
+{
+ KProcess p;
+ K3bProcessOutputCollector out( &p );
+ p << path + "movix-conf" << "--files";
+
+
+ if( !kbd.isEmpty() && kbd != i18n("default") )
+ p << "--kbd" << kbd;
+ if( !font.isEmpty() && font != i18n("default") )
+ p << "--font" << font;
+ if( !bg.isEmpty() && bg != i18n("default") )
+ p << "--background" << bg;
+ if( !lang.isEmpty() && lang != i18n("default") )
+ p << "--lang" << lang;
+ if( !codecs.isEmpty() )
+ p << "--codecs" << codecs.join( "," );
+
+ if( p.start( KProcess::Block, KProcess::AllOutput ) )
+ return QStringList::split( "\n", out.output() );
+ else
+ return QStringList();
+}
diff --git a/libk3b/projects/movixcd/k3bmovixprogram.h b/libk3b/projects/movixcd/k3bmovixprogram.h
new file mode 100644
index 0000000..a6a9ac0
--- /dev/null
+++ b/libk3b/projects/movixcd/k3bmovixprogram.h
@@ -0,0 +1,103 @@
+/*
+ *
+ * $Id: k3bmovixprogram.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_MOVIX_PROGRAM_H_
+#define _K3B_MOVIX_PROGRAM_H_
+
+#include <k3bexternalbinmanager.h>
+#include "k3b_export.h"
+
+class LIBK3B_EXPORT K3bMovixBin : public K3bExternalBin
+{
+ public:
+ K3bMovixBin( K3bExternalProgram* p )
+ : K3bExternalBin( p ) {
+ }
+
+ const QString& movixDataDir() const { return m_movixPath; }
+
+ const QStringList& supportedBootLabels() const { return m_supportedBootLabels; }
+ QStringList supportedSubtitleFonts() const;
+ QStringList supportedLanguages() const;
+ QStringList supportedKbdLayouts() const;
+ QStringList supportedBackgrounds() const;
+ QStringList supportedCodecs() const;
+
+ /*
+ * Unused for eMovix versions 0.9.0 and above
+ */
+ const QStringList& movixFiles() const { return m_movixFiles; }
+
+ /*
+ * Unused for eMovix versions 0.9.0 and above
+ */
+ const QStringList& isolinuxFiles() const { return m_isolinuxFiles; }
+
+ /**
+ * returnes empty string if font was not found
+ *
+ * Unused for eMovix versions 0.9.0 and above
+ */
+ QString subtitleFontDir( const QString& font ) const;
+
+ /**
+ * returnes empty string if lang was not found
+ *
+ * Unused for eMovix versions 0.9.0 and above
+ */
+ QString languageDir( const QString& lang ) const;
+
+ /**
+ * Interface for the movix-conf --files interface for
+ * versions >= 0.9.0
+ */
+ QStringList files( const QString& kbd = QString::null,
+ const QString& font = QString::null,
+ const QString& bg = QString::null,
+ const QString& lang = QString::null,
+ const QStringList& codecs = QStringList() ) const;
+
+ private:
+ QStringList supported( const QString& ) const;
+
+ QString m_movixPath;
+ QStringList m_movixFiles;
+ QStringList m_isolinuxFiles;
+ QStringList m_supportedBootLabels;
+ QStringList m_supportedSubtitleFonts;
+ QStringList m_supportedLanguages;
+
+ friend class K3bMovixProgram;
+};
+
+
+class LIBK3B_EXPORT K3bMovixProgram : public K3bExternalProgram
+{
+ public:
+ K3bMovixProgram();
+
+ bool scan( const QString& );
+
+ bool supportsUserParameters() const { return false; }
+
+ private:
+ bool scanNewEMovix( K3bMovixBin* bin, const QString& );
+ bool scanOldEMovix( K3bMovixBin* bin, const QString& );
+ QStringList determineSupportedBootLabels( const QString& ) const;
+};
+
+
+
+#endif
diff --git a/libk3b/projects/movixdvd/Makefile.am b/libk3b/projects/movixdvd/Makefile.am
new file mode 100644
index 0000000..7af7283
--- /dev/null
+++ b/libk3b/projects/movixdvd/Makefile.am
@@ -0,0 +1,21 @@
+# we need the ../datacd and ../movixcd for the uic generated header files
+AM_CPPFLAGS= -I$(srcdir)/../../core \
+ -I$(srcdir)/../../../libk3bdevice \
+ -I$(srcdir)/../../../src \
+ -I$(srcdir)/../../tools \
+ -I$(srcdir)/../datadvd \
+ -I$(srcdir)/../movixcd \
+ -I$(srcdir)/../datacd \
+ -I$(srcdir)/.. \
+ -I../datacd \
+ -I../movixcd \
+ $(all_includes)
+
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libmovixdvd.la
+
+libmovixdvd_la_SOURCES = k3bmovixdvddoc.cpp k3bmovixdvdjob.cpp
+
+include_HEADERS = k3bmovixdvddoc.h \
+ k3bmovixdvdjob.h
diff --git a/libk3b/projects/movixdvd/k3bmovixdvddoc.cpp b/libk3b/projects/movixdvd/k3bmovixdvddoc.cpp
new file mode 100644
index 0000000..80b8ec2
--- /dev/null
+++ b/libk3b/projects/movixdvd/k3bmovixdvddoc.cpp
@@ -0,0 +1,36 @@
+/*
+ *
+ * $Id: k3bmovixdvddoc.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bmovixdvddoc.h"
+#include "k3bmovixdvdjob.h"
+
+#include <kconfig.h>
+
+
+K3bMovixDvdDoc::K3bMovixDvdDoc( QObject* parent )
+ : K3bMovixDoc( parent )
+{
+}
+
+K3bMovixDvdDoc::~K3bMovixDvdDoc()
+{
+}
+
+K3bBurnJob* K3bMovixDvdDoc::newBurnJob( K3bJobHandler* hdl, QObject* parent )
+{
+ return new K3bMovixDvdJob( this, hdl, parent );
+}
+
+#include "k3bmovixdvddoc.moc"
diff --git a/libk3b/projects/movixdvd/k3bmovixdvddoc.h b/libk3b/projects/movixdvd/k3bmovixdvddoc.h
new file mode 100644
index 0000000..85943f0
--- /dev/null
+++ b/libk3b/projects/movixdvd/k3bmovixdvddoc.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * $Id: k3bmovixdvddoc.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_MOVIX_DVD_DOC_H_
+#define _K3B_MOVIX_DVD_DOC_H_
+
+#include <k3bmovixdoc.h>
+#include "k3b_export.h"
+class KConfig;
+
+
+class LIBK3B_EXPORT K3bMovixDvdDoc : public K3bMovixDoc
+{
+ Q_OBJECT
+
+ public:
+ K3bMovixDvdDoc( QObject* parent = 0 );
+ ~K3bMovixDvdDoc();
+
+ int type() const { return MOVIX_DVD; }
+
+ K3bBurnJob* newBurnJob( K3bJobHandler* hdl, QObject* parent );
+
+ protected:
+ QString typeString() const { return "movixdvd"; }
+};
+
+#endif
diff --git a/libk3b/projects/movixdvd/k3bmovixdvdjob.cpp b/libk3b/projects/movixdvd/k3bmovixdvdjob.cpp
new file mode 100644
index 0000000..b556997
--- /dev/null
+++ b/libk3b/projects/movixdvd/k3bmovixdvdjob.cpp
@@ -0,0 +1,131 @@
+/*
+ *
+ * $Id: k3bmovixdvdjob.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bmovixdvdjob.h"
+#include "k3bmovixdvddoc.h"
+#include "k3bmovixfileitem.h"
+#include "k3bmovixdocpreparer.h"
+
+#include <k3bcore.h>
+#include <k3bdvdjob.h>
+#include <k3bdevice.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+
+
+K3bMovixDvdJob::K3bMovixDvdJob( K3bMovixDvdDoc* doc, K3bJobHandler* jh, QObject* parent )
+ : K3bBurnJob( jh, parent ),
+ m_doc(doc)
+{
+ m_dvdJob = new K3bDvdJob( doc, this, this );
+ m_movixDocPreparer = new K3bMovixDocPreparer( doc, this, this );
+
+ // pipe signals
+ connect( m_dvdJob, SIGNAL(percent(int)), this, SIGNAL(percent(int)) );
+ connect( m_dvdJob, SIGNAL(subPercent(int)), this, SIGNAL(subPercent(int)) );
+ connect( m_dvdJob, SIGNAL(processedSubSize(int, int)), this, SIGNAL(processedSubSize(int, int)) );
+ connect( m_dvdJob, SIGNAL(processedSize(int, int)), this, SIGNAL(processedSize(int, int)) );
+ connect( m_dvdJob, SIGNAL(bufferStatus(int)), this, SIGNAL(bufferStatus(int)) );
+ connect( m_dvdJob, SIGNAL(writeSpeed(int, int)), this, SIGNAL(writeSpeed(int, int)) );
+ connect( m_dvdJob, SIGNAL(newTask(const QString&)), this, SIGNAL(newTask(const QString&)) );
+ connect( m_dvdJob, SIGNAL(newSubTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) );
+ connect( m_dvdJob, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+ connect( m_dvdJob, SIGNAL(infoMessage(const QString&, int)),
+ this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( m_dvdJob, SIGNAL(burning(bool)), this, SIGNAL(burning(bool)) );
+
+ // we need to clean up here
+ connect( m_dvdJob, SIGNAL(finished(bool)), this, SLOT(slotDvdJobFinished(bool)) );
+
+ connect( m_movixDocPreparer, SIGNAL(infoMessage(const QString&, int)),
+ this, SIGNAL(infoMessage(const QString&, int)) );
+}
+
+
+K3bMovixDvdJob::~K3bMovixDvdJob()
+{
+}
+
+
+K3bDevice::Device* K3bMovixDvdJob::writer() const
+{
+ return m_dvdJob->writer();
+}
+
+
+K3bDoc* K3bMovixDvdJob::doc() const
+{
+ return m_doc;
+}
+
+
+void K3bMovixDvdJob::start()
+{
+ jobStarted();
+
+ m_canceled = false;
+ m_dvdJob->setWritingApp( writingApp() );
+
+ if( m_movixDocPreparer->createMovixStructures() ) {
+ m_dvdJob->start();
+ }
+ else {
+ m_movixDocPreparer->removeMovixStructures();
+ jobFinished(false);
+ }
+}
+
+
+void K3bMovixDvdJob::cancel()
+{
+ m_canceled = true;
+ m_dvdJob->cancel();
+}
+
+
+void K3bMovixDvdJob::slotDvdJobFinished( bool success )
+{
+ m_movixDocPreparer->removeMovixStructures();
+
+ if( m_canceled || m_dvdJob->hasBeenCanceled() )
+ emit canceled();
+
+ jobFinished( success );
+}
+
+
+QString K3bMovixDvdJob::jobDescription() const
+{
+ if( m_doc->isoOptions().volumeID().isEmpty() )
+ return i18n("Writing eMovix DVD");
+ else
+ return i18n("Writing eMovix DVD (%1)").arg(m_doc->isoOptions().volumeID());
+}
+
+
+QString K3bMovixDvdJob::jobDetails() const
+{
+ return ( i18n("1 file (%1) and about 8 MB eMovix data",
+ "%n files (%1) and about 8 MB eMovix data",
+ m_doc->movixFileItems().count()).arg(KIO::convertSize(m_doc->size()))
+ + ( m_doc->copies() > 1
+ ? i18n(" - %n copy", " - %n copies", m_doc->copies())
+ : QString::null ) );
+}
+
+#include "k3bmovixdvdjob.moc"
diff --git a/libk3b/projects/movixdvd/k3bmovixdvdjob.h b/libk3b/projects/movixdvd/k3bmovixdvdjob.h
new file mode 100644
index 0000000..2b9ce10
--- /dev/null
+++ b/libk3b/projects/movixdvd/k3bmovixdvdjob.h
@@ -0,0 +1,60 @@
+/*
+ *
+ * $Id: k3bmovixdvdjob.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_MOVIX_DVD_JOB_H_
+#define _K3B_MOVIX_DVD_JOB_H_
+
+#include <k3bjob.h>
+
+class K3bMovixDvdDoc;
+class K3bDevice::Device;
+class K3bDvdJob;
+class KTempFile;
+class K3bMovixInstallation;
+class K3bMovixDocPreparer;
+class K3bDirItem;
+class K3bFileItem;
+
+class K3bMovixDvdJob : public K3bBurnJob
+{
+ Q_OBJECT
+
+ public:
+ K3bMovixDvdJob( K3bMovixDvdDoc* doc, K3bJobHandler*, QObject* parent = 0 );
+ ~K3bMovixDvdJob();
+
+ K3bDoc* doc() const;
+ K3bDevice::Device* writer() const;
+
+ QString jobDescription() const;
+ QString jobDetails() const;
+
+ public slots:
+ void start();
+ void cancel();
+
+ private slots:
+ void slotDvdJobFinished( bool );
+
+ private:
+ K3bMovixDvdDoc* m_doc;
+ K3bDvdJob* m_dvdJob;
+ K3bMovixDocPreparer* m_movixDocPreparer;
+
+ bool m_canceled;
+};
+
+#endif
diff --git a/libk3b/projects/videocd/Makefile.am b/libk3b/projects/videocd/Makefile.am
new file mode 100644
index 0000000..1e18d02
--- /dev/null
+++ b/libk3b/projects/videocd/Makefile.am
@@ -0,0 +1,20 @@
+AM_CPPFLAGS= -I$(srcdir)/../../core \
+ -I$(srcdir)/../../../src \
+ -I$(srcdir)/../../../libk3bdevice \
+ -I$(srcdir)/../../tools \
+ -I$(srcdir)/.. \
+ $(all_includes)
+
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libvcd.la
+
+libvcd_la_SOURCES = k3bvcddoc.cpp k3bvcdtrack.cpp k3bvcdjob.cpp k3bvcdoptions.cpp k3bvcdxmlview.cpp
+
+libvcd_la_LIBADD = mpeginfo/libmpeginfo.la
+
+SUBDIRS = cdi extra mpeginfo
+
+include_HEADERS = k3bvcdjob.h \
+ k3bvcddoc.h \
+ k3bvcdoptions.h
diff --git a/libk3b/projects/videocd/cdi/Makefile.am b/libk3b/projects/videocd/cdi/Makefile.am
new file mode 100644
index 0000000..e487acc
--- /dev/null
+++ b/libk3b/projects/videocd/cdi/Makefile.am
@@ -0,0 +1,5 @@
+
+cdidir = $(kde_datadir)/k3b/cdi
+cdi_DATA = cdi_imag.rtf cdi_text.fnt cdi_vcd.app cdi_vcd.cfg vcd_on_cdi_41.pdf icdia.htm
+
+EXTRA_DIST = $(cdi_DATA)
diff --git a/libk3b/projects/videocd/cdi/cdi_imag.rtf b/libk3b/projects/videocd/cdi/cdi_imag.rtf
new file mode 100644
index 0000000..809145f
--- /dev/null
+++ b/libk3b/projects/videocd/cdi/cdi_imag.rtf
Binary files differ
diff --git a/libk3b/projects/videocd/cdi/cdi_text.fnt b/libk3b/projects/videocd/cdi/cdi_text.fnt
new file mode 100644
index 0000000..0dd0e15
--- /dev/null
+++ b/libk3b/projects/videocd/cdi/cdi_text.fnt
Binary files differ
diff --git a/libk3b/projects/videocd/cdi/cdi_vcd.app b/libk3b/projects/videocd/cdi/cdi_vcd.app
new file mode 100644
index 0000000..ceb31fc
--- /dev/null
+++ b/libk3b/projects/videocd/cdi/cdi_vcd.app
Binary files differ
diff --git a/libk3b/projects/videocd/cdi/cdi_vcd.cfg b/libk3b/projects/videocd/cdi/cdi_vcd.cfg
new file mode 100644
index 0000000..4aed0eb
--- /dev/null
+++ b/libk3b/projects/videocd/cdi/cdi_vcd.cfg
@@ -0,0 +1,12 @@
+CONTROLS=ALL
+CURCOL=YELLOW
+PSDCURCOL=RED
+PSDCURSHAPE=ARROW
+CENTRTRACK=2
+AUTOPLAY=AUTO_ON
+DUALCHAN=DUAL_ON
+TIMECODE_X=64
+TIMECODE_Y=100
+LOTID_X=64
+LOTID_Y=64
+ALBUM=STANDARD \ No newline at end of file
diff --git a/libk3b/projects/videocd/cdi/icdia.htm b/libk3b/projects/videocd/cdi/icdia.htm
new file mode 100644
index 0000000..cd6c47b
--- /dev/null
+++ b/libk3b/projects/videocd/cdi/icdia.htm
@@ -0,0 +1,12 @@
+<HTML>
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+<META HTTP-EQUIV="Refresh" CONTENT="0; URL=http://www.icdia.org/">
+<TITLE>The New International CD-i Association</TITLE>
+</HEAD>
+<BODY>
+
+<A HREF="http://www.icdia.org">The New International CD-i Association - http://www.icdia.org</A>
+
+</BODY>
+</HTML>
diff --git a/libk3b/projects/videocd/cdi/vcd_on_cdi_41.pdf b/libk3b/projects/videocd/cdi/vcd_on_cdi_41.pdf
new file mode 100644
index 0000000..cdf4fed
--- /dev/null
+++ b/libk3b/projects/videocd/cdi/vcd_on_cdi_41.pdf
Binary files differ
diff --git a/libk3b/projects/videocd/extra/Makefile.am b/libk3b/projects/videocd/extra/Makefile.am
new file mode 100644
index 0000000..717fa99
--- /dev/null
+++ b/libk3b/projects/videocd/extra/Makefile.am
@@ -0,0 +1,5 @@
+
+extradir = $(kde_datadir)/k3b/extra
+extra_DATA = k3bphotovcd.mpg k3bphotosvcd.mpg
+
+EXTRA_DIST = $(extra_DATA)
diff --git a/libk3b/projects/videocd/extra/k3bphotosvcd.mpg b/libk3b/projects/videocd/extra/k3bphotosvcd.mpg
new file mode 100644
index 0000000..50156d7
--- /dev/null
+++ b/libk3b/projects/videocd/extra/k3bphotosvcd.mpg
Binary files differ
diff --git a/libk3b/projects/videocd/extra/k3bphotovcd.mpg b/libk3b/projects/videocd/extra/k3bphotovcd.mpg
new file mode 100644
index 0000000..2ddb69e
--- /dev/null
+++ b/libk3b/projects/videocd/extra/k3bphotovcd.mpg
Binary files differ
diff --git a/libk3b/projects/videocd/k3bvcddoc.cpp b/libk3b/projects/videocd/k3bvcddoc.cpp
new file mode 100644
index 0000000..462aea3
--- /dev/null
+++ b/libk3b/projects/videocd/k3bvcddoc.cpp
@@ -0,0 +1,894 @@
+/*
+*
+* $Id: k3bvcddoc.cpp 619556 2007-01-03 17:38:12Z trueg $
+* Copyright (C) 2003-2005 Christian Kvasny <chris@k3b.org>
+*
+* This file is part of the K3b project.
+* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+* See the file "COPYING" for the exact licensing terms.
+*/
+
+// QT-includes
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qfile.h>
+#include <qdatastream.h>
+#include <qdom.h>
+#include <qdatetime.h>
+#include <qtimer.h>
+#include <qtextstream.h>
+
+// KDE-includes
+#include <kprocess.h>
+#include <kurl.h>
+#include <kapplication.h>
+#include <kmessagebox.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kio/global.h>
+#include <kdebug.h>
+#include <kstdguiitem.h>
+
+// K3b-includes
+#include "k3bvcddoc.h"
+#include "k3bvcdtrack.h"
+#include "k3bvcdjob.h"
+#include <k3bglobals.h>
+#include <k3bmsf.h>
+
+
+bool desperate_mode = false;
+bool preserve_header = false;
+bool print_progress = true;
+bool aspect_correction = false;
+byte forced_sequence_header = 0;
+
+K3bVcdDoc::K3bVcdDoc( QObject* parent )
+ : K3bDoc( parent )
+{
+ m_tracks = 0L;
+ m_vcdOptions = new K3bVcdOptions();
+
+ m_docType = VCD;
+ m_vcdType = NONE;
+
+ m_urlAddingTimer = new QTimer( this );
+ connect( m_urlAddingTimer, SIGNAL( timeout() ), this, SLOT( slotWorkUrlQueue() ) );
+
+ // FIXME: remove the newTracks() signal and replace it with the changed signal
+ connect( this, SIGNAL( newTracks() ), this, SIGNAL( changed() ) );
+ connect( this, SIGNAL( trackRemoved( K3bVcdTrack* ) ), this, SIGNAL( changed() ) );
+}
+
+K3bVcdDoc::~K3bVcdDoc()
+{
+ if ( m_tracks ) {
+ m_tracks->setAutoDelete( true );
+ delete m_tracks;
+ }
+
+ delete m_vcdOptions;
+}
+
+bool K3bVcdDoc::newDocument()
+{
+ if ( m_tracks )
+ while ( m_tracks->first() )
+ removeTrack( m_tracks->first() );
+ else
+ m_tracks = new QPtrList<K3bVcdTrack>;
+ m_tracks->setAutoDelete( false );
+
+ return K3bDoc::newDocument();
+}
+
+
+QString K3bVcdDoc::name() const
+{
+ return m_vcdOptions->volumeId();
+}
+
+
+KIO::filesize_t K3bVcdDoc::calcTotalSize() const
+{
+ unsigned long long sum = 0;
+ if ( m_tracks ) {
+ for ( K3bVcdTrack * track = m_tracks->first(); track; track = m_tracks->next() ) {
+ sum += track->size();
+ }
+ }
+ return sum;
+}
+
+KIO::filesize_t K3bVcdDoc::size() const
+{
+ // mode2 -> mode1 int(( n+2047 ) / 2048) * 2352
+ // mode1 -> mode2 int(( n+2351 ) / 2352) * 2048
+ long tracksize = long( ( calcTotalSize() + 2351 ) / 2352 ) * 2048;
+ return tracksize + ISOsize();
+}
+
+KIO::filesize_t K3bVcdDoc::ISOsize() const
+{
+ // 136000b for vcd iso reseved
+ long long iso_size = 136000;
+ if ( vcdOptions() ->CdiSupport() ) {
+ iso_size += vcdOptions() ->CDIsize();
+ }
+
+ return iso_size;
+}
+
+K3b::Msf K3bVcdDoc::length() const
+{
+ return K3b::Msf( size() / 2048 );
+}
+
+
+bool K3bVcdDoc::isImage( const KURL& url )
+{
+ QImage p;
+ return p.load( QFile::encodeName( url.path() ) );
+}
+
+void K3bVcdDoc::addUrls( const KURL::List& urls )
+{
+ // make sure we add them at the end even if urls are in the queue
+ addTracks( urls, 99 );
+}
+
+void K3bVcdDoc::addTracks( const KURL::List& urls, uint position )
+{
+ KURL::List::ConstIterator end( urls.end() );
+ for ( KURL::List::ConstIterator it = urls.begin(); it != end; ++it ) {
+ urlsToAdd.enqueue( new PrivateUrlToAdd( K3b::convertToLocalUrl(*it), position++ ) );
+ }
+
+ m_urlAddingTimer->start( 0 );
+}
+
+void K3bVcdDoc::slotWorkUrlQueue()
+{
+ if ( !urlsToAdd.isEmpty() ) {
+ PrivateUrlToAdd * item = urlsToAdd.dequeue();
+ lastAddedPosition = item->position;
+
+ // append at the end by default
+ if ( lastAddedPosition > m_tracks->count() )
+ lastAddedPosition = m_tracks->count();
+
+ if ( !item->url.isLocalFile() ) {
+ kdDebug() << item->url.path() << " no local file" << endl;
+ return ;
+ }
+
+ if ( !QFile::exists( item->url.path() ) ) {
+ kdDebug() << "(K3bVcdDoc) file not found: " << item->url.path() << endl;
+ m_notFoundFiles.append( item->url.path() );
+ return ;
+ }
+
+ if ( K3bVcdTrack * newTrack = createTrack( item->url ) )
+ addTrack( newTrack, lastAddedPosition );
+
+ delete item;
+
+ emit newTracks();
+ } else {
+ m_urlAddingTimer->stop();
+
+ emit newTracks();
+
+ // reorder pbc tracks
+ setPbcTracks();
+
+ informAboutNotFoundFiles();
+ }
+}
+
+K3bVcdTrack* K3bVcdDoc::createTrack( const KURL& url )
+{
+ char filename[ 255 ];
+ QString error_string = "";
+ strcpy( filename, QFile::encodeName( url.path() ) );
+ K3bMpegInfo* Mpeg = new K3bMpegInfo( filename );
+
+ if ( Mpeg ) {
+ int mpegVersion = Mpeg->version();
+ if ( mpegVersion > 0 ) {
+
+ if ( vcdType() == NONE && mpegVersion < 2 ) {
+ m_urlAddingTimer->stop();
+ setVcdType( vcdTypes( mpegVersion ) );
+ vcdOptions() ->setMpegVersion( mpegVersion );
+ KMessageBox::information( kapp->mainWidget(),
+ i18n( "K3b will create a %1 image from the given MPEG "
+ "files, but these files must already be in %2 "
+ "format. K3b does not yet resample MPEG files." )
+ .arg( i18n( "VCD" ) )
+ .arg( i18n( "VCD" ) ),
+ i18n( "Information" ) );
+ m_urlAddingTimer->start( 0 );
+ } else if ( vcdType() == NONE ) {
+ m_urlAddingTimer->stop();
+ vcdOptions() ->setMpegVersion( mpegVersion );
+ bool force = false;
+ force = ( KMessageBox::questionYesNo( kapp->mainWidget(),
+ i18n( "K3b will create a %1 image from the given MPEG "
+ "files, but these files must already be in %2 "
+ "format. K3b does not yet resample MPEG files." )
+ .arg( i18n( "SVCD" ) )
+ .arg( i18n( "SVCD" ) )
+ + "\n\n"
+ + i18n( "Note: Forcing MPEG2 as VCD is not supported by "
+ "some standalone DVD players." ),
+ i18n( "Information" ),
+ KStdGuiItem::ok().text(),
+ i18n( "Forcing VCD" ) ) == KMessageBox::No );
+ if ( force ) {
+ setVcdType( vcdTypes( 1 ) );
+ vcdOptions() ->setAutoDetect( false );
+ } else
+ setVcdType( vcdTypes( mpegVersion ) );
+
+ m_urlAddingTimer->start( 0 );
+ }
+
+
+ if ( numOfTracks() > 0 && vcdOptions() ->mpegVersion() != mpegVersion ) {
+ KMessageBox::error( kapp->mainWidget(), "(" + url.path() + ")\n" +
+ i18n( "You cannot mix MPEG1 and MPEG2 video files.\nPlease start a new Project for this filetype.\nResample not implemented in K3b yet." ),
+ i18n( "Wrong File Type for This Project" ) );
+
+ delete Mpeg;
+ return 0;
+ }
+
+ K3bVcdTrack* newTrack = new K3bVcdTrack( m_tracks, url.path() );
+ *( newTrack->mpeg_info ) = *( Mpeg->mpeg_info );
+
+ if ( newTrack->isSegment() && !vcdOptions()->PbcEnabled() ) {
+ KMessageBox::information( kapp->mainWidget(),
+ i18n( "PBC (Playback control) enabled.\n"
+ "Videoplayers can not reach Segments (Mpeg Still Pictures) without Playback control ." ) ,
+ i18n( "Information" ) );
+
+ vcdOptions()->setPbcEnabled( true );
+ }
+
+ // set defaults;
+ newTrack->setPlayTime( vcdOptions() ->PbcPlayTime() );
+ newTrack->setWaitTime( vcdOptions() ->PbcWaitTime() );
+ newTrack->setPbcNumKeys( vcdOptions() ->PbcNumkeysEnabled() );
+ delete Mpeg;
+
+ // debugging output
+ newTrack->PrintInfo();
+
+ return newTrack;
+ }
+ } else if ( isImage( url ) ) { // image track
+ // woking on ...
+ // for future use
+ // photoalbum starts here
+ // return here the new photoalbum track
+ }
+
+ if ( Mpeg ) {
+ error_string = Mpeg->error_string();
+ delete Mpeg;
+ }
+
+ // error (unsupported files)
+ KMessageBox::error( kapp->mainWidget(), "(" + url.path() + ")\n" +
+ i18n( "Only MPEG1 and MPEG2 video files are supported.\n" ) + error_string ,
+ i18n( "Wrong File Format" ) );
+
+
+ return 0;
+}
+
+void K3bVcdDoc::addTrack( const KURL& url, uint position )
+{
+ urlsToAdd.enqueue( new PrivateUrlToAdd( url, position ) );
+
+ m_urlAddingTimer->start( 0 );
+}
+
+
+void K3bVcdDoc::addTrack( K3bVcdTrack* track, uint position )
+{
+ if ( m_tracks->count() >= 98 ) {
+ kdDebug() << "(K3bVcdDoc) VCD Green Book only allows 98 tracks." << endl;
+ // TODO: show some messagebox
+ delete track;
+ return ;
+ }
+
+ lastAddedPosition = position;
+
+ if ( !m_tracks->insert( position, track ) ) {
+ lastAddedPosition = m_tracks->count();
+ m_tracks->insert( m_tracks->count(), track );
+ }
+
+ if ( track->isSegment() )
+ vcdOptions() ->increaseSegments( );
+ else
+ vcdOptions() ->increaseSequence( );
+
+ emit newTracks();
+
+ setModified( true );
+}
+
+
+void K3bVcdDoc::removeTrack( K3bVcdTrack* track )
+{
+ if ( !track ) {
+ return ;
+ }
+
+ // set the current item to track
+ if ( m_tracks->findRef( track ) >= 0 ) {
+ // take the current item
+ track = m_tracks->take();
+
+ // remove all pbc references to us?
+ if ( track->hasRevRef() )
+ track->delRefToUs();
+
+ // remove all pbc references from us?
+ track->delRefFromUs();
+
+ // emit signal before deleting the track to avoid crashes
+ // when the view tries to call some of the tracks' methods
+ emit trackRemoved( track );
+
+ if ( track->isSegment() )
+ vcdOptions() ->decreaseSegments( );
+ else
+ vcdOptions() ->decreaseSequence( );
+
+ delete track;
+
+ if ( numOfTracks() == 0 ) {
+ setVcdType( NONE );
+ vcdOptions() ->setAutoDetect( true );
+ }
+
+ // reorder pbc tracks
+ setPbcTracks();
+ }
+}
+
+void K3bVcdDoc::moveTrack( const K3bVcdTrack* track, const K3bVcdTrack* after )
+{
+ if ( track == after )
+ return ;
+
+ // set the current item to track
+ m_tracks->findRef( track );
+ // take the current item
+ track = m_tracks->take();
+
+ // if after == 0 findRef returnes -1
+ int pos = m_tracks->findRef( after );
+ m_tracks->insert( pos + 1, track );
+
+ // reorder pbc tracks
+ setPbcTracks();
+
+ emit changed();
+}
+
+
+QString K3bVcdDoc::typeString() const
+{
+ return "vcd";
+}
+
+
+K3bBurnJob* K3bVcdDoc::newBurnJob( K3bJobHandler* hdl, QObject* parent )
+{
+ return new K3bVcdJob( this, hdl, parent );
+}
+
+void K3bVcdDoc::informAboutNotFoundFiles()
+{
+ if ( !m_notFoundFiles.isEmpty() ) {
+ KMessageBox::informationList( view(), i18n( "Could not find the following files:" ),
+ m_notFoundFiles, i18n( "Not Found" ) );
+
+ m_notFoundFiles.clear();
+ }
+}
+
+void K3bVcdDoc::setVcdType( int type )
+{
+ m_vcdType = type;
+ switch ( type ) {
+ case 0:
+ //vcd 1.1
+ vcdOptions() ->setVcdClass( "vcd" );
+ vcdOptions() ->setVcdVersion( "1.1" );
+ break;
+ case 1:
+ //vcd 2.0
+ vcdOptions() ->setVcdClass( "vcd" );
+ vcdOptions() ->setVcdVersion( "2.0" );
+ break;
+ case 2:
+ //svcd 1.0
+ vcdOptions() ->setVcdClass( "svcd" );
+ vcdOptions() ->setVcdVersion( "1.0" );
+ break;
+ case 3:
+ //hqvcd 1.0
+ vcdOptions() ->setVcdClass( "hqvcd" );
+ vcdOptions() ->setVcdVersion( "1.0" );
+ break;
+
+ }
+}
+
+void K3bVcdDoc::setPbcTracks()
+{
+ // reorder pbc tracks
+ /*
+ if ( !vcdOptions()->PbcEnabled() )
+ return;
+ */
+
+ if ( m_tracks ) {
+ int count = m_tracks->count();
+ kdDebug() << QString( "K3bVcdDoc::setPbcTracks() - we have %1 tracks in list." ).arg( count ) << endl;
+
+ QPtrListIterator<K3bVcdTrack> iterTrack( *m_tracks );
+ K3bVcdTrack* track;
+ while ( ( track = iterTrack.current() ) != 0 ) {
+ ++iterTrack;
+ for ( int i = 0; i < K3bVcdTrack::_maxPbcTracks; i++ ) {
+ // do not change userdefined tracks
+ if ( !track->isPbcUserDefined( i ) ) {
+ if ( track->getPbcTrack( i ) )
+ track->getPbcTrack( i ) ->delFromRevRefList( track );
+
+ K3bVcdTrack* t = 0L;
+ int index = track->index();
+
+ // we are the last track
+ if ( index == count - 1 ) {
+ switch ( i ) {
+ case K3bVcdTrack::PREVIOUS:
+ // we are not alone :)
+ if ( count > 1 ) {
+ t = at( index - 1 );
+ t->addToRevRefList( track );
+ track->setPbcTrack( i, t );
+ } else {
+ track->setPbcTrack( i );
+ track->setPbcNonTrack( i, K3bVcdTrack::VIDEOEND );
+ }
+ break;
+ case K3bVcdTrack::AFTERTIMEOUT:
+ case K3bVcdTrack::NEXT:
+ track->setPbcTrack( i );
+ track->setPbcNonTrack( i, K3bVcdTrack::VIDEOEND );
+ break;
+ case K3bVcdTrack::RETURN:
+ track->setPbcTrack( i );
+ track->setPbcNonTrack( i, K3bVcdTrack::VIDEOEND );
+ break;
+ case K3bVcdTrack::DEFAULT:
+ track->setPbcTrack( i );
+ track->setPbcNonTrack( i, K3bVcdTrack::DISABLED );
+ break;
+ }
+ }
+ // we are the first track
+ else if ( index == 0 ) {
+ switch ( i ) {
+ case K3bVcdTrack::PREVIOUS:
+ track->setPbcTrack( i );
+ track->setPbcNonTrack( i, K3bVcdTrack::VIDEOEND );
+ break;
+ case K3bVcdTrack::AFTERTIMEOUT:
+ case K3bVcdTrack::NEXT:
+ t = at( index + 1 );
+ t->addToRevRefList( track );
+ track->setPbcTrack( i, t );
+ break;
+ case K3bVcdTrack::RETURN:
+ track->setPbcTrack( i );
+ track->setPbcNonTrack( i, K3bVcdTrack::VIDEOEND );
+ break;
+ case K3bVcdTrack::DEFAULT:
+ track->setPbcTrack( i );
+ track->setPbcNonTrack( i, K3bVcdTrack::DISABLED );
+ break;
+ }
+ }
+ // we are one of the other tracks and have PREVIOUS and NEXT Track
+ else {
+ switch ( i ) {
+ case K3bVcdTrack::PREVIOUS:
+ t = at( index - 1 );
+ t->addToRevRefList( track );
+ track->setPbcTrack( i, t );
+ break;
+ case K3bVcdTrack::AFTERTIMEOUT:
+ case K3bVcdTrack::NEXT:
+ t = at( index + 1 );
+ t->addToRevRefList( track );
+ track->setPbcTrack( i, t );
+ break;
+ case K3bVcdTrack::RETURN:
+ track->setPbcTrack( i );
+ track->setPbcNonTrack( i, K3bVcdTrack::VIDEOEND );
+ break;
+ case K3bVcdTrack::DEFAULT:
+ track->setPbcTrack( i );
+ track->setPbcNonTrack( i, K3bVcdTrack::DISABLED );
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+bool K3bVcdDoc::loadDocumentData( QDomElement* root )
+{
+ newDocument();
+
+ QDomNodeList nodes = root->childNodes();
+
+ if ( nodes.length() < 3 )
+ return false;
+
+ if ( nodes.item( 0 ).nodeName() != "general" )
+ return false;
+ if ( !readGeneralDocumentData( nodes.item( 0 ).toElement() ) )
+ return false;
+
+ if ( nodes.item( 1 ).nodeName() != "vcd" )
+ return false;
+
+ if ( nodes.item( 2 ).nodeName() != "contents" )
+ return false;
+
+
+ // vcd Label
+ QDomNodeList vcdNodes = nodes.item( 1 ).childNodes();
+
+ for ( uint i = 0; i < vcdNodes.count(); i++ ) {
+ QDomNode item = vcdNodes.item( i );
+ QString name = item.nodeName();
+
+ kdDebug() << QString( "(K3bVcdDoc::loadDocumentData) nodeName = '%1'" ).arg( name ) << endl;
+
+ if ( name == "volumeId" )
+ vcdOptions() ->setVolumeId( item.toElement().text() );
+ else if ( name == "albumId" )
+ vcdOptions() ->setAlbumId( item.toElement().text() );
+ else if ( name == "volumeSetId" )
+ vcdOptions() ->setVolumeSetId( item.toElement().text() );
+ else if ( name == "preparer" )
+ vcdOptions() ->setPreparer( item.toElement().text() );
+ else if ( name == "publisher" )
+ vcdOptions() ->setPublisher( item.toElement().text() );
+ else if ( name == "vcdType" )
+ setVcdType( vcdTypes( item.toElement().text().toInt() ) );
+ else if ( name == "mpegVersion" )
+ vcdOptions() ->setMpegVersion( item.toElement().text().toInt() );
+ else if ( name == "PreGapLeadout" )
+ vcdOptions() ->setPreGapLeadout( item.toElement().text().toInt() );
+ else if ( name == "PreGapTrack" )
+ vcdOptions() ->setPreGapTrack( item.toElement().text().toInt() );
+ else if ( name == "FrontMarginTrack" )
+ vcdOptions() ->setFrontMarginTrack( item.toElement().text().toInt() );
+ else if ( name == "RearMarginTrack" )
+ vcdOptions() ->setRearMarginTrack( item.toElement().text().toInt() );
+ else if ( name == "FrontMarginTrackSVCD" )
+ vcdOptions() ->setFrontMarginTrackSVCD( item.toElement().text().toInt() );
+ else if ( name == "RearMarginTrackSVCD" )
+ vcdOptions() ->setRearMarginTrackSVCD( item.toElement().text().toInt() );
+ else if ( name == "volumeCount" )
+ vcdOptions() ->setVolumeCount( item.toElement().text().toInt() );
+ else if ( name == "volumeNumber" )
+ vcdOptions() ->setVolumeNumber( item.toElement().text().toInt() );
+ else if ( name == "AutoDetect" )
+ vcdOptions() ->setAutoDetect( item.toElement().text().toInt() );
+ else if ( name == "CdiSupport" )
+ vcdOptions() ->setCdiSupport( item.toElement().text().toInt() );
+ else if ( name == "NonCompliantMode" )
+ vcdOptions() ->setNonCompliantMode( item.toElement().text().toInt() );
+ else if ( name == "Sector2336" )
+ vcdOptions() ->setSector2336( item.toElement().text().toInt() );
+ else if ( name == "UpdateScanOffsets" )
+ vcdOptions() ->setUpdateScanOffsets( item.toElement().text().toInt() );
+ else if ( name == "RelaxedAps" )
+ vcdOptions() ->setRelaxedAps( item.toElement().text().toInt() );
+ else if ( name == "UseGaps" )
+ vcdOptions() ->setUseGaps( item.toElement().text().toInt() );
+ else if ( name == "PbcEnabled" )
+ vcdOptions() ->setPbcEnabled( item.toElement().text().toInt() );
+ else if ( name == "SegmentFolder" )
+ vcdOptions() ->setSegmentFolder( item.toElement().text().toInt() );
+ else if ( name == "Restriction" )
+ vcdOptions() ->setRestriction( item.toElement().text().toInt() );
+ }
+
+ // vcd Tracks
+ QDomNodeList trackNodes = nodes.item( 2 ).childNodes();
+
+ for ( uint i = 0; i < trackNodes.length(); i++ ) {
+
+ // check if url is available
+ QDomElement trackElem = trackNodes.item( i ).toElement();
+ QString url = trackElem.attributeNode( "url" ).value();
+ if ( !QFile::exists( url ) )
+ m_notFoundFiles.append( url );
+ else {
+ KURL k;
+ k.setPath( url );
+ if ( K3bVcdTrack * track = createTrack( k ) ) {
+ track ->setPlayTime( trackElem.attribute( "playtime", "1" ).toInt() );
+ track ->setWaitTime( trackElem.attribute( "waittime", "2" ).toInt() );
+ track ->setReactivity( trackElem.attribute( "reactivity", "0" ).toInt() );
+ track -> setPbcNumKeys( ( trackElem.attribute( "numkeys", "yes" ).contains( "yes" ) ) ? true : false );
+ track -> setPbcNumKeysUserdefined( ( trackElem.attribute( "userdefinednumkeys", "no" ).contains( "yes" ) ) ? true : false );
+
+ addTrack( track, m_tracks->count() );
+ }
+ }
+ }
+
+ emit newTracks();
+
+ // do not add saved pbcTrack links when one ore more files missing.
+ // TODO: add info message to informAboutNotFoundFiles();
+ if ( m_notFoundFiles.isEmpty() ) {
+ int type;
+ int val;
+ bool pbctrack;
+ for ( uint trackId = 0; trackId < trackNodes.length(); trackId++ ) {
+ QDomElement trackElem = trackNodes.item( trackId ).toElement();
+ QDomNodeList trackNodes = trackElem.childNodes();
+ for ( uint i = 0; i < trackNodes.length(); i++ ) {
+ QDomElement trackElem = trackNodes.item( i ).toElement();
+ QString name = trackElem.tagName();
+ if ( name.contains( "pbc" ) ) {
+ if ( trackElem.hasAttribute ( "type" ) ) {
+ type = trackElem.attribute ( "type" ).toInt();
+ if ( trackElem.hasAttribute ( "pbctrack" ) ) {
+ pbctrack = ( trackElem.attribute ( "pbctrack" ) == "yes" );
+ if ( trackElem.hasAttribute ( "val" ) ) {
+ val = trackElem.attribute ( "val" ).toInt();
+ K3bVcdTrack* track = m_tracks->at( trackId );
+ K3bVcdTrack* pbcTrack = m_tracks->at( val );
+ if ( pbctrack ) {
+ pbcTrack->addToRevRefList( track );
+ track->setPbcTrack( type, pbcTrack );
+ track->setUserDefined( type, true );
+ } else {
+ track->setPbcTrack( type );
+ track->setPbcNonTrack( type, val );
+ track->setUserDefined( type, true );
+ }
+ }
+ }
+ }
+ } else if ( name.contains( "numkeys" ) ) {
+ if ( trackElem.hasAttribute ( "key" ) ) {
+ int key = trackElem.attribute ( "key" ).toInt();
+ if ( trackElem.hasAttribute ( "val" ) ) {
+ int val = trackElem.attribute ( "val" ).toInt() - 1;
+ K3bVcdTrack* track = m_tracks->at( trackId );
+ if ( val >= 0 ) {
+ K3bVcdTrack * numkeyTrack = m_tracks->at( val );
+ track->setDefinedNumKey( key, numkeyTrack );
+ } else {
+ track->setDefinedNumKey( key, 0L );
+ }
+ }
+ }
+ }
+
+ }
+
+ }
+ setPbcTracks();
+ setModified( false );
+ }
+
+ informAboutNotFoundFiles();
+ return true;
+}
+
+
+
+bool K3bVcdDoc::saveDocumentData( QDomElement * docElem )
+{
+ QDomDocument doc = docElem->ownerDocument();
+ saveGeneralDocumentData( docElem );
+
+ // save Vcd Label
+ QDomElement vcdMain = doc.createElement( "vcd" );
+
+ QDomElement vcdElem = doc.createElement( "volumeId" );
+ vcdElem.appendChild( doc.createTextNode( vcdOptions() ->volumeId() ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "albumId" );
+ vcdElem.appendChild( doc.createTextNode( vcdOptions() ->albumId() ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "volumeSetId" );
+ vcdElem.appendChild( doc.createTextNode( vcdOptions() ->volumeSetId() ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "preparer" );
+ vcdElem.appendChild( doc.createTextNode( vcdOptions() ->preparer() ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "publisher" );
+ vcdElem.appendChild( doc.createTextNode( vcdOptions() ->publisher() ) );
+ vcdMain.appendChild( vcdElem );
+
+ // applicationId()
+ // systemId()
+
+ vcdElem = doc.createElement( "vcdType" );
+ vcdElem.appendChild( doc.createTextNode( QString::number( vcdType() ) ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "mpegVersion" );
+ vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->mpegVersion() ) ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "PreGapLeadout" );
+ vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->PreGapLeadout() ) ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "PreGapTrack" );
+ vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->PreGapTrack() ) ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "FrontMarginTrack" );
+ vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->FrontMarginTrack() ) ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "RearMarginTrack" );
+ vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->RearMarginTrack() ) ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "FrontMarginTrackSVCD" );
+ vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->FrontMarginTrackSVCD() ) ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "RearMarginTrackSVCD" );
+ vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->RearMarginTrackSVCD() ) ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "volumeCount" );
+ vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->volumeCount() ) ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "volumeNumber" );
+ vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->volumeNumber() ) ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "AutoDetect" );
+ vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->AutoDetect() ) ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "CdiSupport" );
+ vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->CdiSupport() ) ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "NonCompliantMode" );
+ vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->NonCompliantMode() ) ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "Sector2336" );
+ vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->Sector2336() ) ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "UpdateScanOffsets" );
+ vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->UpdateScanOffsets() ) ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "RelaxedAps" );
+ vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->RelaxedAps() ) ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "UseGaps" );
+ vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->UseGaps() ) ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "PbcEnabled" );
+ vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->PbcEnabled() ) ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "SegmentFolder" );
+ vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->SegmentFolder() ) ) );
+ vcdMain.appendChild( vcdElem );
+
+ vcdElem = doc.createElement( "Restriction" );
+ vcdElem.appendChild( doc.createTextNode( QString::number( vcdOptions() ->Restriction() ) ) );
+ vcdMain.appendChild( vcdElem );
+
+ docElem->appendChild( vcdMain );
+
+ // save the tracks
+ // -------------------------------------------------------------
+ QDomElement contentsElem = doc.createElement( "contents" );
+
+ QPtrListIterator<K3bVcdTrack> iterTrack( *m_tracks );
+ K3bVcdTrack* track;
+
+ while ( ( track = iterTrack.current() ) != 0 ) {
+ ++iterTrack;
+
+ QDomElement trackElem = doc.createElement( "track" );
+ trackElem.setAttribute( "url", KIO::decodeFileName( track->absPath() ) );
+ trackElem.setAttribute( "playtime", track->getPlayTime() );
+ trackElem.setAttribute( "waittime", track->getWaitTime() );
+ trackElem.setAttribute( "reactivity", track->Reactivity() );
+ trackElem.setAttribute( "numkeys", ( track->PbcNumKeys() ) ? "yes" : "no" );
+ trackElem.setAttribute( "userdefinednumkeys", ( track->PbcNumKeysUserdefined() ) ? "yes" : "no" );
+
+ for ( int i = 0;
+ i < K3bVcdTrack::_maxPbcTracks;
+ i++ ) {
+ if ( track->isPbcUserDefined( i ) ) {
+ // save pbcTracks
+ QDomElement pbcElem = doc.createElement( "pbc" );
+ pbcElem.setAttribute( "type", i );
+ if ( track->getPbcTrack( i ) ) {
+ pbcElem.setAttribute( "pbctrack", "yes" );
+ pbcElem.setAttribute( "val", track->getPbcTrack( i ) ->index() );
+ } else {
+ pbcElem.setAttribute( "pbctrack", "no" );
+ pbcElem.setAttribute( "val", track->getNonPbcTrack( i ) );
+ }
+ trackElem.appendChild( pbcElem );
+ }
+ }
+ QMap<int, K3bVcdTrack*> numKeyMap = track->DefinedNumKey();
+ QMap<int, K3bVcdTrack*>::const_iterator trackIt;
+
+ for ( trackIt = numKeyMap.begin();
+ trackIt != numKeyMap.end();
+ ++trackIt ) {
+ QDomElement numElem = doc.createElement( "numkeys" );
+ if ( trackIt.data() ) {
+ numElem.setAttribute( "key", trackIt.key() );
+ numElem.setAttribute( "val", trackIt.data() ->index() + 1 );
+ } else {
+ numElem.setAttribute( "key", trackIt.key() );
+ numElem.setAttribute( "val", 0 );
+ }
+ trackElem.appendChild( numElem );
+ }
+
+ contentsElem.appendChild( trackElem );
+ }
+ // -------------------------------------------------------------
+
+ docElem->appendChild( contentsElem );
+
+ return true;
+}
+
+#include "k3bvcddoc.moc"
diff --git a/libk3b/projects/videocd/k3bvcddoc.h b/libk3b/projects/videocd/k3bvcddoc.h
new file mode 100644
index 0000000..8b10837
--- /dev/null
+++ b/libk3b/projects/videocd/k3bvcddoc.h
@@ -0,0 +1,192 @@
+/*
+*
+* $Id: k3bvcddoc.h 619556 2007-01-03 17:38:12Z trueg $
+* Copyright (C) 2003-2004 Christian Kvasny <chris@k3b.org>
+*
+* This file is part of the K3b project.
+* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+* See the file "COPYING" for the exact licensing terms.
+*/
+
+#ifndef K3BVCDDOC_H
+#define K3BVCDDOC_H
+
+// Qt Includes
+#include <qptrqueue.h>
+#include <qfile.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qdatetime.h>
+#include <qtextstream.h>
+#include <qimage.h>
+
+// Kde Includes
+#include <kurl.h>
+
+// K3b Includes
+#include "k3bvcdoptions.h"
+#include "mpeginfo/k3bmpeginfo.h"
+#include <k3bdoc.h>
+#include "k3b_export.h"
+class K3bApp;
+class K3bVcdTrack;
+class K3bVcdJob;
+//class K3bView;
+class QWidget;
+class QTimer;
+class QDomDocument;
+class QDomElement;
+class KConfig;
+
+
+
+class LIBK3B_EXPORT K3bVcdDoc : public K3bDoc
+{
+ Q_OBJECT
+
+ public:
+ K3bVcdDoc( QObject* );
+ ~K3bVcdDoc();
+
+ int type() const { return VCD; }
+
+ QString name() const;
+
+ enum vcdTypes { VCD11, VCD20, SVCD10, HQVCD, NONE};
+
+ bool newDocument();
+ int numOfTracks() const
+ {
+ return m_tracks->count();
+ }
+
+ const QString& vcdImage() const
+ {
+ return m_vcdImage;
+ }
+ void setVcdImage( const QString& s )
+ {
+ m_vcdImage = s;
+ }
+
+ K3bVcdTrack* first()
+ {
+ return m_tracks->first();
+ }
+ K3bVcdTrack* current() const
+ {
+ return m_tracks->current();
+ }
+ K3bVcdTrack* next()
+ {
+ return m_tracks->next();
+ }
+ K3bVcdTrack* prev()
+ {
+ return m_tracks->prev();
+ }
+ K3bVcdTrack* at( uint i )
+ {
+ return m_tracks->at( i );
+ }
+ K3bVcdTrack* take( uint i )
+ {
+ return m_tracks->take( i );
+ }
+
+ const QPtrList<K3bVcdTrack>* tracks() const
+ {
+ return m_tracks;
+ }
+
+ /** get the current size of the project */
+ KIO::filesize_t size() const;
+ K3b::Msf length() const;
+
+ K3bBurnJob* newBurnJob( K3bJobHandler* hdl, QObject* parent );
+ K3bVcdOptions* vcdOptions() const
+ {
+ return m_vcdOptions;
+ }
+
+ int vcdType() const
+ {
+ return m_vcdType;
+ }
+ void setVcdType( int type );
+ void setPbcTracks();
+
+ public slots:
+ /**
+ * will test the file and add it to the project.
+ * connect to at least result() to know when
+ * the process is finished and check error()
+ * to know about the result.
+ **/
+ void addUrls( const KURL::List& );
+ void addTrack( const KURL&, uint );
+ void addTracks( const KURL::List&, uint );
+ /** adds a track without any testing */
+ void addTrack( K3bVcdTrack* track, uint position = 0 );
+
+ // --- TODO: this should read: removeTrack( K3bVcdTrack* )
+ void removeTrack( K3bVcdTrack* );
+ void moveTrack( const K3bVcdTrack* track, const K3bVcdTrack* after );
+
+ protected slots:
+ /** processes queue "urlsToAdd" **/
+ void slotWorkUrlQueue();
+
+ signals:
+ void newTracks();
+
+ void trackRemoved( K3bVcdTrack* );
+
+ protected:
+ /** reimplemented from K3bDoc */
+ bool loadDocumentData( QDomElement* root );
+ /** reimplemented from K3bDoc */
+ bool saveDocumentData( QDomElement* );
+
+ QString typeString() const;
+
+ private:
+ K3bVcdTrack* createTrack( const KURL& url );
+ void informAboutNotFoundFiles();
+
+ QStringList m_notFoundFiles;
+ QString m_vcdImage;
+
+ class PrivateUrlToAdd
+ {
+ public:
+ PrivateUrlToAdd( const KURL& u, int _pos )
+ : url( u ), position( _pos )
+ {}
+ KURL url;
+ int position;
+ };
+
+ /** Holds all the urls that have to be added to the list of tracks. **/
+ QPtrQueue<PrivateUrlToAdd> urlsToAdd;
+ QTimer* m_urlAddingTimer;
+
+ QPtrList<K3bVcdTrack>* m_tracks;
+ KIO::filesize_t calcTotalSize() const;
+ KIO::filesize_t ISOsize() const;
+
+ bool isImage( const KURL& url );
+
+ K3bVcdTrack* m_lastAddedTrack;
+ K3bVcdOptions* m_vcdOptions;
+
+ int m_vcdType;
+ uint lastAddedPosition;
+};
+
+#endif
diff --git a/libk3b/projects/videocd/k3bvcdjob.cpp b/libk3b/projects/videocd/k3bvcdjob.cpp
new file mode 100644
index 0000000..a1b347a
--- /dev/null
+++ b/libk3b/projects/videocd/k3bvcdjob.cpp
@@ -0,0 +1,567 @@
+/*
+*
+* $Id: k3bvcdjob.cpp 619556 2007-01-03 17:38:12Z trueg $
+* Copyright (C) 2003-2004 Christian Kvasny <chris@k3b.org>
+*
+* This file is part of the K3b project.
+* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+* See the file "COPYING" for the exact licensing terms.
+*/
+
+#include <klocale.h>
+#include <kconfig.h>
+#include <kstandarddirs.h>
+#include <kurl.h>
+#include <ktempfile.h>
+#include <kio/global.h>
+
+#include <qstring.h>
+#include <qdatetime.h>
+#include <qfile.h>
+#include <qtimer.h>
+#include <kdebug.h>
+#include <qregexp.h>
+#include <qdom.h>
+
+#include "k3bvcdjob.h"
+
+// K3b Includes
+#include "k3bvcddoc.h"
+#include "k3bvcdtrack.h"
+#include "k3bvcdxmlview.h"
+#include <k3bcore.h>
+#include <k3bdoc.h>
+#include <k3bprocess.h>
+#include <k3bdevice.h>
+#include <k3bexternalbinmanager.h>
+#include <k3bglobals.h>
+#include <k3bcdrecordwriter.h>
+#include <k3bcdrdaowriter.h>
+
+K3bVcdJob::K3bVcdJob( K3bVcdDoc* doc, K3bJobHandler* jh, QObject* parent, const char* name )
+ : K3bBurnJob( jh, parent, name )
+{
+ m_doc = doc;
+ m_doc->setCopies( m_doc->dummy() || m_doc->onlyCreateImages() ? 1 : m_doc->copies() );
+ m_process = 0;
+ m_currentWrittenTrackNumber = 0;
+ m_bytesFinishedTracks = 0;
+ m_writerJob = 0;
+ // m_createimageonlypercent = 33.3;
+ m_createimageonlypercent = 100 / ( m_doc->copies() + 2 );
+ m_currentcopy = 1;
+ m_imageFinished = false;
+}
+
+
+K3bVcdJob::~K3bVcdJob()
+{
+ delete m_process;
+
+ if ( m_writerJob )
+ delete m_writerJob;
+}
+
+
+K3bDoc* K3bVcdJob::doc() const
+{
+ return m_doc;
+}
+
+
+K3bDevice::Device* K3bVcdJob::writer() const
+{
+ if( doc()->onlyCreateImages() )
+ return 0;
+ else
+ return doc() ->burner();
+}
+
+void K3bVcdJob::cancel()
+{
+ cancelAll();
+
+ emit canceled();
+ jobFinished( false );
+}
+
+
+void K3bVcdJob::cancelAll()
+{
+ m_canceled = true;
+
+ if ( m_writerJob )
+ m_writerJob->cancel();
+
+ if ( m_process->isRunning() ) {
+ m_process->disconnect( this );
+ m_process->kill();
+ }
+
+ // remove bin-file if it is unfinished or the user selected to remove image
+ if ( QFile::exists( m_doc->vcdImage() ) ) {
+ if ( !m_doc->onTheFly() && m_doc->removeImages() || !m_imageFinished ) {
+ emit infoMessage( i18n( "Removing Binary file %1" ).arg( m_doc->vcdImage() ), K3bJob::SUCCESS );
+ QFile::remove
+ ( m_doc->vcdImage() );
+ m_doc->setVcdImage( "" );
+ }
+ }
+
+ // remove cue-file if it is unfinished or the user selected to remove image
+ if ( QFile::exists( m_cueFile ) ) {
+ if ( !m_doc->onTheFly() && m_doc->removeImages() || !m_imageFinished ) {
+ emit infoMessage( i18n( "Removing Cue file %1" ).arg( m_cueFile ), K3bJob::SUCCESS );
+ QFile::remove
+ ( m_cueFile );
+ m_cueFile = "";
+ }
+ }
+}
+
+
+void K3bVcdJob::start()
+{
+ kdDebug() << "(K3bVcdJob) starting job" << endl;
+
+ jobStarted();
+ emit burning( false );
+ m_canceled = false;
+
+ int pos = QString( m_doc->vcdImage() ).find( ".bin", QString( m_doc->vcdImage() ).length() - 4 );
+ if ( pos > 0 ) {
+ m_cueFile = m_doc->vcdImage().left( pos ) + ".cue";
+ } else {
+ m_cueFile = m_doc->vcdImage() + ".cue";
+ m_doc->setVcdImage( m_doc->vcdImage() + ".bin" );
+ }
+
+ if ( vcdDoc() ->onlyCreateImages() )
+ m_createimageonlypercent = 50.0;
+
+ // vcdxGen();
+ xmlGen();
+}
+
+void K3bVcdJob::xmlGen()
+{
+
+ KTempFile tempF;
+ m_xmlFile = tempF.name();
+ tempF.unlink();
+
+ K3bVcdXmlView xmlView( m_doc );
+
+ if ( !xmlView.write( m_xmlFile ) ) {
+ kdDebug() << "(K3bVcdJob) could not write xmlfile." << endl;
+ emit infoMessage( i18n( "Could not write correct XML-file." ), K3bJob::ERROR );
+ cancelAll();
+ jobFinished( false );
+ }
+
+ // emit infoMessage( i18n( "XML-file successfully created" ), K3bJob::SUCCESS );
+ emit debuggingOutput( "K3bVcdXml:", xmlView.xmlString() );
+
+ vcdxBuild();
+
+}
+
+void K3bVcdJob::vcdxBuild()
+{
+ emit newTask( i18n( "Creating image files" ) );
+
+ m_stage = stageUnknown;
+ firstTrack = true;
+ delete m_process;
+ m_process = new K3bProcess();
+
+ emit infoMessage( i18n( "Creating Cue/Bin files ..." ), K3bJob::INFO );
+ const K3bExternalBin* bin = k3bcore ->externalBinManager() ->binObject( "vcdxbuild" );
+ if ( !bin ) {
+ kdDebug() << "(K3bVcdJob) could not find vcdxbuild executable" << endl;
+ emit infoMessage( i18n( "Could not find %1 executable." ).arg( "vcdxbuild" ), K3bJob::ERROR );
+ emit infoMessage( i18n( "To create VideoCDs you must install VcdImager Version %1." ).arg( ">= 0.7.12" ), K3bJob::INFO );
+ emit infoMessage( i18n( "You can find this on your distribution disks or download it from http://www.vcdimager.org" ), K3bJob::INFO );
+ cancelAll();
+ jobFinished( false );
+ return ;
+ }
+
+ if ( bin->version < K3bVersion( "0.7.12" ) ) {
+ kdDebug() << "(K3bVcdJob) vcdxbuild executable too old!" << endl;
+ emit infoMessage( i18n( "%1 executable too old: need version %2 or greater." ).arg( "Vcdxbuild" ).arg( "0.7.12" ), K3bJob::ERROR );
+ emit infoMessage( i18n( "You can find this on your distribution disks or download it from http://www.vcdimager.org" ), K3bJob::INFO );
+ cancelAll();
+ jobFinished( false );
+ return ;
+ }
+
+ if ( !bin->copyright.isEmpty() )
+ emit infoMessage( i18n( "Using %1 %2 - Copyright (C) %3" ).arg( bin->name() ).arg( bin->version ).arg( bin->copyright ), INFO );
+
+ *m_process << bin;
+
+ // additional user parameters from config
+ const QStringList& params = k3bcore->externalBinManager() ->program( "vcdxbuild" ) ->userParameters();
+ for ( QStringList::const_iterator it = params.begin(); it != params.end(); ++it )
+ *m_process << *it;
+
+
+ if ( vcdDoc() ->vcdOptions() ->Sector2336() ) {
+ kdDebug() << "(K3bVcdJob) Write 2336 Sectors = on" << endl;
+ *m_process << "--sector-2336";
+ }
+
+ *m_process << "--progress" << "--gui";
+
+ *m_process << QString( "--cue-file=%1" ).arg( m_cueFile );
+
+ *m_process << QString( "--bin-file=%1" ).arg( m_doc->vcdImage() );
+
+ *m_process << QString( "%1" ).arg( QFile::encodeName( m_xmlFile ) );
+
+ connect( m_process, SIGNAL( receivedStderr( KProcess*, char*, int ) ),
+ this, SLOT( slotParseVcdxBuildOutput( KProcess*, char*, int ) ) );
+ connect( m_process, SIGNAL( receivedStdout( KProcess*, char*, int ) ),
+ this, SLOT( slotParseVcdxBuildOutput( KProcess*, char*, int ) ) );
+ connect( m_process, SIGNAL( processExited( KProcess* ) ),
+ this, SLOT( slotVcdxBuildFinished() ) );
+
+ // vcdxbuild commandline parameters
+ kdDebug() << "***** vcdxbuild parameters:" << endl;
+ ;
+ const QValueList<QCString>& args = m_process->args();
+ QString s;
+ for ( QValueList<QCString>::const_iterator it = args.begin(); it != args.end(); ++it ) {
+ s += *it + " ";
+ }
+ kdDebug() << s << flush << endl;
+ emit debuggingOutput( "vcdxbuild command:", s );
+
+ if ( !m_process->start( KProcess::NotifyOnExit, KProcess::AllOutput ) ) {
+ kdDebug() << "(K3bVcdJob) could not start vcdxbuild" << endl;
+ emit infoMessage( i18n( "Could not start %1." ).arg( "vcdxbuild" ), K3bJob::ERROR );
+ cancelAll();
+ jobFinished( false );
+ }
+}
+
+void K3bVcdJob::slotParseVcdxBuildOutput( KProcess*, char* output, int len )
+{
+ QString buffer = QString::fromLocal8Bit( output, len );
+
+ // split to lines
+ QStringList lines = QStringList::split( "\n", buffer );
+
+ QDomDocument xml_doc;
+ QDomElement xml_root;
+
+ // do every line
+ for ( QStringList::Iterator str = lines.begin(); str != lines.end(); ++str ) {
+ *str = ( *str ).stripWhiteSpace();
+
+ emit debuggingOutput( "vcdxbuild", *str );
+
+ xml_doc.setContent( QString( "<?xml version='1.0'?><vcdxbuild>" ) + *str + "</vcdxbuild>" );
+
+ xml_root = xml_doc.documentElement();
+
+ // There should be only one... but ...
+ for ( QDomNode node = xml_root.firstChild(); !node.isNull(); node = node.nextSibling() ) {
+ QDomElement el = node.toElement();
+ if ( el.isNull() )
+ continue;
+
+ const QString tagName = el.tagName().lower();
+
+ if ( tagName == "progress" ) {
+ const QString oper = el.attribute( "operation" ).lower();
+ const unsigned long long pos = el.attribute( "position" ).toLong();
+ const long long size = el.attribute( "size" ).toLong();
+
+ if ( oper == "scan" ) {
+ // Scan Video Files
+ if ( m_stage == stageUnknown || pos < m_bytesFinished ) {
+ const uint index = el.attribute( "id" ).replace( QRegExp( "sequence-" ), "" ).toUInt();
+
+ m_currentWrittenTrack = m_doc->at( m_currentWrittenTrackNumber );
+ emit newSubTask( i18n( "Scanning video file %1 of %2 (%3)" ).arg( index + 1 ).arg( doc() ->numOfTracks() ).arg( m_currentWrittenTrack->fileName() ) );
+ m_bytesFinished = 0;
+
+ if ( !firstTrack ) {
+ m_bytesFinishedTracks += m_doc->at( m_currentWrittenTrackNumber ) ->size();
+ m_currentWrittenTrackNumber++;
+ } else
+ firstTrack = false;
+ }
+ emit subPercent( ( int ) ( 100.0 * ( double ) pos / ( double ) size ) );
+ emit processedSubSize( pos / 1024 / 1024, size / 1024 / 1024 );
+
+ // this is the first of three processes.
+ double relOverallWritten = ( ( double ) m_bytesFinishedTracks + ( double ) pos ) / ( double ) doc() ->size();
+ emit percent( ( int ) ( m_createimageonlypercent * relOverallWritten ) );
+
+ m_bytesFinished = pos;
+ m_stage = stageScan;
+
+ } else if ( oper == "write" ) {
+ emit subPercent( ( int ) ( 100.0 * ( double ) pos / ( double ) size ) );
+ emit processedSubSize( ( pos * 2048 ) / 1024 / 1024, ( size * 2048 ) / 1024 / 1024 );
+ emit percent( ( int ) ( m_createimageonlypercent + ( m_createimageonlypercent * ( double ) pos / ( double ) size ) ) );
+
+ m_stage = stageWrite;
+ } else {
+ return ;
+ }
+ } else if ( tagName == "log" ) {
+ QDomText tel = el.firstChild().toText();
+ const QString level = el.attribute( "level" ).lower();
+ if ( tel.isText() ) {
+ const QString text = tel.data();
+ if ( m_stage == stageWrite && level == "information" )
+ kdDebug() << QString( "(K3bVcdJob) VcdxBuild information, %1" ).arg( text ) << endl;
+ if ( ( text ).startsWith( "writing track" ) )
+ emit newSubTask( i18n( "Creating Image for track %1" ).arg( ( text ).mid( 14 ) ) );
+ else {
+ if ( level != "error" ) {
+ kdDebug() << QString( "(K3bVcdJob) vcdxbuild warning, %1" ).arg( text ) << endl;
+ parseInformation( text );
+ } else {
+ kdDebug() << QString( "(K3bVcdJob) vcdxbuild error, %1" ).arg( text ) << endl;
+ emit infoMessage( text, K3bJob::ERROR );
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void K3bVcdJob::slotVcdxBuildFinished()
+{
+ if ( m_process->normalExit() ) {
+ // TODO: check the process' exitStatus()
+ switch ( m_process->exitStatus() ) {
+ case 0:
+ emit infoMessage( i18n( "Cue/Bin files successfully created." ), K3bJob::SUCCESS );
+ m_imageFinished = true;
+ break;
+ default:
+ emit infoMessage( i18n( "%1 returned an unknown error (code %2)." ).arg( "vcdxbuild" ).arg( m_process->exitStatus() ),
+ K3bJob::ERROR );
+ emit infoMessage( i18n( "Please send me an email with the last output." ), K3bJob::ERROR );
+ cancelAll();
+ jobFinished( false );
+ return ;
+ }
+ } else {
+ emit infoMessage( i18n( "%1 did not exit cleanly." ).arg( "Vcdxbuild" ), K3bJob::ERROR );
+ cancelAll();
+ jobFinished( false );
+ return ;
+ }
+
+ //remove xml-file
+ if ( QFile::exists( m_xmlFile ) )
+ QFile::remove
+ ( m_xmlFile );
+
+ kdDebug() << QString( "(K3bVcdJob) create only image: %1" ).arg( vcdDoc() ->onlyCreateImages() ) << endl;
+ if ( !vcdDoc() ->onlyCreateImages() )
+ startWriterjob();
+ else
+ jobFinished( true );
+}
+
+void K3bVcdJob::startWriterjob()
+{
+ kdDebug() << QString( "(K3bVcdJob) writing copy %1 of %2" ).arg( m_currentcopy ).arg( m_doc->copies() ) << endl;
+ if ( prepareWriterJob() ) {
+ if ( waitForMedia( m_doc->burner() ) < 0 ) {
+ cancel();
+ return ;
+ }
+ // just to be sure we did not get canceled during the async discWaiting
+ if ( m_canceled )
+ return ;
+
+ if ( m_doc->copies() > 1 )
+ emit newTask( i18n( "Writing Copy %1 of %2" ).arg( m_currentcopy ).arg( m_doc->copies() ) );
+
+ emit burning( true );
+ m_writerJob->start();
+ }
+}
+
+bool K3bVcdJob::prepareWriterJob()
+{
+ if ( m_writerJob )
+ delete m_writerJob;
+
+ const K3bExternalBin* cdrecordBin = k3bcore->externalBinManager() ->binObject( "cdrecord" );
+ if ( writingApp() == K3b::DEFAULT && cdrecordBin->hasFeature( "cuefile" ) && m_doc->burner() ->dao() )
+ setWritingApp( K3b::CDRECORD );
+
+ if ( writingApp() == K3b::CDRDAO || writingApp() == K3b::DEFAULT ) {
+ K3bCdrdaoWriter * writer = new K3bCdrdaoWriter( m_doc->burner(), this, this );
+ // create cdrdao job
+ writer->setCommand( K3bCdrdaoWriter::WRITE );
+ writer->setSimulate( m_doc->dummy() );
+ writer->setBurnSpeed( m_doc->speed() );
+
+ writer->setTocFile( m_cueFile );
+
+ m_writerJob = writer;
+
+ } else if ( writingApp() == K3b::CDRECORD ) {
+ K3bCdrecordWriter * writer = new K3bCdrecordWriter( m_doc->burner(), this, this );
+ // create cdrecord job
+
+ writer->setSimulate( m_doc->dummy() );
+ writer->setBurnSpeed( m_doc->speed() );
+ writer->setDao( true );
+ writer->setCueFile( m_cueFile );
+
+ m_writerJob = writer;
+
+ }
+
+ connect( m_writerJob, SIGNAL( infoMessage( const QString&, int ) ), this, SIGNAL( infoMessage( const QString&, int ) ) );
+ connect( m_writerJob, SIGNAL( percent( int ) ), this, SLOT( slotWriterJobPercent( int ) ) );
+ connect( m_writerJob, SIGNAL( processedSize( int, int ) ), this, SLOT( slotProcessedSize( int, int ) ) );
+ connect( m_writerJob, SIGNAL( subPercent( int ) ), this, SIGNAL( subPercent( int ) ) );
+ connect( m_writerJob, SIGNAL( processedSubSize( int, int ) ), this, SIGNAL( processedSubSize( int, int ) ) );
+ connect( m_writerJob, SIGNAL( nextTrack( int, int ) ), this, SLOT( slotWriterNextTrack( int, int ) ) );
+ connect( m_writerJob, SIGNAL( buffer( int ) ), this, SIGNAL( bufferStatus( int ) ) );
+ connect( m_writerJob, SIGNAL( deviceBuffer( int ) ), this, SIGNAL( deviceBuffer( int ) ) );
+ connect( m_writerJob, SIGNAL( writeSpeed( int, int ) ), this, SIGNAL( writeSpeed( int, int ) ) );
+ connect( m_writerJob, SIGNAL( finished( bool ) ), this, SLOT( slotWriterJobFinished( bool ) ) );
+ connect( m_writerJob, SIGNAL( newTask( const QString& ) ), this, SIGNAL( newTask( const QString& ) ) );
+ connect( m_writerJob, SIGNAL( newSubTask( const QString& ) ), this, SIGNAL( newSubTask( const QString& ) ) );
+ connect( m_writerJob, SIGNAL( debuggingOutput( const QString&, const QString& ) ), this, SIGNAL( debuggingOutput( const QString&, const QString& ) ) );
+
+ return true;
+}
+
+void K3bVcdJob::slotWriterJobPercent( int p )
+{
+ emit percent( ( int ) ( ( m_createimageonlypercent * ( m_currentcopy + 1 ) ) + p / ( m_doc->copies() + 2 ) ) );
+}
+
+void K3bVcdJob::slotProcessedSize( int cs, int ts )
+{
+ emit processedSize( cs + ( ts * ( m_currentcopy - 1 ) ) , ts * m_doc->copies() );
+}
+
+void K3bVcdJob::slotWriterNextTrack( int t, int tt )
+{
+ emit newSubTask( i18n( "Writing Track %1 of %2" ).arg( t ).arg( tt ) );
+}
+
+void K3bVcdJob::slotWriterJobFinished( bool success )
+{
+ if ( m_canceled )
+ return ;
+
+ if ( m_currentcopy >= m_doc->copies() ) {
+ // remove bin-file if it is unfinished or the user selected to remove image
+ if ( QFile::exists( m_doc->vcdImage() ) ) {
+ if ( !m_doc->onTheFly() && m_doc->removeImages() || !m_imageFinished ) {
+ emit infoMessage( i18n( "Removing Binary file %1" ).arg( m_doc->vcdImage() ), K3bJob::SUCCESS );
+ QFile::remove
+ ( m_doc->vcdImage() );
+ m_doc->setVcdImage( "" );
+ }
+ }
+
+ // remove cue-file if it is unfinished or the user selected to remove image
+ if ( QFile::exists( m_cueFile ) ) {
+ if ( !m_doc->onTheFly() && m_doc->removeImages() || !m_imageFinished ) {
+ emit infoMessage( i18n( "Removing Cue file %1" ).arg( m_cueFile ), K3bJob::SUCCESS );
+ QFile::remove
+ ( m_cueFile );
+ m_cueFile = "";
+ }
+ }
+ }
+
+ if ( success ) {
+ // allright
+ // the writerJob should have emited the "simulation/writing successful" signal
+ if ( m_currentcopy >= m_doc->copies() ) {
+ jobFinished( true );
+ } else {
+ m_currentcopy++;
+ startWriterjob();
+ }
+ } else {
+ cancelAll();
+ jobFinished( false );
+ }
+}
+
+void K3bVcdJob::parseInformation( const QString &text )
+{
+ // parse warning
+ if ( text.contains( "mpeg user scan data: one or more BCD fields out of range for" ) ) {
+ int index = text.find( " for" );
+
+ emit infoMessage( i18n( "One or more BCD fields out of range for %1" ).arg( text.mid( index + 4 ).stripWhiteSpace() ), K3bJob::WARNING );
+
+ } else if ( text.contains( "mpeg user scan data: from now on, scan information data errors will not be reported anymore" ) ) {
+ emit infoMessage( i18n( "From now on, scan information data errors will not be reported anymore" ), K3bJob::INFO );
+ emit infoMessage( i18n( "Consider enabling the 'update scan offsets' option, if it is not enabled already." ), K3bJob::INFO );
+
+ } else if ( text.contains( "APS' pts seems out of order (actual pts" ) ) {
+ int index = text.find( "(actual pts" );
+ int index2 = text.find( ", last seen pts" );
+ int index3 = text.find( ") -- ignoring this aps" );
+
+ emit infoMessage( i18n( "APS' pts seems out of order (actual pts %1, last seen pts %2)" ).arg( text.mid( index + 12, index2 - index - 12 ).stripWhiteSpace() ).arg( text.mid( index2 + 14, index3 - index2 - 14 ).stripWhiteSpace() ), K3bJob::WARNING );
+ emit infoMessage( i18n( "Ignoring this aps" ), K3bJob::INFO );
+
+ } else if ( text.contains( "bad packet at packet" ) ) {
+ int index = text.find( "at packet #" );
+ int index2 = text.find( "(stream byte offset" );
+ int index3 = text.find( ") -- remaining " );
+ int index4 = text.find( "bytes of stream will be ignored" );
+
+ emit infoMessage( i18n( "Bad packet at packet #%1 (stream byte offset %2)" ).arg( text.mid( index + 11, index2 - index - 11 ).stripWhiteSpace() ).arg( text.mid( index2 + 19, index3 - index2 - 19 ).stripWhiteSpace() ), K3bJob::WARNING );
+ emit infoMessage( i18n( "Remaining %1 bytes of stream will be ignored." ).arg( text.mid( index3 + 15, index4 - index3 - 15 ).stripWhiteSpace() ), K3bJob::WARNING );
+ }
+}
+
+QString K3bVcdJob::jobDescription() const
+{
+ switch ( m_doc->vcdType() ) {
+ case K3bVcdDoc::VCD11:
+ return i18n( "Writing Video CD (Version 1.1)" );
+ case K3bVcdDoc::VCD20:
+ return i18n( "Writing Video CD (Version 2.0)" );
+ case K3bVcdDoc::SVCD10:
+ return i18n( "Writing Super Video CD" );
+ case K3bVcdDoc::HQVCD:
+ return i18n( "Writing High-Quality Video CD" );
+ default:
+ return i18n( "Writing Video CD" );
+ }
+}
+
+
+QString K3bVcdJob::jobDetails() const
+{
+ return ( i18n( "1 MPEG (%1)",
+ "%n MPEGs (%1)",
+ m_doc->tracks() ->count() ).arg( KIO::convertSize( m_doc->size() ) )
+ + ( m_doc->copies() > 1
+ ? i18n( " - %n copy", " - %n copies", m_doc->copies() )
+ : QString::null ) );
+}
+
+#include "k3bvcdjob.moc"
diff --git a/libk3b/projects/videocd/k3bvcdjob.h b/libk3b/projects/videocd/k3bvcdjob.h
new file mode 100644
index 0000000..917c8b1
--- /dev/null
+++ b/libk3b/projects/videocd/k3bvcdjob.h
@@ -0,0 +1,115 @@
+/*
+*
+* $Id: k3bvcdjob.h 619556 2007-01-03 17:38:12Z trueg $
+* Copyright (C) 2003-2004 Christian Kvasny <chris@k3b.org>
+*
+* This file is part of the K3b project.
+* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+* See the file "COPYING" for the exact licensing terms.
+*/
+
+#ifndef K3BVCDJOB_H
+#define K3BVCDJOB_H
+
+#include <k3bjob.h>
+
+class K3bVcdDoc;
+class K3bVcdTrack;
+class QString;
+class K3bProcess;
+class KProcess;
+class QDataStream;
+class K3bAbstractWriter;
+class K3bDevice::Device;
+
+
+class K3bVcdJob : public K3bBurnJob
+{
+ Q_OBJECT
+
+ public:
+ K3bVcdJob( K3bVcdDoc*, K3bJobHandler*, QObject* parent = 0, const char* name = 0 );
+ ~K3bVcdJob();
+
+ K3bDoc* doc() const;
+ K3bVcdDoc* vcdDoc() const
+ {
+ return m_doc;
+ }
+ K3bDevice::Device* writer() const;
+
+ QString jobDescription() const;
+ QString jobDetails() const;
+
+ public slots:
+ void start();
+ void cancel();
+
+ private slots:
+ void cancelAll();
+
+ protected slots:
+ void slotVcdxBuildFinished();
+ void slotParseVcdxBuildOutput( KProcess*, char* output, int len );
+
+ void slotWriterJobPercent( int p );
+ void slotProcessedSize( int cs, int ts );
+ void slotWriterNextTrack( int t, int tt );
+ void slotWriterJobFinished( bool success );
+
+
+ private:
+ bool prepareWriterJob();
+
+ void xmlGen();
+ void vcdxBuild();
+ void parseInformation( const QString& );
+ void startWriterjob();
+
+ int m_copies;
+ int m_finishedCopies;
+
+ unsigned long m_blocksToCopy;
+ unsigned long m_bytesFinishedTracks;
+ unsigned long m_bytesFinished;
+
+ enum { stageUnknown, stageScan, stageWrite, _stage_max };
+
+ K3bVcdDoc* m_doc;
+ K3bDevice::Device* m_writer;
+ K3bDevice::Device* m_reader;
+ K3bVcdTrack* m_currentWrittenTrack;
+
+ int m_speed;
+ int m_stage;
+ int m_currentcopy;
+ int m_currentWrittenTrackNumber;
+
+ double m_createimageonlypercent;
+
+ bool firstTrack;
+ bool m_burnProof;
+ bool m_keepImage;
+ bool m_onlyCreateImage;
+ bool m_onTheFly;
+ bool m_dummy;
+ bool m_fastToc;
+ bool m_readRaw;
+ bool m_imageFinished;
+ bool m_canceled;
+
+ QString m_tempPath;
+ QString m_cueFile;
+ QString m_xmlFile;
+ QString m_collectedOutput;
+
+ K3bAbstractWriter* m_writerJob;
+ K3bProcess* m_process;
+};
+
+#endif
diff --git a/libk3b/projects/videocd/k3bvcdoptions.cpp b/libk3b/projects/videocd/k3bvcdoptions.cpp
new file mode 100644
index 0000000..6009a4a
--- /dev/null
+++ b/libk3b/projects/videocd/k3bvcdoptions.cpp
@@ -0,0 +1,146 @@
+/*
+*
+* $Id: k3bvcdoptions.cpp 619556 2007-01-03 17:38:12Z trueg $
+* Copyright (C) 2003-2004 Christian Kvasny <chris@k3b.org>
+*
+* This file is part of the K3b project.
+* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+* See the file "COPYING" for the exact licensing terms.
+*/
+
+// Kde Includes
+#include <kapplication.h>
+#include <kconfig.h>
+#include <k3bcore.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+
+// Qt Includes
+#include <qstring.h>
+#include <qfile.h>
+
+// K3b Includes
+#include "k3bvcdoptions.h"
+#include <k3bversion.h>
+
+K3bVcdOptions::K3bVcdOptions()
+ : m_restriction( 0 ),
+ m_segment( 0 ),
+ m_sequence( 0 ),
+ m_pbcenabled( PbcEnabled() ),
+ m_pbcnumkeysenabled( PbcNumkeysEnabled() ),
+ m_volumeID( "VIDEOCD" ),
+ m_albumID( "" ),
+ m_volumeSetId( "" ),
+ m_publisher( QString( "K3b - Version %1" ).arg( k3bcore->version() ) ),
+ m_applicationId( "CDI/CDI_VCD.APP;1" ),
+ m_systemId( "CD-RTOS CD-BRIDGE" ),
+ m_vcdclass( "vcd" ),
+ m_vcdversion( "2.0" ),
+ m_pregapleadout( 150 ),
+ m_pregaptrack( 150 ),
+ m_frontmargintrack( 30 ),
+ m_rearmargintrack( 45 ),
+ m_frontmargintrackSVCD( 0 ),
+ m_rearmargintrackSVCD( 0 ),
+ m_mpegversion( 1 ),
+ m_volumeCount( 1 ),
+ m_volumeNumber( 1 ),
+ m_autodetect( true ),
+ m_cdisupport( false ),
+ m_brokensvcdmode( false ),
+ m_VCD30interpretation( false ),
+ m_sector2336( false ),
+ m_updatescanoffsets( false ),
+ m_relaxedaps( false ),
+ m_segmentfolder( true ),
+ m_usegaps( false )
+{}
+
+bool K3bVcdOptions::checkCdiFiles()
+{
+ m_cdisize = 0;
+ if ( !QFile::exists( locate( "data", "k3b/cdi/cdi_imag.rtf" ) ) )
+ return false;
+ if ( !QFile::exists( locate( "data", "k3b/cdi/cdi_text.fnt" ) ) )
+ return false;
+ if ( !QFile::exists( locate( "data", "k3b/cdi/cdi_vcd.app" ) ) )
+ return false;
+ if ( !QFile::exists( locate( "data", "k3b/cdi/cdi_vcd.cfg" ) ) )
+ return false;
+
+ m_cdisize += QFile( locate( "data", "k3b/cdi/cdi_imag.rtf" ) ).size();
+ m_cdisize += QFile( locate( "data", "k3b/cdi/cdi_text.fnt" ) ).size();
+ m_cdisize += QFile( locate( "data", "k3b/cdi/cdi_vcd.app" ) ).size();
+ m_cdisize += QFile( locate( "data", "k3b/cdi/cdi_vcd.cfg" ) ).size();
+
+ return true;
+}
+
+void K3bVcdOptions::save( KConfigBase* c )
+{
+ c->writeEntry( "volume_id", m_volumeID );
+ c->writeEntry( "album_id", m_albumID );
+ c->writeEntry( "volume_set_id", m_volumeSetId );
+ c->writeEntry( "preparer", m_preparer );
+ c->writeEntry( "publisher", m_publisher );
+ c->writeEntry( "volume_count", m_volumeCount );
+ c->writeEntry( "volume_number", m_volumeNumber );
+ c->writeEntry( "autodetect", m_autodetect );
+ c->writeEntry( "cdi_support", m_cdisupport );
+ c->writeEntry( "broken_svcd_mode", m_brokensvcdmode );
+ c->writeEntry( "VCD30interpretation", m_VCD30interpretation );
+ c->writeEntry( "2336_sectors", m_sector2336 );
+ c->writeEntry( "UpdateScanOffsets", m_updatescanoffsets );
+ c->writeEntry( "RelaxedAps", m_relaxedaps );
+ c->writeEntry( "PbcEnabled", m_pbcenabled );
+ c->writeEntry( "SegmentFolder", m_segmentfolder );
+ c->writeEntry( "Restriction", m_restriction );
+ c->writeEntry( "PreGapLeadout", m_pregapleadout );
+ c->writeEntry( "PreGapTrack", m_pregaptrack );
+ c->writeEntry( "FrontMarginTrack", m_frontmargintrack );
+ c->writeEntry( "RearMarginTrack", m_rearmargintrack );
+ c->writeEntry( "UseGaps", m_usegaps );
+}
+
+
+K3bVcdOptions K3bVcdOptions::load( KConfigBase* c )
+{
+ K3bVcdOptions options;
+
+ options.setVolumeId( c->readEntry( "volume_id", options.volumeId() ) );
+ options.setAlbumId( c->readEntry( "album_id", options.albumId() ) );
+ options.setVolumeSetId( c->readEntry( "volume_set_id", options.volumeSetId() ) );
+ options.setPreparer( c->readEntry( "preparer", options.preparer() ) );
+ options.setPublisher( c->readEntry( "publisher", options.publisher() ) );
+ options.setVolumeCount( c->readNumEntry( "volume_count", options.volumeCount() ) );
+ options.setVolumeNumber( c->readNumEntry( "volume_number", options.volumeNumber() ) );
+ options.setAutoDetect( c->readBoolEntry( "autodetect", options.AutoDetect() ) );
+ options.setCdiSupport( c->readBoolEntry( "cdi_support", options.CdiSupport() ) );
+ options.setNonCompliantMode( c->readBoolEntry( "broken_svcd_mode", options.NonCompliantMode() ) );
+ options.setVCD30interpretation( c->readBoolEntry( "VCD30interpretation", options.VCD30interpretation() ) );
+ options.setSector2336( c->readBoolEntry( "2336_sectors", options.Sector2336() ) );
+ options.setUpdateScanOffsets( c->readBoolEntry( "UpdateScanOffsets", options.UpdateScanOffsets() ) );
+ options.setRelaxedAps( c->readBoolEntry( "RelaxedAps", options.RelaxedAps() ) );
+ options.setPbcEnabled( c->readBoolEntry( "PbcEnabled", options.PbcEnabled() ) );
+ options.setSegmentFolder( c->readBoolEntry( "SegmentFolder", options.SegmentFolder() ) );
+ options.setRestriction( c->readNumEntry( "Restriction", options.Restriction() ) );
+ options.setPreGapLeadout( c->readNumEntry( "PreGapLeadout", options.PreGapLeadout() ) );
+ options.setPreGapTrack( c->readNumEntry( "PreGapTrack", options.PreGapTrack() ) );
+ options.setFrontMarginTrack( c->readNumEntry( "FrontMarginTrack", options.FrontMarginTrack() ) );
+ options.setRearMarginTrack( c->readNumEntry( "RearMarginTrack", options.RearMarginTrack() ) );
+ options.setUseGaps( c->readBoolEntry( "UseGaps", options.UseGaps() ) );
+
+ return options;
+}
+
+K3bVcdOptions K3bVcdOptions::defaults()
+{
+ // let the constructor create defaults
+ return K3bVcdOptions();
+}
diff --git a/libk3b/projects/videocd/k3bvcdoptions.h b/libk3b/projects/videocd/k3bvcdoptions.h
new file mode 100644
index 0000000..aa5fed2
--- /dev/null
+++ b/libk3b/projects/videocd/k3bvcdoptions.h
@@ -0,0 +1,377 @@
+/*
+*
+* $Id: k3bvcdoptions.h 619556 2007-01-03 17:38:12Z trueg $
+* Copyright (C) 2003-2004 Christian Kvasny <chris@k3b.org>
+*
+* This file is part of the K3b project.
+* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+* See the file "COPYING" for the exact licensing terms.
+*/
+
+#ifndef K3B_VCD_OPTIONS_H
+#define K3B_VCD_OPTIONS_H
+
+#include <qstring.h>
+#include "k3b_export.h"
+
+class KConfigBase;
+
+class LIBK3B_EXPORT K3bVcdOptions
+{
+ public:
+ K3bVcdOptions();
+ const QString& volumeId() const
+ {
+ return m_volumeID;
+ }
+ const QString& albumId() const
+ {
+ return m_albumID;
+ }
+ const QString& volumeSetId() const
+ {
+ return m_volumeSetId;
+ }
+ const QString& preparer() const
+ {
+ return m_preparer;
+ }
+ const QString& publisher() const
+ {
+ return m_publisher;
+ }
+
+ const QString& applicationId() const
+ {
+ return m_applicationId;
+ }
+ const QString& systemId() const
+ {
+ return m_systemId;
+ }
+
+ const QString& vcdClass() const
+ {
+ return m_vcdclass;
+ }
+ const QString& vcdVersion() const
+ {
+ return m_vcdversion;
+ }
+
+ const int PreGapLeadout()
+ {
+ return m_pregapleadout;
+ }
+ const int PreGapTrack()
+ {
+ return m_pregaptrack;
+ }
+ const int FrontMarginTrack()
+ {
+ return m_frontmargintrack;
+ }
+ const int RearMarginTrack()
+ {
+ return m_rearmargintrack;
+ }
+ const int FrontMarginTrackSVCD()
+ {
+ return m_frontmargintrackSVCD;
+ }
+ const int RearMarginTrackSVCD()
+ {
+ return m_rearmargintrackSVCD;
+ }
+
+ const int mpegVersion() const
+ {
+ return m_mpegversion;
+ }
+
+ const int volumeCount() const
+ {
+ return m_volumeCount;
+ }
+ const int volumeNumber() const
+ {
+ return m_volumeNumber;
+ }
+
+ const bool AutoDetect() const
+ {
+ return m_autodetect;
+ }
+ const bool CdiSupport() const
+ {
+ return m_cdisupport;
+ }
+ const bool NonCompliantMode() const
+ {
+ return m_brokensvcdmode;
+ }
+ const bool VCD30interpretation() const
+ {
+ return m_VCD30interpretation;
+ }
+ const bool Sector2336() const
+ {
+ return m_sector2336;
+ }
+ const bool UpdateScanOffsets() const
+ {
+ return m_updatescanoffsets;
+ }
+ const bool RelaxedAps() const
+ {
+ return m_relaxedaps;
+ }
+ const bool UseGaps() const
+ {
+ return m_usegaps;
+ }
+ const unsigned long long CDIsize() const
+ {
+ return m_cdisize;
+ }
+
+ void setAlbumId( const QString& s )
+ {
+ m_albumID = s;
+ }
+ void setVolumeId( const QString& s )
+ {
+ m_volumeID = s;
+ }
+ void setVolumeSetId( const QString& s )
+ {
+ m_volumeSetId = s;
+ }
+ void setPreparer( const QString& s )
+ {
+ m_preparer = s;
+ }
+ void setPublisher( const QString& s )
+ {
+ m_publisher = s;
+ }
+
+ void setVcdClass( const QString& s )
+ {
+ m_vcdclass = s;
+ }
+ void setVcdVersion( const QString& s )
+ {
+ m_vcdversion = s;
+ }
+
+ void setPreGapLeadout( const int i )
+ {
+ m_pregapleadout = i;
+ }
+ void setPreGapTrack( const int i )
+ {
+ m_pregaptrack = i;
+ }
+ void setFrontMarginTrack( const int i )
+ {
+ m_frontmargintrack = i;
+ }
+ void setRearMarginTrack( const int i )
+ {
+ m_rearmargintrack = i;
+ }
+ void setFrontMarginTrackSVCD( const int i )
+ {
+ m_frontmargintrackSVCD = i;
+ }
+ void setRearMarginTrackSVCD( const int i )
+ {
+ m_rearmargintrackSVCD = i;
+ }
+
+ void setMpegVersion( const int v )
+ {
+ m_mpegversion = v;
+ }
+ void setVolumeCount( const int c )
+ {
+ m_volumeCount = c;
+ }
+ void setVolumeNumber( const int n )
+ {
+ m_volumeNumber = n;
+ }
+
+ void setAutoDetect( const bool& b )
+ {
+ m_autodetect = b;
+ }
+ void setCdiSupport( const bool& b )
+ {
+ m_cdisupport = b;
+ }
+ void setNonCompliantMode( const bool& b )
+ {
+ m_brokensvcdmode = b;
+ }
+ void setVCD30interpretation( const bool& b )
+ {
+ m_VCD30interpretation = b;
+ }
+ void setSector2336( const bool& b )
+ {
+ m_sector2336 = b;
+ }
+ void setUpdateScanOffsets( const bool& b )
+ {
+ m_updatescanoffsets = b;
+ }
+ void setRelaxedAps( const bool& b )
+ {
+ m_relaxedaps = b;
+ }
+ void setUseGaps( const bool& b )
+ {
+ m_usegaps = b;
+ }
+
+ bool checkCdiFiles();
+ void save( KConfigBase* c );
+
+ static K3bVcdOptions load( KConfigBase* c );
+ static K3bVcdOptions defaults();
+
+ void setPbcEnabled( const bool& b )
+ {
+ m_pbcenabled = b;
+ }
+ bool PbcEnabled() const
+ {
+ return m_pbcenabled;
+ };
+ void setPbcNumkeysEnabled( const bool& b )
+ {
+ m_pbcnumkeysenabled = b;
+ }
+ bool PbcNumkeysEnabled() const
+ {
+ return m_pbcnumkeysenabled;
+ };
+
+ void setPbcPlayTime( const int i )
+ {
+ m_def_pbcplaytime = i;
+ }
+ int PbcPlayTime( )
+ {
+ return m_def_pbcplaytime;
+ }
+
+ void setPbcWaitTime( const int i )
+ {
+ m_def_pbcwaittime = i;
+ }
+ int PbcWaitTime( )
+ {
+ return m_def_pbcwaittime;
+ }
+
+ void setSegmentFolder( const bool& b )
+ {
+ m_segmentfolder = b;
+ }
+ bool SegmentFolder() const
+ {
+ return m_segmentfolder;
+ };
+
+ void setRestriction( const int i )
+ {
+ m_restriction = i;
+ }
+ int Restriction() const
+ {
+ return m_restriction;
+ };
+ void increaseSegments( )
+ {
+ m_segment += 1;
+ }
+ void decreaseSegments( )
+ {
+ m_segment -= 1;
+ }
+ bool haveSegments() const
+ {
+ return m_segment > 0;
+ };
+ void increaseSequence( )
+ {
+ m_sequence += 1;
+ }
+ void decreaseSequence( )
+ {
+ m_sequence -= 1;
+ }
+
+ bool haveSequence() const
+ {
+ return m_sequence > 0;
+ };
+
+ private:
+ int m_restriction;
+ int m_segment;
+ int m_sequence;
+
+ // pbc
+ bool m_pbcenabled;
+ bool m_pbcnumkeysenabled;
+
+ // volume descriptor
+ QString m_volumeID;
+ QString m_albumID;
+ QString m_volumeSetId;
+
+ QString m_preparer;
+ QString m_publisher;
+
+ QString m_applicationId;
+ QString m_systemId;
+
+ QString m_vcdclass;
+ QString m_vcdversion;
+
+ int m_pregapleadout;
+ int m_pregaptrack;
+ int m_frontmargintrack;
+ int m_rearmargintrack;
+ int m_frontmargintrackSVCD;
+ int m_rearmargintrackSVCD;
+
+ int m_mpegversion;
+ int m_volumeCount;
+ int m_volumeNumber;
+
+ bool m_autodetect;
+ bool m_cdisupport;
+ bool m_brokensvcdmode;
+ bool m_VCD30interpretation;
+ bool m_sector2336;
+ bool m_updatescanoffsets;
+ bool m_relaxedaps;
+ bool m_segmentfolder;
+ bool m_usegaps;
+
+ int m_def_pbcplaytime;
+ int m_def_pbcwaittime;
+ unsigned long long m_cdisize;
+};
+
+#endif
diff --git a/libk3b/projects/videocd/k3bvcdtrack.cpp b/libk3b/projects/videocd/k3bvcdtrack.cpp
new file mode 100644
index 0000000..7f0043f
--- /dev/null
+++ b/libk3b/projects/videocd/k3bvcdtrack.cpp
@@ -0,0 +1,456 @@
+/*
+*
+* $Id: k3bvcdtrack.cpp 619556 2007-01-03 17:38:12Z trueg $
+* Copyright (C) 2003-2004 Christian Kvasny <chris@k3b.org>
+*
+* This file is part of the K3b project.
+* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+* See the file "COPYING" for the exact licensing terms.
+*/
+
+#include <kapplication.h>
+#include <kconfig.h>
+
+#include <qstring.h>
+#include <qfileinfo.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+// K3b Includes
+#include "k3bvcdtrack.h"
+#include <k3bglobals.h>
+
+K3bVcdTrack::K3bVcdTrack( QPtrList<K3bVcdTrack>* parent, const QString& filename )
+ : m_pbcnumkeys( true ),
+ m_pbcnumkeysuserdefined( false ),
+ m_file( filename )
+{
+ m_parent = parent;
+ m_title = QFileInfo( m_file ).baseName( true );
+
+ m_revreflist = new QPtrList<K3bVcdTrack>;
+
+ for ( int i = 0; i < K3bVcdTrack::_maxPbcTracks; i++ ) {
+ m_pbctrackmap.insert( i, 0L );
+ m_pbcnontrackmap.insert( i, K3bVcdTrack::DISABLED );
+ m_pbcusrdefmap.insert( i, false );
+ }
+
+ m_reactivity = false;
+
+ m_definedkeysmap.clear();
+
+ mpeg_info = new Mpeginfo();
+}
+
+
+K3bVcdTrack::~K3bVcdTrack()
+{}
+
+
+KIO::filesize_t K3bVcdTrack::size() const
+{
+ return m_file.size();
+}
+
+int K3bVcdTrack::index() const
+{
+ int i = m_parent->find( this );
+ if ( i < 0 )
+ kdDebug() << "(K3bVcdTrack) I'm not part of my parent!" << endl;
+ return i;
+}
+
+void K3bVcdTrack::addToRevRefList( K3bVcdTrack* revreftrack )
+{
+ kdDebug() << "K3bVcdTrack::addToRevRefList: track = " << revreftrack << endl;
+
+ m_revreflist->append( revreftrack );
+
+ kdDebug() << "K3bVcdTrack::hasRevRef count = " << m_revreflist->count() << " empty = " << m_revreflist->isEmpty() << endl;
+}
+
+void K3bVcdTrack::delFromRevRefList( K3bVcdTrack* revreftrack )
+{
+ if ( !m_revreflist->isEmpty() ) {
+ m_revreflist->remove
+ ( revreftrack );
+ }
+}
+
+bool K3bVcdTrack::hasRevRef()
+{
+ return !m_revreflist->isEmpty() ;
+}
+
+void K3bVcdTrack::delRefToUs()
+{
+ for ( K3bVcdTrack * track = m_revreflist->first(); track; track = m_revreflist->next() ) {
+ for ( int i = 0; i < K3bVcdTrack::_maxPbcTracks; i++ ) {
+ kdDebug() << "K3bVcdTrack::delRefToUs count = " << m_revreflist->count() << " empty = " << m_revreflist->isEmpty() << " track = " << track << " this = " << this << endl;
+ if ( this == track->getPbcTrack( i ) ) {
+ track->setPbcTrack( i );
+ track->setUserDefined( i, false );
+ track->delFromRevRefList( this );
+ }
+ }
+ }
+}
+
+void K3bVcdTrack::delRefFromUs()
+{
+ for ( int i = 0; i < K3bVcdTrack::_maxPbcTracks; i++ ) {
+ if ( this->getPbcTrack( i ) ) {
+ this->getPbcTrack( i ) ->delFromRevRefList( this );
+ }
+ }
+}
+
+void K3bVcdTrack::setPbcTrack( int which, K3bVcdTrack* pbctrack )
+{
+ kdDebug() << "K3bVcdTrack::setPbcTrack " << which << ", " << pbctrack << endl;
+ m_pbctrackmap.replace( which, pbctrack );
+}
+
+void K3bVcdTrack::setPbcNonTrack( int which, int type )
+{
+ kdDebug() << "K3bVcdTrack::setNonPbcTrack " << which << ", " << type << endl;
+ m_pbcnontrackmap.replace( which, type );
+}
+
+void K3bVcdTrack::setUserDefined( int which, bool ud )
+{
+ m_pbcusrdefmap.replace( which, ud );
+}
+
+K3bVcdTrack* K3bVcdTrack::getPbcTrack( const int& which )
+{
+ if ( m_pbctrackmap.find( which ) == m_pbctrackmap.end() )
+ return 0;
+ else
+ return m_pbctrackmap[ which ];
+}
+
+int K3bVcdTrack::getNonPbcTrack( const int& which )
+{
+ if ( m_pbcnontrackmap.find( which ) == m_pbcnontrackmap.end() )
+ return 0;
+ else
+ return m_pbcnontrackmap[ which ];
+}
+
+bool K3bVcdTrack::isPbcUserDefined( int which )
+{
+ return m_pbcusrdefmap[ which ];
+}
+
+const QString K3bVcdTrack::resolution()
+{
+ if ( mpeg_info->has_video ) {
+ for ( int i = 0; i < 2; i++ ) {
+ if ( mpeg_info->video[ i ].seen ) {
+ return QString( "%1 x %2" ).arg( mpeg_info->video[ i ].hsize ).arg( mpeg_info->video[ i ].vsize );
+ }
+ }
+ }
+
+ return i18n( "n/a" );
+}
+
+const QString K3bVcdTrack::highresolution()
+{
+ if ( mpeg_info->has_video ) {
+ if ( mpeg_info->video[ 2 ].seen ) {
+ return QString( "%1 x %2" ).arg( mpeg_info->video[ 2 ].hsize ).arg( mpeg_info->video[ 2 ].vsize );
+ }
+ }
+ return i18n( "n/a" );
+}
+
+const QString K3bVcdTrack::video_frate()
+{
+ if ( mpeg_info->has_video ) {
+ for ( int i = 0; i < 2; i++ ) {
+ if ( mpeg_info->video[ i ].seen ) {
+ return QString::number( mpeg_info->video[ i ].frate );
+ }
+ }
+ }
+
+ return i18n( "n/a" );
+}
+
+const QString K3bVcdTrack::video_bitrate()
+{
+ if ( mpeg_info->has_video ) {
+ for ( int i = 0; i < 2; i++ ) {
+ if ( mpeg_info->video[ i ].seen ) {
+ return i18n( "%1 bit/s" ).arg( mpeg_info->video[ i ].bitrate ) ;
+ }
+ }
+ }
+
+ return i18n( "n/a" );
+}
+
+
+
+const QString K3bVcdTrack::video_format()
+{
+ if ( mpeg_info->has_video ) {
+ for ( int i = 0; i < 2; i++ ) {
+ if ( mpeg_info->video[ i ].seen ) {
+ switch ( mpeg_info->video[ i ].video_format ) {
+ case 0 :
+ return i18n( "Component" );
+ break;
+ case 1 :
+ return "PAL";
+ break;
+ case 2 :
+ return "NTSC";
+ break;
+ case 3 :
+ return "SECAM";
+ break;
+ case 4 :
+ return "MAC";
+ break;
+ case 5 :
+ default:
+ return i18n( "Unspecified" );
+ kdDebug() << "K3bVcdTrack::video_format() :" << mpeg_info->video[ i ].video_format << endl;
+ break;
+ }
+ }
+ }
+ }
+ return i18n( "n/a" );
+}
+
+const QString K3bVcdTrack::video_chroma()
+{
+ if ( mpeg_info->has_video ) {
+ // MPEG1 only supports 4:2:0 Format
+ if ( version() == K3bMpegInfo::MPEG_VERS_MPEG1 )
+ return QString( "4:2:0" );
+
+ for ( int i = 0; i < 2; i++ ) {
+ if ( mpeg_info->video[ i ].seen ) {
+ switch ( mpeg_info->video[ i ].chroma_format ) {
+ case 1 :
+ return QString( "4:2:0" );
+ break;
+ case 2 :
+ return QString( "4:2:2" );
+ break;
+ case 3 :
+ return QString( "4:4:4" );
+ break;
+
+ }
+ }
+ }
+ }
+
+ return i18n( "n/a" );
+}
+
+const QString K3bVcdTrack::audio_layer()
+{
+ if ( mpeg_info->has_audio ) {
+ for ( int i = 0; i < 2; i++ ) {
+ if ( mpeg_info->audio[ i ].seen ) {
+ return QString::number( mpeg_info->audio[ i ].layer );
+ }
+ }
+ }
+
+ return i18n( "n/a" );
+}
+
+const QString K3bVcdTrack::audio_bitrate()
+{
+ if ( mpeg_info->has_audio ) {
+ for ( int i = 0; i < 2; i++ ) {
+ if ( mpeg_info->audio[ i ].seen ) {
+ return i18n( "%1 bit/s" ).arg( mpeg_info->audio[ i ].bitrate ) ;
+ }
+ }
+ }
+
+ return i18n( "n/a" );
+}
+
+const QString K3bVcdTrack::audio_sampfreq()
+{
+ if ( mpeg_info->has_audio ) {
+ for ( int i = 0; i < 2; i++ ) {
+ if ( mpeg_info->audio[ i ].seen ) {
+ return i18n( "%1 Hz" ).arg( mpeg_info->audio[ i ].sampfreq ) ;
+ }
+ }
+ }
+
+ return i18n( "n/a" );
+}
+
+const QString K3bVcdTrack::audio_mode( )
+{
+ if ( mpeg_info->has_audio ) {
+ for ( int i = 2; i >= 0; i-- )
+ if ( mpeg_info->audio[ i ].seen )
+ return QString( audio_type2str( mpeg_info->audio[ i ].version, mpeg_info->audio[ i ].mode, i ) );
+
+ }
+
+ return i18n( "n/a" );
+}
+
+const QString K3bVcdTrack::audio_copyright( )
+{
+ if ( mpeg_info->has_audio ) {
+ for ( int i = 2; i >= 0; i-- )
+ if ( mpeg_info->audio[ i ].seen )
+ if ( mpeg_info->audio[ i ].copyright )
+ return QString( "(c) " ) + ( mpeg_info->audio[ i ].original ? i18n( "original" ) : i18n( "duplicate" ) );
+ else
+ return ( mpeg_info->audio[ i ].original ? i18n( "original" ) : i18n( "duplicate" ) );
+ }
+
+ return i18n( "n/a" );
+}
+
+const QString K3bVcdTrack::mpegTypeS( bool audio )
+{
+ if ( mpeg_info->has_video && !audio ) {
+ for ( int i = 0; i < 3; i++ )
+ if ( mpeg_info->video[ i ].seen ) {
+ if ( i == 0 ) {
+ return QString( "MPEG%1 " ).arg( mpeg_info->version ) + i18n( "Motion Picture" );
+ } else {
+ return QString( "MPEG%1 " ).arg( mpeg_info->version ) + i18n( "Still Picture" );
+ }
+ }
+ }
+ if ( mpeg_info->has_audio && audio ) {
+ for ( int i = 0; i < 3; i++ )
+ if ( mpeg_info->audio[ i ].seen ) {
+ return QString( "MPEG%1 " ).arg( mpeg_info->audio[ i ].version ) + i18n( "Layer %1" ).arg( mpeg_info->audio[ i ].layer );
+ }
+ }
+
+ return i18n( "n/a" );
+}
+
+const int K3bVcdTrack::mpegType( )
+{
+ if ( mpeg_info->has_video ) {
+ for ( int i = 0; i < 3; i++ )
+ if ( mpeg_info->video[ i ].seen ) {
+ if ( i == 0 ) {
+ return 0; // MPEG_MOTION;
+ } else {
+ return 1; // MPEG_STILL;
+ }
+ }
+ }
+ if ( mpeg_info->has_audio ) {
+ for ( int i = 0; i < 3; i++ )
+ if ( mpeg_info->audio[ i ].seen )
+ return 2; // MPEG_AUDIO;
+ }
+
+ return -1; // MPEG_UNKNOWN;
+}
+
+const QString K3bVcdTrack::audio_type2str( unsigned int version, unsigned int audio_mode, unsigned int audio_type )
+{
+ kdDebug() << "K3bVcdTrack::audio_type2str() version:" << version << " audio_mode:" << audio_mode << " audio_type:" << audio_type << endl;
+
+ QString audio_types[ 3 ][ 5 ] = {
+ {
+ i18n( "unknown" ),
+ i18n( "invalid" ),
+ QString::null,
+ QString::null,
+ QString::null
+ },
+ {
+ i18n( "stereo" ),
+ i18n( "joint stereo" ),
+ i18n( "dual channel" ),
+ i18n( "single channel" )
+ },
+ {
+ QString::null,
+ i18n( "dual channel" ),
+ i18n( "surround sound" ),
+ QString::null,
+ QString::null
+ }
+ };
+ switch ( version ) {
+ case K3bMpegInfo::MPEG_VERS_MPEG1:
+ return audio_types[ 1 ][ audio_mode ];
+ break;
+
+ case K3bMpegInfo::MPEG_VERS_MPEG2:
+ if ( audio_type > 0 ) {
+ return audio_types[ 2 ][ audio_type ];
+ }
+ return audio_types[ 1 ][ audio_mode ];
+ break;
+ }
+
+ return i18n( "n/a" );
+}
+
+// convert a time in second to HH:mm:ss notation
+QString K3bVcdTrack::SecsToHMS( double duration )
+{
+ byte hours = ( byte ) ( duration / 3600 );
+ byte mins = ( byte ) ( ( duration / 60 ) - ( hours * 60 ) );
+ float secs = duration - 60 * mins - 3600 * hours;
+ if ( hours != 0 ) {
+ return QString( "%1:" ).arg( hours ).rightJustify( 3, ' ' ) + QString( "%1:" ).arg( mins ).rightJustify( 3, '0' ) + QString::number( secs, 'f', 2 );
+ }
+ if ( mins != 0 ) {
+ return QString( "%1:" ).arg( mins ).rightJustify( 3, '0' ) + QString::number( secs, 'f', 2 );
+ }
+ return QString::number( secs, 'f', 2 );
+}
+
+void K3bVcdTrack::PrintInfo()
+{
+
+ kdDebug() << "K3bVcdTrack::PrintInfo() ....................." << endl;
+ kdDebug() << " version : MPEG" << version() << endl;
+ kdDebug() << " duration : " << duration() << endl;
+ kdDebug() << " muxrate : " << muxrate() << endl;
+ kdDebug() << " video ......................................" << endl;
+ kdDebug() << " type : " << mpegTypeS() << endl;
+ kdDebug() << " resolution : " << resolution() << endl;
+ kdDebug() << " high resolution: " << highresolution() << endl;
+ kdDebug() << " frate : " << video_frate() << endl;
+ kdDebug() << " bitrate : " << video_bitrate() << endl;
+ kdDebug() << " format : " << video_format( ) << endl;
+ kdDebug() << " chroma : " << video_chroma( ) << endl;
+ kdDebug() << " audio ......................................" << endl;
+ kdDebug() << " type : " << mpegTypeS( true ) << endl;
+ kdDebug() << " mode : " << audio_mode() << endl;
+ kdDebug() << " layer : " << audio_layer() << endl;
+ kdDebug() << " bitrate : " << audio_bitrate() << endl;
+ kdDebug() << " sampfreq : " << audio_sampfreq() << endl;
+
+}
diff --git a/libk3b/projects/videocd/k3bvcdtrack.h b/libk3b/projects/videocd/k3bvcdtrack.h
new file mode 100644
index 0000000..0d9a3cf
--- /dev/null
+++ b/libk3b/projects/videocd/k3bvcdtrack.h
@@ -0,0 +1,198 @@
+/*
+*
+* $Id: k3bvcdtrack.h 619556 2007-01-03 17:38:12Z trueg $
+* Copyright (C) 2003-2004 Christian Kvasny <chris@k3b.org>
+*
+* This file is part of the K3b project.
+* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+* See the file "COPYING" for the exact licensing terms.
+*/
+
+#ifndef K3BVCDTRACK_H
+#define K3BVCDTRACK_H
+
+// Qt Includes
+#include <qstring.h>
+#include <qfileinfo.h>
+#include <qfile.h>
+#include <qptrlist.h>
+
+// Kde Includes
+#include <kio/global.h>
+
+// K3b Includes
+#include "mpeginfo/k3bmpeginfo.h"
+#include "k3b_export.h"
+class LIBK3B_EXPORT K3bVcdTrack
+{
+ public:
+ K3bVcdTrack( QPtrList<K3bVcdTrack>* parent, const QString& filename );
+ ~K3bVcdTrack();
+
+ QString fileName() const
+ {
+ return QFileInfo( m_file ).fileName();
+ }
+ QString absPath() const
+ {
+ return QFileInfo( m_file ).absFilePath();
+ }
+ KIO::filesize_t size() const;
+ int index() const;
+
+ const QString& title() const
+ {
+ return m_title;
+ }
+ void setTitle( const QString& t )
+ {
+ m_title = t;
+ }
+ bool isSegment()
+ {
+ return mpegType() == 1;
+ };
+
+
+
+ // PBC
+ enum PbcTracks { PREVIOUS, NEXT, RETURN, DEFAULT, AFTERTIMEOUT, _maxPbcTracks };
+ enum PbcTypes { DISABLED, VIDEOEND };
+
+ void addToRevRefList( K3bVcdTrack* revreftrack );
+ void delFromRevRefList( K3bVcdTrack* revreftrack );
+ bool hasRevRef();
+ void delRefToUs();
+ void delRefFromUs();
+
+ void setPbcTrack( int, K3bVcdTrack* pbctrack = 0L );
+ void setPbcNonTrack( int, int );
+ void setUserDefined( int, bool );
+ void setPlayTime( int t )
+ {
+ m_pbcplaytime = t;
+ }
+ void setWaitTime( int t )
+ {
+ m_pbcwaittime = t;
+ }
+ void setReactivity( bool b )
+ {
+ m_reactivity = b;
+ }
+ void setPbcNumKeys( const bool& b )
+ {
+ m_pbcnumkeys = b;
+ }
+ bool PbcNumKeys() const
+ {
+ return m_pbcnumkeys;
+ };
+ void setPbcNumKeysUserdefined( const bool& b )
+ {
+ m_pbcnumkeysuserdefined = b;
+ };
+ bool PbcNumKeysUserdefined() const
+ {
+ return m_pbcnumkeysuserdefined;
+ };
+
+ K3bVcdTrack* getPbcTrack( const int& );
+ int getNonPbcTrack( const int& );
+ bool isPbcUserDefined( int );
+ int getPlayTime()
+ {
+ return m_pbcplaytime;
+ }
+ int getWaitTime()
+ {
+ return m_pbcwaittime;
+ }
+ bool Reactivity()
+ {
+ return m_reactivity;
+ }
+
+ // Numeric keys
+ void setDefinedNumKey( int key, K3bVcdTrack* track )
+ {
+ m_definedkeysmap.insert( key, track );
+ }
+ void delDefinedNumKey( int key )
+ {
+ m_definedkeysmap.remove( key );
+ }
+ void delDefinedNumKey()
+ {
+ m_definedkeysmap.clear();
+ }
+ QMap<int, K3bVcdTrack*> DefinedNumKey()
+ {
+ return m_definedkeysmap;
+ }
+
+ // Mpeg Infos
+ const QString resolution();
+ const QString highresolution();
+ const QString video_frate();
+ const QString video_bitrate();
+ const QString audio_layer();
+ const QString audio_bitrate();
+ const QString audio_sampfreq();
+
+ const QString duration()
+ {
+ return SecsToHMS( mpeg_info->playing_time );
+ };
+ const int version()
+ {
+ return mpeg_info->version;
+ };
+ const unsigned long muxrate()
+ {
+ return mpeg_info->muxrate;
+ };
+ const QString video_format( );
+ const QString video_chroma( );
+ const QString audio_mode( );
+ const QString audio_copyright( );
+ const QString mpegTypeS( bool audio = false );
+ const int mpegType();
+
+ void PrintInfo();
+
+ Mpeginfo* mpeg_info;
+
+ protected:
+
+ const QString audio_type2str( unsigned int , unsigned int, unsigned int );
+ QString SecsToHMS( double );
+
+ QPtrList<K3bVcdTrack>* m_parent;
+
+ // PBC
+ QPtrList<K3bVcdTrack>* m_revreflist; // List of Tracks which points to us
+ QMap<int, K3bVcdTrack*> m_pbctrackmap; // Pbc Tracks (Previous, Next, ...)
+ QMap<int, int> m_pbcnontrackmap; // Pbc NON Track types (Previous, Next, ...)
+ QMap<int, bool> m_pbcusrdefmap; // Pbc is userdefined or defaults (Previous, Next, ...)
+ QMap<int, K3bVcdTrack*> m_definedkeysmap;
+
+ bool m_pbcnumkeys;
+ bool m_pbcnumkeysuserdefined;
+
+ int m_pbcplaytime;
+ int m_pbcwaittime;
+ /********************************************************************************/
+
+ bool m_reactivity;
+ int m_filetype;
+ QFile m_file;
+ QString m_title;
+};
+
+#endif
diff --git a/libk3b/projects/videocd/k3bvcdxmlview.cpp b/libk3b/projects/videocd/k3bvcdxmlview.cpp
new file mode 100644
index 0000000..0b6f250
--- /dev/null
+++ b/libk3b/projects/videocd/k3bvcdxmlview.cpp
@@ -0,0 +1,440 @@
+/*
+*
+* $Id: k3bvcdxmlview.cpp 619556 2007-01-03 17:38:12Z trueg $
+* Copyright (C) 2003-2004 Christian Kvasny <chris@k3b.org>
+* THX to Manfred Odenstein <odix@chello.at>
+*
+* This file is part of the K3b project.
+* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+* See the file "COPYING" for the exact licensing terms.
+*/
+
+#include <qfile.h>
+
+#include <kstandarddirs.h>
+#include <kdebug.h>
+
+
+#include "k3bvcdxmlview.h"
+#include "k3bvcdtrack.h"
+#include <k3bcore.h>
+#include <k3bversion.h>
+
+K3bVcdXmlView::K3bVcdXmlView( K3bVcdDoc* pDoc )
+{
+
+ m_doc = pDoc;
+
+}
+
+K3bVcdXmlView::~K3bVcdXmlView()
+{}
+
+bool K3bVcdXmlView::write( const QString& fname )
+{
+
+ QDomDocument xmlDoc( "videocd PUBLIC \"-//GNU//DTD VideoCD//EN\" \"http://www.gnu.org/software/vcdimager/videocd.dtd\"" );
+ // xmlDoc.appendChild( xmlDoc.createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"iso-8859-1\"" ) );
+ xmlDoc.appendChild( xmlDoc.createProcessingInstruction( "xml", "version=\"1.0\"" ) );
+
+ // create root element
+ QDomElement root = xmlDoc.createElement( "videocd" );
+ root.setAttribute( "xmlns", "http://www.gnu.org/software/vcdimager/1.0/" );
+ root.setAttribute( "class", m_doc->vcdOptions() ->vcdClass() );
+ root.setAttribute( "version", m_doc->vcdOptions() ->vcdVersion() );
+ xmlDoc.appendChild( root );
+
+ // create option elements
+
+ // Broken SVCD mode - NonCompliantMode
+ if ( m_doc->vcdOptions() ->NonCompliantMode() ) {
+ QDomElement elemOption;
+ elemOption = addSubElement( xmlDoc, root, "option" );
+ elemOption.setAttribute( "name", "svcd vcd30 mpegav" );
+ elemOption.setAttribute( "value", "true" );
+
+ elemOption = addSubElement( xmlDoc, root, "option" );
+ elemOption.setAttribute( "name", "svcd vcd30 entrysvd" );
+ elemOption.setAttribute( "value", "true" );
+ }
+
+ // VCD3.0 track interpretation
+ if ( m_doc->vcdOptions() ->VCD30interpretation() ) {
+ QDomElement elemOption;
+ elemOption = addSubElement( xmlDoc, root, "option" );
+ elemOption.setAttribute( "name", "svcd vcd30 tracksvd" );
+ elemOption.setAttribute( "value", "true" );
+ }
+
+ // Relaxed aps
+ if ( m_doc->vcdOptions() ->RelaxedAps() ) {
+ QDomElement elemOption;
+ elemOption = addSubElement( xmlDoc, root, "option" );
+ elemOption.setAttribute( "name", "relaxed aps" );
+ elemOption.setAttribute( "value", "true" );
+ }
+
+ // Update scan offsets
+ if ( m_doc->vcdOptions() ->UpdateScanOffsets() ) {
+ QDomElement elemOption;
+ elemOption = addSubElement( xmlDoc, root, "option" );
+ elemOption.setAttribute( "name", "update scan offsets" );
+ elemOption.setAttribute( "value", "true" );
+
+ }
+
+ // Gaps & Margins
+ if ( m_doc->vcdOptions() ->UseGaps() ) {
+ QDomElement elemOption;
+ elemOption = addSubElement( xmlDoc, root, "option" );
+ elemOption.setAttribute( "name", "leadout pregap" );
+ elemOption.setAttribute( "value", m_doc->vcdOptions() ->PreGapLeadout() );
+
+ elemOption = addSubElement( xmlDoc, root, "option" );
+ elemOption.setAttribute( "name", "track pregap" );
+ elemOption.setAttribute( "value", m_doc->vcdOptions() ->PreGapTrack() );
+
+ if ( m_doc->vcdOptions() ->vcdClass() == "vcd" ) {
+ elemOption = addSubElement( xmlDoc, root, "option" );
+ elemOption.setAttribute( "name", "track front margin" );
+ elemOption.setAttribute( "value", m_doc->vcdOptions() ->FrontMarginTrack() );
+
+ elemOption = addSubElement( xmlDoc, root, "option" );
+ elemOption.setAttribute( "name", "track rear margin" );
+ elemOption.setAttribute( "value", m_doc->vcdOptions() ->RearMarginTrack() );
+ } else {
+ elemOption = addSubElement( xmlDoc, root, "option" );
+ elemOption.setAttribute( "name", "track front margin" );
+ elemOption.setAttribute( "value", m_doc->vcdOptions() ->FrontMarginTrackSVCD() );
+
+ elemOption = addSubElement( xmlDoc, root, "option" );
+ elemOption.setAttribute( "name", "track rear margin" );
+ elemOption.setAttribute( "value", m_doc->vcdOptions() ->RearMarginTrackSVCD() );
+ }
+
+ }
+
+ // create info element
+ QDomElement elemInfo = addSubElement( xmlDoc, root, "info" );
+ addSubElement( xmlDoc, elemInfo, "album-id", m_doc->vcdOptions() ->albumId().upper() );
+ addSubElement( xmlDoc, elemInfo, "volume-count", m_doc->vcdOptions() ->volumeCount() );
+ addSubElement( xmlDoc, elemInfo, "volume-number", m_doc->vcdOptions() ->volumeNumber() );
+ addSubElement( xmlDoc, elemInfo, "restriction", m_doc->vcdOptions() ->Restriction() );
+
+ // create pvd element
+ QDomElement elemPvd = addSubElement( xmlDoc, root, "pvd" );
+ addSubElement( xmlDoc, elemPvd, "volume-id", m_doc->vcdOptions() ->volumeId().upper() );
+ addSubElement( xmlDoc, elemPvd, "system-id", m_doc->vcdOptions() ->systemId() );
+ addSubElement( xmlDoc, elemPvd, "application-id", m_doc->vcdOptions() ->applicationId() );
+ addSubElement( xmlDoc, elemPvd, "preparer-id", QString( "K3b - Version %1" ).arg( k3bcore->version() ).upper() );
+ addSubElement( xmlDoc, elemPvd, "publisher-id", m_doc->vcdOptions() ->publisher().upper() );
+
+
+ // create filesystem element
+ QDomElement elemFileSystem = addSubElement( xmlDoc, root, "filesystem" );
+
+ // SEGMENT folder, some standalone DVD-Player need this
+ if ( !m_doc->vcdOptions() ->haveSegments() && m_doc->vcdOptions() ->SegmentFolder() )
+ addFolderElement( xmlDoc, elemFileSystem, "SEGMENT" );
+
+ // create cdi element
+ if ( m_doc->vcdOptions() ->CdiSupport() ) {
+ QDomElement elemFolder = addFolderElement( xmlDoc, elemFileSystem, "CDI" );
+
+ addFileElement( xmlDoc, elemFolder, locate( "data", "k3b/cdi/cdi_imag.rtf" ), "CDI_IMAG.RTF", true );
+ addFileElement( xmlDoc, elemFolder, locate( "data", "k3b/cdi/cdi_text.fnt" ), "CDI_TEXT.FNT" );
+ addFileElement( xmlDoc, elemFolder, locate( "data", "k3b/cdi/cdi_vcd.app" ), "CDI_VCD.APP" );
+
+ QString usercdicfg = locateLocal( "appdata", "cdi/cdi_vcd.cfg" );
+ if ( QFile::exists( usercdicfg ) )
+ addFileElement( xmlDoc, elemFolder, usercdicfg, "CDI_VCD.CFG" );
+ else
+ addFileElement( xmlDoc, elemFolder, locate( "data", "k3b/cdi/cdi_vcd.cfg" ), "CDI_VCD.CFG" );
+ }
+
+ // sequence-items element & segment-items element
+ QDomElement elemsequenceItems;
+ QDomElement elemsegmentItems;
+
+ // sequence-item element & segment-item element
+ QDomElement elemsequenceItem;
+ QDomElement elemsegmentItem;
+
+ // if we have segments, elemsegmentItems must be before any sequence in xml file order
+ if ( m_doc->vcdOptions()->haveSegments() )
+ elemsegmentItems = addSubElement( xmlDoc, root, "segment-items" );
+
+ // sequence must always available ...
+ elemsequenceItems = addSubElement( xmlDoc, root, "sequence-items" );
+ // if we have no sequence (photo (s)vcd) we must add a dummy sequence they inform the user to turn on pbc on there videoplayer
+ if ( !m_doc->vcdOptions()->haveSequence() ) {
+ QString filename;
+ if ( m_doc->vcdOptions()->mpegVersion() == 1 )
+ filename = locate( "data", "k3b/extra/k3bphotovcd.mpg" );
+ else
+ filename = locate( "data", "k3b/extra/k3bphotosvcd.mpg" );
+
+ elemsequenceItem = addSubElement( xmlDoc, elemsequenceItems, "sequence-item" );
+ elemsequenceItem.setAttribute( "src", QString( "%1" ).arg( QFile::encodeName( filename ) ) );
+ elemsequenceItem.setAttribute( "id", "sequence-000" );
+ // default entry
+ QDomElement elemdefaultEntry;
+ elemdefaultEntry = addSubElement( xmlDoc, elemsequenceItem, "default-entry" );
+ elemdefaultEntry.setAttribute( "id", "entry-000" );
+ }
+
+
+ // pbc
+ QDomElement elemPbc;
+
+ // Add Tracks to XML
+ QPtrListIterator<K3bVcdTrack> it( *m_doc->tracks() );
+ for ( ; it.current(); ++it ) {
+ if ( !it.current() ->isSegment() ) {
+ QString seqId = QString::number( it.current() ->index() ).rightJustify( 3, '0' );
+
+ elemsequenceItem = addSubElement( xmlDoc, elemsequenceItems, "sequence-item" );
+ elemsequenceItem.setAttribute( "src", QString( "%1" ).arg( QFile::encodeName( it.current() ->absPath() ) ) );
+ elemsequenceItem.setAttribute( "id", QString( "sequence-%1" ).arg( seqId ) );
+
+ // default entry
+ QDomElement elemdefaultEntry;
+ elemdefaultEntry = addSubElement( xmlDoc, elemsequenceItem, "default-entry" );
+ elemdefaultEntry.setAttribute( "id", QString( "entry-%1" ).arg( seqId ) );
+
+ } else {
+ // sequence-items element needs at least one segment to fit the XML
+ elemsegmentItem = addSubElement( xmlDoc, elemsegmentItems, "segment-item" );
+ elemsegmentItem.setAttribute( "src", QString( "%1" ).arg( QFile::encodeName( it.current() ->absPath() ) ) );
+ elemsegmentItem.setAttribute( "id", QString( "segment-%1" ).arg( QString::number( it.current() ->index() ).rightJustify( 3, '0' ) ) );
+
+ }
+ }
+ for ( it.toFirst(); it.current(); ++it ) {
+
+ if ( m_doc->vcdOptions() ->PbcEnabled() ) {
+ if ( elemPbc.isNull() )
+ elemPbc = addSubElement( xmlDoc, root, "pbc" );
+
+ doPbc( xmlDoc, elemPbc, it.current() );
+ }
+ }
+
+ if ( ! elemPbc.isNull() ) {
+ QDomElement elemEndlist = addSubElement( xmlDoc, elemPbc, "endlist" );
+ elemEndlist.setAttribute( "id", "end" );
+ elemEndlist.setAttribute( "rejected", "true" );
+ }
+
+ m_xmlstring = xmlDoc.toString();
+ kdDebug() << QString( "(K3bVcdXmlView) Write Data to %1:" ).arg( fname ) << endl;
+
+ QFile xmlFile( fname );
+ if ( xmlFile.open( IO_WriteOnly ) ) {
+ QTextStream ts( & xmlFile );
+ ts << m_xmlstring;
+ xmlFile.close();
+ return true;
+ }
+
+ return false;
+}
+
+void K3bVcdXmlView::addComment( QDomDocument& doc, QDomElement& parent, const QString& text )
+{
+ QDomComment comment = doc.createComment( text );
+ parent.appendChild( comment );
+}
+
+QDomElement K3bVcdXmlView::addSubElement( QDomDocument& doc, QDomElement& parent, const QString& name, const QString& value )
+{
+ QDomElement element = doc.createElement( name );
+ parent.appendChild( element );
+ if ( !value.isNull() ) {
+ QDomText t = doc.createTextNode( value );
+ element.appendChild( t );
+ }
+ return element;
+}
+
+QDomElement K3bVcdXmlView::addSubElement( QDomDocument& doc, QDomElement& parent, const QString& name, const int& value )
+{
+ QDomElement element = doc.createElement( name );
+ parent.appendChild( element );
+ if ( value >= -1 ) {
+ QDomText t = doc.createTextNode( QString( "%1" ).arg( value ) );
+ element.appendChild( t );
+ }
+ return element;
+}
+
+QDomElement K3bVcdXmlView::addFolderElement( QDomDocument& doc, QDomElement& parent, const QString& name )
+{
+ QDomElement elemFolder = addSubElement( doc, parent, "folder" );
+ addSubElement( doc, elemFolder, "name", name );
+
+ return elemFolder;
+}
+
+void K3bVcdXmlView::addFileElement( QDomDocument& doc, QDomElement& parent, const QString& src, const QString& name, bool mixed )
+{
+ QDomElement elemFile = addSubElement( doc, parent, "file" );
+ elemFile.setAttribute( "src", QString( "%1" ).arg( src ) );
+ if ( mixed )
+ elemFile.setAttribute( "format", "mixed" );
+
+ addSubElement( doc, elemFile, "name", name );
+}
+
+void K3bVcdXmlView::doPbc( QDomDocument& doc, QDomElement& parent, K3bVcdTrack* track )
+{
+ QString ref = ( track->isSegment() ) ? "segment" : "sequence";
+
+ QDomElement elemSelection = addSubElement( doc, parent, "selection" );
+ elemSelection.setAttribute( "id", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( track->index() ).rightJustify( 3, '0' ) ) );
+
+ setNumkeyBSN( doc, elemSelection, track );
+
+ for ( int i = 0; i < K3bVcdTrack::_maxPbcTracks; i++ ) {
+ QDomElement elemPbcSelectionPNRDT;
+
+ if ( track->getPbcTrack( i ) ) {
+ int index = track->getPbcTrack( i ) ->index();
+ QString ref = ( track->getPbcTrack( i ) ->isSegment() ) ? "segment" : "sequence";
+
+ switch ( i ) {
+ case K3bVcdTrack::PREVIOUS:
+ elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "prev" );
+ elemPbcSelectionPNRDT.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( index ).rightJustify( 3, '0' ) ) );
+ break;
+ case K3bVcdTrack::NEXT:
+ elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "next" );
+ elemPbcSelectionPNRDT.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( index ).rightJustify( 3, '0' ) ) );
+ break;
+ case K3bVcdTrack::RETURN:
+ elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "return" );
+ elemPbcSelectionPNRDT.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( index ).rightJustify( 3, '0' ) ) );
+ break;
+ case K3bVcdTrack::DEFAULT:
+ elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "default" );
+ elemPbcSelectionPNRDT.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( index ).rightJustify( 3, '0' ) ) );
+ break;
+ case K3bVcdTrack::AFTERTIMEOUT:
+ if ( track->getWaitTime() >= 0 ) {
+ elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "timeout" );
+ elemPbcSelectionPNRDT.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( index ).rightJustify( 3, '0' ) ) );
+ }
+ break;
+ }
+ } else {
+ // jump to <endlist> otherwise do noop while disabled
+ if ( track->getNonPbcTrack( i ) == K3bVcdTrack::VIDEOEND ) {
+ switch ( i ) {
+ case K3bVcdTrack::PREVIOUS:
+ elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "prev" );
+ elemPbcSelectionPNRDT.setAttribute( "ref", "end" );
+ break;
+ case K3bVcdTrack::NEXT:
+ elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "next" );
+ elemPbcSelectionPNRDT.setAttribute( "ref", "end" );
+ break;
+ case K3bVcdTrack::RETURN:
+ elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "return" );
+ elemPbcSelectionPNRDT.setAttribute( "ref", "end" );
+ break;
+ case K3bVcdTrack::DEFAULT:
+ elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "default" );
+ elemPbcSelectionPNRDT.setAttribute( "ref", "end" );
+ break;
+ case K3bVcdTrack::AFTERTIMEOUT:
+ if ( track->getWaitTime() >= 0 ) {
+ elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "timeout" );
+ elemPbcSelectionPNRDT.setAttribute( "ref", "end" );
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ addSubElement( doc, elemSelection, "wait", track->getWaitTime() );
+ QDomElement loop = addSubElement( doc, elemSelection, "loop", track->getPlayTime() );
+ if ( track->Reactivity() )
+ loop.setAttribute( "jump-timing", "delayed" );
+ else
+ loop.setAttribute( "jump-timing", "immediate" );
+
+ addSubElement( doc, elemSelection, "play-item" ).setAttribute( "ref", QString( "%1-%2" ).arg( ref ).arg( QString::number( track->index() ).rightJustify( 3, '0' ) ) );
+
+ setNumkeySEL( doc, elemSelection, track );
+}
+
+void K3bVcdXmlView::setNumkeyBSN( QDomDocument& doc, QDomElement& parent, K3bVcdTrack* track )
+{
+ if ( track->PbcNumKeys() ) {
+ if ( track->PbcNumKeysUserdefined() ) {
+ QMap<int, K3bVcdTrack*> numKeyMap = track->DefinedNumKey();
+ QMap<int, K3bVcdTrack*>::const_iterator trackIt;
+
+ m_startkey = 0;
+ trackIt = numKeyMap.begin();
+ if ( trackIt != numKeyMap.end() )
+ m_startkey = trackIt.key();
+
+ if ( m_startkey > 0 )
+ addSubElement( doc, parent, "bsn", m_startkey );
+ else // user has no numKeys defined for this track
+ track->setPbcNumKeys( false );
+
+ } else {
+ // default start with key #1
+ addSubElement( doc, parent, "bsn", 1 );
+ }
+ }
+}
+
+void K3bVcdXmlView::setNumkeySEL( QDomDocument& doc, QDomElement& parent, K3bVcdTrack* track )
+{
+ if ( track->PbcNumKeys() ) {
+ QDomElement elemPbcSelectionNumKeySEL;
+ QString ref = ( track->isSegment() ) ? "segment" : "sequence";
+ int none = m_startkey;
+ if ( track->PbcNumKeysUserdefined() ) {
+ QMap<int, K3bVcdTrack*> numKeyMap = track->DefinedNumKey();
+ QMap<int, K3bVcdTrack*>::const_iterator trackIt;
+
+ for ( trackIt = numKeyMap.begin(); trackIt != numKeyMap.end(); ++trackIt ) {
+
+ kdDebug() << QString( "trackIt key: %1 none: %2" ).arg( trackIt.key() ).arg( none ) << endl;
+ while ( none < trackIt.key() ) {
+ elemPbcSelectionNumKeySEL = addSubElement( doc, parent, "select" );
+ elemPbcSelectionNumKeySEL.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( track->index() ).rightJustify( 3, '0' ) ) );
+ addComment( doc, parent, QString( "key %1 -> %2 (normal none)" ).arg( none ).arg( QFile::encodeName( track->absPath() ) ) );
+ none++;
+ }
+
+ if ( trackIt.data() ) {
+ QString ref = ( trackIt.data() ->isSegment() ) ? "segment" : "sequence";
+ elemPbcSelectionNumKeySEL = addSubElement( doc, parent, "select" );
+ elemPbcSelectionNumKeySEL.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( trackIt.data() ->index() ).rightJustify( 3, '0' ) ) );
+ addComment( doc, parent, QString( "key %1 -> %2" ).arg( trackIt.key() ).arg( QFile::encodeName( trackIt.data() ->absPath() ) ) );
+ } else {
+ elemPbcSelectionNumKeySEL = addSubElement( doc, parent, "select" );
+ elemPbcSelectionNumKeySEL.setAttribute( "ref", "end" );
+ addComment( doc, parent, QString( "key %1 -> end" ).arg( trackIt.key() ) );
+ }
+ none++;
+ }
+ } else {
+ // default reference to itSelf
+ elemPbcSelectionNumKeySEL = addSubElement( doc, parent, "select" );
+ elemPbcSelectionNumKeySEL.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( track->index() ).rightJustify( 3, '0' ) ) );
+ }
+ }
+}
+
diff --git a/libk3b/projects/videocd/k3bvcdxmlview.h b/libk3b/projects/videocd/k3bvcdxmlview.h
new file mode 100644
index 0000000..d99549b
--- /dev/null
+++ b/libk3b/projects/videocd/k3bvcdxmlview.h
@@ -0,0 +1,59 @@
+/*
+*
+* $Id: k3bvcdxmlview.h 619556 2007-01-03 17:38:12Z trueg $
+* Copyright (C) 2003-2004 Christian Kvasny <chris@k3b.org>
+* THX to Manfred Odenstein <odix@chello.at>
+*
+* This file is part of the K3b project.
+* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+* See the file "COPYING" for the exact licensing terms.
+*/
+
+#ifndef K3B_VCD_XMLVIEW_H
+#define K3B_VCD_XMLVIEW_H
+
+#include <qstring.h>
+#include <qdom.h>
+#include <ktempfile.h>
+
+#include <k3bvcddoc.h>
+
+class K3bVcdOptions;
+class K3bVcdTrack;
+
+
+class K3bVcdXmlView
+{
+
+ public:
+ K3bVcdXmlView( K3bVcdDoc* );
+ ~K3bVcdXmlView();
+
+ bool write( const QString& );
+ QString xmlString()
+ {
+ return m_xmlstring;
+ }
+
+ private:
+ QString m_xmlstring;
+
+ void addComment( QDomDocument& doc, QDomElement& parent, const QString& text );
+ QDomElement addSubElement( QDomDocument&, QDomElement&, const QString& name, const QString& value = QString::null );
+ QDomElement addSubElement( QDomDocument&, QDomElement&, const QString& name, const int& value );
+
+ QDomElement addFolderElement( QDomDocument&, QDomElement&, const QString& name );
+ void addFileElement( QDomDocument&, QDomElement&, const QString& src, const QString& name, bool mixed = false );
+ void doPbc( QDomDocument&, QDomElement&, K3bVcdTrack* );
+ void setNumkeyBSN( QDomDocument& , QDomElement&, K3bVcdTrack* );
+ void setNumkeySEL( QDomDocument& , QDomElement&, K3bVcdTrack* );
+ K3bVcdDoc* m_doc;
+ int m_startkey;
+};
+
+#endif
diff --git a/libk3b/projects/videocd/mpeginfo/Makefile.am b/libk3b/projects/videocd/mpeginfo/Makefile.am
new file mode 100644
index 0000000..af1b06b
--- /dev/null
+++ b/libk3b/projects/videocd/mpeginfo/Makefile.am
@@ -0,0 +1,5 @@
+INCLUDES = $(all_includes)
+noinst_LTLIBRARIES = libmpeginfo.la
+
+libmpeginfo_la_SOURCES = k3bmpeginfo.cpp
+
diff --git a/libk3b/projects/videocd/mpeginfo/k3bmpeginfo.cpp b/libk3b/projects/videocd/mpeginfo/k3bmpeginfo.cpp
new file mode 100644
index 0000000..583a0aa
--- /dev/null
+++ b/libk3b/projects/videocd/mpeginfo/k3bmpeginfo.cpp
@@ -0,0 +1,844 @@
+/*
+*
+* $Id: k3bmpeginfo.cpp 619556 2007-01-03 17:38:12Z trueg $
+* Copyright (C) 2003-2004 Christian Kvasny <chris@k3b.org>
+*
+* This file is part of the K3b project.
+* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+* See the file "COPYING" for the exact licensing terms.
+*/
+
+// kde includes
+#include <klocale.h>
+
+// k3b includes
+#include "k3bmpeginfo.h"
+
+static const double frame_rates[ 16 ] =
+ {
+ 0.0, 24000.0 / 1001, 24.0, 25.0,
+ 30000.0 / 1001, 30.0, 50.0, 60000.0 / 1001,
+ 60.0, 0.0,
+ };
+
+K3bMpegInfo::K3bMpegInfo( const char* filename )
+ : m_mpegfile( 0 ),
+ m_filename( filename ),
+ m_done( false ),
+ m_buffstart( 0 ),
+ m_buffend( 0 ),
+ m_buffer( 0 ),
+ m_initial_TS( 0.0 )
+{
+
+ mpeg_info = new Mpeginfo();
+
+ m_mpegfile = fopen( filename, "rb" );
+
+ if ( m_mpegfile == 0 ) {
+ kdDebug() << QString( "Unable to open %1" ).arg( m_filename ) << endl;
+ return ;
+ }
+
+ if ( fseeko( m_mpegfile, 0, SEEK_END ) ) {
+ kdDebug() << QString( "Unable to seek in file %1" ).arg( m_filename ) << endl;
+ return ;
+ }
+
+ llong lof = ftello( m_mpegfile );
+
+ if ( lof == -1 ) {
+ kdDebug() << QString( "Seeking to end of input file %1 failed." ).arg( m_filename ) << endl;
+ //give up..
+ return ;
+ } else
+ m_filesize = lof;
+
+ // nothing to do on an empty file
+ if ( !m_filesize ) {
+ kdDebug() << QString( "File %1 is empty." ).arg( m_filename ) << endl;
+ m_error_string = i18n( "File %1 is empty." ).arg( m_filename );
+ return ;
+ }
+
+ m_buffer = new byte[ BUFFERSIZE ];
+
+ MpegParsePacket ( );
+
+}
+
+K3bMpegInfo::~K3bMpegInfo()
+{
+ if ( m_buffer ) {
+ delete[] m_buffer;
+ }
+ if ( m_mpegfile ) {
+ fclose( m_mpegfile );
+ }
+
+ delete mpeg_info;
+}
+bool K3bMpegInfo::MpegParsePacket ()
+{
+
+ /* verify the packet begins with a pack header */
+ if ( !EnsureMPEG( 0, MPEG_PACK_HEADER_CODE ) ) {
+ llong code = GetNBytes( 0, 4 );
+
+ kdDebug() << QString( "(K3bMpegInfo::mpeg_parse_packet ()) pack header code 0x%1 expected, but 0x%2 found" ).arg( 0x00000100 + MPEG_PACK_HEADER_CODE, 0, 16 ).arg( code, 0, 16 ) << endl;
+
+ if ( code == 0x00000100 + MPEG_SEQUENCE_CODE ) {
+ kdDebug() << "...this looks like a elementary video stream but a multiplexed program stream was required." << endl;
+ m_error_string = i18n( "This looks like a elementary video stream but a multiplexed program stream was required." );
+ }
+
+ if ( ( 0xfff00000 & code ) == 0xfff00000 ) {
+ kdDebug() << "...this looks like a elementary audio stream but a multiplexed program stream was required." << endl;
+ m_error_string = i18n( "This looks like a elementary audio stream but a multiplexed program stream was required." );
+ }
+
+ if ( code == 0x52494646 ) {
+ kdDebug() << "...this looks like a RIFF header but a plain multiplexed program stream was required." << endl;
+ m_error_string = i18n( "This looks like a RIFF header but a plain multiplexed program stream was required." );
+ }
+
+ return false;
+ }
+
+
+ /* take a look at the pack header */
+ int offset = 0;
+ while ( GetByte( offset ) == 0x00 )
+ offset ++;
+ //here we're on the first non null byte let's get back to leave two zeros (packet start code)
+ offset -= 2;
+
+ if ( offset != 0 ) {
+ // we actually skipped some zeroes
+ kdDebug() << QString( "Skipped %1 zeroes at start of file" ).arg( offset ) << endl;
+ }
+
+ // here while schleife
+ while ( offset != -1 ) {
+ offset = MpegParsePacket( offset );
+ }
+
+ /*
+ int pkt = 0;
+ offset = FindNextMarker( 0, MPEG_PACK_HEADER_CODE );
+
+ while ( offset != -1 ) {
+ pkt++;
+ offset = FindNextMarker( offset+1, MPEG_PACK_HEADER_CODE );
+ }
+
+ kdDebug() << "Pkt found: " << pkt << endl;
+ */
+
+ //seek the file duration by fetching the last PACK
+ //and reading its timestamp
+ llong last_pack = bdFindNextMarker( m_filesize - 13, MPEG_PACK_HEADER_CODE );
+ // -12 because a PACK is at least 12 bytes
+ double duration;
+ last_pack += 4;
+ int bits = GetByte( last_pack ) >> 4;
+
+ if ( bits == 0x2 ) /* %0010 ISO11172-1 */
+ {
+ duration = ReadTS( last_pack );
+ } else if ( bits >> 2 == 0x1 ) /* %01xx ISO13818-1 */
+ {
+ duration = ReadTSMpeg2( last_pack );
+ } else {
+ kdDebug() << QString( "no timestamp found" ) << endl;
+ duration = ReadTS( last_pack );
+ }
+
+ mpeg_info->playing_time = duration - m_initial_TS;
+
+
+ if ( !mpeg_info->has_video )
+ for ( int i = 0; i < 2; i++ )
+ if ( mpeg_info->video[ i ].seen )
+ mpeg_info->has_video = true;
+
+ if ( !mpeg_info->has_audio )
+ for ( int i = 0; i < 2; i++ )
+ if ( mpeg_info->audio[ i ].seen )
+ mpeg_info->has_audio = true;
+
+ return true;
+}
+
+llong K3bMpegInfo::MpegParsePacket ( llong offset )
+{
+ byte mark = 0;
+ uint size = 0;
+
+ /* continue until start code seen */
+ offset = FindNextMarker( offset, &mark );
+
+ if ( offset < 0 )
+ return offset;
+
+ switch ( mark ) {
+ int bits;
+
+ case MPEG_PACK_HEADER_CODE:
+ // kdDebug() << QString( "MPEG_PACK_HEADER_CODE @ %1" ).arg( offset ) << endl;
+
+ offset += 4;
+
+ if ( mpeg_info->version != MPEG_VERS_INVALID )
+ break;
+
+ bits = GetByte( offset ) >> 4;
+
+ if ( bits == 0x2 ) /* %0010 ISO11172-1 */
+ {
+ mpeg_info->version = MPEG_VERS_MPEG1;
+
+ unsigned long muxrate = 0;
+
+ muxrate = ( GetByte( offset + 5 ) & 0x7F ) << 15;
+ muxrate |= ( GetByte( offset + 6 ) << 7 );
+ muxrate |= ( GetByte( offset + 7 ) >> 1 );
+
+ mpeg_info->muxrate = muxrate * 50 * 8;
+
+ if ( m_initial_TS == 0.0 )
+ {
+ m_initial_TS = ReadTS( offset );
+ kdDebug() << QString( "Initial TS = %1" ).arg( m_initial_TS ) << endl;
+ }
+
+ } else if ( bits >> 2 == 0x1 ) /* %01xx ISO13818-1 */
+ {
+ mpeg_info->version = MPEG_VERS_MPEG2;
+
+ unsigned long muxrate = 0;
+ muxrate = GetByte( offset + 6 ) << 14;
+ muxrate |= GetByte( offset + 7 ) << 6;
+ muxrate |= GetByte( offset + 8 ) >> 2;
+
+ mpeg_info->muxrate = muxrate * 50 * 8;
+
+ if ( m_initial_TS == 0.0 )
+ {
+ m_initial_TS = ReadTSMpeg2( offset );
+ kdDebug() << QString( "Initial TS = %1" ).arg( m_initial_TS ) << endl;
+ }
+
+ } else {
+ kdDebug() << QString( "packet not recognized as either version 1 or 2 (%1)" ).arg( bits ) << endl;
+ mpeg_info->version = MPEG_VERS_INVALID;
+ return -1;
+ }
+ break;
+
+ case MPEG_SYSTEM_HEADER_CODE:
+ case MPEG_PAD_CODE:
+ case MPEG_PRIVATE_1_CODE:
+ case MPEG_VIDEO_E0_CODE:
+ case MPEG_VIDEO_E1_CODE:
+ case MPEG_VIDEO_E2_CODE:
+ case MPEG_AUDIO_C0_CODE:
+ case MPEG_AUDIO_C1_CODE:
+ case MPEG_AUDIO_C2_CODE:
+
+ offset += 4;
+ size = GetSize( offset );
+ offset += 2;
+ // kdDebug() << QString( "offset = %1, size = %2" ).arg( offset ).arg( size ) << endl;
+
+ switch ( mark ) {
+ case MPEG_SYSTEM_HEADER_CODE:
+ // kdDebug() << QString( "Systemheader: %1" ).arg( m_code, 0, 16 ) << endl;
+ break;
+
+ case MPEG_VIDEO_E0_CODE:
+ case MPEG_VIDEO_E1_CODE:
+ case MPEG_VIDEO_E2_CODE:
+ ParseVideo( offset, mark );
+ // _analyze_video_pes (code & 0xff, buf + pos, size, !parse_pes, ctx);
+ if ( mpeg_info->has_video && mpeg_info->has_audio ) {
+ return -1;
+ } else if ( mark == MPEG_VIDEO_E0_CODE || mpeg_info->version == MPEG_VERS_MPEG2 && mark == MPEG_VIDEO_E1_CODE || mpeg_info->version == MPEG_VERS_MPEG1 && mark == MPEG_VIDEO_E2_CODE ) {
+ mpeg_info->has_video = true;
+ offset = FindNextAudio( offset );
+ }
+ break;
+ case MPEG_AUDIO_C0_CODE:
+ case MPEG_AUDIO_C1_CODE:
+ case MPEG_AUDIO_C2_CODE:
+ offset = SkipPacketHeader( offset - 6 );
+ ParseAudio( offset, mark );
+ // audio packet doesn't begin with 0xFFF
+ if ( !mpeg_info->audio[ GetAudioIdx( mark ) ].seen ) {
+ int a_idx = GetAudioIdx( mark );
+ while ( ( offset < m_filesize - 10 ) && !mpeg_info->audio[ a_idx ].seen ) {
+ if ( ( GetByte( offset ) == 0xFF ) && ( GetByte( offset + 1 ) & 0xF0 ) == 0xF0 )
+ ParseAudio( offset, mark );
+ offset++;
+ }
+ }
+
+ mpeg_info->has_audio = true;
+ if ( mpeg_info->has_video )
+ return -1;
+
+ offset = FindNextVideo( offset );
+ break;
+
+ case MPEG_PRIVATE_1_CODE:
+ kdDebug() << QString( "PrivateCode: %1" ).arg( mark, 0, 16 ) << endl;
+ break;
+ }
+ break;
+
+ case MPEG_PROGRAM_END_CODE:
+ kdDebug() << QString( "ProgramEndCode: %1" ).arg( mark, 0, 16 ) << endl;
+ offset += 4;
+ break;
+
+ case MPEG_PICTURE_CODE:
+ kdDebug() << QString( "PictureCode: %1" ).arg( mark, 0, 16 ) << endl;
+ offset += 3;
+ break;
+
+ default:
+ offset += 4;
+ break;
+ }
+
+ return offset;
+}
+
+byte K3bMpegInfo::GetByte( llong offset )
+{
+ unsigned long nread;
+ if ( ( offset >= m_buffend ) || ( offset < m_buffstart ) ) {
+
+ if ( fseeko( m_mpegfile, offset, SEEK_SET ) ) {
+ kdDebug() << QString( "could not get seek to offset (%1) in file %2 (size:%3)" ).arg( offset ).arg( m_filename ).arg( m_filesize ) << endl;
+ return 0x11;
+ }
+ nread = fread( m_buffer, 1, BUFFERSIZE, m_mpegfile );
+ m_buffstart = offset;
+ m_buffend = offset + nread;
+ if ( ( offset >= m_buffend ) || ( offset < m_buffstart ) ) {
+ // weird
+ kdDebug() << QString( "could not get offset %1 in file %2 [%3]" ).arg( offset ).arg( m_filename ).arg( m_filesize ) << endl;
+ return 0x11;
+ }
+ }
+ return m_buffer[ offset - m_buffstart ];
+}
+
+// same as above but improved for backward search
+byte K3bMpegInfo::bdGetByte( llong offset )
+{
+ unsigned long nread;
+ if ( ( offset >= m_buffend ) || ( offset < m_buffstart ) ) {
+ llong start = offset - BUFFERSIZE + 1 ;
+ start = start >= 0 ? start : 0;
+
+ fseeko( m_mpegfile, start, SEEK_SET );
+
+ nread = fread( m_buffer, 1, BUFFERSIZE, m_mpegfile );
+ m_buffstart = start;
+ m_buffend = start + nread;
+ if ( ( offset >= m_buffend ) || ( offset < m_buffstart ) ) {
+ // weird
+ kdDebug() << QString( "could not get offset %1 in file %2 [%3]" ).arg( offset ).arg( m_filename ).arg( m_filesize ) << endl;
+
+ return 0x11;
+ }
+ }
+ return m_buffer[ offset - m_buffstart ];
+}
+
+
+llong K3bMpegInfo::GetNBytes( llong offset, int n )
+{
+ llong nbytes = 0;
+ n--;
+ for ( int i = 0; i < n; i++ )
+ ( ( char* ) & nbytes ) [ n - i ] = GetByte( offset + i );
+
+ return nbytes;
+
+}
+
+// get a two byte size
+unsigned short int K3bMpegInfo::GetSize( llong offset )
+{
+ return GetByte( offset ) * 256 + GetByte( offset + 1 );
+ // return GetNBytes( offset, 2 );
+
+}
+
+bool K3bMpegInfo::EnsureMPEG( llong offset, byte mark )
+{
+ if ( ( GetByte( offset ) == 0x00 ) &&
+ ( GetByte( offset + 1 ) == 0x00 ) &&
+ ( GetByte( offset + 2 ) == 0x01 ) &&
+ ( GetByte( offset + 3 ) == mark ) )
+ return true;
+ else
+ return false;
+}
+
+
+// find next 0x 00 00 01 xx sequence, returns offset or -1 on err
+llong K3bMpegInfo::FindNextMarker( llong from )
+{
+ llong offset;
+ for ( offset = from; offset < ( m_filesize - 4 ); offset++ ) {
+ if (
+ ( GetByte( offset + 0 ) == 0x00 ) &&
+ ( GetByte( offset + 1 ) == 0x00 ) &&
+ ( GetByte( offset + 2 ) == 0x01 ) ) {
+ return offset;
+ }
+ }
+ return -1;
+}
+
+// find next 0x 00 00 01 xx sequence, returns offset or -1 on err and
+// change mark to xx
+llong K3bMpegInfo::FindNextMarker( llong from, byte* mark )
+{
+ llong offset = FindNextMarker( from );
+ if ( offset >= 0 ) {
+ *mark = GetByte( offset + 3 );
+ return offset;
+ } else {
+ return -1;
+ }
+}
+
+// find next 0X00 00 01 mark
+llong K3bMpegInfo::FindNextMarker( llong from, byte mark )
+{
+ llong offset = from;
+ while ( offset >= 0 ) {
+ offset = FindNextMarker( offset );
+ if ( offset < 0 ) {
+ return -1;
+ }
+ if ( EnsureMPEG( offset, mark ) ) {
+ return offset;
+ } else
+ offset++;
+ }
+
+ //shouldn't be here
+ return -1;
+}
+
+llong K3bMpegInfo::bdFindNextMarker( llong from, byte mark )
+{
+ llong offset;
+ for ( offset = from; offset >= 0; offset-- ) {
+ if (
+ ( bdGetByte( offset ) == 0x00 ) &&
+ ( bdGetByte( offset + 1 ) == 0x00 ) &&
+ ( bdGetByte( offset + 2 ) == 0x01 ) &&
+ ( bdGetByte( offset + 3 ) == mark ) ) {
+ return offset;
+ }
+ }
+ return -1;
+}
+
+llong K3bMpegInfo::bdFindNextMarker( llong from, byte* mark )
+{
+ llong offset;
+ for ( offset = from; offset >= 0; offset-- ) {
+ if ( ( bdGetByte( offset ) == 0x00 ) &&
+ ( bdGetByte( offset + 1 ) == 0x00 ) &&
+ ( bdGetByte( offset + 2 ) == 0x01 ) ) {
+ *mark = bdGetByte( offset + 3 );
+ return offset;
+ }
+ }
+ return -1;
+
+}
+
+llong K3bMpegInfo::FindNextVideo( llong from )
+{
+ llong offset = from;
+ while ( offset >= 0 ) {
+ offset = FindNextMarker( offset );
+ if ( offset < 0 ) {
+ return -1;
+ }
+ if ( EnsureMPEG( offset, MPEG_VIDEO_E0_CODE ) || EnsureMPEG( offset, MPEG_VIDEO_E1_CODE ) || EnsureMPEG( offset, MPEG_VIDEO_E2_CODE ) ) {
+ return offset;
+ } else
+ offset++;
+ }
+
+ //shouldn't be here
+ return -1;
+}
+
+llong K3bMpegInfo::FindNextAudio( llong from )
+{
+ llong offset = from;
+ while ( offset >= 0 ) {
+ offset = FindNextMarker( offset );
+ if ( offset < 0 ) {
+ return -1;
+ }
+ if ( EnsureMPEG( offset, MPEG_AUDIO_C0_CODE ) || EnsureMPEG( offset, MPEG_AUDIO_C1_CODE ) || EnsureMPEG( offset, MPEG_AUDIO_C2_CODE ) ) {
+ return offset;
+ } else
+ offset++;
+ }
+
+ return -1;
+}
+
+
+int K3bMpegInfo::GetVideoIdx ( byte marker )
+{
+ switch ( marker ) {
+ case MPEG_VIDEO_E0_CODE:
+ return 0;
+ break;
+
+ case MPEG_VIDEO_E1_CODE:
+ return 1;
+ break;
+
+ case MPEG_VIDEO_E2_CODE:
+ return 2;
+ break;
+
+ default:
+ kdDebug() << "VideoCode not reached" << endl;
+ break;
+ }
+
+ return -1;
+}
+
+int K3bMpegInfo::GetAudioIdx ( byte marker )
+{
+ switch ( marker ) {
+ case MPEG_AUDIO_C0_CODE:
+ return 0;
+ break;
+
+ case MPEG_AUDIO_C1_CODE:
+ return 1;
+ break;
+
+ case MPEG_AUDIO_C2_CODE:
+ return 2;
+ break;
+
+ default:
+ kdDebug() << "VideoCode not reached" << endl;
+ break;
+ }
+
+ return -1;
+}
+
+llong K3bMpegInfo::SkipPacketHeader( llong offset )
+{
+ byte tmp_byte;
+ if ( mpeg_info->version == MPEG_VERS_MPEG1 ) {
+ // skip startcode and packet size
+ offset += 6;
+ //remove stuffing bytes
+ tmp_byte = GetByte( offset );
+ while ( tmp_byte & 0x80 )
+ tmp_byte = GetByte( ++offset );
+
+ if ( ( tmp_byte & 0xC0 ) == 0x40 ) // next two bits are 01
+ offset += 2;
+
+ tmp_byte = GetByte( offset );
+ if ( ( tmp_byte & 0xF0 ) == 0x20 )
+ offset += 5;
+ else if ( ( tmp_byte & 0xF0 ) == 0x30 )
+ offset += 10;
+ else
+ offset++;
+
+ return offset;
+ } else if ( mpeg_info->version == MPEG_VERS_MPEG2 ) {
+ return ( offset + 9 + GetByte( offset + 8 ) );
+ } else
+ return ( offset + 10 );
+}
+
+void K3bMpegInfo::ParseAudio ( llong offset, byte marker )
+{
+ unsigned brate, srate;
+ bool mpeg2_5 = false;
+
+ const int a_idx = GetAudioIdx( marker );
+
+ if ( mpeg_info->audio[ a_idx ].seen ) /* we have it already */
+ return ;
+
+ if ( ( GetByte( offset ) != 0xFF ) || ( ( GetByte( offset + 1 ) & 0xF0 ) != 0xF0 ) ) {
+ // doesn't start with 12 bits set
+ if ( ( GetByte( offset ) != 0xFF ) || ( ( GetByte( offset + 1 ) & 0xE0 ) != 0xE0 ) ) {
+ // doesn't start with 11 bits set
+ return ;
+ } else {
+ // start with 11 bits set
+ mpeg2_5 = true;
+ }
+ }
+
+ // Find mpeg version 1.0 or 2.0
+ if ( GetByte( offset + 1 ) & 0x08 ) {
+ if ( !mpeg2_5 )
+ mpeg_info->audio[ a_idx ].version = 1;
+ else
+ return ; // invalid 01 encountered
+ } else {
+ if ( !mpeg2_5 )
+ mpeg_info->audio[ a_idx ].version = 2;
+ else
+ mpeg_info->audio[ a_idx ].version = 3; //for mpeg 2.5
+ }
+
+ // Find Layer
+ mpeg_info->audio[ a_idx ].layer = ( GetByte( offset + 1 ) & 0x06 ) >> 1;
+ switch ( mpeg_info->audio[ a_idx ].layer ) {
+ case 0:
+ mpeg_info->audio[ a_idx ].layer = 0;
+ break;
+ case 1:
+ mpeg_info->audio[ a_idx ].layer = 3;
+ break;
+ case 2:
+ mpeg_info->audio[ a_idx ].layer = 2;
+ break;
+ case 3:
+ mpeg_info->audio[ a_idx ].layer = 1;
+ break;
+ }
+
+ // Protection Bit
+ mpeg_info->audio[ a_idx ].protect = GetByte( offset + 1 ) & 0x01;
+ if ( mpeg_info->audio[ a_idx ].protect )
+ mpeg_info->audio[ a_idx ].protect = 0;
+ else
+ mpeg_info->audio[ a_idx ].protect = 1;
+
+ const unsigned bit_rates[ 4 ][ 16 ] = {
+ {
+ 0,
+ },
+ {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0},
+ {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0},
+ {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0}
+ };
+
+
+ /* const unsigned bit_rates [ 3 ][ 3 ][ 16 ] = {
+ {
+ {0, },
+ {0, },
+ {0, },
+ },
+ {
+ {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0},
+ {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0},
+ {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0}
+ },
+ {
+ {0, 32, 48, 56, 64, 80 , 96 , 112, 128, 144, 160, 176, 192, 224, 256, 0},
+ {0, 8, 16, 24, 32, 40, 48, 56, 64 , 80 , 96 , 112, 128, 144, 160, 0},
+ {0, 8, 16, 24, 32, 40, 48, 56, 64 , 80 , 96 , 112, 128, 144, 160, 0}
+ }
+ };
+ */
+
+ const unsigned sampling_rates[ 4 ][ 4 ] = {
+ {
+ 0,
+ },
+ {44100, 48000, 32000, 0}, //mpeg 1
+ {22050, 24000, 16000, 0}, //mpeg 2
+ {11025, 12000, 8000, 0} //mpeg 2.5
+ };
+
+
+ // Bitrate index and sampling index to pass through the array
+ brate = GetByte( offset + 2 ) >> 4;
+ srate = ( GetByte( offset + 2 ) & 0x0f ) >> 2;
+
+ mpeg_info->audio[ a_idx ].bitrate = 1024 * bit_rates[ mpeg_info->audio[ a_idx ].layer ][ brate ];
+ mpeg_info->audio[ a_idx ].byterate = ( float ) ( mpeg_info->audio[ a_idx ].bitrate / 8.0 );
+ mpeg_info->audio[ a_idx ].sampfreq = sampling_rates[ mpeg_info->audio[ a_idx ].version ][ srate ];
+
+ // Audio mode
+ mpeg_info->audio[ a_idx ].mode = 1 + ( GetByte( offset + 3 ) >> 6 ) ;
+
+ // Copyright bit
+ if ( GetByte( offset + 3 ) & 0x08 )
+ mpeg_info->audio[ a_idx ].copyright = true;
+ else
+ mpeg_info->audio[ a_idx ].copyright = false;
+
+ // Original/Copy bit
+ if ( GetByte( offset + 3 ) & 0x04 )
+ mpeg_info->audio[ a_idx ].original = true;
+ else
+ mpeg_info->audio[ a_idx ].original = false;
+
+
+ mpeg_info->audio[ a_idx ].seen = true;
+}
+
+void K3bMpegInfo::ParseVideo ( llong offset, byte marker )
+{
+ unsigned long aratio, frate, brate;
+
+ const int v_idx = GetVideoIdx( marker );
+
+ const double aspect_ratios[ 16 ] = {
+ 0.0000, 1.0000, 0.6735, 0.7031,
+ 0.7615, 0.8055, 0.8437, 0.8935,
+ 0.9375, 0.9815, 1.0255, 1.0695,
+ 1.1250, 1.1575, 1.2015, 0.0000
+ };
+
+ if ( mpeg_info->video[ v_idx ].seen ) /* we have it already */
+ return ;
+
+ offset = FindNextMarker( offset + 1, MPEG_SEQUENCE_CODE );
+
+ if ( !offset )
+ return ;
+
+ offset += 4;
+
+ mpeg_info->video[ v_idx ].hsize = GetSize( offset ) >> 4;
+ mpeg_info->video[ v_idx ].vsize = GetSize( offset + 1 ) & 0x0FFF;
+
+ // Get picture rate
+ offset += 3; // after picture sizes
+
+ aratio = ( GetByte( offset ) & 0x0F ) >> 4;
+ mpeg_info->video[ v_idx ].aratio = aspect_ratios[ aratio ];
+
+ // offset += 3; // after picture sizes
+ frate = GetByte( offset ) & 0x0F;
+ mpeg_info->video[ v_idx ].frate = frame_rates[ frate ];
+
+ offset += 1; // after picture rate
+
+ // 18 following bytes are the bitrate /400
+
+ //read first 16 bytes
+ brate = GetSize( offset );
+ // scale
+ brate <<= 2;
+ byte lasttwo = GetByte( offset + 2 );
+ lasttwo >>= 6;
+ brate |= lasttwo;
+
+ mpeg_info->video[ v_idx ].bitrate = 400 * brate;
+
+ byte mark;
+ while ( true ) {
+ offset = FindNextMarker( offset, &mark );
+ if ( mark == MPEG_GOP_CODE )
+ break;
+ switch ( GetByte( offset + 3 ) ) {
+ case MPEG_EXT_CODE :
+ // Extension
+ offset += 4;
+ switch ( GetByte( offset ) >> 4 ) {
+ case 1:
+ //SequenceExt
+ if ( GetByte( offset + 1 ) & 0x08 )
+ mpeg_info->video[ v_idx ].progressive = true;
+ mpeg_info->video[ v_idx ].chroma_format = ( GetByte( offset + 1 ) & 0x06 ) >> 1;
+ break;
+ case 2:
+ // SequenceDisplayExt
+ mpeg_info->video[ v_idx ].video_format = ( GetByte( offset ) & 0x0E ) >> 1;
+ break;
+ }
+
+ break;
+ case MPEG_USER_CODE :
+ // UserData
+ break;
+ }
+ offset++;
+ }
+
+ mpeg_info->video[ v_idx ].seen = true;
+}
+
+double K3bMpegInfo::ReadTS( llong offset )
+{
+ byte highbit;
+ unsigned long low4Bytes;
+ double TS;
+
+ highbit = ( GetByte( offset ) >> 3 ) & 0x01;
+
+ low4Bytes = ( ( GetByte( offset ) >> 1 ) & 0x03 ) << 30;
+ low4Bytes |= GetByte( offset + 1 ) << 22;
+ low4Bytes |= ( GetByte( offset + 2 ) >> 1 ) << 15;
+ low4Bytes |= GetByte( offset + 3 ) << 7;
+ low4Bytes |= GetByte( offset + 4 ) >> 1;
+
+
+ TS = ( double ) ( highbit * FLOAT_0x10000 * FLOAT_0x10000 );
+ TS += ( double ) ( low4Bytes );
+ TS /= ( double ) ( STD_SYSTEM_CLOCK_FREQ );
+
+ return TS;
+}
+
+double K3bMpegInfo::ReadTSMpeg2( llong offset )
+{
+ byte highbit;
+ unsigned long low4Bytes;
+ unsigned long sys_clock_ref;
+ double TS;
+
+ highbit = ( GetByte( offset ) & 0x20 ) >> 5;
+
+ low4Bytes = ( ( GetByte( offset ) & 0x18 ) >> 3 ) << 30;
+ low4Bytes |= ( GetByte( offset ) & 0x03 ) << 28;
+ low4Bytes |= GetByte( offset + 1 ) << 20;
+ low4Bytes |= ( GetByte( offset + 2 ) & 0xF8 ) << 12;
+ low4Bytes |= ( GetByte( offset + 2 ) & 0x03 ) << 13;
+ low4Bytes |= GetByte( offset + 3 ) << 5;
+ low4Bytes |= ( GetByte( offset + 4 ) ) >> 3;
+
+ sys_clock_ref = ( GetByte( offset + 4 ) & 0x3 ) << 7;
+ sys_clock_ref |= ( GetByte( offset + 5 ) >> 1 );
+
+ TS = ( double ) ( highbit * FLOAT_0x10000 * FLOAT_0x10000 );
+ TS += ( double ) ( low4Bytes );
+ if ( sys_clock_ref == 0 )
+ TS /= ( double ) ( STD_SYSTEM_CLOCK_FREQ );
+ else {
+ TS /= ( double ) ( 27000000 / sys_clock_ref );
+ }
+
+ return TS;
+}
diff --git a/libk3b/projects/videocd/mpeginfo/k3bmpeginfo.h b/libk3b/projects/videocd/mpeginfo/k3bmpeginfo.h
new file mode 100644
index 0000000..3436214
--- /dev/null
+++ b/libk3b/projects/videocd/mpeginfo/k3bmpeginfo.h
@@ -0,0 +1,178 @@
+/*
+*
+* $Id: k3bmpeginfo.h 619556 2007-01-03 17:38:12Z trueg $
+* Copyright (C) 2003-2004 Christian Kvasny <chris@k3b.org>
+*
+* This file is part of the K3b project.
+* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+* See the file "COPYING" for the exact licensing terms.
+*/
+
+#ifndef K3BMPEGINFO
+#define K3BMPEGINFO
+
+#include <stdio.h>
+
+// #define BUFFERSIZE 16384
+#define BUFFERSIZE 65536
+
+#define MPEG_START_CODE_PATTERN ((ulong) 0x00000100)
+#define MPEG_START_CODE_MASK ((ulong) 0xffffff00)
+
+#define MPEG_PICTURE_CODE ((ulong) 0x00000100)
+/* [...slice codes... 0x1a7] */
+
+#define MPEG_USER_CODE ((uchar) 0xb2)
+#define MPEG_SEQUENCE_CODE ((uchar) 0xb3)
+#define MPEG_EXT_CODE ((uchar) 0xb5)
+#define MPEG_SEQ_END_CODE ((uchar) 0xb7)
+#define MPEG_GOP_CODE ((uchar) 0xb8)
+#define MPEG_PROGRAM_END_CODE ((uchar) 0xb9)
+#define MPEG_PACK_HEADER_CODE ((uchar) 0xba)
+#define MPEG_SYSTEM_HEADER_CODE ((uchar) 0xbb)
+#define MPEG_PRIVATE_1_CODE ((uchar) 0xbd)
+#define MPEG_PAD_CODE ((uchar) 0xbe)
+
+#define MPEG_AUDIO_C0_CODE ((uchar) 0xc0) /* default */
+#define MPEG_AUDIO_C1_CODE ((uchar) 0xc1) /* 2nd audio stream id (dual channel) */
+#define MPEG_AUDIO_C2_CODE ((uchar) 0xc2) /* 3rd audio stream id (surround sound) */
+
+#define MPEG_VIDEO_E0_CODE ((uchar) 0xe0) /* motion */
+#define MPEG_VIDEO_E1_CODE ((uchar) 0xe1) /* lowres still */
+#define MPEG_VIDEO_E2_CODE ((uchar) 0xe2) /* hires still */
+
+#define FLOAT_0x10000 (double)((unsigned long)1 << 16)
+#define STD_SYSTEM_CLOCK_FREQ (unsigned long)90000
+
+typedef unsigned char byte;
+typedef long long llong;
+
+#include <kdebug.h>
+
+class video_info
+{
+ public:
+ bool seen;
+ unsigned long hsize;
+ unsigned long vsize;
+ double aratio;
+ double frate;
+ unsigned long bitrate;
+ unsigned long vbvsize;
+ bool progressive;
+ unsigned char video_format;
+ unsigned char chroma_format;
+ bool constrained_flag;
+};
+
+class audio_info
+{
+ public:
+ bool seen;
+ unsigned int version;
+ unsigned int layer;
+ unsigned int protect;
+ unsigned long bitrate;
+ float byterate;
+ unsigned long sampfreq;
+ int mode;
+ bool copyright;
+ bool original;
+};
+
+class Mpeginfo
+{
+
+ public:
+ Mpeginfo()
+ : version( 0 ),
+ muxrate( 0 ),
+ playing_time( 0 ),
+ has_video ( false ),
+ has_audio ( false )
+ {
+ for ( int i = 0; i < 3; i++ ) {
+ video[ i ].seen = false;
+ audio[ i ].seen = false;
+ }
+ };
+
+ ~Mpeginfo()
+ {}
+ ;
+
+ unsigned int version;
+ unsigned long muxrate;
+ double playing_time;
+ bool has_video;
+ bool has_audio;
+ video_info video[ 3 ];
+ audio_info audio[ 3 ];
+};
+
+class K3bMpegInfo
+{
+ public:
+ K3bMpegInfo( const char* filename );
+ ~K3bMpegInfo();
+ enum mpeg_version { MPEG_VERS_INVALID = 0, MPEG_VERS_MPEG1 = 1, MPEG_VERS_MPEG2 = 2 };
+ enum mode { MPEG_STEREO = 1, MPEG_JOINT_STEREO, MPEG_DUAL_CHANNEL, MPEG_SINGLE_CHANNEL };
+
+ const int version()
+ {
+ return mpeg_info->version;
+ };
+ const QString error_string()
+ {
+ return m_error_string;
+ };
+ Mpeginfo* mpeg_info;
+
+
+ private:
+ // General ToolBox
+ byte GetByte( llong offset );
+ byte bdGetByte( llong offset );
+ llong GetNBytes( llong, int );
+ unsigned short int GetSize( llong offset );
+ llong FindNextMarker( llong );
+ llong FindNextMarker( llong, byte* );
+ llong FindNextMarker( llong, byte );
+ llong bdFindNextMarker( llong, byte );
+ llong bdFindNextMarker( llong, byte* );
+ llong FindNextVideo( llong );
+ llong FindNextAudio( llong );
+
+ int GetVideoIdx ( byte );
+ int GetAudioIdx ( byte );
+ bool EnsureMPEG( llong, byte );
+ void ParseVideo ( llong, byte );
+ void ParseAudio ( llong, byte );
+ bool MpegParsePacket ();
+ llong MpegParsePacket ( llong );
+ llong SkipPacketHeader( llong );
+
+ double ReadTS( llong offset );
+ double ReadTSMpeg2( llong offset );
+
+ FILE* m_mpegfile;
+
+ const char* m_filename;
+ llong m_filesize;
+
+ bool m_done;
+
+ llong m_buffstart;
+ llong m_buffend;
+ byte* m_buffer;
+ double m_initial_TS;
+ QString m_error_string;
+
+};
+
+#endif //K3bMpegInfo
diff --git a/libk3b/projects/videodvd/Makefile.am b/libk3b/projects/videodvd/Makefile.am
new file mode 100644
index 0000000..1b3e92b
--- /dev/null
+++ b/libk3b/projects/videodvd/Makefile.am
@@ -0,0 +1,20 @@
+# we need the ../datacd for the uic generated header files
+AM_CPPFLAGS= -I$(srcdir)/../../core \
+ -I$(srcdir)/../../../libk3bdevice \
+ -I$(srcdir)/../../../src \
+ -I$(srcdir)/../../tools \
+ -I$(srcdir)/../datadvd \
+ -I$(srcdir)/../datacd \
+ -I$(srcdir)/.. \
+ -I../datacd \
+ $(all_includes)
+
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libvideodvd.la
+
+libvideodvd_la_SOURCES = k3bvideodvddoc.cpp \
+ k3bvideodvdjob.cpp \
+ k3bvideodvdimager.cpp
+
+include_HEADERS = k3bvideodvddoc.h k3bvideodvdjob.h
diff --git a/libk3b/projects/videodvd/k3bvideodvddoc.cpp b/libk3b/projects/videodvd/k3bvideodvddoc.cpp
new file mode 100644
index 0000000..2f02ac6
--- /dev/null
+++ b/libk3b/projects/videodvd/k3bvideodvddoc.cpp
@@ -0,0 +1,71 @@
+/*
+ *
+ * $Id: k3bvideodvddoc.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bvideodvddoc.h"
+#include "k3bvideodvdjob.h"
+
+#include <k3bdiritem.h>
+
+#include <k3bisooptions.h>
+
+#include <kconfig.h>
+
+
+K3bVideoDvdDoc::K3bVideoDvdDoc( QObject* parent )
+ : K3bDvdDoc( parent )
+{
+}
+
+
+K3bVideoDvdDoc::~K3bVideoDvdDoc()
+{
+}
+
+
+bool K3bVideoDvdDoc::newDocument()
+{
+ if( K3bDataDoc::newDocument() ) {
+
+ // K3bDataDoc::newDocument already deleted m_videoTsDir (again: bad design!)
+ m_videoTsDir = new K3bDirItem( "VIDEO_TS", this, root() );
+ m_videoTsDir->setRemoveable(false);
+ m_videoTsDir->setRenameable(false);
+ m_videoTsDir->setMoveable(false);
+ m_videoTsDir->setHideable(false);
+
+ K3bDirItem* audioTsDir = new K3bDirItem( "AUDIO_TS", this, root() );
+ audioTsDir->setRemoveable(false);
+ audioTsDir->setRenameable(false);
+ audioTsDir->setMoveable(false);
+ audioTsDir->setHideable(false);
+
+ setMultiSessionMode( NONE );
+
+ setModified( false );
+
+ return true;
+ }
+ else
+ return false;
+}
+
+
+K3bBurnJob* K3bVideoDvdDoc::newBurnJob( K3bJobHandler* hdl, QObject* parent )
+{
+ return new K3bVideoDvdJob( this, hdl, parent );
+}
+
+//#include "k3bdvddoc.moc"
diff --git a/libk3b/projects/videodvd/k3bvideodvddoc.h b/libk3b/projects/videodvd/k3bvideodvddoc.h
new file mode 100644
index 0000000..034ae4d
--- /dev/null
+++ b/libk3b/projects/videodvd/k3bvideodvddoc.h
@@ -0,0 +1,46 @@
+/*
+ *
+ * $Id: k3bvideodvddoc.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_VIDEODVD_DOC_H_
+#define _K3B_VIDEODVD_DOC_H_
+
+#include <k3bdvddoc.h>
+#include "k3b_export.h"
+class KConfig;
+
+class LIBK3B_EXPORT K3bVideoDvdDoc : public K3bDvdDoc
+{
+ public:
+ K3bVideoDvdDoc( QObject* parent = 0 );
+ virtual ~K3bVideoDvdDoc();
+
+ virtual int type() const { return VIDEODVD; }
+
+ virtual K3bBurnJob* newBurnJob( K3bJobHandler* hdl, QObject* parent );
+
+ virtual bool newDocument();
+
+ K3bDirItem* videoTsDir() const { return m_videoTsDir; }
+
+ // TODO: implement load- and saveDocumentData since we do not need all those options
+
+ protected:
+ virtual QString typeString() const { return "video_dvd"; }
+
+ private:
+ K3bDirItem* m_videoTsDir;
+};
+
+#endif
diff --git a/libk3b/projects/videodvd/k3bvideodvdimager.cpp b/libk3b/projects/videodvd/k3bvideodvdimager.cpp
new file mode 100644
index 0000000..7362aa0
--- /dev/null
+++ b/libk3b/projects/videodvd/k3bvideodvdimager.cpp
@@ -0,0 +1,221 @@
+/*
+ *
+ * $Id: k3bvideodvdimager.cpp 633751 2007-02-15 08:22:49Z trueg $
+ * Copyright (C) 2004-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bvideodvdimager.h"
+#include "k3bvideodvddoc.h"
+#include <k3bdiritem.h>
+#include <k3bfileitem.h>
+#include <k3bprocess.h>
+#include <k3bglobals.h>
+
+#include <ktempfile.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <qtextstream.h>
+#include <qdir.h>
+#include <qfile.h>
+#include <qptrlist.h>
+
+
+
+class K3bVideoDvdImager::Private
+{
+public:
+ K3bVideoDvdDoc* doc;
+
+ QString tempPath;
+};
+
+
+K3bVideoDvdImager::K3bVideoDvdImager( K3bVideoDvdDoc* doc, K3bJobHandler* jh, QObject* parent, const char* name )
+ : K3bIsoImager( doc, jh, parent, name )
+{
+ d = new Private;
+ d->doc = doc;
+}
+
+
+K3bVideoDvdImager::~K3bVideoDvdImager()
+{
+ delete d;
+}
+
+
+void K3bVideoDvdImager::start()
+{
+ fixVideoDVDSettings();
+ K3bIsoImager::start();
+}
+
+
+void K3bVideoDvdImager::init()
+{
+ fixVideoDVDSettings();
+ K3bIsoImager::init();
+}
+
+
+void K3bVideoDvdImager::fixVideoDVDSettings()
+{
+ // Video DVD defaults, we cannot set these in K3bVideoDvdDoc since they
+ // will be overwritten in the burn dialog unless we create some K3bVideoDVDIsoOptions
+ // class with different defaults. But since the whole Video DVD project is a hack we
+ // go the easy road.
+ K3bIsoOptions o = d->doc->isoOptions();
+ o.setISOLevel(1);
+ o.setISOallow31charFilenames(false);
+ o.setCreateJoliet(false);
+ o.setJolietLong(false);
+ o.setCreateRockRidge(false);
+ o.setCreateUdf(true);
+ d->doc->setIsoOptions( o );
+}
+
+
+void K3bVideoDvdImager::calculateSize()
+{
+ fixVideoDVDSettings();
+ K3bIsoImager::calculateSize();
+}
+
+
+int K3bVideoDvdImager::writePathSpec()
+{
+ //
+ // Create a temp dir and link all contents of the VIDEO_TS dir to make mkisofs
+ // able to handle the VideoDVD stuff.
+ //
+ // mkisofs is not able to create VideoDVDs from graft-points.
+ //
+ // We do this here since K3bIsoImager::start calls cleanup which deletes the temp files
+ //
+ QDir dir( KGlobal::dirs()->resourceDirs( "tmp" ).first() );
+ d->tempPath = K3b::findUniqueFilePrefix( "k3bVideoDvd", dir.path() );
+ kdDebug() << "(K3bVideoDvdImager) creating temp dir: " << d->tempPath << endl;
+ if( !dir.mkdir( d->tempPath, true ) ) {
+ emit infoMessage( i18n("Unable to create temporary directory '%1'.").arg(d->tempPath), ERROR );
+ return -1;
+ }
+
+ dir.cd( d->tempPath );
+ if( !dir.mkdir( "VIDEO_TS" ) ) {
+ emit infoMessage( i18n("Unable to create temporary directory '%1'.").arg(d->tempPath + "/VIDEO_TS"), ERROR );
+ return -1;
+ }
+
+ for( QPtrListIterator<K3bDataItem> it( d->doc->videoTsDir()->children() ); *it; ++it ) {
+ if( (*it)->isDir() ) {
+ emit infoMessage( i18n("Found invalid entry in the VIDEO_TS folder (%1).").arg((*it)->k3bName()), ERROR );
+ return -1;
+ }
+
+ // convert to upper case names
+ if( ::symlink( QFile::encodeName( (*it)->localPath() ),
+ QFile::encodeName( d->tempPath + "/VIDEO_TS/" + (*it)->k3bName().upper() ) ) == -1 ) {
+ emit infoMessage( i18n("Unable to link temporary file in folder %1.").arg( d->tempPath ), ERROR );
+ return -1;
+ }
+ }
+
+
+ return K3bIsoImager::writePathSpec();
+}
+
+
+int K3bVideoDvdImager::writePathSpecForDir( K3bDirItem* dirItem, QTextStream& stream )
+{
+ //
+ // We handle the VIDEO_TS dir differently since otherwise mkisofs is not able to
+ // open the VideoDVD structures (see addMkisofsParameters)
+ //
+ if( dirItem == d->doc->videoTsDir() ) {
+ return 0;
+ }
+
+ int num = 0;
+ for( QPtrListIterator<K3bDataItem> it( dirItem->children() ); it.current(); ++it ) {
+ K3bDataItem* item = it.current();
+ num++;
+
+ if( item->isDir() ) {
+ // we cannot add the video_ts dir twice
+ if( item != d->doc->videoTsDir() ) {
+ stream << escapeGraftPoint( item->writtenPath() )
+ << "="
+ << escapeGraftPoint( dummyDir( static_cast<K3bDirItem*>(item) ) ) << "\n";
+ }
+
+ int x = writePathSpecForDir( dynamic_cast<K3bDirItem*>(item), stream );
+ if( x >= 0 )
+ num += x;
+ else
+ return -1;
+ }
+ else {
+ writePathSpecForFile( static_cast<K3bFileItem*>(item), stream );
+ }
+ }
+
+ return num;
+}
+
+
+bool K3bVideoDvdImager::addMkisofsParameters( bool printSize )
+{
+ // Here is another bad design: we assume that K3bIsoImager::start does not add additional
+ // parameters to the process. :(
+ if( K3bIsoImager::addMkisofsParameters( printSize ) ) {
+ *m_process << "-dvd-video";
+ *m_process << "-f"; // follow symlinks
+ *m_process << d->tempPath;
+ return true;
+ }
+ else
+ return false;
+}
+
+
+void K3bVideoDvdImager::cleanup()
+{
+ if( QFile::exists( d->tempPath ) ) {
+ QDir dir( d->tempPath );
+ dir.cd( "VIDEO_TS" );
+ for( QPtrListIterator<K3bDataItem> it( d->doc->videoTsDir()->children() ); *it; ++it )
+ dir.remove( (*it)->k3bName().upper() );
+ dir.cdUp();
+ dir.rmdir( "VIDEO_TS" );
+ dir.cdUp();
+ dir.rmdir( d->tempPath );
+ }
+ d->tempPath = QString::null;
+
+ K3bIsoImager::cleanup();
+}
+
+
+void K3bVideoDvdImager::slotReceivedStderr( const QString& line )
+{
+ if( line.contains( "Unable to make a DVD-Video image" ) ) {
+ emit infoMessage( i18n("The project does not contain all necessary VideoDVD files."), WARNING );
+ emit infoMessage( i18n("The resulting DVD will most likely not be playable on a Hifi DVD player."), WARNING );
+ }
+ else
+ K3bIsoImager::slotReceivedStderr( line );
+}
+
+#include "k3bvideodvdimager.moc"
diff --git a/libk3b/projects/videodvd/k3bvideodvdimager.h b/libk3b/projects/videodvd/k3bvideodvdimager.h
new file mode 100644
index 0000000..0062d55
--- /dev/null
+++ b/libk3b/projects/videodvd/k3bvideodvdimager.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * $Id: k3bvideodvdimager.h 633751 2007-02-15 08:22:49Z trueg $
+ * Copyright (C) 2004 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_VIDEODVD_IMAGER_H_
+#define _K3B_VIDEODVD_IMAGER_H_
+
+
+#include <k3bisoimager.h>
+
+class K3bVideoDvdDoc;
+
+
+/**
+ * Create VideoDVD images with mkisofs. The difference
+ * to the IsoImager is the -dvd-video option and the fact
+ * that all VIDEO_TS files need to be in one local folder since
+ * otherwise mkisofs is not able to find the dvd structures.
+ */
+class K3bVideoDvdImager : public K3bIsoImager
+{
+ Q_OBJECT
+
+ public:
+ K3bVideoDvdImager( K3bVideoDvdDoc* doc, K3bJobHandler*, QObject* parent = 0, const char* name = 0 );
+ virtual ~K3bVideoDvdImager();
+
+ public slots:
+ virtual void start();
+ virtual void init();
+ virtual void calculateSize();
+
+ protected:
+ bool addMkisofsParameters( bool printSize = false );
+ int writePathSpec();
+ void cleanup();
+ int writePathSpecForDir( K3bDirItem* dirItem, QTextStream& stream );
+
+ protected slots:
+ virtual void slotReceivedStderr( const QString& );
+
+ private:
+ void fixVideoDVDSettings();
+
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/projects/videodvd/k3bvideodvdjob.cpp b/libk3b/projects/videodvd/k3bvideodvdjob.cpp
new file mode 100644
index 0000000..f4e6129
--- /dev/null
+++ b/libk3b/projects/videodvd/k3bvideodvdjob.cpp
@@ -0,0 +1,101 @@
+/*
+ *
+ * $Id: k3bvideodvdjob.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bvideodvdjob.h"
+#include "k3bvideodvddoc.h"
+#include "k3bvideodvdimager.h"
+
+#include <k3bcore.h>
+#include <k3bisoimager.h>
+#include <k3bgrowisofswriter.h>
+#include <k3bglobals.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kapplication.h>
+#include <kconfig.h>
+
+
+
+K3bVideoDvdJob::K3bVideoDvdJob( K3bVideoDvdDoc* doc, K3bJobHandler* jh, QObject* parent )
+ : K3bDvdJob( doc, jh, parent ),
+ m_doc(doc)
+{
+}
+
+
+K3bVideoDvdJob::~K3bVideoDvdJob()
+{
+}
+
+
+void K3bVideoDvdJob::prepareImager()
+{
+ setImager( new K3bVideoDvdImager( m_doc, this ) );
+}
+
+
+bool K3bVideoDvdJob::prepareWriterJob()
+{
+ K3bGrowisofsWriter* writer = new K3bGrowisofsWriter( m_doc->burner(), this, this );
+
+ // these do only make sense with DVD-R(W)
+ writer->setSimulate( m_doc->dummy() );
+ writer->setBurnSpeed( m_doc->speed() );
+
+ // DAO seems to be the better default for Video DVD... !?
+ if( m_doc->writingMode() == K3b::DAO || m_doc->writingMode() == K3b::WRITING_MODE_AUTO )
+ writer->setWritingMode( K3b::DAO );
+
+ writer->setMultiSession( false );
+ writer->setCloseDvd( true );
+
+ if( m_doc->onTheFly() ) {
+ writer->setImageToWrite( QString::null ); // read from stdin
+ writer->setTrackSize( m_isoImager->size() );
+ }
+ else
+ writer->setImageToWrite( m_doc->tempDir() );
+
+ setWriterJob( writer );
+
+ return true;
+}
+
+
+QString K3bVideoDvdJob::jobDescription() const
+{
+ if( m_doc->onlyCreateImages() ) {
+ return i18n("Creating Video DVD Image File");
+ }
+ else {
+ return i18n("Writing Video DVD")
+ + ( m_doc->isoOptions().volumeID().isEmpty()
+ ? QString::null
+ : QString( " (%1)" ).arg(m_doc->isoOptions().volumeID()) );
+ }
+}
+
+
+QString K3bVideoDvdJob::jobDetails() const
+{
+ return ( i18n("ISO9660/Udf Filesystem (Size: %1)").arg(KIO::convertSize( doc()->size() ))
+ + ( m_doc->copies() > 1
+ ? i18n(" - %n copy", " - %n copies", m_doc->copies())
+ : QString::null ) );
+}
+
+#include "k3bvideodvdjob.moc"
diff --git a/libk3b/projects/videodvd/k3bvideodvdjob.h b/libk3b/projects/videodvd/k3bvideodvdjob.h
new file mode 100644
index 0000000..3f03cea
--- /dev/null
+++ b/libk3b/projects/videodvd/k3bvideodvdjob.h
@@ -0,0 +1,46 @@
+/*
+ *
+ * $Id: k3bvideodvdjob.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_VIDEO_DVD_JOB_H_
+#define _K3B_VIDEO_DVD_JOB_H_
+
+#include <k3bdvdjob.h>
+
+
+class K3bVideoDvdDoc;
+
+/**
+ * This class heavily depends on K3bDvdJob and uses some of it's internals.
+ */
+class K3bVideoDvdJob : public K3bDvdJob
+{
+ Q_OBJECT
+
+ public:
+ K3bVideoDvdJob( K3bVideoDvdDoc*, K3bJobHandler*, QObject* parent = 0 );
+ virtual ~K3bVideoDvdJob();
+
+ virtual QString jobDescription() const;
+ virtual QString jobDetails() const;
+
+ private:
+ bool prepareWriterJob();
+ void prepareImager();
+
+ K3bVideoDvdDoc* m_doc;
+};
+
+#endif