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

0001 /*
0002     SPDX-FileCopyrightText: 2011 Michal Malek <michalm@jabster.pl>
0003     SPDX-FileCopyrightText: 1998-2007 Sebastian Trueg <trueg@k3b.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 
0009 #include "k3bdiritem.h"
0010 #include "k3bdatadoc.h"
0011 #include "k3bsessionimportitem.h"
0012 #include "k3bfileitem.h"
0013 #include "k3bisooptions.h"
0014 #include "k3b_i18n.h"
0015 
0016 #include <QDebug>
0017 #include <QMimeDatabase>
0018 
0019 
0020 K3b::DirItem::DirItem(const QString& name, const ItemFlags& flags)
0021     : K3b::DataItem( flags | DIR ),
0022       m_size(0),
0023       m_followSymlinksSize(0),
0024       m_blocks(0),
0025       m_followSymlinksBlocks(0),
0026       m_files(0),
0027       m_dirs(0)
0028 {
0029     m_k3bName = name;
0030 }
0031 
0032 
0033 K3b::DirItem::DirItem( const K3b::DirItem& item )
0034     : K3b::DataItem( item ),
0035       m_size(0),
0036       m_followSymlinksSize(0),
0037       m_blocks(0),
0038       m_followSymlinksBlocks(0),
0039       m_files(0),
0040       m_dirs(0),
0041       m_localPath( item.m_localPath )
0042 {
0043     Q_FOREACH( K3b::DataItem* _item, item.children() ) {
0044         addDataItem( _item->copy() );
0045     }
0046 }
0047 
0048 K3b::DirItem::~DirItem()
0049 {
0050     // delete all children
0051     // doing this by hand is much saver than using the
0052     // auto-delete feature since some of the items' destructors
0053     // may change the list
0054     while( !m_children.isEmpty() ) {
0055         // it is important to use takeDataItem here to be sure
0056         // the size gets updated properly
0057         K3b::DataItem* item = m_children.first();
0058         takeDataItem( item );
0059         delete item;
0060     }
0061 
0062     // this has to be done after deleting the children
0063     // because the directory itself has a size of 0 in K3b
0064     // and all it's files' sizes have already been subtracted
0065     take();
0066 }
0067 
0068 
0069 K3b::DataItem* K3b::DirItem::copy() const
0070 {
0071     return new K3b::DirItem( *this );
0072 }
0073 
0074 
0075 K3b::DirItem* K3b::DirItem::getDirItem() const
0076 {
0077     return const_cast<K3b::DirItem*>(this);
0078 }
0079 
0080 
0081 K3b::DirItem* K3b::DirItem::addDataItem( K3b::DataItem* item )
0082 {
0083     if( canAddDataItem( item ) ) {
0084 
0085         // Detach item from its parent in case it's moved from elsewhere.
0086         // It is essential to do this before calling getDoc()->aboutToAddItemToDir()
0087         // avoid situation when beginRemoveRows() is called after beginInsertRows()
0088         // in DataProjectModel
0089         item->take();
0090 
0091         // inform the doc
0092         if( DataDoc* doc = getDoc() ) {
0093             doc->beginInsertItems( this, m_children.size(), m_children.size() );
0094         }
0095 
0096         addDataItemImpl( item );
0097 
0098         if( DataDoc* doc = getDoc() ) {
0099             doc->endInsertItems( this, m_children.size()-1, m_children.size()-1 );
0100         }
0101     }
0102 
0103     return this;
0104 }
0105 
0106 
0107 void K3b::DirItem::addDataItems( const Children& items )
0108 {
0109     Children newItems;
0110     newItems.reserve( items.size() );
0111     Q_FOREACH( DataItem* item, items ) {
0112         if( canAddDataItem( item ) ) {
0113             // Detach item from its parent in case it's moved from elsewhere.
0114             // It is essential to do this before calling getDoc()->aboutToAddItemToDir()
0115             // avoid situation when beginRemoveRows() is called after beginInsertRows()
0116             // in DataProjectModel
0117             item->take();
0118 
0119             newItems.push_back( item );
0120         }
0121     }
0122 
0123     if( !newItems.empty() ) {
0124         const int start = m_children.size();
0125         const int end = m_children.size() + newItems.size() - 1;
0126 
0127         // inform the doc
0128         if( DataDoc* doc = getDoc() ) {
0129             doc->beginInsertItems( this, start, end );
0130         }
0131 
0132         // pre-alloc space for items
0133         m_children.reserve( m_children.size() + newItems.size() );
0134 
0135         Q_FOREACH( DataItem* item, newItems ) {
0136             addDataItemImpl( item );
0137         }
0138 
0139         if( DataDoc* doc = getDoc() ) {
0140             doc->endInsertItems( this, start, end );
0141         }
0142     }
0143 }
0144 
0145 
0146 void K3b::DirItem::removeDataItems( int start, int count )
0147 {
0148     Children takenItems = takeDataItems( start, count );
0149     qDeleteAll( takenItems );
0150 }
0151 
0152 
0153 K3b::DataItem* K3b::DirItem::takeDataItem( K3b::DataItem* item )
0154 {
0155     int i = m_children.lastIndexOf( item );
0156     if( i > -1 ) {
0157         takeDataItems( i, 1 );
0158         return item;
0159     } else {
0160         return 0;
0161     }
0162 }
0163 
0164 
0165 K3b::DirItem::Children K3b::DirItem::takeDataItems( int start, int count )
0166 {
0167     Children takenItems;
0168 
0169     if( start >= 0 && count > 0 ) {
0170         if( DataDoc* doc = getDoc() ) {
0171             doc->beginRemoveItems( this, start, start+count-1 );
0172         }
0173 
0174         for( int i = 0; i < count; ++i ) {
0175             DataItem* item = m_children.at( start+i );
0176             updateSize( item, true );
0177             if( item->isDir() )
0178                 updateFiles( -1*((DirItem*)item)->numFiles(), -1*((DirItem*)item)->numDirs()-1 );
0179             else
0180                 updateFiles( -1, 0 );
0181 
0182             item->setParentDir( 0 );
0183 
0184             // unset OLD_SESSION flag if it was the last child from previous sessions
0185             updateOldSessionFlag();
0186 
0187             takenItems.append( item );
0188         }
0189 
0190         // filling the gap: move items from after removed range
0191         std::copy( m_children.begin()+start+count, m_children.end(),
0192                m_children.begin()+start );
0193 
0194         // remove unused space
0195         for( int i = 0; i < count; ++i ) {
0196             m_children.pop_back();
0197         }
0198 
0199         // inform the doc
0200         if( DataDoc* doc = getDoc() ) {
0201             doc->endRemoveItems( this, start, start+count-1 );
0202         }
0203 
0204         Q_FOREACH( DataItem* item, takenItems ) {
0205             if( item->isFile() ) {
0206                 // restore the item imported from an old session
0207                 if( DataItem* replaceItem = static_cast<FileItem*>(item)->replaceItemFromOldSession() )
0208                     addDataItem( replaceItem );
0209             }
0210         }
0211     }
0212 
0213     return takenItems;
0214 }
0215 
0216 
0217 K3b::DataItem* K3b::DirItem::nextSibling() const
0218 {
0219     if( !m_children.isEmpty() )
0220         return m_children.first();
0221     else
0222         return K3b::DataItem::nextSibling();
0223 }
0224 
0225 
0226 K3b::DataItem* K3b::DirItem::nextChild( K3b::DataItem* prev ) const
0227 {
0228     // search for prev in children
0229     int index = m_children.lastIndexOf( prev );
0230     if( index < 0 || index+1 == m_children.count() ) {
0231         return 0;
0232     }
0233     else
0234         return m_children[index+1];
0235 }
0236 
0237 
0238 bool K3b::DirItem::alreadyInDirectory( const QString& filename ) const
0239 {
0240     return (find( filename ) != 0);
0241 }
0242 
0243 
0244 K3b::DataItem* K3b::DirItem::find( const QString& filename ) const
0245 {
0246     Q_FOREACH( K3b::DataItem* item, m_children ) {
0247         if( item->k3bName() == filename )
0248             return item;
0249     }
0250     return 0;
0251 }
0252 
0253 
0254 K3b::DataItem* K3b::DirItem::findByPath( const QString& p )
0255 {
0256     if( p.isEmpty() || p == "/" )
0257         return this;
0258 
0259     QString path = p;
0260     if( path.startsWith('/') )
0261         path = path.mid(1);
0262     int pos = path.indexOf( "/" );
0263     if( pos < 0 )
0264         return find( path );
0265     else {
0266         // do it recursively
0267         K3b::DataItem* item = find( path.left(pos) );
0268         if( item && item->isDir() )
0269             return ((K3b::DirItem*)item)->findByPath( path.mid( pos+1 ) );
0270         else
0271             return 0;
0272     }
0273 }
0274 
0275 
0276 bool K3b::DirItem::mkdir( const QString& dirPath )
0277 {
0278     //
0279     // An absolute path always starts at the root item
0280     //
0281     if( dirPath[0] == '/' ) {
0282         if( parent() )
0283             return parent()->mkdir( dirPath );
0284         else
0285             return mkdir( dirPath.mid( 1 ) );
0286     }
0287 
0288     if( findByPath( dirPath ) )
0289         return false;
0290 
0291     QString restPath;
0292     QString dirName;
0293     int pos = dirPath.indexOf( '/' );
0294     if( pos == -1 ) {
0295         dirName = dirPath;
0296     }
0297     else {
0298         dirName = dirPath.left( pos );
0299         restPath = dirPath.mid( pos+1 );
0300     }
0301 
0302     K3b::DataItem* dir = find( dirName );
0303     if( !dir ) {
0304         dir = new K3b::DirItem( dirName );
0305         addDataItem( dir );
0306     } else if( !dir->isDir() ) {
0307         return false;
0308     }
0309 
0310     if( !restPath.isEmpty() )
0311         return static_cast<K3b::DirItem*>(dir)->mkdir( restPath );
0312 
0313     return true;
0314 }
0315 
0316 
0317 KIO::filesize_t K3b::DirItem::itemSize( bool followsylinks ) const
0318 {
0319     if( followsylinks )
0320         return m_followSymlinksSize;
0321     else
0322         return m_size;
0323 }
0324 
0325 
0326 K3b::Msf K3b::DirItem::itemBlocks( bool followSymlinks ) const
0327 {
0328     if( followSymlinks )
0329         return m_followSymlinksBlocks;
0330     else
0331         return m_blocks;
0332 }
0333 
0334 
0335 bool K3b::DirItem::isSubItem( const DataItem* item ) const
0336 {
0337     for( const DirItem* dir = dynamic_cast<const DirItem*>(item); dir != 0; dir = dir->parent() ) {
0338         if( dir == this ) {
0339             return true;
0340         }
0341     }
0342 
0343     return false;
0344 }
0345 
0346 
0347 long K3b::DirItem::numFiles() const
0348 {
0349     return m_files;
0350 }
0351 
0352 
0353 long K3b::DirItem::numDirs() const
0354 {
0355     return m_dirs;
0356 }
0357 
0358 
0359 bool K3b::DirItem::isRemoveable() const
0360 {
0361     if( !K3b::DataItem::isRemoveable() )
0362         return false;
0363 
0364     for( Children::const_iterator it = m_children.constBegin(), end = m_children.constEnd(); it != end; ++it ) {
0365         if( !( *it )->isRemoveable() )
0366             return false;
0367     }
0368 
0369     return true;
0370 }
0371 
0372 
0373 void K3b::DirItem::updateSize( K3b::DataItem* item, bool removed )
0374 {
0375     if ( !item->isFromOldSession() ) {
0376         if( removed ) {
0377             m_followSymlinksSize -= item->itemSize( true );
0378             m_size -= item->itemSize( false );
0379             m_followSymlinksBlocks -= item->itemBlocks( true ).lba();
0380             m_blocks -= item->itemBlocks( false ).lba();
0381         }
0382         else {
0383             m_followSymlinksSize += item->itemSize( true );
0384             m_size += item->itemSize( false );
0385             m_followSymlinksBlocks += item->itemBlocks( true ).lba();
0386             m_blocks += item->itemBlocks( false ).lba();
0387         }
0388     }
0389 
0390     if( parent() )
0391         parent()->updateSize( item, removed );
0392 }
0393 
0394 void K3b::DirItem::updateFiles( long files, long dirs )
0395 {
0396     m_files += files;
0397     m_dirs += dirs;
0398     if( parent() )
0399         parent()->updateFiles( files, dirs );
0400 }
0401 
0402 
0403 void K3b::DirItem::updateOldSessionFlag()
0404 {
0405     if( flags().testFlag( OLD_SESSION ) ) {
0406         for( Children::const_iterator it = m_children.constBegin(), end = m_children.constEnd(); it != end; ++it ) {
0407             if( (*it)->isFromOldSession() ) {
0408                 return;
0409             }
0410         }
0411         setFlags( flags() & ~OLD_SESSION );
0412     }
0413 }
0414 
0415 
0416 bool K3b::DirItem::writeToCd() const
0417 {
0418     // check if this dir contains items to write
0419     Children::const_iterator end( m_children.constEnd() );
0420     for( Children::const_iterator it = m_children.constBegin(); it != end; ++it ) {
0421         if( (*it)->writeToCd() )
0422             return true;
0423     }
0424     return K3b::DataItem::writeToCd();
0425 }
0426 
0427 
0428 QMimeType K3b::DirItem::mimeType() const
0429 {
0430     return QMimeDatabase().mimeTypeForName( "inode/directory" );
0431 }
0432 
0433 
0434 bool K3b::DirItem::canAddDataItem( DataItem* item ) const
0435 {
0436     // check if we are a subdir of item
0437     DirItem* dirItem = dynamic_cast<DirItem*>( item );
0438     if( dirItem && dirItem->isSubItem( this ) ) {
0439         qDebug() << "(K3b::DirItem) trying to move a dir item down in it's own tree.";
0440         return false;
0441     } else if( !item || m_children.contains( item ) ) {
0442         return false;
0443     } else {
0444         return true;
0445     }
0446 }
0447 
0448 
0449 void K3b::DirItem::addDataItemImpl( DataItem* item )
0450 {
0451     if( item->isFile() ) {
0452         // do we replace an old item?
0453         QString name = item->k3bName();
0454         int cnt = 1;
0455         while( DataItem* oldItem = find( name ) ) {
0456             if( !oldItem->isDir() && oldItem->isFromOldSession() ) {
0457                 // in this case we remove this item from it's parent and save it in the new one
0458                 // to be able to recover it
0459                 oldItem->take();
0460                 static_cast<SessionImportItem*>(oldItem)->setReplaceItem( static_cast<FileItem*>(item) );
0461                 static_cast<FileItem*>(item)->setReplacedItemFromOldSession( oldItem );
0462                 break;
0463             }
0464             else {
0465                 //
0466                 // add a counter to the filename
0467                 //
0468                 if( item->k3bName()[item->k3bName().length()-4] == '.' )
0469                     name = item->k3bName().left( item->k3bName().length()-4 ) + QString::number(cnt++) + item->k3bName().right(4);
0470                 else
0471                     name = item->k3bName() + QString::number(cnt++);
0472             }
0473         }
0474         item->setK3bName( name );
0475     }
0476 
0477     m_children.append( item );
0478     updateSize( item, false );
0479     if( item->isDir() )
0480         updateFiles( ((DirItem*)item)->numFiles(), ((DirItem*)item)->numDirs()+1 );
0481     else
0482         updateFiles( 1, 0 );
0483 
0484     item->setParentDir( this );
0485 
0486     // If item is from previous session,flag this directory as such also
0487     if( !isFromOldSession() && item->isFromOldSession() ) {
0488         setFlags( flags() | OLD_SESSION );
0489     }
0490 }
0491 
0492 
0493 K3b::RootItem::RootItem( K3b::DataDoc& doc )
0494     : K3b::DirItem( "root" ),
0495       m_doc( doc )
0496 {
0497 }
0498 
0499 
0500 K3b::RootItem::~RootItem()
0501 {
0502 }
0503 
0504 
0505 K3b::DataDoc* K3b::RootItem::getDoc() const
0506 {
0507     return &m_doc;
0508 }
0509 
0510 
0511 QString K3b::RootItem::k3bName() const
0512 {
0513     return m_doc.isoOptions().volumeID();
0514 }
0515 
0516 
0517 void K3b::RootItem::setK3bName( const QString& text )
0518 {
0519     m_doc.setVolumeID( text );
0520 }