#include "filetree.h" #include "organizercore.h" #include using namespace MOShared; using namespace MOBase; // in mainwindow.cpp QString UnmanagedModName(); FileTreeItem::FileTreeItem() : m_flags(NoFlags), m_loaded(false) { } FileTreeItem::FileTreeItem( FileTreeItem* parent, std::wstring virtualParentPath, std::wstring realPath, Flags flags, std::wstring file, std::wstring mod) : m_parent(parent), m_virtualParentPath(QString::fromStdWString(virtualParentPath)), m_realPath(QString::fromStdWString(realPath)), m_flags(flags), m_file(QString::fromStdWString(file)), m_mod(QString::fromStdWString(mod)), m_loaded(false) { } void FileTreeItem::add(std::unique_ptr child) { m_children.push_back(std::move(child)); } const std::vector>& FileTreeItem::children() const { return m_children; } FileTreeItem* FileTreeItem::parent() { return m_parent; } const QString& FileTreeItem::virtualParentPath() const { return m_virtualParentPath; } QString FileTreeItem::virtualPath() const { if (m_virtualParentPath.isEmpty()) { return m_file; } else { return m_virtualParentPath + "\\" + m_file; } } QString FileTreeItem::dataRelativeParentPath() const { if (m_virtualParentPath == "data") { return ""; } static const QString prefix = "data\\"; auto path = m_virtualParentPath; if (path.startsWith(prefix)) { path = path.mid(prefix.size()); } return path; } QString FileTreeItem::dataRelativeFilePath() const { auto path = dataRelativeParentPath(); if (!path.isEmpty()) { path += "\\"; } return path += m_file; } const QString& FileTreeItem::filename() const { return m_file; } const QString& FileTreeItem::mod() const { return m_mod; } QFileIconProvider::IconType FileTreeItem::icon() const { if (m_flags & Directory) { return QFileIconProvider::Folder; } else { return QFileIconProvider::File; } } bool FileTreeItem::isDirectory() const { return (m_flags & Directory); } bool FileTreeItem::isFromArchive() const { return (m_flags & FromArchive); } bool FileTreeItem::isConflicted() const { return (m_flags & Conflicted); } bool FileTreeItem::isHidden() const { return m_file.endsWith(ModInfo::s_HiddenExt); } bool FileTreeItem::hasChildren() const { if (!isDirectory()) { return false; } if (isLoaded() && m_children.empty()) { return false; } return true; } void FileTreeItem::setLoaded(bool b) { m_loaded = b; } bool FileTreeItem::isLoaded() const { return m_loaded; } QString FileTreeItem::debugName() const { return QString("%1(ld=%2,cs=%3)") .arg(virtualPath()) .arg(m_loaded) .arg(m_children.size()); } FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : QAbstractItemModel(parent), m_core(core), m_flags(NoFlags) { QFileIconProvider provider; m_fileIcon = provider.icon(QFileIconProvider::File); m_directoryIcon = provider.icon(QFileIconProvider::Folder); } void FileTreeModel::setFlags(Flags f) { m_flags = f; } bool FileTreeModel::showConflicts() const { return (m_flags & Conflicts); } bool FileTreeModel::showArchives() const { return (m_flags & Archives); } void FileTreeModel::refresh() { beginResetModel(); m_root = {nullptr, L"", L"", FileTreeItem::Directory, L"Data", L""}; fill(m_root, *m_core.directoryStructure(), L""); endResetModel(); } void FileTreeModel::ensureLoaded(FileTreeItem* item) const { if (!item) { log::error("ensureLoaded(): item is null"); return; } if (item->isLoaded()) { return; } log::debug("{}: loading on demand", item->debugName()); const auto path = item->dataRelativeFilePath(); auto* dir = m_core.directoryStructure()->findSubDirectoryRecursive( path.toStdWString()); if (!dir) { log::error("{}: directory '{}' not found", item->debugName(), path); return; } const_cast(this) ->fill(*item, *dir, item->dataRelativeParentPath().toStdWString()); } void FileTreeModel::fill( FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, const std::wstring& parentPath) { const std::wstring path = parentPath + (parentPath.empty() ? L"" : L"\\") + parentEntry.getName(); std::vector::const_iterator begin, end; parentEntry.getSubDirectories(begin, end); fillDirectories(parentItem, path, begin, end); fillFiles(parentItem, path, parentEntry.getFiles()); parentItem.setLoaded(true); } void FileTreeModel::fillDirectories( FileTreeItem& parentItem, const std::wstring& path, DirectoryIterator begin, DirectoryIterator end) { for (auto itor=begin; itor!=end; ++itor) { const auto& dir = **itor; auto child = std::make_unique( &parentItem, path, L"", FileTreeItem::Directory, dir.getName(), L""); if (dir.isEmpty()) { child->setLoaded(true); } parentItem.add(std::move(child)); } } void FileTreeModel::fillFiles( FileTreeItem& parentItem, const std::wstring& path, const std::vector& files) { for (auto&& file : files) { if (showConflicts() && (file->getAlternatives().size() == 0)) { continue; } bool isArchive = false; int originID = file->getOrigin(isArchive); if (!showArchives() && isArchive) { continue; } FileTreeItem::Flags flags = FileTreeItem::NoFlags; if (isArchive) { flags |= FileTreeItem::FromArchive; } if (!file->getAlternatives().empty()) { flags |= FileTreeItem::Conflicted; } parentItem.add(std::make_unique( &parentItem, path, file->getFullPath(), flags, file->getName(), makeModName(*file, originID))); } } std::wstring FileTreeModel::makeModName(const FileEntry& file, int originID) const { const auto origin = m_core.directoryStructure()->getOriginByID(originID); return origin.getName(); //const auto index = ModInfo::getIndex(QString::fromStdWString(origin.getName())); //if (index == UINT_MAX) { // return UnmanagedModName(); //} // //std::wstring name = ModInfo::getByIndex(index)->name(); // //std::pair archive = file.getArchive(); //if (archive.first.length() != 0) { // name += L" (" + archive.first + L")"; //} // //return name; } FileTreeItem* FileTreeModel::itemFromIndex(const QModelIndex& index) const { auto* data = index.internalPointer(); if (!data) { return nullptr; } return static_cast(data); } QModelIndex FileTreeModel::index( int row, int col, const QModelIndex& parentIndex) const { FileTreeItem* parent = nullptr; if (!parentIndex.isValid()) { parent = &m_root; } else { parent = itemFromIndex(parentIndex); } if (!parent) { log::error("FileTreeModel::index(): parent is null"); return {}; } ensureLoaded(parent); if (static_cast(row) >= parent->children().size()) { log::error( "FileTreeModel::index(): row {} is out of range for {}", row, parent->debugName()); return {}; } if (col >= columnCount({})) { log::error( "FileTreeModel::index(): col {} is out of range for {}", col, parent->debugName()); return {}; } auto* item = parent->children()[static_cast(row)].get(); return createIndex(row, col, item); } QModelIndex FileTreeModel::parent(const QModelIndex& index) const { if (!index.isValid()) { return {}; } auto* item = itemFromIndex(index); if (!item) { return {}; } auto* parent = item->parent(); if (!parent) { return {}; } ensureLoaded(parent); int row = 0; for (auto&& child : parent->children()) { if (child.get() == item) { return createIndex(row, 0, parent); } ++row; } log::error( "FileTreeModel::parent(): item {} has no child {}", parent->debugName(), item->debugName()); return {}; } int FileTreeModel::rowCount(const QModelIndex& parent) const { FileTreeItem* item = nullptr; if (!parent.isValid()) { item = &m_root; } else { item = itemFromIndex(parent); } if (!item) { return 0; } ensureLoaded(item); return static_cast(item->children().size()); } int FileTreeModel::columnCount(const QModelIndex&) const { return 2; } bool FileTreeModel::hasChildren(const QModelIndex& parent) const { const FileTreeItem* item = nullptr; if (!parent.isValid()) { item = &m_root; } else { item = itemFromIndex(parent); } if (!item) { return false; } return item->hasChildren(); } QVariant FileTreeModel::data(const QModelIndex& index, int role) const { switch (role) { case Qt::DisplayRole: { if (auto* item=itemFromIndex(index)) { if (index.column() == 0) { return item->filename(); } else if (index.column() == 1) { return item->mod(); } } break; } case Qt::FontRole: { if (auto* item=itemFromIndex(index)) { QFont f; if (item->isFromArchive()) { f.setItalic(true); } else if (item->isHidden()) { f.setStrikeOut(true); } return f; } break; } case Qt::ToolTipRole: { /* const auto alternatives = file->getAlternatives(); if (!alternatives.empty()) { std::wostringstream altString; altString << tr("Also in:
").toStdWString(); for (std::vector>>::iterator altIter = alternatives.begin(); altIter != alternatives.end(); ++altIter) { if (altIter != alternatives.begin()) { altString << " , "; } altString << "" << m_core.directoryStructure()->getOriginByID(altIter->first).getName() << ""; } fileChild->setToolTip(1, QString("%1").arg(QString::fromStdWString(altString.str()))); fileChild->setForeground(1, QBrush(Qt::red)); } else { fileChild->setToolTip(1, tr("No conflict")); } */ break; } case Qt::ForegroundRole: { if (index.column() == 1) { if (auto* item=itemFromIndex(index)) { if (item->isConflicted()) { return QBrush(Qt::red); } } } break; } case Qt::DecorationRole: { if (index.column() == 0) { if (auto* item=itemFromIndex(index)) { const auto iconType = item->icon(); if (iconType == QFileIconProvider::File) { return m_fileIcon; } else if (iconType == QFileIconProvider::Folder) { return m_directoryIcon; } } } break; } } return {}; } QVariant FileTreeModel::headerData(int i, Qt::Orientation ori, int role) const { if (role == Qt::DisplayRole) { if (i == 0) { return tr("File"); } else if (i == 1) { return tr("Mod"); } } return {}; } Qt::ItemFlags FileTreeModel::flags(const QModelIndex& index) const { auto f = QAbstractItemModel::flags(index); if (auto* item=itemFromIndex(index)) { if (!item->hasChildren()) { f |= Qt::ItemNeverHasChildren; } } return f; }