Changeset View
Changeset View
Standalone View
Standalone View
src/widgets/kdirmodel.cpp
Show All 14 Lines | 1 | /* This file is part of the KDE project | |||
---|---|---|---|---|---|
15 | along with this library; see the file COPYING.LIB. If not, write to | 15 | along with this library; see the file COPYING.LIB. If not, write to | ||
16 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 16 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
17 | Boston, MA 02110-1301, USA. | 17 | Boston, MA 02110-1301, USA. | ||
18 | */ | 18 | */ | ||
19 | 19 | | |||
20 | #include "kdirmodel.h" | 20 | #include "kdirmodel.h" | ||
21 | #include "kdirlister.h" | 21 | #include "kdirlister.h" | ||
22 | #include "kfileitem.h" | 22 | #include "kfileitem.h" | ||
23 | #include "kio_widgets_debug.h" | 23 | | ||
24 | #include <klocalizedstring.h> | 24 | #include <klocalizedstring.h> | ||
25 | #include <kjobuidelegate.h> | 25 | #include <kjobuidelegate.h> | ||
26 | #include <kio/simplejob.h> | 26 | #include <kio/simplejob.h> | ||
27 | #include <kio/fileundomanager.h> | 27 | #include <kio/fileundomanager.h> | ||
28 | #include "joburlcache_p.h" | 28 | #include "joburlcache_p.h" | ||
29 | #include <kurlmimedata.h> | 29 | #include <kurlmimedata.h> | ||
30 | #include <kiconloader.h> | 30 | #include <kiconloader.h> | ||
31 | | ||||
31 | #include <QMimeData> | 32 | #include <QMimeData> | ||
32 | #include <QBitArray> | 33 | #include <QBitArray> | ||
33 | #include <QDebug> | 34 | #include <QDebug> | ||
34 | #include <QFile> | 35 | #include <QFile> | ||
35 | #include <QFileInfo> | 36 | #include <QFileInfo> | ||
36 | #include <QDir> | 37 | #include <QDir> | ||
37 | #include <QIcon> | 38 | #include <QIcon> | ||
39 | #include <QLoggingCategory> | ||||
38 | #include <qplatformdefs.h> | 40 | #include <qplatformdefs.h> | ||
39 | 41 | | |||
40 | #include <algorithm> | 42 | #include <algorithm> | ||
41 | 43 | | |||
42 | #ifdef Q_OS_WIN | 44 | #ifdef Q_OS_WIN | ||
43 | #include <qt_windows.h> | 45 | #include <qt_windows.h> | ||
44 | #endif | 46 | #endif | ||
45 | 47 | | |||
48 | static QLoggingCategory category("kf5.kio.kdirmodel", QtInfoMsg); | ||||
49 | | ||||
46 | class KDirModelNode; | 50 | class KDirModelNode; | ||
47 | class KDirModelDirNode; | 51 | class KDirModelDirNode; | ||
48 | 52 | | |||
49 | static QUrl cleanupUrl(const QUrl &url) | 53 | static QUrl cleanupUrl(const QUrl &url) | ||
50 | { | 54 | { | ||
51 | QUrl u = url; | 55 | QUrl u = url; | ||
52 | u.setPath(QDir::cleanPath(u.path())); // remove double slashes in the path, simplify "foo/." to "foo/", etc. | 56 | u.setPath(QDir::cleanPath(u.path())); // remove double slashes in the path, simplify "foo/." to "foo/", etc. | ||
53 | u = u.adjusted(QUrl::StripTrailingSlash); // KDirLister does this too, so we remove the slash before comparing with the root node url. | 57 | u = u.adjusted(QUrl::StripTrailingSlash); // KDirLister does this too, so we remove the slash before comparing with the root node url. | ||
▲ Show 20 Lines • Show All 235 Lines • ▼ Show 20 Line(s) | 274 | { | |||
289 | } | 293 | } | ||
290 | 294 | | |||
291 | for (;;) { | 295 | for (;;) { | ||
292 | QString nodePath = nodeUrl.path(); | 296 | QString nodePath = nodeUrl.path(); | ||
293 | if (!nodePath.endsWith(QLatin1Char('/'))) { | 297 | if (!nodePath.endsWith(QLatin1Char('/'))) { | ||
294 | nodePath += QLatin1Char('/'); | 298 | nodePath += QLatin1Char('/'); | ||
295 | } | 299 | } | ||
296 | if (!pathStr.startsWith(nodePath)) { | 300 | if (!pathStr.startsWith(nodePath)) { | ||
297 | qCWarning(KIO_WIDGETS) << "The kioslave for" << url.scheme() << "violates the hierarchy structure:" | 301 | qCWarning(category) << "The kioslave for" << url.scheme() << "violates the hierarchy structure:" | ||
298 | << "I arrived at node" << nodePath << ", but" << pathStr << "does not start with that path."; | 302 | << "I arrived at node" << nodePath << ", but" << pathStr << "does not start with that path."; | ||
299 | return nullptr; | 303 | return nullptr; | ||
300 | } | 304 | } | ||
301 | 305 | | |||
302 | // E.g. pathStr is /a/b/c and nodePath is /a/. We want to find the node with url /a/b | 306 | // E.g. pathStr is /a/b/c and nodePath is /a/. We want to find the node with url /a/b | ||
303 | const int nextSlash = pathStr.indexOf(QLatin1Char('/'), nodePath.length()); | 307 | const int nextSlash = pathStr.indexOf(QLatin1Char('/'), nodePath.length()); | ||
304 | const QString newPath = pathStr.left(nextSlash); // works even if nextSlash==-1 | 308 | const QString newPath = pathStr.left(nextSlash); // works even if nextSlash==-1 | ||
305 | nodeUrl.setPath(newPath); | 309 | nodeUrl.setPath(newPath); | ||
Show All 18 Lines | |||||
324 | } | 328 | } | ||
325 | // NOTREACHED | 329 | // NOTREACHED | ||
326 | //return 0; | 330 | //return 0; | ||
327 | } | 331 | } | ||
328 | 332 | | |||
329 | #ifndef NDEBUG | 333 | #ifndef NDEBUG | ||
330 | void KDirModelPrivate::dump() | 334 | void KDirModelPrivate::dump() | ||
331 | { | 335 | { | ||
332 | qCDebug(KIO_WIDGETS) << "Dumping contents of KDirModel" << q << "dirLister url:" << m_dirLister->url(); | 336 | qCDebug(category) << "Dumping contents of KDirModel" << q << "dirLister url:" << m_dirLister->url(); | ||
333 | QHashIterator<QUrl, KDirModelNode *> it(m_nodeHash); | 337 | QHashIterator<QUrl, KDirModelNode *> it(m_nodeHash); | ||
334 | while (it.hasNext()) { | 338 | while (it.hasNext()) { | ||
335 | it.next(); | 339 | it.next(); | ||
336 | qDebug(KIO_WIDGETS) << it.key() << it.value(); | 340 | qCDebug(category) << it.key() << it.value(); | ||
337 | } | 341 | } | ||
338 | } | 342 | } | ||
339 | #endif | 343 | #endif | ||
340 | 344 | | |||
341 | // node -> index. If rowNumber is set (or node is root): O(1). Otherwise: O(n). | 345 | // node -> index. If rowNumber is set (or node is root): O(1). Otherwise: O(n). | ||
342 | QModelIndex KDirModelPrivate::indexForNode(KDirModelNode *node, int rowNumber) const | 346 | QModelIndex KDirModelPrivate::indexForNode(KDirModelNode *node, int rowNumber) const | ||
343 | { | 347 | { | ||
344 | if (node == m_rootNode) { | 348 | if (node == m_rootNode) { | ||
▲ Show 20 Lines • Show All 87 Lines • ▼ Show 20 Line(s) | |||||
432 | void KDirModelPrivate::_k_slotNewItems(const QUrl &directoryUrl, const KFileItemList &items) | 436 | void KDirModelPrivate::_k_slotNewItems(const QUrl &directoryUrl, const KFileItemList &items) | ||
433 | { | 437 | { | ||
434 | //qDebug() << "directoryUrl=" << directoryUrl; | 438 | //qDebug() << "directoryUrl=" << directoryUrl; | ||
435 | 439 | | |||
436 | KDirModelNode *result = nodeForUrl(directoryUrl); // O(depth) | 440 | KDirModelNode *result = nodeForUrl(directoryUrl); // O(depth) | ||
437 | // If the directory containing the items wasn't found, then we have a big problem. | 441 | // If the directory containing the items wasn't found, then we have a big problem. | ||
438 | // Are you calling KDirLister::openUrl(url,true,false)? Please use expandToUrl() instead. | 442 | // Are you calling KDirLister::openUrl(url,true,false)? Please use expandToUrl() instead. | ||
439 | if (!result) { | 443 | if (!result) { | ||
440 | qCWarning(KIO_WIDGETS) << "Items emitted in directory" << directoryUrl | 444 | qCWarning(category) << "Items emitted in directory" << directoryUrl | ||
441 | << "but that directory isn't in KDirModel!" | 445 | << "but that directory isn't in KDirModel!" | ||
442 | << "Root directory:" << urlForNode(m_rootNode); | 446 | << "Root directory:" << urlForNode(m_rootNode); | ||
443 | for (const KFileItem &item : items) { | 447 | for (const KFileItem &item : items) { | ||
444 | qDebug() << "Item:" << item.url(); | 448 | qDebug() << "Item:" << item.url(); | ||
445 | } | 449 | } | ||
446 | #ifndef NDEBUG | 450 | #ifndef NDEBUG | ||
447 | dump(); | 451 | dump(); | ||
448 | #endif | 452 | #endif | ||
449 | Q_ASSERT(result); | 453 | Q_ASSERT(result); | ||
450 | } | 454 | } | ||
451 | Q_ASSERT(isDir(result)); | 455 | Q_ASSERT(isDir(result)); | ||
452 | KDirModelDirNode *dirNode = static_cast<KDirModelDirNode *>(result); | 456 | KDirModelDirNode *dirNode = static_cast<KDirModelDirNode *>(result); | ||
453 | 457 | | |||
454 | const QModelIndex index = indexForNode(dirNode); // O(n) | 458 | const QModelIndex index = indexForNode(dirNode); // O(n) | ||
455 | const int newItemsCount = items.count(); | 459 | const int newItemsCount = items.count(); | ||
456 | const int newRowCount = dirNode->m_childNodes.count() + newItemsCount; | 460 | const int newRowCount = dirNode->m_childNodes.count() + newItemsCount; | ||
457 | #if 0 | 461 | | ||
458 | #ifndef NDEBUG // debugIndex only defined in debug mode | 462 | #ifndef NDEBUG // debugIndex only defined in debug mode | ||
459 | //qDebug() << items.count() << "in" << directoryUrl | 463 | qCDebug(category) << items.count() << "in" << directoryUrl | ||
460 | << "index=" << debugIndex(index) << "newRowCount=" << newRowCount; | 464 | << "index=" << debugIndex(index) << "newRowCount=" << newRowCount; | ||
461 | #endif | 465 | #endif | ||
462 | #endif | | |||
463 | 466 | | |||
464 | q->beginInsertRows(index, newRowCount - newItemsCount, newRowCount - 1); // parent, first, last | 467 | q->beginInsertRows(index, newRowCount - newItemsCount, newRowCount - 1); // parent, first, last | ||
465 | 468 | | |||
466 | const QList<QUrl> urlsBeingFetched = m_urlsBeingFetched.value(dirNode); | 469 | const QList<QUrl> urlsBeingFetched = m_urlsBeingFetched.value(dirNode); | ||
467 | //qDebug() << "urlsBeingFetched for dir" << dirNode << directoryUrl << ":" << urlsBeingFetched; | 470 | qCDebug(category) << "urlsBeingFetched for dir" << dirNode << directoryUrl << ":" << urlsBeingFetched; | ||
468 | 471 | | |||
469 | QList<QModelIndex> emitExpandFor; | 472 | QList<QModelIndex> emitExpandFor; | ||
470 | 473 | | |||
471 | dirNode->m_childNodes.reserve(newRowCount); | 474 | dirNode->m_childNodes.reserve(newRowCount); | ||
472 | KFileItemList::const_iterator it = items.begin(); | 475 | KFileItemList::const_iterator it = items.begin(); | ||
473 | KFileItemList::const_iterator end = items.end(); | 476 | KFileItemList::const_iterator end = items.end(); | ||
474 | for (; it != end; ++it) { | 477 | for (; it != end; ++it) { | ||
475 | const bool isDir = it->isDir(); | 478 | const bool isDir = it->isDir(); | ||
476 | KDirModelNode *node = isDir | 479 | KDirModelNode *node = isDir | ||
477 | ? new KDirModelDirNode(dirNode, *it) | 480 | ? new KDirModelDirNode(dirNode, *it) | ||
478 | : new KDirModelNode(dirNode, *it); | 481 | : new KDirModelNode(dirNode, *it); | ||
479 | #ifndef NDEBUG | 482 | #ifndef NDEBUG | ||
480 | // Test code for possible duplication of items in the childnodes list, | 483 | // Test code for possible duplication of items in the childnodes list, | ||
481 | // not sure if/how it ever happened. | 484 | // not sure if/how it ever happened. | ||
482 | //if (dirNode->m_childNodes.count() && | 485 | //if (dirNode->m_childNodes.count() && | ||
483 | // dirNode->m_childNodes.last()->item().name() == (*it).name()) { | 486 | // dirNode->m_childNodes.last()->item().name() == (*it).name()) { | ||
484 | // qCWarning(KIO_WIDGETS) << "Already having" << (*it).name() << "in" << directoryUrl | 487 | // qCWarning(category) << "Already having" << (*it).name() << "in" << directoryUrl | ||
485 | // << "url=" << dirNode->m_childNodes.last()->item().url(); | 488 | // << "url=" << dirNode->m_childNodes.last()->item().url(); | ||
486 | // abort(); | 489 | // abort(); | ||
487 | //} | 490 | //} | ||
488 | #endif | 491 | #endif | ||
489 | dirNode->m_childNodes.append(node); | 492 | dirNode->m_childNodes.append(node); | ||
490 | const QUrl url = it->url(); | 493 | const QUrl url = it->url(); | ||
491 | m_nodeHash.insert(cleanupUrl(url), node); | 494 | m_nodeHash.insert(cleanupUrl(url), node); | ||
492 | //qDebug() << url; | | |||
493 | 495 | | |||
494 | if (!urlsBeingFetched.isEmpty()) { | 496 | if (!urlsBeingFetched.isEmpty()) { | ||
495 | const QUrl &dirUrl = url; | 497 | const QUrl &dirUrl = url; | ||
496 | for (const QUrl &urlFetched : qAsConst(urlsBeingFetched)) { | 498 | for (const QUrl &urlFetched : qAsConst(urlsBeingFetched)) { | ||
497 | if (dirUrl.matches(urlFetched, QUrl::StripTrailingSlash) || dirUrl.isParentOf(urlFetched)) { | 499 | if (dirUrl.matches(urlFetched, QUrl::StripTrailingSlash) || dirUrl.isParentOf(urlFetched)) { | ||
498 | //qDebug() << "Listing found" << dirUrl.url() << "which is a parent of fetched url" << urlFetched; | 500 | //qDebug() << "Listing found" << dirUrl.url() << "which is a parent of fetched url" << urlFetched; | ||
499 | const QModelIndex parentIndex = indexForNode(node, dirNode->m_childNodes.count() - 1); | 501 | const QModelIndex parentIndex = indexForNode(node, dirNode->m_childNodes.count() - 1); | ||
500 | Q_ASSERT(parentIndex.isValid()); | 502 | Q_ASSERT(parentIndex.isValid()); | ||
Show All 21 Lines | 523 | { | |||
522 | KDirModelNode *result = nodeForUrl(directoryUrl); // O(depth) | 524 | KDirModelNode *result = nodeForUrl(directoryUrl); // O(depth) | ||
523 | Q_ASSERT(isDir(result)); | 525 | Q_ASSERT(isDir(result)); | ||
524 | KDirModelDirNode *dirNode = static_cast<KDirModelDirNode *>(result); | 526 | KDirModelDirNode *dirNode = static_cast<KDirModelDirNode *>(result); | ||
525 | m_urlsBeingFetched.remove(dirNode); | 527 | m_urlsBeingFetched.remove(dirNode); | ||
526 | } | 528 | } | ||
527 | 529 | | |||
528 | void KDirModelPrivate::_k_slotDeleteItems(const KFileItemList &items) | 530 | void KDirModelPrivate::_k_slotDeleteItems(const KFileItemList &items) | ||
529 | { | 531 | { | ||
530 | //qDebug() << items.count(); | 532 | qCDebug(category) << items.count() << "items"; | ||
531 | 533 | | |||
532 | // I assume all items are from the same directory. | 534 | // I assume all items are from the same directory. | ||
533 | // From KDirLister's code, this should be the case, except maybe emitChanges? | 535 | // From KDirLister's code, this should be the case, except maybe emitChanges? | ||
534 | const KFileItem item = items.first(); | 536 | const KFileItem item = items.first(); | ||
535 | Q_ASSERT(!item.isNull()); | 537 | Q_ASSERT(!item.isNull()); | ||
536 | QUrl url = item.url(); | 538 | QUrl url = item.url(); | ||
537 | KDirModelNode *node = nodeForUrl(url); // O(depth) | 539 | KDirModelNode *node = nodeForUrl(url); // O(depth) | ||
538 | if (!node) { | 540 | if (!node) { | ||
539 | qCWarning(KIO_WIDGETS) << "No node found for item that was just removed:" << url; | 541 | qCWarning(category) << "No node found for item that was just removed:" << url; | ||
540 | return; | 542 | return; | ||
541 | } | 543 | } | ||
542 | 544 | | |||
543 | KDirModelDirNode *dirNode = node->parent(); | 545 | KDirModelDirNode *dirNode = node->parent(); | ||
544 | if (!dirNode) { | 546 | if (!dirNode) { | ||
545 | return; | 547 | return; | ||
546 | } | 548 | } | ||
547 | 549 | | |||
Show All 13 Lines | |||||
561 | // Let's use a bit array where each bit represents a given child node. | 563 | // Let's use a bit array where each bit represents a given child node. | ||
562 | const int childCount = dirNode->m_childNodes.count(); | 564 | const int childCount = dirNode->m_childNodes.count(); | ||
563 | QBitArray rowNumbers(childCount, false); | 565 | QBitArray rowNumbers(childCount, false); | ||
564 | for (const KFileItem &item : items) { | 566 | for (const KFileItem &item : items) { | ||
565 | if (!node) { // don't lookup the first item twice | 567 | if (!node) { // don't lookup the first item twice | ||
566 | url = item.url(); | 568 | url = item.url(); | ||
567 | node = nodeForUrl(url); | 569 | node = nodeForUrl(url); | ||
568 | if (!node) { | 570 | if (!node) { | ||
569 | qCWarning(KIO_WIDGETS) << "No node found for item that was just removed:" << url; | 571 | qCWarning(category) << "No node found for item that was just removed:" << url; | ||
570 | continue; | 572 | continue; | ||
571 | } | 573 | } | ||
572 | if (!node->parent()) { | 574 | if (!node->parent()) { | ||
573 | // The root node has been deleted, but it was not first in the list 'items'. | 575 | // The root node has been deleted, but it was not first in the list 'items'. | ||
574 | // see https://bugs.kde.org/show_bug.cgi?id=196695 | 576 | // see https://bugs.kde.org/show_bug.cgi?id=196695 | ||
575 | return; | 577 | return; | ||
576 | } | 578 | } | ||
577 | } | 579 | } | ||
▲ Show 20 Lines • Show All 587 Lines • ▼ Show 20 Line(s) | 1160 | { | |||
1165 | if (d->m_dropsAllowed != NoDrops) { | 1167 | if (d->m_dropsAllowed != NoDrops) { | ||
1166 | if (!index.isValid()) { | 1168 | if (!index.isValid()) { | ||
1167 | if (d->m_dropsAllowed & DropOnDirectory) { | 1169 | if (d->m_dropsAllowed & DropOnDirectory) { | ||
1168 | f |= Qt::ItemIsDropEnabled; | 1170 | f |= Qt::ItemIsDropEnabled; | ||
1169 | } | 1171 | } | ||
1170 | } else { | 1172 | } else { | ||
1171 | KFileItem item = itemForIndex(index); | 1173 | KFileItem item = itemForIndex(index); | ||
1172 | if (item.isNull()) { | 1174 | if (item.isNull()) { | ||
1173 | qCWarning(KIO_WIDGETS) << "Invalid item returned for index"; | 1175 | qCWarning(category) << "Invalid item returned for index"; | ||
1174 | } else if (item.isDir()) { | 1176 | } else if (item.isDir()) { | ||
1175 | if (d->m_dropsAllowed & DropOnDirectory) { | 1177 | if (d->m_dropsAllowed & DropOnDirectory) { | ||
1176 | f |= Qt::ItemIsDropEnabled; | 1178 | f |= Qt::ItemIsDropEnabled; | ||
1177 | } | 1179 | } | ||
1178 | } else { // regular file item | 1180 | } else { // regular file item | ||
1179 | if (d->m_dropsAllowed & DropOnAnyFile) { | 1181 | if (d->m_dropsAllowed & DropOnAnyFile) { | ||
1180 | f |= Qt::ItemIsDropEnabled; | 1182 | f |= Qt::ItemIsDropEnabled; | ||
1181 | } else if (d->m_dropsAllowed & DropOnLocalExecutable) { | 1183 | } else if (d->m_dropsAllowed & DropOnLocalExecutable) { | ||
▲ Show 20 Lines • Show All 127 Lines • Show Last 20 Lines |