File indexing completed on 2024-05-12 04:51:11
0001 /* 0002 SPDX-FileCopyrightText: 2003-2004 Christian Kvasny <chris@k3b.org> 0003 THX to Manfred Odenstein <odix@chello.at> 0004 SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "k3bvcdxmlview.h" 0010 #include "k3bvcdtrack.h" 0011 #include "k3bcore.h" 0012 #include "k3bversion.h" 0013 0014 #include <QDebug> 0015 #include <QDir> 0016 #include <QStandardPaths> 0017 #include <QTextStream> 0018 #include <QDomDocument> 0019 #include <QDomElement> 0020 0021 0022 class K3b::VcdXmlView::Private 0023 { 0024 public: 0025 void addComment( QDomDocument& doc, QDomElement& parent, const QString& text ); 0026 QDomElement addSubElement( QDomDocument&, QDomElement&, const QString& name, const QString& value = QString() ); 0027 QDomElement addSubElement( QDomDocument&, QDomElement&, const QString& name, const int& value ); 0028 0029 QDomElement addFolderElement( QDomDocument&, QDomElement&, const QString& name ); 0030 void addFileElement( QDomDocument&, QDomElement&, const QString& src, const QString& name, bool mixed = false ); 0031 void doPbc( QDomDocument&, QDomElement&, VcdTrack* ); 0032 void setNumkeyBSN( QDomDocument& , QDomElement&, VcdTrack* ); 0033 void setNumkeySEL( QDomDocument& , QDomElement&, VcdTrack* ); 0034 0035 VcdDoc* doc; 0036 QString xmlstring; 0037 int startkey; 0038 }; 0039 0040 void K3b::VcdXmlView::Private::addComment( QDomDocument& doc, QDomElement& parent, const QString& text ) 0041 { 0042 QDomComment comment = doc.createComment( text ); 0043 parent.appendChild( comment ); 0044 } 0045 0046 QDomElement K3b::VcdXmlView::Private::addSubElement( QDomDocument& doc, QDomElement& parent, const QString& name, const QString& value ) 0047 { 0048 QDomElement element = doc.createElement( name ); 0049 parent.appendChild( element ); 0050 if ( !value.isNull() ) { 0051 QDomText t = doc.createTextNode( value ); 0052 element.appendChild( t ); 0053 } 0054 return element; 0055 } 0056 0057 QDomElement K3b::VcdXmlView::Private::addSubElement( QDomDocument& doc, QDomElement& parent, const QString& name, const int& value ) 0058 { 0059 QDomElement element = doc.createElement( name ); 0060 parent.appendChild( element ); 0061 if ( value >= -1 ) { 0062 QDomText t = doc.createTextNode( QString( "%1" ).arg( value ) ); 0063 element.appendChild( t ); 0064 } 0065 return element; 0066 } 0067 0068 QDomElement K3b::VcdXmlView::Private::addFolderElement( QDomDocument& doc, QDomElement& parent, const QString& name ) 0069 { 0070 QDomElement elemFolder = addSubElement( doc, parent, "folder" ); 0071 addSubElement( doc, elemFolder, "name", name ); 0072 0073 return elemFolder; 0074 } 0075 0076 void K3b::VcdXmlView::Private::addFileElement( QDomDocument& doc, QDomElement& parent, const QString& src, const QString& name, bool mixed ) 0077 { 0078 QDomElement elemFile = addSubElement( doc, parent, "file" ); 0079 elemFile.setAttribute( "src", QString( "%1" ).arg( src ) ); 0080 if ( mixed ) 0081 elemFile.setAttribute( "format", "mixed" ); 0082 0083 addSubElement( doc, elemFile, "name", name ); 0084 } 0085 0086 void K3b::VcdXmlView::Private::doPbc( QDomDocument& doc, QDomElement& parent, K3b::VcdTrack* track ) 0087 { 0088 QString ref = ( track->isSegment() ) ? "segment" : "sequence"; 0089 0090 QDomElement elemSelection = addSubElement( doc, parent, "selection" ); 0091 elemSelection.setAttribute( "id", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( track->index() ).rightJustified( 3, '0' ) ) ); 0092 0093 setNumkeyBSN( doc, elemSelection, track ); 0094 0095 Q_FOREACH( VcdTrack::PbcTracks pbc, VcdTrack::trackPlaybackValues() ) { 0096 QDomElement elemPbcSelectionPNRDT; 0097 0098 if ( track->getPbcTrack( pbc ) ) { 0099 int index = track->getPbcTrack( pbc ) ->index(); 0100 QString ref = ( track->getPbcTrack( pbc ) ->isSegment() ) ? "segment" : "sequence"; 0101 0102 switch ( pbc ) { 0103 case K3b::VcdTrack::PREVIOUS: 0104 elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "prev" ); 0105 elemPbcSelectionPNRDT.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( index ).rightJustified( 3, '0' ) ) ); 0106 break; 0107 case K3b::VcdTrack::NEXT: 0108 elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "next" ); 0109 elemPbcSelectionPNRDT.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( index ).rightJustified( 3, '0' ) ) ); 0110 break; 0111 case K3b::VcdTrack::RETURN: 0112 elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "return" ); 0113 elemPbcSelectionPNRDT.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( index ).rightJustified( 3, '0' ) ) ); 0114 break; 0115 case K3b::VcdTrack::DEFAULT: 0116 elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "default" ); 0117 elemPbcSelectionPNRDT.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( index ).rightJustified( 3, '0' ) ) ); 0118 break; 0119 case K3b::VcdTrack::AFTERTIMEOUT: 0120 if ( track->getWaitTime() >= 0 ) { 0121 elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "timeout" ); 0122 elemPbcSelectionPNRDT.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( index ).rightJustified( 3, '0' ) ) ); 0123 } 0124 break; 0125 } 0126 } else { 0127 // jump to <endlist> otherwise do noop while disabled 0128 if ( track->getNonPbcTrack( pbc ) == K3b::VcdTrack::VIDEOEND ) { 0129 switch ( pbc ) { 0130 case K3b::VcdTrack::PREVIOUS: 0131 elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "prev" ); 0132 elemPbcSelectionPNRDT.setAttribute( "ref", "end" ); 0133 break; 0134 case K3b::VcdTrack::NEXT: 0135 elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "next" ); 0136 elemPbcSelectionPNRDT.setAttribute( "ref", "end" ); 0137 break; 0138 case K3b::VcdTrack::RETURN: 0139 elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "return" ); 0140 elemPbcSelectionPNRDT.setAttribute( "ref", "end" ); 0141 break; 0142 case K3b::VcdTrack::DEFAULT: 0143 elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "default" ); 0144 elemPbcSelectionPNRDT.setAttribute( "ref", "end" ); 0145 break; 0146 case K3b::VcdTrack::AFTERTIMEOUT: 0147 if ( track->getWaitTime() >= 0 ) { 0148 elemPbcSelectionPNRDT = addSubElement( doc, elemSelection, "timeout" ); 0149 elemPbcSelectionPNRDT.setAttribute( "ref", "end" ); 0150 } 0151 break; 0152 } 0153 } 0154 } 0155 } 0156 0157 addSubElement( doc, elemSelection, "wait", track->getWaitTime() ); 0158 QDomElement loop = addSubElement( doc, elemSelection, "loop", track->getPlayTime() ); 0159 if ( track->Reactivity() ) 0160 loop.setAttribute( "jump-timing", "delayed" ); 0161 else 0162 loop.setAttribute( "jump-timing", "immediate" ); 0163 0164 addSubElement( doc, elemSelection, "play-item" ).setAttribute( "ref", QString( "%1-%2" ).arg( ref ).arg( QString::number( track->index() ).rightJustified( 3, '0' ) ) ); 0165 0166 setNumkeySEL( doc, elemSelection, track ); 0167 } 0168 0169 void K3b::VcdXmlView::Private::setNumkeyBSN( QDomDocument& doc, QDomElement& parent, K3b::VcdTrack* track ) 0170 { 0171 if ( track->PbcNumKeys() ) { 0172 if ( track->PbcNumKeysUserdefined() ) { 0173 QMap<int, K3b::VcdTrack*> numKeyMap = track->DefinedNumKey(); 0174 QMap<int, K3b::VcdTrack*>::const_iterator trackIt; 0175 0176 startkey = 0; 0177 trackIt = numKeyMap.constBegin(); 0178 if ( trackIt != numKeyMap.constEnd() ) 0179 startkey = trackIt.key(); 0180 0181 if ( startkey > 0 ) 0182 addSubElement( doc, parent, "bsn", startkey ); 0183 else // user has no numKeys defined for this track 0184 track->setPbcNumKeys( false ); 0185 0186 } else { 0187 // default start with key #1 0188 addSubElement( doc, parent, "bsn", 1 ); 0189 } 0190 } 0191 } 0192 0193 void K3b::VcdXmlView::Private::setNumkeySEL( QDomDocument& doc, QDomElement& parent, K3b::VcdTrack* track ) 0194 { 0195 if ( track->PbcNumKeys() ) { 0196 QDomElement elemPbcSelectionNumKeySEL; 0197 QString ref = ( track->isSegment() ) ? "segment" : "sequence"; 0198 int none = startkey; 0199 if ( track->PbcNumKeysUserdefined() ) { 0200 QMap<int, K3b::VcdTrack*> numKeyMap = track->DefinedNumKey(); 0201 QMap<int, K3b::VcdTrack*>::const_iterator trackIt; 0202 0203 for ( trackIt = numKeyMap.constBegin(); trackIt != numKeyMap.constEnd(); ++trackIt ) { 0204 0205 qDebug() << QString( "trackIt key: %1 none: %2" ).arg( trackIt.key() ).arg( none ); 0206 while ( none < trackIt.key() ) { 0207 elemPbcSelectionNumKeySEL = addSubElement( doc, parent, "select" ); 0208 elemPbcSelectionNumKeySEL.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( track->index() ).rightJustified( 3, '0' ) ) ); 0209 addComment( doc, parent, QString( "key %1 -> %2 (normal none)" ).arg( none ).arg( track->absolutePath() ) ); 0210 none++; 0211 } 0212 0213 if ( trackIt.value() ) { 0214 QString ref = ( trackIt.value() ->isSegment() ) ? "segment" : "sequence"; 0215 elemPbcSelectionNumKeySEL = addSubElement( doc, parent, "select" ); 0216 elemPbcSelectionNumKeySEL.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( trackIt.value() ->index() ).rightJustified( 3, '0' ) ) ); 0217 addComment( doc, parent, QString( "key %1 -> %2" ).arg( trackIt.key() ).arg( trackIt.value() ->absolutePath() ) ); 0218 } else { 0219 elemPbcSelectionNumKeySEL = addSubElement( doc, parent, "select" ); 0220 elemPbcSelectionNumKeySEL.setAttribute( "ref", "end" ); 0221 addComment( doc, parent, QString( "key %1 -> end" ).arg( trackIt.key() ) ); 0222 } 0223 none++; 0224 } 0225 } else { 0226 // default reference to itSelf 0227 elemPbcSelectionNumKeySEL = addSubElement( doc, parent, "select" ); 0228 elemPbcSelectionNumKeySEL.setAttribute( "ref", QString( "select-%1-%2" ).arg( ref ).arg( QString::number( track->index() ).rightJustified( 3, '0' ) ) ); 0229 } 0230 } 0231 } 0232 0233 K3b::VcdXmlView::VcdXmlView( K3b::VcdDoc* pDoc ) 0234 : d( new Private ) 0235 { 0236 d->doc = pDoc; 0237 } 0238 0239 K3b::VcdXmlView::~VcdXmlView() 0240 { 0241 delete d; 0242 } 0243 0244 void K3b::VcdXmlView::write( QFile& file ) 0245 { 0246 0247 QDomDocument xmlDoc( "videocd PUBLIC \"-//GNU//DTD VideoCD//EN\" \"http://www.gnu.org/software/vcdimager/videocd.dtd\"" ); 0248 // xmlDoc.appendChild( xmlDoc.createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"iso-8859-1\"" ) ); 0249 xmlDoc.appendChild( xmlDoc.createProcessingInstruction( "xml", "version=\"1.0\"" ) ); 0250 0251 // create root element 0252 QDomElement root = xmlDoc.createElement( "videocd" ); 0253 root.setAttribute( "xmlns", "http://www.gnu.org/software/vcdimager/1.0/" ); 0254 root.setAttribute( "class", d->doc->vcdOptions() ->vcdClass() ); 0255 root.setAttribute( "version", d->doc->vcdOptions() ->vcdVersion() ); 0256 xmlDoc.appendChild( root ); 0257 0258 // create option elements 0259 0260 // Broken SVCD mode - NonCompliantMode 0261 if ( d->doc->vcdOptions() ->NonCompliantMode() ) { 0262 QDomElement elemOption; 0263 elemOption = d->addSubElement( xmlDoc, root, "option" ); 0264 elemOption.setAttribute( "name", "svcd vcd30 mpegav" ); 0265 elemOption.setAttribute( "value", "true" ); 0266 0267 elemOption = d->addSubElement( xmlDoc, root, "option" ); 0268 elemOption.setAttribute( "name", "svcd vcd30 entrysvd" ); 0269 elemOption.setAttribute( "value", "true" ); 0270 } 0271 0272 // VCD3.0 track interpretation 0273 if ( d->doc->vcdOptions() ->VCD30interpretation() ) { 0274 QDomElement elemOption; 0275 elemOption = d->addSubElement( xmlDoc, root, "option" ); 0276 elemOption.setAttribute( "name", "svcd vcd30 tracksvd" ); 0277 elemOption.setAttribute( "value", "true" ); 0278 } 0279 0280 // Relaxed aps 0281 if ( d->doc->vcdOptions() ->RelaxedAps() ) { 0282 QDomElement elemOption; 0283 elemOption = d->addSubElement( xmlDoc, root, "option" ); 0284 elemOption.setAttribute( "name", "relaxed aps" ); 0285 elemOption.setAttribute( "value", "true" ); 0286 } 0287 0288 // Update scan offsets 0289 if ( d->doc->vcdOptions() ->UpdateScanOffsets() ) { 0290 QDomElement elemOption; 0291 elemOption = d->addSubElement( xmlDoc, root, "option" ); 0292 elemOption.setAttribute( "name", "update scan offsets" ); 0293 elemOption.setAttribute( "value", "true" ); 0294 0295 } 0296 0297 // Gaps & Margins 0298 if ( d->doc->vcdOptions() ->UseGaps() ) { 0299 QDomElement elemOption; 0300 elemOption = d->addSubElement( xmlDoc, root, "option" ); 0301 elemOption.setAttribute( "name", "leadout pregap" ); 0302 elemOption.setAttribute( "value", d->doc->vcdOptions() ->PreGapLeadout() ); 0303 0304 elemOption = d->addSubElement( xmlDoc, root, "option" ); 0305 elemOption.setAttribute( "name", "track pregap" ); 0306 elemOption.setAttribute( "value", d->doc->vcdOptions() ->PreGapTrack() ); 0307 0308 if ( d->doc->vcdOptions() ->vcdClass() == "vcd" ) { 0309 elemOption = d->addSubElement( xmlDoc, root, "option" ); 0310 elemOption.setAttribute( "name", "track front margin" ); 0311 elemOption.setAttribute( "value", d->doc->vcdOptions() ->FrontMarginTrack() ); 0312 0313 elemOption = d->addSubElement( xmlDoc, root, "option" ); 0314 elemOption.setAttribute( "name", "track rear margin" ); 0315 elemOption.setAttribute( "value", d->doc->vcdOptions() ->RearMarginTrack() ); 0316 } else { 0317 elemOption = d->addSubElement( xmlDoc, root, "option" ); 0318 elemOption.setAttribute( "name", "track front margin" ); 0319 elemOption.setAttribute( "value", d->doc->vcdOptions() ->FrontMarginTrackSVCD() ); 0320 0321 elemOption = d->addSubElement( xmlDoc, root, "option" ); 0322 elemOption.setAttribute( "name", "track rear margin" ); 0323 elemOption.setAttribute( "value", d->doc->vcdOptions() ->RearMarginTrackSVCD() ); 0324 } 0325 0326 } 0327 0328 // create info element 0329 QDomElement elemInfo = d->addSubElement( xmlDoc, root, "info" ); 0330 d->addSubElement( xmlDoc, elemInfo, "album-id", d->doc->vcdOptions() ->albumId().toUpper() ); 0331 d->addSubElement( xmlDoc, elemInfo, "volume-count", d->doc->vcdOptions() ->volumeCount() ); 0332 d->addSubElement( xmlDoc, elemInfo, "volume-number", d->doc->vcdOptions() ->volumeNumber() ); 0333 d->addSubElement( xmlDoc, elemInfo, "restriction", d->doc->vcdOptions() ->Restriction() ); 0334 0335 // create pvd element 0336 QDomElement elemPvd = d->addSubElement( xmlDoc, root, "pvd" ); 0337 d->addSubElement( xmlDoc, elemPvd, "volume-id", d->doc->vcdOptions() ->volumeId().toUpper() ); 0338 d->addSubElement( xmlDoc, elemPvd, "system-id", d->doc->vcdOptions() ->systemId() ); 0339 d->addSubElement( xmlDoc, elemPvd, "application-id", d->doc->vcdOptions() ->applicationId() ); 0340 d->addSubElement( xmlDoc, elemPvd, "preparer-id", QString( "K3b - Version %1" ).arg( k3bcore->version() ).toUpper() ); 0341 d->addSubElement( xmlDoc, elemPvd, "publisher-id", d->doc->vcdOptions() ->publisher().toUpper() ); 0342 0343 0344 // create filesystem element 0345 QDomElement elemFileSystem = d->addSubElement( xmlDoc, root, "filesystem" ); 0346 0347 // SEGMENT folder, some standalone DVD-Player need this 0348 if ( !d->doc->vcdOptions() ->haveSegments() && d->doc->vcdOptions() ->SegmentFolder() ) 0349 d->addFolderElement( xmlDoc, elemFileSystem, "SEGMENT" ); 0350 0351 // create cdi element 0352 if ( d->doc->vcdOptions() ->CdiSupport() ) { 0353 QDomElement elemFolder = d->addFolderElement( xmlDoc, elemFileSystem, "CDI" ); 0354 0355 d->addFileElement( xmlDoc, elemFolder, QStandardPaths::locate( QStandardPaths::GenericDataLocation, "k3b/cdi/cdi_imag.rtf" ), "CDI_IMAG.RTF", true ); 0356 d->addFileElement( xmlDoc, elemFolder, QStandardPaths::locate( QStandardPaths::GenericDataLocation, "k3b/cdi/cdi_text.fnt" ), "CDI_TEXT.FNT" ); 0357 d->addFileElement( xmlDoc, elemFolder, QStandardPaths::locate( QStandardPaths::GenericDataLocation, "k3b/cdi/cdi_vcd.app" ), "CDI_VCD.APP" ); 0358 0359 QString dirPath = QStandardPaths::writableLocation( QStandardPaths::AppDataLocation ) + "/cdi"; 0360 QDir().mkpath(dirPath); 0361 QString usercdicfg = dirPath + "cdi_vcd.cfg"; 0362 if ( QFile::exists( usercdicfg ) ) 0363 d->addFileElement( xmlDoc, elemFolder, usercdicfg, "CDI_VCD.CFG" ); 0364 else 0365 d->addFileElement( xmlDoc, elemFolder, QStandardPaths::locate( QStandardPaths::GenericDataLocation, "k3b/cdi/cdi_vcd.cfg" ), "CDI_VCD.CFG" ); 0366 } 0367 0368 // sequence-items element & segment-items element 0369 QDomElement elemsequenceItems; 0370 QDomElement elemsegmentItems; 0371 0372 // sequence-item element & segment-item element 0373 QDomElement elemsequenceItem; 0374 QDomElement elemsegmentItem; 0375 0376 // if we have segments, elemsegmentItems must be before any sequence in xml file order 0377 if ( d->doc->vcdOptions()->haveSegments() ) 0378 elemsegmentItems = d->addSubElement( xmlDoc, root, "segment-items" ); 0379 0380 // sequence must always available ... 0381 elemsequenceItems = d->addSubElement( xmlDoc, root, "sequence-items" ); 0382 // 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 0383 if ( !d->doc->vcdOptions()->haveSequence() ) { 0384 QString filename; 0385 if ( d->doc->vcdOptions()->mpegVersion() == 1 ) 0386 filename = QStandardPaths::locate( QStandardPaths::GenericDataLocation, "k3b/extra/k3bphotovcd.mpg" ); 0387 else 0388 filename = QStandardPaths::locate( QStandardPaths::GenericDataLocation, "k3b/extra/k3bphotosvcd.mpg" ); 0389 0390 elemsequenceItem = d->addSubElement( xmlDoc, elemsequenceItems, "sequence-item" ); 0391 elemsequenceItem.setAttribute( "src", filename ); 0392 elemsequenceItem.setAttribute( "id", "sequence-000" ); 0393 // default entry 0394 QDomElement elemdefaultEntry; 0395 elemdefaultEntry = d->addSubElement( xmlDoc, elemsequenceItem, "default-entry" ); 0396 elemdefaultEntry.setAttribute( "id", "entry-000" ); 0397 } 0398 0399 0400 // pbc 0401 QDomElement elemPbc; 0402 0403 // Add Tracks to XML 0404 Q_FOREACH( K3b::VcdTrack* track, *d->doc->tracks() ) { 0405 if ( !track ->isSegment() ) { 0406 QString seqId = QString::number( track ->index() ).rightJustified( 3, '0' ); 0407 0408 elemsequenceItem = d->addSubElement( xmlDoc, elemsequenceItems, "sequence-item" ); 0409 elemsequenceItem.setAttribute( "src", track ->absolutePath() ); 0410 elemsequenceItem.setAttribute( "id", QString( "sequence-%1" ).arg( seqId ) ); 0411 0412 // default entry 0413 QDomElement elemdefaultEntry; 0414 elemdefaultEntry = d->addSubElement( xmlDoc, elemsequenceItem, "default-entry" ); 0415 elemdefaultEntry.setAttribute( "id", QString( "entry-%1" ).arg( seqId ) ); 0416 0417 } else { 0418 // sequence-items element needs at least one segment to fit the XML 0419 elemsegmentItem = d->addSubElement( xmlDoc, elemsegmentItems, "segment-item" ); 0420 elemsegmentItem.setAttribute( "src", track ->absolutePath() ); 0421 elemsegmentItem.setAttribute( "id", QString( "segment-%1" ).arg( QString::number( track ->index() ).rightJustified( 3, '0' ) ) ); 0422 0423 } 0424 } 0425 Q_FOREACH( K3b::VcdTrack* track, *d->doc->tracks() ) { 0426 if ( d->doc->vcdOptions() ->PbcEnabled() ) { 0427 if ( elemPbc.isNull() ) 0428 elemPbc = d->addSubElement( xmlDoc, root, "pbc" ); 0429 0430 d->doPbc( xmlDoc, elemPbc, track ); 0431 } 0432 } 0433 0434 if ( ! elemPbc.isNull() ) { 0435 QDomElement elemEndlist = d->addSubElement( xmlDoc, elemPbc, "endlist" ); 0436 elemEndlist.setAttribute( "id", "end" ); 0437 elemEndlist.setAttribute( "rejected", "true" ); 0438 } 0439 0440 d->xmlstring = xmlDoc.toString(); 0441 0442 QTextStream ts( &file ); 0443 ts << d->xmlstring; 0444 } 0445 0446 const QString& K3b::VcdXmlView::xmlString() const 0447 { 0448 return d->xmlstring; 0449 }