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 }