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