37 #include <sys/types.h>
45 class KDirModelDirNode;
61 KDirModelNode( KDirModelDirNode* parent,
const KFileItem& item ) :
68 const KFileItem& item()
const {
return m_item; }
69 void setItem(
const KFileItem& item) { m_item = item; }
70 KDirModelDirNode* parent()
const {
return m_parent; }
72 int rowNumber()
const;
73 QIcon preview()
const {
return m_preview; }
74 void setPreview(
const QPixmap& pix ) { m_preview = QIcon(); m_preview.addPixmap(pix); }
75 void setPreview(
const QIcon& icn ) { m_preview = icn; }
79 KDirModelDirNode*
const m_parent;
84 class KDirModelDirNode :
public KDirModelNode
87 KDirModelDirNode( KDirModelDirNode* parent,
const KFileItem& item)
88 : KDirModelNode( parent, item),
90 m_childCount(
KDirModel::ChildCountUnknown),
94 qDeleteAll(m_childNodes);
96 QList<KDirModelNode *> m_childNodes;
99 int childCount()
const {
return m_childNodes.isEmpty() ? m_childCount : m_childNodes.count(); }
100 void setChildCount(
int count) { m_childCount = count; }
101 bool isPopulated()
const {
return m_populated; }
102 void setPopulated(
bool populated ) { m_populated = populated; }
103 bool isSlow()
const {
return item().isSlow(); }
106 void collectAllChildUrls(
KUrl::List &urls)
const {
107 Q_FOREACH(KDirModelNode* node, m_childNodes) {
111 static_cast<KDirModelDirNode*>(node)->collectAllChildUrls(urls);
120 int KDirModelNode::rowNumber()
const
122 if (!m_parent)
return 0;
123 return m_parent->m_childNodes.indexOf(const_cast<KDirModelNode*>(
this));
128 class KDirModelPrivate
132 : q(model), m_dirLister(0),
133 m_rootNode(new KDirModelDirNode(0,
KFileItem())),
134 m_dropsAllowed(
KDirModel::NoDrops), m_jobTransfersVisible(false)
137 ~KDirModelPrivate() {
145 void _k_slotRedirection(
const KUrl& oldUrl,
const KUrl& newUrl);
146 void _k_slotJobUrlsChanged(
const QStringList& urlList);
150 m_rootNode =
new KDirModelDirNode(0,
KFileItem());
154 KDirModelNode* expandAllParentsUntil(
const KUrl& url)
const;
157 KDirModelNode* nodeForUrl(
const KUrl& url)
const;
158 KDirModelNode* nodeForIndex(
const QModelIndex& index)
const;
159 QModelIndex indexForNode(KDirModelNode* node,
int rowNumber = -1 )
const;
160 bool isDir(KDirModelNode* node)
const {
161 return (node == m_rootNode) || node->item().isDir();
163 KUrl urlForNode(KDirModelNode* node)
const {
171 KUrl url(node == m_rootNode ? m_dirLister->url() : node->item().url());
172 if (url.hasQuery() || url.hasRef()) {
178 void removeFromNodeHash(KDirModelNode* node,
const KUrl& url);
185 KDirModelDirNode* m_rootNode;
186 KDirModel::DropsAllowed m_dropsAllowed;
187 bool m_jobTransfersVisible;
191 QHash<KUrl, KDirModelNode *> m_nodeHash;
195 KDirModelNode* KDirModelPrivate::nodeForUrl(
const KUrl& _url)
const
198 if (url == urlForNode(m_rootNode))
200 return m_nodeHash.value(url);
203 void KDirModelPrivate::removeFromNodeHash(KDirModelNode* node,
const KUrl& url)
205 if (node->item().isDir()) {
207 static_cast<KDirModelDirNode *
>(node)->collectAllChildUrls(urls);
208 Q_FOREACH(
const KUrl& u, urls) {
209 m_nodeHash.remove(u);
215 KDirModelNode* KDirModelPrivate::expandAllParentsUntil(
const KUrl& _url)
const
220 KUrl nodeUrl = urlForNode(m_rootNode);
229 KDirModelDirNode* dirNode = m_rootNode;
231 if (!pathStr.startsWith(nodeUrl.
path())) {
237 if(!pathStr.startsWith(nodePath)) {
238 kError(7008) <<
"The kioslave for" << url.
protocol() <<
"violates the hierarchy structure:"
239 <<
"I arrived at node" << nodePath <<
", but" << pathStr <<
"does not start with that path.";
244 const int nextSlash = pathStr.indexOf(
'/', nodePath.length());
245 const QString newPath = pathStr.left(nextSlash);
248 KDirModelNode* node = nodeForUrl(nodeUrl);
255 emit q->expand(indexForNode(node));
258 if (nodeUrl == url) {
263 Q_ASSERT(isDir(node));
264 dirNode =
static_cast<KDirModelDirNode *
>(node);
271 void KDirModelPrivate::dump()
273 kDebug() <<
"Dumping contents of KDirModel" << q <<
"dirLister url:" << m_dirLister->url();
274 QHashIterator<KUrl, KDirModelNode *> it(m_nodeHash);
275 while (it.hasNext()) {
277 kDebug() << it.key() << it.value();
283 QModelIndex KDirModelPrivate::indexForNode(KDirModelNode* node,
int rowNumber)
const
285 if (node == m_rootNode)
286 return QModelIndex();
288 Q_ASSERT(node->parent());
289 return q->createIndex(rowNumber == -1 ? node->rowNumber() : rowNumber, 0, node);
293 KDirModelNode* KDirModelPrivate::nodeForIndex(
const QModelIndex& index)
const
295 return index.isValid()
296 ?
static_cast<KDirModelNode*
>(index.internalPointer())
313 if (!index.isValid())
314 str =
"[invalid index, i.e. root]";
316 KDirModelNode* node =
static_cast<KDirModelNode*
>(index.internalPointer());
317 str =
"[index for " + node->item().url().pathOrUrl();
318 if (index.column() > 0)
328 d(new KDirModelPrivate(this))
340 if (d->m_dirLister) {
342 delete d->m_dirLister;
345 d->m_dirLister->setParent(
this);
348 connect( d->m_dirLister, SIGNAL(itemsDeleted(
KFileItemList)),
352 connect( d->m_dirLister, SIGNAL(
clear()),
353 this, SLOT(_k_slotClear()) );
354 connect(d->m_dirLister, SIGNAL(redirection(
KUrl,
KUrl)),
355 this, SLOT(_k_slotRedirection(
KUrl,
KUrl)));
360 return d->m_dirLister;
363 void KDirModelPrivate::_k_slotNewItems(
const KUrl& directoryUrl,
const KFileItemList& items)
367 KDirModelNode* result = nodeForUrl(directoryUrl);
371 kError(7008) <<
"Items emitted in directory" << directoryUrl
372 <<
"but that directory isn't in KDirModel!"
373 <<
"Root directory:" << urlForNode(m_rootNode);
374 Q_FOREACH(
const KFileItem& item, items) {
382 Q_ASSERT(isDir(result));
383 KDirModelDirNode* dirNode =
static_cast<KDirModelDirNode *
>(result);
385 const QModelIndex index = indexForNode(dirNode);
386 const int newItemsCount = items.count();
387 const int newRowCount = dirNode->m_childNodes.count() + newItemsCount;
389 #ifndef NDEBUG // debugIndex only defined in debug mode
390 kDebug(7008) << items.count() <<
"in" << directoryUrl
391 <<
"index=" <<
debugIndex(index) <<
"newRowCount=" << newRowCount;
395 q->beginInsertRows( index, newRowCount - newItemsCount, newRowCount - 1 );
397 const KUrl::List urlsBeingFetched = m_urlsBeingFetched.value(dirNode);
400 QList<QModelIndex> emitExpandFor;
402 KFileItemList::const_iterator it = items.begin();
403 KFileItemList::const_iterator
end = items.end();
404 for ( ; it != end ; ++it ) {
405 const bool isDir = it->isDir();
406 KDirModelNode* node = isDir
407 ?
new KDirModelDirNode( dirNode, *it )
408 : new KDirModelNode( dirNode, *it );
417 dirNode->m_childNodes.append(node);
422 if (!urlsBeingFetched.isEmpty()) {
423 const KUrl dirUrl = url;
424 foreach(
const KUrl& urlFetched, urlsBeingFetched) {
426 kDebug(7008) <<
"Listing found" << dirUrl <<
"which is a parent of fetched url" << urlFetched;
427 const QModelIndex parentIndex = indexForNode(node, dirNode->m_childNodes.count()-1);
428 Q_ASSERT(parentIndex.isValid());
429 emitExpandFor.append(parentIndex);
430 if (isDir && dirUrl != urlFetched) {
431 q->fetchMore(parentIndex);
432 m_urlsBeingFetched[node].append(urlFetched);
439 m_urlsBeingFetched.remove(dirNode);
445 Q_FOREACH(
const QModelIndex& idx, emitExpandFor) {
450 void KDirModelPrivate::_k_slotDeleteItems(
const KFileItemList& items)
459 KDirModelNode* node = nodeForUrl(url);
461 kWarning(7008) <<
"No node found for item that was just removed:" << url;
465 KDirModelDirNode* dirNode = node->parent();
469 QModelIndex parentIndex = indexForNode(dirNode);
472 if (items.count() == 1) {
473 const int r = node->rowNumber();
474 q->beginRemoveRows(parentIndex, r, r);
475 removeFromNodeHash(node, url);
476 delete dirNode->m_childNodes.takeAt(r);
483 const int childCount = dirNode->m_childNodes.count();
484 QBitArray rowNumbers(childCount,
false);
485 Q_FOREACH(
const KFileItem& item, items) {
488 node = nodeForUrl(url);
490 kWarning(7008) <<
"No node found for item that was just removed:" << url;
493 if (!node->parent()) {
499 rowNumbers.setBit(node->rowNumber(), 1);
500 removeFromNodeHash(node, url);
506 bool lastVal =
false;
508 for (
int i = childCount - 1; i >= 0; --i) {
509 const bool val = rowNumbers.testBit(i);
510 if (!lastVal && val) {
514 if ((lastVal && !val) || (i == 0 && val)) {
515 start = val ? i : i + 1;
517 q->beginRemoveRows(parentIndex, start, end);
518 for (
int r = end; r >= start; --r) {
520 delete dirNode->m_childNodes.takeAt(r);
530 QModelIndex topLeft, bottomRight;
535 Q_ASSERT(!fit->first.isNull());
536 Q_ASSERT(!fit->second.isNull());
537 const KUrl oldUrl = fit->first.
url();
538 const KUrl newUrl = fit->second.
url();
539 KDirModelNode* node = nodeForUrl(oldUrl);
543 if (node != m_rootNode) {
544 bool hasNewNode =
false;
546 if (fit->first.isDir() != fit->second.isDir()) {
548 const int r = node->rowNumber();
549 removeFromNodeHash(node, oldUrl);
550 KDirModelDirNode* dirNode = node->parent();
551 delete dirNode->m_childNodes.takeAt(r);
552 node = fit->second.isDir() ?
new KDirModelDirNode(dirNode, fit->second)
553 : new KDirModelNode(dirNode, fit->second);
554 dirNode->m_childNodes.insert(r, node);
557 node->setItem(fit->second);
560 if (oldUrl != newUrl || hasNewNode) {
567 if (fit->first.mimeTypePtr()->name() != fit->second.mimeTypePtr()->name()) {
568 node->setPreview(QIcon());
571 const QModelIndex index = indexForNode(node);
572 if (!topLeft.isValid() || index.row() < topLeft.row()) {
575 if (!bottomRight.isValid() || index.row() > bottomRight.row()) {
580 #ifndef NDEBUG // debugIndex only defined in debug mode
583 bottomRight = bottomRight.sibling(bottomRight.row(), q->columnCount(QModelIndex())-1);
584 emit q->dataChanged(topLeft, bottomRight);
589 void KDirModelPrivate::_k_slotRedirection(
const KUrl& oldUrl,
const KUrl& newUrl)
591 KDirModelNode* node = nodeForUrl(oldUrl);
610 void KDirModelPrivate::_k_slotClear()
612 const int numRows = m_rootNode->m_childNodes.count();
614 q->beginRemoveRows( QModelIndex(), 0, numRows - 1 );
624 void KDirModelPrivate::_k_slotJobUrlsChanged(
const QStringList& urlList)
626 m_allCurrentDestUrls = urlList;
634 KDirModelNode* node = d->nodeForIndex(index);
636 node->setPreview(QIcon());
638 #ifndef NDEBUG // debugIndex only defined in debug mode
641 emit dataChanged(index, index);
651 if (index.isValid()) {
652 KDirModelNode* node =
static_cast<KDirModelNode*
>(index.internalPointer());
655 case Qt::DisplayRole:
656 switch (index.column()) {
679 switch (index.column()) {
684 case Qt::DecorationRole:
685 if (index.column() ==
Name) {
686 if (!node->preview().isNull()) {
688 return node->preview();
695 case Qt::TextAlignmentRole:
696 if (index.column() ==
Size) {
698 const Qt::Alignment alignment = Qt::AlignRight | Qt::AlignVCenter;
699 return int(alignment);
702 case Qt::ToolTipRole:
705 return QVariant::fromValue(item);
710 KDirModelDirNode* dirNode =
static_cast<KDirModelDirNode *
>(node);
711 int count = dirNode->childCount();
714 if (!path.isEmpty()) {
719 QString s = path + QLatin1String(
"\\*.*" );
720 s.replace(
'/',
'\\');
722 WIN32_FIND_DATA findData;
723 HANDLE hFile = FindFirstFile( (LPWSTR)s.utf16(), &findData );
724 if( hFile != INVALID_HANDLE_VALUE ) {
726 if (!( findData.cFileName[0] ==
'.' &&
727 findData.cFileName[1] ==
'\0' ) &&
728 !( findData.cFileName[0] ==
'.' &&
729 findData.cFileName[1] ==
'.' &&
730 findData.cFileName[2] ==
'\0' ) )
732 }
while( FindNextFile( hFile, &findData ) != 0 );
736 DIR*
dir = ::opendir(QFile::encodeName(path));
739 struct dirent *dirEntry = 0;
740 while ((dirEntry = ::readdir(dir))) {
741 if (dirEntry->d_name[0] ==
'.') {
742 if (dirEntry->d_name[1] ==
'\0')
744 if (dirEntry->d_name[1] ==
'.' && dirEntry->d_name[2] ==
'\0')
753 dirNode->setChildCount(count);
759 if (d->m_jobTransfersVisible && d->m_allCurrentDestUrls.isEmpty() ==
false) {
760 KDirModelNode* node = d->nodeForIndex(index);
761 const QString url = node->item().url().url();
763 return QVariant(d->m_allCurrentDestUrls.contains(url));
780 if (index.column() ==
Name && value.type() == QVariant::String) {
781 Q_ASSERT(index.isValid());
782 KDirModelNode* node =
static_cast<KDirModelNode*
>(index.internalPointer());
784 const QString newName = value.toString();
785 if (newName.isEmpty() || newName == item.
text() || (newName == QLatin1String(
".")) || (newName == QLatin1String(
"..")))
796 case Qt::DecorationRole:
797 if (index.column() ==
Name) {
798 Q_ASSERT(index.isValid());
800 KDirModelNode* node =
static_cast<KDirModelNode*
>(index.internalPointer());
803 if (value.type() == QVariant::Icon) {
804 const QIcon icon(qvariant_cast<QIcon>(value));
805 node->setPreview(icon);
806 }
else if (value.type() == QVariant::Pixmap) {
807 node->setPreview(qvariant_cast<QPixmap>(value));
809 emit dataChanged(index, index);
821 KDirModelNode* node = d->nodeForIndex(parent);
822 if (!node || !d->isDir(node))
825 KDirModelDirNode* parentNode =
static_cast<KDirModelDirNode *
>(node);
826 Q_ASSERT(parentNode);
827 const int count = parentNode->m_childNodes.count();
830 for (
int i = 0; i < count; ++i) {
831 filenames << d->urlForNode(parentNode->m_childNodes.at(i)).fileName();
833 kDebug(7008) <<
"rowCount for " << d->urlForNode(parentNode) <<
": " << count << filenames;
841 if (!index.isValid())
842 return QModelIndex();
843 KDirModelNode* childNode =
static_cast<KDirModelNode*
>(index.internalPointer());
845 KDirModelNode* parentNode = childNode->parent();
846 Q_ASSERT(parentNode);
847 return d->indexForNode(parentNode);
852 return left.
url().compare(right.
url()) < 0;
863 d->m_jobTransfersVisible =
true;
868 disconnect(
this, SLOT(_k_slotJobUrlsChanged(
QStringList)));
875 return d->m_jobTransfersVisible;
885 qSort(ret.begin(), ret.end(),
lessThan);
887 KUrl::List::iterator it = ret.begin();
890 while (it != ret.end()) {
910 bool canUseMostLocalUrls =
true;
911 foreach (
const QModelIndex &index, indexes) {
912 const KFileItem& item = d->nodeForIndex(index)->item();
917 canUseMostLocalUrls =
false;
919 QMimeData *
data =
new QMimeData();
920 const bool different = canUseMostLocalUrls && (mostLocalUrls != urls);
924 urls.populateMimeData(mostLocalUrls, data);
926 urls.populateMimeData(data);
930 QString application_x_qiconlist;
931 const int items = urls.count();
932 for (
int i = 0; i < items; i++) {
933 const int offset = i*16;
934 QString tmp(
"%1$@@$%2$@@$32$@@$32$@@$%3$@@$%4$@@$32$@@$16$@@$no data$@@$");
935 application_x_qiconlist += tmp.arg(offset).arg(offset).arg(offset).arg(offset+40);
937 data->setData(
"application/x-qiconlist", application_x_qiconlist.toLatin1());
945 if (!index.isValid()) {
946 return d->m_dirLister->rootItem();
948 return static_cast<KDirModelNode*
>(index.internalPointer())->item();
952 #ifndef KDE_NO_DEPRECATED
971 KDirModelNode* node = d->nodeForUrl(url);
973 kDebug(7007) << url <<
"not found";
974 return QModelIndex();
976 return d->indexForNode(node);
981 KDirModelNode* parentNode = d->nodeForIndex(parent);
982 Q_ASSERT(parentNode);
983 Q_ASSERT(d->isDir(parentNode));
984 KDirModelNode* childNode =
static_cast<KDirModelDirNode *
>(parentNode)->m_childNodes.value(row);
986 return createIndex(row, column, childNode);
988 return QModelIndex();
993 if (orientation == Qt::Horizontal) {
995 case Qt::DisplayRole:
998 return i18nc(
"@title:column",
"Name");
1000 return i18nc(
"@title:column",
"Size");
1002 return i18nc(
"@title:column",
"Date");
1004 return i18nc(
"@title:column",
"Permissions");
1006 return i18nc(
"@title:column",
"Owner");
1008 return i18nc(
"@title:column",
"Group");
1010 return i18nc(
"@title:column",
"Type");
1019 if (!parent.isValid())
1022 const KFileItem& parentItem =
static_cast<KDirModelNode*
>(parent.internalPointer())->item();
1023 Q_ASSERT(!parentItem.
isNull());
1024 return parentItem.
isDir();
1029 Qt::ItemFlags
f = Qt::ItemIsEnabled;
1030 if (index.column() ==
Name) {
1031 f |= Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
1035 if (d->m_dropsAllowed !=
NoDrops) {
1036 if(!index.isValid()) {
1038 f |= Qt::ItemIsDropEnabled;
1043 kWarning(7007) <<
"Invalid item returned for index";
1044 }
else if (item.
isDir()) {
1046 f |= Qt::ItemIsDropEnabled;
1050 f |= Qt::ItemIsDropEnabled;
1055 f |= Qt::ItemIsDropEnabled;
1058 f |= Qt::ItemIsDropEnabled;
1070 if (!parent.isValid())
1078 KDirModelNode* node =
static_cast<KDirModelNode*
>(parent.internalPointer());
1080 return item.
isDir() && !
static_cast<KDirModelDirNode *
>(node)->isPopulated()
1081 &&
static_cast<KDirModelDirNode *
>(node)->m_childNodes.isEmpty();
1086 if (!parent.isValid())
1089 KDirModelNode* parentNode =
static_cast<KDirModelNode*
>(parent.internalPointer());
1091 KFileItem parentItem = parentNode->item();
1092 Q_ASSERT(!parentItem.
isNull());
1093 Q_ASSERT(parentItem.
isDir());
1094 KDirModelDirNode* dirNode =
static_cast<KDirModelDirNode *
>(parentNode);
1095 if( dirNode->isPopulated() )
1097 dirNode->setPopulated(
true );
1099 const KUrl parentUrl = parentItem.
url();
1117 d->m_dropsAllowed = dropsAllowed;
1123 KDirModelNode* result = d->expandAllParentsUntil(url);
1128 if (!(result->item().isNull()) && result->item().url() == url) {
1130 kDebug(7008) <<
"have it already item=" <<url ;
1134 d->m_urlsBeingFetched[result].append(url);
1136 if (result == d->m_rootNode) {
1137 kDebug(7008) <<
"Remembering to emit expand after listing the root url";
1142 kDebug(7008) <<
"Remembering to emit expand after listing" << result->item().url();
1145 const QModelIndex parentIndex = d->indexForNode(result);
1146 Q_ASSERT(parentIndex.isValid());
1150 bool KDirModel::insertRows(
int ,
int,
const QModelIndex&)
1155 bool KDirModel::insertColumns(
int,
int,
const QModelIndex&)
1160 bool KDirModel::removeRows(
int,
int,
const QModelIndex&)
1165 bool KDirModel::removeColumns(
int,
int,
const QModelIndex&)
1170 #include "kdirmodel.moc"