File indexing completed on 2024-05-12 04:51:03

0001 /*
0002     SPDX-FileCopyrightText: 2011 Michal Malek <michalm@jabster.pl>
0003     SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 
0009 #include "k3bdatadoc.h"
0010 #include "k3bfileitem.h"
0011 #include "k3bdataitem.h"
0012 #include "k3bdiritem.h"
0013 #include "k3bsessionimportitem.h"
0014 #include "k3bdatajob.h"
0015 #include "k3bbootitem.h"
0016 #include "k3bspecialdataitem.h"
0017 #include "k3bfilecompilationsizehandler.h"
0018 #include "k3bmkisofshandler.h"
0019 #include "k3bcore.h"
0020 #include "k3bglobals.h"
0021 #include "k3bmsf.h"
0022 #include "k3biso9660.h"
0023 #include "k3bisooptions.h"
0024 #include "k3bdevicehandler.h"
0025 #include "k3bdevice.h"
0026 #include "k3btoc.h"
0027 #include "k3btrack.h"
0028 #include "k3bmultichoicedialog.h"
0029 #include "k3bvalidators.h"
0030 #include "k3bglobalsettings.h"
0031 #include "k3b_i18n.h"
0032 
0033 #include <KConfig>
0034 #include <KMessageBox>
0035 
0036 #include <QDebug>
0037 #include <QDir>
0038 #include <QFile>
0039 #include <QFileInfo>
0040 #include <QStringList>
0041 #include <QTimer>
0042 #include <QApplication>
0043 #include <QDomElement>
0044 
0045 #include <string.h>
0046 #include <stdlib.h>
0047 #include <ctype.h>
0048 
0049 
0050 class K3b::DataDoc::Private
0051 {
0052 public:
0053     Private()
0054     :
0055         oldSessionSize( 0 ),
0056         root( 0 ),
0057         dataMode( 0 ),
0058         verifyData( false ),
0059         importedSession( -1 ),
0060         bootCataloge( 0 ),
0061         bExistingItemsReplaceAll( false ),
0062         bExistingItemsIgnoreAll( false ),
0063         needToCutFilenames( false )
0064     {
0065         sizeHandler = new K3b::FileCompilationSizeHandler();
0066     }
0067 
0068     ~Private()
0069     {
0070         delete root;
0071         delete sizeHandler;
0072         //  delete oldSessionSizeHandler;
0073     }
0074 
0075     FileCompilationSizeHandler* sizeHandler;
0076 
0077     //  FileCompilationSizeHandler* oldSessionSizeHandler;
0078     KIO::filesize_t oldSessionSize;
0079 
0080     QStringList notFoundFiles;
0081     QStringList noPermissionFiles;
0082 
0083     RootItem* root;
0084 
0085     int dataMode;
0086 
0087     bool verifyData;
0088 
0089     IsoOptions isoOptions;
0090 
0091     MultiSessionMode multisessionMode;
0092     QList<DataItem*> oldSession;
0093     int importedSession;
0094 
0095     // boot cd stuff
0096     DataItem* bootCataloge;
0097     QList<BootItem*> bootImages;
0098 
0099     bool bExistingItemsReplaceAll;
0100     bool bExistingItemsIgnoreAll;
0101 
0102     bool needToCutFilenames;
0103     QList<DataItem*> needToCutFilenameItems;
0104 };
0105 
0106 
0107 /**
0108  * There are two ways to fill a data project with files and folders:
0109  * \li Use the addUrl and addUrlsT methods
0110  * \li or create your own K3b::DirItems and K3b::FileItems. The doc will be properly updated
0111  *     by the constructors of the items.
0112  */
0113 K3b::DataDoc::DataDoc( QObject* parent )
0114     : K3b::Doc( parent ),
0115       d( new Private )
0116 {
0117 }
0118 
0119 
0120 K3b::DataDoc::~DataDoc()
0121 {
0122     delete d;
0123 }
0124 
0125 
0126 bool K3b::DataDoc::newDocument()
0127 {
0128     clear();
0129     if ( !d->root )
0130         d->root = new K3b::RootItem( *this );
0131 
0132     d->bExistingItemsReplaceAll = d->bExistingItemsIgnoreAll = false;
0133 
0134     d->multisessionMode = AUTO;
0135     d->dataMode = K3b::DataModeAuto;
0136 
0137     d->isoOptions = K3b::IsoOptions();
0138 
0139     return K3b::Doc::newDocument();
0140 }
0141 
0142 
0143 void K3b::DataDoc::clear()
0144 {
0145     clearImportedSession();
0146     d->importedSession = -1;
0147     d->oldSessionSize = 0;
0148     d->bootCataloge = 0;
0149     if( d->root ) {
0150         while( !d->root->children().isEmpty() )
0151             removeItem( d->root->children().first() );
0152     }
0153     d->sizeHandler->clear();
0154     emit importedSessionChanged( importedSession() );
0155 }
0156 
0157 
0158 QString K3b::DataDoc::name() const
0159 {
0160     return d->isoOptions.volumeID();
0161 }
0162 
0163 
0164 const K3b::IsoOptions& K3b::DataDoc::isoOptions() const
0165 {
0166     return d->isoOptions;
0167 }
0168 
0169 
0170 void K3b::DataDoc::setIsoOptions( const K3b::IsoOptions& isoOptions )
0171 {
0172     d->isoOptions = isoOptions;
0173     emit changed();
0174 }
0175 
0176 
0177 void K3b::DataDoc::setVolumeID( const QString& v )
0178 {
0179     d->isoOptions.setVolumeID( v );
0180     emit changed();
0181     emit volumeIdChanged();
0182 }
0183 
0184 
0185 void K3b::DataDoc::addUrls( const QList<QUrl>& urls )
0186 {
0187     addUrlsToDir( urls, root() );
0188 }
0189 
0190 
0191 void K3b::DataDoc::addUrlsToDir( const QList<QUrl>& l, K3b::DirItem* dir )
0192 {
0193     if( !dir )
0194         dir = root();
0195 
0196     QList<QUrl> urls = K3b::convertToLocalUrls(l);
0197 
0198     for( QList<QUrl>::ConstIterator it = urls.constBegin(); it != urls.constEnd(); ++it ) {
0199         const QUrl& url = *it;
0200         QFileInfo f( url.toLocalFile() );
0201         QString k3bname = f.absoluteFilePath().section( '/', -1 );
0202 
0203         // filenames cannot end in backslashes (mkisofs problem. See comments in k3bisoimager.cpp (escapeGraftPoint()))
0204         while( k3bname[k3bname.length()-1] == '\\' )
0205             k3bname.truncate( k3bname.length()-1 );
0206 
0207         // backup dummy name
0208         if( k3bname.isEmpty() )
0209             k3bname = '1';
0210 
0211         K3b::DirItem* newDirItem = 0;
0212 
0213         // rename the new item if an item with that name already exists
0214         int cnt = 0;
0215         bool ok = false;
0216         while( !ok ) {
0217             ok = true;
0218             QString name( k3bname );
0219             if( cnt > 0 )
0220                 name += QString("_%1").arg(cnt);
0221             if( K3b::DataItem* oldItem = dir->find( name ) ) {
0222                 if( f.isDir() && oldItem->isDir() ) {
0223                     // ok, just reuse the dir
0224                     newDirItem = static_cast<K3b::DirItem*>(oldItem);
0225                 }
0226                 // directories cannot replace files in an old session (I think)
0227                 // and also directories can for sure never be replaced (only be reused as above)
0228                 // so we always rename if the old item is a dir.
0229                 else if( !oldItem->isFromOldSession() ||
0230                          f.isDir() ||
0231                          oldItem->isDir() ) {
0232                     ++cnt;
0233                     ok = false;
0234                 }
0235             }
0236         }
0237         if( cnt > 0 )
0238             k3bname += QString("_%1").arg(cnt);
0239 
0240         // QFileInfo::exists and QFileInfo::isReadable return false for broken symlinks :(
0241         if( f.isDir() && !f.isSymLink() ) {
0242             if( !newDirItem ) {
0243                 newDirItem = new K3b::DirItem( k3bname );
0244                 newDirItem->setLocalPath( url.toLocalFile() ); // HACK: see k3bdiritem.h
0245                 dir->addDataItem( newDirItem );
0246             }
0247 
0248             // recursively add all the files in the directory
0249             QStringList dlist = QDir( f.absoluteFilePath() ).entryList( QDir::AllEntries|QDir::System|QDir::Hidden|QDir::NoDotAndDotDot );
0250             QList<QUrl> newUrls;
0251             for( QStringList::ConstIterator it = dlist.constBegin(); it != dlist.constEnd(); ++it )
0252                 newUrls.append( QUrl::fromLocalFile( f.absoluteFilePath() + '/' + *it ) );
0253             addUrlsToDir( newUrls, newDirItem );
0254         }
0255         else if( f.isSymLink() || f.isFile() ) {
0256             dir->addDataItem( new FileItem( url.toLocalFile(), *this, k3bname ) );
0257         }
0258     }
0259 
0260     emit changed();
0261 
0262     setModified( true );
0263 }
0264 
0265 
0266 bool K3b::DataDoc::nameAlreadyInDir( const QString& name, K3b::DirItem* dir )
0267 {
0268     if( !dir )
0269         return false;
0270     else
0271         return ( dir->find( name ) != 0 );
0272 }
0273 
0274 
0275 K3b::DirItem* K3b::DataDoc::addEmptyDir( const QString& name, K3b::DirItem* parent )
0276 {
0277     if( parent != 0 ) {
0278         K3b::DirItem* item = new K3b::DirItem( name );
0279         parent->addDataItem( item );
0280 
0281         setModified( true );
0282 
0283         return item;
0284     } else {
0285         return 0;
0286     }
0287 }
0288 
0289 
0290 KIO::filesize_t K3b::DataDoc::size() const
0291 {
0292     if( d->isoOptions.doNotCacheInodes() )
0293         return root()->blocks().mode1Bytes() + d->oldSessionSize;
0294     else
0295         return d->sizeHandler->blocks( d->isoOptions.followSymbolicLinks() ||
0296                                       !d->isoOptions.createRockRidge() ).mode1Bytes();
0297 }
0298 
0299 
0300 KIO::filesize_t K3b::DataDoc::burningSize() const
0301 {
0302     return size() - d->oldSessionSize; //d->oldSessionSizeHandler->size();
0303 }
0304 
0305 
0306 K3b::Msf K3b::DataDoc::length() const
0307 {
0308     // 1 block consists of 2048 bytes real data
0309     // and 1 block equals to 1 audio frame
0310     // so this is the way to calculate:
0311 
0312     return K3b::Msf( size() / 2048 );
0313 }
0314 
0315 
0316 K3b::Msf K3b::DataDoc::burningLength() const
0317 {
0318     return K3b::Msf( burningSize() / 2048 );
0319 }
0320 
0321 
0322 bool K3b::DataDoc::loadDocumentData( QDomElement* rootElem )
0323 {
0324     if( !root() )
0325         newDocument();
0326 
0327     QDomNodeList nodes = rootElem->childNodes();
0328 
0329     if( nodes.item(0).nodeName() != "general" ) {
0330         qDebug() << "(K3b::DataDoc) could not find 'general' section.";
0331         return false;
0332     }
0333     if( !readGeneralDocumentData( nodes.item(0).toElement() ) )
0334         return false;
0335 
0336 
0337     // parse options
0338     // -----------------------------------------------------------------
0339     if( nodes.item(1).nodeName() != "options" ) {
0340         qDebug() << "(K3b::DataDoc) could not find 'options' section.";
0341         return false;
0342     }
0343     if( !loadDocumentDataOptions( nodes.item(1).toElement() ) )
0344         return false;
0345     // -----------------------------------------------------------------
0346 
0347 
0348 
0349     // parse header
0350     // -----------------------------------------------------------------
0351     if( nodes.item(2).nodeName() != "header" ) {
0352         qDebug() << "(K3b::DataDoc) could not find 'header' section.";
0353         return false;
0354     }
0355     if( !loadDocumentDataHeader( nodes.item(2).toElement() ) )
0356         return false;
0357     // -----------------------------------------------------------------
0358 
0359 
0360 
0361     // parse files
0362     // -----------------------------------------------------------------
0363     if( nodes.item(3).nodeName() != "files" ) {
0364         qDebug() << "(K3b::DataDoc) could not find 'files' section.";
0365         return false;
0366     }
0367 
0368     if( d->root == 0 )
0369         d->root = new K3b::RootItem( *this );
0370 
0371     QDomNodeList filesList = nodes.item(3).childNodes();
0372     for( int i = 0; i < filesList.count(); i++ ) {
0373 
0374         QDomElement e = filesList.item(i).toElement();
0375         if( !loadDataItem( e, root() ) )
0376             return false;
0377     }
0378 
0379     // -----------------------------------------------------------------
0380 
0381     //
0382     // Old versions of K3b do not properly save the boot catalog location
0383     // and name. So to ensure we have one around even if loading an old project
0384     // file we create a default one here.
0385     //
0386     if( !d->bootImages.isEmpty() && !d->bootCataloge )
0387         createBootCatalogeItem( d->bootImages.first()->parent() );
0388 
0389 
0390     informAboutNotFoundFiles();
0391 
0392     return true;
0393 }
0394 
0395 
0396 bool K3b::DataDoc::loadDocumentDataOptions( QDomElement elem )
0397 {
0398     QDomNodeList headerList = elem.childNodes();
0399     for( int i = 0; i < headerList.count(); i++ ) {
0400 
0401         QDomElement e = headerList.item(i).toElement();
0402         if( e.isNull() )
0403             return false;
0404 
0405         if( e.nodeName() == "rock_ridge")
0406             d->isoOptions.setCreateRockRidge( e.attributeNode( "activated" ).value() == "yes" );
0407 
0408         else if( e.nodeName() == "joliet")
0409             d->isoOptions.setCreateJoliet( e.attributeNode( "activated" ).value() == "yes" );
0410 
0411         else if( e.nodeName() == "udf")
0412             d->isoOptions.setCreateUdf( e.attributeNode( "activated" ).value() == "yes" );
0413 
0414         else if( e.nodeName() == "joliet_allow_103_characters")
0415             d->isoOptions.setJolietLong( e.attributeNode( "activated" ).value() == "yes" );
0416 
0417         else if( e.nodeName() == "iso_allow_lowercase")
0418             d->isoOptions.setISOallowLowercase( e.attributeNode( "activated" ).value() == "yes" );
0419 
0420         else if( e.nodeName() == "iso_allow_period_at_begin")
0421             d->isoOptions.setISOallowPeriodAtBegin( e.attributeNode( "activated" ).value() == "yes" );
0422 
0423         else if( e.nodeName() == "iso_allow_31_char")
0424             d->isoOptions.setISOallow31charFilenames( e.attributeNode( "activated" ).value() == "yes" );
0425 
0426         else if( e.nodeName() == "iso_omit_version_numbers")
0427             d->isoOptions.setISOomitVersionNumbers( e.attributeNode( "activated" ).value() == "yes" );
0428 
0429         else if( e.nodeName() == "iso_omit_trailing_period")
0430             d->isoOptions.setISOomitTrailingPeriod( e.attributeNode( "activated" ).value() == "yes" );
0431 
0432         else if( e.nodeName() == "iso_max_filename_length")
0433             d->isoOptions.setISOmaxFilenameLength( e.attributeNode( "activated" ).value() == "yes" );
0434 
0435         else if( e.nodeName() == "iso_relaxed_filenames")
0436             d->isoOptions.setISOrelaxedFilenames( e.attributeNode( "activated" ).value() == "yes" );
0437 
0438         else if( e.nodeName() == "iso_no_iso_translate")
0439             d->isoOptions.setISOnoIsoTranslate( e.attributeNode( "activated" ).value() == "yes" );
0440 
0441         else if( e.nodeName() == "iso_allow_multidot")
0442             d->isoOptions.setISOallowMultiDot( e.attributeNode( "activated" ).value() == "yes" );
0443 
0444         else if( e.nodeName() == "iso_untranslated_filenames")
0445             d->isoOptions.setISOuntranslatedFilenames( e.attributeNode( "activated" ).value() == "yes" );
0446 
0447         else if( e.nodeName() == "follow_symbolic_links")
0448             d->isoOptions.setFollowSymbolicLinks( e.attributeNode( "activated" ).value() == "yes" );
0449 
0450         else if( e.nodeName() == "create_trans_tbl")
0451             d->isoOptions.setCreateTRANS_TBL( e.attributeNode( "activated" ).value() == "yes" );
0452 
0453         else if( e.nodeName() == "hide_trans_tbl")
0454             d->isoOptions.setHideTRANS_TBL( e.attributeNode( "activated" ).value() == "yes" );
0455 
0456         else if( e.nodeName() == "iso_level")
0457             d->isoOptions.setISOLevel( e.text().toInt() );
0458 
0459         else if( e.nodeName() == "discard_symlinks")
0460             d->isoOptions.setDiscardSymlinks( e.attributeNode( "activated" ).value() == "yes" );
0461 
0462         else if( e.nodeName() == "discard_broken_symlinks")
0463             d->isoOptions.setDiscardBrokenSymlinks( e.attributeNode( "activated" ).value() == "yes" );
0464 
0465         else if( e.nodeName() == "preserve_file_permissions")
0466             d->isoOptions.setPreserveFilePermissions( e.attributeNode( "activated" ).value() == "yes" );
0467 
0468         else if( e.nodeName() == "do_not_cache_inodes" )
0469             d->isoOptions.setDoNotCacheInodes( e.attributeNode( "activated" ).value() == "yes" );
0470 
0471         else if( e.nodeName() == "whitespace_treatment" ) {
0472             if( e.text() == "strip" )
0473                 d->isoOptions.setWhiteSpaceTreatment( K3b::IsoOptions::strip );
0474             else if( e.text() == "extended" )
0475                 d->isoOptions.setWhiteSpaceTreatment( K3b::IsoOptions::extended );
0476             else if (e.text() == "replace")
0477                 d->isoOptions.setWhiteSpaceTreatment( K3b::IsoOptions::replace );
0478             else
0479                 d->isoOptions.setWhiteSpaceTreatment( K3b::IsoOptions::noChange );
0480         }
0481 
0482         else if( e.nodeName() == "whitespace_replace_string")
0483             d->isoOptions.setWhiteSpaceTreatmentReplaceString( e.text() );
0484 
0485         else if( e.nodeName() == "data_track_mode" ) {
0486             if( e.text() == "mode1" )
0487                 d->dataMode = K3b::DataMode1;
0488             else if( e.text() == "mode2" )
0489                 d->dataMode = K3b::DataMode2;
0490             else
0491                 d->dataMode = K3b::DataModeAuto;
0492         }
0493 
0494         else if( e.nodeName() == "multisession" ) {
0495             QString mode = e.text();
0496             if( mode == "start" )
0497                 setMultiSessionMode( START );
0498             else if( mode == "continue" )
0499                 setMultiSessionMode( CONTINUE );
0500             else if( mode == "finish" )
0501                 setMultiSessionMode( FINISH );
0502             else if( mode == "none" )
0503                 setMultiSessionMode( NONE );
0504             else
0505                 setMultiSessionMode( AUTO );
0506         }
0507 
0508         else if( e.nodeName() == "verify_data" )
0509             setVerifyData( e.attributeNode( "activated" ).value() == "yes" );
0510 
0511         else
0512             qDebug() << "(K3b::DataDoc) unknown option entry: " << e.nodeName();
0513     }
0514 
0515     return true;
0516 }
0517 
0518 
0519 bool K3b::DataDoc::loadDocumentDataHeader( QDomElement headerElem )
0520 {
0521     QDomNodeList headerList = headerElem.childNodes();
0522     for( int i = 0; i < headerList.count(); i++ ) {
0523 
0524         QDomElement e = headerList.item(i).toElement();
0525         if( e.isNull() )
0526             return false;
0527 
0528         if( e.nodeName() == "volume_id" )
0529             d->isoOptions.setVolumeID( e.text() );
0530 
0531         else if( e.nodeName() == "application_id" )
0532             d->isoOptions.setApplicationID( e.text() );
0533 
0534         else if( e.nodeName() == "publisher" )
0535             d->isoOptions.setPublisher( e.text() );
0536 
0537         else if( e.nodeName() == "preparer" )
0538             d->isoOptions.setPreparer( e.text() );
0539 
0540         else if( e.nodeName() == "volume_set_id" )
0541             d->isoOptions.setVolumeSetId( e.text() );
0542 
0543         else if( e.nodeName() == "volume_set_size" )
0544             d->isoOptions.setVolumeSetSize( e.text().toInt() );
0545 
0546         else if( e.nodeName() == "volume_set_number" )
0547             d->isoOptions.setVolumeSetNumber( e.text().toInt() );
0548 
0549         else if( e.nodeName() == "system_id" )
0550             d->isoOptions.setSystemId( e.text() );
0551 
0552         else
0553             qDebug() << "(K3b::DataDoc) unknown header entry: " << e.nodeName();
0554     }
0555 
0556     return true;
0557 }
0558 
0559 
0560 bool K3b::DataDoc::loadDataItem( QDomElement& elem, K3b::DirItem* parent )
0561 {
0562     if( !parent )
0563         return false;
0564 
0565     K3b::DataItem* newItem = 0;
0566 
0567     if( elem.nodeName() == "file" ) {
0568         QDomElement urlElem = elem.firstChild().toElement();
0569         if( urlElem.isNull() ) {
0570             qDebug() << "(K3b::DataDoc) file-element without url!";
0571             return false;
0572         }
0573 
0574         QFileInfo f( urlElem.text() );
0575 
0576         // We cannot use exists() here since this always disqualifies broken symlinks
0577         if( !f.isFile() && !f.isSymLink() )
0578             d->notFoundFiles.append( urlElem.text() );
0579 
0580         // broken symlinks are not readable according to QFileInfo which is wrong in our case
0581         else if( f.isFile() && !f.isReadable() )
0582             d->noPermissionFiles.append( urlElem.text() );
0583 
0584         else if( !elem.attribute( "bootimage" ).isEmpty() ) {
0585             K3b::BootItem* bootItem = new K3b::BootItem( urlElem.text(),
0586                                                          *this,
0587                                                          elem.attributeNode( "name" ).value() );
0588             parent->addDataItem( bootItem );
0589             if( elem.attribute( "bootimage" ) == "floppy" )
0590                 bootItem->setImageType( K3b::BootItem::FLOPPY );
0591             else if( elem.attribute( "bootimage" ) == "harddisk" )
0592                 bootItem->setImageType( K3b::BootItem::HARDDISK );
0593             else
0594                 bootItem->setImageType( K3b::BootItem::NONE );
0595             bootItem->setNoBoot( elem.attribute( "no_boot" ) == "yes" );
0596             bootItem->setBootInfoTable( elem.attribute( "boot_info_table" ) == "yes" );
0597             bootItem->setLoadSegment( elem.attribute( "load_segment" ).toInt() );
0598             bootItem->setLoadSize( elem.attribute( "load_size" ).toInt() );
0599 
0600             newItem = bootItem;
0601         }
0602 
0603         else {
0604             newItem = new K3b::FileItem( urlElem.text(),
0605                                          *this,
0606                                          elem.attributeNode( "name" ).value() );
0607             parent->addDataItem( newItem );
0608         }
0609     }
0610     else if( elem.nodeName() == "special" ) {
0611         if( elem.attributeNode( "type" ).value() == "boot cataloge" )
0612             createBootCatalogeItem( parent )->setK3bName( elem.attributeNode( "name" ).value() );
0613     }
0614     else if( elem.nodeName() == "directory" ) {
0615         // This is for the VideoDVD project which already contains the *_TS folders
0616         K3b::DirItem* newDirItem = 0;
0617         if( K3b::DataItem* item = parent->find( elem.attributeNode( "name" ).value() ) ) {
0618             if( item->isDir() ) {
0619                 newDirItem = static_cast<K3b::DirItem*>(item);
0620             }
0621             else {
0622                 qCritical() << "(K3b::DataDoc) INVALID DOCUMENT: item " << item->k3bPath() << " saved twice" << Qt::endl;
0623                 return false;
0624             }
0625         }
0626 
0627         if( !newDirItem ) {
0628             newDirItem = new K3b::DirItem( elem.attributeNode( "name" ).value() );
0629             parent->addDataItem( newDirItem );
0630         }
0631         QDomNodeList childNodes = elem.childNodes();
0632         for( int i = 0; i < childNodes.count(); i++ ) {
0633 
0634             QDomElement e = childNodes.item(i).toElement();
0635             if( !loadDataItem( e, newDirItem ) )
0636                 return false;
0637         }
0638 
0639         newItem = newDirItem;
0640     }
0641     else {
0642         qDebug() << "(K3b::DataDoc) wrong tag in files-section: " << elem.nodeName();
0643         return false;
0644     }
0645 
0646     // load the sort weight
0647     if( newItem )
0648         newItem->setSortWeight( elem.attribute( "sort_weight", "0" ).toInt() );
0649 
0650     return true;
0651 }
0652 
0653 
0654 bool K3b::DataDoc::saveDocumentData( QDomElement* docElem )
0655 {
0656     QDomDocument doc = docElem->ownerDocument();
0657 
0658     saveGeneralDocumentData( docElem );
0659 
0660     // all options
0661     // ----------------------------------------------------------------------
0662     QDomElement optionsElem = doc.createElement( "options" );
0663     saveDocumentDataOptions( optionsElem );
0664     docElem->appendChild( optionsElem );
0665     // ----------------------------------------------------------------------
0666 
0667     // the header stuff
0668     // ----------------------------------------------------------------------
0669     QDomElement headerElem = doc.createElement( "header" );
0670     saveDocumentDataHeader( headerElem );
0671     docElem->appendChild( headerElem );
0672 
0673 
0674     // now do the "real" work: save the entries
0675     // ----------------------------------------------------------------------
0676     QDomElement topElem = doc.createElement( "files" );
0677 
0678     Q_FOREACH( K3b::DataItem* item, root()->children() ) {
0679         saveDataItem( item, &doc, &topElem );
0680     }
0681 
0682     docElem->appendChild( topElem );
0683     // ----------------------------------------------------------------------
0684 
0685     return true;
0686 }
0687 
0688 
0689 void K3b::DataDoc::saveDocumentDataOptions( QDomElement& optionsElem )
0690 {
0691     QDomDocument doc = optionsElem.ownerDocument();
0692 
0693     QDomElement topElem = doc.createElement( "rock_ridge" );
0694     topElem.setAttribute( "activated", isoOptions().createRockRidge() ? "yes" : "no" );
0695     optionsElem.appendChild( topElem );
0696 
0697     topElem = doc.createElement( "joliet" );
0698     topElem.setAttribute( "activated", isoOptions().createJoliet() ? "yes" : "no" );
0699     optionsElem.appendChild( topElem );
0700 
0701     topElem = doc.createElement( "udf" );
0702     topElem.setAttribute( "activated", isoOptions().createUdf() ? "yes" : "no" );
0703     optionsElem.appendChild( topElem );
0704 
0705     topElem = doc.createElement( "joliet_allow_103_characters" );
0706     topElem.setAttribute( "activated", isoOptions().jolietLong() ? "yes" : "no" );
0707     optionsElem.appendChild( topElem );
0708 
0709     topElem = doc.createElement( "iso_allow_lowercase" );
0710     topElem.setAttribute( "activated", isoOptions().ISOallowLowercase() ? "yes" : "no" );
0711     optionsElem.appendChild( topElem );
0712 
0713     topElem = doc.createElement( "iso_allow_period_at_begin" );
0714     topElem.setAttribute( "activated", isoOptions().ISOallowPeriodAtBegin() ? "yes" : "no" );
0715     optionsElem.appendChild( topElem );
0716 
0717     topElem = doc.createElement( "iso_allow_31_char" );
0718     topElem.setAttribute( "activated", isoOptions().ISOallow31charFilenames() ? "yes" : "no" );
0719     optionsElem.appendChild( topElem );
0720 
0721     topElem = doc.createElement( "iso_omit_version_numbers" );
0722     topElem.setAttribute( "activated", isoOptions().ISOomitVersionNumbers() ? "yes" : "no" );
0723     optionsElem.appendChild( topElem );
0724 
0725     topElem = doc.createElement( "iso_omit_trailing_period" );
0726     topElem.setAttribute( "activated", isoOptions().ISOomitTrailingPeriod() ? "yes" : "no" );
0727     optionsElem.appendChild( topElem );
0728 
0729     topElem = doc.createElement( "iso_max_filename_length" );
0730     topElem.setAttribute( "activated", isoOptions().ISOmaxFilenameLength() ? "yes" : "no" );
0731     optionsElem.appendChild( topElem );
0732 
0733     topElem = doc.createElement( "iso_relaxed_filenames" );
0734     topElem.setAttribute( "activated", isoOptions().ISOrelaxedFilenames() ? "yes" : "no" );
0735     optionsElem.appendChild( topElem );
0736 
0737     topElem = doc.createElement( "iso_no_iso_translate" );
0738     topElem.setAttribute( "activated", isoOptions().ISOnoIsoTranslate() ? "yes" : "no" );
0739     optionsElem.appendChild( topElem );
0740 
0741     topElem = doc.createElement( "iso_allow_multidot" );
0742     topElem.setAttribute( "activated", isoOptions().ISOallowMultiDot() ? "yes" : "no" );
0743     optionsElem.appendChild( topElem );
0744 
0745     topElem = doc.createElement( "iso_untranslated_filenames" );
0746     topElem.setAttribute( "activated", isoOptions().ISOuntranslatedFilenames() ? "yes" : "no" );
0747     optionsElem.appendChild( topElem );
0748 
0749     topElem = doc.createElement( "follow_symbolic_links" );
0750     topElem.setAttribute( "activated", isoOptions().followSymbolicLinks() ? "yes" : "no" );
0751     optionsElem.appendChild( topElem );
0752 
0753     topElem = doc.createElement( "create_trans_tbl" );
0754     topElem.setAttribute( "activated", isoOptions().createTRANS_TBL() ? "yes" : "no" );
0755     optionsElem.appendChild( topElem );
0756 
0757     topElem = doc.createElement( "hide_trans_tbl" );
0758     topElem.setAttribute( "activated", isoOptions().hideTRANS_TBL() ? "yes" : "no" );
0759     optionsElem.appendChild( topElem );
0760 
0761     topElem = doc.createElement( "iso_level" );
0762     topElem.appendChild( doc.createTextNode( QString::number(isoOptions().ISOLevel()) ) );
0763     optionsElem.appendChild( topElem );
0764 
0765     topElem = doc.createElement( "discard_symlinks" );
0766     topElem.setAttribute( "activated", isoOptions().discardSymlinks() ? "yes" : "no" );
0767     optionsElem.appendChild( topElem );
0768 
0769     topElem = doc.createElement( "discard_broken_symlinks" );
0770     topElem.setAttribute( "activated", isoOptions().discardBrokenSymlinks() ? "yes" : "no" );
0771     optionsElem.appendChild( topElem );
0772 
0773     topElem = doc.createElement( "preserve_file_permissions" );
0774     topElem.setAttribute( "activated", isoOptions().preserveFilePermissions() ? "yes" : "no" );
0775     optionsElem.appendChild( topElem );
0776 
0777     topElem = doc.createElement( "do_not_cache_inodes" );
0778     topElem.setAttribute( "activated", isoOptions().doNotCacheInodes() ? "yes" : "no" );
0779     optionsElem.appendChild( topElem );
0780 
0781 
0782     topElem = doc.createElement( "whitespace_treatment" );
0783     switch( isoOptions().whiteSpaceTreatment() ) {
0784     case K3b::IsoOptions::strip:
0785         topElem.appendChild( doc.createTextNode( "strip" ) );
0786         break;
0787     case K3b::IsoOptions::extended:
0788         topElem.appendChild( doc.createTextNode( "extended" ) );
0789         break;
0790     case K3b::IsoOptions::replace:
0791         topElem.appendChild( doc.createTextNode( "replace" ) );
0792         break;
0793     default:
0794         topElem.appendChild( doc.createTextNode( "noChange" ) );
0795         break;
0796     }
0797     optionsElem.appendChild( topElem );
0798 
0799     topElem = doc.createElement( "whitespace_replace_string" );
0800     topElem.appendChild( doc.createTextNode( isoOptions().whiteSpaceTreatmentReplaceString() ) );
0801     optionsElem.appendChild( topElem );
0802 
0803     topElem = doc.createElement( "data_track_mode" );
0804     if( d->dataMode == K3b::DataMode1 )
0805         topElem.appendChild( doc.createTextNode( "mode1" ) );
0806     else if( d->dataMode == K3b::DataMode2 )
0807         topElem.appendChild( doc.createTextNode( "mode2" ) );
0808     else
0809         topElem.appendChild( doc.createTextNode( "auto" ) );
0810     optionsElem.appendChild( topElem );
0811 
0812 
0813     // save multisession
0814     topElem = doc.createElement( "multisession" );
0815     switch( d->multisessionMode ) {
0816     case START:
0817         topElem.appendChild( doc.createTextNode( "start" ) );
0818         break;
0819     case CONTINUE:
0820         topElem.appendChild( doc.createTextNode( "continue" ) );
0821         break;
0822     case FINISH:
0823         topElem.appendChild( doc.createTextNode( "finish" ) );
0824         break;
0825     case NONE:
0826         topElem.appendChild( doc.createTextNode( "none" ) );
0827         break;
0828     default:
0829         topElem.appendChild( doc.createTextNode( "auto" ) );
0830         break;
0831     }
0832     optionsElem.appendChild( topElem );
0833 
0834     topElem = doc.createElement( "verify_data" );
0835     topElem.setAttribute( "activated", verifyData() ? "yes" : "no" );
0836     optionsElem.appendChild( topElem );
0837     // ----------------------------------------------------------------------
0838 }
0839 
0840 
0841 void K3b::DataDoc::saveDocumentDataHeader( QDomElement& headerElem )
0842 {
0843     QDomDocument doc = headerElem.ownerDocument();
0844 
0845     QDomElement topElem = doc.createElement( "volume_id" );
0846     topElem.appendChild( doc.createTextNode( isoOptions().volumeID() ) );
0847     headerElem.appendChild( topElem );
0848 
0849     topElem = doc.createElement( "volume_set_id" );
0850     topElem.appendChild( doc.createTextNode( isoOptions().volumeSetId() ) );
0851     headerElem.appendChild( topElem );
0852 
0853     topElem = doc.createElement( "volume_set_size" );
0854     topElem.appendChild( doc.createTextNode( QString::number(isoOptions().volumeSetSize()) ) );
0855     headerElem.appendChild( topElem );
0856 
0857     topElem = doc.createElement( "volume_set_number" );
0858     topElem.appendChild( doc.createTextNode( QString::number(isoOptions().volumeSetNumber()) ) );
0859     headerElem.appendChild( topElem );
0860 
0861     topElem = doc.createElement( "system_id" );
0862     topElem.appendChild( doc.createTextNode( isoOptions().systemId() ) );
0863     headerElem.appendChild( topElem );
0864 
0865     topElem = doc.createElement( "application_id" );
0866     topElem.appendChild( doc.createTextNode( isoOptions().applicationID() ) );
0867     headerElem.appendChild( topElem );
0868 
0869     topElem = doc.createElement( "publisher" );
0870     topElem.appendChild( doc.createTextNode( isoOptions().publisher() ) );
0871     headerElem.appendChild( topElem );
0872 
0873     topElem = doc.createElement( "preparer" );
0874     topElem.appendChild( doc.createTextNode( isoOptions().preparer() ) );
0875     headerElem.appendChild( topElem );
0876     // ----------------------------------------------------------------------
0877 }
0878 
0879 
0880 void K3b::DataDoc::saveDataItem( K3b::DataItem* item, QDomDocument* doc, QDomElement* parent )
0881 {
0882     if( K3b::FileItem* fileItem = dynamic_cast<K3b::FileItem*>( item ) ) {
0883         if( d->oldSession.contains( fileItem ) ) {
0884             qDebug() << "(K3b::DataDoc) ignoring fileitem " << fileItem->k3bName() << " from old session while saving...";
0885         }
0886         else {
0887             QDomElement topElem = doc->createElement( "file" );
0888             topElem.setAttribute( "name", fileItem->k3bName() );
0889             QDomElement subElem = doc->createElement( "url" );
0890             subElem.appendChild( doc->createTextNode( fileItem->localPath() ) );
0891             topElem.appendChild( subElem );
0892 
0893             if( item->sortWeight() != 0 )
0894                 topElem.setAttribute( "sort_weight", QString::number(item->sortWeight()) );
0895 
0896             parent->appendChild( topElem );
0897 
0898             // add boot options as attributes to preserve compatibility to older K3b versions
0899             if( K3b::BootItem* bootItem = dynamic_cast<K3b::BootItem*>( fileItem ) ) {
0900                 if( bootItem->imageType() == K3b::BootItem::FLOPPY )
0901                     topElem.setAttribute( "bootimage", "floppy" );
0902                 else if( bootItem->imageType() == K3b::BootItem::HARDDISK )
0903                     topElem.setAttribute( "bootimage", "harddisk" );
0904                 else
0905                     topElem.setAttribute( "bootimage", "none" );
0906 
0907                 topElem.setAttribute( "no_boot", bootItem->noBoot() ? "yes" : "no" );
0908                 topElem.setAttribute( "boot_info_table", bootItem->bootInfoTable() ? "yes" : "no" );
0909                 topElem.setAttribute( "load_segment", QString::number( bootItem->loadSegment() ) );
0910                 topElem.setAttribute( "load_size", QString::number( bootItem->loadSize() ) );
0911             }
0912         }
0913     }
0914     else if( item == d->bootCataloge ) {
0915         QDomElement topElem = doc->createElement( "special" );
0916         topElem.setAttribute( "name", d->bootCataloge->k3bName() );
0917         topElem.setAttribute( "type", "boot cataloge" );
0918 
0919         parent->appendChild( topElem );
0920     }
0921     else if( K3b::DirItem* dirItem = dynamic_cast<K3b::DirItem*>( item ) ) {
0922         QDomElement topElem = doc->createElement( "directory" );
0923         topElem.setAttribute( "name", dirItem->k3bName() );
0924 
0925         if( item->sortWeight() != 0 )
0926             topElem.setAttribute( "sort_weight", QString::number(item->sortWeight()) );
0927 
0928         Q_FOREACH( K3b::DataItem* item, dirItem->children() ) {
0929             saveDataItem( item, doc, &topElem );
0930         }
0931 
0932         parent->appendChild( topElem );
0933     }
0934 }
0935 
0936 
0937 void K3b::DataDoc::removeItem( K3b::DataItem* item )
0938 {
0939     if( !item )
0940         return;
0941 
0942     if( item->isRemoveable() ) {
0943         delete item;
0944     }
0945     else
0946         qDebug() << "(K3b::DataDoc) tried to remove non-removable entry!";
0947 }
0948 
0949 
0950 void K3b::DataDoc::removeItems( K3b::DirItem* parent, int start, int count )
0951 {
0952     if( parent ) {
0953         parent->removeDataItems( start, count );
0954     }
0955 }
0956 
0957 
0958 void K3b::DataDoc::beginInsertItems( DirItem* parent, int start, int end )
0959 {
0960     emit itemsAboutToBeInserted( parent, start, end );
0961 }
0962 
0963 
0964 void K3b::DataDoc::endInsertItems( DirItem* parent, int start, int end )
0965 {
0966     for( int i = start; i <= end; ++i ) {
0967         DataItem* item = parent->children().at( i );
0968         // update the project size
0969         if( !item->isFromOldSession() )
0970             d->sizeHandler->addFile( item );
0971 
0972         // update the boot item list
0973         if( item->isBootItem() )
0974             d->bootImages.append( static_cast<K3b::BootItem*>( item ) );
0975     }
0976 
0977     emit itemsInserted( parent, start, end );
0978     emit changed();
0979 }
0980 
0981 
0982 void K3b::DataDoc::beginRemoveItems( DirItem* parent, int start, int end )
0983 {
0984     emit itemsAboutToBeRemoved( parent, start, end );
0985 
0986     for( int i = start; i <= end; ++i ) {
0987         DataItem* item = parent->children().at( i );
0988         // update the project size
0989         if( !item->isFromOldSession() )
0990             d->sizeHandler->removeFile( item );
0991 
0992         // update the boot item list
0993         if( item->isBootItem() ) {
0994             d->bootImages.removeAll( static_cast<K3b::BootItem*>( item ) );
0995             if( d->bootImages.isEmpty() ) {
0996                 delete d->bootCataloge;
0997                 d->bootCataloge = 0;
0998             }
0999         }
1000     }
1001 }
1002 
1003 
1004 void K3b::DataDoc::endRemoveItems( DirItem* parent, int start, int end )
1005 {
1006     emit itemsRemoved( parent, start, end );
1007     emit changed();
1008 }
1009 
1010 
1011 void K3b::DataDoc::moveItem( K3b::DataItem* item, K3b::DirItem* newParent )
1012 {
1013     if( !item || !newParent ) {
1014         qDebug() << "(K3b::DataDoc) item or parentitem was NULL while moving.";
1015         return;
1016     }
1017 
1018     if( !item->isMoveable() ) {
1019         qDebug() << "(K3b::DataDoc) item is not movable! ";
1020         return;
1021     }
1022 
1023     item->reparent( newParent );
1024 }
1025 
1026 
1027 void K3b::DataDoc::moveItems( const QList<K3b::DataItem*>& itemList, K3b::DirItem* newParent )
1028 {
1029     if( !newParent ) {
1030         qDebug() << "(K3b::DataDoc) tried to move items to nowhere...!";
1031         return;
1032     }
1033 
1034     Q_FOREACH( K3b::DataItem* item, itemList ) {
1035         // check if newParent is subdir of item
1036         if( K3b::DirItem* dirItem = dynamic_cast<K3b::DirItem*>( item ) ) {
1037             if( dirItem->isSubItem( newParent ) ) {
1038                 continue;
1039             }
1040         }
1041 
1042         if( item->isMoveable() )
1043             item->reparent( newParent );
1044     }
1045 }
1046 
1047 
1048 K3b::BurnJob* K3b::DataDoc::newBurnJob( K3b::JobHandler* hdl, QObject* parent )
1049 {
1050     return new K3b::DataJob( this, hdl, parent );
1051 }
1052 
1053 
1054 QString K3b::DataDoc::treatWhitespace( const QString& path )
1055 {
1056 
1057     // TODO:
1058     // It could happen that two files with different names
1059     // will have the same name after the treatment
1060     // Perhaps we should add a number at the end or something
1061     // similar (s.a.)
1062 
1063 
1064     if( isoOptions().whiteSpaceTreatment() != K3b::IsoOptions::noChange ) {
1065         QString result = path;
1066 
1067         if( isoOptions().whiteSpaceTreatment() == K3b::IsoOptions::replace ) {
1068             result.replace( ' ', isoOptions().whiteSpaceTreatmentReplaceString() );
1069         }
1070         else if( isoOptions().whiteSpaceTreatment() == K3b::IsoOptions::strip ) {
1071             result.remove( ' ' );
1072         }
1073         else if( isoOptions().whiteSpaceTreatment() == K3b::IsoOptions::extended ) {
1074             result.truncate(0);
1075             for( int i = 0; i < path.length(); i++ ) {
1076                 if( path[i] == ' ' ) {
1077                     if( path[i+1] != ' ' )
1078                         result.append( path[++i].toUpper() );
1079                 }
1080                 else
1081                     result.append( path[i] );
1082             }
1083         }
1084 
1085         qDebug() << "(K3b::DataDoc) converted " << path << " to " << result;
1086         return result;
1087     }
1088     else
1089         return path;
1090 }
1091 
1092 
1093 void K3b::DataDoc::prepareFilenames()
1094 {
1095     d->needToCutFilenames = false;
1096     d->needToCutFilenameItems.clear();
1097 
1098     //
1099     // if joliet is used cut the names and rename if necessary
1100     // 64 characters for standard joliet and 103 characters for long joliet names
1101     //
1102     // Rockridge supports the full 255 UNIX chars and in case Rockridge is disabled we leave
1103     // it to mkisofs for now since handling all the options to alter the ISO9660 standard it just
1104     // too much.
1105     //
1106 
1107     K3b::DataItem* item = root();
1108     int maxlen = ( isoOptions().jolietLong() ? 103 : 64 );
1109     while( (item = item->nextSibling()) ) {
1110         item->setWrittenName( treatWhitespace( item->k3bName() ) );
1111 
1112         if( isoOptions().createJoliet() && item->writtenName().length() > maxlen ) {
1113             d->needToCutFilenames = true;
1114             item->setWrittenName( K3b::cutFilename( item->writtenName(), maxlen ) );
1115             d->needToCutFilenameItems.append( item );
1116         }
1117 
1118         // TODO: check the Joliet charset
1119     }
1120 
1121     //
1122     // 3. check if a directory contains items with the same name
1123     //
1124     prepareFilenamesInDir( root() );
1125 }
1126 
1127 
1128 void K3b::DataDoc::prepareFilenamesInDir( K3b::DirItem* dir )
1129 {
1130     if( !dir )
1131         return;
1132 
1133     QList<K3b::DataItem*> sortedChildren;
1134     QList<K3b::DataItem*> children( dir->children() );
1135     QList<K3b::DataItem*>::const_iterator it = children.constEnd();
1136     while ( it != children.constBegin() ) {
1137         --it;
1138         K3b::DataItem* item = *it;
1139 
1140         if( item->isDir() )
1141             prepareFilenamesInDir( dynamic_cast<K3b::DirItem*>( item ) );
1142 
1143         // insertion sort
1144         int i = 0;
1145         while( i < sortedChildren.count() && item->writtenName() > sortedChildren.at(i)->writtenName() )
1146             ++i;
1147 
1148         sortedChildren.insert( i, item );
1149     }
1150 
1151     if( isoOptions().createJoliet() || isoOptions().createRockRidge() ) {
1152         QList<K3b::DataItem*> sameNameList;
1153         while( !sortedChildren.isEmpty() ) {
1154 
1155             sameNameList.clear();
1156 
1157             do {
1158                 sameNameList.append( sortedChildren.takeFirst() );
1159             } while( !sortedChildren.isEmpty() &&
1160                      sortedChildren.first()->writtenName() == sameNameList.first()->writtenName() );
1161 
1162             if( sameNameList.count() > 1 ) {
1163                 // now we need to rename the items
1164                 unsigned int maxlen = 255;
1165                 if( isoOptions().createJoliet() ) {
1166                     if( isoOptions().jolietLong() )
1167                         maxlen = 103;
1168                     else
1169                         maxlen = 64;
1170                 }
1171 
1172                 int cnt = 1;
1173                 Q_FOREACH( K3b::DataItem* item, sameNameList ) {
1174                     item->setWrittenName( K3b::appendNumberToFilename( item->writtenName(), cnt++, maxlen ) );
1175                 }
1176             }
1177         }
1178     }
1179 }
1180 
1181 
1182 bool K3b::DataDoc::needToCutFilenames() const
1183 {
1184     return d->needToCutFilenames;
1185 }
1186 
1187 
1188 QList<K3b::DataItem*> K3b::DataDoc::needToCutFilenameItems() const
1189 {
1190     return d->needToCutFilenameItems;
1191 }
1192 
1193 
1194 void K3b::DataDoc::informAboutNotFoundFiles()
1195 {
1196     if( !d->notFoundFiles.isEmpty() ) {
1197         KMessageBox::informationList( qApp->activeWindow(), i18n("Could not find the following files:"),
1198                                       d->notFoundFiles, i18n("Not Found") );
1199         d->notFoundFiles.clear();
1200     }
1201 
1202     if( !d->noPermissionFiles.isEmpty() ) {
1203         KMessageBox::informationList( qApp->activeWindow(), i18n("No permission to read the following files:"),
1204                                       d->noPermissionFiles, i18n("No Read Permission") );
1205 
1206         d->noPermissionFiles.clear();
1207     }
1208 }
1209 
1210 
1211 K3b::DataDoc::MultiSessionMode K3b::DataDoc::multiSessionMode() const
1212 {
1213     return d->multisessionMode;
1214 }
1215 
1216 
1217 void K3b::DataDoc::setMultiSessionMode( K3b::DataDoc::MultiSessionMode mode )
1218 {
1219     if( d->multisessionMode == NONE || d->multisessionMode == START )
1220         clearImportedSession();
1221 
1222     d->multisessionMode = mode;
1223 }
1224 
1225 
1226 int K3b::DataDoc::dataMode() const
1227 {
1228     return d->dataMode;
1229 }
1230 
1231 
1232 void K3b::DataDoc::setDataMode( int m )
1233 {
1234     d->dataMode = m;
1235 }
1236 
1237 
1238 void K3b::DataDoc::setVerifyData( bool b )
1239 {
1240     d->verifyData = b;
1241 }
1242 
1243 
1244 bool K3b::DataDoc::verifyData() const
1245 {
1246     return d->verifyData;
1247 }
1248 
1249 
1250 bool K3b::DataDoc::importSession( K3b::Device::Device* device, int session )
1251 {
1252     K3b::Device::DiskInfo diskInfo = device->diskInfo();
1253     // DVD+RW media is reported as non-appendable
1254     if( !diskInfo.appendable() &&
1255         !(diskInfo.mediaType() & (K3b::Device::MEDIA_DVD_PLUS_RW|K3b::Device::MEDIA_DVD_RW_OVWR)) )
1256         return false;
1257 
1258     K3b::Device::Toc toc = device->readToc();
1259     if( toc.isEmpty() ||
1260         toc.last().type() != K3b::Device::Track::TYPE_DATA )
1261         return false;
1262 
1263     long startSec = toc.last().firstSector().lba();
1264     if ( session > 0 ) {
1265         for ( K3b::Device::Toc::const_iterator it = toc.constBegin(); it != toc.constEnd(); ++it ) {
1266             if ( ( *it ).session() == session ) {
1267                 startSec = ( *it ).firstSector().lba();
1268                 break;
1269             }
1270         }
1271     }
1272     K3b::Iso9660 iso( device, startSec );
1273 
1274     if( iso.open() ) {
1275         // remove previously imported sessions
1276         clearImportedSession();
1277 
1278         // set multisession option
1279         if( d->multisessionMode != FINISH && d->multisessionMode != AUTO )
1280             d->multisessionMode = CONTINUE;
1281 
1282         // since in iso9660 it is possible that two files share it's data
1283         // simply summing the file sizes could result in wrong values
1284         // that's why we use the size from the toc. This is more accurate
1285         // anyway since there might be files overwritten or removed
1286         d->oldSessionSize = toc.last().lastSector().mode1Bytes();
1287         d->importedSession = session;
1288 
1289         qDebug() << "(K3b::DataDoc) imported session size: " << KIO::convertSize(d->oldSessionSize);
1290 
1291         // the track size for DVD+RW media and DVD-RW Overwrite media has nothing to do with the filesystem
1292         // size. in that case we need to use the filesystem's size (which is ok since it's one track anyway,
1293         // no real multisession)
1294         if( diskInfo.mediaType() & (K3b::Device::MEDIA_DVD_PLUS_RW|K3b::Device::MEDIA_DVD_RW_OVWR) ) {
1295             d->oldSessionSize = iso.primaryDescriptor().volumeSpaceSize
1296                                * iso.primaryDescriptor().logicalBlockSize;
1297         }
1298 
1299         // import some former settings
1300         d->isoOptions.setCreateRockRidge( iso.firstRRDirEntry() != 0 );
1301         d->isoOptions.setCreateJoliet( iso.firstJolietDirEntry() != 0 );
1302         d->isoOptions.setVolumeID( iso.primaryDescriptor().volumeId );
1303         // TODO: also import some other pd fields
1304 
1305         const K3b::Iso9660Directory* rootDir = iso.firstRRDirEntry();
1306         // J�rg Schilling says that it is impossible to import the joliet tree for multisession
1307 //     if( !rootDir )
1308 //       rootDir = iso.firstJolietDirEntry();
1309         if( !rootDir )
1310             rootDir = iso.firstIsoDirEntry();
1311 
1312         if( rootDir ) {
1313             createSessionImportItems( rootDir, root() );
1314             emit changed();
1315             emit importedSessionChanged( importedSession() );
1316             return true;
1317         }
1318         else {
1319             qDebug() << "(K3b::DataDoc::importSession) Could not find primary volume desc.";
1320             return false;
1321         }
1322     }
1323     else {
1324         qDebug() << "(K3b::DataDoc) unable to read toc.";
1325         return false;
1326     }
1327 }
1328 
1329 
1330 void K3b::DataDoc::createSessionImportItems( const K3b::Iso9660Directory* importDir, K3b::DirItem* parent )
1331 {
1332     if( !parent )
1333         return;
1334 
1335     Q_ASSERT(importDir);
1336     QStringList entries = importDir->entries();
1337     entries.removeAll( "." );
1338     entries.removeAll( ".." );
1339     for( QStringList::const_iterator it = entries.constBegin();
1340          it != entries.constEnd(); ++it ) {
1341         if( const K3b::Iso9660Entry* entry = importDir->entry( *it ) ) {
1342             K3b::DataItem* oldItem = parent->find( entry->name() );
1343             if( entry->isDirectory() ) {
1344                 K3b::DirItem* dir = 0;
1345                 if( oldItem && oldItem->isDir() ) {
1346                     dir = (K3b::DirItem*)oldItem;
1347                 }
1348                 else {
1349                     // we overwrite without warning!
1350                     if( oldItem )
1351                         removeItem( oldItem );
1352                     dir = new K3b::DirItem( entry->name() );
1353                     parent->addDataItem( dir );
1354                 }
1355 
1356                 dir->setRemoveable(false);
1357                 dir->setRenameable(false);
1358                 dir->setMoveable(false);
1359                 dir->setHideable(false);
1360                 dir->setWriteToCd(false);
1361                 dir->setExtraInfo( i18n("From previous session") );
1362                 d->oldSession.append( dir );
1363 
1364                 createSessionImportItems( static_cast<const K3b::Iso9660Directory*>(entry), dir );
1365             }
1366             else {
1367                 const K3b::Iso9660File* file = static_cast<const K3b::Iso9660File*>(entry);
1368 
1369                 // we overwrite without warning!
1370                 if( oldItem )
1371                     removeItem( oldItem );
1372 
1373                 K3b::SessionImportItem* item = new K3b::SessionImportItem( file );
1374                 item->setExtraInfo( i18n("From previous session") );
1375                 parent->addDataItem( item );
1376                 d->oldSession.append( item );
1377             }
1378         }
1379     }
1380 }
1381 
1382 
1383 void K3b::DataDoc::clearImportedSession()
1384 {
1385     //  d->oldSessionSizeHandler->clear();
1386     d->importedSession = -1;
1387     d->oldSessionSize = 0;
1388 
1389     while( !d->oldSession.isEmpty() ) {
1390         K3b::DataItem* item = d->oldSession.takeFirst();
1391 
1392         if( item->isDir() ) {
1393             K3b::DirItem* dir = (K3b::DirItem*)item;
1394             if( dir->numDirs() + dir->numFiles() == 0 ) {
1395                 // this imported dir is not needed anymore
1396                 // since it is empty
1397                 delete item;
1398             }
1399             else {
1400                 Q_FOREACH( K3b::DataItem* item, dir->children() ) {
1401                     if( !d->oldSession.contains( item ) ) {
1402                         // now the dir becomes a totally normal dir
1403                         dir->setRemoveable(true);
1404                         dir->setRenameable(true);
1405                         dir->setMoveable(true);
1406                         dir->setHideable(true);
1407                         dir->setWriteToCd(true);
1408                         dir->setExtraInfo( "" );
1409                         break;
1410                     }
1411                 }
1412             }
1413         }
1414         else {
1415             delete item;
1416         }
1417     }
1418 
1419     d->multisessionMode = AUTO;
1420 
1421     emit changed();
1422     emit importedSessionChanged( importedSession() );
1423 }
1424 
1425 QList<K3b::BootItem*> K3b::DataDoc::bootImages()
1426 {
1427     return d->bootImages;
1428 }
1429 
1430 
1431 K3b::DataItem* K3b::DataDoc::bootCataloge()
1432 {
1433     return d->bootCataloge;
1434 }
1435 
1436 
1437 K3b::DirItem* K3b::DataDoc::bootImageDir()
1438 {
1439     K3b::DataItem* b = d->root->find( "boot" );
1440     if( !b ) {
1441         b = new K3b::DirItem( "boot" );
1442         d->root->addDataItem( b );
1443         setModified( true );
1444     }
1445 
1446     // if we cannot create the dir because there is a file named boot just use the root dir
1447     if( !b->isDir() )
1448         return d->root;
1449     else
1450         return static_cast<K3b::DirItem*>(b);
1451 }
1452 
1453 
1454 K3b::BootItem* K3b::DataDoc::createBootItem( const QString& filename, K3b::DirItem* dir )
1455 {
1456     if( !dir )
1457         dir = bootImageDir();
1458 
1459     K3b::BootItem* boot = new K3b::BootItem( filename, *this );
1460     dir->addDataItem( boot );
1461 
1462     if( !d->bootCataloge )
1463         createBootCatalogeItem(dir);
1464 
1465     return boot;
1466 }
1467 
1468 
1469 K3b::DataItem* K3b::DataDoc::createBootCatalogeItem( K3b::DirItem* dir )
1470 {
1471     if( !d->bootCataloge ) {
1472         QString newName = "boot.catalog";
1473         int i = 0;
1474         while( dir->alreadyInDirectory( "boot.catalog" ) ) {
1475             ++i;
1476             newName = QString( "boot%1.catalog" ).arg(i);
1477         }
1478 
1479         K3b::SpecialDataItem* b = new K3b::SpecialDataItem( 0, newName );
1480         dir->addDataItem( b );
1481         d->bootCataloge = b;
1482         d->bootCataloge->setRemoveable(false);
1483         d->bootCataloge->setHideable(false);
1484         d->bootCataloge->setWriteToCd(false);
1485         d->bootCataloge->setExtraInfo( i18n("El Torito boot catalog file") );
1486         b->setSpecialType( i18n("Boot catalog") );
1487     }
1488     else
1489         d->bootCataloge->reparent( dir );
1490 
1491     return d->bootCataloge;
1492 }
1493 
1494 
1495 QList<K3b::DataItem*> K3b::DataDoc::findItemByLocalPath( const QString& path ) const
1496 {
1497     Q_UNUSED( path );
1498     return QList<K3b::DataItem*>();
1499 }
1500 
1501 
1502 int K3b::DataDoc::importedSession() const
1503 {
1504     return ( d->oldSession.isEmpty() ? -1 : d->importedSession );
1505 }
1506 
1507 
1508 K3b::Device::MediaTypes K3b::DataDoc::supportedMediaTypes() const
1509 {
1510     return Device::MEDIA_WRITABLE;
1511 }
1512 
1513 
1514 K3b::RootItem* K3b::DataDoc::root() const
1515 {
1516     return d->root;
1517 }
1518 
1519 #include "moc_k3bdatadoc.cpp"