• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KIO

kdirmodel.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002    Copyright (C) 2006 David Faure <faure@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017    Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "kdirmodel.h"
00021 #include "kdirlister.h"
00022 #include "kfileitem.h"
00023 #include <kdatetime.h>
00024 #include <kicon.h>
00025 #include <klocale.h>
00026 #include <kglobal.h>
00027 #include <kio/copyjob.h>
00028 #include <kio/jobuidelegate.h>
00029 #include <kurl.h>
00030 #include <kdebug.h>
00031 #include <QMimeData>
00032 #include <QFile>
00033 #include <QFileInfo>
00034 #include <QDir>
00035 #include <sys/types.h>
00036 #include <dirent.h>
00037 
00038 class KDirModelNode;
00039 class KDirModelDirNode;
00040 
00041 // We create our own tree behind the scenes to have fast lookup from an item to its parent,
00042 // and also to get the children of an item fast.
00043 class KDirModelNode
00044 {
00045 public:
00046     KDirModelNode( KDirModelDirNode* parent, const KFileItem& item ) :
00047         m_item(item),
00048         m_parent(parent),
00049         m_preview()
00050     {
00051     }
00052     //KUrl url() const { return m_item->url(); }
00053 
00054     // m_item is KFileItem() for the root item
00055     const KFileItem& item() const { return m_item; }
00056     void setItem(const KFileItem& item) { m_item = item; }
00057     KDirModelDirNode* parent() const { return m_parent; }
00058     // linear search
00059     int rowNumber() const;
00060     QIcon preview() const { return m_preview; }
00061     void addPreview( const QPixmap& pix ) { m_preview.addPixmap(pix); }
00062     void setPreview( const QIcon& icn ) { m_preview = icn; }
00063 
00064 private:
00065     KFileItem m_item;
00066     KDirModelDirNode* const m_parent;
00067     QIcon m_preview;
00068 };
00069 
00070 // Specialization for directory nodes
00071 class KDirModelDirNode : public KDirModelNode
00072 {
00073 public:
00074     KDirModelDirNode( KDirModelDirNode* parent, const KFileItem& item)
00075         : KDirModelNode( parent, item),
00076           m_childNodes(),
00077           m_childCount(KDirModel::ChildCountUnknown),
00078           m_populated(false)
00079     {}
00080     ~KDirModelDirNode() {
00081         qDeleteAll(m_childNodes);
00082     }
00083     QList<KDirModelNode *> m_childNodes;
00084 
00085     // If we listed the directory, the child count is known. Otherwise it can be set via setChildCount.
00086     int childCount() const { return m_childNodes.isEmpty() ? m_childCount : m_childNodes.count(); }
00087     void setChildCount(int count) { m_childCount = count; }
00088     bool isPopulated() const { return m_populated; }
00089     void setPopulated( bool populated ) { m_populated = populated; }
00090 
00091 private:
00092     int m_childCount:31;
00093     bool m_populated:1;
00094 };
00095 
00096 int KDirModelNode::rowNumber() const
00097 {
00098     if (!m_parent) return 0;
00099     return m_parent->m_childNodes.indexOf(const_cast<KDirModelNode*>(this));
00100 }
00101 
00103 
00104 class KDirModelPrivate
00105 {
00106 public:
00107     KDirModelPrivate( KDirModel* model )
00108         : q(model), m_dirLister(0),
00109           m_rootNode(new KDirModelDirNode(0, KFileItem())),
00110           m_dropsAllowed(KDirModel::NoDrops)
00111     {
00112     }
00113     ~KDirModelPrivate() {
00114         delete m_rootNode;
00115     }
00116 
00117     void _k_slotNewItems(const KFileItemList&);
00118     void _k_slotDeleteItem(const KFileItem&);
00119     void _k_slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >&);
00120     void _k_slotClear();
00121 
00122     void clear() {
00123         delete m_rootNode;
00124         m_rootNode = new KDirModelDirNode(0, KFileItem());
00125     }
00126     // Find the row number and node for a given url.
00127     // This has to drill down from the root node.
00128     // Returns (0,0) if there is no node for this url.
00129     // If returnLastParent is set, then return the last known parent if there is no node for this url
00130     // (special case for expandToUrl)
00131     QPair<int /*row*/, KDirModelNode*> nodeForUrl(const KUrl& url, bool returnLastParent = false) const;
00132     KDirModelNode* nodeForIndex(const QModelIndex& index) const;
00133     QModelIndex indexForNode(KDirModelNode* node, int rowNumber = -1 /*unknown*/) const;
00134     bool isDir(KDirModelNode* node) const {
00135         return (node == m_rootNode) || node->item().isDir();
00136     }
00137 
00138     KDirModel* q;
00139     KDirLister* m_dirLister;
00140     KDirModelDirNode* m_rootNode;
00141     KDirModel::DropsAllowed m_dropsAllowed;
00142     // key = current known parent node (always a KDirModelDirNode but KDirModelNode is more convenient),
00143     // value = final url[s] being fetched
00144     QMap<KDirModelNode*, KUrl::List> m_urlsBeingFetched;
00145 };
00146 
00147 // If we want to support arbitrary trees like "home:/ as a child of system:/" then,
00148 // we need to get the parent KFileItem in _k_slotNewItems, and then we can use a QHash<KFileItem,KDirModelNode*> cache.
00149 // (well there isn't a parent kfileitem, rather a parent url... hmm, back to square one with hashes-of-urls..)
00150 // For now we'll assume "child url = parent url + filename"
00151 QPair<int /*row*/, KDirModelNode*> KDirModelPrivate::nodeForUrl(const KUrl& _url, bool returnLastParent) const // O(n*m)
00152 {
00153     KUrl url(_url);
00154     url.adjustPath(KUrl::RemoveTrailingSlash);
00155 
00156     //kDebug(7008) << url;
00157     KUrl nodeUrl = m_dirLister->url();
00158     // For a URL without a path, like "applications:" or "settings://",
00159     // we want to resolve here "no path" to "/ assumed".
00160     // We don't do it before (e.g. in KDirLister) because we want to
00161     // give the ioslave a chance for a redirect (e.g. kio_ftp redirects "no path"
00162     // to the user's home dir)
00163     if (nodeUrl.path().isEmpty())
00164         nodeUrl.setPath("/");
00165 
00166     if (url == nodeUrl)
00167         return qMakePair(0, static_cast<KDirModelNode *>(m_rootNode));
00168 
00169     const QString pathStr = url.path();
00170     KDirModelDirNode* dirNode = m_rootNode;
00171 
00172     if ( !pathStr.startsWith(nodeUrl.path()) ) {
00173         return qMakePair(0, static_cast<KDirModelNode*>(0));
00174     }
00175 
00176     for (;;) {
00177         Q_ASSERT( pathStr.startsWith(nodeUrl.path()) );
00178         bool foundChild = false;
00179         QList<KDirModelNode *>::const_iterator it = dirNode->m_childNodes.begin();
00180         const QList<KDirModelNode *>::const_iterator end = dirNode->m_childNodes.end();
00181         int row = 0;
00182         for ( ; it != end ; ++it, ++row ) {
00183             const KUrl u = (*it)->item().url();
00184             if ( u == url ) {
00185                 //kDebug(7008) << "Found! " << u;
00186                 return qMakePair(row, *it);
00187             }
00188             // This used to be urlStr.startsWith(u.url()+'/'), but KUrl::url() is a slow operation.
00189             if ( (url.protocol() == u.protocol()) && (pathStr.startsWith(u.path()+'/')) ) {
00190                 //kDebug(7008) << "going into " << node->item().url();
00191                 Q_ASSERT( isDir(*it) );
00192                 dirNode = static_cast<KDirModelDirNode *>( *it );
00193                 foundChild = true;
00194                 break;
00195             }
00196         }
00197         if (!foundChild) {
00198             //kDebug(7008) << "child equal or starting with " << url << " not found";
00199             if (returnLastParent)
00200                 return qMakePair(-1 /*not implemented*/, static_cast<KDirModelNode*>(dirNode));
00201             else
00202                 return qMakePair(0, static_cast<KDirModelNode*>(0));
00203         }
00204         nodeUrl = dirNode->item().url();
00205         //kDebug(7008) << " " << nodeUrl;
00206     }
00207     // NOTREACHED
00208     //return qMakePair(0, static_cast<KDirModelNode*>(0));
00209 }
00210 
00211 // node -> index. If rowNumber is set (or node is root): O(1). Otherwise: O(n).
00212 QModelIndex KDirModelPrivate::indexForNode(KDirModelNode* node, int rowNumber) const
00213 {
00214     if (node == m_rootNode)
00215         return QModelIndex();
00216 
00217     Q_ASSERT(node->parent());
00218     return q->createIndex(rowNumber == -1 ? node->rowNumber() : rowNumber, 0, node);
00219 }
00220 
00221 // index -> node. O(1)
00222 KDirModelNode* KDirModelPrivate::nodeForIndex(const QModelIndex& index) const
00223 {
00224     return index.isValid()
00225         ? static_cast<KDirModelNode*>(index.internalPointer())
00226         : m_rootNode;
00227 }
00228 
00229 // We don't use QHash<KUrl,...> anymore, it's too slow.
00230 // Idea from George, to make QHash<KUrl,...> fast: - cache hash value into QUrl or KUrl
00231 // This also helps making operator== fast [which means operator== has to call qHash if cached value isn't there]
00232 // But it means invalidating the cached hash value when the url is modified,
00233 // so it can't be done in the current KUrl due to the public inheritance from QUrl.
00234 
00235 
00236 /*
00237  * This model wraps the data held by KDirLister.
00238  *
00239  * The internal pointer of the QModelIndex for a given file is the node for that file in our own tree.
00240  * E.g. index(2,0) returns a QModelIndex with row=2 internalPointer=<KDirModelNode for the 3rd child of the root>
00241  *
00242  * Invalid parent index means root of the tree, m_rootNode
00243  */
00244 
00245 #ifndef NDEBUG
00246 static QString debugIndex(const QModelIndex& index)
00247 {
00248     QString str;
00249     if (!index.isValid())
00250         str = "[invalid index, i.e. root]";
00251     else {
00252         KDirModelNode* node = static_cast<KDirModelNode*>(index.internalPointer());
00253         str = "[index for " + node->item().url().pathOrUrl();
00254         if (index.column() > 0)
00255             str += ", column " + QString::number(index.column());
00256         str += ']';
00257     }
00258     return str;
00259 }
00260 #endif
00261 
00262 KDirModel::KDirModel(QObject* parent)
00263     : QAbstractItemModel(parent),
00264       d(new KDirModelPrivate(this))
00265 {
00266     setDirLister(new KDirLister(this));
00267 }
00268 
00269 KDirModel::~KDirModel()
00270 {
00271     delete d;
00272 }
00273 
00274 void KDirModel::setDirLister(KDirLister* dirLister)
00275 {
00276     if (d->m_dirLister) {
00277         d->clear();
00278         delete d->m_dirLister;
00279     }
00280     d->m_dirLister = dirLister;
00281     d->m_dirLister->setParent(this);
00282     connect( d->m_dirLister, SIGNAL(newItems(KFileItemList)),
00283              this, SLOT(_k_slotNewItems(KFileItemList)) );
00284     connect( d->m_dirLister, SIGNAL(deleteItem(KFileItem)),
00285              this, SLOT(_k_slotDeleteItem(KFileItem)) );
00286     connect( d->m_dirLister, SIGNAL(refreshItems(QList<QPair<KFileItem, KFileItem> >)),
00287              this, SLOT(_k_slotRefreshItems(QList<QPair<KFileItem, KFileItem> >)) );
00288     connect( d->m_dirLister, SIGNAL(clear()),
00289              this, SLOT(_k_slotClear()) );
00290 }
00291 
00292 KDirLister* KDirModel::dirLister() const
00293 {
00294     return d->m_dirLister;
00295 }
00296 
00297 void KDirModelPrivate::_k_slotNewItems(const KFileItemList& items)
00298 {
00299     // Find parent item - it's the same for all the items
00300     // TODO (if Michael Brade agrees): add parent url to the newItems signal
00301     // This way we can finally support properly trees where the urls are using different protocols.
00302     KUrl dir( items.first().url().upUrl() );
00303     dir.adjustPath(KUrl::RemoveTrailingSlash);
00304 
00305     //kDebug(7008) << "dir=" << dir;
00306 
00307     const QPair<int, KDirModelNode*> result = nodeForUrl(dir); // O(n*m)
00308     Q_ASSERT(result.second); // Are you calling KDirLister::openUrl(url,true,false)? Please use expandToUrl() instead.
00309     Q_ASSERT(isDir(result.second));
00310     KDirModelDirNode* dirNode = static_cast<KDirModelDirNode *>(result.second);
00311 
00312     const QModelIndex index = indexForNode(dirNode, result.first); // O(1)
00313     const int newItemsCount = items.count();
00314     const int newRowCount = dirNode->m_childNodes.count() + newItemsCount;
00315 #ifndef NDEBUG // debugIndex only defined in debug mode
00316     kDebug(7008) << items.count() << "in" << dir
00317              << "index=" << debugIndex(index) << "newRowCount=" << newRowCount;
00318 #endif
00319     q->beginInsertRows( index, newRowCount - newItemsCount, newRowCount - 1 ); // parent, first, last
00320 
00321     const KUrl::List urlsBeingFetched = m_urlsBeingFetched.value(dirNode);
00322     //kDebug(7008) << "urlsBeingFetched for dir" << dirNode << dir << ":" << urlsBeingFetched;
00323 
00324     QList<QModelIndex> emitExpandFor;
00325 
00326     KFileItemList::const_iterator it = items.begin();
00327     KFileItemList::const_iterator end = items.end();
00328     for ( ; it != end ; ++it ) {
00329         const bool isDir = it->isDir();
00330         KDirModelNode* node = isDir
00331                               ? new KDirModelDirNode( dirNode, *it )
00332                               : new KDirModelNode( dirNode, *it );
00333         dirNode->m_childNodes.append(node);
00334 
00335         if (isDir && !urlsBeingFetched.isEmpty()) {
00336             const KUrl dirUrl = it->url();
00337             foreach(const KUrl& urlFetched, urlsBeingFetched) {
00338                 if (dirUrl.isParentOf(urlFetched)) {
00339                     //kDebug(7008) << "Listing found" << dirUrl << "which is a parent of fetched url" << urlFetched;
00340                     const QModelIndex parentIndex = indexForNode(node, dirNode->m_childNodes.count()-1);
00341                     Q_ASSERT(parentIndex.isValid());
00342                     emitExpandFor.append(parentIndex);
00343                     if (dirUrl != urlFetched) {
00344                         q->fetchMore(parentIndex);
00345                         m_urlsBeingFetched[node].append(urlFetched);
00346                     }
00347                 }
00348             }
00349         }
00350     }
00351 
00352     m_urlsBeingFetched.remove(dirNode);
00353 
00354     q->endInsertRows();
00355 
00356     // Emit expand signal after rowsInserted signal has been emitted,
00357     // so that any proxy model will have updated its mapping already
00358     Q_FOREACH(const QModelIndex& idx, emitExpandFor) {
00359         emit q->expand(idx);
00360     }
00361 }
00362 
00363 void KDirModelPrivate::_k_slotDeleteItem(const KFileItem& item)
00364 {
00365     //KUrl dir( item->url().upUrl() );
00366     //dir.adjustPath(KUrl::RemoveTrailingSlash);
00367 
00368     Q_ASSERT(!item.isNull());
00369     const QPair<int, KDirModelNode*> result = nodeForUrl(item.url()); // O(n*m)
00370     const int rowNumber = result.first;
00371     KDirModelNode* node = result.second;
00372     if (!node)
00373         return;
00374 
00375     KDirModelDirNode* dirNode = node->parent();
00376     if (!dirNode)
00377         return;
00378 
00379     QModelIndex parentIndex = indexForNode(dirNode); // O(n)
00380     q->beginRemoveRows( parentIndex, rowNumber, rowNumber );
00381     dirNode->m_childNodes.removeAt(rowNumber);
00382     q->endRemoveRows();
00383 }
00384 
00385 void KDirModelPrivate::_k_slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >& items)
00386 {
00387     QModelIndex topLeft, bottomRight;
00388 
00389     // Solution 1: we could emit dataChanged for one row (if items.size()==1) or all rows
00390     // Solution 2: more fine-grained, actually figure out the beginning and end rows.
00391     for ( QList<QPair<KFileItem, KFileItem> >::const_iterator fit = items.begin(), fend = items.end() ; fit != fend ; ++fit ) {
00392         const QModelIndex index = q->indexForUrl( fit->first.url() ); // O(n*m); maybe we could look up to the parent only once
00393         nodeForIndex(index)->setItem(fit->second);
00394         if (!topLeft.isValid() || index.row() < topLeft.row()) {
00395             topLeft = index;
00396         }
00397         if (!bottomRight.isValid() || index.row() > bottomRight.row()) {
00398             bottomRight = index;
00399         }
00400     }
00401 #ifndef NDEBUG // debugIndex only defined in debug mode
00402     kDebug(7008) << "slotRefreshItems: dataChanged(" << debugIndex(topLeft) << " - " << debugIndex(bottomRight);
00403 #endif
00404     bottomRight = bottomRight.sibling(bottomRight.row(), q->columnCount(QModelIndex())-1);
00405     emit q->dataChanged(topLeft, bottomRight);
00406 }
00407 
00408 void KDirModelPrivate::_k_slotClear()
00409 {
00410     const int numRows = m_rootNode->m_childNodes.count();
00411     q->beginRemoveRows( QModelIndex(), 0, numRows );
00412     q->endRemoveRows();
00413 
00414     //emit layoutAboutToBeChanged();
00415     clear();
00416     //emit layoutChanged();
00417 }
00418 
00419 void KDirModel::itemChanged( const QModelIndex& index )
00420 {
00421     emit dataChanged(index, index);
00422 }
00423 
00424 int KDirModel::columnCount( const QModelIndex & ) const
00425 {
00426     return ColumnCount;
00427 }
00428 
00429 QVariant KDirModel::data( const QModelIndex & index, int role ) const
00430 {
00431     if (index.isValid()) {
00432         KDirModelNode* node = static_cast<KDirModelNode*>(index.internalPointer());
00433         const KFileItem& item( node->item() );
00434         switch (role) {
00435         case Qt::DisplayRole:
00436             switch (index.column()) {
00437             case Name:
00438                 return item.text();
00439             case Size:
00440                 //
00441                 //return KIO::convertSize(item->size());
00442                 // Default to "file size in bytes" like in kde3's filedialog
00443                 return KGlobal::locale()->formatNumber(item.size(), 0);
00444             case ModifiedTime: {
00445                 KDateTime dt = item.time(KFileItem::ModificationTime);
00446                 return KGlobal::locale()->formatDateTime(dt);
00447             }
00448             case Permissions:
00449                 return item.permissionsString();
00450             case Owner:
00451                 return item.user();
00452             case Group:
00453                 return item.group();
00454             case Type:
00455                 return item.mimeComment();
00456             }
00457             break;
00458         case Qt::EditRole:
00459             switch (index.column()) {
00460             case Name:
00461                 return item.text();
00462             }
00463             break;
00464         case Qt::DecorationRole:
00465             if (index.column() == Name) {
00466                 if (!node->preview().isNull()) {
00467                     //kDebug(7008) << item->url() << " preview found";
00468                     return node->preview();
00469                 }
00470                 Q_ASSERT(!item.isNull());
00471                 //kDebug(7008) << item->url() << " overlays=" << item->overlays();
00472                 return KIcon(item.iconName(), 0, item.overlays());
00473             }
00474             break;
00475         case Qt::TextAlignmentRole:
00476             if (index.column() == Size) {
00477                 // use a right alignment for L2R and R2L languages
00478                 const Qt::Alignment alignment = Qt::AlignRight | Qt::AlignVCenter;
00479                 return int(alignment);
00480             }
00481             break;
00482         case FileItemRole:
00483             return QVariant::fromValue(item);
00484         case ChildCountRole:
00485             if (!item.isDir())
00486                 return ChildCountUnknown;
00487             else {
00488                 KDirModelDirNode* dirNode = static_cast<KDirModelDirNode *>(node);
00489                 int count = dirNode->childCount();
00490                 if (count == ChildCountUnknown && item.isReadable()) {
00491                     const QString path = item.localPath();
00492                     if (!path.isEmpty()) {
00493 #if 0 // slow
00494                         QDir dir(path);
00495                         count = dir.entryList(QDir::AllEntries|QDir::NoDotAndDotDot|QDir::System).count();
00496 #else
00497                         DIR* dir = ::opendir(QFile::encodeName(path));
00498                         if (dir) {
00499                             count = 0;
00500                             struct dirent *dirEntry = 0;
00501                             while ((dirEntry = ::readdir(dir))) {
00502                                 if (dirEntry->d_name[0] == '.') {
00503                                     if (dirEntry->d_name[1] == '\0') // skip "."
00504                                         continue;
00505                                     if (dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0') // skip ".."
00506                                         continue;
00507                                 }
00508                                 ++count;
00509                             }
00510                             ::closedir(dir);
00511                         }
00512 #endif
00513                         //kDebug(7008) << "child count for " << path << ":" << count;
00514                         dirNode->setChildCount(count);
00515                     }
00516                 }
00517                 return count;
00518             }
00519         }
00520     }
00521     return QVariant();
00522 }
00523 
00524 void KDirModel::sort( int column, Qt::SortOrder order )
00525 {
00526     // Not implemented - we should probably use QSortFilterProxyModel instead.
00527     return QAbstractItemModel::sort(column, order);
00528 }
00529 
00530 bool KDirModel::setData( const QModelIndex & index, const QVariant & value, int role )
00531 {
00532     switch (role) {
00533     case Qt::EditRole:
00534         if (index.column() == Name && value.type() == QVariant::String) {
00535             Q_ASSERT(index.isValid());
00536             KDirModelNode* node = static_cast<KDirModelNode*>(index.internalPointer());
00537             const KFileItem& item = node->item();
00538             const QString newName = value.toString();
00539             if (newName.isEmpty() || newName == item.text())
00540                 return true;
00541             KUrl newurl(item.url());
00542             newurl.setPath(newurl.directory(KUrl::AppendTrailingSlash) + newName);
00543             KIO::Job * job = KIO::moveAs(item.url(), newurl, newurl.isLocalFile() ? KIO::HideProgressInfo : KIO::DefaultFlags);
00544             job->ui()->setAutoErrorHandlingEnabled(true);
00545             // TODO undo handling
00546             return true;
00547         }
00548         break;
00549     case Qt::DecorationRole:
00550         if (index.column() == Name) {
00551             Q_ASSERT(index.isValid());
00552             // Set new pixmap - e.g. preview
00553             KDirModelNode* node = static_cast<KDirModelNode*>(index.internalPointer());
00554             //kDebug(7008) << "setting icon for " << node->item()->url();
00555             Q_ASSERT(node);
00556             if (value.type() == QVariant::Icon) {
00557                 const QIcon icon(qvariant_cast<QIcon>(value));
00558                 Q_ASSERT(!icon.isNull());
00559                 node->setPreview(icon);
00560             } else if (value.type() == QVariant::Pixmap) {
00561                 node->addPreview(qvariant_cast<QPixmap>(value));
00562             }
00563             emit dataChanged(index, index);
00564             return true;
00565         }
00566         break;
00567     default:
00568         break;
00569     }
00570     return false;
00571 }
00572 
00573 int KDirModel::rowCount( const QModelIndex & parent ) const
00574 {
00575     KDirModelDirNode* parentNode = static_cast<KDirModelDirNode *>(d->nodeForIndex(parent));
00576     Q_ASSERT(parentNode);
00577     const int count = parentNode->m_childNodes.count();
00578     //kDebug(7008) << "rowCount for " << parentUrl << ": " << count;;
00579     return count;
00580 }
00581 
00582 // sibling() calls parent() and isn't virtual! So parent() should be fast...
00583 QModelIndex KDirModel::parent( const QModelIndex & index ) const
00584 {
00585     if (!index.isValid())
00586         return QModelIndex();
00587     KDirModelNode* childNode = static_cast<KDirModelNode*>(index.internalPointer());
00588     Q_ASSERT(childNode);
00589     KDirModelNode* parentNode = childNode->parent();
00590     Q_ASSERT(parentNode);
00591     return d->indexForNode(parentNode); // O(n)
00592 }
00593 
00594 QStringList KDirModel::mimeTypes( ) const
00595 {
00596     return QStringList() << QLatin1String("text/uri-list")
00597                          << QLatin1String( "application/x-kde-cutselection" ) // TODO
00598                          << QLatin1String( "text/plain" )
00599                          << QLatin1String( "application/x-kde-urilist" );
00600 }
00601 
00602 QMimeData * KDirModel::mimeData( const QModelIndexList & indexes ) const
00603 {
00604     KUrl::List urls;
00605     foreach ( const QModelIndex &index, indexes ) {
00606         urls << d->nodeForIndex( index )->item().url();
00607     }
00608     QMimeData *data = new QMimeData();
00609     urls.populateMimeData( data );
00610     return data;
00611 }
00612 
00613 // Public API; not much point in calling it internally
00614 KFileItem KDirModel::itemForIndex( const QModelIndex& index ) const
00615 {
00616     if (!index.isValid()) {
00617         return d->m_dirLister->rootItem();
00618     } else {
00619         return static_cast<KDirModelNode*>(index.internalPointer())->item();
00620     }
00621 }
00622 
00623 QModelIndex KDirModel::indexForItem( const KFileItem* item ) const
00624 {
00625     // Note that we can only use the URL here, not the pointer.
00626     // KFileItems can be copied.
00627     return indexForUrl(item->url()); // O(n*m)
00628 }
00629 
00630 QModelIndex KDirModel::indexForItem( const KFileItem& item ) const
00631 {
00632     // Note that we can only use the URL here, not the pointer.
00633     // KFileItems can be copied.
00634     return indexForUrl(item.url()); // O(n*m)
00635 }
00636 
00637 // url -> index. O(n*m)
00638 QModelIndex KDirModel::indexForUrl(const KUrl& url) const
00639 {
00640     const QPair<int, KDirModelNode*> result = d->nodeForUrl(url); // O(n*m) (m is the depth from the root)
00641     if (!result.second) {
00642         kDebug(7007) << url << "not found";
00643         return QModelIndex();
00644     }
00645     return d->indexForNode(result.second, result.first); // O(1)
00646 }
00647 
00648 QModelIndex KDirModel::index( int row, int column, const QModelIndex & parent ) const
00649 {
00650     KDirModelNode* parentNode = d->nodeForIndex(parent); // O(1)
00651     Q_ASSERT(parentNode);
00652     Q_ASSERT(d->isDir(parentNode));
00653     KDirModelNode* childNode = static_cast<KDirModelDirNode *>(parentNode)->m_childNodes.value(row); // O(1)
00654     if (childNode)
00655         return createIndex(row, column, childNode);
00656     else
00657         return QModelIndex();
00658 }
00659 
00660 QVariant KDirModel::headerData( int section, Qt::Orientation orientation, int role ) const
00661 {
00662     Q_UNUSED(orientation);
00663     switch (role) {
00664     case Qt::DisplayRole:
00665         switch (section) {
00666         case Name:
00667             return i18nc("@title:column","Name");
00668         case Size:
00669             return i18nc("@title:column","Size");
00670         case ModifiedTime:
00671             return i18nc("@title:column","Date");
00672         case Permissions:
00673             return i18nc("@title:column","Permissions");
00674         case Owner:
00675             return i18nc("@title:column","Owner");
00676         case Group:
00677             return i18nc("@title:column","Group");
00678         case Type:
00679             return i18nc("@title:column","Type");
00680         }
00681     }
00682     return QVariant();
00683 }
00684 
00685 bool KDirModel::hasChildren( const QModelIndex & parent ) const
00686 {
00687     if (!parent.isValid())
00688         return true;
00689 
00690     const KFileItem& parentItem = static_cast<KDirModelNode*>(parent.internalPointer())->item();
00691     Q_ASSERT(!parentItem.isNull());
00692     return parentItem.isDir();
00693 }
00694 
00695 Qt::ItemFlags KDirModel::flags( const QModelIndex & index ) const
00696 {
00697     Qt::ItemFlags f = Qt::ItemIsEnabled;
00698     if (index.column() == Name) {
00699         f |= Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
00700     }
00701 
00702     // Allow dropping onto this item?
00703     if (d->m_dropsAllowed != NoDrops) {
00704         if(!index.isValid()) {
00705             if (d->m_dropsAllowed & DropOnDirectory) {
00706                 f |= Qt::ItemIsDropEnabled;
00707             }
00708         } else {
00709             KFileItem item = itemForIndex(index);
00710             if (item.isNull()) {
00711                 kWarning(7007) << "Invalid item returned for index";
00712             } else if (item.isDir()) {
00713                 if (d->m_dropsAllowed & DropOnDirectory) {
00714                     f |= Qt::ItemIsDropEnabled;
00715                 }
00716             } else { // regular file item
00717                 if (d->m_dropsAllowed & DropOnAnyFile)
00718                     f |= Qt::ItemIsDropEnabled;
00719                 else if (d->m_dropsAllowed & DropOnLocalExecutable) {
00720                     if (item.isLocalFile()) {
00721                         // Desktop file?
00722                         if (item.mimeTypePtr()->is("application/x-desktop"))
00723                             f |= Qt::ItemIsDropEnabled;
00724                         // Executable, shell script ... ?
00725                         else if ( QFileInfo( item.localPath() ).isExecutable() )
00726                             f |= Qt::ItemIsDropEnabled;
00727                     }
00728                 }
00729             }
00730         }
00731     }
00732 
00733     return f;
00734 }
00735 
00736 bool KDirModel::canFetchMore( const QModelIndex & parent ) const
00737 {
00738     if (!parent.isValid())
00739         return false;
00740 
00741     // We now have a bool KDirModelNode::m_populated,
00742     // to avoid calling fetchMore more than once on empty dirs.
00743     // But this wastes memory, and how often does someone open and re-open an empty dir in a treeview?
00744     // Maybe we can ask KDirLister "have you listed <url> already"? (to discuss with M. Brade)
00745 
00746     KDirModelNode* node = static_cast<KDirModelNode*>(parent.internalPointer());
00747     const KFileItem& item = node->item();
00748     return item.isDir() && !static_cast<KDirModelDirNode *>(node)->isPopulated()
00749         && static_cast<KDirModelDirNode *>(node)->m_childNodes.isEmpty();
00750 }
00751 
00752 void KDirModel::fetchMore( const QModelIndex & parent )
00753 {
00754     if (!parent.isValid())
00755         return;
00756 
00757     KDirModelNode* parentNode = static_cast<KDirModelNode*>(parent.internalPointer());
00758 
00759     KFileItem parentItem = parentNode->item();
00760     Q_ASSERT(!parentItem.isNull());
00761     Q_ASSERT(parentItem.isDir());
00762     KDirModelDirNode* dirNode = static_cast<KDirModelDirNode *>(parentNode);
00763     if( dirNode->isPopulated() )
00764         return;
00765     dirNode->setPopulated( true );
00766 
00767     const KUrl parentUrl = parentItem.url();
00768     d->m_dirLister->openUrl(parentUrl, KDirLister::Keep);
00769 }
00770 
00771 bool KDirModel::dropMimeData( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent )
00772 {
00773     // Not sure we want to implement any drop handling at this level,
00774     // but for sure the default QAbstractItemModel implementation makes no sense for a dir model.
00775     Q_UNUSED(data);
00776     Q_UNUSED(action);
00777     Q_UNUSED(row);
00778     Q_UNUSED(column);
00779     Q_UNUSED(parent);
00780     return false;
00781 }
00782 
00783 void KDirModel::setDropsAllowed(DropsAllowed dropsAllowed)
00784 {
00785     d->m_dropsAllowed = dropsAllowed;
00786 }
00787 
00788 void KDirModel::expandToUrl(const KUrl& url)
00789 {
00790     const QPair<int, KDirModelNode*> result = d->nodeForUrl(url, true /*return last parent*/); // O(n*m)
00791 
00792     if (!result.second) // doesn't seem related to our base url?
00793         return;
00794     if (!(result.second->item().isNull()) && result.second->item().url() == url) {
00795         // We have it already, nothing to do
00796         kDebug(7008) << "have it already item=" <<url /*result.second->item()*/;
00797         return;
00798     }
00799 
00800     d->m_urlsBeingFetched[result.second].append(url);
00801 
00802     if (result.second == d->m_rootNode) {
00803         kDebug(7008) << "Remembering to emit expand after listing the root url";
00804         // the root is fetched by default, so it must be currently being fetched
00805         return;
00806     }
00807 
00808     kDebug(7008) << "Remembering to emit expand after listing" << result.second->item().url();
00809 
00810     // start a new fetch to look for the next level down the URL
00811     const QModelIndex parentIndex = d->indexForNode(result.second, result.first);
00812     Q_ASSERT(parentIndex.isValid());
00813     fetchMore(parentIndex);
00814 }
00815 
00816 bool KDirModel::insertRows(int , int, const QModelIndex&)
00817 {
00818     return false;
00819 }
00820 
00821 bool KDirModel::insertColumns(int, int, const QModelIndex&)
00822 {
00823     return false;
00824 }
00825 
00826 bool KDirModel::removeRows(int, int, const QModelIndex&)
00827 {
00828     return false;
00829 }
00830 
00831 bool KDirModel::removeColumns(int, int, const QModelIndex&)
00832 {
00833     return false;
00834 }
00835 
00836 #include "kdirmodel.moc"

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs 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