00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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/fileundomanager.h>
00029 #include <kio/jobuidelegate.h>
00030 #include <kurl.h>
00031 #include <kdebug.h>
00032 #include <QMimeData>
00033 #include <QFile>
00034 #include <QFileInfo>
00035 #include <QDir>
00036 #include <sys/types.h>
00037 #include <dirent.h>
00038
00039 #ifdef Q_WS_WIN
00040 #include <windows.h>
00041 #endif
00042
00043 class KDirModelNode;
00044 class KDirModelDirNode;
00045
00046 static KUrl cleanupUrl(const KUrl& url) {
00047 KUrl u = url;
00048 u.cleanPath();
00049 u.adjustPath(KUrl::RemoveTrailingSlash);
00050 u.setQuery(QString());
00051 u.setRef(QString());
00052 return u;
00053 }
00054
00055
00056
00057 class KDirModelNode
00058 {
00059 public:
00060 KDirModelNode( KDirModelDirNode* parent, const KFileItem& item ) :
00061 m_item(item),
00062 m_parent(parent),
00063 m_preview()
00064 {
00065 }
00066
00067 const KFileItem& item() const { return m_item; }
00068 void setItem(const KFileItem& item) { m_item = item; }
00069 KDirModelDirNode* parent() const { return m_parent; }
00070
00071 int rowNumber() const;
00072 QIcon preview() const { return m_preview; }
00073 void setPreview( const QPixmap& pix ) { m_preview = QIcon(); m_preview.addPixmap(pix); }
00074 void setPreview( const QIcon& icn ) { m_preview = icn; }
00075
00076 private:
00077 KFileItem m_item;
00078 KDirModelDirNode* const m_parent;
00079 QIcon m_preview;
00080 };
00081
00082
00083 class KDirModelDirNode : public KDirModelNode
00084 {
00085 public:
00086 KDirModelDirNode( KDirModelDirNode* parent, const KFileItem& item)
00087 : KDirModelNode( parent, item),
00088 m_childNodes(),
00089 m_childCount(KDirModel::ChildCountUnknown),
00090 m_populated(false)
00091 {}
00092 ~KDirModelDirNode() {
00093 qDeleteAll(m_childNodes);
00094 }
00095 QList<KDirModelNode *> m_childNodes;
00096
00097
00098 int childCount() const { return m_childNodes.isEmpty() ? m_childCount : m_childNodes.count(); }
00099 void setChildCount(int count) { m_childCount = count; }
00100 bool isPopulated() const { return m_populated; }
00101 void setPopulated( bool populated ) { m_populated = populated; }
00102
00103
00104 void collectAllChildUrls(KUrl::List &urls) const {
00105 Q_FOREACH(KDirModelNode* node, m_childNodes) {
00106 const KFileItem& item = node->item();
00107 urls.append(cleanupUrl(item.url()));
00108 if (item.isDir())
00109 static_cast<KDirModelDirNode*>(node)->collectAllChildUrls(urls);
00110 }
00111 }
00112
00113 private:
00114 int m_childCount:31;
00115 bool m_populated:1;
00116 };
00117
00118 int KDirModelNode::rowNumber() const
00119 {
00120 if (!m_parent) return 0;
00121 return m_parent->m_childNodes.indexOf(const_cast<KDirModelNode*>(this));
00122 }
00123
00125
00126 class KDirModelPrivate
00127 {
00128 public:
00129 KDirModelPrivate( KDirModel* model )
00130 : q(model), m_dirLister(0),
00131 m_rootNode(new KDirModelDirNode(0, KFileItem())),
00132 m_dropsAllowed(KDirModel::NoDrops)
00133 {
00134 }
00135 ~KDirModelPrivate() {
00136 delete m_rootNode;
00137 }
00138
00139 void _k_slotNewItems(const KUrl& directoryUrl, const KFileItemList&);
00140 void _k_slotDeleteItems(const KFileItemList&);
00141 void _k_slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >&);
00142 void _k_slotClear();
00143 void _k_slotRedirection(const KUrl& oldUrl, const KUrl& newUrl);
00144
00145 void clear() {
00146 delete m_rootNode;
00147 m_rootNode = new KDirModelDirNode(0, KFileItem());
00148 }
00149
00150
00151 KDirModelNode* expandAllParentsUntil(const KUrl& url) const;
00152
00153
00154 KDirModelNode* nodeForUrl(const KUrl& url) const;
00155 KDirModelNode* nodeForIndex(const QModelIndex& index) const;
00156 QModelIndex indexForNode(KDirModelNode* node, int rowNumber = -1 ) const;
00157 bool isDir(KDirModelNode* node) const {
00158 return (node == m_rootNode) || node->item().isDir();
00159 }
00160 KUrl urlForNode(KDirModelNode* node) const {
00168 KUrl url(node == m_rootNode ? m_dirLister->url() : node->item().url());
00169 if (url.hasQuery() || url.hasRef()) {
00170 url.setQuery(QString());
00171 url.setRef(QString());
00172 }
00173 return url;
00174 }
00175 void removeFromNodeHash(KDirModelNode* node, const KUrl& url);
00176 #ifndef NDEBUG
00177 void dump();
00178 #endif
00179
00180 KDirModel* q;
00181 KDirLister* m_dirLister;
00182 KDirModelDirNode* m_rootNode;
00183 KDirModel::DropsAllowed m_dropsAllowed;
00184
00185
00186 QMap<KDirModelNode*, KUrl::List> m_urlsBeingFetched;
00187 QHash<KUrl, KDirModelNode *> m_nodeHash;
00188 };
00189
00190 KDirModelNode* KDirModelPrivate::nodeForUrl(const KUrl& _url) const
00191 {
00192 KUrl url = cleanupUrl(_url);
00193 if (url == urlForNode(m_rootNode))
00194 return m_rootNode;
00195 return m_nodeHash.value(url);
00196 }
00197
00198 void KDirModelPrivate::removeFromNodeHash(KDirModelNode* node, const KUrl& url)
00199 {
00200 if (node->item().isDir()) {
00201 KUrl::List urls;
00202 static_cast<KDirModelDirNode *>(node)->collectAllChildUrls(urls);
00203 Q_FOREACH(const KUrl& u, urls) {
00204 m_nodeHash.remove(u);
00205 }
00206 }
00207 m_nodeHash.remove(cleanupUrl(url));
00208 }
00209
00210 KDirModelNode* KDirModelPrivate::expandAllParentsUntil(const KUrl& _url) const
00211 {
00212 KUrl url = cleanupUrl(_url);
00213
00214
00215 KUrl nodeUrl = urlForNode(m_rootNode);
00216 if (url == nodeUrl)
00217 return m_rootNode;
00218
00219
00220 if (url.protocol() != nodeUrl.protocol())
00221 return 0;
00222
00223 const QString pathStr = url.path();
00224 KDirModelDirNode* dirNode = m_rootNode;
00225
00226 if (!pathStr.startsWith(nodeUrl.path())) {
00227 return 0;
00228 }
00229
00230 for (;;) {
00231 const QString nodePath = nodeUrl.path(KUrl::AddTrailingSlash);
00232 if(!pathStr.startsWith(nodePath)) {
00233 kError(7008) << "The kioslave for" << url.protocol() << "violates the hierarchy structure:"
00234 << "I arrived at node" << nodePath << ", but" << pathStr << "does not start with that path.";
00235 return 0;
00236 }
00237
00238
00239 const int nextSlash = pathStr.indexOf('/', nodePath.length());
00240 const QString newPath = pathStr.left(nextSlash);
00241 nodeUrl.setPath(newPath);
00242 nodeUrl.adjustPath(KUrl::RemoveTrailingSlash);
00243 KDirModelNode* node = nodeForUrl(nodeUrl);
00244 if (!node) {
00245
00246
00247 return dirNode;
00248 }
00249
00250 emit q->expand(indexForNode(node));
00251
00252
00253 if (nodeUrl == url) {
00254
00255 return node;
00256 }
00257
00258 Q_ASSERT(isDir(node));
00259 dirNode = static_cast<KDirModelDirNode *>(node);
00260 }
00261
00262
00263 }
00264
00265 #ifndef NDEBUG
00266 void KDirModelPrivate::dump()
00267 {
00268 kDebug() << "Dumping contents of KDirModel" << q << "dirLister url:" << m_dirLister->url();
00269 QHashIterator<KUrl, KDirModelNode *> it(m_nodeHash);
00270 while (it.hasNext()) {
00271 it.next();
00272 kDebug() << it.key() << it.value();
00273 }
00274 }
00275 #endif
00276
00277
00278 QModelIndex KDirModelPrivate::indexForNode(KDirModelNode* node, int rowNumber) const
00279 {
00280 if (node == m_rootNode)
00281 return QModelIndex();
00282
00283 Q_ASSERT(node->parent());
00284 return q->createIndex(rowNumber == -1 ? node->rowNumber() : rowNumber, 0, node);
00285 }
00286
00287
00288 KDirModelNode* KDirModelPrivate::nodeForIndex(const QModelIndex& index) const
00289 {
00290 return index.isValid()
00291 ? static_cast<KDirModelNode*>(index.internalPointer())
00292 : m_rootNode;
00293 }
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304 #ifndef NDEBUG
00305 static QString debugIndex(const QModelIndex& index)
00306 {
00307 QString str;
00308 if (!index.isValid())
00309 str = "[invalid index, i.e. root]";
00310 else {
00311 KDirModelNode* node = static_cast<KDirModelNode*>(index.internalPointer());
00312 str = "[index for " + node->item().url().pathOrUrl();
00313 if (index.column() > 0)
00314 str += ", column " + QString::number(index.column());
00315 str += ']';
00316 }
00317 return str;
00318 }
00319 #endif
00320
00321 KDirModel::KDirModel(QObject* parent)
00322 : QAbstractItemModel(parent),
00323 d(new KDirModelPrivate(this))
00324 {
00325 setDirLister(new KDirLister(this));
00326 }
00327
00328 KDirModel::~KDirModel()
00329 {
00330 delete d;
00331 }
00332
00333 void KDirModel::setDirLister(KDirLister* dirLister)
00334 {
00335 if (d->m_dirLister) {
00336 d->clear();
00337 delete d->m_dirLister;
00338 }
00339 d->m_dirLister = dirLister;
00340 d->m_dirLister->setParent(this);
00341 connect( d->m_dirLister, SIGNAL(itemsAdded(KUrl,KFileItemList)),
00342 this, SLOT(_k_slotNewItems(KUrl,KFileItemList)) );
00343 connect( d->m_dirLister, SIGNAL(itemsDeleted(KFileItemList)),
00344 this, SLOT(_k_slotDeleteItems(KFileItemList)) );
00345 connect( d->m_dirLister, SIGNAL(refreshItems(QList<QPair<KFileItem, KFileItem> >)),
00346 this, SLOT(_k_slotRefreshItems(QList<QPair<KFileItem, KFileItem> >)) );
00347 connect( d->m_dirLister, SIGNAL(clear()),
00348 this, SLOT(_k_slotClear()) );
00349 connect(d->m_dirLister, SIGNAL(redirection(KUrl, KUrl)),
00350 this, SLOT(_k_slotRedirection(KUrl, KUrl)));
00351 }
00352
00353 KDirLister* KDirModel::dirLister() const
00354 {
00355 return d->m_dirLister;
00356 }
00357
00358 void KDirModelPrivate::_k_slotNewItems(const KUrl& directoryUrl, const KFileItemList& items)
00359 {
00360
00361
00362 KDirModelNode* result = nodeForUrl(directoryUrl);
00363
00364
00365 if (!result) {
00366 kError(7008) << "Items emitted in directory" << directoryUrl
00367 << "but that directory isn't in KDirModel!"
00368 << "Root directory:" << urlForNode(m_rootNode);
00369 #ifndef NDEBUG
00370 dump();
00371 #endif
00372 Q_ASSERT(result);
00373 }
00374 Q_ASSERT(isDir(result));
00375 KDirModelDirNode* dirNode = static_cast<KDirModelDirNode *>(result);
00376
00377 const QModelIndex index = indexForNode(dirNode);
00378 const int newItemsCount = items.count();
00379 const int newRowCount = dirNode->m_childNodes.count() + newItemsCount;
00380 #if 0
00381 #ifndef NDEBUG // debugIndex only defined in debug mode
00382 kDebug(7008) << items.count() << "in" << directoryUrl
00383 << "index=" << debugIndex(index) << "newRowCount=" << newRowCount;
00384 #endif
00385 #endif
00386
00387 q->beginInsertRows( index, newRowCount - newItemsCount, newRowCount - 1 );
00388
00389 const KUrl::List urlsBeingFetched = m_urlsBeingFetched.value(dirNode);
00390
00391
00392 QList<QModelIndex> emitExpandFor;
00393
00394 KFileItemList::const_iterator it = items.begin();
00395 KFileItemList::const_iterator end = items.end();
00396 for ( ; it != end ; ++it ) {
00397 const bool isDir = it->isDir();
00398 KDirModelNode* node = isDir
00399 ? new KDirModelDirNode( dirNode, *it )
00400 : new KDirModelNode( dirNode, *it );
00401 #ifndef NDEBUG
00402
00403
00404
00405
00406
00407
00408 #endif
00409 dirNode->m_childNodes.append(node);
00410 const KUrl url = it->url();
00411 m_nodeHash.insert(cleanupUrl(url), node);
00412
00413
00414 if (!urlsBeingFetched.isEmpty()) {
00415 const KUrl dirUrl = url;
00416 foreach(const KUrl& urlFetched, urlsBeingFetched) {
00417 if (dirUrl.isParentOf(urlFetched)) {
00418 kDebug(7008) << "Listing found" << dirUrl << "which is a parent of fetched url" << urlFetched;
00419 const QModelIndex parentIndex = indexForNode(node, dirNode->m_childNodes.count()-1);
00420 Q_ASSERT(parentIndex.isValid());
00421 emitExpandFor.append(parentIndex);
00422 if (isDir && dirUrl != urlFetched) {
00423 q->fetchMore(parentIndex);
00424 m_urlsBeingFetched[node].append(urlFetched);
00425 }
00426 }
00427 }
00428 }
00429 }
00430
00431 m_urlsBeingFetched.remove(dirNode);
00432
00433 q->endInsertRows();
00434
00435
00436
00437 Q_FOREACH(const QModelIndex& idx, emitExpandFor) {
00438 emit q->expand(idx);
00439 }
00440 }
00441
00442 void KDirModelPrivate::_k_slotDeleteItems(const KFileItemList& items)
00443 {
00444
00445
00446
00447
00448 const KFileItem item = items.first();
00449 Q_ASSERT(!item.isNull());
00450 KUrl url = item.url();
00451 KDirModelNode* node = nodeForUrl(url);
00452 if (!node) {
00453 kWarning(7008) << "No node found for item that was just removed:" << url;
00454 return;
00455 }
00456
00457 KDirModelDirNode* dirNode = node->parent();
00458 if (!dirNode)
00459 return;
00460
00461 QModelIndex parentIndex = indexForNode(dirNode);
00462
00463
00464 if (items.count() == 1) {
00465 const int r = node->rowNumber();
00466 q->beginRemoveRows(parentIndex, r, r);
00467 removeFromNodeHash(node, url);
00468 delete dirNode->m_childNodes.takeAt(r);
00469 q->endRemoveRows();
00470 return;
00471 }
00472
00473
00474
00475 const int childCount = dirNode->m_childNodes.count();
00476 QBitArray rowNumbers(childCount, false);
00477 Q_FOREACH(const KFileItem& item, items) {
00478 if (!node) {
00479 url = item.url();
00480 node = nodeForUrl(url);
00481 if (!node) {
00482 kWarning(7008) << "No node found for item that was just removed:" << url;
00483 continue;
00484 }
00485 }
00486 rowNumbers.setBit(node->rowNumber(), 1);
00487 removeFromNodeHash(node, url);
00488 node = 0;
00489 }
00490
00491 int start = -1;
00492 int end = -1;
00493 bool lastVal = false;
00494
00495 for (int i = childCount - 1; i >= 0; --i) {
00496 const bool val = rowNumbers.testBit(i);
00497 if (!lastVal && val) {
00498 end = i;
00499
00500 }
00501 if ((lastVal && !val) || (i == 0 && val)) {
00502 start = val ? i : i + 1;
00503
00504 q->beginRemoveRows(parentIndex, start, end);
00505 for (int r = end; r >= start; --r) {
00506
00507 delete dirNode->m_childNodes.takeAt(r);
00508 }
00509 q->endRemoveRows();
00510 }
00511 lastVal = val;
00512 }
00513 }
00514
00515 void KDirModelPrivate::_k_slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >& items)
00516 {
00517 QModelIndex topLeft, bottomRight;
00518
00519
00520
00521 for ( QList<QPair<KFileItem, KFileItem> >::const_iterator fit = items.begin(), fend = items.end() ; fit != fend ; ++fit ) {
00522 Q_ASSERT(!fit->first.isNull());
00523 Q_ASSERT(!fit->second.isNull());
00524 const KUrl oldUrl = fit->first.url();
00525 const KUrl newUrl = fit->second.url();
00526 KDirModelNode* node = nodeForUrl(oldUrl);
00527
00528 if (!node)
00529 continue;
00530 if (node != m_rootNode) {
00531 bool hasNewNode = false;
00532
00533 if (fit->first.isDir() != fit->second.isDir()) {
00534
00535 const int r = node->rowNumber();
00536 removeFromNodeHash(node, oldUrl);
00537 KDirModelDirNode* dirNode = node->parent();
00538 delete dirNode->m_childNodes.takeAt(r);
00539 node = fit->second.isDir() ? new KDirModelDirNode(dirNode, fit->second)
00540 : new KDirModelNode(dirNode, fit->second);
00541 dirNode->m_childNodes.insert(r, node);
00542 hasNewNode = true;
00543 } else {
00544 node->setItem(fit->second);
00545 }
00546
00547 if (oldUrl != newUrl || hasNewNode) {
00548
00549
00550 m_nodeHash.remove(cleanupUrl(oldUrl));
00551 m_nodeHash.insert(cleanupUrl(newUrl), node);
00552 }
00553
00554 if (fit->first.mimeTypePtr()->name() != fit->second.mimeTypePtr()->name()) {
00555 node->setPreview(QIcon());
00556 }
00557
00558 const QModelIndex index = indexForNode(node);
00559 if (!topLeft.isValid() || index.row() < topLeft.row()) {
00560 topLeft = index;
00561 }
00562 if (!bottomRight.isValid() || index.row() > bottomRight.row()) {
00563 bottomRight = index;
00564 }
00565 }
00566 }
00567 #ifndef NDEBUG // debugIndex only defined in debug mode
00568 kDebug(7008) << "dataChanged(" << debugIndex(topLeft) << " - " << debugIndex(bottomRight);
00569 #endif
00570 bottomRight = bottomRight.sibling(bottomRight.row(), q->columnCount(QModelIndex())-1);
00571 emit q->dataChanged(topLeft, bottomRight);
00572 }
00573
00574
00575
00576 void KDirModelPrivate::_k_slotRedirection(const KUrl& oldUrl, const KUrl& newUrl)
00577 {
00578 KDirModelNode* node = nodeForUrl(oldUrl);
00579 if (!node)
00580 return;
00581 m_nodeHash.remove(cleanupUrl(oldUrl));
00582 m_nodeHash.insert(cleanupUrl(newUrl), node);
00583
00584
00585
00586
00587 KFileItem item = node->item();
00588 if (!item.isNull()) {
00589 item.setUrl(newUrl);
00590 node->setItem(item);
00591 }
00592
00593
00594
00595 }
00596
00597 void KDirModelPrivate::_k_slotClear()
00598 {
00599 const int numRows = m_rootNode->m_childNodes.count();
00600 if (numRows > 0) {
00601 q->beginRemoveRows( QModelIndex(), 0, numRows - 1 );
00602 q->endRemoveRows();
00603 }
00604
00605 m_nodeHash.clear();
00606
00607 clear();
00608
00609 }
00610
00611 void KDirModel::itemChanged( const QModelIndex& index )
00612 {
00613
00614
00615
00616 KDirModelNode* node = d->nodeForIndex(index);
00617 if (node)
00618 node->setPreview(QIcon());
00619
00620 #ifndef NDEBUG // debugIndex only defined in debug mode
00621
00622 #endif
00623 emit dataChanged(index, index);
00624 }
00625
00626 int KDirModel::columnCount( const QModelIndex & ) const
00627 {
00628 return ColumnCount;
00629 }
00630
00631 QVariant KDirModel::data( const QModelIndex & index, int role ) const
00632 {
00633 if (index.isValid()) {
00634 KDirModelNode* node = static_cast<KDirModelNode*>(index.internalPointer());
00635 const KFileItem& item( node->item() );
00636 switch (role) {
00637 case Qt::DisplayRole:
00638 switch (index.column()) {
00639 case Name:
00640 return item.text();
00641 case Size:
00642
00643
00644
00645 return KGlobal::locale()->formatNumber(item.size(), 0);
00646 case ModifiedTime: {
00647 KDateTime dt = item.time(KFileItem::ModificationTime);
00648 return KGlobal::locale()->formatDateTime(dt);
00649 }
00650 case Permissions:
00651 return item.permissionsString();
00652 case Owner:
00653 return item.user();
00654 case Group:
00655 return item.group();
00656 case Type:
00657 return item.mimeComment();
00658 }
00659 break;
00660 case Qt::EditRole:
00661 switch (index.column()) {
00662 case Name:
00663 return item.text();
00664 }
00665 break;
00666 case Qt::DecorationRole:
00667 if (index.column() == Name) {
00668 if (!node->preview().isNull()) {
00669
00670 return node->preview();
00671 }
00672 Q_ASSERT(!item.isNull());
00673
00674 return KIcon(item.iconName(), 0, item.overlays());
00675 }
00676 break;
00677 case Qt::TextAlignmentRole:
00678 if (index.column() == Size) {
00679
00680 const Qt::Alignment alignment = Qt::AlignRight | Qt::AlignVCenter;
00681 return int(alignment);
00682 }
00683 break;
00684 case Qt::ToolTipRole:
00685 return item.text();
00686 case FileItemRole:
00687 return QVariant::fromValue(item);
00688 case ChildCountRole:
00689 if (!item.isDir())
00690 return ChildCountUnknown;
00691 else {
00692 KDirModelDirNode* dirNode = static_cast<KDirModelDirNode *>(node);
00693 int count = dirNode->childCount();
00694 if (count == ChildCountUnknown && item.isReadable()) {
00695 const QString path = item.localPath();
00696 if (!path.isEmpty()) {
00697
00698
00699
00700 #ifdef Q_WS_WIN
00701 QString s = path + QLatin1String( "\\*.*" );
00702 s.replace('/', '\\');
00703 count = 0;
00704 WIN32_FIND_DATA findData;
00705 HANDLE hFile = FindFirstFile( (LPWSTR)s.utf16(), &findData );
00706 if( hFile != INVALID_HANDLE_VALUE ) {
00707 do {
00708 if (!( findData.cFileName[0] == '.' &&
00709 findData.cFileName[1] == '\0' ) &&
00710 !( findData.cFileName[0] == '.' &&
00711 findData.cFileName[1] == '.' &&
00712 findData.cFileName[2] == '\0' ) )
00713 ++count;
00714 } while( FindNextFile( hFile, &findData ) != 0 );
00715 FindClose( hFile );
00716 }
00717 #else
00718 DIR* dir = ::opendir(QFile::encodeName(path));
00719 if (dir) {
00720 count = 0;
00721 struct dirent *dirEntry = 0;
00722 while ((dirEntry = ::readdir(dir))) {
00723 if (dirEntry->d_name[0] == '.') {
00724 if (dirEntry->d_name[1] == '\0')
00725 continue;
00726 if (dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0')
00727 continue;
00728 }
00729 ++count;
00730 }
00731 ::closedir(dir);
00732 }
00733 #endif
00734
00735 dirNode->setChildCount(count);
00736 }
00737 }
00738 return count;
00739 }
00740 }
00741 }
00742 return QVariant();
00743 }
00744
00745 void KDirModel::sort( int column, Qt::SortOrder order )
00746 {
00747
00748 return QAbstractItemModel::sort(column, order);
00749 }
00750
00751 bool KDirModel::setData( const QModelIndex & index, const QVariant & value, int role )
00752 {
00753 switch (role) {
00754 case Qt::EditRole:
00755 if (index.column() == Name && value.type() == QVariant::String) {
00756 Q_ASSERT(index.isValid());
00757 KDirModelNode* node = static_cast<KDirModelNode*>(index.internalPointer());
00758 const KFileItem& item = node->item();
00759 const QString newName = value.toString();
00760 if (newName.isEmpty() || newName == item.text())
00761 return true;
00762 KUrl newurl(item.url());
00763 newurl.setPath(newurl.directory(KUrl::AppendTrailingSlash) + newName);
00764 KIO::Job * job = KIO::moveAs(item.url(), newurl, newurl.isLocalFile() ? KIO::HideProgressInfo : KIO::DefaultFlags);
00765 job->ui()->setAutoErrorHandlingEnabled(true);
00766
00767 KIO::FileUndoManager::self()->recordJob( KIO::FileUndoManager::Rename, item.url(), newurl, job );
00768 return true;
00769 }
00770 break;
00771 case Qt::DecorationRole:
00772 if (index.column() == Name) {
00773 Q_ASSERT(index.isValid());
00774
00775 KDirModelNode* node = static_cast<KDirModelNode*>(index.internalPointer());
00776
00777 Q_ASSERT(node);
00778 if (value.type() == QVariant::Icon) {
00779 const QIcon icon(qvariant_cast<QIcon>(value));
00780 node->setPreview(icon);
00781 } else if (value.type() == QVariant::Pixmap) {
00782 node->setPreview(qvariant_cast<QPixmap>(value));
00783 }
00784 emit dataChanged(index, index);
00785 return true;
00786 }
00787 break;
00788 default:
00789 break;
00790 }
00791 return false;
00792 }
00793
00794 int KDirModel::rowCount( const QModelIndex & parent ) const
00795 {
00796 KDirModelNode* node = d->nodeForIndex(parent);
00797 if (!node || !d->isDir(node))
00798 return 0;
00799
00800 KDirModelDirNode* parentNode = static_cast<KDirModelDirNode *>(node);
00801 Q_ASSERT(parentNode);
00802 const int count = parentNode->m_childNodes.count();
00803 #if 0
00804 QStringList filenames;
00805 for (int i = 0; i < count; ++i) {
00806 filenames << d->urlForNode(parentNode->m_childNodes.at(i)).fileName();
00807 }
00808 kDebug(7008) << "rowCount for " << d->urlForNode(parentNode) << ": " << count << filenames;
00809 #endif
00810 return count;
00811 }
00812
00813
00814 QModelIndex KDirModel::parent( const QModelIndex & index ) const
00815 {
00816 if (!index.isValid())
00817 return QModelIndex();
00818 KDirModelNode* childNode = static_cast<KDirModelNode*>(index.internalPointer());
00819 Q_ASSERT(childNode);
00820 KDirModelNode* parentNode = childNode->parent();
00821 Q_ASSERT(parentNode);
00822 return d->indexForNode(parentNode);
00823 }
00824
00825 static bool lessThan(const KUrl &left, const KUrl &right)
00826 {
00827 return left.url().compare(right.url()) < 0;
00828 }
00829
00830 void KDirModel::requestSequenceIcon(const QModelIndex& index, int sequenceIndex) {
00831 emit needSequenceIcon(index, sequenceIndex);
00832 }
00833
00834 KUrl::List KDirModel::simplifiedUrlList(const KUrl::List &urls)
00835 {
00836 if (!urls.count()) {
00837 return urls;
00838 }
00839
00840 KUrl::List ret(urls);
00841 qSort(ret.begin(), ret.end(), lessThan);
00842
00843 KUrl::List::iterator it = ret.begin();
00844 KUrl url = *it;
00845 ++it;
00846 while (it != ret.end()) {
00847 if (url.isParentOf(*it)) {
00848 it = ret.erase(it);
00849 } else {
00850 url = *it;
00851 ++it;
00852 }
00853 }
00854
00855 return ret;
00856 }
00857
00858 QStringList KDirModel::mimeTypes( ) const
00859 {
00860 return KUrl::List::mimeDataTypes();
00861 }
00862
00863 QMimeData * KDirModel::mimeData( const QModelIndexList & indexes ) const
00864 {
00865 KUrl::List urls, mostLocalUrls;
00866 bool canUseMostLocalUrls = true;
00867 foreach (const QModelIndex &index, indexes) {
00868 const KFileItem& item = d->nodeForIndex(index)->item();
00869 urls << item.url();
00870 bool isLocal;
00871 mostLocalUrls << item.mostLocalUrl(isLocal);
00872 if (!isLocal)
00873 canUseMostLocalUrls = false;
00874 }
00875 QMimeData *data = new QMimeData();
00876 const bool different = canUseMostLocalUrls && (mostLocalUrls != urls);
00877 urls = simplifiedUrlList(urls);
00878 if (different) {
00879 mostLocalUrls = simplifiedUrlList(mostLocalUrls);
00880 urls.populateMimeData(mostLocalUrls, data);
00881 } else {
00882 urls.populateMimeData(data);
00883 }
00884
00885
00886 QString application_x_qiconlist;
00887 const int items = urls.count();
00888 for (int i = 0; i < items; i++) {
00889 const int offset = i*16;
00890 QString tmp("%1$@@$%2$@@$32$@@$32$@@$%3$@@$%4$@@$32$@@$16$@@$no data$@@$");
00891 application_x_qiconlist += tmp.arg(offset).arg(offset).arg(offset).arg(offset+40);
00892 }
00893 data->setData("application/x-qiconlist", application_x_qiconlist.toLatin1());
00894
00895 return data;
00896 }
00897
00898
00899 KFileItem KDirModel::itemForIndex( const QModelIndex& index ) const
00900 {
00901 if (!index.isValid()) {
00902 return d->m_dirLister->rootItem();
00903 } else {
00904 return static_cast<KDirModelNode*>(index.internalPointer())->item();
00905 }
00906 }
00907
00908 QModelIndex KDirModel::indexForItem( const KFileItem* item ) const
00909 {
00910
00911
00912 return indexForUrl(item->url());
00913 }
00914
00915 QModelIndex KDirModel::indexForItem( const KFileItem& item ) const
00916 {
00917
00918
00919 return indexForUrl(item.url());
00920 }
00921
00922
00923 QModelIndex KDirModel::indexForUrl(const KUrl& url) const
00924 {
00925 KDirModelNode* node = d->nodeForUrl(url);
00926 if (!node) {
00927 kDebug(7007) << url << "not found";
00928 return QModelIndex();
00929 }
00930 return d->indexForNode(node);
00931 }
00932
00933 QModelIndex KDirModel::index( int row, int column, const QModelIndex & parent ) const
00934 {
00935 KDirModelNode* parentNode = d->nodeForIndex(parent);
00936 Q_ASSERT(parentNode);
00937 Q_ASSERT(d->isDir(parentNode));
00938 KDirModelNode* childNode = static_cast<KDirModelDirNode *>(parentNode)->m_childNodes.value(row);
00939 if (childNode)
00940 return createIndex(row, column, childNode);
00941 else
00942 return QModelIndex();
00943 }
00944
00945 QVariant KDirModel::headerData( int section, Qt::Orientation orientation, int role ) const
00946 {
00947 if (orientation == Qt::Horizontal) {
00948 switch (role) {
00949 case Qt::DisplayRole:
00950 switch (section) {
00951 case Name:
00952 return i18nc("@title:column","Name");
00953 case Size:
00954 return i18nc("@title:column","Size");
00955 case ModifiedTime:
00956 return i18nc("@title:column","Date");
00957 case Permissions:
00958 return i18nc("@title:column","Permissions");
00959 case Owner:
00960 return i18nc("@title:column","Owner");
00961 case Group:
00962 return i18nc("@title:column","Group");
00963 case Type:
00964 return i18nc("@title:column","Type");
00965 }
00966 }
00967 }
00968 return QVariant();
00969 }
00970
00971 bool KDirModel::hasChildren( const QModelIndex & parent ) const
00972 {
00973 if (!parent.isValid())
00974 return true;
00975
00976 const KFileItem& parentItem = static_cast<KDirModelNode*>(parent.internalPointer())->item();
00977 Q_ASSERT(!parentItem.isNull());
00978 return parentItem.isDir();
00979 }
00980
00981 Qt::ItemFlags KDirModel::flags( const QModelIndex & index ) const
00982 {
00983 Qt::ItemFlags f = Qt::ItemIsEnabled;
00984 if (index.column() == Name) {
00985 f |= Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
00986 }
00987
00988
00989 if (d->m_dropsAllowed != NoDrops) {
00990 if(!index.isValid()) {
00991 if (d->m_dropsAllowed & DropOnDirectory) {
00992 f |= Qt::ItemIsDropEnabled;
00993 }
00994 } else {
00995 KFileItem item = itemForIndex(index);
00996 if (item.isNull()) {
00997 kWarning(7007) << "Invalid item returned for index";
00998 } else if (item.isDir()) {
00999 if (d->m_dropsAllowed & DropOnDirectory) {
01000 f |= Qt::ItemIsDropEnabled;
01001 }
01002 } else {
01003 if (d->m_dropsAllowed & DropOnAnyFile)
01004 f |= Qt::ItemIsDropEnabled;
01005 else if (d->m_dropsAllowed & DropOnLocalExecutable) {
01006 if (!item.localPath().isEmpty()) {
01007
01008 if (item.mimeTypePtr()->is("application/x-desktop"))
01009 f |= Qt::ItemIsDropEnabled;
01010
01011 else if ( QFileInfo( item.localPath() ).isExecutable() )
01012 f |= Qt::ItemIsDropEnabled;
01013 }
01014 }
01015 }
01016 }
01017 }
01018
01019 return f;
01020 }
01021
01022 bool KDirModel::canFetchMore( const QModelIndex & parent ) const
01023 {
01024 if (!parent.isValid())
01025 return false;
01026
01027
01028
01029
01030
01031
01032 KDirModelNode* node = static_cast<KDirModelNode*>(parent.internalPointer());
01033 const KFileItem& item = node->item();
01034 return item.isDir() && !static_cast<KDirModelDirNode *>(node)->isPopulated()
01035 && static_cast<KDirModelDirNode *>(node)->m_childNodes.isEmpty();
01036 }
01037
01038 void KDirModel::fetchMore( const QModelIndex & parent )
01039 {
01040 if (!parent.isValid())
01041 return;
01042
01043 KDirModelNode* parentNode = static_cast<KDirModelNode*>(parent.internalPointer());
01044
01045 KFileItem parentItem = parentNode->item();
01046 Q_ASSERT(!parentItem.isNull());
01047 Q_ASSERT(parentItem.isDir());
01048 KDirModelDirNode* dirNode = static_cast<KDirModelDirNode *>(parentNode);
01049 if( dirNode->isPopulated() )
01050 return;
01051 dirNode->setPopulated( true );
01052
01053 const KUrl parentUrl = parentItem.url();
01054 d->m_dirLister->openUrl(parentUrl, KDirLister::Keep);
01055 }
01056
01057 bool KDirModel::dropMimeData( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent )
01058 {
01059
01060
01061 Q_UNUSED(data);
01062 Q_UNUSED(action);
01063 Q_UNUSED(row);
01064 Q_UNUSED(column);
01065 Q_UNUSED(parent);
01066 return false;
01067 }
01068
01069 void KDirModel::setDropsAllowed(DropsAllowed dropsAllowed)
01070 {
01071 d->m_dropsAllowed = dropsAllowed;
01072 }
01073
01074 void KDirModel::expandToUrl(const KUrl& url)
01075 {
01076
01077 KDirModelNode* result = d->expandAllParentsUntil(url);
01078
01079
01080 if (!result)
01081 return;
01082 if (!(result->item().isNull()) && result->item().url() == url) {
01083
01084 kDebug(7008) << "have it already item=" <<url ;
01085 return;
01086 }
01087
01088 d->m_urlsBeingFetched[result].append(url);
01089
01090 if (result == d->m_rootNode) {
01091 kDebug(7008) << "Remembering to emit expand after listing the root url";
01092
01093 return;
01094 }
01095
01096 kDebug(7008) << "Remembering to emit expand after listing" << result->item().url();
01097
01098
01099 const QModelIndex parentIndex = d->indexForNode(result);
01100 Q_ASSERT(parentIndex.isValid());
01101 fetchMore(parentIndex);
01102 }
01103
01104 bool KDirModel::insertRows(int , int, const QModelIndex&)
01105 {
01106 return false;
01107 }
01108
01109 bool KDirModel::insertColumns(int, int, const QModelIndex&)
01110 {
01111 return false;
01112 }
01113
01114 bool KDirModel::removeRows(int, int, const QModelIndex&)
01115 {
01116 return false;
01117 }
01118
01119 bool KDirModel::removeColumns(int, int, const QModelIndex&)
01120 {
01121 return false;
01122 }
01123
01124 #include "kdirmodel.moc"