• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdeutils
  • Sitemap
  • Contact Us
 

ark

archivemodel.cpp

Go to the documentation of this file.
00001 /*
00002  * ark -- archiver for the KDE project
00003  *
00004  * Copyright (C) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * as published by the Free Software Foundation; either version 2
00009  * of the License, or (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00019  *
00020  */
00021 #include "archivemodel.h"
00022 #include "kerfuffle/archive.h"
00023 #include "kerfuffle/jobs.h"
00024 
00025 #include <QList>
00026 #include <QPixmap>
00027 #include <QFont>
00028 #include <QMimeData>
00029 #include <QDir>
00030 #include <QtDBus/QtDBus>
00031 
00032 #include <KDebug>
00033 #include <KLocale>
00034 #include <KMimeType>
00035 #include <KIconLoader>
00036 #include <KIO/NetAccess>
00037 
00038 class ArchiveDirNode;
00039 
00040 class ArchiveNode
00041 {
00042     public:
00043         ArchiveNode( ArchiveDirNode *parent, const ArchiveEntry & entry )
00044             : m_parent( parent ), m_row( -1 )
00045         {
00046             setEntry( entry );
00047         }
00048 
00049         virtual ~ArchiveNode() {}
00050 
00051         ArchiveEntry entry() const { return m_entry; }
00052         void setEntry( const ArchiveEntry & entry )
00053         {
00054             m_entry = entry;
00055             QStringList pieces = entry[ FileName ].toString().split( '/', QString::SkipEmptyParts );
00056             m_name = pieces.isEmpty()? QString() : pieces.last();
00057         }
00058 
00059         ArchiveDirNode *parent() const { return m_parent; }
00060 
00061         int row();
00062         QString name() const { return m_name; }
00063 
00064         virtual bool isDir() const { return false; }
00065 
00066         QPixmap icon()
00067         {
00068             if ( m_icon.isNull() )
00069             {
00070                 KMimeType::Ptr mimeType = KMimeType::findByPath( m_entry[ FileName ].toString(), 0, true );
00071                 m_icon = KIconLoader::global()->loadMimeTypeIcon( mimeType->iconName(), KIconLoader::Small );
00072             }
00073             return m_icon;
00074         }
00075 
00076     protected:
00077         QPixmap         m_icon;
00078 
00079     private:
00080         ArchiveEntry    m_entry;
00081         ArchiveDirNode *m_parent;
00082         QString         m_name;
00083         int             m_row;
00084 };
00085 
00086 class ArchiveDirNode: public ArchiveNode
00087 {
00088     public:
00089         ArchiveDirNode( ArchiveDirNode *parent, const ArchiveEntry & entry )
00090             : ArchiveNode( parent, entry )
00091         {
00092             m_icon = KIconLoader::global()->loadMimeTypeIcon( KMimeType::mimeType( "inode/directory" )->iconName(), KIconLoader::Small );
00093         }
00094 
00095         ~ArchiveDirNode()
00096         {
00097             clear();
00098         }
00099 
00100         QList<ArchiveNode*>& entries() { return m_entries; }
00101 
00102         virtual bool isDir() const { return true; }
00103 
00104         ArchiveNode* find( const QString & name )
00105         {
00106             foreach( ArchiveNode *node, m_entries )
00107             {
00108                 if ( node && ( node->name() == name ) )
00109                 {
00110                     return node;
00111                 }
00112             }
00113             return 0;
00114         }
00115 
00116         ArchiveNode* findByPath( const QString & path )
00117         {
00118             QStringList pieces = path.split( '/' );
00119             if ( pieces.isEmpty() )
00120             {
00121                 return 0;
00122             }
00123 
00124             ArchiveNode *next = find( pieces[ 0 ] );
00125 
00126             if ( pieces.count() == 1 )
00127             {
00128                 return next;
00129             }
00130             if ( next && next->isDir() )
00131             {
00132                 pieces.removeAt(0);
00133                 return static_cast<ArchiveDirNode*>( next )->findByPath( pieces.join( "/" ) );
00134             }
00135             return 0;
00136         }
00137 
00138         void clear()
00139         {
00140             qDeleteAll( m_entries );
00141             m_entries.clear();
00142         }
00143 
00144     private:
00145         QList<ArchiveNode*> m_entries;
00146 };
00147 
00148 int ArchiveNode::row()
00149 {
00150     if ( m_row != -1 ) return m_row;
00151 
00152     if ( parent() )
00153     {
00154         m_row = parent()->entries().indexOf( const_cast<ArchiveNode*>( this ) );
00155         return m_row;
00156     }
00157     return 0;
00158 }
00159 
00160 ArchiveModel::ArchiveModel( QObject *parent )
00161     : QAbstractItemModel( parent ), m_archive( 0 ),
00162       m_rootNode( new ArchiveDirNode( 0, ArchiveEntry() ) ),
00163       m_jobTracker(0)
00164 {
00165 }
00166 
00167 ArchiveModel::~ArchiveModel()
00168 {
00169     delete m_archive;
00170     m_archive = 0;
00171 
00172     delete m_rootNode;
00173     m_rootNode = 0;
00174 }
00175 
00176 QVariant ArchiveModel::data( const QModelIndex &index, int role ) const
00177 {
00178     if ( index.isValid() )
00179     {
00180         ArchiveNode *node = static_cast<ArchiveNode*>( index.internalPointer() );
00181         switch ( role )
00182         {
00183             case Qt::DisplayRole:
00184                 {
00185                     //TODO: complete the columns
00186                     int columnId = m_showColumns.at(index.column());
00187                     switch (columnId) {
00188                         case FileName:
00189                             return node->name();
00190                         case Size:
00191                             if ( node->isDir() || node->entry().contains( Link ) )
00192                             {
00193                                 return QVariant();
00194                             }
00195                             else
00196                             {
00197                                 return KIO::convertSize( node->entry()[ Size ].toULongLong() );
00198                             }
00199                         case CompressedSize:
00200                             if ( node->isDir() || node->entry().contains( Link ) )
00201                             {
00202                                 return QVariant();
00203                             }
00204                             else
00205                             {
00206                                 return KIO::convertSize( node->entry()[ CompressedSize ].toULongLong() );
00207                             }
00208                         case Ratio:
00209                             if ( node->isDir() || node->entry().contains( Link ) )
00210                             {
00211                                 return QVariant();
00212                             }
00213                             else
00214                             {
00215                                 qulonglong compressedSize = node->entry()[ CompressedSize ].toULongLong();
00216                                 qulonglong size = node->entry()[ Size ].toULongLong();
00217                                 return QString::number( int(100 * float(size - compressedSize) / size) ) + " %";
00218                             }
00219 
00220                         default:
00221                             return node->entry().value(columnId);
00222                     }
00223                     break;
00224                 }
00225             case Qt::DecorationRole:
00226                 if ( index.column() == 0 )
00227                 {
00228                     return node->icon();
00229                 }
00230                 return QVariant();
00231             case Qt::FontRole:
00232                 {
00233                     QFont f;
00234                     f.setItalic(node->entry()[ IsPasswordProtected ].toBool());
00235                     return f;
00236                 }
00237             default:
00238                 return QVariant();
00239         }
00240     }
00241     return QVariant();
00242 }
00243 
00244 Qt::ItemFlags ArchiveModel::flags( const QModelIndex &index ) const
00245 {
00246     Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index);
00247 
00248     if ( index.isValid() )
00249     {
00250         return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | defaultFlags;
00251     }
00252 
00253     return 0;
00254 }
00255 
00256 QVariant ArchiveModel::headerData( int section, Qt::Orientation, int role ) const
00257 {
00258     if ( role == Qt::DisplayRole )
00259     {
00260         Q_ASSERT(section < m_showColumns.size());
00261 
00262         int columnId = m_showColumns.at(section);
00263 
00264         switch ( columnId )
00265         {
00266             case FileName:
00267                 return i18nc( "Name of a file inside an archive", "Name" );
00268             case Size:
00269                 return i18nc( "Uncompressed size of a file inside an archive", "Size" );
00270             case CompressedSize:
00271                 return i18nc( "Compressed size of a file inside an archive", "Compressed" );
00272             case Ratio:
00273                 return i18nc( "Compression rate of file", "Rate" );
00274             case Owner:
00275                 return i18nc( "File's owner username", "Owner" );
00276             case Group:
00277                 return i18nc( "File's group", "Group" );
00278             case Permissions:
00279                 return i18nc( "File permissions", "Mode" );
00280             case CRC:
00281                 return i18nc( "CRC hash code", "CRC" );
00282             case Method:
00283                 return i18nc( "Compression method", "Method" );
00284             case Version:
00285                 //TODO: what exactly is a file version?
00286                 return i18nc( "File version", "Version" );
00287             case Timestamp:
00288                 return i18nc( "Timestamp", "Time" );
00289             case Comment:
00290                 return i18nc( "File comment", "Comment" );
00291             default:
00292                 return i18nc( "Unnamed column", "??" );
00293 
00294         }
00295     }
00296     return QVariant();
00297 }
00298 
00299 QModelIndex ArchiveModel::index( int row, int column, const QModelIndex &parent ) const
00300 {
00301     if ( hasIndex( row, column, parent ) )
00302     {
00303         ArchiveDirNode *parentNode = parent.isValid()? static_cast<ArchiveDirNode*>( parent.internalPointer() ) : m_rootNode;
00304 
00305         Q_ASSERT( parentNode->isDir() );
00306 
00307         ArchiveNode *item = parentNode->entries().value( row, 0 );
00308         if ( item )
00309         {
00310             return createIndex( row, column, item );
00311         }
00312     }
00313 
00314     return QModelIndex();
00315 }
00316 
00317 QModelIndex ArchiveModel::parent( const QModelIndex &index ) const
00318 {
00319     if ( index.isValid() )
00320     {
00321         ArchiveNode *item = static_cast<ArchiveNode*>( index.internalPointer() );
00322         Q_ASSERT( item );
00323         if ( item->parent() && ( item->parent() != m_rootNode ) )
00324         {
00325             return createIndex( item->parent()->row(), 0, item->parent() );
00326         }
00327     }
00328     return QModelIndex();
00329 }
00330 
00331 ArchiveEntry ArchiveModel::entryForIndex( const QModelIndex &index )
00332 {
00333     if ( index.isValid() )
00334     {
00335         ArchiveNode *item = static_cast<ArchiveNode*>( index.internalPointer() );
00336         Q_ASSERT( item );
00337         return item->entry();
00338     }
00339     return ArchiveEntry();
00340 }
00341 
00342 int ArchiveModel::childCount( const QModelIndex &index )
00343 {
00344     if ( index.isValid() )
00345     {
00346         ArchiveNode *item = static_cast<ArchiveNode*>( index.internalPointer() );
00347         Q_ASSERT( item );
00348         if ( item->isDir() )
00349         {
00350             return static_cast<ArchiveDirNode*>( item )->entries().count();
00351         }
00352         return 0;
00353     }
00354     return -1;
00355 }
00356 
00357 int ArchiveModel::rowCount( const QModelIndex &parent ) const
00358 {
00359     if ( parent.column() <= 0 )
00360     {
00361         ArchiveNode *parentNode = parent.isValid()? static_cast<ArchiveNode*>( parent.internalPointer() ) : m_rootNode;
00362 
00363         if ( parentNode && parentNode->isDir() )
00364         {
00365             return static_cast<ArchiveDirNode*>( parentNode )->entries().count();
00366         }
00367     }
00368     return 0;
00369 }
00370 
00371 int ArchiveModel::columnCount( const QModelIndex &parent ) const
00372 {
00373     return m_showColumns.size();
00374     if ( parent.isValid() )
00375     {
00376         return static_cast<ArchiveNode*>( parent.internalPointer() )->entry().size();
00377     }
00378 }
00379 
00380 Qt::DropActions ArchiveModel::supportedDropActions () const
00381 {
00382     return Qt::CopyAction | Qt::MoveAction;
00383 }
00384 
00385 QStringList ArchiveModel::mimeTypes () const
00386 {
00387     QStringList types;
00388 
00389 
00390     types << QString("text/uri-list")
00391         << QString( "text/plain" )
00392         << QString( "text/x-moz-url" )
00393         << QString( "application/x-kde-urilist" );
00394 
00395     types << "application/x-kde-extractdrag";
00396 
00397     return types;
00398 }
00399 
00400 QMimeData * ArchiveModel::mimeData ( const QModelIndexList & indexes ) const
00401 {
00402 
00403     kDebug (1601) ;
00404     //prepare the fallback kio_slave filenames
00405     QStringList files;
00406     bool noFallback = false;
00407 
00408     QString archiveName = m_archive->fileName();
00409     QString ext = QFileInfo(archiveName).suffix().toUpper();
00410     if (ext == "TAR") {
00411         archiveName.prepend("tar:");
00412     } else if (ext == "ZIP") {
00413         archiveName.prepend("zip:");
00414     } else if (archiveName.right(6).toUpper() == "TAR.GZ") {
00415             archiveName.prepend("tar:");
00416     } else
00417         noFallback = true;
00418 
00419     if (archiveName.right(1) != "/") {
00420         archiveName.append("/");
00421     }
00422 
00423     //Populate the internal list of files
00424     foreach ( const QModelIndex &index, indexes ) {
00425         
00426         //to limit only one index per row
00427         if (index.column() != 0) continue;
00428 
00429         QString file = archiveName + static_cast<ArchiveNode*>( index.internalPointer() )->entry()[ InternalID ].toString();
00430         files << file;
00431     }
00432 
00433     KUrl::List kiolist(files);
00434 
00435     //prepare the dbus-based drag/drop mimedata
00436     QMimeData *data = new QMimeData();
00437     data->setData("application/x-kde-dndextract", 
00438             QDBusConnection::sessionBus().baseService().toUtf8()
00439             );
00440 
00441 
00442     if (!noFallback)
00443         kiolist.populateMimeData(data);
00444     return data;
00445 }
00446 
00447 bool ArchiveModel::dropMimeData ( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent )
00448 {
00449     Q_UNUSED( action );
00450     Q_UNUSED(parent  );
00451     Q_UNUSED( column );
00452     Q_UNUSED( row );
00453 
00454     if (!data->hasUrls())
00455         return false;
00456 
00457     QStringList paths;
00458     foreach(const QUrl &url, data->urls()) {
00459         paths << url.path();
00460     }
00461 
00462     emit droppedFiles(paths);
00463 
00464     return true;
00465 }
00466 
00467 ArchiveDirNode* ArchiveModel::parentFor( const ArchiveEntry& entry )
00468 {
00469     QStringList pieces = entry[ FileName ].toString().split( '/', QString::SkipEmptyParts );
00470     pieces.removeLast();
00471 
00472     ArchiveDirNode *parent = m_rootNode;
00473 
00474     foreach( const QString &piece, pieces )
00475     {
00476         ArchiveNode *node = parent->find( piece );
00477         if ( !node )
00478         {
00479             ArchiveEntry e;
00480             e[ FileName ] = parent->entry()[ FileName ].toString() + '/' + piece;
00481             e[ IsDirectory ] = true;
00482             node = new ArchiveDirNode( parent, e );
00483             insertNode( node );
00484         }
00485         if ( !node->isDir() )
00486         {
00487             ArchiveEntry e( node->entry() );
00488             node = new ArchiveDirNode( parent, e );
00489             //Maybe we have both a file and a directory of the same name
00490             // We avoid removing previous entries unless necessary
00491             insertNode( node );
00492         }
00493         parent = static_cast<ArchiveDirNode*>( node );
00494     }
00495 
00496     return parent;
00497 }
00498 QModelIndex ArchiveModel::indexForNode( ArchiveNode *node )
00499 {
00500     Q_ASSERT( node );
00501     if ( node != m_rootNode )
00502     {
00503         Q_ASSERT( node->parent() );
00504         Q_ASSERT( node->parent()->isDir() );
00505         return createIndex( node->row(), 0, node );
00506     }
00507     return QModelIndex();
00508 }
00509 
00510 void ArchiveModel::slotEntryRemoved( const QString & path )
00511 {
00512     // TODO: Do something
00513     kDebug (1601) << "Removed node at path " << path;
00514     ArchiveNode *entry = m_rootNode->findByPath( path );
00515     if ( entry )
00516     {
00517         ArchiveDirNode *parent = entry->parent();
00518         QModelIndex index = indexForNode( entry );
00519 
00520         beginRemoveRows( indexForNode( parent ), entry->row(), entry->row() );
00521 
00522         delete parent->entries()[ entry->row() ];
00523         parent->entries()[ entry->row() ] = 0;
00524 
00525         endRemoveRows();
00526     } else
00527         kDebug (1601) << "Did not find the removed node";
00528 }
00529 
00530 void ArchiveModel::slotUserQuery(Query *query)
00531 {
00532     query->execute();
00533 }
00534 
00535 void ArchiveModel::slotNewEntry( const ArchiveEntry& entry )
00536 {
00537     kDebug (1601) << entry; 
00538 
00539     //if there are no addidional columns registered, then have a look at the
00540     //entry and populate some
00541     if (m_showColumns.isEmpty()) {
00542 
00543         //these are the columns we are interested in showing in the display
00544         static const QList<int> columnsForDisplay = 
00545             QList<int>()
00546             << FileName
00547             << Size
00548             << CompressedSize
00549             << Permissions
00550             << Owner
00551             << Group
00552             << Ratio
00553             << CRC
00554             << Method
00555             << Version
00556             << Timestamp
00557             << Comment;
00558 
00559         QList<int> toInsert;
00560 
00561         foreach(int column, columnsForDisplay) {
00562             if (entry.contains(column))
00563                 toInsert << column;
00564         }
00565         beginInsertColumns(QModelIndex(), 0, toInsert.size() - 1 );
00566         m_showColumns << toInsert;
00567         endInsertColumns();
00568 
00569         kDebug( 1601 ) << "Show columns detected: " << m_showColumns;
00570 
00571     }
00572 
00574     if (m_rootNode){
00575         ArchiveNode *existing = m_rootNode->findByPath( entry[ FileName ].toString() );
00576         if ( existing ) {
00577             kDebug (1601) << "Refreshing entry for" << entry[FileName].toString(); 
00578             //TODO: benchmark whether it's a bad idea to reset the entry here.
00579             existing->setEntry(entry);
00580             return;
00581         }
00582     }
00583 
00585     ArchiveDirNode *parent  = parentFor( entry ); 
00586     
00588     QString name = entry[ FileName ].toString().split( '/', QString::SkipEmptyParts ).last();
00589     ArchiveNode *node = parent->find( name );
00590     if ( node )
00591     {
00592         node->setEntry( entry );
00593     }
00594     else
00595     {
00596         if ( entry[ FileName ].toString().endsWith( '/' ) || ( entry.contains( IsDirectory ) && entry[ IsDirectory ].toBool() ) )
00597         {
00598             node = new ArchiveDirNode( parent, entry );
00599         }
00600         else
00601         {
00602             node = new ArchiveNode( parent, entry );
00603         }
00604         insertNode( node );
00605     }
00606 }
00607 
00608 void ArchiveModel::insertNode( ArchiveNode *node )
00609 {
00610     Q_ASSERT(node);
00611     ArchiveDirNode *parent = node->parent();
00612     Q_ASSERT(parent);
00613     beginInsertRows( indexForNode( parent ), parent->entries().count(), parent->entries().count() );
00614     parent->entries().append( node );
00615     endInsertRows();
00616 }
00617 
00618 void ArchiveModel::setArchive( Kerfuffle::Archive *archive )
00619 {
00620     delete m_archive;
00621     m_archive = archive;
00622     m_rootNode->clear();
00623 
00624     // TODO: make sure if it's ok to not have calls to beginRemoveColumns here
00625     m_showColumns.clear();
00626 
00627     if ( m_archive )
00628     {
00629         Kerfuffle::ListJob *job = m_archive->list(); // TODO: call "open" or "create"?
00630 
00631         connect( job, SIGNAL( newEntry( const ArchiveEntry& ) ),
00632              this, SLOT( slotNewEntry( const ArchiveEntry& ) ) );
00633 
00634         connect( job, SIGNAL( result( KJob * ) ),
00635                  this, SIGNAL( loadingFinished(KJob *) ) );
00636 
00637         if ( m_jobTracker )
00638         {
00639             m_jobTracker->registerJob( job );
00640         }
00641 
00642         emit loadingStarted();
00643         job->start();
00644     }
00645     reset();
00646 }
00647 
00648 ExtractJob* ArchiveModel::extractFile( const QVariant& fileName, const QString & destinationDir, Archive::CopyFlags flags ) const
00649 {
00650     QList<QVariant> files;
00651     files << fileName;
00652     return extractFiles( files, destinationDir, flags );
00653 }
00654 
00655 ExtractJob* ArchiveModel::extractFiles( const QList<QVariant>& files, const QString & destinationDir, Kerfuffle::Archive::CopyFlags flags ) const
00656 {
00657     Q_ASSERT( m_archive );
00658     ExtractJob *newJob = m_archive->copyFiles( files, destinationDir, flags );
00659     connect(newJob, SIGNAL(userQuery(Query*)),
00660             this, SLOT(slotUserQuery(Query*)));
00661     return newJob;
00662 }
00663 
00664 AddJob* ArchiveModel::addFiles( const QStringList & paths )
00665 {
00666     Q_ASSERT( m_archive );
00667 
00668     if ( !m_archive->isReadOnly())
00669     {
00670         AddJob *job = m_archive->addFiles( paths );
00671         m_jobTracker->registerJob( job );
00672         connect( job, SIGNAL( newEntry( const ArchiveEntry& ) ),
00673             this, SLOT( slotNewEntry( const ArchiveEntry& ) ) );
00674         connect(job, SIGNAL(userQuery(Query*)),
00675                 this, SLOT(slotUserQuery(Query*)));
00676 
00677 
00678         return job;
00679     }
00680     return 0;
00681 }
00682 
00683 DeleteJob* ArchiveModel::deleteFiles( const QList<QVariant> & files )
00684 {
00685     Q_ASSERT( m_archive );
00686     if ( !m_archive->isReadOnly() )
00687     {
00688         DeleteJob *job = m_archive->deleteFiles( files );
00689         m_jobTracker->registerJob( job );
00690         connect( job, SIGNAL( entryRemoved( const QString & ) ),
00691                  this, SLOT( slotEntryRemoved( const QString & ) ) );
00692 
00693         connect(job, SIGNAL(userQuery(Query*)),
00694                 this, SLOT(slotUserQuery(Query*)));
00695         return job;
00696     }
00697     return 0;
00698 }

ark

Skip menu "ark"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members

kdeutils

Skip menu "kdeutils"
  • ark
  • kcalc
  • kcharselect
  • kdessh
  • kdf
  • kfloppy
  • kgpg
  • ktimer
  • kwallet
  • okteta
  • printer-applet
  • superkaramba
  • sweeper
Generated for kdeutils by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal