/* * * $Id: k3bdatadoc.cpp 619556 2007-01-03 17:38:12Z trueg $ * Copyright (C) 2003 Sebastian Trueg * * This file is part of the K3b project. * Copyright (C) 1998-2007 Sebastian Trueg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * See the file "COPYING" for the exact licensing terms. */ #include "k3bdatadoc.h" #include "k3bfileitem.h" #include "k3bdiritem.h" #include "k3bsessionimportitem.h" #include "k3bdatajob.h" #include "k3bbootitem.h" #include "k3bspecialdataitem.h" #include "k3bfilecompilationsizehandler.h" #include "k3bmkisofshandler.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * There are two ways to fill a data project with files and folders: * \li Use the addUrl and addUrls methods * \li or create your own K3bDirItems and K3bFileItems. The doc will be properly updated * by the constructors of the items. */ K3bDataDoc::K3bDataDoc( TQObject* parent ) : K3bDoc( parent ) { m_root = 0; m_sizeHandler = new K3bFileCompilationSizeHandler(); } K3bDataDoc::~K3bDataDoc() { delete m_root; delete m_sizeHandler; // delete m_oldSessionSizeHandler; } bool K3bDataDoc::newDocument() { clearImportedSession(); m_bootCataloge = 0; m_oldSessionSize = 0; m_bExistingItemsReplaceAll = m_bExistingItemsIgnoreAll = false; if( m_root ) { while( m_root->children().getFirst() ) removeItem( m_root->children().getFirst() ); } else m_root = new K3bRootItem( this ); m_sizeHandler->clear(); m_multisessionMode = AUTO; m_dataMode = K3b::DATA_MODE_AUTO; m_isoOptions = K3bIsoOptions(); return K3bDoc::newDocument(); } TQString K3bDataDoc::name() const { return m_isoOptions.volumeID(); } void K3bDataDoc::setIsoOptions( const K3bIsoOptions& o ) { m_isoOptions = o; emit changed(); } void K3bDataDoc::setVolumeID( const TQString& v ) { m_isoOptions.setVolumeID( v ); emit changed(); } void K3bDataDoc::addUrls( const KURL::List& urls ) { addUrls( urls, root() ); } void K3bDataDoc::addUrls( const KURL::List& l, K3bDirItem* dir ) { if( !dir ) dir = root(); KURL::List urls = K3b::convertToLocalUrls(l); for( KURL::List::ConstIterator it = urls.begin(); it != urls.end(); ++it ) { const KURL& url = *it; TQFileInfo f( url.path() ); TQString k3bname = f.absFilePath().section( "/", -1 ); // filenames cannot end in backslashes (mkisofs problem. See comments in k3bisoimager.cpp (escapeGraftPoint())) while( k3bname[k3bname.length()-1] == '\\' ) k3bname.truncate( k3bname.length()-1 ); // backup dummy name if( k3bname.isEmpty() ) k3bname = "1"; K3bDirItem* newDirItem = 0; // rename the new item if an item with that name already exists int cnt = 0; bool ok = false; while( !ok ) { ok = true; TQString name( k3bname ); if( cnt > 0 ) name += TQString("_%1").arg(cnt); if( K3bDataItem* oldItem = dir->find( name ) ) { if( f.isDir() && oldItem->isDir() ) { // ok, just reuse the dir newDirItem = static_cast(oldItem); } // directories cannot replace files in an old session (I think) // and also directories can for sure never be replaced (only be reused as above) // so we always rename if the old item is a dir. else if( !oldItem->isFromOldSession() || f.isDir() || oldItem->isDir() ) { ++cnt; ok = false; } } } if( cnt > 0 ) k3bname += TQString("_%1").arg(cnt); // TQFileInfo::exists and TQFileInfo::isReadable return false for broken symlinks :( if( f.isDir() && !f.isSymLink() ) { if( !newDirItem ) { newDirItem = new K3bDirItem( k3bname, this, dir ); newDirItem->setLocalPath( url.path() ); // HACK: see k3bdiritem.h } // recursively add all the files in the directory TQStringList dlist = TQDir( f.absFilePath() ).entryList( TQDir::All|TQDir::System|TQDir::Hidden ); dlist.remove("."); dlist.remove(".."); KURL::List newUrls; for( TQStringList::Iterator it = dlist.begin(); it != dlist.end(); ++it ) newUrls.append( KURL::fromPathOrURL( f.absFilePath() + "/" + *it ) ); addUrls( newUrls, newDirItem ); } else if( f.isSymLink() || f.isFile() ) (void)new K3bFileItem( url.path(), this, dir, k3bname ); } emit changed(); setModified( true ); } bool K3bDataDoc::nameAlreadyInDir( const TQString& name, K3bDirItem* dir ) { if( !dir ) return false; else return ( dir->find( name ) != 0 ); } K3bDirItem* K3bDataDoc::addEmptyDir( const TQString& name, K3bDirItem* parent ) { K3bDirItem* item = new K3bDirItem( name, this, parent ); setModified( true ); return item; } KIO::filesize_t K3bDataDoc::size() const { if( m_isoOptions.doNotCacheInodes() ) return root()->blocks().mode1Bytes() + m_oldSessionSize; else return m_sizeHandler->blocks( m_isoOptions.followSymbolicLinks() || !m_isoOptions.createRockRidge() ).mode1Bytes() + m_oldSessionSize; } KIO::filesize_t K3bDataDoc::burningSize() const { return size() - m_oldSessionSize; //m_oldSessionSizeHandler->size(); } K3b::Msf K3bDataDoc::length() const { // 1 block consists of 2048 bytes real data // and 1 block equals to 1 audio frame // so this is the way to calculate: return K3b::Msf( size() / 2048 ); } K3b::Msf K3bDataDoc::burningLength() const { return K3b::Msf( burningSize() / 2048 ); } TQString K3bDataDoc::typeString() const { return TQString::fromLatin1("data"); } bool K3bDataDoc::loadDocumentData( TQDomElement* rootElem ) { if( !root() ) newDocument(); TQDomNodeList nodes = rootElem->childNodes(); if( nodes.item(0).nodeName() != "general" ) { kdDebug() << "(K3bDataDoc) could not find 'general' section." << endl; return false; } if( !readGeneralDocumentData( nodes.item(0).toElement() ) ) return false; // parse options // ----------------------------------------------------------------- if( nodes.item(1).nodeName() != "options" ) { kdDebug() << "(K3bDataDoc) could not find 'options' section." << endl; return false; } if( !loadDocumentDataOptions( nodes.item(1).toElement() ) ) return false; // ----------------------------------------------------------------- // parse header // ----------------------------------------------------------------- if( nodes.item(2).nodeName() != "header" ) { kdDebug() << "(K3bDataDoc) could not find 'header' section." << endl; return false; } if( !loadDocumentDataHeader( nodes.item(2).toElement() ) ) return false; // ----------------------------------------------------------------- // parse files // ----------------------------------------------------------------- if( nodes.item(3).nodeName() != "files" ) { kdDebug() << "(K3bDataDoc) could not find 'files' section." << endl; return false; } if( m_root == 0 ) m_root = new K3bRootItem( this ); TQDomNodeList filesList = nodes.item(3).childNodes(); for( uint i = 0; i < filesList.count(); i++ ) { TQDomElement e = filesList.item(i).toElement(); if( !loadDataItem( e, root() ) ) return false; } // ----------------------------------------------------------------- // // Old versions of K3b do not properly save the boot catalog location // and name. So to ensure we have one around even if loading an old project // file we create a default one here. // if( !m_bootImages.isEmpty() && !m_bootCataloge ) createBootCatalogeItem( m_bootImages.first()->parent() ); informAboutNotFoundFiles(); return true; } bool K3bDataDoc::loadDocumentDataOptions( TQDomElement elem ) { TQDomNodeList headerList = elem.childNodes(); for( uint i = 0; i < headerList.count(); i++ ) { TQDomElement e = headerList.item(i).toElement(); if( e.isNull() ) return false; if( e.nodeName() == "rock_ridge") m_isoOptions.setCreateRockRidge( e.attributeNode( "activated" ).value() == "yes" ); else if( e.nodeName() == "joliet") m_isoOptions.setCreateJoliet( e.attributeNode( "activated" ).value() == "yes" ); else if( e.nodeName() == "udf") m_isoOptions.setCreateUdf( e.attributeNode( "activated" ).value() == "yes" ); else if( e.nodeName() == "joliet_allow_103_characters") m_isoOptions.setJolietLong( e.attributeNode( "activated" ).value() == "yes" ); else if( e.nodeName() == "iso_allow_lowercase") m_isoOptions.setISOallowLowercase( e.attributeNode( "activated" ).value() == "yes" ); else if( e.nodeName() == "iso_allow_period_at_begin") m_isoOptions.setISOallowPeriodAtBegin( e.attributeNode( "activated" ).value() == "yes" ); else if( e.nodeName() == "iso_allow_31_char") m_isoOptions.setISOallow31charFilenames( e.attributeNode( "activated" ).value() == "yes" ); else if( e.nodeName() == "iso_omit_version_numbers") m_isoOptions.setISOomitVersionNumbers( e.attributeNode( "activated" ).value() == "yes" ); else if( e.nodeName() == "iso_omit_trailing_period") m_isoOptions.setISOomitTrailingPeriod( e.attributeNode( "activated" ).value() == "yes" ); else if( e.nodeName() == "iso_max_filename_length") m_isoOptions.setISOmaxFilenameLength( e.attributeNode( "activated" ).value() == "yes" ); else if( e.nodeName() == "iso_relaxed_filenames") m_isoOptions.setISOrelaxedFilenames( e.attributeNode( "activated" ).value() == "yes" ); else if( e.nodeName() == "iso_no_iso_translate") m_isoOptions.setISOnoIsoTranslate( e.attributeNode( "activated" ).value() == "yes" ); else if( e.nodeName() == "iso_allow_multidot") m_isoOptions.setISOallowMultiDot( e.attributeNode( "activated" ).value() == "yes" ); else if( e.nodeName() == "iso_untranslated_filenames") m_isoOptions.setISOuntranslatedFilenames( e.attributeNode( "activated" ).value() == "yes" ); else if( e.nodeName() == "follow_symbolic_links") m_isoOptions.setFollowSymbolicLinks( e.attributeNode( "activated" ).value() == "yes" ); else if( e.nodeName() == "create_trans_tbl") m_isoOptions.setCreateTRANS_TBL( e.attributeNode( "activated" ).value() == "yes" ); else if( e.nodeName() == "hide_trans_tbl") m_isoOptions.setHideTRANS_TBL( e.attributeNode( "activated" ).value() == "yes" ); else if( e.nodeName() == "iso_level") m_isoOptions.setISOLevel( e.text().toInt() ); else if( e.nodeName() == "discard_symlinks") m_isoOptions.setDiscardSymlinks( e.attributeNode( "activated" ).value() == "yes" ); else if( e.nodeName() == "discard_broken_symlinks") m_isoOptions.setDiscardBrokenSymlinks( e.attributeNode( "activated" ).value() == "yes" ); else if( e.nodeName() == "preserve_file_permissions") m_isoOptions.setPreserveFilePermissions( e.attributeNode( "activated" ).value() == "yes" ); else if( e.nodeName() == "force_input_charset") m_isoOptions.setForceInputCharset( e.attributeNode( "activated" ).value() == "yes" ); else if( e.nodeName() == "input_charset") m_isoOptions.setInputCharset( e.text() ); else if( e.nodeName() == "do_not_cache_inodes" ) m_isoOptions.setDoNotCacheInodes( e.attributeNode( "activated" ).value() == "yes" ); else if( e.nodeName() == "whitespace_treatment" ) { if( e.text() == "strip" ) m_isoOptions.setWhiteSpaceTreatment( K3bIsoOptions::strip ); else if( e.text() == "extended" ) m_isoOptions.setWhiteSpaceTreatment( K3bIsoOptions::extended ); else if( e.text() == "extended" ) m_isoOptions.setWhiteSpaceTreatment( K3bIsoOptions::replace ); else m_isoOptions.setWhiteSpaceTreatment( K3bIsoOptions::noChange ); } else if( e.nodeName() == "whitespace_replace_string") m_isoOptions.setWhiteSpaceTreatmentReplaceString( e.text() ); else if( e.nodeName() == "data_track_mode" ) { if( e.text() == "mode1" ) m_dataMode = K3b::MODE1; else if( e.text() == "mode2" ) m_dataMode = K3b::MODE2; else m_dataMode = K3b::DATA_MODE_AUTO; } else if( e.nodeName() == "multisession" ) { TQString mode = e.text(); if( mode == "start" ) setMultiSessionMode( START ); else if( mode == "continue" ) setMultiSessionMode( CONTINUE ); else if( mode == "finish" ) setMultiSessionMode( FINISH ); else if( mode == "none" ) setMultiSessionMode( NONE ); else setMultiSessionMode( AUTO ); } else if( e.nodeName() == "verify_data" ) setVerifyData( e.attributeNode( "activated" ).value() == "yes" ); else kdDebug() << "(K3bDataDoc) unknown option entry: " << e.nodeName() << endl; } return true; } bool K3bDataDoc::loadDocumentDataHeader( TQDomElement headerElem ) { TQDomNodeList headerList = headerElem.childNodes(); for( uint i = 0; i < headerList.count(); i++ ) { TQDomElement e = headerList.item(i).toElement(); if( e.isNull() ) return false; if( e.nodeName() == "volume_id" ) m_isoOptions.setVolumeID( e.text() ); else if( e.nodeName() == "application_id" ) m_isoOptions.setApplicationID( e.text() ); else if( e.nodeName() == "publisher" ) m_isoOptions.setPublisher( e.text() ); else if( e.nodeName() == "preparer" ) m_isoOptions.setPreparer( e.text() ); else if( e.nodeName() == "volume_set_id" ) m_isoOptions.setVolumeSetId( e.text() ); else if( e.nodeName() == "volume_set_size" ) m_isoOptions.setVolumeSetSize( e.text().toInt() ); else if( e.nodeName() == "volume_set_number" ) m_isoOptions.setVolumeSetNumber( e.text().toInt() ); else if( e.nodeName() == "system_id" ) m_isoOptions.setSystemId( e.text() ); else kdDebug() << "(K3bDataDoc) unknown header entry: " << e.nodeName() << endl; } return true; } bool K3bDataDoc::loadDataItem( TQDomElement& elem, K3bDirItem* parent ) { K3bDataItem* newItem = 0; if( elem.nodeName() == "file" ) { TQDomElement urlElem = elem.firstChild().toElement(); if( urlElem.isNull() ) { kdDebug() << "(K3bDataDoc) file-element without url!" << endl; return false; } TQFileInfo f( urlElem.text() ); // We canot use exists() here since this always disqualifies broken symlinks if( !f.isFile() && !f.isSymLink() ) m_notFoundFiles.append( urlElem.text() ); // broken symlinks are not readable according to TQFileInfo which is wrong in our case else if( f.isFile() && !f.isReadable() ) m_noPermissionFiles.append( urlElem.text() ); else if( !elem.attribute( "bootimage" ).isEmpty() ) { K3bBootItem* bootItem = new K3bBootItem( urlElem.text(), this, parent, elem.attributeNode( "name" ).value() ); if( elem.attribute( "bootimage" ) == "floppy" ) bootItem->setImageType( K3bBootItem::FLOPPY ); else if( elem.attribute( "bootimage" ) == "harddisk" ) bootItem->setImageType( K3bBootItem::HARDDISK ); else bootItem->setImageType( K3bBootItem::NONE ); bootItem->setNoBoot( elem.attribute( "no_boot" ) == "yes" ); bootItem->setBootInfoTable( elem.attribute( "boot_info_table" ) == "yes" ); bootItem->setLoadSegment( elem.attribute( "load_segment" ).toInt() ); bootItem->setLoadSize( elem.attribute( "load_size" ).toInt() ); newItem = bootItem; } else { newItem = new K3bFileItem( urlElem.text(), this, parent, elem.attributeNode( "name" ).value() ); } } else if( elem.nodeName() == "special" ) { if( elem.attributeNode( "type" ).value() == "boot cataloge" ) createBootCatalogeItem( parent )->setK3bName( elem.attributeNode( "name" ).value() ); } else if( elem.nodeName() == "directory" ) { // This is for the VideoDVD project which already contains the *_TS folders K3bDirItem* newDirItem = 0; if( K3bDataItem* item = parent->find( elem.attributeNode( "name" ).value() ) ) { if( item->isDir() ) { newDirItem = static_cast(item); } else { kdError() << "(K3bDataDoc) INVALID DOCUMENT: item " << item->k3bPath() << " saved twice" << endl; return false; } } if( !newDirItem ) newDirItem = new K3bDirItem( elem.attributeNode( "name" ).value(), this, parent ); TQDomNodeList childNodes = elem.childNodes(); for( uint i = 0; i < childNodes.count(); i++ ) { TQDomElement e = childNodes.item(i).toElement(); if( !loadDataItem( e, newDirItem ) ) return false; } newItem = newDirItem; } else { kdDebug() << "(K3bDataDoc) wrong tag in files-section: " << elem.nodeName() << endl; return false; } // load the sort weight if( newItem ) newItem->setSortWeight( elem.attribute( "sort_weight", "0" ).toInt() ); return true; } bool K3bDataDoc::saveDocumentData( TQDomElement* docElem ) { TQDomDocument doc = docElem->ownerDocument(); saveGeneralDocumentData( docElem ); // all options // ---------------------------------------------------------------------- TQDomElement optionsElem = doc.createElement( "options" ); saveDocumentDataOptions( optionsElem ); docElem->appendChild( optionsElem ); // ---------------------------------------------------------------------- // the header stuff // ---------------------------------------------------------------------- TQDomElement headerElem = doc.createElement( "header" ); saveDocumentDataHeader( headerElem ); docElem->appendChild( headerElem ); // now do the "real" work: save the entries // ---------------------------------------------------------------------- TQDomElement topElem = doc.createElement( "files" ); TQPtrListIterator it( root()->children() ); for( ; it.current(); ++it ) { saveDataItem( it.current(), &doc, &topElem ); } docElem->appendChild( topElem ); // ---------------------------------------------------------------------- return true; } void K3bDataDoc::saveDocumentDataOptions( TQDomElement& optionsElem ) { TQDomDocument doc = optionsElem.ownerDocument(); TQDomElement topElem = doc.createElement( "rock_ridge" ); topElem.setAttribute( "activated", isoOptions().createRockRidge() ? "yes" : "no" ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "joliet" ); topElem.setAttribute( "activated", isoOptions().createJoliet() ? "yes" : "no" ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "udf" ); topElem.setAttribute( "activated", isoOptions().createUdf() ? "yes" : "no" ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "joliet_allow_103_characters" ); topElem.setAttribute( "activated", isoOptions().jolietLong() ? "yes" : "no" ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "iso_allow_lowercase" ); topElem.setAttribute( "activated", isoOptions().ISOallowLowercase() ? "yes" : "no" ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "iso_allow_period_at_begin" ); topElem.setAttribute( "activated", isoOptions().ISOallowPeriodAtBegin() ? "yes" : "no" ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "iso_allow_31_char" ); topElem.setAttribute( "activated", isoOptions().ISOallow31charFilenames() ? "yes" : "no" ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "iso_omit_version_numbers" ); topElem.setAttribute( "activated", isoOptions().ISOomitVersionNumbers() ? "yes" : "no" ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "iso_omit_trailing_period" ); topElem.setAttribute( "activated", isoOptions().ISOomitTrailingPeriod() ? "yes" : "no" ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "iso_max_filename_length" ); topElem.setAttribute( "activated", isoOptions().ISOmaxFilenameLength() ? "yes" : "no" ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "iso_relaxed_filenames" ); topElem.setAttribute( "activated", isoOptions().ISOrelaxedFilenames() ? "yes" : "no" ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "iso_no_iso_translate" ); topElem.setAttribute( "activated", isoOptions().ISOnoIsoTranslate() ? "yes" : "no" ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "iso_allow_multidot" ); topElem.setAttribute( "activated", isoOptions().ISOallowMultiDot() ? "yes" : "no" ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "iso_untranslated_filenames" ); topElem.setAttribute( "activated", isoOptions().ISOuntranslatedFilenames() ? "yes" : "no" ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "follow_symbolic_links" ); topElem.setAttribute( "activated", isoOptions().followSymbolicLinks() ? "yes" : "no" ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "create_trans_tbl" ); topElem.setAttribute( "activated", isoOptions().createTRANS_TBL() ? "yes" : "no" ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "hide_trans_tbl" ); topElem.setAttribute( "activated", isoOptions().hideTRANS_TBL() ? "yes" : "no" ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "iso_level" ); topElem.appendChild( doc.createTextNode( TQString::number(isoOptions().ISOLevel()) ) ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "discard_symlinks" ); topElem.setAttribute( "activated", isoOptions().discardSymlinks() ? "yes" : "no" ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "discard_broken_symlinks" ); topElem.setAttribute( "activated", isoOptions().discardBrokenSymlinks() ? "yes" : "no" ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "preserve_file_permissions" ); topElem.setAttribute( "activated", isoOptions().preserveFilePermissions() ? "yes" : "no" ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "force_input_charset" ); topElem.setAttribute( "activated", isoOptions().forceInputCharset() ? "yes" : "no" ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "do_not_cache_inodes" ); topElem.setAttribute( "activated", isoOptions().doNotCacheInodes() ? "yes" : "no" ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "input_charset" ); topElem.appendChild( doc.createTextNode( isoOptions().inputCharset() ) ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "whitespace_treatment" ); switch( isoOptions().whiteSpaceTreatment() ) { case K3bIsoOptions::strip: topElem.appendChild( doc.createTextNode( "strip" ) ); break; case K3bIsoOptions::extended: topElem.appendChild( doc.createTextNode( "extended" ) ); break; case K3bIsoOptions::replace: topElem.appendChild( doc.createTextNode( "replace" ) ); break; default: topElem.appendChild( doc.createTextNode( "noChange" ) ); break; } optionsElem.appendChild( topElem ); topElem = doc.createElement( "whitespace_replace_string" ); topElem.appendChild( doc.createTextNode( isoOptions().whiteSpaceTreatmentReplaceString() ) ); optionsElem.appendChild( topElem ); topElem = doc.createElement( "data_track_mode" ); if( m_dataMode == K3b::MODE1 ) topElem.appendChild( doc.createTextNode( "mode1" ) ); else if( m_dataMode == K3b::MODE2 ) topElem.appendChild( doc.createTextNode( "mode2" ) ); else topElem.appendChild( doc.createTextNode( "auto" ) ); optionsElem.appendChild( topElem ); // save multisession topElem = doc.createElement( "multisession" ); switch( m_multisessionMode ) { case START: topElem.appendChild( doc.createTextNode( "start" ) ); break; case CONTINUE: topElem.appendChild( doc.createTextNode( "continue" ) ); break; case FINISH: topElem.appendChild( doc.createTextNode( "finish" ) ); break; case NONE: topElem.appendChild( doc.createTextNode( "none" ) ); break; default: topElem.appendChild( doc.createTextNode( "auto" ) ); break; } optionsElem.appendChild( topElem ); topElem = doc.createElement( "verify_data" ); topElem.setAttribute( "activated", verifyData() ? "yes" : "no" ); optionsElem.appendChild( topElem ); // ---------------------------------------------------------------------- } void K3bDataDoc::saveDocumentDataHeader( TQDomElement& headerElem ) { TQDomDocument doc = headerElem.ownerDocument(); TQDomElement topElem = doc.createElement( "volume_id" ); topElem.appendChild( doc.createTextNode( isoOptions().volumeID() ) ); headerElem.appendChild( topElem ); topElem = doc.createElement( "volume_set_id" ); topElem.appendChild( doc.createTextNode( isoOptions().volumeSetId() ) ); headerElem.appendChild( topElem ); topElem = doc.createElement( "volume_set_size" ); topElem.appendChild( doc.createTextNode( TQString::number(isoOptions().volumeSetSize()) ) ); headerElem.appendChild( topElem ); topElem = doc.createElement( "volume_set_number" ); topElem.appendChild( doc.createTextNode( TQString::number(isoOptions().volumeSetNumber()) ) ); headerElem.appendChild( topElem ); topElem = doc.createElement( "system_id" ); topElem.appendChild( doc.createTextNode( isoOptions().systemId() ) ); headerElem.appendChild( topElem ); topElem = doc.createElement( "application_id" ); topElem.appendChild( doc.createTextNode( isoOptions().applicationID() ) ); headerElem.appendChild( topElem ); topElem = doc.createElement( "publisher" ); topElem.appendChild( doc.createTextNode( isoOptions().publisher() ) ); headerElem.appendChild( topElem ); topElem = doc.createElement( "preparer" ); topElem.appendChild( doc.createTextNode( isoOptions().preparer() ) ); headerElem.appendChild( topElem ); // ---------------------------------------------------------------------- } void K3bDataDoc::saveDataItem( K3bDataItem* item, TQDomDocument* doc, TQDomElement* parent ) { if( K3bFileItem* fileItem = dynamic_cast( item ) ) { if( m_oldSession.contains( fileItem ) ) { kdDebug() << "(K3bDataDoc) ignoring fileitem " << fileItem->k3bName() << " from old session while saving..." << endl; } else { TQDomElement topElem = doc->createElement( "file" ); topElem.setAttribute( "name", fileItem->k3bName() ); TQDomElement subElem = doc->createElement( "url" ); subElem.appendChild( doc->createTextNode( fileItem->localPath() ) ); topElem.appendChild( subElem ); if( item->sortWeight() != 0 ) topElem.setAttribute( "sort_weight", TQString::number(item->sortWeight()) ); parent->appendChild( topElem ); // add boot options as attributes to preserve compatibility to older K3b versions if( K3bBootItem* bootItem = dynamic_cast( fileItem ) ) { if( bootItem->imageType() == K3bBootItem::FLOPPY ) topElem.setAttribute( "bootimage", "floppy" ); else if( bootItem->imageType() == K3bBootItem::HARDDISK ) topElem.setAttribute( "bootimage", "harddisk" ); else topElem.setAttribute( "bootimage", "none" ); topElem.setAttribute( "no_boot", bootItem->noBoot() ? "yes" : "no" ); topElem.setAttribute( "boot_info_table", bootItem->bootInfoTable() ? "yes" : "no" ); topElem.setAttribute( "load_segment", TQString::number( bootItem->loadSegment() ) ); topElem.setAttribute( "load_size", TQString::number( bootItem->loadSize() ) ); } } } else if( item == m_bootCataloge ) { TQDomElement topElem = doc->createElement( "special" ); topElem.setAttribute( "name", m_bootCataloge->k3bName() ); topElem.setAttribute( "type", "boot cataloge" ); parent->appendChild( topElem ); } else if( K3bDirItem* dirItem = dynamic_cast( item ) ) { TQDomElement topElem = doc->createElement( "directory" ); topElem.setAttribute( "name", dirItem->k3bName() ); if( item->sortWeight() != 0 ) topElem.setAttribute( "sort_weight", TQString::number(item->sortWeight()) ); TQPtrListIterator it( dirItem->children() ); for( ; it.current(); ++it ) { saveDataItem( it.current(), doc, &topElem ); } parent->appendChild( topElem ); } } void K3bDataDoc::removeItem( K3bDataItem* item ) { if( !item ) return; if( item->isRemoveable() ) { delete item; } else kdDebug() << "(K3bDataDoc) tried to remove non-removable entry!" << endl; } void K3bDataDoc::itemRemovedFromDir( K3bDirItem*, K3bDataItem* removedItem ) { // update the project size if( !removedItem->isFromOldSession() ) m_sizeHandler->removeFile( removedItem ); // update the boot item list if( removedItem->isBootItem() ) { m_bootImages.removeRef( static_cast( removedItem ) ); if( m_bootImages.isEmpty() ) { delete m_bootCataloge; m_bootCataloge = 0; } } emit itemRemoved( removedItem ); emit changed(); } void K3bDataDoc::itemAddedToDir( K3bDirItem*, K3bDataItem* item ) { // update the project size if( !item->isFromOldSession() ) m_sizeHandler->addFile( item ); // update the boot item list if( item->isBootItem() ) m_bootImages.append( static_cast( item ) ); emit itemAdded( item ); emit changed(); } void K3bDataDoc::moveItem( K3bDataItem* item, K3bDirItem* newParent ) { if( !item || !newParent ) { kdDebug() << "(K3bDataDoc) item or parentitem was NULL while moving." << endl; return; } if( !item->isMoveable() ) { kdDebug() << "(K3bDataDoc) item is not movable! " << endl; return; } item->reparent( newParent ); } void K3bDataDoc::moveItems( TQPtrList itemList, K3bDirItem* newParent ) { if( !newParent ) { kdDebug() << "(K3bDataDoc) tried to move items to nowhere...!" << endl; return; } TQPtrListIterator it( itemList ); for( ; it.current(); ++it ) { // check if newParent is subdir of item if( K3bDirItem* dirItem = dynamic_cast( it.current() ) ) { if( dirItem->isSubItem( newParent ) ) { continue; } } if( it.current()->isMoveable() ) it.current()->reparent( newParent ); } } K3bBurnJob* K3bDataDoc::newBurnJob( K3bJobHandler* hdl, TQObject* parent ) { return new K3bDataJob( this, hdl, parent ); } TQString K3bDataDoc::treatWhitespace( const TQString& path ) { // TODO: // It could happen that two files with different names // will have the same name after the treatment // Perhaps we should add a number at the end or something // similar (s.a.) if( isoOptions().whiteSpaceTreatment() != K3bIsoOptions::noChange ) { TQString result = path; if( isoOptions().whiteSpaceTreatment() == K3bIsoOptions::replace ) { result.replace( ' ', isoOptions().whiteSpaceTreatmentReplaceString() ); } else if( isoOptions().whiteSpaceTreatment() == K3bIsoOptions::strip ) { result.remove( ' ' ); } else if( isoOptions().whiteSpaceTreatment() == K3bIsoOptions::extended ) { result.truncate(0); for( uint i = 0; i < path.length(); i++ ) { if( path[i] == ' ' ) { if( path[i+1] != ' ' ) result.append( path[++i].upper() ); } else result.append( path[i] ); } } kdDebug() << "(K3bDataDoc) converted " << path << " to " << result << endl; return result; } else return path; } void K3bDataDoc::prepareFilenames() { m_needToCutFilenames = false; m_needToCutFilenameItems.clear(); // // if joliet is used cut the names and rename if necessary // 64 characters for standard joliet and 103 characters for long joliet names // // Rockridge supports the full 255 UNIX chars and in case Rockridge is disabled we leave // it to mkisofs for now since handling all the options to alter the ISO9660 standard it just // too much. // K3bDataItem* item = root(); unsigned int maxlen = ( isoOptions().jolietLong() ? 103 : 64 ); while( (item = item->nextSibling()) ) { item->setWrittenName( treatWhitespace( item->k3bName() ) ); if( isoOptions().createJoliet() && item->writtenName().length() > maxlen ) { m_needToCutFilenames = true; item->setWrittenName( K3b::cutFilename( item->writtenName(), maxlen ) ); m_needToCutFilenameItems.append( item ); } // TODO: check the Joliet charset } // // 3. check if a directory contains items with the same name // prepareFilenamesInDir( root() ); } void K3bDataDoc::prepareFilenamesInDir( K3bDirItem* dir ) { if( !dir ) return; TQPtrList sortedChildren; TQPtrListIterator it( dir->children() ); for( it.toLast(); it.current(); --it ) { K3bDataItem* item = it.current(); if( item->isDir() ) prepareFilenamesInDir( dynamic_cast( item ) ); // insertion sort unsigned int i = 0; while( i < sortedChildren.count() && item->writtenName() > sortedChildren.at(i)->writtenName() ) ++i; sortedChildren.insert( i, item ); } if( isoOptions().createJoliet() || isoOptions().createRockRidge() ) { TQPtrList sameNameList; while( !sortedChildren.isEmpty() ) { sameNameList.clear(); do { sameNameList.append( sortedChildren.first() ); sortedChildren.removeFirst(); } while( !sortedChildren.isEmpty() && sortedChildren.first()->writtenName() == sameNameList.first()->writtenName() ); if( sameNameList.count() > 1 ) { // now we need to rename the items unsigned int maxlen = 255; if( isoOptions().createJoliet() ) { if( isoOptions().jolietLong() ) maxlen = 103; else maxlen = 64; } int cnt = 1; for( TQPtrListIterator it( sameNameList ); it.current(); ++it ) { it.current()->setWrittenName( K3b::appendNumberToFilename( it.current()->writtenName(), cnt++, maxlen ) ); } } } } } void K3bDataDoc::informAboutNotFoundFiles() { if( !m_notFoundFiles.isEmpty() ) { KMessageBox::informationList( TQT_TQWIDGET(tqApp->activeWindow()), i18n("Could not find the following files:"), m_notFoundFiles, i18n("Not Found") ); m_notFoundFiles.clear(); } if( !m_noPermissionFiles.isEmpty() ) { KMessageBox::informationList( TQT_TQWIDGET(tqApp->activeWindow()), i18n("No permission to read the following files:"), m_noPermissionFiles, i18n("No Read Permission") ); m_noPermissionFiles.clear(); } } void K3bDataDoc::setMultiSessionMode( K3bDataDoc::MultiSessionMode mode ) { if( m_multisessionMode == NONE || m_multisessionMode == START ) clearImportedSession(); m_multisessionMode = mode; } bool K3bDataDoc::importSession( K3bDevice::Device* device ) { K3bDevice::DiskInfo diskInfo = device->diskInfo(); // DVD+RW media is reported as non-appendable if( !diskInfo.appendable() && !(diskInfo.mediaType() & (K3bDevice::MEDIA_DVD_PLUS_RW|K3bDevice::MEDIA_DVD_RW_OVWR)) ) return false; K3bDevice::Toc toc = device->readToc(); if( toc.isEmpty() || toc.last().type() != K3bDevice::Track::DATA ) return false; long startSec = toc.last().firstSector().lba(); K3bIso9660 iso( device, startSec ); if( iso.open() ) { // remove previously imported sessions clearImportedSession(); // set multisession option if( m_multisessionMode != FINISH && m_multisessionMode != AUTO ) m_multisessionMode = CONTINUE; // since in iso9660 it is possible that two files share it's data // simply summing the file sizes could result in wrong values // that's why we use the size from the toc. This is more accurate // anyway since there might be files overwritten or removed m_oldSessionSize = toc.last().lastSector().mode1Bytes(); kdDebug() << "(K3bDataDoc) imported session size: " << KIO::convertSize(m_oldSessionSize) << endl; // the track size for DVD+RW media and DVD-RW Overwrite media has nothing to do with the filesystem // size. in that case we need to use the filesystem's size (which is ok since it's one track anyway, // no real multisession) if( diskInfo.mediaType() & (K3bDevice::MEDIA_DVD_PLUS_RW|K3bDevice::MEDIA_DVD_RW_OVWR) ) { m_oldSessionSize = iso.primaryDescriptor().volumeSpaceSize * iso.primaryDescriptor().logicalBlockSize; } // import some former settings m_isoOptions.setCreateRockRidge( iso.firstRRDirEntry() != 0 ); m_isoOptions.setCreateJoliet( iso.firstJolietDirEntry() != 0 ); m_isoOptions.setVolumeID( iso.primaryDescriptor().volumeId ); // TODO: also import some other pd fields const K3bIso9660Directory* rootDir = iso.firstRRDirEntry(); // J�rg Schilling says that it is impossible to import the joliet tree for multisession // if( !rootDir ) // rootDir = iso.firstJolietDirEntry(); if( !rootDir ) rootDir = iso.firstIsoDirEntry(); if( rootDir ) { createSessionImportItems( rootDir, root() ); emit changed(); return true; } else { kdDebug() << "(K3bDataDoc::importSession) Could not find primary volume desc." << endl; return false; } } else { kdDebug() << "(K3bDataDoc) unable to read toc." << endl; return false; } } void K3bDataDoc::createSessionImportItems( const K3bIso9660Directory* importDir, K3bDirItem* parent ) { Q_ASSERT(importDir); TQStringList entries = importDir->entries(); entries.remove( "." ); entries.remove( ".." ); for( TQStringList::const_iterator it = entries.begin(); it != entries.end(); ++it ) { const K3bIso9660Entry* entry = importDir->entry( *it ); K3bDataItem* oldItem = parent->find( entry->name() ); if( entry->isDirectory() ) { K3bDirItem* dir = 0; if( oldItem && oldItem->isDir() ) { dir = (K3bDirItem*)oldItem; } else { // we overwrite without warning! if( oldItem ) removeItem( oldItem ); dir = new K3bDirItem( entry->name(), this, parent ); } dir->setRemoveable(false); dir->setRenameable(false); dir->setMoveable(false); dir->setHideable(false); dir->setWriteToCd(false); dir->setExtraInfo( i18n("From previous session") ); m_oldSession.append( dir ); createSessionImportItems( static_cast(entry), dir ); } else { const K3bIso9660File* file = static_cast(entry); // we overwrite without warning! if( oldItem ) removeItem( oldItem ); K3bSessionImportItem* item = new K3bSessionImportItem( file, this, parent ); item->setExtraInfo( i18n("From previous session") ); m_oldSession.append( item ); } } } void K3bDataDoc::clearImportedSession() { // m_oldSessionSizeHandler->clear(); m_oldSessionSize = 0; m_oldSession.setAutoDelete(false); K3bDataItem* item = m_oldSession.first(); while( !m_oldSession.isEmpty() ) { if( item == 0 ) item = m_oldSession.first(); if( item->isDir() ) { K3bDirItem* dir = (K3bDirItem*)item; if( dir->numDirs() + dir->numFiles() == 0 ) { // this imported dir is not needed anymore // since it is empty m_oldSession.remove(); delete dir; } else { for( TQPtrListIterator it( dir->children() ); it.current(); ++it ) { if( !m_oldSession.contains(it.current()) ) { m_oldSession.remove(); // now the dir becomes a totally normal dir dir->setRemoveable(true); dir->setRenameable(true); dir->setMoveable(true); dir->setHideable(true); dir->setWriteToCd(true); dir->setExtraInfo( "" ); break; } } } } else { m_oldSession.remove(); delete item; } item = m_oldSession.next(); } m_multisessionMode = AUTO; emit changed(); } K3bDirItem* K3bDataDoc::bootImageDir() { K3bDataItem* b = m_root->find( "boot" ); if( !b ) { b = new K3bDirItem( "boot", this, m_root ); setModified( true ); } // if we cannot create the dir because there is a file named boot just use the root dir if( !b->isDir() ) return m_root; else return static_cast(b); } K3bBootItem* K3bDataDoc::createBootItem( const TQString& filename, K3bDirItem* dir ) { if( !dir ) dir = bootImageDir(); K3bBootItem* boot = new K3bBootItem( filename, this, dir ); if( !m_bootCataloge ) createBootCatalogeItem(dir); return boot; } K3bDataItem* K3bDataDoc::createBootCatalogeItem( K3bDirItem* dir ) { if( !m_bootCataloge ) { TQString newName = "boot.catalog"; int i = 0; while( dir->alreadyInDirectory( "boot.catalog" ) ) { ++i; newName = TQString( "boot%1.catalog" ).arg(i); } K3bSpecialDataItem* b = new K3bSpecialDataItem( this, 0, dir, newName ); m_bootCataloge = b; m_bootCataloge->setRemoveable(false); m_bootCataloge->setHideable(false); m_bootCataloge->setWriteToCd(false); m_bootCataloge->setExtraInfo( i18n("El Torito boot catalog file") ); b->setMimeType( i18n("Boot catalog") ); } else m_bootCataloge->reparent( dir ); return m_bootCataloge; } TQValueList K3bDataDoc::findItemByLocalPath( const TQString& path ) const { Q_UNUSED( path ); return TQValueList(); } bool K3bDataDoc::sessionImported() const { return !m_oldSession.isEmpty(); } #include "k3bdatadoc.moc"