You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
441 lines
19 KiB
441 lines
19 KiB
/*
|
|
*
|
|
* $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' ) ) );
|
|
}
|
|
}
|
|
}
|
|
|