File indexing completed on 2024-03-24 15:48:00

0001 /* This file is part of the KDEproject
0002    Copyright (C) 2000 David Faure <faure@kde.org>
0003    2000 Carsten Pfeiffer <pfeiffer@kde.org>
0004    2002 Klaas Freitag <freitag@suse.de>
0005 
0006    This library is free software; you can redistribute it and/or
0007    modify it under the terms of the GNU Library General Public
0008    License version 2 as published by the Free Software Foundation.
0009 
0010    This library is distributed in the hope that it will be useful,
0011    but WITHOUT ANY WARRANTY; without even the implied warranty of
0012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013    Library General Public License for more details.
0014 
0015    You should have received a copy of the GNU Library General Public License
0016    along with this library; see the file COPYING.LIB.  If not, write to
0017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018    Boston, MA 02110-1301, USA.
0019 */
0020 
0021 #include "filetreebranch.h"
0022 
0023 #include <sys/types.h>
0024 #include <sys/stat.h>
0025 #include <unistd.h>
0026 
0027 #include <qdir.h>
0028 #include <qmimedatabase.h>
0029 
0030 #include <kfileitem.h>
0031 
0032 #include "filetreeview.h"
0033 #include "libfiletree_logging.h"
0034 
0035 
0036 #undef DEBUG_LISTING
0037 #undef DEBUG_MAPPING
0038 
0039 
0040 FileTreeBranch::FileTreeBranch(FileTreeView *parent,
0041                                const QUrl &url,
0042                                const QString &name,
0043                                const QIcon &pix,
0044                                bool showHidden,
0045                                FileTreeViewItem *branchRoot)
0046     : KCoreDirLister(parent),
0047       m_root(branchRoot),
0048       m_name(name),
0049       m_rootIcon(pix),
0050       m_openRootIcon(pix),
0051       m_lastFoundItem(nullptr),
0052       m_recurseChildren(true),
0053       m_showExtensions(true)
0054 {
0055     setObjectName("FileTreeBranch");
0056 
0057     QUrl u(url);
0058     if (u.isLocalFile()) {              // for local files,
0059         QDir d(u.path());               // ensure path is canonical
0060         u.setPath(d.canonicalPath());
0061     }
0062     m_startURL = u;
0063     qCDebug(LIBFILETREE_LOG) << "for" << u;
0064 
0065     // if no root is specified, create a new one
0066     if (m_root == nullptr) m_root = new FileTreeViewItem(parent,
0067                 KFileItem(u, "inode/directory", S_IFDIR),
0068                 this);
0069     //m_root->setExpandable(true);
0070     //m_root->setFirstColumnSpanned(true);
0071     bool sb = blockSignals(true);           // don't want setText() to signal
0072     m_root->setIcon(0, pix);
0073     m_root->setText(0, name);
0074     m_root->setToolTip(0, QString("%1 - %2").arg(name, u.url(QUrl::PreferLocalFile)));
0075     blockSignals(sb);
0076 
0077     setShowHiddenFiles(showHidden);
0078 
0079     connect(this, &KCoreDirLister::itemsAdded, this, &FileTreeBranch::slotItemsAdded);
0080     connect(this, &KCoreDirLister::itemsDeleted, this, &FileTreeBranch::slotItemsDeleted);
0081     connect(this, &KCoreDirLister::refreshItems, this, &FileTreeBranch::slotRefreshItems);
0082     connect(this, &KCoreDirLister::started, this, &FileTreeBranch::slotListerStarted);
0083 
0084     connect(this, &KCoreDirLister::listingDirCompleted, this, &FileTreeBranch::slotListerCompleted);
0085     connect(this, &KCoreDirLister::listingDirCanceled, this, &FileTreeBranch::slotListerCanceled);
0086     connect(this, &KCoreDirLister::clearDir, this, &FileTreeBranch::slotListerClear);
0087     connect(this, &KCoreDirLister::clearDir, this, &FileTreeBranch::slotListerClearUrl);
0088     connect(this, QOverload<const QUrl &, const QUrl &>::of(&KCoreDirLister::redirection), this, &FileTreeBranch::slotRedirect);
0089 
0090     m_openChildrenURLs.append(u);
0091 }
0092 
0093 QUrl FileTreeBranch::rootUrl() const
0094 {
0095     QUrl u = m_startURL.adjusted(QUrl::StripTrailingSlash);
0096     u.setPath(u.path()+'/');
0097     return (u);
0098 }
0099 
0100 void FileTreeBranch::setRoot(FileTreeViewItem *r)
0101 {
0102     m_root = r;
0103 }
0104 
0105 FileTreeViewItem *FileTreeBranch::root() const
0106 {
0107     return (m_root);
0108 }
0109 
0110 QString FileTreeBranch::name() const
0111 {
0112     return (m_name);
0113 }
0114 
0115 void FileTreeBranch::setName(const QString &newName)
0116 {
0117     m_name = newName;
0118 }
0119 
0120 QIcon FileTreeBranch::pixmap() const
0121 {
0122     return (m_rootIcon);
0123 }
0124 
0125 QIcon FileTreeBranch::openPixmap() const
0126 {
0127     return (m_openRootIcon);
0128 }
0129 
0130 void FileTreeBranch::setOpen(bool open)
0131 {
0132     if (root() != nullptr) {
0133         root()->setExpanded(open);
0134     }
0135 }
0136 
0137 void FileTreeBranch::setOpenPixmap(const QIcon &pix)
0138 {
0139     m_openRootIcon = pix;
0140     if (root()->isExpanded()) {
0141         root()->setIcon(0, pix);
0142     }
0143 }
0144 
0145 void FileTreeBranch::slotListerStarted(const QUrl &url)
0146 {
0147 #ifdef DEBUG_LISTING
0148     qCDebug(LIBFILETREE_LOG) << "lister started for" << url;
0149 #endif // DEBUG_LISTING
0150 
0151     FileTreeViewItem *item = findItemByUrl(url);
0152     if (item != nullptr) {
0153         emit populateStarted(item);
0154     }
0155 }
0156 
0157 // Renames seem to be emitted from KDirLister as a delete of the old followed
0158 // by an add of the new.  Therefore there is no need to check for renaming here.
0159 
0160 void FileTreeBranch::slotRefreshItems(const QList<QPair<KFileItem, KFileItem> > &list)
0161 {
0162 #ifdef DEBUG_LISTING
0163     qCDebug(LIBFILETREE_LOG) << "Refreshing" << list.count() << "items";
0164 #endif // DEBUG_LISTING
0165 
0166     FileTreeViewItemList treeViewItList;        // tree view items updated
0167     for (int i = 0; i < list.count(); ++i) {
0168         const KFileItem fi2 = list[i].second;       // not interested in the first
0169 #ifdef DEBUG_LISTING
0170         qCDebug(LIBFILETREE_LOG) << fi2.url();
0171 #endif // DEBUG_LISTING
0172         FileTreeViewItem *item = findItemByUrl(fi2.url());
0173         if (item != nullptr) {
0174             treeViewItList.append(item);
0175             item->setIcon(0, QIcon::fromTheme(fi2.iconName()));
0176             item->setText(0, fi2.text());
0177         }
0178     }
0179 
0180     if (treeViewItList.count() > 0) {
0181         emit changedTreeViewItems(this, treeViewItList);
0182     }
0183 }
0184 
0185 // This would never work in KDE4.  It relies on being able to set the
0186 // KFileItem's extra data to hold its corresponding tree item pointer,
0187 // but since now KFileItem values (not pointers/references) are passed
0188 // around it is not possible to modify the KDirLister's internal KFileItem.
0189 //
0190 // Use findItemByUrl(fi.url()) instead.
0191 //
0192 //FileTreeViewItem *FileTreeBranch::treeItemForFileItem(const KFileItem &fi)
0193 //{
0194 //    if (fi.isNull()) return (nullptr);
0195 //    //qCDebug(LIBFILETREE_LOG) << "for" << fi.url();
0196 //    FileTreeViewItem *ftvi = static_cast<FileTreeViewItem *>(const_cast<void *>(fi.extraData(this)));
0197 //    return (ftvi);
0198 //}
0199 
0200 FileTreeViewItem *FileTreeBranch::findItemByUrl(const QUrl &url)
0201 {
0202     FileTreeViewItem *resultItem = nullptr;
0203 
0204     if (url == m_lastFoundUrl) {            // most likely and fastest first
0205 #ifdef DEBUG_MAPPING
0206         qCDebug(LIBFILETREE_LOG) << "Found as last" << url;
0207 #endif
0208         return (m_lastFoundItem);           // no more to do
0209     } else if (url == m_startURL) {         // see if is the root
0210 #ifdef DEBUG_MAPPING
0211         qCDebug(LIBFILETREE_LOG) << "Found as root" << url;
0212 #endif
0213         resultItem = m_root;
0214     } else if (m_itemMap.contains(url)) {       // see if in our map
0215 #ifdef DEBUG_MAPPING
0216         qCDebug(LIBFILETREE_LOG) << "Found in map" << url;
0217 #endif
0218         resultItem = m_itemMap[url];
0219     } else {                        // need to ask the lister
0220         // See comments on the removed treeItemForFileItem() above.
0221         // We should never get here, the TVImap should have the data for
0222         // every item that we create.
0223         //
0224         ////qCDebug(LIBFILETREE_LOG) << "searching dirlister for" << url;
0225         //const KFileItem it = findByUrl(url);
0226         //if (!it.isNull() )
0227         //{
0228         //    //qCDebug(LIBFILETREE_LOG) << "found item url" << it.url();
0229         //    resultItem = treeItemForFileItem(it);
0230         //}
0231 
0232 #ifdef DEBUG_MAPPING
0233         qCDebug(LIBFILETREE_LOG) << "Not found" << url;
0234 #endif
0235     }
0236 
0237     if (resultItem != nullptr) {                // found something
0238         m_lastFoundItem = resultItem;           // cache for next time
0239         m_lastFoundUrl = url;               // path this applies to
0240     }
0241 
0242     return (resultItem);
0243 }
0244 
0245 // Find an item by a relative path.  If the branch is known, this
0246 // saves having to convert an input path into an URL and then doing
0247 // lots of comparisons on it as above.
0248 FileTreeViewItem *FileTreeBranch::findItemByPath(const QString &path)
0249 {
0250 #ifdef DEBUG_MAPPING
0251     qCDebug(LIBFILETREE_LOG) << path;
0252 #endif
0253 #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
0254     const QStringList pathSplit = path.split('/', Qt::SkipEmptyParts);
0255 #else
0256     const QStringList pathSplit = path.split('/', QString::SkipEmptyParts);
0257 #endif
0258     FileTreeViewItem *item = m_root;
0259 
0260     for (const QString &part : pathSplit)
0261     {
0262         FileTreeViewItem *foundItem = nullptr;
0263         for (int i = 0; i<item->childCount(); ++i)
0264         {
0265             FileTreeViewItem *child = static_cast<FileTreeViewItem *>(item->child(i));
0266             if (child->text(0)==part)
0267             {
0268                 foundItem = child;
0269                 break;
0270             }
0271         }
0272 
0273         if (foundItem==nullptr)
0274         {            
0275 #ifdef DEBUG_MAPPING
0276             qCDebug(LIBFILETREE_LOG) << "didn't find" << part << "under" << item->url();
0277 #endif
0278             return (nullptr);               // no child with that name
0279         }
0280 
0281         item = foundItem;
0282     }
0283 
0284 #ifdef DEBUG_MAPPING
0285     qCDebug(LIBFILETREE_LOG) << "found" << item->url();
0286 #endif
0287     return (item);
0288 }
0289 
0290 void FileTreeBranch::itemRenamed(FileTreeViewItem *item)
0291 {
0292     QUrl u = m_itemMap.key(item);           // find key for that item
0293     if (u.isEmpty()) return;                // not in map, ignore
0294     m_itemMap.remove(u);                // remove old from map
0295     m_itemMap[item->url()] = item;          // save new item in map
0296 }
0297 
0298 // No longer needed, itemsAdded signal passes parent URL
0299 //FileTreeViewItem *FileTreeBranch::parentFTVItem(const KFileItem &fi)
0300 //{
0301 //    if (fi.isNull()) return (nullptr);
0302 //
0303 //    QUrl url = fi.url();
0304 //    //qCDebug(LIBFILETREE_LOG) << "for" << url;
0305 //    url.setFileName(QString());
0306 //    return (findItemByUrl(url));
0307 //}
0308 
0309 FileTreeViewItem *FileTreeBranch::createTreeViewItem(FileTreeViewItem *parent,
0310         const KFileItem &fileItem)
0311 {
0312     FileTreeViewItem *tvi = nullptr;
0313 
0314     if (parent != nullptr && !fileItem.isNull()) {
0315         tvi = new FileTreeViewItem(parent, fileItem, this);
0316         const QString p = fileItem.url().url(QUrl::PreferLocalFile|QUrl::StripTrailingSlash);
0317         m_itemMap[fileItem.url()] = tvi;
0318 #ifdef DEBUG_MAPPING
0319         qCDebug(LIBFILETREE_LOG) << "stored in map" << fileItem.url();
0320 #endif
0321     } else {
0322 #ifdef DEBUG_MAPPING
0323         qCDebug(LIBFILETREE_LOG) << "no parent/fileitem for new item!";
0324 #endif
0325     }
0326     return (tvi);
0327 }
0328 
0329 void FileTreeBranch::slotItemsAdded(const QUrl &parent, const KFileItemList &items)
0330 {
0331 #ifdef DEBUG_LISTING
0332     qCDebug(LIBFILETREE_LOG) << "Adding" << items.count() << "items";
0333 #endif // DEBUG_LISTING
0334 
0335     FileTreeViewItem *parentItem = findItemByUrl(parent);
0336     if (parentItem == nullptr) {
0337         qCWarning(LIBFILETREE_LOG) << "parent item not found for" << parent;
0338         return;
0339     }
0340 
0341     FileTreeViewItem *newItem;
0342     FileTreeViewItemList treeViewItList;        // tree view items created
0343     for (KFileItemList::const_iterator it = items.constBegin();
0344             it != items.constEnd(); ++it) {
0345         const KFileItem currItem = (*it);
0346 
0347         /* Only create a new FileTreeViewItem if it does not yet exist */
0348         if (findItemByUrl(currItem.url()) != nullptr) {
0349             continue;
0350         }
0351 
0352         newItem = createTreeViewItem(parentItem, currItem);
0353         if (newItem == nullptr) {          // should never happen now,
0354             // 'parent' checked above
0355             qCWarning(LIBFILETREE_LOG) << "failed to create item for" << currItem.url();
0356             continue;
0357         }
0358 
0359         // Cut off the file extension if requested, if it is not a directory
0360         if (!m_showExtensions && !currItem.isDir()) {
0361             QString name = currItem.text();
0362             //int mPoint = name.lastIndexOf('.');
0363             //if (mPoint>0) name = name.left(mPoint);
0364 
0365             QMimeDatabase db;
0366             QString ext = db.suffixForFileName(name);
0367             if (!ext.isEmpty())
0368             {
0369                 name.chop(ext.length()+1);
0370                 newItem->setText(0, name);
0371             }
0372         }
0373 
0374         // TODO: is this useful (for local dirs) even in non-dirOnlyMode?
0375         //
0376         /* Now try to find out if there are children for dirs in the treeview */
0377         /* This stats a directory on the local file system and checks the */
0378         /* hardlink entry in the stat-buf. This works only for local directories. */
0379         if (dirOnlyMode() && !m_recurseChildren && currItem.isLocalFile() && currItem.isDir()) {
0380             QUrl url = currItem.url();
0381             QString filename = url.toLocalFile();
0382             /* do the stat trick of Carsten. The problem is, that the hardlink
0383              *  count only contains directory links. Thus, this method only seem
0384              * to work in dir-only mode */
0385 #ifdef DEBUG_LISTING
0386             qCDebug(LIBFILETREE_LOG) << "Doing stat on" << filename;
0387 #endif // DEBUG_LISTING
0388             struct stat statBuf;
0389             if (stat(QFile::encodeName(filename).constData(), &statBuf) == 0) {
0390                 int hardLinks = statBuf.st_nlink;  /* Count of dirs */
0391 #ifdef DEBUG_LISTING
0392                 qCDebug(LIBFILETREE_LOG) << "stat succeeded, hardlinks: " << hardLinks;
0393 #endif // DEBUG_LISTING
0394                 // If the link count is > 2, the directory likely has subdirs. If it's < 2
0395                 // it's something weird like a mounted SMB share. In that case we don't know
0396                 // if there are subdirs, thus show it as expandable.
0397 
0398                 if (hardLinks != 2) {
0399                     newItem->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
0400                 } else {
0401                     newItem->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicator);
0402                 }
0403 
0404                 if (hardLinks >= 2) { // "Normal" directory with subdirs
0405                     hardLinks -= 2;
0406 #ifdef DEBUG_LISTING
0407                     qCDebug(LIBFILETREE_LOG) << "Emitting directoryChildCount" << hardLinks << "for" << url;
0408 #endif // DEBUG_LISTING
0409                     emit directoryChildCount(newItem, hardLinks);
0410                 }
0411             } else {
0412                 qCWarning(LIBFILETREE_LOG) << "stat of" << filename << "failed!";
0413             }
0414         }
0415 
0416         treeViewItList.append(newItem);
0417     }
0418 
0419     if (treeViewItList.count() > 0) {
0420         emit newTreeViewItems(this, treeViewItList);
0421     }
0422 }
0423 
0424 void FileTreeBranch::setChildRecurse(bool t)
0425 {
0426     m_recurseChildren = t;
0427     if (!t) {
0428         m_openChildrenURLs.clear();
0429     }
0430 }
0431 
0432 bool FileTreeBranch::childRecurse()
0433 {
0434     return (m_recurseChildren);
0435 }
0436 
0437 void FileTreeBranch::setShowExtensions(bool visible)
0438 {
0439     m_showExtensions = visible;
0440 }
0441 
0442 bool FileTreeBranch::showExtensions() const
0443 {
0444     return (m_showExtensions);
0445 }
0446 
0447 /*
0448  * The signal that tells that a directory was deleted may arrive before the signal
0449  * for its children arrive. Thus, we must walk through the children of a dir and
0450  * remove them before removing the dir itself.
0451  */
0452 
0453 void FileTreeBranch::slotItemsDeleted(const KFileItemList &items)
0454 {
0455     for (KFileItemList::const_iterator it = items.constBegin();
0456             it != items.constEnd(); ++it) {
0457         const KFileItem fi = (*it);
0458         itemDeleted(&fi);
0459     }
0460 }
0461 
0462 void FileTreeBranch::itemDeleted(const KFileItem *fi)
0463 {
0464     if (fi->isNull()) {
0465         return;
0466     }
0467 #ifdef DEBUG_LISTING
0468     qCDebug(LIBFILETREE_LOG) << "for" << fi->url();
0469 #endif // DEBUG_LISTING
0470 
0471     FileTreeViewItem *ftvi = findItemByUrl(fi->url());
0472     if (ftvi == nullptr) {
0473 #ifdef DEBUG_LISTING
0474         qCDebug(LIBFILETREE_LOG) << "no tree item!";
0475 #endif // DEBUG_LISTING
0476         return;
0477     }
0478 
0479     int nChildren = ftvi->childCount();
0480     if (nChildren > 0) {
0481 #ifdef DEBUG_LISTING
0482         qCDebug(LIBFILETREE_LOG) << "child count" << nChildren;
0483 #endif // DEBUG_LISTING
0484         for (int i = 0; i < nChildren; ++i) {
0485             FileTreeViewItem *ch = static_cast<FileTreeViewItem *>(ftvi->child(i));
0486             if (ch != nullptr) {
0487                 itemDeleted(ch->fileItem());
0488             }
0489         }
0490     }
0491 
0492     QUrl u = fi->url();
0493     if (u == m_lastFoundUrl) {
0494         m_lastFoundUrl = QUrl();            // invalidate last-found cache
0495         m_lastFoundItem = nullptr;
0496     }
0497     m_itemMap.remove(u);                // remove from item map
0498 
0499     delete ftvi;                    // finally remove view item
0500 }
0501 
0502 void FileTreeBranch::slotListerCanceled(const QUrl &url)
0503 {
0504 #ifdef DEBUG_LISTING
0505     qCDebug(LIBFILETREE_LOG) << "lister cancelled for" << url;
0506 #endif // DEBUG_LISTING
0507 
0508     // remove the URL from the children-to-recurse list
0509     m_openChildrenURLs.removeAll(url);
0510 
0511     // stop animations, etc.
0512     FileTreeViewItem *item = findItemByUrl(url);
0513     if (item != nullptr) {
0514         emit populateFinished(item);
0515     }
0516 }
0517 
0518 void FileTreeBranch::slotListerClear()
0519 {
0520 #ifdef DEBUG_LISTING
0521     qCDebug(LIBFILETREE_LOG);
0522 #endif // DEBUG_LISTING
0523     /* this slots needs to clear all listed items, but NOT the root item */
0524     if (m_root != nullptr) {
0525         deleteChildrenOf(m_root);
0526     }
0527 }
0528 
0529 void FileTreeBranch::slotListerClearUrl(const QUrl &url)
0530 {
0531 #ifdef DEBUG_LISTING
0532     qCDebug(LIBFILETREE_LOG) << "for" << url;
0533 #endif // DEBUG_LISTING
0534     FileTreeViewItem *ftvi = findItemByUrl(url);
0535     if (ftvi != nullptr) {
0536         deleteChildrenOf(ftvi);
0537     }
0538 }
0539 
0540 void FileTreeBranch::deleteChildrenOf(QTreeWidgetItem *parent)
0541 {
0542     // for some strange reason, slotListerClearUrl() sometimes calls us
0543     // with a nullptr parent.
0544     if (parent == nullptr) {
0545         return;
0546     }
0547 
0548     QList<QTreeWidgetItem *> childs = parent->takeChildren();
0549     qDeleteAll(childs);
0550 }
0551 
0552 void FileTreeBranch::slotRedirect(const QUrl &oldUrl, const QUrl &newUrl)
0553 {
0554     if (oldUrl.adjusted(QUrl::StripTrailingSlash) ==
0555         m_startURL.adjusted(QUrl::StripTrailingSlash)) {
0556         m_startURL = newUrl;
0557     }
0558 }
0559 
0560 void FileTreeBranch::slotListerCompleted(const QUrl &url)
0561 {
0562 #ifdef DEBUG_LISTING
0563     qCDebug(LIBFILETREE_LOG) << "lister completed for" << url;
0564 #endif // DEBUG_LISTING
0565     FileTreeViewItem *currParent = findItemByUrl(url);
0566     if (currParent == nullptr) {
0567         return;
0568     }
0569 
0570 #ifdef DEBUG_LISTING
0571     qCDebug(LIBFILETREE_LOG) << "current parent" << currParent
0572                              << "already listed?" << currParent->alreadyListed();
0573 #endif // DEBUG_LISTING
0574 
0575     emit populateFinished(currParent);
0576     emit directoryChildCount(currParent, currParent->childCount());
0577 
0578     /* This is a walk through the children of the last populated directory.
0579      * Here we start the dirlister on every child of the dir and wait for its
0580      * finish. When it has finished, we go to the next child.
0581      * This must be done for non local file systems in dirOnly- and Full-Mode
0582      * and for local file systems only in full mode, because the stat trick
0583      * (see addItem-Method) does only work for dirs, not for files in the directory.
0584      */
0585     /* Set bit that the parent dir was listed completely */
0586     currParent->setListed(true);
0587 
0588 #ifdef DEBUG_LISTING
0589     qCDebug(LIBFILETREE_LOG) << "recurseChildren" << m_recurseChildren
0590                              << "isLocalFile" << m_startURL.isLocalFile()
0591                              << "dirOnlyMode" << dirOnlyMode();
0592 #endif // DEBUG_LISTING
0593 
0594     if (m_recurseChildren && (!m_startURL.isLocalFile() || !dirOnlyMode())) {
0595         bool wantRecurseUrl = false;
0596         /* look if the url is in the list for url to recurse */
0597         for (const QUrl &u : qAsConst(m_openChildrenURLs)) {
0598             /* it is only interesting that the url _is_in_ the list. */
0599             if (u.adjusted(QUrl::StripTrailingSlash) == url.adjusted(QUrl::StripTrailingSlash)) {
0600                 wantRecurseUrl = true;
0601                 break;
0602             }
0603         }
0604 
0605 #ifdef DEBUG_LISTING
0606         qCDebug(LIBFILETREE_LOG) << "Recurse for" << url << wantRecurseUrl;
0607 #endif // DEBUG_LISTING
0608         int nChildren = 0;
0609 
0610         if (wantRecurseUrl && currParent != nullptr) {
0611             /* now walk again through the tree and populate the children to get +-signs */
0612             /* This is the starting point. The visible folder has finished,
0613                processing the children has not yet started */
0614             nChildren = currParent->childCount();
0615             if (nChildren == 0) {
0616                 /* This happens if there is no child at all */
0617 #ifdef DEBUG_LISTING
0618                 qCDebug(LIBFILETREE_LOG) << "No children to recurse";
0619 #endif // DEBUG_LISTING
0620             }
0621 
0622             /* Since we have listed the children to recurse, we can remove the entry
0623              * in the list of the URLs to see the children.
0624              */
0625             m_openChildrenURLs.removeAll(url);
0626         }
0627 
0628         /* There are some children. We start a dirlister job on every child item
0629          * which is a directory to find out how much children are in the child
0630          * of the last opened dir.  Skip non directory entries.
0631         */
0632 
0633         for (int i = 0; i < nChildren; ++i) {
0634             const FileTreeViewItem *ch = static_cast<FileTreeViewItem *>(currParent->child(i));
0635             if (ch->isDir() && !ch->alreadyListed()) {
0636                 const KFileItem *fi = ch->fileItem();
0637                 if (!fi->isNull() && fi->isReadable()) {
0638                     QUrl recurseUrl = fi->url();
0639 #ifdef DEBUG_LISTING
0640                     qCDebug(LIBFILETREE_LOG) << "Starting to list" << recurseUrl;
0641 #endif // DEBUG_LISTING
0642                     openUrl(recurseUrl, KCoreDirLister::Keep);
0643                 }
0644             }
0645         }
0646     }
0647 #ifdef DEBUG_LISTING
0648     else {
0649         qCDebug(LIBFILETREE_LOG) << "no need to recurse";
0650     }
0651 #endif // DEBUG_LISTING
0652 }
0653 
0654 /* This slot is called when a tree view item is expanded in the GUI */
0655 bool FileTreeBranch::populate(const QUrl &url, FileTreeViewItem *currItem)
0656 {
0657     bool ret = false;
0658     if (currItem == nullptr) {
0659         return (ret);
0660     }
0661 
0662 #ifdef DEBUG_LISTING
0663     qCDebug(LIBFILETREE_LOG) << "populating" << url;
0664 #endif // DEBUG_LISTING
0665 
0666     /* Add this url to the list of urls to recurse for children */
0667     if (m_recurseChildren) {
0668         m_openChildrenURLs.append(url);
0669 #ifdef DEBUG_LISTING
0670         qCDebug(LIBFILETREE_LOG) << "Adding as open child";
0671 #endif // DEBUG_LISTING
0672     }
0673 
0674     if (!currItem->alreadyListed()) {
0675 #ifdef DEBUG_LISTING
0676         qCDebug(LIBFILETREE_LOG) << "Starting to list";
0677 #endif // DEBUG_LISTING
0678         ret = openUrl(url, KCoreDirLister::Keep);   // start the lister
0679     } else {
0680 #ifdef DEBUG_LISTING
0681         qCDebug(LIBFILETREE_LOG) << "Children already exist";
0682 #endif // DEBUG_LISTING
0683         slotListerCompleted(url);
0684         ret = true;
0685     }
0686 
0687     return (ret);
0688 }