00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "archivemodel.h"
00022 #include "kerfuffle/archive.h"
00023 #include "kerfuffle/jobs.h"
00024
00025 #include <QList>
00026 #include <QPixmap>
00027
00028 #include <KLocale>
00029 #include <KMimeType>
00030 #include <KIconLoader>
00031 #include <KIO/NetAccess>
00032
00033 class ArchiveDirNode;
00034
00035 class ArchiveNode
00036 {
00037 public:
00038 ArchiveNode( ArchiveDirNode *parent, const ArchiveEntry & entry )
00039 : m_parent( parent ), m_row( -1 )
00040 {
00041 setEntry( entry );
00042 }
00043
00044 virtual ~ArchiveNode() {}
00045
00046 ArchiveEntry entry() const { return m_entry; }
00047 void setEntry( const ArchiveEntry & entry )
00048 {
00049 m_entry = entry;
00050 QStringList pieces = entry[ FileName ].toString().split( '/', QString::SkipEmptyParts );
00051 m_name = pieces.isEmpty()? QString() : pieces.last();
00052 }
00053
00054 ArchiveDirNode *parent() const { return m_parent; }
00055
00056 int row();
00057 QString name() const { return m_name; }
00058
00059 virtual bool isDir() const { return false; }
00060
00061 QPixmap icon()
00062 {
00063 if ( m_icon.isNull() )
00064 {
00065 KMimeType::Ptr mimeType = KMimeType::findByPath( m_entry[ FileName ].toString(), 0, true );
00066 m_icon = KIconLoader::global()->loadMimeTypeIcon( mimeType->iconName(), KIconLoader::Small );
00067 }
00068 return m_icon;
00069 }
00070
00071 protected:
00072 QPixmap m_icon;
00073
00074 private:
00075 ArchiveEntry m_entry;
00076 ArchiveDirNode *m_parent;
00077 QString m_name;
00078 int m_row;
00079 };
00080
00081 class ArchiveDirNode: public ArchiveNode
00082 {
00083 public:
00084 ArchiveDirNode( ArchiveDirNode *parent, const ArchiveEntry & entry )
00085 : ArchiveNode( parent, entry )
00086 {
00087 m_icon = KIconLoader::global()->loadMimeTypeIcon( KMimeType::mimeType( "inode/directory" )->iconName(), KIconLoader::Small );
00088 }
00089
00090 ~ArchiveDirNode()
00091 {
00092 clear();
00093 }
00094
00095 QList<ArchiveNode*>& entries() { return m_entries; }
00096
00097 virtual bool isDir() const { return true; }
00098
00099 ArchiveNode* find( const QString & name )
00100 {
00101 foreach( ArchiveNode *node, m_entries )
00102 {
00103 if ( node && ( node->name() == name ) )
00104 {
00105 return node;
00106 }
00107 }
00108 return 0;
00109 }
00110
00111 ArchiveNode* findByPath( const QString & path )
00112 {
00113 QStringList pieces = path.split( '/' );
00114 if ( pieces.isEmpty() )
00115 {
00116 return 0;
00117 }
00118
00119 ArchiveNode *next = find( pieces[ 0 ] );
00120
00121 if ( pieces.count() == 1 )
00122 {
00123 return next;
00124 }
00125 if ( next->isDir() )
00126 return static_cast<ArchiveDirNode*>( next )->findByPath( pieces.join( "/" ) );
00127 return 0;
00128 }
00129
00130 void clear()
00131 {
00132 qDeleteAll( m_entries );
00133 m_entries.clear();
00134 }
00135
00136 private:
00137 QList<ArchiveNode*> m_entries;
00138 };
00139
00140 int ArchiveNode::row()
00141 {
00142 if ( m_row != -1 ) return m_row;
00143
00144 if ( parent() )
00145 {
00146 m_row = parent()->entries().indexOf( const_cast<ArchiveNode*>( this ) );
00147 return m_row;
00148 }
00149 return 0;
00150 }
00151
00152 ArchiveModel::ArchiveModel( QObject *parent )
00153 : QAbstractItemModel( parent ), m_archive( 0 ),
00154 m_rootNode( new ArchiveDirNode( 0, ArchiveEntry() ) ),
00155 m_jobTracker(0)
00156 {
00157 }
00158
00159 ArchiveModel::~ArchiveModel()
00160 {
00161 delete m_archive;
00162 m_archive = 0;
00163
00164 delete m_rootNode;
00165 m_rootNode = 0;
00166 }
00167
00168 QVariant ArchiveModel::data( const QModelIndex &index, int role ) const
00169 {
00170 if ( index.isValid() )
00171 {
00172 ArchiveNode *node = static_cast<ArchiveNode*>( index.internalPointer() );
00173 switch ( role )
00174 {
00175 case Qt::DisplayRole:
00176 if ( index.column() == 0 )
00177 {
00178 return node->name();
00179 }
00180 else
00181 {
00182 if ( node->isDir() || node->entry().contains( Link ) )
00183 {
00184 return QVariant();
00185 }
00186 else
00187 {
00188 return KIO::convertSize( node->entry()[ Size ].toULongLong() );
00189 }
00190 }
00191 case Qt::DecorationRole:
00192 if ( index.column() == 0 )
00193 {
00194 return node->icon();
00195 }
00196 return QVariant();
00197 default:
00198 return QVariant();
00199 }
00200 }
00201 return QVariant();
00202 }
00203
00204 Qt::ItemFlags ArchiveModel::flags( const QModelIndex &index ) const
00205 {
00206 if ( index.isValid() )
00207 {
00208 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
00209 }
00210
00211 return 0;
00212 }
00213
00214 QVariant ArchiveModel::headerData( int section, Qt::Orientation, int role ) const
00215 {
00216 if ( role == Qt::DisplayRole )
00217 {
00218 switch ( section )
00219 {
00220 case 0:
00221 return i18nc( "Name of a file inside an archive", "Name" );
00222 case 1:
00223 return i18n( "Size" );
00224 }
00225 }
00226 return QVariant();
00227 }
00228
00229 QModelIndex ArchiveModel::index( int row, int column, const QModelIndex &parent ) const
00230 {
00231 if ( hasIndex( row, column, parent ) )
00232 {
00233 ArchiveDirNode *parentNode = parent.isValid()? static_cast<ArchiveDirNode*>( parent.internalPointer() ) : m_rootNode;
00234
00235 Q_ASSERT( parentNode->isDir() );
00236
00237 ArchiveNode *item = parentNode->entries().value( row, 0 );
00238 if ( item )
00239 {
00240 return createIndex( row, column, item );
00241 }
00242 }
00243
00244 return QModelIndex();
00245 }
00246
00247 QModelIndex ArchiveModel::parent( const QModelIndex &index ) const
00248 {
00249 if ( index.isValid() )
00250 {
00251 ArchiveNode *item = static_cast<ArchiveNode*>( index.internalPointer() );
00252 Q_ASSERT( item );
00253 if ( item->parent() && ( item->parent() != m_rootNode ) )
00254 {
00255 return createIndex( item->parent()->row(), 0, item->parent() );
00256 }
00257 }
00258 return QModelIndex();
00259 }
00260
00261 ArchiveEntry ArchiveModel::entryForIndex( const QModelIndex &index )
00262 {
00263 if ( index.isValid() )
00264 {
00265 ArchiveNode *item = static_cast<ArchiveNode*>( index.internalPointer() );
00266 Q_ASSERT( item );
00267 return item->entry();
00268 }
00269 return ArchiveEntry();
00270 }
00271
00272 int ArchiveModel::childCount( const QModelIndex &index )
00273 {
00274 if ( index.isValid() )
00275 {
00276 ArchiveNode *item = static_cast<ArchiveNode*>( index.internalPointer() );
00277 Q_ASSERT( item );
00278 if ( item->isDir() )
00279 {
00280 return static_cast<ArchiveDirNode*>( item )->entries().count();
00281 }
00282 return 0;
00283 }
00284 return -1;
00285 }
00286
00287 int ArchiveModel::rowCount( const QModelIndex &parent ) const
00288 {
00289 if ( parent.column() <= 0 )
00290 {
00291 ArchiveNode *parentNode = parent.isValid()? static_cast<ArchiveNode*>( parent.internalPointer() ) : m_rootNode;
00292
00293 if ( parentNode && parentNode->isDir() )
00294 {
00295 return static_cast<ArchiveDirNode*>( parentNode )->entries().count();
00296 }
00297 }
00298 return 0;
00299 }
00300
00301 int ArchiveModel::columnCount( const QModelIndex &parent ) const
00302 {
00303 if ( parent.isValid() )
00304 {
00305 return static_cast<ArchiveNode*>( parent.internalPointer() )->entry().size();
00306 }
00307 return 2;
00308 }
00309
00310 ArchiveDirNode* ArchiveModel::parentFor( const ArchiveEntry& entry )
00311 {
00312 QStringList pieces = entry[ FileName ].toString().split( '/', QString::SkipEmptyParts );
00313 pieces.removeLast();
00314
00315 ArchiveDirNode *parent = m_rootNode;
00316
00317 foreach( QString piece, pieces )
00318 {
00319 ArchiveNode *node = parent->find( piece );
00320 if ( !node )
00321 {
00322 ArchiveEntry e;
00323 e[ FileName ] = parent->entry()[ FileName ].toString() + '/' + piece;
00324 node = new ArchiveDirNode( parent, e );
00325 }
00326 if ( !node->isDir() )
00327 {
00328 ArchiveEntry e( node->entry() );
00329 int index = parent->entries().indexOf( node );
00330 delete node;
00331 node = new ArchiveDirNode( parent, e );
00332 parent->entries()[ index ] = node;
00333 }
00334 parent = static_cast<ArchiveDirNode*>( node );
00335 }
00336
00337 return parent;
00338 }
00339 QModelIndex ArchiveModel::indexForNode( ArchiveNode *node )
00340 {
00341 Q_ASSERT( node );
00342 if ( node != m_rootNode )
00343 {
00344 Q_ASSERT( node->parent() );
00345 Q_ASSERT( node->parent()->isDir() );
00346 return createIndex( node->row(), 0, node );
00347 }
00348 return QModelIndex();
00349 }
00350
00351 void ArchiveModel::slotEntryRemoved( const QString & path )
00352 {
00353
00354 ArchiveNode *entry = m_rootNode->findByPath( path );
00355 if ( entry )
00356 {
00357 ArchiveDirNode *parent = entry->parent();
00358 QModelIndex index = indexForNode( entry );
00359
00360 beginRemoveRows( indexForNode( parent ), entry->row(), entry->row() );
00361
00362 delete parent->entries()[ entry->row() ];
00363 parent->entries()[ entry->row() ] = 0;
00364
00365 endRemoveRows();
00366 }
00367 }
00368
00369 void ArchiveModel::slotNewEntry( const ArchiveEntry& entry )
00370 {
00372 ArchiveDirNode *parent = parentFor( entry );
00373 QModelIndex parentIndex = indexForNode( parent );
00374
00375
00377 QString name = entry[ FileName ].toString().split( '/', QString::SkipEmptyParts ).last();
00378 ArchiveNode *node = parent->find( name );
00379 if ( node )
00380 {
00381 node->setEntry( entry );
00382 }
00383 else
00384 {
00385 beginInsertRows( parentIndex, m_rootNode->entries().count(), m_rootNode->entries().count() );
00386
00387 if ( entry[ FileName ].toString().endsWith( '/' ) || ( entry.contains( IsDirectory ) && entry[ IsDirectory ].toBool() ) )
00388 {
00389 node = new ArchiveDirNode( parent, entry );
00390 }
00391 else
00392 {
00393 node = new ArchiveNode( parent, entry );
00394 }
00395 parent->entries().append( node );
00396
00397 endInsertRows();
00398 }
00399 }
00400
00401 void ArchiveModel::setArchive( Kerfuffle::Archive *archive )
00402 {
00403 delete m_archive;
00404 m_archive = archive;
00405 m_rootNode->clear();
00406 if ( m_archive )
00407 {
00408 Kerfuffle::ListJob *job = m_archive->list();
00409
00410 connect( job, SIGNAL( newEntry( const ArchiveEntry& ) ),
00411 this, SLOT( slotNewEntry( const ArchiveEntry& ) ) );
00412
00413 connect( job, SIGNAL( result( KJob * ) ),
00414 this, SIGNAL( loadingFinished() ) );
00415
00416 if ( m_jobTracker )
00417 {
00418 m_jobTracker->registerJob( job );
00419 }
00420
00421 emit loadingStarted();
00422 job->start();
00423 }
00424 reset();
00425 }
00426
00427 ExtractJob* ArchiveModel::extractFile( const QVariant& fileName, const QString & destinationDir, bool preservePaths )
00428 {
00429 QList<QVariant> files;
00430 files << fileName;
00431 return extractFiles( files, destinationDir, preservePaths );
00432 }
00433
00434 ExtractJob* ArchiveModel::extractFiles( const QList<QVariant>& files, const QString & destinationDir, bool preservePaths )
00435 {
00436 Q_ASSERT( m_archive );
00437 return m_archive->copyFiles( files, destinationDir, preservePaths );
00438 }
00439
00440 AddJob* ArchiveModel::addFiles( const QStringList & paths )
00441 {
00442 Q_ASSERT( m_archive );
00443
00444 if ( !m_archive->isReadOnly())
00445 {
00446 AddJob *job = m_archive->addFiles( paths );
00447 m_jobTracker->registerJob( job );
00448 connect( job, SIGNAL( newEntry( const ArchiveEntry& ) ),
00449 this, SLOT( slotNewEntry( const ArchiveEntry& ) ) );
00450 return job;
00451 }
00452 return 0;
00453 }
00454
00455 DeleteJob* ArchiveModel::deleteFiles( const QList<QVariant> & files )
00456 {
00457 Q_ASSERT( m_archive );
00458 if ( !m_archive->isReadOnly() )
00459 {
00460 DeleteJob *job = m_archive->deleteFiles( files );
00461 m_jobTracker->registerJob( job );
00462 connect( job, SIGNAL( entryRemoved( const QString & ) ),
00463 this, SLOT( slotEntryRemoved( const QString & ) ) );
00464 return job;
00465 }
00466 return 0;
00467 }