File indexing completed on 2024-04-28 09:43:49

0001 /***********************************************************************
0002  * SPDX-FileCopyrightText: 2003-2004 Max Howell <max.howell@methylblue.com>
0003  * SPDX-FileCopyrightText: 2008-2009 Martin Sandsmark <martin.sandsmark@kde.org>
0004  * SPDX-FileCopyrightText: 2017-2022 Harald Sitter <sitter@kde.org>
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0007  ***********************************************************************/
0008 
0009 #include "fileTree.h"
0010 
0011 #include <QDir>
0012 #include <QUrl>
0013 
0014 #include "fileCleaner.h"
0015 #include "filelight_debug.h"
0016 
0017 Folder::~Folder()
0018 {
0019     // Trees can be partially destroyed, make sure we remove the reference up to us, it's not a smart pointer
0020     // because that'd risk causing a loop.
0021     if (!m_parent) {
0022         for (auto &file : files) {
0023             file->m_parent = nullptr;
0024         }
0025     }
0026     FileCleaner::instance()->clean(files);
0027 }
0028 
0029 QString File::displayName() const
0030 {
0031     const QString decodedName = QString::fromUtf8(m_name);
0032     return url().isLocalFile() ? QDir::toNativeSeparators(decodedName) : decodedName;
0033 }
0034 
0035 QString File::displayPath(const std::shared_ptr<Folder> &root) const
0036 {
0037     // Use QUrl to sanitize the path for display and then run it through
0038     // QDir to make sure we use native path separators.
0039     const QUrl url = this->url(root);
0040     const QString cleanPath = url.toDisplayString(QUrl::PreferLocalFile | QUrl::NormalizePathSegments);
0041     return url.isLocalFile() ? QDir::toNativeSeparators(cleanPath) : cleanPath;
0042 }
0043 
0044 QUrl File::url(const std::shared_ptr<Folder> &root) const
0045 {
0046     QString path;
0047 
0048     // prevent returning empty string when there is something we could return
0049     const auto rootPtr = root.get() != this ? root.get() : nullptr;
0050     for (const File *d = this; d != rootPtr && d; d = d->parent()) {
0051         path.prepend(QString::fromUtf8(d->name8Bit()));
0052     }
0053 
0054     return QUrl::fromUserInput(path, QString(), QUrl::AssumeLocalFile);
0055 }
0056 
0057 void Folder::clone(const Folder *that, std::shared_ptr<Folder> other)
0058 {
0059     struct Clone {
0060         const Folder *source;
0061         std::shared_ptr<Folder> target;
0062         std::shared_ptr<Folder> parent;
0063     };
0064     QList<Clone> completedClones;
0065     QList<Clone> clones({{that, other, nullptr}});
0066     while (!clones.isEmpty()) {
0067         const auto &clone = clones.takeLast();
0068         qCDebug(FILELIGHT_LOG) << "cloning" << clone.source->m_name << clone.source << "into" << clone.target.get() << "parent" << clone.parent.get();
0069         for (const auto &file : clone.source->files) {
0070             qCDebug(FILELIGHT_LOG) << "  " << file->displayName() << file->isFolder();
0071             if (file->isFolder()) {
0072                 auto folder = std::dynamic_pointer_cast<Folder>(file);
0073                 clones.append(Clone{folder.get(), std::make_shared<Folder>(folder->m_name.constData()), clone.target});
0074             } else {
0075                 clone.target->append(file->m_name.constData(), file->m_size);
0076             }
0077         }
0078         completedClones.append(clone);
0079     }
0080     for (auto it = completedClones.rbegin(); it != completedClones.rend(); ++it) {
0081         // Appending forwards the size data, it must only happen after all duplicating is done so the sizes are known.
0082         // And obviously in reverse order of existence in the tree.
0083         auto clone = *it;
0084         if (clone.parent) {
0085             clone.parent->append(clone.target);
0086         }
0087     }
0088 }
0089 
0090 std::shared_ptr<Folder> Folder::duplicate() const
0091 {
0092     // We completely detach the subtree upon duplication, otherwise we'd have parent pointers pointing back into
0093     // the old tree causing major headaches when trying to keep clean object states. This does take longer
0094     // because we need to create additional objects, but is safer overall.
0095     auto other = std::make_shared<Folder>(m_name.constData());
0096     clone(this, other);
0097     return other;
0098 }